home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume13 / combine / part02 < prev    next >
Encoding:
Text File  |  1990-06-15  |  49.2 KB  |  1,983 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v13i055: combine: 3 file compare/merge utility (part 2of3)
  3. from: cliff@gcx1.SSD.CSD.HARRIS.COM (Cliff Van Dyke)
  4. Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5.  
  6. Posting-number: Volume 13, Issue 55
  7. Submitted-by: cliff@gcx1.SSD.CSD.HARRIS.COM (Cliff Van Dyke)
  8. Archive-name: combine/part02
  9.  
  10. # This is a shell archive.  Remove anything before this line,
  11. # then unpack it by saving it in a file and typing "sh file".
  12. #
  13. # Wrapped by cliff on Tue Feb 13 14:08:12 EST 1990
  14. # Contents:  combine2.c data.c main.c os_dep.h pass2.c unix.c util.h
  15.  
  16. echo x - combine2.c
  17. sed 's/^@//' > "combine2.c" <<'@//E*O*F combine2.c//'
  18. /*
  19.  * The combine utility is a product of Harris, Inc. and is provided for
  20.  * unrestricted use provided that this legend is included on all tape
  21.  * media and as a part of the software program in whole or part.  Users
  22.  * may copy, modify, license or distribute the combine utility without charge.
  23.  * 
  24.  * THE COMBINE UTILITY IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
  25.  * INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
  26.  * PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
  27.  * PRACTICE.
  28.  * 
  29.  * The combine utility is provided with no support and without any obligation
  30.  * on the part of Harris, Inc. to assist in its use, correction,
  31.  * modification or enhancement.
  32.  * 
  33.  * HARRIS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  34.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THE COMBINE
  35.  * UTILITY OR ANY PART THEREOF.
  36.  * 
  37.  * In no event will Harris, Inc. be liable for any lost revenue
  38.  * or profits or other special, indirect and consequential damages, even if
  39.  * Harris has been advised of the possibility of such damages.
  40.  * 
  41.  * Harris Computer Systems Division
  42.  * 2101 W Cypress Creek Rd
  43.  * Fort Lauderdale, Florida 33309
  44.  */
  45. #include <stdio.h>
  46. #include <ctype.h>
  47. #include "util.h"
  48. #include "os_dep.h"
  49. /*
  50.  * main: Main program for the COMBINE2 utility
  51.  *
  52.  * The COMBINE2 utility converts the 'HED' output file of the COMBINE
  53.  * utility to be a standard symbolic file.
  54.  *
  55.  * Return value:
  56.  *      This procedure has no return value.
  57.  */
  58. void main (argc, argv)
  59. int     argc;            /* command line argument count */
  60.  
  61. char  **argv;            /* command line arguments */
  62.  
  63. {
  64.  
  65.     int     buf_len;    /* Actual number of characters in buffer */
  66.  
  67.     bool delete;        /* TRUE if currently deleteing lines */
  68.  
  69.     int     file_count;    /* Number of file arguments */
  70.  
  71.     int     i;        /* Misc. variable */
  72.  
  73.     FILE * in_file;        /* input file */
  74.  
  75. #define LINE_LENGTH 512
  76.     char    mbuffer[LINE_LENGTH];/* Line buffer */
  77.  
  78.     FILE * out_file;    /* Output file */
  79.  
  80.     int     status;        /* Misc. status */
  81.  
  82.     extern  void error ();
  83. #ifndef VOS
  84.     extern int      errno;
  85. #endif
  86.  
  87.  
  88.  
  89.     /*
  90.      * Handle each command line argument.
  91.      */
  92.     in_file = stdin;
  93.     out_file = stdout;
  94.  
  95.     file_count = 0;
  96.  
  97.     for (i = 1; i < argc; ++i) {
  98.  
  99.         /*
  100.          * Handle options arguments.
  101.          */
  102.         if (argv[i][0] == '-' && argv[i][1] != '\0') {
  103.  
  104.             sprintf (mbuffer,
  105.                 "Unrecognized option '%s' specified", argv[i]);
  106.             error (mbuffer);
  107.  
  108. #ifdef VOS
  109.  
  110.         /*
  111.          * Handle redirections of 'stdin':
  112.          *
  113.          * This code won't get executed on a UNIX O.S. However, on
  114.          * VOS this code allows the same syntax to work.
  115.          */
  116.         } else if (argv[i][0] == '<' && argv[i][1] != '\0') {
  117.             in_file = fopen (&argv[i][1], "r");
  118.  
  119.             if (in_file == 0) {
  120.                 perror( &argv[i][1]);
  121.                 exit(2);
  122.             }
  123.  
  124.         /*
  125.          * Handle redirections of 'stdout':
  126.          *
  127.          * This code won't get executed on a UNIX O.S. However, on VOS this
  128.          * code allows the same syntax to work.
  129.          */
  130.  
  131.         } else if (argv[i][0] == '>' && argv[i][1] != '\0') {
  132.             out_file = fopen (&argv[i][1], "w");
  133.  
  134.             if (out_file == 0) {
  135.                 perror( &argv[i][1] );
  136.                 exit( 2);
  137.             }
  138. #endif
  139.  
  140.         /*
  141.          * Handle file arguments not preceeded by a specific
  142.          * option argument.
  143.          */
  144.         } else {
  145.  
  146.             if (file_count >= 2) {
  147.                 error ("Too many files specified");
  148.             }
  149.  
  150.             if (file_count == 0) {
  151.                 in_file = fopen (argv[i], "r");
  152.  
  153.                 if (in_file == 0) {
  154.                     perror(argv[i]);
  155.                     exit( 2 );
  156.                 }
  157.  
  158.             } else {
  159.  
  160.                 out_file = fopen (argv[i], "w");
  161.  
  162.                 if (out_file == 0) {
  163.                     perror(argv[i]);
  164.                     exit(2);
  165.                 }
  166.  
  167.             }
  168.  
  169.             file_count++;
  170.         }
  171.     }
  172.  
  173.     /*
  174.      * Read the next line of the file.
  175.      */
  176. #ifdef VOS
  177.     out_file -> carriage_control = FALSE;
  178. #endif
  179.  
  180.     delete = FALSE;
  181.  
  182.     for (;;) {
  183.  
  184.         status = os_dep_file_read (in_file, mbuffer, LINE_LENGTH,
  185.             &buf_len);
  186.  
  187.         if (status != SS_NORMAL) {
  188.             if (status == SS_EOT) {
  189.                 exit (0);
  190.             } else {
  191.                 perror( "input file" ) ;
  192.                 exit( 2 );
  193.             }
  194.         }
  195.  
  196.         if (buf_len > 0 && mbuffer[0] == '~') {
  197.  
  198.             if (buf_len > 1) {
  199.  
  200.                 if (mbuffer[1] == '~') {
  201.  
  202.                     if (buf_len > 2) {
  203.                         if (mbuffer[2] == 'I') {
  204.                             continue;
  205.                         } else if (mbuffer[2] == 'D') {
  206.                             delete = TRUE;
  207.                             continue;
  208.                         }
  209.                     }
  210.  
  211.                 } else if (mbuffer[1] == 'E') {
  212.                     delete = FALSE;
  213.                     continue;
  214.                 }
  215.  
  216.             }
  217.  
  218.         }
  219.  
  220.         if (!delete) {
  221.             status = os_dep_file_write (out_file, mbuffer, buf_len);
  222.  
  223.             if (status != SS_NORMAL) {
  224. #ifdef VOS
  225.                 sprintf (mbuffer,
  226.                     "Write error on output file: %d\n",
  227.                     status);
  228.                 error (mbuffer);
  229. #else
  230.                 perror( "output file" ) ;
  231. #endif
  232.             }
  233.         }
  234.  
  235.     }
  236.  
  237. }
  238. /*
  239.  * error: output fatal error message
  240.  *
  241.  * This routine outputs an error message and terminates.
  242.  *
  243.  * Return value:
  244.  *      This procedure has no return value.
  245.  */
  246. void error (error_ptr)
  247. char   *error_ptr;        /* input -- Record to output. */
  248.  
  249. {
  250.  
  251.     fprintf (stderr, "combine2: %s.\n", error_ptr);
  252.     exit (2);
  253.  
  254. }
  255. @//E*O*F combine2.c//
  256. chmod u=rw,g=rw,o=rw combine2.c
  257.  
  258. echo x - data.c
  259. sed 's/^@//' > "data.c" <<'@//E*O*F data.c//'
  260. /*
  261.  * The combine utility is a product of Harris, Inc. and is provided for
  262.  * unrestricted use provided that this legend is included on all tape
  263.  * media and as a part of the software program in whole or part.  Users
  264.  * may copy, modify, license or distribute the combine utility without charge.
  265.  * 
  266.  * THE COMBINE UTILITY IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
  267.  * INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
  268.  * PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
  269.  * PRACTICE.
  270.  * 
  271.  * The combine utility is provided with no support and without any obligation
  272.  * on the part of Harris, Inc. to assist in its use, correction,
  273.  * modification or enhancement.
  274.  * 
  275.  * HARRIS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  276.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THE COMBINE
  277.  * UTILITY OR ANY PART THEREOF.
  278.  * 
  279.  * In no event will Harris, Inc. be liable for any lost revenue
  280.  * or profits or other special, indirect and consequential damages, even if
  281.  * Harris has been advised of the possibility of such damages.
  282.  * 
  283.  * Harris Computer Systems Division
  284.  * 2101 W Cypress Creek Rd
  285.  * Fort Lauderdale, Florida 33309
  286.  */
  287. #include <stdio.h>
  288. #include <ctype.h>
  289. #include "util.h"
  290. #define ALLOC
  291. #include "combine.h"
  292. @//E*O*F data.c//
  293. chmod u=rw,g=rw,o=rw data.c
  294.  
  295. echo x - main.c
  296. sed 's/^@//' > "main.c" <<'@//E*O*F main.c//'
  297. /*
  298.  * The combine utility is a product of Harris, Inc. and is provided for
  299.  * unrestricted use provided that this legend is included on all tape
  300.  * media and as a part of the software program in whole or part.  Users
  301.  * may copy, modify, license or distribute the combine utility without charge.
  302.  * 
  303.  * THE COMBINE UTILITY IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
  304.  * INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
  305.  * PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
  306.  * PRACTICE.
  307.  * 
  308.  * The combine utility is provided with no support and without any obligation
  309.  * on the part of Harris, Inc. to assist in its use, correction,
  310.  * modification or enhancement.
  311.  * 
  312.  * HARRIS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  313.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THE COMBINE
  314.  * UTILITY OR ANY PART THEREOF.
  315.  * 
  316.  * In no event will Harris, Inc. be liable for any lost revenue
  317.  * or profits or other special, indirect and consequential damages, even if
  318.  * Harris has been advised of the possibility of such damages.
  319.  * 
  320.  * Harris Computer Systems Division
  321.  * 2101 W Cypress Creek Rd
  322.  * Fort Lauderdale, Florida 33309
  323.  */
  324. #include <ctype.h>
  325. #include <stdio.h>
  326. #include <sys/types.h>
  327. #include <sys/stat.h>
  328. #include "util.h"
  329. #include "combine.h"
  330. /*
  331.  * main: Main program for the COMBINE utility
  332.  *
  333.  * This routine is the driver for the utility.
  334.  *
  335.  * Return value:
  336.  *      This procedure has no return value.
  337.  */
  338.  
  339. void main (argc, argv)
  340. int     argc;            /* command line argument count */
  341.  
  342. char  **argv;            /* command line arguments */
  343.  
  344. {
  345.  
  346.     struct stat     stat_buf;/* Buf. to find last written date */
  347.  
  348.     /*
  349.      * Execute program phases.
  350.      */
  351.  
  352.     if (!isatty (fileno (stdout))) {
  353.         fstat (fileno (stdout), &stat_buf);
  354.         setvbuf (stdout, mem_alloc (stat_buf.st_blksize),
  355.             _IOFBF, stat_buf.st_blksize);
  356.     }
  357.  
  358.     init (argc, argv);    /* Perform program initialization */
  359.  
  360.     if (p1_debug || pa_debug) {
  361.         fputs ("Start Pass1\n", stderr);
  362.     }
  363.     pass1 ();  /* Read files building symbol table and record arrays. */
  364.     if (p1_debug || pa_debug) {
  365.         dump_sym_tab ("Pass1 symbol table");
  366.         dump_arrays ("Pass1 arrays");
  367.     }
  368.  
  369.     if (p2_debug || pa_debug) {
  370.         fputs ("Start Pass2\n", stderr);
  371.     }
  372.     pass2 ();        /* Determine anchor points in files. */
  373.     if (p2_debug || pa_debug) {
  374.         dump_arrays ("Pass2 arrays");
  375.     }
  376.  
  377.     if (p3_debug || pa_debug) {
  378.         fputs ("Start Pass3\n", stderr);
  379.     }
  380.     pass3 ();        /* Expand anchors to non-unique lines. */
  381.     if (p3_debug || pa_debug) {
  382.         dump_arrays ("Pass3 arrays");
  383.     }
  384.  
  385.     if (p4_debug || pa_debug) {
  386.         fputs ("Start Pass4\n", stderr);
  387.     }
  388.     pass4 ();        /* Fix non-uniques surrounded by insertions */
  389.     if (p4_debug || pa_debug) {
  390.         dump_arrays ("Pass4 arrays");
  391.     }
  392.  
  393.     if (p5_debug || pa_debug) {
  394.         fputs ("Start Pass5\n", stderr);
  395.     }
  396.     pass5 ();        /* Write output files. */
  397.  
  398.     if (statistics_flag) {
  399.         dump_statistics ();
  400.     }
  401.  
  402.     if (old_new1_change_count == 0 &&
  403.             (file_count == 2 ||
  404.                 (old_new2_change_count == 0 &&
  405.                     new1_new2_change_count == 0))) {
  406.         exit (0);
  407.     } else {
  408.         exit (1);
  409.     }
  410.  
  411. }
  412. /*
  413.   * dump_arrays: dump arrays for debugging purposes
  414.   *
  415.   * This routine outputs the record arrays to the standard output file.
  416.   *
  417.   * Return value:
  418.   *      This procedure has no return value.
  419.   */
  420.  
  421. void dump_arrays (message)
  422. char   *message;        /* input */
  423.  /* Message to print before arrays */
  424.  
  425. {
  426.  
  427.     int     i;        /* Misc. variable */
  428.  
  429.     int     index;        /* Index into record array */
  430.  
  431.     int     files_left;    /* number of files left to do */
  432.  
  433.     bool file_done[MAX_FILE_COUNT];/* TRUE if EOT reached on file */
  434.  
  435.     record_type * record_ptr;/* Pointer to current record */
  436.  
  437.  
  438.  
  439.     /*
  440.      * Initialize completion parameters.
  441.      */
  442.     printf ("%s\n", message);
  443.  
  444.     files_left = file_count;
  445.     for (i = 0; i < file_count; ++i) {
  446.         file_done[i] = FALSE;
  447.     }
  448.  
  449.     /*
  450.      * For each iteration of the file read the nth record of the each file.
  451.      */
  452.     for (index = BEGIN_INDEX + 1; files_left != 0; ++index) {
  453.  
  454.         printf ("record: %5d ", index);
  455.  
  456.         /*
  457.          * Handle each file.
  458.          */
  459.  
  460.         for (i = 0; i < file_count; ++i) {
  461.  
  462.             if (!file_done[i]) {
  463.  
  464.                 if (index >= files[i].record_array_size - 1) {
  465.                     file_done[i] = TRUE;
  466.                     --files_left;
  467.                     if (files_left == 0) {
  468.                         break;
  469.                     }
  470.                     printf ("%38.38s", " ");
  471.                     continue;
  472.                 }
  473.  
  474.                 record_ptr = &(files[i].record[index]);
  475.  
  476.                 printf ("    rfa:%6d val1:%6d val2:%6d",
  477.                         record_ptr -> rfa,
  478.                         record_ptr -> value[0],
  479.                         record_ptr -> value[1]);
  480.  
  481.             } else {
  482.                 printf ("%38.38s", " ");
  483.  
  484.             }
  485.  
  486.         }
  487.  
  488.         printf ("\n");
  489.  
  490.     }
  491.  
  492. }
  493. /*
  494.  * dump_statistics: Dump statistics
  495.  *
  496.  * This routine outputs the execution statistics * to the standard output
  497.  * file.
  498.  *
  499.  * Return value:
  500.  *      This procedure has no return value.
  501.  */
  502. void dump_statistics () {
  503.  
  504.     int     i;        /* Misc. variable */
  505.  
  506.  
  507.  
  508.     /*
  509.      * Initialize completion parameters.
  510.      */
  511.  
  512.     printf ("\fStatistics:\n\n");
  513.  
  514.     printf ("Cache misses: %d\n", cache_miss);
  515.     printf ("Hash collisions: %d\n", hash_collisions);
  516.  
  517.     printf ("Line counts:\n");
  518.     for (i = 0; i < file_count; ++i) {
  519.         printf ("   %5d: '%s'\n",
  520.             files[i].record_array_size - DUMMY_RECORD_COUNT,
  521.             files[i].name_ptr);
  522.     }
  523.  
  524.     printf ("Changes:\n");
  525.     printf ("   '%s' and '%s' ", files[OLD_FILE].name_ptr,
  526.             files[NEW1_FILE].name_ptr);
  527.     if (old_new1_change_count == 0) {
  528.         printf ("are identical.\n");
  529.     } else {
  530.         printf ("have %d differences.\n", old_new1_change_count);
  531.     }
  532.  
  533.     if (file_count > 2) {
  534.         printf ("   '%s' and '%s' ", files[OLD_FILE].name_ptr,
  535.                 files[NEW2_FILE].name_ptr);
  536.         if (old_new2_change_count == 0) {
  537.             printf ("are identical.\n");
  538.         } else {
  539.             printf ("have %d differences.\n", old_new2_change_count);
  540.         }
  541.  
  542.         printf ("   '%s' and '%s' ", files[NEW1_FILE].name_ptr,
  543.                 files[NEW2_FILE].name_ptr);
  544.         if (new1_new2_change_count == 0) {
  545.             printf ("are identical.\n");
  546.         } else {
  547.             printf ("have %d differences.\n", new1_new2_change_count);
  548.         }
  549.     }
  550.  
  551. }
  552. /*
  553.  * dump_sym_tab: dump symbol table for debugging purposes
  554.  *
  555.  * This routine outputs the symbol table to the standard output file.
  556.  *
  557.  * Return value:
  558.  *      This procedure has no return value.
  559.  */
  560. void dump_sym_tab (message)
  561. char   *message;        /* input */
  562.  /* Message to print before table */
  563.  
  564. {
  565.  
  566.     int     i;        /* Misc. variable */
  567.  
  568.  
  569.  
  570.     /*
  571.      * Write each used symbol table entry.
  572.      */
  573.  
  574.     printf ("%s\n", message);
  575.  
  576.     for (i = 0; i < sym_tab_size; ++i) {
  577.         if (sym_tab_cache_ptr[i] != CACHE_FREE_ENTRY) {
  578.  
  579.             printf ("hash:%5d old:%5d new1:%5d ", i,
  580.                     files[OLD_FILE].sym_tab_index[i],
  581.                     files[NEW1_FILE].sym_tab_index[i]);
  582.  
  583.             if (file_count == 3) {
  584.                 printf ("new2:%5d ", files[NEW2_FILE].sym_tab_index[i]);
  585.             }
  586.  
  587.             if (sym_tab_cache_ptr[i] ==
  588.                 (cache_entry_type *) CACHE_NOT_IN_CACHE) {
  589.  
  590.                 printf ("(record not in cache)");
  591.             } else {
  592.                 if (sym_tab_cache_ptr[i] -> hash_code != i) {
  593.                     printf ("(cache_hash_code wrong: %d)",
  594.                         sym_tab_cache_ptr[i]->hash_code);
  595.                 }
  596.                 if (sym_tab_cache_ptr[i] -> record_length < 0) {
  597.                     sym_tab_cache_ptr[i] ->recordp[0] = '\0';
  598.                 } else {
  599.                     sym_tab_cache_ptr[i] ->
  600.                         recordp[sym_tab_cache_ptr[i] ->
  601.                         record_length] = '\0';
  602.                 }
  603.                 printf ("cache_record(%d): %s",
  604.                     sym_tab_cache_ptr[i] -> record_length,
  605.                     sym_tab_cache_ptr[i] -> recordp);
  606.             }
  607.  
  608.             printf ("\n");
  609.  
  610.         }
  611.     }
  612.  
  613. }
  614. /*
  615.  * init: Perform program initialization.
  616.  *
  617.  *
  618.  * This routine interprets the command line and opens the files.
  619.  *
  620.  * Return value:
  621.  *      This procedure has no return value.
  622.  */
  623.  
  624. void init (argc, argv)
  625. int     argc;            /* argument count from 'main' */
  626.  
  627. char  **argv;            /* arguments from 'main' */
  628.  
  629.  
  630. {
  631.  
  632.     char   *basename_ptr = 0;/* basename of files */
  633.  
  634.     int     cache_entry_size;/* Number of bytes in a cache entry */
  635.  
  636.     cache_entry_type * cache_ptr;/* Pointer to cache entry */
  637.  
  638.     int     different_basenames = 0;
  639.                 /* TRUE if file basenames are different */
  640.  
  641.     int     directory_count = 0;
  642.                 /* number of command line arguments which are
  643.                    actually directories */
  644.  
  645.     FILE * dummy_file;    /* can't assign to stdin on UNIX */
  646.  
  647.     long    etime;        /* Current time of day */
  648.  
  649.     int     is_directory[MAX_FILE_COUNT]; /* TRUE if file is a directory */
  650.  
  651.     int     i;        /* Misc. variable */
  652.     int     j;        /* Misc. variable */
  653.     int     k;        /* Misc. variable */
  654.  
  655.     int    max_record_len = LINE_LENGTH;    /* max initial record length */
  656.  
  657.     int     record_count;    /* Number of records in record array */
  658.  
  659.     struct stat     stat_buf;/* Buf. to find last written date */
  660.  
  661.     char   *the_cache;    /* Ptr to head of cache */
  662.  
  663.     char   *temp_ptr;    /* Misc char ptr */
  664.  
  665.     int     total_record_count;/* Total number of records in all files */
  666.  
  667.     int     c;        /* Option character */
  668.  
  669.     extern int      optind;    /* Option index */
  670.  
  671.     extern char    *optarg;    /* Option argument pointer */
  672.  
  673.     extern int      getopt ();/* getopt routine */
  674.  
  675.     extern char    *ctime ();/* convert time routine */
  676.  
  677.     extern char    *strrchr ();/* search for character in string */
  678.  
  679.  
  680. #ifdef VOS
  681.     stdout -> carriage_control = TRUE;
  682. #endif
  683.  
  684.     /*
  685.      * Scan options arguments.
  686.      */
  687.     (void) time (&etime);
  688.     (void) strcpy (exec_time, ctime (&etime));
  689.     exec_time[strlen (exec_time) - 1] = '\0'; /* remove newline character */
  690.  
  691.     for (;;) {
  692.  
  693.         c = getopt (argc, argv, "bBhsqc:d:p:P:1:2:");
  694.         if (c == EOF) {
  695.             break;
  696.         }
  697.  
  698.         switch (c) {
  699.  
  700.         /*
  701.          * B and b option: Blank remove and blank compress
  702.          * options.
  703.          */
  704.         case 'b':
  705.             blank_compress = TRUE;
  706.             compress_records = TRUE;
  707.             break;
  708.  
  709.         case 'B':
  710.             blank_remove = TRUE;
  711.             compress_records = TRUE;
  712.             break;
  713.         /*
  714.          * c option: Compare only specified columns.
  715.          */
  716.  
  717.         case 'c':
  718.             compress_records = TRUE;
  719.             if ((column_count + 1) == (MAX_COLUMNS)) {
  720.                 error ("Too many -c options");
  721.             }
  722.  
  723.             for (j = 0; isdigit (optarg[j]); ++j) {
  724.             }
  725.             if (j == 0) {
  726.                 error ("-c option not followed by number");
  727.             }
  728.             first_column[column_count] = atoi (optarg) - 1;
  729.             /* Zero relative */
  730.  
  731.             if (first_column[column_count] < 0) {
  732.                 error ("Column specification less than column 1");
  733.             }
  734.  
  735.             if (optarg[j] != ',') {
  736.                 error ("Column specifications not seperated by comma");
  737.             }
  738.  
  739.             optarg += j + 1;
  740.  
  741.             for (j = 0; isdigit (optarg[j]); ++j) {
  742.             }
  743.             if (j == 0) {
  744.                 error ("-c option not followed by two numbers");
  745.             }
  746.             last_column[column_count] = atoi (optarg) - 1;
  747.             /* Zero relative */
  748.             if (last_column[column_count] < first_column[column_count]) {
  749.                 error ("Last column spec. less then first column spec.");
  750.             }
  751.  
  752.             max_record_len = max(max_record_len,
  753.                              last_column[column_count] + 1);
  754.  
  755.             column_count++;
  756.             break;
  757.  
  758.         /*
  759.          * D option: Debug. Print debug output.
  760.          */
  761.         case 'd':
  762.             switch (*optarg) {
  763.             case 'a':
  764.                 pa_debug = TRUE;
  765.                 break;
  766.             case '1':
  767.                 p1_debug = TRUE;
  768.                 break;
  769.             case '2':
  770.                 p2_debug = TRUE;
  771.                 break;
  772.             case '3':
  773.                 p3_debug = TRUE;
  774.                 break;
  775.             case '4':
  776.                 p4_debug = TRUE;
  777.                 break;
  778.             case '5':
  779.                 p5_debug = TRUE;
  780.                 break;
  781.             default:
  782.                 error ("invalid argument following -d option");
  783.             }
  784.             break;
  785.  
  786.         /*
  787.          * h option: name of file to output HED edit file to
  788.          */
  789.         case 'h':
  790. #ifdef VOS
  791.             stdout -> carriage_control = FALSE;
  792. #endif
  793.             hed_flag = TRUE;
  794.             break;
  795.  
  796.         /*
  797.          * -P option: Number of prefix lines to output to listing file.
  798.          * -p option: Number of postfix lines to output to listing file.
  799.          */
  800.         case 'P':
  801.             prefix_lines = atoi (optarg);
  802.             if (prefix_lines > CACHE_ENTRIES - 10) {
  803.                 error ("Too many prefix lines");
  804.             }
  805.             break;
  806.  
  807.         case 'p':
  808.             postfix_lines = atoi (optarg);
  809.             break;
  810.  
  811.         /*
  812.          * -s option: Output page of statistics to stdout
  813.          */
  814.         case 's':
  815.             statistics_flag = TRUE;
  816.             break;
  817.  
  818.         /*
  819.          * -1 option: Text string to associate with 'new1' file.
  820.          * -2 option: Text string to associate with 'new2' file.
  821.          */
  822.         case '1':
  823.             files[NEW1_FILE].text_ptr = optarg;
  824.             break;
  825.  
  826.         case '2':
  827.             files[NEW2_FILE].text_ptr = optarg;
  828.             break;
  829.  
  830.         /*
  831.          * Q option: Quiet. Produce no output if no differences.
  832.          */
  833.         case 'q':
  834.             quiet_option = TRUE;
  835.             break;
  836.         }
  837.  
  838.     }
  839.  
  840.     /*
  841.      * Handle each command line argument.
  842.      */
  843.     for (i = optind; i < argc; ++i) {
  844.  
  845.         /*
  846.          * Handle redirections of 'stdin':
  847.          *
  848.          * This code won't get executed on a UNIX O.S. However,
  849.          * on VOS this code allows the same syntax to work.
  850.          */
  851.         if (argv[i][0] == '<' && argv[i][1] != '\0') {
  852.             dummy_file = freopen (&argv[i][1], "r", stdin);
  853.  
  854.             if (dummy_file == 0) {
  855.                     perror(&argv[i][1]);
  856.                 exit( 2 ) ;
  857.             }
  858.  
  859.         /*
  860.          * Handle redirections of 'stdout':
  861.          *
  862.          * This code won't get executed on a UNIX O.S. However, on VOS this
  863.          * code allows the same syntax to work.
  864.          */
  865.         } else if (argv[i][0] == '>' && argv[i][1] != '\0') {
  866.             dummy_file = freopen (&argv[i][1], "w", stdout);
  867.  
  868.             if (dummy_file == 0) {
  869.                     perror(&argv[i][1]);
  870.                     exit(2);
  871.             }
  872.  
  873.         /*
  874.          * Handle file arguments not preceeded by a specific option argument.
  875.          */
  876.         } else {
  877.  
  878.             if (file_count >= MAX_FILE_COUNT) {
  879.                 error ("Too many files specified");
  880.             }
  881.  
  882.             files[file_count].name_ptr = argv[i];
  883.  
  884.             stat (files[file_count].name_ptr, &stat_buf);
  885.             is_directory[file_count] =
  886.                 (stat_buf.st_mode & S_IFMT) == S_IFDIR;
  887.             if (is_directory[file_count]) {
  888.                 directory_count++;
  889.             } else {
  890.                 temp_ptr = strrchr (argv[i], '/');
  891.                 if (temp_ptr == 0) {
  892.                     temp_ptr = argv[i];
  893.                 }
  894.                 if (basename_ptr &&
  895.                     strcmp (temp_ptr, basename_ptr) != 0) {
  896.                     different_basenames = 1;
  897.                 }
  898.                 basename_ptr = temp_ptr;
  899.             }
  900.  
  901.             file_count++;
  902.         }
  903.     }
  904.  
  905.     /*
  906.      * Resolve actual file names and open files.
  907.      *
  908.      * The name specified on the command line might be a directory name.
  909.      */
  910.  
  911.     if (file_count < 2) {
  912.         error ("not enough files specified");
  913.     }
  914.     if (file_count == directory_count) {
  915.         error ("cannot compare directories");
  916.     }
  917.     if (directory_count != 0 &&
  918.             file_count - directory_count > 1 &&
  919.             different_basenames) {
  920.         error ("ambiguous directory name");
  921.     }
  922.  
  923.     total_record_count = 0;
  924.     for (i = 0; i < file_count; ++i) {
  925.  
  926.         if (is_directory[i]) {
  927.             temp_ptr = mem_alloc (strlen (files[i].name_ptr) +
  928.                     strlen (basename_ptr) + 2);
  929.             sprintf (temp_ptr, "%s/%s", files[i].name_ptr,
  930.                     basename_ptr);
  931.             files[i].name_ptr = temp_ptr;
  932.         }
  933.  
  934. #ifdef VOS
  935.         files[i].seq_fd =
  936.             fopen (files[i].name_ptr, "r", max_record_len, "s", $OPEN_DB);
  937.         files[i].rnd_fd =
  938.             fopen (files[i].name_ptr, "r", max_record_len, "s", $OPEN_RMAI);
  939. #else
  940.         files[i].seq_fd = fopen (files[i].name_ptr, "r");
  941.         files[i].rnd_fd = fopen (files[i].name_ptr, "r");
  942. #endif
  943.  
  944.         if (files[i].seq_fd == 0 || files[i].rnd_fd == 0) {
  945.             perror(files[i].name_ptr);
  946.             exit(2);
  947.         }
  948.  
  949.         fstat (fileno (files[i].seq_fd), &stat_buf);
  950.  
  951.         temp_ptr = ctime (&(stat_buf.st_mtime));
  952.         temp_ptr[strlen (temp_ptr) - 1] = '\0';
  953.         files[i].lw_ptr = mem_alloc (strlen (temp_ptr) + 1);
  954.         strcpy (files[i].lw_ptr, temp_ptr);
  955.  
  956.         setvbuf (files[i].seq_fd, mem_alloc (stat_buf.st_blksize),
  957.             _IOFBF, stat_buf.st_blksize);
  958.         setvbuf (files[i].rnd_fd, mem_alloc (stat_buf.st_blksize),
  959.             _IOFBF, stat_buf.st_blksize);
  960.  
  961.         /* estimate record count by assuming 20 chars per record */
  962.         /* Don't allow overly small record counts */
  963.         record_count = max( stat_buf.st_size / 20, RA_ORIG);
  964.         files[i].record_array_alloc = record_count;
  965.         total_record_count += record_count;
  966.  
  967.         files[i].record = (record_type *)
  968.             mem_alloc (record_count * sizeof (record_type));
  969.  
  970.     }
  971.  
  972.     /*
  973.      * Sort column ranges into ascending order.
  974.      */
  975.     for (i = 0; i + 1 < column_count; ++i) {
  976.         for (j = i + 1; j < column_count; ++j) {
  977.             if (first_column[i] > first_column[j]) {
  978.                 k = first_column[i];
  979.                 first_column[i] = first_column[j];
  980.                 first_column[j] = k;
  981.                 k = last_column[i];
  982.                 last_column[i] = last_column[j];
  983.                 last_column[j] = k;
  984.             }
  985.         }
  986.     }
  987.  
  988.     /*
  989.      * Ensure there are no overlapping column ranges.
  990.      */
  991.     for (i = 0; i + 1 < column_count; ++i) {
  992.         if (last_column[i] >= first_column[i + 1]) {
  993.             error ("overlaping column ranges specified");
  994.         }
  995.     }
  996.  
  997.     /*
  998.      * Allocate cache entries.
  999.      *
  1000.      * Cache entries include an extra word at the end of the buffer.
  1001.      * This word allows a word of blanks to be inserted after the end
  1002.      * of each read line. This, in turn, allows hash code computations
  1003.      * and line comparisons to be word oriented rather than byte oriented.
  1004.      *
  1005.      * The cache is allocated in one chunk below for two reasons:
  1006.      *    1) For small files the huge number of allocations consumes
  1007.      *     significant time.
  1008.      *    2) Less memory is used since mem_alloc allocates a block
  1009.      *       which is larger than is actually requested. (The next larger
  1010.      *       power of two.)
  1011.      */
  1012.     cache_entry_size =
  1013.         sizeof (cache_entry_type) + sizeof (int) + max_record_len;
  1014.     cache_entry_size += sizeof (int) - (cache_entry_size % sizeof (int));
  1015.     the_cache = mem_alloc (CACHE_ENTRIES * cache_entry_size);
  1016.     for (i = 0; i < CACHE_ENTRIES; ++i) {
  1017.         cache_ptr = (cache_entry_type *) the_cache;
  1018.         cache_ptr -> recordp = the_cache + sizeof(cache_entry_type);
  1019.         cache_ptr -> record_alen = cache_entry_size -
  1020.                     sizeof(cache_entry_type);
  1021.         cache_ptr -> hash_code = HASH_FREE_ENTRY;
  1022.         enq_head_dll (cache_head_ptr, cache_tail_ptr, cache_ptr,
  1023.                 cache_next_ptr, cache_prev_ptr);
  1024.         the_cache += cache_entry_size;
  1025.     }
  1026.  
  1027.     /*
  1028.      * Compute size of symbol table.
  1029.      *
  1030.      * 1) Initially quess size of symbol table as the sum of the number of
  1031.      *    records in all of the input files times 2.
  1032.      * 2) Never allocate a symbol table of less than 1024 entries. (This step
  1033.      *    is required due to the organization of the prime number table.)
  1034.      * 3) Round the size down to a multiple of 1024. (This tries to force the
  1035.      *    symbol table to be an integer number of pages. It also limits the
  1036.      *    size of the prime number table).
  1037.      * 4) Round the size down to a prime number. (The hashing algorithm requires*
  1038.      *    that the size of the table is a prime number).
  1039.      */
  1040.     sym_tab_size = total_record_count * 2;
  1041.     sym_tab_size = max (1024, sym_tab_size);
  1042.  
  1043.     /* Prime number table contains only those primes which are less than
  1044.        and closest to a multiple of 1024 */
  1045.     for (i = 1; primes[i] != -1; ++i) {
  1046.         if (sym_tab_size < primes[i]) {
  1047.             break;
  1048.         }
  1049.     }
  1050.  
  1051.     sym_tab_size = primes[i - 1];
  1052.  
  1053.     /*
  1054.      * Allocate symbol table.
  1055.      */
  1056.     for (i = 0; i < file_count; ++i) {
  1057.         files[i].sym_tab_index = (int *) mem_alloc (sym_tab_size * sizeof (int));
  1058.     }
  1059.  
  1060.     sym_tab_cache_ptr = (cache_entry_type **)
  1061.         mem_alloc (sym_tab_size * sizeof (cache_entry_type *));
  1062.  
  1063. }
  1064. /*
  1065.  * link_records: link two records together.
  1066.  *
  1067.  * This routine links a record in the current file to a record in the
  1068.  * corresponding file.
  1069.  *
  1070.  * If either of these records are already
  1071.  * linked to a record in the other file, finish up all of the
  1072.  * linkages. Pass5 considers it an inconsistent state if only two of
  1073.  * the three linkages between files are made. Usually, this inconsistent
  1074.  * state will clear itself up. However, certain input files will indeed
  1075.  * allow the inconsistency to remain.
  1076.  *
  1077.  * Note: This routine also discovers an attempt to link records in an
  1078.  * impossible fashion. Suppose, this record in the 'current' file is
  1079.  * already linked to record A in the 'other' file. This record in the
  1080.  * 'corresponding' file is already linked to record B in the 'other' file.
  1081.  * Any attempt to link the current and corresponding records would
  1082.  * require that record A and record B be the same record (impossible).
  1083.  * In that circumstance, this routine acts as a no-op. The calling
  1084.  * routine is not informed since this new information wouldn't change the
  1085.  * decision making process which it is going through.
  1086.  *
  1087.  * Return value:
  1088.  *      This procedure has no return value.
  1089.  */
  1090. void link_records (match_no, index1, index2)
  1091. int     match_no;        /* input */
  1092.                  /* Which relationship is being scanned */
  1093.  
  1094. int     index1;            /* Index into the current file of the record to
  1095.                    link. */
  1096.  
  1097. int     index2;            /* Index into the corresponding file of the
  1098.                    record to link. */
  1099.  
  1100. {
  1101.  
  1102.     file_type * file1_ptr;    /* First file - current_file */
  1103.  
  1104.     file_type * file2_ptr;    /* Second file - corresponding file */
  1105.  
  1106.     file_type * file3_ptr;    /* Third file - other file */
  1107.  
  1108.     int     file1_sub;    /* For each record of the first file, this is a
  1109.                    subscript of the 'value' array of the
  1110.                    relationship between file1 and file2 */
  1111.  
  1112.     int     file2_sub;    /* For each record of the second file, this is
  1113.                    a subscript of the 'value' array of the
  1114.                    relationship between file2 and file1 */
  1115.  
  1116.     int     file3_sub;    /* For each record of the third file, this is a
  1117.                    subscript of the 'value' array of the
  1118.                    relationship between file3 and file1 */
  1119.  
  1120.     int     hash_code;    /* Hash code for the record being linked. */
  1121.  
  1122.     int     index3;        /* Index into record array of file3 is the
  1123.                    'next' record in file3 */
  1124.  
  1125.     int    *other_val1_ptr;    /* Pointer to the 'value' field in the record
  1126.                    on file1. This is the 'value' which
  1127.                    indicates the relationship to file3. */
  1128.  
  1129.     int    *other_val2_ptr;    /* Pointer to the 'value' field in the record
  1130.                    on file2. This is the 'value' which
  1131.                    indicates the relationship to file3. */
  1132.  
  1133.     int    *val1_ptr;    /* Pointer to the 'value' field in record on
  1134.                    file1. This is the 'value' which indicates
  1135.                    the relationship to file2. */
  1136.  
  1137.     int    *val2_ptr;    /* Pointer to the 'value' field in record on
  1138.                    file2. This is the 'value' which indicates
  1139.                    the relationship to file1. */
  1140.  
  1141.     int    *val3_ptr;    /* Pointer to the 'value' field in record on
  1142.                    file3. */
  1143.  
  1144.  
  1145.  
  1146.     /*
  1147.      * Set up misc local variables.
  1148.      */
  1149.  
  1150.     if (p3_debug || p4_debug) {
  1151.         printf ("link_records: matchno: %d indices: %d %d\n",
  1152.                 match_no, index1, index2);
  1153.     }
  1154.  
  1155.     file1_ptr = &files[curr_file[match_no]];
  1156.     file2_ptr = &files[corres_file[match_no]];
  1157.     file1_sub = value_sub[match_no];
  1158.     file2_sub = rev_value_sub[match_no];
  1159.  
  1160.     /*
  1161.      * Link the two records together.
  1162.      */
  1163.  
  1164.     val1_ptr = &(file1_ptr -> record[index1].value[file1_sub]);
  1165.     val2_ptr = &(file2_ptr -> record[index2].value[file2_sub]);
  1166.  
  1167.     hash_code = *val1_ptr;
  1168.     *val1_ptr = index2;
  1169.     *val2_ptr = index1;
  1170.  
  1171.     /*
  1172.      * If either of these two records are already linked to the third file,
  1173.      *     connect these two record to the record in the third file.
  1174.      */
  1175.  
  1176.     other_val1_ptr =
  1177.         &(file1_ptr -> record[index1].value[other_sub (file1_sub)]);
  1178.     other_val2_ptr =
  1179.         &(file2_ptr -> record[index2].value[other_sub (file2_sub)]);
  1180.  
  1181.     if (is_hash_code (*other_val1_ptr)) {
  1182.         if (*other_val1_ptr != hash_code) {
  1183.             error ("hash code mis-match 1");
  1184.         }
  1185.         if (is_hash_code (*other_val2_ptr)) {
  1186.             if (*other_val2_ptr != hash_code) {
  1187.                 error ("hash code mis-match 2");
  1188.             }
  1189.             return;
  1190.         } else {
  1191.             index3 = *other_val2_ptr;
  1192.             *other_val1_ptr = index3;
  1193.         }
  1194.     } else {
  1195.         index3 = *other_val1_ptr;
  1196.         if (is_hash_code (*other_val2_ptr)) {
  1197.             if (*other_val2_ptr != hash_code) {
  1198.                 error ("hash code mis-match 3");
  1199.             }
  1200.             *other_val2_ptr = index3;
  1201.         } else {
  1202.             if (*other_val1_ptr != *other_val2_ptr) {
  1203.                 /* error( "other file index mismatch 1" ) ; */
  1204.                 /* In this error condition, just undo what
  1205.                    we've already done */
  1206.                 *val1_ptr = hash_code;
  1207.                 *val2_ptr = hash_code;
  1208.                 return;
  1209.             }
  1210.         }
  1211.     }
  1212.  
  1213.     /*
  1214.      * Connect the record in the third file to the record in the first file.
  1215.      */
  1216.     file3_ptr = &files[other_file[match_no]];
  1217.     file3_sub = other_value_sub[match_no];
  1218.     val3_ptr = &(file3_ptr -> record[index3].value[file3_sub]);
  1219.  
  1220.     if (is_hash_code (*val3_ptr)) {
  1221.         if (*val3_ptr != hash_code) {
  1222.             error ("hash code mis-match 4");
  1223.         }
  1224.         *val3_ptr = index1;
  1225.     } else {
  1226.         if (*val3_ptr != index1) {
  1227.             error ("other file index mismatch 2");
  1228.         }
  1229.     }
  1230.  
  1231.     /*
  1232.      * Connect the record in the third file to the record in the second file.
  1233.      */
  1234.  
  1235.     val3_ptr =
  1236.         &(file3_ptr -> record[index3].value[other_sub (file3_sub)]);
  1237.  
  1238.     if (is_hash_code (*val3_ptr)) {
  1239.         if (*val3_ptr != hash_code) {
  1240.             error ("hash code mis-match 5");
  1241.         }
  1242.         *val3_ptr = index2;
  1243.     } else {
  1244.         if (*val3_ptr != index2) {
  1245.             error ("other file index mismatch 3");
  1246.         }
  1247.     }
  1248.  
  1249. }
  1250. /*
  1251.  * error: output fatal error message
  1252.  *
  1253.  * This routine outputs an error message and terminates.
  1254.  *
  1255.  * Return value:
  1256.  *      This procedure has no return value.
  1257.  */
  1258.  
  1259. void error (error_ptr)
  1260. char   *error_ptr;        /* input */
  1261.  /* Record to output. */
  1262.  
  1263. {
  1264.     fprintf (stderr, "combine: %s.\n", error_ptr);
  1265.     exit (2);
  1266. }
  1267. /*
  1268.  * mem_alloc: allocate memory
  1269.  *
  1270.  * This routine uses the standard memory allocator, heowever, if memory
  1271.  * is not available, this routine outputs an error message and terminates.
  1272.  *
  1273.  * Return value:
  1274.  *      This procedure returns a pointer to the allocated block.
  1275.  */
  1276. char   *mem_alloc (size)
  1277. int     size;            /* input */
  1278.                 /* Size (in bytes) of the block to allocate */
  1279.  
  1280. {
  1281.  
  1282.     char   *block_ptr;    /* Misc. variable */
  1283.  
  1284.     extern char    *malloc ();
  1285.  
  1286.     block_ptr = malloc (size);
  1287.     if (block_ptr == 0) {
  1288.         error ("not enough memory -- files too big");
  1289.     }
  1290.  
  1291.     return (block_ptr);
  1292.  
  1293. }
  1294.  
  1295. /*
  1296.  * reread_into_cache -- re-read a record from a file into a cache entry
  1297.  *
  1298.  * This routine is used to re-read a record (which has previously been
  1299.  * read) into a cache entry.
  1300.  */
  1301. void reread_into_cache( file_ptr, index, cache_ptr )
  1302.     file_type * file_ptr;        /* file to be read from */
  1303.     int    index;            /* record number to read */
  1304.     cache_entry_type * cache_ptr;    /* cache entry to read into */
  1305. {
  1306.     int status;
  1307.     char mbuffer[LINE_LENGTH];
  1308.  
  1309.     status = fseek (file_ptr->rnd_fd, file_ptr->record[index].rfa, 0);
  1310.     if ( status == -1 ) {
  1311.         (void) sprintf (mbuffer, "Disk error while seeking '%s'",
  1312.                 file_ptr -> name_ptr);
  1313.         error (mbuffer);
  1314.     }
  1315.  
  1316.     status = read_into_cache(file_ptr->rnd_fd,
  1317.             file_ptr->record[index].rfa,
  1318.             cache_ptr);
  1319.  
  1320.     if (status < 0) {
  1321.         (void) sprintf (mbuffer, "Disk error while re-reading '%s'",
  1322.                 file_ptr -> name_ptr);
  1323.         error (mbuffer);
  1324.     }
  1325. }
  1326.  
  1327. /*
  1328.  * read_into_cache -- read a record from a file into a cache entry
  1329.  *
  1330.  * Read a record into a cache entry. This routine reads an entire record
  1331.  * into the cache entry. If the currently allocated buffer is too small,
  1332.  * a larger buffer will be allocated.
  1333.  *
  1334.  * Return Value:
  1335.  *    Byte count read (-1 for EOF)
  1336.  */
  1337. int read_into_cache( fp, rfa, cache_ptr)
  1338.     FILE    *fp;            /* File to read */
  1339.     rfa_type    rfa;        /* rfa to read (already positioned) */
  1340.     cache_entry_type * cache_ptr;    /* cache entry to read into */
  1341. {
  1342.     char    c;
  1343.     char   *char_ptr;
  1344.     int    status;
  1345.     int     i;
  1346.  
  1347.     char_ptr = fgets (cache_ptr->recordp, cache_ptr->record_alen, fp);
  1348.     if (char_ptr == NULL)
  1349.         return (-1);
  1350.  
  1351.     i = strlen (cache_ptr->recordp) - 1;
  1352.     if (cache_ptr->recordp[i] != '\n') {
  1353.         status = fseek (fp, rfa, 0);
  1354.         if ( status == -1 ) 
  1355.             error("Internal error: cannot reseek");
  1356.         for (i=0;;i++) {
  1357.             c = getc (fp);
  1358.             if (feof (fp)) {
  1359.             /* not (c==EOF) because of binary files */
  1360.                 break;
  1361.             /* This is sort of a kludge, we only check for
  1362.                non-ascii if the record length is too long */
  1363.             } else if (!isascii (c) || c == '\0' ) {
  1364.                 error ("non-ascii character in file");
  1365.             } else if (c == '\n') {
  1366.                 break;
  1367.             }
  1368.         }
  1369. #ifdef notdef     /* The i+=sizeof(int) covers this already */
  1370.         i+=2;    /* Leave room from newline and null byte */
  1371. #endif notdef
  1372.         i+=sizeof(int); /* leave space at end for extra nulls for
  1373.                    checksum algorithm */
  1374.         i += sizeof (int) - (i % sizeof (int));
  1375.  
  1376.         /*
  1377.          * Don't deallocate the old buffer since it was probably
  1378.          * allocated as a part of a larger buffer.
  1379.          */
  1380.         cache_ptr->recordp = mem_alloc(i);
  1381.         cache_ptr->record_alen = i;
  1382.  
  1383.         status = fseek (fp, rfa, 0);
  1384.         if ( status == -1 ) 
  1385.             error("Internal error: cannot reseek");
  1386.  
  1387.         char_ptr = fgets (cache_ptr->recordp, cache_ptr->record_alen, fp);
  1388.         if (char_ptr == NULL)
  1389.             return (-1);
  1390.  
  1391.         i = strlen (cache_ptr->recordp) - 1;
  1392.         /* Perhaps we should warn about this */
  1393.         if (cache_ptr->recordp[i] != '\n')
  1394.             i++;
  1395.     }
  1396.     cache_ptr->recordp[i] = '\0';
  1397.     cache_ptr->record_length = i;
  1398.  
  1399.     return (i);
  1400.  
  1401. }
  1402. @//E*O*F main.c//
  1403. chmod u=rw,g=rw,o=rw main.c
  1404.  
  1405. echo x - os_dep.h
  1406. sed 's/^@//' > "os_dep.h" <<'@//E*O*F os_dep.h//'
  1407. /*
  1408.  * The combine utility is a product of Harris, Inc. and is provided for
  1409.  * unrestricted use provided that this legend is included on all tape
  1410.  * media and as a part of the software program in whole or part.  Users
  1411.  * may copy, modify, license or distribute the combine utility without charge.
  1412.  * 
  1413.  * THE COMBINE UTILITY IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
  1414.  * INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
  1415.  * PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
  1416.  * PRACTICE.
  1417.  * 
  1418.  * The combine utility is provided with no support and without any obligation
  1419.  * on the part of Harris, Inc. to assist in its use, correction,
  1420.  * modification or enhancement.
  1421.  * 
  1422.  * HARRIS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  1423.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THE COMBINE
  1424.  * UTILITY OR ANY PART THEREOF.
  1425.  * 
  1426.  * In no event will Harris, Inc. be liable for any lost revenue
  1427.  * or profits or other special, indirect and consequential damages, even if
  1428.  * Harris has been advised of the possibility of such damages.
  1429.  * 
  1430.  * Harris Computer Systems Division
  1431.  * 2101 W Cypress Creek Rd
  1432.  * Fort Lauderdale, Florida 33309
  1433.  */
  1434. #define STS_type int
  1435. #define rfa_type int
  1436.  
  1437. #define SZ$_FILE_NAME 256
  1438. #ifdef VOS    /* 24-bit word */
  1439. #define HI7       077400000    /* Hi 7 bits of a word */
  1440. #else        /* 32-bit word */
  1441. #define HI7       0xFE000000    /* Hi 7 bits of a word */
  1442. #endif
  1443.  
  1444. #define SS_NORMAL                 1
  1445. #define SS_READ_ERR               2
  1446. #define SS_WRITE_ERR              3
  1447. #define SS_EOT                    4
  1448. #define SS_SEEK_ERR          5
  1449.  
  1450. extern  STS_type os_dep_file_seek ();
  1451. extern  rfa_type os_dep_file_pos ();
  1452. extern int      os_dep_file_open ();
  1453. extern  STS_type os_dep_file_close ();
  1454. extern  STS_type os_dep_file_write ();
  1455. extern  STS_type os_dep_file_read ();
  1456. @//E*O*F os_dep.h//
  1457. chmod u=rw,g=rw,o=rw os_dep.h
  1458.  
  1459. echo x - pass2.c
  1460. sed 's/^@//' > "pass2.c" <<'@//E*O*F pass2.c//'
  1461. /*
  1462.  * The combine utility is a product of Harris, Inc. and is provided for
  1463.  * unrestricted use provided that this legend is included on all tape
  1464.  * media and as a part of the software program in whole or part.  Users
  1465.  * may copy, modify, license or distribute the combine utility without charge.
  1466.  * 
  1467.  * THE COMBINE UTILITY IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
  1468.  * INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
  1469.  * PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
  1470.  * PRACTICE.
  1471.  * 
  1472.  * The combine utility is provided with no support and without any obligation
  1473.  * on the part of Harris, Inc. to assist in its use, correction,
  1474.  * modification or enhancement.
  1475.  * 
  1476.  * HARRIS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  1477.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THE COMBINE
  1478.  * UTILITY OR ANY PART THEREOF.
  1479.  * 
  1480.  * In no event will Harris, Inc. be liable for any lost revenue
  1481.  * or profits or other special, indirect and consequential damages, even if
  1482.  * Harris has been advised of the possibility of such damages.
  1483.  * 
  1484.  * Harris Computer Systems Division
  1485.  * 2101 W Cypress Creek Rd
  1486.  * Fort Lauderdale, Florida 33309
  1487.  */
  1488. #include <stdio.h>
  1489. #include <ctype.h>
  1490. #include "util.h"
  1491. #include "combine.h"
  1492. /*
  1493.  * pass2: Determine anchor points in the files.
  1494.  *
  1495.  * This routine identifies lines which occur precisely once in atleast two
  1496.  * files and no more than once in the third file. All such lines are
  1497.  * anchor points for passes 3 and 4.
  1498.  *
  1499.  * This routine scans the symbol table. For each record which meets the
  1500.  * above criteria, links are made in the record arrays to associate
  1501.  * anchor records with each other.
  1502.  *
  1503.  * Return value:
  1504.  *      This procedure has no return value.
  1505.  */
  1506.  
  1507. void pass2 () {
  1508.  
  1509.     register int     good_files; /* Number of files that a record is unique in. */
  1510.  
  1511.     register int     hash_code;    /* Index into symbol table */
  1512.  
  1513.     register int     i;        /* Misc. variable */
  1514.  
  1515.     int     indexes[MAX_FILE_COUNT];/* Index into each record array */
  1516.  
  1517.     /*
  1518.      * Clear the indexes for all non-existant files.
  1519.      */
  1520.  
  1521.     for (i = file_count; i < MAX_FILE_COUNT; ++i) {
  1522.         indexes[i] = 0;
  1523.     }
  1524.  
  1525.     /*
  1526.      * Set up a pseudo line at the front and end of each file as an
  1527.      * anchor point.
  1528.      */
  1529.  
  1530.     for (i = 0; i < MATCH_COUNT; ++i) {
  1531.         if (files[curr_file[i]].record != 0 &&
  1532.                 files[corres_file[i]].record != 0) {
  1533.  
  1534.             files[curr_file[i]].record[BEGIN_INDEX].
  1535.                 value[value_sub[i]] = BEGIN_INDEX;
  1536.             files[curr_file[i]].
  1537.                 record[files[curr_file[i]].record_array_size-1].
  1538.                 value[value_sub[i]] =
  1539.                 files[corres_file[i]].record_array_size - 1;
  1540.  
  1541.         }
  1542.     }
  1543.  
  1544.     /*
  1545.      * Test each entry in the symbol table.
  1546.      */
  1547.  
  1548.     for (hash_code = 1; hash_code < sym_tab_size; ++hash_code) {
  1549.  
  1550.         /*
  1551.          * Quickly see if the hash code is used at all
  1552.          */
  1553.         if ( sym_tab_cache_ptr[hash_code] == CACHE_FREE_ENTRY ){
  1554.             continue;
  1555.         }
  1556.  
  1557.  
  1558.         /*
  1559.          * Ensure the record occurs at most once in all files.
  1560.          *
  1561.          * This code counts the number of files a unique record is found in.
  1562.          * If the record does not exist precisely once in atleast two files or
  1563.          * if the record is not unique in any file, then the record cannot be
  1564.          * an anchor record.
  1565.          */
  1566.  
  1567.         good_files = 0;    /* Assume the record exists in no files */
  1568.         for (i = 0; i < file_count; ++i) {
  1569.             indexes[i] = files[i].sym_tab_index[hash_code];
  1570.             /* if record is not unique in this file */
  1571.             if (indexes[i] < 0) {
  1572.                 good_files = 0;
  1573.                 break;
  1574.             /* if record is unique in this file */
  1575.             } else if (indexes[i] > 0) {
  1576.                 good_files++;
  1577.             }
  1578.         }
  1579.  
  1580.         if (good_files < 2) {/* Record not unique in enough files */
  1581.             continue;
  1582.         }
  1583.  
  1584.         /*
  1585.          * Link up anchors between any two files.
  1586.          *
  1587.          * If the current file and the corresponding file both contain
  1588.          * the same unique line. Link the current file to the
  1589.          * corresponding file.
  1590.          */
  1591.  
  1592.         for (i = 0; i < MATCH_COUNT; ++i) {
  1593.  
  1594.             if (indexes[curr_file[i]] > 0 &&
  1595.                 indexes[corres_file[i]] > 0) {
  1596.  
  1597.                 files[curr_file[i]].
  1598.                     record[indexes[curr_file[i]]].
  1599.                     value[value_sub[i]] =
  1600.                     indexes[corres_file[i]];
  1601.  
  1602.             }
  1603.  
  1604.         }
  1605.  
  1606.     }
  1607.  
  1608. }
  1609. @//E*O*F pass2.c//
  1610. chmod u=rw,g=rw,o=rw pass2.c
  1611.  
  1612. echo x - unix.c
  1613. sed 's/^@//' > "unix.c" <<'@//E*O*F unix.c//'
  1614. /*
  1615.  * The combine utility is a product of Harris, Inc. and is provided for
  1616.  * unrestricted use provided that this legend is included on all tape
  1617.  * media and as a part of the software program in whole or part.  Users
  1618.  * may copy, modify, license or distribute the combine utility without charge.
  1619.  * 
  1620.  * THE COMBINE UTILITY IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
  1621.  * INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
  1622.  * PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
  1623.  * PRACTICE.
  1624.  * 
  1625.  * The combine utility is provided with no support and without any obligation
  1626.  * on the part of Harris, Inc. to assist in its use, correction,
  1627.  * modification or enhancement.
  1628.  * 
  1629.  * HARRIS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  1630.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THE COMBINE
  1631.  * UTILITY OR ANY PART THEREOF.
  1632.  * 
  1633.  * In no event will Harris, Inc. be liable for any lost revenue
  1634.  * or profits or other special, indirect and consequential damages, even if
  1635.  * Harris has been advised of the possibility of such damages.
  1636.  * 
  1637.  * Harris Computer Systems Division
  1638.  * 2101 W Cypress Creek Rd
  1639.  * Fort Lauderdale, Florida 33309
  1640.  */
  1641. #include <sys/types.h>
  1642. #include <sys/uio.h>
  1643. #include <sys/socket.h>
  1644. #include <sys/ioctl.h>
  1645. #include <sys/file.h>
  1646. #include "util.h"
  1647. #include "os_dep.h"
  1648. #include <stdio.h>
  1649. #include <ctype.h>
  1650.  
  1651. /*
  1652.  * os_dep_file_pos -- Return current position in file
  1653.  *
  1654.  * This routine returns the current position in the file. The value
  1655.  * returned is suitable for passing to os_dep_file_seek and is suitable
  1656.  * for no other purpose.
  1657.  *
  1658.  * Return value:
  1659.  *   Current position in the file.
  1660.  */
  1661. rfa_type os_dep_file_pos (fp)
  1662. FILE * fp;
  1663. {
  1664.  
  1665.     return (ftell (fp));
  1666. }
  1667. /*
  1668.  * os_dep_file_read -- Read a record from a file.
  1669.  *
  1670.  * This routine reads a record from a file.
  1671.  *
  1672.  * Return value:
  1673.  *   SS_NORMAL      Operation performed as requested.
  1674.  *   SS_EOT         End of disk area reached
  1675.  *   SS_READ_ERR    Error on read operation.
  1676.  *   SS_SEEK_ERR    Seek error occured
  1677.  *
  1678.  */
  1679.  
  1680. STS_type os_dep_file_read (fp,
  1681.     buf_ptr,
  1682.     buf_len,
  1683.     ret_len_ptr)
  1684. FILE * fp;
  1685. char   *buf_ptr;
  1686. int     buf_len;
  1687.  
  1688. int    *ret_len_ptr;    /* output */
  1689.              /* Returns the number of bytes read. */
  1690.              /* A value of -1 indicates that an EOF record was read */
  1691.  
  1692. {
  1693.     char    c;
  1694.     char   *char_ptr;
  1695.     STS_type status;
  1696.     int     i;
  1697.  
  1698.  
  1699.     if (TRUE) {
  1700.  
  1701.         char_ptr = fgets (buf_ptr, buf_len + 2, fp);
  1702.         /* printf( "(%d:%d) %s", buf_len, strlen( buf_ptr), buf_ptr ) ;
  1703.         */
  1704.  
  1705.         if (char_ptr == NULL) {
  1706.             return (SS_EOT);
  1707.         }
  1708.  
  1709.         i = strlen (buf_ptr) - 1;
  1710.         if (buf_ptr[i] != '\n') {
  1711.             i++;
  1712.             for (;;) {
  1713.                 c = getc (fp);
  1714.                 if (feof (fp)) {
  1715.                 /* not (c==EOF) because of binary files */
  1716.                     break;
  1717.                 } else if (c == '\n') {
  1718.                     break;
  1719.                 }
  1720.             }
  1721.         }
  1722.         *ret_len_ptr = i;
  1723.         buf_ptr[i] = '\0';
  1724.  
  1725.     } else {
  1726.  
  1727.         status = read (fileno (fp), buf_ptr, buf_len);
  1728.         if (status == (-1)) {
  1729.             return (SS_READ_ERR);
  1730.         }
  1731.  
  1732.     }
  1733.  
  1734.     return (SS_NORMAL);
  1735.  
  1736. }
  1737.  
  1738. /*
  1739.  *
  1740.  * os_dep_file_seek -- Seek to record within file
  1741.  *
  1742.  * This routine seek to a particular record within a file.
  1743.  *
  1744.  * Return value:
  1745.  *   SS_NORMAL      Operation performed as requested.
  1746.  *   SS_SEEK_ERR    Error on seek operation.
  1747.  *
  1748.  */
  1749.  
  1750. STS_type os_dep_file_seek (fp, rfa)
  1751. FILE * fp;
  1752. rfa_type rfa;
  1753.  
  1754. {
  1755.  
  1756.     STS_type status;
  1757.  
  1758.  
  1759.  
  1760.     status = fseek (fp, rfa, 0);
  1761.  
  1762.     if (status == -1) {
  1763.         return (SS_SEEK_ERR);
  1764.     } else {
  1765.         return (SS_NORMAL);
  1766.     }
  1767.  
  1768. }
  1769. /*
  1770.  *
  1771.  * os_dep_file_write -- Write a record to a file.
  1772.  *
  1773.  * This routine writes a record to a file.
  1774.  *
  1775.  * Return value:
  1776.  *   SS_NORMAL      Operation performed as requested.
  1777.  *   SS_WRITE_ERR   Error on write operation.
  1778.  *
  1779.  */
  1780.  
  1781. STS_type os_dep_file_write (fp,
  1782.     buf_ptr,
  1783.     buf_len)
  1784. FILE * fp;
  1785. char   *buf_ptr;
  1786. int     buf_len;        /* buffer length in bytes */
  1787.  
  1788. {
  1789.  
  1790.  
  1791.  
  1792.     buf_ptr[buf_len] = '\0';
  1793.     fputs (buf_ptr, fp);
  1794.     fputc ('\n', fp);
  1795.     return (SS_NORMAL);
  1796.  
  1797. }
  1798. @//E*O*F unix.c//
  1799. chmod u=rw,g=rw,o=rw unix.c
  1800.  
  1801. echo x - util.h
  1802. sed 's/^@//' > "util.h" <<'@//E*O*F util.h//'
  1803. /*
  1804.  * The combine utility is a product of Harris, Inc. and is provided for
  1805.  * unrestricted use provided that this legend is included on all tape
  1806.  * media and as a part of the software program in whole or part.  Users
  1807.  * may copy, modify, license or distribute the combine utility without charge.
  1808.  * 
  1809.  * THE COMBINE UTILITY IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND
  1810.  * INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
  1811.  * PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
  1812.  * PRACTICE.
  1813.  * 
  1814.  * The combine utility is provided with no support and without any obligation
  1815.  * on the part of Harris, Inc. to assist in its use, correction,
  1816.  * modification or enhancement.
  1817.  * 
  1818.  * HARRIS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
  1819.  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THE COMBINE
  1820.  * UTILITY OR ANY PART THEREOF.
  1821.  * 
  1822.  * In no event will Harris, Inc. be liable for any lost revenue
  1823.  * or profits or other special, indirect and consequential damages, even if
  1824.  * Harris has been advised of the possibility of such damages.
  1825.  * 
  1826.  * Harris Computer Systems Division
  1827.  * 2101 W Cypress Creek Rd
  1828.  * Fort Lauderdale, Florida 33309
  1829.  */
  1830. /*
  1831.  * Type extensions and abbreviations This creates the HOS environment. Such
  1832.  * things as bit_type remain unexported.
  1833.  */
  1834.  
  1835. #define uint   unsigned int
  1836.  
  1837. #define ABS(x)   ( (x) >= 0 ? (x) : -(x) )
  1838. #define MOD(x,y) ( (x) % (y) )
  1839. #define mod(x,y) MOD(x,y)
  1840.  
  1841. #define  min(_a,_b) ( (_a) < (_b) ? (_a) : (_b) )
  1842. #define  max(_a,_b) ( (_a) > (_b) ? (_a) : (_b) )
  1843.  
  1844. #define NULLC     '\0'
  1845. #define NEWLINE   '\n'
  1846. #define NULL_PTR  0
  1847.  
  1848. #define bool int
  1849. #define TRUE 1
  1850. #define FALSE 0
  1851. /* All of the following macros deal with a doubly-linked list (dll).
  1852.  *
  1853.  * A 'dll' is a doubly-linked list of nodes, where one endof the list is
  1854.  * designated the "head" and the other endis designated the "tail". A 'dll'
  1855.  * is composed of a head pointer, a tail pointer, and zero or more nodes
  1856.  * each containing pointers to the next and previous nodes in the 'dll'. The
  1857.  * head pointer points to the first node in the 'dll' which is linked
  1858.  * through the "next" pointer fields in the nodes. The tail pointer points
  1859.  * to the last node in the 'dll' which is linked through the "previous"
  1860.  * pointer fields in the nodes. Each list is terminated by a NULL_PTR. If
  1861.  * either the "head" pointer or the "tail" pointer is a NULL_PTR, then the
  1862.  * 'dll' is empty.
  1863.  *
  1864.  * All of the macros share the following parameter definitons.
  1865.  *
  1866.  * Parameter definitions:
  1867.  * ----------------------
  1868.  *
  1869.  * _head_ptr  (input) node_type *
  1870.  *           The head pointer of the 'dll'.
  1871.  *
  1872.  * _tail_ptr  (input) node_type *
  1873.  *           The tail pointer of the 'dll'.
  1874.  *
  1875.  * _node_ptr  (input) node_type *
  1876.  *           The variable which points to the node the insert into the
  1877.  *         'dll' or the variable to receive a pointer to the node removed from the
  1878.  *           'dll'. If the 'dll' is empty, then this variable is NULL_PTR.
  1879.  *
  1880.  * _next_ptr  (input) field_name_in_nd
  1881.  *           The name of the next pointer field in the node structure.
  1882.  *
  1883.  * _prev_ptr  (input) field_name_in_nd
  1884.  *           The name of the previous pointer field in the node structure.
  1885.  */
  1886.  
  1887. /*
  1888.  *
  1889.  *   deq_head_dll -- Remove a node from the front of a 'dll'.
  1890.  *
  1891.  */
  1892.  
  1893. #define deq_head_dll( _head_ptr, _tail_ptr, _node_ptr, _next_ptr, _prev_ptr ) \
  1894.  \
  1895.    (_node_ptr) = (_head_ptr) ; \
  1896.    if ( (_node_ptr) != NULL_PTR ) { \
  1897.       (_head_ptr) = (_node_ptr) -> _next_ptr ; \
  1898.       if ( (_head_ptr) == NULL_PTR ) { \
  1899.      (_tail_ptr) = NULL_PTR ; \
  1900.       } else { \
  1901.      (_head_ptr) -> _prev_ptr = NULL_PTR ; \
  1902.       } ; \
  1903.    }
  1904.  
  1905. /*
  1906.  *
  1907.  *   deq_tail_dll -- Remove a node from the tail of a 'dll'.
  1908.  *
  1909.  */
  1910.  
  1911. #define deq_tail_dll( _head_ptr, _tail_ptr, _node_ptr, _next_ptr, _prev_ptr ) \
  1912.  \
  1913.    (_node_ptr) = (_tail_ptr) ; \
  1914.    if ( (_node_ptr) != NULL_PTR ) { \
  1915.       (_tail_ptr) = (_node_ptr) -> _prev_ptr ; \
  1916.       if ( (_tail_ptr) == NULL_PTR ) { \
  1917.      (_head_ptr) = NULL_PTR ; \
  1918.       } else { \
  1919.      (_tail_ptr) -> _next_ptr = NULL_PTR ; \
  1920.       } ; \
  1921.    }
  1922.  
  1923. /*
  1924.  *
  1925.  *   enq_head_dll -- Add a node to the head of an 'dll'.
  1926.  *
  1927.  */
  1928.  
  1929. #define enq_head_dll( _head_ptr, _tail_ptr, _node_ptr, _next_ptr, _prev_ptr ) \
  1930.  \
  1931.    (_node_ptr) -> _prev_ptr = NULL_PTR ; \
  1932.    if ( _head_ptr == NULL_PTR ) { \
  1933.       _tail_ptr = (_node_ptr) ; \
  1934.       (_node_ptr) -> _next_ptr = NULL_PTR ; \
  1935.    } else { \
  1936.       (_node_ptr) -> _next_ptr = (_head_ptr) ; \
  1937.       (_head_ptr) -> _prev_ptr = (_node_ptr) ; \
  1938.    } ; \
  1939.    (_head_ptr) = (_node_ptr)
  1940.  
  1941. /*
  1942.  *
  1943.  *   enq_tail_dll -- Add a node to the tail of a 'dll'.
  1944.  *
  1945.  */
  1946.  
  1947. #define enq_tail_dll( _head_ptr, _tail_ptr, _node_ptr, _next_ptr, _prev_ptr ) \
  1948.  \
  1949.    if ( (_head_ptr) == NULL_PTR ) { \
  1950.       (_head_ptr) = (_tail_ptr) = (_node_ptr) ; \
  1951.       (_node_ptr) -> _next_ptr  = (_node_ptr) -> _prev_ptr = NULL_PTR ; \
  1952.    } else { \
  1953.       (_tail_ptr) -> _next_ptr = (_node_ptr) ; \
  1954.       (_node_ptr) -> _prev_ptr = (_tail_ptr) ; \
  1955.       (_tail_ptr)              = (_node_ptr) ; \
  1956.       (_node_ptr) -> _next_ptr = NULL_PTR ; \
  1957.    }
  1958.  
  1959. /*
  1960.  *
  1961.  *   rem_dll -- Remove a node from anywhere in a 'dll'.
  1962.  *
  1963.  */
  1964.  
  1965. #define rem_dll( _head_ptr, _tail_ptr, _node_ptr, _next_ptr, _prev_ptr ) \
  1966.  \
  1967.    if ( (_node_ptr) -> _prev_ptr == NULL_PTR ) { \
  1968.       (_head_ptr) = (_node_ptr) -> _next_ptr ; \
  1969.    } else { \
  1970.       (_node_ptr) -> _prev_ptr -> _next_ptr = (_node_ptr) -> _next_ptr ; \
  1971.    } ; \
  1972.  \
  1973.    if ( (_node_ptr) -> _next_ptr == NULL_PTR ) { \
  1974.       (_tail_ptr) = (_node_ptr) -> _prev_ptr ; \
  1975.    } else { \
  1976.       (_node_ptr) -> _next_ptr -> _prev_ptr = (_node_ptr) -> _prev_ptr ; \
  1977.    }
  1978. @//E*O*F util.h//
  1979. chmod u=rw,g=rw,o=rw util.h
  1980.  
  1981. exit 0
  1982.  
  1983.