home *** CD-ROM | disk | FTP | other *** search
/ Phoenix CD 2.0 / Phoenix_CD.cdr / 15a / diff24.zip / DIFF23.C < prev    next >
C/C++ Source or Header  |  1988-08-10  |  19KB  |  757 lines

  1. y
  2.  
  3. DIFF23.ARC: Extract/view member filespec: (Enter)=*.*? 
  4.  
  5. File Name     Length    Date      Time    (Enter) or (S)kip, (V)iew, (X)tract
  6. ---------     ------   ------    ------   -----------------------------------
  7. DIFF.C         18560  08-02-88  19:12:54  Action? v [View]
  8.  
  9. /*----------------------------------------------------------------------------
  10.  
  11.     diff.c -- file compare and change bar inserter for text files
  12.     originally produced by D Krantz, modified for Turbo C by P van Es
  13.     date 4/19/88
  14.  
  15.         5/2/88  v1.1    options have been UNIX-fied
  16.     5/4/88  v1.2    comparisons made faster
  17.             bugs corrected
  18.             upper case handling rationalised
  19.     6/14/88 v2.0    automatic resync feature added
  20.     6/18/88 v2.1    screen listing switchable
  21.     6/20/88 v2.2    resync made faster for small changes
  22.     6/21/88 v2.3    resync checksum lookup added
  23.  
  24.   ---------------------------------------------------------------------------*/
  25.  
  26. /* file difference utility */
  27.  
  28. #if defined (__COMPACT__)
  29. #else
  30. #error Must be compiled with COMPACT model
  31. #endif
  32. More: (Enter) or (Y)es, (N)o, (NS)non-stop?                                              
  33. #include <ctype.h>
  34. #include <stdio.h>
  35.  
  36. typedef unsigned char    byte;    /* define type byte, vals 0-255 */
  37. #define OPT_FLAG  '-'        /* command line option switch recogniser */
  38. #define MAXLINE   83        /* maximum characters in the input line */
  39. #define FORMFEED  'L'-'@'
  40. #define EOF_REACHED    0    /* eof during resync */
  41. #define RE_SYNCED    1    /* we managed to re-sync */
  42. #define NO_SYNC        2    /* no sync but no eof either */
  43.  
  44. struct LINE {            /* structure defining a line internally */
  45.     int linenum;        /* what line on page */
  46.     int pagenum;        /* what page */
  47.     byte checksum;        /* use a checksum for quick comparison */
  48.     struct LINE *prev;    /* pointer to previous line */
  49.     struct LINE *link;    /* linked list pointer */
  50.     struct LINE *same;    /* pointer to next line with checksum */
  51.     char text [MAXLINE];    /* text of line */
  52. };
  53.  
  54. typedef struct LINE *line_ptr;
  55. More: (Enter) or (Y)es, (N)o, (NS)non-stop? ns                                               typedef char *char_ptr;
  56. typedef FILE *FILE_PTR;
  57. struct LINE root[3];        /* root of internal linked list */
  58. int line_count[3] = {1,1,1};    /* file's line counter */
  59. int page_count[3] = {1,1,1};    /* file's page counter */
  60. int command_errors = 0;        /* number of command line errors */
  61. char xx1[132], xx2[132];    /* space for file names */
  62. int files = 0;            /* nr of files in command line */
  63. char_ptr infile_name[3] = {NULL,xx1,xx2};
  64. char outfile_name[132];        /* output file name */
  65. FILE_PTR infile[3];        /* input file pointers */
  66. FILE *outfile;            /* changebarred output file ptr */
  67.  
  68. static line_ptr at[3] = {NULL, &(root[1]), &(root[2])};
  69. static line_ptr same_check [256] [2];
  70.  
  71. int debug = 0;            /* trace switch */
  72. int trace_enabled = 0;        /* keyboard tracing switch */
  73. int bar_col = 78;        /* column for change bar */
  74. int top_skip = 0;        /* lines to skip at top of page */
  75. int bot_skip = 0;        /* lines to skip at bottom of page */
  76. int page_len = 66;        /* length of a page */
  77. int up_case = 0;        /* boolean - ignore case */
  78. int re_sync = 3;        /* lines that must match for resync */
  79. int output = 0;            /* boolean - is change barred outfile on */
  80. int blanks = 0;            /* boolean - are blank lines significant */
  81. int skip1 = 0;            /* pages in first file to skip */
  82. int skip2 = 0;            /* pages in second file to skip */
  83. int scr_on = 1;            /* screen listing turned on */
  84.  
  85. #if 0 /* tracing and other debug functions turned off */
  86.  
  87. #define trace( x )    callstack( x )
  88. #define ret        { callpop(); return; }
  89. #define ret_val( x )    { callpop(); return( x ); }
  90. #define TRACER_FUNCTIONS
  91.  
  92. #else
  93.  
  94. #define trace( x )    /* nothing */
  95. #define ret        { return; }
  96. #define ret_val( x )    { return( x ); }
  97.  
  98. #endif
  99.  
  100. /*--------------------------------------------------------------------------*/
  101. main (argc, argv)
  102.     int argc;
  103.     char *argv[];
  104. {
  105.     int i;
  106.     trace ( "main" );
  107.     if (argc == 1)
  108.         help();
  109.     else
  110.         printf ("diff -- version 2.3 6/21/88\n");
  111.  
  112.     strip_opt (argc, argv);
  113.     if (files<2) {
  114.         printf ("\nError: Must specify two files");
  115.         exit (2);
  116.     }
  117.     open_files();
  118.     if (command_errors)
  119.         exit (2);
  120.  
  121.     /* set root previous pointers to 0 */
  122.     at[1]->prev = NULL;
  123.     at[2]->prev = NULL;
  124.  
  125.     page_skip();
  126.     diff();
  127.     ret;
  128. }
  129.  
  130. /* dont_look - tells us whether or not this line should be considered for
  131.    comparison or is a filler (eg header, blank) line */
  132.  
  133. dont_look (line)
  134.     line_ptr line;
  135. {
  136.     int i;
  137.     trace ("dont_look");
  138.     if (line == NULL)    /* EOF reached */
  139.         ret_val(NULL);
  140.     if (line->linenum <= top_skip)
  141.         ret_val(1);
  142.     if (line->linenum > page_len - bot_skip)
  143.         ret_val(1);
  144.     if (!blanks)
  145.     {
  146.         for (i=0; i<MAXLINE; i++)
  147.             switch (line->text[i])
  148.             {
  149.                 case '\0':
  150.                 case '\n': ret_val(1);
  151.                 case '\t':
  152.                 case ' ':  break;
  153.                 default:   ret_val(0);
  154.             }
  155.            }
  156.     ret_val(0);
  157. }
  158.  
  159. /* equal - tells us if the pointers 'a' and 'b' point to line buffers
  160.    containing equivalent text or not */
  161.  
  162. equal (a,b)
  163.     line_ptr a,b;
  164. {
  165.     trace ("equal");
  166.     if ((a==NULL) || (b==NULL))
  167.         ret_val(0);
  168.     if (a->checksum != b->checksum)
  169.         ret_val(0);
  170.     if (up_case)
  171.         ret_val (!strcmp (a->text, b->text))
  172.     else
  173.         /* this function ignores case on comparison */
  174.         ret_val (!stricmp (a->text, b->text))
  175. }
  176.  
  177. /* position - moves the input pointer for file 'f' such that the next line to
  178.    be read will be 'where' */
  179.  
  180. position (f,where)
  181.     int f;
  182.     line_ptr where;
  183. {
  184.     trace ("position");
  185.     at[f] = &root[f];
  186.     if (where == NULL)
  187.         ret;
  188.     while (at[f]->link != where)
  189.         at[f] = at[f]->link;
  190.     ret;
  191. }
  192.  
  193.  
  194. /* checksum - calculates a simple checksum for the line, and stores it in
  195.    the line buffer.  This allows for faster comparisons */
  196.  
  197. checksum (a)
  198.     line_ptr a;
  199. {
  200.     int i;
  201.     a->checksum = 0;
  202.     for (i=0; a->text[i] != NULL; i++) {
  203.         if (up_case)
  204.             a->checksum ^= a->text[i];
  205.         else
  206.             /* ignore case */
  207.             a->checksum ^= toupper (a->text[i]);
  208.     }
  209. }
  210.  
  211.  
  212. /* next_line - allocates, links and returns the next line from file 'f' if
  213.    no lines are buffered, otherwise returns the next buffered line from 'f'
  214.    and updates the link pointer to the next buffered line; it also inserts
  215.    the line in the correct place in the array same_check */
  216.  
  217. line_ptr next_line (f)
  218.     int f;
  219. {
  220.     char *malloc();
  221.     line_ptr place_hold, start;
  222.  
  223.     trace ("next_line");
  224.     if (at[f]->link != NULL) {
  225.         at[f] = at[f]->link;
  226.         ret_val (at[f]);
  227.     }
  228.     else {
  229.         at[f]->link = (line_ptr) malloc (sizeof(struct LINE));
  230.         if (at[f]->link == NULL) {
  231.             printf ("\nError: Out of Memory");
  232.             exit (2);
  233.         }
  234.         place_hold = at[f];
  235.         at[f] = at[f]->link;
  236.         if (place_hold == &(root[f]))
  237.             at[f]->prev = NULL;
  238.         else
  239.             at[f]->prev = place_hold;
  240.         at[f]->link = NULL;
  241.         at[f]->same = NULL;
  242.         if (fgets (at[f]->text, MAXLINE, infile[f]) == NULL) {
  243.             free (at[f]);
  244.             at[f] = place_hold;
  245.             at[f]->link = NULL;
  246.             at[f]->same = NULL;
  247.             ret_val (NULL)
  248.         }
  249.         /* calculate a checksum for the new line of text */
  250.         checksum (at[f]);
  251.  
  252. #ifdef EMBEDDED_FORMFEEDS
  253.         if ((strchr (at[f]->text, FORMFEED) != NULL) ||
  254.             (line_count[f] > page_len))
  255. #else
  256.         if ((*(at[f]->text) == FORMFEED) ||
  257.             (line_count[f] > page_len))
  258. #endif
  259.         {
  260.             page_count[f]++;
  261.             line_count[f]=1;
  262.         }
  263.         at[f]->linenum = line_count[f]++;
  264.         at[f]->pagenum = page_count[f];
  265.  
  266.         /* insert it in the correct place in the array unless it is
  267.            a dont_look line */
  268.         if (!dont_look (at[f])) {
  269.              start = same_check [at[f]->checksum][f-1];
  270.             if (start == NULL)
  271.                 same_check [at[f]->checksum][f-1] = at[f];
  272.             else {
  273.                 while (start->same != NULL)
  274.                     start = start->same;
  275.                 /* start is NULL now, insert at[f] here */
  276.                 start->same = at[f];
  277.             }
  278.         }
  279.  
  280.         ret_val (at[f]);
  281.     }
  282. }
  283.  
  284. /* discard - deallocates all buffered lines from the root up to and inclu-
  285.    ding 'to' for file 'f', including from same_check */
  286.  
  287. discard (f,to)
  288.     int f;
  289.     line_ptr to;
  290. {
  291.     line_ptr temp;
  292.     trace ("discard");
  293.     for (;;) {
  294.         if (root[f].link == NULL || to == NULL)
  295.             break;
  296.  
  297.         temp = root[f].link;
  298.         /* ok the line exists, now find the record in same_check */
  299.         if (!dont_look (temp)) {
  300.             /* replace with temp->same */
  301.             same_check [temp->checksum][f-1] = temp->same;
  302.         }
  303.  
  304.         root[f].link = root[f].link->link;
  305.         root[f].link->prev = NULL;
  306.         free (temp);
  307.         if (temp == to)
  308.             break;
  309.     }
  310.     at[f] = &root[f];
  311.     ret;
  312. }
  313.  
  314. /* put - if change barred output file is turned on, prints all lines from
  315.    the root of file 1 up to and including 'line'.  This is only called if
  316.    a match exists for each significant line in file 2 */
  317.  
  318. put (line)
  319.     line_ptr line;
  320. {
  321.     line_ptr temp;
  322.     trace ("put");
  323.     if (output)
  324.         for (temp = root[1].link; ;) {
  325.             if (temp == NULL)
  326.                 ret
  327.             fputs (temp->text, outfile);
  328.             if (temp == line)
  329.                 ret
  330.             temp = temp->link;
  331.         }
  332.     ret;
  333. }
  334.  
  335. /* change_bar - inserts a change bar into the text pointed to by 'str'
  336.    and returns a pointer to 'str' */
  337.  
  338. char *change_bar (str)
  339.     char *str;
  340. {
  341.     int i;
  342.     char temp [MAXLINE+1], *dest, *base;
  343.     trace ("change_bar");
  344.     base = str;
  345.     dest = temp;
  346.     i = 0;
  347.     if (bar_col != 0) {
  348.         for (i=0; *str != '\n'; i++) {
  349.             if ((*str == '\r') && (*(str+1) != '\n'))
  350.                 i = 0;
  351.             *(dest++) = *(str++);
  352.         }
  353.         while (i++ < bar_col)
  354.             *(str)++ = ' ';
  355.         strcpy (str, "|\n");
  356.     }
  357.     else {
  358.         if (str[0] != ' ') {
  359.             strcpy (temp,str);
  360.             strcpy (str+1,temp);
  361.         }
  362.         str[0] = '|';
  363.     }
  364.     ret_val (base);
  365. }
  366.  
  367. /* added - prints a change summary for all significant lines from the root
  368.    of file 1 up to and including 'line'.  If output is enabled, adds a
  369.    change bar to the text and outputs the line to the output file */
  370.  
  371. added (line)
  372.     line_ptr line;
  373. {
  374.     line_ptr temp;
  375.     trace ("added");
  376.     for (temp = root[1].link; ;) {
  377.         if (temp == NULL)
  378.             ret
  379.         if (!dont_look (temp) && scr_on)
  380.             printf ("%.3d:%.2d< %s", temp->pagenum,
  381.                 temp->linenum, temp->text);
  382.         if (output)
  383.             if (dont_look (temp))
  384.                 fputs (temp->text, outfile);
  385.             else
  386.                 fputs (change_bar (temp->text), outfile);
  387.         if (temp == line)
  388.             ret
  389.         temp = temp->link;
  390.     }
  391. }
  392.  
  393. /* deleted - outputs a change summary for all lines i file 2 from the root
  394.    up to and including 'line' */
  395.  
  396. deleted (line)
  397.     line_ptr line;
  398. {
  399.     line_ptr temp;
  400.     trace ("deleted");
  401.     for (temp = root[2].link; ;) {
  402.         if (temp == NULL)
  403.             ret
  404.         if (!dont_look (temp) && scr_on)
  405.             printf ("%.3d:%.2d> %s", temp->pagenum,
  406.                 temp->linenum, temp->text);
  407.         if (temp == line)
  408.             ret
  409.         temp = temp->link;
  410.     }
  411. }
  412.  
  413. /* resync - resynchronizes file 1 and file 2 after a difference is detected
  414.    and outputs changed lines and change summaries via added() and deleted().
  415.    Exits with the file inputs pointing at the next two lines that match,
  416.    unless it is impossible to sync up again, in which case all lines in file 1
  417.    are printed via added().  Deallocates lines printed by this function. */
  418.  
  419. resync (first, second,lookahead)
  420.     line_ptr first, second;
  421.     int lookahead;
  422. {
  423.     line_ptr file1_start, file2_start, last_bad1, last_bad2, t1, t2,
  424.         check;
  425.     int i, j, k, moved1, moved2;
  426.     trace ("resync");
  427.  
  428.     /* first ensure sufficient lines in memory */
  429.     position (2, second);
  430.         last_bad2 = second;
  431.     for (k=0; k<lookahead && last_bad2 != NULL ; k++)
  432.         last_bad2 = next_line(2);
  433.  
  434.     /* now reset file pointers */
  435.     moved1 = 0;
  436.     file1_start = first;
  437.     position (1,first);
  438.  
  439.     for (k=0; k < lookahead; k++) {
  440.         while (dont_look (file1_start = next_line(1)));
  441.         if (file1_start == NULL) goto no_sy;
  442.         moved2 = 0;
  443.  
  444.         /* now see if there is a matching line at all */
  445.         check = same_check [file1_start->checksum][1];
  446.         while (check != NULL) {
  447.             /* look for matching entries */
  448.             while (!equal (check, file1_start)) {
  449.                 check = check->same;
  450.                 if (check == NULL)
  451.                     break;
  452.             }
  453.  
  454.             if (check != NULL) {
  455.                 /* ok we have a hit */
  456.                 t1 = file1_start;
  457.                 file2_start = check;
  458.                 t2 = file2_start;
  459.                 position (1, file1_start);
  460.                 position (2, file2_start);
  461.                 for (i=0; (i<re_sync) && equal (t1,t2); i++) {
  462.                     while (dont_look (t1 = next_line (1)));
  463.                     while (dont_look (t2 = next_line (2)));
  464.                     if ((t1 == NULL) || (t2 == NULL))
  465.                         break;
  466.                 }
  467.                 if (i == re_sync) {
  468.                     moved2++;
  469.                     if ((last_bad2 = file2_start->prev) == NULL)
  470.                         moved2 = 0;
  471.                     goto synced;
  472.                 }
  473.                 /* get next entry */
  474.                 check = check->same;
  475.             } /* if check != NULL */
  476.         } /* end of while check != NULL */
  477.  
  478.         /* else no sync, no matching entries yet, loop the for list */
  479.         last_bad1 = file1_start;
  480.         position (1, file1_start);
  481.         while (dont_look (file1_start = next_line (1)));
  482.         moved1++;
  483.  
  484.     } /* for each line in file 1 lookahead */
  485.         ret_val (NO_SYNC);
  486.  
  487. no_sy:
  488.     position (1,first);
  489.     while ((first = next_line(1)) != NULL) {
  490.         added (first);
  491.         discard (1, first);
  492.     }
  493.     ret_val (EOF_REACHED);
  494.  
  495. synced:
  496.     if (moved1) {
  497.         added (last_bad1);
  498.         discard (1, last_bad1);
  499.     }
  500.     position (1, file1_start);
  501.     if (moved2) {
  502.         deleted (last_bad2);
  503.         discard (2, last_bad2);
  504.     }
  505.     position (2, file2_start);
  506.     if (scr_on)
  507.         printf ("\n");
  508.     ret_val (RE_SYNCED);
  509. }
  510.  
  511. /* diff - differencing executive.  Prints and deallocates all lines up to
  512.    where a difference is detected, at which point resync() is called.  Exits
  513.    on end of file 1 */
  514.  
  515. diff()
  516. {
  517.     int look_lines, result;
  518.     line_ptr first, second;
  519.  
  520.     trace ("diff");
  521.     for (;;) {
  522.         while (dont_look (first = next_line (1)));
  523.         if (first == NULL) {
  524.             put (first);
  525.             ret;
  526.         }
  527.         while (dont_look (second = next_line (2)));
  528.         if (equal (first, second)) {
  529.             put (first);
  530.             discard (1,first);
  531.             discard (2,second);
  532.         }
  533.         else {
  534.             look_lines = 10;   /* start with 10 lines look-ahead */
  535.             result = NO_SYNC;
  536.                         while (result == NO_SYNC) {
  537.                 result = resync (first, second, look_lines);
  538.                 look_lines *= 2;
  539.                 /* when look_lines reaches 80, assume a large
  540.                    difference and set it to 400 */
  541.                 if (look_lines == 80)
  542.                     look_lines = 400;
  543.             }
  544.         }
  545.  
  546.         if (second == NULL)
  547.             ret
  548.     }
  549. }
  550.  
  551. /* page_skip - skips the first 'skip1' pages of file 1, and then the first
  552.    'skip2' pages of file 2.  This is useful to jump over tables of contents */
  553.  
  554. page_skip()
  555. {
  556.     line_ptr first, second;
  557.     trace ("page_skip");
  558.     for (;;) {
  559.         first = next_line (1);
  560.         if ((first == NULL) || (first->pagenum > skip1))
  561.             break;
  562.         put (first);
  563.         discard (1, first);
  564.     }
  565.     if (first != NULL)
  566.         position (1, first);
  567.     for (;;) {
  568.         second = next_line (2);
  569.         if ((second == NULL) || (second->pagenum > skip2))
  570.             break;
  571.         discard (2,second);
  572.     }
  573.     if (second != NULL)
  574.         position (2,second);
  575.     ret;
  576. }
  577.  
  578. /* help - outputs usage information */
  579. help()
  580. {
  581.     printf ("\ndiff - text file differencer and change barrer"
  582.         "\nusage: diff [option{option}] newfile oldfile [barfile]"
  583.         "\n"
  584.         "\noptions:");
  585. #ifdef TRACER_FUNCTIONS
  586.     printf ("\n   -t   trace operation, default off");
  587. #endif
  588.     printf ("\n   -b n column of barfile for change bar, default 78"
  589.         "\n   -h n lines at top of page to skip for headers, default 0"
  590.         "\n   -f n lines at bottom of page to skip for footers, default = 0"
  591.         "\n   -p n lines per page (embedded form feeds override) default = 66"
  592.         "\n   -c   uppercase/lowercase is significant (default is off)"
  593.         "\n   -r n lines that must match before files are considered synced"
  594.         "\n        after differences are found. default = 3"
  595.         "\n   -w   blank lines are considered significant (default is not)"
  596.         "\n   -s   screen listing off (default is on)"
  597.         "\n   -n n pages in NEWFILE to skip before compare.  Also sets -o. default = 0"
  598.         "\n   -o n pages in OLDFILE to skip before compare.  Must come after -n."
  599.         "\n        default = 0");
  600.     exit (0);
  601. }
  602.  
  603. /* open_files - opens the input and the output files */
  604. open_files()
  605. {
  606.     int i;
  607.     trace ("open_files");
  608.     for (i=1; i<3; i++)
  609.         if ((infile[i] = fopen (infile_name[i], "r")) == NULL) {
  610.             printf ("\nError: can't open %s", infile_name[i]);
  611.             command_errors++;
  612.         }
  613.     if (files>2)
  614.         if ((outfile = fopen (outfile_name, "w")) == NULL) {
  615.             printf ("\nError: can't create %s", outfile_name);
  616.             command_errors++;
  617.         }
  618.     ret;
  619. }
  620.  
  621.  
  622. /* strip_opt - processes each command line option */
  623. strip_opt (ac,av)
  624.     int ac;
  625.     char *av[];
  626. {
  627.     int i;
  628.     trace ("strip_opt");
  629.     for (i=1; i<ac; i++) {
  630.         if (av[i][0] == OPT_FLAG) {
  631.             switch (av[i][1]) {
  632.                 case 'b': bar_col = atoi (av[++i]);
  633.                       break;
  634.                 case 'h': top_skip = atoi (av[++i]);
  635.                       break;
  636.                 case 'f': bot_skip = atoi (av[++i]);
  637.                       break;
  638.                 case 'p': page_len = atoi (av[++i]);
  639.                       break;
  640.                 case 'c': up_case = 1;
  641.                       break;
  642.                 case 'r': re_sync = atoi (av[++i]);
  643.                       break;
  644.                 case 'w': blanks = 1;
  645.                       break;
  646.                 case 's': scr_on = 0;
  647.                       break;
  648.                 case 'n': skip1 = skip2 = atoi (av[++i]);
  649.                       break;
  650.                 case 'o': skip2 = atoi (av[++i]);
  651.                       break;
  652. #ifdef TRACER_FUNCTIONS
  653.                 case 't': trace_enabled = debug = 1;
  654.                       break;
  655. #endif
  656.                 default:  printf ("\nUnrecognised option %s",av[i]);
  657.                       command_errors++;
  658.             } /* switch av[i][1] */
  659.         }
  660.         else {
  661.             switch (files) {
  662.                 case 0:    strcpy (infile_name[1], av[i]);
  663.                     break;
  664.                 case 1: strcpy (infile_name[2], av[i]);
  665.                     break;
  666.                 case 2: strcpy (outfile_name, av[i]);
  667.                     output = 1;
  668.                     break;
  669.                 default:
  670.                     printf ("\nError: too many files at %s",av[i]);
  671.                     command_errors++;
  672.             }
  673.             files++;
  674.         }
  675.     } /* for each command line argument */
  676.     if (!scr_on && !output) {
  677.         printf ("\nError: no output file or screen listing will be generated.");
  678.         command_errors++;
  679.     }
  680.     ret;
  681. }
  682.  
  683. #ifdef TRACER_FUNCTIONS
  684.  
  685. char_ptr names[20];
  686. int stack = 0;
  687.  
  688. callstack (str)
  689.     char *str;
  690. {
  691.     int i;
  692.     char c;
  693.  
  694.     names[stack++] = str;
  695.     if (debug) {
  696.         for (i=0; i<stack; i++)
  697.             printf ("   ");
  698.         printf ("Entering %s\n",str);
  699.     }
  700.     if (trace_enabled && kbhit()) {
  701.         switch (getch()) {
  702.             case 't':
  703.             case 'T': debug = !debug;
  704.                   break;
  705.             case 's':
  706.             case 'S': printf ("\n------------");
  707.                   for (i = stack-1; i>=0; i--)
  708.                     printf ("\n%s", names[i]);
  709.                   printf ("\n------------\n");
  710.                   printf ("free: %lu\n", coreleft());
  711.                   break;
  712.             default:  break;
  713.         }
  714.     }
  715. }
  716.  
  717. callpop()
  718. {
  719.     int i;
  720.     if (debug) {
  721.         for (i=0; i<stack; i++)
  722.             printf ("   ");
  723.         printf ("Exiting %s\n", names[stack-1]);
  724.     }
  725.     stack--;
  726. }
  727.  
  728. printentry (lp, title)
  729.     line_ptr lp;
  730.     char *title;
  731. {
  732.         if (debug) {
  733.         if (lp == NULL)
  734.             printf ("at %s: NULL entry\n");
  735.         else
  736.             printf ("at %s:  p %.3d:%.2d check %.3d\n",title,lp->pagenum,
  737.                 lp->linenum, lp->checksum);
  738.             printf ("%s",lp->text);
  739.             if (lp->link != NULL)
  740.                 printf ("next line : %s",lp->link->text);
  741.             if (lp->same != NULL)
  742.                 printf ("same line : %s",lp->same->text);
  743.             if (lp->prev != NULL)
  744.                 printf ("prev line : %s",lp->prev->text);
  745.             printf ("\n");
  746.     }
  747. }
  748.  
  749. #endif
  750.  
  751.  
  752.  
  753.  
  754.  
  755.  
  756. File Name     Length    Date      Time    (Enter) or (S)kip, (V)iew, (X)tract
  757. ----- ---- 8