home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 September / Simtel20_Sept92.cdr / msdos / trojanpr / filetest.arc / FILECRC.SRC < prev    next >
Text File  |  1988-03-09  |  31KB  |  1,039 lines

  1. {  PROGRAM TO CREATE OF FILE OF  THE CRC'S OF THE FILES ON THE DEFAULT DISK  }
  2.  
  3. {
  4.  
  5.   This program was written by Ted H. Emigh, and has been placed in the public
  6.   domain, to be used at the user's discretion.  The CRC routines and the
  7.   discussion of the CRC were written by David Dantowitz, Digital Equipment
  8.   Corporation,  Dantowitz%eagle1.dec@decwrl.
  9.  
  10.   This program calculates the CRC (cyclic redundancy check) for all the files
  11.   on the disk (with the exception of files that are hidden system files).  The
  12.   CRC's are placed in a file (CHECK$$$.NEW) to be compared with the CRC's
  13.   calculated at a previous time in the file CHECK$$$.CRC.  The comparison is
  14.   done with the program COMPARE.PAS.  This program is set to automatically
  15.   chain to COMPARE.PAS to automate the procedure, but this can be turned off
  16.   by deleting the lines:
  17.     Assign (chain_file,'COMPARE.CHN');
  18.     Chain(chain_file);
  19.   at the end of this program.
  20.  
  21.  
  22.    For a good discussion of polynomial selection see "Cyclic
  23.    Codes for Error Detection", by W. W. Peterson and
  24.    D. T. Brown, Proceedings of the IEEE, volume 49, pp 228-235,
  25.    January 1961.
  26.  
  27.    A reference on table driven CRC computation is "A Cyclic
  28.    Redundancy Checking (CRC) Algorithm" by A. B. Marton and
  29.    T. K. Frambs, The Honeywell Computer Journal, volume 5,
  30.    number 3, 1971.
  31.  
  32.    Also used to prepare these examples was "Computer Networks",
  33.    by Andrew S. Tanenbaum, Prentice Hall, Inc.  Englewood Cliffs,
  34.    New Jersey, 1981.
  35.  
  36.    The following three polynomials are international standards:
  37.  
  38.  
  39.         CRC-12 = X^12 + X^11 + X^3 + X^2 + X^1 + 1
  40.         CRC-16 = X^16 + X^15 + X^2 + 1
  41.         CRC-CCITT = X^16 + X^12 + X^5 + 1
  42.  
  43.    The polynomials can be represented by a binary number, where a 1
  44.    indicates the inclusion of the power term in the polynomial.  Since
  45.    the highest order term is always included, that term is not needed
  46.    in specifying the polynomial, and usually is dropped.  In addition,
  47.    the bits are specified from low-order to high-order.  For example,
  48.    the polynomial CRC-12 can be represented in the following manner:
  49.  
  50.     Order                   0  1  2  3  4  5  6  7  8  9 10 11 12
  51.     Term Included ?         Y  Y  Y  Y  N  N  N  N  N  N  N  Y  Y
  52.     Binary Representation   1  1  1  1  0  0  0  0  0  0  0  1 (1)<-- DROPPED
  53.  
  54.    The binary and hex representations for the three polynomials are:
  55.  
  56.                    Binary                     Hex
  57.  
  58.         CRC-12    = 1111 0000 0001           $0F01
  59.         CRC-16    = 1010 0000 0000 0001      $A001
  60.         CRC-CCITT = 1000 0100 0000 1000      $8404    (Used below)
  61.  
  62.    The first is used with 6-bit characters and the second two
  63.    with 8-bit characters.  All of the above will detect any
  64.    odd number of errors.  The second two will catch all 16-bit
  65.    bursts, a high percentage of random 17-bit bursts (~99.997%) and
  66.    also a large percentage of random 18-bit or larger bursts (~99.998%).
  67.    The paper mentioned above (Peterson and Brown) discusses how
  68.    to compute the statistics presented which have been quoted
  69.    from Tanenbaum.  Notice that some errors can be generated in
  70.    nonrandom ways that can substantially reduce the chances of
  71.    detecting errors.
  72.  
  73.    (A burst of length N is defined a sequence of N bits, where
  74.    the first and last bits are incorrect and the bits in the
  75.    middle are any possible combination of correct and incorrect.
  76.    See the paper by Peterson and Brown for more information)
  77.  
  78. }
  79.  
  80. {$G512,P512,U+,R+ }
  81. Program FILECRC;
  82.  
  83. Const
  84.   BufSize = 192;  { Number of 128 byte sectors in the CRC buffer }
  85.   Buffer_Length = 24576;  { BufSize * 128 = Length of the CRC buffer }
  86.   Version = 1.02;
  87.   Version_Date = '12 SEP 86';
  88.   POLY = $8404;  {  CRC Polynomial Used  }
  89.  
  90. Type
  91.   Bytes = Array [1..24576] of Byte;  {  Length is 1..Buffer_Length  }
  92.  
  93.   Registers = record  {  Registers for 8088/8086/80286  }
  94.                 ax, bx, cx, dx, bp, si, di, ds, es, flags : integer;
  95.               end;
  96.  
  97.   DTA_record = record  {  DTA as used by MSDOS  }
  98.                  dos : array [1..21] of char;
  99.                  attribute : byte;  {  Attribute byte  }
  100.                  time_of_day : integer;  {  Time of Day of File Creation  }
  101.                  date : integer;  {  Date of File Creation  }
  102.                  low_size, high_size : integer;  {  Size of the File  }
  103.                  filename: array [1..13] of char;  { File Name  }
  104.                  junk : array [1..85] of byte;
  105.                end;
  106.  
  107.   string255 = string[255];
  108.  
  109. Var
  110.   {  Variables used in Calculating the CRC  }
  111.  
  112.   str_length, RecsRead, CRC_value : integer;
  113.   table_256 : Array [0 .. 255] of Integer;  {CRC Table to speed computations}
  114.   byte_string : Bytes;
  115.  
  116.   {  Variables used in setting up the input and output files  }
  117.  
  118.   filvar : file;
  119.   chain_file : file;
  120.   outfile : TEXT[$4000];
  121.   check_crc : boolean;
  122.  
  123.   {  Misc. Variables  }
  124.  
  125.   root : string255;  {  Contains the default drive and root directory }
  126.   global_reg : registers;  {  Registers for the DOS calls  }
  127.  
  128.  
  129. Procedure generate_table_256(POLY : Integer);
  130.  
  131. {
  132.     This routine computes the remainder values of 0 through 255 divided
  133.   by the polynomial represented by POLY.  These values are placed in a
  134.   table and used to compute the CRC of a block of data efficiently.
  135.   More space is used, but the CRC computation will be faster.
  136.  
  137.  
  138.  
  139.     This implementation only permits polynomials up to degree 16.
  140. }
  141.  
  142.  
  143. Var
  144.    val, i, result : Integer;
  145.  
  146. Begin
  147. For val := 0 to 255 Do
  148.   Begin
  149.      result := val;
  150.      For i := 1 to 8 Do
  151.         Begin
  152.            If (result and 1) = 1
  153.               then result := (result shr 1) xor POLY
  154.               else result :=  result shr 1;
  155.         End;
  156.  
  157.      table_256[val] := result;
  158.   End
  159. End;
  160.  
  161.  
  162. Function crc_string_256(Var s : Bytes; s_length, initial_crc : Integer)
  163.                         : Integer;
  164.  
  165. {
  166.      This routine computes the CRC value and returns it as the function
  167.   value.  The routine takes an array of Bytes, a length and an initial
  168.   value for the CRC.  The routine requires that a table of 256 values
  169.   be set up by a previous call to Generate_table_256.
  170.  
  171.       This routine uses table_256.
  172. }
  173.  
  174. Begin
  175.  
  176. inline(
  177.  
  178. $c4/$7e/<s/                {les di,s[bp]            (es:di points to array)  }
  179. $8b/$46/<initial_crc/      {mov ax,initial_crc[bp]  (initial CRC value)      }
  180. $8b/$4e/<s_length/         {mov cx,s_length[bp]     (count)                  }
  181. $be/table_256/             {mov si,offset table_256 (table address)          }
  182.  
  183.  
  184. { next:  }
  185.  
  186. $26/$32/$05/               {xor al,es:[di]          CRC = CRC XOR next byte  }
  187. $47/                       {inc di                  (point to next byte)     }
  188.  
  189. { intermediate steps, see comments for overall effect }
  190.  
  191. $31/$db/                   {xor bx,bx               (bx <- 0)                }
  192. $86/$d8/                   {xchg al,bl              (bx <- ax and 0FF)       }
  193. $86/$e0/                   {xchg al,ah              (ax <- ax shr 8)         }
  194. $d1/$e3/                   {shl bx,1                (bx <- bx+bx)            }
  195.  
  196. $33/$00/                   {xor ax,[bx+si]          CRC = (CRC shr 8) XOR
  197.                                                           table[CRC and 0FF] }
  198.  
  199. $e2/$f0/                   {loop next               (count <- count -1)      }
  200.  
  201. $89/$46/<s+4);             {mov s+4[bp],ax          (crc_string_256 := CRC)  }
  202.  
  203.  
  204. {  basic algorithm expressed above
  205.  
  206. crc := initial_crc
  207.  
  208. For each byte Do
  209. Begin
  210.   crc := crc XOR next_byte;
  211.   crc := (crc shr 8) XOR table_256 [crc and $FF];
  212. End;
  213.  
  214. crc_string_256 := crc;
  215. }
  216. End;
  217.  
  218.  
  219.  
  220. Procedure set_attr (attr : byte; asciiz : string255);
  221. {
  222.  
  223.   This routine sets the file attributes.  Uses Function $43 in
  224.   Interrupt $21.
  225.  
  226.   Turbo Pascal is unable to open and read various types files
  227.   (e.g., r/o and files that are both hidden and system).  This
  228.   gets around that by always setting the attribute to 0, then
  229.   reseting it to the original value.
  230.  
  231.   attr  is the attribute to be set on the file
  232.   asciiz is a string variable with the file name
  233.  
  234. }
  235.  
  236. begin
  237.   asciiz := asciiz + chr(0);  {  Make a valid DOS ASCIIZ name  }
  238.   {  Set up the registers for the interrupt  }
  239.   global_reg.ax := $4301;
  240.   global_reg.ds := seg(asciiz);
  241.   global_reg.dx := ofs(asciiz)+1;
  242.   global_reg.cx := attr;
  243.   intr ($21, global_reg);
  244. end;
  245.  
  246.  
  247. Procedure get_crc(this_file : string255; dta : DTA_record);
  248. {
  249.   This procedure computes the CRC for a file.  Value is returned
  250.   in the global variable CRC_value.
  251.  
  252.   this_file is a string variable containing the file name
  253.   dta is a DTA_Record containing the file's DTA
  254.  
  255. }
  256.  
  257. var
  258.   length  : real;  {  Length of the File  }
  259.  
  260. begin
  261.  
  262.   {  Change the Attribute byte so we can always open it  }
  263.   {    To save some time, this is only done if the file  }
  264.   {    Has any attribute other than ARCHIVE              }
  265.  
  266.   if (dta.attribute and $DF <> 0) then
  267.     set_attr ( 0, this_file);
  268.  
  269.   {  Get the size of the file  }
  270.  
  271.   if dta.low_size < 0 then
  272.     {  Negative low_size is really number between 32768 and 65536  }
  273.     length := int(dta.high_size)*65536.0 + 32768.0
  274.               + int(dta.low_size and $7FFF)
  275.   else
  276.     length := int(dta.high_size)*65536.0 + int(dta.low_size);
  277.  
  278.   {  Open the file as untyped  }
  279.  
  280.   Assign (Filvar, this_file);
  281.   Reset (Filvar);
  282.  
  283.   {  Calculate the CRC  }
  284.  
  285.   CRC_value := 0;
  286.   While length > 0.5 do
  287.   Begin
  288.     {  Read a segment of the file to process  }
  289.     BlockRead(filvar,byte_string,BufSize,RecsRead);
  290.     {  Get the correct number of bytes to process  }
  291.     if length >= Buffer_Length then
  292.       str_length := Buffer_Length
  293.     else
  294.       str_length := round(length);
  295.     {  Compute the CRC  }
  296.     CRC_value := crc_string_256(byte_string, str_length, CRC_value);
  297.     {  Adjust the file length  }
  298.     length := length - Buffer_Length;
  299.   End;
  300.  
  301.   Close (Filvar);
  302.  
  303.   {  Restore the correct Attribute Byte  }
  304.   if (dta.attribute and $DF <> 0) then
  305.     set_attr ( dta.attribute, this_file);
  306.  
  307. end;
  308.  
  309.  
  310. Procedure directory(current_directory : string255);
  311.  
  312. {
  313.   Procedure to calculate the CRC of all the files in a directory,
  314.   then all subdirectories in that directory
  315.  
  316.   current_directory contains the directory name (including drive)
  317.  
  318. }
  319.  
  320. var
  321.   DTA_ofs, DTA_seg : integer;  {  Contains the current DTA address  }
  322.   reg : Registers;  {  Local 8088/8086/80286 registers  }
  323.   DTA : DTA_record;  {  Local DTA  }
  324.   this_directory, this_file, asciiz : string255;  { directory and file names }
  325.  
  326.  
  327. function get_file : string255;
  328.  
  329. {  Get the file name from the DTA  }
  330.  
  331. var
  332.   i : integer;
  333.   temp_file : string255;
  334.  
  335. begin
  336.   i := 1;
  337.   temp_file := '';
  338.   repeat
  339.     temp_file := temp_file + DTA.filename[i];
  340.     i := i+1;
  341.   until dta.filename[i] = chr(0);
  342.  
  343.   get_file := temp_file;
  344.  
  345. end;
  346.  
  347.  
  348. function is_directory : boolean;
  349.  
  350. {  Function to tell if the file is a directory entry  }
  351.  
  352. begin
  353.   is_directory := ((dta.attribute and $10) <> 0)
  354.                    and (dta.filename[1] <> '.');
  355. end;
  356.  
  357. Procedure set_DTA(offset, segment : integer);
  358.  
  359. {   sets the disk DTA
  360.     Uses MSDOS Function $1A with interrupt $21
  361.     offset is the offset of the new DTA
  362.     segment is the segment of the new DTA
  363. }
  364.  
  365. begin
  366.   reg.ax := $1a00;
  367.   reg.ds := segment;
  368.   reg.dx := offset;
  369.   intr($21, reg);
  370. end;
  371.  
  372. Procedure get_DTA(var offset, segment : integer);
  373.  
  374. {   gets the disk DTA
  375.     Uses MSDOS Function $2F with Interrupt $21
  376.     offset will return with the current DTA offset
  377.     segment will return with the current DTA segment
  378. }
  379.  
  380. begin
  381.   reg.ax := $2f00;
  382.   intr($21, reg);
  383.   offset := reg.bx;
  384.   segment := reg.es;
  385. end;
  386.  
  387.  
  388. Function find_first (attr_mask : byte) : boolean;
  389.  
  390. {
  391.     Find the first file matching the ASCIIZ string.
  392.     attr_mask is $27 for files only and $37 for directories & files
  393.  
  394.     INT 21 function 4EH
  395.     Returns TRUE if found, FALSE if not found
  396. }
  397.  
  398. begin
  399.   reg.ax := $4e00;
  400.   reg.ds := seg(asciiz);
  401.   reg.dx := ofs(asciiz)+1;
  402.   reg.cx := attr_mask;
  403.   intr($21, reg);
  404.   find_first := (lo(reg.ax) <> 18);
  405.  
  406. end;
  407.  
  408.  
  409. Function find_next (attr_mask : byte) : boolean;
  410.  
  411. {
  412.     Find the next file matching the ASCIIZ string.
  413.     attr_mask is $27 for files only and $37 for directories & files
  414.  
  415.     Returns TRUE if found, FALSE if not found
  416. }
  417.  
  418. begin
  419.   reg.ax := $4f00;
  420.   reg.cx := attr_mask;
  421.   intr($21, reg);
  422.   find_next := (lo(reg.ax) <> 18);
  423. end;
  424.  
  425.  
  426. begin { directory }
  427.  
  428.   get_DTA(DTA_ofs, DTA_seg); { Save the current DTA location }
  429.  
  430.   set_DTA(ofs(DTA), seg(DTA)); { Set the DTA location to local area }
  431.  
  432. {
  433.   Find and print the files in the current directory
  434. }
  435.  
  436.   asciiz := current_directory + '\*.*' + CHR(0);  {  CHR(0) to make proper  }
  437.  
  438. {  Process all the files before doing any directories  }
  439.  
  440.   if find_first ($27) then
  441.     repeat
  442.       if dta.filename[1] <> '.' then
  443.         begin
  444.           this_file := get_file;
  445.           get_crc(current_directory + '\' + this_file, dta);
  446.           writeln(outfile,current_directory,' ',this_file,' ',
  447.                 dta. attribute,' ',dta.time_of_day,' ',dta.date,' ',
  448.                 dta.low_size,' ',dta.high_size,' ',CRC_value);
  449.         end;
  450.     until not find_next ($27);
  451.  
  452. {  Now process all the directories  }
  453.  
  454.   if find_first ($37) then
  455.     repeat
  456.       if is_directory then
  457.       begin
  458.         this_directory := current_directory + '\' + get_file;
  459.         Writeln(this_directory);
  460.         directory(this_directory);  {  Now do all subdirectories  }
  461.       end;
  462.     until not find_next ($37);
  463.  
  464.   set_dta(DTA_ofs, DTA_seg); { restore the old DTA }
  465.  
  466. end;
  467.  
  468.  
  469. Function current_drive : byte;
  470. {
  471.   Function to return the current drive
  472.   Uses MSDOS Function $19 with Interrupt $21
  473.   current_drive is 1 if A, 2 if B, 3 if C, etc.
  474.  
  475. }
  476.  
  477. begin
  478.   global_reg.ax := $1900;
  479.   intr($21, global_reg);
  480.   current_drive := 1 + lo(global_reg.ax);
  481. end;
  482.  
  483.  
  484. BEGIN  {  FILECRC  }
  485.  
  486.   {  root will have the current drive designation  }
  487.   root := chr(current_drive + ord('A') - 1) + ':';
  488.  
  489.   Writeln('CRC file integrity program');
  490.   Writeln('Version ',version:5:2,', ',version_date);
  491.   Write('Written by Ted H. Emigh -- ');
  492.   Writeln('emigh@ncsugn.uucp or NEMIGH@TUCC.BITNET');
  493.  
  494.   Assign (filvar,'CHECK$$$.CRC');
  495.   {$I-}
  496.   Reset (filvar);   {  See if CHECK$$$.CRC exists  }
  497.   {$I+}
  498.   {  check_crc will be TRUE if CHECK$$$.CRC exists  }
  499.   check_crc := (IOresult = 0);
  500.   if check_crc then
  501.   begin
  502.     Assign (outfile,'CHECK$$$.NEW');
  503.     Writeln ('Creating File CHECK$$$.NEW');
  504.   end
  505.   else
  506.   begin
  507.     Assign (outfile,'CHECK$$$.CRC');
  508.     Writeln ('Creating File CHECK$$$.CRC');
  509.   end;
  510.   Close (filvar);
  511.   Rewrite (outfile);  {  Open the output file  }
  512.  
  513.   Generate_table_256(POLY);  {  Generate the table for CRC check  }
  514.  
  515.   Writeln(root+'\');
  516.   directory(root);  {  Now, do the CRC check  }
  517.  
  518.   Close (outfile);
  519.  
  520.   { Now compare this with the previous CRC's  }
  521.  
  522.   if check_crc then
  523.   begin
  524.     Assign (chain_file,'COMPARE.CHN');
  525.     Chain(chain_file);
  526.   end;
  527. end.
  528. \Rogue\Monster\
  529. else
  530.   echo "will not over write ./filecrc.pas"
  531. fi
  532. if `test ! -s ./compare.pas`
  533. then
  534. echo "writing ./compare.pas"
  535. cat > ./compare.pas << '\Rogue\Monster\'
  536.  
  537. {   PROGRAM TO COMPARE THE CRC'S OF THE FILE LISTS IN  }
  538. {   CHECK$$$.NEW AND CHECK$$$.CRC                      }
  539.  
  540. {$G512,P512,U+,R+ }
  541. Program Compare;
  542.  
  543. TYPE
  544.   string255 = string[255];
  545.   string64 = string[64];
  546.   string12 = string[12];
  547.  
  548.   Registers = record
  549.                 ax, bx, cx, dx, bp, si, di, ds, es, flags : integer;
  550.               end;
  551.   Months = array [1..12] of string[3];
  552.  
  553.   Directory_record = record
  554.                        directory : string64;
  555.                        FileNum   : integer;
  556.                      end;
  557.  
  558.   File_Rec = record
  559.                name                : string12;
  560.                time_of_day, date   : integer;
  561.                low_size,high_size  : integer;
  562.                attribute           : byte;
  563.                crc                 : integer;
  564.              end;
  565.  
  566.  
  567. CONST
  568.   month : Months = ('JAN','FEB','MAR','APR','MAY','JUN',
  569.                     'JUL','AUG','SEP','OCT','NOV','DEC');
  570.   Version = 1.02;
  571.   Version_Date = '12 SEP 86';
  572.  
  573. VAR
  574.  
  575.   {  File Creation time and date  }
  576.   TimeOfDay, FileDate : integer;
  577.   directory_number, file_number : integer;
  578.   {  Number of files in each category  }
  579.   old_file, new_file, OK_file, Update_file, Mod_file : integer;
  580.  
  581.   old_filename, new_filename : string64;
  582.   infile : TEXT[$0800];  { file for reading file lists }
  583.   newfile : TEXT; { file for writing names of new files created }
  584.   modfile : TEXT; { file for writing names of modified files }
  585.   updatefile : TEXT; { file for writing names of updated files }
  586.   tempfile : file; { used in renaming files }
  587.  
  588.   CRC_value : Integer;
  589.  
  590.   filename : string12;
  591.   Name_of_File, CRC_string, instring : string255;
  592.  
  593.   attribute : byte;
  594.   lowsize, highsize : integer;
  595.   new, new_dir : boolean;
  596.  
  597.   number_directories, direct_count : integer;
  598.  
  599.   this_directory, current_directory : string64;
  600.  
  601.   directories : array [1..200] of directory_record;
  602.   fileinfo : array [1..1900] of file_rec;
  603.  
  604.  
  605. function get_string  : string255;
  606. {
  607.   This function returns a string up to the first space from infile
  608. }
  609. var
  610.   inchar : char;
  611.   temp_string : string255;
  612.  
  613. begin
  614.   {  Ignore any leading blanks  }
  615.   Repeat
  616.     read(infile, inchar);
  617.   Until inchar <> ' ';
  618.  
  619.   temp_string := '';
  620.  
  621.   {  Now, add on to temp_string until a blank is found  }
  622.   Repeat
  623.     temp_string := temp_string + inchar;
  624.     read(infile, inchar);
  625.   Until inchar = ' ';
  626.  
  627.   get_string := temp_string;
  628.  
  629. end;
  630.  
  631. procedure read_old_file;
  632. {
  633.   Procedure to read in the old list of files and set up the list of
  634.   directories (variable directories), and the list of files along with
  635.   the various data (variable fileinfo).
  636.   On return,
  637.   old_file has the number of files in the list and
  638.   number_directories has the number of directories.
  639.  
  640.   The variables directories and fileinfo have the following information:
  641.   directories  directory : Name of the directory (up to 64 characters)
  642.                FileNum   : Number of the name in fileinfo that contains
  643.                            the information for the first file in this
  644.                            directory.
  645.  
  646.   fileinfo     name        : Name of the file
  647.                time_of_day : Time of day in DOS format
  648.                date        : Date in DOS format
  649.                low_size    : Low byte of the file size
  650.                high_size   : High byte of the file size
  651.                attribute   : Attribute of the file
  652.                crc         : CRC of the file
  653.  
  654. }
  655.  
  656. begin
  657.   Reset (infile);  {  Set to read Old List of Files  }
  658.   old_file := 0;  {  Number of files in the list  }
  659.   number_directories := 0;  {  Number of directories in the list  }
  660.   While not eof(infile) do
  661.   begin
  662.     old_file := old_file + 1;  {  Another file  }
  663.     this_directory := get_string;  {  Get the directory name  }
  664.     fileinfo[old_file].name := get_string;  {  Get the file name  }
  665.     if this_directory <> current_directory then
  666.     begin
  667.       current_directory := this_directory;
  668.       number_directories := number_directories + 1;
  669.       directories[number_directories].directory := this_directory;
  670.       directories[number_directories].FileNum := old_file;
  671.     end;
  672.     With fileinfo[old_file] do
  673.       Readln(infile,attribute, Time_of_day, date, low_size, high_size, crc);
  674.   end;
  675.   directories[number_directories + 1].FileNum := old_file + 1;
  676.   Close (infile);
  677. end;
  678.  
  679.  
  680. function get_time(date1,date2 : integer) : string64;
  681. {
  682.   This function returns the time and date of file creation.
  683.   date1 is the time of day in DOS format
  684.   date2 is the date of creation in DOS format
  685.  
  686.   get_time is a string with the time and date (e.g., 14:31:42  8 AUG 1986)
  687. }
  688.  
  689. var
  690.   hour, minute, second : integer;
  691.   temp, time : string64;
  692.   year, n_month, day : integer;
  693.  
  694. begin
  695.  
  696.   if date2 <> 0 then
  697.   begin
  698.     hour := date1 shr 11;
  699.     minute := (date1 shr 5) - (hour shl 6);
  700.     second := (date1 - (minute shl 5) - (hour shl 11))*2;
  701.     year := date2 shr 9;
  702.     n_month := (date2 shr 5) - (year shl 4);
  703.     day := date2 - (n_month shl 5) - (year shl 9);
  704.     Str(hour:2,temp);
  705.     time := temp + ':';
  706.     Str(minute:2,temp);
  707.     time := time + temp + ':';
  708.     Str(second:2,temp);
  709.     time := time + temp + '   ';
  710.     Str(day:2,temp);
  711.     time := time + temp + ' ' + month[n_month] + ' ';
  712.     Str(year + 1980:4,temp);
  713.     get_time := time + temp;
  714.   end
  715.   else
  716.     get_time := '                      ';
  717.  
  718. end;
  719.  
  720. procedure write_old_file ( file_number : integer);
  721. {
  722.   Procedure to write the attribute, size and CRC for a file from
  723.   the old list
  724.  
  725.   file_number is the number of the file name
  726.  
  727. }
  728.  
  729. var
  730.   filesize : real;
  731. begin
  732.   with fileinfo[file_number] do
  733.   begin
  734.     if low_size < 0 then
  735.       filesize := int(high_size)*65536.0 + 32768.0 + int(low_size and $7FFF)
  736.     else
  737.       filesize := int(high_size)*65536.0 + int(low_size);
  738.     Write ('  Attribute = ',attribute:3,', Size = ',filesize:10:0);
  739.     Writeln(', CRC = ',CRC);
  740.   end;
  741. end;
  742.  
  743.  
  744. procedure write_new_file;
  745. {
  746.   Procedure to write the attribute, size and CRC for a file from
  747.   the new list
  748.  
  749. }
  750.  
  751. var
  752.   filesize : real;
  753. begin
  754.   if lowsize < 0 then
  755.     filesize := int(highsize)*65536.0 + 32768.0 + int(lowsize and $7FFF)
  756.   else
  757.     filesize := int(highsize)*65536.0 + int(lowsize);
  758.   Write ('  Attribute = ',attribute:3,', Size = ',filesize:10:0);
  759.   Writeln(', CRC = ', CRC_value)
  760. end;
  761.  
  762.  
  763. procedure find_directory( var number : integer; var newdir : boolean);
  764. {
  765.   Procedure to the the directory from the old list that matches the
  766.   directory name from the new list
  767.  
  768.   If the directory name is the same as the current directory, then
  769.   number and newdir are unchanged.
  770.  
  771.   If the directory name is not the same, and it exists on the old list,
  772.   number will be the number of the old directory, and newdir is FALSE.
  773.   The current directory will be updated.
  774.  
  775.   If the directory name is not the same, and it does not exist on the
  776.   old list, newdir is FALSE.  Number is number of directories + 1, but
  777.   is never used.
  778.  
  779. }
  780. begin
  781.   {  If the directory is the same, then the status of number and newdir  }
  782.   {  will not change                                                     }
  783.   if this_directory <> current_directory then
  784.   begin  {  search from the beginning  --  nothing fancy  }
  785.     number := 0;
  786.     Repeat
  787.       number := number + 1;
  788.     Until (number > number_directories) or
  789.       (this_directory = directories[number].directory);
  790.     newdir := (number > number_directories);
  791.     current_directory := this_directory;
  792.   end;
  793. end;
  794.  
  795. procedure find_file( var number : integer; var new : boolean;
  796.                     number_begin, number_end : integer);
  797. {
  798.   Procedure to find the file name.  The directory name has been
  799.   found prior to this time, so the starting point in the search
  800.   has been found.  The search will continue until the first file
  801.    name in the next directory.
  802.  
  803. }
  804. begin
  805.   number := number_begin -1;
  806.   Repeat
  807.     number := number + 1;
  808.   Until (number = number_end) or (filename = fileinfo[number].name);
  809.   new := (filename <> fileinfo[number].name);
  810. end;
  811.  
  812. procedure file_new;
  813. {
  814.   This procedure processes the new files.  new_file is the counter
  815.   for the number of new files.  The file name and information is
  816.   written to the file assigned to newfile.
  817. }
  818.  
  819. var
  820.   filesize : real;
  821.  
  822. begin
  823.   new_file := new_file + 1;
  824.   Write (newfile,this_directory + '\' + filename);
  825.   Writeln (newfile,' Date: ',get_time(TimeOfDay, FileDate));
  826.   if lowsize < 0 then
  827.     filesize := int(highsize)*65536.0 + 32768.0 + int(lowsize and $7FFF)
  828.   else
  829.     filesize := int(highsize)*65536.0 + int(lowsize);
  830.   Writeln (newfile,'  Attribute = ',attribute:3,
  831.            ', Size = ',filesize:10:0,', CRC = ', CRC_value);
  832. end;
  833.  
  834. procedure file_updated;
  835. {
  836.   This procedure processes the updated files.  Update_file is the counter
  837.   for the number of updated files.
  838. }
  839.  
  840. var
  841.   filesize : real;
  842.  
  843. begin
  844.   Update_file := Update_file + 1;
  845.   Writeln (updatefile,this_directory + '\' + filename);
  846.   With fileinfo[file_number] do
  847.   Begin
  848.     Write (updatefile,'Old Date: ',get_time(time_of_day,date));
  849.     if lowsize < 0 then
  850.       filesize := int(highsize)*65536.0 + 32768.0 + int(lowsize and $7FFF)
  851.     else
  852.       filesize := int(highsize)*65536.0 + int(lowsize);
  853.     Writeln (updatefile,' Attr = ',attribute:3,
  854.            ', Size = ',filesize:10:0,', CRC = ', CRC);
  855.   End;
  856.   Write (updatefile,'New Date: ',get_time(TimeOfDay, FileDate));
  857.   if lowsize < 0 then
  858.     filesize := int(highsize)*65536.0 + 32768.0 + int(lowsize and $7FFF)
  859.   else
  860.     filesize := int(highsize)*65536.0 + int(lowsize);
  861.   Writeln (updatefile,' Attr = ',attribute:3,
  862.            ', Size = ',filesize:10:0,', CRC = ', CRC_value);
  863. end;
  864.  
  865. procedure file_OK;
  866. {
  867.   This procedure processes the files that have not been changed, modified
  868.   or deleted.  OK_file is the counter for the number of such files.
  869. }
  870.  
  871. begin
  872.   OK_file := OK_file + 1;
  873. end;
  874.  
  875. procedure bad_CRC;
  876. {
  877.   This procedure processes the files that have been modified without
  878.   changing the directory entry date or time.  Mod_file is the counter for
  879.   the number of such files.  In normal operations, this should not happen,
  880.   so for such files, the name and date information is shown on the console
  881.   and sent to the file assigned to modfile.
  882. }
  883.  
  884. begin
  885.   Mod_file := Mod_file + 1;
  886.   Writeln ('CRC''s do not match!  File: ',this_directory+filename);
  887.   Writeln ('Date: ',get_time(TimeOfDay, FileDate));
  888.   Write ('Old file:');
  889.   write_old_file(file_number);
  890.   Write ('New file:');
  891.   write_new_file;
  892.   Write (modfile, this_directory + '\' + filename);
  893.   Writeln (modfile,' Date: ', get_time(TimeOfDay, FileDate));
  894. end;
  895.  
  896. procedure read_new_file;
  897. {
  898.   Procedure to read the list of new files, and compare them to the
  899.   old files.  The various comparison types are processed according to
  900.   the preceeding routines.
  901. }
  902.  
  903. begin
  904.   current_directory := '';
  905.   new_dir := FALSE;
  906.  
  907.   Assign (infile, new_filename);
  908.   Reset (infile);
  909.  
  910.   While not eof(infile) do
  911.   begin
  912.     this_directory := get_string;  {  First is the directory name  }
  913.     filename := get_string;  {  Next is the file name  }
  914.     Readln(infile, attribute, TimeOfDay, FileDate, lowsize,
  915.            highsize, crc_value);  {  Then the file parameters  }
  916.     {  Find the entry in the list of old files with the same name  }
  917.     find_directory(directory_number,new_dir);
  918.     if not new_dir then
  919.       find_file(file_number,new,
  920.                 directories[directory_number].FileNum,
  921.                 directories[directory_number + 1].FileNum-1);
  922.     if (new_dir or new) then  {  New directory means new file  }
  923.       file_new
  924.     else  {  Existing file, compare the two  }
  925.       if (fileinfo[file_number].Time_of_day <> TimeOfDay)
  926.         or (fileinfo[file_number].date <> FileDate) then
  927.           file_updated
  928.       else
  929.         if (fileinfo[file_number].crc <> CRC_value) then bad_CRC
  930.         else
  931.           file_OK;
  932.   end;
  933.   Close (infile);
  934. end;
  935.  
  936.  
  937. BEGIN  {  Compare  }
  938.  
  939.   Writeln('CRC file integrity comparison program');
  940.   Writeln('Version ',version:5:2,', ',version_date);
  941.   Write('Written by Ted H. Emigh -- ');
  942.   Writeln('emigh@ncsugn.uucp or NEMIGH@TUCC.BITNET');
  943.  
  944.   number_directories := 1;
  945.   current_directory := '';
  946.   directories[1].directory := current_directory;
  947.   directories[1].FileNum := 1;
  948.  
  949.   {  Reset the counters for the various comparisons  }
  950.  
  951.   New_file := 0;
  952.   OK_file := 0;
  953.   Update_file := 0;
  954.   Mod_file := 0;
  955.  
  956.   {  Set up the input and output files  }
  957.  
  958.   Case ParamCount of
  959.     0 : begin  {  No command line parameters, use default names  }
  960.           old_filename := 'CHECK$$$.CRC';
  961.           new_filename := 'CHECK$$$.NEW';
  962.         end;
  963.     1 : begin  {  File name with listing of new files has been given  }
  964.           old_filename := 'CHECK$$$.CRC';
  965.           new_filename := ParamStr(1);
  966.         end;
  967.     else
  968.         begin  {  Both file names have been given  }
  969.           old_filename := ParamStr(2);
  970.           new_filename := ParamStr(1);
  971.         end;
  972.   end;
  973.  
  974.   {  Set up the various input and output files  }
  975.  
  976.   Assign (infile,old_filename);
  977.   Assign(newfile,'FILES$$$.NEW');
  978.   Rewrite (newfile);
  979.   Writeln (newfile,'New files created on this disk');
  980.   Assign(modfile,'FILES$$$.MOD');
  981.   Rewrite (modfile);
  982.   Writeln (modfile,'Files that were modified without updating the directory');
  983.   Assign(updatefile,'FILES$$$.UPD');
  984.   Rewrite (updatefile);
  985.   Writeln (updatefile,'Files that were updated on this disk');
  986.  
  987.  
  988.   Writeln ('Reading old CRC list, please wait ...');
  989.   read_old_file;
  990.  
  991.   Writeln ('Reading new CRC list and checking, please wait ...');
  992.   read_new_file;
  993.  
  994.   {  Print the summary numbers for this check  }
  995.  
  996.   Writeln ('Number of Files in the last CRC check:           ',old_file);
  997.   Writeln ('Number of Files that are the same as last time:  ',OK_file);
  998.   Writeln ('Number of New Files:                             ',new_file);
  999.   Writeln ('Number of Deleted Files:                         ',
  1000.             old_file - update_file - OK_file - Mod_file);
  1001.   Writeln ('Number of Updated Files:                         ',update_file);
  1002.   Writeln ('Number of Invalidly Modified Files:              ',Mod_file);
  1003.   Writeln;
  1004.   Writeln;
  1005.  
  1006.  
  1007.   {  Erase the output files if they are empty  }
  1008.  
  1009.   Close (newfile);
  1010.   if new_file = 0 then Erase (newfile);
  1011.   Close (modfile);
  1012.   if Mod_file = 0 then Erase (modfile);
  1013.   Close (updatefile);
  1014.   if update_file = 0 then Erase (updatefile);
  1015.  
  1016.   {  No command line parameters  --  Rename the files with the file lists  }
  1017.  
  1018.   if ParamCount = 0 then
  1019.   begin
  1020.     Assign (tempfile, 'CHECK$$$.OLD');
  1021.     {$I-}
  1022.     Reset (tempfile);  {  See if the file already exists  }
  1023.     {$I+}
  1024.     if IOresult =0 then
  1025.       Erase (tempfile);  {  Yes, it exists -- delete it  }
  1026.     Close (tempfile);
  1027.     Assign (tempfile, 'CHECK$$$.CRC');
  1028.     Rename (tempfile, 'CHECK$$$.OLD');
  1029.     Assign (tempfile, 'CHECK$$$.NEW');
  1030.     Rename (tempfile, 'CHECK$$$.CRC');
  1031.     Writeln ('Old CRC file is now CHECK$$$.OLD');
  1032.     Writeln ('New CRC file is now CHECK$$$.CRC');
  1033.     Writeln;
  1034.   end;
  1035.  
  1036.  
  1037.  
  1038. end.
  1039.