rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

tools/rpmdigest.c
Go to the documentation of this file.

    00001 #include "system.h"
    00002 /*@unchecked@*/
    00003 extern const char * __progname;
    00004
    00005 #define _RPMIOB_INTERNAL
    00006 #include <rpmiotypes.h>
    00007 #include <rpmio_internal.h>     /* XXX fdGetFILE */
    00008 #include <poptIO.h>
    00009 #include "debug.h"
    00010
    00011 static int _rpmdc_debug = 0;
    00012
    00013 /* XXX older 0install manifest format. */
    00014 static int _old_0install = 0;
    00015
    00016 #define _KFB(n) (1U << (n))
    00017 #define _DFB(n) (_KFB(n) | 0x40000000)
    00018
    00019 #define F_ISSET(_dc, _FLAG) ((_dc)->flags & ((RPMDC_FLAGS_##_FLAG) & ~0x40000000))
    00020
    00024 enum dcFlags_e {
    00025     RPMDC_FLAGS_NONE            = 0,
    00026         /* 0 reserved */
    00027     RPMDC_FLAGS_WARN            = _DFB( 1),
    00028     RPMDC_FLAGS_CREATE          = _DFB( 2),
    00029     RPMDC_FLAGS_DIRSONLY        = _DFB( 3),
    00030         /* 4-13 reserved */
    00031     RPMDC_FLAGS_BINARY          = _DFB(14),
    00032     RPMDC_FLAGS_STATUS          = _DFB(15),
    00033     RPMDC_FLAGS_0INSTALL        = _DFB(16)
    00034         /* 17-31 unused */
    00035 };
    00036
    00039 typedef struct rpmdc_s * rpmdc;
    00040
    00043 struct rpmdc_s {
    00044     int ftsoptions;
    00045     FTS * t;
    00046     FTSENT * p;
    00047     struct stat sb;
    00049     enum dcFlags_e flags;
    00050     uint32_t algo;
    00051     uint32_t dalgo;
    00052 /*@observer@*/ /*@null@*/
    00053     const char * dalgoName;
    00054     const char * digest;
    00055     size_t digestlen;
    00056     const char * fn;
    00057     FD_t fd;
    00058     int (*parse) (rpmdc dc);
    00059     const char * (*print) (rpmdc dc, int rc);
    00060     const char * ofn;
    00061     FD_t ofd;
    00062     uint32_t oalgo;
    00063     const char * oalgoName;
    00064     ARGV_t manifests;
    00065     ARGI_t algos;
    00066     ARGV_t digests;
    00067     ARGV_t paths;
    00068     unsigned char buf[BUFSIZ];
    00069     ssize_t nb;
    00070     int ix;
    00071
    00072     size_t ncomputed;
    00073     size_t nchecked;
    00074     size_t nmatched;
    00075     size_t nfailed;
    00076     struct rpmop_s totalops;
    00077     struct rpmop_s readops;
    00078     struct rpmop_s digestops;
    00079 };
    00080
    00083 static struct rpmdc_s _dc = {
    00084         .ftsoptions = FTS_PHYSICAL,
    00085         .flags = RPMDC_FLAGS_CREATE
    00086 };
    00087
    00090 static rpmdc dc = &_dc;
    00091
    00092 /*==============================================================*/
    00093 static uint32_t rpmdcName2Algo(const char * dname)
    00094         /*@*/
    00095 {
    00096     struct poptOption * opt = rpmioDigestPoptTable;
    00097     uint32_t dalgo = 0xffffffff;
    00098
    00099     /* XXX compatible with 0install legacy derangement. bug imho. */
    00100     if (!strcmp(dname, "sha1new"))
    00101         dname = "sha1";
    00102
    00103     for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
    00104         if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
    00105             continue;
    00106         if (opt->longName == NULL)
    00107             continue;
    00108         if (!(opt->val > 0 && opt->val < 256))
    00109             continue;
    00110         if (strcmp(opt->longName, dname))
    00111             continue;
    00112         dalgo = (uint32_t) opt->val;
    00113         break;
    00114     }
    00115     return dalgo;
    00116 }
    00117
    00118 /*@null@*/
    00119 static const char * rpmdcAlgo2Name(uint32_t dalgo)
    00120         /*@*/
    00121 {
    00122     struct poptOption * opt = rpmioDigestPoptTable;
    00123     const char * dalgoName = NULL;
    00124
    00125     for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
    00126         if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
    00127             continue;
    00128         if (opt->longName == NULL)
    00129             continue;
    00130         if (!(opt->val > 0 && opt->val < 256))
    00131             continue;
    00132         if ((uint32_t)opt->val != dalgo)
    00133             continue;
    00134         dalgoName = opt->longName;
    00135         break;
    00136     }
    00137     return dalgoName;
    00138 }
    00139
    00140 /*==============================================================*/
    00141
    00142 static int rpmdcParseCoreutils(rpmdc dc)
    00143         /*@globals h_errno, fileSystem, internalState @*/
    00144         /*@modifies h_errno, fileSystem, internalState @*/
    00145 {
    00146     int rc = -1;        /* assume failure */
    00147
    00148     if (dc->manifests != NULL)  /* note rc=0 return with no files to load. */
    00149     while ((dc->fn = *dc->manifests++) != NULL) {
    00150         char buf[BUFSIZ];
    00151         unsigned lineno;
    00152         FILE *fp;
    00153
    00154         if (strcmp(dc->fn, "-") == 0) {
    00155             dc->fd = NULL;
    00156             fp = stdin;
    00157         } else {
    00158             /* XXX .fpio is needed because of fgets(3) usage. */
    00159             dc->fd = Fopen(dc->fn, "r.fpio");
    00160             if (dc->fd == NULL || Ferror(dc->fd) || (fp = fdGetFILE(dc->fd)) == NULL) {
    00161                 fprintf(stderr, _("%s: Failed to open %s: %s\n"),
    00162                                 __progname, dc->fn, Fstrerror(dc->fd));
    00163                 if (dc->fd != NULL) (void) Fclose(dc->fd);
    00164                 dc->fd = NULL;
    00165                 fp = NULL;
    00166                 goto exit;
    00167             }
    00168         }
    00169
    00170         lineno = 0;
    00171         while (fgets(buf, sizeof(buf), fp) != NULL) {
    00172             const char * dname, * digest, * path;
    00173             char *se = buf + (int)strlen(buf);
    00174             int c, xx;
    00175
    00176             lineno++;
    00177             while (se > buf && xisspace((int)se[-1]))
    00178                 se--;
    00179             *se = '\0';
    00180
    00181             /* Skip blank lines */
    00182             if (buf[0] == '\0') /*@innercontinue@*/ continue;
    00183             /* Skip comment lines */
    00184             if (buf[0] == '#')  /*@innercontinue@*/ continue;
    00185
    00186             /* Parse "[algo:]digest [* ]path" line. */
    00187             dname = NULL; path = NULL;
    00188             for (digest = se = buf; (c = (int)*se) != 0; se++)
    00189             switch (c) {
    00190             default:
    00191                 /*@switchbreak@*/ break;
    00192             case ':':
    00193                 *se++ = '\0';
    00194                 dname = digest;
    00195                 digest = se;
    00196                 /*@switchbreak@*/ break;
    00197             case ' ':
    00198                 se[0] = '\0';   /* loop will terminate */
    00199                 if (se[1] == ' ' || se[1] == '*')
    00200                     se[1] = '\0';
    00201                 path = se + 2;
    00202                 /*@switchbreak@*/ break;
    00203             }
    00204             if (path == NULL) {
    00205                 fprintf(stderr, _("%s: %s line %u: No file path found.\n"),
    00206                                 __progname, dc->fn, lineno);
    00207                 rc = 2;
    00208                 goto exit;
    00209             }
    00210
    00211             /* Map name to algorithm number. */
    00212             if (dname) {
    00213                 if ((dc->dalgo = rpmdcName2Algo(dname)) != 0xffffffff)
    00214                     dc->dalgoName = xstrdup(dname);
    00215                 if (dc->dalgo == 0xffffffff) {
    00216                     fprintf(stderr, _("%s: %s line %u: Unknown digest name \"%s\"\n"),
    00217                                 __progname, dc->fn, lineno, dname);
    00218                     rc = 2;
    00219                     goto exit;
    00220                 }
    00221             } else
    00222                 dc->dalgo = dc->algo;
    00223
    00224             /* Save {algo, digest, path} for processing. */
    00225             xx = argiAdd(&dc->algos, -1, dc->dalgo);
    00226             xx = argvAdd(&dc->digests, digest);
    00227             xx = argvAdd(&dc->paths, path);
    00228         }
    00229
    00230         if (dc->fd != NULL) {
    00231             (void) Fclose(dc->fd);
    00232             dc->fd = NULL;
    00233         }
    00234     }
    00235     rc = 0;
    00236
    00237 exit:
    00238     return rc;
    00239 }
    00240
    00241 /*@null@*/
    00242 static const char * rpmdcPrintCoreutils(rpmdc dc, int rc)
    00243 {
    00244     const char *msg = (rc ? "FAILED" : "OK");
    00245     char * t, * te;
    00246     size_t nb = 0;
    00247
    00248     /* Don't bother formatting if noone cares. */
    00249     if (rc == 0 && F_ISSET(dc, STATUS))
    00250         return NULL;
    00251
    00252     /* Calculate size of message. */
    00253     if (dc->dalgoName != NULL)
    00254         nb += strlen(dc->dalgoName) + sizeof(":") - 1;
    00255 assert(dc->digest != NULL);
    00256     if (dc->digest != NULL && dc->digestlen > 0)
    00257         nb += dc->digestlen;
    00258     nb += sizeof(" *") - 1;
    00259     if (dc->fn != NULL)
    00260         nb += strlen(dc->fn);
    00261     nb += strlen(msg);
    00262     nb += sizeof("\n");         /* XXX trailing NUL */
    00263
    00264     /* Compose the message. */
    00265     te = t = xmalloc(nb);
    00266     *te = '\0';
    00267
    00268     if (dc->manifests) {
    00269         if (rc || !F_ISSET(dc, STATUS)) {
    00270             if (dc->fn)
    00271                 te = stpcpy( stpcpy(te, dc->fn), ": ");
    00272             te = stpcpy(te, msg);
    00273             *te++ = '\n';
    00274         }
    00275     } else {
    00276         if (dc->dalgoName)
    00277             te = stpcpy( stpcpy(te, dc->dalgoName), ":");
    00278         te = stpcpy(te, dc->digest);
    00279         *te++ = ' ';
    00280         *te++ = (F_ISSET(dc, BINARY) ? '*' : ' ');
    00281         te = stpcpy(te, dc->fn);
    00282         *te++ = '\n';
    00283     }
    00284     *te = '\0';
    00285
    00286     return t;
    00287 }
    00288
    00289 /*==============================================================*/
    00290
    00291 static int rpmdcParseZeroInstall(rpmdc dc)
    00292         /*@globals h_errno, fileSystem, internalState @*/
    00293         /*@modifies h_errno, fileSystem, internalState @*/
    00294 {
    00295     int rc = 0; /* assume success */
    00296
    00297     if (dc->manifests != NULL)  /* note rc=0 return with no files to load. */
    00298     while ((dc->fn = *dc->manifests++) != NULL) {
    00299         unsigned lineno;
    00300         char * be;
    00301         rpmiob iob = NULL;
    00302         int xx = rpmiobSlurp(dc->fn, &iob);
    00303         const char * digest;
    00304         char * f;
    00305         char * fe;
    00306
    00307         if (!(xx == 0 && iob != NULL)) {
    00308             fprintf(stderr, _("%s: Failed to open %s\n"), __progname, dc->fn);
    00309             rc = -1;
    00310             goto bottom;
    00311         }
    00312
    00313         be = (char *)(iob->b + iob->blen);
    00314         while (be > (char *)iob->b && (be[-1] == '\n' || be[-1] == '\r')) {
    00315           be--;
    00316           *be = '\0';
    00317         }
    00318
    00319         /* Parse "algo=digest" from last line. */
    00320         be = strrchr((char *)iob->b, '=');
    00321         if (be == NULL) {
    00322             fprintf(stderr,
    00323                 _("%s: %s: Manifest needs \"algo=digest\" as last line\n"),
    00324                 __progname, dc->fn);
    00325             rc = 2;
    00326             goto bottom;
    00327         }
    00328         *be = '\0';
    00329         dc->digest = be + 1;
    00330         while (be > (char *)iob->b && !(be[-1] == '\n' || be[-1] == '\r'))
    00331             be--;
    00332         if (be <= (char *)iob->b) {
    00333             fprintf(stderr, _("%s: %s: Manifest is empty\n"),
    00334                 __progname, dc->fn);
    00335             rc = 2;
    00336             goto bottom;
    00337         }
    00338
    00339         /* Map name to algorithm number. */
    00340         if ((dc->dalgo = rpmdcName2Algo(be)) == 0xffffffff) {
    00341             fprintf(stderr, _("%s: %s: Unknown digest algo name \"%s\"\n"),
    00342                         __progname, dc->fn, be);
    00343             rc = 2;
    00344             goto bottom;
    00345         }
    00346         *be = '\0';
    00347
    00348         /* Verify the manifest digest. */
    00349         {   DIGEST_CTX ctx = rpmDigestInit(dc->dalgo, 0);
    00350
    00351             (void) rpmDigestUpdate(ctx, (char *)iob->b, (be - (char *)iob->b));
    00352             digest = NULL;
    00353             (void) rpmDigestFinal(ctx, &digest, NULL, 1);
    00354             if (strcmp(dc->digest, digest)) {
    00355                 fprintf(stderr,
    00356                         _("%s: %s: Manifest digest check: Expected(%s) != (%s)\n"),
    00357                         __progname, dc->fn, dc->digest, digest);
    00358                 rc = 2;
    00359                 goto bottom;
    00360             }
    00361             digest = _free(digest);
    00362         }
    00363
    00364         /* Parse and save manifest items. */
    00365         lineno = 0;
    00366         for (f = (char *)iob->b; *f; f = fe) {
    00367             static const char hexdigits[] = "0123456789ABCDEFabcdef";
    00368             const char * _dn = NULL;
    00369             const char * path;
    00370
    00371             lineno++;
    00372             fe = f;
    00373             while (*fe && !(*fe == '\n' || *fe == '\r'))
    00374                 fe++;
    00375             while (*fe && (*fe == '\n' || *fe == '\r'))
    00376                 *fe++ = '\0';
    00377             switch ((int)*f) {
    00378             case 'D':
    00379                 _dn = f + 2;
    00380                 continue;
    00381                 /*@notreached@*/ break;
    00382             case 'F':
    00383             case 'S':
    00384             case 'X':
    00385                 digest = f + 2;
    00386                 f += 2;
    00387                 while (*f && strchr(hexdigits, *f) != NULL)
    00388                     f++;
    00389                 if (*f != ' ') {
    00390                     fprintf(stderr, _("%s: %s line %u: Malformed digest field.\n"),
    00391                         __progname, dc->fn, lineno);
    00392                     rc = 2;
    00393                     goto bottom;
    00394                 }
    00395                 *f++ = '\0';
    00396                 while (*f && xisdigit(*f))
    00397                     f++;
    00398                 if (*f != ' ') {
    00399                     fprintf(stderr, _("%s: %s line %u: Malformed mtime field.\n"),
    00400                         __progname, dc->fn, lineno);
    00401                     rc = 2;
    00402                     goto bottom;
    00403                 }
    00404                 *f++ = '\0';
    00405                 while (*f && xisdigit(*f))
    00406                     f++;
    00407                 if (*f != ' ') {
    00408                     fprintf(stderr, _("%s: %s line %u: Malformed size field.\n"),
    00409                         __progname, dc->fn, lineno);
    00410                     rc = 2;
    00411                     goto bottom;
    00412                 }
    00413                 *f++ = '\0';
    00414                 if (*f == '\0') {
    00415                     fprintf(stderr, _("%s: %s line %u: No file path.\n"),
    00416                         __progname, dc->fn, lineno);
    00417                     rc = 2;
    00418                     goto bottom;
    00419                 }
    00420
    00421                 if (_dn && *_dn == '/')
    00422                     path = rpmExpand(_dn+1, "/", f, NULL);
    00423                 else
    00424                     path = xstrdup(f);
    00425
    00426                 /* Save {algo, digest, path} for processing. */
    00427                 xx = argiAdd(&dc->algos, -1, dc->dalgo);
    00428                 xx = argvAdd(&dc->digests, digest);
    00429                 xx = argvAdd(&dc->paths, path);
    00430                 path = _free(path);
    00431                 break;
    00432             }
    00433         }
    00434
    00435 bottom:
    00436         iob = rpmiobFree(iob);
    00437         if (rc != 0)
    00438             goto exit;
    00439     }
    00440
    00441 exit:
    00442     return rc;
    00443 }
    00444
    00445 /*@null@*/
    00446 static const char * rpmdcPrintZeroInstall(rpmdc dc, int rc)
    00447 {
    00448     char * t, * te;
    00449     size_t nb = 0;
    00450     char _mtime[32];
    00451     char _size[32];
    00452     const struct stat * st = &dc->sb;
    00453     const char * _bn;
    00454
    00455     /* Don't bother formatting if noone cares. */
    00456     if (rc == 0 && F_ISSET(dc, STATUS))
    00457         return NULL;
    00458
    00459     snprintf(_mtime, sizeof(_mtime), "%llu",
    00460                 (unsigned long long) st->st_mtime);
    00461     _mtime[sizeof(_mtime)-1] = '\0';
    00462     snprintf(_size, sizeof(_size), "%llu",
    00463                 (unsigned long long) st->st_size);
    00464     _size[sizeof(_size)-1] = '\0';
    00465     if ((_bn = strrchr(dc->fn, '/')) != NULL)
    00466         _bn++;
    00467     else
    00468         _bn = dc->fn;
    00469
    00470     /* Calculate size of message. */
    00471     nb += sizeof("F");
    00472     if (dc->digest != NULL && dc->digestlen > 0)
    00473         nb += 1 + dc->digestlen;
    00474     nb += 1 + strlen(_mtime);
    00475     nb += 1 + strlen(_size);
    00476     nb += 1 + strlen(_bn);
    00477     nb += sizeof("\n");         /* XXX trailing NUL */
    00478
    00479     /* Compose the message. */
    00480     te = t = xmalloc(nb);
    00481     *te = '\0';
    00482
    00483     if (dc->manifests) {
    00484         const char *msg = (rc ? "FAILED" : "OK");
    00485         if (rc || !F_ISSET(dc, STATUS)) {
    00486             if (dc->fn)
    00487                 te = stpcpy( stpcpy(te, dc->fn), ": ");
    00488             te = stpcpy(te, msg);
    00489             *te++ = '\n';
    00490         }
    00491     } else {
    00492         if (S_ISDIR(st->st_mode)) {
    00493             *te++ = 'D';
    00494             if (_old_0install) {
    00495                 *te++ = ' ';
    00496                 te = stpcpy(te, _mtime);
    00497             }
    00498             *te++ = ' ';
    00499             *te++ = '/';
    00500             te = stpcpy(te, _bn);
    00501             *te++ = '\n';
    00502         } else if (S_ISREG(st->st_mode) || S_ISLNK(st->st_mode)) {
    00503             if (S_ISLNK(st->st_mode))
    00504                 *te++ = 'S';
    00505             else
    00506                 *te++ = (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) ? 'X' : 'F';
    00507             *te++ = ' ';
    00508             te = stpcpy(te, dc->digest);
    00509             *te++ = ' ';
    00510             te = stpcpy(te, _mtime);
    00511             *te++ = ' ';
    00512             te = stpcpy(te, _size);
    00513             *te++ = ' ';
    00514             te = stpcpy(te, _bn);
    00515             *te++ = '\n';
    00516         }
    00517     }
    00518     *te = '\0';
    00519
    00520     return t;
    00521 }
    00522
    00523 /*==============================================================*/
    00524
    00525 static int rpmdcPrintFile(rpmdc dc)
    00526 {
    00527     static int asAscii = 1;
    00528     int rc = 0;
    00529
    00530 if (_rpmdc_debug)
    00531 fprintf(stderr, "\trpmdcPrintFile(%p) fd %p fn %s\n", dc, dc->fd, dc->fn);
    00532
    00533 assert(dc->fd != NULL);
    00534     fdFiniDigest(dc->fd, dc->dalgo, &dc->digest, &dc->digestlen, asAscii);
    00535 assert(dc->digest != NULL);
    00536     dc->ncomputed++;
    00537
    00538     if (dc->manifests) {
    00539         dc->nchecked++;
    00540         if ((rc = strcmp(dc->digest, dc->digests[dc->ix])) != 0)
    00541             dc->nfailed++;
    00542         else
    00543             dc->nmatched++;
    00544     }
    00545
    00546     {   const char * t = (*dc->print) (dc, rc);
    00547         if (dc->ofd && t && *t) {
    00548             size_t nb = strlen(t);
    00549             nb = Fwrite(t, nb, sizeof(*t), dc->ofd);
    00550             (void) Fflush(dc->ofd);
    00551         }
    00552         t = _free(t);
    00553     }
    00554
    00555     dc->digest = _free(dc->digest);
    00556     dc->digestlen = 0;
    00557     return rc;
    00558 }
    00559
    00560 static int rpmdcFiniFile(rpmdc dc)
    00561 {
    00562     uint32_t dalgo = (dc->manifests ? dc->algos->vals[dc->ix] : dc->algo);
    00563     int rc = 0;
    00564     int xx;
    00565
    00566     /* Only regular files have dc->fd != NULL here. Skip all other paths. */
    00567     if (dc->fd == NULL)
    00568         return rc;
    00569
    00570 if (_rpmdc_debug)
    00571 fprintf(stderr, "\trpmdcFiniFile(%p) fn %s\n", dc, dc->fn);
    00572     switch (dalgo) {
    00573     default:
    00574         dc->dalgo = dalgo;
    00575         dc->dalgoName = NULL;
    00576         xx = rpmdcPrintFile(dc);
    00577         if (xx) rc = xx;
    00578         break;
    00579     case 256:           /* --all digests requested. */
    00580       { struct poptOption * opt = rpmioDigestPoptTable;
    00581         for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
    00582             if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
    00583                 continue;
    00584             if (opt->arg != (void *)&rpmioDigestHashAlgo)
    00585                 continue;
    00586             dc->dalgo = opt->val;
    00587             if (!(dc->dalgo > 0 && dc->dalgo < 256))
    00588                 continue;
    00589             dc->dalgoName = opt->longName;
    00590             xx = rpmdcPrintFile(dc);
    00591             if (xx) rc = xx;
    00592         }
    00593       } break;
    00594     }
    00595
    00596     (void) rpmswAdd(&dc->readops, fdstat_op(dc->fd, FDSTAT_READ));
    00597     (void) rpmswAdd(&dc->digestops, fdstat_op(dc->fd, FDSTAT_DIGEST));
    00598     Fclose(dc->fd);
    00599     dc->fd = NULL;
    00600
    00601     return rc;
    00602 }
    00603
    00604 static int rpmdcCalcFile(rpmdc dc)
    00605 {
    00606     int rc = 0;
    00607
    00608 if (_rpmdc_debug)
    00609 fprintf(stderr, "\trpmdcCalcFile(%p) fn %s\n", dc, dc->fn);
    00610     /* Skip (unopened) non-files. */
    00611     if (dc->fd != NULL)
    00612     do {
    00613         dc->nb = Fread(dc->buf, sizeof(dc->buf[0]), sizeof(dc->buf), dc->fd);
    00614         if (Ferror(dc->fd)) {
    00615             rc = 2;
    00616             break;
    00617         }
    00618     } while (dc->nb > 0);
    00619
    00620     return rc;
    00621 }
    00622
    00623 static int rpmdcInitFile(rpmdc dc)
    00624 {
    00625     int rc = 0;
    00626
    00627 if (_rpmdc_debug)
    00628 fprintf(stderr, "\trpmdcInitFile(%p) fn %s\n", dc, dc->fn);
    00629     /* Skip non-files. */
    00630     if (!S_ISREG(dc->sb.st_mode)) {
    00631         /* XXX not found return code? */
    00632         goto exit;
    00633     }
    00634
    00635     dc->fd = Fopen(dc->fn, "r.ufdio");
    00636     if (dc->fd == NULL || Ferror(dc->fd)) {
    00637         fprintf(stderr, _("open of %s failed: %s\n"), dc->fn, Fstrerror(dc->fd));
    00638         if (dc->fd != NULL) Fclose(dc->fd);
    00639         dc->fd = NULL;
    00640         rc = 2;
    00641         goto exit;
    00642     }
    00643
    00644     switch (dc->algo) {
    00645     default:
    00646         /* XXX TODO: instantiate verify digests for all identical paths. */
    00647         dc->dalgo = dc->algo;
    00648         fdInitDigest(dc->fd, dc->dalgo, 0);
    00649         break;
    00650     case 256:           /* --all digests requested. */
    00651       { struct poptOption * opt = rpmioDigestPoptTable;
    00652         for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
    00653             if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)
    00654                 continue;
    00655             if (opt->longName == NULL)
    00656                 continue;
    00657             if (!(opt->val > 0 && opt->val < 256))
    00658                 continue;
    00659             dc->dalgo = opt->val;
    00660             dc->dalgoName = opt->longName;
    00661             fdInitDigest(dc->fd, dc->dalgo, 0);
    00662         }
    00663       } break;
    00664     }
    00665
    00666 exit:
    00667     return rc;
    00668 }
    00669
    00670 static int
    00671 rpmdcVisitF(rpmdc dc)
    00672         /*@modifies dc @*/
    00673 {
    00674     int rc = 0;
    00675     int xx;
    00676
    00677 if (_rpmdc_debug)
    00678 fprintf(stderr, "*** rpmdcVisitF(%p) fn %s\n", dc, dc->fn);
    00679     if ((xx = rpmdcInitFile(dc)) != 0)
    00680         rc = xx;
    00681     else {
    00682         if ((xx = rpmdcCalcFile(dc)) != 0)
    00683             rc = xx;
    00684         if ((xx = rpmdcFiniFile(dc)) != 0)
    00685             rc = xx;
    00686     }
    00687     return rc;
    00688 }
    00689
    00690 static int
    00691 rpmdcSortLexical(const FTSENT ** a, const FTSENT ** b)
    00692         /*@*/
    00693 {
    00694     return strcmp((*a)->fts_name, (*b)->fts_name);
    00695 }
    00696
    00697 static int
    00698 rpmdcSortDirsLast(const FTSENT ** a, const FTSENT ** b)
    00699         /*@*/
    00700 {
    00701     if (S_ISDIR((*a)->fts_statp->st_mode)) {
    00702         if (!S_ISDIR((*b)->fts_statp->st_mode))
    00703             return 1;
    00704     } else if (S_ISDIR((*b)->fts_statp->st_mode))
    00705         return -1;
    00706     return strcmp((*a)->fts_name, (*b)->fts_name);
    00707 }
    00708
    00709 static int
    00710 rpmdcCWalk(rpmdc dc)
    00711 {
    00712     char *const * paths = (char * const *) dc->paths;
    00713     int ftsoptions = dc->ftsoptions;
    00714     int rval = 0;
    00715
    00716     dc->t = Fts_open(paths, ftsoptions,
    00717         (F_ISSET(dc, 0INSTALL) && _old_0install ? rpmdcSortLexical : rpmdcSortDirsLast));
    00718     if (dc->t == NULL) {
    00719         fprintf(stderr, "Fts_open: %s", strerror(errno));
    00720         return -1;
    00721     }
    00722
    00723     while ((dc->p = Fts_read(dc->t)) != NULL) {
    00724 #ifdef  NOTYET
    00725         int indent = 0;
    00726         if (F_ISSET(dc, INDENT))
    00727             indent = dc->p->fts_level * 4;
    00728         if (rpmdcCheckExcludes(dc->p->fts_name, dc->p->fts_path)) {
    00729             (void) Fts_set(dc->t, dc->p, FTS_SKIP);
    00730             continue;
    00731         }
    00732 #endif
    00733
    00734         dc->fn = dc->p->fts_path;       /* XXX eliminate dc->fn */
    00735         memcpy(&dc->sb, dc->p->fts_statp, sizeof(dc->sb));
    00736
    00737         switch(dc->p->fts_info) {
    00738         case FTS_D:
    00739 #ifdef  NOTYET
    00740             if (!F_ISSET(dc, DIRSONLY))
    00741                 (void) printf("\n");
    00742             if (!F_ISSET(dc, NOCOMMENT))
    00743                 (void) printf("# %s\n", dc->p->fts_path);
    00744             (void) rpmdcVisitD(dc);
    00745 #endif
    00746             /* XXX don't visit topdirs for 0install. */
    00747             if (F_ISSET(dc, 0INSTALL) && dc->p->fts_level > 0)
    00748                 rpmdcVisitF(dc);
    00749             /*@switchbreak@*/ break;
    00750         case FTS_DP:
    00751 #ifdef  NOTYET
    00752             if (!F_ISSET(dc, NOCOMMENT) && (dc->p->fts_level > 0))
    00753                 (void) printf("%*s# %s\n", indent, "", dc->p->fts_path);
    00754             (void) printf("%*s..\n", indent, "");
    00755             if (!F_ISSET(dc, DIRSONLY))
    00756                 (void) printf("\n");
    00757 #endif
    00758             /*@switchbreak@*/ break;
    00759         case FTS_DNR:
    00760         case FTS_ERR:
    00761         case FTS_NS:
    00762             (void) fprintf(stderr, "%s: %s: %s\n", __progname,
    00763                         dc->p->fts_path, strerror(dc->p->fts_errno));
    00764             /*@switchbreak@*/ break;
    00765         default:
    00766             if (!F_ISSET(dc, DIRSONLY))
    00767                 rpmdcVisitF(dc);
    00768             /*@switchbreak@*/ break;
    00769         }
    00770     }
    00771     (void) Fts_close(dc->t);
    00772     dc->p = NULL;
    00773     dc->t = NULL;
    00774     return rval;
    00775 }
    00776
    00777 static int rpmdcLoadManifests(rpmdc dc)
    00778         /*@globals h_errno, fileSystem, internalState @*/
    00779         /*@modifies dc, h_errno, fileSystem, internalState @*/
    00780 {
    00781     return (dc->manifests != NULL ? (*dc->parse) (dc) : 0);
    00782 }
    00783
    00784 #if !defined(POPT_ARG_ARGV)
    00785 static int _poptSaveString(const char ***argvp, unsigned int argInfo, const char * val)
    00786         /*@*/
    00787 {
    00788     ARGV_t argv;
    00789     int argc = 0;
    00790     if (argvp == NULL)
    00791         return -1;
    00792     if (*argvp)
    00793     while ((*argvp)[argc] != NULL)
    00794         argc++;
    00795     *argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp));
    00796     if ((argv = *argvp) != NULL) {
    00797         argv[argc++] = xstrdup(val);
    00798         argv[argc  ] = NULL;
    00799     }
    00800     return 0;
    00801 }
    00802
    00805 static void rpmdcArgCallback(poptContext con,
    00806                 /*@unused@*/ enum poptCallbackReason reason,
    00807                 const struct poptOption * opt, /*@unused@*/ const char * arg,
    00808                 /*@unused@*/ void * data)
    00809         /*@globals fileSystem @*/
    00810         /*@modifies fileSystem @*/
    00811 {
    00812     /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
    00813     if (opt->arg == NULL)
    00814     switch (opt->val) {
    00815         int xx;
    00816     case 'c':
    00817 assert(arg != NULL);
    00818         xx = _poptSaveString(&_dc.manifests, opt->argInfo, arg);
    00819         break;
    00820
    00821     default:
    00822         fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val);
    00823         poptPrintUsage(con, stderr, 0);
    00824 /*@-exitarg@*/
    00825         exit(2);
    00826 /*@=exitarg@*/
    00827         /*@notreached@*/ break;
    00828     }
    00829 }
    00830 #endif  /* POPT_ARG_ARGV */
    00831
    00832 static struct poptOption _optionsTable[] = {
    00833 #if !defined(POPT_ARG_ARGV)
    00834 /*@-type@*/ /* FIX: cast? */
    00835  { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
    00836         rpmdcArgCallback, 0, NULL, NULL },
    00837 /*@=type@*/
    00838 #endif  /* POPT_ARG_ARGV */
    00839
    00840   { "0install", '0', POPT_BIT_SET,      &_dc.flags, RPMDC_FLAGS_0INSTALL,
    00841         N_("Print 0install manifest"), NULL },
    00842
    00843   { "binary", 'b', POPT_BIT_SET,        &_dc.flags, RPMDC_FLAGS_BINARY,
    00844         N_("Read in binary mode"), NULL },
    00845
    00846 #if !defined(POPT_ARG_ARGV)
    00847   { "check", 'c', POPT_ARG_STRING,      NULL, 'c',
    00848         N_("Read digests from MANIFEST file and verify (may be used more than once)"),
    00849         N_("MANIFEST") },
    00850 #else
    00851   { "check", 'c', POPT_ARG_ARGV,        &_dc.manifests, 0,
    00852         N_("Read digests from MANIFEST file and verify (may be used more than once)"),
    00853         N_("MANIFEST") },
    00854 #endif
    00855   { "create",'c', POPT_BIT_SET,         &_dc.flags, RPMDC_FLAGS_CREATE,
    00856         N_("Print file tree specification to stdout"), NULL },
    00857   { "dirs",'d', POPT_BIT_SET,           &_dc.flags, RPMDC_FLAGS_DIRSONLY,
    00858         N_("Directories only"), NULL },
    00859
    00860   { "text", 't', POPT_BIT_CLR,          &_dc.flags, RPMDC_FLAGS_BINARY,
    00861         N_("read in text mode (default)"), NULL },
    00862
    00863 #ifdef  NOTYET          /* XXX todo for popt-1.15 */
    00864   { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
    00865         N_("\
    00866 The following two options are useful only when verifying digests:\
    00867 "), NULL },
    00868 #endif
    00869
    00870   { "status", '\0', POPT_BIT_SET,       &_dc.flags, RPMDC_FLAGS_STATUS,
    00871         N_("no output when verifying"), NULL },
    00872   { "warn", 'w', POPT_BIT_SET,          &_dc.flags, RPMDC_FLAGS_WARN,
    00873         N_("warn about improperly formatted checksum lines"), NULL },
    00874
    00875  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
    00876         N_("Available digests:"), NULL },
    00877
    00878  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
    00879         N_("Common options for all rpmio executables:"),
    00880         NULL },
    00881
    00882   POPT_AUTOALIAS
    00883   POPT_AUTOHELP
    00884
    00885   { NULL, -1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
    00886         N_("\
    00887 When checking, the input should be a former output of this program.  The\n\
    00888 default mode is to print a line with digest, a character indicating type\n\
    00889 (`*' for binary, ` ' for text), and name for each FILE.\n\
    00890 \0"), NULL }, /* embed NUL to work around expandMacro bug in Doxygen 1.7.1 */
    00891
    00892   POPT_TABLEEND
    00893 };
    00894
    00895 static struct poptOption *optionsTable = &_optionsTable[0];
    00896
    00897 int
    00898 main(int argc, char *argv[])
    00899 {
    00900     poptContext optCon = rpmioInit(argc, argv, optionsTable);
    00901     ARGV_t av;
    00902     int ac;
    00903     int rc = 0;
    00904     int xx;
    00905
    00906     rpmswEnter(&dc->totalops, -1);
    00907
    00908     if (F_ISSET(dc, 0INSTALL)) {
    00909         dc->parse = rpmdcParseZeroInstall;
    00910         dc->print = rpmdcPrintZeroInstall;
    00911         if ((int)rpmioDigestHashAlgo < 0)
    00912             rpmioDigestHashAlgo = PGPHASHALGO_SHA1;
    00913         dc->algo = rpmioDigestHashAlgo;
    00914         dc->oalgo = dc->algo;
    00915         dc->oalgoName = rpmdcAlgo2Name(dc->oalgo);
    00916         if (!strcmp(dc->oalgoName, "sha1"))
    00917             dc->oalgoName = "sha1new";
    00918     } else {
    00919         dc->parse = rpmdcParseCoreutils;
    00920         dc->print = rpmdcPrintCoreutils;
    00921         if ((int)rpmioDigestHashAlgo < 0)
    00922             rpmioDigestHashAlgo = PGPHASHALGO_MD5;
    00923         dc->algo = rpmioDigestHashAlgo;
    00924     }
    00925
    00926     if (dc->ofn == NULL)
    00927         dc->ofn = "-";
    00928     dc->ftsoptions = rpmioFtsOpts;
    00929     if (!(dc->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)))
    00930         dc->ftsoptions |= FTS_PHYSICAL;
    00931     dc->ftsoptions |= FTS_NOCHDIR;
    00932
    00933     dc->ofd = Fopen(dc->ofn, "w.ufdio");
    00934     if (F_ISSET(dc, 0INSTALL))
    00935         fdInitDigest(dc->ofd, dc->oalgo, 0);
    00936
    00937     av = poptGetArgs(optCon);
    00938     ac = argvCount(av);
    00939     if ((ac == 0 && dc->manifests == NULL)
    00940      || (ac >  0 && dc->manifests != NULL))
    00941     {
    00942         poptPrintUsage(optCon, stderr, 0);
    00943         rc = 2;
    00944         goto exit;
    00945     }
    00946
    00947     if (dc->manifests != NULL) {
    00948         if ((xx = rpmdcLoadManifests(dc)) != 0)
    00949             rc = xx;
    00950     } else {
    00951         int i;
    00952         for (i = 0; i < ac; i++)
    00953             xx = argvAdd(&dc->paths, av[i]);
    00954     }
    00955     if (rc)
    00956         goto exit;
    00957
    00958     if (dc->manifests != NULL) {
    00959         dc->ix = 0;
    00960         av = dc->paths;
    00961         if (av != NULL)
    00962         while ((dc->fn = *av++) != NULL) {
    00963             if ((xx = Lstat(dc->fn, &dc->sb)) != 0
    00964              || (xx = rpmdcVisitF(dc)) != 0)
    00965                 rc = xx;
    00966             dc->ix++;
    00967         }
    00968     } else {
    00969         if ((xx = rpmdcCWalk(dc)) != 0)
    00970             rc = xx;
    00971     }
    00972
    00973 exit:
    00974     if (dc->nfailed)
    00975         fprintf(stderr, "%s: WARNING: %u of %u computed checksums did NOT match\n",
    00976                 __progname, (unsigned) dc->nfailed, (unsigned) dc->ncomputed);
    00977
    00978     if (dc->ofd) {
    00979         /* Print the output spewage digest for 0install format manifests. */
    00980         if (rc == 0 && F_ISSET(dc, 0INSTALL) && dc->manifests == NULL) {
    00981             static int asAscii = 1;
    00982             char *t;
    00983             fdFiniDigest(dc->ofd, dc->oalgo, &dc->digest, &dc->digestlen, asAscii);
    00984 assert(dc->digest != NULL);
    00985             t = rpmExpand(dc->oalgoName, "=", dc->digest, "\n", NULL);
    00986             (void) Fwrite(t, strlen(t), sizeof(*t), dc->ofd);
    00987             t = _free(t);
    00988             dc->digest = _free(dc->digest);
    00989         }
    00990         (void) Fclose(dc->ofd);
    00991         dc->ofd = NULL;
    00992     }
    00993
    00994 #ifdef  NOTYET
    00995     dc->manifests = argvFree(dc->manifests);
    00996 #endif
    00997     dc->algos = argiFree(dc->algos);
    00998     dc->digests = argvFree(dc->digests);
    00999     dc->paths = argvFree(dc->paths);
    01000
    01001     rpmswExit(&dc->totalops, 0);
    01002     if (_rpmsw_stats) {
    01003         rpmswPrint(" total:", &dc->totalops);
    01004         rpmswPrint("  read:", &dc->readops);
    01005         rpmswPrint("digest:", &dc->digestops);
    01006     }
    01007
    01008     optCon = rpmioFini(optCon);
    01009
    01010     return rc;
    01011 }