Realize the remaining bits of direct rpmdb interface are dead too
[platform/upstream/rpm.git] / build / rpmfc.c
1 #include "system.h"
2
3 #include <signal.h>
4 #if HAVE_GELF_H
5 #include <gelf.h>
6
7 #if !defined(DT_GNU_HASH)
8 #define DT_GNU_HASH             0x6ffffef5
9 #endif
10
11 #endif
12
13 #include <rpm/header.h>
14 #include <rpm/rpmbuild.h>
15 #include <rpm/argv.h>
16 #include <rpm/rpmfc.h>
17 #include <rpm/rpmlog.h>
18 #include <rpm/rpmfileutil.h>
19
20 #include <rpm/rpmds.h>
21 #include <rpm/rpmfi.h>
22
23 #include "debug.h"
24
25 /**
26  */
27 struct rpmfc_s {
28     int nfiles;         /*!< no. of files */
29     int fknown;         /*!< no. of classified files */
30     int fwhite;         /*!< no. of "white" files */
31     int ix;             /*!< current file index */
32     int skipProv;       /*!< Don't auto-generate Provides:? */
33     int skipReq;        /*!< Don't auto-generate Requires:? */
34     int tracked;        /*!< Versioned Provides: tracking dependency added? */
35     size_t brlen;       /*!< strlen(spec->buildRoot) */
36
37     ARGV_t fn;          /*!< (no. files) file names */
38     ARGI_t fcolor;      /*!< (no. files) file colors */
39     ARGI_t fcdictx;     /*!< (no. files) file class dictionary indices */
40     ARGI_t fddictx;     /*!< (no. files) file depends dictionary start */
41     ARGI_t fddictn;     /*!< (no. files) file depends dictionary no. entries */
42     ARGV_t cdict;       /*!< (no. classes) file class dictionary */
43     ARGV_t ddict;       /*!< (no. dependencies) file depends dictionary */
44     ARGI_t ddictx;      /*!< (no. dependencies) file->dependency mapping */
45
46     rpmds provides;     /*!< (no. provides) package provides */
47     rpmds requires;     /*!< (no. requires) package requires */
48
49     StringBuf sb_java;  /*!< concatenated list of java colored files. */
50     StringBuf sb_perl;  /*!< concatenated list of perl colored files. */
51     StringBuf sb_python;/*!< concatenated list of python colored files. */
52
53 };
54
55 /**
56  */
57 struct rpmfcTokens_s {
58     const char * token;
59     rpm_color_t colors;
60 };  
61
62 /**
63  */
64 static int rpmfcExpandAppend(ARGV_t * argvp, ARGV_const_t av)
65 {
66     ARGV_t argv = *argvp;
67     int argc = argvCount(argv);
68     int ac = argvCount(av);
69     int i;
70
71     argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
72     for (i = 0; i < ac; i++)
73         argv[argc + i] = rpmExpand(av[i], NULL);
74     argv[argc + ac] = NULL;
75     *argvp = argv;
76     return 0;
77 }
78
79 /** \ingroup rpmbuild
80  * Return output from helper script.
81  * @todo Use poll(2) rather than select(2), if available.
82  * @param dir           directory to run in (or NULL)
83  * @param argv          program and arguments to run
84  * @param writePtr      bytes to feed to script on stdin (or NULL)
85  * @param writeBytesLeft no. of bytes to feed to script on stdin
86  * @param failNonZero   is script failure an error?
87  * @return              buffered stdout from script, NULL on error
88  */     
89 static StringBuf getOutputFrom(const char * dir, ARGV_t argv,
90                         const char * writePtr, size_t writeBytesLeft,
91                         int failNonZero)
92 {
93     pid_t child, reaped;
94     int toProg[2];
95     int fromProg[2];
96     int status;
97     void *oldhandler;
98     StringBuf readBuff;
99     int done;
100
101     /* FIX: cast? */
102     oldhandler = signal(SIGPIPE, SIG_IGN);
103
104     toProg[0] = toProg[1] = 0;
105     fromProg[0] = fromProg[1] = 0;
106     if (pipe(toProg) < 0 || pipe(fromProg) < 0) {
107         rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
108         return NULL;
109     }
110     
111     if (!(child = fork())) {
112         (void) close(toProg[1]);
113         (void) close(fromProg[0]);
114         
115         (void) dup2(toProg[0], STDIN_FILENO);   /* Make stdin the in pipe */
116         (void) dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
117
118         (void) close(toProg[0]);
119         (void) close(fromProg[1]);
120
121         if (dir && chdir(dir)) {
122             rpmlog(RPMLOG_ERR, _("Couldn't chdir to %s: %s\n"),
123                     dir, strerror(errno));
124             _exit(EXIT_FAILURE);
125         }
126         
127         rpmlog(RPMLOG_DEBUG, "\texecv(%s) pid %d\n",
128                         argv[0], (unsigned)getpid());
129
130         unsetenv("MALLOC_CHECK_");
131         (void) execvp(argv[0], (char *const *)argv);
132         /* XXX this error message is probably not seen. */
133         rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
134                 argv[0], strerror(errno));
135         _exit(EXIT_FAILURE);
136     }
137     if (child < 0) {
138         rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
139                 argv[0], strerror(errno));
140         return NULL;
141     }
142
143     (void) close(toProg[0]);
144     (void) close(fromProg[1]);
145
146     /* Do not block reading or writing from/to prog. */
147     (void) fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
148     (void) fcntl(toProg[1], F_SETFL, O_NONBLOCK);
149     
150     readBuff = newStringBuf();
151
152     do {
153         fd_set ibits, obits;
154         struct timeval tv;
155         int nfd, nbw, nbr;
156         int rc;
157
158         done = 0;
159 top:
160         FD_ZERO(&ibits);
161         FD_ZERO(&obits);
162         if (fromProg[0] >= 0) {
163             FD_SET(fromProg[0], &ibits);
164         }
165         if (toProg[1] >= 0) {
166             FD_SET(toProg[1], &obits);
167         }
168         /* XXX values set to limit spinning with perl doing ~100 forks/sec. */
169         tv.tv_sec = 0;
170         tv.tv_usec = 10000;
171         nfd = ((fromProg[0] > toProg[1]) ? fromProg[0] : toProg[1]);
172         if ((rc = select(nfd, &ibits, &obits, NULL, &tv)) < 0) {
173             if (errno == EINTR)
174                 goto top;
175             break;
176         }
177
178         /* Write any data to program */
179         if (toProg[1] >= 0 && FD_ISSET(toProg[1], &obits)) {
180           if (writePtr && writeBytesLeft > 0) {
181             if ((nbw = write(toProg[1], writePtr,
182                     (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
183                 if (errno != EAGAIN) {
184                     perror("getOutputFrom()");
185                     exit(EXIT_FAILURE);
186                 }
187                 nbw = 0;
188             }
189             writeBytesLeft -= nbw;
190             writePtr += nbw;
191           } else if (toProg[1] >= 0) {  /* close write fd */
192             (void) close(toProg[1]);
193             toProg[1] = -1;
194           }
195         }
196         
197         /* Read any data from prog */
198         {   char buf[BUFSIZ+1];
199             while ((nbr = read(fromProg[0], buf, sizeof(buf)-1)) > 0) {
200                 buf[nbr] = '\0';
201                 appendStringBuf(readBuff, buf);
202             }
203         }
204
205         /* terminate on (non-blocking) EOF or error */
206         done = (nbr == 0 || (nbr < 0 && errno != EAGAIN));
207
208     } while (!done);
209
210     /* Clean up */
211     if (toProg[1] >= 0)
212         (void) close(toProg[1]);
213     if (fromProg[0] >= 0)
214         (void) close(fromProg[0]);
215     /* FIX: cast? */
216     (void) signal(SIGPIPE, oldhandler);
217
218     /* Collect status from prog */
219     reaped = waitpid(child, &status, 0);
220     rpmlog(RPMLOG_DEBUG, "\twaitpid(%d) rc %d status %x\n",
221         (unsigned)child, (unsigned)reaped, status);
222
223     if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
224         rpmlog(RPMLOG_ERR, _("%s failed\n"), argv[0]);
225         return NULL;
226     }
227     if (writeBytesLeft) {
228         rpmlog(RPMLOG_ERR, _("failed to write all data to %s\n"), argv[0]);
229         return NULL;
230     }
231     return readBuff;
232 }
233
234 int rpmfcExec(ARGV_const_t av, StringBuf sb_stdin, StringBuf * sb_stdoutp,
235                 int failnonzero)
236 {
237     char * s = NULL;
238     ARGV_t xav = NULL;
239     ARGV_t pav = NULL;
240     int pac = 0;
241     int ec = -1;
242     StringBuf sb = NULL;
243     const char * buf_stdin = NULL;
244     size_t buf_stdin_len = 0;
245     int xx;
246
247     if (sb_stdoutp)
248         *sb_stdoutp = NULL;
249     if (!(av && *av))
250         goto exit;
251
252     /* Find path to executable with (possible) args. */
253     s = rpmExpand(av[0], NULL);
254     if (!(s && *s))
255         goto exit;
256
257     /* Parse args buried within expanded exacutable. */
258     pac = 0;
259     xx = poptParseArgvString(s, &pac, (const char ***)&pav);
260     if (!(xx == 0 && pac > 0 && pav != NULL))
261         goto exit;
262
263     /* Build argv, appending args to the executable args. */
264     xav = NULL;
265     xx = argvAppend(&xav, pav);
266     if (av[1])
267         xx = rpmfcExpandAppend(&xav, av + 1);
268
269     if (sb_stdin != NULL) {
270         buf_stdin = getStringBuf(sb_stdin);
271         buf_stdin_len = strlen(buf_stdin);
272     }
273
274     /* Read output from exec'd helper. */
275     sb = getOutputFrom(NULL, xav, buf_stdin, buf_stdin_len, failnonzero);
276
277     if (sb_stdoutp != NULL) {
278         *sb_stdoutp = sb;
279         sb = NULL;      /* XXX don't free */
280     }
281
282     ec = 0;
283
284 exit:
285     sb = freeStringBuf(sb);
286     xav = argvFree(xav);
287     pav = _free(pav);   /* XXX popt mallocs in single blob. */
288     s = _free(s);
289     return ec;
290 }
291
292 /**
293  */
294 static int rpmfcSaveArg(ARGV_t * argvp, const char * key)
295 {
296     int rc = 0;
297
298     if (argvSearch(*argvp, key, NULL) == NULL) {
299         rc = argvAdd(argvp, key);
300         rc = argvSort(*argvp, NULL);
301     }
302     return rc;
303 }
304
305 static void rpmfcAddFileDep(ARGV_t * argvp, int ix, rpmds ds)
306 {
307     rpmTag tagN = rpmdsTagN(ds);
308     char *key = NULL;
309
310     if (ds == NULL) {
311         return;
312     }
313
314     assert(tagN == RPMTAG_PROVIDENAME || tagN == RPMTAG_REQUIRENAME);
315
316     rasprintf(&key, "%08d%c %s %s 0x%08x", ix, tagN == RPMTAG_PROVIDENAME ? 'P' : 'R',
317                 rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
318
319     rpmfcSaveArg(argvp, key);
320     free(key);
321 }
322
323 /**
324  * Run per-interpreter dependency helper.
325  * @param fc            file classifier
326  * @param deptype       'P' == Provides:, 'R' == Requires:, helper
327  * @param nsdep         class name for interpreter (e.g. "perl")
328  * @return              0 on success
329  */
330 static int rpmfcHelper(rpmfc fc, unsigned char deptype, const char * nsdep)
331 {
332     const char * fn = fc->fn[fc->ix];
333     char *buf = NULL;
334     StringBuf sb_stdout = NULL;
335     StringBuf sb_stdin;
336     char *av[2];
337     rpmds * depsp, ds;
338     const char * N;
339     const char * EVR;
340     rpmsenseFlags Flags, dsContext;
341     rpmTag tagN;
342     ARGV_t pav;
343     const char * s;
344     int pac;
345     int xx;
346     int i;
347
348     switch (deptype) {
349     default:
350         return -1;
351         break;
352     case 'P':
353         if (fc->skipProv)
354             return 0;
355         rasprintf(&buf, "%%{?__%s_provides}", nsdep);
356         depsp = &fc->provides;
357         dsContext = RPMSENSE_FIND_PROVIDES;
358         tagN = RPMTAG_PROVIDENAME;
359         break;
360     case 'R':
361         if (fc->skipReq)
362             return 0;
363         rasprintf(&buf, "%%{?__%s_requires}", nsdep);
364         depsp = &fc->requires;
365         dsContext = RPMSENSE_FIND_REQUIRES;
366         tagN = RPMTAG_REQUIRENAME;
367         break;
368     }
369     av[0] = buf;
370     av[1] = NULL;
371
372     sb_stdin = newStringBuf();
373     appendLineStringBuf(sb_stdin, fn);
374     sb_stdout = NULL;
375     xx = rpmfcExec(av, sb_stdin, &sb_stdout, 0);
376     sb_stdin = freeStringBuf(sb_stdin);
377
378     if (xx == 0 && sb_stdout != NULL) {
379         pav = NULL;
380         xx = argvSplit(&pav, getStringBuf(sb_stdout), " \t\n\r");
381         pac = argvCount(pav);
382         if (pav)
383         for (i = 0; i < pac; i++) {
384             N = pav[i];
385             EVR = "";
386             Flags = dsContext;
387             if (pav[i+1] && strchr("=<>", *pav[i+1])) {
388                 i++;
389                 for (s = pav[i]; *s; s++) {
390                     switch(*s) {
391                     default:
392 assert(*s != '\0');
393                         break;
394                     case '=':
395                         Flags |= RPMSENSE_EQUAL;
396                         break;
397                     case '<':
398                         Flags |= RPMSENSE_LESS;
399                         break;
400                     case '>':
401                         Flags |= RPMSENSE_GREATER;
402                         break;
403                     }
404                 }
405                 i++;
406                 EVR = pav[i];
407 assert(EVR != NULL);
408             }
409
410
411             /* Add tracking dependency for versioned Provides: */
412             if (!fc->tracked && deptype == 'P' && *EVR != '\0') {
413                 ds = rpmdsSingle(RPMTAG_REQUIRENAME,
414                         "rpmlib(VersionedDependencies)", "3.0.3-1",
415                         RPMSENSE_RPMLIB|(RPMSENSE_LESS|RPMSENSE_EQUAL));
416                 xx = rpmdsMerge(&fc->requires, ds);
417                 ds = rpmdsFree(ds);
418                 fc->tracked = 1;
419             }
420
421             ds = rpmdsSingle(tagN, N, EVR, Flags);
422
423             /* Add to package dependencies. */
424             xx = rpmdsMerge(depsp, ds);
425
426             /* Add to file dependencies. */
427             rpmfcAddFileDep(&fc->ddict, fc->ix, ds);
428
429             ds = rpmdsFree(ds);
430         }
431
432         pav = argvFree(pav);
433     }
434     sb_stdout = freeStringBuf(sb_stdout);
435     free(buf);
436
437     return 0;
438 }
439
440 /**
441  */
442 static const struct rpmfcTokens_s const rpmfcTokens[] = {
443   { "directory",                RPMFC_DIRECTORY|RPMFC_INCLUDE },
444
445   { " shared object",           RPMFC_LIBRARY },
446   { " executable",              RPMFC_EXECUTABLE },
447   { " statically linked",       RPMFC_STATIC },
448   { " not stripped",            RPMFC_NOTSTRIPPED },
449   { " archive",                 RPMFC_ARCHIVE },
450
451   { "ELF 32-bit",               RPMFC_ELF32|RPMFC_INCLUDE },
452   { "ELF 64-bit",               RPMFC_ELF64|RPMFC_INCLUDE },
453
454   { " script",                  RPMFC_SCRIPT },
455   { " document",                RPMFC_DOCUMENT },
456
457   { " compressed",              RPMFC_COMPRESSED },
458
459   { "troff or preprocessor input",      RPMFC_MANPAGE|RPMFC_INCLUDE },
460   { "GNU Info",                 RPMFC_MANPAGE|RPMFC_INCLUDE },
461
462   { "perl script text",         RPMFC_PERL|RPMFC_INCLUDE },
463   { "Perl5 module source text", RPMFC_PERL|RPMFC_MODULE|RPMFC_INCLUDE },
464
465   { " /usr/bin/python",         RPMFC_PYTHON|RPMFC_INCLUDE },
466
467   /* XXX "a /usr/bin/python -t script text executable" */
468   /* XXX "python 2.3 byte-compiled" */
469   { "python ",                  RPMFC_PYTHON|RPMFC_INCLUDE },
470
471   { "libtool library ",         RPMFC_LIBTOOL|RPMFC_INCLUDE },
472   { "pkgconfig ",               RPMFC_PKGCONFIG|RPMFC_INCLUDE },
473
474   /* XXX .NET executables and libraries.  file(1) cannot differ from win32 
475    * executables unfortunately :( */
476   { "Mono/.Net assembly",       RPMFC_MONO|RPMFC_INCLUDE },
477
478   { "current ar archive",       RPMFC_STATIC|RPMFC_LIBRARY|RPMFC_ARCHIVE|RPMFC_INCLUDE },
479
480   { "Zip archive data",         RPMFC_COMPRESSED|RPMFC_ARCHIVE|RPMFC_INCLUDE },
481   { "tar archive",              RPMFC_ARCHIVE|RPMFC_INCLUDE },
482   { "cpio archive",             RPMFC_ARCHIVE|RPMFC_INCLUDE },
483   { "RPM v3",                   RPMFC_ARCHIVE|RPMFC_INCLUDE },
484   { "RPM v4",                   RPMFC_ARCHIVE|RPMFC_INCLUDE },
485
486   { " image",                   RPMFC_IMAGE|RPMFC_INCLUDE },
487   { " font metrics",            RPMFC_WHITE|RPMFC_INCLUDE },
488   { " font",                    RPMFC_FONT|RPMFC_INCLUDE },
489   { " Font",                    RPMFC_FONT|RPMFC_INCLUDE },
490
491   { " commands",                RPMFC_SCRIPT|RPMFC_INCLUDE },
492   { " script",                  RPMFC_SCRIPT|RPMFC_INCLUDE },
493
494   { "empty",                    RPMFC_WHITE|RPMFC_INCLUDE },
495
496   { "HTML",                     RPMFC_WHITE|RPMFC_INCLUDE },
497   { "SGML",                     RPMFC_WHITE|RPMFC_INCLUDE },
498   { "XML",                      RPMFC_WHITE|RPMFC_INCLUDE },
499
500   { " source",                  RPMFC_WHITE|RPMFC_INCLUDE },
501   { "GLS_BINARY_LSB_FIRST",     RPMFC_WHITE|RPMFC_INCLUDE },
502   { " DB ",                     RPMFC_WHITE|RPMFC_INCLUDE },
503
504   { "symbolic link to",         RPMFC_SYMLINK },
505   { "socket",                   RPMFC_DEVICE },
506   { "special",                  RPMFC_DEVICE },
507   { " text",                    RPMFC_TEXT|RPMFC_INCLUDE },
508
509   { "ASCII",                    RPMFC_WHITE },
510   { "ISO-8859",                 RPMFC_WHITE },
511
512   { "data",                     RPMFC_WHITE },
513
514   { "application",              RPMFC_WHITE },
515   { "boot",                     RPMFC_WHITE },
516   { "catalog",                  RPMFC_WHITE },
517   { "code",                     RPMFC_WHITE },
518   { "file",                     RPMFC_WHITE },
519   { "format",                   RPMFC_WHITE },
520   { "message",                  RPMFC_WHITE },
521   { "program",                  RPMFC_WHITE },
522
523   { "broken symbolic link to ", RPMFC_WHITE|RPMFC_ERROR },
524   { "can't read",               RPMFC_WHITE|RPMFC_ERROR },
525   { "can't stat",               RPMFC_WHITE|RPMFC_ERROR },
526   { "executable, can't read",   RPMFC_WHITE|RPMFC_ERROR },
527   { "core file",                RPMFC_WHITE|RPMFC_ERROR },
528
529   { NULL,                       RPMFC_BLACK }
530 };
531
532 int rpmfcColoring(const char * fmstr)
533 {
534     rpmfcToken fct;
535     rpm_color_t fcolor = RPMFC_BLACK;
536
537     for (fct = rpmfcTokens; fct->token != NULL; fct++) {
538         if (strstr(fmstr, fct->token) == NULL)
539             continue;
540         fcolor |= fct->colors;
541         if (fcolor & RPMFC_INCLUDE)
542             return fcolor;
543     }
544     return fcolor;
545 }
546
547 void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
548 {
549     rpm_color_t fcolor;
550     int ndx;
551     int cx;
552     int dx;
553     int fx;
554
555 int nprovides;
556 int nrequires;
557
558     if (fp == NULL) fp = stderr;
559
560     if (msg)
561         fprintf(fp, "===================================== %s\n", msg);
562
563 nprovides = rpmdsCount(fc->provides);
564 nrequires = rpmdsCount(fc->requires);
565
566     if (fc)
567     for (fx = 0; fx < fc->nfiles; fx++) {
568 assert(fx < fc->fcdictx->nvals);
569         cx = fc->fcdictx->vals[fx];
570 assert(fx < fc->fcolor->nvals);
571         fcolor = fc->fcolor->vals[fx];
572
573         fprintf(fp, "%3d %s", fx, fc->fn[fx]);
574         if (fcolor != RPMFC_BLACK)
575                 fprintf(fp, "\t0x%x", fc->fcolor->vals[fx]);
576         else
577                 fprintf(fp, "\t%s", fc->cdict[cx]);
578         fprintf(fp, "\n");
579
580         if (fc->fddictx == NULL || fc->fddictn == NULL)
581             continue;
582
583 assert(fx < fc->fddictx->nvals);
584         dx = fc->fddictx->vals[fx];
585 assert(fx < fc->fddictn->nvals);
586         ndx = fc->fddictn->vals[fx];
587
588         while (ndx-- > 0) {
589             const char * depval;
590             unsigned char deptype;
591             unsigned ix;
592
593             ix = fc->ddictx->vals[dx++];
594             deptype = ((ix >> 24) & 0xff);
595             ix &= 0x00ffffff;
596             depval = NULL;
597             switch (deptype) {
598             default:
599 assert(depval != NULL);
600                 break;
601             case 'P':
602                 if (nprovides > 0) {
603 assert(ix < nprovides);
604                     (void) rpmdsSetIx(fc->provides, ix-1);
605                     if (rpmdsNext(fc->provides) >= 0)
606                         depval = rpmdsDNEVR(fc->provides);
607                 }
608                 break;
609             case 'R':
610                 if (nrequires > 0) {
611 assert(ix < nrequires);
612                     (void) rpmdsSetIx(fc->requires, ix-1);
613                     if (rpmdsNext(fc->requires) >= 0)
614                         depval = rpmdsDNEVR(fc->requires);
615                 }
616                 break;
617             }
618             if (depval)
619                 fprintf(fp, "\t%s\n", depval);
620         }
621     }
622 }
623
624 rpmfc rpmfcFree(rpmfc fc)
625 {
626     if (fc) {
627         fc->fn = argvFree(fc->fn);
628         fc->fcolor = argiFree(fc->fcolor);
629         fc->fcdictx = argiFree(fc->fcdictx);
630         fc->fddictx = argiFree(fc->fddictx);
631         fc->fddictn = argiFree(fc->fddictn);
632         fc->cdict = argvFree(fc->cdict);
633         fc->ddict = argvFree(fc->ddict);
634         fc->ddictx = argiFree(fc->ddictx);
635
636         fc->provides = rpmdsFree(fc->provides);
637         fc->requires = rpmdsFree(fc->requires);
638
639         fc->sb_java = freeStringBuf(fc->sb_java);
640         fc->sb_perl = freeStringBuf(fc->sb_perl);
641         fc->sb_python = freeStringBuf(fc->sb_python);
642
643     }
644     fc = _free(fc);
645     return NULL;
646 }
647
648 rpmfc rpmfcNew(void)
649 {
650     rpmfc fc = xcalloc(1, sizeof(*fc));
651     return fc;
652 }
653
654 rpmds rpmfcProvides(rpmfc fc)
655 {
656     return (fc != NULL ? fc->provides : NULL);
657 }
658
659 rpmds rpmfcRequires(rpmfc fc)
660 {
661     return (fc != NULL ? fc->requires : NULL);
662 }
663
664
665 /**
666  * Extract script dependencies.
667  * @param fc            file classifier
668  * @return              0 on success
669  */
670 static int rpmfcSCRIPT(rpmfc fc)
671 {
672     const char * fn = fc->fn[fc->ix];
673     const char * bn;
674     rpmds ds;
675     char buf[BUFSIZ];
676     FILE * fp;
677     char * s, * se;
678     int i;
679     struct stat sb, * st = &sb;
680     int is_executable;
681     int xx;
682
683     /* Only executable scripts are searched. */
684     if (stat(fn, st) < 0)
685         return -1;
686     is_executable = (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
687
688     fp = fopen(fn, "r");
689     if (fp == NULL || ferror(fp)) {
690         if (fp) (void) fclose(fp);
691         return -1;
692     }
693
694     /* Look for #! interpreter in first 10 lines. */
695     for (i = 0; i < 10; i++) {
696
697         s = fgets(buf, sizeof(buf) - 1, fp);
698         if (s == NULL || ferror(fp) || feof(fp))
699             break;
700         s[sizeof(buf)-1] = '\0';
701         if (!(s[0] == '#' && s[1] == '!'))
702             continue;
703         s += 2;
704
705         while (*s && strchr(" \t\n\r", *s) != NULL)
706             s++;
707         if (*s == '\0')
708             continue;
709         if (*s != '/')
710             continue;
711
712         for (se = s+1; *se; se++) {
713             if (strchr(" \t\n\r", *se) != NULL)
714                 break;
715         }
716         *se = '\0';
717         se++;
718
719         if (is_executable) {
720             /* Add to package requires. */
721             ds = rpmdsSingle(RPMTAG_REQUIRENAME, s, "", RPMSENSE_FIND_REQUIRES);
722             xx = rpmdsMerge(&fc->requires, ds);
723
724             /* Add to file requires. */
725             rpmfcAddFileDep(&fc->ddict, fc->ix, ds);
726
727             ds = rpmdsFree(ds);
728         }
729
730         /* Set color based on interpreter name. */
731         bn = basename(s);
732         if (rstreq(bn, "perl"))
733             fc->fcolor->vals[fc->ix] |= RPMFC_PERL;
734         else if (rstreqn(bn, "python", sizeof("python")-1))
735             fc->fcolor->vals[fc->ix] |= RPMFC_PYTHON;
736
737         break;
738     }
739
740     (void) fclose(fp);
741
742     if (fc->fcolor->vals[fc->ix] & RPMFC_PERL) {
743         if (fc->fcolor->vals[fc->ix] & RPMFC_MODULE)
744             xx = rpmfcHelper(fc, 'P', "perl");
745         if (is_executable || (fc->fcolor->vals[fc->ix] & RPMFC_MODULE))
746             xx = rpmfcHelper(fc, 'R', "perl");
747     }
748     if (fc->fcolor->vals[fc->ix] & RPMFC_PYTHON) {
749         xx = rpmfcHelper(fc, 'P', "python");
750 #ifdef  NOTYET
751         if (is_executable)
752 #endif
753             xx = rpmfcHelper(fc, 'R', "python");
754     } else 
755     if (fc->fcolor->vals[fc->ix] & RPMFC_LIBTOOL) {
756         xx = rpmfcHelper(fc, 'P', "libtool");
757 #ifdef  NOTYET
758         if (is_executable)
759 #endif
760             xx = rpmfcHelper(fc, 'R', "libtool");
761     } else
762     if (fc->fcolor->vals[fc->ix] & RPMFC_PKGCONFIG) {
763         xx = rpmfcHelper(fc, 'P', "pkgconfig");
764 #ifdef  NOTYET
765         if (is_executable)
766 #endif
767             xx = rpmfcHelper(fc, 'R', "pkgconfig");
768     } else 
769     if (fc->fcolor->vals[fc->ix] & RPMFC_MONO) {
770         xx = rpmfcHelper(fc, 'P', "mono");
771         if (is_executable)
772             xx = rpmfcHelper(fc, 'R', "mono");
773     }
774
775     return 0;
776 }
777
778 /**
779  * Extract Elf dependencies.
780  * @param fc            file classifier
781  * @return              0 on success
782  */
783 static int rpmfcELF(rpmfc fc)
784 {
785 #if HAVE_GELF_H && HAVE_LIBELF
786     const char * fn = fc->fn[fc->ix];
787     Elf * elf;
788     Elf_Scn * scn;
789     Elf_Data * data;
790     GElf_Ehdr ehdr_mem, * ehdr;
791     GElf_Shdr shdr_mem, * shdr;
792     GElf_Verdef def_mem, * def;
793     GElf_Verneed need_mem, * need;
794     GElf_Dyn dyn_mem, * dyn;
795     unsigned int auxoffset;
796     unsigned int offset;
797     int fdno;
798     int cnt2;
799     int cnt;
800     char *buf = NULL;
801     const char * s;
802     struct stat sb, * st = &sb;
803     char * soname = NULL;
804     rpmds * depsp, ds;
805     rpmTag tagN;
806     rpmsenseFlags dsContext;
807     int xx;
808     int isElf64;
809     int isDSO;
810     int gotSONAME = 0;
811     int gotDEBUG = 0;
812     int gotHASH = 0;
813     int gotGNUHASH = 0;
814     static int filter_GLIBC_PRIVATE = 0;
815     static int oneshot = 0;
816
817     if (oneshot == 0) {
818         oneshot = 1;
819         filter_GLIBC_PRIVATE = rpmExpandNumeric("%{?_filter_GLIBC_PRIVATE}");
820     }
821
822     /* Files with executable bit set only. */
823     if (stat(fn, st) != 0)
824         return(-1);
825
826     fdno = open(fn, O_RDONLY);
827     if (fdno < 0)
828         return fdno;
829
830     (void) elf_version(EV_CURRENT);
831
832     elf = NULL;
833     if ((elf = elf_begin (fdno, ELF_C_READ, NULL)) == NULL
834      || elf_kind(elf) != ELF_K_ELF
835      || (ehdr = gelf_getehdr(elf, &ehdr_mem)) == NULL
836      || !(ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC))
837         goto exit;
838
839     isElf64 = ehdr->e_ident[EI_CLASS] == ELFCLASS64;
840     isDSO = ehdr->e_type == ET_DYN;
841
842     scn = NULL;
843     while ((scn = elf_nextscn(elf, scn)) != NULL) {
844         shdr = gelf_getshdr(scn, &shdr_mem);
845         if (shdr == NULL)
846             break;
847
848         soname = _free(soname);
849         switch (shdr->sh_type) {
850         default:
851             continue;
852             break;
853         case SHT_GNU_verdef:
854             data = NULL;
855             if (!fc->skipProv)
856             while ((data = elf_getdata (scn, data)) != NULL) {
857                 offset = 0;
858                 for (cnt = shdr->sh_info; --cnt >= 0; ) {
859                 
860                     def = gelf_getverdef (data, offset, &def_mem);
861                     if (def == NULL)
862                         break;
863                     auxoffset = offset + def->vd_aux;
864                     for (cnt2 = def->vd_cnt; --cnt2 >= 0; ) {
865                         GElf_Verdaux aux_mem, * aux;
866
867                         aux = gelf_getverdaux (data, auxoffset, &aux_mem);
868                         if (aux == NULL)
869                             break;
870
871                         s = elf_strptr(elf, shdr->sh_link, aux->vda_name);
872                         if (s == NULL)
873                             break;
874                         if (def->vd_flags & VER_FLG_BASE) {
875                             soname = _free(soname);
876                             soname = xstrdup(s);
877                             auxoffset += aux->vda_next;
878                             continue;
879                         } else
880                         if (soname != NULL
881                          && !(filter_GLIBC_PRIVATE != 0
882                                 && rstreq(s, "GLIBC_PRIVATE")))
883                         {
884                             rasprintf(&buf, "%s(%s)%s", soname, s,
885 #if !defined(__alpha__)
886                                                         isElf64 ? "(64bit)" : "");
887 #else
888                                                         "");
889 #endif
890
891                             /* Add to package provides. */
892                             ds = rpmdsSingle(RPMTAG_PROVIDES,
893                                         buf, "", RPMSENSE_FIND_PROVIDES);
894                             xx = rpmdsMerge(&fc->provides, ds);
895
896                             /* Add to file dependencies. */
897                             rpmfcAddFileDep(&fc->ddict, fc->ix, ds);
898
899                             ds = rpmdsFree(ds);
900                             free(buf);
901                         }
902                         auxoffset += aux->vda_next;
903                     }
904                     offset += def->vd_next;
905                 }
906             }
907             break;
908         case SHT_GNU_verneed:
909             data = NULL;
910             /* Files with executable bit set only. */
911             if (!fc->skipReq && (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
912             while ((data = elf_getdata (scn, data)) != NULL) {
913                 offset = 0;
914                 for (cnt = shdr->sh_info; --cnt >= 0; ) {
915                     need = gelf_getverneed (data, offset, &need_mem);
916                     if (need == NULL)
917                         break;
918
919                     s = elf_strptr(elf, shdr->sh_link, need->vn_file);
920                     if (s == NULL)
921                         break;
922                     soname = _free(soname);
923                     soname = xstrdup(s);
924                     auxoffset = offset + need->vn_aux;
925                     for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) {
926                         GElf_Vernaux aux_mem, * aux;
927
928                         aux = gelf_getvernaux (data, auxoffset, &aux_mem);
929                         if (aux == NULL)
930                             break;
931
932                         s = elf_strptr(elf, shdr->sh_link, aux->vna_name);
933                         if (s == NULL)
934                             break;
935
936                         /* Filter dependencies that contain GLIBC_PRIVATE */
937                         if (soname != NULL
938                          && !(filter_GLIBC_PRIVATE != 0
939                                 && rstreq(s, "GLIBC_PRIVATE")))
940                         {
941                             rasprintf(&buf, "%s(%s)%s", soname, s,
942 #if !defined(__alpha__)
943                                                         isElf64 ? "(64bit)" : "");
944 #else
945                                                         "");
946 #endif
947
948                             /* Add to package dependencies. */
949                             ds = rpmdsSingle(RPMTAG_REQUIRENAME,
950                                         buf, "", RPMSENSE_FIND_REQUIRES);
951                             xx = rpmdsMerge(&fc->requires, ds);
952
953                             /* Add to file dependencies. */
954                             rpmfcAddFileDep(&fc->ddict, fc->ix, ds);
955                             ds = rpmdsFree(ds);
956                             free(buf);
957                         }
958                         auxoffset += aux->vna_next;
959                     }
960                     offset += need->vn_next;
961                 }
962             }
963             break;
964         case SHT_DYNAMIC:
965             data = NULL;
966             while ((data = elf_getdata (scn, data)) != NULL) {
967                 for (cnt = 0; cnt < (shdr->sh_size / shdr->sh_entsize); ++cnt) {
968                     dyn = gelf_getdyn (data, cnt, &dyn_mem);
969                     if (dyn == NULL)
970                         break;
971                     s = NULL;
972                     switch (dyn->d_tag) {
973                     default:
974                         continue;
975                         break;
976                     case DT_HASH:    
977                         gotHASH= 1;
978                         continue;
979                     case DT_GNU_HASH:
980                         gotGNUHASH= 1;
981                         continue;
982                     case DT_DEBUG:    
983                         gotDEBUG = 1;
984                         continue;
985                     case DT_NEEDED:
986                         /* Files with executable bit set only. */
987                         if (fc->skipReq || !(st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
988                             continue;
989                         /* Add to package requires. */
990                         depsp = &fc->requires;
991                         tagN = RPMTAG_REQUIRENAME;
992                         dsContext = RPMSENSE_FIND_REQUIRES;
993                         s = elf_strptr(elf, shdr->sh_link, dyn->d_un.d_val);
994 assert(s != NULL);
995                         break;
996                     case DT_SONAME:
997                         gotSONAME = 1;
998                         /* Add to package provides. */
999                         if (fc->skipProv)
1000                             continue;
1001                         depsp = &fc->provides;
1002                         tagN = RPMTAG_PROVIDENAME;
1003                         dsContext = RPMSENSE_FIND_PROVIDES;
1004                         s = elf_strptr(elf, shdr->sh_link, dyn->d_un.d_val);
1005 assert(s != NULL);
1006                         break;
1007                     }
1008                     if (s == NULL)
1009                         continue;
1010
1011                     rasprintf(&buf, "%s%s", s,
1012 #if !defined(__alpha__)
1013                                             isElf64 ? "()(64bit)" : "");
1014 #else
1015                                             "");
1016 #endif
1017
1018                     /* Add to package dependencies. */
1019                     ds = rpmdsSingle(tagN, buf, "", dsContext);
1020                     xx = rpmdsMerge(depsp, ds);
1021
1022                     /* Add to file dependencies. */
1023                     rpmfcAddFileDep(&fc->ddict, fc->ix, ds);
1024
1025                     ds = rpmdsFree(ds);
1026                     free(buf);
1027                 }
1028             }
1029             break;
1030         }
1031     }
1032
1033     /* For DSOs which use the .gnu_hash section and don't have a .hash
1034      * section, we need to ensure that we have a new enough glibc. */ 
1035     if (gotGNUHASH && !gotHASH) {
1036         ds = rpmdsSingle(RPMTAG_REQUIRENAME, "rtld(GNU_HASH)", "", 
1037                          RPMSENSE_FIND_REQUIRES);
1038         rpmdsMerge(&fc->requires, ds);
1039         rpmfcAddFileDep(&fc->ddict, fc->ix, ds);
1040         ds = rpmdsFree(ds);
1041     }
1042
1043     /* For DSO's, provide the basename of the file if DT_SONAME not found. */
1044     if (!fc->skipProv && isDSO && !gotDEBUG && !gotSONAME) {
1045         depsp = &fc->provides;
1046         tagN = RPMTAG_PROVIDENAME;
1047         dsContext = RPMSENSE_FIND_PROVIDES;
1048
1049         s = strrchr(fn, '/');
1050         if (s)
1051             s++;
1052         else
1053             s = fn;
1054
1055         rasprintf(&buf, "%s%s", s,
1056 #if !defined(__alpha__)
1057                                 isElf64 ? "()(64bit)" : "");
1058 #else
1059                                 "");
1060 #endif
1061
1062         /* Add to package dependencies. */
1063         ds = rpmdsSingle(tagN, buf, "", dsContext);
1064         xx = rpmdsMerge(depsp, ds);
1065
1066         /* Add to file dependencies. */
1067         rpmfcAddFileDep(&fc->ddict, fc->ix, ds);
1068
1069         ds = rpmdsFree(ds);
1070         free(buf);
1071     }
1072
1073 exit:
1074     soname = _free(soname);
1075     if (elf) (void) elf_end(elf);
1076     xx = close(fdno);
1077     return 0;
1078 #else
1079     return -1;
1080 #endif
1081 }
1082
1083 static int rpmfcMISC(rpmfc fc)
1084 {
1085     struct stat st;
1086     int rc = -1;
1087     const char *what = NULL;
1088     const char * fn = fc->fn[fc->ix];
1089     /* this part is enumerated, compare equality not bit flags */
1090     int ftype = fc->fcolor->vals[fc->ix] & 0x000F0000;
1091
1092     if (ftype == RPMFC_FONT) {
1093         what = "fontconfig";
1094     } else if (ftype == RPMFC_TEXT && rpmFileHasSuffix(fn, ".desktop")) {
1095         what = "desktop";
1096     }
1097
1098     if (what == NULL || stat(fn, &st) < 0 || !S_ISREG(st.st_mode)) {
1099         goto exit;
1100     }
1101
1102     (void) rpmfcHelper(fc, 'P', what);
1103     rc = 0;
1104
1105 exit:
1106     return rc;
1107 }
1108 typedef const struct rpmfcApplyTbl_s {
1109     int (*func) (rpmfc fc);
1110     int colormask;
1111 } * rpmfcApplyTbl;
1112
1113 /**
1114  */
1115 static const struct rpmfcApplyTbl_s const rpmfcApplyTable[] = {
1116     { rpmfcELF,         RPMFC_ELF },
1117     { rpmfcSCRIPT,      (RPMFC_SCRIPT|RPMFC_BOURNE|
1118                          RPMFC_PERL|RPMFC_PYTHON|RPMFC_MONO|
1119                          RPMFC_PKGCONFIG|RPMFC_LIBTOOL) },
1120     { rpmfcMISC,        RPMFC_FONT|RPMFC_TEXT },
1121     { NULL, 0 }
1122 };
1123
1124 rpmRC rpmfcApply(rpmfc fc)
1125 {
1126     rpmfcApplyTbl fcat;
1127     const char * s;
1128     char * se;
1129     rpmds ds;
1130     const char * N;
1131     const char * EVR;
1132     rpmsenseFlags Flags;
1133     unsigned char deptype;
1134     int nddict;
1135     int previx;
1136     unsigned int val;
1137     int dix;
1138     int ix;
1139     int i;
1140     int xx;
1141
1142     /* Generate package and per-file dependencies. */
1143     for (fc->ix = 0; fc->fn[fc->ix] != NULL; fc->ix++) {
1144
1145         /* XXX Insure that /usr/lib{,64}/python files are marked RPMFC_PYTHON */
1146         /* XXX HACK: classification by path is intrinsically stupid. */
1147         {   const char *fn = strstr(fc->fn[fc->ix], "/usr/lib");
1148             if (fn) {
1149                 fn += sizeof("/usr/lib")-1;
1150                 if (fn[0] == '6' && fn[1] == '4')
1151                     fn += 2;
1152                 if (rstreqn(fn, "/python", sizeof("/python")-1))
1153                     fc->fcolor->vals[fc->ix] |= RPMFC_PYTHON;
1154             }
1155         }
1156
1157         if (fc->fcolor->vals[fc->ix])
1158         for (fcat = rpmfcApplyTable; fcat->func != NULL; fcat++) {
1159             if (!(fc->fcolor->vals[fc->ix] & fcat->colormask))
1160                 continue;
1161             xx = (*fcat->func) (fc);
1162         }
1163     }
1164
1165     /* Generate per-file indices into package dependencies. */
1166     nddict = argvCount(fc->ddict);
1167     previx = -1;
1168     for (i = 0; i < nddict; i++) {
1169         s = fc->ddict[i];
1170
1171         /* Parse out (file#,deptype,N,EVR,Flags) */
1172         ix = strtol(s, &se, 10);
1173         if ( se == NULL ) {
1174                 rpmlog(RPMLOG_ERR, _("Conversion of %s to long integer failed.\n"), s);
1175                 return RPMRC_FAIL;
1176         }
1177         
1178         deptype = *se++;
1179         se++;
1180         N = se;
1181         while (*se && *se != ' ')
1182             se++;
1183         *se++ = '\0';
1184         EVR = se;
1185         while (*se && *se != ' ')
1186             se++;
1187         *se++ = '\0';
1188         Flags = strtol(se, NULL, 16);
1189
1190         dix = -1;
1191         switch (deptype) {
1192         default:
1193             break;
1194         case 'P':       
1195             ds = rpmdsSingle(RPMTAG_PROVIDENAME, N, EVR, Flags);
1196             dix = rpmdsFind(fc->provides, ds);
1197             ds = rpmdsFree(ds);
1198             break;
1199         case 'R':
1200             ds = rpmdsSingle(RPMTAG_REQUIRENAME, N, EVR, Flags);
1201             dix = rpmdsFind(fc->requires, ds);
1202             ds = rpmdsFree(ds);
1203             break;
1204         }
1205
1206 /* XXX assertion incorrect while generating -debuginfo deps. */
1207 #if 0
1208 assert(dix >= 0);
1209 #else
1210         if (dix < 0)
1211             continue;
1212 #endif
1213
1214         val = (deptype << 24) | (dix & 0x00ffffff);
1215         xx = argiAdd(&fc->ddictx, -1, val);
1216
1217         if (previx != ix) {
1218             previx = ix;
1219             xx = argiAdd(&fc->fddictx, ix, argiCount(fc->ddictx)-1);
1220         }
1221         if (fc->fddictn && fc->fddictn->vals)
1222             fc->fddictn->vals[ix]++;
1223     }
1224
1225     return RPMRC_OK;
1226 }
1227
1228 rpmRC rpmfcClassify(rpmfc fc, ARGV_t argv, rpm_mode_t * fmode)
1229 {
1230     ARGV_t fcav = NULL;
1231     ARGV_t dav;
1232     const char * s, * se;
1233     size_t slen;
1234     int fcolor;
1235     int xx;
1236     int msflags = MAGIC_CHECK | MAGIC_COMPRESS | MAGIC_NO_CHECK_TOKENS;
1237     magic_t ms = NULL;
1238
1239     if (fc == NULL || argv == NULL)
1240         return 0;
1241
1242     fc->nfiles = argvCount(argv);
1243
1244     /* Initialize the per-file dictionary indices. */
1245     xx = argiAdd(&fc->fddictx, fc->nfiles-1, 0);
1246     xx = argiAdd(&fc->fddictn, fc->nfiles-1, 0);
1247
1248     /* Build (sorted) file class dictionary. */
1249     xx = argvAdd(&fc->cdict, "");
1250     xx = argvAdd(&fc->cdict, "directory");
1251
1252     ms = magic_open(msflags);
1253     if (ms == NULL) {
1254         rpmlog(RPMLOG_ERR, _("magic_open(0x%x) failed: %s\n"),
1255                 msflags, strerror(errno));
1256         return RPMRC_FAIL;
1257     }
1258
1259     xx = magic_load(ms, NULL);
1260     if (xx == -1) {
1261         rpmlog(RPMLOG_ERR, _("magic_load failed: %s\n"), magic_error(ms));
1262         magic_close(ms);
1263         return RPMRC_FAIL;
1264     }
1265
1266     for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
1267         const char * ftype;
1268         rpm_mode_t mode = (fmode ? fmode[fc->ix] : 0);
1269
1270         s = argv[fc->ix];
1271         slen = strlen(s);
1272
1273         switch (mode & S_IFMT) {
1274         case S_IFCHR:   ftype = "character special";    break;
1275         case S_IFBLK:   ftype = "block special";        break;
1276         case S_IFIFO:   ftype = "fifo (named pipe)";    break;
1277         case S_IFSOCK:  ftype = "socket";               break;
1278         case S_IFDIR:
1279         case S_IFLNK:
1280         case S_IFREG:
1281         default:
1282             /* XXX all files with extension ".pm" are perl modules for now. */
1283             if (rpmFileHasSuffix(s, ".pm"))
1284                 ftype = "Perl5 module source text";
1285
1286             /* XXX all files with extension ".la" are libtool for now. */
1287             else if (rpmFileHasSuffix(s, ".la"))
1288                 ftype = "libtool library file";
1289
1290             /* XXX all files with extension ".pc" are pkgconfig for now. */
1291             else if (rpmFileHasSuffix(s, ".pc"))
1292                 ftype = "pkgconfig file";
1293
1294             /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */
1295             else if (slen >= fc->brlen+sizeof("/dev/") && rstreqn(s+fc->brlen, "/dev/", sizeof("/dev/")-1))
1296                 ftype = "";
1297             else
1298                 ftype = magic_file(ms, s);
1299
1300             if (ftype == NULL) {
1301                 rpmlog(RPMLOG_ERR, 
1302                        _("Recognition of file \"%s\" failed: mode %06o %s\n"),
1303                        s, mode, magic_error(ms));
1304                 magic_close(ms);
1305                 return RPMRC_FAIL;
1306             }
1307         }
1308
1309         se = ftype;
1310         rpmlog(RPMLOG_DEBUG, "%s: %s\n", s, se);
1311
1312         /* Save the path. */
1313         xx = argvAdd(&fc->fn, s);
1314
1315         /* Save the file type string. */
1316         xx = argvAdd(&fcav, se);
1317
1318         /* Add (filtered) entry to sorted class dictionary. */
1319         fcolor = rpmfcColoring(se);
1320         xx = argiAdd(&fc->fcolor, fc->ix, fcolor);
1321
1322         if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE))
1323             xx = rpmfcSaveArg(&fc->cdict, se);
1324     }
1325
1326     /* Build per-file class index array. */
1327     fc->fknown = 0;
1328     for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
1329         se = fcav[fc->ix];
1330
1331         dav = argvSearch(fc->cdict, se, NULL);
1332         if (dav) {
1333             xx = argiAdd(&fc->fcdictx, fc->ix, (dav - fc->cdict));
1334             fc->fknown++;
1335         } else {
1336             xx = argiAdd(&fc->fcdictx, fc->ix, 0);
1337             fc->fwhite++;
1338         }
1339     }
1340
1341     fcav = argvFree(fcav);
1342
1343     if (ms != NULL)
1344         magic_close(ms);
1345
1346     return RPMRC_OK;
1347 }
1348
1349 /**
1350  */
1351 typedef struct DepMsg_s * DepMsg_t;
1352
1353 /**
1354  */
1355 struct DepMsg_s {
1356     const char * msg;
1357     char * const argv[4];
1358     rpmTag ntag;
1359     rpmTag vtag;
1360     rpmTag ftag;
1361     int mask;
1362     int xor;
1363 };
1364
1365 /**
1366  */
1367 static struct DepMsg_s depMsgs[] = {
1368   { "Provides",         { "%{?__find_provides}", NULL, NULL, NULL },
1369         RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
1370         0, -1 },
1371   { "Requires(interp)", { NULL, "interp", NULL, NULL },
1372         RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
1373         RPMSENSE_INTERP, 0 },
1374   { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
1375         -1, -1, RPMTAG_REQUIREFLAGS,
1376         RPMSENSE_RPMLIB, 0 },
1377   { "Requires(verify)", { NULL, "verify", NULL, NULL },
1378         -1, -1, RPMTAG_REQUIREFLAGS,
1379         RPMSENSE_SCRIPT_VERIFY, 0 },
1380   { "Requires(pre)",    { NULL, "pre", NULL, NULL },
1381         -1, -1, RPMTAG_REQUIREFLAGS,
1382         RPMSENSE_SCRIPT_PRE, 0 },
1383   { "Requires(post)",   { NULL, "post", NULL, NULL },
1384         -1, -1, RPMTAG_REQUIREFLAGS,
1385         RPMSENSE_SCRIPT_POST, 0 },
1386   { "Requires(preun)",  { NULL, "preun", NULL, NULL },
1387         -1, -1, RPMTAG_REQUIREFLAGS,
1388         RPMSENSE_SCRIPT_PREUN, 0 },
1389   { "Requires(postun)", { NULL, "postun", NULL, NULL },
1390         -1, -1, RPMTAG_REQUIREFLAGS,
1391         RPMSENSE_SCRIPT_POSTUN, 0 },
1392   { "Requires",         { "%{?__find_requires}", NULL, NULL, NULL },
1393         -1, -1, RPMTAG_REQUIREFLAGS,    /* XXX inherit name/version arrays */
1394         RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 },
1395   { "Conflicts",        { "%{?__find_conflicts}", NULL, NULL, NULL },
1396         RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS,
1397         0, -1 },
1398   { "Obsoletes",        { "%{?__find_obsoletes}", NULL, NULL, NULL },
1399         RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS,
1400         0, -1 },
1401   { NULL,               { NULL, NULL, NULL, NULL },     0, 0, 0, 0, 0 }
1402 };
1403
1404 static DepMsg_t DepMsgs = depMsgs;
1405
1406 /**
1407  */
1408 static void printDeps(Header h)
1409 {
1410     DepMsg_t dm;
1411     rpmds ds = NULL;
1412     const char * DNEVR;
1413     rpmsenseFlags Flags;
1414     int bingo = 0;
1415
1416     for (dm = DepMsgs; dm->msg != NULL; dm++) {
1417         if (dm->ntag != -1) {
1418             ds = rpmdsFree(ds);
1419             ds = rpmdsNew(h, dm->ntag, 0);
1420         }
1421         if (dm->ftag == 0)
1422             continue;
1423
1424         ds = rpmdsInit(ds);
1425         if (ds == NULL)
1426             continue;   /* XXX can't happen */
1427
1428         bingo = 0;
1429         while (rpmdsNext(ds) >= 0) {
1430
1431             Flags = rpmdsFlags(ds);
1432         
1433             if (!((Flags & dm->mask) ^ dm->xor))
1434                 continue;
1435             if (bingo == 0) {
1436                 rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : ""));
1437                 bingo = 1;
1438             }
1439             if ((DNEVR = rpmdsDNEVR(ds)) == NULL)
1440                 continue;       /* XXX can't happen */
1441             rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2);
1442         }
1443         if (bingo)
1444             rpmlog(RPMLOG_NOTICE, "\n");
1445     }
1446     ds = rpmdsFree(ds);
1447 }
1448
1449 /**
1450  */
1451 static int rpmfcGenerateDependsHelper(const rpmSpec spec, Package pkg, rpmfi fi)
1452 {
1453     StringBuf sb_stdin;
1454     StringBuf sb_stdout;
1455     DepMsg_t dm;
1456     int failnonzero = 0;
1457     int rc = RPMRC_OK;
1458
1459     /*
1460      * Create file manifest buffer to deliver to dependency finder.
1461      */
1462     sb_stdin = newStringBuf();
1463     fi = rpmfiInit(fi, 0);
1464     if (fi != NULL)
1465     while (rpmfiNext(fi) >= 0)
1466         appendLineStringBuf(sb_stdin, rpmfiFN(fi));
1467
1468     for (dm = DepMsgs; dm->msg != NULL; dm++) {
1469         rpmTag tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
1470         rpmsenseFlags tagflags;
1471         char * s = NULL;
1472
1473         switch(tag) {
1474         case RPMTAG_PROVIDEFLAGS:
1475             if (!pkg->autoProv)
1476                 continue;
1477             failnonzero = 1;
1478             tagflags = RPMSENSE_FIND_PROVIDES;
1479             break;
1480         case RPMTAG_REQUIREFLAGS:
1481             if (!pkg->autoReq)
1482                 continue;
1483             failnonzero = 0;
1484             tagflags = RPMSENSE_FIND_REQUIRES;
1485             break;
1486         default:
1487             continue;
1488             break;
1489         }
1490
1491         if (rpmfcExec(dm->argv, sb_stdin, &sb_stdout, failnonzero) == -1)
1492             continue;
1493
1494         s = rpmExpand(dm->argv[0], NULL);
1495         rpmlog(RPMLOG_NOTICE, _("Finding  %s: %s\n"), dm->msg, (s ? s : ""));
1496         free(s);
1497
1498         if (sb_stdout == NULL) {
1499             rc = RPMRC_FAIL;
1500             rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1501             break;
1502         }
1503
1504         /* Parse dependencies into header */
1505         rc = parseRCPOT(spec, pkg, getStringBuf(sb_stdout), tag, 0, tagflags);
1506         sb_stdout = freeStringBuf(sb_stdout);
1507
1508         if (rc) {
1509             rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1510             break;
1511         }
1512     }
1513
1514     sb_stdin = freeStringBuf(sb_stdin);
1515
1516     return rc;
1517 }
1518
1519 rpmRC rpmfcGenerateDepends(const rpmSpec spec, Package pkg)
1520 {
1521     rpmfi fi = pkg->cpioList;
1522     rpmfc fc = NULL;
1523     rpmds ds;
1524     ARGV_t av;
1525     rpm_mode_t * fmode;
1526     int ac = rpmfiFC(fi);
1527     char *buf = NULL;
1528     const char * N;
1529     const char * EVR;
1530     int genConfigDeps;
1531     int rc = RPMRC_OK;
1532     int xx;
1533     int idx;
1534     struct rpmtd_s td;
1535
1536     /* Skip packages with no files. */
1537     if (ac <= 0)
1538         return 0;
1539
1540     /* Skip packages that have dependency generation disabled. */
1541     if (! (pkg->autoReq || pkg->autoProv))
1542         return 0;
1543
1544     /* If new-fangled dependency generation is disabled ... */
1545     if (!rpmExpandNumeric("%{?_use_internal_dependency_generator}")) {
1546         /* ... then generate dependencies using %{__find_requires} et al. */
1547         rc = rpmfcGenerateDependsHelper(spec, pkg, fi);
1548         printDeps(pkg->header);
1549         return rc;
1550     }
1551
1552     /* Extract absolute file paths in argv format. */
1553     av = xcalloc(ac+1, sizeof(*av));
1554     fmode = xcalloc(ac+1, sizeof(*fmode));
1555
1556     genConfigDeps = 0;
1557     fi = rpmfiInit(fi, 0);
1558     if (fi != NULL)
1559     while ((idx = rpmfiNext(fi)) >= 0) {
1560         rpmfileAttrs fileAttrs;
1561
1562         /* Does package have any %config files? */
1563         fileAttrs = rpmfiFFlags(fi);
1564         genConfigDeps |= (fileAttrs & RPMFILE_CONFIG);
1565
1566         av[idx] = xstrdup(rpmfiFN(fi));
1567         fmode[idx] = rpmfiFMode(fi);
1568     }
1569     av[ac] = NULL;
1570
1571     fc = rpmfcNew();
1572     fc->skipProv = !pkg->autoProv;
1573     fc->skipReq = !pkg->autoReq;
1574     fc->tracked = 0;
1575     fc->brlen = (spec->buildRoot ? strlen(spec->buildRoot) : 0);
1576
1577     /* Copy (and delete) manually generated dependencies to dictionary. */
1578     if (!fc->skipProv) {
1579         ds = rpmdsNew(pkg->header, RPMTAG_PROVIDENAME, 0);
1580         xx = rpmdsMerge(&fc->provides, ds);
1581         ds = rpmdsFree(ds);
1582         xx = headerDel(pkg->header, RPMTAG_PROVIDENAME);
1583         xx = headerDel(pkg->header, RPMTAG_PROVIDEVERSION);
1584         xx = headerDel(pkg->header, RPMTAG_PROVIDEFLAGS);
1585
1586         /* Add config dependency, Provides: config(N) = EVR */
1587         if (genConfigDeps) {
1588             N = rpmdsN(pkg->ds);
1589             if (N == NULL) {
1590                 rc = RPMRC_FAIL;
1591                 rpmlog(RPMLOG_ERR, _("Unable to get current dependency name.\n"));
1592                 goto exit;
1593             }
1594             EVR = rpmdsEVR(pkg->ds);
1595             if (EVR == NULL) {
1596                 rc = RPMRC_FAIL;
1597                 rpmlog(RPMLOG_ERR, _("Unable to get current dependency epoch-version-release.\n"));
1598                 goto exit;
1599             }
1600             rasprintf(&buf, "config(%s)", N);
1601             ds = rpmdsSingle(RPMTAG_PROVIDENAME, buf, EVR,
1602                         (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1603             free(buf);
1604             xx = rpmdsMerge(&fc->provides, ds);
1605             ds = rpmdsFree(ds);
1606         }
1607     }
1608
1609     if (!fc->skipReq) {
1610         ds = rpmdsNew(pkg->header, RPMTAG_REQUIRENAME, 0);
1611         xx = rpmdsMerge(&fc->requires, ds);
1612         ds = rpmdsFree(ds);
1613         xx = headerDel(pkg->header, RPMTAG_REQUIRENAME);
1614         xx = headerDel(pkg->header, RPMTAG_REQUIREVERSION);
1615         xx = headerDel(pkg->header, RPMTAG_REQUIREFLAGS);
1616
1617         /* Add config dependency,  Requires: config(N) = EVR */
1618         if (genConfigDeps) {
1619             N = rpmdsN(pkg->ds);
1620             if (N == NULL) {
1621                 rc = RPMRC_FAIL;
1622                 rpmlog(RPMLOG_ERR, _("Unable to get current dependency name.\n"));
1623                 goto exit;
1624             }
1625             EVR = rpmdsEVR(pkg->ds);
1626             if (EVR == NULL) {
1627                 rc = RPMRC_FAIL;
1628                 rpmlog(RPMLOG_ERR, _("Unable to get current dependency epoch-version-release.\n"));
1629                 goto exit;
1630             }
1631             rasprintf(&buf, "config(%s)", N);
1632             ds = rpmdsSingle(RPMTAG_REQUIRENAME, buf, EVR,
1633                         (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1634             free(buf);
1635             xx = rpmdsMerge(&fc->requires, ds);
1636             ds = rpmdsFree(ds);
1637         }
1638     }
1639
1640     /* Build file class dictionary. */
1641     rc = rpmfcClassify(fc, av, fmode);
1642     if ( rc != RPMRC_OK )
1643         goto exit;
1644
1645     /* Build file/package dependency dictionary. */
1646     rc = rpmfcApply(fc);
1647     if ( rc != RPMRC_OK )
1648         goto exit;
1649
1650     /* Add per-file colors(#files) */
1651     if (rpmtdFromArgi(&td, RPMTAG_FILECOLORS, fc->fcolor)) {
1652         rpm_color_t *fcolor;
1653         if (ac != rpmtdCount(&td)) {
1654             rc = RPMRC_FAIL;
1655             rpmlog(RPMLOG_ERR, _("File count from file info doesn't match file in container.\n"));
1656             goto exit;
1657         }
1658         assert(rpmtdType(&td) == RPM_INT32_TYPE);
1659         /* XXX Make sure only primary (i.e. Elf32/Elf64) colors are added. */
1660         while ((fcolor = rpmtdNextUint32(&td))) {
1661             *fcolor &= 0x0f;
1662         }
1663         headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1664     }
1665
1666     /* Add classes(#classes) */
1667     if (rpmtdFromArgv(&td, RPMTAG_CLASSDICT, fc->cdict)) {
1668         if (rpmtdType(&td) != RPM_STRING_ARRAY_TYPE) {
1669             rc = RPMRC_FAIL;
1670             rpmlog(RPMLOG_ERR, _("Container not of string array data type.\n"));
1671             goto exit;
1672         }
1673         headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1674     }
1675
1676     /* Add per-file classes(#files) */
1677     if (rpmtdFromArgi(&td, RPMTAG_FILECLASS, fc->fcdictx)) {
1678         assert(rpmtdType(&td) == RPM_INT32_TYPE);
1679         headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1680     }
1681
1682     /* Add Provides: */
1683     if (fc->provides != NULL && rpmdsCount(fc->provides) > 0 && !fc->skipProv) {
1684         rpmds pi = rpmdsInit(fc->provides);
1685         while (rpmdsNext(pi) >= 0) {
1686             const char *name = rpmdsN(pi);
1687             const char *evr = rpmdsEVR(pi);
1688             rpmsenseFlags flags = rpmdsFlags(pi);
1689         
1690             headerPutString(pkg->header, RPMTAG_PROVIDENAME, name);
1691             headerPutString(pkg->header, RPMTAG_PROVIDEVERSION, evr);
1692             headerPutUint32(pkg->header, RPMTAG_PROVIDEFLAGS, &flags, 1);
1693         }
1694     }
1695
1696     /* Add Requires: */
1697     if (fc->requires != NULL && rpmdsCount(fc->requires) > 0 && !fc->skipReq) {
1698         rpmds pi = rpmdsInit(fc->requires);
1699         while (rpmdsNext(pi) >= 0) {
1700             const char *name = rpmdsN(pi);
1701             const char *evr = rpmdsEVR(pi);
1702             rpmsenseFlags flags = rpmdsFlags(pi);
1703         
1704             headerPutString(pkg->header, RPMTAG_REQUIRENAME, name);
1705             headerPutString(pkg->header, RPMTAG_REQUIREVERSION, evr);
1706             headerPutUint32(pkg->header, RPMTAG_REQUIREFLAGS, &flags, 1);
1707         }
1708     }
1709
1710     /* Add dependency dictionary(#dependencies) */
1711     if (rpmtdFromArgi(&td, RPMTAG_DEPENDSDICT, fc->ddictx)) {
1712         assert(rpmtdType(&td) == RPM_INT32_TYPE);
1713         headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1714     }
1715
1716     /* Add per-file dependency (start,number) pairs (#files) */
1717     if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSX, fc->fddictx)) {
1718         assert(rpmtdType(&td) == RPM_INT32_TYPE);
1719         headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1720     }
1721
1722     if (rpmtdFromArgi(&td, RPMTAG_FILEDEPENDSN, fc->fddictn)) {
1723         assert(rpmtdType(&td) == RPM_INT32_TYPE);
1724         headerPut(pkg->header, &td, HEADERPUT_DEFAULT);
1725     }
1726
1727     printDeps(pkg->header);
1728
1729 if (fc != NULL && _rpmfc_debug) {
1730 char *msg = NULL;
1731 rasprintf(&msg, "final: files %d cdict[%d] %d%% ddictx[%d]", fc->nfiles, argvCount(fc->cdict), ((100 * fc->fknown)/fc->nfiles), argiCount(fc->ddictx));
1732 rpmfcPrint(msg, fc, NULL);
1733 free(msg);
1734 }
1735 exit:
1736     /* Clean up. */
1737     fmode = _free(fmode);
1738     fc = rpmfcFree(fc);
1739     av = argvFree(av);
1740
1741     return rc;
1742 }