home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / games / volume1 / fortune / part01 / fortune.c < prev    next >
C/C++ Source or Header  |  1987-05-26  |  9KB  |  443 lines

  1. /* $Header: fortune.c,v 1.18 87/05/08 13:26:02 arnold Exp $ */
  2.  
  3. # include    <sys/types.h>
  4. # include    <stdio.h>
  5. # include    <sys/file.h>
  6. # include    <sys/stat.h>
  7. # include    "strfile.h"
  8.  
  9. #ifndef NO_REGEX
  10. # include    <ctype.h>
  11. #endif
  12.  
  13. # define    TRUE    1
  14. # define    FALSE    0
  15. # define    bool    short
  16.  
  17. # define    MINW    6        /* minimum wait if desired */
  18. # define    CPERS    20        /* # of chars for each sec */
  19. # define    SLEN    160        /* # of chars in short fortune */
  20.  
  21. # define    FORTFILE    "/usr/games/lib/fortunes.dat"
  22.  
  23. bool    Wflag        = FALSE;    /* wait desired after fortune */
  24. bool    Sflag        = FALSE;    /* short fortune desired */
  25. bool    Lflag        = FALSE;    /* long fortune desired */
  26. bool    Oflag        = FALSE;    /* offensive fortunes only */
  27. bool    Aflag        = FALSE;    /* any fortune allowed */
  28. #ifndef NO_REGEX
  29. bool    Mflag        = FALSE;    /* dump fortunes matching a pattern */
  30. #endif
  31.  
  32. char    *Fortfile    = FORTFILE,    /* fortune database */
  33.     *Usage[]    = {
  34. # ifdef    NO_REGEX
  35.        "usage:  fortune [ - ] [ -wsloa ] [ file ]",
  36. # else
  37.        "usage:  fortune [ - ] [ -wsloai ] [ -m pattern ] [ file ]",
  38. # endif
  39.        "    - - give this summary of usage",
  40.        "    w - have program wait after printing message in order",
  41.        "        to give time to read",
  42.        "    s - short fortunes only",
  43.        "    l - long fortunes only",
  44.        "    o - offensive fortunes only",
  45.        "    a - any fortune, regular or offensive",
  46. # ifndef NO_REGEX
  47.        "    m - print fortunes which match a pattern",
  48.        "    i - ignore case in matching patterns",
  49. # endif
  50.        "        Mail suggested fortunes to \"fortune@ucbvax.berkeley.edu\"",
  51.     NULL
  52.     };
  53.  
  54. off_t    Seekpts[2];            /* seek pointers to fortunes */
  55.  
  56. FILE    *Inf;                /* input file */
  57.  
  58. STRFILE    Tbl;                /* input table */
  59.  
  60. char    *malloc();
  61.  
  62. #ifndef NO_REGEX
  63. char    *conv_pat();
  64. #endif
  65.  
  66. #ifndef NO_REGEX
  67. #ifdef REGCMP
  68. # define    RE_COMP(p)    (Re_pat = regcmp(p, NULL))
  69. # define    BAD_COMP(f)    ((f) == NULL)
  70. # define    RE_EXEC(p)    regex(Re_pat, (p))
  71.  
  72. char    *Re_pat;
  73.  
  74. char    *regcmp(), *regex();
  75. #else
  76. # define    RE_COMP(p)    (p = re_comp(p))
  77. # define    BAD_COMP(f)    ((f) != NULL)
  78. # define    RE_EXEC(p)    re_exec(p)
  79.  
  80. char    *re_comp(), *re_exec();
  81. #endif
  82. #endif
  83.  
  84. time_t    time();
  85.  
  86. main(ac, av)
  87. int    ac;
  88. char    *av[];
  89. {
  90.     register char    c;
  91.     register int    nchar = 0;
  92.  
  93.     getargs(ac, av);
  94.     if ((Inf = fopen(Fortfile, "r+")) == NULL) {
  95.         perror(Fortfile);
  96.         exit(-1);
  97.     }
  98.     if (fread((char *) &Tbl, sizeof Tbl, 1, Inf) != 1) {    /* NOSTRICT */
  99.         fprintf(stderr, "fortune file is truncated\n");
  100.         exit(-1);
  101.     }
  102.     if (Tbl.str_longlen <= SLEN && Lflag) {
  103.         fprintf(stderr, "Sorry, no long strings in this file\n");
  104.         exit(0);
  105.     }
  106.     if (Tbl.str_shortlen > SLEN && Sflag) {
  107.         fprintf(stderr, "Sorry, no short strings in this file\n");
  108.         exit(0);
  109.     }
  110.  
  111.     /*
  112.      * initialize the pointer to the first -o fortune if need be.
  113.      */
  114.     if (Tbl.str_delims[2] == 0)
  115.         Tbl.str_delims[2] = Tbl.str_delims[0];
  116.  
  117. #ifndef NO_REGEX
  118.     if (Mflag) {
  119.         find_matches();
  120.         /* NOTREACHED */
  121.     }
  122. #endif
  123.  
  124.     do {
  125.         getfort();
  126.     } while ((Sflag && !is_short()) || (Lflag && !is_long()));
  127.  
  128.     (void) fseek(Inf, Seekpts[0], 0);
  129.     while (c = getc(Inf)) {
  130.         nchar++;
  131.         putchar(c);
  132.     }
  133.     (void) fflush(stdout);
  134.     (void) fseek(Inf, 0L, 0);
  135. #ifdef    LOCK_EX
  136.     /*
  137.      * if we can, we exclusive lock, but since it isn't very
  138.      * important, we just punt if we don't have easy locking
  139.      * available.
  140.      */
  141.     (void) flock(fileno(Inf), LOCK_EX);
  142. #endif    LOCK_EX
  143.     if (fwrite((char *) &Tbl, 1, sizeof Tbl, Inf) != sizeof Tbl)
  144.         fprintf(stderr, "can't update fortune data file\n");
  145. #ifdef    LOCK_EX
  146.     (void) flock(fileno(Inf), LOCK_UN);
  147. #endif    LOCK_EX
  148.     if (Wflag)
  149.         sleep((unsigned int) max((int) nchar / CPERS, MINW));
  150.     exit(0);
  151. }
  152.  
  153. /*
  154.  * is_short:
  155.  *    Return TRUE if fortune is "short".
  156.  */
  157. is_short()
  158. {
  159.     register int    nchar;
  160.  
  161.     if (!(Tbl.str_flags & (STR_RANDOM | STR_ORDERED)))
  162.         return (Seekpts[1] - Seekpts[0] <= SLEN);
  163.     (void) fseek(Inf, Seekpts[0], 0);
  164.     nchar = 0;
  165.     while (getc(Inf))
  166.         nchar++;
  167.     return (nchar <= SLEN);
  168. }
  169.  
  170. /*
  171.  * is_long:
  172.  *    Return TRUE if fortune is "long".
  173.  */
  174. is_long()
  175. {
  176.     register int    nchar;
  177.  
  178.     if (!(Tbl.str_flags & (STR_RANDOM | STR_ORDERED)))
  179.         return (Seekpts[1] - Seekpts[0] > SLEN);
  180.     (void) fseek(Inf, Seekpts[0], 0);
  181.     nchar = 0;
  182.     while (getc(Inf))
  183.         nchar++;
  184.     return (nchar > SLEN);
  185. }
  186.  
  187. /*
  188.  *    This routine evaluates the arguments on the command line
  189.  */
  190. getargs(ac, av)
  191. register int    ac;
  192. register char    *av[];
  193. {
  194.     register int    i;
  195.     register char    *sp;
  196.     register int    j;
  197.     register short    bad;
  198.     register int    ignore_case;
  199. # ifndef NO_REGEX
  200.     register char    *pat;
  201. # endif
  202.  
  203.     ignore_case = FALSE;
  204.     bad = 0;
  205.     pat = NULL;
  206.     for (i = 1; i < ac; i++)  {
  207.         if (av[i][0] != '-') {
  208.             (void) setuid(getuid());
  209.             (void) setgid(getgid());
  210.             Fortfile = av[i];
  211.         }
  212.         else if (av[i][1] == '\0') {
  213.             j = 0;
  214.             while (Usage[j] != NULL)
  215.                 puts(Usage[j++]);
  216.             exit(0);
  217.             /* NOTREACHED */
  218.         }
  219.         else
  220.             for (sp = &av[i][1]; *sp != '\0'; sp++)
  221.                 switch (*sp) {
  222.                   case 'w':    /* give time to read */
  223.                     Wflag++;
  224.                     break;
  225.                   case 's':    /* short ones only */
  226.                     Sflag++;
  227.                     Lflag = 0;
  228.                     break;
  229.                   case 'l':    /* long ones only */
  230.                     Lflag++;
  231.                     Sflag = 0;
  232.                     break;
  233.                   case 'o':    /* offensive ones only */
  234.                     Oflag++;
  235.                     break;
  236.                   case 'a':    /* any fortune */
  237.                     Aflag++;
  238.                     /*
  239.                      * initialize the random number
  240.                      * generator; throw away the first
  241.                      * few numbers to avoid any non-
  242.                      * randomness in startup
  243.                      */
  244.                     srnd(time((time_t *) NULL) + getpid());
  245.                     for (j = 0; j < 20; j++)
  246.                         (void) rnd(100);
  247.                     break;
  248.                   case 'm':    /* dump out the fortunes */
  249. # ifdef    NO_REGEX
  250.                   case 'i':
  251.                     fprintf(stderr,
  252.                         "can't match fortunes on this system (Sorry)\n");
  253.                     bad++;
  254. # else
  255.                     Mflag++;
  256.                     if (sp[1]) {
  257.                         pat = ++sp;
  258.                         while (*sp)
  259.                             sp++;
  260.                     }
  261.                     else if (i + 1 < ac)
  262.                         pat = av[++i];
  263.                     else {
  264.                         fprintf(stderr,
  265.                             "must give pattern\n");
  266.                         bad++;
  267.                         break;
  268.                     }
  269.                     break;
  270.                   case 'i':
  271.                     ignore_case++;
  272.                     break;
  273. # endif    NO_REGEX
  274.                   default:
  275.                     fprintf(stderr, "unknown flag: '%c'\n",
  276.                         *sp);
  277.                     bad++;
  278.                     break;
  279.                 }
  280.     }
  281.  
  282. # ifndef NO_REGEX
  283.     if (pat != NULL) {
  284.         if (ignore_case)
  285.             pat = conv_pat(pat);
  286.         if (BAD_COMP(RE_COMP(pat))) {
  287. #ifndef REGCMP
  288.             fprintf(stderr, "%s\n", pat);
  289. #else
  290.             fprintf(stderr, "bad pattern: %s\n", pat);
  291. #endif
  292.             bad++;
  293.         }
  294.     }
  295. # endif    NO_REGEX
  296.  
  297.     if (bad) {
  298.         printf("use \"%s -\" to get usage\n", av[0]);
  299.         exit(-1);
  300.     }
  301. }
  302.  
  303. /*
  304.  * getfort:
  305.  *    Get the fortune data file's seek pointer for the next fortune.
  306.  */
  307. getfort()
  308. {
  309.     register int    fortune;
  310.  
  311.     /*
  312.      * Make sure all values are in range.
  313.      */
  314.  
  315.     if (Tbl.str_delims[1] >= Tbl.str_delims[0])
  316.         Tbl.str_delims[1] %= Tbl.str_delims[0];
  317.     if (Tbl.str_delims[2] >= Tbl.str_numstr) {
  318.         Tbl.str_delims[2] -= Tbl.str_delims[0];
  319.         Tbl.str_delims[2] %= Tbl.str_numstr - Tbl.str_delims[0];
  320.         Tbl.str_delims[2] += Tbl.str_delims[0];
  321.     }
  322.  
  323.     if (Aflag) {
  324.         if (rnd((int) Tbl.str_numstr) < Tbl.str_delims[0])
  325.             fortune = Tbl.str_delims[1]++;
  326.         else
  327.             fortune = Tbl.str_delims[2]++;
  328.     }
  329.     else if (Oflag)
  330.         fortune = Tbl.str_delims[2]++;
  331.     else
  332.         fortune = Tbl.str_delims[1]++;
  333.  
  334.     (void) fseek(Inf, (off_t) (sizeof Seekpts[0]) * fortune + sizeof Tbl,
  335.              0);
  336.     if (fread((char *) Seekpts, (sizeof Seekpts[0]), 2, Inf) < 2) {
  337.         fprintf(stderr, "fortune data file corrupted\n");
  338.         exit(-2);
  339.     }
  340. }
  341.  
  342. max(i, j)
  343. register int    i, j;
  344. {
  345.     return (i >= j ? i : j);
  346. }
  347.  
  348. #ifndef    NO_REGEX
  349. /*
  350.  * conv_pat:
  351.  *    Convert the pattern to an ignore-case equivalent.
  352.  */
  353. char *
  354. conv_pat(orig)
  355. register char    *orig;
  356. {
  357.     register char    *sp;
  358.     register int    cnt;
  359.     register char    *new;
  360.  
  361.     cnt = 1;    /* allow for '\0' */
  362.     for (sp = orig; *sp != '\0'; sp++)
  363.         if (isalpha(*sp))
  364.             cnt += 4;
  365.         else
  366.             cnt++;
  367.     if ((new = malloc(cnt)) == NULL) {
  368.         fprintf(stderr, "pattern too long for ignoring case\n");
  369.         exit(1);
  370.     }
  371.  
  372.     for (sp = new; *orig != '\0'; orig++) {
  373.         if (islower(*orig)) {
  374.             *sp++ = '[';
  375.             *sp++ = *orig;
  376.             *sp++ = toupper(*orig);
  377.             *sp++ = ']';
  378.         }
  379.         else if (isupper(*orig)) {
  380.             *sp++ = '[';
  381.             *sp++ = *orig;
  382.             *sp++ = tolower(*orig);
  383.             *sp++ = ']';
  384.         }
  385.         else
  386.             *sp++ = *orig;
  387.     }
  388.     *sp = '\0';
  389.     return new;
  390. }
  391.  
  392. /*
  393.  * find_matches:
  394.  *    Find all the fortunes which match the pattern we've been given.
  395.  */
  396. find_matches()
  397. {
  398.     register char        *sp;
  399.     register char        *fortune;
  400.     register int        found_one;
  401.     register int        i;
  402.     register int        start, end;
  403.  
  404.     if (Oflag || Aflag)
  405.         end = Tbl.str_numstr;
  406.     else
  407.         end = Tbl.str_delims[0];
  408.     if (Oflag) {
  409.         start = Tbl.str_delims[0];
  410.         (void) fseek(Inf, Tbl.str_dpos[0], 0);
  411.     }
  412.     else {
  413.         start = 0;
  414.         (void) fseek(Inf,
  415.                  (off_t) (sizeof Tbl +
  416.                       sizeof Seekpts[0] * (Tbl.str_numstr + 1)),
  417.                  0);
  418.     }
  419.  
  420.     if ((fortune = malloc(Tbl.str_longlen + 1)) == NULL) {
  421.         perror("malloc");
  422.         exit(1);
  423.     }
  424.     found_one = FALSE;
  425.     for (i = start; i < end; i++) {
  426.         sp = fortune;
  427.         while ((*sp++ = getc(Inf)) != '\0')
  428.             continue;
  429.         if (RE_EXEC(fortune)) {
  430.             if (found_one)
  431.                 printf("%%%%\n");
  432.             (void) fwrite(fortune, 1, sp - fortune, stdout);
  433.             found_one = TRUE;
  434.         }
  435.     }
  436.     if (found_one)
  437.         exit(0);
  438.     else
  439.         exit(1);
  440.     /* NOTREACHED */
  441. }
  442. # endif    NO_REGEX
  443.