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

  1. /*      $NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $  */
  2.  
  3. /*-
  4.  * Copyright (c) 1986, 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. /* Modified September, 1995, Amy A. Lewis
  40.  * 1: removed all file-locking dreck.  Unnecessary
  41.  * 2: Fixed bug that made fortune -f report a different list than
  42.  *    fortune with any other parameters, or none, and which forced
  43.  *    the program to read only one file (named 'fortunes')
  44.  * 3: removed the unnecessary print_file_list()
  45.  * 4: Added "OFFDIR" to pathnames.h as the directory in which offensive
  46.  *    fortunes are kept.  This considerably simplifies our life by
  47.  *    permitting us to dispense with a lot of silly tests for the string
  48.  *    "-o" at the end of a filename.
  49.  * 5: I think the problems with trying to find filenames were fixed by
  50.  *    the change in the way that offensive files are defined.  Two birds,
  51.  *    one stone!
  52.  * 6: Calculated probabilities for all files, so that -f will print them.
  53.  */
  54.  
  55. /* Changes Copyright (c) 1997 Dennis L. Clark.  All rights reserved.
  56.  *
  57.  *    The changes in this file may be freely redistributed, modified or
  58.  *    included in other software, as long as both the above copyright
  59.  *    notice and these conditions appear intact.
  60.  */
  61.  
  62. /* Modified May 1997, Dennis L. Clark (dbugger@progsoc.uts.edu.au)
  63.  *  + Various portability fixes
  64.  *  + Percent selection of files with -a now works on datafiles which
  65.  *    appear in both unoffensive and offensive directories (see man page
  66.  *    for details)
  67.  *  + The -s and -l options are now more consistant in their
  68.  *    interpretation of fortune length
  69.  *  + The -s and -l options can now be combined wit the -m option
  70.  */
  71.  
  72. #if 0                /* comment out the stuff here, and get rid of silly warnings */
  73. #ifndef lint
  74. static char copyright[] =
  75. "@(#) Copyright (c) 1986, 1993\n\
  76.     The Regents of the University of California.  All rights reserved.\n";
  77.  
  78. #endif /* not lint */
  79.  
  80. #ifndef lint
  81. #if 0
  82. static char sccsid[] = "@(#)fortune.c    8.1 (Berkeley) 5/31/93";
  83.  
  84. #else
  85. static char rcsid[] = "$NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $";
  86.  
  87. #endif
  88. #endif /* not lint */
  89. #endif /* killing warnings */
  90.  
  91. #define        PROGRAM_NAME        "fortune-mod"
  92. #define        PROGRAM_VERSION        "9708"
  93.  
  94. #include    <sys/types.h>
  95. #include    <sys/time.h>
  96. #include    <sys/param.h>
  97. #include    <sys/stat.h>
  98. #include    <netinet/in.h>
  99.  
  100. #include    <time.h>
  101. #include    <dirent.h>
  102. #include    <fcntl.h>
  103. #include    <assert.h>
  104. #include    <unistd.h>
  105. #include    <stdio.h>
  106. #include    <ctype.h>
  107. #include    <stdlib.h>
  108. #include    <string.h>
  109.  
  110. #ifdef NeXT
  111.     #include    <libc.h>
  112.     #include    <sys/dir.h>
  113.     #define    strdup    NXCopyStringBuffer
  114. #endif
  115.  
  116. /* This makes GNU libc to prototype the BSD regex functions */
  117. #ifdef BSD_REGEX
  118. #define    _REGEX_RE_COMP
  119. #endif
  120.  
  121. #ifdef HAVE_REGEX_H
  122. #include    <regex.h>
  123. #endif
  124. #ifdef HAVE_REGEXP_H
  125. #include    <regexp.h>
  126. #endif
  127. #ifdef HAVE_RX_H
  128. #include    <rx.h>
  129. #endif
  130.  
  131. #include    "strfile.h"
  132.  
  133. #ifndef NeXT
  134.     #define    TRUE    1
  135.     #define    FALSE    0
  136. #endif
  137. #define    bool    short
  138.  
  139. #define    MINW    6        /* minimum wait if desired */
  140. #define    CPERS    20        /* # of chars for each sec */
  141.  
  142. #define    POS_UNKNOWN    ((off_t) -1)    /* pos for file unknown */
  143. #define    NO_PROB        (-1)    /* no prob specified for file */
  144.  
  145. #ifdef DEBUG
  146. #define    DPRINTF(l,x)    if (Debug >= l) fprintf x;
  147. #undef        NDEBUG
  148. #else
  149. #define    DPRINTF(l,x)
  150. #define    NDEBUG    1
  151. #endif
  152.  
  153. typedef struct fd
  154. {
  155.     int percent;
  156.     int fd, datfd;
  157.     off_t pos;
  158.     FILE *inf;
  159.     char *name;
  160.     char *path;
  161.     char *datfile, *posfile;
  162.     bool read_tbl;
  163.     bool was_pos_file;
  164.     STRFILE tbl;
  165.     int num_children;
  166.     struct fd *child, *parent;
  167.     struct fd *next, *prev;
  168. }
  169. FILEDESC;
  170.  
  171. bool Found_one;            /* did we find a match? */
  172. bool Find_files = FALSE;    /* just find a list of proper fortune files */
  173. bool Wait = FALSE;        /* wait desired after fortune */
  174. bool Short_only = FALSE;    /* short fortune desired */
  175. bool Long_only = FALSE;        /* long fortune desired */
  176. bool Offend = FALSE;        /* offensive fortunes only */
  177. bool All_forts = FALSE;        /* any fortune allowed */
  178. bool Equal_probs = FALSE;    /* scatter un-allocated prob equally */
  179.  
  180. #ifndef NO_REGEX
  181. bool Match = FALSE;        /* dump fortunes matching a pattern */
  182.  
  183. #endif
  184. #ifdef DEBUG
  185. bool Debug = FALSE;        /* print debug messages */
  186.  
  187. #endif
  188.  
  189. unsigned char *Fortbuf = NULL;    /* fortune buffer for -m */
  190.  
  191. int Fort_len = 0, Spec_prob = 0,    /* total prob specified on cmd line */
  192.   Num_files, Num_kids,        /* totals of files and children. */
  193.   SLEN = 160;            /* max. characters in a "short" fortune */
  194.  
  195. off_t Seekpts[2];        /* seek pointers to fortunes */
  196.  
  197. FILEDESC *File_list = NULL,    /* Head of file list */
  198.  *File_tail = NULL;        /* Tail of file list */
  199. FILEDESC *Fortfile;        /* Fortune file to use */
  200.  
  201. STRFILE Noprob_tbl;        /* sum of data for all no prob files */
  202.  
  203. #ifdef BSD_REGEX
  204.  
  205. #define    RE_COMP(p)    re_comp(p)
  206. #define    BAD_COMP(f)    ((f) != NULL)
  207. #define    RE_EXEC(p)    re_exec(p)
  208.  
  209. #else
  210.  
  211. #ifdef POSIX_REGEX
  212. #define    RE_COMP(p)    regcomp(&Re_pat, (p), REG_NOSUB)
  213. #define    BAD_COMP(f)    ((f) != 0)
  214. #define    RE_EXEC(p)    (regexec(&Re_pat, (p), 0, NULL, 0) == 0)
  215.  
  216. regex_t Re_pat;
  217. #else
  218. #define NO_REGEX
  219. #endif /* POSIX_REGEX */
  220.  
  221. #endif /* BSD_REGEX */
  222.  
  223. int add_dir(register FILEDESC *);
  224.  
  225. char *program_version(void)
  226. {
  227.     static char buf[BUFSIZ];
  228.     (void) sprintf(buf, "%s version %s", PROGRAM_NAME, PROGRAM_VERSION);
  229.     return buf;
  230. }
  231.  
  232. void usage(void)
  233. {
  234.     (void) fprintf(stderr, "%s\n",program_version());
  235.     (void) fprintf(stderr, "fortune [-a");
  236. #ifdef    DEBUG
  237.     (void) fprintf(stderr, "D");
  238. #endif /* DEBUG */
  239.     (void) fprintf(stderr, "f");
  240. #ifndef    NO_REGEX
  241.     (void) fprintf(stderr, "i");
  242. #endif /* NO_REGEX */
  243.     (void) fprintf(stderr, "losw]");
  244. #ifndef    NO_REGEX
  245.     (void) fprintf(stderr, " [-m pattern]");
  246. #endif /* NO_REGEX */
  247.     (void) fprintf(stderr, " [-n number] [ [#%%] file/directory/all]\n");
  248.     exit(1);
  249. }
  250.  
  251. #define    STR(str)    ((str) == NULL ? "NULL" : (str))
  252.  
  253.  
  254. /*
  255.  * calc_equal_probs:
  256.  *      Set the global values for number of files/children, to be used
  257.  * in printing probabilities when listing files
  258.  */
  259. void calc_equal_probs(void)
  260. {
  261.     FILEDESC *fiddlylist;
  262.  
  263.     Num_files = Num_kids = 0;
  264.     fiddlylist = File_list;
  265.     while (fiddlylist != NULL)
  266.     {
  267.     Num_files++;
  268.     Num_kids += fiddlylist->num_children;
  269.     fiddlylist = fiddlylist->next;
  270.     }
  271. }
  272.  
  273. /*
  274.  * print_list:
  275.  *      Print out the actual list, recursively.
  276.  */
  277. void print_list(register FILEDESC * list, int lev)
  278. {
  279.     while (list != NULL)
  280.     {
  281.     fprintf(stderr, "%*s", lev * 4, "");
  282.     if (list->percent == NO_PROB)
  283.         if (!Equal_probs)
  284. /* This, with some changes elsewhere, gives proper percentages for every case
  285.  * fprintf(stderr, "___%%"); */
  286.         fprintf(stderr, "%5.2f%%", (100.0 - Spec_prob) *
  287.             list->tbl.str_numstr / Noprob_tbl.str_numstr);
  288.         else if (lev == 0)
  289.         fprintf(stderr, "%5.2f%%", 100.0 / Num_files);
  290.         else
  291.         fprintf(stderr, "%5.2f%%", 100.0 / Num_kids);
  292.     else
  293.         fprintf(stderr, "%5.2f%%", 1.0 * list->percent);
  294.     fprintf(stderr, " %s", STR(list->name));
  295.     DPRINTF(1, (stderr, " (%s, %s, %s)\n", STR(list->path),
  296.             STR(list->datfile), STR(list->posfile)));
  297.     putc('\n', stderr);
  298.     if (list->child != NULL)
  299.         print_list(list->child, lev + 1);
  300.     list = list->next;
  301.     }
  302. }
  303.  
  304. #ifndef    NO_REGEX
  305. /*
  306.  * conv_pat:
  307.  *      Convert the pattern to an ignore-case equivalent.
  308.  */
  309. char *conv_pat(register char *orig)
  310. {
  311.     register char *sp;
  312.     register unsigned int cnt;
  313.     register char *new;
  314.  
  315.     cnt = 1;            /* allow for '\0' */
  316.     for (sp = orig; *sp != '\0'; sp++)
  317.     if (isalpha(*sp))
  318.         cnt += 4;
  319.     else
  320.         cnt++;
  321.     if ((new = malloc(cnt)) == NULL)
  322.     {
  323.     fprintf(stderr, "pattern too long for ignoring case\n");
  324.     exit(1);
  325.     }
  326.  
  327.     for (sp = new; *orig != '\0'; orig++)
  328.     {
  329.     if (islower(*orig))
  330.     {
  331.         *sp++ = '[';
  332.         *sp++ = *orig;
  333.         *sp++ = toupper(*orig);
  334.         *sp++ = ']';
  335.     }
  336.     else if (isupper(*orig))
  337.     {
  338.         *sp++ = '[';
  339.         *sp++ = *orig;
  340.         *sp++ = tolower(*orig);
  341.         *sp++ = ']';
  342.     }
  343.     else
  344.         *sp++ = *orig;
  345.     }
  346.     *sp = '\0';
  347.     return new;
  348. }
  349. #endif /* NO_REGEX */
  350.  
  351. /*
  352.  * do_malloc:
  353.  *      Do a malloc, checking for NULL return.
  354.  */
  355. void *do_malloc(unsigned int size)
  356. {
  357.     void *new;
  358.  
  359.     if ((new = malloc(size)) == NULL)
  360.     {
  361.     (void) fprintf(stderr, "fortune: out of memory.\n");
  362.     exit(1);
  363.     }
  364.     return new;
  365. }
  366.  
  367. /*
  368.  * do_free:
  369.  *      Free malloc'ed space, if any.
  370.  */
  371. void do_free(void *ptr)
  372. {
  373.     if (ptr != NULL)
  374.     free(ptr);
  375. }
  376.  
  377. /*
  378.  * copy:
  379.  *      Return a malloc()'ed copy of the string
  380.  */
  381. char *copy(char *str, unsigned int len)
  382. {
  383.     char *new, *sp;
  384.  
  385.     new = do_malloc(len + 1);
  386.     sp = new;
  387.     do
  388.     {
  389.     *sp++ = *str;
  390.     }
  391.     while (*str++);
  392.     return new;
  393. }
  394.  
  395. /*
  396.  * new_fp:
  397.  *      Return a pointer to an initialized new FILEDESC.
  398.  */
  399. FILEDESC *new_fp(void)
  400. {
  401.     register FILEDESC *fp;
  402.  
  403.     fp = (FILEDESC *) do_malloc(sizeof *fp);
  404.     fp->datfd = -1;
  405.     fp->pos = POS_UNKNOWN;
  406.     fp->inf = NULL;
  407.     fp->fd = -1;
  408.     fp->percent = NO_PROB;
  409.     fp->read_tbl = FALSE;
  410.     fp->next = NULL;
  411.     fp->prev = NULL;
  412.     fp->child = NULL;
  413.     fp->parent = NULL;
  414.     fp->datfile = NULL;
  415.     fp->posfile = NULL;
  416.     return fp;
  417. }
  418.  
  419. /*
  420.  * is_dir:
  421.  *      Return TRUE if the file is a directory, FALSE otherwise.
  422.  */
  423. int is_dir(char *file)
  424. {
  425.     auto struct stat sbuf;
  426.  
  427.     if (stat(file, &sbuf) < 0)
  428.     return FALSE;
  429.     return (sbuf.st_mode & S_IFDIR);
  430. }
  431.  
  432. /*
  433.  * is_fortfile:
  434.  *      Return TRUE if the file is a fortune database file.  We try and
  435.  *      exclude files without reading them if possible to avoid
  436.  *      overhead.  Files which start with ".", or which have "illegal"
  437.  *      suffixes, as contained in suflist[], are ruled out.
  438.  */
  439. int is_fortfile(char *file, char **datp, char **posp)
  440. {
  441.     register int i;
  442.     register char *sp;
  443.     register char *datfile;
  444.     static char *suflist[] =
  445.     {                /* list of "illegal" suffixes" */
  446.     "dat", "pos", "c", "h", "p", "i", "f",
  447.     "pas", "ftn", "ins.c", "ins,pas",
  448.     "ins.ftn", "sml",
  449.     NULL
  450.     };
  451.  
  452.     DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file));
  453.  
  454.     if ((sp = strrchr(file, '/')) == NULL)
  455.     sp = file;
  456.     else
  457.     sp++;
  458.     if (*sp == '.')
  459.     {
  460.     DPRINTF(2, (stderr, "FALSE (file starts with '.')\n"));
  461.     return FALSE;
  462.     }
  463.     if ((sp = strrchr(sp, '.')) != NULL)
  464.     {
  465.     sp++;
  466.     for (i = 0; suflist[i] != NULL; i++)
  467.         if (strcmp(sp, suflist[i]) == 0)
  468.         {
  469.         DPRINTF(2, (stderr, "FALSE (file has suffix \".%s\")\n", sp));
  470.         return FALSE;
  471.         }
  472.     }
  473.  
  474.     datfile = copy(file, (unsigned int) (strlen(file) + 4));    /* +4 for ".dat" */
  475.     strcat(datfile, ".dat");
  476.     if (access(datfile, R_OK) < 0)
  477.     {
  478.     free(datfile);
  479.     DPRINTF(2, (stderr, "FALSE (no \".dat\" file)\n"));
  480.     return FALSE;
  481.     }
  482.     if (datp != NULL)
  483.     *datp = datfile;
  484.     else
  485.     free(datfile);
  486.     DPRINTF(2, (stderr, "TRUE\n"));
  487.     return TRUE;
  488. }
  489.  
  490. /*
  491.  * add_file:
  492.  *      Add a file to the file list.
  493.  */
  494. int add_file(int percent, register char *file, char *dir,
  495.          FILEDESC ** head, FILEDESC ** tail, FILEDESC * parent)
  496. {
  497.     register FILEDESC *fp;
  498.     register int fd;
  499.     register char *path;
  500.     register bool was_malloc;
  501.     register bool isdir;
  502.     auto char *sp;
  503.     auto bool found;
  504.  
  505.     if (dir == NULL)
  506.     {
  507.     path = file;
  508.     was_malloc = FALSE;
  509.     }
  510.     else
  511.     {
  512.     path = do_malloc((unsigned int) (strlen(dir) + strlen(file) + 2));
  513.     (void) strcat(strcat(strcpy(path, dir), "/"), file);
  514.     was_malloc = TRUE;
  515.     }
  516.     if ((isdir = is_dir(path)) && parent != NULL)
  517.     {
  518.     if (was_malloc)
  519.         free(path);
  520.     return FALSE;        /* don't recurse */
  521.     }
  522.  
  523.     DPRINTF(1, (stderr, "trying to add file \"%s\"\n", path));
  524.     if ((fd = open(path, 0)) < 0)
  525.     {
  526.     found = FALSE;
  527.     if (dir == NULL && (strchr(file,'/') == NULL))
  528.         if ( ((sp = strrchr(file,'-')) != NULL) && (strcmp(sp,"-o") == 0) )
  529.         {
  530.         /* BSD-style '-o' offensive file suffix */
  531.         *sp = '\0';
  532.         found = add_file(percent, file, OFFDIR, head, tail, parent);
  533.         /* put the suffix back in for better identification later */
  534.         *sp = '-';
  535.         }
  536.         else if (All_forts)
  537.         found = (add_file(percent, file, FORTDIR, head, tail, parent)
  538.             || add_file(percent, file, OFFDIR, head, tail, parent));
  539.         else if (Offend)
  540.         found = add_file(percent, file, OFFDIR, head, tail, parent);
  541.         else
  542.         found = add_file(percent, file, FORTDIR, head, tail, parent);
  543.     if (!found && parent == NULL && dir == NULL)
  544.         perror(path);
  545.     if (was_malloc)
  546.         free(path);
  547.     return found;
  548.     }
  549.  
  550.     DPRINTF(2, (stderr, "path = \"%s\"\n", path));
  551.  
  552.     fp = new_fp();
  553.     fp->fd = fd;
  554.     fp->percent = percent;
  555.     fp->name = file;
  556.     fp->path = path;
  557.     fp->parent = parent;
  558.  
  559.     if ((isdir && !add_dir(fp)) ||
  560.     (!isdir &&
  561.      !is_fortfile(path, &fp->datfile, &fp->posfile)))
  562.     {
  563.     if (parent == NULL)
  564.         fprintf(stderr,
  565.             "fortune:%s not a fortune file or directory\n",
  566.             path);
  567.     if (was_malloc)
  568.         free(path);
  569.     do_free(fp->datfile);
  570.     do_free(fp->posfile);
  571.     if (fp->fd >= 0) close(fp->fd);
  572.     free(fp);
  573.     return FALSE;
  574.     }
  575.     if (*head == NULL)
  576.     *head = *tail = fp;
  577.     else if (fp->percent == NO_PROB)
  578.     {
  579.     (*tail)->next = fp;
  580.     fp->prev = *tail;
  581.     *tail = fp;
  582.     }
  583.     else
  584.     {
  585.     (*head)->prev = fp;
  586.     fp->next = *head;
  587.     *head = fp;
  588.     }
  589.  
  590.     return TRUE;
  591. }
  592.  
  593. /*
  594.  * add_dir:
  595.  *      Add the contents of an entire directory.
  596.  */
  597. int add_dir(register FILEDESC * fp)
  598. {
  599.     register DIR *dir;
  600. #ifdef NeXT
  601.     register struct direct *dirent;
  602. #else
  603.     register struct dirent *dirent;
  604. #endif
  605.     auto FILEDESC *tailp;
  606.     auto char *name;
  607.  
  608.     close(fp->fd);
  609.     fp->fd = -1;
  610.     if ((dir = opendir(fp->path)) == NULL)
  611.     {
  612.     perror(fp->path);
  613.     return FALSE;
  614.     }
  615.     tailp = NULL;
  616.     DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path));
  617.     fp->num_children = 0;
  618.     while ((dirent = readdir(dir)) != NULL)
  619.     {
  620.     if (dirent->d_name[0] == 0)
  621.         continue;
  622.     name = strdup(dirent->d_name);
  623.     if (add_file(NO_PROB, name, fp->path, &fp->child, &tailp, fp))
  624.         fp->num_children++;
  625.     else
  626.         free(name);
  627.     }
  628.     if (fp->num_children == 0)
  629.     {
  630.     fprintf(stderr,
  631.         "fortune: %s: No fortune files in directory.\n", fp->path);
  632.     return FALSE;
  633.     }
  634.     return TRUE;
  635. }
  636.  
  637. /*
  638.  * form_file_list:
  639.  *      Form the file list from the file specifications.
  640.  */
  641. int form_file_list(register char **files, register int file_cnt)
  642. {
  643.     register int i, percent;
  644.     register char *sp;
  645.  
  646.     if (file_cnt == 0)
  647.     if (All_forts)
  648.         return (add_file(NO_PROB, FORTDIR, NULL, &File_list,
  649.                  &File_tail, NULL)
  650.             & add_file(NO_PROB, OFFDIR, NULL, &File_list,
  651.                    &File_tail, NULL));
  652.     else if (Offend)
  653.         return add_file(NO_PROB, OFFDIR, NULL, &File_list,
  654.                 &File_tail, NULL);
  655.     else
  656.         return add_file(NO_PROB, FORTDIR, NULL, &File_list,
  657.                 &File_tail, NULL);
  658.     for (i = 0; i < file_cnt; i++)
  659.     {
  660.     percent = NO_PROB;
  661.     if (!isdigit(files[i][0]))
  662.         sp = files[i];
  663.     else
  664.     {
  665.         percent = 0;
  666.         for (sp = files[i]; isdigit(*sp); sp++)
  667.         percent = percent * 10 + *sp - '0';
  668.         if (percent > 100)
  669.         {
  670.         fprintf(stderr, "percentages must be <= 100\n");
  671.         return FALSE;
  672.         }
  673.         if (*sp == '.')
  674.         {
  675.         fprintf(stderr, "percentages must be integers\n");
  676.         return FALSE;
  677.         }
  678.         /*
  679.          * If the number isn't followed by a '%', then
  680.          * it was not a percentage, just the first part
  681.          * of a file name which starts with digits.
  682.          */
  683.         if (*sp != '%')
  684.         {
  685.         percent = NO_PROB;
  686.         sp = files[i];
  687.         }
  688.         else if (*++sp == '\0')
  689.         {
  690.         if (++i >= file_cnt)
  691.         {
  692.             fprintf(stderr, "percentages must precede files\n");
  693.             return FALSE;
  694.         }
  695.         sp = files[i];
  696.         }
  697.     }
  698.     if (strcmp(sp, "all") == 0)
  699.         sp = FORTDIR;
  700.     if (!add_file(percent, sp, NULL, &File_list, &File_tail, NULL))
  701.         return FALSE;
  702.     }
  703.     return TRUE;
  704. }
  705.  
  706. /*
  707.  *    This routine evaluates the arguments on the command line
  708.  */
  709. void getargs(int argc, char **argv)
  710. {
  711.     register int ignore_case;
  712.  
  713. #ifndef NO_REGEX
  714.     register char *pat = NULL;
  715.  
  716. #endif /* NO_REGEX */
  717.     extern char *optarg;
  718.     extern int optind;
  719.     int ch;
  720.  
  721.     ignore_case = FALSE;
  722.  
  723. #ifdef DEBUG
  724.     while ((ch = getopt(argc, argv, "aDefilm:n:osvw")) != EOF)
  725. #else
  726.     while ((ch = getopt(argc, argv, "aefilm:n:osvw")) != EOF)
  727. #endif /* DEBUG */
  728.     switch (ch)
  729.       {
  730.       case 'a':        /* any fortune */
  731.           All_forts++;
  732.           break;
  733. #ifdef DEBUG
  734.       case 'D':
  735.           Debug++;
  736.           break;
  737. #endif /* DEBUG */
  738.       case 'e':
  739.           Equal_probs++;    /* scatter un-allocted prob equally */
  740.           break;
  741.       case 'f':        /* find fortune files */
  742.           Find_files++;
  743.           break;
  744.       case 'l':        /* long ones only */
  745.           Long_only++;
  746.           Short_only = FALSE;
  747.           break;
  748.       case 'n':
  749.           SLEN = atoi(optarg);
  750.           break;
  751.       case 'o':        /* offensive ones only */
  752.           Offend++;
  753.           break;
  754.       case 's':        /* short ones only */
  755.           Short_only++;
  756.           Long_only = FALSE;
  757.           break;
  758.       case 'w':        /* give time to read */
  759.           Wait++;
  760.           break;
  761. #ifdef    NO_REGEX
  762.       case 'i':        /* case-insensitive match */
  763.       case 'm':        /* dump out the fortunes */
  764.           (void) fprintf(stderr,
  765.           "fortune: can't match fortunes on this system (Sorry)\n");
  766.           exit(0);
  767. #else /* NO_REGEX */
  768.       case 'm':        /* dump out the fortunes */
  769.           Match++;
  770.           pat = optarg;
  771.           break;
  772.       case 'i':        /* case-insensitive match */
  773.           ignore_case++;
  774.           break;
  775. #endif /* NO_REGEX */
  776.       case 'v':
  777.           (void) printf("%s\n", program_version());
  778.           exit(0);
  779.       case '?':
  780.       default:
  781.           usage();
  782.       }
  783.     argc -= optind;
  784.     argv += optind;
  785.  
  786.     if (!form_file_list(argv, argc))
  787.     exit(1);        /* errors printed through form_file_list() */
  788. #ifdef DEBUG
  789. /*      if (Debug >= 1)
  790.  * print_list(File_list, 0); */
  791. #endif /* DEBUG */
  792. /* If (Find_files) print_list() moved to main */
  793. #ifndef NO_REGEX
  794.     if (pat != NULL)
  795.     {
  796.     if (ignore_case)
  797.         pat = conv_pat(pat);
  798.     if (BAD_COMP(RE_COMP(pat)))
  799.     {
  800. #ifndef REGCMP
  801.         fprintf(stderr, "%s\n", pat);
  802. #else /* REGCMP */
  803.         fprintf(stderr, "bad pattern: %s\n", pat);
  804. #endif /* REGCMP */
  805.     }
  806.     }
  807. #endif /* NO_REGEX */
  808. }
  809.  
  810. /*
  811.  * init_prob:
  812.  *      Initialize the fortune probabilities.
  813.  */
  814. void init_prob(void)
  815. {
  816.     register FILEDESC *fp, *last;
  817.     register int percent, num_noprob, frac;
  818.  
  819.     /*
  820.      * Distribute the residual probability (if any) across all
  821.      * files with unspecified probability (i.e., probability of 0)
  822.      * (if any).
  823.      */
  824.  
  825.     percent = 0;
  826.     num_noprob = 0;
  827.     last = NULL;
  828.     for (fp = File_tail; fp != NULL; fp = fp->prev)
  829.     if (fp->percent == NO_PROB)
  830.     {
  831.         num_noprob++;
  832.         if (Equal_probs)
  833.         last = fp;
  834.     }
  835.     else
  836.         percent += fp->percent;
  837.     DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's\n",
  838.         percent, num_noprob));
  839.     if (percent > 100)
  840.     {
  841.     fprintf(stderr,
  842.         "fortune: probabilities sum to %d%%!\n", percent);
  843.     exit(1);
  844.     }
  845.     else if (percent < 100 && num_noprob == 0)
  846.     {
  847.     fprintf(stderr,
  848.         "fortune: no place to put residual probability (%d%%)\n",
  849.         percent);
  850.     exit(1);
  851.     }
  852.     else if (percent == 100 && num_noprob != 0)
  853.     {
  854.     fprintf(stderr,
  855.         "fortune: no probability left to put in residual files\n");
  856.     exit(1);
  857.     }
  858.     Spec_prob = percent;    /* this is for -f when % is specified on cmd line */
  859.     percent = 100 - percent;
  860.     if (Equal_probs)
  861.     if (num_noprob != 0)
  862.     {
  863.         if (num_noprob > 1)
  864.         {
  865.         frac = percent / num_noprob;
  866.         DPRINTF(1, (stderr, ", frac = %d%%", frac));
  867.         for (fp = File_list; fp != last; fp = fp->next)
  868.             if (fp->percent == NO_PROB)
  869.             {
  870.             fp->percent = frac;
  871.             percent -= frac;
  872.             }
  873.         }
  874.         last->percent = percent;
  875.         DPRINTF(1, (stderr, ", residual = %d%%", percent));
  876.     }
  877.     else
  878.     {
  879.         DPRINTF(1, (stderr,
  880.             ", %d%% distributed over remaining fortunes\n",
  881.             percent));
  882.     }
  883.     DPRINTF(1, (stderr, "\n"));
  884.  
  885. #ifdef DEBUG
  886. /*      if (Debug >= 1)
  887.      * print_list(File_list, 0); *//* Causes crash with new %% code */
  888. #endif
  889. }
  890.  
  891. /*
  892.  * zero_tbl:
  893.  *      Zero out the fields we care about in a tbl structure.
  894.  */
  895. void zero_tbl(register STRFILE * tp)
  896. {
  897.     tp->str_numstr = 0;
  898.     tp->str_longlen = 0;
  899.     tp->str_shortlen = -1;
  900. }
  901.  
  902. /*
  903.  * sum_tbl:
  904.  *      Merge the tbl data of t2 into t1.
  905.  */
  906. void sum_tbl(register STRFILE * t1, register STRFILE * t2)
  907. {
  908.     t1->str_numstr += t2->str_numstr;
  909.     if (t1->str_longlen < t2->str_longlen)
  910.     t1->str_longlen = t2->str_longlen;
  911.     if (t1->str_shortlen > t2->str_shortlen)
  912.     t1->str_shortlen = t2->str_shortlen;
  913. }
  914.  
  915. /*
  916.  * get_tbl:
  917.  *      Get the tbl data file the datfile.
  918.  */
  919. void get_tbl(FILEDESC * fp)
  920. {
  921.     auto int fd;
  922.     register FILEDESC *child;
  923.  
  924.     if (fp->read_tbl)
  925.     return;
  926.     if (fp->child == NULL)
  927.     {
  928.     if ((fd = open(fp->datfile, 0)) < 0)
  929.     {
  930.         perror(fp->datfile);
  931.         exit(1);
  932.     }
  933.     if (read(fd, (char *) &fp->tbl, sizeof fp->tbl) != sizeof fp->tbl)
  934.     {
  935.         fprintf(stderr,
  936.             "fortune: %s corrupted\n", fp->path);
  937.         exit(1);
  938.     }
  939.     /* fp->tbl.str_version = ntohl(fp->tbl.str_version); */
  940.     fp->tbl.str_numstr = ntohl(fp->tbl.str_numstr);
  941.     fp->tbl.str_longlen = ntohl(fp->tbl.str_longlen);
  942.     fp->tbl.str_shortlen = ntohl(fp->tbl.str_shortlen);
  943.     fp->tbl.str_flags = ntohl(fp->tbl.str_flags);
  944.     close(fd);
  945.     }
  946.     else
  947.     {
  948.     zero_tbl(&fp->tbl);
  949.     for (child = fp->child; child != NULL; child = child->next)
  950.     {
  951.         get_tbl(child);
  952.         sum_tbl(&fp->tbl, &child->tbl);
  953.     }
  954.     }
  955.     fp->read_tbl = TRUE;
  956. }
  957.  
  958. /*
  959.  * sum_noprobs:
  960.  *      Sum up all the noprob probabilities, starting with fp.
  961.  */
  962. void sum_noprobs(register FILEDESC * fp)
  963. {
  964.     static bool did_noprobs = FALSE;
  965.  
  966.     if (did_noprobs)
  967.     return;
  968.     zero_tbl(&Noprob_tbl);
  969.     while (fp != NULL)
  970.     {
  971.     get_tbl(fp);
  972.     /* This conditional should help us return correct values for -f
  973.      * when a percentage is specified */
  974.     if (fp->percent == NO_PROB)
  975.         sum_tbl(&Noprob_tbl, &fp->tbl);
  976.     fp = fp->next;
  977.     }
  978.     did_noprobs = TRUE;
  979. }
  980.  
  981. /*
  982.  * pick_child
  983.  *      Pick a child from a chosen parent.
  984.  */
  985. FILEDESC *pick_child(FILEDESC * parent)
  986. {
  987.     register FILEDESC *fp;
  988.     register int choice;
  989.  
  990.     if (Equal_probs)
  991.     {
  992.     choice = random() % parent->num_children;
  993.     DPRINTF(1, (stderr, "    choice = %d (of %d)\n",
  994.             choice, parent->num_children));
  995.     for (fp = parent->child; choice--; fp = fp->next)
  996.         continue;
  997.     DPRINTF(1, (stderr, "    using %s\n", fp->name));
  998.     return fp;
  999.     }
  1000.     else
  1001.     {
  1002.     get_tbl(parent);
  1003.     choice = random() % parent->tbl.str_numstr;
  1004.     DPRINTF(1, (stderr, "    choice = %d (of %ld)\n",
  1005.             choice, parent->tbl.str_numstr));
  1006.     for (fp = parent->child; choice >= fp->tbl.str_numstr;
  1007.          fp = fp->next)
  1008.     {
  1009.         choice -= fp->tbl.str_numstr;
  1010.         DPRINTF(1, (stderr, "\tskip %s, %ld (choice = %d)\n",
  1011.             fp->name, fp->tbl.str_numstr, choice));
  1012.     }
  1013.     DPRINTF(1, (stderr, "    using %s, %ld\n", fp->name,
  1014.             fp->tbl.str_numstr));
  1015.     return fp;
  1016.     }
  1017. }
  1018.  
  1019. /*
  1020.  * open_dat:
  1021.  *      Open up the dat file if we need to.
  1022.  */
  1023. void open_dat(FILEDESC * fp)
  1024. {
  1025.     if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, 0)) < 0)
  1026.     {
  1027.     perror(fp->datfile);
  1028.     exit(1);
  1029.     }
  1030. }
  1031.  
  1032. /*
  1033.  * get_pos:
  1034.  *      Get the position from the pos file, if there is one.  If not,
  1035.  *      return a random number.
  1036.  */
  1037. void get_pos(FILEDESC * fp)
  1038. {
  1039.     assert(fp->read_tbl);
  1040.     if (fp->pos == POS_UNKNOWN)
  1041.     {
  1042.     fp->pos = random() % fp->tbl.str_numstr;
  1043.     }
  1044.     if (++(fp->pos) >= fp->tbl.str_numstr)
  1045.     fp->pos -= fp->tbl.str_numstr;
  1046.     DPRINTF(1, (stderr, "pos for %s is %ld\n", fp->name, fp->pos));
  1047. }
  1048.  
  1049. /*
  1050.  * get_fort:
  1051.  *      Get the fortune data file's seek pointer for the next fortune.
  1052.  */
  1053. void get_fort(void)
  1054. {
  1055.     register FILEDESC *fp;
  1056.     register int choice;
  1057.  
  1058.     if (File_list->next == NULL || File_list->percent == NO_PROB)
  1059.     fp = File_list;
  1060.     else
  1061.     {
  1062.     choice = random() % 100;
  1063.     DPRINTF(1, (stderr, "choice = %d\n", choice));
  1064.     for (fp = File_list; fp->percent != NO_PROB; fp = fp->next)
  1065.         if (choice < fp->percent)
  1066.         break;
  1067.         else
  1068.         {
  1069.         choice -= fp->percent;
  1070.         DPRINTF(1, (stderr,
  1071.                 "    skip \"%s\", %d%% (choice = %d)\n",
  1072.                 fp->name, fp->percent, choice));
  1073.         }
  1074.     DPRINTF(1, (stderr,
  1075.             "using \"%s\", %d%% (choice = %d)\n",
  1076.             fp->name, fp->percent, choice));
  1077.     }
  1078.     if (fp->percent != NO_PROB)
  1079.     get_tbl(fp);
  1080.     else
  1081.     {
  1082.     if (fp->next != NULL)
  1083.     {
  1084.         sum_noprobs(fp);
  1085.         choice = random() % Noprob_tbl.str_numstr;
  1086.         DPRINTF(1, (stderr, "choice = %d (of %ld) \n", choice,
  1087.             Noprob_tbl.str_numstr));
  1088.         while (choice >= fp->tbl.str_numstr)
  1089.         {
  1090.         choice -= fp->tbl.str_numstr;
  1091.         fp = fp->next;
  1092.         DPRINTF(1, (stderr,
  1093.                 "    skip \"%s\", %ld (choice = %d)\n",
  1094.                 fp->name, fp->tbl.str_numstr,
  1095.                 choice));
  1096.         }
  1097.         DPRINTF(1, (stderr, "using \"%s\", %ld\n", fp->name,
  1098.             fp->tbl.str_numstr));
  1099.     }
  1100.     get_tbl(fp);
  1101.     }
  1102.     if (fp->child != NULL)
  1103.     {
  1104.     DPRINTF(1, (stderr, "picking child\n"));
  1105.     fp = pick_child(fp);
  1106.     }
  1107.     Fortfile = fp;
  1108.     get_pos(fp);
  1109.     open_dat(fp);
  1110.     lseek(fp->datfd,
  1111.       (off_t) (sizeof fp->tbl + fp->pos * sizeof Seekpts[0]), 0);
  1112.     read(fp->datfd, Seekpts, sizeof Seekpts);
  1113.     Seekpts[0] = ntohl(Seekpts[0]);
  1114.     Seekpts[1] = ntohl(Seekpts[1]);
  1115. }
  1116.  
  1117. /*
  1118.  * open_fp:
  1119.  *      Assocatiate a FILE * with the given FILEDESC.
  1120.  */
  1121. void open_fp(FILEDESC * fp)
  1122. {
  1123.     if (fp->inf == NULL && (fp->inf = fdopen(fp->fd, "r")) == NULL)
  1124.     {
  1125.     perror(fp->path);
  1126.     exit(1);
  1127.     }
  1128. }
  1129.  
  1130. #ifndef    NO_REGEX
  1131. /*
  1132.  * maxlen_in_list
  1133.  *      Return the maximum fortune len in the file list.
  1134.  */
  1135. int maxlen_in_list(FILEDESC * list)
  1136. {
  1137.     register FILEDESC *fp;
  1138.     register int len, maxlen;
  1139.  
  1140.     maxlen = 0;
  1141.     for (fp = list; fp != NULL; fp = fp->next)
  1142.     {
  1143.     if (fp->child != NULL)
  1144.     {
  1145.         if ((len = maxlen_in_list(fp->child)) > maxlen)
  1146.         maxlen = len;
  1147.     }
  1148.     else
  1149.     {
  1150.         get_tbl(fp);
  1151.         if (fp->tbl.str_longlen > maxlen)
  1152.         maxlen = fp->tbl.str_longlen;
  1153.     }
  1154.     }
  1155.     return maxlen;
  1156. }
  1157.  
  1158. /*
  1159.  * matches_in_list
  1160.  *      Print out the matches from the files in the list.
  1161.  */
  1162. void matches_in_list(FILEDESC * list)
  1163. {
  1164.     unsigned char *sp;
  1165.     register FILEDESC *fp;
  1166.     int in_file, nchar;
  1167.  
  1168.     for (fp = list; fp != NULL; fp = fp->next)
  1169.     {
  1170.     if (fp->child != NULL)
  1171.     {
  1172.         matches_in_list(fp->child);
  1173.         continue;
  1174.     }
  1175.     DPRINTF(1, (stderr, "searching in %s\n", fp->path));
  1176.     open_fp(fp);
  1177.     sp = Fortbuf;
  1178.     in_file = FALSE;
  1179.     while (fgets(sp, Fort_len, fp->inf) != NULL)
  1180.         if (!STR_ENDSTRING(sp, fp->tbl))
  1181.         sp += strlen(sp);
  1182.         else
  1183.         {
  1184.         *sp = '\0';
  1185.         nchar = sp - Fortbuf;
  1186.         DPRINTF(1, (stdout, "nchar = %d\n", nchar));
  1187.         if ( (nchar < SLEN || !Short_only) &&
  1188.             (nchar > SLEN || !Long_only) &&
  1189.             RE_EXEC(Fortbuf) )
  1190.         {
  1191.             if (!in_file)
  1192.             {
  1193.             fprintf(stderr, "(%s)\n%c\n", fp->name, fp->tbl.str_delim);
  1194.             Found_one = TRUE;
  1195.             in_file = TRUE;
  1196.             }
  1197.             fwrite(Fortbuf, 1, sp - Fortbuf, stdout);
  1198.             printf("%c\n", fp->tbl.str_delim);
  1199.         }
  1200.         sp = Fortbuf;
  1201.         }
  1202.     }
  1203. }
  1204.  
  1205. /*
  1206.  * find_matches:
  1207.  *      Find all the fortunes which match the pattern we've been given.
  1208.  */
  1209. int find_matches(void)
  1210. {
  1211.     Fort_len = maxlen_in_list(File_list);
  1212.     DPRINTF(2, (stderr, "Maximum length is %d\n", Fort_len));
  1213.     /* extra length, "%\n" is appended */
  1214.     Fortbuf = do_malloc((unsigned int) Fort_len + 10);
  1215.  
  1216.     Found_one = FALSE;
  1217.     matches_in_list(File_list);
  1218.     return Found_one;
  1219.     /* NOTREACHED */
  1220. }
  1221. #endif /* NO_REGEX */
  1222.  
  1223. void display(FILEDESC * fp)
  1224. {
  1225.     register char *p, ch;
  1226.     unsigned char line[BUFSIZ];
  1227.  
  1228.     open_fp(fp);
  1229.     fseek(fp->inf, (long) Seekpts[0], 0);
  1230.     for (Fort_len = 0; fgets(line, sizeof line, fp->inf) != NULL &&
  1231.      !STR_ENDSTRING(line, fp->tbl); Fort_len++)
  1232.     {
  1233.     if (fp->tbl.str_flags & STR_ROTATED)
  1234.         for (p = line; (ch = *p); ++p)
  1235.         if (isupper(ch))
  1236.             *p = 'A' + (ch - 'A' + 13) % 26;
  1237.         else if (islower(ch))
  1238.             *p = 'a' + (ch - 'a' + 13) % 26;
  1239.     fputs(line, stdout);
  1240.     }
  1241.     fflush(stdout);
  1242. }
  1243.  
  1244. /*
  1245.  * fortlen:
  1246.  *      Return the length of the fortune.
  1247.  */
  1248. int fortlen(void)
  1249. {
  1250.     register int nchar;
  1251.     unsigned char line[BUFSIZ];
  1252.  
  1253.     if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED)))
  1254.     nchar = (Seekpts[1] - Seekpts[0]) - 2;    /* for %^J delimiter */
  1255.     else
  1256.     {
  1257.     open_fp(Fortfile);
  1258.     fseek(Fortfile->inf, (long) Seekpts[0], 0);
  1259.     nchar = 0;
  1260.     while (fgets(line, sizeof line, Fortfile->inf) != NULL &&
  1261.            !STR_ENDSTRING(line, Fortfile->tbl))
  1262.         nchar += strlen(line);
  1263.     }
  1264.     Fort_len = nchar;
  1265.     return nchar;
  1266. }
  1267.  
  1268. int max(register int i, register int j)
  1269. {
  1270.     return (i >= j ? i : j);
  1271. }
  1272.  
  1273. int main(int ac, char *av[])
  1274. {
  1275.     getargs(ac, av);
  1276.  
  1277. #ifndef NO_REGEX
  1278.     if (Match)
  1279.     exit(find_matches() != 0);
  1280. #endif
  1281.     init_prob();
  1282.     if (Find_files)
  1283.     {
  1284.     sum_noprobs(File_list);
  1285.     if (Equal_probs)
  1286.         calc_equal_probs();
  1287.     print_list(File_list, 0);
  1288.     exit(0);
  1289.     }
  1290.     srandom((int) (time((time_t *) NULL) + getpid()));
  1291.     do
  1292.     {
  1293.     get_fort();
  1294.     }
  1295.     while ((Short_only && fortlen() > SLEN) ||
  1296.        (Long_only && fortlen() <= SLEN));
  1297.  
  1298.     display(Fortfile);
  1299.  
  1300.     if (Wait)
  1301.     {
  1302.         fortlen();
  1303.     sleep((unsigned int) max(Fort_len / CPERS, MINW));
  1304.     }
  1305.     exit(0);
  1306.     /* NOTREACHED */
  1307. }
  1308.