home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / filutl / brik2src.arc / BRIK.C < prev    next >
C/C++ Source or Header  |  1989-08-04  |  30KB  |  967 lines

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