Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / build / rpmfc.c
1 #include "system.h"
2
3 #include <errno.h>
4 #include <libgen.h>
5 #include <sys/select.h>
6 #include <sys/wait.h>
7 #include <signal.h>
8 #include <magic.h>
9 #include <regex.h>
10
11 #include <rpm/header.h>
12 #include <rpm/argv.h>
13 #include <rpm/rpmfc.h>
14 #include <rpm/rpmlog.h>
15 #include <rpm/rpmfileutil.h>
16 #include <rpm/rpmds.h>
17 #include <rpm/rpmfi.h>
18 #include <rpm/rpmstrpool.h>
19
20 #include "lib/rpmfi_internal.h"         /* rpmfiles stuff for now */
21 #include "build/rpmbuild_internal.h"
22
23 #include "debug.h"
24
25 struct matchRule {
26     regex_t *path;
27     regex_t *magic;
28     ARGV_t flags;
29 };
30
31 typedef struct rpmfcAttr_s {
32     char *name;
33     struct matchRule incl;
34     struct matchRule excl;
35 } * rpmfcAttr;
36
37 typedef struct {
38     int fileIx;
39     rpmds dep;
40 } rpmfcFileDep;
41
42 typedef struct {
43     rpmfcFileDep *data;
44     int size;
45     int alloced;
46 } rpmfcFileDeps;
47
48 /**
49  */
50 struct rpmfc_s {
51     Package pkg;
52     int nfiles;         /*!< no. of files */
53     int fknown;         /*!< no. of classified files */
54     int fwhite;         /*!< no. of "white" files */
55     int skipProv;       /*!< Don't auto-generate Provides:? */
56     int skipReq;        /*!< Don't auto-generate Requires:? */
57     char *buildRoot;    /*!< (Build) root dir */
58     size_t brlen;       /*!< rootDir length */
59
60     rpmfcAttr *atypes;  /*!< known file attribute types */
61
62     char ** fn;         /*!< (no. files) file names */
63     ARGV_t *fattrs;     /*!< (no. files) file attribute tokens */
64     rpm_color_t *fcolor;/*!< (no. files) file colors */
65     rpmsid *fcdictx;    /*!< (no. files) file class dictionary indices */
66     ARGI_t fddictx;     /*!< (no. files) file depends dictionary start */
67     ARGI_t fddictn;     /*!< (no. files) file depends dictionary no. entries */
68     ARGI_t ddictx;      /*!< (no. dependencies) file->dependency mapping */
69     rpmstrPool cdict;   /*!< file class dictionary */
70     rpmfcFileDeps fileDeps; /*!< file dependency mapping */
71
72     rpmstrPool pool;    /*!< general purpose string storage */
73 };
74
75 struct rpmfcTokens_s {
76     const char * token;
77     rpm_color_t colors;
78 };  
79
80 static int regMatch(regex_t *reg, const char *val)
81 {
82     return (reg && regexec(reg, val, 0, NULL, 0) == 0);
83 }
84
85 static regex_t * regFree(regex_t *reg)
86 {
87     if (reg) {
88         regfree(reg);
89         free(reg);
90     }
91     return NULL;
92 }
93
94 static void ruleFree(struct matchRule *rule)
95 {
96     regFree(rule->path);
97     regFree(rule->magic);
98     argvFree(rule->flags);
99 }
100
101 static char *rpmfcAttrMacroV(const char *arg, va_list args)
102 {
103     const char *s;
104     int blen;
105     char *buf = NULL, *obuf;
106     char *pe;
107     va_list args2;
108
109     if (arg == NULL || rstreq(arg, ""))
110         return NULL;
111
112     va_copy(args2, args);
113     blen = sizeof("%{?_") - 1;
114     for (s = arg; s != NULL; s = va_arg(args, const char *)) {
115         blen += sizeof("_") - 1 + strlen(s);
116     }
117     blen += sizeof("}") - 1;
118
119     buf = xmalloc(blen + 1);
120
121     pe = buf;
122     pe = stpcpy(pe, "%{?_");
123     for (s = arg; s != NULL; s = va_arg(args2, const char *)) {
124         *pe++ = '_';
125         pe = stpcpy(pe, s);
126     }
127     va_end(args2);
128     *pe++ = '}';
129     *pe = '\0';
130
131     obuf = rpmExpand(buf, NULL);
132     free(buf);
133
134     return rstreq(obuf, "") ? _free(obuf) : obuf;
135 }
136
137 static char *rpmfcAttrMacro(const char *arg, ...)
138 {
139     va_list args;
140     char *s;
141
142     va_start(args, arg);
143     s = rpmfcAttrMacroV(arg, args);
144     va_end(args);
145     return s;
146 }
147
148 static regex_t *rpmfcAttrReg(const char *arg, ...)
149 {
150     regex_t *reg = NULL;
151     char *pattern;
152     va_list args;
153
154     va_start(args, arg);
155     pattern = rpmfcAttrMacroV(arg, args);
156     va_end(args);
157     if (pattern) {
158         reg = xcalloc(1, sizeof(*reg));
159         if (regcomp(reg, pattern, REG_EXTENDED) != 0) { 
160             rpmlog(RPMLOG_WARNING, _("Ignoring invalid regex %s\n"), pattern);
161             reg = _free(reg);
162         }
163         rfree(pattern);
164     }
165     return reg;
166 }
167
168 static rpmfcAttr rpmfcAttrNew(const char *name)
169 {
170     rpmfcAttr attr = xcalloc(1, sizeof(*attr));
171     struct matchRule *rules[] = { &attr->incl, &attr->excl, NULL };
172
173     attr->name = xstrdup(name);
174     for (struct matchRule **rule = rules; rule && *rule; rule++) {
175         const char *prefix = (*rule == &attr->incl) ? NULL : "exclude";
176         char *flags;
177
178         if (prefix) {
179             flags = rpmfcAttrMacro(name, prefix, "flags", NULL);
180
181             (*rule)->path = rpmfcAttrReg(name, prefix, "path", NULL);
182             (*rule)->magic = rpmfcAttrReg(name, prefix, "magic", NULL);
183         } else {
184             flags = rpmfcAttrMacro(name, "flags", NULL);
185
186             (*rule)->path = rpmfcAttrReg(name, "path", NULL);
187             (*rule)->magic = rpmfcAttrReg(name, "magic", NULL);
188         }
189         (*rule)->flags = argvSplitString(flags, ",", ARGV_SKIPEMPTY);
190         argvSort((*rule)->flags, NULL);
191
192         free(flags);
193     }
194
195     return attr;
196 }
197
198 static rpmfcAttr rpmfcAttrFree(rpmfcAttr attr)
199 {
200     if (attr) {
201         ruleFree(&attr->incl);
202         ruleFree(&attr->excl);
203         rfree(attr->name);
204         rfree(attr);
205     }
206     return NULL;
207 }
208
209 /**
210  */
211 static int rpmfcExpandAppend(ARGV_t * argvp, ARGV_const_t av)
212 {
213     ARGV_t argv = *argvp;
214     int argc = argvCount(argv);
215     int ac = argvCount(av);
216     int i;
217
218     argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
219     for (i = 0; i < ac; i++)
220         argv[argc + i] = rpmExpand(av[i], NULL);
221     argv[argc + ac] = NULL;
222     *argvp = argv;
223     return 0;
224 }
225
226 static rpmds rpmdsSingleNS(rpmstrPool pool,
227                         rpmTagVal tagN, const char *namespace,
228                         const char * N, const char * EVR, rpmsenseFlags Flags)
229 {
230     rpmds ds = NULL;
231     if (namespace) {
232         char *NSN = rpmExpand(namespace, "(", N, ")", NULL);
233         ds = rpmdsSinglePool(pool, tagN, NSN, EVR, Flags);
234         free(NSN);
235     } else {
236         ds = rpmdsSinglePool(pool, tagN, N, EVR, Flags);
237     }
238     return ds;
239 }
240
241 #define max(x,y) ((x) > (y) ? (x) : (y))
242
243 /** \ingroup rpmbuild
244  * Return output from helper script.
245  * @todo Use poll(2) rather than select(2), if available.
246  * @param argv          program and arguments to run
247  * @param writePtr      bytes to feed to script on stdin (or NULL)
248  * @param writeBytesLeft no. of bytes to feed to script on stdin
249  * @param failNonZero   is script failure an error?
250  * @param buildRoot     buildRoot directory (or NULL)
251  * @return              buffered stdout from script, NULL on error
252  */     
253 static StringBuf getOutputFrom(ARGV_t argv,
254                         const char * writePtr, size_t writeBytesLeft,
255                         int failNonZero, const char *buildRoot)
256 {
257     pid_t child, reaped;
258     int toProg[2] = { -1, -1 };
259     int fromProg[2] = { -1, -1 };
260     int status;
261     StringBuf readBuff;
262     int myerrno = 0;
263     int ret = 1; /* assume failure */
264
265     if (pipe(toProg) < 0 || pipe(fromProg) < 0) {
266         rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
267         return NULL;
268     }
269     
270     child = fork();
271     if (child == 0) {
272         close(toProg[1]);
273         close(fromProg[0]);
274         
275         dup2(toProg[0], STDIN_FILENO);   /* Make stdin the in pipe */
276         close(toProg[0]);
277
278         dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
279         close(fromProg[1]);
280
281         rpmlog(RPMLOG_DEBUG, "\texecv(%s) pid %d\n",
282                         argv[0], (unsigned)getpid());
283
284         if (buildRoot)
285             setenv("RPM_BUILD_ROOT", buildRoot, 1);
286
287         unsetenv("MALLOC_CHECK_");
288         execvp(argv[0], (char *const *)argv);
289         rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
290                 argv[0], strerror(errno));
291         _exit(EXIT_FAILURE);
292     }
293     if (child < 0) {
294         rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
295                 argv[0], strerror(errno));
296         return NULL;
297     }
298
299     close(toProg[0]);
300     close(fromProg[1]);
301
302     readBuff = newStringBuf();
303
304     while (1) {
305         fd_set ibits, obits;
306         int nfd = 0;
307         ssize_t iorc;
308         char buf[BUFSIZ+1];
309
310         FD_ZERO(&ibits);
311         FD_ZERO(&obits);
312
313         FD_SET(fromProg[0], &ibits);
314         nfd = max(nfd, fromProg[0]);
315
316         if (writeBytesLeft > 0) {
317             FD_SET(toProg[1], &obits);
318             nfd = max(nfd, toProg[1]);
319         } else if (toProg[1] >= 0) {
320             /* Close write-side pipe to notify child on EOF */
321             close(toProg[1]);
322             toProg[1] = -1;
323         }
324
325         do {
326             iorc = select(nfd + 1, &ibits, &obits, NULL, NULL);
327         } while (iorc == -1 && errno == EINTR);
328
329         if (iorc < 0) {
330             myerrno = errno;
331             break;
332         }
333
334         /* Write data to child */
335         if (writeBytesLeft > 0 && FD_ISSET(toProg[1], &obits)) {
336             size_t nb = (1024 < writeBytesLeft) ? 1024 : writeBytesLeft;
337             do {
338                 iorc = write(toProg[1], writePtr, nb);
339             } while (iorc == -1 && errno == EINTR);
340
341             if (iorc < 0) {
342                 myerrno = errno;
343                 break;
344             }
345             writeBytesLeft -= iorc;
346             writePtr += iorc;
347         }
348         
349         /* Read when we get data back from the child */
350         if (FD_ISSET(fromProg[0], &ibits)) {
351             do {
352                 iorc = read(fromProg[0], buf, sizeof(buf)-1);
353             } while (iorc == -1 && errno == EINTR);
354
355             if (iorc == 0) break; /* EOF, we're done */
356             if (iorc < 0) {
357                 myerrno = errno;
358                 break;
359             }
360             buf[iorc] = '\0';
361             appendStringBuf(readBuff, buf);
362         }
363     }
364
365     /* Clean up */
366     if (toProg[1] >= 0)
367         close(toProg[1]);
368     if (fromProg[0] >= 0)
369         close(fromProg[0]);
370
371     /* Collect status from prog */
372     reaped = waitpid(child, &status, 0);
373     rpmlog(RPMLOG_DEBUG, "\twaitpid(%d) rc %d status %x\n",
374         (unsigned)child, (unsigned)reaped, status);
375
376     if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
377         rpmlog(RPMLOG_ERR, _("%s failed: %x\n"), argv[0], status);
378         goto exit;
379     }
380     if (writeBytesLeft || myerrno) {
381         rpmlog(RPMLOG_ERR, _("failed to write all data to %s: %s\n"), 
382                 argv[0], strerror(myerrno));
383         goto exit;
384     }
385     ret = 0;
386
387 exit:
388     if (ret) {
389         readBuff = freeStringBuf(readBuff);
390     }
391
392     return readBuff;
393 }
394
395 int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp,
396                 int failnonzero, const char *buildRoot)
397 {
398     char * s = NULL;
399     ARGV_t xav = NULL;
400     ARGV_t pav = NULL;
401     int pac = 0;
402     int ec = -1;
403     StringBuf sb = NULL;
404     const char * buf_stdin = NULL;
405     size_t buf_stdin_len = 0;
406
407     if (sb_stdoutp)
408         *sb_stdoutp = NULL;
409     if (!(av && *av))
410         goto exit;
411
412     /* Find path to executable with (possible) args. */
413     s = rpmExpand(av[0], NULL);
414     if (!(s && *s))
415         goto exit;
416
417     /* Parse args buried within expanded executable. */
418     if (!(poptParseArgvString(s, &pac, (const char ***)&pav) == 0 && pac > 0 && pav != NULL))
419         goto exit;
420
421     /* Build argv, appending args to the executable args. */
422     argvAppend(&xav, pav);
423     if (av[1])
424         rpmfcExpandAppend(&xav, av + 1);
425
426     if (sb_stdin != NULL) {
427         buf_stdin = getStringBuf(sb_stdin);
428         buf_stdin_len = strlen(buf_stdin);
429     }
430
431     if (_rpmfc_debug) {
432         char *cmd = argvJoin(xav, " ");
433         rpmlog(RPMLOG_DEBUG, "Executing %s on %s\n", cmd, buf_stdin);
434         free(cmd);
435     }
436
437     /* Read output from exec'd helper. */
438     sb = getOutputFrom(xav, buf_stdin, buf_stdin_len, failnonzero, buildRoot);
439
440     if (sb_stdoutp != NULL) {
441         *sb_stdoutp = sb;
442         sb = NULL;      /* XXX don't free */
443     }
444
445     ec = 0;
446
447 exit:
448     freeStringBuf(sb);
449     argvFree(xav);
450     free(pav);  /* XXX popt mallocs in single blob. */
451     free(s);
452     return ec;
453 }
454
455 static void argvAddUniq(ARGV_t * argvp, const char * key)
456 {
457     if (argvSearch(*argvp, key, NULL) == NULL) {
458         argvAdd(argvp, key);
459         argvSort(*argvp, NULL);
460     }
461 }
462
463 #define hasAttr(_a, _n) (argvSearch((_a), (_n), NULL) != NULL)
464
465 static void rpmfcAddFileDep(rpmfcFileDeps *fileDeps, rpmds ds, int ix)
466 {
467     if (fileDeps->size == fileDeps->alloced) {
468         fileDeps->alloced <<= 2;
469         fileDeps->data  = xrealloc(fileDeps->data,
470             fileDeps->alloced * sizeof(fileDeps->data[0]));
471     }
472     fileDeps->data[fileDeps->size].fileIx = ix;
473     fileDeps->data[fileDeps->size++].dep = ds;
474 }
475
476 static ARGV_t runCmd(const char *nsdep, const char *depname,
477                      const char *buildRoot, const char *fn)
478 {
479     ARGV_t output = NULL;
480     char *buf = NULL;
481     char *mname = rstrscat(NULL, "__", nsdep, "_", depname, NULL);
482
483     rasprintf(&buf, "%%{?%s:%%{%s} %%{?%s_opts}}", mname, mname, mname);
484     if (!rstreq(buf, "")) {
485         ARGV_t av = NULL;
486         StringBuf sb_stdout = NULL;
487         StringBuf sb_stdin = newStringBuf();
488         argvAdd(&av, buf);
489
490         appendLineStringBuf(sb_stdin, fn);
491         if (rpmfcExec(av, sb_stdin, &sb_stdout, 0, buildRoot) == 0) {
492             argvSplit(&output, getStringBuf(sb_stdout), "\n\r");
493         }
494
495         argvFree(av);
496         freeStringBuf(sb_stdin);
497         freeStringBuf(sb_stdout);
498     }
499     free(buf);
500     free(mname);
501     return output;
502 }
503
504 struct addReqProvDataFc {
505     rpmfc fc;
506     const char *namespace;
507     regex_t *exclude;
508 };
509
510 static rpmRC addReqProvFc(void *cbdata, rpmTagVal tagN,
511                           const char * N, const char * EVR, rpmsenseFlags Flags,
512                           int index)
513 {
514     struct addReqProvDataFc *data = cbdata;
515     rpmfc fc = data->fc;
516     const char *namespace = data->namespace;
517     regex_t *exclude = data->exclude;
518
519     rpmds ds = rpmdsSingleNS(fc->pool, tagN, namespace, N, EVR, Flags);
520     /* Add to package and file dependencies unless filtered */
521     if (regMatch(exclude, rpmdsDNEVR(ds)+2) == 0)
522         rpmfcAddFileDep(&fc->fileDeps, ds, index);
523
524     return RPMRC_OK;
525 }
526
527 /**
528  * Run per-interpreter dependency helper.
529  * @param fc            file classifier
530  * @param ix            file index
531  * @param nsdep         class name for interpreter (e.g. "perl")
532  * @param depname       "provides" or "requires"
533  * @param dsContext     RPMSENSE_FIND_PROVIDES or RPMSENSE_FIND_REQUIRES
534  * @param tagN          RPMTAG_PROVIDENAME or RPMTAG_REQUIRENAME
535  * @return              0 on success
536  */
537 static int rpmfcHelper(rpmfc fc, int ix,
538                        const char *nsdep, const char *depname,
539                        rpmsenseFlags dsContext, rpmTagVal tagN)
540 {
541     ARGV_t pav = NULL;
542     const char * fn = fc->fn[ix];
543     char *namespace = NULL;
544     int pac;
545     int rc = 0;
546     regex_t *exclude = NULL;
547     regex_t *exclude_from = NULL;
548     regex_t *global_exclude_from = NULL;
549
550     /* If the entire path is filtered out, there's nothing more to do */
551     exclude_from = rpmfcAttrReg(depname, "exclude", "from", NULL);
552     if (regMatch(exclude_from, fn+fc->brlen))
553         goto exit;
554
555     global_exclude_from = rpmfcAttrReg("global", depname, "exclude", "from", NULL);
556     if (regMatch(global_exclude_from, fn+fc->brlen))
557         goto exit;
558
559     pav = runCmd(nsdep, depname, fc->buildRoot, fn);
560     if (pav == NULL)
561         goto exit;
562
563     pac = argvCount(pav);
564     namespace = rpmfcAttrMacro(nsdep, "namespace", NULL);
565     exclude = rpmfcAttrReg(depname, "exclude", NULL);
566
567     struct addReqProvDataFc data;
568     data.fc = fc;
569     data.namespace = namespace;
570     data.exclude = exclude;
571
572     for (int i = 0; i < pac; i++) {
573         if (parseRCPOT(NULL, fc->pkg, pav[i], tagN, ix, dsContext, addReqProvFc, &data))
574             rc++;
575     }
576
577     argvFree(pav);
578     regFree(exclude);
579     free(namespace);
580
581 exit:
582     regFree(exclude_from);
583     regFree(global_exclude_from);
584     return rc;
585 }
586
587 /* Only used for elf coloring and controlling RPMTAG_FILECLASS inclusion now */
588 static const struct rpmfcTokens_s rpmfcTokens[] = {
589   { "directory",                RPMFC_INCLUDE },
590
591   { "ELF 32-bit",               RPMFC_ELF32|RPMFC_INCLUDE },
592   { "ELF 64-bit",               RPMFC_ELF64|RPMFC_INCLUDE },
593
594   { "troff or preprocessor input",      RPMFC_INCLUDE },
595   { "GNU Info",                 RPMFC_INCLUDE },
596
597   { "perl ",                    RPMFC_INCLUDE },
598   { "Perl5 module source text", RPMFC_INCLUDE },
599   { "python ",                  RPMFC_INCLUDE },
600
601   { "libtool library ",         RPMFC_INCLUDE },
602   { "pkgconfig ",               RPMFC_INCLUDE },
603
604   { "Objective caml ",          RPMFC_INCLUDE },
605   { "Mono/.Net assembly",       RPMFC_INCLUDE },
606
607   { "current ar archive",       RPMFC_INCLUDE },
608   { "Zip archive data",         RPMFC_INCLUDE },
609   { "tar archive",              RPMFC_INCLUDE },
610   { "cpio archive",             RPMFC_INCLUDE },
611   { "RPM v3",                   RPMFC_INCLUDE },
612   { "RPM v4",                   RPMFC_INCLUDE },
613
614   { " image",                   RPMFC_INCLUDE },
615   { " font",                    RPMFC_INCLUDE },
616   { " Font",                    RPMFC_INCLUDE },
617
618   { " commands",                RPMFC_INCLUDE },
619   { " script",                  RPMFC_INCLUDE },
620
621   { "empty",                    RPMFC_INCLUDE },
622
623   { "HTML",                     RPMFC_INCLUDE },
624   { "SGML",                     RPMFC_INCLUDE },
625   { "XML",                      RPMFC_INCLUDE },
626
627   { " source",                  RPMFC_INCLUDE },
628   { "GLS_BINARY_LSB_FIRST",     RPMFC_INCLUDE },
629   { " DB ",                     RPMFC_INCLUDE },
630
631   { " text",                    RPMFC_INCLUDE },
632
633   { NULL,                       RPMFC_BLACK }
634 };
635
636 static void argvAddTokens(ARGV_t *argv, const char *tnames)
637 {
638     if (tnames) {
639         ARGV_t tokens = NULL;
640         argvSplit(&tokens, tnames, ",");
641         for (ARGV_t token = tokens; token && *token; token++)
642             argvAddUniq(argv, *token);
643         argvFree(tokens);
644     }
645 }
646
647 static int matches(const struct matchRule *rule,
648                    const char *ftype, const char *path, int executable)
649 {
650     if (!executable && hasAttr(rule->flags, "exeonly"))
651         return 0;
652     if (rule->magic && rule->path && hasAttr(rule->flags, "magic_and_path")) {
653         return (regMatch(rule->magic, ftype) && regMatch(rule->path, path));
654     } else {
655         return (regMatch(rule->magic, ftype) || regMatch(rule->path, path));
656     }
657 }
658
659 static void rpmfcAttributes(rpmfc fc, int ix, const char *ftype, const char *fullpath)
660 {
661     const char *path = fullpath + fc->brlen;
662     int is_executable = 0;
663     struct stat st;
664     if (stat(fullpath, &st) == 0) {
665         is_executable = (S_ISREG(st.st_mode)) &&
666                         (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
667     }
668
669     for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++) {
670         /* Filter out excludes */
671         if (matches(&(*attr)->excl, ftype, path, is_executable))
672             continue;
673
674         /* Add attributes on libmagic type & path pattern matches */
675         if (matches(&(*attr)->incl, ftype, path, is_executable))
676             argvAddTokens(&fc->fattrs[ix], (*attr)->name);
677     }
678 }
679
680 /* Return color for a given libmagic classification string */
681 static rpm_color_t rpmfcColor(const char * fmstr)
682 {
683     rpmfcToken fct;
684     rpm_color_t fcolor = RPMFC_BLACK;
685
686     for (fct = rpmfcTokens; fct->token != NULL; fct++) {
687         if (strstr(fmstr, fct->token) == NULL)
688             continue;
689
690         fcolor |= fct->colors;
691         if (fcolor & RPMFC_INCLUDE)
692             break;
693     }
694
695     return fcolor;
696 }
697
698 void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
699 {
700     int ndx;
701     int dx;
702     int fx;
703
704     if (fp == NULL) fp = stderr;
705
706     if (msg)
707         fprintf(fp, "===================================== %s\n", msg);
708
709     if (fc)
710     for (fx = 0; fx < fc->nfiles; fx++) {
711         fprintf(fp, "%3d %s", fx, fc->fn[fx]);
712         if (_rpmfc_debug) {
713             rpmsid cx = fc->fcdictx[fx] + 1; /* id's are one off */
714             rpm_color_t fcolor = fc->fcolor[fx];
715             ARGV_t fattrs = fc->fattrs[fx];
716
717             if (fcolor != RPMFC_BLACK)
718                 fprintf(fp, "\t0x%x", fc->fcolor[fx]);
719             else
720                 fprintf(fp, "\t%s", rpmstrPoolStr(fc->cdict, cx));
721             if (fattrs) {
722                 char *attrs = argvJoin(fattrs, ",");
723                 fprintf(fp, " [%s]", attrs);
724                 free(attrs);
725             } else {
726                 fprintf(fp, " [none]");
727             }
728         }
729         fprintf(fp, "\n");
730
731         if (fc->fddictx == NULL || fc->fddictn == NULL)
732             continue;
733
734 assert(fx < fc->fddictx->nvals);
735         dx = fc->fddictx->vals[fx];
736 assert(fx < fc->fddictn->nvals);
737         ndx = fc->fddictn->vals[fx];
738
739         while (ndx-- > 0) {
740             const char * depval;
741             unsigned char deptype;
742             unsigned ix;
743             rpmds ds;
744
745             ix = fc->ddictx->vals[dx++];
746             deptype = ((ix >> 24) & 0xff);
747             ix &= 0x00ffffff;
748             depval = NULL;
749             ds = rpmfcDependencies(fc, rpmdsDToTagN(deptype));
750             (void) rpmdsSetIx(ds, ix-1);
751             if (rpmdsNext(ds) >= 0)
752                 depval = rpmdsDNEVR(ds);
753             if (depval)
754                 fprintf(fp, "\t%s\n", depval);
755         }
756     }
757 }
758
759 rpmfc rpmfcFree(rpmfc fc)
760 {
761     if (fc) {
762         for (rpmfcAttr *attr = fc->atypes; attr && *attr; attr++)
763             rpmfcAttrFree(*attr);
764         free(fc->atypes);
765         free(fc->buildRoot);
766         for (int i = 0; i < fc->nfiles; i++) {
767             free(fc->fn[i]);
768             argvFree(fc->fattrs[i]);
769         }
770         free(fc->fn);
771         free(fc->fattrs);
772         free(fc->fcolor);
773         free(fc->fcdictx);
774         free(fc->pkg);
775         argiFree(fc->fddictx);
776         argiFree(fc->fddictn);
777         argiFree(fc->ddictx);
778
779         for (int i = 0; i < fc->fileDeps.size; i++) {
780             rpmdsFree(fc->fileDeps.data[i].dep);
781         }
782         free(fc->fileDeps.data);
783
784         rpmstrPoolFree(fc->cdict);
785
786         rpmstrPoolFree(fc->pool);
787         memset(fc, 0, sizeof(*fc)); /* trash and burn */
788         free(fc);
789     }
790     return NULL;
791 }
792
793 rpmfc rpmfcCreate(const char *buildRoot, rpmFlags flags)
794 {
795     rpmfc fc = xcalloc(1, sizeof(*fc));
796     if (buildRoot) {
797         fc->buildRoot = xstrdup(buildRoot);
798         fc->brlen = strlen(buildRoot);
799     }
800     fc->pool = rpmstrPoolCreate();
801     fc->pkg = xcalloc(1, sizeof(*fc->pkg));
802     fc->fileDeps.alloced = 10;
803     fc->fileDeps.data = xmalloc(fc->fileDeps.alloced *
804         sizeof(fc->fileDeps.data[0]));
805     return fc;
806 }
807
808 rpmfc rpmfcNew(void)
809 {
810     return rpmfcCreate(NULL, 0);
811 }
812
813 rpmds rpmfcDependencies(rpmfc fc, rpmTagVal tag)
814 {
815     if (fc) {
816         return *packageDependencies(fc->pkg, tag);
817     }
818     return NULL;
819 }
820
821 rpmds rpmfcProvides(rpmfc fc)
822 {
823     return rpmfcDependencies(fc, RPMTAG_PROVIDENAME);
824 }
825
826 rpmds rpmfcRequires(rpmfc fc)
827 {
828     return rpmfcDependencies(fc, RPMTAG_REQUIRENAME);
829 }
830
831 rpmds rpmfcRecommends(rpmfc fc)
832 {
833     return rpmfcDependencies(fc, RPMTAG_RECOMMENDNAME);
834 }
835
836 rpmds rpmfcSuggests(rpmfc fc)
837 {
838     return rpmfcDependencies(fc, RPMTAG_SUGGESTNAME);
839 }
840
841 rpmds rpmfcSupplements(rpmfc fc)
842 {
843     return rpmfcDependencies(fc, RPMTAG_SUPPLEMENTNAME);
844 }
845
846 rpmds rpmfcEnhances(rpmfc fc)
847 {
848     return rpmfcDependencies(fc, RPMTAG_ENHANCENAME);
849 }
850
851 rpmds rpmfcConflicts(rpmfc fc)
852 {
853     return rpmfcDependencies(fc, RPMTAG_CONFLICTNAME);
854 }
855
856 rpmds rpmfcObsoletes(rpmfc fc)
857 {
858     return rpmfcDependencies(fc, RPMTAG_OBSOLETENAME);
859 }
860
861
862 /* Versioned deps are less than unversioned deps */
863 static int cmpVerDeps(const void *a, const void *b)
864 {
865     rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
866     rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
867
868     int aIsVersioned = rpmdsFlags(fDepA->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
869     int bIsVersioned = rpmdsFlags(fDepB->dep) & RPMSENSE_SENSEMASK ? 1 : 0;
870
871     return bIsVersioned - aIsVersioned;
872 }
873
874 /* Sort by index */
875 static int cmpIndexDeps(const void *a, const void *b)
876 {
877     rpmfcFileDep *fDepA = (rpmfcFileDep *) a;
878     rpmfcFileDep *fDepB = (rpmfcFileDep *) b;
879
880     return fDepA->fileIx - fDepB->fileIx;
881 }
882
883 /*
884  * Remove unversioned deps if corresponding versioned deps exist but only
885  * if the versioned dependency has the same type and the same color as the versioned.
886  */
887 static void rpmfcNormalizeFDeps(rpmfc fc)
888 {
889     rpmstrPool versionedDeps = rpmstrPoolCreate();
890     rpmfcFileDep *normalizedFDeps = xmalloc(fc->fileDeps.size *
891         sizeof(normalizedFDeps[0]));
892     int ix = 0;
893     char *depStr;
894
895     /* Sort. Versioned dependencies first */
896     qsort(fc->fileDeps.data, fc->fileDeps.size, sizeof(fc->fileDeps.data[0]),
897         cmpVerDeps);
898
899     for (int i = 0; i < fc->fileDeps.size; i++) {
900         switch (rpmdsTagN(fc->fileDeps.data[i].dep)) {
901         case RPMTAG_REQUIRENAME:
902         case RPMTAG_RECOMMENDNAME:
903         case RPMTAG_SUGGESTNAME:
904             rasprintf(&depStr, "%08x_%c_%s",
905                 fc->fcolor[fc->fileDeps.data[i].fileIx],
906                 rpmdsD(fc->fileDeps.data[i].dep),
907                 rpmdsN(fc->fileDeps.data[i].dep));
908
909             if (rpmdsFlags(fc->fileDeps.data[i].dep) & RPMSENSE_SENSEMASK) {
910                 /* preserve versioned require dependency */
911                 normalizedFDeps[ix++] = fc->fileDeps.data[i];
912                 rpmstrPoolId(versionedDeps, depStr, 1);
913             } else if (!rpmstrPoolId(versionedDeps, depStr, 0)) {
914                 /* preserve unversioned require dep only if versioned dep doesn't exist */
915                     normalizedFDeps[ix++] =fc-> fileDeps.data[i];
916             } else {
917                 rpmdsFree(fc->fileDeps.data[i].dep);
918             }
919             free(depStr);
920             break;
921         default:
922             /* Preserve all non-require dependencies */
923             normalizedFDeps[ix++] = fc->fileDeps.data[i];
924             break;
925         }
926     }
927     rpmstrPoolFree(versionedDeps);
928
929     free(fc->fileDeps.data);
930     fc->fileDeps.data = normalizedFDeps;
931     fc->fileDeps.size = ix;
932 }
933
934 static rpmRC rpmfcApplyInternal(rpmfc fc)
935 {
936     rpmds ds, * dsp;
937     int previx;
938     unsigned int val;
939     int dix;
940     int ix;
941
942     /* Generate package and per-file dependencies. */
943     for (ix = 0; ix < fc->nfiles && fc->fn[ix] != NULL; ix++) {
944         for (ARGV_t fattr = fc->fattrs[ix]; fattr && *fattr; fattr++) {
945             if (!fc->skipProv) {
946                 rpmfcHelper(fc, ix, *fattr, "provides",
947                             RPMSENSE_FIND_PROVIDES, RPMTAG_PROVIDENAME);
948             }
949             if (!fc->skipReq) {
950                 rpmfcHelper(fc, ix, *fattr, "requires",
951                             RPMSENSE_FIND_REQUIRES, RPMTAG_REQUIRENAME);
952                 rpmfcHelper(fc, ix, *fattr, "recommends",
953                             RPMSENSE_FIND_REQUIRES, RPMTAG_RECOMMENDNAME);
954                 rpmfcHelper(fc, ix, *fattr, "suggests",
955                             RPMSENSE_FIND_REQUIRES, RPMTAG_SUGGESTNAME);
956                 rpmfcHelper(fc, ix, *fattr, "supplements",
957                             RPMSENSE_FIND_REQUIRES, RPMTAG_SUPPLEMENTNAME);
958                 rpmfcHelper(fc, ix, *fattr, "enhances",
959                             RPMSENSE_FIND_REQUIRES, RPMTAG_ENHANCENAME);
960                 rpmfcHelper(fc, ix, *fattr, "conflicts",
961                             RPMSENSE_FIND_REQUIRES, RPMTAG_CONFLICTNAME);
962                 rpmfcHelper(fc, ix, *fattr, "obsoletes",
963                             RPMSENSE_FIND_REQUIRES, RPMTAG_OBSOLETENAME);
964             }
965         }
966     }
967     /* No more additions after this, freeze pool to minimize memory use */
968
969     rpmfcNormalizeFDeps(fc);
970     for (int i = 0; i < fc->fileDeps.size; i++) {
971         ds = fc->fileDeps.data[i].dep;
972         rpmdsMerge(packageDependencies(fc->pkg, rpmdsTagN(ds)), ds);
973     }
974
975     /* Sort by index */
976     qsort(fc->fileDeps.data, fc->fileDeps.size,
977         sizeof(fc->fileDeps.data[0]), cmpIndexDeps);
978
979     /* Generate per-file indices into package dependencies. */
980     previx = -1;
981     for (int i = 0; i < fc->fileDeps.size; i++) {
982         ds = fc->fileDeps.data[i].dep;
983         ix = fc->fileDeps.data[i].fileIx;
984         dsp = packageDependencies(fc->pkg, rpmdsTagN(ds));
985         dix = rpmdsFind(*dsp, ds);
986         if (dix < 0)
987             continue;
988
989         val = (rpmdsD(ds) << 24) | (dix & 0x00ffffff);
990         argiAdd(&fc->ddictx, -1, val);
991
992         if (previx != ix) {
993             previx = ix;
994             argiAdd(&fc->fddictx, ix, argiCount(fc->ddictx)-1);
995         }
996         if (fc->fddictn && fc->fddictn->vals)
997             fc->fddictn->vals[ix]++;
998
999     }
1000     return RPMRC_OK;
1001 }
1002
1003 static int initAttrs(rpmfc fc)
1004 {
1005     ARGV_t files = NULL;
1006     char * attrPath = rpmExpand("%{_fileattrsdir}/*.attr", NULL);
1007     int nattrs = 0;
1008
1009     /* Discover known attributes from pathnames + initialize them */
1010     if (rpmGlob(attrPath, NULL, &files) == 0) {
1011         nattrs = argvCount(files);
1012         fc->atypes = xcalloc(nattrs + 1, sizeof(*fc->atypes));
1013         for (int i = 0; i < nattrs; i++) {
1014             char *bn = basename(files[i]);
1015             bn[strlen(bn)-strlen(".attr")] = '\0';
1016             fc->atypes[i] = rpmfcAttrNew(bn);
1017         }
1018         fc->atypes[nattrs] = NULL;
1019         argvFree(files);
1020     }
1021     free(attrPath);
1022     return nattrs;
1023 }
1024
1025 rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
1026 {
1027     int msflags = MAGIC_CHECK | MAGIC_COMPRESS | MAGIC_NO_CHECK_TOKENS;
1028     magic_t ms = NULL;
1029     rpmRC rc = RPMRC_FAIL;
1030
1031     if (fc == NULL) {
1032         rpmlog(RPMLOG_ERR, _("Empty file classifier\n"));
1033         goto exit;
1034     }
1035
1036     /* It is OK when we have no files to classify. */
1037     if (argv == NULL)
1038         return RPMRC_OK;
1039
1040     if (initAttrs(fc) < 1) {
1041         rpmlog(RPMLOG_ERR, _("No file attributes configured\n"));
1042         goto exit;
1043     }
1044
1045     fc->nfiles = argvCount(argv);
1046     fc->fn = xcalloc(fc->nfiles, sizeof(*fc->fn));
1047     fc->fattrs = xcalloc(fc->nfiles, sizeof(*fc->fattrs));
1048     fc->fcolor = xcalloc(fc->nfiles, sizeof(*fc->fcolor));
1049     fc->fcdictx = xcalloc(fc->nfiles, sizeof(*fc->fcdictx));
1050
1051     /* Initialize the per-file dictionary indices. */
1052     argiAdd(&fc->fddictx, fc->nfiles-1, 0);
1053     argiAdd(&fc->fddictn, fc->nfiles-1, 0);
1054
1055     /* Build (sorted) file class dictionary. */
1056     fc->cdict = rpmstrPoolCreate();
1057
1058     ms = magic_open(msflags);
1059     if (ms == NULL) {
1060         rpmlog(RPMLOG_ERR, _("magic_open(0x%x) failed: %s\n"),
1061                 msflags, strerror(errno));
1062         goto exit;
1063     }
1064
1065     if (magic_load(ms, NULL) == -1) {
1066         rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(ms));
1067         goto exit;
1068     }
1069
1070     for (int ix = 0; ix < fc->nfiles; ix++) {
1071         rpmsid ftypeId;
1072         const char * ftype;
1073         const char * s = argv[ix];
1074         size_t slen = strlen(s);
1075         int fcolor = RPMFC_BLACK;
1076         rpm_mode_t mode = (fmode ? fmode[ix] : 0);
1077         int is_executable = (mode & (S_IXUSR|S_IXGRP|S_IXOTH));
1078
1079         switch (mode & S_IFMT) {
1080         case S_IFCHR:   ftype = "character special";    break;
1081         case S_IFBLK:   ftype = "block special";        break;
1082         case S_IFIFO:   ftype = "fifo (named pipe)";    break;
1083         case S_IFSOCK:  ftype = "socket";               break;
1084         case S_IFDIR:   ftype = "directory";            break;
1085         case S_IFLNK:
1086         case S_IFREG:
1087         default:
1088             /* XXX all files with extension ".pm" are perl modules for now. */
1089             if (rpmFileHasSuffix(s, ".pm"))
1090                 ftype = "Perl5 module source text";
1091
1092             /* XXX all files with extension ".la" are libtool for now. */
1093             else if (rpmFileHasSuffix(s, ".la"))
1094                 ftype = "libtool library file";
1095
1096             /* XXX all files with extension ".pc" are pkgconfig for now. */
1097             else if (rpmFileHasSuffix(s, ".pc"))
1098                 ftype = "pkgconfig file";
1099
1100             /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */
1101             else if (slen >= fc->brlen+sizeof("/dev/") && rstreqn(s+fc->brlen, "/dev/", sizeof("/dev/")-1))
1102                 ftype = "";
1103             else
1104                 ftype = magic_file(ms, s);
1105
1106             if (ftype == NULL) {
1107                 rpmlog(is_executable ? RPMLOG_ERR : RPMLOG_WARNING, 
1108                        _("Recognition of file \"%s\" failed: mode %06o %s\n"),
1109                        s, mode, magic_error(ms));
1110                 /* only executable files are critical to dep extraction */
1111                 if (is_executable) {
1112                     goto exit;
1113                 }
1114                 /* unrecognized non-executables get treated as "data" */
1115                 ftype = "data";
1116             }
1117         }
1118
1119         rpmlog(RPMLOG_DEBUG, "%s: %s\n", s, ftype);
1120
1121         /* Save the path. */
1122         fc->fn[ix] = xstrdup(s);
1123
1124         /* Add (filtered) file coloring */
1125         fcolor |= rpmfcColor(ftype);
1126
1127         /* Add attributes based on file type and/or path */
1128         rpmfcAttributes(fc, ix, ftype, s);
1129
1130         fc->fcolor[ix] = fcolor;
1131
1132         /* Add to file class dictionary and index array */
1133         if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE)) {
1134             ftypeId = rpmstrPoolId(fc->cdict, ftype, 1);
1135             fc->fknown++;
1136         } else {
1137             ftypeId = rpmstrPoolId(fc->cdict, "", 1);
1138             fc->fwhite++;
1139         }
1140         /* Pool id's start from 1, for headers we want it from 0 */
1141         fc->fcdictx[ix] = ftypeId - 1;
1142     }
1143     rc = RPMRC_OK;
1144
1145 exit:
1146     /* No more additions after this, freeze pool to minimize memory use */
1147     rpmstrPoolFreeze(fc->cdict, 0);
1148     if (ms != NULL)
1149         magic_close(ms);
1150
1151     return rc;
1152 }
1153
1154 /**
1155  */
1156 typedef struct DepMsg_s * DepMsg_t;
1157
1158 /**
1159  */
1160 struct DepMsg_s {
1161     const char * msg;
1162     char * const argv[4];
1163     rpmTagVal ntag;
1164     rpmTagVal vtag;
1165     rpmTagVal ftag;
1166     int mask;
1167     int xormask;
1168 };
1169
1170 /**
1171  */
1172 static struct DepMsg_s depMsgs[] = {
1173   { "Provides",         { "%{?__find_provides}", NULL, NULL, NULL },
1174         RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
1175         0, -1 },
1176   { "Requires(interp)", { NULL, "interp", NULL, NULL },
1177         RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
1178         RPMSENSE_INTERP, 0 },
1179   { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
1180         -1, -1, RPMTAG_REQUIREFLAGS,
1181         RPMSENSE_RPMLIB, 0 },
1182   { "Requires(verify)", { NULL, "verify", NULL, NULL },
1183         -1, -1, RPMTAG_REQUIREFLAGS,
1184         RPMSENSE_SCRIPT_VERIFY, 0 },
1185   { "Requires(pre)",    { NULL, "pre", NULL, NULL },
1186         -1, -1, RPMTAG_REQUIREFLAGS,
1187         RPMSENSE_SCRIPT_PRE, 0 },
1188   { "Requires(post)",   { NULL, "post", NULL, NULL },
1189         -1, -1, RPMTAG_REQUIREFLAGS,
1190         RPMSENSE_SCRIPT_POST, 0 },
1191   { "Requires(preun)",  { NULL, "preun", NULL, NULL },
1192         -1, -1, RPMTAG_REQUIREFLAGS,
1193         RPMSENSE_SCRIPT_PREUN, 0 },
1194   { "Requires(postun)", { NULL, "postun", NULL, NULL },
1195         -1, -1, RPMTAG_REQUIREFLAGS,
1196         RPMSENSE_SCRIPT_POSTUN, 0 },
1197   { "Requires(pretrans)",       { NULL, "pretrans", NULL, NULL },
1198         -1, -1, RPMTAG_REQUIREFLAGS,
1199         RPMSENSE_PRETRANS, 0 },
1200   { "Requires(posttrans)",      { NULL, "posttrans", NULL, NULL },
1201         -1, -1, RPMTAG_REQUIREFLAGS,
1202         RPMSENSE_POSTTRANS, 0 },
1203   { "Requires",         { "%{?__find_requires}", NULL, NULL, NULL },
1204         -1, -1, RPMTAG_REQUIREFLAGS,    /* XXX inherit name/version arrays */
1205         RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 },
1206   { "Conflicts",        { "%{?__find_conflicts}", NULL, NULL, NULL },
1207         RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS,
1208         0, -1 },
1209   { "Obsoletes",        { "%{?__find_obsoletes}", NULL, NULL, NULL },
1210         RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
1211         0, -1 },
1212   { "Recommends",               { "%{?__find_recommends}", NULL, NULL, NULL },
1213         RPMTAG_RECOMMENDNAME, RPMTAG_RECOMMENDVERSION, RPMTAG_RECOMMENDFLAGS,
1214         0, -1 },
1215   { "Suggests", { "%{?__find_suggests}", NULL, NULL, NULL },
1216         RPMTAG_SUGGESTNAME, RPMTAG_SUGGESTVERSION, RPMTAG_SUGGESTFLAGS,
1217         0, -1 },
1218   { "Supplements",      { "%{?__find_supplements}", NULL, NULL, NULL },
1219         RPMTAG_SUPPLEMENTNAME, RPMTAG_SUPPLEMENTVERSION, RPMTAG_SUPPLEMENTFLAGS,
1220         0, -1 },
1221   { "Enhances",         { "%{?__find_enhances}", NULL, NULL, NULL },
1222         RPMTAG_ENHANCENAME, RPMTAG_ENHANCEVERSION, RPMTAG_ENHANCEFLAGS,
1223         0, -1 },
1224   { NULL,               { NULL, NULL, NULL, NULL },     0, 0, 0, 0, 0 }
1225 };
1226
1227 static DepMsg_t DepMsgs = depMsgs;
1228
1229 /**
1230  */
1231 static void printDeps(rpmfc fc)
1232 {
1233     DepMsg_t dm;
1234     rpmds ds = NULL;
1235     const char * DNEVR;
1236     rpmsenseFlags Flags;
1237     int bingo = 0;
1238
1239     for (dm = DepMsgs; dm->msg != NULL; dm++) {
1240         if (dm->ntag != -1) {
1241             ds = rpmfcDependencies(fc, dm->ntag);
1242         }
1243         if (dm->ftag == 0)
1244             continue;
1245
1246         ds = rpmdsInit(ds);
1247         if (ds == NULL)
1248             continue;   /* XXX can't happen */
1249
1250         bingo = 0;
1251         while (rpmdsNext(ds) >= 0) {
1252
1253             Flags = rpmdsFlags(ds);
1254         
1255             if (!((Flags & dm->mask) ^ dm->xormask))
1256                 continue;
1257             if (bingo == 0) {
1258                 rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : ""));
1259                 bingo = 1;
1260             }
1261             if ((DNEVR = rpmdsDNEVR(ds)) == NULL)
1262                 continue;       /* XXX can't happen */
1263             rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2);
1264         }
1265         if (bingo)
1266             rpmlog(RPMLOG_NOTICE, "\n");
1267     }
1268 }
1269
1270 static rpmRC rpmfcApplyExternal(rpmfc fc)
1271 {
1272     StringBuf sb_stdin = newStringBuf();
1273     rpmRC rc = RPMRC_OK;
1274
1275     /* Create file manifest buffer to deliver to dependency finder. */
1276     for (int i = 0; i < fc->nfiles; i++)
1277         appendLineStringBuf(sb_stdin, fc->fn[i]);
1278
1279     for (DepMsg_t dm = DepMsgs; dm->msg != NULL; dm++) {
1280         rpmTagVal tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
1281         rpmsenseFlags tagflags;
1282         char * s = NULL;
1283         StringBuf sb_stdout = NULL;
1284         int failnonzero = (tag == RPMTAG_PROVIDEFLAGS);
1285
1286         switch (tag) {
1287         case RPMTAG_PROVIDEFLAGS:
1288             if (fc->skipProv)
1289                 continue;
1290             tagflags = RPMSENSE_FIND_PROVIDES;
1291             break;
1292         case RPMTAG_REQUIREFLAGS:
1293         case RPMTAG_RECOMMENDFLAGS:
1294         case RPMTAG_SUGGESTFLAGS:
1295         case RPMTAG_SUPPLEMENTFLAGS:
1296         case RPMTAG_ENHANCEFLAGS:
1297         case RPMTAG_CONFLICTFLAGS:
1298         case RPMTAG_OBSOLETEFLAGS:
1299             if (fc->skipReq)
1300                 continue;
1301             tagflags = RPMSENSE_FIND_REQUIRES;
1302             break;
1303         default:
1304             continue;
1305             break;
1306         }
1307
1308         s = rpmExpand(dm->argv[0], NULL);
1309         rpmlog(RPMLOG_NOTICE, _("Finding  %s: %s\n"), dm->msg, s);
1310         free(s);
1311
1312         if (rpmfcExec(dm->argv, sb_stdin, &sb_stdout,
1313                         failnonzero, fc->buildRoot) == -1)
1314             continue;
1315
1316         if (sb_stdout == NULL) {
1317             rc = RPMRC_FAIL;
1318             rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1319             break;
1320         }
1321
1322         /* Parse dependencies into header */
1323         rc = parseRCPOT(NULL, fc->pkg, getStringBuf(sb_stdout), dm->ntag != -1 ? dm->ntag : RPMTAG_REQUIRENAME, 0, tagflags, addReqProvPkg, NULL);
1324         freeStringBuf(sb_stdout);
1325
1326         if (rc) {
1327             rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1328             break;
1329         }
1330     }
1331
1332     freeStringBuf(sb_stdin);
1333
1334     return rc;
1335 }
1336
1337 rpmRC rpmfcApply(rpmfc fc)
1338 {
1339     rpmRC rc;
1340     /* If new-fangled dependency generation is disabled ... */
1341     if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) {
1342         /* ... then generate dependencies using %{__find_requires} et al. */
1343         rpmlog(RPMLOG_WARNING,
1344             _("Deprecated external dependency generator is used!\n"));
1345         rc = rpmfcApplyExternal(fc);
1346     } else {
1347         /* ... otherwise generate per-file dependencies */
1348         rc = rpmfcApplyInternal(fc);
1349     }
1350     return rc;
1351 }
1352
1353 rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
1354 {
1355     rpmfi fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD);
1356     rpmfc fc = NULL;
1357     rpm_mode_t * fmode = NULL;
1358     int ac = rpmfiFC(fi);
1359     int genConfigDeps = 0;
1360     rpmRC rc = RPMRC_OK;
1361     int idx;
1362     struct rpmtd_s td;
1363
1364     /* Skip packages with no files. */
1365     if (ac <= 0)
1366         goto exit;
1367
1368     /* Extract absolute file paths in argv format. */
1369     fmode = xcalloc(ac+1, sizeof(*fmode));
1370
1371     fi = rpmfiInit(fi, 0);
1372     while ((idx = rpmfiNext(fi)) >= 0) {
1373         /* Does package have any %config files? */
1374         genConfigDeps |= (rpmfiFFlags(fi) & RPMFILE_CONFIG);
1375         fmode[idx] = rpmfiFMode(fi);
1376     }
1377
1378     fc = rpmfcCreate(spec->buildRoot, 0);
1379     free(fc->pkg);
1380     fc->pkg = pkg;
1381     fc->skipProv = !pkg->autoProv;
1382     fc->skipReq = !pkg->autoReq;
1383
1384     if (!fc->skipProv && genConfigDeps) {
1385         /* Add config dependency, Provides: config(N) = EVR */
1386         rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_PROVIDENAME, "config",
1387                                  rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
1388                                  (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1389         rpmdsMerge(packageDependencies(pkg, RPMTAG_PROVIDENAME), ds);
1390         rpmdsFree(ds);
1391     }
1392     if (!fc->skipReq && genConfigDeps) {
1393         rpmds ds = rpmdsSingleNS(fc->pool, RPMTAG_REQUIRENAME, "config",
1394                                  rpmdsN(pkg->ds), rpmdsEVR(pkg->ds),
1395                                  (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1396         rpmdsMerge(packageDependencies(pkg, RPMTAG_REQUIRENAME), ds);
1397         rpmdsFree(ds);
1398     }
1399
1400     /* Build file class dictionary. */
1401     rc = rpmfcClassify(fc, pkg->dpaths, fmode);
1402     if ( rc != RPMRC_OK )
1403         goto exit;
1404
1405     /* Build file/package dependency dictionary. */
1406     rc = rpmfcApply(fc);
1407     if (rc != RPMRC_OK)
1408         goto exit;
1409
1410     /* Add per-file colors(#files) */
1411     /* XXX Make sure only primary (i.e. Elf32/Elf64) colors are added. */
1412     for (int i = 0; i < fc->nfiles; i++)
1413         fc->fcolor[i] &= 0x0f;
1414     headerPutUint32(pkg->header, RPMTAG_FILECOLORS, fc->fcolor, fc->nfiles);
1415     
1416     /* Add classes(#classes) */
1417     for (rpmsid id = 1; id <= rpmstrPoolNumStr(fc->cdict); id++) {
1418         headerPutString(pkg->header, RPMTAG_CLASSDICT,
1419                         rpmstrPoolStr(fc->cdict, id));
1420     }
1421
1422     /* Add per-file classes(#files) */
1423     headerPutUint32(pkg->header, RPMTAG_FILECLASS, fc->fcdictx, fc->nfiles);
1424
1425     /* Add dependency dictionary(#dependencies) */
1426     if (rpmtdFromArgi(&td, RPMTAG_DEPENDSDICT, fc->ddictx)) {
1427         headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1428
1429         /* Add per-file dependency (start,number) pairs (#files) */
1430         if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSX, fc->fddictx)) {
1431             headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1432         }
1433
1434         if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSN, fc->fddictn)) {
1435             headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1436         }
1437     }
1438
1439
1440     if (_rpmfc_debug) {
1441         char *msg = NULL;
1442         rasprintf(&msg, "final: files %d cdict[%d] %d%% ddictx[%d]",
1443                   fc->nfiles, rpmstrPoolNumStr(fc->cdict),
1444                   ((100 * fc->fknown)/fc->nfiles), argiCount(fc->ddictx));
1445         rpmfcPrint(msg, fc, NULL);
1446         free(msg);
1447     }
1448 exit:
1449     printDeps(fc);
1450
1451     /* Clean up. */
1452     if (fc)
1453         fc->pkg = NULL;
1454     free(fmode);
1455     rpmfcFree(fc);
1456     rpmfiFree(fi);
1457
1458     return rc;
1459 }