|
|
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.