home *** CD-ROM | disk | FTP | other *** search
/ SPACE 2 / SPACE - Library 2 - Volume 1.iso / program / 177 / c / diff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-09-15  |  23.0 KB  |  880 lines

  1. /*------------------------------------------------------------------------------
  2. --------------------------------------------------------------------------------
  3. --
  4. --            Name: DIFF.C
  5. --       Processor: VAX | MS-DOS
  6. --           Class: C Program
  7. --   Creation Date: 1/8/87
  8. --        Revision: 07/23/87 by J.K.LaPeer for Megamax C compiler for Atari ST
  9. --          Author: D. Krantz
  10. --
  11. --     Description: File compare and change-bar for text files.
  12. --
  13. --------------------------------------------------------------------------------
  14. ------------------------------------------------------------------------------*/
  15.  
  16. /* File Difference Utility */
  17.  
  18. #include <ctype.h>
  19. #include <stdio.h>
  20.  
  21. #define OPT_FLAG  '/'    /* command line option switch recognizer    */
  22.  
  23. #ifdef VAX11C
  24. #define MAXLINE 161    /* maximum characters in input line        */
  25. #else
  26. #define MAXLINE 85
  27. #endif
  28.  
  29. #define FORMFEED 'L'-'@'
  30.  
  31. struct LINE {  /* structure defining a line internally  */
  32.  int linenum; /* what line on page    */
  33.  int pagenum; /* what page line is from   */
  34.  struct LINE *link; /* linked list pointer   */
  35.  char text[ MAXLINE ]; /* text of line    */
  36.  char dup[ MAXLINE ]; /* uppercase copy of line text  */
  37. };
  38.  
  39. typedef struct LINE *line_ptr;
  40.  
  41. typedef char *char_ptr;
  42.  
  43. typedef FILE *FILE_PTR;
  44.  
  45. struct LINE root[ 3 ];  /* root of internal linked lists */
  46.  
  47. FILE_PTR msg;   /* differences summary file pointer */
  48.  
  49. int line_count[ 3 ] = { 1, 1, 1 }; /* file's line counter  */
  50. int page_count[ 3 ] = { 1, 1, 1 }; /* file's page counter  */
  51. int command_errors = 0;  /* how many command line errors  */
  52. char xx1[ 132 ], xx2[ 132 ]; /* space to retain file names  */
  53. int files = 0;  /* how many files specified on command line */
  54. char_ptr infile_name[ 3 ] = { NULL, xx1, xx2 };
  55. char outfile_name[ 132 ]; /* changebarred output filename  */
  56. FILE_PTR infile[ 3 ];  /* input file pointers   */
  57. FILE *outfile;   /* changebarred output file pointer */
  58. static line_ptr at[ 3 ] = { NULL, &(root[ 1 ]), &(root[ 2 ]) };
  59.  
  60. int debug = 0;  /* trace switch     */
  61. int trace_enabled = 0; /* keyboard tracing switch   */
  62. int bar_col = 78; /* column where change bar is to appear  */
  63. int top_skip = 0; /* lines to skip at top of page   */
  64. int bot_skip = 0; /* lines to skip at bottom of page  */
  65. int page_len = 66; /* length of a page    */
  66. int up_case = 0; /* boolean, is upper/lower case significant? */
  67. int re_sync = 5; /* lines that must match for resynchronization */
  68. int output = 0;  /* boolean, is change-barred output file on? */
  69. int blanks = 0;  /* boolean, are blank lines significant? */
  70. int lookahead = 200; /* how many lines to look ahead before giving up*/
  71. int skip1 = 0;  /* how many pages of first file to skip  */
  72. int skip2 = 0;  /* how many pages of second file to skip */
  73.  
  74. #if 0 /* tracing and other debug functions turned off */
  75.  
  76. #define trace( x )     callstack( x )
  77. #define ret            { callpop(); return; }
  78. #define ret_val( x )   { callpop(); return( x ); }
  79. #define TRACER_FUNCTIONS
  80.  
  81. #else
  82.  
  83. #define trace( x ) /** nothing **/
  84. #define ret  { return; }
  85. #define ret_val( x ) { return( x ); }
  86.  
  87. #endif
  88.  
  89. /*------------------------------------------------------------------------------
  90. ------------------------------------------------------------------------------*/
  91. main( argc, argv )
  92.    int argc;
  93.    char *argv[];
  94. {
  95.    int i;
  96.  trace( "main" );
  97.  if( argc == 1 )
  98.   help();
  99.  msg = stdout;
  100.  for( i = 1; i < argc; i++ )
  101.   strip_opt( argv[ i ] );
  102.  if( files < 2 )
  103.  {
  104.   printf( "\nError: Must specify two files" );
  105.   exit( 2 );
  106.  }
  107.  open_files();
  108.  if( command_errors )
  109.   exit( 2 );
  110.  page_skip();
  111.  diff();
  112.  ret;
  113. }
  114.  
  115. /*------------------------------------------------------------------------------
  116. DONT_LOOK - Tells us whether or not this line should be considered for
  117. comparison or is a filler (e.g. header, blank) line.
  118. ------------------------------------------------------------------------------*/
  119. dont_look( line )
  120.    line_ptr line;
  121. {
  122.    int i;
  123.  trace( "dont_look" );
  124.  if( line == NULL )
  125.   ret_val( 0 );
  126.  if( line->linenum <= top_skip )
  127.   ret_val( 1 );
  128.  if( line->linenum > page_len - bot_skip )
  129.   ret_val( 1 );
  130.  if( !blanks )
  131.  {
  132.   for( i = 0; i < MAXLINE; i++ )
  133.    switch( line->text[ i ] )
  134.    {
  135.     case '\0':
  136.     case '\n':
  137.      ret_val( 1 );
  138.     case '\t':
  139.     case ' ':
  140.      break;
  141.     default:
  142.      ret_val( 0 );
  143.    }
  144.  }
  145.  ret_val( 0 );
  146. }
  147.  
  148. /*------------------------------------------------------------------------------
  149. EQUAL - tells us if the pointers 'a' and 'b' point to line buffers containing
  150. equivalent text or not.
  151. ------------------------------------------------------------------------------*/
  152. equal( a, b )
  153.    line_ptr a, b;
  154. {
  155.  trace( "equal" );
  156.  if( (a == NULL) || (b == NULL) )
  157.   ret_val( 0 );
  158.  if( up_case )
  159.   ret_val( !strcmp( a->dup, b->dup ) )
  160.  else
  161.   ret_val( !strcmp( a->text, b->text ) )
  162. }
  163.  
  164. /*------------------------------------------------------------------------------
  165. POSITION - moves the input pointer for file 'f' such that the next line to
  166. be read will be 'where'.
  167. ------------------------------------------------------------------------------*/
  168. position( f, where )
  169.    int f;
  170.    line_ptr where;
  171. {
  172.  trace( "position" );
  173.  at[ f ] = &root[ f ];
  174.  while( at[ f ]->link != where )
  175.   at[ f ] = at[ f ]->link;
  176.  ret;
  177. }
  178.  
  179. /*------------------------------------------------------------------------------
  180. FIX - fixes the end-of-line sequence on a VAX to be just a newline instead of
  181. a carriage-return/newline.
  182. ------------------------------------------------------------------------------*/
  183. char *fix( str )
  184.    char *str;
  185. {
  186.    char *strsave;
  187.  
  188.  trace( "fix" );
  189.  strsave = str;
  190.  if( str == NULL )
  191.   ret_val( NULL )
  192. #ifdef VAX11C
  193.  while( *str != '\0' )
  194.  {
  195.   if( match( str, "\r\n" ) )
  196.   {
  197.    *str = '\n';
  198.    *(str + 1) = '\0';
  199.   }
  200.   str++;
  201.  }
  202. #endif
  203.  ret_val( strsave );
  204. }
  205.  
  206. /*------------------------------------------------------------------------------
  207. INDEX - returns a pointer to the first occurance of 'c' in the string pointed
  208. to by 'str', or NULL if 'str' does not contain 'c'.
  209. ------------------------------------------------------------------------------*/
  210. char *index( str, c )
  211.    char *str, c;
  212. {
  213.  trace( "index" );
  214.  while( (*str != c) && *(str++) );
  215.  if( *str == c )
  216.    ret_val( str )
  217.  ret_val( NULL );
  218. }
  219.  
  220. /*------------------------------------------------------------------------------
  221. NEXT_LINE - allocates, links, and returns the next line from file 'f' if no
  222. lines are buffered, otherwise returns the next buffered line from file 'f'
  223. and updates the link pointer to the next buffered line.
  224. ------------------------------------------------------------------------------*/
  225. line_ptr next_line( f )
  226.    int f;
  227. {
  228.    char *malloc();
  229.    line_ptr temp, place_hold;
  230.  
  231.  trace( "next_line" );
  232.  if( at[ f ]->link != NULL )
  233.  {
  234.   at[ f ] = at[ f ]->link;
  235.   ret_val( at[ f ] );
  236.  }
  237.  else
  238.  {
  239.   at[ f ]->link = (line_ptr)malloc( sizeof( struct LINE ) );
  240.   if( at[ f ]->link == NULL )
  241.   {
  242.    printf( "\nOut of Memory" );
  243.    exit( 2 );
  244.   }
  245.   place_hold = at[ f ];
  246.   at[ f ] = at[ f ]->link;
  247.   at[ f ]->link = NULL;
  248.   if( fix( fgets( at[ f ]->text, MAXLINE, infile[ f ] ) ) == NULL)
  249.   {
  250.    free( at[ f ] );
  251.    at[ f ] = place_hold;
  252.    at[ f ]->link = NULL;
  253.    ret_val( NULL )
  254.   }
  255. #ifdef EMBEDDED_FORMFEEDS
  256.   if( (index( at[ f ]->text, FORMFEED ) != NULL) || 
  257.    (line_count[ f ] > page_len ) )
  258. #else
  259.   if( ( *(at[ f ]->text) == FORMFEED) || 
  260.    (line_count[ f ] > page_len ) )
  261. #endif
  262.   {
  263.    page_count[ f ]++;
  264.    line_count[ f ] = 1;
  265.   }
  266.   at[ f ]->linenum = line_count[ f ]++;
  267.   at[ f ]->pagenum = page_count[ f ];
  268.   if( up_case )
  269.   {
  270.    strcpy( at[ f ]->dup, at[ f ]->text );
  271.    upper( at[ f ]->dup );
  272.   }
  273.   ret_val( at[ f ] );
  274.  }
  275. }
  276.  
  277. /*------------------------------------------------------------------------------
  278. DISCARD - deallocates all buffered lines from the root up to and including
  279. 'to' for file 'f'.
  280. ------------------------------------------------------------------------------*/
  281. discard( f, to )
  282.    int f;
  283.    line_ptr to;
  284. {
  285.    line_ptr temp;
  286.  
  287.  trace( "discard" );
  288.  for(;;)
  289.  {
  290.   if( root[ f ].link == NULL )
  291.    break;
  292.   temp = root[ f ].link;
  293.   root[ f ].link = root[ f ].link->link;
  294.   free( temp );
  295.   if( temp == to )
  296.    break;
  297.  }
  298.  at[ f ] = &root[ f ];
  299.  ret;
  300. }
  301.  
  302. /*------------------------------------------------------------------------------
  303. VFPUTS - for VAX, un-fixes newline at end of line to be carriage-return/newline.
  304. ------------------------------------------------------------------------------*/
  305. vfputs( str, file )
  306.    char *str;
  307.    FILE *file;
  308. {
  309.    int i;
  310.  trace( "vfputs" );
  311. #ifdef VAX11C
  312.  for( i = 0; i < MAXLINE; i++ )
  313.  {
  314.   if( str[ i ] == '\n' )
  315.   {
  316.    strcpy( str + i, "\r\n" );
  317.    break;
  318.   }
  319.  }
  320.  fputs( str, file );
  321. #else
  322.  fputs( str, file );
  323. #endif
  324.  ret;
  325. }
  326.  
  327. /*------------------------------------------------------------------------------
  328. PUT - If change-barred output file is turned on, prints all lines from the
  329. root of file 1 up to and including 'line'. This is called only if a match
  330. exists for each significant line in file 2.
  331. ------------------------------------------------------------------------------*/
  332. put( line )
  333.    line_ptr line;
  334. {
  335.    line_ptr temp;
  336.  
  337.  trace( "put" );
  338.  if( output )
  339.   for( temp = root[ 1 ].link; ; )
  340.   {
  341.    if( temp == NULL )
  342.     ret
  343.    vfputs( temp->text, outfile );
  344.    if( temp == line )
  345.     ret
  346.    temp = temp->link;
  347.   }
  348.  ret;
  349. }
  350.  
  351. /*------------------------------------------------------------------------------
  352. CHANGE_BAR - inserts a change-bar into the text pointed to by
  353. 'str' and returns a pointer to 'str'.
  354. ------------------------------------------------------------------------------*/
  355. char *change_bar( str )
  356.    char *str;
  357. {
  358.    int i;
  359.    char temp[ MAXLINE + 1 ], *dest,*base;
  360.  
  361.  trace( "change_bar" );
  362.  base = str;
  363.  dest = temp;
  364.  i = 0;
  365.  if( bar_col != 0 )
  366.  {
  367.   for( i = 0; *str != '\n'; i++ )
  368.   {
  369.    if( (*str == '\r') && (*(str + 1) != '\n') )
  370.     i = 0;
  371.    *(dest++) = *(str++);
  372.   }
  373.   while( i++ < bar_col ) 
  374.    *(str)++ = ' ';
  375.   strcpy( str, "|\n" );
  376.  }
  377.  else
  378.   if( str[ 0 ] != ' ' )
  379.   {
  380.    strcpy( temp, str );
  381.    strcpy( str + 1, temp );
  382.    str[ 0 ] = '|';
  383.   }
  384.  ret_val( base );
  385. }
  386.  
  387. /*------------------------------------------------------------------------------
  388. ADDED - Prints a change summary for all significant lines from the root of
  389. file 1 up to and including 'line'. If output is enabled, adds a change bar
  390. to the text and outputs the line to the output file.
  391. ------------------------------------------------------------------------------*/
  392. added( line )
  393.    line_ptr line;
  394. {
  395.    line_ptr temp;
  396.  
  397.  trace( "added" );
  398.  for( temp = root[ 1 ].link; ; )
  399.  {
  400.   if( temp == NULL )
  401.    ret
  402.   if( !dont_look( temp ) )
  403.    fprintf( msg, "+%d:%d -> %s", temp->pagenum, 
  404.       temp->linenum, temp->text );
  405.   if( output )
  406.    if( dont_look( temp ) )
  407.     vfputs( temp->text, outfile );
  408.    else
  409.     vfputs( change_bar( temp->text ), outfile );
  410.   if( temp == line )
  411.    ret
  412.   temp = temp->link;
  413.  }
  414. }
  415.  
  416. /*------------------------------------------------------------------------------
  417. DELETED - outputs a change summary for all lines in file 2 from the root up to
  418. and including 'line'.
  419. ------------------------------------------------------------------------------*/
  420. deleted( line )
  421.    line_ptr line;
  422. {
  423.    line_ptr temp;
  424.  
  425.  trace( "deleted" );
  426.  for( temp = root[ 2 ].link; ; )
  427.  {
  428.   if( temp == NULL )
  429.    ret
  430.   if( !dont_look( temp ) )
  431.    fprintf( msg, "-%d:%d -> %s", temp->pagenum,
  432.       temp->linenum, temp->text );
  433.   if( temp == line )
  434.    ret
  435.   temp = temp->link;
  436.  }
  437.  ret;
  438. }
  439.  
  440. /*------------------------------------------------------------------------------
  441. RESYNC - resynchronizes file 1 and file 2 after a difference is detected, and
  442. outputs changed lines and change summaries via added() and deleted(). Exits
  443. with the file inputs pointing at the next two lines that match, unless
  444. it is impossible to sync up again, in which case all lines in file 1 are
  445. printed via added(). Deallocates all lines printed by this function.
  446. ------------------------------------------------------------------------------*/
  447. resync( first, second )
  448.    line_ptr first, second;
  449. {
  450.    line_ptr file1_start, file2_start, last_bad1, last_bad2, t1, t2;
  451.    int i, j ,k, moved1, moved2;
  452.  
  453.  trace( "resync" );
  454.  
  455.  moved1 = 0;
  456.  file1_start = first;
  457.  
  458.  position( 1, first );
  459.  for( k = 0; k < lookahead; k++ )
  460.  {
  461.   while( dont_look( file1_start = next_line( 1 ) ) );
  462.   if( file1_start == NULL ) goto no_sy;
  463.  
  464.   moved2 = 0;
  465.   file2_start = second;
  466.  
  467.   position( 2, second );
  468.   for( j = 0; j < lookahead ; j++ )
  469.   {
  470.    while( dont_look( file2_start = next_line( 2 ) ) );
  471.    if( file2_start == NULL ) goto eof2;
  472.    
  473.    t1 = file1_start;
  474.    t2 = file2_start;
  475.    for( i = 0; (i < re_sync) && equal( t1, t2 ); i++ )
  476.    {
  477.     while( dont_look( t1 = next_line( 1 ) ) );
  478.     while( dont_look( t2 = next_line( 2 ) ) );
  479.     if( (t1 == NULL) || (t2 == NULL) )
  480.      break;
  481.    }
  482.    if( i == re_sync ) goto synced;
  483.  
  484.    last_bad2 = file2_start;
  485.    position( 2, file2_start );
  486.    while( dont_look( file2_start = next_line( 2 ) ) );
  487.    moved2 ++;
  488.   }
  489. eof2:
  490.   last_bad1 = file1_start;
  491.   position( 1, file1_start );
  492.   while( dont_look( file1_start = next_line( 1 ) ) );
  493.   moved1++;
  494.  }
  495.  printf( "\n*** ERROR - lost sync in file %s at page %d line %d",
  496.   infile_name[ 1 ], first->pagenum, first->linenum );
  497.  fclose( outfile );
  498.  exit( 2 );
  499. no_sy:
  500.  position( 1, first );
  501.  while( (first = next_line( 1 )) != NULL )
  502.  {
  503.   added( first );
  504.   discard( 1, first );
  505.  } 
  506.  ret;
  507. synced:
  508.  if( moved1 )
  509.  {
  510.   added( last_bad1 );
  511.   discard( 1, last_bad1 );
  512.  }
  513.  position( 1, file1_start );
  514.  if( moved2 )
  515.  {
  516.   deleted( last_bad2 );
  517.   discard( 2, last_bad2 );
  518.  }
  519.  position( 2, file2_start );
  520.  fprintf( msg, "\n" );
  521.  ret;
  522. }
  523.  
  524. /*------------------------------------------------------------------------------
  525. DIFF - differencing executive. Prints and deallocates all lines up to where
  526. a difference is detected, at which point resync() is called. Exits on end
  527. of file 1.
  528. ------------------------------------------------------------------------------*/
  529. diff()
  530. {
  531.    line_ptr first, second;
  532.  
  533.  trace( "diff" );
  534.  for( ;; )
  535.  {
  536.   while( dont_look( first = next_line( 1 ) ) );
  537.   if( first == NULL )
  538.   {
  539.    put( first );
  540.    ret;
  541.   }
  542.   while( dont_look( second = next_line( 2 ) ) );
  543.   if( equal( first, second ) )
  544.   {
  545.    put( first );
  546.    discard( 1, first );
  547.    discard( 2, second );
  548.   }
  549.   else
  550.    resync( first, second );
  551.   if( second == NULL )
  552.    ret
  553.  }
  554. }
  555.  
  556. /*------------------------------------------------------------------------------
  557. PAGE_SKIP - skips the first 'skip1' pages of file 1, and then the first 'skip2'
  558. pages of file 2. This is useful to jump over tables of contents, etc.
  559. ------------------------------------------------------------------------------*/
  560. page_skip()
  561. {
  562.    line_ptr first, second;
  563.  
  564.  trace( "page_skip" );
  565.  for( ; ; )
  566.  {
  567.   first = next_line( 1 );
  568.   if( (first == NULL) || (first->pagenum > skip1) )
  569.    break;
  570.   put( first );
  571.   discard( 1, first );
  572.  }
  573.  if( first != NULL )
  574.   position( 1, first );
  575.  for( ; ; )
  576.  {
  577.   second = next_line( 2 );
  578.   if( (second == NULL) || (second->pagenum > skip2) )
  579.    break;
  580.   discard( 2, second );
  581.  }
  582.  if( second != NULL )
  583.   position( 2, second );
  584.  ret;
  585. }
  586.  
  587. /*------------------------------------------------------------------------------
  588. HELP - outputs usage information.
  589. ------------------------------------------------------------------------------*/
  590. help()
  591. {
  592.  printf( "\nDIFF" );
  593.  printf( "\nText File Differencer and Change Barrer" );
  594.  printf( "\n" );
  595.  printf( "\nFormat:" );
  596.  printf( "\nDIFF [option{option}] newfile oldfile [barfile]" );
  597.  printf( "\n" );
  598.  printf( "\n   newfile = latest revision of text file" );
  599.  printf( "\n   oldfile = baseline to compare against" );
  600.  printf( "\n   barfile = output file if changebars are desired" );
  601.  printf( "\n" );
  602.  printf( "\nOptions:" );
  603. #ifdef TRACER_FUNCTIONS
  604.  printf( "\n   /TRACE       Makes a mess of the display and runs real s\
  605. low" );
  606.  printf( "\n                default = trace off" );
  607.  printf( "\n" );
  608. #endif
  609.  printf( "\n   /BAR_COL=n   Column of output file in which change bar w\
  610. ill appear" );
  611.  printf( "\n                default = 78" );
  612.  printf( "\n" );
  613.  printf( "\n   /TOP_SKIP=n  Lines at top of page to skip for running he\
  614. ads & page nos." );
  615.  printf( "\n                default = 0" );
  616.  printf( "\n" );
  617.  printf( "\n   /BOT_SKIP=n  Lines at botom of page to skip for running \
  618. foots and page nos." );
  619.  printf( "\n                default = 0" );
  620.  printf( "\n" );
  621.  printf( "\n   /PAGE_LEN=n  Lines per page (embedded formfeeds overrrid\
  622. e)" );
  623.  printf( "\n                default = 66" );
  624.  printf( "\n" );
  625.  printf( "\n   /UP_CASE     Upper/Lower case is significant/is not sign\
  626. ificant" );
  627.  printf( "\n   /NOUP_CASE   default" );
  628.  printf( "\n" );
  629.  printf( "\n   /RE_SYNC=n   Lines that must match before files are cons\
  630. idered synced" );
  631.  printf( "\n                after differences are found - default = 5" );
  632.  printf( "\n" );
  633.  printf( "\n   /OUTPUT=file File to redirect differences summary to. " );
  634.  printf( "\n                default = SYS$OUTPUT or console." );
  635.  printf( "\n" );
  636.  printf( "\n   /BLANKS      Blank lines are considered significant" );
  637.  printf( "\n   /NOBLANKS    default" );
  638.  printf( "\n" );
  639.  printf( "\n   /LOOKAHEAD=n Lines to look ahead in each file to resync \
  640. after difference" );
  641.  printf( "\n                default = 200" );
  642.  printf( "\n" );
  643.  printf( "\n   /SKIP1=n     Pages in NEWFILE to skip before compare. Al\
  644. so sets /SKIP2" );
  645.  printf( "\n                default = 0" );
  646.  printf( "\n" );
  647.  printf( "\n   /SKIP2=n     Pages in OLDFILE to skip before compare. Mu\
  648. st be after /SKIP1" );
  649.  printf( "\n                default = 0" );
  650.  printf( "\n" );
  651. }
  652.  
  653. /*------------------------------------------------------------------------------
  654. OPEN_FILES - opens the input and output files.
  655. ------------------------------------------------------------------------------*/
  656. open_files()
  657. {
  658.    int i;
  659.  
  660.  trace( "open_files" );
  661.  for( i = 1; i < 3; i++ )
  662.   if( (infile[ i ] = fopen( infile_name[ i ], "r")) == NULL )
  663.   {
  664.    printf( "\nError: Can't open %s", infile_name[ i ] );
  665.    command_errors++;
  666.   }
  667.  if( files > 2 )
  668.   if( (outfile = fopen( outfile_name, "w" )) == NULL )
  669.   {
  670.    printf( "\nError: Can't create %s", outfile_name );
  671.    command_errors++;
  672.   }
  673.  ret;
  674. }
  675.  
  676. /*------------------------------------------------------------------------------
  677. REDIRECT - performs output redirection under VAX 11 VMS.
  678. ------------------------------------------------------------------------------*/
  679. redirect( str )
  680.    char *str;
  681. {
  682.    char filename[ 132 ], *ptr, *dest;
  683.  
  684.  trace( "redirect" );
  685.  dest = filename;
  686.  if( (ptr = index( str, '=' ) + 1) == (char *)(NULL + 1) )
  687.  {
  688.   printf( "\nERROR in option %s", str );
  689.   command_errors++;
  690.  }
  691.  while( (*ptr != OPT_FLAG) && ((*(dest++) = *(ptr++)) != '\0') );
  692.  *dest = '\0';
  693.  if( (msg = fopen( filename, "w" )) == NULL )
  694.  {
  695.   printf( "\nERROR creating %s", filename );
  696.   command_errors++;
  697.  }
  698.  ret;
  699.  
  700. /*------------------------------------------------------------------------------
  701. STRIP_OPT - processes each command line option.
  702. ------------------------------------------------------------------------------*/
  703. strip_opt( str )
  704.    char *str;
  705. {
  706.  trace( "strip_opt" );
  707.  upper( str );
  708.  if( str[ 0 ] == OPT_FLAG )
  709.  {
  710.   if( match( str + 1, "BAR_COL" ) )
  711.    bar_col = num( str );
  712.   else if( match( str + 1, "TOP_SKIP" ) )
  713.    top_skip = num( str );
  714.   else if( match( str + 1, "BOT_SKIP" ) )
  715.    bot_skip = num( str );
  716.   else if( match( str + 1, "PAGE_LEN" ) )
  717.    page_len = num( str );
  718.   else if( match( str + 1, "UP_CASE" ) )
  719.    up_case = 1;
  720.   else if( match( str + 1, "NOUP_CASE" ) )
  721.    up_case = 0;
  722.   else if( match( str + 1, "RE_SYNC" ) )
  723.    re_sync = num( str );
  724.   else if( match( str + 1, "BLANKS" ) )
  725.    blanks = 1;
  726.   else if( match( str + 1, "NOBLANKS" ) )
  727.    blanks = 0;
  728.   else if( match( str + 1, "LOOKAHEAD" ) )
  729.    lookahead = num( str );
  730.   else if( match( str + 1, "SKIP1" ) )
  731.    skip1 = skip2 = num( str );
  732.   else if( match( str + 1, "SKIP2" ) )
  733.    skip2 = num( str );
  734. #ifdef TRACER_FUNCTIONS
  735.   else if( match( str + 1, "TRACE" ) )
  736.    trace_enabled = debug = 1;
  737. #endif 
  738.   else if( match( str + 1, "OUTPUT" ) )
  739.    redirect( str );
  740.   else
  741.   {
  742.    printf( "\nUnrecognized Option: %s", str );
  743.    command_errors++;
  744.   }
  745.  }
  746.  else
  747.  {
  748.   switch( files ) 
  749.   {
  750.    case 0:
  751.     strcpy( infile_name[ 1 ], str );
  752.     break;
  753.    case 1:
  754.     strcpy( infile_name[ 2 ], str );
  755.     break;
  756.    case 2:
  757.     strcpy( outfile_name, str );
  758.     output = 1;
  759.     break;
  760.    default:
  761.     printf( "\nError: Too many files at %s", str );
  762.     command_errors++;
  763.     break;
  764.   }
  765.   files++;
  766.  }
  767.  if( index( str + 1, OPT_FLAG ) != NULL )
  768.   strip_opt( index( str + 1, OPT_FLAG ) );
  769.  ret;
  770. }
  771.  
  772. /*------------------------------------------------------------------------------
  773. UPPER - converts the string 'str' to upper case.
  774. ------------------------------------------------------------------------------*/
  775. upper( str )
  776.    char *str;
  777. {
  778.  trace( "upper" );
  779.  for( ; ; )
  780.  {
  781.   if( *str == '\0' )
  782.    ret
  783.   *str = toupper( *str );
  784.   str++;
  785.  }
  786. }
  787.  
  788. /*------------------------------------------------------------------------------
  789. MATCH - looks for a match of 'str' with the first (strlen( str) ) characters
  790. of 'pattern'. Returns 0 for no match, nonzero on match.
  791. ------------------------------------------------------------------------------*/
  792. int match( str, pattern )
  793.    char *str, *pattern;
  794. {
  795.  trace( "match" );
  796.  for( ; ; )
  797.  {
  798.   if( *str != *pattern )
  799.    ret_val( 0 )
  800.   str++;
  801.   pattern++;
  802.   if( *pattern == '\0' )
  803.    ret_val( 1 )
  804.   if( *str == '\0' )
  805.    ret_val( 1 )
  806.   if( *str == '=' )
  807.    ret_val( 1 )
  808.  }
  809. }
  810.  
  811. /*------------------------------------------------------------------------------
  812. NUM - returns the integer associated with a command line option. An equal
  813. sign must appear in the option.
  814. ------------------------------------------------------------------------------*/
  815. int num( str )
  816.    char *str;
  817. {
  818.  trace( "num" );
  819.  if( index( str, '=' ) == NULL )
  820.   ret_val( 0 )
  821.  else
  822.   ret_val( atoi( index( str, '=' ) + 1 ) )
  823. }
  824.  
  825. #ifdef TRACER_FUNCTIONS
  826.  
  827. char_ptr names[ 20 ];
  828. int stack = 0;
  829.  
  830. callstack( str )
  831.    char *str;
  832. {
  833.    int i;
  834.    char c;
  835.  
  836.  names[ stack++ ] = str;
  837.  if( debug )
  838.  {
  839.   for( i = 0; i < stack; i++ )
  840.    printf( "   " );
  841.   printf( "Entering %s\n", str );
  842.  }
  843. #ifndef VAX11C
  844.  if( trace_enabled && kbhit() )
  845.  {
  846.   switch( getch() )
  847.   {
  848.    case 't':
  849.    case 'T':
  850.     debug = !debug;
  851.     break;
  852.    case 's':
  853.    case 'S':
  854.     printf( "\n-----------" );
  855.     for( i = stack - 1; i >= 0; i-- )
  856.      printf( "\n%s", names[ i ] );
  857.     printf( "\n-----------\n" );
  858.     break;
  859.    default:
  860.     break;
  861.   }
  862.  }
  863. #endif
  864. }
  865.  
  866. callpop()
  867. {
  868.    int i;
  869.  if( debug )
  870.  {
  871.   for( i = 0; i < stack; i++ )
  872.    printf( "   " );
  873.   printf( "Exiting  %s\n", names[ stack ] );
  874.  }
  875.  stack--;
  876. }
  877.  
  878. #endif
  879.