home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Games / fortune / Source / fortune.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-06-17  |  28.1 KB  |  1,327 lines

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