home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / OPENSTEP / UNIX / Games / fortune-mod-9708-I / util / strfile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-12-08  |  13.0 KB  |  542 lines

  1. /*      $NetBSD: strfile.c,v 1.3 1995/03/23 08:28:47 cgd Exp $  */
  2.  
  3. /*-
  4.  * Copyright (c) 1989, 1993
  5.  *    The Regents of the University of California.  All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to Berkeley by
  8.  * Ken Arnold.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  * 3. All advertising materials mentioning features or use of this software
  19.  *    must display the following acknowledgement:
  20.  *    This product includes software developed by the University of
  21.  *    California, Berkeley and its contributors.
  22.  * 4. Neither the name of the University nor the names of its contributors
  23.  *    may be used to endorse or promote products derived from this software
  24.  *    without specific prior written permission.
  25.  *
  26.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  27.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  30.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  31.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  32.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  34.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  36.  * SUCH DAMAGE.
  37.  */
  38.  
  39. /*
  40.  * Changes, September 1995, to make the damn thing actually sort instead
  41.  * of just pretending.  Amy A. Lewis
  42.  * 
  43.  * And lots more.
  44.  * 
  45.  * Fixed the special cases of %^J% (an empty fortune), no 'separator' at
  46.  * the end of the file, and a trailing newline at the end of the file, all
  47.  * of which produced total ballsup at one point or another.
  48.  * 
  49.  * This included adding a routine to go back and write over the last pointer
  50.  * written or stored, for the case of an empty fortune.
  51.  * 
  52.  * unstr also had to be modified (well, for *lots* of reasons, but this was
  53.  * one) to be certain to put the delimiters in the right places.
  54.  */
  55.  
  56. /*
  57.  * 
  58.  #ifndef lint
  59.  static char copyright[] =
  60.  "@(#) Copyright (c) 1989, 1993\n\
  61.  The Regents of the University of California.  All rights reserved.\n";
  62.  #endif / * not lint * /
  63.  
  64.  #ifndef lint
  65.  #if 0
  66.  static char sccsid[] = "@(#)strfile.c  8.1 (Berkeley) 5/31/93";
  67.  #else
  68.  static char rcsid[] = "$NetBSD: strfile.c,v 1.3 1995/03/23 08:28:47 cgd Exp $";
  69.  #endif
  70.  #endif / * not lint * /
  71.  *
  72.  *I haven't the faintest flipping idea what all that is, so kill the warnings 
  73.  */
  74.  
  75. #ifdef NeXT
  76.     #include    <libc.h>
  77. #endif
  78. #include    <stdlib.h>
  79. #include    <unistd.h>
  80. #include    <netinet/in.h>
  81. #include    <sys/param.h>
  82. #include    <stdio.h>
  83. #include    <ctype.h>
  84. #include    <string.h>
  85. #include    "strfile.h"
  86.  
  87. #ifndef MAXPATHLEN
  88. #define    MAXPATHLEN    1024
  89. #endif /* MAXPATHLEN */
  90.  
  91. /*
  92.  *    This program takes a file composed of strings seperated by
  93.  * lines containing only the delimiting character (the default
  94.  * character is '%') and creates another file which consists of a table
  95.  * describing the file (structure from "strfile.h"), a table of seek
  96.  * pointers to the start of the strings, and the strings, each terminated
  97.  * by a null byte.  Usage:
  98.  *
  99.  *      % strfile [-iorsx] [ -cC ] sourcefile [ datafile ]
  100.  *
  101.  *      c - Change delimiting character from '%' to 'C'
  102.  *      s - Silent.  Give no summary of data processed at the end of
  103.  *          the run.
  104.  *      o - order the strings in alphabetic order
  105.  *      i - if ordering, ignore case 
  106.  *      r - randomize the order of the strings
  107.  *      x - set rotated bit
  108.  *
  109.  *              Ken Arnold      Sept. 7, 1978 --
  110.  *
  111.  *      Added ordering options.
  112.  * 
  113.  * Made ordering options do more than set the bloody flag, September 95 A. Lewis
  114.  * 
  115.  * Always make sure that your loop control variables aren't set to bloody
  116.  * *zero* before distributing the bloody code, all right?
  117.  * 
  118.  */
  119.  
  120. #ifndef NeXT
  121.     #define    TRUE    1
  122.     #define    FALSE    0
  123. #endif
  124.  
  125. #define    STORING_PTRS    (Oflag || Rflag)
  126. #define    CHUNKSIZE    512
  127.  
  128. #define    ALWAYS    1
  129. #define    ALLOC(ptr,sz)    if (ALWAYS) { \
  130.             if (ptr == NULL) \
  131.                 ptr = malloc((unsigned int) (CHUNKSIZE * sizeof *ptr)); \
  132.             else if (((sz) + 1) % CHUNKSIZE == 0) \
  133.                 ptr = realloc((void *) ptr, ((unsigned int) ((sz) + CHUNKSIZE) * sizeof *ptr)); \
  134.             if (ptr == NULL) { \
  135.                 fprintf(stderr, "out of space\n"); \
  136.                 exit(1); \
  137.             } \
  138.         }
  139.  
  140. typedef struct
  141. {
  142.     char first;
  143.     off_t pos;
  144. }
  145. STR;
  146.  
  147. char *Infile = NULL,        /* input file name */
  148.   Outfile[MAXPATHLEN] = "",    /* output file name */
  149.   Delimch = '%';        /* delimiting character */
  150.  
  151. int Sflag = FALSE;        /* silent run flag */
  152. int Oflag = FALSE;        /* ordering flag */
  153. int Iflag = FALSE;        /* ignore case flag */
  154. int Rflag = FALSE;        /* randomize order flag */
  155. int Xflag = FALSE;        /* set rotated bit */
  156. long Num_pts = 0;        /* number of pointers/strings */
  157.  
  158. off_t *Seekpts;
  159.  
  160. FILE *Sort_1, *Sort_2;        /* pointers for sorting */
  161.  
  162. STRFILE Tbl;            /* statistics table */
  163.  
  164. STR *Firstch;            /* first chars of each string */
  165.  
  166. void usage(void)
  167. {
  168.     fprintf(stderr,
  169.         "strfile [-iorsx] [-c char] sourcefile [datafile]\n");
  170.     exit(1);
  171. }
  172.  
  173. /*
  174.  *    This routine evaluates arguments from the command line
  175.  */
  176. void getargs(int argc, char **argv)
  177. {
  178.     extern char *optarg;
  179.     extern int optind;
  180.     int ch;
  181.  
  182.     while ((ch = getopt(argc, argv, "c:iorsx")) != EOF)
  183.     switch (ch)
  184.       {
  185.       case 'c':        /* new delimiting char */
  186.           Delimch = *optarg;
  187.           if (!isascii(Delimch))
  188.           {
  189.           printf("bad delimiting character: '\\%o\n'",
  190.              Delimch);
  191.           }
  192.           break;
  193.       case 'i':        /* ignore case in ordering */
  194.           Iflag++;
  195.           break;
  196.       case 'o':        /* order strings */
  197.           Oflag++;
  198.           break;
  199.       case 'r':        /* randomize pointers */
  200.           Rflag++;
  201.           break;
  202.       case 's':        /* silent */
  203.           Sflag++;
  204.           break;
  205.       case 'x':        /* set the rotated bit */
  206.           Xflag++;
  207.           break;
  208.       case '?':
  209.       default:
  210.           usage();
  211.       }
  212.     argv += optind;
  213.  
  214.     if (*argv)
  215.     {
  216.     Infile = *argv;
  217.     if (*++argv)
  218.         (void) strcpy(Outfile, *argv);
  219.     }
  220.     if (!Infile)
  221.     {
  222.     puts("No input file name");
  223.     usage();
  224.     }
  225.     if (*Outfile == '\0')
  226.     {
  227.     strcpy(Outfile, Infile);
  228.     strcat(Outfile, ".dat");
  229.     }
  230. }
  231.  
  232. /*
  233.  * add_offset:
  234.  *      Add an offset to the list, or write it out, as appropriate.
  235.  */
  236. void add_offset(FILE * fp, off_t off)
  237. {
  238.     off_t net;
  239.  
  240.     if (!STORING_PTRS)
  241.     {
  242.     net = htonl(off);
  243.     fwrite(&net, 1, sizeof net, fp);
  244.     }
  245.     else
  246.     {
  247.     ALLOC(Seekpts, Num_pts + 1);
  248.     Seekpts[Num_pts] = off;
  249.     }
  250.     Num_pts++;
  251. }
  252.  
  253. /*
  254.  * fix_last_offset:
  255.  *     Used when we have two separators in a row.
  256.  */
  257. void fix_last_offset(FILE * fp, off_t off)
  258. {
  259.     off_t net;
  260.  
  261.     if (!STORING_PTRS)
  262.     {
  263.     net = htonl(off);
  264.     fseek(fp, -(sizeof net), SEEK_CUR);
  265.     fwrite(&net, 1, sizeof net, fp);
  266.     }
  267.     else
  268.     Seekpts[Num_pts - 1] = off;
  269. }
  270.  
  271. /*
  272.  * cmp_str:
  273.  *      Compare two strings in the file
  274.  */
  275. int cmp_str(const void *v1, const void *v2)
  276. {
  277.     register int c1, c2;
  278.     register int n1, n2;
  279.     register STR *p1, *p2;
  280.  
  281. #define    SET_N(nf,ch)    (nf = (ch == '\n'))
  282. #define    IS_END(ch,nf)    (ch == Delimch && nf)
  283.  
  284.     p1 = (STR *) v1;
  285.     p2 = (STR *) v2;
  286.     c1 = p1->first;
  287.     c2 = p2->first;
  288.     if (c1 != c2)
  289.     return c1 - c2;
  290.  
  291.     fseek(Sort_1, p1->pos, 0);
  292.     fseek(Sort_2, p2->pos, 0);
  293.  
  294.     n1 = FALSE;
  295.     n2 = FALSE;
  296.     while (!isalnum(c1 = getc(Sort_1)) && c1 != '\0')
  297.     SET_N(n1, c1);
  298.     while (!isalnum(c2 = getc(Sort_2)) && c2 != '\0')
  299.     SET_N(n2, c2);
  300.  
  301.     while (!IS_END(c1, n1) && !IS_END(c2, n2))
  302.     {
  303.     if (Iflag)
  304.     {
  305.         if (isupper(c1))
  306.         c1 = tolower(c1);
  307.         if (isupper(c2))
  308.         c2 = tolower(c2);
  309.     }
  310.     if (c1 != c2)
  311.         return c1 - c2;
  312.     SET_N(n1, c1);
  313.     SET_N(n2, c2);
  314.     c1 = getc(Sort_1);
  315.     c2 = getc(Sort_2);
  316.     }
  317.     if (IS_END(c1, n1))
  318.     c1 = 0;
  319.     if (IS_END(c2, n2))
  320.     c2 = 0;
  321.     return c1 - c2;
  322. }
  323.  
  324. /*
  325.  * do_order:
  326.  *      Order the strings alphabetically (possibly ignoring case).
  327.  */
  328. void
  329.   do_order(void)
  330. {
  331.     register long i;
  332.     register off_t *lp;
  333.     register STR *fp;
  334.  
  335.     Sort_1 = fopen(Infile, "r");
  336.     Sort_2 = fopen(Infile, "r");
  337.     qsort((char *) Firstch, (int) Num_pts - 1, sizeof *Firstch, cmp_str);
  338. /*      i = Tbl.str_numstr;
  339.  * Fucking brilliant.  Tbl.str_numstr was initialized to zero, and is still zero
  340.  */
  341.     i = Num_pts - 1;
  342.     lp = Seekpts;
  343.     fp = Firstch;
  344.     while (i--)
  345.     *lp++ = fp++->pos;
  346.     fclose(Sort_1);
  347.     fclose(Sort_2);
  348.     Tbl.str_flags |= STR_ORDERED;
  349. }
  350.  
  351. char *
  352.   unctrl(char c)
  353. {
  354.     static char buf[3];
  355.  
  356.     if (isprint(c))
  357.     {
  358.     buf[0] = c;
  359.     buf[1] = '\0';
  360.     }
  361.     else if (c == 0177)
  362.     {
  363.     buf[0] = '^';
  364.     buf[1] = '?';
  365.     }
  366.     else
  367.     {
  368.     buf[0] = '^';
  369.     buf[1] = c + 'A' - 1;
  370.     }
  371.     return buf;
  372. }
  373.  
  374. /*
  375.  * randomize:
  376.  *      Randomize the order of the string table.  We must be careful
  377.  *      not to randomize across delimiter boundaries.  All
  378.  *      randomization is done within each block.
  379.  */
  380. void randomize(void)
  381. {
  382.     register int cnt, i;
  383.     register off_t tmp;
  384.     register off_t *sp;
  385.     extern time_t time(time_t *);
  386.  
  387.     srandom((int) (time((time_t *) NULL) + getpid()));
  388.  
  389.     Tbl.str_flags |= STR_RANDOM;
  390. /*      cnt = Tbl.str_numstr;
  391.  * See comment above.  Isn't this stuff distributed worldwide?  How embarrassing!
  392.  */
  393.     cnt = Num_pts;
  394.  
  395.     /*
  396.      * move things around randomly
  397.      */
  398.  
  399.     for (sp = Seekpts; cnt > 0; cnt--, sp++)
  400.     {
  401.     i = random() % cnt;
  402.     tmp = sp[0];
  403.     sp[0] = sp[i];
  404.     sp[i] = tmp;
  405.     }
  406. }
  407.  
  408. /*
  409.  * main:
  410.  *      Drive the sucker.  There are two main modes -- either we store
  411.  *      the seek pointers, if the table is to be sorted or randomized,
  412.  *      or we write the pointer directly to the file, if we are to stay
  413.  *      in file order.  If the former, we allocate and re-allocate in
  414.  *      CHUNKSIZE blocks; if the latter, we just write each pointer,
  415.  *      and then seek back to the beginning to write in the table.
  416.  */
  417. int main(int ac, char **av)
  418. {
  419.     register unsigned char *sp;
  420.     register FILE *inf, *outf;
  421.     register off_t last_off, length, pos, *p;
  422.     register int first, cnt;
  423.     register char *nsp;
  424.     register STR *fp;
  425.     static char string[257];
  426.  
  427.     getargs(ac, av);        /* evalute arguments */
  428.     if ((inf = fopen(Infile, "r")) == NULL)
  429.     {
  430.     perror(Infile);
  431.     exit(1);
  432.     }
  433.  
  434.     if ((outf = fopen(Outfile, "w")) == NULL)
  435.     {
  436.     perror(Outfile);
  437.     exit(1);
  438.     }
  439.     if (!STORING_PTRS)
  440.     (void) fseek(outf, sizeof Tbl, 0);
  441.  
  442.     /*
  443.      * Write the strings onto the file
  444.      */
  445.  
  446.     Tbl.str_longlen = 0;
  447.     Tbl.str_shortlen = (unsigned int) 0xffffffff;
  448.     Tbl.str_delim = Delimch;
  449.     Tbl.str_version = VERSION;
  450.     first = Oflag;
  451.     add_offset(outf, ftell(inf));
  452.     last_off = 0;
  453.     do
  454.     {
  455.     sp = fgets(string, 256, inf);
  456.     if (sp == NULL || STR_ENDSTRING(sp, Tbl))
  457.     {
  458.         pos = ftell(inf);
  459.         length = pos - last_off - (sp ? strlen(sp) : 0);
  460.         if (!length)
  461.         /* Here's where we go back and fix things, if the
  462.          * 'fortune' just read was the null string.
  463.          * We had to make the assignment of last_off slightly
  464.          * redundant to achieve this.
  465.          */
  466.         {
  467.         if (pos - last_off == 2)
  468.             fix_last_offset(outf, pos);
  469.         last_off = pos;
  470.         continue;
  471.         }
  472.         last_off = pos;
  473.         add_offset(outf, pos);
  474.         if (Tbl.str_longlen < length)
  475.         Tbl.str_longlen = length;
  476.         if (Tbl.str_shortlen > length)
  477.         Tbl.str_shortlen = length;
  478.         first = Oflag;
  479.     }
  480.     else if (first)
  481.     {
  482.         for (nsp = sp; !isalnum(*nsp); nsp++)
  483.         continue;
  484.         ALLOC(Firstch, Num_pts);
  485.         fp = &Firstch[Num_pts - 1];
  486.         if (Iflag && isupper(*nsp))
  487.         fp->first = tolower(*nsp);
  488.         else
  489.         fp->first = *nsp;
  490.         fp->pos = Seekpts[Num_pts - 1];
  491.         first = FALSE;
  492.     }
  493.     }
  494.     while (sp != NULL);
  495.  
  496.     /*
  497.      * write the tables in
  498.      */
  499.  
  500.     fclose(inf);
  501.  
  502.     if (Oflag)
  503.     do_order();
  504.     else if (Rflag)
  505.     randomize();
  506.  
  507.     if (Xflag)
  508.     Tbl.str_flags |= STR_ROTATED;
  509.  
  510.     if (!Sflag)
  511.     {
  512.     printf("\"%s\" created\n", Outfile);
  513.     if (Num_pts == 2)
  514.         puts("There was 1 string");
  515.     else
  516.         printf("There were %ld strings\n", Num_pts - 1);
  517.     printf("Longest string: %lu byte%s\n", Tbl.str_longlen,
  518.            Tbl.str_longlen == 1 ? "" : "s");
  519.     printf("Shortest string: %lu byte%s\n", Tbl.str_shortlen,
  520.            Tbl.str_shortlen == 1 ? "" : "s");
  521.     }
  522.  
  523.     fseek(outf, (off_t) 0, 0);
  524.     Tbl.str_version = htonl(Tbl.str_version);
  525.     Tbl.str_numstr = htonl(Num_pts - 1);
  526.     /* Look, Ma!  After using the variable three times, let's store
  527.      * something in it!
  528.      */
  529.     Tbl.str_longlen = htonl(Tbl.str_longlen);
  530.     Tbl.str_shortlen = htonl(Tbl.str_shortlen);
  531.     Tbl.str_flags = htonl(Tbl.str_flags);
  532.     fwrite((char *) &Tbl, sizeof Tbl, 1, outf);
  533.     if (STORING_PTRS)
  534.     {
  535.     for (p = Seekpts, cnt = Num_pts; cnt--; ++p)
  536.         *p = htonl(*p);
  537.     fwrite((char *) Seekpts, sizeof *Seekpts, (int) Num_pts, outf);
  538.     }
  539.     fclose(outf);
  540.     exit(0);
  541. }
  542.