home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / futils / futils~1 / src / misc1s.zoo / misc1 / combine / pass5.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-06-06  |  26.0 KB  |  1,021 lines

  1. #include <stdio.h>
  2. #include <ctype.h>
  3. #include "util.h"
  4. #include "combine.h"
  5. /*
  6.  * pass5: Output compared records
  7.  *
  8.  * The record arrays for the three files are traversed outputing the
  9.  * resultant file. An index is maintained into each of the record arrays.
  10.  * As each index is incremented the record is output. This routine merely
  11.  * determines which file the next record is to come from.
  12.  *
  13.  * Return value:
  14.  *      This procedure has no return value.
  15.  */
  16. void pass5 () {
  17.  
  18.     int     i;        /* Misc. variable */
  19.  
  20.     int     indexes[MAX_FILE_COUNT];
  21.                 /* index into each of the record arrays */
  22.  
  23.     int     j;        /* Misc. variable */
  24.  
  25.     record_type * new_rec_ptr;/* Pointer to the current record */
  26.  
  27.     record_type * old_rec_ptr;/* Pointer to the record for the previously
  28.                    current record */
  29.  
  30.     relate_type relate;    /* Relationships for the current record under
  31.                    consideration */
  32.  
  33.  
  34.     /*
  35.      * Initialize the scan.
  36.      *
  37.      * Note: for files which don't exist, the value initialized below
  38.      * needs to yield a TRUE result for the end of file comparison done
  39.      * below.
  40.      */
  41.     for (i = 0; i < MAX_FILE_COUNT; ++i) {
  42.         indexes[i] = BEGIN_INDEX + 1;/* skip the dummy begin record */
  43.     }
  44.  
  45.     /*
  46.      * For each line output.
  47.      */
  48.     for (;;) {
  49.  
  50.         /*
  51.          * For each file,
  52.          *    if the current record in the file is not involved in a
  53.          *    move operation, then the record should be dumped
  54.          *    immediately.
  55.          *
  56.          * This condition occurs for the following cirucumstances:
  57.          *    The record exists in the current position in all
  58.          *        three files.
  59.          *    The record exists in the current position in this
  60.          *        file and one of the other files. However,
  61.          *        the record does not occur anywhere in the
  62.          *        third file.
  63.          *    The record exists in the current position in this
  64.          *        file and does not occur anywhere in the other
  65.          *        two files.
  66.          */
  67.         for (i = 0; i < file_count; ++i) {
  68.  
  69.             pass5_analyze_relationship (
  70.                     i,/* file containing record */
  71.                     indexes, /* current posit. in all files */
  72.                     &relate); /* Returns file relationships */
  73.  
  74.             if (!relate.moved) {
  75.  
  76.                 if (indexes[OLD_FILE] >=
  77.                     files[OLD_FILE].record_array_size - 1 &&
  78.                     indexes[NEW1_FILE] >=
  79.                     files[NEW1_FILE].record_array_size-1 &&
  80.                     indexes[NEW2_FILE] >=
  81.                     files[NEW2_FILE].record_array_size-1) {
  82.  
  83.                     relate.relation = INSERT_EOT;
  84.                     pass5_dump_record (i, indexes, &relate);
  85.                     return;
  86.                 }
  87.  
  88.                 pass5_dump_record (i, indexes, &relate);
  89.                 goto continue_big_loop;
  90.             }
  91.  
  92.         }
  93.  
  94.         /*
  95.          * All other cases involve a move of text from one location to
  96.          * another.
  97.          *
  98.          * The 'pass5_move' routine analyses which text is the shortest text.
  99.          */
  100.  
  101.         i = pass5_move (indexes);
  102.  
  103.         pass5_analyze_relationship (
  104.                 i,      /* file containing record */
  105.                 indexes,  /* current position in all files */
  106.                 &relate); /* Returns file relationships */
  107.  
  108.         old_rec_ptr = &(files[i].record[indexes[i]]);
  109.  
  110.         for (;;) {
  111.  
  112.             pass5_dump_record (i, indexes, &relate);
  113.  
  114.             /*
  115.              * If any of the files involved in the move have now
  116.              *    reached EOT,
  117.              *     go on to process bigger and better things.
  118.              */
  119.             for (j = 0; j < file_count; ++j) {
  120.                 if (!is_hash_code (relate.index[j])) {
  121.                     relate.index[j]++;
  122.                     if (relate.index[j] + 1 >=
  123.                         files[j].record_array_size) {
  124.                         goto continue_big_loop;
  125.                     }
  126.                 }
  127.             }
  128.  
  129.             /*
  130.              * Note: this code should continue to dump records
  131.              * without re-iterating the pass5_move routine.
  132.              * Since the pass5_move routine executes in a loop,
  133.              * the above_mentioned enhancement is required to ensure
  134.              * that pass5 completes in linear time.
  135.              *
  136.              * The test below determines if the current record
  137.              * is logically next to the previous record.
  138.              * If so, the record is merely dumped.
  139.              * Note: the record relationship does not change
  140.              * for this record.
  141.              *
  142.              * A record is logically next to a previous one
  143.              * if either the value field is a hash code for
  144.              * both records or if the record index in the
  145.              * value field is one greater then that of the
  146.              * previous record.
  147.              */
  148.  
  149.             new_rec_ptr = &(files[i].record[indexes[i]]);
  150.  
  151.             for (j = 0; j < MAX_VALUE_SUB; ++j) {
  152.                 if (is_hash_code (old_rec_ptr -> value[j])) {
  153.                     if(is_hash_code(new_rec_ptr->value[j])){
  154.                         continue;
  155.                     }
  156.                 } else if (old_rec_ptr->value[j]+1 ==
  157.                        new_rec_ptr -> value[j]) {
  158.                     continue;
  159.                 }
  160.                 goto continue_big_loop;
  161.             }
  162.  
  163.             old_rec_ptr = new_rec_ptr;
  164.  
  165.         }
  166.  
  167. continue_big_loop: ;
  168.     }
  169.  
  170. }
  171. /*
  172.  * pass5_analyze_relationship -- determine relationsip to other files
  173.  *
  174.  * This routine determines the relationship between one record in one file
  175.  * and the records at the current position in the other files.
  176.  * The routine returns information regarding whether the record exists
  177.  * in each file at the current position and whether the record is involved
  178.  * in a move operation.
  179.  *
  180.  * Return value:
  181.  *      This procedure returns no value.
  182.  */
  183. void pass5_analyze_relationship (file_no, indexes, rel_ptr)
  184. int     file_no;        /* input -- file number of the file
  185.                  containing the record to be compared. */
  186.  
  187. int     indexes[MAX_FILE_COUNT];/* input -- Array of the current
  188.                    indexes into the record arrays */
  189.  
  190. relate_type * rel_ptr;        /* output -- Returns relationship between
  191.                    current record and records at the current
  192.                    position in the corresponding files. */
  193.  
  194. {
  195.  
  196.     int     i;        /* Misc. variable */
  197.  
  198.     int     power;        /* Current power of 2 */
  199.  
  200.     record_type * record_ptr;/* Pointer to the record block is the record
  201.                    currently being analyzed. */
  202.  
  203.  
  204.  
  205.     /*
  206.      * Initialize the value_index array to contain the index of the current
  207.      * record in each of the files.
  208.      *
  209.      * The record is at the current position in the current file. The record
  210.      * is in the position indicated by the 'value' field in the record entry
  211.      * for the other files.
  212.      */
  213.     record_ptr = &(files[file_no].record[indexes[file_no]]);
  214.  
  215.     rel_ptr -> index[file_no] = indexes[file_no];
  216.     rel_ptr -> index[corres_file[file_no * 2]] =
  217.         record_ptr -> value[value_sub[file_no * 2]];
  218.     rel_ptr -> index[corres_file[file_no * 2 + 1]] =
  219.         record_ptr -> value[value_sub[file_no * 2 + 1]];
  220.  
  221.     /*
  222.      * Compare the current position in each file with the index of
  223.      * this record in each file.
  224.      */
  225.     rel_ptr -> moved = FALSE;
  226.     rel_ptr -> in_all = TRUE;
  227.     rel_ptr -> relation = 0;
  228.  
  229.     power = 1;
  230.  
  231.     for (i = 0; i < file_count; ++i) {
  232.  
  233.         rel_ptr -> current[i] = (rel_ptr -> index[i] == indexes[i]);
  234.  
  235.         if (rel_ptr -> current[i]) {
  236.             rel_ptr -> relation += power;
  237.  
  238.         } else {
  239.             rel_ptr -> in_all = FALSE;
  240.             if (!is_hash_code (rel_ptr -> index[i])) {
  241.                 rel_ptr -> moved = TRUE;
  242.             }
  243.         }
  244.  
  245.         power *= 2;
  246.  
  247.     }
  248.  
  249. }
  250. /*
  251.  * pass5_dump_record: write a record for pass 5
  252.  *
  253.  * This routine writes the specified record to the output files and
  254.  * increments the appropriate indexes.
  255.  *
  256.  * Return value:
  257.  *      This routine returns no value.
  258.  */
  259. void pass5_dump_record (file_no, indexes, rel_ptr)
  260. int     file_no;        /* input -- File number of the file to
  261.                    read record from. */
  262.  
  263. int     indexes[MAX_FILE_COUNT];/* input/output -- Indexes of the current
  264.                    records. Each index is incremented as
  265.                    appropriate */
  266.  
  267. relate_type * rel_ptr;        /* input -- Relationships
  268.                    of this record to other files. */
  269.  
  270. {
  271.  
  272.     cache_entry_type * cache_ptr;/* Current cache entry pointer */
  273.  
  274.     file_type * file_ptr;    /* Pointer to the current file */
  275.  
  276.     int     i;        /* Misc. variable */
  277.  
  278.     /*
  279.      * Consider the case that the comparison was done with the -B
  280.      * option. In that case, records reported to be identical here
  281.      * may actually only be similar. The loop below computes the
  282.      * largest file_no which has a record "identical" to the
  283.      * current one. The theory is that the largest file_no contains
  284.      * the "newest" record (and by deduction, the "best" record).
  285.      */
  286.     for (i = file_no+1; i < file_count; ++i) {
  287.         if (rel_ptr -> current[i]) {
  288.             if ( p5_debug ) {
  289.                 printf ( "debug: oldfile:%d newfile: %d\n",
  290.                     file_no, i );
  291.             }
  292.             file_no = i;
  293.         }
  294.     }
  295.  
  296.     /*
  297.      * If this is the end of the world,
  298.      *     Pass the word on.
  299.      */
  300.     if (rel_ptr -> relation == INSERT_EOT) {
  301.         if (hed_flag) {
  302.             pass5_write_hed (rel_ptr, (cache_entry_type *) 0);
  303.         } else {
  304.             pass5_write_listing(indexes, rel_ptr,
  305.                 (cache_entry_type *) 0);
  306.         }
  307.         return;
  308.     }
  309.  
  310.     /*
  311.      * Initialize local variables.
  312.      *
  313.      * The cache of records is maintained as a queue of the previously seen
  314.      * records. This queue is used by the 'write' routines.
  315.      */
  316.  
  317.     file_ptr = &files[file_no];
  318.     deq_tail_dll (cache_head_ptr, cache_tail_ptr, cache_ptr,
  319.             cache_next_ptr, cache_prev_ptr);
  320.     enq_head_dll (cache_head_ptr, cache_tail_ptr, cache_ptr,
  321.             cache_next_ptr, cache_prev_ptr);
  322.  
  323.     /*
  324.      * Write record to output files.
  325.      */
  326.     if (p5_debug) {
  327.         printf ( "debug: file:%d indexes:%5d %5d %5d current: %d %d %d move: %d\n",
  328.                 file_no, indexes[0], indexes[1], indexes[2],
  329.                 rel_ptr -> current[0], rel_ptr -> current[1], rel_ptr -> current[2],
  330.                 rel_ptr -> moved);
  331.     }
  332.     if (hed_flag) {
  333.         /* if hed_flag, always read record into cache */
  334.         reread_into_cache( file_ptr, indexes[file_no], cache_ptr );
  335.         pass5_write_hed (rel_ptr, cache_ptr);
  336.     } else {
  337.         /* for normal output, wait to read into cache until we
  338.            know for sure it is going to be needed. Most
  339.            records are merely skipped and not output at all. */
  340.         pass5_write_listing (indexes, rel_ptr, cache_ptr);
  341.     }
  342.  
  343.     /*
  344.      * Increment the count of differences.
  345.      */
  346.     switch (rel_ptr -> relation) {
  347.     case INSERT_OLD:
  348.     case INSERT_NEW1_NEW2:
  349.         old_new1_change_count++;
  350.         old_new2_change_count++;
  351.         break;
  352.     case INSERT_NEW1:
  353.     case INSERT_OLD_NEW2:
  354.         old_new1_change_count++;
  355.         new1_new2_change_count++;
  356.         break;
  357.     case INSERT_NEW2:
  358.     case INSERT_OLD_NEW1:
  359.         old_new2_change_count++;
  360.         new1_new2_change_count++;
  361.         break;
  362.     case INSERT_OLD_NEW1_NEW2:
  363.         break;
  364.     default:
  365.         error ("pass5_dump_record: invalid relation");
  366.     }
  367.  
  368.     /*
  369.      * Increment the record indexes.
  370.      */
  371.     for (i = 0; i < file_count; ++i) {
  372.         if (rel_ptr -> current[i]) {
  373.             indexes[i]++;
  374.         }
  375.     }
  376. }
  377. /*
  378.  * pass5_move: Analyse which text moved.
  379.  *
  380.  * This routine is called when a group of moved lines is detected.
  381.  * A group of moved lines exists when (for the current position in
  382.  * the file) the next line occurs in all three files but at different
  383.  * relative positions. This routine scans forward in the each of the
  384.  * 3 files trying to determine the shortest group of lines.
  385.  *
  386.  * In determining the shortest group of lines, inserted and deleted records
  387.  * are not considered. Rather, the scan continues until a line which is
  388.  * before the current line is found.
  389.  *
  390.  * Return value:
  391.  *      This procedure return the file number of the file to be traversed
  392.  *      to indicate the shortest common sequence of lines.
  393.  */
  394. int     pass5_move (indexes)
  395. int    *indexes;        /* Array of pointers to the current indexes
  396.                    into the record arrays */
  397.  
  398. {
  399.  
  400.     int     corres_file_recno;
  401.                 /* Record number in the corresponding file */
  402.  
  403.     int     depth;        /* Number of lines tested */
  404.  
  405.     int     i;        /* Misc. variable */
  406.  
  407.     record_type * record;    /* Pointer to record array for the current
  408.                    file. */
  409.  
  410.     /*
  411.      * For each record, six different comparison need to be made. That is,
  412.      * for each of the three files there is a relationship to each of the
  413.      * other two files. A comparison needs to be made for each of those
  414.      * relationships. The tables below describe the six relationships.
  415.      */
  416.  
  417.     int     index[MATCH_COUNT];/* Current index into the record array of
  418.                    'curr_file' */
  419.  
  420.     int     prev_value[MATCH_COUNT];
  421.                 /* Previous contents of the 'value' field for
  422.                    the record */
  423.  
  424.     bool bypass[MATCH_COUNT];/* TRUE if file should not by considered for
  425.                    processing */
  426.  
  427.  
  428.     /*
  429.      * Initialize the index array and the prev_value array from the values
  430.      * passed in.
  431.      *
  432.      * During this initialization, all entries which contain hash codes
  433.      * are skipped over. All such entries represent a line which exists in
  434.      * only two of the files.
  435.      */
  436.  
  437.     for (i = 0; i < MATCH_COUNT; ++i) {
  438.  
  439.         bypass[i] = (files[curr_file[i]].record == 0) ||
  440.             (files[corres_file[i]].record == 0) ||
  441.             (indexes[curr_file[i]] + 1 >=
  442.                 files[curr_file[i]].record_array_size);
  443.  
  444.         if (!bypass[i]) {
  445.             record = files[curr_file[i]].record;
  446.             index[i] = indexes[curr_file[i]];
  447.             while(is_hash_code(
  448.                 record[index[i]].value[value_sub[i]])) {
  449.  
  450.                 index[i]++;
  451.             }
  452.             prev_value[i] = record[index[i]].value[value_sub[i]];
  453.         }
  454.  
  455.     }
  456.  
  457.     /*
  458.      * Indentify the shortest common sequence of lines.
  459.      *
  460.      * For each iteration of this loop the index into the record array for
  461.      * each of the six cases is incremented by one. The record detected at the
  462.      * new index is compared against the previous record for the same case.
  463.      *
  464.      * In the documentation below, the 'current' file is the one which
  465.      * has a relationship and the 'corresponding' file is the one related to.
  466.      */
  467.     for (depth = 0;; depth++) {
  468.         for (i = 0; i < MATCH_COUNT; ++i) {
  469.  
  470.             /*
  471.              * If this files has no records or no records left
  472.              *    to dump,
  473.              *     ignore the file.
  474.              *
  475.              * This happens in the case of a two file comparision.
  476.              * or in the case of one file completely output.
  477.              */
  478.             if (bypass[i]) {
  479.                 continue;
  480.             }
  481.  
  482.             /*
  483.              * If we have reached the end of the 'current' file,
  484.              *    If this line is also the end of the
  485.              *        'corresponding' file,
  486.              *       If the 'corresponding' file is at EOF already,
  487.              *           the 'current' file contains the
  488.              *        shortest sequence
  489.              *       else
  490.              *           the 'corresponding' file contains the
  491.              *        shortest sequence
  492.              *    else
  493.              *        the 'current' file contains the shortest
  494.              *        common sequence.
  495.              */
  496.             if(index[i]+1 >= files[curr_file[i]].record_array_size){
  497.                 if (prev_value[i] + 1 >=
  498.                     files[corres_file[i]].record_array_size) {
  499.  
  500.                     if (indexes[corres_file[i]] + 1 >=
  501.                         files[corres_file[i]].
  502.                         record_array_size) {
  503.  
  504.                         return (curr_file[i]);
  505.                     } else {
  506.                         return (corres_file[i]);
  507.                     }
  508.                 } else {
  509.                     return (curr_file[i]);
  510.                 }
  511.             }
  512.  
  513.             /*
  514.              * For each record in the 'current' file which doesn't
  515.              *     have a record in the 'corresponding' file,
  516.              *     skip past the record.
  517.              *
  518.              * Since what we are trying to find is the shortest
  519.              * group of records in the 'corresponding' file,
  520.              * the search should not be penalized by
  521.              * inserted records in the 'current' file.
  522.              */
  523.             record = files[curr_file[i]].record;
  524.             index[i]++;
  525.             while (is_hash_code(record[index[i]].
  526.                 value[value_sub[i]])) {
  527.  
  528.                 index[i]++;
  529.             }
  530.  
  531.             /*
  532.              * If the record number in the corresponding file
  533.              * is one of the records being considered as
  534.              * moved by the 'corresponding' file, the 'current'
  535.              * file contains the shortest common sequence.
  536.              *
  537.              * Note: this routine is only invoked if all files
  538.              * are currently involved in a move.
  539.              *
  540.              * By outputting lines from the 'current' file,
  541.              * we will soon get to the current point in the
  542.              * 'corresponding' file. At that point, the
  543.              * current line in the 'corresponding' file will
  544.              * no longer be diagnosed as having moved.
  545.              */
  546.             corres_file_recno = record[index[i]].
  547.                 value[value_sub[i]];
  548.  
  549.             if (corres_file_recno >= indexes[corres_file[i]] &&
  550.                 corres_file_recno <=
  551.                 indexes[corres_file[i]] + depth) {
  552.                 if (p5_debug)
  553.                     printf("move optimization found\n");
  554.  
  555.                 return (curr_file[i]);
  556.             }
  557.         }
  558.         /*
  559.          * The above loop and this loop were split in order to give the
  560.          * above loop every opportunity to find a move optimization.
  561.          */
  562.         for (i = 0; i < MATCH_COUNT; ++i) {
  563.             if (bypass[i])
  564.                 continue;
  565.             record = files[curr_file[i]].record;
  566.             corres_file_recno = record[index[i]].
  567.                 value[value_sub[i]];
  568.  
  569.             /*
  570.              * If the record numbers in the 'corresponding' file are
  571.              *  still going in the forward direction,
  572.              *  then we haven't completed the search.
  573.              *
  574.              * Record numbers need not be immediately next to
  575.              * one another (e.g. record number 5 and record
  576.              * number 6). Record numbers merely need to
  577.              * following one another. This ensures that the common
  578.              * sequence check isn't interrupted by deletions in the
  579.              * 'corresponding' file.
  580.              *
  581.              * The above comment is hereby rescinded. Cliff 1/23/85
  582.              */
  583.             if (prev_value[i] + 1 == corres_file_recno) {
  584.                 prev_value[i] = corres_file_recno;
  585.             } else {
  586.                 if (p5_debug)
  587.                     printf("short move found %d\n", depth);
  588.                 return (curr_file[i]);
  589.             }
  590.         }
  591.     }
  592.  
  593. }
  594.  
  595. /*
  596.  * pass5_write_hed: write a record to 'hed' file.
  597.  *
  598.  * This routine writes the specified record to the 'hed' output file.
  599.  *
  600.  * Return value:
  601.  *      This routine returns no value.
  602.  */
  603. void pass5_write_hed (rel_ptr, cache_ptr)
  604. relate_type * rel_ptr;        /* input */
  605.              /* Relationships of this record to other files. */
  606.  
  607. cache_entry_type * cache_ptr;    /* input */
  608.              /* Cache entry describing buffer */
  609.  
  610. {
  611.  
  612.     bool flag;        /* TRUE if NEW1 and OLD file not same */
  613.  
  614.     int     i;        /* Misc. variable */
  615.  
  616. #define HED_END_RECORD "~End of changes"
  617.  
  618.     static int      prev_relation = INSERT_NONE;
  619.                 /* Relationship of previous call */
  620.  
  621.     static  bool prev_in_all = TRUE;
  622.             /* TRUE if previous record was in all of the files */
  623.  
  624.  
  625.     /*
  626.      * Handle EOT case.
  627.      */
  628.     if (rel_ptr -> relation == INSERT_EOT) {
  629.         if (!prev_in_all) {
  630.             puts (HED_END_RECORD);
  631.         }
  632.         return;
  633.     }
  634.  
  635.     /*
  636.      * Write any header of trailing records to 'hed' file.
  637.      */
  638.  
  639.     if (rel_ptr -> relation != prev_relation) {
  640.         if (!prev_in_all) {
  641.             puts (HED_END_RECORD);
  642.         }
  643.  
  644.  
  645.         if (!rel_ptr -> in_all) {
  646.             (void) fputs ("~~", stdout);
  647.  
  648.             if (rel_ptr -> current[OLD_FILE]) {
  649.                 (void) fputs ("Delete ", stdout);
  650.             } else {
  651.                 (void) fputs ("Insert ", stdout);
  652.             }
  653.  
  654.             flag = FALSE;
  655.  
  656.             for (i = NEW1_FILE; i < file_count; ++i) {
  657.  
  658.                 if (rel_ptr -> current[OLD_FILE] !=
  659.                         rel_ptr -> current[i]) {
  660.  
  661.                     if (flag) {
  662.                         (void) fputs (" and ", stdout);
  663.                     }
  664.  
  665.                     (void) fputs("'", stdout);
  666.                     (void) fputs(files[i].text_ptr?
  667.                              files[i].text_ptr:
  668.                              files[i].name_ptr,
  669.                              stdout);
  670.                     (void) fputs("'", stdout);
  671.                     flag = TRUE;
  672.  
  673.                     if (!is_hash_code(rel_ptr->index[i]) &&
  674.                         !is_hash_code(
  675.                         rel_ptr->index[OLD_FILE])) {
  676.  
  677.                         (void) fputs(" (MOVED)",stdout);
  678.                     }
  679.  
  680.                 }
  681.  
  682.             }
  683.  
  684.             (void) putchar ('\n');
  685.  
  686.         }
  687.  
  688.         prev_relation = rel_ptr -> relation;
  689.         prev_in_all = rel_ptr -> in_all;
  690.     }
  691.  
  692.     /*
  693.      * Write record to 'hed' file.
  694.      */
  695.     cache_ptr -> recordp[cache_ptr -> record_length] = '\0';
  696.     (void) puts (cache_ptr -> recordp);
  697.  
  698. }
  699. /*
  700.  * pass5_write_listing: write a record to 'listing' file.
  701.  *
  702.  * This routine writes a summary of the changes to the standard output.
  703.  *
  704.  * This routine writes all changed lines to standard output. It also
  705.  * writes a few (-prefix and -postifx options) unchanged lines immediately
  706.  * preceeding and following the changed lines. This routine assumes that
  707.  * the caller, 'pass5_dump_record', is maintaining a queue of all records
  708.  * read in the cache. The current record should be at the head of the
  709.  * cache. The previous record should immediately follow that, etc.
  710.  *
  711.  * Return value:
  712.  *      This routine returns no value.
  713.  */
  714.  
  715. void pass5_write_listing (indexes, rel_ptr, cache_ptr)
  716. int     indexes[MAX_FILE_COUNT];/* input */
  717.                  /* Indexes of the current records. */
  718.  
  719. relate_type * rel_ptr;        /* input */
  720.                  /* Relationships of this record to other files. */
  721.  
  722. cache_entry_type * cache_ptr;    /* input */
  723.                  /* Cache entry describing buffer */
  724.  
  725. {
  726.  
  727.     static  relate_type all_rel;
  728.                 /* relation used for outputting prefix lines.*/
  729.  
  730.     int     i;        /* Misc. variable */
  731.  
  732.     static int      postfix_count = 0;
  733.                 /* Number of postfix lines remaining to output*/
  734.  
  735.     cache_entry_type * prefix_cache_ptr;
  736.                 /* Pointer to cache entry containing the
  737.                    current prefix line */
  738.  
  739.     int     prefix_count;    /* Number of prefix lines to output */
  740.  
  741.     static int      prev_relation = INSERT_NONE;
  742.                 /* Relationship of previous call */
  743.  
  744.     static  bool prev_in_all = TRUE;
  745.                 /* TRUE if previous line was in all files at
  746.                    the current position */
  747.  
  748.     static int      same_count = 0;
  749.                 /* Number of indentical lines scanned so far
  750.                    which might later be output as prefix lines.
  751.                    */
  752.  
  753.  
  754.  
  755.     /*
  756.      * Handle EOT case.
  757.      */
  758.     if (rel_ptr -> relation == INSERT_EOT) {
  759.         if (!quiet_option) {
  760.             if (old_new1_change_count == 0 &&
  761.                 (file_count == 2 ||
  762.                     (old_new2_change_count == 0 &&
  763.                         new1_new2_change_count == 0))) {
  764.  
  765.                 (void) strcpy (cache_tail_ptr -> recordp, "Files are identical");
  766.                 cache_tail_ptr -> record_length = strlen (cache_tail_ptr -> recordp);
  767.                 pass5_write_listing_line ((relate_type *) 0, cache_tail_ptr);
  768.             } else {
  769.                 cache_tail_ptr -> record_length = 0;
  770.                 pass5_write_listing_line((relate_type *) 0,
  771.                     cache_tail_ptr);
  772.                 (void) strcpy(cache_tail_ptr->recordp,
  773.                     "[ Comparison Complete ]");
  774.                 cache_tail_ptr->record_length =
  775.                     strlen(cache_tail_ptr -> recordp);
  776.                 pass5_write_listing_line((relate_type *) 0,
  777.                     cache_tail_ptr);
  778.             }
  779.             if (page_length)  (void) putchar('\f');
  780.         }
  781.         return;
  782.     }
  783.  
  784.     /*
  785.      * Write any prefix lines which need to be written.
  786.      */
  787.     if (rel_ptr -> relation != prev_relation && prev_in_all) {
  788.  
  789.         prefix_count = min (prefix_lines, same_count);
  790.  
  791.         if (same_count > prefix_count) {
  792.             /* Output separator line */
  793.             pass5_write_listing_line ((relate_type *) 0,
  794.                 (cache_entry_type *) 0);
  795.         }
  796.  
  797.         prefix_cache_ptr = cache_ptr;
  798.         for (i = 0; i < prefix_count; ++i) {
  799.             prefix_cache_ptr = prefix_cache_ptr -> cache_next_ptr;
  800.         }
  801.  
  802.         all_rel.relation = INSERT_OLD_NEW1_NEW2;
  803.         all_rel.moved = FALSE;
  804.         all_rel.in_all = TRUE;
  805.  
  806.         while (prefix_count > 0) {
  807.  
  808.             for (i = 0; i < file_count; ++i) {
  809.                 all_rel.index[i] = indexes[i] - prefix_count;
  810.                 all_rel.current[i] = TRUE;
  811.             }
  812.  
  813.             pass5_write_listing_line (
  814.                 &all_rel, /* Indicate all lines are related */
  815.                 prefix_cache_ptr); /* Pointer to record in cache */
  816.  
  817.             prefix_count--;
  818.             prefix_cache_ptr = prefix_cache_ptr -> cache_prev_ptr;
  819.  
  820.         }
  821.  
  822.     }
  823.  
  824.     /*
  825.      * If this is the first identical line,
  826.      *     indicate how many postifx lines need to be output.
  827.      */
  828.     if (rel_ptr -> relation != prev_relation && rel_ptr -> in_all &&
  829.             prev_relation != INSERT_NONE) {
  830.         postfix_count = postfix_lines;
  831.     }
  832.  
  833.     prev_relation = rel_ptr -> relation;
  834.     prev_in_all = rel_ptr -> in_all;
  835.  
  836.     /*
  837.      * If this line is a changed line or a postfix line,
  838.      *     dump it.
  839.      */
  840.     if (!rel_ptr -> in_all || postfix_count > 0) {
  841.         pass5_write_listing_line (
  842.                 rel_ptr,/* Line relationships */
  843.                 cache_ptr);/* Pointer to record in cache */
  844.     }
  845.  
  846.     /*
  847.      * Count the number of identical lines
  848.      * and keep track of the number of postfix lines printed.
  849.      */
  850.     if (rel_ptr -> in_all) {
  851.         if (postfix_count <= 0) {
  852.             same_count++;
  853.         }
  854.         postfix_count--;
  855.     } else {
  856.         same_count = 0;
  857.     }
  858.  
  859. }
  860. /*
  861.  * pass5_write_listing_line: write a record to 'listing' file.
  862.  *
  863.  * This routine writes a single line to the 'listing' file. The line has
  864.  * already been checked to ensure that the line is to be output.
  865.  *
  866.  * Return value:
  867.  *      This routine returns no value.
  868.  */
  869.  
  870. void pass5_write_listing_line (rel_ptr, cache_ptr)
  871. relate_type * rel_ptr;    /* input -- Relationships of this record to
  872.                 other files. If this pointer is 0, write the line
  873.                pointed to by cache_entry_type with
  874.                no special formatting */
  875.  
  876. cache_entry_type * cache_ptr;    /* input -- Cache entry describing buffer
  877.                    If this pointer is 0, write a
  878.                    separator line between changes */
  879.  
  880. {
  881.  
  882.     int     i;        /* Misc. variable */
  883.  
  884.     static int      line_count = 0;/* Number of lines left on a page */
  885.  
  886.     static int      page_count = 0;/* Number of pages output so far */
  887.  
  888.  
  889.  
  890.     /*
  891.      * Output the header lines, if required.
  892.      *
  893.      * Don't output separator record too close to bottom of a page.
  894.      *
  895.      * Don't use any pagination if "page_length" is zero
  896.      */
  897.     if ( page_length )  --line_count;
  898.     if ( (page_length || !page_count)  &&
  899.          (line_count <= 0 || (line_count <= 10 && cache_ptr == 0))
  900.        ) {
  901.  
  902.         if ( (page_count > 0) && (line_count > 0) )
  903.            (void) putchar('\f');
  904.         page_count++;
  905.         line_count = (page_length - HEAD_LENGTH);
  906.         if ( page_count > 1 )  ++line_count;
  907.  
  908.         printf ("Time of comparison: %s", exec_time);
  909.         if ( page_length )
  910.             printf ("                          Page %d\n\n", page_count);
  911.         else
  912.             puts ("\n\n");
  913.  
  914.         if (file_count == 2) {
  915.             pass5_write_listing_head("Old ", OLD_FILE);
  916.             pass5_write_listing_head("New ", NEW1_FILE);
  917.             (void) putchar ('\n');
  918.             puts ("Status  Old   New                   Contents");
  919.             puts ("------  ---   ---                   --------");
  920.         } else {
  921.             pass5_write_listing_head("Old ", OLD_FILE);
  922.             pass5_write_listing_head("New1", NEW1_FILE);
  923.             pass5_write_listing_head("New2", NEW2_FILE);
  924.             (void) putchar ('\n');
  925.             puts ("Status  Old   New1  New2                  Contents");
  926.             puts ("------  ---   ----  ----                  --------");
  927.         }
  928.  
  929.         puts ("\n");
  930.  
  931.         if (cache_ptr == 0) {
  932.             return;    /* Don't put separator record at top of page */
  933.         }
  934.  
  935.     }
  936.  
  937.     /*
  938.      * If rel_ptr exists,
  939.      * Output the Line status and file line numbers:
  940.      */
  941.     if (rel_ptr != 0) {
  942.         if (!rel_ptr -> in_all) {
  943.  
  944.             if (rel_ptr -> current[OLD_FILE]) {
  945.                 if (rel_ptr -> moved) {
  946.                     fputs ("MOV[O]", stdout);
  947.                 } else {
  948.                     fputs ("Delete", stdout);
  949.                 }
  950.             } else {
  951.                 if (rel_ptr -> moved) {
  952.                     fputs ("MOV[N]", stdout);
  953.                 } else {
  954.                     fputs ("Insert", stdout);
  955.                 }
  956.             }
  957.  
  958.         } else {
  959.  
  960.             fputs ("      ", stdout);
  961.  
  962.         }
  963.  
  964.         cache_ptr->record_length = -1;
  965.         for (i = 0; i < file_count; ++i) {
  966.             if (is_hash_code (rel_ptr -> index[i])) {
  967.                 fputs ("       ", stdout);
  968.             } else {
  969.                 if (cache_ptr->record_length == -1) {
  970.                     reread_into_cache( &files[i],
  971.                         rel_ptr->index[i],
  972.                         cache_ptr );
  973.                 }
  974.                 printf ("%6d ", rel_ptr -> index[i]);
  975.             }
  976.         }
  977.  
  978.     } else if (cache_ptr == 0) {
  979.  
  980.         puts ("******");
  981.         return;
  982.  
  983.     }
  984.  
  985.     /*
  986.      * Finally, write the line.
  987.      */
  988.     if (cache_ptr -> record_length == -1) {
  989.         puts (" < EOF.. >");
  990.     } else {
  991.         cache_ptr -> recordp[cache_ptr -> record_length] = '\0';
  992.         puts (cache_ptr -> recordp);
  993.     }
  994.  
  995. }
  996. /*
  997.  * pass5_write_listing_head: write a header line to 'listing' file.
  998.  *
  999.  * This routine writes a single header line to the 'listing' file.
  1000.  *
  1001.  * Return value:
  1002.  *      This routine returns no value.
  1003.  */
  1004. void pass5_write_listing_head (name_ptr, file_no)
  1005. char * name_ptr;        /* input -- name of header */
  1006.  
  1007. int     file_no;        /* input -- file number of the file
  1008.                  containing the record to be compared. */
  1009.  
  1010. {
  1011.     printf ("%s area: '%s'   Last Written: %s",
  1012.         name_ptr,
  1013.         files[file_no].name_ptr,
  1014.         files[file_no].lw_ptr);
  1015.  
  1016.     if ( files[file_no].text_ptr ) {
  1017.         printf ("   Text: '%s'", files[file_no].text_ptr );
  1018.     }
  1019.     (void) putchar ('\n');
  1020. }
  1021.