rpm 5.2.1

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 }