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