/** \ingroup rpmdb
- * \file lib/hdrNVR.c
+ * \file lib/headerutil.c
*/
#include "system.h"
#include <rpm/rpmtypes.h>
#include <rpm/header.h>
#include <rpm/rpmstring.h>
+#include <rpm/rpmds.h>
#include "debug.h"
-static int NEVRA(Header h, const char **np,
- uint32_t **ep, const char **vp, const char **rp,
- const char **ap)
-{
- if (np) *np = headerGetString(h, RPMTAG_NAME);
- if (vp) *vp = headerGetString(h, RPMTAG_VERSION);
- if (rp) *rp = headerGetString(h, RPMTAG_RELEASE);
- if (ap) *ap = headerGetString(h, RPMTAG_ARCH);
- if (ep) {
- struct rpmtd_s td;
- headerGet(h, RPMTAG_EPOCH, &td, HEADERGET_DEFAULT);
- *ep = rpmtdGetUint32(&td);
- }
- return 0;
-}
-
-int headerNVR(Header h, const char **np, const char **vp, const char **rp)
-{
- return NEVRA(h, np, NULL, vp, rp, NULL);
-}
-
-int headerNEVRA(Header h, const char **np,
- uint32_t **ep, const char **vp, const char **rp,
- const char **ap)
-{
- return NEVRA(h, np, ep, vp, rp, ap);
-}
-
-static char *getNEVRA(Header h, rpmTag tag, const char **np)
-{
- if (np) *np = headerGetString(h, RPMTAG_NAME);
- return headerGetAsString(h, tag);
-}
-
-char * headerGetNEVR(Header h, const char ** np)
-{
- return getNEVRA(h, RPMTAG_NEVR, np);
-}
-
-char * headerGetNEVRA(Header h, const char ** np)
-{
- return getNEVRA(h, RPMTAG_NEVRA, np);
-}
-
-char * headerGetEVR(Header h, const char ** np)
-{
- return getNEVRA(h, RPMTAG_EVR, np);
-}
-
-rpm_color_t headerGetColor(Header h)
-{
- return headerGetNumber(h, RPMTAG_HEADERCOLOR);
-}
-
int headerIsSource(Header h)
{
return (!headerIsEntry(h, RPMTAG_SOURCERPM));
}
headerFreeIterator(hi);
- return headerReload(nh, RPMTAG_HEADERIMAGE);
+ return nh;
}
void headerCopyTags(Header headerFrom, Header headerTo,
return headerPutType(h, tag, RPM_BIN_TYPE, val, size);
}
+static int dncmp(const void * a, const void * b)
+{
+ const char *const * first = a;
+ const char *const * second = b;
+ return strcmp(*first, *second);
+}
+
+static void compressFilelist(Header h)
+{
+ struct rpmtd_s fileNames;
+ char ** dirNames;
+ const char ** baseNames;
+ uint32_t * dirIndexes;
+ rpm_count_t count, realCount = 0;
+ int i;
+ int dirIndex = -1;
+
+ /*
+ * This assumes the file list is already sorted, and begins with a
+ * single '/'. That assumption isn't critical, but it makes things go
+ * a bit faster.
+ */
+
+ if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
+ headerDel(h, RPMTAG_OLDFILENAMES);
+ return; /* Already converted. */
+ }
+
+ if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM))
+ return;
+ count = rpmtdCount(&fileNames);
+ if (count < 1)
+ return;
+
+ dirNames = xmalloc(sizeof(*dirNames) * count); /* worst case */
+ baseNames = xmalloc(sizeof(*dirNames) * count);
+ dirIndexes = xmalloc(sizeof(*dirIndexes) * count);
+
+ /* HACK. Source RPM, so just do things differently */
+ { const char *fn = rpmtdGetString(&fileNames);
+ if (fn && *fn != '/') {
+ dirIndex = 0;
+ dirNames[dirIndex] = xstrdup("");
+ while ((i = rpmtdNext(&fileNames)) >= 0) {
+ dirIndexes[i] = dirIndex;
+ baseNames[i] = rpmtdGetString(&fileNames);
+ realCount++;
+ }
+ goto exit;
+ }
+ }
+
+ /*
+ * XXX EVIL HACK, FIXME:
+ * This modifies (and then restores) a const string from rpmtd
+ * through basename retrieved from strrchr() which silently
+ * casts away const on return.
+ */
+ while ((i = rpmtdNext(&fileNames)) >= 0) {
+ char ** needle;
+ char savechar;
+ char * baseName;
+ size_t len;
+ char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */
+
+ if (filename == NULL) /* XXX can't happen */
+ continue;
+ baseName = strrchr(filename, '/');
+ if (baseName == NULL) {
+ baseName = filename;
+ } else {
+ baseName += 1;
+ }
+ len = baseName - filename;
+ needle = dirNames;
+ savechar = *baseName;
+ *baseName = '\0';
+ if (dirIndex < 0 ||
+ (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
+ char *s = xmalloc(len + 1);
+ rstrlcpy(s, filename, len + 1);
+ dirIndexes[realCount] = ++dirIndex;
+ dirNames[dirIndex] = s;
+ } else
+ dirIndexes[realCount] = needle - dirNames;
+
+ *baseName = savechar;
+ baseNames[realCount] = baseName;
+ realCount++;
+ }
+
+exit:
+ if (count > 0) {
+ headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, realCount);
+ headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, realCount);
+ headerPutStringArray(h, RPMTAG_DIRNAMES,
+ (const char **) dirNames, dirIndex + 1);
+ }
+
+ rpmtdFreeData(&fileNames);
+ for (i = 0; i <= dirIndex; i++) {
+ free(dirNames[i]);
+ }
+ free(dirNames);
+ free(baseNames);
+ free(dirIndexes);
+
+ headerDel(h, RPMTAG_OLDFILENAMES);
+}
+
+static void expandFilelist(Header h)
+{
+ struct rpmtd_s filenames;
+
+ if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
+ (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
+ if (rpmtdCount(&filenames) < 1)
+ return;
+ rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
+ headerPut(h, &filenames, HEADERPUT_DEFAULT);
+ rpmtdFreeData(&filenames);
+ }
+
+ (void) headerDel(h, RPMTAG_DIRNAMES);
+ (void) headerDel(h, RPMTAG_BASENAMES);
+ (void) headerDel(h, RPMTAG_DIRINDEXES);
+}
+
+/*
+ * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
+ * Retrofit an explicit "Provides: name = epoch:version-release.
+ */
+static void providePackageNVR(Header h)
+{
+ const char *name = headerGetString(h, RPMTAG_NAME);
+ char *pEVR = headerGetAsString(h, RPMTAG_EVR);
+ rpmsenseFlags pFlags = RPMSENSE_EQUAL;
+ int bingo = 1;
+ struct rpmtd_s pnames;
+ rpmds hds, nvrds;
+
+ /* Generate provides for this package name-version-release. */
+ if (!(name && pEVR))
+ return;
+
+ /*
+ * Rpm prior to 3.0.3 does not have versioned provides.
+ * If no provides at all are available, we can just add.
+ */
+ if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
+ goto exit;
+ }
+
+ /*
+ * Otherwise, fill in entries on legacy packages.
+ */
+ if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
+ while (rpmtdNext(&pnames) >= 0) {
+ rpmsenseFlags fdummy = RPMSENSE_ANY;
+
+ headerPutString(h, RPMTAG_PROVIDEVERSION, "");
+ headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
+ }
+ goto exit;
+ }
+
+ /* see if we already have this provide */
+ hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0);
+ nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
+ if (rpmdsFind(hds, nvrds) >= 0) {
+ bingo = 0;
+ }
+ rpmdsFree(hds);
+ rpmdsFree(nvrds);
+
+
+exit:
+ if (bingo) {
+ headerPutString(h, RPMTAG_PROVIDENAME, name);
+ headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR);
+ headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
+ }
+ rpmtdFreeData(&pnames);
+ free(pEVR);
+}
+
+static void legacyRetrofit(Header h)
+{
+ /*
+ * The file list was moved to a more compressed format which not
+ * only saves memory (nice), but gives fingerprinting a nice, fat
+ * speed boost (very nice). Go ahead and convert old headers to
+ * the new style (this is a noop for new headers).
+ */
+ compressFilelist(h);
+
+ /* Retrofit "Provide: name = EVR" for binary packages. */
+ if (!headerIsSource(h)) {
+ providePackageNVR(h);
+ }
+}
+
+int headerConvert(Header h, int op)
+{
+ int rc = 1;
+
+ if (h == NULL)
+ return 0;
+
+ switch (op) {
+ case HEADERCONV_EXPANDFILELIST:
+ expandFilelist(h);
+ break;
+ case HEADERCONV_COMPRESSFILELIST:
+ compressFilelist(h);
+ break;
+ case HEADERCONV_RETROFIT_V3:
+ legacyRetrofit(h);
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ return rc;
+};
+