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