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