home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / BRIK.ZIP / BRIK.C < prev    next >
C/C++ Source or Header  |  1989-03-13  |  26KB  |  875 lines

  1. /* ::[[ @(#) brik.c 1.44 89/03/12 14:34:35 ]]:: */
  2. #ifndef LINT
  3.  static char sccsid[]="::[[ @(#) brik.c 1.44 89/03/12 14:34:35 ]]::";
  4. #endif
  5.  
  6. #define DATESTAMP "1989/03/12 14:34:35"
  7.  
  8. /*
  9. (c) Copyright 1989 Rahul Dhesi, All rights reserved.  Permission is
  10. granted to copy and distribute this file in modified or unmodified
  11. form, for any purpose whether commercial or noncommercial, provided
  12. that (a) this paragraph, and the author identification printed by
  13. the program, are preserved unchanged, and (b) no attempt is made to
  14. restrict redistribution of this file.  Inclusion of this file in a
  15. collection whose distribution is restricted by a compilation copyright
  16. shall be considered an attempt to restrict redistribution of this file.
  17. Notwithstanding the above, there are no restrictions on the copying or
  18. distribution of not-human-readable executable machine code created by
  19. processing this file with a language translator.
  20. */
  21.  
  22. /*
  23. Checksum: 1280097858      (check or update this with "brik")
  24. */
  25.  
  26. /*
  27. The following code assumes the ASCII character set and 8-bit bytes.
  28. */
  29.  
  30. #ifndef OK_STDIO
  31. # include <stdio.h>
  32. # define OK_STDIO
  33. #endif
  34.  
  35. #include "brik.h"          /* configuration options */
  36. #include "assert.h"
  37.  
  38. FILE *fopen();
  39. FILE *efopen();
  40. char *fgets();
  41. int fclose();
  42. char *strchr();
  43. int strlen();
  44. int strcmp();
  45. char *strcat();
  46. int fseek();
  47. long ftell();
  48. int fwrite();
  49. void hdrcrc();
  50. void showerr();
  51. void longhelp();
  52. void shorthelp();
  53. long findcrc();
  54.  
  55. #ifdef ANSIPROTO
  56. void addbfcrc (char *, int);
  57. int printhdr (void);
  58. FILE *efopen (char *, char *, int);
  59. long xatol (char *);
  60. int main (int, char **);
  61. int dofname (char *);
  62. int dofptr (FILE *, char *);
  63. int whole_check (FILE *, char *);
  64. void hdrcrc (FILE *, char *);
  65. long findcrc (FILE *, char *);
  66. int updatefile (FILE *, long, long, char *);
  67. void longhelp(void);
  68. void shorthelp(void);
  69. void showerr (char *, int);
  70. char suffix(void);
  71. int lowerit (int);
  72.  
  73. int strlen (char *);
  74. char *strchr (char *, char);
  75. char *strcpy (char *, char *);
  76. char *strcat (char *, char *);
  77. char *strncat (char *, char *, int);
  78. int getopt (int, char **, char *);
  79. void exit (int);
  80. int strcmp (char *, char *);
  81. int readnames (FILE *);
  82. char *nextfile (int, char *, int);
  83. void brktst(void);
  84. #endif
  85.  
  86. #define MYNL      10       /* newline for CRC calculation */
  87. #define ERRLIMIT 127       /* exit(n) returns n not exceeding this */
  88. #define LINESIZE  8192     /* handle lines of up to this length */
  89. #define ERRBUFSIZ 1024     /* buffer size for perror() message */
  90. #define PATTERN   "Checksum:"    /* look for this header */
  91. #define CHARSINCRC 10      /* characters in CRC */
  92. #define BACKSIZE  1024     /* how much to seek back looking for header */
  93. #define CMTCH     '#'      /* begins comment in CRC list */
  94.  
  95. /* define macro for testing if chars within range */
  96. #ifndef BINCHAR
  97. # define LOW_PRINT '\007'   /* smallest printable char */
  98. # define HIGH_PRINT '\176'  /* biggest printable char */
  99. # define BINCHAR(c)     ((c) < LOW_PRINT || (c) > HIGH_PRINT)
  100. #endif /* BINCHAR */
  101.  
  102. /* error levels */
  103. #define LVL_WARN  0
  104. #define LVL_ERR   1
  105. #define LVL_FATAL 2
  106.  
  107. #ifdef  USEINDEX
  108. # define strchr   index
  109. #endif  /* USEINDEX */
  110.  
  111. #ifdef TRAIL_T             /* need trailing "t" in fopen string */
  112. # define BRIK_RD  "rt"
  113. # define BRIK_RW  "r+t"
  114. # define BRIK_RDB "rb"
  115. #else
  116. # define BRIK_RD  "r"
  117. # define BRIK_RW  "r+"
  118. # define BRIK_RDB "r"
  119. #endif   /* TRAIL_T */
  120.  
  121. #define  whitespace(x)     (strchr(" \t\n",(x)) != NULL)
  122. /* format strings for printing CRCs and filenames etc. */
  123. static char ok[] =      "ok ";
  124. static char bad[] =     "BAD";
  125. static char blank[] =   "   ";
  126. static char fmtstr[] = "%10lu%c %s %s\n";
  127.  
  128.  
  129. int patlen;                /* length of PATTERN */
  130. int errcount = 0;          /* count of errors */
  131. int gen1 = 0;              /* generate CRCs for all files */
  132. int gen2 = 0;              /* generate CRCs for files with headers */
  133. int silent = 0;            /* be quiet, just set error status */
  134. int verbose = 0;           /* be verbose, print message for good files too */
  135. int updfile = 0;           /* update file by inserting CRC */
  136. int check1 = 0;            /* whether to check header crcs */
  137. int check2 = 0;            /* whether to check whole file crcs */
  138. int fromfile = 0;          /* read filenames from a file */
  139. int binary = 0;            /* manipulate binary file */
  140. int trailing = 0;          /* include trailing empty lines */
  141.  
  142. #ifdef DEBUG
  143. int debugging = 0;
  144. #endif
  145.  
  146. /* opens file, prints error message if can't open */
  147. FILE *efopen (fname, mode, level)
  148. char *fname;               /* filename to open */
  149. char *mode;                /* mode, e.g. "r" or "r+" */
  150. int level;                 /* error level */
  151. {
  152.    FILE *fptr;
  153.    fptr = fopen (fname, mode);
  154.    if (fptr == NULL)
  155.       showerr (fname, level);
  156.    return (fptr);
  157. }
  158.  
  159. /* LOWERIT is a function or macro that returns lowercase of a character */
  160. #ifndef LOWERIT
  161. # ifdef AVOID_MACROS
  162. #  define LOWERIT    lowerit
  163. # else
  164. #  define LOWERIT(c)        ((c)>='A' && (c)<='Z' ? ('a'-'A')+(c) : (c))
  165. # endif
  166. #endif
  167.  
  168. /* Function needed by SEEKFIX code even if a macro is available */
  169. int lowerit (c) int c;  /* returns lowercase of an ASCII character */
  170. {
  171.   if (c >= 'A' && c <= 'Z') return (('a'-'A') + c);
  172.   else return (c);
  173. }
  174.  
  175. /* STRNICMP is a case-insensitive strncmp */
  176. #ifndef STRNICMP
  177. int STRNICMP (s1, s2, n)
  178. register char *s1, *s2;
  179. int n;
  180. {
  181.    assert (n >= 0);
  182.    assert (LOWERIT('X') == 'x');
  183.    assert (LOWERIT('*') == '*');
  184.  
  185.    for ( ; LOWERIT(*s1) == LOWERIT(*s2);  s1++, s2++) {
  186.       if (--n == 0 || *s1 == '\0')
  187.          return(0);
  188.    }
  189.    return(LOWERIT(*s1) - LOWERIT(*s2));
  190. }
  191. #endif /* STRNICMP */
  192.  
  193. #ifdef AVOID_MACROS
  194. # define BRINCMP     STRNICMP
  195. #else
  196. # define BRINCMP(s1,s2,n) (LOWERIT(*(s1))!=LOWERIT(*(s2))||STRNICMP(s1,s2,n))
  197. #endif
  198.  
  199.  
  200. #define xdigit(x)    ((x) >= '0' && (x) <= '9')
  201.  
  202. /*
  203. xatol is given a string that (supposedly) begins with a string
  204. of digits.  It returns a corresponding positive numeric value.
  205. */
  206. long xatol (str)
  207. char *str;
  208. {
  209.    long retval;
  210.    retval = 0L;
  211.    while (xdigit(*str)) {
  212.       retval = retval * 10L + (*str-'0');
  213.       str++;
  214.    }
  215.    return (retval);
  216. }
  217.  
  218. main (argc, argv)
  219. int argc;
  220. char **argv;
  221. {
  222.    int i;
  223.    int c;                        /* next option letter */
  224.    int count = 0;                /* count of required options seen */
  225.    char *infname;                /* name of file to read filenames from */
  226.    FILE *infptr;                 /* open file ptr for infname */
  227.  
  228.    extern int optind;            /* from getopt: next arg to process */
  229.    extern int opterr;            /* used by getopt */
  230.  
  231.    opterr = 0;                   /* so getopt won't print err msg */
  232.  
  233.    patlen = strlen (PATTERN);
  234.  
  235. #ifdef DEBUG
  236.    while ((c = getopt (argc, argv, "cCgGsvWfbThd")) != EOF)
  237. #else
  238.    while ((c = getopt (argc, argv, "cCgGsvWfbTh")) != EOF)
  239. #endif
  240.    {
  241.       switch (c) {
  242.          case 'c':   check1++; count++; break;
  243.          case 'C':   check2++; count++; break;
  244.          case 'g':   gen1++; count++; break;
  245.          case 'G':   gen2++; count++; break;
  246.          case 's':   silent++; verbose = 0; break;
  247.          case 'v':   verbose++; silent = 0; break;
  248.          case 'W':   updfile++; break;
  249.          case 'f':   fromfile++; break;
  250.          case 'b':   binary++; trailing = 0; break;
  251.          case 'T':   trailing++; binary = 0; break;
  252. #ifdef DEBUG
  253.          case 'd':   debugging++; break;
  254. #endif
  255.          case 'h':   longhelp();
  256.          case '?':   shorthelp();
  257.       }
  258.    }
  259.  
  260.    if (count != 1)
  261.       shorthelp();
  262.  
  263.    if (binary && (check1 || gen1)) {
  264.       fprintf (stderr, "brik: fatal: Can't read or update CRC header in binary mode\n");
  265.       exit (1);
  266.    }
  267.  
  268.    if (updfile && !gen1) {
  269.       fprintf (stderr, "brik: fatal: Use of -W requires -g\n");
  270.       exit (1);
  271.    }
  272.  
  273.    if ((gen1 || gen2) && !updfile)
  274.       silent = 0;
  275.  
  276.    i = optind;
  277.  
  278.    if (fromfile) {                  /* read filenames from file */
  279.       if (i >= argc) {              /* need filenames after -f */
  280.          fprintf (stderr, "brik: fatal: Filename(s) needed after -f\n");
  281.          exit (1);
  282.       }
  283.       for (; i < argc;  i++) {
  284.          infname = argv[i];
  285.          if (strcmp(infname, "-") == 0) { /* "-" means stdin */
  286.             readnames (stdin);
  287.          } else {
  288. #ifdef WILDCARD
  289.             extern char *nextfile();
  290.             nextfile (0, infname, 0);     /* initialize fileset 0 */
  291.             while ((infname = nextfile(1, (char *) NULL, 0)) != NULL) {
  292.                infptr = efopen (infname, BRIK_RD, LVL_ERR);
  293.                readnames (infptr);
  294.                fclose (infptr);
  295.             }
  296. #else
  297.             infptr = efopen (infname, BRIK_RD, LVL_ERR);
  298.             readnames (infptr);
  299.             fclose (infptr);
  300. #endif /* WILDCARD */
  301.          }
  302.       }
  303.    } else {                         /* read filenames from command line */
  304.       if (i >= argc) {
  305. #ifdef TRAIL_T
  306.          if (binary) {
  307.             fprintf (stderr, "brik: fatal: Can't handle stdin in binary mode\n");
  308.             exit (1);
  309.          }
  310. #endif
  311.          dofptr (stdin, "stdin");      /* if no files, read stdin */
  312.       } else {
  313.          for (;  i < argc;  i ++) {
  314. #ifdef WILDCARD
  315.             extern char *nextfile();
  316.             char *one_name;               /* a matching filename */
  317.             nextfile (0, argv[i], 0);     /* initialize fileset 0 */
  318.             while ((one_name = nextfile(1, (char *) NULL, 0)) != NULL)
  319.                dofname (one_name);
  320. #else
  321.             dofname (argv[i]);
  322. #endif /* WILDCARD */
  323.          }
  324.       }
  325.    }
  326. errexit:
  327.    if (errcount > ERRLIMIT)
  328.       errcount = ERRLIMIT;       /* don't overflow status code */
  329.    exit (errcount);
  330.    /*NOTREACHED*/
  331. }
  332.  
  333. /*
  334. **   Reads names from supplied file pointer and handles them.  Just
  335. **   returns if supplied NULL file pointer.  Will also expand wildcards
  336. **   in names read from this file.
  337. */
  338. readnames (infptr)
  339. FILE *infptr;
  340. {
  341.    char buf[LINESIZE];
  342.    if (infptr == NULL)
  343.       return;
  344.    while (fgets (buf, LINESIZE, infptr) != NULL) {
  345. #ifdef WILDCARD
  346.       char *fname;                  /* matching filename */
  347.       extern char *nextfile();
  348. #endif /* WILDCARD */
  349.       buf[strlen(buf)-1] = '\0'; /* zap trailing newline */
  350. #ifdef WILDCARD
  351.       nextfile (0, buf, 1);     /* initialize fileset 1 */
  352.       while ((fname = nextfile(1, (char *) NULL, 1)) != NULL) {
  353.          dofname (fname);
  354.       }
  355. #else
  356.       dofname (buf);
  357. #endif /* WILDCARD */
  358.    }
  359. }
  360.  
  361. /* do one filename */
  362. dofname (this_arg)
  363. char *this_arg;
  364. {
  365.    FILE *this_file;
  366.    char *mode;                         /* "r", "rb", "rw", etc. for fopen */
  367. #ifdef BRKTST
  368.    extern void brktst();
  369.    brktst();
  370. #endif
  371.  
  372.    if (strcmp(this_arg,"-") == 0) {
  373. #ifdef TRAIL_T
  374.       if (binary) {
  375.          fprintf (stderr, "brik: fatal: Can't handle stdin in binary mode\n");
  376.          exit (1);
  377.       }
  378. #endif
  379.       this_file = stdin;
  380.       this_arg = "stdin";
  381.    } else {
  382.       if (updfile) {
  383.          assert (!binary);
  384.          this_file = efopen (this_arg, BRIK_RW, LVL_ERR);
  385.       } else {
  386.          if (binary && !check2) /* check2 reads filenames, not data */
  387.             mode = BRIK_RDB;
  388.          else
  389.             mode = BRIK_RD;
  390.          this_file = efopen (this_arg, mode, LVL_ERR);
  391.       }
  392.    }
  393.    if (this_file == NULL)
  394.       errcount++;
  395.    else {
  396. #ifdef NOCASE
  397.       char *p;
  398.       for (p = this_arg;  *p != '\0';  p++)
  399.          *p = LOWERIT(*p);
  400. #endif
  401.       dofptr (this_file, this_arg);
  402.       fclose (this_file);
  403.    }
  404. }
  405.  
  406. char suffix()
  407. {
  408.    return (binary ? 'b' : (trailing ? 'T' : ' '));
  409. }
  410.  
  411.  
  412. /*
  413. **   Do one file pointer.  Decides if CRC header will be read or written,
  414. **   or whether just the whole file will be read.
  415. */
  416. dofptr (fptr, fname)
  417. FILE *fptr;
  418. char *fname;
  419. {
  420.    if (check2)
  421.       whole_check (fptr, fname);    /* do whole file check from list */
  422.    else if (gen1 || check1)         /* header-based CRC check or update */
  423.       hdrcrc (fptr, fname);
  424.    else {                           /* whole-file CRC calculation */
  425.       extern long crccode;
  426.       assert (gen2);
  427.       printhdr();
  428.       findcrc (fptr, fname);
  429.       printf (fmtstr, crccode, suffix(), blank, fname);
  430.    }
  431. }
  432.  
  433.  
  434. /* Does whole file check from a list of files and CRCs */
  435. whole_check (fptr, listname)
  436. FILE *fptr;                   /* open file ptr of CRC list file */
  437. char *listname;               /* name of CRC list file */
  438. {
  439.    long fcrc;                 /* recorded crc */
  440.    char *fname;               /* name of file whose CRC being checked */
  441.    char buf [LINESIZE];       /* line buffer */
  442.    char *p;                   /* temp ptr */
  443.    FILE *orgfile;             /* file pointer for original file to check */
  444.    int lino = 0;              /* line no. in list file for error msg */
  445.    char *mode;                /* mode string for fopen */
  446.  
  447.    while (fgets (buf, LINESIZE, fptr) != NULL) {
  448.       lino++;
  449.       p = buf;
  450.       if (*p == CMTCH)              /* skip comment lines */
  451.          continue;
  452.       while (*p != '\0' && whitespace(*p))      /* skip whitespace */
  453.          p++;
  454.       if (*p == '\0')
  455.          continue;                              /* skip empty lines */
  456.       if (!xdigit(*p))
  457.          goto badline;
  458.       fcrc = xatol (p); /* recorded CRC */
  459.  
  460.       while (xdigit(*p))
  461.          p++;                                   /* skip past numeric chars */
  462.  
  463.       binary = trailing = 0;
  464.       if (*p == 'b')                            /* 'b' means binary */
  465.          binary = 1;
  466.  
  467.       if (*p == 'T')                            /* 'T' means trailing mode */
  468.          trailing = 1;
  469.  
  470.       while (*p != '\0' && !whitespace(*p)) /* to whitespace */
  471.          p++;
  472.       while (whitespace(*p))   /* skip whitespace */
  473.          p++;
  474.  
  475.       if (*p == '\n' || *p == '\0') {     /* if at end of line */
  476.          goto badline;
  477.       }
  478.       fname = p;
  479.       while (*p != '\0' && !whitespace(*p))  /* skip to whitespace */
  480.          p++;
  481.       *p = '\0';                    /* null-terminate filename */
  482.  
  483.       if (binary)
  484.          mode = BRIK_RDB;
  485.       else
  486.          mode = BRIK_RD;
  487.  
  488.       orgfile = efopen (fname, mode, LVL_ERR);
  489.       if (orgfile == NULL) {
  490.          errcount++;
  491.       } else {
  492.          long foundcrc;
  493.          assert (!(binary && trailing));
  494.          foundcrc = findcrc (orgfile, fname);
  495.          if (foundcrc == fcrc) {
  496.             if (verbose)
  497.                printf (fmtstr, foundcrc, suffix(), ok, fname);
  498.          } else {
  499.             if (!silent)
  500.                printf (fmtstr, foundcrc, suffix(), bad, fname);
  501.             errcount ++;
  502.          }
  503.          fclose (orgfile);
  504.       }
  505.    }
  506.    return;
  507. badline:
  508.    fprintf (stderr,
  509.       "brik: error: Abandoning %s due to badly formatted line %d\n",
  510.       listname, lino);
  511.    return;
  512. }
  513.  
  514.  
  515. /*
  516. Initializing the CRC to all one bits avoids failure of detection
  517. should entire data stream get cyclically bit-shifted by one position.
  518. The calculation of the probability of this happening is left as
  519. an exercise for the reader.
  520. */
  521. #define INITCRC   0xFFFFFFFFL;
  522.  
  523. /*
  524. **   hdrcrc processes one file given an open file pointer
  525. **   and the filename.  The filename is used for messages etc.
  526. **   It does all manipulation of header-related CRCs, i.e.,
  527. **   checking generating header CRC.  It deals only with text files.
  528. */
  529. void hdrcrc (fptr, fname)
  530. FILE *fptr;
  531. char *fname;
  532. {
  533.    char buf[LINESIZE];
  534.    int lino = 0;
  535.    char *ptr;
  536.    long fcrc;   /* crc recorded in file */
  537.    extern long crccode;
  538.    long hdrpos;                     /* where we found crc header in file */
  539.  
  540.    crccode = INITCRC;
  541.  
  542.    assert (!binary);
  543.  
  544. #ifndef NIXSEEK
  545.    hdrpos = ftell (fptr);
  546. #endif
  547.    while (fgets (buf, LINESIZE, fptr) != NULL) {
  548. #ifdef BRKTST
  549.       extern void brktst();
  550.       brktst();
  551. #endif
  552.       lino++;
  553.       if (BRINCMP (buf, PATTERN, patlen) == 0) {      /* found header */
  554. #ifdef NIXSEEK
  555.          hdrpos = ftell (fptr);        /* seek posn of line with header */
  556. #endif
  557.          ptr = buf + patlen;           /* point to beyond header */
  558.          while (*ptr != '\0' && whitespace(*ptr))
  559.             ptr++;                     /* skip white space */
  560.          fcrc = xatol (ptr);           /* get stored crc */
  561.          while (xdigit(*ptr))
  562.             ptr++;                     /* skip past digits */
  563.          if (check1) {
  564.             if (*ptr == 'T')           /* if 'T' suffix then */
  565.                trailing = 1;          /* ..include trailing empty lines */
  566.             else
  567.                trailing = 0;
  568.          }
  569.  
  570.          findcrc (fptr, fname);        /* find CRC for rest of file */
  571.  
  572.          if (gen1) {                   /* generating CRC */
  573.             if (updfile) {             /* if updating file posn */
  574.                updatefile (fptr, hdrpos, crccode, fname); /* then do it */
  575.             } else if (!silent)
  576.                printf (fmtstr, crccode, suffix(), blank, fname);
  577.          } else {                      /* checking CRC */
  578.             if (fcrc == crccode) {
  579.                if (verbose)
  580.                   printf (fmtstr, crccode, suffix(), ok, fname);
  581.             } else {
  582.                if (!silent)
  583.                   printf (fmtstr, crccode, suffix(), bad, fname);
  584.                errcount ++;
  585.             }
  586.          }
  587.          return;
  588.       } /* end if (BRINCMP (...) ) */
  589. #ifndef NIXSEEK
  590.       hdrpos = ftell (fptr);
  591. #endif
  592.    } /* end of while (fgets(...)) */
  593.  
  594.    /* reach here if header not found -- this is an error */
  595.    if (!silent)
  596.       printf ("%10s      %s\n", "????", fname);
  597.    errcount++;
  598.    return;
  599. }
  600.  
  601. /* update file with CRC -- must be seekable */
  602. updatefile (fptr, hdrpos, crccode, fname)
  603. FILE *fptr;
  604. long hdrpos;
  605. long crccode;
  606. char *fname;
  607. {
  608.    char buf[LINESIZE];
  609.    int buflen;             /* will hold count of chars in buf */
  610.    int chars_to_print;     /* chars needed to fill in CRC */
  611.  
  612.    /*
  613.    1 for blank, CHARSINCRC for actual CRC, and possibly
  614.    1 more for 'T' suffix if including trailing empty lines too
  615.    */
  616.    chars_to_print = 1 + CHARSINCRC + (trailing ? 1 : 0);
  617.  
  618. #ifndef NIXSEEK
  619.    /* hdrpos is already seek position of header */
  620.    if (fseek (fptr, hdrpos, 0) != 0) { /* seek back */
  621.       fprintf(stderr,
  622.          "brik: error: No CRC written, seek failed on %s\n",fname);
  623.       return;
  624.    }
  625.  
  626. SEEKFIX
  627.  
  628.    fgets (buf, LINESIZE, fptr);
  629.    if (BRINCMP (buf, PATTERN, patlen) == 0)
  630.       goto foundit;
  631.    fprintf(stderr,
  632.       "brik: error: No CRC written, header lost in %s\n",fname);
  633.    return;
  634. #else
  635.    /* Following code does fseeks in a non-ANSI-conformant way */
  636.    /* hdrpos is seek position *after* header was read.  Need to get back */
  637.    if (hdrpos >= BACKSIZE)
  638.       hdrpos -= BACKSIZE;
  639.    else
  640.       hdrpos = 0L;
  641.    if (fseek (fptr, hdrpos, 0) != 0) {       /* seek back first */
  642.       fprintf(stderr,"brik: error: No CRC written, seek failed on %s\n",fname);
  643.       return;
  644.    }
  645.    /* now seek forward until we see CRC header again */
  646.    hdrpos = ftell (fptr);
  647.    while (fgets (buf, LINESIZE, fptr) != NULL) {
  648.       if (BRINCMP (buf, PATTERN, patlen) == 0)
  649.          goto foundit;
  650.       hdrpos = ftell (fptr);
  651.    }
  652.    fprintf(stderr,"brik: error: No CRC written, header lost in %s\n",fname);
  653.    return;
  654. #endif /* NIXSEEK */
  655.  
  656. foundit:    /* hdrpos points to line with header */
  657.    if (fseek (fptr, hdrpos, 0) != 0) { /* seek failed */
  658.       fprintf(stderr,"brik: error: No CRC written, seek failed on %s\n",fname);
  659.       return;
  660.    }
  661. SEEKFIX
  662.    /* we are seeked back to the line with the CRC header */
  663.  
  664. #ifdef CHECKSEEK  /* triple-check seeks */
  665.    {
  666.       char tmpbf1[LINESIZE];
  667.       char tmpbf2[LINESIZE];
  668.       fseek (fptr, hdrpos, 0);
  669.       assert (ftell (fptr) == hdrpos);
  670. SEEKFIX
  671.       fgets (tmpbf1, LINESIZE, fptr);
  672.       fseek (fptr, 0L, 0); fseek (fptr, 0L, 2);    /* exercise seeks */
  673.       fseek (fptr, hdrpos, 0);
  674.       assert (ftell (fptr) == hdrpos);
  675. SEEKFIX
  676.       fgets (tmpbf2, LINESIZE, fptr);
  677.       if (strcmp(tmpbf1,tmpbf2) != 0 || BRINCMP(tmpbf1,PATTERN,patlen) != 0) {
  678.          fprintf (stderr,
  679.             "brik: error: Bad seek on %s, abandoning this file\n", fname);
  680.          return;
  681.       }
  682.       fseek (fptr, hdrpos, 0);
  683. SEEKFIX
  684.    }
  685. #endif /* CHECKSEEK */
  686.  
  687. #ifdef DEBUG
  688.    if (debugging) {  /* zap newline, print buffer, restore newline */
  689.       int nlpos; char savech;
  690.       nlpos = strlen(buf) - 1;  savech = buf[nlpos];  buf[nlpos] = '\0';
  691.       fprintf (stderr, "read header  [%s]\n", buf);
  692.       buf[nlpos] = savech;
  693.    }
  694. #endif
  695.  
  696.    buflen = strlen (buf);
  697. #ifdef DEBUG
  698.    if (debugging)  /* need chars_to_print plus one trailing space or newline */
  699.       fprintf(stderr,"need %d chars, have %d\n",chars_to_print+1,buflen-patlen);
  700. #endif
  701.    if (buflen - patlen > chars_to_print) {      /* if enough space */
  702.       char sprbuf[1+CHARSINCRC+1+1+6];  /* blank+CRC+suffix+null+fudge */
  703.       char *ptr;
  704.       int i;
  705.       ptr = &buf[patlen];                 /* point to beyond header */
  706.       sprintf (sprbuf, " %10lu%c", crccode, 'T');
  707.       for (i = 0;  i < chars_to_print;  i++) /* trailing 'T' possibly ignored */
  708.          ptr[i] = sprbuf[i];
  709.       if (ptr[i] != '\n')
  710.          ptr[i] = ' ';           /* terminate with newline or blank */
  711.       fseek (fptr, 0L, 1);       /* after read, must seek before write */
  712.       if (fwrite (buf, 1, buflen, fptr) != buflen) {
  713.          fprintf(stderr,
  714.             "brik: error: Write failed while writing CRC to %s\n",fname);
  715.       } else if (verbose)
  716.          printf (fmtstr, crccode, suffix(), blank, fname);
  717.          /* printf ("%10lu      %s\n", crccode, fname); */
  718. #ifdef DEBUG
  719.       buf[strlen(buf)-1] = '\0'; /* zap trailing newline */
  720.       if (debugging)
  721.          fprintf (stderr, "wrote header [%s]\n", buf);
  722. #endif
  723.    } else {
  724.       fprintf(stderr,"brik: error: Not enough space for CRC in %s\n",fname);
  725.       return;
  726.    }
  727. }
  728.  
  729. void longhelp()
  730. {
  731. printf ("     Brik 1.0 (%s), a free CRC-32 program by Rahul Dhesi\n\n",
  732.                         DATESTAMP);
  733.  
  734. printf ("Usage:  brik -cCgGsvWfbT [ file ] ...\n\n");
  735.  
  736. printf ("Brik 1.0 generates and verifies 32-bit CRC values (checksums).  Optionally\n");
  737. printf ("it will read or update a \"Checksum: xxxxxxxxxx\" header at the beginning of\n");
  738. printf ("a line in which xxxxxxxxxx represents the CRC of all lines in the file\n");
  739. printf ("*after* this header.  One of -c, -C, -g, or -G is required.  If no\n");
  740. #ifdef WILDCARD
  741. printf ("filename is given, or if a filename is -, standard input is read.\n");
  742. printf ("Wildcards are allowed on the command line and in files read with -f.\n\n");
  743. #else
  744. /* extra newline */
  745. printf ("filename is given, or if a filename is -, standard input is read.\n\n");
  746. #endif
  747.  
  748. printf ("   -g     look for Checksum: header, generate CRC for rest of file\n");
  749. printf ("   -c     get CRC from header, verify CRC of rest of file\n");
  750. printf ("   -G     generate CRC for entire file (add -b for binary files)\n");
  751. printf ("   -C     verify all file CRCs from output of -G (-f is not needed)\n");
  752. printf ("   -b     use binary mode--read file byte by byte, not line by line\n");
  753.  
  754. #ifdef WILDCARD
  755. printf ("   -f     read filenames (wildcards ok) from specified files\n");
  756. #else
  757. printf ("   -f     read filenames from specified files\n");
  758. #endif
  759.  
  760. printf ("   -v     be verbose, report all results (else only errors are reported)\n");
  761. printf ("   -s     be silent, say nothing, just return status code\n");
  762. printf ("   -W     after generating CRC with -g, write it to original header\n");
  763. printf ("   -T     include trailing empty lines, normally ignored (text mode only)\n\n");
  764.  
  765. #ifndef EXITBUG
  766. printf ("The exit code is the number of files with bad or missing CRCs.\n");
  767. #endif
  768.  
  769. exit (1);
  770. }
  771.  
  772. /*
  773. **   Generates CRC of an open file, from current file position to end
  774. **   Except in -T mode, will ignore all trailing empty lines in text
  775. **   files.  Algorithm for this is:
  776. **      1.   After each nonempty line, save crccode so far.
  777. **      2.   At end of file, if last line was empty, use saved crccode rather
  778. **           than current one.
  779. */
  780.  
  781. long findcrc (fptr, fname)
  782. FILE *fptr;
  783. char *fname;
  784. {
  785.    int count;
  786.    char buf[LINESIZE];
  787.    extern long crccode;
  788.    int warned = 0;
  789.    long savedcrc;       /* save crccode here to handle trailing empty lines */
  790.    int buflen;
  791.  
  792.    savedcrc = crccode = INITCRC;
  793.  
  794.    if (binary) {                                   /* binary */
  795.       while ((count = fread (buf, 1, LINESIZE, fptr)) > 0) {
  796. #ifdef BRKTST
  797.          extern void brktst(); brktst();
  798. #endif
  799.          addbfcrc (buf, count);
  800.       }
  801.       return (crccode);
  802.    } else {                                           /* text */
  803.       buflen = 1;                   /* assume empty lines so far */
  804.       while (fgets (buf, LINESIZE, fptr) != NULL) {
  805.          char *p;
  806.          char *limit;
  807. #ifdef BRKTST
  808.          extern void brktst(); brktst();
  809. #endif
  810.          buflen = strlen (buf);
  811.          limit = buf + buflen;
  812.          for (p = buf;  p != limit;  p++) {
  813.             if (!warned && BINCHAR(*p)) {
  814.                fprintf (stderr,
  815.                   "brik: warning: File %s is probably binary, don't trust text mode CRC\n",
  816.                   fname);
  817.                warned = 1;
  818.             }
  819.             if (*p == '\n')
  820.                *p = MYNL;
  821.          }
  822.          addbfcrc (buf, buflen);
  823.          if (buflen != 1)
  824.             savedcrc = crccode;
  825.       }
  826.       if (!trailing && buflen == 1)
  827.          crccode = savedcrc;
  828.       return (crccode);
  829.    }
  830. }
  831.  
  832. printhdr ()
  833. {
  834.    static int firsttime = 1;
  835.    if (firsttime) {
  836.       printf ("%c Whole file CRCs generated by Brik v1.0.  Use \"brik -C\" to verify them.\n\n",
  837.          CMTCH);
  838.         printf ("%c CRC-32        filename\n", CMTCH);
  839.         printf ("%c ------        --------\n\n", CMTCH);
  840.       firsttime = 0;
  841.    }
  842. }
  843.  
  844. /*
  845. **   Prints error message via perror().  The message is printed in the
  846. **   format "brik: %s: %s" where the first %s is the level text ("warning",
  847. **   "error", or "fatal") and the second %s is the string supplied by
  848. **   perror().
  849. **
  850. **   superfluous right now because it is only called from efopen()
  851. **   and only with level = LVL_ERR.
  852. */
  853.  
  854. void showerr (errmsg, level)
  855. char *errmsg;
  856. int level;
  857. {
  858. #define ERRSTRMAX  40         /* don't copy more than this many chars */
  859.    static char leveltext[][7] =   {"warning", "error", "fatal"};
  860.    char errbuf[ERRBUFSIZ];       /* buffer for error message */
  861.    strcpy (errbuf, "brik: ");
  862.    assert (level >= LVL_WARN && level <= LVL_FATAL);
  863.    strncat (errbuf, leveltext[level], ERRSTRMAX);
  864.    strcat (errbuf, ": ");
  865.    strncat (errbuf, errmsg, ERRSTRMAX);
  866.    perror (errbuf);
  867. }
  868.  
  869. void shorthelp()
  870. {
  871.    fprintf (stderr,
  872.    "brik: fatal: One of -cCgG required, -svWfbT are optional (-h for help)\n");
  873.    exit (1);
  874. }
  875.