#include <stdio.h>
#include <string.h>
#include <rpm/rpmcli.h>
#define RPM_VERSION(major,minor) (major*1000+minor)
#include <rpm/rpmts.h>
#include <rpm/rpmte.h>
#include <rpm/rpmdb.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
/* Chip, this is somewhat stripped down from the default callback used by
the rpmcli. It has to be here to insure that we open the pkg again.
If we don't do this we get segfaults. I also, kept the updating of some
of the rpmcli static vars, but I may not have needed to do this.
Also, we probably want to give a nice interface such that we could allow
users of RPM2 to do their own callback, but that will have to come later.
*/
void * _null_callback(
const void * arg,
const rpmCallbackType what,
const rpm_loff_t amount,
const rpm_loff_t total,
fnpyKey key,
rpmCallbackData data)
{
void * rc = NULL;
const char * filename = (const char *)key;
static FD_t fd = NULL;
/* Code stolen from rpminstall.c and modified */
switch(what) {
case RPMCALLBACK_INST_OPEN_FILE:
if (filename == NULL || filename[0] == '\0')
return NULL;
fd = Fopen(filename, "r.ufdio");
/* FIX: still necessary? */
if (fd == NULL || Ferror(fd)) {
fprintf(stderr, "open of %s failed!\n", filename);
if (fd != NULL) {
Fclose(fd);
fd = NULL;
}
} else
#if RPM2_API < RPM_VERSION(4,9)
fd = fdLink(fd, "persist (showProgress)");
#else
fd = fdLink(fd);
#endif
return (void *)fd;
break;
case RPMCALLBACK_INST_CLOSE_FILE:
/* FIX: still necessary? */
#if RPM2_API < RPM_VERSION(4,9)
fd = fdFree(fd, "persist (showProgress)");
#else
fd = fdFree(fd);
#endif
if (fd != NULL) {
Fclose(fd);
fd = NULL;
}
break;
case RPMCALLBACK_INST_START:
break;
case RPMCALLBACK_TRANS_PROGRESS:
case RPMCALLBACK_INST_PROGRESS:
break;
case RPMCALLBACK_TRANS_START:
break;
case RPMCALLBACK_TRANS_STOP:
break;
case RPMCALLBACK_REPACKAGE_START:
break;
case RPMCALLBACK_REPACKAGE_PROGRESS:
break;
case RPMCALLBACK_REPACKAGE_STOP:
break;
case RPMCALLBACK_UNINST_PROGRESS:
break;
case RPMCALLBACK_UNINST_START:
break;
case RPMCALLBACK_UNINST_STOP:
break;
case RPMCALLBACK_UNPACK_ERROR:
break;
case RPMCALLBACK_CPIO_ERROR:
break;
case RPMCALLBACK_UNKNOWN:
break;
default:
break;
}
return rc;
}
void
_populate_header_tags(HV *href)
{
rpmtd names;
const char *name;
names = rpmtdNew();
rpmTagGetNames(names, 1);
while ((name = rpmtdNextString(names)) != NULL) {
(void)hv_store(href, name, strlen(name),
newSViv(rpmTagGetValue(name + strlen("RPMTAG_"))), 0);
}
}
void
_populate_constant(HV *href, char *name, int val)
{
(void)hv_store(href, name, strlen(name), newSViv(val), 0);
}
#define REGISTER_CONSTANT(name) _populate_constant(constants, #name, name)
MODULE = RPM2 PACKAGE = RPM2
PROTOTYPES: ENABLE
BOOT:
{
HV *header_tags, *constants;
rpmReadConfigFiles(NULL, NULL);
header_tags = perl_get_hv("RPM2::header_tag_map", TRUE);
_populate_header_tags(header_tags);
constants = perl_get_hv("RPM2::constants", TRUE);
/* not the 'standard' way of doing perl constants, but a lot easier to maintain */
REGISTER_CONSTANT(RPMVSF_DEFAULT);
REGISTER_CONSTANT(RPMVSF_NOHDRCHK);
REGISTER_CONSTANT(RPMVSF_NEEDPAYLOAD);
REGISTER_CONSTANT(RPMVSF_NOSHA1HEADER);
REGISTER_CONSTANT(RPMVSF_NODSAHEADER);
REGISTER_CONSTANT(RPMVSF_NORSAHEADER);
REGISTER_CONSTANT(RPMVSF_NOMD5);
REGISTER_CONSTANT(RPMVSF_NODSA);
REGISTER_CONSTANT(RPMVSF_NORSA);
REGISTER_CONSTANT(_RPMVSF_NODIGESTS);
REGISTER_CONSTANT(_RPMVSF_NOSIGNATURES);
REGISTER_CONSTANT(_RPMVSF_NOHEADER);
REGISTER_CONSTANT(_RPMVSF_NOPAYLOAD);
REGISTER_CONSTANT(TR_ADDED);
REGISTER_CONSTANT(TR_REMOVED);
}
double
rpm_api_version(pkg)
char * pkg
CODE:
(void)pkg; /* Not used */
int major = RPM2_API / 1000;
double minor = (int)RPM2_API % 1000;
while (minor >= 1) { minor /= 10; }
RETVAL = major + minor;
OUTPUT:
RETVAL
void
add_macro(pkg, name, val)
char * pkg
char * name
char * val
CODE:
(void)pkg; /* Not used */
addMacro(NULL, name, NULL, val, RMIL_DEFAULT);
void
delete_macro(pkg, name)
char * pkg
char * name
CODE:
(void)pkg; /* Not used */
delMacro(NULL, name);
void
expand_macro(pkg, str)
char * pkg
char * str
PREINIT:
char *ret;
PPCODE:
(void)pkg; /* Not used */
ret = rpmExpand(str, NULL);
PUSHs(sv_2mortal(newSVpv(ret, 0)));
free(ret);
int
rpmvercmp(one, two)
char* one
char* two
void
_read_package_info(fp, vsflags)
FILE *fp
int vsflags
PREINIT:
rpmts ts;
Header ret;
rpmRC rc;
FD_t fd;
PPCODE:
ts = rpmtsCreate();
/* XXX Determine type of signature verification when reading
vsflags |= _RPMTS_VSF_NOLEGACY;
vsflags |= _RPMTS_VSF_NODIGESTS;
vsflags |= _RPMTS_VSF_NOSIGNATURES;
xx = rpmtsSetVerifySigFlags(ts, vsflags);
*/
fd = fdDup(fileno(fp));
rpmtsSetVSFlags(ts, vsflags);
rc = rpmReadPackageFile(ts, fd, "filename or other identifier", &ret);
Fclose(fd);
if (rc == RPMRC_OK) {
SV *h_sv;
EXTEND(SP, 1);
h_sv = sv_newmortal();
sv_setref_pv(h_sv, "RPM2::C::Header", (void *)ret);
PUSHs(h_sv);
}
else {
croak("error reading package");
}
ts = rpmtsFree(ts);
void
_create_transaction(vsflags)
int vsflags
PREINIT:
rpmts ret;
SV *h_sv;
PPCODE:
/* Looking at librpm, it does not look like this ever
returns error (though maybe it should).
*/
ret = rpmtsCreate();
/* Should I save the old vsflags aside? */
rpmtsSetVSFlags(ret, vsflags);
/* Convert and throw the results on the stack */
EXTEND(SP, 1);
h_sv = sv_newmortal();
sv_setref_pv(h_sv, "RPM2::C::Transaction", (void *)ret);
PUSHs(h_sv);
void
_read_from_file(fp)
FILE *fp
PREINIT:
SV *h_sv;
FD_t fd;
Header h;
PPCODE:
fd = fdDup(fileno(fp));
h = headerRead(fd, HEADER_MAGIC_YES);
if (h) {
EXTEND(SP, 1);
h_sv = sv_newmortal();
sv_setref_pv(h_sv, "RPM2::C::Header", (void *)h);
PUSHs(h_sv);
}
Fclose(fd);
rpmts
_open_rpm_db(for_write)
int for_write
PREINIT:
rpmts ts;
CODE:
ts = rpmtsCreate();
if (rpmtsOpenDB(ts, for_write ? O_RDWR : O_RDONLY)) {
croak("rpmtsOpenDB failed");
RETVAL = NULL;
}
RETVAL = ts;
OUTPUT:
RETVAL
MODULE = RPM2 PACKAGE = RPM2::C::DB
void
DESTROY(ts)
rpmts ts
CODE:
rpmtsCloseDB(ts);
rpmtsFree(ts);
void
_close_rpm_db(self)
rpmts self
CODE:
rpmtsCloseDB(self);
rpmtsFree(self);
rpmdbMatchIterator
_init_iterator(ts, rpmtag, key, len)
rpmts ts
int rpmtag
char *key
size_t len
CODE:
/* See rpmtsInitIterator() code for explanation of this */
if (rpmtag == RPMDBI_PACKAGES) {
len = strlen (key);
}
RETVAL = rpmtsInitIterator(ts, rpmtag, len ? key : NULL, len);
OUTPUT:
RETVAL
MODULE = RPM2 PACKAGE = RPM2::C::PackageIterator
Header
_iterator_next(i)
rpmdbMatchIterator i
PREINIT:
Header ret;
SV * h_sv;
unsigned int offset;
PPCODE:
ret = rpmdbNextIterator(i);
if (ret)
headerLink(ret);
if(ret != NULL)
offset = rpmdbGetIteratorOffset(i);
else
offset = 0;
EXTEND(SP, 2);
h_sv = sv_newmortal();
sv_setref_pv(h_sv, "RPM2::C::Header", (void *)ret);
PUSHs(h_sv);
PUSHs(sv_2mortal(newSViv(offset)));
RETVAL = ret;
void
DESTROY(i)
rpmdbMatchIterator i
CODE:
rpmdbFreeIterator(i);
MODULE = RPM2 PACKAGE = RPM2::C::Header
void
DESTROY(h)
Header h
CODE:
headerFree(h);
void
tag_by_id(h, tag)
Header h
int tag
PREINIT:
rpmtd tagdata;
int ok;
PPCODE:
tagdata = rpmtdNew();
if (tagdata == NULL) {
croak("Out of memory");
}
ok = headerGet(h, tag, tagdata, HEADERGET_DEFAULT);
if (!ok) {
/* nop, empty stack */
}
else {
switch(tagdata->type)
{
case RPM_STRING_ARRAY_TYPE:
{
int i;
char **s;
EXTEND(SP, tagdata->count);
s = (char **)tagdata->data;
for (i = 0; i < tagdata->count; i++) {
PUSHs(sv_2mortal(newSVpv(s[i], 0)));
}
}
break;
case RPM_STRING_TYPE:
PUSHs(sv_2mortal(newSVpv((char *)tagdata->data, 0)));
break;
case RPM_CHAR_TYPE:
{
int i;
char *r;
EXTEND(SP, tagdata->count);
r = (char *)tagdata->data;
for (i = 0; i < tagdata->count; i++) {
PUSHs(sv_2mortal(newSViv(r[i])));
}
}
break;
case RPM_INT8_TYPE:
{
int i;
uint8_t *r;
EXTEND(SP, tagdata->count);
r = (uint8_t *)tagdata->data;
for (i = 0; i < tagdata->count; i++) {
PUSHs(sv_2mortal(newSViv(r[i])));
}
}
break;
case RPM_INT16_TYPE:
{
int i;
uint16_t *r;
EXTEND(SP, tagdata->count);
r = (uint16_t *)tagdata->data;
for (i = 0; i < tagdata->count; i++) {
PUSHs(sv_2mortal(newSViv(r[i])));
}
}
break;
case RPM_INT32_TYPE:
{
int i;
uint32_t *r;
EXTEND(SP, tagdata->count);
r = (uint32_t *)tagdata->data;
for (i = 0; i < tagdata->count; i++) {
PUSHs(sv_2mortal(newSViv(r[i])));
}
}
break;
default:
croak("unknown rpm tag type %d", tagdata->type);
}
}
rpmtdFreeData(tagdata);
int
_header_compare(h1, h2)
Header h1
Header h2
CODE:
RETVAL = rpmVersionCompare(h1, h2);
OUTPUT:
RETVAL
int
_header_is_source(h)
Header h
CODE:
RETVAL = headerIsEntry(h, RPMTAG_SOURCEPACKAGE);
OUTPUT:
RETVAL
void
_header_sprintf(h, format)
Header h
char * format
PREINIT:
char * s;
PPCODE:
s = headerFormat(h, format, NULL);
PUSHs(sv_2mortal(newSVpv((char *)s, 0)));
/* By the way, the #if below is completely useless, free() would work for both */
free(s);
MODULE = RPM2 PACKAGE = RPM2::C::Transaction
void
DESTROY(t)
rpmts t
CODE:
t = rpmtsFree(t);
# XXX: Add relocations some day.
int
_add_install(t, h, fn, upgrade)
rpmts t
Header h
char * fn
int upgrade
PREINIT:
rpmRC rc = 0;
CODE:
rc = rpmtsAddInstallElement(t, h, (fnpyKey) fn, upgrade, NULL);
RETVAL = (rc == RPMRC_OK) ? 1 : 0;
OUTPUT:
RETVAL
int
_add_delete(t, h, offset)
rpmts t
Header h
unsigned int offset
PREINIT:
rpmRC rc = 0;
CODE:
rc = rpmtsAddEraseElement(t, h, offset);
RETVAL = (rc == RPMRC_OK) ? 1 : 0;
OUTPUT:
RETVAL
int
_element_count(t)
rpmts t
PREINIT:
int ret;
CODE:
ret = rpmtsNElements(t);
RETVAL = ret;
OUTPUT:
RETVAL
int
_close_db(t)
rpmts t
PREINIT:
int ret;
CODE:
ret = rpmtsCloseDB(t);
RETVAL = (ret == 0) ? 1 : 0;
OUTPUT:
RETVAL
int
_check(t)
rpmts t
PREINIT:
int ret;
CODE:
ret = rpmtsCheck(t);
RETVAL = (ret == 0) ? 1 : 0;
OUTPUT:
RETVAL
int
_order(t)
rpmts t
PREINIT:
int ret;
CODE:
ret = rpmtsOrder(t);
/* XXX: May want to do something different here. It actually
returns the number of non-ordered elements...maybe we
want this?
*/
RETVAL = (ret == 0) ? 1 : 0;
OUTPUT:
RETVAL
void
_elements(t, type)
rpmts t;
rpmElementType type;
PREINIT:
rpmtsi i;
rpmte te;
const char * NEVR;
PPCODE:
i = rpmtsiInit(t);
if(i == NULL) {
printf("Did not get a thing!\n");
return;
} else {
while((te = rpmtsiNext(i, type)) != NULL) {
NEVR = rpmteNEVR(te);
XPUSHs(sv_2mortal(newSVpv(NEVR, 0)));
}
i = rpmtsiFree(i);
}
int
_run(t, ok_probs, prob_filter)
rpmts t
rpmprobFilterFlags prob_filter
PREINIT:
int ret;
CODE:
/* Make sure we could run this transactions */
ret = rpmtsCheck(t);
if (ret != 0) {
RETVAL = 0;
return;
}
ret = rpmtsOrder(t);
if (ret != 0) {
RETVAL = 0;
return;
}
/* XXX: Should support callbacks eventually */
(void) rpmtsSetNotifyCallback(t, _null_callback, (void *) ((long)0));
ret = rpmtsRun(t, NULL, prob_filter);
RETVAL = (ret == 0) ? 1 : 0;
OUTPUT:
RETVAL