rpm  5.2.1
rpmfc.c
Go to the documentation of this file.
1 #include "system.h"
2 
3 #include <signal.h> /* getOutputFrom() */
4 
5 #include <rpmio.h>
6 #include <rpmiotypes.h> /* XXX fnpyKey */
7 #include <rpmlog.h>
8 #include <rpmurl.h>
9 #include <rpmmg.h>
10 #include <argv.h>
11 #define _MIRE_INTERNAL
12 #include <mire.h>
13 
14 #include <rpmtag.h>
15 #define _RPMEVR_INTERNAL
16 #include <rpmbuild.h>
17 
18 #define _RPMNS_INTERNAL
19 #include <rpmns.h>
20 
21 #define _RPMFC_INTERNAL
22 #include <rpmfc.h>
23 
24 #define _RPMDS_INTERNAL
25 #include <rpmds.h>
26 #include <rpmfi.h>
27 
28 #include "debug.h"
29 
30 /*@access rpmds @*/
31 /*@access miRE @*/
32 
33 /*@unchecked@*/
34 static int _filter_values = 1;
35 /*@unchecked@*/
36 static int _filter_execs = 1;
37 
40 static int rpmfcExpandAppend(/*@out@*/ ARGV_t * argvp, const ARGV_t av)
41  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
42  /*@modifies *argvp, rpmGlobalMacroContext, internalState @*/
43  /*@requires maxRead(argvp) >= 0 @*/
44 {
45  ARGV_t argv = *argvp;
46  int argc = argvCount(argv);
47  int ac = argvCount(av);
48  int i;
49 
50  argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
51  for (i = 0; i < ac; i++)
52  argv[argc + i] = rpmExpand(av[i], NULL);
53  argv[argc + ac] = NULL;
54  *argvp = argv;
55  return 0;
56 }
57 
68 /*@null@*/
69 static rpmiob getOutputFrom(/*@null@*/ const char * dir, ARGV_t argv,
70  const char * writePtr, size_t writeBytesLeft,
71  int failNonZero)
72  /*@globals h_errno, fileSystem, internalState@*/
73  /*@modifies fileSystem, internalState@*/
74 {
75  pid_t child, reaped;
76  int toProg[2];
77  int fromProg[2];
78  int status;
79  void *oldhandler;
80  rpmiob iob = NULL;
81  int done;
82 
83  /*@-type@*/ /* FIX: cast? */
84  oldhandler = signal(SIGPIPE, SIG_IGN);
85  /*@=type@*/
86 
87  toProg[0] = toProg[1] = 0;
88  fromProg[0] = fromProg[1] = 0;
89  if (pipe(toProg) < 0 || pipe(fromProg) < 0) {
90  rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
91  return NULL;
92  }
93 
94  if (!(child = fork())) {
95  (void) close(toProg[1]);
96  (void) close(fromProg[0]);
97 
98  (void) dup2(toProg[0], STDIN_FILENO); /* Make stdin the in pipe */
99  (void) dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
100 
101  (void) close(toProg[0]);
102  (void) close(fromProg[1]);
103 
104  if (dir) {
105  (void) Chdir(dir);
106  }
107 
108  rpmlog(RPMLOG_DEBUG, D_("\texecv(%s) pid %d\n"),
109  argv[0], (unsigned)getpid());
110 
111  unsetenv("MALLOC_CHECK_");
112  (void) execvp(argv[0], (char *const *)argv);
113  /* XXX this error message is probably not seen. */
114  rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
115  argv[0], strerror(errno));
116  _exit(EXIT_FAILURE);
117  }
118  if (child < 0) {
119  rpmlog(RPMLOG_ERR, _("Couldn't fork %s: %s\n"),
120  argv[0], strerror(errno));
121  return NULL;
122  }
123 
124  (void) close(toProg[0]);
125  (void) close(fromProg[1]);
126 
127  /* Do not block reading or writing from/to prog. */
128  (void) fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
129  (void) fcntl(toProg[1], F_SETFL, O_NONBLOCK);
130 
131  iob = rpmiobNew(0);
132 
133  do {
134  fd_set ibits, obits;
135  struct timeval tv;
136  int nfd;
137  ssize_t nbr;
138  ssize_t nbw;
139  int rc;
140 
141  done = 0;
142 top:
143  FD_ZERO(&ibits);
144  FD_ZERO(&obits);
145  if (fromProg[0] >= 0) {
146  FD_SET(fromProg[0], &ibits);
147  }
148  if (toProg[1] >= 0) {
149  FD_SET(toProg[1], &obits);
150  }
151  /* XXX values set to limit spinning with perl doing ~100 forks/sec. */
152  tv.tv_sec = 0;
153  tv.tv_usec = 10000;
154  nfd = ((fromProg[0] > toProg[1]) ? fromProg[0] : toProg[1]);
155  if ((rc = select(nfd, &ibits, &obits, NULL, &tv)) < 0) {
156  if (errno == EINTR)
157  goto top;
158  break;
159  }
160 
161  /* Write any data to program */
162  if (toProg[1] >= 0 && FD_ISSET(toProg[1], &obits)) {
163  if (writePtr && writeBytesLeft > 0) {
164  if ((nbw = write(toProg[1], writePtr,
165  ((size_t)1024<writeBytesLeft) ? (size_t)1024 : writeBytesLeft)) < 0)
166  {
167  if (errno != EAGAIN) {
168  perror("getOutputFrom()");
169  exit(EXIT_FAILURE);
170  }
171  nbw = 0;
172  }
173  writeBytesLeft -= nbw;
174  writePtr += nbw;
175  } else if (toProg[1] >= 0) { /* close write fd */
176  (void) close(toProg[1]);
177  toProg[1] = -1;
178  }
179  }
180 
181  /* Read any data from prog */
182  { char buf[BUFSIZ+1];
183  while ((nbr = read(fromProg[0], buf, sizeof(buf)-1)) > 0) {
184  buf[nbr] = '\0';
185  iob = rpmiobAppend(iob, buf, 0);
186  }
187  }
188 
189  /* terminate on (non-blocking) EOF or error */
190  done = (nbr == 0 || (nbr < 0 && errno != EAGAIN));
191 
192  } while (!done);
193 
194  /* Clean up */
195  if (toProg[1] >= 0)
196  (void) close(toProg[1]);
197  if (fromProg[0] >= 0)
198  (void) close(fromProg[0]);
199 /*@-type@*/ /* FIX: cast? */
200  (void) signal(SIGPIPE, oldhandler);
201 /*@=type@*/
202 
203  /* Collect status from prog */
204  reaped = waitpid(child, &status, 0);
205  rpmlog(RPMLOG_DEBUG, D_("\twaitpid(%d) rc %d status %x\n"),
206  (unsigned)child, (unsigned)reaped, status);
207 
208  if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
209  const char *cmd = argvJoin(argv);
210  int rc = (WIFEXITED(status) ? WEXITSTATUS(status) : -1);
211 
212  rpmlog(RPMLOG_ERR, _("Command \"%s\" failed, exit(%d)\n"), cmd, rc);
213  cmd = _free(cmd);
214  iob = rpmiobFree(iob);
215  return NULL;
216  }
217  if (writeBytesLeft) {
218  rpmlog(RPMLOG_ERR, _("failed to write all data to %s\n"), argv[0]);
219  iob = rpmiobFree(iob);
220  return NULL;
221  }
222  return iob;
223 }
224 
225 int rpmfcExec(ARGV_t av, rpmiob iob_stdin, rpmiob * iob_stdoutp,
226  int failnonzero)
227 {
228  const char * s = NULL;
229  ARGV_t xav = NULL;
230  ARGV_t pav = NULL;
231  int pac = 0;
232  int ec = -1;
233  rpmiob iob = NULL;
234  const char * buf_stdin = NULL;
235  size_t buf_stdin_len = 0;
236  int xx;
237 
238  if (iob_stdoutp)
239  *iob_stdoutp = NULL;
240  if (!(av && *av))
241  goto exit;
242 
243  /* Find path to executable with (possible) args. */
244  s = rpmExpand(av[0], NULL);
245  if (!(s && *s))
246  goto exit;
247 
248  /* Parse args buried within expanded executable. */
249  pac = 0;
250  xx = poptParseArgvString(s, &pac, (const char ***)&pav);
251  if (!(xx == 0 && pac > 0 && pav != NULL))
252  goto exit;
253 
254  /* Build argv, appending args to the executable args. */
255  xav = NULL;
256  xx = argvAppend(&xav, pav);
257  if (av[1])
258  xx = rpmfcExpandAppend(&xav, av + 1);
259 
260  if (iob_stdin != NULL) {
261  buf_stdin = rpmiobStr(iob_stdin);
262  buf_stdin_len = rpmiobLen(iob_stdin);
263  }
264 
265  /* Read output from exec'd helper. */
266  iob = getOutputFrom(NULL, xav, buf_stdin, buf_stdin_len, failnonzero);
267 
268  if (iob_stdoutp != NULL) {
269  *iob_stdoutp = iob;
270  iob = NULL; /* XXX don't free */
271  }
272 
273  ec = 0;
274 
275 exit:
276  iob = rpmiobFree(iob);
277  xav = argvFree(xav);
278  pav = _free(pav); /* XXX popt mallocs in single blob. */
279  s = _free(s);
280  return ec;
281 }
282 
285 static int rpmfcSaveArg(/*@out@*/ ARGV_t * argvp, const char * key)
286  /*@modifies *argvp @*/
287  /*@requires maxSet(argvp) >= 0 @*/
288 {
289  int rc = 0;
290 
291  if (argvSearch(*argvp, key, NULL) == NULL) {
292  rc = argvAdd(argvp, key);
293  rc = argvSort(*argvp, NULL);
294  }
295  return rc;
296 }
297 
300 static char * rpmfcFileDep(/*@returned@*/ char * buf, size_t ix,
301  /*@null@*/ rpmds ds)
302  /*@globals internalState @*/
303  /*@modifies buf, internalState @*/
304  /*@requires maxSet(buf) >= 0 @*/
305 {
306  rpmTag tagN = rpmdsTagN(ds);
307  char deptype = 'X';
308 
309  buf[0] = '\0';
310  switch (tagN) {
311  default:
312 assert(0);
313  /*@notreached@*/ break;
314  case RPMTAG_PROVIDENAME:
315  deptype = 'P';
316  break;
317  case RPMTAG_REQUIRENAME:
318  deptype = 'R';
319  break;
320  }
321 /*@-nullpass@*/
322  if (ds != NULL)
323  sprintf(buf, "%08u%c %s %s 0x%08x", (unsigned)ix, deptype,
324  rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
325 /*@=nullpass@*/
326  return buf;
327 };
328 
329 /*@null@*/
330 static void * rpmfcExpandRegexps(const char * str, int * nmirep)
331  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
332  /*@modifies *nmirep, rpmGlobalMacroContext, internalState @*/
333 {
334  ARGV_t av = NULL;
335  int ac = 0;
336  miRE mire = NULL;
337  int nmire = 0;
338  const char * s;
339  int xx;
340  int i;
341 
342  s = rpmExpand(str, NULL);
343  if (s) {
344  xx = poptParseArgvString(s, &ac, (const char ***)&av);
345  s = _free(s);
346  }
347  if (ac == 0 || av == NULL || *av == NULL)
348  goto exit;
349 
350  for (i = 0; i < ac; i++) {
351  xx = mireAppend(RPMMIRE_REGEX, 0, av[i], NULL, &mire, &nmire);
352  /* XXX add REG_NOSUB? better error msg? */
353  if (xx) {
355  _("Compilation of pattern '%s'"
356  " (expanded from '%s') failed. Skipping ...\n"),
357  av[i], str);
358  nmire--; /* XXX does this actually skip?!? */
359  }
360  }
361  if (nmire == 0)
362  mire = mireFree(mire);
363 
364 exit:
365  av = _free(av);
366  if (nmirep)
367  *nmirep = nmire;
368  return mire;
369 }
370 
371 static int rpmfcMatchRegexps(void * mires, int nmire,
372  const char * str, char deptype)
373  /*@modifies mires @*/
374 {
375  miRE mire = mires;
376  int xx;
377  int i;
378 
379  for (i = 0; i < nmire; i++) {
380  rpmlog(RPMLOG_DEBUG, D_("Checking %c: '%s'\n"), deptype, str);
381  if ((xx = mireRegexec(mire + i, str, 0)) < 0)
382  continue;
383  rpmlog(RPMLOG_NOTICE, _("Skipping %c: '%s'\n"), deptype, str);
384  return 1;
385  }
386  return 0;
387 }
388 
389 /*@null@*/
390 static void * rpmfcFreeRegexps(/*@only@*/ void * mires, int nmire)
391  /*@modifies mires @*/
392 {
393  miRE mire = mires;
394 /*@-refcounttrans@*/
395  return mireFreeAll(mire, nmire);
396 /*@=refcounttrans@*/
397 }
398 
406 static int rpmfcHelper(rpmfc fc, unsigned char deptype, const char * nsdep)
407  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
408  /*@modifies fc, rpmGlobalMacroContext, fileSystem, internalState @*/
409 {
410  miRE mire = NULL;
411  int nmire = 0;
412  const char * fn = fc->fn[fc->ix];
413  char buf[BUFSIZ];
414  rpmiob iob_stdout = NULL;
415  rpmiob iob_stdin;
416  const char *av[2];
417  rpmds * depsp, ds;
418  const char * N;
419  const char * EVR;
420  rpmTag tagN;
421  evrFlags Flags;
422  evrFlags dsContext;
423  ARGV_t pav;
424  const char * s;
425  int pac;
426  int xx;
427  int i;
428 
429  switch (deptype) {
430  default:
431  return -1;
432  /*@notreached@*/ break;
433  case 'P':
434  if (fc->skipProv)
435  return 0;
436  xx = snprintf(buf, sizeof(buf), "%%{?__%s_provides}", nsdep);
437  depsp = &fc->provides;
438  dsContext = RPMSENSE_FIND_PROVIDES;
439  tagN = RPMTAG_PROVIDENAME;
440  mire = fc->Pmires;
441  nmire = fc->Pnmire;
442  break;
443  case 'R':
444  if (fc->skipReq)
445  return 0;
446  xx = snprintf(buf, sizeof(buf), "%%{?__%s_requires}", nsdep);
447  depsp = &fc->requires;
448  dsContext = RPMSENSE_FIND_REQUIRES;
449  tagN = RPMTAG_REQUIRENAME;
450  mire = fc->Rmires;
451  nmire = fc->Rnmire;
452  break;
453  }
454  buf[sizeof(buf)-1] = '\0';
455  av[0] = buf;
456  av[1] = NULL;
457 
458  iob_stdin = rpmiobNew(0);
459  iob_stdin = rpmiobAppend(iob_stdin, fn, 1);
460  iob_stdout = NULL;
461  xx = rpmfcExec(av, iob_stdin, &iob_stdout, 0);
462  iob_stdin = rpmiobFree(iob_stdin);
463 
464  if (xx == 0 && iob_stdout != NULL) {
465  pav = NULL;
466  xx = argvSplit(&pav, rpmiobStr(iob_stdout), " \t\n\r");
467  pac = argvCount(pav);
468  if (pav)
469  for (i = 0; i < pac; i++) {
470  N = pav[i];
471  EVR = "";
472  Flags = dsContext;
473  if (pav[i+1] && strchr("=<>", *pav[i+1])) {
474  i++;
475  for (s = pav[i]; *s; s++) {
476  switch(*s) {
477  default:
478 assert(*s != '\0');
479  /*@switchbreak@*/ break;
480  case '=':
481  Flags |= RPMSENSE_EQUAL;
482  /*@switchbreak@*/ break;
483  case '<':
484  Flags |= RPMSENSE_LESS;
485  /*@switchbreak@*/ break;
486  case '>':
487  Flags |= RPMSENSE_GREATER;
488  /*@switchbreak@*/ break;
489  }
490  }
491  i++;
492  EVR = pav[i];
493 assert(EVR != NULL);
494  }
495 
496  if (_filter_values && rpmfcMatchRegexps(mire, nmire, N, deptype))
497  continue;
498 
499  /* Add tracking dependency for versioned Provides: */
500  if (!fc->tracked && deptype == 'P' && *EVR != '\0') {
502  "rpmlib(VersionedDependencies)", "3.0.3-1",
503  RPMSENSE_RPMLIB|(RPMSENSE_LESS|RPMSENSE_EQUAL));
504  xx = rpmdsMerge(&fc->requires, ds);
505  (void)rpmdsFree(ds);
506  ds = NULL;
507  fc->tracked = 1;
508  }
509 
510  ds = rpmdsSingle(tagN, N, EVR, Flags);
511 
512  /* Add to package dependencies. */
513  xx = rpmdsMerge(depsp, ds);
514 
515  /* Add to file dependencies. */
516  xx = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(buf, fc->ix, ds));
517 
518  (void)rpmdsFree(ds);
519  ds = NULL;
520  }
521 
522  pav = argvFree(pav);
523  }
524  iob_stdout = rpmiobFree(iob_stdout);
525 
526  return 0;
527 }
528 
531 /*@-nullassign@*/
532 /*@unchecked@*/ /*@observer@*/
533 static struct rpmfcTokens_s rpmfcTokens[] = {
534  { "directory", RPMFC_DIRECTORY|RPMFC_INCLUDE },
535 
536  { " shared object", RPMFC_LIBRARY },
537  { " executable", RPMFC_EXECUTABLE },
538  { " statically linked", RPMFC_STATIC },
539  { " not stripped", RPMFC_NOTSTRIPPED },
540  { " archive", RPMFC_ARCHIVE },
541 
542  { "MIPS, N32 MIPS32", RPMFC_ELFMIPSN32|RPMFC_INCLUDE },
543  { "ELF 32-bit", RPMFC_ELF32|RPMFC_INCLUDE },
544  { "ELF 64-bit", RPMFC_ELF64|RPMFC_INCLUDE },
545 
546  { " script", RPMFC_SCRIPT },
547  { " text", RPMFC_TEXT },
548  { " document", RPMFC_DOCUMENT },
549 
550  { " compressed", RPMFC_COMPRESSED },
551 
552  { "troff or preprocessor input", RPMFC_MANPAGE|RPMFC_INCLUDE },
553  { "GNU Info", RPMFC_MANPAGE|RPMFC_INCLUDE },
554 
555  { "perl script text", RPMFC_PERL|RPMFC_INCLUDE },
556  { "Perl5 module source text", RPMFC_PERL|RPMFC_MODULE|RPMFC_INCLUDE },
557 
558  { "PHP script text", RPMFC_PHP|RPMFC_INCLUDE },
559 
560  /* XXX "a /usr/bin/python -t script text executable" */
561  /* XXX "python 2.3 byte-compiled" */
562  { " /usr/bin/python", RPMFC_PYTHON|RPMFC_INCLUDE },
563  { "python ", RPMFC_PYTHON|RPMFC_INCLUDE },
564 
565  { "libtool library ", RPMFC_LIBTOOL|RPMFC_INCLUDE },
566  { "pkgconfig ", RPMFC_PKGCONFIG|RPMFC_INCLUDE },
567 
568  { "Bourne ", RPMFC_BOURNE|RPMFC_INCLUDE },
569  { "Bourne-Again ", RPMFC_BOURNE|RPMFC_INCLUDE },
570 
571  { "Java ", RPMFC_JAVA|RPMFC_INCLUDE },
572 
573  { "Mono/.Net assembly", RPMFC_MONO|RPMFC_INCLUDE },
574 
575  { "current ar archive", RPMFC_STATIC|RPMFC_LIBRARY|RPMFC_ARCHIVE|RPMFC_INCLUDE },
576 
577  { "Zip archive data", RPMFC_COMPRESSED|RPMFC_ARCHIVE|RPMFC_INCLUDE },
578  { "tar archive", RPMFC_ARCHIVE|RPMFC_INCLUDE },
579  { "cpio archive", RPMFC_ARCHIVE|RPMFC_INCLUDE },
580  { "RPM v3", RPMFC_ARCHIVE|RPMFC_INCLUDE },
581  { "RPM v4", RPMFC_ARCHIVE|RPMFC_INCLUDE },
582 
583  { " image", RPMFC_IMAGE|RPMFC_INCLUDE },
584  { " font", RPMFC_FONT|RPMFC_INCLUDE },
585  { " Font", RPMFC_FONT|RPMFC_INCLUDE },
586 
587  { " commands", RPMFC_SCRIPT|RPMFC_INCLUDE },
588  { " script", RPMFC_SCRIPT|RPMFC_INCLUDE },
589 
590  { "empty", RPMFC_WHITE|RPMFC_INCLUDE },
591 
592  { "HTML", RPMFC_WHITE|RPMFC_INCLUDE },
593  { "SGML", RPMFC_WHITE|RPMFC_INCLUDE },
594  { "XML", RPMFC_WHITE|RPMFC_INCLUDE },
595 
596  { " program text", RPMFC_WHITE|RPMFC_INCLUDE },
597  { " source", RPMFC_WHITE|RPMFC_INCLUDE },
598  { "GLS_BINARY_LSB_FIRST", RPMFC_WHITE|RPMFC_INCLUDE },
599  { " DB ", RPMFC_WHITE|RPMFC_INCLUDE },
600 
601  { "ASCII English text", RPMFC_WHITE|RPMFC_INCLUDE },
602  { "ASCII text", RPMFC_WHITE|RPMFC_INCLUDE },
603  { "ISO-8859 text", RPMFC_WHITE|RPMFC_INCLUDE },
604 
605  { "symbolic link to", RPMFC_SYMLINK },
606  { "socket", RPMFC_DEVICE },
607  { "special", RPMFC_DEVICE },
608 
609  { "ASCII", RPMFC_WHITE },
610  { "ISO-8859", RPMFC_WHITE },
611 
612  { "data", RPMFC_WHITE },
613 
614  { "application", RPMFC_WHITE },
615  { "boot", RPMFC_WHITE },
616  { "catalog", RPMFC_WHITE },
617  { "code", RPMFC_WHITE },
618  { "file", RPMFC_WHITE },
619  { "format", RPMFC_WHITE },
620  { "message", RPMFC_WHITE },
621  { "program", RPMFC_WHITE },
622 
623  { "broken symbolic link to ", RPMFC_WHITE|RPMFC_ERROR },
624  { "can't read", RPMFC_WHITE|RPMFC_ERROR },
625  { "can't stat", RPMFC_WHITE|RPMFC_ERROR },
626  { "executable, can't read", RPMFC_WHITE|RPMFC_ERROR },
627  { "core file", RPMFC_WHITE|RPMFC_ERROR },
628 
629  { NULL, RPMFC_BLACK }
630 };
631 /*@=nullassign@*/
632 
633 int rpmfcColoring(const char * fmstr)
634 {
635  rpmfcToken fct;
636  int fcolor = RPMFC_BLACK;
637 
638  for (fct = rpmfcTokens; fct->token != NULL; fct++) {
639  if (strstr(fmstr, fct->token) == NULL)
640  continue;
641  fcolor |= fct->colors;
642  if (fcolor & RPMFC_INCLUDE)
643  return fcolor;
644  }
645  return fcolor;
646 }
647 
648 void rpmfcPrint(const char * msg, rpmfc fc, FILE * fp)
649 {
650  int fcolor;
651  int ndx;
652  int cx;
653  int dx;
654  size_t fx;
655 
656 unsigned nprovides;
657 unsigned nrequires;
658 
659  if (fp == NULL) fp = stderr;
660 
661  if (msg)
662  fprintf(fp, "===================================== %s\n", msg);
663 
664 nprovides = rpmdsCount(fc->provides);
665 nrequires = rpmdsCount(fc->requires);
666 
667  if (fc)
668  for (fx = 0; fx < fc->nfiles; fx++) {
669 assert(fx < fc->fcdictx->nvals);
670  cx = fc->fcdictx->vals[fx];
671 assert(fx < fc->fcolor->nvals);
672  fcolor = fc->fcolor->vals[fx];
673 
674  fprintf(fp, "%3d %s", (int)fx, fc->fn[fx]);
675  if (fcolor != RPMFC_BLACK)
676  fprintf(fp, "\t0x%x", fc->fcolor->vals[fx]);
677  else
678  fprintf(fp, "\t%s", fc->cdict[cx]);
679  fprintf(fp, "\n");
680 
681  if (fc->fddictx == NULL || fc->fddictn == NULL)
682  continue;
683 
684 assert(fx < fc->fddictx->nvals);
685  dx = fc->fddictx->vals[fx];
686 assert(fx < fc->fddictn->nvals);
687  ndx = fc->fddictn->vals[fx];
688 
689  while (ndx-- > 0) {
690  const char * depval;
691  unsigned char deptype;
692  unsigned ix;
693 
694  ix = fc->ddictx->vals[dx++];
695  deptype = ((ix >> 24) & 0xff);
696  ix &= 0x00ffffff;
697  depval = NULL;
698  switch (deptype) {
699  default:
700 assert(depval != NULL);
701  /*@switchbreak@*/ break;
702  case 'P':
703  if (nprovides > 0) {
704 assert(ix < nprovides);
705  (void) rpmdsSetIx(fc->provides, ix-1);
706  if (rpmdsNext(fc->provides) >= 0)
707  depval = rpmdsDNEVR(fc->provides);
708  }
709  /*@switchbreak@*/ break;
710  case 'R':
711  if (nrequires > 0) {
712 assert(ix < nrequires);
713  (void) rpmdsSetIx(fc->requires, ix-1);
714  if (rpmdsNext(fc->requires) >= 0)
715  depval = rpmdsDNEVR(fc->requires);
716  }
717  /*@switchbreak@*/ break;
718  }
719  if (depval)
720  fprintf(fp, "\t%s\n", depval);
721  }
722  }
723 }
724 
730 static int rpmfcSCRIPT(rpmfc fc)
731  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
732  /*@modifies fc, rpmGlobalMacroContext, fileSystem, internalState @*/
733 {
734  const char * fn = fc->fn[fc->ix];
735  const char * bn;
736  rpmds ds;
737  char buf[BUFSIZ];
738  FILE * fp;
739  char * s, * se;
740  int i;
741  int is_executable;
742  int xx;
743 
744  /* Extract dependencies only from files with executable bit set. */
745  { struct stat sb, * st = &sb;
746  if (stat(fn, st) != 0)
747  return -1;
748  is_executable = (int)(st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH));
749  }
750 
751  fp = fopen(fn, "r");
752  if (fp == NULL || ferror(fp)) {
753  if (fp) (void) fclose(fp);
754  return -1;
755  }
756 
757  /* Look for #! interpreter in first 10 lines. */
758  for (i = 0; i < 10; i++) {
759 
760  s = fgets(buf, sizeof(buf) - 1, fp);
761  if (s == NULL || ferror(fp) || feof(fp))
762  break;
763  s[sizeof(buf)-1] = '\0';
764  if (!(s[0] == '#' && s[1] == '!'))
765  continue;
766  s += 2;
767 
768  while (*s && strchr(" \t\n\r", *s) != NULL)
769  s++;
770  if (*s == '\0')
771  continue;
772  if (*s != '/')
773  continue;
774 
775  for (se = s+1; *se; se++) {
776  if (strchr(" \t\n\r", *se) != NULL)
777  /*@innerbreak@*/ break;
778  }
779  *se = '\0';
780  se++;
781 
782  if (!_filter_values
783  || (!fc->skipReq
784  && !rpmfcMatchRegexps(fc->Rmires, fc->Rnmire, s, 'R')))
785  if (is_executable) {
786  /* Add to package requires. */
787  ds = rpmdsSingle(RPMTAG_REQUIRENAME, s, "", RPMSENSE_FIND_REQUIRES);
788  xx = rpmdsMerge(&fc->requires, ds);
789 
790  /* Add to file requires. */
791  xx = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(se, fc->ix, ds));
792 
793  (void)rpmdsFree(ds);
794  ds = NULL;
795  }
796 
797  /* Set color based on interpreter name. */
798  /* XXX magic token should have already done this?!? */
799 /*@-moduncon@*/
800  bn = basename(s);
801 /*@=moduncon@*/
802  if (!strcmp(bn, "perl"))
803  fc->fcolor->vals[fc->ix] |= RPMFC_PERL;
804  else if (!strncmp(bn, "python", sizeof("python")-1))
805  fc->fcolor->vals[fc->ix] |= RPMFC_PYTHON;
806  else if (!strncmp(bn, "php", sizeof("php")-1))
807  fc->fcolor->vals[fc->ix] |= RPMFC_PHP;
808 
809  break;
810  }
811 
812  (void) fclose(fp);
813 
814  if (fc->fcolor->vals[fc->ix] & RPMFC_PERL) {
815  if (strncmp(fn, "/usr/share/doc/", sizeof("/usr/share/doc/")-1)) {
816  if (fc->fcolor->vals[fc->ix] & RPMFC_MODULE)
817  xx = rpmfcHelper(fc, 'P', "perl");
818  if (is_executable || (fc->fcolor->vals[fc->ix] & RPMFC_MODULE))
819  xx = rpmfcHelper(fc, 'R', "perl");
820  }
821  } else
822  if (fc->fcolor->vals[fc->ix] & RPMFC_PYTHON) {
823  xx = rpmfcHelper(fc, 'P', "python");
824 #ifdef NOTYET
825  if (is_executable)
826 #endif
827  xx = rpmfcHelper(fc, 'R', "python");
828  } else
829  if (fc->fcolor->vals[fc->ix] & RPMFC_LIBTOOL) {
830  xx = rpmfcHelper(fc, 'P', "libtool");
831 #ifdef NOTYET
832  if (is_executable)
833 #endif
834  xx = rpmfcHelper(fc, 'R', "libtool");
835  } else
836  if (fc->fcolor->vals[fc->ix] & RPMFC_PKGCONFIG) {
837  xx = rpmfcHelper(fc, 'P', "pkgconfig");
838 #ifdef NOTYET
839  if (is_executable)
840 #endif
841  xx = rpmfcHelper(fc, 'R', "pkgconfig");
842  } else
843  if (fc->fcolor->vals[fc->ix] & RPMFC_BOURNE) {
844 #ifdef NOTYET
845  xx = rpmfcHelper(fc, 'P', "executable");
846 #endif
847  if (is_executable)
848  xx = rpmfcHelper(fc, 'R', "executable");
849  } else
850  if (fc->fcolor->vals[fc->ix] & RPMFC_PHP) {
851  xx = rpmfcHelper(fc, 'P', "php");
852  if (is_executable)
853  xx = rpmfcHelper(fc, 'R', "php");
854  } else
855  if (fc->fcolor->vals[fc->ix] & RPMFC_MONO) {
856  xx = rpmfcHelper(fc, 'P', "mono");
857  if (is_executable)
858  xx = rpmfcHelper(fc, 'R', "mono");
859  }
860  return 0;
861 }
862 
869 static int rpmfcMergePR(void * context, rpmds ds)
870  /*@globals fileSystem, internalState @*/
871  /*@modifies ds, fileSystem, internalState @*/
872 {
873  rpmfc fc = context;
874  char buf[BUFSIZ];
875  int rc = 0;
876 
877 if (_rpmfc_debug < 0)
878 fprintf(stderr, "*** rpmfcMergePR(%p, %p) %s\n", context, ds, tagName(rpmdsTagN(ds)));
879  switch(rpmdsTagN(ds)) {
880  default:
881  rc = -1;
882  break;
883  case RPMTAG_PROVIDENAME:
884  if (!_filter_values
885  || (!fc->skipProv
886  && !rpmfcMatchRegexps(fc->Pmires, fc->Pnmire, ds->N[0], 'P')))
887  {
888  /* Add to package provides. */
889  rc = rpmdsMerge(&fc->provides, ds);
890 
891  /* Add to file dependencies. */
892  buf[0] = '\0';
893  rc = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(buf, fc->ix, ds));
894  }
895  break;
896  case RPMTAG_REQUIRENAME:
897  if (!_filter_values
898  || (!fc->skipReq
899  && !rpmfcMatchRegexps(fc->Rmires, fc->Rnmire, ds->N[0], 'R')))
900  {
901  /* Add to package requires. */
902  rc = rpmdsMerge(&fc->requires, ds);
903 
904  /* Add to file dependencies. */
905  buf[0] = '\0';
906  rc = rpmfcSaveArg(&fc->ddict, rpmfcFileDep(buf, fc->ix, ds));
907  }
908  break;
909  }
910  return rc;
911 }
912 
918 static int rpmfcELF(rpmfc fc)
919  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
920  /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
921 {
922  const char * fn = fc->fn[fc->ix];
923  int flags = 0;
924 
925  if (fc->skipProv)
926  flags |= RPMELF_FLAG_SKIPPROVIDES;
927  if (fc->skipReq)
928  flags |= RPMELF_FLAG_SKIPREQUIRES;
929 
930  return rpmdsELF(fn, flags, rpmfcMergePR, fc);
931 }
932 
933 typedef struct rpmfcApplyTbl_s {
934  int (*func) (rpmfc fc);
936 } * rpmfcApplyTbl;
937 
941 /*@-nullassign@*/
942 /*@unchecked@*/
943 static struct rpmfcApplyTbl_s rpmfcApplyTable[] = {
944  { rpmfcELF, RPMFC_ELF },
946  { NULL, 0 }
947 };
948 /*@=nullassign@*/
949 
951 {
952  rpmfcApplyTbl fcat;
953  const char * s;
954  char * se;
955  rpmds ds;
956  const char * fn;
957  const char * N;
958  const char * EVR;
959  evrFlags Flags;
960  unsigned char deptype;
961  int nddict;
962  int previx;
963  unsigned int val;
964  int dix;
965  int ix;
966  int i;
967  int xx;
968  int skipping;
969 
970  miRE mire;
971  int skipProv = fc->skipProv;
972  int skipReq = fc->skipReq;
973  int j;
974 
975  if (_filter_execs) {
976  fc->Pnmire = 0;
977  fc->PFnmire = 0;
978  fc->Rnmire = 0;
979  fc->RFnmire = 0;
980 
981  fc->PFmires = rpmfcExpandRegexps("%{__noautoprovfiles}", &fc->PFnmire);
982  fc->RFmires = rpmfcExpandRegexps("%{__noautoreqfiles}", &fc->RFnmire);
983  fc->Pmires = rpmfcExpandRegexps("%{__noautoprov}", &fc->Pnmire);
984  fc->Rmires = rpmfcExpandRegexps("%{__noautoreq}", &fc->Rnmire);
985  rpmlog(RPMLOG_DEBUG, D_("%i _noautoprov patterns.\n"), fc->Pnmire);
986  rpmlog(RPMLOG_DEBUG, D_("%i _noautoreq patterns.\n"), fc->Rnmire);
987  }
988 
989 /* Make sure something didn't go wrong previously! */
990 assert(fc->fn != NULL);
991  /* Generate package and per-file dependencies. */
992  for (fc->ix = 0; fc->fn[fc->ix] != NULL; fc->ix++) {
993 
994  /* XXX Insure that /usr/lib{,64}/python files are marked RPMFC_PYTHON */
995  /* XXX HACK: classification by path is intrinsically stupid. */
996  { fn = strstr(fc->fn[fc->ix], "/usr/lib");
997  if (fn) {
998  fn += sizeof("/usr/lib")-1;
999  if ((fn[0] == '3' && fn[1] == '2') ||
1000  (fn[0] == '6' && fn[1] == '4'))
1001  fn += 2;
1002  if (!strncmp(fn, "/python", sizeof("/python")-1))
1003  fc->fcolor->vals[fc->ix] |= RPMFC_PYTHON;
1004  }
1005  }
1006 
1007  if (fc->fcolor->vals[fc->ix])
1008  for (fcat = rpmfcApplyTable; fcat->func != NULL; fcat++) {
1009  if (!(fc->fcolor->vals[fc->ix] & fcat->colormask))
1010  /*@innercontinue@*/ continue;
1011 
1012  if (_filter_execs) {
1013  fc->skipProv = skipProv;
1014  fc->skipReq = skipReq;
1015  if ((mire = fc->PFmires) != NULL)
1016  for (j = 0; j < fc->PFnmire; j++, mire++) {
1017  fn = fc->fn[fc->ix] + fc->brlen;
1018  if ((xx = mireRegexec(mire, fn, 0)) < 0)
1019  /*@innercontinue@*/ continue;
1020  rpmlog(RPMLOG_NOTICE, _("skipping %s provides detection\n"),
1021  fn);
1022  fc->skipProv = 1;
1023  /*@innerbreak@*/ break;
1024  }
1025  if ((mire = fc->RFmires) != NULL)
1026  for (j = 0; j < fc->RFnmire; j++, mire++) {
1027  fn = fc->fn[fc->ix] + fc->brlen;
1028  if ((xx = mireRegexec(mire, fn, 0)) < 0)
1029  /*@innercontinue@*/ continue;
1030  rpmlog(RPMLOG_NOTICE, _("skipping %s requires detection\n"),
1031  fn);
1032  fc->skipReq = 1;
1033  /*@innerbreak@*/ break;
1034  }
1035  }
1036 
1037  xx = (*fcat->func) (fc);
1038  }
1039  }
1040 
1041  if (_filter_execs) {
1042  fc->PFmires = rpmfcFreeRegexps(fc->PFmires, fc->PFnmire);
1043  fc->RFmires = rpmfcFreeRegexps(fc->RFmires, fc->RFnmire);
1044  fc->Pmires = rpmfcFreeRegexps(fc->Pmires, fc->Pnmire);
1045  fc->Rmires = rpmfcFreeRegexps(fc->Rmires, fc->Rnmire);
1046  }
1047  fc->skipProv = skipProv;
1048  fc->skipReq = skipReq;
1049 
1050  /* Generate per-file indices into package dependencies. */
1051  nddict = argvCount(fc->ddict);
1052  previx = -1;
1053  for (i = 0; i < nddict; i++) {
1054  s = fc->ddict[i];
1055 
1056  /* Parse out (file#,deptype,N,EVR,Flags) */
1057  ix = strtol(s, &se, 10);
1058 assert(se != NULL);
1059  deptype = *se++;
1060  se++;
1061  N = se;
1062  while (*se && *se != ' ')
1063  se++;
1064  *se++ = '\0';
1065  EVR = se;
1066  while (*se && *se != ' ')
1067  se++;
1068  *se++ = '\0';
1069  Flags = strtol(se, NULL, 16);
1070 
1071  dix = -1;
1072  skipping = 0;
1073  switch (deptype) {
1074  default:
1075  /*@switchbreak@*/ break;
1076  case 'P':
1077  skipping = fc->skipProv;
1078  ds = rpmdsSingle(RPMTAG_PROVIDENAME, N, EVR, Flags);
1079  dix = rpmdsFind(fc->provides, ds);
1080  (void)rpmdsFree(ds);
1081  ds = NULL;
1082  /*@switchbreak@*/ break;
1083  case 'R':
1084  skipping = fc->skipReq;
1085  ds = rpmdsSingle(RPMTAG_REQUIRENAME, N, EVR, Flags);
1086  dix = rpmdsFind(fc->requires, ds);
1087  (void)rpmdsFree(ds);
1088  ds = NULL;
1089  /*@switchbreak@*/ break;
1090  }
1091 
1092 /* XXX assertion incorrect while generating -debuginfo deps. */
1093 #if 0
1094 assert(dix >= 0);
1095 #else
1096  if (dix < 0)
1097  continue;
1098 #endif
1099 
1100  val = (deptype << 24) | (dix & 0x00ffffff);
1101  xx = argiAdd(&fc->ddictx, -1, val);
1102 
1103  if (previx != ix) {
1104  previx = ix;
1105  xx = argiAdd(&fc->fddictx, ix, argiCount(fc->ddictx)-1);
1106  }
1107  if (fc->fddictn && fc->fddictn->vals && !skipping)
1108  fc->fddictn->vals[ix]++;
1109  }
1110 
1111  return RPMRC_OK;
1112 }
1113 
1115 {
1116  ARGV_t fcav = NULL;
1117  ARGV_t dav;
1118  rpmmg mg = NULL;
1119  const char * s, * se;
1120  size_t slen;
1121  int fcolor;
1122  int xx;
1123  const char * magicfile = NULL;
1124 
1125  if (fc == NULL || argv == NULL)
1126  return RPMRC_OK;
1127 
1128  magicfile = rpmExpand("%{?_rpmfc_magic_path}", NULL);
1129  if (magicfile == NULL || *magicfile == '\0')
1130  magicfile = _free(magicfile);
1131  mg = rpmmgNew(magicfile, 0);
1132 assert(mg != NULL); /* XXX figger a proper return path. */
1133 
1134  fc->nfiles = argvCount(argv);
1135 
1136  /* Initialize the per-file dictionary indices. */
1137  xx = argiAdd(&fc->fddictx, fc->nfiles-1, 0);
1138  xx = argiAdd(&fc->fddictn, fc->nfiles-1, 0);
1139 
1140  /* Build (sorted) file class dictionary. */
1141  xx = argvAdd(&fc->cdict, "");
1142  xx = argvAdd(&fc->cdict, "directory");
1143 
1144  for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
1145  const char * ftype;
1146  int freeftype;
1147  rpmuint16_t mode = (fmode ? fmode[fc->ix] : 0);
1148  int urltype;
1149 
1150  ftype = ""; freeftype = 0;
1151  urltype = urlPath(argv[fc->ix], &s);
1152 assert(s != NULL && *s == '/');
1153  slen = strlen(s);
1154 
1155  switch (mode & S_IFMT) {
1156  case S_IFCHR: ftype = "character special"; /*@switchbreak@*/ break;
1157  case S_IFBLK: ftype = "block special"; /*@switchbreak@*/ break;
1158 #if defined(S_IFIFO)
1159  case S_IFIFO: ftype = "fifo (named pipe)"; /*@switchbreak@*/ break;
1160 #endif
1161 #if defined(S_IFSOCK)
1162 /*@-unrecog@*/
1163  case S_IFSOCK: ftype = "socket"; /*@switchbreak@*/ break;
1164 /*@=unrecog@*/
1165 #endif
1166  case S_IFDIR:
1167  case S_IFLNK:
1168  case S_IFREG:
1169  default:
1170 
1171 #define _suffix(_s, _x) \
1172  (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
1173 
1174  /* XXX all files with extension ".pm" are perl modules for now. */
1175  if (_suffix(s, ".pm"))
1176  ftype = "Perl5 module source text";
1177 
1178  /* XXX all files with extension ".jar" are java archives for now. */
1179  else if (_suffix(s, ".jar"))
1180  ftype = "Java archive file";
1181 
1182  /* XXX all files with extension ".class" are java class files for now. */
1183  else if (_suffix(s, ".class"))
1184  ftype = "Java class file";
1185 
1186  /* XXX all files with extension ".la" are libtool for now. */
1187  else if (_suffix(s, ".la"))
1188  ftype = "libtool library file";
1189 
1190  /* XXX all files with extension ".pc" are pkgconfig for now. */
1191  else if (_suffix(s, ".pc"))
1192  ftype = "pkgconfig file";
1193 
1194  /* XXX all files with extension ".php" are PHP for now. */
1195  else if (_suffix(s, ".php"))
1196  ftype = "PHP script text";
1197 
1198  /* XXX skip all files in /dev/ which are (or should be) %dev dummies. */
1199  else if (slen >= fc->brlen+sizeof("/dev/") && !strncmp(s+fc->brlen, "/dev/", sizeof("/dev/")-1))
1200  ftype = "";
1201  else if (magicfile) {
1202  ftype = rpmmgFile(mg, s);
1203 assert(ftype != NULL); /* XXX never happens, rpmmgFile() returns "" */
1204  freeftype = 1;
1205  }
1206  /*@switchbreak@*/ break;
1207  }
1208 
1209  se = ftype;
1210 
1211 if (_rpmfc_debug) /* XXX noisy */
1212  rpmlog(RPMLOG_DEBUG, "%s: %s\n", s, se);
1213 
1214  /* Save the path. */
1215  xx = argvAdd(&fc->fn, s);
1216 
1217  /* Save the file type string. */
1218  xx = argvAdd(&fcav, se);
1219 
1220  /* Add (filtered) entry to sorted class dictionary. */
1221  fcolor = rpmfcColoring(se);
1222  xx = argiAdd(&fc->fcolor, (int)fc->ix, fcolor);
1223 
1224  if (fcolor != RPMFC_WHITE && (fcolor & RPMFC_INCLUDE))
1225  xx = rpmfcSaveArg(&fc->cdict, se);
1226 
1227 /*@-modobserver -observertrans @*/ /* XXX mixed types in variable */
1228  if (freeftype)
1229  ftype = _free(ftype);
1230 /*@=modobserver =observertrans @*/
1231  }
1232 
1233  /* Build per-file class index array. */
1234  fc->fknown = 0;
1235  for (fc->ix = 0; fc->ix < fc->nfiles; fc->ix++) {
1236  se = fcav[fc->ix];
1237 assert(se != NULL);
1238 
1239  dav = argvSearch(fc->cdict, se, NULL);
1240  if (dav) {
1241  xx = argiAdd(&fc->fcdictx, (int)fc->ix, (dav - fc->cdict));
1242  fc->fknown++;
1243  } else {
1244  xx = argiAdd(&fc->fcdictx, (int)fc->ix, 0);
1245  fc->fwhite++;
1246  }
1247  }
1248 
1249  fcav = argvFree(fcav);
1250 
1251  mg = rpmmgFree(mg);
1253  D_("categorized %d files into %d classes (using %s).\n"),
1254  fc->nfiles, argvCount(fc->cdict), magicfile);
1255  magicfile = _free(magicfile);
1256 
1257  return RPMRC_OK;
1258 }
1259 
1262 typedef struct DepMsg_s * DepMsg_t;
1263 
1266 struct DepMsg_s {
1267 /*@observer@*/ /*@null@*/
1268  const char * msg;
1269 /*@observer@*/
1270  const char * argv[4];
1274  int mask;
1275  int xor;
1276 };
1277 
1280 /*@-nullassign@*/
1281 /*@unchecked@*/
1282 static struct DepMsg_s depMsgs[] = {
1283  { "Provides", { "%{?__find_provides}", NULL, NULL, NULL },
1285  0, -1 },
1286  { "Requires(interp)", { NULL, "interp", NULL, NULL },
1288  _notpre(RPMSENSE_INTERP), 0 },
1289  { "Requires(rpmlib)", { NULL, "rpmlib", NULL, NULL },
1290  -1, -1, RPMTAG_REQUIREFLAGS,
1291  _notpre(RPMSENSE_RPMLIB), 0 },
1292  { "Requires(verify)", { NULL, "verify", NULL, NULL },
1293  -1, -1, RPMTAG_REQUIREFLAGS,
1294  RPMSENSE_SCRIPT_VERIFY, 0 },
1295  { "Requires(pre)", { NULL, "pre", NULL, NULL },
1296  -1, -1, RPMTAG_REQUIREFLAGS,
1297  _notpre(RPMSENSE_SCRIPT_PRE), 0 },
1298  { "Requires(post)", { NULL, "post", NULL, NULL },
1299  -1, -1, RPMTAG_REQUIREFLAGS,
1300  _notpre(RPMSENSE_SCRIPT_POST), 0 },
1301  { "Requires(preun)", { NULL, "preun", NULL, NULL },
1302  -1, -1, RPMTAG_REQUIREFLAGS,
1303  _notpre(RPMSENSE_SCRIPT_PREUN), 0 },
1304  { "Requires(postun)", { NULL, "postun", NULL, NULL },
1305  -1, -1, RPMTAG_REQUIREFLAGS,
1306  _notpre(RPMSENSE_SCRIPT_POSTUN), 0 },
1307  { "Requires", { "%{?__find_requires}", NULL, NULL, NULL },
1308  -1, -1, RPMTAG_REQUIREFLAGS, /* XXX inherit name/version arrays */
1309  RPMSENSE_FIND_REQUIRES|RPMSENSE_TRIGGERIN|RPMSENSE_TRIGGERUN|RPMSENSE_TRIGGERPOSTUN|RPMSENSE_TRIGGERPREIN, 0 },
1310  { "Conflicts", { "%{?__find_conflicts}", NULL, NULL, NULL },
1312  0, -1 },
1313  { "Obsoletes", { "%{?__find_obsoletes}", NULL, NULL, NULL },
1315  0, -1 },
1316  { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 }
1317 };
1318 /*@=nullassign@*/
1319 
1320 /*@unchecked@*/
1321 static DepMsg_t DepMsgs = depMsgs;
1322 
1327 static void printDeps(Header h)
1328  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1329  /*@modifies h, rpmGlobalMacroContext, fileSystem, internalState @*/
1330 {
1331  DepMsg_t dm;
1332  rpmds ds = NULL;
1333  int flags = 0x2; /* XXX no filtering, !scareMem */
1334  const char * DNEVR;
1335  evrFlags Flags;
1336  int bingo = 0;
1337 
1338  for (dm = DepMsgs; dm->msg != NULL; dm++) {
1339  if ((int)dm->ntag != -1) {
1340  (void)rpmdsFree(ds);
1341  ds = NULL;
1342  ds = rpmdsNew(h, dm->ntag, flags);
1343  }
1344  if (dm->ftag == 0)
1345  continue;
1346 
1347  ds = rpmdsInit(ds);
1348  if (ds == NULL)
1349  continue; /* XXX can't happen */
1350 
1351  bingo = 0;
1352  while (rpmdsNext(ds) >= 0) {
1353 
1354  Flags = rpmdsFlags(ds);
1355 
1356  if (!((Flags & dm->mask) ^ dm->xor))
1357  /*@innercontinue@*/ continue;
1358  if (bingo == 0) {
1359  rpmlog(RPMLOG_NOTICE, "%s:", (dm->msg ? dm->msg : ""));
1360  bingo = 1;
1361  }
1362  if ((DNEVR = rpmdsDNEVR(ds)) == NULL)
1363  /*@innercontinue@*/ continue; /* XXX can't happen */
1364  rpmlog(RPMLOG_NOTICE, " %s", DNEVR+2);
1365  }
1366  if (bingo)
1367  rpmlog(RPMLOG_NOTICE, "\n");
1368  }
1369  (void)rpmdsFree(ds);
1370  ds = NULL;
1371 }
1372 
1376  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1377  /*@modifies fi, rpmGlobalMacroContext, fileSystem, internalState @*/
1378 {
1379  rpmiob iob_stdin;
1380  rpmiob iob_stdout;
1381  DepMsg_t dm;
1382  int failnonzero = 0;
1383  rpmRC rc = RPMRC_OK;
1384 
1385  /*
1386  * Create file manifest buffer to deliver to dependency finder.
1387  */
1388  iob_stdin = rpmiobNew(0);
1389  fi = rpmfiInit(fi, 0);
1390  if (fi != NULL)
1391  while (rpmfiNext(fi) >= 0)
1392  iob_stdin = rpmiobAppend(iob_stdin, rpmfiFN(fi), 1);
1393 
1394  for (dm = DepMsgs; dm->msg != NULL; dm++) {
1395  rpmTag tag;
1396  rpmsenseFlags tagflags;
1397  char * s;
1398  int xx;
1399 
1400  tag = (dm->ftag > 0) ? dm->ftag : dm->ntag;
1401  tagflags = 0;
1402  s = NULL;
1403 
1404  switch(tag) {
1405  case RPMTAG_PROVIDEFLAGS:
1406  if (!pkg->autoProv)
1407  continue;
1408  failnonzero = 1;
1409  tagflags = RPMSENSE_FIND_PROVIDES;
1410  /*@switchbreak@*/ break;
1411  case RPMTAG_REQUIREFLAGS:
1412  if (!pkg->autoReq)
1413  continue;
1414  failnonzero = 0;
1415  tagflags = RPMSENSE_FIND_REQUIRES;
1416  /*@switchbreak@*/ break;
1417  default:
1418  continue;
1419  /*@notreached@*/ /*@switchbreak@*/ break;
1420  }
1421 
1422  xx = rpmfcExec(dm->argv, iob_stdin, &iob_stdout, failnonzero);
1423  if (xx == -1)
1424  continue;
1425 
1426  s = rpmExpand(dm->argv[0], NULL);
1427  rpmlog(RPMLOG_NOTICE, _("Finding %s: %s\n"), dm->msg,
1428  (s ? s : ""));
1429  s = _free(s);
1430 
1431  if (iob_stdout == NULL) {
1432  rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1433  rc = RPMRC_FAIL;
1434  break;
1435  }
1436 
1437  /* Parse dependencies into header */
1438  if (spec->_parseRCPOT)
1439  rc = spec->_parseRCPOT(spec, pkg, rpmiobStr(iob_stdout), tag,
1440  0, tagflags);
1441  iob_stdout = rpmiobFree(iob_stdout);
1442 
1443  if (rc) {
1444  rpmlog(RPMLOG_ERR, _("Failed to find %s:\n"), dm->msg);
1445  break;
1446  }
1447  }
1448 
1449  iob_stdin = rpmiobFree(iob_stdin);
1450 
1451  return rc;
1452 }
1453 
1456 /*@-nullassign@*/
1457 /*@unchecked@*/
1458 static struct DepMsg_s scriptMsgs[] = {
1459  { "Requires(pre)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1461  RPMSENSE_SCRIPT_PRE, 0 },
1462  { "Requires(post)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1464  RPMSENSE_SCRIPT_POST, 0 },
1465  { "Requires(preun)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1467  RPMSENSE_SCRIPT_PREUN, 0 },
1468  { "Requires(postun)", { "%{?__scriptlet_requires}", NULL, NULL, NULL },
1470  RPMSENSE_SCRIPT_POSTUN, 0 },
1471  { NULL, { NULL, NULL, NULL, NULL }, 0, 0, 0, 0, 0 }
1472 };
1473 /*@=nullassign@*/
1474 
1475 /*@unchecked@*/
1476 static DepMsg_t ScriptMsgs = scriptMsgs;
1477 
1480 static int rpmfcGenerateScriptletDeps(const Spec spec, Package pkg)
1481  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1482  /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
1483 {
1484  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
1485  rpmiob iob_stdin = rpmiobNew(0);
1486  rpmiob iob_stdout = NULL;
1487  DepMsg_t dm;
1488  int failnonzero = 0;
1489  int rc = 0;
1490  int xx;
1491 
1492  for (dm = ScriptMsgs; dm->msg != NULL; dm++) {
1493  int tag, tagflags;
1494  char * s;
1495 
1496  tag = dm->ftag;
1497  tagflags = RPMSENSE_FIND_REQUIRES | dm->mask;
1498 
1499  /* Retrieve scriptlet interpreter. */
1500  he->tag = dm->ntag;
1501  xx = headerGet(pkg->header, he, 0);
1502  if (!xx || he->p.str == NULL)
1503  continue;
1504  xx = strcmp(he->p.str, "/bin/sh") && strcmp(he->p.str, "/bin/bash");
1505  he->p.ptr = _free(he->p.ptr);
1506  if (xx)
1507  continue;
1508 
1509  /* Retrieve scriptlet body. */
1510  he->tag = dm->vtag;
1511  xx = headerGet(pkg->header, he, 0);
1512  if (!xx || he->p.str == NULL)
1513  continue;
1514  iob_stdin = rpmiobEmpty(iob_stdin);
1515  iob_stdin = rpmiobAppend(iob_stdin, he->p.str, 1);
1516  iob_stdin = rpmiobRTrim(iob_stdin);
1517  he->p.ptr = _free(he->p.ptr);
1518 
1519  xx = rpmfcExec(dm->argv, iob_stdin, &iob_stdout, failnonzero);
1520  if (xx == -1)
1521  continue;
1522 
1523  /* Parse dependencies into header */
1524  s = rpmiobStr(iob_stdout);
1525  if (s != NULL && *s != '\0') {
1526  char * se = s;
1527  /* XXX Convert "executable(/path/to/file)" to "/path/to/file". */
1528  while ((se = strstr(se, "executable(/")) != NULL) {
1529 /*@-modobserver@*/ /* FIX: rpmiobStr should not be observer */
1530  se = stpcpy(se, " ");
1531  *se = '/'; /* XXX stpcpy truncates the '/' */
1532 /*@=modobserver@*/
1533  se = strchr(se, ')');
1534  if (se == NULL)
1535  /*@innerbreak@*/ break;
1536  *se++ = ' ';
1537  }
1538  if (spec->_parseRCPOT)
1539  rc = spec->_parseRCPOT(spec, pkg, s, tag, 0, tagflags);
1540  }
1541  iob_stdout = rpmiobFree(iob_stdout);
1542 
1543  }
1544 
1545  iob_stdin = rpmiobFree(iob_stdin);
1546 
1547  return rc;
1548 }
1549 
1550 rpmRC rpmfcGenerateDepends(void * specp, void * pkgp)
1551 {
1552  HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
1553  const Spec spec = specp;
1554  Package pkg = pkgp;
1555  rpmfi fi = pkg->cpioList;
1556  rpmfc fc = NULL;
1557  rpmds ds;
1558  int flags = 0x2; /* XXX no filtering, !scareMem */
1559  ARGV_t av;
1560  rpmuint16_t * fmode;
1561  int ac = rpmfiFC(fi);
1562  char buf[BUFSIZ];
1563  const char * N;
1564  const char * EVR;
1565  int genConfigDeps, internaldeps;
1566  rpmRC rc = RPMRC_OK;
1567  int i;
1568  int xx;
1569 
1570  /* Skip packages with no files. */
1571  if (ac <= 0)
1572  return RPMRC_OK;
1573 
1574  /* Skip packages that have dependency generation disabled. */
1575  if (! (pkg->autoReq || pkg->autoProv))
1576  return RPMRC_OK;
1577 
1578  /* If new-fangled dependency generation is disabled ... */
1579  internaldeps = rpmExpandNumeric("%{?_use_internal_dependency_generator}");
1580  if (internaldeps == 0) {
1581  /* ... then generate dependencies using %{__find_requires} et al. */
1582  rc = rpmfcGenerateDependsHelper(spec, pkg, fi);
1583  printDeps(pkg->header);
1584  return rc;
1585  }
1586 
1587  /* Generate scriptlet Dependencies. */
1588  if (internaldeps > 1)
1589  xx = rpmfcGenerateScriptletDeps(spec, pkg);
1590 
1591  /* Extract absolute file paths in argv format. */
1592  /* XXX TODO: should use argvFoo ... */
1593  av = xcalloc(ac+1, sizeof(*av));
1594  fmode = xcalloc(ac+1, sizeof(*fmode));
1595 
1596  genConfigDeps = 0;
1597  fi = rpmfiInit(fi, 0);
1598  if (fi != NULL)
1599  while ((i = rpmfiNext(fi)) >= 0) {
1600  rpmfileAttrs fileAttrs;
1601 
1602  /* Does package have any %config files? */
1603  fileAttrs = rpmfiFFlags(fi);
1604  genConfigDeps |= (fileAttrs & RPMFILE_CONFIG);
1605 
1606  av[i] = xstrdup(rpmfiFN(fi));
1607  fmode[i] = rpmfiFMode(fi);
1608  }
1609  av[ac] = NULL;
1610 
1611  fc = rpmfcNew();
1612  fc->skipProv = !pkg->autoProv;
1613  fc->skipReq = !pkg->autoReq;
1614  fc->tracked = 0;
1615 
1616  { const char * buildRootURL;
1617  const char * buildRoot;
1618  buildRootURL = rpmGenPath(spec->rootURL, "%{?buildroot}", NULL);
1619  (void) urlPath(buildRootURL, &buildRoot);
1620  if (buildRoot && !strcmp(buildRoot, "/")) buildRoot = NULL;
1621  fc->brlen = (buildRoot ? strlen(buildRoot) : 0);
1622  buildRootURL = _free(buildRootURL);
1623  }
1624 
1625  /* Copy (and delete) manually generated dependencies to dictionary. */
1626  if (!fc->skipProv) {
1627  ds = rpmdsNew(pkg->header, RPMTAG_PROVIDENAME, flags);
1628  xx = rpmdsMerge(&fc->provides, ds);
1629  (void)rpmdsFree(ds);
1630  ds = NULL;
1631  he->tag = RPMTAG_PROVIDENAME;
1632  xx = headerDel(pkg->header, he, 0);
1633  he->tag = RPMTAG_PROVIDEVERSION;
1634  xx = headerDel(pkg->header, he, 0);
1635  he->tag = RPMTAG_PROVIDEFLAGS;
1636  xx = headerDel(pkg->header, he, 0);
1637 
1638  /* Add config dependency, Provides: config(N) = EVR */
1639  if (genConfigDeps) {
1640  N = rpmdsN(pkg->ds);
1641 assert(N != NULL);
1642  EVR = rpmdsEVR(pkg->ds);
1643 assert(EVR != NULL);
1644  sprintf(buf, "config(%s)", N);
1645  ds = rpmdsSingle(RPMTAG_PROVIDENAME, buf, EVR,
1646  (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1647  xx = rpmdsMerge(&fc->provides, ds);
1648  (void)rpmdsFree(ds);
1649  ds = NULL;
1650  }
1651  }
1652 
1653  if (!fc->skipReq) {
1654  ds = rpmdsNew(pkg->header, RPMTAG_REQUIRENAME, flags);
1655  xx = rpmdsMerge(&fc->requires, ds);
1656  (void)rpmdsFree(ds);
1657  ds = NULL;
1658  he->tag = RPMTAG_REQUIRENAME;
1659  xx = headerDel(pkg->header, he, 0);
1660  he->tag = RPMTAG_REQUIREVERSION;
1661  xx = headerDel(pkg->header, he, 0);
1662  he->tag = RPMTAG_REQUIREFLAGS;
1663  xx = headerDel(pkg->header, he, 0);
1664 
1665  /* Add config dependency, Requires: config(N) = EVR */
1666  if (genConfigDeps) {
1667  N = rpmdsN(pkg->ds);
1668 assert(N != NULL);
1669  EVR = rpmdsEVR(pkg->ds);
1670 assert(EVR != NULL);
1671  sprintf(buf, "config(%s)", N);
1672  ds = rpmdsSingle(RPMTAG_REQUIRENAME, buf, EVR,
1673  (RPMSENSE_EQUAL|RPMSENSE_CONFIG));
1674  xx = rpmdsMerge(&fc->requires, ds);
1675  (void)rpmdsFree(ds);
1676  ds = NULL;
1677  }
1678  }
1679 
1680  /* Build file class dictionary. */
1681  xx = rpmfcClassify(fc, av, fmode);
1682 
1683  /* Build file/package dependency dictionary. */
1684  xx = rpmfcApply(fc);
1685 
1686  /* Add per-file colors(#files) */
1687  he->tag = RPMTAG_FILECOLORS;
1688  he->t = RPM_UINT32_TYPE;
1689  he->p.ui32p = argiData(fc->fcolor);
1690  he->c = argiCount(fc->fcolor);
1691 assert(ac == (int)he->c);
1692  if (he->p.ptr != NULL && he->c > 0) {
1693  rpmuint32_t * fcolors = he->p.ui32p;
1694 
1695  /* XXX Make sure only primary (i.e. Elf32/Elf64) colors are added. */
1696  for (i = 0; i < (int)he->c; i++)
1697  fcolors[i] &= 0x0f;
1698 
1699  xx = headerPut(pkg->header, he, 0);
1700  }
1701 
1702  /* Add classes(#classes) */
1703  he->tag = RPMTAG_CLASSDICT;
1704  he->t = RPM_STRING_ARRAY_TYPE;
1705  he->p.argv = argvData(fc->cdict);
1706  he->c = argvCount(fc->cdict);
1707  if (he->p.ptr != NULL && he->c > 0) {
1708  xx = headerPut(pkg->header, he, 0);
1709  }
1710 
1711  /* Add per-file classes(#files) */
1712  he->tag = RPMTAG_FILECLASS;
1713  he->t = RPM_UINT32_TYPE;
1714  he->p.ui32p = argiData(fc->fcdictx);
1715  he->c = argiCount(fc->fcdictx);
1716 assert(ac == (int)he->c);
1717  if (he->p.ptr != NULL && he->c > 0) {
1718  xx = headerPut(pkg->header, he, 0);
1719  }
1720 
1721  /* Add Provides: */
1722  if (fc->provides != NULL && (he->c = rpmdsCount(fc->provides)) > 0
1723  && !fc->skipProv)
1724  {
1725  he->tag = RPMTAG_PROVIDENAME;
1726  he->t = RPM_STRING_ARRAY_TYPE;
1727  he->p.argv = fc->provides->N;
1728  xx = headerPut(pkg->header, he, 0);
1729 
1730  /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
1731 /*@-nullpass@*/
1732  he->tag = RPMTAG_PROVIDEVERSION;
1733  he->t = RPM_STRING_ARRAY_TYPE;
1734  he->p.argv = fc->provides->EVR;
1735 assert(he->p.ptr != NULL);
1736  xx = headerPut(pkg->header, he, 0);
1737 
1738  he->tag = RPMTAG_PROVIDEFLAGS;
1739  he->t = RPM_UINT32_TYPE;
1740  he->p.ui32p = (rpmuint32_t *) fc->provides->Flags;
1741 assert(he->p.ptr != NULL);
1742  xx = headerPut(pkg->header, he, 0);
1743 /*@=nullpass@*/
1744  }
1745 
1746  /* Add Requires: */
1747  if (fc->requires != NULL && (he->c = rpmdsCount(fc->requires)) > 0
1748  && !fc->skipReq)
1749  {
1750  he->tag = RPMTAG_REQUIRENAME;
1751  he->t = RPM_STRING_ARRAY_TYPE;
1752  he->p.argv = fc->requires->N;
1753 assert(he->p.ptr != NULL);
1754  xx = headerPut(pkg->header, he, 0);
1755 
1756  /* XXX rpm prior to 3.0.2 did not always supply EVR and Flags. */
1757 /*@-nullpass@*/
1758  he->tag = RPMTAG_REQUIREVERSION;
1759  he->t = RPM_STRING_ARRAY_TYPE;
1760  he->p.argv = fc->requires->EVR;
1761 assert(he->p.ptr != NULL);
1762  xx = headerPut(pkg->header, he, 0);
1763 
1764  he->tag = RPMTAG_REQUIREFLAGS;
1765  he->t = RPM_UINT32_TYPE;
1766  he->p.ui32p = (rpmuint32_t *) fc->requires->Flags;
1767 assert(he->p.ptr != NULL);
1768  xx = headerPut(pkg->header, he, 0);
1769 /*@=nullpass@*/
1770  }
1771 
1772  /* Add dependency dictionary(#dependencies) */
1773  he->tag = RPMTAG_DEPENDSDICT;
1774  he->t = RPM_UINT32_TYPE;
1775  he->p.ui32p = argiData(fc->ddictx);
1776  he->c = argiCount(fc->ddictx);
1777  if (he->p.ptr != NULL) {
1778  xx = headerPut(pkg->header, he, 0);
1779  }
1780 
1781  /* Add per-file dependency (start,number) pairs (#files) */
1782  he->tag = RPMTAG_FILEDEPENDSX;
1783  he->t = RPM_UINT32_TYPE;
1784  he->p.ui32p = argiData(fc->fddictx);
1785  he->c = argiCount(fc->fddictx);
1786 assert(ac == (int)he->c);
1787  if (he->p.ptr != NULL) {
1788  xx = headerPut(pkg->header, he, 0);
1789  }
1790 
1791  he->tag = RPMTAG_FILEDEPENDSN;
1792  he->t = RPM_UINT32_TYPE;
1793  he->p.ui32p = argiData(fc->fddictn);
1794  he->c = argiCount(fc->fddictn);
1795 assert(ac == (int)he->c);
1796  if (he->p.ptr != NULL) {
1797  xx = headerPut(pkg->header, he, 0);
1798  }
1799 
1800  printDeps(pkg->header);
1801 
1802 if (fc != NULL && _rpmfc_debug) {
1803 char msg[BUFSIZ];
1804 sprintf(msg, "final: files %u cdict[%d] %u%% ddictx[%d]", (unsigned int)fc->nfiles, argvCount(fc->cdict), (unsigned int)((100 * fc->fknown)/fc->nfiles), argiCount(fc->ddictx));
1805 rpmfcPrint(msg, fc, NULL);
1806 }
1807 
1808  /* Clean up. */
1809  fmode = _free(fmode);
1810  fc = rpmfcFree(fc);
1811  av = argvFree(av);
1812 
1813  return rc;
1814 }
1815 
1816 /*@-mustmod@*/
1817 static void rpmfcFini(void * _fc)
1818  /*@modifies _fc @*/
1819 {
1820  rpmfc fc = _fc;
1821 
1822  fc->fn = argvFree(fc->fn);
1823  fc->fcolor = argiFree(fc->fcolor);
1824  fc->fcdictx = argiFree(fc->fcdictx);
1825  fc->fddictx = argiFree(fc->fddictx);
1826  fc->fddictn = argiFree(fc->fddictn);
1827  fc->cdict = argvFree(fc->cdict);
1828  fc->ddict = argvFree(fc->ddict);
1829  fc->ddictx = argiFree(fc->ddictx);
1830 
1831  (void)rpmdsFree(fc->provides);
1832  fc->provides = NULL;
1833  (void)rpmdsFree(fc->requires);
1834  fc->requires = NULL;
1835 
1836  fc->iob_java = rpmiobFree(fc->iob_java);
1837  fc->iob_perl = rpmiobFree(fc->iob_perl);
1838  fc->iob_python = rpmiobFree(fc->iob_python);
1839  fc->iob_php = rpmiobFree(fc->iob_php);
1840 }
1841 /*@=mustmod@*/
1842 
1843 /*@unchecked@*/ /*@only@*/ /*@null@*/
1845 
1846 static rpmfc rpmfcGetPool(/*@null@*/ rpmioPool pool)
1847  /*@globals _rpmfcPool, fileSystem, internalState @*/
1848  /*@modifies pool, _rpmfcPool, fileSystem, internalState @*/
1849 {
1850  rpmfc fc;
1851 
1852  if (_rpmfcPool == NULL) {
1853  _rpmfcPool = rpmioNewPool("fc", sizeof(*fc), -1, _rpmfc_debug,
1854  NULL, NULL, rpmfcFini);
1855  pool = _rpmfcPool;
1856  }
1857  return (rpmfc) rpmioGetPool(pool, sizeof(*fc));
1858 }
1859 
1861 {
1862  rpmfc fc = rpmfcGetPool(_rpmfcPool);
1863  return rpmfcLink(fc);
1864 }
1865