home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 211_01 / xsortr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1980-01-01  |  20.8 KB  |  929 lines

  1. /* XSORTR.C   VERS:- 01.00  DATE:- 09/26/86  TIME:- 09:37:56 PM */
  2. /*
  3. %CC1 $1.C -O -X -E5980
  4. %CLINK $1 DIO -F CHARFUN STRFUN -S -E5980
  5. %DELETE $1.CRL 
  6. */
  7. /* 
  8. Description:
  9.  
  10. Sort the file given as the only command line parameter (except for 
  11. DIO redirectors and -xxx options).
  12.  
  13. Output is to terminal or as redirected by DIO.
  14.  
  15. The file is read, record by record, to construct a tree, 
  16. each node of which holds file sector and character-count 
  17. information for the corresponding record.
  18. Display of the tree gives the sorted file.
  19.  
  20. The sort key can be a sequence of digit fields.
  21. The key length can be specified.
  22. The key start can be defined by a string each record is searched for.
  23.  
  24. Records can be of variable length, defined by a delimiter string,
  25. eg <\n> if each line is a record.
  26. Records can be multiline.
  27.  
  28. The number of records is limited only by the free space 
  29. below the stack (enough for ca 2000 records) 
  30. and the amount of free disk space (which need be only enough 
  31. to hold the sorted file).  
  32.  
  33. With a CMI hard disk, the install speed is about 1 second per record.
  34.  
  35. The impetus for writing this code came from the need to merge 
  36. and sort lists of abstracts.  If the cas_flag is set, an unsorted file,  
  37. comprising a set of lists and presumed to contain duplicate records, 
  38. is sorted and output.  If a record is a duplicate, only one copy of 
  39. the record is displayed, but the first identifying line of the duplicate 
  40. is displayed at the head of this copy.
  41.  
  42. To carry out a transformation of set of chem. abstracts files into a 
  43. single file of primary information (abs. #, title, etc, but no abstract or
  44. index terms), then to sort the file:
  45.  
  46.     xsrchr  [<filelist]  file1  [file2 ....]  +cas/temp  -p 
  47.             [program enters full screen input -
  48.                 type in search and io delimiter strings
  49.                 then ESC to continue]
  50.     xsortr  cas/temp  +cas/sort
  51.  
  52. For a summary of the various options: see help_mess().
  53.  
  54. For usage:  see help_mess().
  55.  
  56. NOTE: the program expects a modified version of DIO, the failure to use
  57. which may cause complications, probably easily remedied. 
  58.  
  59.  
  60. The functions install() and treeprint() are from MERGE, from the
  61. Van Nuys Toolkit, by Eugene H. Mallory.
  62. The function decode() is an adaptation of code from SEARCH, from the
  63. Van Nuys Toolkit.
  64.  
  65.  
  66. By J.A. Rupley, Tucson, Arizona
  67. Coded for BDS C compiler, version 1.50a
  68. */
  69.  
  70.         /*page eject*/
  71.  
  72. #include "bdscio.h"
  73. #include "dio.h"
  74.  
  75. #define SIZE_STRUCT     12
  76. #define SMALL_BUF    20
  77. #define NO_KEY        1
  78. #define DUP        2
  79. #define EMPTY        4
  80.  
  81. #define ISDIGIT(x)    (((x >= '0') && (x <= '9')) ? x : 0)
  82. #define TOUPPER(x)    (((x >= 'a') && (x <= 'z')) ? x - ' ' : x)
  83. struct tnode
  84. {
  85.     struct tnode *left;
  86.     struct tnode *right;
  87.     int c_pattern;
  88.     int c_end;
  89.     int s_start;
  90.     char c_start;
  91.     char type;
  92. }
  93. ;
  94.  
  95. struct tnode *root, *space;
  96.  
  97. char buf1[SECSIZ * 2], buf2[SECSIZ * 2], *s1, *s2;
  98. char delim[SMALL_BUF], pattern[SMALL_BUF];
  99. char *p_pattern, *p_delim;
  100. char select_key, reject_key;
  101. char *ptr;
  102.  
  103. int fd;
  104. int xi, xj, xtemp, c_limit;
  105. int sec_start, char_start, char_pattern, sec_end, char_end, c_count;
  106. int rctr, lctr, maxr, maxl, left_moves, right_moves, left_right, rec_num;
  107. int flag1, flag2;
  108. int optionerr;
  109. int cas_flag, lines_out, verbose, out_lim, num_only, upper_case, reverse, column, length;
  110. int good_count, good_delim, digit_count, digit_delim;
  111. int no_key_count, dup_count, empty_count, no_key_flag, dup_flag, empty_flag;
  112.  
  113. main(argc, argv)
  114. char **argv;
  115. int argc;
  116.  
  117. {
  118.     dioinit(&argc, argv);
  119.  
  120.     set_options(&argc, argv);
  121.  
  122.     root = NULL;
  123.     maxl = maxr = rec_num = 0;
  124.     left_moves = right_moves = left_right = 0;
  125.  
  126.     sec_start = char_start = sec_end = char_end = 0;
  127.     flag1 = flag2 = 0;
  128.     dup_count = no_key_count = empty_count = 0;
  129.  
  130.     good_delim = digit_delim = 0;
  131.     for (xi = 0; delim[xi]; xi++)
  132.     {
  133.         if (delim[xi] > 0x1f)
  134.             good_delim++;
  135.         if (isdigit(delim[xi]))
  136.             digit_delim++;
  137.     }
  138.  
  139.     while (!flag2)
  140.     {
  141.         rec_num++;
  142.  
  143.         space = sbrk(SIZE_STRUCT);
  144.         if (space == -1)
  145.             error("XSORT: Out of space at record %d.", rec_num);
  146.  
  147.         if (!get_record() && (flag2 == 1))
  148.             break;
  149.  
  150.         space->s_start = sec_start;
  151.         space->c_start = char_start;
  152.         space->c_pattern = char_pattern;
  153.         space->c_end = (char_end - strlen(delim));
  154.         space->type = NULL;
  155.         space->left = NULL;
  156.         space->right = NULL;
  157.  
  158.         /*If a record is empty, a duplicate, or lacks the sort key:*/
  159.         /*    a flag is set, for controlling displays that show */
  160.         /*        the progess of the sort;*/
  161.         /*    a counter is updated, for display in the */
  162.         /*        summary output;*/
  163.         /*    space->type is set, for testing in later compares*/
  164.         /*        and in selection/rejection for output.*/
  165.  
  166.         dup_flag = no_key_flag = empty_flag = 0;
  167.         c_limit = space->c_end - space->c_pattern;
  168.  
  169.         if (!good_count)
  170.         {
  171.             empty_flag++;
  172.             empty_count++;
  173.             space->type |= 4;
  174.         }
  175.         else
  176.             if ((space->c_pattern < 0)
  177.             || (num_only && !digit_count)
  178.             || (c_limit <= column))
  179.         {
  180.             no_key_flag++;
  181.             no_key_count++;
  182.             space->type |= 1;
  183.         }
  184.  
  185.         rctr = lctr = 0;
  186.  
  187.         fill_buffx(space, buf2, &s2);
  188.  
  189.         install(&root, &space, &root);
  190.  
  191.         if (lctr && rctr)
  192.             left_right++;
  193.         else
  194.             if (lctr)
  195.                 left_moves++;
  196.             else
  197.                 if (rctr)
  198.                     right_moves++;
  199.  
  200.         /*For duplicate records, comp_record() sets dup_flag */
  201.         /*and node->type/space->type |= DUP.*/
  202.  
  203.         if (dup_flag)
  204.             dup_count++;
  205.  
  206.         /*Screen output during record-by-record read of file.*/
  207.  
  208.         if (verbose || dup_flag || empty_flag || no_key_flag)
  209.         {
  210.             typef("rec_num= %-4d lctr= %-4d rctr= %-4d ",
  211.             rec_num, lctr, rctr);
  212.             if (!(space->type & (EMPTY + NO_KEY)))
  213.             {
  214.                 c_limit = 16 < c_limit ? 16 : c_limit;
  215.                 for (xi = 0; xi < c_limit; xi++)
  216.                 {
  217.                     if (s2[xi] < 0x20)
  218.                         break;
  219.                     typef("%c", s2[xi]);
  220.                 }
  221.             }
  222.             if (no_key_flag)
  223.                 typef("\t%d no sort key(s)", no_key_count);
  224.             if (dup_flag)
  225.                 typef("\t%d duplicate(s)", dup_count);
  226.             if (empty_flag)
  227.                 typef("\t%d empty record(s)", empty_count);
  228.             typef("\n");
  229.         }
  230.  
  231.         if (kbhit())
  232.             getchar();
  233.     }
  234.  
  235.     typef("\n\nSummary:\n");
  236.  
  237.     typef("%d records read with:\n\t%d missing sort key(s)\t%d duplicate(s)\t%d empty record(s)\n",
  238.     rec_num, no_key_count, dup_count, empty_count);
  239.     typef("during sorting there were:\n\t%d moves left\t\t%d moves right\t%d left and right\n\n",
  240.     left_moves, right_moves, left_right);
  241.  
  242.     typef("Tree installed - pausing...\n");
  243.     sleep(60);
  244.  
  245.     rctr = lctr = 0;
  246.     if (root != NULL)
  247.         treeprint(root);
  248.  
  249.     dioflush();
  250. }
  251.  
  252.         /*page eject*/
  253.  
  254.  
  255. install(nodeptr, spaceptr, rootptr)
  256. struct tnode **nodeptr, **spaceptr, **rootptr;
  257. {
  258.     if (*nodeptr == NULL)
  259.     {
  260.         if (rctr == 0 && lctr > 30)
  261.         {
  262.             (*spaceptr)->right = (*rootptr);
  263.             *rootptr = *spaceptr;
  264.         }
  265.         else
  266.             if (lctr == 0 && rctr > 30)
  267.         {
  268.             (*spaceptr)->left = (*rootptr);
  269.             *rootptr = *spaceptr;
  270.         }
  271.         else
  272.             {
  273.             *nodeptr = *spaceptr;
  274.         }
  275.         return;
  276.     }
  277.     else
  278.         {
  279.  
  280.         if (comp_record(*nodeptr, *spaceptr) > 0)
  281.         {
  282. left :
  283.             lctr++;
  284.             install(&((*nodeptr)->left), spaceptr, rootptr);
  285.             return;
  286.         }
  287.         else
  288.             {
  289. right :
  290.             rctr++;
  291.             install(&((*nodeptr)->right), spaceptr, rootptr);
  292.             return;
  293.         }
  294.     }
  295. }
  296.  
  297.  
  298. treeprint(nodeptr)
  299. struct tnode *nodeptr;
  300. {
  301.     if (lctr > maxl)
  302.         maxl = lctr;
  303.     if (rctr > maxr)
  304.         maxr = rctr;
  305.  
  306.     if (nodeptr->left != NULL)
  307.     {
  308.         lctr++;
  309.         treeprint(nodeptr->left);
  310.         lctr--;
  311.     }
  312.  
  313.     put_record(nodeptr);
  314.  
  315.     if (nodeptr->right != NULL)
  316.     {
  317.         rctr++;
  318.         treeprint(nodeptr->right);
  319.         rctr--;
  320.     }
  321. }
  322.  
  323.  
  324.         /*page eject*/
  325.  
  326. /*
  327. Return (0, <0, >0), depending on values of the records held at spaceptr 
  328. (the record just read from the disk file) and at nodeptr (a record 
  329. previously installed in the tree).
  330. The buffer (buf2) for the new record at spaceptr is filled once before
  331. entering install; the buffer (buf1) for the record previously installed at 
  332. nodeptr must be filled fresh for each compare.
  333. Records that are empty are treated as infinitely small.  Records that 
  334. contain no sort key are treated as smaller than all except empty records.
  335. If the record at spaceptr duplicates the record at nodeptr, dup_flag and 
  336. spaceptr->type (or nodeptr->type) are set.
  337. Change in whether node->type or space->type is set, or whether in install
  338. the test on the value returned from comp_record() is '>=' or '>',
  339. determines the order of display of duplicates (or of the header line if the
  340. cas_flag is set (see put_record()).
  341. */
  342.  
  343. int comp_record(nodeptr, spaceptr)
  344. struct tnode *nodeptr, *spaceptr;
  345. {
  346.  
  347.     xi = spaceptr->type & (EMPTY + NO_KEY);
  348.     xj = nodeptr->type & (EMPTY + NO_KEY);
  349.  
  350.     if (xi || xj)
  351.     {
  352.         if (xi && (xi >= xj))
  353.             xtemp = 1;
  354.         else
  355.             xtemp = -1;
  356.     }
  357.     else
  358.         {
  359.         fill_bufx(nodeptr, buf1, &s1);
  360.         if (!(xtemp = string_compare(s1 + column, s2 + column)))
  361.         {
  362.             dup_flag++;
  363.             nodeptr->type |= DUP;
  364.         }
  365.     }
  366.  
  367.     if (!reverse)
  368.         return xtemp;
  369.     else
  370.         return -xtemp;
  371. }
  372.  
  373.  
  374. /*
  375. Output the record for which sector and character count is held at nodeptr.  
  376. Reject_key (and select_key) are tested to decide on rejection (or selection)
  377. for output.
  378. Out_lim (and lines_out) control the number of characters (or lines) 
  379. displayed.
  380. Cas_flag if set causes display of the first line of a duplicate record 
  381. at the head of the output of the main copy of the record.  This line 
  382. should give identifying information, so, in the case of duplicates, 
  383. the output shows the sources of both copies.
  384. If one wants the one-line output for the duplicate to follow output 
  385. of the main copy of the record, change the test on comp_record in install
  386. between '>=' to '>' or the set of type between node->type and space->type
  387. in comp_record().
  388. */
  389.  
  390. int put_record(nodeptr)
  391. struct tnode *nodeptr;
  392. {
  393.     int cas_duplicate, line_count;
  394.  
  395.     cas_duplicate = 0;
  396.     line_count = lines_out;
  397.  
  398.     if (nodeptr->type & reject_key)
  399.     {
  400.         if (cas_duplicate = ((nodeptr->type & DUP) && cas_flag))
  401.             line_count = 1;
  402.         else
  403.             return 0;
  404.     }
  405.  
  406.     if (select_key)
  407.         if (!(nodeptr->type & select_key))
  408.             return 0;
  409.  
  410.     /*If out_lim is set, adjust the character-count and sector.*/
  411.     /*information to limit the number of characters output.*/
  412.     /*If line_count is set, limit the number of lines displayed.*/
  413.     /*If cas_flag is set, do not output a record delimiter at the*/
  414.     /*end of the line; this allows the main copy of the record to follow*/
  415.     /*immediately after the header line.*/
  416.  
  417.     xtemp = out_lim + nodeptr->c_start;
  418.     if (out_lim && (xtemp < nodeptr->c_end))
  419.     {
  420.         xi = xtemp / SECSIZ;
  421.         c_limit = xtemp % SECSIZ;
  422.     }
  423.     else
  424.         {
  425.         xi = nodeptr->c_end / SECSIZ;
  426.         c_limit = nodeptr->c_end % SECSIZ;
  427.     }
  428.  
  429.     xj = nodeptr->c_start;
  430.  
  431.     seek(fd, nodeptr->s_start, 0);
  432.  
  433.     while (xi--)
  434.     {
  435.         read(fd, buf2, 1);
  436.         while (xj < SECSIZ)
  437.         {
  438.             putchar(buf2[xj]);
  439.             if (buf2[xj++] == '\n')
  440.                 if (!--line_count)
  441.                 {
  442.                     if (!cas_duplicate)
  443.                         puts(delim);
  444.                     return 1;
  445.                 }
  446.         }
  447.         xj = 0;
  448.     }
  449.  
  450.     if (read(fd, buf2, 1) == -1)
  451.         error("XSORT: read error.");
  452.  
  453.     while (xj < c_limit)
  454.     {
  455.         putchar(buf2[xj]);
  456.         if (buf2[xj++] == '\n')
  457.             if (!--line_count)
  458.             {
  459.                 if (!cas_duplicate)
  460.                     puts(delim);
  461.                 return 1;
  462.             }
  463.     }
  464.  
  465.     puts(delim);
  466.     return 1;
  467. }
  468.  
  469.  
  470. /*
  471. Fill buf1 or buf2 with two sectors from the file, with the first sector 
  472. containing the first character after the end of the key delimiter.
  473. Thus at least one sector of key information can be tested in the sort.
  474. The addresses s1 and s2 are pointers to the start of the key information.
  475. The key characters are stripped (& 0x7f).
  476. */
  477.  
  478. int fill_bufx(nodeptr, bufx, sx)
  479. char *bufx, **sx;
  480. struct tnode *nodeptr;
  481. {
  482.     if (nodeptr->type & (EMPTY + NO_KEY))
  483.         return 0;
  484.  
  485.     seek(fd, (nodeptr->s_start + nodeptr->c_pattern / SECSIZ), 0);
  486.     if (read(fd, bufx, 2) == -1)
  487.         error("XSORT: read error.");
  488.  
  489.     *sx = bufx + (nodeptr->c_pattern % SECSIZ);
  490.  
  491.     xtemp = nodeptr->c_end - nodeptr->c_pattern;
  492.     ptr = *sx + ((length < xtemp) ? length : xtemp);
  493.  
  494.     *ptr = '\0';
  495.  
  496.     for (ptr = *sx; *ptr; ptr++)
  497.         *ptr = *ptr & 0x7f;
  498.  
  499.     return 1;
  500. }
  501.  
  502.  
  503. /*
  504. Sector by sector read of the file to be sorted, setting:
  505.     end of record (found by test with delim);
  506.     start of key information (found by test with pattern).
  507. Information stored:
  508.     sec_start = file sector containing start of record;
  509.     char_start = character count to start of record from 0 = start
  510.                 of sector sec_start;
  511.     char_pattern = character count for start of key information.
  512.     char_end = character count for end of record, adjusted in main() 
  513.                 by subtracting strlen(delim);
  514. */
  515.  
  516. int get_record()
  517. {
  518.     p_delim = delim;
  519.     p_pattern = pattern;
  520.     sec_start = sec_end;
  521.     c_count = char_start = char_end % SECSIZ;
  522.     char_pattern = sec_end = char_end = -1;
  523.     good_count = digit_count = 0;
  524.  
  525.     if (!*pattern)
  526.         char_pattern = char_start;
  527.  
  528.     if (seek(fd, sec_start, 0) == -1)
  529.         error("XSORT: seek error.");
  530.  
  531.     while (TRUE)
  532.     {
  533.         if ((flag1 = read(fd, buf2, 1)) == -1)
  534.             error("XSORT: read error.");
  535.  
  536.         if (flag1 && !flag2)
  537.             if (!sec_scan())
  538.                 continue;
  539.  
  540.         char_end = c_count;
  541.         sec_end = sec_start + c_count / SECSIZ;
  542.  
  543.         if (!flag1 || flag2)
  544.         {
  545.             if (char_end != char_start)
  546.             {
  547.                 char_end = char_end + strlen(delim);
  548.                 flag2 = 2;
  549.             }
  550.             else
  551.                 flag2 = 1;
  552.             return 0;
  553.         }
  554.         else
  555.             return 1;
  556.     }
  557. }
  558.  
  559.  
  560. /*
  561. Search a sector for character sequences = delim and pattern.
  562. Maintain: good_count = number of non-control characters, used in testing
  563. for an empty record; digit_count = number of digits in the record, 
  564. also used in testing for an empty record.
  565. Input is stripped (& 0x7f).
  566. */
  567.  
  568. int sec_scan()
  569. {
  570.     for (xi = (c_count % SECSIZ); xi < SECSIZ; xi++)
  571.     {
  572.         if ((xtemp = buf2[xi] & 0x7f) == CPMEOF)
  573.             return -(flag2 = 1);
  574.         c_count++;
  575.  
  576.         xtemp = TOUPPER(xtemp);
  577.  
  578.         if (*p_pattern)
  579.         {
  580.             if (xtemp == *p_pattern)
  581.             {
  582.                 if (!*++p_pattern)
  583.                     char_pattern = c_count;
  584.             }
  585.             else
  586.                 p_pattern = pattern;
  587.         }
  588.         else
  589.             if (ISDIGIT(xtemp))
  590.             digit_count++;
  591.  
  592.         if (xtemp > 0x1f)
  593.             good_count++;
  594.  
  595.         if (xtemp == *p_delim)
  596.         {
  597.             if (!*++p_delim)
  598.             {
  599.                 if ((good_count -= good_delim) < 0)
  600.                     good_count = 0;
  601.                 if ((digit_count -= digit_delim) < 0)
  602.                     digit_count = 0;
  603.                 return 1;
  604.             }
  605.         }
  606.         else
  607.             p_delim = delim;
  608.     }
  609.  
  610.     return 0;
  611. }
  612.  
  613.  
  614. /*
  615. Compare records by values of digit strings.  A digit string is terminated 
  616. by the first non-digit character.  Alpha and white characters are passed over.
  617. By recursive call of num_compare(), digit strings are tested in sequence
  618. until the records differ or until meeting the allowed length of the compare.
  619. */
  620.  
  621. int num_compare(t1, t2)
  622. char *t1, *t2;
  623. {
  624.     while ((!(xi = ISDIGIT(*t1)) || (xi == '0')) && *t1)
  625.         t1++;
  626.     while ((!(xj = ISDIGIT(*t2)) || (xj == '0')) && *t2)
  627.         t2++;
  628.  
  629.     for (xtemp = 0; xi && xj; t1++, t2++)
  630.     {
  631.             if (!xtemp && (xi != xj))
  632.                 xtemp = xi - xj;
  633.             xi = ISDIGIT(*t1);
  634.             xj = ISDIGIT(*t2);
  635.     }
  636.  
  637.     if (!*t1 || !*t2)
  638.     {
  639.         if (!*t1 && !*t2)
  640.             return 0;
  641.         else
  642.             if (!*t1)
  643.                 return -1;
  644.         else
  645.             return 1;
  646.     }
  647.  
  648.     if (xi)
  649.         return 1;
  650.     if (xj)
  651.         return -1;
  652.  
  653.     if (xtemp)
  654.         return xtemp;
  655.  
  656.     return num_compare(t1, t2);
  657. }
  658.  
  659.  
  660. /*
  661. Compare character strings.
  662. Call num_compare() if num_only is set.
  663. */
  664.  
  665. int string_compare(t1, t2)
  666. char *t1, *t2;
  667. {
  668.     if (num_only)
  669.         return num_compare(t1, t2);
  670.  
  671.     if (upper_case)
  672.     {
  673.         for( ;TOUPPER(*t1) == TOUPPER(*t2); t1++, t2++)
  674.             if (!*t1)
  675.                 return 0;
  676.         return TOUPPER(*t1) - TOUPPER(*t2);
  677.     }
  678.     else
  679.     {
  680.         for( ;*t1 == *t2; t1++, t2++)
  681.             if (!*t1)
  682.                 return 0;
  683.         return *t1 - *t2;
  684.     }
  685. }
  686.  
  687.  
  688.         /*page eject*/
  689.  
  690. /*
  691. Get_options(), etc, set control variables, open input file, display 
  692. help message, etc.
  693. */
  694.  
  695. void set_options(argcpntr, argv)
  696. char **argv;
  697. int *argcpntr;
  698. {
  699.     /*default options:    for CAS files*/
  700.     verbose = select_key = out_lim = 0;
  701.     lines_out = column = upper_case = 0;
  702.     cas_flag = DUP;
  703.     num_only = 1;
  704.     length = 20;
  705.     reverse = 1;
  706.     reject_key = EMPTY + DUP + NO_KEY;
  707.     strcpy(delim, "\n\*\r\n");
  708.     strcpy(pattern, "AN  CA");
  709.  
  710.     get_options(argcpntr, argv);
  711.  
  712.     if (optionerr)
  713.         help_mess();
  714.  
  715.     if (*argcpntr < 2)
  716.     {
  717.         typef("XSORT: No file.\n");
  718.         help_mess();
  719.     }
  720.  
  721.     if ((fd = open(argv[1], READ)) == -1)
  722.         error("XSORT: Can't open file %s.", argv[1]);
  723.  
  724.     if ((length == 0) || (length > SECSIZ))
  725.         length = SECSIZ;
  726.     if (column > 0)
  727.         column--;
  728.     if (length <= column)
  729.         error("XSORT: Length <= column.");
  730.  
  731.     if (!*pattern)
  732.         reject_key = reject_key & (EMPTY + DUP);
  733. }
  734.  
  735.  
  736. /*
  737. Setuppat() and decode() convert command line character strings, with the
  738. symbols '^' prefacing a control character, '_' = space, and '\' used 
  739. as in 'C' (\n = newline, etc).
  740. */
  741.  
  742. void pat_decode(pat, str)
  743. char *pat;
  744. char *str;
  745. {
  746.     int c;
  747.  
  748.     xj = xi = 0;
  749.  
  750.     while (c = pat[xi++])
  751.         decode(c, pat, str, 0);
  752.     str[xj] = 0;
  753.     return;
  754. }
  755.  
  756.  
  757. int decode(c, pat, str, not_flag)
  758. int c, not_flag;
  759. char *pat, *str;
  760. {
  761.     switch (c)
  762.     {
  763.     case '_' :
  764.         str[xj++] = ' ';
  765.         break;
  766.     case '^' :
  767.         c = pat[xi++];
  768.         if (c > 0x7f || c <= 0x40)
  769.             error("DECODE: illegal character follows ^");
  770.         str[xj++] = toupper(c) - 0x40;
  771.         break;
  772.     case '\\' :
  773.         c = pat[xi++];
  774.         if (c == 0)
  775.             error("DECODE: \\ requires character following.");
  776.         switch (c)
  777.         {
  778.         case 'N' :
  779.             c = '\n';
  780.             break;
  781.         case 'F' :
  782.             c = '\f';
  783.             break;
  784.         case 'R' :
  785.             c = '\r';
  786.             break;
  787.         case 'T' :
  788.             c = '\t';
  789.             break;
  790.         case 'B' :
  791.             c = '\b';
  792.             break;
  793.         }
  794.         if (isdigit(c))
  795.             c = c - '0';
  796.         str[xj++] = c;
  797.         break;
  798.     default :
  799.         str[xj++] = c;
  800.     }
  801.     return;
  802. }
  803.  
  804.  
  805. void help_mess()
  806. {
  807.     typef("USAGE:  xsort  fn1  [redir: >fnout etc]  [options: -p.. -d.. etc]\n");
  808.     typef("\n");
  809.     typef("XSORT: Legal options:\n");
  810.     typef("    default setup = CAS files;  false = 0;  _, \\x, and ^x  OK in strings.\n");
  811.     typef("    options = true/false toggles, unless value(n)/string(str) indicated.\n");
  812.     typef("-Sn=1 \tSelect for:   CAS files (n=1);   standard sort (n=0).\n");
  813.     typef("      \t\t=1  for CAS files;\t\t=0  for standard sort.\n");
  814.     typef("      \t\tthe Sn option, if used, must be last.\n");
  815.     typef("-U=0  \tUpper case compares.\t\t-R=1     Reverse sort.\n");
  816.     typef("-An=0 \tOutput selected types.\t\t-Xn=7    Reject selected types.\n");
  817.     typef("      \t\t1 => key missing\t2 => duplicates\t\t4 => empty\n");
  818.     typef("-V=0  \tVerbose output to screen during sorting.\n");
  819.     typef("-#=1  \tDigit sequences only used in string compares.\n");
  820.     typef("-On=0 \tIf n>0, output only n characters; if n<0, output n lines.\n");
  821.     typef("-Cn=0 \tColumn position to begin compare.\n");
  822.     typef("-Ln=20\tLength of string to compare (default = SECSIZ).\n");
  823.     typef("-Dstr \tDelimiter string for record:\n");
  824.     typef("      \t\t=\\n\\*\\r\\n for CAS files;\t=\\n   for standard sort\n");
  825.     typef("-Pstr \tPattern at end of which start compare:\n");
  826.     typef("      \t\t=AN__CA   for CAS files;\t=\\0   for standard sort");
  827.     exit(0);
  828. }
  829.  
  830.  
  831. void get_options(argcpntr, argv)
  832. char **argv;
  833. int *argcpntr;
  834. {
  835.     int ii, jj;
  836.     char *ss;
  837.  
  838.     optionerr = 0;
  839.  
  840.     for (ii = *argcpntr - 1; ii > 0; ii--)
  841.         if (argv[ii][0] == '-')
  842.         {
  843.             ss = &argv[ii][1];
  844.  
  845.             switch (toupper(*ss++))
  846.             {
  847.             case 'U' :
  848.                 upper_case = !upper_case;
  849.                 break;
  850.             case 'R' :
  851.                 reverse = !reverse;
  852.                 break;
  853.             case 'V' :
  854.                 verbose = !verbose;
  855.                 break;
  856.             case 'A' :
  857.                 select_key = atoi(ss);
  858.                 if (select_key)
  859.                     reject_key = 0;
  860.                 break;
  861.             case 'X' :
  862.                 reject_key = atoi(ss);
  863.                 if (reject_key)
  864.                     select_key = 0;
  865.                 break;
  866.             case '#' :
  867.                 num_only = !num_only;
  868.                 break;
  869.             case 'S' :
  870.                 if (*ss == '0')
  871.                 {
  872.                     reject_key = lines_out = cas_flag = 0;
  873.                     select_key = out_lim = num_only = 0;
  874.                     column = length = upper_case = reverse = 0;
  875.                     verbose = 1;
  876.                     strcpy(delim, "\n");
  877.                     *pattern = '\0';
  878.                     break;
  879.                 }
  880.                 if (*ss == '1')
  881.                 {
  882.                     verbose = select_key = out_lim = 0;
  883.                     lines_out = column = upper_case = 0;
  884.                     cas_flag = DUP;
  885.                     num_only = 1;
  886.                     length = 20;
  887.                     reverse = 1;
  888.                     reject_key = EMPTY + DUP + NO_KEY;
  889.                     strcpy(delim, "\n\*\r\n");
  890.                     strcpy(pattern, "AN  CA");
  891.                     break;
  892.                 }
  893.             case 'O' :
  894.                 out_lim = lines_out = 0;
  895.                 if (*ss == '-')
  896.                     lines_out = atoi(++ss);
  897.                 else
  898.                     out_lim = atoi(ss);
  899.                 break;
  900.             case 'C' :
  901.                 column = atoi(ss);
  902.                 break;
  903.             case 'L' :
  904.                 length = atoi(ss);
  905.                 break;
  906.             case 'D' :
  907.                 pat_decode(ss, delim);
  908.                 break;
  909.             case 'P' :
  910.                 pat_decode(ss, pattern);
  911.                 break;
  912.             case 'H' :
  913.                 optionerr = TRUE;
  914.                 break;
  915.             default :
  916.                 typef("XSORT: Illegal option %c.\n", *--ss);
  917.                 ss++;
  918.                 optionerr = TRUE;
  919.                 break;
  920.             }
  921.  
  922.             for (jj = ii; jj < (*argcpntr - 1); jj++)
  923.                 argv[jj] = argv[jj + 1];
  924.             (*argcpntr)--;
  925.         }
  926. }
  927.  
  928.  
  929.