12490Sjkh/*- 22490Sjkh * Copyright (c) 1986, 1993 32490Sjkh * The Regents of the University of California. All rights reserved. 42490Sjkh * 52490Sjkh * This code is derived from software contributed to Berkeley by 62490Sjkh * Ken Arnold. 72490Sjkh * 82490Sjkh * Redistribution and use in source and binary forms, with or without 92490Sjkh * modification, are permitted provided that the following conditions 102490Sjkh * are met: 112490Sjkh * 1. Redistributions of source code must retain the above copyright 122490Sjkh * notice, this list of conditions and the following disclaimer. 132490Sjkh * 2. Redistributions in binary form must reproduce the above copyright 142490Sjkh * notice, this list of conditions and the following disclaimer in the 152490Sjkh * documentation and/or other materials provided with the distribution. 16203926Suqs * 3. Neither the name of the University nor the names of its contributors 172490Sjkh * may be used to endorse or promote products derived from this software 182490Sjkh * without specific prior written permission. 192490Sjkh * 202490Sjkh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 212490Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 222490Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 232490Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 242490Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 252490Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 262490Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 272490Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 282490Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 292490Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 302490Sjkh * SUCH DAMAGE. 312490Sjkh */ 322490Sjkh 33114725Sobrien#if 0 342490Sjkh#ifndef lint 3515944Sachestatic const char copyright[] = 362490Sjkh"@(#) Copyright (c) 1986, 1993\n\ 372490Sjkh The Regents of the University of California. All rights reserved.\n"; 382490Sjkh#endif /* not lint */ 392490Sjkh 402490Sjkh#ifndef lint 4115944Sachestatic const char sccsid[] = "@(#)fortune.c 8.1 (Berkeley) 5/31/93"; 42114725Sobrien#endif /* not lint */ 4353920Sbillf#endif 44114725Sobrien#include <sys/cdefs.h> 45114725Sobrien__FBSDID("$FreeBSD$"); 462490Sjkh 47203926Suqs#include <sys/stat.h> 48203926Suqs#include <sys/endian.h> 492490Sjkh 50203926Suqs#include <assert.h> 51203926Suqs#include <ctype.h> 52203926Suqs#include <dirent.h> 53203926Suqs#include <fcntl.h> 54203926Suqs#include <locale.h> 55203926Suqs#include <regex.h> 56242576Seadler#include <stdbool.h> 57203926Suqs#include <stdio.h> 58203926Suqs#include <stdlib.h> 59203926Suqs#include <string.h> 60203926Suqs#include <time.h> 61203926Suqs#include <unistd.h> 622490Sjkh 63203926Suqs#include "strfile.h" 64203926Suqs#include "pathnames.h" 652490Sjkh 66242576Seadler#define TRUE true 67242576Seadler#define FALSE false 682490Sjkh 69203926Suqs#define MINW 6 /* minimum wait if desired */ 70203926Suqs#define CPERS 20 /* # of chars for each sec */ 71203926Suqs#define SLEN 160 /* # of chars in short fortune */ 722490Sjkh 73203926Suqs#define POS_UNKNOWN ((uint32_t) -1) /* pos for file unknown */ 74203926Suqs#define NO_PROB (-1) /* no prob specified for file */ 752490Sjkh 76203926Suqs#ifdef DEBUG 77203926Suqs#define DPRINTF(l,x) { if (Debug >= l) fprintf x; } 78203926Suqs#undef NDEBUG 79203926Suqs#else 80203926Suqs#define DPRINTF(l,x) 81203926Suqs#define NDEBUG 1 82203926Suqs#endif 83203926Suqs 842490Sjkhtypedef struct fd { 852490Sjkh int percent; 862490Sjkh int fd, datfd; 87142022Sru uint32_t pos; 882490Sjkh FILE *inf; 89203922Suqs const char *name; 90203922Suqs const char *path; 912490Sjkh char *datfile, *posfile; 922490Sjkh bool read_tbl; 932490Sjkh bool was_pos_file; 942490Sjkh STRFILE tbl; 952490Sjkh int num_children; 962490Sjkh struct fd *child, *parent; 972490Sjkh struct fd *next, *prev; 982490Sjkh} FILEDESC; 992490Sjkh 100227101Sedstatic bool Found_one; /* did we find a match? */ 101227101Sedstatic bool Find_files = FALSE; /* just find a list of proper fortune files */ 102227101Sedstatic bool Fortunes_only = FALSE; /* check only "fortunes" files */ 103227101Sedstatic bool Wait = FALSE; /* wait desired after fortune */ 104227101Sedstatic bool Short_only = FALSE; /* short fortune desired */ 105227101Sedstatic bool Long_only = FALSE; /* long fortune desired */ 106227101Sedstatic bool Offend = FALSE; /* offensive fortunes only */ 107227101Sedstatic bool All_forts = FALSE; /* any fortune allowed */ 108227101Sedstatic bool Equal_probs = FALSE; /* scatter un-allocted prob equally */ 109227101Sedstatic bool Match = FALSE; /* dump fortunes matching a pattern */ 110242577Seadlerstatic bool WriteToDisk = false; /* use files on disk to save state */ 1112490Sjkh#ifdef DEBUG 112243036Sdimstatic int Debug = 0; /* print debug messages */ 1132490Sjkh#endif 1142490Sjkh 115227101Sedstatic char *Fortbuf = NULL; /* fortune buffer for -m */ 1162490Sjkh 117227101Sedstatic int Fort_len = 0; 1182490Sjkh 119227101Sedstatic off_t Seekpts[2]; /* seek pointers to fortunes */ 1202490Sjkh 121227101Sedstatic FILEDESC *File_list = NULL, /* Head of file list */ 1222490Sjkh *File_tail = NULL; /* Tail of file list */ 123227101Sedstatic FILEDESC *Fortfile; /* Fortune file to use */ 1242490Sjkh 125227101Sedstatic STRFILE Noprob_tbl; /* sum of data for all no prob files */ 1262490Sjkh 127227101Sedstatic const char *Fortune_path; 128227101Sedstatic char **Fortune_path_arr; 129173396Sedwin 130227101Sedstatic int add_dir(FILEDESC *); 131227101Sedstatic int add_file(int, const char *, const char *, FILEDESC **, 132227101Sed FILEDESC **, FILEDESC *); 133227101Sedstatic void all_forts(FILEDESC *, char *); 134227101Sedstatic char *copy(const char *, u_int); 135227101Sedstatic void display(FILEDESC *); 136227101Sedstatic void do_free(void *); 137227101Sedstatic void *do_malloc(u_int); 138227101Sedstatic int form_file_list(char **, int); 139227101Sedstatic int fortlen(void); 140227101Sedstatic void get_fort(void); 141227101Sedstatic void get_pos(FILEDESC *); 142227101Sedstatic void get_tbl(FILEDESC *); 143227101Sedstatic void getargs(int, char *[]); 144227101Sedstatic void getpath(void); 145227101Sedstatic void init_prob(void); 146227101Sedstatic int is_dir(const char *); 147227101Sedstatic int is_fortfile(const char *, char **, char **, int); 148227101Sedstatic int is_off_name(const char *); 149227101Sedstatic int max(int, int); 150227101Sedstatic FILEDESC *new_fp(void); 151227101Sedstatic char *off_name(const char *); 152227101Sedstatic void open_dat(FILEDESC *); 153227101Sedstatic void open_fp(FILEDESC *); 154227101Sedstatic FILEDESC *pick_child(FILEDESC *); 155227101Sedstatic void print_file_list(void); 156227101Sedstatic void print_list(FILEDESC *, int); 157227101Sedstatic void sum_noprobs(FILEDESC *); 158227101Sedstatic void sum_tbl(STRFILE *, STRFILE *); 159227101Sedstatic void usage(void); 160227101Sedstatic void zero_tbl(STRFILE *); 1612490Sjkh 162227101Sedstatic char *conv_pat(char *); 163227101Sedstatic int find_matches(void); 164227101Sedstatic void matches_in_list(FILEDESC *); 165227101Sedstatic int maxlen_in_list(FILEDESC *); 1662490Sjkh 167130851Sphkstatic regex_t Re_pat; 1682490Sjkh 1692490Sjkhint 170203922Suqsmain(int argc, char *argv[]) 1712490Sjkh{ 1722490Sjkh int fd; 1732490Sjkh 174242577Seadler if (getenv("FORTUNE_SAVESTATE") != NULL) 175242577Seadler WriteToDisk = true; 176242577Seadler 17717537Sache (void) setlocale(LC_ALL, ""); 17815944Sache 179173396Sedwin getpath(); 180203922Suqs getargs(argc, argv); 1812490Sjkh 1822490Sjkh if (Match) 1832490Sjkh exit(find_matches() != 0); 1842490Sjkh 1852490Sjkh init_prob(); 1862490Sjkh do { 1872490Sjkh get_fort(); 1882490Sjkh } while ((Short_only && fortlen() > SLEN) || 1892490Sjkh (Long_only && fortlen() <= SLEN)); 1902490Sjkh 1912490Sjkh display(Fortfile); 1922490Sjkh 193242577Seadler if (WriteToDisk) { 194242577Seadler if ((fd = creat(Fortfile->posfile, 0666)) < 0) { 195242577Seadler perror(Fortfile->posfile); 196242577Seadler exit(1); 197242577Seadler } 198242577Seadler /* 199242577Seadler * if we can, we exclusive lock, but since it isn't very 200242577Seadler * important, we just punt if we don't have easy locking 201242577Seadler * available. 202242577Seadler */ 203242577Seadler flock(fd, LOCK_EX); 204242577Seadler write(fd, (char *) &Fortfile->pos, sizeof Fortfile->pos); 205242577Seadler if (!Fortfile->was_pos_file) 206242577Seadler chmod(Fortfile->path, 0666); 207242577Seadler flock(fd, LOCK_UN); 2082490Sjkh } 2092490Sjkh if (Wait) { 2102490Sjkh if (Fort_len == 0) 2112490Sjkh (void) fortlen(); 2122490Sjkh sleep((unsigned int) max(Fort_len / CPERS, MINW)); 2132490Sjkh } 214203926Suqs 215204178Suqs exit(0); 2162490Sjkh} 2172490Sjkh 218227101Sedstatic void 219203922Suqsdisplay(FILEDESC *fp) 2202490Sjkh{ 22153210Sbillf char *p; 22253210Sbillf unsigned char ch; 2232490Sjkh char line[BUFSIZ]; 2242490Sjkh 2252490Sjkh open_fp(fp); 226203926Suqs fseeko(fp->inf, Seekpts[0], SEEK_SET); 2272490Sjkh for (Fort_len = 0; fgets(line, sizeof line, fp->inf) != NULL && 2282490Sjkh !STR_ENDSTRING(line, fp->tbl); Fort_len++) { 2292490Sjkh if (fp->tbl.str_flags & STR_ROTATED) 23015944Sache for (p = line; (ch = *p) != '\0'; ++p) { 23115944Sache if (isascii(ch)) { 23215944Sache if (isupper(ch)) 23315944Sache *p = 'A' + (ch - 'A' + 13) % 26; 23415944Sache else if (islower(ch)) 23515944Sache *p = 'a' + (ch - 'a' + 13) % 26; 23615944Sache } 23715944Sache } 23851863Sdcs if (fp->tbl.str_flags & STR_COMMENTS 23951863Sdcs && line[0] == fp->tbl.str_delim 24051863Sdcs && line[1] == fp->tbl.str_delim) 24151863Sdcs continue; 2422490Sjkh fputs(line, stdout); 2432490Sjkh } 2442490Sjkh (void) fflush(stdout); 2452490Sjkh} 2462490Sjkh 2472490Sjkh/* 2482490Sjkh * fortlen: 2492490Sjkh * Return the length of the fortune. 2502490Sjkh */ 251227101Sedstatic int 252203922Suqsfortlen(void) 2532490Sjkh{ 25453210Sbillf int nchar; 255173401Sedwin char line[BUFSIZ]; 2562490Sjkh 2572490Sjkh if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED))) 258123905Sceri nchar = (int)(Seekpts[1] - Seekpts[0]); 2592490Sjkh else { 2602490Sjkh open_fp(Fortfile); 261203926Suqs fseeko(Fortfile->inf, Seekpts[0], SEEK_SET); 2622490Sjkh nchar = 0; 2632490Sjkh while (fgets(line, sizeof line, Fortfile->inf) != NULL && 2642490Sjkh !STR_ENDSTRING(line, Fortfile->tbl)) 2652490Sjkh nchar += strlen(line); 2662490Sjkh } 2672490Sjkh Fort_len = nchar; 268203926Suqs 269203926Suqs return (nchar); 2702490Sjkh} 2712490Sjkh 2722490Sjkh/* 2732490Sjkh * This routine evaluates the arguments on the command line 2742490Sjkh */ 275227101Sedstatic void 276203922Suqsgetargs(int argc, char *argv[]) 2772490Sjkh{ 27853210Sbillf int ignore_case; 27953210Sbillf char *pat; 2802490Sjkh int ch; 2812490Sjkh 2822490Sjkh ignore_case = FALSE; 2832490Sjkh pat = NULL; 2842490Sjkh 285203926Suqs#ifdef DEBUG 28647440Simp while ((ch = getopt(argc, argv, "aDefilm:osw")) != -1) 2872490Sjkh#else 28847440Simp while ((ch = getopt(argc, argv, "aefilm:osw")) != -1) 2892490Sjkh#endif /* DEBUG */ 2902490Sjkh switch(ch) { 2912490Sjkh case 'a': /* any fortune */ 2922490Sjkh All_forts++; 2932490Sjkh break; 294203926Suqs#ifdef DEBUG 2952490Sjkh case 'D': 2962490Sjkh Debug++; 2972490Sjkh break; 298203926Suqs#endif /* DEBUG */ 2992490Sjkh case 'e': 3002490Sjkh Equal_probs++; /* scatter un-allocted prob equally */ 3012490Sjkh break; 3022490Sjkh case 'f': /* find fortune files */ 3032490Sjkh Find_files++; 3042490Sjkh break; 3052490Sjkh case 'l': /* long ones only */ 3062490Sjkh Long_only++; 3072490Sjkh Short_only = FALSE; 3082490Sjkh break; 3092490Sjkh case 'o': /* offensive ones only */ 3102490Sjkh Offend++; 3112490Sjkh break; 3122490Sjkh case 's': /* short ones only */ 3132490Sjkh Short_only++; 3142490Sjkh Long_only = FALSE; 3152490Sjkh break; 3162490Sjkh case 'w': /* give time to read */ 3172490Sjkh Wait++; 3182490Sjkh break; 3192490Sjkh case 'm': /* dump out the fortunes */ 3202490Sjkh Match++; 3212490Sjkh pat = optarg; 3222490Sjkh break; 3232490Sjkh case 'i': /* case-insensitive match */ 3242490Sjkh ignore_case++; 3252490Sjkh break; 3262490Sjkh case '?': 3272490Sjkh default: 3282490Sjkh usage(); 3292490Sjkh } 3302490Sjkh argc -= optind; 3312490Sjkh argv += optind; 3322490Sjkh 3332490Sjkh if (!form_file_list(argv, argc)) 3342490Sjkh exit(1); /* errors printed through form_file_list() */ 3352490Sjkh if (Find_files) { 3362490Sjkh print_file_list(); 3372490Sjkh exit(0); 3382490Sjkh } 33915957Sache#ifdef DEBUG 34015957Sache else if (Debug >= 1) 34115957Sache print_file_list(); 34215957Sache#endif /* DEBUG */ 3432490Sjkh 3442490Sjkh if (pat != NULL) { 345130851Sphk int error; 346130851Sphk 3472490Sjkh if (ignore_case) 3482490Sjkh pat = conv_pat(pat); 349130851Sphk error = regcomp(&Re_pat, pat, REG_BASIC); 350130851Sphk if (error) { 351130851Sphk fprintf(stderr, "regcomp(%s) fails\n", pat); 352203926Suqs exit(1); 3532490Sjkh } 3542490Sjkh } 3552490Sjkh} 3562490Sjkh 3572490Sjkh/* 3582490Sjkh * form_file_list: 3592490Sjkh * Form the file list from the file specifications. 3602490Sjkh */ 361227101Sedstatic int 362203922Suqsform_file_list(char **files, int file_cnt) 3632490Sjkh{ 36453210Sbillf int i, percent; 36553210Sbillf char *sp; 366173396Sedwin char **pstr; 3672490Sjkh 36849040Sbillf if (file_cnt == 0) { 36915957Sache if (Find_files) { 37015957Sache Fortunes_only = TRUE; 371173396Sedwin pstr = Fortune_path_arr; 372173396Sedwin i = 0; 373173396Sedwin while (*pstr) { 374203926Suqs i += add_file(NO_PROB, *pstr++, NULL, 375173396Sedwin &File_list, &File_tail, NULL); 376173396Sedwin } 37715957Sache Fortunes_only = FALSE; 378173396Sedwin if (!i) { 379173401Sedwin fprintf(stderr, "No fortunes found in %s.\n", 380173401Sedwin Fortune_path); 381173396Sedwin } 382203926Suqs return (i != 0); 383173396Sedwin } else { 384173396Sedwin pstr = Fortune_path_arr; 385173396Sedwin i = 0; 386173396Sedwin while (*pstr) { 387173396Sedwin i += add_file(NO_PROB, "fortunes", *pstr++, 388173396Sedwin &File_list, &File_tail, NULL); 389173396Sedwin } 390173396Sedwin if (!i) { 391173401Sedwin fprintf(stderr, "No fortunes found in %s.\n", 392173401Sedwin Fortune_path); 393173396Sedwin } 394203926Suqs return (i != 0); 395173396Sedwin } 39649040Sbillf } 3972490Sjkh for (i = 0; i < file_cnt; i++) { 3982490Sjkh percent = NO_PROB; 39915944Sache if (!isdigit((unsigned char)files[i][0])) 4002490Sjkh sp = files[i]; 4012490Sjkh else { 4022490Sjkh percent = 0; 40315944Sache for (sp = files[i]; isdigit((unsigned char)*sp); sp++) 4042490Sjkh percent = percent * 10 + *sp - '0'; 4052490Sjkh if (percent > 100) { 4062490Sjkh fprintf(stderr, "percentages must be <= 100\n"); 407203926Suqs return (FALSE); 4082490Sjkh } 4092490Sjkh if (*sp == '.') { 4102490Sjkh fprintf(stderr, "percentages must be integers\n"); 411203926Suqs return (FALSE); 4122490Sjkh } 4132490Sjkh /* 4142490Sjkh * If the number isn't followed by a '%', then 4152490Sjkh * it was not a percentage, just the first part 4162490Sjkh * of a file name which starts with digits. 4172490Sjkh */ 4182490Sjkh if (*sp != '%') { 4192490Sjkh percent = NO_PROB; 4202490Sjkh sp = files[i]; 4212490Sjkh } 4222490Sjkh else if (*++sp == '\0') { 4232490Sjkh if (++i >= file_cnt) { 4242490Sjkh fprintf(stderr, "percentages must precede files\n"); 425203926Suqs return (FALSE); 4262490Sjkh } 4272490Sjkh sp = files[i]; 4282490Sjkh } 4292490Sjkh } 430173396Sedwin if (strcmp(sp, "all") == 0) { 431173396Sedwin pstr = Fortune_path_arr; 432173396Sedwin i = 0; 433173396Sedwin while (*pstr) { 434203926Suqs i += add_file(NO_PROB, *pstr++, NULL, 435173396Sedwin &File_list, &File_tail, NULL); 436173396Sedwin } 437173396Sedwin if (!i) { 438173401Sedwin fprintf(stderr, "No fortunes found in %s.\n", 439173401Sedwin Fortune_path); 440203926Suqs return (FALSE); 441173396Sedwin } 442203926Suqs } else if (!add_file(percent, sp, NULL, &File_list, 443173396Sedwin &File_tail, NULL)) { 444203926Suqs return (FALSE); 445173396Sedwin } 4462490Sjkh } 447203926Suqs 448203926Suqs return (TRUE); 4492490Sjkh} 4502490Sjkh 4512490Sjkh/* 4522490Sjkh * add_file: 4532490Sjkh * Add a file to the file list. 4542490Sjkh */ 455227101Sedstatic int 456203922Suqsadd_file(int percent, const char *file, const char *dir, FILEDESC **head, 457203922Suqs FILEDESC **tail, FILEDESC *parent) 4582490Sjkh{ 45953210Sbillf FILEDESC *fp; 46053210Sbillf int fd; 461203922Suqs const char *path; 462203922Suqs char *tpath, *offensive; 46353210Sbillf bool was_malloc; 46453210Sbillf bool isdir; 4652490Sjkh 4662490Sjkh if (dir == NULL) { 4672490Sjkh path = file; 468203922Suqs tpath = NULL; 4692490Sjkh was_malloc = FALSE; 4702490Sjkh } 4712490Sjkh else { 472203922Suqs tpath = do_malloc((unsigned int)(strlen(dir) + strlen(file) + 2)); 473203922Suqs strcat(strcat(strcpy(tpath, dir), "/"), file); 474203922Suqs path = tpath; 4752490Sjkh was_malloc = TRUE; 4762490Sjkh } 4772490Sjkh if ((isdir = is_dir(path)) && parent != NULL) { 4782490Sjkh if (was_malloc) 479203922Suqs free(tpath); 480203926Suqs return (FALSE); /* don't recurse */ 4812490Sjkh } 4822490Sjkh offensive = NULL; 4832490Sjkh if (!isdir && parent == NULL && (All_forts || Offend) && 4842490Sjkh !is_off_name(path)) { 4852490Sjkh offensive = off_name(path); 4862490Sjkh if (Offend) { 4872490Sjkh if (was_malloc) 488203922Suqs free(tpath); 4892490Sjkh path = offensive; 49015957Sache offensive = NULL; 49115957Sache was_malloc = TRUE; 49215957Sache DPRINTF(1, (stderr, "\ttrying \"%s\"\n", path)); 4932490Sjkh file = off_name(file); 4942490Sjkh } 4952490Sjkh } 4962490Sjkh 4972490Sjkh DPRINTF(1, (stderr, "adding file \"%s\"\n", path)); 4982490Sjkhover: 499203926Suqs if ((fd = open(path, O_RDONLY)) < 0) { 5002490Sjkh /* 5012490Sjkh * This is a sneak. If the user said -a, and if the 5022490Sjkh * file we're given isn't a file, we check to see if 5032490Sjkh * there is a -o version. If there is, we treat it as 5042490Sjkh * if *that* were the file given. We only do this for 5052490Sjkh * individual files -- if we're scanning a directory, 5062490Sjkh * we'll pick up the -o file anyway. 5072490Sjkh */ 5082490Sjkh if (All_forts && offensive != NULL) { 5092490Sjkh if (was_malloc) 510203922Suqs free(tpath); 51115957Sache path = offensive; 5122490Sjkh offensive = NULL; 5132490Sjkh was_malloc = TRUE; 5142490Sjkh DPRINTF(1, (stderr, "\ttrying \"%s\"\n", path)); 5152490Sjkh file = off_name(file); 5162490Sjkh goto over; 5172490Sjkh } 518173396Sedwin if (dir == NULL && file[0] != '/') { 519173396Sedwin int i = 0; 520173396Sedwin char **pstr = Fortune_path_arr; 521173396Sedwin 522173396Sedwin while (*pstr) { 523203926Suqs i += add_file(percent, file, *pstr++, 524173396Sedwin head, tail, parent); 525173396Sedwin } 526173396Sedwin if (!i) { 527173401Sedwin fprintf(stderr, "No '%s' found in %s.\n", 528173401Sedwin file, Fortune_path); 529173396Sedwin } 530203926Suqs return (i != 0); 531173396Sedwin } 532173396Sedwin /* 5332490Sjkh if (parent == NULL) 5342490Sjkh perror(path); 535173396Sedwin */ 5362490Sjkh if (was_malloc) 537203922Suqs free(tpath); 538203926Suqs return (FALSE); 5392490Sjkh } 5402490Sjkh 5412490Sjkh DPRINTF(2, (stderr, "path = \"%s\"\n", path)); 5422490Sjkh 5432490Sjkh fp = new_fp(); 5442490Sjkh fp->fd = fd; 5452490Sjkh fp->percent = percent; 5462490Sjkh fp->name = file; 5472490Sjkh fp->path = path; 5482490Sjkh fp->parent = parent; 5492490Sjkh 5502490Sjkh if ((isdir && !add_dir(fp)) || 5512490Sjkh (!isdir && 5522490Sjkh !is_fortfile(path, &fp->datfile, &fp->posfile, (parent != NULL)))) 5532490Sjkh { 5542490Sjkh if (parent == NULL) 5552490Sjkh fprintf(stderr, 5562490Sjkh "fortune:%s not a fortune file or directory\n", 5572490Sjkh path); 5582490Sjkh if (was_malloc) 559203922Suqs free(tpath); 5602490Sjkh do_free(fp->datfile); 5612490Sjkh do_free(fp->posfile); 562203926Suqs free(fp); 5632490Sjkh do_free(offensive); 564203926Suqs return (FALSE); 5652490Sjkh } 5662490Sjkh /* 5672490Sjkh * If the user said -a, we need to make this node a pointer to 5682490Sjkh * both files, if there are two. We don't need to do this if 5692490Sjkh * we are scanning a directory, since the scan will pick up the 5702490Sjkh * -o file anyway. 5712490Sjkh */ 5722490Sjkh if (All_forts && parent == NULL && !is_off_name(path)) 5732490Sjkh all_forts(fp, offensive); 5742490Sjkh if (*head == NULL) 5752490Sjkh *head = *tail = fp; 5762490Sjkh else if (fp->percent == NO_PROB) { 5772490Sjkh (*tail)->next = fp; 5782490Sjkh fp->prev = *tail; 5792490Sjkh *tail = fp; 5802490Sjkh } 5812490Sjkh else { 5822490Sjkh (*head)->prev = fp; 5832490Sjkh fp->next = *head; 5842490Sjkh *head = fp; 5852490Sjkh } 586242577Seadler if (WriteToDisk) 587242577Seadler fp->was_pos_file = (access(fp->posfile, W_OK) >= 0); 5882490Sjkh 589203926Suqs return (TRUE); 5902490Sjkh} 5912490Sjkh 5922490Sjkh/* 5932490Sjkh * new_fp: 5942490Sjkh * Return a pointer to an initialized new FILEDESC. 5952490Sjkh */ 596227101Sedstatic FILEDESC * 597203922Suqsnew_fp(void) 5982490Sjkh{ 59953210Sbillf FILEDESC *fp; 6002490Sjkh 601203926Suqs fp = do_malloc(sizeof(*fp)); 6022490Sjkh fp->datfd = -1; 6032490Sjkh fp->pos = POS_UNKNOWN; 6042490Sjkh fp->inf = NULL; 6052490Sjkh fp->fd = -1; 6062490Sjkh fp->percent = NO_PROB; 6072490Sjkh fp->read_tbl = FALSE; 6082490Sjkh fp->next = NULL; 6092490Sjkh fp->prev = NULL; 6102490Sjkh fp->child = NULL; 6112490Sjkh fp->parent = NULL; 6122490Sjkh fp->datfile = NULL; 6132490Sjkh fp->posfile = NULL; 614203926Suqs 615203926Suqs return (fp); 6162490Sjkh} 6172490Sjkh 6182490Sjkh/* 6192490Sjkh * off_name: 6202490Sjkh * Return a pointer to the offensive version of a file of this name. 6212490Sjkh */ 622227101Sedstatic char * 623203922Suqsoff_name(const char *file) 6242490Sjkh{ 6252490Sjkh char *new; 6262490Sjkh 6272490Sjkh new = copy(file, (unsigned int) (strlen(file) + 2)); 628203926Suqs 629203926Suqs return (strcat(new, "-o")); 6302490Sjkh} 6312490Sjkh 6322490Sjkh/* 6332490Sjkh * is_off_name: 6342490Sjkh * Is the file an offensive-style name? 6352490Sjkh */ 636227101Sedstatic int 637203922Suqsis_off_name(const char *file) 6382490Sjkh{ 6392490Sjkh int len; 6402490Sjkh 6412490Sjkh len = strlen(file); 642203926Suqs 6432490Sjkh return (len >= 3 && file[len - 2] == '-' && file[len - 1] == 'o'); 6442490Sjkh} 6452490Sjkh 6462490Sjkh/* 6472490Sjkh * all_forts: 6482490Sjkh * Modify a FILEDESC element to be the parent of two children if 6492490Sjkh * there are two children to be a parent of. 6502490Sjkh */ 651227101Sedstatic void 652203922Suqsall_forts(FILEDESC *fp, char *offensive) 6532490Sjkh{ 65453210Sbillf char *sp; 65553210Sbillf FILEDESC *scene, *obscene; 65653210Sbillf int fd; 657203926Suqs char *datfile, *posfile; 6582490Sjkh 6592490Sjkh if (fp->child != NULL) /* this is a directory, not a file */ 6602490Sjkh return; 6612490Sjkh if (!is_fortfile(offensive, &datfile, &posfile, FALSE)) 6622490Sjkh return; 663203926Suqs if ((fd = open(offensive, O_RDONLY)) < 0) 6642490Sjkh return; 6652490Sjkh DPRINTF(1, (stderr, "adding \"%s\" because of -a\n", offensive)); 6662490Sjkh scene = new_fp(); 6672490Sjkh obscene = new_fp(); 6682490Sjkh *scene = *fp; 6692490Sjkh 6702490Sjkh fp->num_children = 2; 6712490Sjkh fp->child = scene; 6722490Sjkh scene->next = obscene; 6732490Sjkh obscene->next = NULL; 6742490Sjkh scene->child = obscene->child = NULL; 6752490Sjkh scene->parent = obscene->parent = fp; 6762490Sjkh 6772490Sjkh fp->fd = -1; 6782490Sjkh scene->percent = obscene->percent = NO_PROB; 6792490Sjkh 6802490Sjkh obscene->fd = fd; 6812490Sjkh obscene->inf = NULL; 6822490Sjkh obscene->path = offensive; 683229403Sed if ((sp = strrchr(offensive, '/')) == NULL) 6842490Sjkh obscene->name = offensive; 6852490Sjkh else 6862490Sjkh obscene->name = ++sp; 6872490Sjkh obscene->datfile = datfile; 6882490Sjkh obscene->posfile = posfile; 689242577Seadler obscene->read_tbl = false; 690242577Seadler if (WriteToDisk) 691242577Seadler obscene->was_pos_file = (access(obscene->posfile, W_OK) >= 0); 6922490Sjkh} 6932490Sjkh 6942490Sjkh/* 6952490Sjkh * add_dir: 6962490Sjkh * Add the contents of an entire directory. 6972490Sjkh */ 698227101Sedstatic int 699203922Suqsadd_dir(FILEDESC *fp) 7002490Sjkh{ 70153210Sbillf DIR *dir; 70253210Sbillf struct dirent *dirent; 703203926Suqs FILEDESC *tailp; 704203926Suqs char *name; 7052490Sjkh 7062490Sjkh (void) close(fp->fd); 7072490Sjkh fp->fd = -1; 7082490Sjkh if ((dir = opendir(fp->path)) == NULL) { 7092490Sjkh perror(fp->path); 710203926Suqs return (FALSE); 7112490Sjkh } 7122490Sjkh tailp = NULL; 7132490Sjkh DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path)); 7142490Sjkh fp->num_children = 0; 7152490Sjkh while ((dirent = readdir(dir)) != NULL) { 7162490Sjkh if (dirent->d_namlen == 0) 7172490Sjkh continue; 7182490Sjkh name = copy(dirent->d_name, dirent->d_namlen); 7192490Sjkh if (add_file(NO_PROB, name, fp->path, &fp->child, &tailp, fp)) 7202490Sjkh fp->num_children++; 7212490Sjkh else 7222490Sjkh free(name); 7232490Sjkh } 7242490Sjkh if (fp->num_children == 0) { 7252490Sjkh (void) fprintf(stderr, 7262490Sjkh "fortune: %s: No fortune files in directory.\n", fp->path); 727203926Suqs return (FALSE); 7282490Sjkh } 729203926Suqs 730203926Suqs return (TRUE); 7312490Sjkh} 7322490Sjkh 7332490Sjkh/* 7342490Sjkh * is_dir: 7352490Sjkh * Return TRUE if the file is a directory, FALSE otherwise. 7362490Sjkh */ 737227101Sedstatic int 738203922Suqsis_dir(const char *file) 7392490Sjkh{ 740203926Suqs struct stat sbuf; 7412490Sjkh 7422490Sjkh if (stat(file, &sbuf) < 0) 743203926Suqs return (FALSE); 744203926Suqs 7452490Sjkh return (sbuf.st_mode & S_IFDIR); 7462490Sjkh} 7472490Sjkh 7482490Sjkh/* 7492490Sjkh * is_fortfile: 7502490Sjkh * Return TRUE if the file is a fortune database file. We try and 7512490Sjkh * exclude files without reading them if possible to avoid 7522490Sjkh * overhead. Files which start with ".", or which have "illegal" 7532490Sjkh * suffixes, as contained in suflist[], are ruled out. 7542490Sjkh */ 7552490Sjkh/* ARGSUSED */ 756227101Sedstatic int 757203922Suqsis_fortfile(const char *file, char **datp, char **posp, int check_for_offend) 7582490Sjkh{ 75953210Sbillf int i; 760203922Suqs const char *sp; 76153210Sbillf char *datfile; 762203922Suqs static const char *suflist[] = { 763203922Suqs /* list of "illegal" suffixes" */ 764203922Suqs "dat", "pos", "c", "h", "p", "i", "f", 765203922Suqs "pas", "ftn", "ins.c", "ins,pas", 766203922Suqs "ins.ftn", "sml", 767203922Suqs NULL 768203922Suqs }; 7692490Sjkh 7702490Sjkh DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file)); 7712490Sjkh 7722490Sjkh /* 7732490Sjkh * Preclude any -o files for offendable people, and any non -o 7742490Sjkh * files for completely offensive people. 7752490Sjkh */ 7762490Sjkh if (check_for_offend && !All_forts) { 7772490Sjkh i = strlen(file); 77815957Sache if (Offend ^ (file[i - 2] == '-' && file[i - 1] == 'o')) { 77915957Sache DPRINTF(2, (stderr, "FALSE (offending file)\n")); 780203926Suqs return (FALSE); 78115957Sache } 7822490Sjkh } 7832490Sjkh 784229403Sed if ((sp = strrchr(file, '/')) == NULL) 7852490Sjkh sp = file; 7862490Sjkh else 7872490Sjkh sp++; 7882490Sjkh if (*sp == '.') { 7892490Sjkh DPRINTF(2, (stderr, "FALSE (file starts with '.')\n")); 790203926Suqs return (FALSE); 7912490Sjkh } 79215957Sache if (Fortunes_only && strncmp(sp, "fortunes", 8) != 0) { 79315957Sache DPRINTF(2, (stderr, "FALSE (check fortunes only)\n")); 794203926Suqs return (FALSE); 79515957Sache } 796229403Sed if ((sp = strrchr(sp, '.')) != NULL) { 7972490Sjkh sp++; 7982490Sjkh for (i = 0; suflist[i] != NULL; i++) 7992490Sjkh if (strcmp(sp, suflist[i]) == 0) { 8002490Sjkh DPRINTF(2, (stderr, "FALSE (file has suffix \".%s\")\n", sp)); 801203926Suqs return (FALSE); 8022490Sjkh } 8032490Sjkh } 8042490Sjkh 8052490Sjkh datfile = copy(file, (unsigned int) (strlen(file) + 4)); /* +4 for ".dat" */ 8062490Sjkh strcat(datfile, ".dat"); 8072490Sjkh if (access(datfile, R_OK) < 0) { 80844599Sdcs DPRINTF(2, (stderr, "FALSE (no readable \".dat\" file)\n")); 80944599Sdcs#ifdef DEBUG 81044599Sdcs if (Debug < 2) 81144599Sdcs DPRINTF(0, (stderr, "Warning: file \"%s\" unreadable\n", datfile)); 81244599Sdcs#endif 8132490Sjkh free(datfile); 814203926Suqs return (FALSE); 8152490Sjkh } 8162490Sjkh if (datp != NULL) 8172490Sjkh *datp = datfile; 8182490Sjkh else 8192490Sjkh free(datfile); 82015944Sache if (posp != NULL) { 821242577Seadler if (WriteToDisk) { 822242577Seadler *posp = copy(file, (unsigned int) (strlen(file) + 4)); /* +4 for ".dat" */ 823242577Seadler strcat(*posp, ".pos"); 824242577Seadler } 825242577Seadler else { 826242577Seadler *posp = NULL; 827242577Seadler } 8282490Sjkh } 8292490Sjkh DPRINTF(2, (stderr, "TRUE\n")); 830203926Suqs 831203926Suqs return (TRUE); 8322490Sjkh} 8332490Sjkh 8342490Sjkh/* 8352490Sjkh * copy: 8362490Sjkh * Return a malloc()'ed copy of the string 8372490Sjkh */ 838227101Sedstatic char * 839203922Suqscopy(const char *str, unsigned int len) 8402490Sjkh{ 841203926Suqs char *new, *sp; 8422490Sjkh 8432490Sjkh new = do_malloc(len + 1); 8442490Sjkh sp = new; 8452490Sjkh do { 8462490Sjkh *sp++ = *str; 8472490Sjkh } while (*str++); 848203926Suqs 849203926Suqs return (new); 8502490Sjkh} 8512490Sjkh 8522490Sjkh/* 8532490Sjkh * do_malloc: 8542490Sjkh * Do a malloc, checking for NULL return. 8552490Sjkh */ 856227101Sedstatic void * 857203922Suqsdo_malloc(unsigned int size) 8582490Sjkh{ 859203926Suqs void *new; 8602490Sjkh 8612490Sjkh if ((new = malloc(size)) == NULL) { 8622490Sjkh (void) fprintf(stderr, "fortune: out of memory.\n"); 8632490Sjkh exit(1); 8642490Sjkh } 865203926Suqs 866203926Suqs return (new); 8672490Sjkh} 8682490Sjkh 8692490Sjkh/* 8702490Sjkh * do_free: 8712490Sjkh * Free malloc'ed space, if any. 8722490Sjkh */ 873227101Sedstatic void 874203922Suqsdo_free(void *ptr) 8752490Sjkh{ 8762490Sjkh if (ptr != NULL) 8772490Sjkh free(ptr); 8782490Sjkh} 8792490Sjkh 8802490Sjkh/* 8812490Sjkh * init_prob: 8822490Sjkh * Initialize the fortune probabilities. 8832490Sjkh */ 884227101Sedstatic void 885203922Suqsinit_prob(void) 8862490Sjkh{ 88753210Sbillf FILEDESC *fp, *last = NULL; 88853210Sbillf int percent, num_noprob, frac; 8892490Sjkh 8902490Sjkh /* 8912490Sjkh * Distribute the residual probability (if any) across all 8922490Sjkh * files with unspecified probability (i.e., probability of 0) 8932490Sjkh * (if any). 8942490Sjkh */ 8952490Sjkh 8962490Sjkh percent = 0; 8972490Sjkh num_noprob = 0; 8982490Sjkh for (fp = File_tail; fp != NULL; fp = fp->prev) 8992490Sjkh if (fp->percent == NO_PROB) { 9002490Sjkh num_noprob++; 9012490Sjkh if (Equal_probs) 9022490Sjkh last = fp; 903203926Suqs } else 9042490Sjkh percent += fp->percent; 9052490Sjkh DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's", 9062490Sjkh percent, num_noprob)); 9072490Sjkh if (percent > 100) { 9082490Sjkh (void) fprintf(stderr, 90915957Sache "fortune: probabilities sum to %d%% > 100%%!\n", percent); 9102490Sjkh exit(1); 911203926Suqs } else if (percent < 100 && num_noprob == 0) { 9122490Sjkh (void) fprintf(stderr, 91315957Sache "fortune: no place to put residual probability (%d%% < 100%%)\n", 9142490Sjkh percent); 9152490Sjkh exit(1); 916203926Suqs } else if (percent == 100 && num_noprob != 0) { 9172490Sjkh (void) fprintf(stderr, 91815957Sache "fortune: no probability left to put in residual files (100%%)\n"); 9192490Sjkh exit(1); 9202490Sjkh } 9212490Sjkh percent = 100 - percent; 92249040Sbillf if (Equal_probs) { 9232490Sjkh if (num_noprob != 0) { 9242490Sjkh if (num_noprob > 1) { 9252490Sjkh frac = percent / num_noprob; 9262490Sjkh DPRINTF(1, (stderr, ", frac = %d%%", frac)); 927173396Sedwin for (fp = File_tail; fp != last; fp = fp->prev) 9282490Sjkh if (fp->percent == NO_PROB) { 9292490Sjkh fp->percent = frac; 9302490Sjkh percent -= frac; 9312490Sjkh } 9322490Sjkh } 9332490Sjkh last->percent = percent; 9342490Sjkh DPRINTF(1, (stderr, ", residual = %d%%", percent)); 9352490Sjkh } 936203922Suqs else 937203926Suqs DPRINTF(1, (stderr, 9382490Sjkh ", %d%% distributed over remaining fortunes\n", 9392490Sjkh percent)); 9402490Sjkh } 9412490Sjkh DPRINTF(1, (stderr, "\n")); 9422490Sjkh 9432490Sjkh#ifdef DEBUG 9442490Sjkh if (Debug >= 1) 9452490Sjkh print_file_list(); 9462490Sjkh#endif 9472490Sjkh} 9482490Sjkh 9492490Sjkh/* 9502490Sjkh * get_fort: 9512490Sjkh * Get the fortune data file's seek pointer for the next fortune. 9522490Sjkh */ 953227101Sedstatic void 954203922Suqsget_fort(void) 9552490Sjkh{ 95653210Sbillf FILEDESC *fp; 95753210Sbillf int choice; 9582490Sjkh 9592490Sjkh if (File_list->next == NULL || File_list->percent == NO_PROB) 9602490Sjkh fp = File_list; 9612490Sjkh else { 962181385Sache choice = arc4random_uniform(100); 9632490Sjkh DPRINTF(1, (stderr, "choice = %d\n", choice)); 9642490Sjkh for (fp = File_list; fp->percent != NO_PROB; fp = fp->next) 9652490Sjkh if (choice < fp->percent) 9662490Sjkh break; 9672490Sjkh else { 9682490Sjkh choice -= fp->percent; 9692490Sjkh DPRINTF(1, (stderr, 9702490Sjkh " skip \"%s\", %d%% (choice = %d)\n", 9712490Sjkh fp->name, fp->percent, choice)); 9722490Sjkh } 9732490Sjkh DPRINTF(1, (stderr, 9742490Sjkh "using \"%s\", %d%% (choice = %d)\n", 9752490Sjkh fp->name, fp->percent, choice)); 9762490Sjkh } 9772490Sjkh if (fp->percent != NO_PROB) 9782490Sjkh get_tbl(fp); 9792490Sjkh else { 9802490Sjkh if (fp->next != NULL) { 9812490Sjkh sum_noprobs(fp); 982181385Sache choice = arc4random_uniform(Noprob_tbl.str_numstr); 983142022Sru DPRINTF(1, (stderr, "choice = %d (of %u) \n", choice, 9842490Sjkh Noprob_tbl.str_numstr)); 985203922Suqs while ((unsigned int)choice >= fp->tbl.str_numstr) { 9862490Sjkh choice -= fp->tbl.str_numstr; 9872490Sjkh fp = fp->next; 9882490Sjkh DPRINTF(1, (stderr, 989142022Sru " skip \"%s\", %u (choice = %d)\n", 9902490Sjkh fp->name, fp->tbl.str_numstr, 9912490Sjkh choice)); 9922490Sjkh } 993142022Sru DPRINTF(1, (stderr, "using \"%s\", %u\n", fp->name, 9942490Sjkh fp->tbl.str_numstr)); 9952490Sjkh } 9962490Sjkh get_tbl(fp); 9972490Sjkh } 9982490Sjkh if (fp->child != NULL) { 9992490Sjkh DPRINTF(1, (stderr, "picking child\n")); 10002490Sjkh fp = pick_child(fp); 10012490Sjkh } 10022490Sjkh Fortfile = fp; 10032490Sjkh get_pos(fp); 10042490Sjkh open_dat(fp); 1005203926Suqs lseek(fp->datfd, 1006203926Suqs (off_t) (sizeof fp->tbl + fp->pos * sizeof Seekpts[0]), SEEK_SET); 10072490Sjkh read(fp->datfd, Seekpts, sizeof Seekpts); 1008142022Sru Seekpts[0] = be64toh(Seekpts[0]); 1009142022Sru Seekpts[1] = be64toh(Seekpts[1]); 10102490Sjkh} 10112490Sjkh 10122490Sjkh/* 10132490Sjkh * pick_child 10142490Sjkh * Pick a child from a chosen parent. 10152490Sjkh */ 1016227101Sedstatic FILEDESC * 1017203922Suqspick_child(FILEDESC *parent) 10182490Sjkh{ 101953210Sbillf FILEDESC *fp; 102053210Sbillf int choice; 10212490Sjkh 10222490Sjkh if (Equal_probs) { 1023181385Sache choice = arc4random_uniform(parent->num_children); 10242490Sjkh DPRINTF(1, (stderr, " choice = %d (of %d)\n", 10252490Sjkh choice, parent->num_children)); 10262490Sjkh for (fp = parent->child; choice--; fp = fp->next) 10272490Sjkh continue; 10282490Sjkh DPRINTF(1, (stderr, " using %s\n", fp->name)); 1029203926Suqs return (fp); 10302490Sjkh } 10312490Sjkh else { 10322490Sjkh get_tbl(parent); 1033181385Sache choice = arc4random_uniform(parent->tbl.str_numstr); 1034142022Sru DPRINTF(1, (stderr, " choice = %d (of %u)\n", 10352490Sjkh choice, parent->tbl.str_numstr)); 1036203922Suqs for (fp = parent->child; (unsigned)choice >= fp->tbl.str_numstr; 10372490Sjkh fp = fp->next) { 10382490Sjkh choice -= fp->tbl.str_numstr; 1039142022Sru DPRINTF(1, (stderr, "\tskip %s, %u (choice = %d)\n", 10402490Sjkh fp->name, fp->tbl.str_numstr, choice)); 10412490Sjkh } 1042142022Sru DPRINTF(1, (stderr, " using %s, %u\n", fp->name, 10432490Sjkh fp->tbl.str_numstr)); 1044203926Suqs return (fp); 10452490Sjkh } 10462490Sjkh} 10472490Sjkh 10482490Sjkh/* 10492490Sjkh * sum_noprobs: 10502490Sjkh * Sum up all the noprob probabilities, starting with fp. 10512490Sjkh */ 1052227101Sedstatic void 1053203922Suqssum_noprobs(FILEDESC *fp) 10542490Sjkh{ 10552490Sjkh static bool did_noprobs = FALSE; 10562490Sjkh 10572490Sjkh if (did_noprobs) 10582490Sjkh return; 10592490Sjkh zero_tbl(&Noprob_tbl); 10602490Sjkh while (fp != NULL) { 10612490Sjkh get_tbl(fp); 10622490Sjkh sum_tbl(&Noprob_tbl, &fp->tbl); 10632490Sjkh fp = fp->next; 10642490Sjkh } 10652490Sjkh did_noprobs = TRUE; 10662490Sjkh} 10672490Sjkh 1068227101Sedstatic int 1069203922Suqsmax(int i, int j) 10702490Sjkh{ 10712490Sjkh return (i >= j ? i : j); 10722490Sjkh} 10732490Sjkh 10742490Sjkh/* 10752490Sjkh * open_fp: 10762490Sjkh * Assocatiate a FILE * with the given FILEDESC. 10772490Sjkh */ 1078227101Sedstatic void 1079203922Suqsopen_fp(FILEDESC *fp) 10802490Sjkh{ 10812490Sjkh if (fp->inf == NULL && (fp->inf = fdopen(fp->fd, "r")) == NULL) { 10822490Sjkh perror(fp->path); 10832490Sjkh exit(1); 10842490Sjkh } 10852490Sjkh} 10862490Sjkh 10872490Sjkh/* 10882490Sjkh * open_dat: 10892490Sjkh * Open up the dat file if we need to. 10902490Sjkh */ 1091227101Sedstatic void 1092203922Suqsopen_dat(FILEDESC *fp) 10932490Sjkh{ 1094203926Suqs if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, O_RDONLY)) < 0) { 10952490Sjkh perror(fp->datfile); 10962490Sjkh exit(1); 10972490Sjkh } 10982490Sjkh} 10992490Sjkh 11002490Sjkh/* 11012490Sjkh * get_pos: 11022490Sjkh * Get the position from the pos file, if there is one. If not, 11032490Sjkh * return a random number. 11042490Sjkh */ 1105227101Sedstatic void 1106203922Suqsget_pos(FILEDESC *fp) 11072490Sjkh{ 11082490Sjkh int fd; 11092490Sjkh 11102490Sjkh assert(fp->read_tbl); 11112490Sjkh if (fp->pos == POS_UNKNOWN) { 1112242577Seadler if (WriteToDisk) { 1113242577Seadler if ((fd = open(fp->posfile, O_RDONLY)) < 0 || 1114242577Seadler read(fd, &fp->pos, sizeof fp->pos) != sizeof fp->pos) 1115242577Seadler fp->pos = arc4random_uniform(fp->tbl.str_numstr); 1116242577Seadler else if (fp->pos >= fp->tbl.str_numstr) 1117242577Seadler fp->pos %= fp->tbl.str_numstr; 1118242577Seadler if (fd >= 0) 1119242577Seadler close(fd); 1120242577Seadler } 1121242577Seadler else 1122181385Sache fp->pos = arc4random_uniform(fp->tbl.str_numstr); 11232490Sjkh } 11242490Sjkh if (++(fp->pos) >= fp->tbl.str_numstr) 11252490Sjkh fp->pos -= fp->tbl.str_numstr; 1126142022Sru DPRINTF(1, (stderr, "pos for %s is %ld\n", fp->name, (long)fp->pos)); 11272490Sjkh} 11282490Sjkh 11292490Sjkh/* 11302490Sjkh * get_tbl: 11312490Sjkh * Get the tbl data file the datfile. 11322490Sjkh */ 1133227101Sedstatic void 1134203922Suqsget_tbl(FILEDESC *fp) 11352490Sjkh{ 1136203926Suqs int fd; 113753210Sbillf FILEDESC *child; 11382490Sjkh 11392490Sjkh if (fp->read_tbl) 11402490Sjkh return; 11412490Sjkh if (fp->child == NULL) { 1142203926Suqs if ((fd = open(fp->datfile, O_RDONLY)) < 0) { 11432490Sjkh perror(fp->datfile); 11442490Sjkh exit(1); 11452490Sjkh } 11462490Sjkh if (read(fd, (char *) &fp->tbl, sizeof fp->tbl) != sizeof fp->tbl) { 11472490Sjkh (void)fprintf(stderr, 11482490Sjkh "fortune: %s corrupted\n", fp->path); 11492490Sjkh exit(1); 11502490Sjkh } 1151142022Sru /* fp->tbl.str_version = be32toh(fp->tbl.str_version); */ 1152142022Sru fp->tbl.str_numstr = be32toh(fp->tbl.str_numstr); 1153142022Sru fp->tbl.str_longlen = be32toh(fp->tbl.str_longlen); 1154142022Sru fp->tbl.str_shortlen = be32toh(fp->tbl.str_shortlen); 1155142022Sru fp->tbl.str_flags = be32toh(fp->tbl.str_flags); 11562490Sjkh (void) close(fd); 11572490Sjkh } 11582490Sjkh else { 11592490Sjkh zero_tbl(&fp->tbl); 11602490Sjkh for (child = fp->child; child != NULL; child = child->next) { 11612490Sjkh get_tbl(child); 11622490Sjkh sum_tbl(&fp->tbl, &child->tbl); 11632490Sjkh } 11642490Sjkh } 11652490Sjkh fp->read_tbl = TRUE; 11662490Sjkh} 11672490Sjkh 11682490Sjkh/* 11692490Sjkh * zero_tbl: 11702490Sjkh * Zero out the fields we care about in a tbl structure. 11712490Sjkh */ 1172227101Sedstatic void 1173203922Suqszero_tbl(STRFILE *tp) 11742490Sjkh{ 11752490Sjkh tp->str_numstr = 0; 11762490Sjkh tp->str_longlen = 0; 1177142022Sru tp->str_shortlen = ~0; 11782490Sjkh} 11792490Sjkh 11802490Sjkh/* 11812490Sjkh * sum_tbl: 11822490Sjkh * Merge the tbl data of t2 into t1. 11832490Sjkh */ 1184227101Sedstatic void 1185203922Suqssum_tbl(STRFILE *t1, STRFILE *t2) 11862490Sjkh{ 11872490Sjkh t1->str_numstr += t2->str_numstr; 11882490Sjkh if (t1->str_longlen < t2->str_longlen) 11892490Sjkh t1->str_longlen = t2->str_longlen; 11902490Sjkh if (t1->str_shortlen > t2->str_shortlen) 11912490Sjkh t1->str_shortlen = t2->str_shortlen; 11922490Sjkh} 11932490Sjkh 11942490Sjkh#define STR(str) ((str) == NULL ? "NULL" : (str)) 11952490Sjkh 11962490Sjkh/* 11972490Sjkh * print_file_list: 11982490Sjkh * Print out the file list 11992490Sjkh */ 1200227101Sedstatic void 1201203922Suqsprint_file_list(void) 12022490Sjkh{ 12032490Sjkh print_list(File_list, 0); 12042490Sjkh} 12052490Sjkh 12062490Sjkh/* 12072490Sjkh * print_list: 12082490Sjkh * Print out the actual list, recursively. 12092490Sjkh */ 1210227101Sedstatic void 1211203922Suqsprint_list(FILEDESC *list, int lev) 12122490Sjkh{ 12132490Sjkh while (list != NULL) { 12142490Sjkh fprintf(stderr, "%*s", lev * 4, ""); 12152490Sjkh if (list->percent == NO_PROB) 12162490Sjkh fprintf(stderr, "___%%"); 12172490Sjkh else 12182490Sjkh fprintf(stderr, "%3d%%", list->percent); 12192490Sjkh fprintf(stderr, " %s", STR(list->name)); 122015957Sache DPRINTF(1, (stderr, " (%s, %s, %s)", STR(list->path), 12212490Sjkh STR(list->datfile), STR(list->posfile))); 122215957Sache fprintf(stderr, "\n"); 12232490Sjkh if (list->child != NULL) 12242490Sjkh print_list(list->child, lev + 1); 12252490Sjkh list = list->next; 12262490Sjkh } 12272490Sjkh} 12282490Sjkh 12292490Sjkh/* 12302490Sjkh * conv_pat: 12312490Sjkh * Convert the pattern to an ignore-case equivalent. 12322490Sjkh */ 1233227101Sedstatic char * 1234203922Suqsconv_pat(char *orig) 12352490Sjkh{ 123653210Sbillf char *sp; 123753210Sbillf unsigned int cnt; 123853210Sbillf char *new; 12392490Sjkh 12402490Sjkh cnt = 1; /* allow for '\0' */ 12412490Sjkh for (sp = orig; *sp != '\0'; sp++) 124215944Sache if (isalpha((unsigned char)*sp)) 12432490Sjkh cnt += 4; 12442490Sjkh else 12452490Sjkh cnt++; 12462490Sjkh if ((new = malloc(cnt)) == NULL) { 12472490Sjkh fprintf(stderr, "pattern too long for ignoring case\n"); 12482490Sjkh exit(1); 12492490Sjkh } 12502490Sjkh 12512490Sjkh for (sp = new; *orig != '\0'; orig++) { 125215944Sache if (islower((unsigned char)*orig)) { 12532490Sjkh *sp++ = '['; 12542490Sjkh *sp++ = *orig; 125515944Sache *sp++ = toupper((unsigned char)*orig); 12562490Sjkh *sp++ = ']'; 12572490Sjkh } 125815944Sache else if (isupper((unsigned char)*orig)) { 12592490Sjkh *sp++ = '['; 12602490Sjkh *sp++ = *orig; 126115944Sache *sp++ = tolower((unsigned char)*orig); 12622490Sjkh *sp++ = ']'; 12632490Sjkh } 12642490Sjkh else 12652490Sjkh *sp++ = *orig; 12662490Sjkh } 12672490Sjkh *sp = '\0'; 1268203926Suqs 1269203926Suqs return (new); 12702490Sjkh} 12712490Sjkh 12722490Sjkh/* 12732490Sjkh * find_matches: 12742490Sjkh * Find all the fortunes which match the pattern we've been given. 12752490Sjkh */ 1276227101Sedstatic int 1277203922Suqsfind_matches(void) 12782490Sjkh{ 12792490Sjkh Fort_len = maxlen_in_list(File_list); 12802490Sjkh DPRINTF(2, (stderr, "Maximum length is %d\n", Fort_len)); 12812490Sjkh /* extra length, "%\n" is appended */ 12822490Sjkh Fortbuf = do_malloc((unsigned int) Fort_len + 10); 12832490Sjkh 12842490Sjkh Found_one = FALSE; 12852490Sjkh matches_in_list(File_list); 1286203926Suqs 1287203926Suqs return (Found_one); 12882490Sjkh} 12892490Sjkh 12902490Sjkh/* 12912490Sjkh * maxlen_in_list 12922490Sjkh * Return the maximum fortune len in the file list. 12932490Sjkh */ 1294227101Sedstatic int 1295203922Suqsmaxlen_in_list(FILEDESC *list) 12962490Sjkh{ 129753210Sbillf FILEDESC *fp; 129853210Sbillf int len, maxlen; 12992490Sjkh 13002490Sjkh maxlen = 0; 13012490Sjkh for (fp = list; fp != NULL; fp = fp->next) { 13022490Sjkh if (fp->child != NULL) { 13032490Sjkh if ((len = maxlen_in_list(fp->child)) > maxlen) 13042490Sjkh maxlen = len; 13052490Sjkh } 13062490Sjkh else { 13072490Sjkh get_tbl(fp); 1308203926Suqs if (fp->tbl.str_longlen > (unsigned int)maxlen) 13092490Sjkh maxlen = fp->tbl.str_longlen; 13102490Sjkh } 13112490Sjkh } 1312203926Suqs 1313203926Suqs return (maxlen); 13142490Sjkh} 13152490Sjkh 13162490Sjkh/* 13172490Sjkh * matches_in_list 13182490Sjkh * Print out the matches from the files in the list. 13192490Sjkh */ 1320227101Sedstatic void 1321203922Suqsmatches_in_list(FILEDESC *list) 13222490Sjkh{ 132353210Sbillf char *sp, *p; 132453210Sbillf FILEDESC *fp; 1325203926Suqs int in_file; 1326203926Suqs unsigned char ch; 13272490Sjkh 13282490Sjkh for (fp = list; fp != NULL; fp = fp->next) { 13292490Sjkh if (fp->child != NULL) { 13302490Sjkh matches_in_list(fp->child); 13312490Sjkh continue; 13322490Sjkh } 13332490Sjkh DPRINTF(1, (stderr, "searching in %s\n", fp->path)); 13342490Sjkh open_fp(fp); 13352490Sjkh sp = Fortbuf; 13362490Sjkh in_file = FALSE; 13372490Sjkh while (fgets(sp, Fort_len, fp->inf) != NULL) 133851863Sdcs if (fp->tbl.str_flags & STR_COMMENTS 133951863Sdcs && sp[0] == fp->tbl.str_delim 134051863Sdcs && sp[1] == fp->tbl.str_delim) 134151863Sdcs continue; 134251863Sdcs else if (!STR_ENDSTRING(sp, fp->tbl)) 13432490Sjkh sp += strlen(sp); 13442490Sjkh else { 13452490Sjkh *sp = '\0'; 134615944Sache if (fp->tbl.str_flags & STR_ROTATED) 134715944Sache for (p = Fortbuf; (ch = *p) != '\0'; ++p) { 134815944Sache if (isascii(ch)) { 134915944Sache if (isupper(ch)) 135015944Sache *p = 'A' + (ch - 'A' + 13) % 26; 135115944Sache else if (islower(ch)) 135215944Sache *p = 'a' + (ch - 'a' + 13) % 26; 135315944Sache } 135415944Sache } 1355130851Sphk if (regexec(&Re_pat, Fortbuf, 0, NULL, 0) != REG_NOMATCH) { 13562490Sjkh printf("%c%c", fp->tbl.str_delim, 13572490Sjkh fp->tbl.str_delim); 13582490Sjkh if (!in_file) { 13592490Sjkh printf(" (%s)", fp->name); 13602490Sjkh Found_one = TRUE; 13612490Sjkh in_file = TRUE; 13622490Sjkh } 13632490Sjkh putchar('\n'); 13642490Sjkh (void) fwrite(Fortbuf, 1, (sp - Fortbuf), stdout); 13652490Sjkh } 13662490Sjkh sp = Fortbuf; 13672490Sjkh } 13682490Sjkh } 13692490Sjkh} 13702490Sjkh 1371227101Sedstatic void 1372203922Suqsusage(void) 13732490Sjkh{ 13742490Sjkh (void) fprintf(stderr, "fortune [-a"); 13752490Sjkh#ifdef DEBUG 13762490Sjkh (void) fprintf(stderr, "D"); 13772490Sjkh#endif /* DEBUG */ 1378141581Sru (void) fprintf(stderr, "efilosw]"); 13792490Sjkh (void) fprintf(stderr, " [-m pattern]"); 1380141581Sru (void) fprintf(stderr, " [[N%%] file/directory/all]\n"); 13812490Sjkh exit(1); 13822490Sjkh} 1383173396Sedwin 1384173396Sedwin/* 1385173396Sedwin * getpath 1386173396Sedwin * Set up file search patch from environment var FORTUNE_PATH; 1387173396Sedwin * if not set, use the compiled in FORTDIR. 1388173396Sedwin */ 1389173396Sedwin 1390227101Sedstatic void 1391173396Sedwingetpath(void) 1392173396Sedwin{ 1393173401Sedwin int nstr, foundenv; 1394173401Sedwin char *pch, **ppch, *str, *path; 1395173396Sedwin 1396173401Sedwin foundenv = 1; 1397173396Sedwin Fortune_path = getenv("FORTUNE_PATH"); 1398173401Sedwin if (Fortune_path == NULL) { 1399173401Sedwin Fortune_path = FORTDIR; 1400173401Sedwin foundenv = 0; 1401173401Sedwin } 1402173396Sedwin path = strdup(Fortune_path); 1403173396Sedwin 1404173396Sedwin for (nstr = 2, pch = path; *pch != '\0'; pch++) { 1405173396Sedwin if (*pch == ':') 1406173396Sedwin nstr++; 1407173396Sedwin } 1408173396Sedwin 1409173396Sedwin ppch = Fortune_path_arr = (char **)calloc(nstr, sizeof(char *)); 1410173396Sedwin 1411173396Sedwin nstr = 0; 1412173396Sedwin str = strtok(path, ":"); 1413173396Sedwin while (str) { 1414173396Sedwin if (is_dir(str)) { 1415173396Sedwin nstr++; 1416173396Sedwin *ppch++ = str; 1417173396Sedwin } 1418173396Sedwin str = strtok(NULL, ":"); 1419173396Sedwin } 1420173401Sedwin 1421173396Sedwin if (nstr == 0) { 1422173401Sedwin if (foundenv == 1) { 1423173401Sedwin fprintf(stderr, 1424173401Sedwin "fortune: FORTUNE_PATH: None of the specified " 1425173401Sedwin "directories found.\n"); 1426173401Sedwin exit(1); 1427173401Sedwin } 1428173396Sedwin free(path); 1429203922Suqs Fortune_path_arr[0] = strdup(FORTDIR); 1430173396Sedwin } 1431173396Sedwin} 1432