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