diff options
author | Dexter Morgan <dmorgan@mageia.org> | 2011-06-02 20:41:15 +0000 |
---|---|---|
committer | Dexter Morgan <dmorgan@mageia.org> | 2011-06-02 20:41:15 +0000 |
commit | d96d2995b711af8cc5b44f5193f179825093d4a3 (patch) | |
tree | b7a819e981820bc1c560a197467b266c084ef7dc /URPM.xs | |
download | perl-URPM-d96d2995b711af8cc5b44f5193f179825093d4a3.tar perl-URPM-d96d2995b711af8cc5b44f5193f179825093d4a3.tar.gz perl-URPM-d96d2995b711af8cc5b44f5193f179825093d4a3.tar.bz2 perl-URPM-d96d2995b711af8cc5b44f5193f179825093d4a3.tar.xz perl-URPM-d96d2995b711af8cc5b44f5193f179825093d4a3.zip |
Branch for updates
Diffstat (limited to 'URPM.xs')
-rw-r--r-- | URPM.xs | 3944 |
1 files changed, 3944 insertions, 0 deletions
@@ -0,0 +1,3944 @@ +/* Copyright (c) 2002, 2003, 2004, 2005 MandrakeSoft SA + * Copyright (c) 2005, 2006, 2007, 2008 Mandriva SA + * + * All rights reserved. + * This program is free software; you can redistribute it and/or + * modify it under the same terms as Perl itself. + * + * $Id: URPM.xs 259125 2009-08-10 14:37:07Z cfergeau $ + * + */ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include <sys/utsname.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <unistd.h> +#include <zlib.h> +#include <libintl.h> + +#undef Fflush +#undef Mkdir +#undef Stat +#undef Fstat + +static inline void *_free(const void * p) { + if (p != NULL) free((void *)p); + return NULL; +} +typedef struct rpmSpec_s * Spec; + +#include <rpm/rpmio.h> +#include <rpm/rpmdb.h> +#include <rpm/rpmts.h> +#include <rpm/rpmte.h> +#include <rpm/rpmps.h> +#include <rpm/rpmpgp.h> +#include <rpm/rpmcli.h> +#include <rpm/rpmbuild.h> +#include <rpm/rpmlog.h> + +struct s_Package { + char *info; + int filesize; + char *requires; + char *suggests; + char *obsoletes; + char *conflicts; + char *provides; + char *rflags; + char *summary; + unsigned flag; + Header h; +}; + +struct s_Transaction { + rpmts ts; + int count; +}; + +struct s_TransactionData { + SV* callback_open; + SV* callback_close; + SV* callback_trans; + SV* callback_uninst; + SV* callback_inst; + long min_delta; + SV *data; /* chain with another data user provided */ +}; + +typedef struct s_Transaction* URPM__DB; +typedef struct s_Transaction* URPM__Transaction; +typedef struct s_Package* URPM__Package; + +#define FLAG_ID 0x001fffffU +#define FLAG_RATE 0x00e00000U +#define FLAG_BASE 0x01000000U +#define FLAG_SKIP 0x02000000U +#define FLAG_DISABLE_OBSOLETE 0x04000000U +#define FLAG_INSTALLED 0x08000000U +#define FLAG_REQUESTED 0x10000000U +#define FLAG_REQUIRED 0x20000000U +#define FLAG_UPGRADE 0x40000000U +#define FLAG_NO_HEADER_FREE 0x80000000U + +#define FLAG_ID_MAX 0x001ffffe +#define FLAG_ID_INVALID 0x001fffff + +#define FLAG_RATE_POS 21 +#define FLAG_RATE_MAX 5 +#define FLAG_RATE_INVALID 0 + + +#define FILENAME_TAG 1000000 +#define FILESIZE_TAG 1000001 + +#define FILTER_MODE_ALL_FILES 0 +#define FILTER_MODE_CONF_FILES 2 + +/* promote epoch sense should be : + 0 for compability with old packages + 1 for rpm 4.2 and better new approach. */ +#define PROMOTE_EPOCH_SENSE 1 + +static ssize_t write_nocheck(int fd, const void *buf, size_t count) { + return write(fd, buf, count); +} +static const void* unused_variable(const void *p) { + return p; +} + +static int rpmError_callback_data; + +int rpmError_callback() { + write_nocheck(rpmError_callback_data, rpmlogMessage(), strlen(rpmlogMessage())); + return RPMLOG_DEFAULT; +} + +static int rpm_codeset_is_utf8 = 0; + +static SV* +newSVpv_utf8(const char *s, STRLEN len) +{ + SV *sv = newSVpv(s, len); + SvUTF8_on(sv); + return sv; +} + +static void +get_fullname_parts(URPM__Package pkg, char **name, char **version, char **release, char **arch, char **eos) { + char *_version = NULL, *_release = NULL, *_arch = NULL, *_eos = NULL; + + if ((_eos = strchr(pkg->info, '@')) != NULL) { + *_eos = 0; /* mark end of string to enable searching backwards */ + if ((_arch = strrchr(pkg->info, '.')) != NULL) { + *_arch = 0; + if ((release != NULL || version != NULL || name != NULL) && (_release = strrchr(pkg->info, '-')) != NULL) { + *_release = 0; + if ((version != NULL || name != NULL) && (_version = strrchr(pkg->info, '-')) != NULL) { + if (name != NULL) *name = pkg->info; + if (version != NULL) *version = _version + 1; + } + if (release != NULL) *release = _release + 1; + *_release = '-'; + } + if (arch != NULL) *arch = _arch + 1; + *_arch = '.'; + } + if (eos != NULL) *eos = _eos; + *_eos = '@'; + } +} + +static char * +get_name(Header header, int32_t tag) { + struct rpmtd_s val; + + headerGet(header, tag, &val, HEADERGET_MINMEM); + char *name = (char *) rpmtdGetString(&val); + return name ? name : ""; +} + +static int +get_int(Header header, int32_t tag) { + struct rpmtd_s val; + + headerGet(header, tag, &val, HEADERGET_DEFAULT); + uint32_t *ep = rpmtdGetUint32(&val); + return ep ? *ep : 0; +} + +static int +sigsize_to_filesize(int sigsize) { + return sigsize + 440; /* 440 is the rpm header size (?) empirical, but works */ +} + +static int +print_list_entry(char *buff, int sz, const char *name, uint32_t flags, const char *evr) { + int len = strlen(name); + char *p = buff; + + if (len >= sz || !strncmp(name, "rpmlib(", 7)) return -1; + memcpy(p, name, len); p += len; + + if (flags & (RPMSENSE_PREREQ|RPMSENSE_SCRIPT_PREUN|RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_POSTUN|RPMSENSE_SCRIPT_POST)) { + if (p - buff + 3 >= sz) return -1; + memcpy(p, "[*]", 4); p += 3; + } + if (evr != NULL) { + len = strlen(evr); + if (len > 0) { + if (p - buff + 6 + len >= sz) return -1; + *p++ = '['; + if (flags & RPMSENSE_LESS) *p++ = '<'; + if (flags & RPMSENSE_GREATER) *p++ = '>'; + if (flags & RPMSENSE_EQUAL) *p++ = '='; + if ((flags & (RPMSENSE_LESS|RPMSENSE_EQUAL|RPMSENSE_GREATER)) == RPMSENSE_EQUAL) *p++ = '='; + *p++ = ' '; + memcpy(p, evr, len); p+= len; + *p++ = ']'; + } + } + *p = 0; /* make sure to mark null char, Is it really necessary ? */ + + return p - buff; +} + +static int +ranges_overlap(uint32_t aflags, char *sa, uint32_t bflags, char *sb, int b_nopromote) { + if (!aflags || !bflags) + return 1; /* really faster to test it there instead of later */ + else { + int sense = 0; + char *eosa = strchr(sa, ']'); + char *eosb = strchr(sb, ']'); + char *ea, *va, *ra, *eb, *vb, *rb; + + if (eosa) *eosa = 0; + if (eosb) *eosb = 0; + /* parse sa as an [epoch:]version[-release] */ + for (ea = sa; *sa >= '0' && *sa <= '9'; ++sa); + if (*sa == ':') { + *sa++ = 0; /* ea could be an empty string (should be interpreted as 0) */ + va = sa; + } else { + va = ea; /* no epoch */ + ea = NULL; + } + if ((ra = strrchr(sa, '-'))) *ra++ = 0; + /* parse sb as an [epoch:]version[-release] */ + for (eb = sb; *sb >= '0' && *sb <= '9'; ++sb); + if (*sb == ':') { + *sb++ = 0; /* ea could be an empty string (should be interpreted as 0) */ + vb = sb; + } else { + vb = eb; /* no epoch */ + eb = NULL; + } + if ((rb = strrchr(sb, '-'))) *rb++ = 0; + /* now compare epoch */ + if (ea && eb) + sense = rpmvercmp(*ea ? ea : "0", *eb ? eb : "0"); + else if (ea && *ea && atol(ea) > 0) + sense = b_nopromote ? 1 : 0; + else if (eb && *eb && atol(eb) > 0) + sense = -1; + /* now compare version and release if epoch has not been enough */ + if (sense == 0) { + sense = rpmvercmp(va, vb); + if (sense == 0 && ra && *ra && rb && *rb) + sense = rpmvercmp(ra, rb); + } + /* restore all character that have been modified inline */ + if (rb) rb[-1] = '-'; + if (ra) ra[-1] = '-'; + if (eb) vb[-1] = ':'; + if (ea) va[-1] = ':'; + if (eosb) *eosb = ']'; + if (eosa) *eosa = ']'; + /* finish the overlap computation */ + if (sense < 0 && ((aflags & RPMSENSE_GREATER) || (bflags & RPMSENSE_LESS))) + return 1; + else if (sense > 0 && ((aflags & RPMSENSE_LESS) || (bflags & RPMSENSE_GREATER))) + return 1; + else if (sense == 0 && (((aflags & RPMSENSE_EQUAL) && (bflags & RPMSENSE_EQUAL)) || + ((aflags & RPMSENSE_LESS) && (bflags & RPMSENSE_LESS)) || + ((aflags & RPMSENSE_GREATER) && (bflags & RPMSENSE_GREATER)))) + return 1; + else + return 0; + } +} + +static int has_old_suggests; +int32_t is_old_suggests(int32_t flags) { + int is = flags & RPMSENSE_MISSINGOK; + if (is) has_old_suggests = is; + return is; +} +int32_t is_not_old_suggests(int32_t flags) { + return !is_old_suggests(flags); +} + +typedef int (*callback_list_str)(char *s, int slen, const char *name, const uint32_t flags, const char *evr, void *param); + +static int +callback_list_str_xpush(char *s, int slen, const char *name, uint32_t flags, const char *evr, __attribute__((unused)) void *param) { + dSP; + if (s) { + XPUSHs(sv_2mortal(newSVpv(s, slen))); + } else { + char buff[4096]; + int len = print_list_entry(buff, sizeof(buff)-1, name, flags, evr); + if (len >= 0) + XPUSHs(sv_2mortal(newSVpv(buff, len))); + } + PUTBACK; + /* returning zero indicates to continue processing */ + return 0; +} +static int +callback_list_str_xpush_requires(char *s, int slen, const char *name, const uint32_t flags, const char *evr, __attribute__((unused)) void *param) { + dSP; + if (s) { + XPUSHs(sv_2mortal(newSVpv(s, slen))); + } else if (is_not_old_suggests(flags)) { + char buff[4096]; + int len = print_list_entry(buff, sizeof(buff)-1, name, flags, evr); + if (len >= 0) + XPUSHs(sv_2mortal(newSVpv(buff, len))); + } + PUTBACK; + /* returning zero indicates to continue processing */ + return 0; +} +static int +callback_list_str_xpush_old_suggests(char *s, int slen, const char *name, uint32_t flags, const char *evr, __attribute__((unused)) void *param) { + dSP; + if (s) { + XPUSHs(sv_2mortal(newSVpv(s, slen))); + } else if (is_old_suggests(flags)) { + char buff[4096]; + int len = print_list_entry(buff, sizeof(buff)-1, name, flags, evr); + if (len >= 0) + XPUSHs(sv_2mortal(newSVpv(buff, len))); + } + PUTBACK; + /* returning zero indicates to continue processing */ + return 0; +} + +struct cb_overlap_s { + char *name; + int32_t flags; + char *evr; + int direction; /* indicate to compare the above at left or right to the iteration element */ + int b_nopromote; +}; + +static int +callback_list_str_overlap(char *s, int slen, const char *name, uint32_t flags, const char *evr, void *param) { + struct cb_overlap_s *os = (struct cb_overlap_s *)param; + int result = 0; + char *eos = NULL; + char *eon = NULL; + char eosc = '\0'; + char eonc = '\0'; + + /* we need to extract name, flags and evr from a full sense information, store result in local copy */ + if (s) { + if (slen) { eos = s + slen; eosc = *eos; *eos = 0; } + name = s; + while (*s && *s != ' ' && *s != '[' && *s != '<' && *s != '>' && *s != '=') ++s; + if (*s) { + eon = s; + while (*s) { + if (*s == ' ' || *s == '[' || *s == '*' || *s == ']'); + else if (*s == '<') flags |= RPMSENSE_LESS; + else if (*s == '>') flags |= RPMSENSE_GREATER; + else if (*s == '=') flags |= RPMSENSE_EQUAL; + else break; + ++s; + } + evr = s; + } else + evr = ""; + } + + /* mark end of name */ + if (eon) { eonc = *eon; *eon = 0; } + /* names should be equal, else it will not overlap */ + if (!strcmp(name, os->name)) { + /* perform overlap according to direction needed, negative for left */ + if (os->direction < 0) + result = ranges_overlap(os->flags, os->evr, flags, (char *) evr, os->b_nopromote); + else + result = ranges_overlap(flags, (char *) evr, os->flags, os->evr, os->b_nopromote); + } + + /* fprintf(stderr, "cb_list_str_overlap result=%d, os->direction=%d, os->name=%s, os->evr=%s, name=%s, evr=%s\n", + result, os->direction, os->name, os->evr, name, evr); */ + + /* restore s if needed */ + if (eon) *eon = eonc; + if (eos) *eos = eosc; + + return result; +} + +static int +return_list_str(char *s, Header header, int32_t tag_name, int32_t tag_flags, int32_t tag_version, callback_list_str f, void *param) { + int count = 0; + + if (s != NULL) { + char *ps = strchr(s, '@'); + if (tag_flags && tag_version) { + while(ps != NULL) { + ++count; + if (f(s, ps-s, NULL, 0, NULL, param)) return -count; + s = ps + 1; ps = strchr(s, '@'); + } + ++count; + if (f(s, 0, NULL, 0, NULL, param)) return -count; + } else { + char *eos; + while(ps != NULL) { + *ps = 0; eos = strchr(s, '['); if (!eos) eos = strchr(s, ' '); + ++count; + if (f(s, eos ? eos-s : ps-s, NULL, 0, NULL, param)) { *ps = '@'; return -count; } + *ps = '@'; /* restore in memory modified char */ + s = ps + 1; ps = strchr(s, '@'); + } + eos = strchr(s, '['); if (!eos) eos = strchr(s, ' '); + ++count; + if (f(s, eos ? eos-s : 0, NULL, 0, NULL, param)) return -count; + } + } else if (header) { + struct rpmtd_s list, flags, list_evr; + + if (headerGet(header, tag_name, &list, HEADERGET_DEFAULT)) { + memset((void*)&flags, 0, sizeof(flags)); + memset((void*)&list_evr, 0, sizeof(list_evr)); + if (tag_flags) headerGet(header, tag_flags, &flags, HEADERGET_DEFAULT); + if (tag_version) headerGet(header, tag_version, &list_evr, HEADERGET_DEFAULT); + while (rpmtdNext(&list) >= 0) { + ++count; + uint32_t *flag = rpmtdNextUint32(&flags); + if (f(NULL, 0, rpmtdGetString(&list), flag ? *flag : 0, + rpmtdNextString(&list_evr), param)) { + rpmtdFreeData(&list); + if (tag_flags) rpmtdFreeData(&flags); + if (tag_version) rpmtdFreeData(&list_evr); + return -count; + } + } + rpmtdFreeData(&list); + if (tag_flags) rpmtdFreeData(&flags); + if (tag_version) rpmtdFreeData(&list_evr); + } + } + return count; +} + +static int +xpush_simple_list_str(Header header, int32_t tag_name) { + dSP; + if (header) { + struct rpmtd_s list; + const char *val; + int size; + + if (!headerGet(header, tag_name, &list, HEADERGET_DEFAULT)) return 0; + size = rpmtdCount(&list); + + while ((val = rpmtdNextString(&list))) { + XPUSHs(sv_2mortal(newSVpv(val, 0))); + } + rpmtdFreeData(&list); + PUTBACK; + return size; + } else return 0; +} + +void +return_list_int32_t(Header header, int32_t tag_name) { + dSP; + if (header) { + struct rpmtd_s list; + + if (headerGet(header, tag_name, &list, HEADERGET_DEFAULT)) { + uint32_t *val; + while ((val = rpmtdNextUint32(&list))) + XPUSHs(sv_2mortal(newSViv(*val))); + rpmtdFreeData(&list); + } + } + PUTBACK; +} + +void +return_list_uint_16(Header header, int32_t tag_name) { + dSP; + if (header) { + struct rpmtd_s list; + if (headerGet(header, tag_name, &list, HEADERGET_DEFAULT)) { + int count = rpmtdCount(&list); + int i; + uint16_t *list_ = list.data; + for(i = 0; i < count; i++) { + XPUSHs(sv_2mortal(newSViv(list_[i]))); + } + rpmtdFreeData(&list); + } + } + PUTBACK; +} + +void +return_list_tag_modifier(Header header, int32_t tag_name) { + dSP; + int i; + struct rpmtd_s td; + if (!headerGet(header, tag_name, &td, HEADERGET_DEFAULT)) return; + int count = rpmtdCount(&td); + int32_t *list = td.data; + + for (i = 0; i < count; i++) { + char buff[15]; + char *s = buff; + switch (tag_name) { + case RPMTAG_FILEFLAGS: + if (list[i] & RPMFILE_CONFIG) *s++ = 'c'; + if (list[i] & RPMFILE_DOC) *s++ = 'd'; + if (list[i] & RPMFILE_GHOST) *s++ = 'g'; + if (list[i] & RPMFILE_LICENSE) *s++ = 'l'; + if (list[i] & RPMFILE_MISSINGOK) *s++ = 'm'; + if (list[i] & RPMFILE_NOREPLACE) *s++ = 'n'; + if (list[i] & RPMFILE_SPECFILE) *s++ = 'S'; + if (list[i] & RPMFILE_README) *s++ = 'R'; + if (list[i] & RPMFILE_EXCLUDE) *s++ = 'e'; + if (list[i] & RPMFILE_ICON) *s++ = 'i'; + if (list[i] & RPMFILE_UNPATCHED) *s++ = 'u'; + if (list[i] & RPMFILE_PUBKEY) *s++ = 'p'; + break; + default: + rpmtdFreeData(&td); + return; + } + *s = '\0'; + XPUSHs(sv_2mortal(newSVpv(buff, strlen(buff)))); + } + rpmtdFreeData(&td); + PUTBACK; +} + +void +return_list_tag(URPM__Package pkg, int32_t tag_name) { + dSP; + if (pkg->h != NULL) { + struct rpmtd_s td; + if (headerGet(pkg->h, tag_name, &td, HEADERGET_DEFAULT)) { + void *list = td.data; + int32_t count = rpmtdCount(&td); + if (tag_name == RPMTAG_ARCH) { + XPUSHs(sv_2mortal(newSVpv(headerIsEntry(pkg->h, RPMTAG_SOURCERPM) ? (char *) list : "src", 0))); + } else + switch (rpmtdType(&td)) { + case RPM_NULL_TYPE: + break; + case RPM_CHAR_TYPE: + case RPM_INT8_TYPE: + case RPM_INT16_TYPE: + case RPM_INT32_TYPE: + { + int i; + int *r; + r = (int *)list; + for (i=0; i < count; i++) { + XPUSHs(sv_2mortal(newSViv(r[i]))); + } + } + break; + case RPM_STRING_TYPE: + XPUSHs(sv_2mortal(newSVpv((char *) list, 0))); + break; + case RPM_BIN_TYPE: + break; + case RPM_STRING_ARRAY_TYPE: + { + int i; + char **s; + + s = (char **)list; + for (i = 0; i < count; i++) { + XPUSHs(sv_2mortal(newSVpv(s[i], 0))); + } + } + break; + case RPM_I18NSTRING_TYPE: + break; + case RPM_INT64_TYPE: + break; + } + } + } else { + char *name; + char *version; + char *release; + char *arch; + char *eos; + switch (tag_name) { + case RPMTAG_NAME: + { + get_fullname_parts(pkg, &name, &version, &release, &arch, &eos); + if (version - name < 1) croak("invalid fullname"); + XPUSHs(sv_2mortal(newSVpv(name, version-name - 1))); + } + break; + case RPMTAG_VERSION: + { + get_fullname_parts(pkg, &name, &version, &release, &arch, &eos); + if (release - version < 1) croak("invalid fullname"); + XPUSHs(sv_2mortal(newSVpv(version, release-version - 1))); + } + break; + case RPMTAG_RELEASE: + { + get_fullname_parts(pkg, &name, &version, &release, &arch, &eos); + if (arch - release < 1) croak("invalid fullname"); + XPUSHs(sv_2mortal(newSVpv(release, arch-release - 1))); + } + break; + case RPMTAG_ARCH: + { + get_fullname_parts(pkg, &name, &version, &release, &arch, &eos); + XPUSHs(sv_2mortal(newSVpv(arch, eos-arch))); + } + break; + case RPMTAG_SUMMARY: + XPUSHs(sv_2mortal(newSVpv(pkg->summary, 0))); + break; + } + } + PUTBACK; +} + + +void +return_files(Header header, int filter_mode) { + dSP; + if (header) { + char buff[4096]; + char *p, *s; + STRLEN len; + unsigned int i; + + struct rpmtd_s td_flags, td_fmodes; + int32_t *flags = NULL; + uint16_t *fmodes = NULL; + if (filter_mode) { + headerGet(header, RPMTAG_FILEFLAGS, &td_flags, HEADERGET_DEFAULT); + headerGet(header, RPMTAG_FILEMODES, &td_fmodes, HEADERGET_DEFAULT); + flags = td_flags.data; + fmodes = td_fmodes.data; + } + + struct rpmtd_s td_baseNames, td_dirIndexes, td_dirNames, td_list; + headerGet(header, RPMTAG_BASENAMES, &td_baseNames, HEADERGET_DEFAULT); + headerGet(header, RPMTAG_DIRINDEXES, &td_dirIndexes, HEADERGET_DEFAULT); + headerGet(header, RPMTAG_DIRNAMES, &td_dirNames, HEADERGET_DEFAULT); + + char **baseNames = td_baseNames.data; + char **dirNames = td_dirNames.data; + int32_t *dirIndexes = td_dirIndexes.data; + + char **list = NULL; + if (!baseNames || !dirNames || !dirIndexes) { + if (!headerGet(header, RPMTAG_OLDFILENAMES, &td_list, HEADERGET_DEFAULT)) return; + list = td_list.data; + } + + for(i = 0; i < rpmtdCount(&td_baseNames); i++) { + if (list) { + s = list[i]; + len = strlen(list[i]); + } else { + len = strlen(dirNames[dirIndexes[i]]); + if (len >= sizeof(buff)) continue; + memcpy(p = buff, dirNames[dirIndexes[i]], len + 1); p += len; + len = strlen(baseNames[i]); + if (p - buff + len >= sizeof(buff)) continue; + memcpy(p, baseNames[i], len + 1); p += len; + s = buff; + len = p-buff; + } + + if (filter_mode) { + if ((filter_mode & FILTER_MODE_CONF_FILES) && flags && (flags[i] & RPMFILE_CONFIG) == 0) continue; + } + + XPUSHs(sv_2mortal(newSVpv(s, len))); + } + + free(baseNames); + free(dirNames); + free(list); + } + PUTBACK; +} + +void +return_problems(rpmps ps, int translate_message, int raw_message) { + dSP; + if (ps && rpmpsNumProblems(ps) > 0) { + rpmpsi iterator = rpmpsInitIterator(ps); + while (rpmpsNextIterator(iterator) >= 0) { + rpmProblem p = rpmpsGetProblem(iterator); + + if (translate_message) { + /* translate error using rpm localization */ + const char *buf = rpmProblemString(p); + SV *sv = newSVpv(buf, 0); + if (rpm_codeset_is_utf8) SvUTF8_on(sv); + XPUSHs(sv_2mortal(sv)); + _free(buf); + } + if (raw_message) { + const char *pkgNEVR = rpmProblemGetPkgNEVR(p) ? rpmProblemGetPkgNEVR(p) : ""; + const char *altNEVR = rpmProblemGetAltNEVR(p) ? rpmProblemGetAltNEVR(p) : ""; + const char *s = rpmProblemGetStr(p) ? rpmProblemGetStr(p) : ""; + SV *sv; + + switch (rpmProblemGetType(p)) { + case RPMPROB_BADARCH: + sv = newSVpvf("badarch@%s", pkgNEVR); break; + + case RPMPROB_BADOS: + sv = newSVpvf("bados@%s", pkgNEVR); break; + + case RPMPROB_PKG_INSTALLED: + sv = newSVpvf("installed@%s", pkgNEVR); break; + + case RPMPROB_BADRELOCATE: + sv = newSVpvf("badrelocate@%s@%s", pkgNEVR, s); break; + + case RPMPROB_NEW_FILE_CONFLICT: + case RPMPROB_FILE_CONFLICT: + sv = newSVpvf("conflicts@%s@%s@%s", pkgNEVR, altNEVR, s); break; + + case RPMPROB_OLDPACKAGE: + sv = newSVpvf("installed@%s@%s", pkgNEVR, altNEVR); break; + + case RPMPROB_DISKSPACE: + sv = newSVpvf("diskspace@%s@%s@%lld", pkgNEVR, s, (long long)rpmProblemGetDiskNeed(p)); break; + case RPMPROB_DISKNODES: + sv = newSVpvf("disknodes@%s@%s@%lld", pkgNEVR, s, (long long)rpmProblemGetDiskNeed(p)); break; + case RPMPROB_REQUIRES: + sv = newSVpvf("requires@%s@%s", pkgNEVR, altNEVR+2); break; + + case RPMPROB_CONFLICT: + sv = newSVpvf("conflicts@%s@%s", pkgNEVR, altNEVR+2); break; + + default: + sv = newSVpvf("unknown@%s", pkgNEVR); break; + } + XPUSHs(sv_2mortal(sv)); + } + } + rpmpsFreeIterator(iterator); + } + PUTBACK; +} + +static char * +pack_list(Header header, int32_t tag_name, int32_t tag_flags, int32_t tag_version, int32_t (*check_flag)(int32_t)) { + char buff[65536]; + int32_t *flags = NULL; + char **list_evr = NULL; + unsigned int i; + char *p = buff; + + struct rpmtd_s td; + if (headerGet(header, tag_name, &td, HEADERGET_DEFAULT)) { + char **list = td.data; + + struct rpmtd_s td_flags, td_list_evr; + if (tag_flags && headerGet(header, tag_flags, &td_flags, HEADERGET_DEFAULT)) flags = td_flags.data; + if (tag_version && headerGet(header, tag_version, &td_list_evr, HEADERGET_DEFAULT)) list_evr = td_list_evr.data; + for(i = 0; i < rpmtdCount(&td); i++) { + if (check_flag && !check_flag(flags[i])) continue; + int len = print_list_entry(p, sizeof(buff)-(p-buff)-1, list[i], flags ? flags[i] : 0, list_evr ? list_evr[i] : NULL); + if (len < 0) continue; + p += len; + *p++ = '@'; + } + if (p > buff) p[-1] = 0; + + free(list); + free(list_evr); + } + + return p > buff ? memcpy(malloc(p-buff), buff, p-buff) : NULL; +} + +static void +pack_header(URPM__Package pkg) { + if (pkg->h) { + if (pkg->info == NULL) { + char buff[1024]; + char *p = buff; + char *name = get_name(pkg->h, RPMTAG_NAME); + char *version = get_name(pkg->h, RPMTAG_VERSION); + char *release = get_name(pkg->h, RPMTAG_RELEASE); + char *arch = headerIsEntry(pkg->h, RPMTAG_SOURCERPM) ? get_name(pkg->h, RPMTAG_ARCH) : "src"; + + p += 1 + snprintf(buff, sizeof(buff), "%s-%s-%s.%s@%d@%d@%s", name, version, release, arch, + get_int(pkg->h, RPMTAG_EPOCH), get_int(pkg->h, RPMTAG_SIZE), + get_name(pkg->h, RPMTAG_GROUP)); + pkg->info = memcpy(malloc(p-buff), buff, p-buff); + } + if (pkg->filesize == 0) pkg->filesize = sigsize_to_filesize(get_int(pkg->h, RPMTAG_SIGSIZE)); + if (pkg->requires == NULL && pkg->suggests == NULL) + has_old_suggests = 0; + pkg->requires = pack_list(pkg->h, RPMTAG_REQUIRENAME, RPMTAG_REQUIREFLAGS, RPMTAG_REQUIREVERSION, is_not_old_suggests); + if (has_old_suggests) + pkg->suggests = pack_list(pkg->h, RPMTAG_REQUIRENAME, RPMTAG_REQUIREFLAGS, RPMTAG_REQUIREVERSION, is_old_suggests); + else + pkg->suggests = pack_list(pkg->h, RPMTAG_SUGGESTSNAME, 0, 0, NULL); + if (pkg->obsoletes == NULL) + pkg->obsoletes = pack_list(pkg->h, RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEFLAGS, RPMTAG_OBSOLETEVERSION, NULL); + if (pkg->conflicts == NULL) + pkg->conflicts = pack_list(pkg->h, RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTFLAGS, RPMTAG_CONFLICTVERSION, NULL); + if (pkg->provides == NULL) + pkg->provides = pack_list(pkg->h, RPMTAG_PROVIDENAME, RPMTAG_PROVIDEFLAGS, RPMTAG_PROVIDEVERSION, NULL); + if (pkg->summary == NULL) { + char *summary = get_name(pkg->h, RPMTAG_SUMMARY); + int len = 1 + strlen(summary); + + pkg->summary = memcpy(malloc(len), summary, len); + } + + if (!(pkg->flag & FLAG_NO_HEADER_FREE)) pkg->h =headerFree(pkg->h); + pkg->h = 0; + } +} + +static void +update_hash_entry(HV *hash, char *name, STRLEN len, int force, IV use_sense, URPM__Package pkg) { + SV** isv; + + if (!len) len = strlen(name); + if ((isv = hv_fetch(hash, name, len, force))) { + /* check if an entry has been found or created, it should so be updated */ + if (!SvROK(*isv) || SvTYPE(SvRV(*isv)) != SVt_PVHV) { + SV* choice_set = (SV*)newHV(); + if (choice_set) { + SvREFCNT_dec(*isv); /* drop the old as we are changing it */ + if (!(*isv = newRV_noinc(choice_set))) { + SvREFCNT_dec(choice_set); + *isv = &PL_sv_undef; + } + } + } + if (isv && *isv != &PL_sv_undef) { + char id[8]; + STRLEN id_len = snprintf(id, sizeof(id), "%d", pkg->flag & FLAG_ID); + SV **sense = hv_fetch((HV*)SvRV(*isv), id, id_len, 1); + if (sense && use_sense) sv_setiv(*sense, use_sense); + } + } +} + +static void +update_provide_entry(char *name, STRLEN len, int force, IV use_sense, URPM__Package pkg, HV *provides) { + update_hash_entry(provides, name, len, force, use_sense, pkg); +} + +static void +update_provides(URPM__Package pkg, HV *provides) { + if (pkg->h) { + int len; + struct rpmtd_s td, td_flags; + int32_t *flags = NULL; + unsigned int i; + + /* examine requires for files which need to be marked in provides */ + if (headerGet(pkg->h, RPMTAG_REQUIRENAME, &td, HEADERGET_DEFAULT)) { + char **list = td.data; + for (i = 0; i < rpmtdCount(&td); ++i) { + len = strlen(list[i]); + if (list[i][0] == '/') (void)hv_fetch(provides, list[i], len, 1); + } + } + + /* update all provides */ + if (headerGet(pkg->h, RPMTAG_PROVIDENAME, &td, HEADERGET_DEFAULT)) { + char **list = td.data; + if (headerGet(pkg->h, RPMTAG_PROVIDEFLAGS, &td_flags, HEADERGET_DEFAULT)) + flags = td_flags.data; + for (i = 0; i < rpmtdCount(&td); ++i) { + len = strlen(list[i]); + if (!strncmp(list[i], "rpmlib(", 7)) continue; + update_provide_entry(list[i], len, 1, flags && flags[i] & (RPMSENSE_PREREQ|RPMSENSE_SCRIPT_PREUN|RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_POSTUN|RPMSENSE_SCRIPT_POST|RPMSENSE_LESS|RPMSENSE_EQUAL|RPMSENSE_GREATER), + pkg, provides); + } + } + } else { + char *ps, *s, *es; + + if ((s = pkg->requires) != NULL && *s != 0) { + ps = strchr(s, '@'); + while(ps != NULL) { + if (s[0] == '/') { + *ps = 0; es = strchr(s, '['); if (!es) es = strchr(s, ' '); *ps = '@'; + (void)hv_fetch(provides, s, es != NULL ? es-s : ps-s, 1); + } + s = ps + 1; ps = strchr(s, '@'); + } + if (s[0] == '/') { + es = strchr(s, '['); if (!es) es = strchr(s, ' '); + (void)hv_fetch(provides, s, es != NULL ? (U32)(es-s) : strlen(s), 1); + } + } + + if ((s = pkg->provides) != NULL && *s != 0) { + char *es; + + ps = strchr(s, '@'); + while(ps != NULL) { + *ps = 0; es = strchr(s, '['); if (!es) es = strchr(s, ' '); *ps = '@'; + update_provide_entry(s, es != NULL ? es-s : ps-s, 1, es != NULL, pkg, provides); + s = ps + 1; ps = strchr(s, '@'); + } + es = strchr(s, '['); if (!es) es = strchr(s, ' '); + update_provide_entry(s, es != NULL ? es-s : 0, 1, es != NULL, pkg, provides); + } + } +} + +static void +update_obsoletes(URPM__Package pkg, HV *obsoletes) { + if (pkg->h) { + struct rpmtd_s td; + + /* update all provides */ + if (headerGet(pkg->h, RPMTAG_OBSOLETENAME, &td, HEADERGET_DEFAULT)) { + char **list = td.data; + unsigned int i; + for (i = 0; i < rpmtdCount(&td); ++i) + update_hash_entry(obsoletes, list[i], 0, 1, 0, pkg); + } + } else { + char *ps, *s; + + if ((s = pkg->obsoletes) != NULL && *s != 0) { + char *es; + + ps = strchr(s, '@'); + while(ps != NULL) { + *ps = 0; es = strchr(s, '['); if (!es) es = strchr(s, ' '); *ps = '@'; + update_hash_entry(obsoletes, s, es != NULL ? es-s : ps-s, 1, 0, pkg); + s = ps + 1; ps = strchr(s, '@'); + } + es = strchr(s, '['); if (!es) es = strchr(s, ' '); + update_hash_entry(obsoletes, s, es != NULL ? es-s : 0, 1, 0, pkg); + } + } +} + +static void +update_provides_files(URPM__Package pkg, HV *provides) { + if (pkg->h) { + STRLEN len; + char **list = NULL; + unsigned int i; + + struct rpmtd_s td_baseNames, td_dirIndexes, td_dirNames; + if (headerGet(pkg->h, RPMTAG_BASENAMES, &td_baseNames, HEADERGET_DEFAULT) && + headerGet(pkg->h, RPMTAG_DIRINDEXES, &td_dirIndexes, HEADERGET_DEFAULT) && + headerGet(pkg->h, RPMTAG_DIRNAMES, &td_dirNames, HEADERGET_DEFAULT)) { + + char **baseNames = td_baseNames.data; + char **dirNames = td_dirNames.data; + int32_t *dirIndexes = td_dirIndexes.data; + + char buff[4096]; + char *p; + + for(i = 0; i < rpmtdCount(&td_baseNames); i++) { + len = strlen(dirNames[dirIndexes[i]]); + if (len >= sizeof(buff)) continue; + memcpy(p = buff, dirNames[dirIndexes[i]], len + 1); p += len; + len = strlen(baseNames[i]); + if (p - buff + len >= sizeof(buff)) continue; + memcpy(p, baseNames[i], len + 1); p += len; + + update_provide_entry(buff, p-buff, 0, 0, pkg, provides); + } + + free(baseNames); + free(dirNames); + } else { + struct rpmtd_s td; + headerGet(pkg->h, RPMTAG_OLDFILENAMES, &td, HEADERGET_DEFAULT); + if (list) { + for (i = 0; i < rpmtdCount(&td); i++) { + len = strlen(list[i]); + + update_provide_entry(list[i], len, 0, 0, pkg, provides); + } + + free(list); + } + } + } +} + +int +open_archive(char *filename, pid_t *pid, int *empty_archive) { + int fd; + struct { + char header[4]; + char toc_d_count[4]; + char toc_l_count[4]; + char toc_f_count[4]; + char toc_str_size[4]; + char uncompress[40]; + char trailer[4]; + } buf; + + fd = open(filename, O_RDONLY); + if (fd >= 0) { + int pos = lseek(fd, -(int)sizeof(buf), SEEK_END); + if (read(fd, &buf, sizeof(buf)) != sizeof(buf) || strncmp(buf.header, "cz[0", 4) || strncmp(buf.trailer, "0]cz", 4)) { + /* this is not an archive, open it without magic, but first rewind at begin of file */ + lseek(fd, 0, SEEK_SET); + } else if (pos == 0) { + *empty_archive = 1; + fd = -1; + } else { + /* this is an archive, create a pipe and fork for reading with uncompress defined inside */ + int fdno[2]; + + if (!pipe(fdno)) { + if ((*pid = fork()) != 0) { + fd_set readfds; + struct timeval timeout; + + FD_ZERO(&readfds); + FD_SET(fdno[0], &readfds); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + select(fdno[0]+1, &readfds, NULL, NULL, &timeout); + + close(fd); + fd = fdno[0]; + close(fdno[1]); + } else { + char *unpacker[22]; /* enough for 40 bytes in uncompress to never overbuf */ + char *p = buf.uncompress; + int ip = 0; + char *ld_loader = getenv("LD_LOADER"); + + if (ld_loader && *ld_loader) { + unpacker[ip++] = ld_loader; + } + + buf.trailer[0] = 0; /* make sure end-of-string is right */ + while (*p) { + if (*p == ' ' || *p == '\t') *p++ = 0; + else { + unpacker[ip++] = p; + while (*p && *p != ' ' && *p != '\t') ++p; + } + } + unpacker[ip] = NULL; /* needed for execlp */ + + lseek(fd, 0, SEEK_SET); + dup2(fd, STDIN_FILENO); close(fd); + dup2(fdno[1], STDOUT_FILENO); close(fdno[1]); + + /* get rid of "decompression OK, trailing garbage ignored" */ + fd = open("/dev/null", O_WRONLY); + dup2(fd, STDERR_FILENO); close(fd); + + execvp(unpacker[0], unpacker); + exit(1); + } + } else { + close(fd); + fd = -1; + } + } + } + return fd; +} + +static int +call_package_callback(SV *urpm, SV *sv_pkg, SV *callback) { + if (sv_pkg != NULL && callback != NULL) { + int count; + + /* now, a callback will be called for sure */ + dSP; + PUSHMARK(SP); + XPUSHs(urpm); + XPUSHs(sv_pkg); + PUTBACK; + count = call_sv(callback, G_SCALAR); + SPAGAIN; + if (count == 1 && !POPi) { + /* package should not be added in depslist, so we free it */ + SvREFCNT_dec(sv_pkg); + sv_pkg = NULL; + } + PUTBACK; + } + + return sv_pkg != NULL; +} + +static int +parse_line(AV *depslist, HV *provides, HV *obsoletes, URPM__Package pkg, char *buff, SV *urpm, SV *callback) { + SV *sv_pkg; + URPM__Package _pkg; + char *tag, *data; + int data_len; + + if (buff[0] == 0) { + return 1; + } else if ((tag = buff)[0] == '@' && (data = strchr(tag+1, '@')) != NULL) { + *tag++ = *data++ = 0; + data_len = 1+strlen(data); + if (!strcmp(tag, "info")) { + pkg->info = memcpy(malloc(data_len), data, data_len); + pkg->flag &= ~FLAG_ID; + pkg->flag |= 1 + av_len(depslist); + sv_pkg = sv_setref_pv(newSVpv("", 0), "URPM::Package", + _pkg = memcpy(malloc(sizeof(struct s_Package)), pkg, sizeof(struct s_Package))); + if (call_package_callback(urpm, sv_pkg, callback)) { + if (provides) update_provides(_pkg, provides); + if (obsoletes) update_obsoletes(_pkg, obsoletes); + av_push(depslist, sv_pkg); + } + memset(pkg, 0, sizeof(struct s_Package)); + } else if (!strcmp(tag, "filesize")) { + pkg->filesize = atoi(data); + } else if (!strcmp(tag, "requires")) { + free(pkg->requires); pkg->requires = memcpy(malloc(data_len), data, data_len); + } else if (!strcmp(tag, "suggests")) { + free(pkg->suggests); pkg->suggests = memcpy(malloc(data_len), data, data_len); + } else if (!strcmp(tag, "obsoletes")) { + free(pkg->obsoletes); pkg->obsoletes = memcpy(malloc(data_len), data, data_len); + } else if (!strcmp(tag, "conflicts")) { + free(pkg->conflicts); pkg->conflicts = memcpy(malloc(data_len), data, data_len); + } else if (!strcmp(tag, "provides")) { + free(pkg->provides); pkg->provides = memcpy(malloc(data_len), data, data_len); + } else if (!strcmp(tag, "summary")) { + free(pkg->summary); pkg->summary = memcpy(malloc(data_len), data, data_len); + } + return 1; + } else { + fprintf(stderr, "bad line <%s>\n", buff); + return 0; + } +} + +#if 0 +static void pack_rpm_header(Header *h) { + Header packed = headerNew(); + + HeaderIterator hi = headerInitIterator(*h); + struct rpmtd_s td; + while (headerNext(hi, &td)) { + // fprintf(stderr, "adding %s %d\n", tagname(tag), c); + headerPut(packed, &td, HEADERPUT_DEFAULT); + rpmtdFreeData(&td); + } + + headerFreeIterator(hi); + *h = headerFree(*h); + + *h = packed; +} + +static void drop_tags(Header *h) { + headerDel(*h, RPMTAG_FILEUSERNAME); /* user ownership is correct */ + headerDel(*h, RPMTAG_FILEGROUPNAME); /* group ownership is correct */ + headerDel(*h, RPMTAG_FILEMTIMES); /* correct time without it */ + headerDel(*h, RPMTAG_FILEINODES); /* hardlinks work without it */ + headerDel(*h, RPMTAG_FILEDEVICES); /* it is the same number for every file */ + headerDel(*h, RPMTAG_FILESIZES); /* ? */ + headerDel(*h, RPMTAG_FILERDEVS); /* it seems unused. always empty */ + headerDel(*h, RPMTAG_FILEVERIFYFLAGS); /* only used for -V */ + /* keep RPMTAG_FILEFLAGS for %config (rpmnew) to work */ + /* keep RPMTAG_FILELANGS for %lang (_install_langs) to work */ + /* keep RPMTAG_FILELINKTOS for checking conflicts between symlinks */ + /* keep RPMTAG_FILEMODES otherwise it segfaults with excludepath */ + + /* keep RPMTAG_POSTIN RPMTAG_POSTUN RPMTAG_PREIN RPMTAG_PREUN */ + /* keep RPMTAG_TRIGGERSCRIPTS RPMTAG_TRIGGERVERSION RPMTAG_TRIGGERFLAGS RPMTAG_TRIGGERNAME */ + /* small enough, and only in some packages. not needed per se */ + + headerDel(*h, RPMTAG_ICON); + headerDel(*h, RPMTAG_GIF); + headerDel(*h, RPMTAG_EXCLUDE); + headerDel(*h, RPMTAG_EXCLUSIVE); + headerDel(*h, RPMTAG_COOKIE); + headerDel(*h, RPMTAG_VERIFYSCRIPT); + + /* always the same for our packages */ + headerDel(*h, RPMTAG_VENDOR); + headerDel(*h, RPMTAG_DISTRIBUTION); + + /* keep RPMTAG_SIGSIZE, useful to tell the size of the rpm file (+440) */ + + headerDel(*h, RPMTAG_DSAHEADER); + headerDel(*h, RPMTAG_SHA1HEADER); + headerDel(*h, RPMTAG_SIGMD5); + headerDel(*h, RPMTAG_SIGGPG); + + pack_rpm_header(h); +} +#endif + +static int +update_header(char *filename, URPM__Package pkg, __attribute__((unused)) int keep_all_tags, int vsflags) { + int d = open(filename, O_RDONLY); + + if (d >= 0) { + unsigned char sig[4]; + + if (read(d, &sig, sizeof(sig)) == sizeof(sig)) { + lseek(d, 0, SEEK_SET); + if (sig[0] == 0xed && sig[1] == 0xab && sig[2] == 0xee && sig[3] == 0xdb) { + FD_t fd = fdDup(d); + Header header; + rpmts ts; + + close(d); + ts = rpmtsCreate(); + rpmtsSetVSFlags(ts, _RPMVSF_NOSIGNATURES | vsflags); + if (fd != NULL && rpmReadPackageFile(ts, fd, filename, &header) == 0 && header) { + char *basename; + int32_t size; + + basename = strrchr(filename, '/'); + size = fdSize(fd); + Fclose(fd); + + /* this is only kept for compatibility with older distros + (where ->filename on "unpacked" URPM::Package rely on FILENAME_TAG) */ + headerPutString(header, FILENAME_TAG, basename != NULL ? basename + 1 : filename); + + if (pkg->h && !(pkg->flag & FLAG_NO_HEADER_FREE)) pkg->h = headerFree(pkg->h); + pkg->h = header; + pkg->flag &= ~FLAG_NO_HEADER_FREE; + + /*if (!keep_all_tags) drop_tags(&pkg->h);*/ + (void)rpmtsFree(ts); + return 1; + } + (void)rpmtsFree(ts); + } else if (sig[0] == 0x8e && sig[1] == 0xad && sig[2] == 0xe8 && sig[3] == 0x01) { + FD_t fd = fdDup(d); + + close(d); + if (fd != NULL) { + if (pkg->h && !(pkg->flag & FLAG_NO_HEADER_FREE)) pkg->h = headerFree(pkg->h); + pkg->h = headerRead(fd, HEADER_MAGIC_YES); + pkg->flag &= ~FLAG_NO_HEADER_FREE; + Fclose(fd); + return 1; + } + } + } + } + return 0; +} + +static int +read_config_files(int force) { + static int already = 0; + int rc = 0; + + if (!already || force) { + rc = rpmReadConfigFiles(NULL, NULL); + already = (rc == 0); /* set config as load only if it succeed */ + } + return rc; +} + +static void +ts_nosignature(rpmts ts) { + rpmtsSetVSFlags(ts, _RPMVSF_NODIGESTS | _RPMVSF_NOSIGNATURES); +} + +static void *rpmRunTransactions_callback(__attribute__((unused)) const void *h, + const rpmCallbackType what, + const rpm_loff_t amount, + const rpm_loff_t total, + fnpyKey pkgKey, + rpmCallbackData data) { + static struct timeval tprev; + static struct timeval tcurr; + static FD_t fd = NULL; + long delta; + int i; + struct s_TransactionData *td = data; + SV *callback = NULL; + char *callback_type = NULL; + char *callback_subtype = NULL; + + if (!td) + return NULL; + + switch (what) { + case RPMCALLBACK_INST_OPEN_FILE: + callback = td->callback_open; + callback_type = "open"; + break; + case RPMCALLBACK_INST_CLOSE_FILE: + callback = td->callback_close; + callback_type = "close"; + break; + case RPMCALLBACK_TRANS_START: + case RPMCALLBACK_TRANS_PROGRESS: + case RPMCALLBACK_TRANS_STOP: + callback = td->callback_trans; + callback_type = "trans"; + break; + case RPMCALLBACK_UNINST_START: + case RPMCALLBACK_UNINST_PROGRESS: + case RPMCALLBACK_UNINST_STOP: + callback = td->callback_uninst; + callback_type = "uninst"; + break; + case RPMCALLBACK_INST_START: + case RPMCALLBACK_INST_PROGRESS: + callback = td->callback_inst; + callback_type = "inst"; + break; + default: + break; + } + + if (callback != NULL) { + switch (what) { + case RPMCALLBACK_TRANS_START: + case RPMCALLBACK_UNINST_START: + case RPMCALLBACK_INST_START: + callback_subtype = "start"; + gettimeofday(&tprev, NULL); + break; + case RPMCALLBACK_TRANS_PROGRESS: + case RPMCALLBACK_UNINST_PROGRESS: + case RPMCALLBACK_INST_PROGRESS: + callback_subtype = "progress"; + gettimeofday(&tcurr, NULL); + delta = 1000000 * (tcurr.tv_sec - tprev.tv_sec) + (tcurr.tv_usec - tprev.tv_usec); + if (delta < td->min_delta && amount < total - 1) + callback = NULL; /* avoid calling too often a given callback */ + else + tprev = tcurr; + break; + case RPMCALLBACK_TRANS_STOP: + case RPMCALLBACK_UNINST_STOP: + callback_subtype = "stop"; + break; + default: + break; + } + + if (callback != NULL) { + /* now, a callback will be called for sure */ + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(td->data); + XPUSHs(sv_2mortal(newSVpv(callback_type, 0))); + XPUSHs(pkgKey != NULL ? sv_2mortal(newSViv((long)pkgKey - 1)) : &PL_sv_undef); + if (callback_subtype != NULL) { + XPUSHs(sv_2mortal(newSVpv(callback_subtype, 0))); + XPUSHs(sv_2mortal(newSViv(amount))); + XPUSHs(sv_2mortal(newSViv(total))); + } + PUTBACK; + i = call_sv(callback, callback == td->callback_open ? G_SCALAR : G_DISCARD); + SPAGAIN; + if (callback == td->callback_open) { + if (i != 1) croak("callback_open should return a file handle"); + i = POPi; + fd = fdDup(i); + if (fd) { +#ifdef RPM490 + fd = fdLink(fd); +#else + fd = fdLink(fd, "persist perl-URPM"); +#endif + Fcntl(fd, F_SETFD, (void *)1); /* necessary to avoid forked/execed process to lock removable */ + } + PUTBACK; + } else if (callback == td->callback_close) { +#ifdef RPM490 + fd = fdFree(fd); +#else + fd = fdFree(fd, "persist perl-URPM"); +#endif + if (fd) { + Fclose(fd); + fd = NULL; + } + } + FREETMPS; + LEAVE; + } + } + return callback == td->callback_open ? fd : NULL; +} + +int rpmtag_from_string(char *tag) +{ + if (!strcmp(tag, "name")) + return RPMTAG_NAME; + else if (!strcmp(tag, "whatprovides")) + return RPMTAG_PROVIDENAME; + else if (!strcmp(tag, "whatrequires")) + return RPMTAG_REQUIRENAME; + else if (!strcmp(tag, "whatconflicts")) + return RPMTAG_CONFLICTNAME; + else if (!strcmp(tag, "group")) + return RPMTAG_GROUP; + else if (!strcmp(tag, "triggeredby")) + return RPMTAG_TRIGGERNAME; + else if (!strcmp(tag, "path")) + return RPMTAG_BASENAMES; + else croak("unknown tag [%s]", tag); +} + +MODULE = URPM PACKAGE = URPM::Package PREFIX = Pkg_ + +void +Pkg_DESTROY(pkg) + URPM::Package pkg + CODE: + free(pkg->info); + free(pkg->requires); + free(pkg->suggests); + free(pkg->obsoletes); + free(pkg->conflicts); + free(pkg->provides); + free(pkg->rflags); + free(pkg->summary); + if (pkg->h && !(pkg->flag & FLAG_NO_HEADER_FREE)) pkg->h = headerFree(pkg->h); + free(pkg); + +void +Pkg_name(pkg) + URPM::Package pkg + PPCODE: + if (pkg->info) { + char *name; + char *version; + + get_fullname_parts(pkg, &name, &version, NULL, NULL, NULL); + if (version - name < 1) croak("invalid fullname"); + XPUSHs(sv_2mortal(newSVpv(name, version-name-1))); + } else if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_NAME), 0))); + } + +void +Pkg_version(pkg) + URPM::Package pkg + PPCODE: + if (pkg->info) { + char *version; + char *release; + + get_fullname_parts(pkg, NULL, &version, &release, NULL, NULL); + if (release - version < 1) croak("invalid fullname"); + XPUSHs(sv_2mortal(newSVpv(version, release-version-1))); + } else if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_VERSION), 0))); + } + +void +Pkg_release(pkg) + URPM::Package pkg + PPCODE: + if (pkg->info) { + char *release; + char *arch; + + get_fullname_parts(pkg, NULL, NULL, &release, &arch, NULL); + if (arch - release < 1) croak("invalid fullname"); + XPUSHs(sv_2mortal(newSVpv(release, arch-release-1))); + } else if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_RELEASE), 0))); + } + +void +Pkg_arch(pkg) + URPM::Package pkg + PPCODE: + if (pkg->info) { + char *arch; + char *eos; + + get_fullname_parts(pkg, NULL, NULL, NULL, &arch, &eos); + XPUSHs(sv_2mortal(newSVpv(arch, eos-arch))); + } else if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(headerIsEntry(pkg->h, RPMTAG_SOURCERPM) ? get_name(pkg->h, RPMTAG_ARCH) : "src", 0))); + } + +int +Pkg_is_arch_compat__XS(pkg) + URPM::Package pkg + INIT: + CODE: + read_config_files(0); + if (pkg->info) { + char *arch; + char *eos; + + get_fullname_parts(pkg, NULL, NULL, NULL, &arch, &eos); + *eos = 0; + RETVAL = rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch); + *eos = '@'; + } else if (pkg->h && headerIsEntry(pkg->h, RPMTAG_SOURCERPM)) { + char *arch = get_name(pkg->h, RPMTAG_ARCH); + RETVAL = rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch); + } else { + RETVAL = 0; + } + OUTPUT: + RETVAL + +int +Pkg_is_platform_compat(pkg) + URPM::Package pkg + INIT: + CODE: + croak("is_platform_compat() is available only since rpm 4.4.8"); + { /* to match last } and avoid another #ifdef for it */ + RETVAL = 0; + } + + OUTPUT: + RETVAL + +void +Pkg_summary(pkg) + URPM::Package pkg + PPCODE: + if (pkg->summary) { + XPUSHs(sv_2mortal(newSVpv_utf8(pkg->summary, 0))); + } else if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv_utf8(get_name(pkg->h, RPMTAG_SUMMARY), 0))); + } + +void +Pkg_description(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv_utf8(get_name(pkg->h, RPMTAG_DESCRIPTION), 0))); + } + +void +Pkg_sourcerpm(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_SOURCERPM), 0))); + } + +void +Pkg_packager(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv_utf8(get_name(pkg->h, RPMTAG_PACKAGER), 0))); + } + +void +Pkg_buildhost(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_BUILDHOST), 0))); + } + +int +Pkg_buildtime(pkg) + URPM::Package pkg + CODE: + if (pkg->h) { + RETVAL = get_int(pkg->h, RPMTAG_BUILDTIME); + } else { + RETVAL = 0; + } + OUTPUT: + RETVAL + +int +Pkg_installtid(pkg) + URPM::Package pkg + CODE: + if (pkg->h) { + RETVAL = get_int(pkg->h, RPMTAG_INSTALLTID); + } else { + RETVAL = 0; + } + OUTPUT: + RETVAL + +void +Pkg_url(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_URL), 0))); + } + +void +Pkg_license(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_LICENSE), 0))); + } + +void +Pkg_distribution(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_DISTRIBUTION), 0))); + } + +void +Pkg_vendor(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_VENDOR), 0))); + } + +void +Pkg_os(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_OS), 0))); + } + +void +Pkg_payload_format(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_PAYLOADFORMAT), 0))); + } + +void +Pkg_fullname(pkg) + URPM::Package pkg + PREINIT: + I32 gimme = GIMME_V; + PPCODE: + if (pkg->info) { + if (gimme == G_SCALAR) { + char *eos; + if ((eos = strchr(pkg->info, '@')) != NULL) { + XPUSHs(sv_2mortal(newSVpv(pkg->info, eos-pkg->info))); + } + } else if (gimme == G_ARRAY) { + char *name, *version, *release, *arch, *eos; + get_fullname_parts(pkg, &name, &version, &release, &arch, &eos); + if (version - name < 1 || release - version < 1 || arch - release < 1) + croak("invalid fullname"); + EXTEND(SP, 4); + PUSHs(sv_2mortal(newSVpv(name, version-name-1))); + PUSHs(sv_2mortal(newSVpv(version, release-version-1))); + PUSHs(sv_2mortal(newSVpv(release, arch-release-1))); + PUSHs(sv_2mortal(newSVpv(arch, eos-arch))); + } + } else if (pkg->h) { + char *name = get_name(pkg->h, RPMTAG_NAME); + char *version = get_name(pkg->h, RPMTAG_VERSION); + char *release = get_name(pkg->h, RPMTAG_RELEASE); + char *arch = headerIsEntry(pkg->h, RPMTAG_SOURCERPM) ? get_name(pkg->h, RPMTAG_ARCH) : "src"; + + if (gimme == G_SCALAR) { + XPUSHs(sv_2mortal(newSVpvf("%s-%s-%s.%s", name, version, release, arch))); + } else if (gimme == G_ARRAY) { + EXTEND(SP, 4); + PUSHs(sv_2mortal(newSVpv(name, 0))); + PUSHs(sv_2mortal(newSVpv(version, 0))); + PUSHs(sv_2mortal(newSVpv(release, 0))); + PUSHs(sv_2mortal(newSVpv(arch, 0))); + } + } + +int +Pkg_epoch(pkg) + URPM::Package pkg + CODE: + if (pkg->info) { + char *s, *eos; + + if ((s = strchr(pkg->info, '@')) != NULL) { + if ((eos = strchr(s+1, '@')) != NULL) *eos = 0; /* mark end of string to enable searching backwards */ + RETVAL = atoi(s+1); + if (eos != NULL) *eos = '@'; + } else { + RETVAL = 0; + } + } else if (pkg->h) { + RETVAL = get_int(pkg->h, RPMTAG_EPOCH); + } else RETVAL = 0; + OUTPUT: + RETVAL + +int +Pkg_compare_pkg(lpkg, rpkg) + URPM::Package lpkg + URPM::Package rpkg + PREINIT: + int compare = 0; + int lepoch; + char *lversion; + char *lrelease; + char *larch; + char *leos; + int repoch; + char *rversion; + char *rrelease; + char *rarch; + char *reos; + CODE: + if (lpkg == rpkg) RETVAL = 0; + else { + if (lpkg->info) { + char *s; + + if ((s = strchr(lpkg->info, '@')) != NULL) { + if ((leos = strchr(s+1, '@')) != NULL) *leos = 0; /* mark end of string to enable searching backwards */ + lepoch = atoi(s+1); + if (leos != NULL) *leos = '@'; + } else { + lepoch = 0; + } + get_fullname_parts(lpkg, NULL, &lversion, &lrelease, &larch, &leos); + /* temporarily mark end of each substring */ + lrelease[-1] = 0; + larch[-1] = 0; + } else if (lpkg->h) { + lepoch = get_int(lpkg->h, RPMTAG_EPOCH); + lversion = get_name(lpkg->h, RPMTAG_VERSION); + lrelease = get_name(lpkg->h, RPMTAG_RELEASE); + larch = headerIsEntry(lpkg->h, RPMTAG_SOURCERPM) ? get_name(lpkg->h, RPMTAG_ARCH) : "src"; + } else croak("undefined package"); + if (rpkg->info) { + char *s; + + if ((s = strchr(rpkg->info, '@')) != NULL) { + if ((reos = strchr(s+1, '@')) != NULL) *reos = 0; /* mark end of string to enable searching backwards */ + repoch = atoi(s+1); + if (reos != NULL) *reos = '@'; + } else { + repoch = 0; + } + get_fullname_parts(rpkg, NULL, &rversion, &rrelease, &rarch, &reos); + /* temporarily mark end of each substring */ + rrelease[-1] = 0; + rarch[-1] = 0; + } else if (rpkg->h) { + repoch = get_int(rpkg->h, RPMTAG_EPOCH); + rversion = get_name(rpkg->h, RPMTAG_VERSION); + rrelease = get_name(rpkg->h, RPMTAG_RELEASE); + rarch = headerIsEntry(rpkg->h, RPMTAG_SOURCERPM) ? get_name(rpkg->h, RPMTAG_ARCH) : "src"; + } else { + /* restore info string modified */ + if (lpkg->info) { + lrelease[-1] = '-'; + larch[-1] = '.'; + } + croak("undefined package"); + } + compare = lepoch - repoch; + if (!compare) { + compare = rpmvercmp(lversion, rversion); + if (!compare) { + compare = rpmvercmp(lrelease, rrelease); + if (!compare) { + int lscore, rscore; + char *eolarch = strchr(larch, '@'); + char *eorarch = strchr(rarch, '@'); + + read_config_files(0); + if (eolarch) *eolarch = 0; lscore = rpmMachineScore(RPM_MACHTABLE_INSTARCH, larch); + if (eorarch) *eorarch = 0; rscore = rpmMachineScore(RPM_MACHTABLE_INSTARCH, rarch); + if (lscore == 0) { + if (rscore == 0) +#if 0 + /* Nanar: TODO check this + * hu ?? what is the goal of strcmp, some of arch are equivalent */ + compare = 0 +#endif + compare = strcmp(larch, rarch); + else + compare = -1; + } else { + if (rscore == 0) + compare = 1; + else + compare = rscore - lscore; /* score are lower for better */ + } + if (eolarch) *eolarch = '@'; + if (eorarch) *eorarch = '@'; + } + } + } + /* restore info string modified */ + if (lpkg->info) { + lrelease[-1] = '-'; + larch[-1] = '.'; + } + if (rpkg->info) { + rrelease[-1] = '-'; + rarch[-1] = '.'; + } + RETVAL = compare; + } + OUTPUT: + RETVAL + +int +Pkg_compare(pkg, evr) + URPM::Package pkg + char *evr + PREINIT: + int compare = 0; + int _epoch; + char *_version; + char *_release; + char *_eos; + CODE: + if (pkg->info) { + char *s; + + if ((s = strchr(pkg->info, '@')) != NULL) { + if ((_eos = strchr(s+1, '@')) != NULL) *_eos = 0; /* mark end of string to enable searching backwards */ + _epoch = atoi(s+1); + if (_eos != NULL) *_eos = '@'; + } else { + _epoch = 0; + } + get_fullname_parts(pkg, NULL, &_version, &_release, &_eos, NULL); + /* temporarily mark end of each substring */ + _release[-1] = 0; + _eos[-1] = 0; + } else if (pkg->h) { + _epoch = get_int(pkg->h, RPMTAG_EPOCH); + } else croak("undefined package"); + if (!compare) { + char *epoch, *version, *release; + + /* extract epoch and version from evr */ + version = evr; + while (*version && isdigit(*version)) version++; + if (*version == ':') { + epoch = evr; + *version++ = 0; + if (!*epoch) epoch = "0"; + compare = _epoch - (*epoch ? atoi(epoch) : 0); + version[-1] = ':'; /* restore in memory modification */ + } else { + /* there is no epoch defined, so assume epoch = 0 */ + version = evr; + compare = _epoch; + } + if (!compare) { + if (!pkg->info) + _version = get_name(pkg->h, RPMTAG_VERSION); + /* continue extracting release if any */ + if ((release = strrchr(version, '-')) != NULL) { + *release++ = 0; + compare = rpmvercmp(_version, version); + if (!compare) { + /* need to compare with release here */ + if (!pkg->info) + _release = get_name(pkg->h, RPMTAG_RELEASE); + compare = rpmvercmp(_release, release); + } + release[-1] = '-'; /* restore in memory modification */ + } else { + compare = rpmvercmp(_version, version); + } + } + } + /* restore info string modified */ + if (pkg->info) { + _release[-1] = '-'; + _eos[-1] = '.'; + } + RETVAL = compare; + OUTPUT: + RETVAL + +int +Pkg_size(pkg) + URPM::Package pkg + CODE: + if (pkg->info) { + char *s, *eos; + + if ((s = strchr(pkg->info, '@')) != NULL && (s = strchr(s+1, '@')) != NULL) { + if ((eos = strchr(s+1, '@')) != NULL) *eos = 0; /* mark end of string to enable searching backwards */ + RETVAL = atoi(s+1); + if (eos != NULL) *eos = '@'; + } else { + RETVAL = 0; + } + } else if (pkg->h) { + RETVAL = get_int(pkg->h, RPMTAG_SIZE); + } else RETVAL = 0; + OUTPUT: + RETVAL + +int +Pkg_filesize(pkg) + URPM::Package pkg + CODE: + if (pkg->filesize) { + RETVAL = pkg->filesize; + } else if (pkg->h) { + RETVAL = sigsize_to_filesize(get_int(pkg->h, RPMTAG_SIGSIZE)); + } else RETVAL = 0; + OUTPUT: + RETVAL + +void +Pkg_group(pkg) + URPM::Package pkg + PPCODE: + if (pkg->info) { + char *s; + + if ((s = strchr(pkg->info, '@')) != NULL && (s = strchr(s+1, '@')) != NULL && (s = strchr(s+1, '@')) != NULL) { + char *eos = strchr(s+1, '@'); + XPUSHs(sv_2mortal(newSVpv_utf8(s+1, eos != NULL ? eos-s-1 : 0))); + } + } else if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv_utf8(get_name(pkg->h, RPMTAG_GROUP), 0))); + } + +void +Pkg_filename(pkg) + URPM::Package pkg + PPCODE: + if (pkg->info) { + char *eon; + + if ((eon = strchr(pkg->info, '@')) != NULL) { + char savbuf[4]; + memcpy(savbuf, eon, 4); /* there should be at least epoch and size described so (@0@0 minimum) */ + memcpy(eon, ".rpm", 4); + XPUSHs(sv_2mortal(newSVpv(pkg->info, eon-pkg->info+4))); + memcpy(eon, savbuf, 4); + } + } else if (pkg->h) { + char *name = get_name(pkg->h, RPMTAG_NAME); + char *version = get_name(pkg->h, RPMTAG_VERSION); + char *release = get_name(pkg->h, RPMTAG_RELEASE); + char *arch = headerIsEntry(pkg->h, RPMTAG_SOURCERPM) ? get_name(pkg->h, RPMTAG_ARCH) : "src"; + + XPUSHs(sv_2mortal(newSVpvf("%s-%s-%s.%s.rpm", name, version, release, arch))); + } + +# deprecated +void +Pkg_header_filename(pkg) + URPM::Package pkg + PPCODE: + if (pkg->info) { + char *eon; + + if ((eon = strchr(pkg->info, '@')) != NULL) { + XPUSHs(sv_2mortal(newSVpv(pkg->info, eon-pkg->info))); + } + } else if (pkg->h) { + char buff[1024]; + char *p = buff; + char *name = get_name(pkg->h, RPMTAG_NAME); + char *version = get_name(pkg->h, RPMTAG_VERSION); + char *release = get_name(pkg->h, RPMTAG_RELEASE); + char *arch = headerIsEntry(pkg->h, RPMTAG_SOURCERPM) ? get_name(pkg->h, RPMTAG_ARCH) : "src"; + + p += snprintf(buff, sizeof(buff), "%s-%s-%s.%s", name, version, release, arch); + XPUSHs(sv_2mortal(newSVpv(buff, p-buff))); + } + +void +Pkg_id(pkg) + URPM::Package pkg + PPCODE: + if ((pkg->flag & FLAG_ID) <= FLAG_ID_MAX) { + XPUSHs(sv_2mortal(newSViv(pkg->flag & FLAG_ID))); + } + +void +Pkg_set_id(pkg, id=-1) + URPM::Package pkg + int id + PPCODE: + if ((pkg->flag & FLAG_ID) <= FLAG_ID_MAX) { + XPUSHs(sv_2mortal(newSViv(pkg->flag & FLAG_ID))); + } + pkg->flag &= ~FLAG_ID; + pkg->flag |= id >= 0 && id <= FLAG_ID_MAX ? id : FLAG_ID_INVALID; + +void +Pkg_requires(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->requires, pkg->h, RPMTAG_REQUIRENAME, RPMTAG_REQUIREFLAGS, RPMTAG_REQUIREVERSION, + callback_list_str_xpush_requires, NULL); + SPAGAIN; + +void +Pkg_requires_nosense(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->requires, pkg->h, RPMTAG_REQUIRENAME, RPMTAG_REQUIREFLAGS, 0, + callback_list_str_xpush_requires, NULL); + SPAGAIN; + +void +Pkg_suggests(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + int count = return_list_str(pkg->suggests, pkg->h, RPMTAG_SUGGESTSNAME, 0, 0, callback_list_str_xpush, NULL); + if (count == 0) + return_list_str(pkg->suggests, pkg->h, RPMTAG_REQUIRENAME, RPMTAG_REQUIREFLAGS, 0, + callback_list_str_xpush_old_suggests, NULL); + SPAGAIN; + +void +Pkg_obsoletes(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->obsoletes, pkg->h, RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEFLAGS, RPMTAG_OBSOLETEVERSION, + callback_list_str_xpush, NULL); + SPAGAIN; + +void +Pkg_obsoletes_nosense(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->obsoletes, pkg->h, RPMTAG_OBSOLETENAME, 0, 0, callback_list_str_xpush, NULL); + SPAGAIN; + +int +Pkg_obsoletes_overlap(pkg, s, b_nopromote=1, direction=-1) + URPM::Package pkg + char *s + int b_nopromote + int direction + PREINIT: + struct cb_overlap_s os; + char *eon = NULL; + char eonc = '\0'; + CODE: + os.name = s; + os.flags = 0; + while (*s && *s != ' ' && *s != '[' && *s != '<' && *s != '>' && *s != '=') ++s; + if (*s) { + eon = s; + while (*s) { + if (*s == ' ' || *s == '[' || *s == '*' || *s == ']'); + else if (*s == '<') os.flags |= RPMSENSE_LESS; + else if (*s == '>') os.flags |= RPMSENSE_GREATER; + else if (*s == '=') os.flags |= RPMSENSE_EQUAL; + else break; + ++s; + } + os.evr = s; + } else + os.evr = ""; + os.direction = direction; + os.b_nopromote = b_nopromote; + /* mark end of name */ + if (eon) { eonc = *eon; *eon = 0; } + /* return_list_str returns a negative value is the callback has returned non-zero */ + RETVAL = return_list_str(pkg->obsoletes, pkg->h, RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEFLAGS, RPMTAG_OBSOLETEVERSION, + callback_list_str_overlap, &os) < 0; + /* restore end of name */ + if (eon) *eon = eonc; + OUTPUT: + RETVAL + +void +Pkg_conflicts(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->conflicts, pkg->h, RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTFLAGS, RPMTAG_CONFLICTVERSION, + callback_list_str_xpush, NULL); + SPAGAIN; + +void +Pkg_conflicts_nosense(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->conflicts, pkg->h, RPMTAG_CONFLICTNAME, 0, 0, callback_list_str_xpush, NULL); + SPAGAIN; + +void +Pkg_provides(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->provides, pkg->h, RPMTAG_PROVIDENAME, RPMTAG_PROVIDEFLAGS, RPMTAG_PROVIDEVERSION, + callback_list_str_xpush, NULL); + SPAGAIN; + +void +Pkg_provides_nosense(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_str(pkg->provides, pkg->h, RPMTAG_PROVIDENAME, 0, 0, callback_list_str_xpush, NULL); + SPAGAIN; + +int +Pkg_provides_overlap(pkg, s, b_nopromote=1, direction=1) + URPM::Package pkg + char *s + int b_nopromote + int direction + PREINIT: + struct cb_overlap_s os; + char *eon = NULL; + char eonc = '\0'; + CODE: + os.name = s; + os.flags = 0; + while (*s && *s != ' ' && *s != '[' && *s != '<' && *s != '>' && *s != '=') ++s; + if (*s) { + eon = s; + while (*s) { + if (*s == ' ' || *s == '[' || *s == '*' || *s == ']'); + else if (*s == '<') os.flags |= RPMSENSE_LESS; + else if (*s == '>') os.flags |= RPMSENSE_GREATER; + else if (*s == '=') os.flags |= RPMSENSE_EQUAL; + else break; + ++s; + } + os.evr = s; + } else + os.evr = ""; + os.direction = direction; + os.b_nopromote = b_nopromote; + /* mark end of name */ + if (eon) { eonc = *eon; *eon = 0; } + /* return_list_str returns a negative value is the callback has returned non-zero */ + RETVAL = return_list_str(pkg->provides, pkg->h, RPMTAG_PROVIDENAME, RPMTAG_PROVIDEFLAGS, RPMTAG_PROVIDEVERSION, + callback_list_str_overlap, &os) < 0; + /* restore end of name */ + if (eon) *eon = eonc; + OUTPUT: + RETVAL + +void +Pkg_buildarchs(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_BUILDARCHS); + SPAGAIN; + +void +Pkg_excludearchs(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_EXCLUDEARCH); + SPAGAIN; + +void +Pkg_exclusivearchs(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_EXCLUSIVEARCH); + SPAGAIN; + +void +Pkg_dirnames(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_DIRNAMES); + SPAGAIN; + +void Pkg_distepoch(pkg) + URPM::Package pkg + PPCODE: +#ifdef RPMTAG_DISTEPOCH + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_DISTEPOCH), 0))); + } +#else + croak("distepoch isn't available with this rpm version"); +#endif + +void Pkg_disttag(pkg) + URPM::Package pkg + PPCODE: + if (pkg->h) { + XPUSHs(sv_2mortal(newSVpv(get_name(pkg->h, RPMTAG_DISTTAG), 0))); + } + +void +Pkg_filelinktos(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_FILELINKTOS); + SPAGAIN; + +void +Pkg_files(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_files(pkg->h, 0); + SPAGAIN; + +void +Pkg_files_md5sum(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_FILEMD5S); + SPAGAIN; + +void +Pkg_files_owner(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_FILEUSERNAME); + SPAGAIN; + +void +Pkg_files_group(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_FILEGROUPNAME); + SPAGAIN; + +void +Pkg_files_mtime(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_int32_t(pkg->h, RPMTAG_FILEMTIMES); + SPAGAIN; + +void +Pkg_files_size(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_int32_t(pkg->h, RPMTAG_FILESIZES); + SPAGAIN; + +void +Pkg_files_uid(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_int32_t(pkg->h, RPMTAG_FILEUIDS); + SPAGAIN; + +void +Pkg_files_gid(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_int32_t(pkg->h, RPMTAG_FILEGIDS); + SPAGAIN; + +void +Pkg_files_mode(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_uint_16(pkg->h, RPMTAG_FILEMODES); + SPAGAIN; + +void +Pkg_files_flags(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_int32_t(pkg->h, RPMTAG_FILEFLAGS); + SPAGAIN; + +void +Pkg_conf_files(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_files(pkg->h, FILTER_MODE_CONF_FILES); + SPAGAIN; + +void +Pkg_changelog_time(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + return_list_int32_t(pkg->h, RPMTAG_CHANGELOGTIME); + SPAGAIN; + +void +Pkg_changelog_name(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_CHANGELOGNAME); + SPAGAIN; + +void +Pkg_changelog_text(pkg) + URPM::Package pkg + PPCODE: + PUTBACK; + xpush_simple_list_str(pkg->h, RPMTAG_CHANGELOGTEXT); + SPAGAIN; + +void +Pkg_queryformat(pkg, fmt) + URPM::Package pkg + char *fmt + PREINIT: + char *s; + PPCODE: + if (pkg->h) { + s = headerFormat(pkg->h, fmt, NULL); + if (s) { + XPUSHs(sv_2mortal(newSVpv_utf8(s,0))); + } + } + +void +Pkg_get_tag(pkg, tagname) + URPM::Package pkg + int tagname; + PPCODE: + PUTBACK; + return_list_tag(pkg, tagname); + SPAGAIN; + +void +Pkg_get_tag_modifiers(pkg, tagname) + URPM::Package pkg + int tagname; + PPCODE: + PUTBACK; + return_list_tag_modifier(pkg->h, tagname); + SPAGAIN; + +void +Pkg_pack_header(pkg) + URPM::Package pkg + CODE: + pack_header(pkg); + +int +Pkg_update_header(pkg, filename, ...) + URPM::Package pkg + char *filename + PREINIT: + int packing = 0; + int keep_all_tags = 0; + CODE: + /* compability mode with older interface of parse_hdlist */ + if (items == 3) { + packing = SvIV(ST(2)); + } else if (items > 3) { + int i; + for (i = 2; i < items-1; i+=2) { + STRLEN len; + char *s = SvPV(ST(i), len); + + if (len == 7 && !memcmp(s, "packing", 7)) { + packing = SvTRUE(ST(i + 1)); + } else if (len == 13 && !memcmp(s, "keep_all_tags", 13)) { + keep_all_tags = SvTRUE(ST(i+1)); + } + } + } + RETVAL = update_header(filename, pkg, !packing && keep_all_tags, RPMVSF_DEFAULT); + if (RETVAL && packing) pack_header(pkg); + OUTPUT: + RETVAL + +void +Pkg_free_header(pkg) + URPM::Package pkg + CODE: + if (pkg->h && !(pkg->flag & FLAG_NO_HEADER_FREE)) pkg->h = headerFree(pkg->h); + pkg->h = NULL; + +void +Pkg_build_info(pkg, fileno, provides_files=NULL) + URPM::Package pkg + int fileno + char *provides_files + CODE: + if (pkg->info) { + char buff[65536]; + size_t size; + + /* info line should be the last to be written */ + if (pkg->provides && *pkg->provides) { + size = snprintf(buff, sizeof(buff), "@provides@%s\n", pkg->provides); + if (size < sizeof(buff)) { + if (provides_files && *provides_files) { + --size; + size += snprintf(buff+size, sizeof(buff)-size, "@%s\n", provides_files); + } + write_nocheck(fileno, buff, size); + } + } + if (pkg->conflicts && *pkg->conflicts) { + size = snprintf(buff, sizeof(buff), "@conflicts@%s\n", pkg->conflicts); + if (size < sizeof(buff)) write_nocheck(fileno, buff, size); + } + if (pkg->obsoletes && *pkg->obsoletes) { + size = snprintf(buff, sizeof(buff), "@obsoletes@%s\n", pkg->obsoletes); + if (size < sizeof(buff)) write_nocheck(fileno, buff, size); + } + if (pkg->requires && *pkg->requires) { + size = snprintf(buff, sizeof(buff), "@requires@%s\n", pkg->requires); + if (size < sizeof(buff)) write_nocheck(fileno, buff, size); + } + if (pkg->suggests && *pkg->suggests) { + size = snprintf(buff, sizeof(buff), "@suggests@%s\n", pkg->suggests); + if (size < sizeof(buff)) write_nocheck(fileno, buff, size); + } + if (pkg->summary && *pkg->summary) { + size = snprintf(buff, sizeof(buff), "@summary@%s\n", pkg->summary); + if (size < sizeof(buff)) write_nocheck(fileno, buff, size); + } + if (pkg->filesize) { + size = snprintf(buff, sizeof(buff), "@filesize@%d\n", pkg->filesize); + if (size < sizeof(buff)) write_nocheck(fileno, buff, size); + } + size = snprintf(buff, sizeof(buff), "@info@%s\n", pkg->info); + write_nocheck(fileno, buff, size); + } else croak("no info available for package %s", + pkg->h ? get_name(pkg->h, RPMTAG_NAME) : "-"); + +void +Pkg_build_header(pkg, fileno) + URPM::Package pkg + int fileno + CODE: + if (pkg->h) { + FD_t fd; + + if ((fd = fdDup(fileno)) != NULL) { + headerWrite(fd, pkg->h, HEADER_MAGIC_YES); + Fclose(fd); + } else croak("unable to get rpmio handle on fileno %d", fileno); + } else croak("no header available for package"); + +int +Pkg_flag(pkg, name) + URPM::Package pkg + char *name + PREINIT: + unsigned mask; + CODE: + if (!strcmp(name, "skip")) mask = FLAG_SKIP; + else if (!strcmp(name, "disable_obsolete")) mask = FLAG_DISABLE_OBSOLETE; + else if (!strcmp(name, "installed")) mask = FLAG_INSTALLED; + else if (!strcmp(name, "requested")) mask = FLAG_REQUESTED; + else if (!strcmp(name, "required")) mask = FLAG_REQUIRED; + else if (!strcmp(name, "upgrade")) mask = FLAG_UPGRADE; + else croak("unknown flag: %s", name); + RETVAL = pkg->flag & mask; + OUTPUT: + RETVAL + +int +Pkg_set_flag(pkg, name, value=1) + URPM::Package pkg + char *name + int value + PREINIT: + unsigned mask; + CODE: + if (!strcmp(name, "skip")) mask = FLAG_SKIP; + else if (!strcmp(name, "disable_obsolete")) mask = FLAG_DISABLE_OBSOLETE; + else if (!strcmp(name, "installed")) mask = FLAG_INSTALLED; + else if (!strcmp(name, "requested")) mask = FLAG_REQUESTED; + else if (!strcmp(name, "required")) mask = FLAG_REQUIRED; + else if (!strcmp(name, "upgrade")) mask = FLAG_UPGRADE; + else croak("unknown flag: %s", name); + RETVAL = pkg->flag & mask; + if (value) pkg->flag |= mask; + else pkg->flag &= ~mask; + OUTPUT: + RETVAL + +int +Pkg_flag_skip(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_SKIP; + OUTPUT: + RETVAL + +int +Pkg_set_flag_skip(pkg, value=1) + URPM::Package pkg + int value + CODE: + RETVAL = pkg->flag & FLAG_SKIP; + if (value) pkg->flag |= FLAG_SKIP; + else pkg->flag &= ~FLAG_SKIP; + OUTPUT: + RETVAL + +int +Pkg_flag_base(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_BASE; + OUTPUT: + RETVAL + +int +Pkg_set_flag_base(pkg, value=1) + URPM::Package pkg + int value + CODE: + RETVAL = pkg->flag & FLAG_BASE; + if (value) pkg->flag |= FLAG_BASE; + else pkg->flag &= ~FLAG_BASE; + OUTPUT: + RETVAL + +int +Pkg_flag_disable_obsolete(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_DISABLE_OBSOLETE; + OUTPUT: + RETVAL + +int +Pkg_set_flag_disable_obsolete(pkg, value=1) + URPM::Package pkg + int value + CODE: + RETVAL = pkg->flag & FLAG_DISABLE_OBSOLETE; + if (value) pkg->flag |= FLAG_DISABLE_OBSOLETE; + else pkg->flag &= ~FLAG_DISABLE_OBSOLETE; + OUTPUT: + RETVAL + +int +Pkg_flag_installed(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_INSTALLED; + OUTPUT: + RETVAL + +int +Pkg_set_flag_installed(pkg, value=1) + URPM::Package pkg + int value + CODE: + RETVAL = pkg->flag & FLAG_INSTALLED; + if (value) pkg->flag |= FLAG_INSTALLED; + else pkg->flag &= ~FLAG_INSTALLED; + OUTPUT: + RETVAL + +int +Pkg_flag_requested(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_REQUESTED; + OUTPUT: + RETVAL + +int +Pkg_set_flag_requested(pkg, value=1) + URPM::Package pkg + int value + CODE: + RETVAL = pkg->flag & FLAG_REQUESTED; + if (value) pkg->flag |= FLAG_REQUESTED; + else pkg->flag &= ~FLAG_REQUESTED; + OUTPUT: + RETVAL + +int +Pkg_flag_required(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_REQUIRED; + OUTPUT: + RETVAL + +int +Pkg_set_flag_required(pkg, value=1) + URPM::Package pkg + int value + CODE: + RETVAL = pkg->flag & FLAG_REQUIRED; + if (value) pkg->flag |= FLAG_REQUIRED; + else pkg->flag &= ~FLAG_REQUIRED; + OUTPUT: + RETVAL + +int +Pkg_flag_upgrade(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_UPGRADE; + OUTPUT: + RETVAL + +int +Pkg_set_flag_upgrade(pkg, value=1) + URPM::Package pkg + int value + CODE: + RETVAL = pkg->flag & FLAG_UPGRADE; + if (value) pkg->flag |= FLAG_UPGRADE; + else pkg->flag &= ~FLAG_UPGRADE; + OUTPUT: + RETVAL + +int +Pkg_flag_selected(pkg) + URPM::Package pkg + CODE: + RETVAL = pkg->flag & FLAG_UPGRADE ? pkg->flag & (FLAG_BASE | FLAG_REQUIRED) : 0; + OUTPUT: + RETVAL + +int +Pkg_flag_available(pkg) + URPM::Package pkg + CODE: + RETVAL = (pkg->flag & FLAG_INSTALLED && !(pkg->flag & FLAG_UPGRADE)) || + (pkg->flag & FLAG_UPGRADE ? pkg->flag & (FLAG_BASE | FLAG_REQUIRED) : 0); + OUTPUT: + RETVAL + +int +Pkg_rate(pkg) + URPM::Package pkg + CODE: + RETVAL = (pkg->flag & FLAG_RATE) >> FLAG_RATE_POS; + OUTPUT: + RETVAL + +int +Pkg_set_rate(pkg, rate) + URPM::Package pkg + int rate + CODE: + RETVAL = (pkg->flag & FLAG_RATE) >> FLAG_RATE_POS; + pkg->flag &= ~FLAG_RATE; + pkg->flag |= (rate >= 0 && rate <= FLAG_RATE_MAX ? rate : FLAG_RATE_INVALID) << FLAG_RATE_POS; + OUTPUT: + RETVAL + +void +Pkg_rflags(pkg) + URPM::Package pkg + PREINIT: + I32 gimme = GIMME_V; + PPCODE: + if (gimme == G_ARRAY && pkg->rflags != NULL) { + char *s = pkg->rflags; + char *eos; + while ((eos = strchr(s, '\t')) != NULL) { + XPUSHs(sv_2mortal(newSVpv(s, eos-s))); + s = eos + 1; + } + XPUSHs(sv_2mortal(newSVpv(s, 0))); + } + +void +Pkg_set_rflags(pkg, ...) + URPM::Package pkg + PREINIT: + I32 gimme = GIMME_V; + char *new_rflags; + STRLEN total_len; + int i; + PPCODE: + total_len = 0; + for (i = 1; i < items; ++i) + total_len += SvCUR(ST(i)) + 1; + + new_rflags = malloc(total_len); + total_len = 0; + for (i = 1; i < items; ++i) { + STRLEN len; + char *s = SvPV(ST(i), len); + memcpy(new_rflags + total_len, s, len); + new_rflags[total_len + len] = '\t'; + total_len += len + 1; + } + new_rflags[total_len - 1] = 0; /* but mark end-of-string correctly */ + + if (gimme == G_ARRAY && pkg->rflags != NULL) { + char *s = pkg->rflags; + char *eos; + while ((eos = strchr(s, '\t')) != NULL) { + XPUSHs(sv_2mortal(newSVpv(s, eos-s))); + s = eos + 1; + } + XPUSHs(sv_2mortal(newSVpv(s, 0))); + } + + free(pkg->rflags); + pkg->rflags = new_rflags; + + +MODULE = URPM PACKAGE = URPM::DB PREFIX = Db_ + +URPM::DB +Db_open(prefix=NULL, write_perm=0) + char *prefix + int write_perm + PREINIT: + URPM__DB db; + CODE: + read_config_files(0); + db = malloc(sizeof(struct s_Transaction)); + db->count = 1; + db->ts = rpmtsCreate(); + rpmtsSetRootDir(db->ts, prefix && prefix[0] ? prefix : NULL); + if (rpmtsOpenDB(db->ts, write_perm ? O_RDWR | O_CREAT : O_RDONLY) == 0) { + RETVAL = db; + } else { + RETVAL = NULL; + (void)rpmtsFree(db->ts); + free(db); + } + OUTPUT: + RETVAL + +int +Db_rebuild(prefix="") + char *prefix + PREINIT: + rpmts ts; + CODE: + read_config_files(0); + ts = rpmtsCreate(); + rpmtsSetRootDir(ts, prefix); + RETVAL = rpmtsRebuildDB(ts) == 0; + (void)rpmtsFree(ts); + OUTPUT: + RETVAL + +int +Db_verify(prefix="") + char *prefix + PREINIT: + rpmts ts; + CODE: + ts = rpmtsCreate(); + rpmtsSetRootDir(ts, prefix); + RETVAL = rpmtsVerifyDB(ts) == 0; + ts = rpmtsFree(ts); + OUTPUT: + RETVAL + +void +Db_DESTROY(db) + URPM::DB db + CODE: + (void)rpmtsFree(db->ts); + if (!--db->count) free(db); + +int +Db_traverse(db,callback) + URPM::DB db + SV *callback + PREINIT: + Header header; + rpmdbMatchIterator mi; + int count = 0; + CODE: +#ifdef RPM490 + db->ts = rpmtsLink(db->ts); +#else + db->ts = rpmtsLink(db->ts, "URPM::DB::traverse"); +#endif + ts_nosignature(db->ts); + mi = rpmtsInitIterator(db->ts, RPMDBI_PACKAGES, NULL, 0); + while ((header = rpmdbNextIterator(mi))) { + if (SvROK(callback)) { + dSP; + URPM__Package pkg = calloc(1, sizeof(struct s_Package)); + + pkg->flag = FLAG_ID_INVALID | FLAG_NO_HEADER_FREE; + pkg->h = header; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(sv_setref_pv(newSVpv("", 0), "URPM::Package", pkg))); + PUTBACK; + + call_sv(callback, G_DISCARD | G_SCALAR); + + SPAGAIN; + pkg->h = 0; /* avoid using it anymore, in case it has been copied inside callback */ + } + ++count; + } + rpmdbFreeIterator(mi); + (void)rpmtsFree(db->ts); + RETVAL = count; + OUTPUT: + RETVAL + +int +Db_traverse_tag(db,tag,names,callback) + URPM::DB db + char *tag + SV *names + SV *callback + PREINIT: + Header header; + rpmdbMatchIterator mi; + int count = 0; + CODE: + if (SvROK(names) && SvTYPE(SvRV(names)) == SVt_PVAV) { + AV* names_av = (AV*)SvRV(names); + int len = av_len(names_av); + int i, rpmtag; + + rpmtag = rpmtag_from_string(tag); + + for (i = 0; i <= len; ++i) { + STRLEN str_len; + SV **isv = av_fetch(names_av, i, 0); + char *name = SvPV(*isv, str_len); +#ifdef RPM490 + db->ts = rpmtsLink(db->ts); +#else + db->ts = rpmtsLink(db->ts, "URPM::DB::traverse_tag"); +#endif + ts_nosignature(db->ts); + mi = rpmtsInitIterator(db->ts, rpmtag, name, str_len); + while ((header = rpmdbNextIterator(mi))) { + if (SvROK(callback)) { + dSP; + URPM__Package pkg = calloc(1, sizeof(struct s_Package)); + + pkg->flag = FLAG_ID_INVALID | FLAG_NO_HEADER_FREE; + pkg->h = header; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(sv_setref_pv(newSVpv("", 0), "URPM::Package", pkg))); + PUTBACK; + + call_sv(callback, G_DISCARD | G_SCALAR); + + SPAGAIN; + pkg->h = 0; /* avoid using it anymore, in case it has been copied inside callback */ + } + ++count; + } + (void)rpmdbFreeIterator(mi); + (void)rpmtsFree(db->ts); + } + } else croak("bad arguments list"); + RETVAL = count; + OUTPUT: + RETVAL + +int +Db_traverse_tag_find(db,tag,name,callback) + URPM::DB db + char *tag + char *name + SV *callback + PREINIT: + Header header; + rpmdbMatchIterator mi; + CODE: + int rpmtag = rpmtag_from_string(tag); + int found = 0; +#ifdef RPM490 + db->ts = rpmtsLink(db->ts); +#else + db->ts = rpmtsLink(db->ts, "URPM::DB::traverse_tag"); +#endif + ts_nosignature(db->ts); + mi = rpmtsInitIterator(db->ts, rpmtag, name, 0); + while ((header = rpmdbNextIterator(mi))) { + dSP; + URPM__Package pkg = calloc(1, sizeof(struct s_Package)); + + pkg->flag = FLAG_ID_INVALID | FLAG_NO_HEADER_FREE; + pkg->h = header; + + PUSHMARK(SP); + XPUSHs(sv_2mortal(sv_setref_pv(newSVpv("", 0), "URPM::Package", pkg))); + PUTBACK; + + int count = call_sv(callback, G_SCALAR); + + SPAGAIN; + pkg->h = 0; /* avoid using it anymore, in case it has been copied inside callback */ + + if (count == 1 && POPi) { + found = 1; + break; + } + } + (void)rpmdbFreeIterator(mi); + (void)rpmtsFree(db->ts); + RETVAL = found; + OUTPUT: + RETVAL + +URPM::Transaction +Db_create_transaction(db, prefix="/") + URPM::DB db + char *prefix + CODE: + /* this is *REALLY* dangerous to create a new transaction while another is open, + so use the db transaction instead. */ +#ifdef RPM490 + db->ts = rpmtsLink(db->ts); +#else + db->ts = rpmtsLink(db->ts, "URPM::DB::create_transaction"); +#endif + ++db->count; + RETVAL = db; + OUTPUT: + RETVAL + + +MODULE = URPM PACKAGE = URPM::Transaction PREFIX = Trans_ + +void +Trans_DESTROY(trans) + URPM::Transaction trans + CODE: + (void)rpmtsFree(trans->ts); + if (!--trans->count) free(trans); + +void +Trans_set_script_fd(trans, fdno) + URPM::Transaction trans + int fdno + CODE: + rpmtsSetScriptFd(trans->ts, fdDup(fdno)); + +int +Trans_add(trans, pkg, ...) + URPM::Transaction trans + URPM::Package pkg + CODE: + if ((pkg->flag & FLAG_ID) <= FLAG_ID_MAX && pkg->h != NULL) { + int update = 0; + rpmRelocation *relocations = NULL; + /* compability mode with older interface of add */ + if (items == 3) { + update = SvIV(ST(2)); + } else if (items > 3) { + int i; + for (i = 2; i < items-1; i+=2) { + STRLEN len; + char *s = SvPV(ST(i), len); + + if (len == 6 && !memcmp(s, "update", 6)) { + update = SvIV(ST(i+1)); + } else if (len == 11 && !memcmp(s, "excludepath", 11)) { + if (SvROK(ST(i+1)) && SvTYPE(SvRV(ST(i+1))) == SVt_PVAV) { + AV *excludepath = (AV*)SvRV(ST(i+1)); + I32 j = 1 + av_len(excludepath); + relocations = calloc(j + 1, sizeof(rpmRelocation)); + while (--j >= 0) { + SV **e = av_fetch(excludepath, j, 0); + if (e != NULL && *e != NULL) { + relocations[j].oldPath = SvPV_nolen(*e); + } + } + } + } + } + } + RETVAL = rpmtsAddInstallElement(trans->ts, pkg->h, (fnpyKey)(1+(long)(pkg->flag & FLAG_ID)), update, relocations) == 0; + /* free allocated memory, check rpm is copying it just above, at least in 4.0.4 */ + free(relocations); + } else RETVAL = 0; + OUTPUT: + RETVAL + +int +Trans_remove(trans, name) + URPM::Transaction trans + char *name + PREINIT: + Header h; + rpmdbMatchIterator mi; + int count = 0; + char *boa = NULL, *bor = NULL; + CODE: + /* hide arch in name if present */ + if ((boa = strrchr(name, '.'))) { + *boa = 0; + if ((bor = strrchr(name, '-'))) { + *bor = 0; + if (!strrchr(name, '-')) { + *boa = '.'; boa = NULL; + } + *bor = '-'; bor = NULL; + } else { + *boa = '.'; boa = NULL; + } + } + mi = rpmtsInitIterator(trans->ts, RPMDBI_LABEL, name, 0); + while ((h = rpmdbNextIterator(mi))) { + unsigned int recOffset = rpmdbGetIteratorOffset(mi); + if (recOffset != 0) { + rpmtsAddEraseElement(trans->ts, h, recOffset); + ++count; + } + } + rpmdbFreeIterator(mi); + if (boa) *boa = '.'; + RETVAL=count; + OUTPUT: + RETVAL + +int +Trans_traverse(trans, callback) + URPM::Transaction trans + SV *callback + PREINIT: + rpmdbMatchIterator mi; + Header h; + int c = 0; + CODE: + mi = rpmtsInitIterator(trans->ts, RPMDBI_PACKAGES, NULL, 0); + while ((h = rpmdbNextIterator(mi))) { + if (SvROK(callback)) { + dSP; + URPM__Package pkg = calloc(1, sizeof(struct s_Package)); + pkg->flag = FLAG_ID_INVALID | FLAG_NO_HEADER_FREE; + pkg->h = h; + PUSHMARK(SP); + XPUSHs(sv_2mortal(sv_setref_pv(newSVpv("", 0), "URPM::Package", pkg))); + PUTBACK; + call_sv(callback, G_DISCARD | G_SCALAR); + SPAGAIN; + pkg->h = 0; /* avoid using it anymore, in case it has been copied inside callback */ + } + ++c; + } + rpmdbFreeIterator(mi); + RETVAL = c; + OUTPUT: + RETVAL + +void +Trans_check(trans, ...) + URPM::Transaction trans + PREINIT: + I32 gimme = GIMME_V; + int translate_message = 0; + int i; + PPCODE: + for (i = 1; i < items-1; i+=2) { + STRLEN len; + char *s = SvPV(ST(i), len); + + if (len == 17 && !memcmp(s, "translate_message", 17)) { + translate_message = SvIV(ST(i+1)); + } + } + if (rpmtsCheck(trans->ts)) { + if (gimme == G_SCALAR) { + XPUSHs(sv_2mortal(newSViv(0))); + } else if (gimme == G_ARRAY) { + XPUSHs(sv_2mortal(newSVpv("error while checking dependencies", 0))); + } + } else { + rpmps ps = rpmtsProblems(trans->ts); + if (rpmpsNumProblems(ps) > 0) { + if (gimme == G_SCALAR) { + XPUSHs(sv_2mortal(newSViv(0))); + } else if (gimme == G_ARRAY) { + /* now translation is handled by rpmlib, but only for version 4.2 and above */ + PUTBACK; + return_problems(ps, 1, 0); + SPAGAIN; + } + } else if (gimme == G_SCALAR) { + XPUSHs(sv_2mortal(newSViv(1))); + } + ps = rpmpsFree(ps); + } + +void +Trans_order(trans) + URPM::Transaction trans + PREINIT: + I32 gimme = GIMME_V; + PPCODE: + if (rpmtsOrder(trans->ts) == 0) { + if (gimme == G_SCALAR) { + XPUSHs(sv_2mortal(newSViv(1))); + } + } else { + if (gimme == G_SCALAR) { + XPUSHs(sv_2mortal(newSViv(0))); + } else if (gimme == G_ARRAY) { + XPUSHs(sv_2mortal(newSVpv("error while ordering dependencies", 0))); + } + } + +int +Trans_NElements(trans) + URPM::Transaction trans + CODE: + RETVAL = rpmtsNElements(trans->ts); + OUTPUT: + RETVAL + +char * +Trans_Element_name(trans, index) + URPM::Transaction trans + int index + CODE: + rpmte te = rpmtsElement(trans->ts, index); + RETVAL = te ? (char *) rpmteN(te) : NULL; + OUTPUT: + RETVAL + +char * +Trans_Element_version(trans, index) + URPM::Transaction trans + int index + CODE: + rpmte te = rpmtsElement(trans->ts, index); + RETVAL = te ? (char *) rpmteV(te) : NULL; + OUTPUT: + RETVAL + +char * +Trans_Element_release(trans, index) + URPM::Transaction trans + int index + CODE: + rpmte te = rpmtsElement(trans->ts, index); + RETVAL = te ? (char *) rpmteR(te) : NULL; + OUTPUT: + RETVAL + +char * +Trans_Element_fullname(trans, index) + URPM::Transaction trans + int index + CODE: + rpmte te = rpmtsElement(trans->ts, index); + RETVAL = te ? (char *) rpmteNEVRA(te) : NULL; + OUTPUT: + RETVAL + +void +Trans_run(trans, data, ...) + URPM::Transaction trans + SV *data + PREINIT: + struct s_TransactionData td = { NULL, NULL, NULL, NULL, NULL, 100000, data }; + rpmtransFlags transFlags = RPMTRANS_FLAG_NONE; + int probFilter = 0; + int translate_message = 0, raw_message = 0; + int i; + PPCODE: + for (i = 2 ; i < items - 1 ; i += 2) { + STRLEN len; + char *s = SvPV(ST(i), len); + + if (len == 4 && !memcmp(s, "test", 4)) { + if (SvIV(ST(i+1))) transFlags |= RPMTRANS_FLAG_TEST; + } else if (len == 11 && !memcmp(s, "excludedocs", 11)) { + if (SvIV(ST(i+1))) transFlags |= RPMTRANS_FLAG_NODOCS; + } else if (len == 5) { + if (!memcmp(s, "force", 5)) { + if (SvIV(ST(i+1))) probFilter |= (RPMPROB_FILTER_REPLACEPKG | + RPMPROB_FILTER_REPLACEOLDFILES | + RPMPROB_FILTER_REPLACENEWFILES | + RPMPROB_FILTER_OLDPACKAGE); + } else if (!memcmp(s, "delta", 5)) + td.min_delta = SvIV(ST(i+1)); + } else if (len == 6 && !memcmp(s, "nosize", 6)) { + if (SvIV(ST(i+1))) probFilter |= (RPMPROB_FILTER_DISKSPACE|RPMPROB_FILTER_DISKNODES); + } else if (len == 9 && !memcmp(s, "noscripts", 9)) { + if (SvIV(ST(i+1))) transFlags |= (RPMTRANS_FLAG_NOSCRIPTS | + RPMTRANS_FLAG_NOPRE | + RPMTRANS_FLAG_NOPREUN | + RPMTRANS_FLAG_NOPOST | + RPMTRANS_FLAG_NOPOSTUN ); + } else if (len == 10 && !memcmp(s, "oldpackage", 10)) { + if (SvIV(ST(i+1))) probFilter |= RPMPROB_FILTER_OLDPACKAGE; + } else if (len == 11 && !memcmp(s, "replacepkgs", 11)) { + if (SvIV(ST(i+1))) probFilter |= RPMPROB_FILTER_REPLACEPKG; + } else if (len == 11 && !memcmp(s, "raw_message", 11)) { + raw_message = 1; + } else if (len == 12 && !memcmp(s, "replacefiles", 12)) { + if (SvIV(ST(i+1))) probFilter |= RPMPROB_FILTER_REPLACEOLDFILES | RPMPROB_FILTER_REPLACENEWFILES; + } else if (len == 9 && !memcmp(s, "repackage", 9)) { + if (SvIV(ST(i+1))) transFlags |= RPMTRANS_FLAG_REPACKAGE; + } else if (len == 6 && !memcmp(s, "justdb", 6)) { + if (SvIV(ST(i+1))) transFlags |= RPMTRANS_FLAG_JUSTDB; + } else if (len == 10 && !memcmp(s, "ignorearch", 10)) { + if (SvIV(ST(i+1))) probFilter |= RPMPROB_FILTER_IGNOREARCH; + } else if (len == 17 && !memcmp(s, "translate_message", 17)) + translate_message = 1; + else if (len >= 9 && !memcmp(s, "callback_", 9)) { + if (len == 9+4 && !memcmp(s+9, "open", 4)) { + if (SvROK(ST(i+1))) td.callback_open = ST(i+1); + } else if (len == 9+5 && !memcmp(s+9, "close", 5)) { + if (SvROK(ST(i+1))) td.callback_close = ST(i+1); + } else if (len == 9+5 && !memcmp(s+9, "trans", 5)) { + if (SvROK(ST(i+1))) td.callback_trans = ST(i+1); + } else if (len == 9+6 && !memcmp(s+9, "uninst", 6)) { + if (SvROK(ST(i+1))) td.callback_uninst = ST(i+1); + } else if (len == 9+4 && !memcmp(s+9, "inst", 4)) { + if (SvROK(ST(i+1))) td.callback_inst = ST(i+1); + } + } + } + /* check macros */ + { + char *repa = rpmExpand("%_repackage_all_erasures", NULL); + if (repa && *repa && *repa != '0') + transFlags |= RPMTRANS_FLAG_REPACKAGE; + if (repa) free(repa); + } + rpmtsSetFlags(trans->ts, transFlags); +#ifdef RPM490 + trans->ts = rpmtsLink(trans->ts); +#else + trans->ts = rpmtsLink(trans->ts, "URPM::Transaction::run"); +#endif + rpmtsSetNotifyCallback(trans->ts, rpmRunTransactions_callback, &td); + if (rpmtsRun(trans->ts, NULL, probFilter) > 0) { + rpmps ps = rpmtsProblems(trans->ts); + PUTBACK; + return_problems(ps, translate_message, raw_message || !translate_message); + SPAGAIN; + ps = rpmpsFree(ps); + } + rpmtsEmpty(trans->ts); + (void)rpmtsFree(trans->ts); + +MODULE = URPM PACKAGE = URPM PREFIX = Urpm_ + +BOOT: +(void) read_config_files(0); + +void +Urpm_bind_rpm_textdomain_codeset() + CODE: + rpm_codeset_is_utf8 = 1; + bind_textdomain_codeset("rpm", "UTF-8"); + +int +Urpm_read_config_files() + CODE: + RETVAL = (read_config_files(1) == 0); /* force re-read of configuration files */ + OUTPUT: + RETVAL + +void +Urpm_list_rpm_tag(urpm=Nullsv) + SV *urpm + CODE: + croak("list_rpm_tag() has been removed from perl-URPM. please report if you need it back"); + +int +rpmvercmp(one, two) + char *one + char *two + +int +Urpm_ranges_overlap(a, b, b_nopromote=1) + char *a + char *b + int b_nopromote + PREINIT: + char *sa = a, *sb = b; + int aflags = 0, bflags = 0; + CODE: + while (*sa && *sa != ' ' && *sa != '[' && *sa != '<' && *sa != '>' && *sa != '=' && *sa == *sb) { + ++sa; + ++sb; + } + if ((*sa && *sa != ' ' && *sa != '[' && *sa != '<' && *sa != '>' && *sa != '=') || + (*sb && *sb != ' ' && *sb != '[' && *sb != '<' && *sb != '>' && *sb != '=')) { + /* the strings are sure to be different */ + RETVAL = 0; + } else { + while (*sa) { + if (*sa == ' ' || *sa == '[' || *sa == '*' || *sa == ']'); + else if (*sa == '<') aflags |= RPMSENSE_LESS; + else if (*sa == '>') aflags |= RPMSENSE_GREATER; + else if (*sa == '=') aflags |= RPMSENSE_EQUAL; + else break; + ++sa; + } + while (*sb) { + if (*sb == ' ' || *sb == '[' || *sb == '*' || *sb == ']'); + else if (*sb == '<') bflags |= RPMSENSE_LESS; + else if (*sb == '>') bflags |= RPMSENSE_GREATER; + else if (*sb == '=') bflags |= RPMSENSE_EQUAL; + else break; + ++sb; + } + RETVAL = ranges_overlap(aflags, sa, bflags, sb, b_nopromote); + } + OUTPUT: + RETVAL + +void +Urpm_parse_synthesis__XS(urpm, filename, ...) + SV *urpm + char *filename + PPCODE: + if (SvROK(urpm) && SvTYPE(SvRV(urpm)) == SVt_PVHV) { + SV **fdepslist = hv_fetch((HV*)SvRV(urpm), "depslist", 8, 0); + AV *depslist = fdepslist && SvROK(*fdepslist) && SvTYPE(SvRV(*fdepslist)) == SVt_PVAV ? (AV*)SvRV(*fdepslist) : NULL; + SV **fprovides = hv_fetch((HV*)SvRV(urpm), "provides", 8, 0); + HV *provides = fprovides && SvROK(*fprovides) && SvTYPE(SvRV(*fprovides)) == SVt_PVHV ? (HV*)SvRV(*fprovides) : NULL; + SV **fobsoletes = hv_fetch((HV*)SvRV(urpm), "obsoletes", 9, 0); + HV *obsoletes = fobsoletes && SvROK(*fobsoletes) && SvTYPE(SvRV(*fobsoletes)) == SVt_PVHV ? (HV*)SvRV(*fobsoletes) : NULL; + + if (depslist != NULL) { + char buff[65536]; + char *p, *eol; + int buff_len; + struct s_Package pkg; + gzFile f; + int start_id = 1 + av_len(depslist); + SV *callback = NULL; + + if (items > 2) { + int i; + for (i = 2; i < items-1; i+=2) { + STRLEN len; + char *s = SvPV(ST(i), len); + + if (len == 8 && !memcmp(s, "callback", 8)) { + if (SvROK(ST(i+1))) callback = ST(i+1); + } + } + } + + PUTBACK; + if ((f = gzopen(filename, "rb")) != NULL) { + memset(&pkg, 0, sizeof(struct s_Package)); + buff[sizeof(buff)-1] = 0; + p = buff; + int ok = 1; + while ((buff_len = gzread(f, p, sizeof(buff)-1-(p-buff))) >= 0 && + (buff_len += p-buff)) { + buff[buff_len] = 0; + p = buff; + if ((eol = strchr(p, '\n')) != NULL) { + do { + *eol++ = 0; + if (!parse_line(depslist, provides, obsoletes, &pkg, p, urpm, callback)) { ok = 0; break; } + p = eol; + } while ((eol = strchr(p, '\n')) != NULL); + } else { + /* a line larger than sizeof(buff) has been encountered, bad file problably */ + fprintf(stderr, "invalid line <%s>\n", p); + ok = 0; + break; + } + if (gzeof(f)) { + if (!parse_line(depslist, provides, obsoletes, &pkg, p, urpm, callback)) ok = 0; + break; + } else { + /* move the remaining non-complete-line at beginning */ + memmove(buff, p, buff_len-(p-buff)); + /* point to the end of the non-complete-line */ + p = &buff[buff_len-(p-buff)]; + } + } + if (gzclose(f) != 0) ok = 0; + SPAGAIN; + if (ok) { + XPUSHs(sv_2mortal(newSViv(start_id))); + XPUSHs(sv_2mortal(newSViv(av_len(depslist)))); + } + } else { + SV **nofatal = hv_fetch((HV*)SvRV(urpm), "nofatal", 7, 0); + if (!errno) errno = EINVAL; /* zlib error */ + if (!nofatal || !SvIV(*nofatal)) + croak(errno == ENOENT + ? "unable to read synthesis file %s" + : "unable to uncompress synthesis file %s", filename); + } + } else croak("first argument should contain a depslist ARRAY reference"); + } else croak("first argument should be a reference to a HASH"); + +void +Urpm_parse_hdlist__XS(urpm, filename, ...) + SV *urpm + char *filename + PPCODE: + if (SvROK(urpm) && SvTYPE(SvRV(urpm)) == SVt_PVHV) { + SV **fdepslist = hv_fetch((HV*)SvRV(urpm), "depslist", 8, 0); + AV *depslist = fdepslist && SvROK(*fdepslist) && SvTYPE(SvRV(*fdepslist)) == SVt_PVAV ? (AV*)SvRV(*fdepslist) : NULL; + SV **fprovides = hv_fetch((HV*)SvRV(urpm), "provides", 8, 0); + HV *provides = fprovides && SvROK(*fprovides) && SvTYPE(SvRV(*fprovides)) == SVt_PVHV ? (HV*)SvRV(*fprovides) : NULL; + SV **fobsoletes = hv_fetch((HV*)SvRV(urpm), "obsoletes", 9, 0); + HV *obsoletes = fobsoletes && SvROK(*fobsoletes) && SvTYPE(SvRV(*fobsoletes)) == SVt_PVHV ? (HV*)SvRV(*fobsoletes) : NULL; + + if (depslist != NULL) { + pid_t pid = 0; + int d; + int empty_archive = 0; + FD_t fd; + + d = open_archive(filename, &pid, &empty_archive); + fd = fdDup(d); + close(d); + + if (empty_archive) { + XPUSHs(sv_2mortal(newSViv(1 + av_len(depslist)))); + XPUSHs(sv_2mortal(newSViv(av_len(depslist)))); + } else if (d >= 0 && fd) { + Header header; + int start_id = 1 + av_len(depslist); + int packing = 0; + SV *callback = NULL; + + /* compability mode with older interface of parse_hdlist */ + if (items == 3) { + packing = SvTRUE(ST(2)); + } else if (items > 3) { + int i; + for (i = 2; i < items-1; i+=2) { + STRLEN len; + char *s = SvPV(ST(i), len); + + if (len == 7 && !memcmp(s, "packing", 7)) { + packing = SvTRUE(ST(i+1)); + } else if (len == 8 && !memcmp(s, "callback", 8)) { + if (SvROK(ST(i+1))) callback = ST(i+1); + } + } + } + + PUTBACK; + do { + header=headerRead(fd, HEADER_MAGIC_YES); + if (header != NULL) { + struct s_Package pkg, *_pkg; + SV *sv_pkg; + + memset(&pkg, 0, sizeof(struct s_Package)); + pkg.flag = 1 + av_len(depslist); + pkg.h = header; + sv_pkg = sv_setref_pv(newSVpv("", 0), "URPM::Package", + _pkg = memcpy(malloc(sizeof(struct s_Package)), &pkg, sizeof(struct s_Package))); + if (call_package_callback(urpm, sv_pkg, callback)) { + if (provides) { + update_provides(_pkg, provides); + update_provides_files(_pkg, provides); + } + if (obsoletes) update_obsoletes(_pkg, obsoletes); + if (packing) pack_header(_pkg); + av_push(depslist, sv_pkg); + } + } + } while (header != NULL); + + int ok = Fclose(fd) == 0; + + if (pid) { + kill(pid, SIGTERM); + int status; + int rc = waitpid(pid, &status, 0); + ok = rc != -1 && WEXITSTATUS(status) != 1; /* in our standard case, gzip will exit with status code 2, meaning "decompression OK, trailing garbage ignored" */ + pid = 0; + } else if (!empty_archive) { + ok = av_len(depslist) >= start_id; + } + SPAGAIN; + if (ok) { + XPUSHs(sv_2mortal(newSViv(start_id))); + XPUSHs(sv_2mortal(newSViv(av_len(depslist)))); + } + } else { + SV **nofatal = hv_fetch((HV*)SvRV(urpm), "nofatal", 7, 0); + if (!nofatal || !SvIV(*nofatal)) + croak("cannot open hdlist file %s", filename); + } + } else croak("first argument should contain a depslist ARRAY reference"); + } else croak("first argument should be a reference to a HASH"); + +void +Urpm_parse_rpm(urpm, filename, ...) + SV *urpm + char *filename + PPCODE: + if (SvROK(urpm) && SvTYPE(SvRV(urpm)) == SVt_PVHV) { + SV **fdepslist = hv_fetch((HV*)SvRV(urpm), "depslist", 8, 0); + AV *depslist = fdepslist && SvROK(*fdepslist) && SvTYPE(SvRV(*fdepslist)) == SVt_PVAV ? (AV*)SvRV(*fdepslist) : NULL; + SV **fprovides = hv_fetch((HV*)SvRV(urpm), "provides", 8, 0); + HV *provides = fprovides && SvROK(*fprovides) && SvTYPE(SvRV(*fprovides)) == SVt_PVHV ? (HV*)SvRV(*fprovides) : NULL; + SV **fobsoletes = hv_fetch((HV*)SvRV(urpm), "obsoletes", 8, 0); + HV *obsoletes = fobsoletes && SvROK(*fobsoletes) && SvTYPE(SvRV(*fobsoletes)) == SVt_PVHV ? (HV*)SvRV(*fobsoletes) : NULL; + + if (depslist != NULL) { + struct s_Package pkg, *_pkg; + SV *sv_pkg; + int packing = 0; + int keep_all_tags = 0; + SV *callback = NULL; + rpmVSFlags vsflags = RPMVSF_DEFAULT; + + /* compability mode with older interface of parse_hdlist */ + if (items == 3) { + packing = SvTRUE(ST(2)); + } else if (items > 3) { + int i; + for (i = 2; i < items-1; i+=2) { + STRLEN len; + char *s = SvPV(ST(i), len); + + if (len == 7 && !memcmp(s, "packing", 7)) { + packing = SvTRUE(ST(i + 1)); + } else if (len == 13 && !memcmp(s, "keep_all_tags", 13)) { + keep_all_tags = SvTRUE(ST(i+1)); + } else if (len == 8 && !memcmp(s, "callback", 8)) { + if (SvROK(ST(i+1))) callback = ST(i+1); + } else if (len == 5) { + if (!memcmp(s, "nopgp", 5)) { + if (SvIV(ST(i+1))) vsflags |= (RPMVSF_NOSHA1 | RPMVSF_NOSHA1HEADER); + } + else if (!memcmp(s, "nogpg", 5)) { + if (SvIV(ST(i+1))) vsflags |= (RPMVSF_NOSHA1 | RPMVSF_NOSHA1HEADER); + } + else if (!memcmp(s, "nomd5", 5)) { + if (SvIV(ST(i+1))) vsflags |= (RPMVSF_NOMD5 | RPMVSF_NOMD5HEADER); + } + else if (!memcmp(s, "norsa", 5)) { + if (SvIV(ST(i+1))) vsflags |= (RPMVSF_NORSA | RPMVSF_NORSAHEADER); + } + else if (!memcmp(s, "nodsa", 5)) { + if (SvIV(ST(i+1))) vsflags |= (RPMVSF_NODSA | RPMVSF_NODSAHEADER); + } + } else if (len == 9) { + if (!memcmp(s, "nodigests", 9)) { + if (SvIV(ST(i+1))) vsflags |= _RPMVSF_NODIGESTS; + } else + if (!memcmp(s, "nopayload", 9)) { + if (SvIV(ST(i+1))) vsflags |= _RPMVSF_NOPAYLOAD; + } + } + } + } + PUTBACK; + memset(&pkg, 0, sizeof(struct s_Package)); + pkg.flag = 1 + av_len(depslist); + _pkg = memcpy(malloc(sizeof(struct s_Package)), &pkg, sizeof(struct s_Package)); + + if (update_header(filename, _pkg, keep_all_tags, vsflags)) { + sv_pkg = sv_setref_pv(newSVpv("", 0), "URPM::Package", _pkg); + if (call_package_callback(urpm, sv_pkg, callback)) { + if (provides) { + update_provides(_pkg, provides); + update_provides_files(_pkg, provides); + } + if (obsoletes) update_obsoletes(_pkg, obsoletes); + if (packing) pack_header(_pkg); + av_push(depslist, sv_pkg); + } + SPAGAIN; + /* only one element read */ + XPUSHs(sv_2mortal(newSViv(av_len(depslist)))); + XPUSHs(sv_2mortal(newSViv(av_len(depslist)))); + } else free(_pkg); + } else croak("first argument should contain a depslist ARRAY reference"); + } else croak("first argument should be a reference to a HASH"); + +int +Urpm_verify_rpm(filename, ...) + char *filename + PREINIT: + FD_t fd; + int i, oldlogmask; + rpmts ts = NULL; + struct rpmQVKArguments_s qva; + CODE: + /* Don't display error messages */ + oldlogmask = rpmlogSetMask(RPMLOG_UPTO(RPMLOG_PRI(4))); + memset(&qva, 0, sizeof(struct rpmQVKArguments_s)); + qva.qva_source = RPMQV_RPM; + qva.qva_flags = VERIFY_ALL; + for (i = 1 ; i < items - 1 ; i += 2) { + STRLEN len; + char *s = SvPV(ST(i), len); + if (len == 9 && !strncmp(s, "nodigests", 9)) { + if (SvIV(ST(i+1))) qva.qva_flags &= ~VERIFY_DIGEST; + } else if (len == 12 && !strncmp(s, "nosignatures", 12)) { + if (SvIV(ST(i+1))) qva.qva_flags &= ~VERIFY_SIGNATURE; + } + } + fd = Fopen(filename, "r"); + if (fd == NULL) { + RETVAL = 0; + } else { + read_config_files(0); + ts = rpmtsCreate(); + rpmtsSetRootDir(ts, "/"); + rpmtsOpenDB(ts, O_RDONLY); + if (rpmVerifySignatures(&qva, ts, fd, filename)) { + RETVAL = 0; + } else { + RETVAL = 1; + } + Fclose(fd); + (void)rpmtsFree(ts); + } + rpmlogSetMask(oldlogmask); + + OUTPUT: + RETVAL + + +char * +Urpm_get_gpg_fingerprint(filename) + char * filename + PREINIT: + uint8_t fingerprint[sizeof(pgpKeyID_t)]; + char fingerprint_str[sizeof(pgpKeyID_t) * 2 + 1]; + const uint8_t *pkt = NULL; + size_t pktlen = 0; + int rc; + + CODE: + memset (fingerprint, 0, sizeof (fingerprint)); + if ((rc = pgpReadPkts(filename, (uint8_t ** ) &pkt, &pktlen)) <= 0) { + pktlen = 0; + } else if (rc != PGPARMOR_PUBKEY) { + pktlen = 0; + } else { + unsigned int i; + pgpPubkeyFingerprint (pkt, pktlen, fingerprint); + for (i = 0; i < sizeof (pgpKeyID_t); i++) { + sprintf(&fingerprint_str[i*2], "%02x", fingerprint[i]); + } + } + _free(pkt); + RETVAL = fingerprint_str; + OUTPUT: + RETVAL + + +char * +Urpm_verify_signature(filename, prefix="/") + char *filename + char *prefix + PREINIT: + rpmts ts = NULL; + char result[1024]; + rpmRC rc; + FD_t fd; + Header h; + CODE: + fd = Fopen(filename, "r"); + if (fd == NULL) { + RETVAL = "NOT OK (could not read file)"; + } else { + read_config_files(0); + ts = rpmtsCreate(); + rpmtsSetRootDir(ts, prefix); + rpmtsOpenDB(ts, O_RDONLY); + rpmtsSetVSFlags(ts, RPMVSF_DEFAULT); + rc = rpmReadPackageFile(ts, fd, filename, &h); + Fclose(fd); + *result = '\0'; + switch(rc) { + case RPMRC_OK: + if (h) { + char *fmtsig = headerFormat( + h, + "%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:" + "{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|", + NULL); + snprintf(result, sizeof(result), "OK (%s)", fmtsig); + free(fmtsig); + } else snprintf(result, sizeof(result), "NOT OK (bad rpm): %s", rpmlogMessage()); + break; + case RPMRC_NOTFOUND: + snprintf(result, sizeof(result), "NOT OK (signature not found): %s", rpmlogMessage()); + break; + case RPMRC_FAIL: + snprintf(result, sizeof(result), "NOT OK (fail): %s", rpmlogMessage()); + break; + case RPMRC_NOTTRUSTED: + snprintf(result, sizeof(result), "NOT OK (key not trusted): %s", rpmlogMessage()); + break; + case RPMRC_NOKEY: + snprintf(result, sizeof(result), "NOT OK (no key): %s", rpmlogMessage()); + break; + } + RETVAL = result; + if (h) h = headerFree(h); + (void)rpmtsFree(ts); + } + + OUTPUT: + RETVAL + + +int +Urpm_import_pubkey_file(db, filename) + URPM::DB db + char * filename + PREINIT: + const uint8_t *pkt = NULL; + size_t pktlen = 0; + int rc; + CODE: +#ifdef RPM490 + rpmts ts = rpmtsLink(db->ts); +#else + rpmts ts = rpmtsLink(db->ts, "URPM::import_pubkey_file"); +#endif + rpmtsClean(ts); + + if ((rc = pgpReadPkts(filename, (uint8_t ** ) &pkt, &pktlen)) <= 0) { + RETVAL = 0; + } else if (rc != PGPARMOR_PUBKEY) { + RETVAL = 0; + } else if (rpmtsImportPubkey(ts, pkt, pktlen) != RPMRC_OK) { + RETVAL = 0; + } else { + RETVAL = 1; + } + pkt = _free(pkt); + (void)rpmtsFree(ts); + OUTPUT: + RETVAL + +int +Urpm_import_pubkey(...) + CODE: + unused_variable(&items); + croak("import_pubkey() is dead. use import_pubkey_file() instead"); + RETVAL = 1; + OUTPUT: + RETVAL + +int +Urpm_archscore(arch) + const char * arch + PREINIT: + CODE: + read_config_files(0); + RETVAL=rpmMachineScore(RPM_MACHTABLE_INSTARCH, arch); + OUTPUT: + RETVAL + +int +Urpm_osscore(os) + const char * os + PREINIT: + CODE: + read_config_files(0); + RETVAL=rpmMachineScore(RPM_MACHTABLE_INSTOS, os); + OUTPUT: + RETVAL + +int +Urpm_platformscore(platform) + const char * platform + CODE: + read_config_files(0); + unused_variable(platform); + croak("platformscore() is available only since rpm 4.4.8"); + RETVAL=0; + OUTPUT: + RETVAL + +void +Urpm_stream2header(fp) + FILE *fp + PREINIT: + FD_t fd; + URPM__Package pkg; + PPCODE: + if ((fd = fdDup(fileno(fp)))) { + pkg = (URPM__Package)malloc(sizeof(struct s_Package)); + memset(pkg, 0, sizeof(struct s_Package)); + pkg->h = headerRead(fd, HEADER_MAGIC_YES); + if (pkg->h) { + SV *sv_pkg; + EXTEND(SP, 1); + sv_pkg = sv_newmortal(); + sv_setref_pv(sv_pkg, "URPM::Package", (void*)pkg); + PUSHs(sv_pkg); + } + Fclose(fd); + } + +void +Urpm_spec2srcheader(specfile) + char *specfile + PREINIT: + rpmts ts = rpmtsCreate(); + URPM__Package pkg; + Spec spec = NULL; + Header header = NULL; + PPCODE: +/* ensure the config is in memory with all macro */ + read_config_files(0); +/* Do not verify architecture */ +#define SPEC_ANYARCH 1 +/* Do not verify whether sources exist */ +#define SPEC_FORCE 1 +#ifdef RPM490 + spec = rpmSpecParse(specfile, SPEC_ANYARCH|SPEC_FORCE, NULL); + header = rpmSpecSourceHeader(spec); + if (spec) { +#else + if (!parseSpec(ts, specfile, "/", NULL, 0, NULL, NULL, SPEC_ANYARCH, SPEC_FORCE)) { +#endif + SV *sv_pkg; +#ifndef RPM490 + // FIXME: has disappeared in rpm-4.9.0rpmSpecParse + // (See http://www.rpm.org/wiki/Releases/4.9.0) + spec = rpmtsSetSpec(ts, NULL); + header = spec->sourceHeader; + if (! header) + initSourceHeader(spec); +#endif + pkg = (URPM__Package)calloc(1, sizeof(struct s_Package)); + headerPutString(header, RPMTAG_SOURCERPM, ""); + + { + struct rpmtd_s td = { + .tag = RPMTAG_ARCH, + .type = RPM_STRING_TYPE, + .data = (void *) "src", + .count = 1, + }; + /* parseSpec() sets RPMTAG_ARCH to %{_target_cpu} whereas we really a header similar to .src.rpm header */ + headerMod(header, &td); + } + + pkg->h = headerLink(spec->sourceHeader); + sv_pkg = sv_newmortal(); + sv_setref_pv(sv_pkg, "URPM::Package", (void*)pkg); + XPUSHs(sv_pkg); +#ifdef RPM490 + spec = rpmSpecFree(spec); +#else + spec = freeSpec(spec); +#endif + } else { + XPUSHs(&PL_sv_undef); + /* apparently rpmlib sets errno this when given a bad spec. */ + if (errno == EBADF) + errno = 0; + } + ts = rpmtsFree(ts); + +void +expand(name) + char * name + PPCODE: + const char * value = rpmExpand(name, NULL); + XPUSHs(sv_2mortal(newSVpv(value, 0))); + +void +add_macro_noexpand(macro) + char * macro + CODE: + rpmDefineMacro(NULL, macro, RMIL_DEFAULT); + +void +del_macro(name) + char * name + CODE: + delMacro(NULL, name); + +void +loadmacrosfile(filename) + char * filename + PPCODE: + rpmInitMacros(NULL, filename); + +void +resetmacros() + PPCODE: + rpmFreeMacros(NULL); + +void +setVerbosity(level) + int level + PPCODE: + rpmSetVerbosity(level); + +const char * +rpmErrorString() + CODE: + RETVAL = rpmlogMessage(); + OUTPUT: + RETVAL + +void +rpmErrorWriteTo(fd) + int fd + CODE: + rpmError_callback_data = fd; + rpmlogSetCallback(rpmError_callback, NULL); + + /* vim:set ts=8 sts=2 sw=2: */ |