home *** CD-ROM | disk | FTP | other *** search
/ Phoenix CD 2.0 / Phoenix_CD.cdr / 15a / diff24.zip / DIFF.C < prev    next >
C/C++ Source or Header  |  1988-11-29  |  36KB  |  925 lines

  1. /*----------------------------------------------------------------------------
  2.  
  3.     diff.c -- file compare and change bar inserter for text files
  4.     originally produced by D Krantz, modified for Turbo C by P van Es
  5.     date 4/19/88
  6.  
  7.     5/2/88  v1.1 options have been UNIX-fied
  8.     5/4/88  v1.2 comparisons made faster
  9.                  bugs corrected
  10.                  upper case handling rationalised
  11.     6/14/88 v2.0 automatic resync feature added
  12.     6/18/88 v2.1 screen listing switchable
  13.     6/20/88 v2.2 resync made faster for small changes
  14.     6/21/88 v2.3 resync checksum lookup added
  15.    11/25/88 v2.4 by Ricky Lively:
  16.                  when either file reaches EOF,
  17.                    shows remaining and unmatched lines in both files.
  18.                  change checksum to crc.
  19.                  add absolute text line counter.
  20.                  add text line length.
  21.                  increase text line size from 83 to 255.
  22.                  add -l minimum comparison column.
  23.                  add -m maximum comparison column.
  24.                  add -e tab expansion.
  25.                  add -i ignore all white space.
  26.                  add -t trim eol white space.
  27.                  add version number to help.
  28.                  change text to test line, to preserve white space, case, etc.
  29.                  save position of text lines and reposition to print.
  30.                  clean up warnings and includes.
  31.  
  32.   ---------------------------------------------------------------------------*/
  33.  
  34. /* file difference utility */
  35.  
  36. #if !defined (__COMPACT__)
  37. #error Must be compiled with COMPACT model
  38. #endif
  39.  
  40. #include  <ctype.h>
  41. #include  <stdio.h>
  42. #include  <process.h>                                                          /* exit                               */
  43. #include  <string.h>                                                           /* strcmp, stricmp, strcpy            */
  44. #include  <stdlib.h>                                                           /* malloc, free, atoi                 */
  45.  
  46. typedef   unsigned  char      byte;
  47. #define   OPT_FLAG  '-'                                                        /* command line switch recogniser     */
  48. #define   MAXLINE   256                                                        /* maximum input line characters      */
  49. #define   FORMFEED  'L'-'@'
  50. #define   EOF_REACHED 0                                                        /* eof during resync                  */
  51. #define   RE_SYNCED 1                                                          /* we managed to re-sync              */
  52. #define   NO_SYNC   2                                                          /* no sync but no eof either          */
  53. #define   VERSION   "v2.4 11/25/88"                                            /* version number                     */
  54.  
  55. struct    LINE                                                                 /* structure defining line internally */
  56. {   int       lineanum;                                                        /* what line in file                  */
  57.     int       linenum;                                                         /* what line on page                  */
  58.     int       pagenum;                                                         /* what page                          */
  59.     int       length;                                                          /* length of text line                */
  60.     byte      crc;                                                             /* use crc for quick comparison       */
  61.     struct    LINE      *prev;                                                 /* pointer to previous line           */
  62.     struct    LINE      *link;                                                 /* linked list pointer                */
  63.     struct    LINE      *same;                                                 /* pointer to next line with crc      */
  64.     char      test[MAXLINE];                                                   /* text of line for testing           */
  65.     long      position;                                                        /* position in file of text line      */
  66. };
  67.  
  68. typedef   struct    LINE      *line_ptr;
  69. typedef   FILE      *FILE_PTR;
  70. struct    LINE      root[3];                                                   /* root of internal linked list       */
  71. int       linea_count[3] = {1,1,1};                                            /* file's absolute line counter       */
  72. int       line_count[3] = {1,1,1};                                             /* file's line counter                */
  73. int       page_count[3] = {1,1,1};                                             /* file's page counter                */
  74. int       command_errors = 0;                                                  /* number of command line errors      */
  75. char      xx1[132],                                                            /* space for file names               */
  76.           xx2[132];
  77. int       files = 0;                                                           /* nr of files in command line        */
  78. char      *infile_name[3] = {NULL,xx1,xx2};
  79. char      outfile_name[132];                                                   /* output file name                   */
  80. FILE_PTR  infile[3];                                                           /* input file pointers                */
  81. FILE      *outfile;                                                            /* changebarred output file ptr       */
  82.  
  83. static    line_ptr  at[3] = {NULL, &(root[1]), &(root[2])};
  84. static    line_ptr  same_crc[256][2];
  85. char      text[MAXLINE];                                                       /* text of line                       */
  86.  
  87. int       debug = 0;                                                           /* trace switch                       */
  88. int       trace_enabled = 0;                                                   /* keyboard tracing switch            */
  89. int       bar_col = 78;                                                        /* column for change bar              */
  90. int       right_col = MAXLINE - 1;                                             /* maximum column to compare up to    */
  91. int       left_col = 0;                                                        /* minimum column to compare from     */
  92. int       top_skip = 0;                                                        /* lines to skip at top of page       */
  93. int       bot_skip = 0;                                                        /* lines to skip at bottom of page    */
  94. int       page_len = 66;                                                       /* length of a page                   */
  95. int       up_case = 0;                                                         /* boolean - ignore case              */
  96. int       re_sync = 3;                                                         /* lines that must match for resync   */
  97. int       output = 0;                                                          /* boolean - change barred outfile on */
  98. int       blanks = 0;                                                          /* boolean - blank lines significant  */
  99. int       skip1 = 0;                                                           /* pages in first file to skip        */
  100. int       skip2 = 0;                                                           /* pages in second file to skip       */
  101. int       scr_on = 1;                                                          /* screen listing turned on           */
  102. int       tabs = 1;                                                            /* boolean - expand tabs to spaces    */
  103. int       white = 0;                                                           /* boolean - ignore white space       */
  104. int       trim = 0;                                                            /* boolean - trim off trailing white  */
  105.  
  106. static    int       crctab[] =                                                 /* CRC lookup table                   */
  107. {   0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
  108.     0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
  109.     0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
  110.     0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
  111.     0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
  112.     0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
  113.     0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
  114.     0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
  115.     0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
  116.     0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
  117.     0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
  118.     0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
  119.     0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
  120.     0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
  121.     0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
  122.     0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
  123.     0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
  124.     0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
  125.     0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
  126.     0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
  127.     0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
  128.     0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
  129.     0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
  130.     0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
  131.     0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
  132.     0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
  133.     0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
  134.     0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
  135.     0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
  136.     0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
  137.     0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
  138.     0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
  139. };
  140.  
  141. #if 0                                                                          /* trace & other debug functions off  */
  142. #include  <conio.h>                                                            /* kbhit, getch                       */
  143. #include  <alloc.h>                                                            /* coreleft                           */
  144. #define   trace( x )          callstack( x )
  145. #define   ret                 { callpop(); return; }
  146. #define   ret_val( x )        { callpop(); return( x ); }
  147. #define   TRACER_FUNCTIONS
  148. #else
  149. #define   trace( x )                                                           /* nothing                            */
  150. #define   ret                 { return; }
  151. #define   ret_val( x )        { return( x ); }
  152. #endif
  153.  
  154. /*--------------------------------------------------------------------------*/
  155. #ifdef  TRACER_FUNCTIONS
  156.  
  157. char      *names[20];
  158. int       stack = 0;
  159.  
  160. int
  161. callstack(
  162.     char      *str )
  163. {   int       i;
  164.  
  165.     names[stack++] = str;
  166.     if( debug )
  167.     {   for( i = 0; i < stack; i++ )
  168.             printf( "   " );
  169.         printf( "Entering %s\n", str );
  170.     }
  171.     if( trace_enabled && kbhit() )
  172.     {   switch( getch() )
  173.         {   case 't':
  174.             case 'T':   debug = !debug;
  175.                         break;
  176.             case 's':
  177.             case 'S':   printf( "\n------------" );
  178.                         for( i = stack - 1; i >= 0; i-- )
  179.                             printf( "\n%s", names[i] );
  180.                         printf( "\n------------\n" );
  181.                         printf( "free: %lu\n", coreleft() );
  182.                         break;
  183.             default:    break;
  184.     }   }
  185.     return;
  186. }
  187.  
  188. int
  189. callpop(
  190.     void )
  191. {   int       i;
  192.  
  193.     if( debug )
  194.     {   for( i = 0; i < stack; i++ )
  195.         printf( "   " );
  196.         printf( "Exiting %s\n", names[stack - 1] );
  197.     }
  198.     stack--;
  199.     return;
  200. }
  201.  
  202. #endif
  203.  
  204. int
  205. dont_look(
  206.     line_ptr  line )
  207. /*   tells us whether or not this line should be considered for comparison or
  208.  * is a filler (eg header, blank) line
  209.  */
  210. {   register  int       i;
  211.  
  212.     trace( "dont_look" );
  213.     if( line == NULL )                                                         /* EOF reached                        */
  214.         ret_val( 0 );
  215.     if( line->linenum <= top_skip )
  216.         ret_val( 1 );
  217.     if( line->linenum > page_len - bot_skip )
  218.         ret_val( 1 );
  219.     if( !blanks )
  220.     {   for( i = 0; i < MAXLINE; i++ )
  221.             switch( line->test[i] )
  222.             {   case '\0':
  223.                 case '\n':    ret_val( 1 );
  224.                 case '\t':
  225.                 case ' ' :    break;
  226.                 default  :    ret_val( 0 );
  227.     }       }
  228.     ret_val( 0 );
  229. }
  230.  
  231. int
  232. equal(
  233.     line_ptr  a,
  234.     line_ptr  b )
  235. /*   tells us if the pointers 'a' and 'b' point to line buffers containing
  236.  * equivalent text or not
  237.  */
  238. {   trace( "equal" );
  239.     if(( a == NULL ) || ( b == NULL ))
  240.         ret_val( 0 );
  241.     if( a->crc != b->crc )
  242.         ret_val( 0 );
  243.     if(( a->length < left_col ) ||                                             /* are lines shorter than left column */
  244.        ( b->length < left_col ))
  245.         ret_val( 1 );
  246.     if( up_case )
  247.         ret_val( !strncmp( &( a->test[left_col] ),
  248.                            &( b->test[left_col] ),
  249.                            right_col - left_col ))
  250.     else
  251.         ret_val( !strnicmp( &( a->test[left_col] ),
  252.                             &( b->test[left_col] ),
  253.                             right_col - left_col ))
  254. }
  255.  
  256. int
  257. position(
  258.     int       f,
  259.     line_ptr  where )
  260. /*   moves the input pointer for file 'f' such that the next line to be read
  261.  * will be 'where'
  262.  */
  263. {   trace( "position" );
  264.     at[f] = &root[f];
  265.     if( where == NULL )
  266.         ret;
  267.     while( at[f]->link != where )
  268.         at[f] = at[f]->link;
  269.     ret;
  270. }
  271.  
  272. int
  273. addcrc(                                                                        /* update a CRC check                 */
  274.     int       crc,                                                             /* running CRC value                  */
  275.     unsigned  char      c )                                                    /* character to add                   */
  276. /*   CRC computation logic
  277.    The logic for this method of calculating the CRC 16 bit polynomial
  278.    is taken from an article by David Schwaderer in the April 1985
  279.    issue of PC Tech Journal.
  280. */
  281. {   return(( crc >> 8 ) & 0x00ff ) ^ crctab[( crc ^ c ) & 0x00ff];
  282. }
  283.  
  284. void
  285. makecrc(
  286.     line_ptr  a )
  287. /*   calculates a crc for the line, and stores it in the line buffer.  This
  288.  * allows for faster comparisons
  289.  */
  290. {   register  int       i;
  291.     register  int       j;
  292.     register  int       cs;
  293.  
  294.     trace( "makecrc" );
  295.     cs = 0;
  296.     if( a->length > right_col )                                                /* set length of compare to min of    */
  297.         j = right_col;                                                         /*   right_col or line length         */
  298.     else
  299.         j = a->length;
  300.     if( a->length < left_col )                                                 /* is line shorter than left column   */
  301.         i = j;                                                                 /*   set while terminate immediately  */
  302.     else
  303.         i = left_col;
  304.     while( i < j )
  305.         cs = addcrc( cs, a->test[i++] );
  306.     a->crc = ( byte ) cs;
  307. }
  308.  
  309. void
  310. tabexpand(                                                                     /* expand tabs to spaces              */
  311.     char      *s1,
  312.     char      *s2 )
  313. {   register  int       i;
  314.     register  char      j;
  315.  
  316.     trace( "tabexpand" );
  317.     for( i = j = 0; s2[i]; i++ )
  318.     {   if( s2[i] != '\t' )
  319.         {   s1[j++] = s2[i];
  320.             continue;
  321.         }
  322.         do  s1[j++] = ' ';
  323.         while( j % 8 != 0 );
  324.     }
  325.     s1[j] = 0;
  326. }
  327.  
  328. void
  329. nowhite(                                                                       /* ignore white space                 */
  330.     char      *s1,
  331.     char      *s2 )
  332. {   register  int       i,
  333.                         j;
  334.  
  335.     trace( "nowhite" );
  336.     for( i = j = 0; s2[i]; i++ )
  337.     {   if( s2[i] != ' ' && s2[i] != '\t' )
  338.             s1[j++] = s2[i];
  339.     }
  340.     s1[j] = 0;
  341. }
  342.  
  343. void
  344. trimline(                                                                      /* trim off trailing white space      */
  345.     char      *s1 )
  346. {   register  int       i;
  347.  
  348.     trace( "trimline" );
  349.     i = strlen( s1 );
  350.     if( s1[--i] == '\n' )                                                      /* if linefeed last char, skip        */
  351.         --i;
  352.     for( ; s1[i] == ' ' || s1[i] == '\t'; i-- )
  353.         ;
  354.     s1[++i] = '\n';                                                            /* assume linefeed should be last char*/
  355.     s1[++i] = '\0';                                                            /* end string after linefeed          */
  356. }
  357.  
  358. line_ptr
  359. next_line(
  360.     int       f )
  361. /*   allocates, links and returns the next line from file 'f' if no lines are
  362.  * buffered, otherwise returns the next buffered line from 'f' and updates the
  363.  * link pointer to the next buffered line; it also inserts the line in the
  364.  * correct place in the array same_crc
  365.  */
  366. {   line_ptr  place_hold,
  367.               start;
  368.     char      buffer[MAXLINE];
  369.  
  370.     trace( "next_line" );
  371.     if( at[f]->link != NULL )
  372.     {   at[f] = at[f]->link;
  373.         ret_val( at[f] );
  374.     }
  375.     else
  376.     {   at[f]->link = ( line_ptr ) malloc( sizeof( struct LINE ));
  377.         if( at[f]->link == NULL )
  378.         {   printf( "\nError: Out of Memory" );
  379.             exit (2);
  380.         }
  381.         place_hold = at[f];
  382.         at[f] = at[f]->link;
  383.         if( place_hold == &( root[f] ))
  384.             at[f]->prev = NULL;
  385.         else
  386.             at[f]->prev = place_hold;
  387.         at[f]->link = NULL;
  388.         at[f]->same = NULL;
  389.         at[f]->position = ftell( infile[f] );
  390.         if( fgets( at[f]->test, MAXLINE, infile[f] ) == NULL )
  391.         {   free( at[f] );
  392.             at[f] = place_hold;
  393.             at[f]->link = NULL;
  394.             at[f]->same = NULL;
  395.             ret_val( NULL );
  396.         }
  397.         if( white != 0 )
  398.         {   nowhite( buffer, at[f]->test );
  399.             strcpy( at[f]->test, buffer );
  400.         }
  401.         else
  402.         {   if( trim != 0 )
  403.                 trimline( at[f]->test );
  404.             if( tabs != 0 )
  405.             {   tabexpand( buffer, at[f]->test );
  406.                 strcpy( at[f]->test, buffer );
  407.         }   }
  408.         if( up_case )
  409.             strupr( at[f]->test );
  410.         at[f]->length   = strlen( at[f]->test );
  411.         makecrc( at[f] );                                                      /* calc crc for new text line    */
  412.  
  413. #ifdef EMBEDDED_FORMFEEDS
  414.         if(( strchr( at[f]->test, FORMFEED ) != NULL ) ||
  415.            ( line_count[f] > page_len ))
  416. #else
  417.         if(( *(at[f]->test ) == FORMFEED ) ||
  418.            ( line_count[f] > page_len ))
  419. #endif
  420.         {   page_count[f]++;
  421.             line_count[f] = 1;
  422.         }
  423.         at[f]->lineanum = linea_count[f]++;
  424.         at[f]->linenum  = line_count[f]++;
  425.         at[f]->pagenum  = page_count[f];
  426.  
  427.         if( !dont_look( at[f] ))                                               /* insert in array unless dont_look   */
  428.         {   start = same_crc[at[f]->crc][f - 1];
  429.             if( start == NULL )
  430.                 same_crc[at[f]->crc][f - 1] = at[f];
  431.             else
  432.             {   while( start->same != NULL )
  433.                     start = start->same;
  434.                 start->same = at[f];                                           /* start NULL now, insert at[f] here  */
  435.         }   }
  436.     ret_val( at[f] );
  437. }   }
  438.  
  439. int
  440. discard (
  441.     int       f,
  442.     line_ptr  to )
  443. /*   deallocates all buffered lines from the root up to and including 'to' for
  444.  * file 'f', including from same_crc
  445.  */
  446. {   line_ptr  temp;
  447.  
  448.     trace( "discard" );
  449.     for(;;)
  450.     {   if( root[f].link == NULL || to == NULL )
  451.             break;
  452.         temp = root[f].link;
  453.       if( !dont_look( temp ))                                                  /* line exists, find same_crc recd  */
  454.           same_crc[temp->crc][f - 1] = temp->same;                             /* replace with temp->same            */
  455.       root[f].link = root[f].link->link;
  456.       root[f].link->prev = NULL;
  457.       free( temp );
  458.       if( temp == to )
  459.           break;
  460.     }
  461.     at[f] = &root[f];
  462.     ret;
  463. }
  464.  
  465. int 
  466. getline(
  467.     char      text[],                                                          /* text string to return              */
  468.     int       file,                                                            /* file number to read                */
  469.     long      position )                                                       /* position of line to read           */
  470. /*  go back to file and get the original line for output.
  471.  */
  472. {   long      before;                                                          /* marker to return to after read     */
  473.  
  474.     trace( "getline" );
  475.     before = ftell( infile[file] );
  476.     fseek( infile[file], position, SEEK_SET );
  477.     fgets( text, MAXLINE, infile[file] );
  478.     fseek( infile[file], before, SEEK_SET );
  479.     ret;
  480. }
  481.  
  482. int
  483. put(
  484.     line_ptr  line )
  485. /*  if change barred output file is turned on, prints all lines from the root
  486.  * of file 1 up to and including 'line'.  This is only called if a match exists
  487.  * for each significant line in file 2.
  488.  */
  489. {   line_ptr  temp;
  490.  
  491.     trace( "put" );
  492.     if( output )
  493.         for( temp = root[1].link; ; )
  494.         {   if( temp == NULL )
  495.                 ret;
  496.             getline( text, 1, temp->position );
  497.             fputs( text, outfile );
  498.             if( temp == line )
  499.                 ret;
  500.             temp = temp->link;
  501.         }
  502.     ret;
  503. }
  504.  
  505.  
  506.  
  507. char
  508. *change_bar(
  509.     char      *str )
  510. /*   inserts a change bar into the text pointed to by 'str' and returns a
  511.  * pointer to 'str'
  512.  */
  513. {   int       i;
  514.     char      temp[MAXLINE + 1],
  515.               *dest,
  516.               *base;
  517.  
  518.     trace( "change_bar" );
  519.     base = str;
  520.     dest = temp;
  521.     i = 0;
  522.     if( bar_col != 0 )
  523.     {   for( i = 0; *str != '\n'; i++ )
  524.         {   if(( *str == '\r' ) && ( *( str + 1 ) != '\n' ))
  525.                 i = 0;
  526.             *( dest++ ) = *( str++ );
  527.         }
  528.         while( i++ < bar_col )
  529.             *( str )++ = ' ';
  530.         strcpy( str, "|\n" );
  531.     }
  532.     else
  533.     {   if( str[0] != ' ' )
  534.         {   strcpy( temp, str );
  535.             strcpy( str + 1, temp );
  536.         }
  537.         str[0] = '|';
  538.     }
  539.     ret_val( base );
  540. }
  541.  
  542. int
  543. added(
  544.     line_ptr  line )
  545. /*  prints a change summary for all significant lines from the root of file 1
  546.  * up to and including 'line'.  If output is enabled, adds a change bar to the
  547.  * text and outputs the line to the output file
  548.  */
  549. {   line_ptr  temp;
  550.  
  551.     trace( "added" );
  552.     for( temp = root[1].link; ; )
  553.     {   if( temp == NULL )
  554.             ret;
  555.         getline( text, 1, temp->position );
  556.         if( !dont_look( temp ) && scr_on )
  557.             printf( "%.3d:%.2d(%.4d)< %s",  temp->pagenum,
  558.                                             temp->linenum,
  559.                                             temp->lineanum,
  560.                                             text );
  561.         if( output )
  562.             if( dont_look( temp ))
  563.                 fputs( text, outfile );
  564.             else
  565.                 fputs( change_bar( text ), outfile );
  566.         if( temp == line )
  567.             ret;
  568.         temp = temp->link;
  569. }   }
  570.  
  571. int
  572. deleted(
  573.     line_ptr  line )
  574. /*  outputs a change summary for all lines in file 2 from the root up to and
  575.  * including 'line'
  576.  */
  577. {   line_ptr  temp;
  578.  
  579.     trace( "deleted" );
  580.     for( temp = root[2].link; ; )
  581.     {   if( temp == NULL )
  582.             ret;
  583.         getline( text, 2, temp->position );
  584.         if( !dont_look( temp ) && scr_on )
  585.             printf( "%.3d:%.2d(%.4d)> %s",  temp->pagenum,
  586.                                             temp->linenum,
  587.                                             temp->lineanum,
  588.                                             text );
  589.         if( temp == line )
  590.             ret;
  591.         temp = temp->link;
  592. }   }
  593.  
  594. int
  595. resync(
  596.     line_ptr  first,
  597.     line_ptr  second,
  598.     int       lookahead )
  599. /*  resynchronizes file 1 and file 2 after a difference is detected and
  600.  * outputs changed lines and change summaries via added() and deleted().
  601.  * Exits with the file inputs pointing at the next two lines that match,
  602.  * unless it is impossible to sync up again, in which case all lines in file 1
  603.  * are printed via added().  Deallocates lines printed by this function.
  604.  */
  605. {   line_ptr  file1_start,
  606.               file2_start,
  607.               last_bad1,
  608.               last_bad2,
  609.               t1,
  610.               t2,
  611.               crc;
  612.     int       i,
  613.               k,
  614.               moved1,
  615.               moved2;
  616.  
  617.     trace( "resync" );
  618.     position( 2, second );                                                     /* ensure enough lines in memory      */
  619.     last_bad2 = second;
  620.     for( k = 0; k < lookahead && last_bad2 != NULL ; k++ )
  621.         last_bad2 = next_line( 2 );
  622.  
  623.     moved1 = 0;                                                                /* now reset file pointers            */
  624.     file1_start = first;
  625.     position( 1, first );
  626.  
  627.     for( k = 0; k < lookahead; k++ )
  628.     {   while( dont_look( file1_start = next_line( 1 )))
  629.             ;
  630.         if( file1_start == NULL )
  631.             goto no_sy;
  632.         moved2 = 0;
  633.  
  634.         crc = same_crc[file1_start->crc][1];                                   /* see if any matching line at all    */
  635.         while( crc != NULL )
  636.         {   while( !equal( crc, file1_start ))                                 /* look for matching entries          */
  637.             {   crc = crc->same;
  638.                 if( crc == NULL )
  639.                     break;
  640.             }
  641.  
  642.             if( crc != NULL )
  643.             {   t1 = file1_start;                                              /* ok we have a hit                   */
  644.                 file2_start = crc;
  645.                 t2 = file2_start;
  646.                 position( 1, file1_start );
  647.                 position( 2, file2_start );
  648.                 for( i = 0; ( i < re_sync ) && equal( t1, t2 ); i++ )
  649.                 {   while( dont_look( t1 = next_line( 1 )))
  650.                         ;
  651.                     while( dont_look( t2 = next_line( 2 )))
  652.                         ;
  653.                     if(( t1 == NULL ) || ( t2 == NULL ))
  654.                         break;
  655.                 }
  656.                 if( i == re_sync )
  657.                 {   moved2++;
  658.                     if(( last_bad2 = file2_start->prev ) == NULL )
  659.                         moved2 = 0;
  660.                     goto synced;
  661.                 }
  662.                 crc = crc->same;                                               /* get next entry                     */
  663.         }   }
  664.  
  665.         last_bad1 = file1_start;                                               /* else no sync/match yet, loop list  */
  666.         position( 1, file1_start );
  667.         while( dont_look( file1_start = next_line( 1 )))
  668.             ;
  669.         moved1++;
  670.  
  671.     }
  672.     ret_val( NO_SYNC );
  673.  
  674. no_sy:
  675.     position( 1, first);
  676.     while(( first = next_line( 1 )) != NULL )
  677.     {   added( first );
  678.         discard( 1, first );
  679.     }
  680.     ret_val( EOF_REACHED );
  681.  
  682. synced:
  683.     if( moved1 )
  684.     {   added( last_bad1 );
  685.         discard( 1, last_bad1 );
  686.     }
  687.     position( 1, file1_start );
  688.     if( moved2 )
  689.     {   deleted( last_bad2 );
  690.         discard( 2, last_bad2 );
  691.     }
  692.     position( 2, file2_start );
  693.     if( scr_on )
  694.         printf( "\n" );
  695.     ret_val( RE_SYNCED );
  696. }
  697.  
  698.  
  699. diff(
  700.     void )
  701. /*  differencing executive.  Prints and deallocates all lines up to where a
  702.  * difference is detected, at which point resync() is called.  Exits on end of
  703.  * file 1
  704.  */
  705. {   int       look_lines,
  706.               result;
  707.     line_ptr  first,
  708.               second;
  709.  
  710.     trace( "diff" );
  711.     for( ;; )
  712.     {   while( dont_look( first = next_line( 1 )))
  713.             ;
  714.         if( first == NULL )                                                    /* file 1 End-of-File                 */
  715.         {   put( first );
  716.             while(( second = next_line( 2 )) != NULL )                         /* put all file 2 as deleted          */
  717.             {   if( second->length > left_col )                                /*   only if lines are long enough    */
  718.                 {   deleted( second );
  719.                     discard( 2, second );
  720.             }   }
  721.             ret;
  722.         }
  723.         while( dont_look( second = next_line( 2 )))
  724.             ;
  725.         if( equal( first, second ))
  726.         {   put( first );
  727.             discard( 1, first );
  728.             discard( 2, second );
  729.         }
  730.         else
  731.         {   look_lines = 10;                                                   /* start with 10 lines look-ahead     */
  732.             result = NO_SYNC;
  733.             while( result == NO_SYNC )
  734.             {   result = resync( first, second, look_lines );
  735.                 look_lines *= 2;
  736.                 if( look_lines == 80 )                                         /* when 80, assume BIG diff, set 400  */
  737.                     look_lines = 400;
  738.         }   }
  739.         if( second == NULL )
  740.             ret;
  741. }   }
  742.  
  743. int
  744. page_skip(
  745.     void )
  746. /*  skips the first 'skip1' pages of file 1, and then the first 'skip2' pages
  747.  * of file 2.  This is useful to jump over tables of contents
  748.  */
  749. {   line_ptr  first,
  750.               second;
  751.  
  752.     trace( "page_skip" );
  753.     for( ;; )
  754.     {   first = next_line( 1 );
  755.         if(( first == NULL ) || ( first->pagenum > skip1 ))
  756.             break;
  757.         put( first );
  758.         discard( 1, first );
  759.     }
  760.     if( first != NULL )
  761.         position( 1, first );
  762.     for( ;; )
  763.     {   second = next_line( 2 );
  764.         if(( second == NULL ) || ( second->pagenum > skip2 ))
  765.             break;
  766.         discard( 2, second );
  767.     }
  768.     if( second != NULL )
  769.         position( 2, second );
  770.     ret;
  771. }
  772.  
  773. int
  774. help(                                                                          /* outputs usage information          */
  775.     void )
  776. {   printf( "\ndiff - text file differencer and change barrer ("
  777.             VERSION
  778.             ")"
  779.             "\nusage: diff [option{option}] newfile oldfile [barfile]"
  780.             "\n"
  781.             "\noptions: (default) description" );
  782. #ifdef TRACER_FUNCTIONS
  783.     printf( "\n   -t   (off) trace operation"
  784.             "\n              hit keyboard t to turn off trace while executing"
  785.             "\n              hit keyboard s to see memory usage" );
  786. #endif
  787.     printf( "\n   -b n  (78) column of barfile for change bar"
  788.             "\n   -l n   (1) compare from left column n"
  789.             "\n   -m n (255) compare to maximum column n"
  790.             "\n   -h n   (0) lines at top of each page to skip for headers"
  791.             "\n   -f n   (0) lines at bottom of each page to skip for footers"
  792.             "\n   -p n  (66) lines per page (embedded form feeds override)"
  793.             "\n   -c   (off) uppercase/lowercase is significant"
  794.             "\n   -r n   (3) lines that must match before files are considered resynced"
  795.             "\n              after differences are found"
  796.             "\n   -w   (not) blank lines are considered significant"
  797.             "\n   -s    (on) screen listing off"
  798.             "\n   -e    (on) tab expansion to blanks off"
  799.             "\n   -i   (off) ignore all white space (tabs and blanks)"
  800.             "\n   -t   (off) trim end-of-line white space (tabs and blanks)"
  801.             "\n   -n n   (0) pages in NEWFILE to skip before compare (ex. table of contents)"
  802.             "\n   -o n   (0) pages in OLDFILE to skip before compare"
  803.             "\n");
  804.     ret;
  805. }
  806.  
  807. int
  808. open_files(                                                                    /* opens the input and output files   */
  809.     void )
  810. {   int       i;
  811.  
  812.     trace( "open_files" );
  813.     for( i = 1; i < 3; i++ )
  814.         if(( infile[i] = fopen( infile_name[i], "r" )) == NULL )
  815.         {   printf( "\nError: can't open %s", infile_name[i] );
  816.             command_errors++;
  817.         }
  818.     if( files > 2 )
  819.         if(( outfile = fopen( outfile_name, "w" )) == NULL )
  820.         {   printf( "\nError: can't create %s", outfile_name );
  821.             command_errors++;
  822.         }
  823.     ret;
  824. }
  825.  
  826. strip_opt(                                                                     /* processes each command line option */
  827.     int       ac,
  828.     char      *av[] )
  829. {   int       i;
  830.  
  831.     trace( "strip_opt" );
  832.     for( i = 1; i < ac; i++ )
  833.     {   if( av[i][0] == OPT_FLAG )
  834.         {   switch( av[i][1] )
  835.             {   case 'b':   bar_col  = atoi( av[++i] );
  836.                             break;
  837.                 case 'l':   left_col = atoi( av[++i] ) - 1;                    /* array address, so make 0 based     */
  838.                             break;
  839.                 case 'm':   right_col = atoi( av[++i] );
  840.                             break;
  841.                 case 'h':   top_skip = atoi( av[++i] );
  842.                             break;
  843.                 case 'f':   bot_skip = atoi( av[++i] );
  844.                             break;
  845.                 case 'p':   page_len = atoi( av[++i] );
  846.                             break;
  847.                 case 'c':   up_case  = 1;
  848.                             break;
  849.                 case 'r':   re_sync  = atoi( av[++i] );
  850.                             break;
  851.                 case 'w':   blanks   = 1;
  852.                             break;
  853.                 case 's':   scr_on   = 0;
  854.                             break;
  855.                 case 'e':   tabs     = 0;
  856.                             break;
  857.                 case 'i':   white    = 1;
  858.                             break;
  859.                 case 't':   trim     = 1;
  860.                             break;
  861.                 case 'n':   skip1    = atoi( av[++i] );
  862.                             break;
  863.                 case 'o':   skip2    = atoi( av[++i] );
  864.                             break;
  865. #ifdef TRACER_FUNCTIONS
  866.                 case 't':   trace_enabled = debug = 1;
  867.                             break;
  868. #endif
  869.                 default:    printf( "\nUnrecognised option %s", av[i] );
  870.                             command_errors++;
  871.         }   }
  872.         else
  873.         {   switch( files )
  874.             {   case 0:     strcpy( infile_name[1], av[i] );
  875.                             break;
  876.                 case 1:     strcpy( infile_name[2], av[i] );
  877.                             break;
  878.                 case 2:     strcpy( outfile_name, av[i] );
  879.                             output = 1;
  880.                             break;
  881.                 default:    printf( "\nError: too many files at %s", av[i] );
  882.                             command_errors++;
  883.             }
  884.             files++;
  885.     }   }
  886.     if( !scr_on && !output )
  887.     {   printf( "\nError: no output file or screen listing will be generated." );
  888.         command_errors++;
  889.     }
  890.     ret;
  891. }
  892.  
  893. int
  894. main(
  895.     int       argc,
  896.     char      *argv[] )
  897. {   trace ( "main" );
  898.     if( argc == 1 )
  899.     {   help();
  900.         exit( 0 );
  901.     }
  902.     else
  903.         printf( "Pag:Ln Line#  diff -- "
  904.                  VERSION
  905.                  "\n" );
  906.  
  907.     strip_opt (argc, argv);
  908.     if( files < 2 )
  909.     {   printf( "\nError: Must specify two files" );
  910.         exit( 2 );
  911.     }
  912.     open_files();
  913.     if( command_errors )
  914.         exit( 2 );
  915.  
  916.     at[1]->prev = NULL;                                                        /* set root previous pointers to 0    */
  917.     at[2]->prev = NULL;
  918.  
  919.     page_skip();
  920.     diff();
  921.     added( NULL );                                                             /* print out all from file 1          */
  922.     deleted( NULL );                                                           /* print out all from file 2          */
  923.     ret;
  924. }
  925.