Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / build / rpmfc.c
index 2f0eac4..2fbfc69 100644 (file)
@@ -1,6 +1,7 @@
 #include "system.h"
 
 #include <errno.h>
+#include <libgen.h>
 #include <sys/select.h>
 #include <sys/wait.h>
 #include <signal.h>
@@ -16,6 +17,7 @@
 #include <rpm/rpmfi.h>
 #include <rpm/rpmstrpool.h>
 
+#include "lib/rpmfi_internal.h"                /* rpmfiles stuff for now */
 #include "build/rpmbuild_internal.h"
 
 #include "debug.h"
@@ -32,13 +34,24 @@ typedef struct rpmfcAttr_s {
     struct matchRule excl;
 } * rpmfcAttr;
 
+typedef struct {
+    int fileIx;
+    rpmds dep;
+} rpmfcFileDep;
+
+typedef struct {
+    rpmfcFileDep *data;
+    int size;
+    int alloced;
+} rpmfcFileDeps;
+
 /**
  */
 struct rpmfc_s {
+    Package pkg;
     int nfiles;                /*!< no. of files */
     int fknown;                /*!< no. of classified files */
     int fwhite;                /*!< no. of "white" files */
-    int ix;            /*!< current file index */
     int skipProv;      /*!< Don't auto-generate Provides:? */
     int skipReq;       /*!< Don't auto-generate Requires:? */
     char *buildRoot;   /*!< (Build) root dir */
@@ -54,10 +67,9 @@ struct rpmfc_s {
     ARGI_t fddictn;    /*!< (no. files) file depends dictionary no. entries */
     ARGI_t ddictx;     /*!< (no. dependencies) file->dependency mapping */
     rpmstrPool cdict;  /*!< file class dictionary */
-    rpmstrPool ddict;  /*!< file depends dictionary */
+    rpmfcFileDeps fileDeps; /*!< file dependency mapping */
 
-    rpmds provides;    /*!< (no. provides) package provides */
-    rpmds requires;    /*!< (no. requires) package requires */
+    rpmstrPool pool;   /*!< general purpose string storage */
 };
 
 struct rpmfcTokens_s {
@@ -86,22 +98,62 @@ static void ruleFree(struct matchRule *rule)
     argvFree(rule->flags);
 }
 
-static char *rpmfcAttrMacro(const char *name,
-                           const char *attr_prefix, const char *attr)
+static char *rpmfcAttrMacroV(const char *arg, va_list args)
 {
-    char *ret;
-    if (attr_prefix && attr_prefix[0] != '\0')
-       ret = rpmExpand("%{?__", name, "_", attr_prefix, "_", attr, "}", NULL);
-    else
-       ret = rpmExpand("%{?__", name, "_", attr, "}", NULL);
-    return rstreq(ret, "") ? _free(ret) : ret;
+    const char *s;
+    int blen;
+    char *buf = NULL, *obuf;
+    char *pe;
+    va_list args2;
+
+    if (arg == NULL || rstreq(arg, ""))
+       return NULL;
+
+    va_copy(args2, args);
+    blen = sizeof("%{?_") - 1;
+    for (s = arg; s != NULL; s = va_arg(args, const char *)) {
+       blen += sizeof("_") - 1 + strlen(s);
+    }
+    blen += sizeof("}") - 1;
+
+    buf = xmalloc(blen + 1);
+
+    pe = buf;
+    pe = stpcpy(pe, "%{?_");
+    for (s = arg; s != NULL; s = va_arg(args2, const char *)) {
+       *pe++ = '_';
+       pe = stpcpy(pe, s);
+    }
+    va_end(args2);
+    *pe++ = '}';
+    *pe = '\0';
+
+    obuf = rpmExpand(buf, NULL);
+    free(buf);
+
+    return rstreq(obuf, "") ? _free(obuf) : obuf;
 }
 
-static regex_t *rpmfcAttrReg(const char *name,
-                            const char *attr_prefix, const char *attr)
+static char *rpmfcAttrMacro(const char *arg, ...)
+{
+    va_list args;
+    char *s;
+
+    va_start(args, arg);
+    s = rpmfcAttrMacroV(arg, args);
+    va_end(args);
+    return s;
+}
+
+static regex_t *rpmfcAttrReg(const char *arg, ...)
 {
     regex_t *reg = NULL;
-    char *pattern = rpmfcAttrMacro(name, attr_prefix, attr);
+    char *pattern;
+    va_list args;
+
+    va_start(args, arg);
+    pattern = rpmfcAttrMacroV(arg, args);
+    va_end(args);
     if (pattern) {
        reg = xcalloc(1, sizeof(*reg));
        if (regcomp(reg, pattern, REG_EXTENDED) != 0) { 
@@ -121,10 +173,19 @@ static rpmfcAttr rpmfcAttrNew(const char *name)
     attr->name = xstrdup(name);
     for (struct matchRule **rule = rules; rule && *rule; rule++) {
        const char *prefix = (*rule == &attr->incl) ? NULL : "exclude";
-       char *flags = rpmfcAttrMacro(name, prefix, "flags");
+       char *flags;
+
+       if (prefix) {
+           flags = rpmfcAttrMacro(name, prefix, "flags", NULL);
+
+           (*rule)->path = rpmfcAttrReg(name, prefix, "path", NULL);
+           (*rule)->magic = rpmfcAttrReg(name, prefix, "magic", NULL);
+       } else {
+           flags = rpmfcAttrMacro(name, "flags", NULL);
 
-       (*rule)->path = rpmfcAttrReg(name, prefix, "path");
-       (*rule)->magic = rpmfcAttrReg(name, prefix, "magic");
+           (*rule)->path = rpmfcAttrReg(name, "path", NULL);
+           (*rule)->magic = rpmfcAttrReg(name, "magic", NULL);
+       }
        (*rule)->flags = argvSplitString(flags, ",", ARGV_SKIPEMPTY);
        argvSort((*rule)->flags, NULL);
 
@@ -162,16 +223,17 @@ static int rpmfcExpandAppend(ARGV_t * argvp, ARGV_const_t av)
     return 0;
 }
 
-static rpmds rpmdsSingleNS(rpmTagVal tagN, const char *namespace,
+static rpmds rpmdsSingleNS(rpmstrPool pool,
+                       rpmTagVal tagN, const char *namespace,
                        const char * N, const char * EVR, rpmsenseFlags Flags)
 {
     rpmds ds = NULL;
     if (namespace) {
        char *NSN = rpmExpand(namespace, "(", N, ")", NULL);
-       ds = rpmdsSingle(tagN, NSN, EVR, Flags);
+       ds = rpmdsSinglePool(pool, tagN, NSN, EVR, Flags);
        free(NSN);
     } else {
-       ds = rpmdsSingle(tagN, N, EVR, Flags);
+       ds = rpmdsSinglePool(pool, tagN, N, EVR, Flags);
     }
     return ds;
 }
@@ -207,8 +269,6 @@ static StringBuf getOutputFrom(ARGV_t argv,
     
     child = fork();
     if (child == 0) {
-       /* NSPR messes with SIGPIPE, reset to default for the kids */
-       signal(SIGPIPE, SIG_DFL);
        close(toProg[1]);
        close(fromProg[0]);
        
@@ -368,6 +428,12 @@ int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp,
        buf_stdin_len = strlen(buf_stdin);
     }
 
+    if (_rpmfc_debug) {
+       char *cmd = argvJoin(xav, " ");
+       rpmlog(RPMLOG_DEBUG, "Executing %s on %s\n", cmd, buf_stdin);
+       free(cmd);
+    }
+
     /* Read output from exec'd helper. */
     sb = getOutputFrom(xav, buf_stdin, buf_stdin_len, failnonzero, buildRoot);
 
@@ -396,15 +462,15 @@ static void argvAddUniq(ARGV_t * argvp, const char * key)
 
 #define hasAttr(_a, _n) (argvSearch((_a), (_n), NULL) != NULL)
 
-static void rpmfcAddFileDep(rpmstrPool ddict, int ix, rpmds ds, char deptype)
+static void rpmfcAddFileDep(rpmfcFileDeps *fileDeps, rpmds ds, int ix)
 {
-    if (ds) {
-       char *key = NULL;
-       rasprintf(&key, "%08d%c %s %s 0x%08x", ix, deptype,
-                 rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
-       rpmstrPoolId(ddict, key, 1);
-       free(key);
+    if (fileDeps->size == fileDeps->alloced) {
+       fileDeps->alloced <<= 2;
+       fileDeps->data  = xrealloc(fileDeps->data,
+           fileDeps->alloced * sizeof(fileDeps->data[0]));
     }
+    fileDeps->data[fileDeps->size].fileIx = ix;
+    fileDeps->data[fileDeps->size++].dep = ds;
 }
 
 static ARGV_t runCmd(const char *nsdep, const char *depname,
@@ -423,7 +489,7 @@ static ARGV_t runCmd(const char *nsdep, const char *depname,
 
        appendLineStringBuf(sb_stdin, fn);
        if (rpmfcExec(av, sb_stdin, &sb_stdout, 0, buildRoot) == 0) {
-           argvSplit(&output, getStringBuf(sb_stdout), " \t\n\r");
+           argvSplit(&output, getStringBuf(sb_stdout), "\n\r");
        }
 
        argvFree(av);
@@ -435,74 +501,77 @@ static ARGV_t runCmd(const char *nsdep, const char *depname,
     return output;
 }
 
+struct addReqProvDataFc {
+    rpmfc fc;
+    const char *namespace;
+    regex_t *exclude;
+};
+
+static rpmRC addReqProvFc(void *cbdata, rpmTagVal tagN,
+                         const char * N, const char * EVR, rpmsenseFlags Flags,
+                         int index)
+{
+    struct addReqProvDataFc *data = cbdata;
+    rpmfc fc = data->fc;
+    const char *namespace = data->namespace;
+    regex_t *exclude = data->exclude;
+
+    rpmds ds = rpmdsSingleNS(fc->pool, tagN, namespace, N, EVR, Flags);
+    /* Add to package and file dependencies unless filtered */
+    if (regMatch(exclude, rpmdsDNEVR(ds)+2) == 0)
+       rpmfcAddFileDep(&fc->fileDeps, ds, index);
+
+    return RPMRC_OK;
+}
+
 /**
  * Run per-interpreter dependency helper.
  * @param fc           file classifier
+ * @param ix           file index
  * @param nsdep                class name for interpreter (e.g. "perl")
  * @param depname      "provides" or "requires"
- * @param depsp                fc->provides or fc->requires
  * @param dsContext    RPMSENSE_FIND_PROVIDES or RPMSENSE_FIND_REQUIRES
  * @param tagN         RPMTAG_PROVIDENAME or RPMTAG_REQUIRENAME
- * @return             0
+ * @return             0 on success
  */
-static int rpmfcHelper(rpmfc fc, const char *nsdep, const char *depname,
-                      rpmds *depsp, rpmsenseFlags dsContext, rpmTagVal tagN)
+static int rpmfcHelper(rpmfc fc, int ix,
+                      const char *nsdep, const char *depname,
+                      rpmsenseFlags dsContext, rpmTagVal tagN)
 {
     ARGV_t pav = NULL;
-    const char * fn = fc->fn[fc->ix];
+    const char * fn = fc->fn[ix];
     char *namespace = NULL;
     int pac;
+    int rc = 0;
     regex_t *exclude = NULL;
     regex_t *exclude_from = NULL;
+    regex_t *global_exclude_from = NULL;
 
     /* If the entire path is filtered out, there's nothing more to do */
-    exclude_from = rpmfcAttrReg(depname, "exclude", "from");
+    exclude_from = rpmfcAttrReg(depname, "exclude", "from", NULL);
     if (regMatch(exclude_from, fn+fc->brlen))
        goto exit;
 
+    global_exclude_from = rpmfcAttrReg("global", depname, "exclude", "from", NULL);
+    if (regMatch(global_exclude_from, fn+fc->brlen))
+       goto exit;
+
     pav = runCmd(nsdep, depname, fc->buildRoot, fn);
     if (pav == NULL)
        goto exit;
 
     pac = argvCount(pav);
-    namespace = rpmfcAttrMacro(nsdep, NULL, "namespace");
-    exclude = rpmfcAttrReg(depname, NULL, "exclude");
-
-    for (int i = 0; i < pac; i++) {
-       rpmds ds = NULL;
-       const char *N = pav[i];
-       const char *EVR = "";
-       rpmsenseFlags Flags = dsContext;
-       if (pav[i+1] && strchr("=<>", *pav[i+1])) {
-           i++;
-           for (const char *s = pav[i]; *s; s++) {
-               switch(*s) {
-               default:
-                   break;
-               case '=':
-                   Flags |= RPMSENSE_EQUAL;
-                   break;
-               case '<':
-                   Flags |= RPMSENSE_LESS;
-                   break;
-               case '>':
-                   Flags |= RPMSENSE_GREATER;
-                   break;
-               }
-           }
-           i++;
-           EVR = pav[i];
-       }
-
-       ds = rpmdsSingleNS(tagN, namespace, N, EVR, Flags);
+    namespace = rpmfcAttrMacro(nsdep, "namespace", NULL);
+    exclude = rpmfcAttrReg(depname, "exclude", NULL);
 
-       /* Add to package and file dependencies unless filtered */
-       if (regMatch(exclude, rpmdsDNEVR(ds)+2) == 0) {
-           (void) rpmdsMerge(depsp, ds);
-           rpmfcAddFileDep(fc->ddict, fc->ix, ds, tagN == RPMTAG_PROVIDENAME ? 'P' : 'R');
-       }
+    struct addReqProvDataFc data;
+    data.fc = fc;
+    data.namespace = namespace;
+    data.exclude = exclude;
 
-       rpmdsFree(ds);
+    for (int i = 0; i < pac; i++) {
+       if (parseRCPOT(NULL, fc->pkg, pav[i], tagN, ix, dsContext, addReqProvFc, &data))
+           rc++;
     }
 
     argvFree(pav);
@@ -511,39 +580,8 @@ static int rpmfcHelper(rpmfc fc, const char *nsdep, const char *depname,
 
 exit:
     regFree(exclude_from);
-    return 0;
-}
-
-/**
- * Run per-interpreter Provides: dependency helper.
- * @param fc           file classifier
- * @param nsdep                class name for interpreter (e.g. "perl")
- * @return             0
- */
-static int rpmfcHelperProvides(rpmfc fc, const char * nsdep)
-{
-    if (fc->skipProv)
-       return 0;
-
-    rpmfcHelper(fc, nsdep, "provides", &fc->provides, RPMSENSE_FIND_PROVIDES, RPMTAG_PROVIDENAME);
-
-    return 0;
-}
-
-/**
- * Run per-interpreter Requires: dependency helper.
- * @param fc           file classifier
- * @param nsdep                class name for interpreter (e.g. "perl")
- * @return             0
- */
-static int rpmfcHelperRequires(rpmfc fc, const char * nsdep)
-{
-    if (fc->skipReq)
-       return 0;
-
-    rpmfcHelper(fc, nsdep, "requires", &fc->requires, RPMSENSE_FIND_REQUIRES, RPMTAG_REQUIRENAME);
-
-    return 0;
+    regFree(global_exclude_from);
+    return rc;
 }
 
 /* Only used for elf coloring and controlling RPMTAG_FILECLASS inclusion now */
@@ -618,7 +656,7 @@ static int matches(const struct matchRule *rule,
     }
 }
 
-static void rpmfcAttributes(rpmfc fc, const char *ftype, const char *fullpath)
+static void rpmfcAttributes(rpmfc fc, int ix, const char *ftype, const char *fullpath)
 {
     const char *path = fullpath + fc->brlen;
     int is_executable = 0;
@@ -635,7 +673,7 @@ static void rpmfcAttributes(rpmfc fc, const char *ftype, const char *fullpath)
 
        /* Add attributes on libmagic type & path pattern matches */
        if (matches(&(*attr)->incl, ftype, path, is_executable))
-           argvAddTokens(&fc->fattrs[fc->ix], (*attr)->name);
+           argvAddTokens(&fc->fattrs[ix], (*attr)->name);
     }
 }
 
@@ -659,39 +697,34 @@ static rpm_color_t rpmfcColor(const char * fmstr)
 
 void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
 {
-    rpm_color_t fcolor;
     int ndx;
     int dx;
     int fx;
 
-int nprovides;
-int nrequires;
-
     if (fp == NULL) fp = stderr;
 
     if (msg)
        fprintf(fp, "===================================== %s\n", msg);
 
-nprovides = rpmdsCount(fc->provides);
-nrequires = rpmdsCount(fc->requires);
-
     if (fc)
     for (fx = 0; fx < fc->nfiles; fx++) {
-       rpmsid cx = fc->fcdictx[fx] + 1; /* id's are one off */
-       fcolor = fc->fcolor[fx];
-       ARGV_t fattrs = fc->fattrs[fx];
-
        fprintf(fp, "%3d %s", fx, fc->fn[fx]);
-       if (fcolor != RPMFC_BLACK)
+       if (_rpmfc_debug) {
+           rpmsid cx = fc->fcdictx[fx] + 1; /* id's are one off */
+           rpm_color_t fcolor = fc->fcolor[fx];
+           ARGV_t fattrs = fc->fattrs[fx];
+
+           if (fcolor != RPMFC_BLACK)
                fprintf(fp, "\t0x%x", fc->fcolor[fx]);
-       else
+           else
                fprintf(fp, "\t%s", rpmstrPoolStr(fc->cdict, cx));
-       if (fattrs) {
-           char *attrs = argvJoin(fattrs, ",");
-           fprintf(fp, " [%s]", attrs);
-           free(attrs);
-       } else {
-           fprintf(fp, " [none]");
+           if (fattrs) {
+               char *attrs = argvJoin(fattrs, ",");
+               fprintf(fp, " [%s]", attrs);
+               free(attrs);
+           } else {
+               fprintf(fp, " [none]");
+           }
        }
        fprintf(fp, "\n");
 
@@ -707,32 +740,16 @@ assert(fx < fc->fddictn->nvals);
            const char * depval;
            unsigned char deptype;
            unsigned ix;
+           rpmds ds;
 
            ix = fc->ddictx->vals[dx++];
            deptype = ((ix >> 24) & 0xff);
            ix &= 0x00ffffff;
            depval = NULL;
-           switch (deptype) {
-           default:
-assert(depval != NULL);
-               break;
-           case 'P':
-               if (nprovides > 0) {
-assert(ix < nprovides);
-                   (void) rpmdsSetIx(fc->provides, ix-1);
-                   if (rpmdsNext(fc->provides) >= 0)
-                       depval = rpmdsDNEVR(fc->provides);
-               }
-               break;
-           case 'R':
-               if (nrequires > 0) {
-assert(ix < nrequires);
-                   (void) rpmdsSetIx(fc->requires, ix-1);
-                   if (rpmdsNext(fc->requires) >= 0)
-                       depval = rpmdsDNEVR(fc->requires);
-               }
-               break;
-           }
+           ds = rpmfcDependencies(fc, rpmdsDToTagN(deptype));
+           (void) rpmdsSetIx(ds, ix-1);
+           if (rpmdsNext(ds) >= 0)
+               depval = rpmdsDNEVR(ds);
            if (depval)
                fprintf(fp, "\t%s\n", depval);
        }
@@ -754,15 +771,19 @@ rpmfc rpmfcFree(rpmfc fc)
        free(fc->fattrs);
        free(fc->fcolor);
        free(fc->fcdictx);
+       free(fc->pkg);
        argiFree(fc->fddictx);
        argiFree(fc->fddictn);
        argiFree(fc->ddictx);
 
-       rpmstrPoolFree(fc->ddict);
+       for (int i = 0; i < fc->fileDeps.size; i++) {
+           rpmdsFree(fc->fileDeps.data[i].dep);
+       }
+       free(fc->fileDeps.data);
+
        rpmstrPoolFree(fc->cdict);
 
-       rpmdsFree(fc->provides);
-       rpmdsFree(fc->requires);
+       rpmstrPoolFree(fc->pool);
        memset(fc, 0, sizeof(*fc)); /* trash and burn */
        free(fc);
     }
@@ -776,6 +797,11 @@ rpmfc rpmfcCreate(const char *buildRoot, rpmFlags flags)
        fc->buildRoot = xstrdup(buildRoot);
        fc->brlen = strlen(buildRoot);
     }
+    fc->pool = rpmstrPoolCreate();
+    fc->pkg = xcalloc(1, sizeof(*fc->pkg));
+    fc->fileDeps.alloced = 10;
+    fc->fileDeps.data = xmalloc(fc->fileDeps.alloced *
+       sizeof(fc->fileDeps.data[0]));
     return fc;
 }
 
@@ -784,86 +810,183 @@ rpmfc rpmfcNew(void)
     return rpmfcCreate(NULL, 0);
 }
 
+rpmds rpmfcDependencies(rpmfc fc, rpmTagVal tag)
+{
+    if (fc) {
+       return *packageDependencies(fc->pkg, tag);
+    }
+    return NULL;
+}
+
 rpmds rpmfcProvides(rpmfc fc)
 {
-    return (fc != NULL ? fc->provides : NULL);
+    return rpmfcDependencies(fc, RPMTAG_PROVIDENAME);
 }
 
 rpmds rpmfcRequires(rpmfc fc)
 {
-    return (fc != NULL ? fc->requires : NULL);
+    return rpmfcDependencies(fc, RPMTAG_REQUIRENAME);
 }
 
-rpmRC rpmfcApply(rpmfc fc)
+rpmds rpmfcRecommends(rpmfc fc)
 {
-    const char * s;
-    char * se;
-    rpmds ds;
-    const char * N;
-    const char * EVR;
-    rpmsenseFlags Flags;
-    unsigned char deptype;
-    int nddict;
+    return rpmfcDependencies(fc, RPMTAG_RECOMMENDNAME);
+}
+
+rpmds rpmfcSuggests(rpmfc fc)
+{
+    return rpmfcDependencies(fc, RPMTAG_SUGGESTNAME);
+}
+
+rpmds rpmfcSupplements(rpmfc fc)
+{
+    return rpmfcDependencies(fc, RPMTAG_SUPPLEMENTNAME);
+}
+
+rpmds rpmfcEnhances(rpmfc fc)
+{
+    return rpmfcDependencies(fc, RPMTAG_ENHANCENAME);
+}
+
+rpmds rpmfcConflicts(rpmfc fc)
+{
+    return rpmfcDependencies(fc, RPMTAG_CONFLICTNAME);
+}
+
+rpmds rpmfcObsoletes(rpmfc fc)
+{
+    return rpmfcDependencies(fc, RPMTAG_OBSOLETENAME);
+}
+
+
+/* Versioned deps are less than unversioned deps */
+static int cmpVerDeps(const void *a, const void *b)
+{
+    rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
+    rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
+
+    int aIsVersioned = rpmdsFlags(fDepA->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
+    int bIsVersioned = rpmdsFlags(fDepB->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
+
+    return bIsVersioned - aIsVersioned;
+}
+
+/* Sort by index */
+static int cmpIndexDeps(const void *a, const void *b)
+{
+    rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
+    rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
+
+    return fDepA->fileIx - fDepB->fileIx;
+}
+
+/*
+ * Remove unversioned deps if corresponding versioned deps exist but only
+ * if the versioned dependency has the same type and the same color as the versioned.
+ */
+static void rpmfcNormalizeFDeps(rpmfc fc)
+{
+    rpmstrPool versionedDeps = rpmstrPoolCreate();
+    rpmfcFileDep *normalizedFDeps = xmalloc(fc->fileDeps.size *
+       sizeof(normalizedFDeps[0]));
+    int ix = 0;
+    char *depStr;
+
+    /* Sort. Versioned dependencies first */
+    qsort(fc->fileDeps.data, fc->fileDeps.size, sizeof(fc->fileDeps.data[0]),
+       cmpVerDeps);
+
+    for (int i = 0; i < fc->fileDeps.size; i++) {
+       switch (rpmdsTagN(fc->fileDeps.data[i].dep)) {
+       case RPMTAG_REQUIRENAME:
+       case RPMTAG_RECOMMENDNAME:
+       case RPMTAG_SUGGESTNAME:
+           rasprintf(&depStr, "%08x_%c_%s",
+               fc->fcolor[fc->fileDeps.data[i].fileIx],
+               rpmdsD(fc->fileDeps.data[i].dep),
+               rpmdsN(fc->fileDeps.data[i].dep));
+
+           if (rpmdsFlags(fc->fileDeps.data[i].dep) & RPMSENSE_SENSEMASK) {
+               /* preserve versioned require dependency */
+               normalizedFDeps[ix++] = fc->fileDeps.data[i];
+               rpmstrPoolId(versionedDeps, depStr, 1);
+           } else if (!rpmstrPoolId(versionedDeps, depStr, 0)) {
+               /* preserve unversioned require dep only if versioned dep doesn't exist */
+                   normalizedFDeps[ix++] =fc-> fileDeps.data[i];
+           } else {
+               rpmdsFree(fc->fileDeps.data[i].dep);
+           }
+           free(depStr);
+           break;
+       default:
+           /* Preserve all non-require dependencies */
+           normalizedFDeps[ix++] = fc->fileDeps.data[i];
+           break;
+       }
+    }
+    rpmstrPoolFree(versionedDeps);
+
+    free(fc->fileDeps.data);
+    fc->fileDeps.data = normalizedFDeps;
+    fc->fileDeps.size = ix;
+}
+
+static rpmRC rpmfcApplyInternal(rpmfc fc)
+{
+    rpmds ds, * dsp;
     int previx;
     unsigned int val;
     int dix;
     int ix;
 
     /* Generate package and per-file dependencies. */
-    for (fc->ix = 0; fc->ix < fc->nfiles && fc->fn[fc->ix] != NULL; fc->ix++) {
-       for (ARGV_t fattr = fc->fattrs[fc->ix]; fattr && *fattr; fattr++) {
-           rpmfcHelperProvides(fc, *fattr);
-           rpmfcHelperRequires(fc, *fattr);
+    for (ix = 0; ix < fc->nfiles && fc->fn[ix] != NULL; ix++) {
+       for (ARGV_t fattr = fc->fattrs[ix]; fattr && *fattr; fattr++) {
+           if (!fc->skipProv) {
+               rpmfcHelper(fc, ix, *fattr, "provides",
+                           RPMSENSE_FIND_PROVIDES, RPMTAG_PROVIDENAME);
+           }
+           if (!fc->skipReq) {
+               rpmfcHelper(fc, ix, *fattr, "requires",
+                           RPMSENSE_FIND_REQUIRES, RPMTAG_REQUIRENAME);
+               rpmfcHelper(fc, ix, *fattr, "recommends",
+                           RPMSENSE_FIND_REQUIRES, RPMTAG_RECOMMENDNAME);
+               rpmfcHelper(fc, ix, *fattr, "suggests",
+                           RPMSENSE_FIND_REQUIRES, RPMTAG_SUGGESTNAME);
+               rpmfcHelper(fc, ix, *fattr, "supplements",
+                           RPMSENSE_FIND_REQUIRES, RPMTAG_SUPPLEMENTNAME);
+               rpmfcHelper(fc, ix, *fattr, "enhances",
+                           RPMSENSE_FIND_REQUIRES, RPMTAG_ENHANCENAME);
+               rpmfcHelper(fc, ix, *fattr, "conflicts",
+                           RPMSENSE_FIND_REQUIRES, RPMTAG_CONFLICTNAME);
+               rpmfcHelper(fc, ix, *fattr, "obsoletes",
+                           RPMSENSE_FIND_REQUIRES, RPMTAG_OBSOLETENAME);
+           }
        }
     }
     /* No more additions after this, freeze pool to minimize memory use */
-    rpmstrPoolFreeze(fc->ddict, 0);
+
+    rpmfcNormalizeFDeps(fc);
+    for (int i = 0; i < fc->fileDeps.size; i++) {
+       ds = fc->fileDeps.data[i].dep;
+       rpmdsMerge(packageDependencies(fc->pkg, rpmdsTagN(ds)), ds);
+    }
+
+    /* Sort by index */
+    qsort(fc->fileDeps.data, fc->fileDeps.size,
+       sizeof(fc->fileDeps.data[0]), cmpIndexDeps);
 
     /* Generate per-file indices into package dependencies. */
-    nddict = rpmstrPoolNumStr(fc->ddict);
     previx = -1;
-    for (rpmsid id = 1; id <= nddict; id++) {
-       s = rpmstrPoolStr(fc->ddict, id);
-
-       /* Parse out (file#,deptype,N,EVR,Flags) */
-       ix = strtol(s, &se, 10);
-       if ( se == NULL ) {
-               rpmlog(RPMLOG_ERR, _("Conversion of %s to long integer failed.\n"), s);
-               return RPMRC_FAIL;
-       }
-       
-       deptype = *se++;
-       se++;
-       N = se;
-       while (*se && *se != ' ')
-           se++;
-       *se++ = '\0';
-       EVR = se;
-       while (*se && *se != ' ')
-           se++;
-       *se++ = '\0';
-       Flags = strtol(se, NULL, 16);
-
-       dix = -1;
-       switch (deptype) {
-       default:
-           break;
-       case 'P':
-           ds = rpmdsSingle(RPMTAG_PROVIDENAME, N, EVR, Flags);
-           dix = rpmdsFind(fc->provides, ds);
-           rpmdsFree(ds);
-           break;
-       case 'R':
-           ds = rpmdsSingle(RPMTAG_REQUIRENAME, N, EVR, Flags);
-           dix = rpmdsFind(fc->requires, ds);
-           rpmdsFree(ds);
-           break;
-       }
-
+    for (int i = 0; i < fc->fileDeps.size; i++) {
+       ds = fc->fileDeps.data[i].dep;
+       ix = fc->fileDeps.data[i].fileIx;
+       dsp = packageDependencies(fc->pkg, rpmdsTagN(ds));
+       dix = rpmdsFind(*dsp, ds);
        if (dix < 0)
            continue;
 
-       val = (deptype << 24) | (dix & 0x00ffffff);
+       val = (rpmdsD(ds) << 24) | (dix & 0x00ffffff);
        argiAdd(&fc->ddictx, -1, val);
 
        if (previx != ix) {
@@ -872,8 +995,8 @@ rpmRC rpmfcApply(rpmfc fc)
        }
        if (fc->fddictn && fc->fddictn->vals)
            fc->fddictn->vals[ix]++;
-    }
 
+    }
     return RPMRC_OK;
 }
 
@@ -931,7 +1054,6 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
 
     /* Build (sorted) file class dictionary. */
     fc->cdict = rpmstrPoolCreate();
-    fc->ddict = rpmstrPoolCreate();
 
     ms = magic_open(msflags);
     if (ms == NULL) {
@@ -945,13 +1067,13 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
        goto exit;
     }
 
-    for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
+    for (int ix = 0; ix < fc->nfiles; ix++) {
        rpmsid ftypeId;
        const char * ftype;
-       const char * s = argv[fc->ix];
+       const char * s = argv[ix];
        size_t slen = strlen(s);
        int fcolor = RPMFC_BLACK;
-       rpm_mode_t mode = (fmode ? fmode[fc->ix] : 0);
+       rpm_mode_t mode = (fmode ? fmode[ix] : 0);
        int is_executable = (mode & (S_IXUSR|S_IXGRP|S_IXOTH));
 
        switch (mode & S_IFMT) {
@@ -997,15 +1119,15 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
        rpmlog(RPMLOG_DEBUG, "%s: %s\n", s, ftype);
 
        /* Save the path. */
-       fc->fn[fc->ix] = xstrdup(s);
+       fc->fn[ix] = xstrdup(s);
 
        /* Add (filtered) file coloring */
        fcolor |= rpmfcColor(ftype);
 
        /* Add attributes based on file type and/or path */
-       rpmfcAttributes(fc, ftype, s);
+       rpmfcAttributes(fc, ix, ftype, s);
 
-       fc->fcolor[fc->ix] = fcolor;
+       fc->fcolor[ix] = fcolor;
 
        /* Add to file class dictionary and index array */
        if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE)) {
@@ -1016,7 +1138,7 @@ rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
            fc->fwhite++;
        }
        /* Pool id's start from 1, for headers we want it from 0 */
-       fc->fcdictx[fc->ix] = ftypeId - 1;
+       fc->fcdictx[ix] = ftypeId - 1;
     }
     rc = RPMRC_OK;
 
@@ -1087,6 +1209,18 @@ static struct DepMsg_s depMsgs[] = {
   { "Obsoletes",       { "%{?__find_obsoletes}", NULL, NULL, NULL },
        RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
        0, -1 },
+  { "Recommends",              { "%{?__find_recommends}", NULL, NULL, NULL },
+       RPMTAG_RECOMMENDNAME, RPMTAG_RECOMMENDVERSION, RPMTAG_RECOMMENDFLAGS,
+       0, -1 },
+  { "Suggests",        { "%{?__find_suggests}", NULL, NULL, NULL },
+       RPMTAG_SUGGESTNAME, RPMTAG_SUGGESTVERSION, RPMTAG_SUGGESTFLAGS,
+       0, -1 },
+  { "Supplements",     { "%{?__find_supplements}", NULL, NULL, NULL },
+       RPMTAG_SUPPLEMENTNAME, RPMTAG_SUPPLEMENTVERSION, RPMTAG_SUPPLEMENTFLAGS,
+       0, -1 },
+  { "Enhances",                { "%{?__find_enhances}", NULL, NULL, NULL },
+       RPMTAG_ENHANCENAME, RPMTAG_ENHANCEVERSION, RPMTAG_ENHANCEFLAGS,
+       0, -1 },
   { NULL,              { NULL, NULL, NULL, NULL },     0, 0, 0, 0, 0 }
 };
 
@@ -1094,7 +1228,7 @@ static DepMsg_t DepMsgs = depMsgs;
 
 /**
  */
-static void printDeps(Header h)
+static void printDeps(rpmfc fc)
 {
     DepMsg_t dm;
     rpmds ds = NULL;
@@ -1104,8 +1238,7 @@ static void printDeps(Header h)
 
     for (dm = DepMsgs; dm->msg != NULL; dm++) {
        if (dm->ntag != -1) {
-           rpmdsFree(ds);
-           ds = rpmdsNew(h, dm->ntag, 0);
+           ds = rpmfcDependencies(fc, dm->ntag);
        }
        if (dm->ftag == 0)
            continue;
@@ -1132,18 +1265,16 @@ static void printDeps(Header h)
        if (bingo)
            rpmlog(RPMLOG_NOTICE, "\n");
     }
-    rpmdsFree(ds);
 }
 
-static rpmRC rpmfcGenerateDependsHelper(const rpmSpec spec, Package pkg, rpmfi fi)
+static rpmRC rpmfcApplyExternal(rpmfc fc)
 {
     StringBuf sb_stdin = newStringBuf();
     rpmRC rc = RPMRC_OK;
 
     /* Create file manifest buffer to deliver to dependency finder. */
-    fi = rpmfiInit(fi, 0);
-    while (rpmfiNext(fi) >= 0)
-       appendLineStringBuf(sb_stdin, rpmfiFN(fi));
+    for (int i = 0; i < fc->nfiles; i++)
+       appendLineStringBuf(sb_stdin, fc->fn[i]);
 
     for (DepMsg_t dm = DepMsgs; dm->msg != NULL; dm++) {
        rpmTagVal tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
@@ -1152,14 +1283,20 @@ static rpmRC rpmfcGenerateDependsHelper(const rpmSpec spec, Package pkg, rpmfi f
        StringBuf sb_stdout = NULL;
        int failnonzero = (tag == RPMTAG_PROVIDEFLAGS);
 
-       switch(tag) {
+       switch (tag) {
        case RPMTAG_PROVIDEFLAGS:
-           if (!pkg->autoProv)
+           if (fc->skipProv)
                continue;
            tagflags = RPMSENSE_FIND_PROVIDES;
            break;
        case RPMTAG_REQUIREFLAGS:
-           if (!pkg->autoReq)
+       case RPMTAG_RECOMMENDFLAGS:
+       case RPMTAG_SUGGESTFLAGS:
+       case RPMTAG_SUPPLEMENTFLAGS:
+       case RPMTAG_ENHANCEFLAGS:
+       case RPMTAG_CONFLICTFLAGS:
+       case RPMTAG_OBSOLETEFLAGS:
+           if (fc->skipReq)
                continue;
            tagflags = RPMSENSE_FIND_REQUIRES;
            break;
@@ -1173,7 +1310,7 @@ static rpmRC rpmfcGenerateDependsHelper(const rpmSpec spec, Package pkg, rpmfi f
        free(s);
 
        if (rpmfcExec(dm->argv, sb_stdin, &sb_stdout,
-                       failnonzero, spec->buildRoot) == -1)
+                       failnonzero, fc->buildRoot) == -1)
            continue;
 
        if (sb_stdout == NULL) {
@@ -1183,7 +1320,7 @@ static rpmRC rpmfcGenerateDependsHelper(const rpmSpec spec, Package pkg, rpmfi f
        }
 
        /* Parse dependencies into header */
-       rc = parseRCPOT(spec, pkg, getStringBuf(sb_stdout), tag, 0, tagflags);
+       rc = parseRCPOT(NULL, fc->pkg, getStringBuf(sb_stdout), dm->ntag != -1 ? dm->ntag : RPMTAG_REQUIRENAME, 0, tagflags, addReqProvPkg, NULL);
        freeStringBuf(sb_stdout);
 
        if (rc) {
@@ -1197,11 +1334,26 @@ static rpmRC rpmfcGenerateDependsHelper(const rpmSpec spec, Package pkg, rpmfi f
     return rc;
 }
 
+rpmRC rpmfcApply(rpmfc fc)
+{
+    rpmRC rc;
+    /* If new-fangled dependency generation is disabled ... */
+    if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) {
+       /* ... then generate dependencies using %{__find_requires} et al. */
+       rpmlog(RPMLOG_WARNING,
+           _("Deprecated external dependency generator is used!\n"));
+       rc = rpmfcApplyExternal(fc);
+    } else {
+       /* ... otherwise generate per-file dependencies */
+       rc = rpmfcApplyInternal(fc);
+    }
+    return rc;
+}
+
 rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
 {
-    rpmfi fi = pkg->cpioList;
+    rpmfi fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD);
     rpmfc fc = NULL;
-    ARGV_t av = NULL;
     rpm_mode_t * fmode = NULL;
     int ac = rpmfiFC(fi);
     int genConfigDeps = 0;
@@ -1213,78 +1365,46 @@ rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
     if (ac <= 0)
        goto exit;
 
-    /* Skip packages that have dependency generation disabled. */
-    if (! (pkg->autoReq || pkg->autoProv))
-       goto exit;
-
-    /* If new-fangled dependency generation is disabled ... */
-    if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) {
-       /* ... then generate dependencies using %{__find_requires} et al. */
-       rc = rpmfcGenerateDependsHelper(spec, pkg, fi);
-       goto exit;
-    }
-
     /* Extract absolute file paths in argv format. */
-    av = xcalloc(ac+1, sizeof(*av));
     fmode = xcalloc(ac+1, sizeof(*fmode));
 
     fi = rpmfiInit(fi, 0);
     while ((idx = rpmfiNext(fi)) >= 0) {
        /* Does package have any %config files? */
        genConfigDeps |= (rpmfiFFlags(fi) & RPMFILE_CONFIG);
-
-       av[idx] = xstrdup(rpmfiFN(fi));
        fmode[idx] = rpmfiFMode(fi);
     }
-    av[ac] = NULL;
 
     fc = rpmfcCreate(spec->buildRoot, 0);
+    free(fc->pkg);
+    fc->pkg = pkg;
     fc->skipProv = !pkg->autoProv;
     fc->skipReq = !pkg->autoReq;
 
-    /* Copy (and delete) manually generated dependencies to dictionary. */
-    if (!fc->skipProv) {
-       rpmdsMerge(&fc->provides, pkg->provides);
-
-       headerDel(pkg->header, RPMTAG_PROVIDENAME);
-       headerDel(pkg->header, RPMTAG_PROVIDEVERSION);
-       headerDel(pkg->header, RPMTAG_PROVIDEFLAGS);
-
+    if (!fc->skipProv && genConfigDeps) {
        /* Add config dependency, Provides: config(N) = EVR */
-       if (genConfigDeps) {
-           rpmds ds = rpmdsSingleNS(RPMTAG_PROVIDENAME, "config",
-                              rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
-                              (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
-           rpmdsMerge(&fc->provides, ds);
-           rpmdsFree(ds);
-       }
+       rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_PROVIDENAME, "config",
+                                rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
+                                (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
+       rpmdsMerge(packageDependencies(pkg, RPMTAG_PROVIDENAME), ds);
+       rpmdsFree(ds);
     }
-
-    if (!fc->skipReq) {
-       rpmdsMerge(&fc->requires, pkg->requires);
-
-       headerDel(pkg->header, RPMTAG_REQUIRENAME);
-       headerDel(pkg->header, RPMTAG_REQUIREVERSION);
-       headerDel(pkg->header, RPMTAG_REQUIREFLAGS);
-
-       /* Add config dependency,  Requires: config(N) = EVR */
-       if (genConfigDeps) {
-           rpmds ds = rpmdsSingleNS(RPMTAG_REQUIRENAME, "config",
-                              rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
-                              (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
-           rpmdsMerge(&fc->requires, ds);
-           rpmdsFree(ds);
-       }
+    if (!fc->skipReq && genConfigDeps) {
+       rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_REQUIRENAME, "config",
+                                rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
+                                (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
+       rpmdsMerge(packageDependencies(pkg, RPMTAG_REQUIRENAME), ds);
+       rpmdsFree(ds);
     }
 
     /* Build file class dictionary. */
-    rc = rpmfcClassify(fc, av, fmode);
+    rc = rpmfcClassify(fc, pkg->dpaths, fmode);
     if ( rc != RPMRC_OK )
        goto exit;
 
     /* Build file/package dependency dictionary. */
     rc = rpmfcApply(fc);
-    if ( rc != RPMRC_OK )
+    if (rc != RPMRC_OK)
        goto exit;
 
     /* Add per-file colors(#files) */
@@ -1302,42 +1422,18 @@ rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
     /* Add per-file classes(#files) */
     headerPutUint32(pkg->header, RPMTAG_FILECLASS, fc->fcdictx, fc->nfiles);
 
-    /* Add Provides: */
-    if (!fc->skipProv) {
-       rpmds pi = rpmdsInit(fc->provides);
-       while (rpmdsNext(pi) >= 0) {
-           rpmsenseFlags flags = rpmdsFlags(pi);
-       
-           headerPutString(pkg->header, RPMTAG_PROVIDENAME, rpmdsN(pi));
-           headerPutString(pkg->header, RPMTAG_PROVIDEVERSION, rpmdsEVR(pi));
-           headerPutUint32(pkg->header, RPMTAG_PROVIDEFLAGS, &flags, 1);
-       }
-    }
-
-    /* Add Requires: */
-    if (!fc->skipReq) {
-       rpmds pi = rpmdsInit(fc->requires);
-       while (rpmdsNext(pi) >= 0) {
-           rpmsenseFlags flags = rpmdsFlags(pi);
-       
-           headerPutString(pkg->header, RPMTAG_REQUIRENAME, rpmdsN(pi));
-           headerPutString(pkg->header, RPMTAG_REQUIREVERSION, rpmdsEVR(pi));
-           headerPutUint32(pkg->header, RPMTAG_REQUIREFLAGS, &flags, 1);
-       }
-    }
-
     /* Add dependency dictionary(#dependencies) */
     if (rpmtdFromArgi(&td, RPMTAG_DEPENDSDICT, fc->ddictx)) {
        headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
-    }
 
-    /* Add per-file dependency (start,number) pairs (#files) */
-    if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSX, fc->fddictx)) {
-       headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
-    }
+       /* Add per-file dependency (start,number) pairs (#files) */
+       if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSX, fc->fddictx)) {
+           headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+       }
 
-    if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSN, fc->fddictn)) {
-       headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+       if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSN, fc->fddictn)) {
+           headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
+       }
     }
 
 
@@ -1350,12 +1446,14 @@ rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
        free(msg);
     }
 exit:
-    printDeps(pkg->header);
+    printDeps(fc);
 
     /* Clean up. */
+    if (fc)
+       fc->pkg = NULL;
     free(fmode);
     rpmfcFree(fc);
-    argvFree(av);
+    rpmfiFree(fi);
 
     return rc;
 }