Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / lib / headerutil.c
index a9f1221..2003219 100644 (file)
@@ -1,5 +1,5 @@
 /** \ingroup rpmdb
- * \file lib/hdrNVR.c
+ * \file lib/headerutil.c
  */
 
 #include "system.h"
@@ -7,63 +7,10 @@
 #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));
@@ -84,7 +31,7 @@ Header headerCopy(Header h)
     }
     headerFreeIterator(hi);
 
-    return headerReload(nh, RPMTAG_HEADERIMAGE);
+    return nh;
 }
 
 void headerCopyTags(Header headerFrom, Header headerTo, 
@@ -243,3 +190,229 @@ int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size)
     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;
+};
+