rpm  5.2.1
rpmdigest.c
Go to the documentation of this file.
1 #include "system.h"
2 /*@unchecked@*/
3 extern const char * __progname;
4 
5 #define _RPMIOB_INTERNAL
6 #include <rpmiotypes.h>
7 #include <rpmio_internal.h> /* XXX fdGetFILE */
8 #include <poptIO.h>
9 #include "debug.h"
10 
11 static int _rpmdc_debug = 0;
12 
13 /* XXX older 0install manifest format. */
14 static int _old_0install = 0;
15 
16 #define _KFB(n) (1U << (n))
17 #define _DFB(n) (_KFB(n) | 0x40000000)
18 
19 #define F_ISSET(_dc, _FLAG) ((_dc)->flags & ((RPMDC_FLAGS_##_FLAG) & ~0x40000000))
20 
24 enum dcFlags_e {
26  /* 0 reserved */
30  /* 4-13 reserved */
34  /* 17-31 unused */
35 };
36 
39 typedef struct rpmdc_s * rpmdc;
40 
43 struct rpmdc_s {
44  int ftsoptions;
45  FTS * t;
46  FTSENT * p;
47  struct stat sb;
50  uint32_t algo;
51  uint32_t dalgo;
52 /*@observer@*/ /*@null@*/
53  const char * dalgoName;
54  const char * digest;
55  size_t digestlen;
56  const char * fn;
58  int (*parse) (rpmdc dc);
59  const char * (*print) (rpmdc dc, int rc);
60  const char * ofn;
62  uint32_t oalgo;
63  const char * oalgoName;
68  unsigned char buf[BUFSIZ];
69  ssize_t nb;
70  int ix;
71 
72  size_t ncomputed;
73  size_t nchecked;
74  size_t nmatched;
75  size_t nfailed;
76  struct rpmop_s totalops;
77  struct rpmop_s readops;
79 };
80 
83 static struct rpmdc_s _dc = {
85  .flags = RPMDC_FLAGS_CREATE
86 };
87 
90 static rpmdc dc = &_dc;
91 
92 /*==============================================================*/
93 static uint32_t rpmdcName2Algo(const char * dname)
94  /*@*/
95 {
96  struct poptOption * opt = rpmioDigestPoptTable;
97  uint32_t dalgo = 0xffffffff;
98 
99  /* XXX compatible with 0install legacy derangement. bug imho. */
100  if (!strcmp(dname, "sha1new"))
101  dname = "sha1";
102 
103  for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
104  if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
105  continue;
106  if (opt->longName == NULL)
107  continue;
108  if (!(opt->val > 0 && opt->val < 256))
109  continue;
110  if (strcmp(opt->longName, dname))
111  continue;
112  dalgo = (uint32_t) opt->val;
113  break;
114  }
115  return dalgo;
116 }
117 
118 /*@null@*/
119 static const char * rpmdcAlgo2Name(uint32_t dalgo)
120  /*@*/
121 {
122  struct poptOption * opt = rpmioDigestPoptTable;
123  const char * dalgoName = NULL;
124 
125  for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
126  if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
127  continue;
128  if (opt->longName == NULL)
129  continue;
130  if (!(opt->val > 0 && opt->val < 256))
131  continue;
132  if ((uint32_t)opt->val != dalgo)
133  continue;
134  dalgoName = opt->longName;
135  break;
136  }
137  return dalgoName;
138 }
139 
140 /*==============================================================*/
141 
142 static int rpmdcParseCoreutils(rpmdc dc)
143  /*@globals h_errno, fileSystem, internalState @*/
144  /*@modifies h_errno, fileSystem, internalState @*/
145 {
146  int rc = -1; /* assume failure */
147 
148  if (dc->manifests != NULL) /* note rc=0 return with no files to load. */
149  while ((dc->fn = *dc->manifests++) != NULL) {
150  char buf[BUFSIZ];
151  unsigned lineno;
152  FILE *fp;
153 
154  if (strcmp(dc->fn, "-") == 0) {
155  dc->fd = NULL;
156  fp = stdin;
157  } else {
158  /* XXX .fpio is needed because of fgets(3) usage. */
159  dc->fd = Fopen(dc->fn, "r.fpio");
160  if (dc->fd == NULL || Ferror(dc->fd) || (fp = fdGetFILE(dc->fd)) == NULL) {
161  fprintf(stderr, _("%s: Failed to open %s: %s\n"),
162  __progname, dc->fn, Fstrerror(dc->fd));
163  if (dc->fd != NULL) (void) Fclose(dc->fd);
164  dc->fd = NULL;
165  fp = NULL;
166  goto exit;
167  }
168  }
169 
170  lineno = 0;
171  while (fgets(buf, sizeof(buf), fp) != NULL) {
172  const char * dname, * digest, * path;
173  char *se = buf + (int)strlen(buf);
174  int c, xx;
175 
176  lineno++;
177  while (se > buf && xisspace((int)se[-1]))
178  se--;
179  *se = '\0';
180 
181  /* Skip blank lines */
182  if (buf[0] == '\0') /*@innercontinue@*/ continue;
183  /* Skip comment lines */
184  if (buf[0] == '#') /*@innercontinue@*/ continue;
185 
186  /* Parse "[algo:]digest [* ]path" line. */
187  dname = NULL; path = NULL;
188  for (digest = se = buf; (c = (int)*se) != 0; se++)
189  switch (c) {
190  default:
191  /*@switchbreak@*/ break;
192  case ':':
193  *se++ = '\0';
194  dname = digest;
195  digest = se;
196  /*@switchbreak@*/ break;
197  case ' ':
198  se[0] = '\0'; /* loop will terminate */
199  if (se[1] == ' ' || se[1] == '*')
200  se[1] = '\0';
201  path = se + 2;
202  /*@switchbreak@*/ break;
203  }
204  if (path == NULL) {
205  fprintf(stderr, _("%s: %s line %u: No file path found.\n"),
206  __progname, dc->fn, lineno);
207  rc = 2;
208  goto exit;
209  }
210 
211  /* Map name to algorithm number. */
212  if (dname) {
213  if ((dc->dalgo = rpmdcName2Algo(dname)) != 0xffffffff)
214  dc->dalgoName = xstrdup(dname);
215  if (dc->dalgo == 0xffffffff) {
216  fprintf(stderr, _("%s: %s line %u: Unknown digest name \"%s\"\n"),
217  __progname, dc->fn, lineno, dname);
218  rc = 2;
219  goto exit;
220  }
221  } else
222  dc->dalgo = dc->algo;
223 
224  /* Save {algo, digest, path} for processing. */
225  xx = argiAdd(&dc->algos, -1, dc->dalgo);
226  xx = argvAdd(&dc->digests, digest);
227  xx = argvAdd(&dc->paths, path);
228  }
229 
230  if (dc->fd != NULL) {
231  (void) Fclose(dc->fd);
232  dc->fd = NULL;
233  }
234  }
235  rc = 0;
236 
237 exit:
238  return rc;
239 }
240 
241 /*@null@*/
242 static const char * rpmdcPrintCoreutils(rpmdc dc, int rc)
243 {
244  const char *msg = (rc ? "FAILED" : "OK");
245  char * t, * te;
246  size_t nb = 0;
247 
248  /* Don't bother formatting if noone cares. */
249  if (rc == 0 && F_ISSET(dc, STATUS))
250  return NULL;
251 
252  /* Calculate size of message. */
253  if (dc->dalgoName != NULL)
254  nb += strlen(dc->dalgoName) + sizeof(":") - 1;
255 assert(dc->digest != NULL);
256  if (dc->digest != NULL && dc->digestlen > 0)
257  nb += dc->digestlen;
258  nb += sizeof(" *") - 1;
259  if (dc->fn != NULL)
260  nb += strlen(dc->fn);
261  nb += strlen(msg);
262  nb += sizeof("\n"); /* XXX trailing NUL */
263 
264  /* Compose the message. */
265  te = t = xmalloc(nb);
266  *te = '\0';
267 
268  if (dc->manifests) {
269  if (rc || !F_ISSET(dc, STATUS)) {
270  if (dc->fn)
271  te = stpcpy( stpcpy(te, dc->fn), ": ");
272  te = stpcpy(te, msg);
273  *te++ = '\n';
274  }
275  } else {
276  if (dc->dalgoName)
277  te = stpcpy( stpcpy(te, dc->dalgoName), ":");
278  te = stpcpy(te, dc->digest);
279  *te++ = ' ';
280  *te++ = (F_ISSET(dc, BINARY) ? '*' : ' ');
281  te = stpcpy(te, dc->fn);
282  *te++ = '\n';
283  }
284  *te = '\0';
285 
286  return t;
287 }
288 
289 /*==============================================================*/
290 
291 static int rpmdcParseZeroInstall(rpmdc dc)
292  /*@globals h_errno, fileSystem, internalState @*/
293  /*@modifies h_errno, fileSystem, internalState @*/
294 {
295  int rc = 0; /* assume success */
296 
297  if (dc->manifests != NULL) /* note rc=0 return with no files to load. */
298  while ((dc->fn = *dc->manifests++) != NULL) {
299  unsigned lineno;
300  char * be;
301  rpmiob iob = NULL;
302  int xx = rpmiobSlurp(dc->fn, &iob);
303  const char * digest;
304  char * f;
305  char * fe;
306 
307  if (!(xx == 0 && iob != NULL)) {
308  fprintf(stderr, _("%s: Failed to open %s\n"), __progname, dc->fn);
309  rc = -1;
310  goto bottom;
311  }
312 
313  be = (char *)(iob->b + iob->blen);
314  while (be > (char *)iob->b && (be[-1] == '\n' || be[-1] == '\r')) {
315  be--;
316  *be = '\0';
317  }
318 
319  /* Parse "algo=digest" from last line. */
320  be = strrchr((char *)iob->b, '=');
321  if (be == NULL) {
322  fprintf(stderr,
323  _("%s: %s: Manifest needs \"algo=digest\" as last line\n"),
324  __progname, dc->fn);
325  rc = 2;
326  goto bottom;
327  }
328  *be = '\0';
329  dc->digest = be + 1;
330  while (be > (char *)iob->b && !(be[-1] == '\n' || be[-1] == '\r'))
331  be--;
332  if (be <= (char *)iob->b) {
333  fprintf(stderr, _("%s: %s: Manifest is empty\n"),
334  __progname, dc->fn);
335  rc = 2;
336  goto bottom;
337  }
338 
339  /* Map name to algorithm number. */
340  if ((dc->dalgo = rpmdcName2Algo(be)) == 0xffffffff) {
341  fprintf(stderr, _("%s: %s: Unknown digest algo name \"%s\"\n"),
342  __progname, dc->fn, be);
343  rc = 2;
344  goto bottom;
345  }
346  *be = '\0';
347 
348  /* Verify the manifest digest. */
349  { DIGEST_CTX ctx = rpmDigestInit(dc->dalgo, 0);
350 
351  (void) rpmDigestUpdate(ctx, (char *)iob->b, (be - (char *)iob->b));
352  digest = NULL;
353  (void) rpmDigestFinal(ctx, &digest, NULL, 1);
354  if (strcmp(dc->digest, digest)) {
355  fprintf(stderr,
356  _("%s: %s: Manifest digest check: Expected(%s) != (%s)\n"),
357  __progname, dc->fn, dc->digest, digest);
358  rc = 2;
359  goto bottom;
360  }
361  digest = _free(digest);
362  }
363 
364  /* Parse and save manifest items. */
365  lineno = 0;
366  for (f = (char *)iob->b; *f; f = fe) {
367  static const char hexdigits[] = "0123456789ABCDEFabcdef";
368  const char * _dn = NULL;
369  const char * path;
370 
371  lineno++;
372  fe = f;
373  while (*fe && !(*fe == '\n' || *fe == '\r'))
374  fe++;
375  while (*fe && (*fe == '\n' || *fe == '\r'))
376  *fe++ = '\0';
377  switch ((int)*f) {
378  case 'D':
379  _dn = f + 2;
380  continue;
381  /*@notreached@*/ break;
382  case 'F':
383  case 'S':
384  case 'X':
385  digest = f + 2;
386  f += 2;
387  while (*f && strchr(hexdigits, *f) != NULL)
388  f++;
389  if (*f != ' ') {
390  fprintf(stderr, _("%s: %s line %u: Malformed digest field.\n"),
391  __progname, dc->fn, lineno);
392  rc = 2;
393  goto bottom;
394  }
395  *f++ = '\0';
396  while (*f && xisdigit(*f))
397  f++;
398  if (*f != ' ') {
399  fprintf(stderr, _("%s: %s line %u: Malformed mtime field.\n"),
400  __progname, dc->fn, lineno);
401  rc = 2;
402  goto bottom;
403  }
404  *f++ = '\0';
405  while (*f && xisdigit(*f))
406  f++;
407  if (*f != ' ') {
408  fprintf(stderr, _("%s: %s line %u: Malformed size field.\n"),
409  __progname, dc->fn, lineno);
410  rc = 2;
411  goto bottom;
412  }
413  *f++ = '\0';
414  if (*f == '\0') {
415  fprintf(stderr, _("%s: %s line %u: No file path.\n"),
416  __progname, dc->fn, lineno);
417  rc = 2;
418  goto bottom;
419  }
420 
421  if (_dn && *_dn == '/')
422  path = rpmExpand(_dn+1, "/", f, NULL);
423  else
424  path = xstrdup(f);
425 
426  /* Save {algo, digest, path} for processing. */
427  xx = argiAdd(&dc->algos, -1, dc->dalgo);
428  xx = argvAdd(&dc->digests, digest);
429  xx = argvAdd(&dc->paths, path);
430  path = _free(path);
431  break;
432  }
433  }
434 
435 bottom:
436  iob = rpmiobFree(iob);
437  if (rc != 0)
438  goto exit;
439  }
440 
441 exit:
442  return rc;
443 }
444 
445 /*@null@*/
446 static const char * rpmdcPrintZeroInstall(rpmdc dc, int rc)
447 {
448  char * t, * te;
449  size_t nb = 0;
450  char _mtime[32];
451  char _size[32];
452  const struct stat * st = &dc->sb;
453  const char * _bn;
454 
455  /* Don't bother formatting if noone cares. */
456  if (rc == 0 && F_ISSET(dc, STATUS))
457  return NULL;
458 
459  snprintf(_mtime, sizeof(_mtime), "%llu",
460  (unsigned long long) st->st_mtime);
461  _mtime[sizeof(_mtime)-1] = '\0';
462  snprintf(_size, sizeof(_size), "%llu",
463  (unsigned long long) st->st_size);
464  _size[sizeof(_size)-1] = '\0';
465  if ((_bn = strrchr(dc->fn, '/')) != NULL)
466  _bn++;
467  else
468  _bn = dc->fn;
469 
470  /* Calculate size of message. */
471  nb += sizeof("F");
472  if (dc->digest != NULL && dc->digestlen > 0)
473  nb += 1 + dc->digestlen;
474  nb += 1 + strlen(_mtime);
475  nb += 1 + strlen(_size);
476  nb += 1 + strlen(_bn);
477  nb += sizeof("\n"); /* XXX trailing NUL */
478 
479  /* Compose the message. */
480  te = t = xmalloc(nb);
481  *te = '\0';
482 
483  if (dc->manifests) {
484  const char *msg = (rc ? "FAILED" : "OK");
485  if (rc || !F_ISSET(dc, STATUS)) {
486  if (dc->fn)
487  te = stpcpy( stpcpy(te, dc->fn), ": ");
488  te = stpcpy(te, msg);
489  *te++ = '\n';
490  }
491  } else {
492  if (S_ISDIR(st->st_mode)) {
493  *te++ = 'D';
494  if (_old_0install) {
495  *te++ = ' ';
496  te = stpcpy(te, _mtime);
497  }
498  *te++ = ' ';
499  *te++ = '/';
500  te = stpcpy(te, _bn);
501  *te++ = '\n';
502  } else if (S_ISREG(st->st_mode) || S_ISLNK(st->st_mode)) {
503  if (S_ISLNK(st->st_mode))
504  *te++ = 'S';
505  else
506  *te++ = (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ? 'X' : 'F';
507  *te++ = ' ';
508  te = stpcpy(te, dc->digest);
509  *te++ = ' ';
510  te = stpcpy(te, _mtime);
511  *te++ = ' ';
512  te = stpcpy(te, _size);
513  *te++ = ' ';
514  te = stpcpy(te, _bn);
515  *te++ = '\n';
516  }
517  }
518  *te = '\0';
519 
520  return t;
521 }
522 
523 /*==============================================================*/
524 
525 static int rpmdcPrintFile(rpmdc dc)
526 {
527  static int asAscii = 1;
528  int rc = 0;
529 
530 if (_rpmdc_debug)
531 fprintf(stderr, "\trpmdcPrintFile(%p) fd %p fn %s\n", dc, dc->fd, dc->fn);
532 
533 assert(dc->fd != NULL);
534  fdFiniDigest(dc->fd, dc->dalgo, &dc->digest, &dc->digestlen, asAscii);
535 assert(dc->digest != NULL);
536  dc->ncomputed++;
537 
538  if (dc->manifests) {
539  dc->nchecked++;
540  if ((rc = strcmp(dc->digest, dc->digests[dc->ix])) != 0)
541  dc->nfailed++;
542  else
543  dc->nmatched++;
544  }
545 
546  { const char * t = (*dc->print) (dc, rc);
547  if (dc->ofd && t && *t) {
548  size_t nb = strlen(t);
549  nb = Fwrite(t, nb, sizeof(*t), dc->ofd);
550  (void) Fflush(dc->ofd);
551  }
552  t = _free(t);
553  }
554 
555  dc->digest = _free(dc->digest);
556  dc->digestlen = 0;
557  return rc;
558 }
559 
560 static int rpmdcFiniFile(rpmdc dc)
561 {
562  uint32_t dalgo = (dc->manifests ? dc->algos->vals[dc->ix] : dc->algo);
563  int rc = 0;
564  int xx;
565 
566  /* Only regular files have dc->fd != NULL here. Skip all other paths. */
567  if (dc->fd == NULL)
568  return rc;
569 
570 if (_rpmdc_debug)
571 fprintf(stderr, "\trpmdcFiniFile(%p) fn %s\n", dc, dc->fn);
572  switch (dalgo) {
573  default:
574  dc->dalgo = dalgo;
575  dc->dalgoName = NULL;
576  xx = rpmdcPrintFile(dc);
577  if (xx) rc = xx;
578  break;
579  case 256: /* --all digests requested. */
580  { struct poptOption * opt = rpmioDigestPoptTable;
581  for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
582  if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
583  continue;
584  if (opt->arg != (void *)&rpmioDigestHashAlgo)
585  continue;
586  dc->dalgo = opt->val;
587  if (!(dc->dalgo > 0 && dc->dalgo < 256))
588  continue;
589  dc->dalgoName = opt->longName;
590  xx = rpmdcPrintFile(dc);
591  if (xx) rc = xx;
592  }
593  } break;
594  }
595 
596  (void) rpmswAdd(&dc->readops, fdstat_op(dc->fd, FDSTAT_READ));
597  (void) rpmswAdd(&dc->digestops, fdstat_op(dc->fd, FDSTAT_DIGEST));
598  Fclose(dc->fd);
599  dc->fd = NULL;
600 
601  return rc;
602 }
603 
604 static int rpmdcCalcFile(rpmdc dc)
605 {
606  int rc = 0;
607 
608 if (_rpmdc_debug)
609 fprintf(stderr, "\trpmdcCalcFile(%p) fn %s\n", dc, dc->fn);
610  /* Skip (unopened) non-files. */
611  if (dc->fd != NULL)
612  do {
613  dc->nb = Fread(dc->buf, sizeof(dc->buf[0]), sizeof(dc->buf), dc->fd);
614  if (Ferror(dc->fd)) {
615  rc = 2;
616  break;
617  }
618  } while (dc->nb > 0);
619 
620  return rc;
621 }
622 
623 static int rpmdcInitFile(rpmdc dc)
624 {
625  int rc = 0;
626 
627 if (_rpmdc_debug)
628 fprintf(stderr, "\trpmdcInitFile(%p) fn %s\n", dc, dc->fn);
629  /* Skip non-files. */
630  if (!S_ISREG(dc->sb.st_mode)) {
631  /* XXX not found return code? */
632  goto exit;
633  }
634 
635  dc->fd = Fopen(dc->fn, "r.ufdio");
636  if (dc->fd == NULL || Ferror(dc->fd)) {
637  fprintf(stderr, _("open of %s failed: %s\n"), dc->fn, Fstrerror(dc->fd));
638  if (dc->fd != NULL) Fclose(dc->fd);
639  dc->fd = NULL;
640  rc = 2;
641  goto exit;
642  }
643 
644  switch (dc->algo) {
645  default:
646  /* XXX TODO: instantiate verify digests for all identical paths. */
647  dc->dalgo = dc->algo;
648  fdInitDigest(dc->fd, dc->dalgo, 0);
649  break;
650  case 256: /* --all digests requested. */
651  { struct poptOption * opt = rpmioDigestPoptTable;
652  for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
653  if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
654  continue;
655  if (opt->longName == NULL)
656  continue;
657  if (!(opt->val > 0 && opt->val < 256))
658  continue;
659  dc->dalgo = opt->val;
660  dc->dalgoName = opt->longName;
661  fdInitDigest(dc->fd, dc->dalgo, 0);
662  }
663  } break;
664  }
665 
666 exit:
667  return rc;
668 }
669 
670 static int
671 rpmdcVisitF(rpmdc dc)
672  /*@modifies dc @*/
673 {
674  int rc = 0;
675  int xx;
676 
677 if (_rpmdc_debug)
678 fprintf(stderr, "*** rpmdcVisitF(%p) fn %s\n", dc, dc->fn);
679  if ((xx = rpmdcInitFile(dc)) != 0)
680  rc = xx;
681  else {
682  if ((xx = rpmdcCalcFile(dc)) != 0)
683  rc = xx;
684  if ((xx = rpmdcFiniFile(dc)) != 0)
685  rc = xx;
686  }
687  return rc;
688 }
689 
690 static int
691 rpmdcSortLexical(const FTSENT ** a, const FTSENT ** b)
692  /*@*/
693 {
694  return strcmp((*a)->fts_name, (*b)->fts_name);
695 }
696 
697 static int
698 rpmdcSortDirsLast(const FTSENT ** a, const FTSENT ** b)
699  /*@*/
700 {
701  if (S_ISDIR((*a)->fts_statp->st_mode)) {
702  if (!S_ISDIR((*b)->fts_statp->st_mode))
703  return 1;
704  } else if (S_ISDIR((*b)->fts_statp->st_mode))
705  return -1;
706  return strcmp((*a)->fts_name, (*b)->fts_name);
707 }
708 
709 static int
710 rpmdcCWalk(rpmdc dc)
711 {
712  char *const * paths = (char * const *) dc->paths;
713  int ftsoptions = dc->ftsoptions;
714  int rval = 0;
715 
716  dc->t = Fts_open(paths, ftsoptions,
718  if (dc->t == NULL) {
719  fprintf(stderr, "Fts_open: %s", strerror(errno));
720  return -1;
721  }
722 
723  while ((dc->p = Fts_read(dc->t)) != NULL) {
724 #ifdef NOTYET
725  int indent = 0;
726  if (F_ISSET(dc, INDENT))
727  indent = dc->p->fts_level * 4;
728  if (rpmdcCheckExcludes(dc->p->fts_name, dc->p->fts_path)) {
729  (void) Fts_set(dc->t, dc->p, FTS_SKIP);
730  continue;
731  }
732 #endif
733 
734  dc->fn = dc->p->fts_path; /* XXX eliminate dc->fn */
735  memcpy(&dc->sb, dc->p->fts_statp, sizeof(dc->sb));
736 
737  switch(dc->p->fts_info) {
738  case FTS_D:
739 #ifdef NOTYET
740  if (!F_ISSET(dc, DIRSONLY))
741  (void) printf("\n");
742  if (!F_ISSET(dc, NOCOMMENT))
743  (void) printf("# %s\n", dc->p->fts_path);
744  (void) rpmdcVisitD(dc);
745 #endif
746  /* XXX don't visit topdirs for 0install. */
747  if (F_ISSET(dc, 0INSTALL) && dc->p->fts_level > 0)
748  rpmdcVisitF(dc);
749  /*@switchbreak@*/ break;
750  case FTS_DP:
751 #ifdef NOTYET
752  if (!F_ISSET(dc, NOCOMMENT) && (dc->p->fts_level > 0))
753  (void) printf("%*s# %s\n", indent, "", dc->p->fts_path);
754  (void) printf("%*s..\n", indent, "");
755  if (!F_ISSET(dc, DIRSONLY))
756  (void) printf("\n");
757 #endif
758  /*@switchbreak@*/ break;
759  case FTS_DNR:
760  case FTS_ERR:
761  case FTS_NS:
762  (void) fprintf(stderr, "%s: %s: %s\n", __progname,
763  dc->p->fts_path, strerror(dc->p->fts_errno));
764  /*@switchbreak@*/ break;
765  default:
766  if (!F_ISSET(dc, DIRSONLY))
767  rpmdcVisitF(dc);
768  /*@switchbreak@*/ break;
769  }
770  }
771  (void) Fts_close(dc->t);
772  dc->p = NULL;
773  dc->t = NULL;
774  return rval;
775 }
776 
777 static int rpmdcLoadManifests(rpmdc dc)
778  /*@globals h_errno, fileSystem, internalState @*/
779  /*@modifies dc, h_errno, fileSystem, internalState @*/
780 {
781  return (dc->manifests != NULL ? (*dc->parse) (dc) : 0);
782 }
783 
784 #if !defined(POPT_ARG_ARGV)
785 static int _poptSaveString(const char ***argvp, unsigned int argInfo, const char * val)
786  /*@*/
787 {
788  ARGV_t argv;
789  int argc = 0;
790  if (argvp == NULL)
791  return -1;
792  if (*argvp)
793  while ((*argvp)[argc] != NULL)
794  argc++;
795  *argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp));
796  if ((argv = *argvp) != NULL) {
797  argv[argc++] = xstrdup(val);
798  argv[argc ] = NULL;
799  }
800  return 0;
801 }
802 
805 static void rpmdcArgCallback(poptContext con,
806  /*@unused@*/ enum poptCallbackReason reason,
807  const struct poptOption * opt, /*@unused@*/ const char * arg,
808  /*@unused@*/ void * data)
809  /*@globals fileSystem @*/
810  /*@modifies fileSystem @*/
811 {
812  /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
813  if (opt->arg == NULL)
814  switch (opt->val) {
815  int xx;
816  case 'c':
817 assert(arg != NULL);
818  xx = _poptSaveString(&_dc.manifests, opt->argInfo, arg);
819  break;
820 
821  default:
822  fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val);
823  poptPrintUsage(con, stderr, 0);
824 /*@-exitarg@*/
825  exit(2);
826 /*@=exitarg@*/
827  /*@notreached@*/ break;
828  }
829 }
830 #endif /* POPT_ARG_ARGV */
831 
832 static struct poptOption _optionsTable[] = {
833 #if !defined(POPT_ARG_ARGV)
834 /*@-type@*/ /* FIX: cast? */
835  { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
836  rpmdcArgCallback, 0, NULL, NULL },
837 /*@=type@*/
838 #endif /* POPT_ARG_ARGV */
839 
840  { "0install", '0', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_0INSTALL,
841  N_("Print 0install manifest"), NULL },
842 
843  { "binary", 'b', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_BINARY,
844  N_("Read in binary mode"), NULL },
845 
846 #if !defined(POPT_ARG_ARGV)
847  { "check", 'c', POPT_ARG_STRING, NULL, 'c',
848  N_("Read digests from MANIFEST file and verify (may be used more than once)"),
849  N_("MANIFEST") },
850 #else
851  { "check", 'c', POPT_ARG_ARGV, &_dc.manifests, 0,
852  N_("Read digests from MANIFEST file and verify (may be used more than once)"),
853  N_("MANIFEST") },
854 #endif
855  { "create",'c', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_CREATE,
856  N_("Print file tree specification to stdout"), NULL },
857  { "dirs",'d', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_DIRSONLY,
858  N_("Directories only"), NULL },
859 
860  { "text", 't', POPT_BIT_CLR, &_dc.flags, RPMDC_FLAGS_BINARY,
861  N_("read in text mode (default)"), NULL },
862 
863 #ifdef NOTYET /* XXX todo for popt-1.15 */
864  { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
865  N_("\
866 The following two options are useful only when verifying digests:\
867 "), NULL },
868 #endif
869 
870  { "status", '\0', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_STATUS,
871  N_("no output when verifying"), NULL },
872  { "warn", 'w', POPT_BIT_SET, &_dc.flags, RPMDC_FLAGS_WARN,
873  N_("warn about improperly formatted checksum lines"), NULL },
874 
875  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
876  N_("Available digests:"), NULL },
877 
878  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
879  N_("Common options for all rpmio executables:"),
880  NULL },
881 
882  POPT_AUTOALIAS
883  POPT_AUTOHELP
884 
885  { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
886  N_("\
887 When checking, the input should be a former output of this program. The\n\
888 default mode is to print a line with digest, a character indicating type\n\
889 (`*' for binary, ` ' for text), and name for each FILE.\n\
890 \0"), NULL }, /* embed NUL to work around expandMacro bug in Doxygen 1.7.1 */
891 
892  POPT_TABLEEND
893 };
894 
895 static struct poptOption *optionsTable = &_optionsTable[0];
896 
897 int
898 main(int argc, char *argv[])
899 {
900  poptContext optCon = rpmioInit(argc, argv, optionsTable);
901  ARGV_t av;
902  int ac;
903  int rc = 0;
904  int xx;
905 
906  rpmswEnter(&dc->totalops, -1);
907 
908  if (F_ISSET(dc, 0INSTALL)) {
911  if ((int)rpmioDigestHashAlgo < 0)
914  dc->oalgo = dc->algo;
915  dc->oalgoName = rpmdcAlgo2Name(dc->oalgo);
916  if (!strcmp(dc->oalgoName, "sha1"))
917  dc->oalgoName = "sha1new";
918  } else {
921  if ((int)rpmioDigestHashAlgo < 0)
924  }
925 
926  if (dc->ofn == NULL)
927  dc->ofn = "-";
928  dc->ftsoptions = rpmioFtsOpts;
929  if (!(dc->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)))
930  dc->ftsoptions |= FTS_PHYSICAL;
931  dc->ftsoptions |= FTS_NOCHDIR;
932 
933  dc->ofd = Fopen(dc->ofn, "w.ufdio");
934  if (F_ISSET(dc, 0INSTALL))
935  fdInitDigest(dc->ofd, dc->oalgo, 0);
936 
937  av = poptGetArgs(optCon);
938  ac = argvCount(av);
939  if ((ac == 0 && dc->manifests == NULL)
940  || (ac > 0 && dc->manifests != NULL))
941  {
942  poptPrintUsage(optCon, stderr, 0);
943  rc = 2;
944  goto exit;
945  }
946 
947  if (dc->manifests != NULL) {
948  if ((xx = rpmdcLoadManifests(dc)) != 0)
949  rc = xx;
950  } else {
951  int i;
952  for (i = 0; i < ac; i++)
953  xx = argvAdd(&dc->paths, av[i]);
954  }
955  if (rc)
956  goto exit;
957 
958  if (dc->manifests != NULL) {
959  dc->ix = 0;
960  av = dc->paths;
961  if (av != NULL)
962  while ((dc->fn = *av++) != NULL) {
963  if ((xx = Lstat(dc->fn, &dc->sb)) != 0
964  || (xx = rpmdcVisitF(dc)) != 0)
965  rc = xx;
966  dc->ix++;
967  }
968  } else {
969  if ((xx = rpmdcCWalk(dc)) != 0)
970  rc = xx;
971  }
972 
973 exit:
974  if (dc->nfailed)
975  fprintf(stderr, "%s: WARNING: %u of %u computed checksums did NOT match\n",
976  __progname, (unsigned) dc->nfailed, (unsigned) dc->ncomputed);
977 
978  if (dc->ofd) {
979  /* Print the output spewage digest for 0install format manifests. */
980  if (rc == 0 && F_ISSET(dc, 0INSTALL) && dc->manifests == NULL) {
981  static int asAscii = 1;
982  char *t;
983  fdFiniDigest(dc->ofd, dc->oalgo, &dc->digest, &dc->digestlen, asAscii);
984 assert(dc->digest != NULL);
985  t = rpmExpand(dc->oalgoName, "=", dc->digest, "\n", NULL);
986  (void) Fwrite(t, strlen(t), sizeof(*t), dc->ofd);
987  t = _free(t);
988  dc->digest = _free(dc->digest);
989  }
990  (void) Fclose(dc->ofd);
991  dc->ofd = NULL;
992  }
993 
994 #ifdef NOTYET
995  dc->manifests = argvFree(dc->manifests);
996 #endif
997  dc->algos = argiFree(dc->algos);
998  dc->digests = argvFree(dc->digests);
999  dc->paths = argvFree(dc->paths);
1000 
1001  rpmswExit(&dc->totalops, 0);
1002  if (_rpmsw_stats) {
1003  rpmswPrint(" total:", &dc->totalops);
1004  rpmswPrint(" read:", &dc->readops);
1005  rpmswPrint("digest:", &dc->digestops);
1006  }
1007 
1008  optCon = rpmioFini(optCon);
1009 
1010  return rc;
1011 }