home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / trojanpr / filecrc.arc / FILECRC.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-05-30  |  15.1 KB  |  525 lines

  1.  
  2. {  PROGRAM TO CREATE OF FILE OF  THE CRC'S OF THE FILES ON THE DEFAULT DISK  }
  3.  
  4. {
  5.  
  6.   This program was written by Ted H. Emigh, and has been placed in the public
  7.   domain, to be used at the user's discretion.  The CRC routines and the
  8.   discussion of the CRC were written by David Dantowitz, Digital Equipment
  9.   Corporation,  Dantowitz%eagle1.dec@decwrl.
  10.  
  11.   This program calculates the CRC (cyclic redundancy check) for all the files
  12.   on the default disk.  The CRC's are placed in a file (CHECK$$$.NEW) to be
  13.   compared with the CRC's calculated at a previous time in the file
  14.   CHECK$$$.CRC.  The comparison is done with the program COMPARE.PAS.
  15.  
  16.  
  17.    For a good discussion of polynomial selection see "Cyclic
  18.    Codes for Error Detection", by W. W. Peterson and
  19.    D. T. Brown, Proceedings of the IEEE, volume 49, pp 228-235,
  20.    January 1961.
  21.  
  22.    A reference on table driven CRC computation is "A Cyclic
  23.    Redundancy Checking (CRC) Algorithm" by A. B. Marton and
  24.    T. K. Frambs, The Honeywell Computer Journal, volume 5,
  25.    number 3, 1971.
  26.  
  27.    Also used to prepare these examples was "Computer Networks",
  28.    by Andrew S. Tanenbaum, Prentice Hall, Inc.  Englewood Cliffs,
  29.    New Jersey, 1981.
  30.  
  31.    The following three polynomials are international standards:
  32.  
  33.  
  34.         CRC-12 = X^12 + X^11 + X^3 + X^2 + X^1 + 1
  35.         CRC-16 = X^16 + X^15 + X^2 + 1
  36.         CRC-CCITT = X^16 + X^12 + X^5 + 1
  37.  
  38.    The polynomials can be represented by a binary number, where a 1
  39.    indicates the inclusion of the power term in the polynomial.  Since
  40.    the highest order term is always included, that term is not needed
  41.    in specifying the polynomial, and usually is dropped.  In addition,
  42.    the bits are specified from low-order to high-order.  For example,
  43.    the polynomial CRC-12 can be represented in the following manner:
  44.  
  45.     Order                   0  1  2  3  4  5  6  7  8  9 10 11 12
  46.     Term Included ?         Y  Y  Y  Y  N  N  N  N  N  N  N  Y  Y
  47.     Binary Representation   1  1  1  1  0  0  0  0  0  0  0  1 (1)<-- DROPPED
  48.  
  49.    The binary and hex representations for the three polynomials are:
  50.  
  51.                    Binary                     Hex
  52.  
  53.         CRC-12    = 1111 0000 0001           $0F01
  54.         CRC-16    = 1010 0000 0000 0001      $A001
  55.         CRC-CCITT = 1000 0100 0000 1000      $8404    (Used below)
  56.  
  57.    The first is used with 6-bit characters and the second two
  58.    with 8-bit characters.  All of the above will detect any
  59.    odd number of errors.  The second two will catch all 16-bit
  60.    bursts, a high percentage of random 17-bit bursts (~99.997%) and
  61.    also a large percentage of random 18-bit or larger bursts (~99.998%).
  62.    The paper mentioned above (Peterson and Brown) discusses how 
  63.    to compute the statistics presented which have been quoted 
  64.    from Tanenbaum.  Notice that some errors can be generated in
  65.    nonrandom ways that can substantially reduce the chances of
  66.    detecting errors.
  67.  
  68.    (A burst of length N is defined a sequence of N bits, where
  69.    the first and last bits are incorrect and the bits in the
  70.    middle are any possible combination of correct and incorrect.
  71.    See the paper by Peterson and Brown for more information)
  72.  
  73. Version  1.00:  13 August 1986.  First Production Version.
  74.          1.01:  1 September 1986.  Allowed CRC of hidden system files.
  75.                 First Version to Usenet.
  76.          1.02:  12 September 1986.  Fixed bug in handling of hidden files.
  77.          1.10:  30 May 1988.  Eliminated chaining.  Added sensitive list.
  78. }
  79. {  The following compiler directives allow for I/O redirection (G,P & D),
  80. suppresses interrupts except when writing (U), and eliminates range
  81. checking (R) }
  82.  
  83. {$G512,P512,D-,U-,R- }
  84.  
  85. Program FILECRC;
  86.  
  87. Const
  88.   BufSize = 192;  { Number of 128 byte sectors in the CRC buffer }
  89.   Buffer_Length = 24576;  { BufSize * 128 = Length of the CRC buffer }
  90.   Version = 1.10;
  91.   Version_Date = '30 May 1988';
  92.   POLY = $8404;  {  CRC Polynomial Used  }
  93.  
  94. Type
  95.   Bytes = Array [1..Buffer_Length] of Byte;
  96.  
  97.   Registers = record  {  Registers for 8088/8086/80286  }
  98.                 ax, bx, cx, dx, bp, si, di, ds, es, flags : integer;
  99.               end;
  100.  
  101.   DTA_record = record  {  DTA as used by MSDOS  }
  102.                  dos : array [1..21] of char;  {  Used by DOS, not us  }
  103.                  attribute : byte;  {  Attribute byte  }
  104.                  time_of_day : integer;  {  Time of Day of File Creation  }
  105.                  date : integer;  {  Date of File Creation  }
  106.                  low_size, high_size : integer;  {  Size of the File  }
  107.                  filename: array [1..13] of char;  { File Name  }
  108.                  junk : array [1..85] of byte;
  109.                end;
  110.  
  111.   string255 = string[255];
  112.  
  113. Var
  114.   {  Variables used in Calculating the CRC  }
  115.  
  116.   str_length, RecsRead, CRC_value : integer;
  117.   table_256 : Array [0 .. 255] of Integer;  {CRC Table to speed computations}
  118.   byte_string : Bytes;  {  This is a buffer to increase the disk reads  }
  119.  
  120.   {  Variables used in setting up the input and output files  }
  121.  
  122.   filvar : file;
  123.   outfile : TEXT[$4000];
  124.  
  125.   {  Misc. Variables  }
  126.  
  127.   root : string255;  {  Contains the default drive and root directory }
  128.   global_reg : registers;  {  Registers for the DOS calls  }
  129.  
  130.  
  131. Procedure generate_table_256(POLY : Integer);
  132.  
  133. {
  134.     This routine computes the remainder values of 0 through 255 divided
  135.   by the polynomial represented by POLY.  These values are placed in a
  136.   table and used to compute the CRC of a block of data efficiently.
  137.   More space is used, but the CRC computation will be faster.
  138.  
  139.  
  140.  
  141.     This implementation only permits polynomials up to degree 16.
  142. }
  143.  
  144.  
  145. Var
  146.    val, i, result : Integer;
  147.  
  148. Begin
  149. For val := 0 to 255 Do
  150.   Begin
  151.      result := val;
  152.      For i := 1 to 8 Do
  153.         Begin
  154.            If (result and 1) = 1
  155.               then result := (result shr 1) xor POLY
  156.               else result :=  result shr 1;
  157.         End;
  158.  
  159.      table_256[val] := result;
  160.   End
  161. End;
  162.  
  163.  
  164. Function crc_string_256(Var s : Bytes; s_length, initial_crc : Integer)
  165.                         : Integer;
  166.  
  167. {
  168.      This routine computes the CRC value and returns it as the function
  169.   value.  The routine takes an array of Bytes, a length and an initial
  170.   value for the CRC.  The routine requires that a table of 256 values
  171.   be set up by a previous call to Generate_table_256.
  172.  
  173.       This routine uses table_256.
  174. }
  175.  
  176. Begin
  177.  
  178. inline(
  179.  
  180. $c4/$7e/<s/                {les di,s[bp]            (es:di points to array)  }
  181. $8b/$46/<initial_crc/      {mov ax,initial_crc[bp]  (initial CRC value)      }
  182. $8b/$4e/<s_length/         {mov cx,s_length[bp]     (count)                  }
  183. $be/table_256/             {mov si,offset table_256 (table address)          }
  184.  
  185.  
  186. { next:  }
  187.  
  188. $26/$32/$05/               {xor al,es:[di]          CRC = CRC XOR next byte  }
  189. $47/                       {inc di                  (point to next byte)     }
  190.  
  191. { intermediate steps, see comments for overall effect }
  192.  
  193. $31/$db/                   {xor bx,bx               (bx <- 0)                }
  194. $86/$d8/                   {xchg al,bl              (bx <- ax and 0FF)       }
  195. $86/$e0/                   {xchg al,ah              (ax <- ax shr 8)         }
  196. $d1/$e3/                   {shl bx,1                (bx <- bx+bx)            }
  197.  
  198. $33/$00/                   {xor ax,[bx+si]          CRC = (CRC shr 8) XOR
  199.                                                           table[CRC and 0FF] }
  200.  
  201. $e2/$f0/                   {loop next               (count <- count -1)      }
  202.  
  203. $89/$46/<s+4);             {mov s+4[bp],ax          (crc_string_256 := CRC)  }
  204.  
  205.  
  206. {  basic algorithm expressed above
  207.  
  208. crc := initial_crc
  209.  
  210. For each byte Do
  211. Begin
  212.   crc := crc XOR next_byte;
  213.   crc := (crc shr 8) XOR table_256 [crc and $FF];
  214. End;
  215.  
  216. crc_string_256 := crc;
  217. }
  218. End;
  219.  
  220.  
  221.  
  222. Procedure set_attr (attr : byte; asciiz : string255);
  223. {
  224.  
  225.   This routine sets the file attributes.  Uses Function $43 in
  226.   Interrupt $21.
  227.  
  228.   Turbo Pascal is unable to open and read various types files
  229.   (e.g., r/o and files that are both hidden and system).  This
  230.   gets around that by always setting the attribute to 0, then
  231.   resetting it to the original value.
  232.  
  233.   attr  is the attribute to be set on the file
  234.   asciiz is a string variable with the file name
  235.  
  236. }
  237.  
  238. begin
  239.   asciiz := asciiz + chr(0);  {  Make a valid DOS ASCIIZ name  }
  240.   {  Set up the registers for the interrupt  }
  241.   global_reg.ax := $4301;
  242.   global_reg.ds := seg(asciiz);
  243.   global_reg.dx := ofs(asciiz)+1;
  244.   global_reg.cx := attr;
  245.   intr ($21, global_reg);
  246. end;
  247.  
  248.  
  249. Procedure get_crc(this_file : string255; dta : DTA_record);
  250. {
  251.   This procedure computes the CRC for a file.  Value is returned
  252.   in the global variable CRC_value.
  253.  
  254.   this_file is a string variable containing the file name
  255.   dta is a DTA_Record containing the file's DTA
  256.  
  257. }
  258.  
  259. var
  260.   length  : real;  {  Length of the File  }
  261.  
  262. begin
  263.  
  264.   {  Change the Attribute byte so we can always open it  }
  265.   {    To save some time, this is only done if the file  }
  266.   {    Has any attribute other than ARCHIVE              }
  267.  
  268.   if (dta.attribute and $DF <> 0) then
  269.     set_attr ( 0, this_file);
  270.  
  271.   {  Get the size of the file  }
  272.  
  273.   if dta.low_size < 0 then
  274.     {  Negative low_size is really number between 32768 and 65536  }
  275.     length := int(dta.high_size)*65536.0 + 32768.0
  276.               + int(dta.low_size and $7FFF)
  277.   else
  278.     length := int(dta.high_size)*65536.0 + int(dta.low_size);
  279.  
  280.   {  Open the file as untyped  }
  281.  
  282.   Assign (Filvar, this_file);
  283.   Reset (Filvar);
  284.  
  285.   {  Calculate the CRC  }
  286.  
  287.   CRC_value := 0;
  288.   While length > 0.5 do
  289.   Begin
  290.     {  Read a segment of the file to process  }
  291.     BlockRead(filvar,byte_string,BufSize,RecsRead);
  292.     {  Get the correct number of bytes to process  }
  293.     if length >= Buffer_Length then
  294.       str_length := Buffer_Length
  295.     else
  296.       str_length := round(length);
  297.     {  Compute the CRC  }
  298.     CRC_value := crc_string_256(byte_string, str_length, CRC_value);
  299.     {  Adjust the file length  }
  300.     length := length - Buffer_Length;
  301.   End;
  302.  
  303.   Close (Filvar);
  304.  
  305.   {  Restore the correct Attribute Byte  }
  306.   if (dta.attribute and $DF <> 0) then
  307.     set_attr ( dta.attribute, this_file);
  308.  
  309. end;
  310.  
  311.  
  312. Procedure directory(current_directory : string255);
  313.  
  314. {
  315.   Procedure to calculate the CRC of all the files in a directory,
  316.   then all subdirectories in that directory
  317.  
  318.   current_directory contains the directory name (including drive)
  319.  
  320. }
  321.  
  322. var
  323.   DTA_ofs, DTA_seg : integer;  {  Contains the current DTA address  }
  324.   reg : Registers;  {  Local 8088/8086/80286 registers  }
  325.   DTA : DTA_record;  {  Local DTA  }
  326.   this_directory, this_file, asciiz : string255;  { directory and file names }
  327.  
  328.  
  329. function get_file : string255;
  330.  
  331. {  Get the file name from the DTA  }
  332.  
  333. var
  334.   i : integer;
  335.   temp_file : string255;
  336.  
  337. begin
  338.   i := 1;
  339.   temp_file := '';
  340.   repeat
  341.     temp_file := temp_file + DTA.filename[i];
  342.     i := i+1;
  343.   until dta.filename[i] = chr(0);
  344.  
  345.   get_file := temp_file;
  346.  
  347. end;
  348.  
  349.  
  350. function is_directory : boolean;
  351.  
  352. {  Function to tell if the file is a directory entry  }
  353. {  ignore parent directory and current directory      }
  354.  
  355. begin
  356.   is_directory := ((dta.attribute and $10) <> 0)
  357.                    and (dta.filename[1] <> '.');
  358. end;
  359.  
  360. Procedure set_DTA(offset, segment : integer);
  361.  
  362. {   sets the disk DTA
  363.     Uses MSDOS Function $1A with interrupt $21
  364.     offset is the offset of the new DTA
  365.     segment is the segment of the new DTA
  366. }
  367.  
  368. begin
  369.   reg.ax := $1a00;
  370.   reg.ds := segment;
  371.   reg.dx := offset;
  372.   intr($21, reg);
  373. end;
  374.  
  375. Procedure get_DTA(var offset, segment : integer);
  376.  
  377. {   gets the disk DTA
  378.     Uses MSDOS Function $2F with Interrupt $21
  379.     offset will return with the current DTA offset
  380.     segment will return with the current DTA segment
  381. }
  382.  
  383. begin
  384.   reg.ax := $2f00;
  385.   intr($21, reg);
  386.   offset := reg.bx;
  387.   segment := reg.es;
  388. end;
  389.  
  390.  
  391. Function find_first (attr_mask : byte) : boolean;
  392.  
  393. {
  394.     Find the first file matching the ASCIIZ string.
  395.     attr_mask is $27 for files only and $37 for directories & files
  396.  
  397.     INT 21 function 4EH
  398.     Returns TRUE if found, FALSE if not found
  399. }
  400.  
  401. begin
  402.   reg.ax := $4e00;
  403.   reg.ds := seg(asciiz);
  404.   reg.dx := ofs(asciiz)+1;
  405.   reg.cx := attr_mask;
  406.   intr($21, reg);
  407.   find_first := (lo(reg.ax) <> 18);
  408.  
  409. end;
  410.  
  411.  
  412. Function find_next (attr_mask : byte) : boolean;
  413.  
  414. {
  415.     Find the next file matching the ASCIIZ string.
  416.     attr_mask is $27 for files only and $37 for directories & files
  417.  
  418.     Returns TRUE if found, FALSE if not found
  419. }
  420.  
  421. begin
  422.   reg.ax := $4f00;
  423.   reg.cx := attr_mask;
  424.   intr($21, reg);
  425.   find_next := (lo(reg.ax) <> 18);
  426. end;
  427.  
  428.  
  429. begin { directory }
  430.  
  431.   get_DTA(DTA_ofs, DTA_seg); { Save the current DTA location }
  432.  
  433.   set_DTA(ofs(DTA), seg(DTA)); { Set the DTA location to local area }
  434.  
  435. {
  436.   Find and print the files in the current directory
  437. }
  438.  
  439.   asciiz := current_directory + '\*.*' + CHR(0);  {  CHR(0) to make proper  }
  440.  
  441. {  Process all the files before doing any directories  }
  442.  
  443.   if find_first ($27) then
  444.     repeat
  445.       if dta.filename[1] <> '.' then
  446.         begin
  447.           this_file := get_file;
  448.           get_crc(current_directory + '\' + this_file, dta);
  449.           writeln(outfile,current_directory,' ',this_file,' ',
  450.                 dta. attribute,' ',dta.time_of_day,' ',dta.date,' ',
  451.                 dta.low_size,' ',dta.high_size,' ',CRC_value);
  452.         end;
  453.     until not find_next ($27);
  454.  
  455. {  Now process all the directories  }
  456.  
  457.   if find_first ($37) then
  458.     repeat
  459.       if is_directory then
  460.       begin
  461.         this_directory := current_directory + '\' + get_file;
  462.         Writeln(this_directory);
  463.         flush(output);  {  Flush the directory name to the console  }
  464.         directory(this_directory);  {  Now do all subdirectories  }
  465.       end;
  466.     until not find_next ($37);
  467.  
  468.   set_dta(DTA_ofs, DTA_seg); { restore the old DTA }
  469.  
  470. end;
  471.  
  472.  
  473. Function current_drive : byte;
  474. {
  475.   Function to return the current drive
  476.   Uses MSDOS Function $19 with Interrupt $21
  477.   current_drive is 1 if A, 2 if B, 3 if C, etc.
  478.  
  479. }
  480.  
  481. begin
  482.   global_reg.ax := $1900;
  483.   intr($21, global_reg);
  484.   current_drive := 1 + lo(global_reg.ax);
  485. end;
  486.  
  487.  
  488. BEGIN  {  FILECRC  }
  489.  
  490.   {  root will have the current drive designation  }
  491.   root := chr(current_drive + ord('A') - 1) + ':';
  492.  
  493.   Writeln('CRC file integrity program');
  494.   Writeln('Version ',version:5:2,', ',version_date);
  495.   Write('Written by Ted H. Emigh -- ');
  496.   Writeln('emigh@ncsugn.uucp or NEMIGH@TUCC.BITNET');
  497.  
  498.   Assign (filvar,'CHECK$$$.CRC');
  499.   {$I-}
  500.   Reset (filvar);   {  See if CHECK$$$.CRC exists  }
  501.   {$I+}
  502.   {  IOresult = 0 will be TRUE if CHECK$$$.CRC exists  }
  503.   if (IOresult = 0) then
  504.   begin
  505.     Assign (outfile,'CHECK$$$.NEW');
  506.     Writeln ('Creating File CHECK$$$.NEW');
  507.   end
  508.   else
  509.   begin
  510.     Assign (outfile,'CHECK$$$.CRC');
  511.     Writeln ('Creating File CHECK$$$.CRC');
  512.   end;
  513.   flush(output);  {  Flush the console output  }
  514.   Close (filvar);
  515.   Rewrite (outfile);  {  Open the output file  }
  516.  
  517.   Generate_table_256(POLY);  {  Generate the table for CRC computations  }
  518.  
  519.   Writeln(root+'\');
  520.   directory(root);  {  Now, do the CRC computations  }
  521.  
  522.   Close (outfile);
  523.  
  524. end.
  525.