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