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> 56242686Seadler#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 66242686Seadler#define TRUE true 67242686Seadler#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 1002490Sjkhbool Found_one; /* did we find a match? */ 1012490Sjkhbool Find_files = FALSE; /* just find a list of proper fortune files */ 102203926Suqsbool Fortunes_only = FALSE; /* check only "fortunes" files */ 1032490Sjkhbool Wait = FALSE; /* wait desired after fortune */ 1042490Sjkhbool Short_only = FALSE; /* short fortune desired */ 1052490Sjkhbool Long_only = FALSE; /* long fortune desired */ 1062490Sjkhbool Offend = FALSE; /* offensive fortunes only */ 1072490Sjkhbool All_forts = FALSE; /* any fortune allowed */ 1082490Sjkhbool Equal_probs = FALSE; /* scatter un-allocted prob equally */ 1092490Sjkhbool Match = FALSE; /* dump fortunes matching a pattern */ 110242687Seadlerstatic bool WriteToDisk = false; /* use files on disk to save state */ 1112490Sjkh#ifdef DEBUG 112243197Sdimint Debug = FALSE; /* print debug messages */ 1132490Sjkh#endif 1142490Sjkh 1152490Sjkhchar *Fortbuf = NULL; /* fortune buffer for -m */ 1162490Sjkh 1172490Sjkhint Fort_len = 0; 1182490Sjkh 119142022Sruoff_t Seekpts[2]; /* seek pointers to fortunes */ 1202490Sjkh 1212490SjkhFILEDESC *File_list = NULL, /* Head of file list */ 1222490Sjkh *File_tail = NULL; /* Tail of file list */ 1232490SjkhFILEDESC *Fortfile; /* Fortune file to use */ 1242490Sjkh 1252490SjkhSTRFILE Noprob_tbl; /* sum of data for all no prob files */ 1262490Sjkh 127203922Suqsconst char *Fortune_path; 128173396Sedwinchar **Fortune_path_arr; 129173396Sedwin 13093063Simpint add_dir(FILEDESC *); 131203922Suqsint add_file(int, const char *, const char *, FILEDESC **, FILEDESC **, 132203922Suqs FILEDESC *); 13393063Simpvoid all_forts(FILEDESC *, char *); 134203922Suqschar *copy(const char *, u_int); 13593063Simpvoid display(FILEDESC *); 13693063Simpvoid do_free(void *); 13793063Simpvoid *do_malloc(u_int); 13893063Simpint form_file_list(char **, int); 13993063Simpint fortlen(void); 14093063Simpvoid get_fort(void); 14193063Simpvoid get_pos(FILEDESC *); 14293063Simpvoid get_tbl(FILEDESC *); 14393063Simpvoid getargs(int, char *[]); 144173396Sedwinvoid getpath(void); 14593063Simpvoid init_prob(void); 146203922Suqsint is_dir(const char *); 147203922Suqsint is_fortfile(const char *, char **, char **, int); 148203922Suqsint is_off_name(const char *); 14993063Simpint max(int, int); 1502490SjkhFILEDESC * 15193063Simp new_fp(void); 152203922Suqschar *off_name(const char *); 15393063Simpvoid open_dat(FILEDESC *); 15493063Simpvoid open_fp(FILEDESC *); 1552490SjkhFILEDESC * 15693063Simp pick_child(FILEDESC *); 15793063Simpvoid print_file_list(void); 15893063Simpvoid print_list(FILEDESC *, int); 15993063Simpvoid sum_noprobs(FILEDESC *); 16093063Simpvoid sum_tbl(STRFILE *, STRFILE *); 16193063Simpvoid usage(void); 16293063Simpvoid zero_tbl(STRFILE *); 1632490Sjkh 16493063Simpchar *conv_pat(char *); 16593063Simpint find_matches(void); 16693063Simpvoid matches_in_list(FILEDESC *); 16793063Simpint maxlen_in_list(FILEDESC *); 1682490Sjkh 169130851Sphkstatic regex_t Re_pat; 1702490Sjkh 1712490Sjkhint 172203922Suqsmain(int argc, char *argv[]) 1732490Sjkh{ 1742490Sjkh int fd; 1752490Sjkh 176242687Seadler if (getenv("FORTUNE_SAVESTATE") != NULL) 177242687Seadler WriteToDisk = true; 178242687Seadler 17917537Sache (void) setlocale(LC_ALL, ""); 18015944Sache 181173396Sedwin getpath(); 182203922Suqs getargs(argc, argv); 1832490Sjkh 1842490Sjkh if (Match) 1852490Sjkh exit(find_matches() != 0); 1862490Sjkh 1872490Sjkh init_prob(); 1882490Sjkh do { 1892490Sjkh get_fort(); 1902490Sjkh } while ((Short_only && fortlen() > SLEN) || 1912490Sjkh (Long_only && fortlen() <= SLEN)); 1922490Sjkh 1932490Sjkh display(Fortfile); 1942490Sjkh 195242687Seadler if (WriteToDisk) { 196242687Seadler if ((fd = creat(Fortfile->posfile, 0666)) < 0) { 197242687Seadler perror(Fortfile->posfile); 198242687Seadler exit(1); 199242687Seadler } 200242687Seadler /* 201242687Seadler * if we can, we exclusive lock, but since it isn't very 202242687Seadler * important, we just punt if we don't have easy locking 203242687Seadler * available. 204242687Seadler */ 205242687Seadler flock(fd, LOCK_EX); 206242687Seadler write(fd, (char *) &Fortfile->pos, sizeof Fortfile->pos); 207242687Seadler if (!Fortfile->was_pos_file) 208242687Seadler chmod(Fortfile->path, 0666); 209242687Seadler flock(fd, LOCK_UN); 2102490Sjkh } 2112490Sjkh if (Wait) { 2122490Sjkh if (Fort_len == 0) 2132490Sjkh (void) fortlen(); 2142490Sjkh sleep((unsigned int) max(Fort_len / CPERS, MINW)); 2152490Sjkh } 216203926Suqs 217204178Suqs exit(0); 2182490Sjkh} 2192490Sjkh 2202490Sjkhvoid 221203922Suqsdisplay(FILEDESC *fp) 2222490Sjkh{ 22353210Sbillf char *p; 22453210Sbillf unsigned char ch; 2252490Sjkh char line[BUFSIZ]; 2262490Sjkh 2272490Sjkh open_fp(fp); 228203926Suqs fseeko(fp->inf, Seekpts[0], SEEK_SET); 2292490Sjkh for (Fort_len = 0; fgets(line, sizeof line, fp->inf) != NULL && 2302490Sjkh !STR_ENDSTRING(line, fp->tbl); Fort_len++) { 2312490Sjkh if (fp->tbl.str_flags & STR_ROTATED) 23215944Sache for (p = line; (ch = *p) != '\0'; ++p) { 23315944Sache if (isascii(ch)) { 23415944Sache if (isupper(ch)) 23515944Sache *p = 'A' + (ch - 'A' + 13) % 26; 23615944Sache else if (islower(ch)) 23715944Sache *p = 'a' + (ch - 'a' + 13) % 26; 23815944Sache } 23915944Sache } 24051863Sdcs if (fp->tbl.str_flags & STR_COMMENTS 24151863Sdcs && line[0] == fp->tbl.str_delim 24251863Sdcs && line[1] == fp->tbl.str_delim) 24351863Sdcs continue; 2442490Sjkh fputs(line, stdout); 2452490Sjkh } 2462490Sjkh (void) fflush(stdout); 2472490Sjkh} 2482490Sjkh 2492490Sjkh/* 2502490Sjkh * fortlen: 2512490Sjkh * Return the length of the fortune. 2522490Sjkh */ 2532490Sjkhint 254203922Suqsfortlen(void) 2552490Sjkh{ 25653210Sbillf int nchar; 257173401Sedwin char line[BUFSIZ]; 2582490Sjkh 2592490Sjkh if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED))) 260123905Sceri nchar = (int)(Seekpts[1] - Seekpts[0]); 2612490Sjkh else { 2622490Sjkh open_fp(Fortfile); 263203926Suqs fseeko(Fortfile->inf, Seekpts[0], SEEK_SET); 2642490Sjkh nchar = 0; 2652490Sjkh while (fgets(line, sizeof line, Fortfile->inf) != NULL && 2662490Sjkh !STR_ENDSTRING(line, Fortfile->tbl)) 2672490Sjkh nchar += strlen(line); 2682490Sjkh } 2692490Sjkh Fort_len = nchar; 270203926Suqs 271203926Suqs return (nchar); 2722490Sjkh} 2732490Sjkh 2742490Sjkh/* 2752490Sjkh * This routine evaluates the arguments on the command line 2762490Sjkh */ 2772490Sjkhvoid 278203922Suqsgetargs(int argc, char *argv[]) 2792490Sjkh{ 28053210Sbillf int ignore_case; 28153210Sbillf char *pat; 2822490Sjkh int ch; 2832490Sjkh 2842490Sjkh ignore_case = FALSE; 2852490Sjkh pat = NULL; 2862490Sjkh 287203926Suqs#ifdef DEBUG 28847440Simp while ((ch = getopt(argc, argv, "aDefilm:osw")) != -1) 2892490Sjkh#else 29047440Simp while ((ch = getopt(argc, argv, "aefilm:osw")) != -1) 2912490Sjkh#endif /* DEBUG */ 2922490Sjkh switch(ch) { 2932490Sjkh case 'a': /* any fortune */ 2942490Sjkh All_forts++; 2952490Sjkh break; 296203926Suqs#ifdef DEBUG 2972490Sjkh case 'D': 2982490Sjkh Debug++; 2992490Sjkh break; 300203926Suqs#endif /* DEBUG */ 3012490Sjkh case 'e': 3022490Sjkh Equal_probs++; /* scatter un-allocted prob equally */ 3032490Sjkh break; 3042490Sjkh case 'f': /* find fortune files */ 3052490Sjkh Find_files++; 3062490Sjkh break; 3072490Sjkh case 'l': /* long ones only */ 3082490Sjkh Long_only++; 3092490Sjkh Short_only = FALSE; 3102490Sjkh break; 3112490Sjkh case 'o': /* offensive ones only */ 3122490Sjkh Offend++; 3132490Sjkh break; 3142490Sjkh case 's': /* short ones only */ 3152490Sjkh Short_only++; 3162490Sjkh Long_only = FALSE; 3172490Sjkh break; 3182490Sjkh case 'w': /* give time to read */ 3192490Sjkh Wait++; 3202490Sjkh break; 3212490Sjkh case 'm': /* dump out the fortunes */ 3222490Sjkh Match++; 3232490Sjkh pat = optarg; 3242490Sjkh break; 3252490Sjkh case 'i': /* case-insensitive match */ 3262490Sjkh ignore_case++; 3272490Sjkh break; 3282490Sjkh case '?': 3292490Sjkh default: 3302490Sjkh usage(); 3312490Sjkh } 3322490Sjkh argc -= optind; 3332490Sjkh argv += optind; 3342490Sjkh 3352490Sjkh if (!form_file_list(argv, argc)) 3362490Sjkh exit(1); /* errors printed through form_file_list() */ 3372490Sjkh if (Find_files) { 3382490Sjkh print_file_list(); 3392490Sjkh exit(0); 3402490Sjkh } 34115957Sache#ifdef DEBUG 34215957Sache else if (Debug >= 1) 34315957Sache print_file_list(); 34415957Sache#endif /* DEBUG */ 3452490Sjkh 3462490Sjkh if (pat != NULL) { 347130851Sphk int error; 348130851Sphk 3492490Sjkh if (ignore_case) 3502490Sjkh pat = conv_pat(pat); 351130851Sphk error = regcomp(&Re_pat, pat, REG_BASIC); 352130851Sphk if (error) { 353130851Sphk fprintf(stderr, "regcomp(%s) fails\n", pat); 354203926Suqs exit(1); 3552490Sjkh } 3562490Sjkh } 3572490Sjkh} 3582490Sjkh 3592490Sjkh/* 3602490Sjkh * form_file_list: 3612490Sjkh * Form the file list from the file specifications. 3622490Sjkh */ 3632490Sjkhint 364203922Suqsform_file_list(char **files, int file_cnt) 3652490Sjkh{ 36653210Sbillf int i, percent; 36753210Sbillf char *sp; 368173396Sedwin char **pstr; 3692490Sjkh 37049040Sbillf if (file_cnt == 0) { 37115957Sache if (Find_files) { 37215957Sache Fortunes_only = TRUE; 373173396Sedwin pstr = Fortune_path_arr; 374173396Sedwin i = 0; 375173396Sedwin while (*pstr) { 376203926Suqs i += add_file(NO_PROB, *pstr++, NULL, 377173396Sedwin &File_list, &File_tail, NULL); 378173396Sedwin } 37915957Sache Fortunes_only = FALSE; 380173396Sedwin if (!i) { 381173401Sedwin fprintf(stderr, "No fortunes found in %s.\n", 382173401Sedwin Fortune_path); 383173396Sedwin } 384203926Suqs return (i != 0); 385173396Sedwin } else { 386173396Sedwin pstr = Fortune_path_arr; 387173396Sedwin i = 0; 388173396Sedwin while (*pstr) { 389173396Sedwin i += add_file(NO_PROB, "fortunes", *pstr++, 390173396Sedwin &File_list, &File_tail, NULL); 391173396Sedwin } 392173396Sedwin if (!i) { 393173401Sedwin fprintf(stderr, "No fortunes found in %s.\n", 394173401Sedwin Fortune_path); 395173396Sedwin } 396203926Suqs return (i != 0); 397173396Sedwin } 39849040Sbillf } 3992490Sjkh for (i = 0; i < file_cnt; i++) { 4002490Sjkh percent = NO_PROB; 40115944Sache if (!isdigit((unsigned char)files[i][0])) 4022490Sjkh sp = files[i]; 4032490Sjkh else { 4042490Sjkh percent = 0; 40515944Sache for (sp = files[i]; isdigit((unsigned char)*sp); sp++) 4062490Sjkh percent = percent * 10 + *sp - '0'; 4072490Sjkh if (percent > 100) { 4082490Sjkh fprintf(stderr, "percentages must be <= 100\n"); 409203926Suqs return (FALSE); 4102490Sjkh } 4112490Sjkh if (*sp == '.') { 4122490Sjkh fprintf(stderr, "percentages must be integers\n"); 413203926Suqs return (FALSE); 4142490Sjkh } 4152490Sjkh /* 4162490Sjkh * If the number isn't followed by a '%', then 4172490Sjkh * it was not a percentage, just the first part 4182490Sjkh * of a file name which starts with digits. 4192490Sjkh */ 4202490Sjkh if (*sp != '%') { 4212490Sjkh percent = NO_PROB; 4222490Sjkh sp = files[i]; 4232490Sjkh } 4242490Sjkh else if (*++sp == '\0') { 4252490Sjkh if (++i >= file_cnt) { 4262490Sjkh fprintf(stderr, "percentages must precede files\n"); 427203926Suqs return (FALSE); 4282490Sjkh } 4292490Sjkh sp = files[i]; 4302490Sjkh } 4312490Sjkh } 432173396Sedwin if (strcmp(sp, "all") == 0) { 433173396Sedwin pstr = Fortune_path_arr; 434173396Sedwin i = 0; 435173396Sedwin while (*pstr) { 436203926Suqs i += add_file(NO_PROB, *pstr++, NULL, 437173396Sedwin &File_list, &File_tail, NULL); 438173396Sedwin } 439173396Sedwin if (!i) { 440173401Sedwin fprintf(stderr, "No fortunes found in %s.\n", 441173401Sedwin Fortune_path); 442203926Suqs return (FALSE); 443173396Sedwin } 444203926Suqs } else if (!add_file(percent, sp, NULL, &File_list, 445173396Sedwin &File_tail, NULL)) { 446203926Suqs return (FALSE); 447173396Sedwin } 4482490Sjkh } 449203926Suqs 450203926Suqs return (TRUE); 4512490Sjkh} 4522490Sjkh 4532490Sjkh/* 4542490Sjkh * add_file: 4552490Sjkh * Add a file to the file list. 4562490Sjkh */ 4572490Sjkhint 458203922Suqsadd_file(int percent, const char *file, const char *dir, FILEDESC **head, 459203922Suqs FILEDESC **tail, FILEDESC *parent) 4602490Sjkh{ 46153210Sbillf FILEDESC *fp; 46253210Sbillf int fd; 463203922Suqs const char *path; 464203922Suqs char *tpath, *offensive; 46553210Sbillf bool was_malloc; 46653210Sbillf bool isdir; 4672490Sjkh 4682490Sjkh if (dir == NULL) { 4692490Sjkh path = file; 470203922Suqs tpath = NULL; 4712490Sjkh was_malloc = FALSE; 4722490Sjkh } 4732490Sjkh else { 474203922Suqs tpath = do_malloc((unsigned int)(strlen(dir) + strlen(file) + 2)); 475203922Suqs strcat(strcat(strcpy(tpath, dir), "/"), file); 476203922Suqs path = tpath; 4772490Sjkh was_malloc = TRUE; 4782490Sjkh } 4792490Sjkh if ((isdir = is_dir(path)) && parent != NULL) { 4802490Sjkh if (was_malloc) 481203922Suqs free(tpath); 482203926Suqs return (FALSE); /* don't recurse */ 4832490Sjkh } 4842490Sjkh offensive = NULL; 4852490Sjkh if (!isdir && parent == NULL && (All_forts || Offend) && 4862490Sjkh !is_off_name(path)) { 4872490Sjkh offensive = off_name(path); 4882490Sjkh if (Offend) { 4892490Sjkh if (was_malloc) 490203922Suqs free(tpath); 4912490Sjkh path = offensive; 49215957Sache offensive = NULL; 49315957Sache was_malloc = TRUE; 49415957Sache DPRINTF(1, (stderr, "\ttrying \"%s\"\n", path)); 4952490Sjkh file = off_name(file); 4962490Sjkh } 4972490Sjkh } 4982490Sjkh 4992490Sjkh DPRINTF(1, (stderr, "adding file \"%s\"\n", path)); 5002490Sjkhover: 501203926Suqs if ((fd = open(path, O_RDONLY)) < 0) { 5022490Sjkh /* 5032490Sjkh * This is a sneak. If the user said -a, and if the 5042490Sjkh * file we're given isn't a file, we check to see if 5052490Sjkh * there is a -o version. If there is, we treat it as 5062490Sjkh * if *that* were the file given. We only do this for 5072490Sjkh * individual files -- if we're scanning a directory, 5082490Sjkh * we'll pick up the -o file anyway. 5092490Sjkh */ 5102490Sjkh if (All_forts && offensive != NULL) { 5112490Sjkh if (was_malloc) 512203922Suqs free(tpath); 51315957Sache path = offensive; 5142490Sjkh offensive = NULL; 5152490Sjkh was_malloc = TRUE; 5162490Sjkh DPRINTF(1, (stderr, "\ttrying \"%s\"\n", path)); 5172490Sjkh file = off_name(file); 5182490Sjkh goto over; 5192490Sjkh } 520173396Sedwin if (dir == NULL && file[0] != '/') { 521173396Sedwin int i = 0; 522173396Sedwin char **pstr = Fortune_path_arr; 523173396Sedwin 524173396Sedwin while (*pstr) { 525203926Suqs i += add_file(percent, file, *pstr++, 526173396Sedwin head, tail, parent); 527173396Sedwin } 528173396Sedwin if (!i) { 529173401Sedwin fprintf(stderr, "No '%s' found in %s.\n", 530173401Sedwin file, Fortune_path); 531173396Sedwin } 532203926Suqs return (i != 0); 533173396Sedwin } 534173396Sedwin /* 5352490Sjkh if (parent == NULL) 5362490Sjkh perror(path); 537173396Sedwin */ 5382490Sjkh if (was_malloc) 539203922Suqs free(tpath); 540203926Suqs return (FALSE); 5412490Sjkh } 5422490Sjkh 5432490Sjkh DPRINTF(2, (stderr, "path = \"%s\"\n", path)); 5442490Sjkh 5452490Sjkh fp = new_fp(); 5462490Sjkh fp->fd = fd; 5472490Sjkh fp->percent = percent; 5482490Sjkh fp->name = file; 5492490Sjkh fp->path = path; 5502490Sjkh fp->parent = parent; 5512490Sjkh 5522490Sjkh if ((isdir && !add_dir(fp)) || 5532490Sjkh (!isdir && 5542490Sjkh !is_fortfile(path, &fp->datfile, &fp->posfile, (parent != NULL)))) 5552490Sjkh { 5562490Sjkh if (parent == NULL) 5572490Sjkh fprintf(stderr, 5582490Sjkh "fortune:%s not a fortune file or directory\n", 5592490Sjkh path); 5602490Sjkh if (was_malloc) 561203922Suqs free(tpath); 5622490Sjkh do_free(fp->datfile); 5632490Sjkh do_free(fp->posfile); 564203926Suqs free(fp); 5652490Sjkh do_free(offensive); 566203926Suqs return (FALSE); 5672490Sjkh } 5682490Sjkh /* 5692490Sjkh * If the user said -a, we need to make this node a pointer to 5702490Sjkh * both files, if there are two. We don't need to do this if 5712490Sjkh * we are scanning a directory, since the scan will pick up the 5722490Sjkh * -o file anyway. 5732490Sjkh */ 5742490Sjkh if (All_forts && parent == NULL && !is_off_name(path)) 5752490Sjkh all_forts(fp, offensive); 5762490Sjkh if (*head == NULL) 5772490Sjkh *head = *tail = fp; 5782490Sjkh else if (fp->percent == NO_PROB) { 5792490Sjkh (*tail)->next = fp; 5802490Sjkh fp->prev = *tail; 5812490Sjkh *tail = fp; 5822490Sjkh } 5832490Sjkh else { 5842490Sjkh (*head)->prev = fp; 5852490Sjkh fp->next = *head; 5862490Sjkh *head = fp; 5872490Sjkh } 588242687Seadler if (WriteToDisk) 589242687Seadler fp->was_pos_file = (access(fp->posfile, W_OK) >= 0); 5902490Sjkh 591203926Suqs return (TRUE); 5922490Sjkh} 5932490Sjkh 5942490Sjkh/* 5952490Sjkh * new_fp: 5962490Sjkh * Return a pointer to an initialized new FILEDESC. 5972490Sjkh */ 5982490SjkhFILEDESC * 599203922Suqsnew_fp(void) 6002490Sjkh{ 60153210Sbillf FILEDESC *fp; 6022490Sjkh 603203926Suqs fp = do_malloc(sizeof(*fp)); 6042490Sjkh fp->datfd = -1; 6052490Sjkh fp->pos = POS_UNKNOWN; 6062490Sjkh fp->inf = NULL; 6072490Sjkh fp->fd = -1; 6082490Sjkh fp->percent = NO_PROB; 6092490Sjkh fp->read_tbl = FALSE; 6102490Sjkh fp->next = NULL; 6112490Sjkh fp->prev = NULL; 6122490Sjkh fp->child = NULL; 6132490Sjkh fp->parent = NULL; 6142490Sjkh fp->datfile = NULL; 6152490Sjkh fp->posfile = NULL; 616203926Suqs 617203926Suqs return (fp); 6182490Sjkh} 6192490Sjkh 6202490Sjkh/* 6212490Sjkh * off_name: 6222490Sjkh * Return a pointer to the offensive version of a file of this name. 6232490Sjkh */ 6242490Sjkhchar * 625203922Suqsoff_name(const char *file) 6262490Sjkh{ 6272490Sjkh char *new; 6282490Sjkh 6292490Sjkh new = copy(file, (unsigned int) (strlen(file) + 2)); 630203926Suqs 631203926Suqs return (strcat(new, "-o")); 6322490Sjkh} 6332490Sjkh 6342490Sjkh/* 6352490Sjkh * is_off_name: 6362490Sjkh * Is the file an offensive-style name? 6372490Sjkh */ 6382490Sjkhint 639203922Suqsis_off_name(const char *file) 6402490Sjkh{ 6412490Sjkh int len; 6422490Sjkh 6432490Sjkh len = strlen(file); 644203926Suqs 6452490Sjkh return (len >= 3 && file[len - 2] == '-' && file[len - 1] == 'o'); 6462490Sjkh} 6472490Sjkh 6482490Sjkh/* 6492490Sjkh * all_forts: 6502490Sjkh * Modify a FILEDESC element to be the parent of two children if 6512490Sjkh * there are two children to be a parent of. 6522490Sjkh */ 6532490Sjkhvoid 654203922Suqsall_forts(FILEDESC *fp, char *offensive) 6552490Sjkh{ 65653210Sbillf char *sp; 65753210Sbillf FILEDESC *scene, *obscene; 65853210Sbillf int fd; 659203926Suqs char *datfile, *posfile; 6602490Sjkh 6612490Sjkh if (fp->child != NULL) /* this is a directory, not a file */ 6622490Sjkh return; 6632490Sjkh if (!is_fortfile(offensive, &datfile, &posfile, FALSE)) 6642490Sjkh return; 665203926Suqs if ((fd = open(offensive, O_RDONLY)) < 0) 6662490Sjkh return; 6672490Sjkh DPRINTF(1, (stderr, "adding \"%s\" because of -a\n", offensive)); 6682490Sjkh scene = new_fp(); 6692490Sjkh obscene = new_fp(); 6702490Sjkh *scene = *fp; 6712490Sjkh 6722490Sjkh fp->num_children = 2; 6732490Sjkh fp->child = scene; 6742490Sjkh scene->next = obscene; 6752490Sjkh obscene->next = NULL; 6762490Sjkh scene->child = obscene->child = NULL; 6772490Sjkh scene->parent = obscene->parent = fp; 6782490Sjkh 6792490Sjkh fp->fd = -1; 6802490Sjkh scene->percent = obscene->percent = NO_PROB; 6812490Sjkh 6822490Sjkh obscene->fd = fd; 6832490Sjkh obscene->inf = NULL; 6842490Sjkh obscene->path = offensive; 685249828Seadler if ((sp = strrchr(offensive, '/')) == NULL) 6862490Sjkh obscene->name = offensive; 6872490Sjkh else 6882490Sjkh obscene->name = ++sp; 6892490Sjkh obscene->datfile = datfile; 6902490Sjkh obscene->posfile = posfile; 691242687Seadler obscene->read_tbl = false; 692242687Seadler if (WriteToDisk) 693242687Seadler obscene->was_pos_file = (access(obscene->posfile, W_OK) >= 0); 6942490Sjkh} 6952490Sjkh 6962490Sjkh/* 6972490Sjkh * add_dir: 6982490Sjkh * Add the contents of an entire directory. 6992490Sjkh */ 7002490Sjkhint 701203922Suqsadd_dir(FILEDESC *fp) 7022490Sjkh{ 70353210Sbillf DIR *dir; 70453210Sbillf struct dirent *dirent; 705203926Suqs FILEDESC *tailp; 706203926Suqs char *name; 7072490Sjkh 7082490Sjkh (void) close(fp->fd); 7092490Sjkh fp->fd = -1; 7102490Sjkh if ((dir = opendir(fp->path)) == NULL) { 7112490Sjkh perror(fp->path); 712203926Suqs return (FALSE); 7132490Sjkh } 7142490Sjkh tailp = NULL; 7152490Sjkh DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path)); 7162490Sjkh fp->num_children = 0; 7172490Sjkh while ((dirent = readdir(dir)) != NULL) { 7182490Sjkh if (dirent->d_namlen == 0) 7192490Sjkh continue; 7202490Sjkh name = copy(dirent->d_name, dirent->d_namlen); 7212490Sjkh if (add_file(NO_PROB, name, fp->path, &fp->child, &tailp, fp)) 7222490Sjkh fp->num_children++; 7232490Sjkh else 7242490Sjkh free(name); 7252490Sjkh } 7262490Sjkh if (fp->num_children == 0) { 7272490Sjkh (void) fprintf(stderr, 7282490Sjkh "fortune: %s: No fortune files in directory.\n", fp->path); 729203926Suqs return (FALSE); 7302490Sjkh } 731203926Suqs 732203926Suqs return (TRUE); 7332490Sjkh} 7342490Sjkh 7352490Sjkh/* 7362490Sjkh * is_dir: 7372490Sjkh * Return TRUE if the file is a directory, FALSE otherwise. 7382490Sjkh */ 7392490Sjkhint 740203922Suqsis_dir(const char *file) 7412490Sjkh{ 742203926Suqs struct stat sbuf; 7432490Sjkh 7442490Sjkh if (stat(file, &sbuf) < 0) 745203926Suqs return (FALSE); 746203926Suqs 7472490Sjkh return (sbuf.st_mode & S_IFDIR); 7482490Sjkh} 7492490Sjkh 7502490Sjkh/* 7512490Sjkh * is_fortfile: 7522490Sjkh * Return TRUE if the file is a fortune database file. We try and 7532490Sjkh * exclude files without reading them if possible to avoid 7542490Sjkh * overhead. Files which start with ".", or which have "illegal" 7552490Sjkh * suffixes, as contained in suflist[], are ruled out. 7562490Sjkh */ 7572490Sjkh/* ARGSUSED */ 7582490Sjkhint 759203922Suqsis_fortfile(const char *file, char **datp, char **posp, int check_for_offend) 7602490Sjkh{ 76153210Sbillf int i; 762203922Suqs const char *sp; 76353210Sbillf char *datfile; 764203922Suqs static const char *suflist[] = { 765203922Suqs /* list of "illegal" suffixes" */ 766203922Suqs "dat", "pos", "c", "h", "p", "i", "f", 767203922Suqs "pas", "ftn", "ins.c", "ins,pas", 768203922Suqs "ins.ftn", "sml", 769203922Suqs NULL 770203922Suqs }; 7712490Sjkh 7722490Sjkh DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file)); 7732490Sjkh 7742490Sjkh /* 7752490Sjkh * Preclude any -o files for offendable people, and any non -o 7762490Sjkh * files for completely offensive people. 7772490Sjkh */ 7782490Sjkh if (check_for_offend && !All_forts) { 7792490Sjkh i = strlen(file); 78015957Sache if (Offend ^ (file[i - 2] == '-' && file[i - 1] == 'o')) { 78115957Sache DPRINTF(2, (stderr, "FALSE (offending file)\n")); 782203926Suqs return (FALSE); 78315957Sache } 7842490Sjkh } 7852490Sjkh 786249828Seadler if ((sp = strrchr(file, '/')) == NULL) 7872490Sjkh sp = file; 7882490Sjkh else 7892490Sjkh sp++; 7902490Sjkh if (*sp == '.') { 7912490Sjkh DPRINTF(2, (stderr, "FALSE (file starts with '.')\n")); 792203926Suqs return (FALSE); 7932490Sjkh } 79415957Sache if (Fortunes_only && strncmp(sp, "fortunes", 8) != 0) { 79515957Sache DPRINTF(2, (stderr, "FALSE (check fortunes only)\n")); 796203926Suqs return (FALSE); 79715957Sache } 798249828Seadler if ((sp = strrchr(sp, '.')) != NULL) { 7992490Sjkh sp++; 8002490Sjkh for (i = 0; suflist[i] != NULL; i++) 8012490Sjkh if (strcmp(sp, suflist[i]) == 0) { 8022490Sjkh DPRINTF(2, (stderr, "FALSE (file has suffix \".%s\")\n", sp)); 803203926Suqs return (FALSE); 8042490Sjkh } 8052490Sjkh } 8062490Sjkh 8072490Sjkh datfile = copy(file, (unsigned int) (strlen(file) + 4)); /* +4 for ".dat" */ 8082490Sjkh strcat(datfile, ".dat"); 8092490Sjkh if (access(datfile, R_OK) < 0) { 81044599Sdcs DPRINTF(2, (stderr, "FALSE (no readable \".dat\" file)\n")); 81144599Sdcs#ifdef DEBUG 81244599Sdcs if (Debug < 2) 81344599Sdcs DPRINTF(0, (stderr, "Warning: file \"%s\" unreadable\n", datfile)); 81444599Sdcs#endif 8152490Sjkh free(datfile); 816203926Suqs return (FALSE); 8172490Sjkh } 8182490Sjkh if (datp != NULL) 8192490Sjkh *datp = datfile; 8202490Sjkh else 8212490Sjkh free(datfile); 82215944Sache if (posp != NULL) { 823242687Seadler if (WriteToDisk) { 824242687Seadler *posp = copy(file, (unsigned int) (strlen(file) + 4)); /* +4 for ".dat" */ 825242687Seadler strcat(*posp, ".pos"); 826242687Seadler } 827242687Seadler else { 828242687Seadler *posp = NULL; 829242687Seadler } 8302490Sjkh } 8312490Sjkh DPRINTF(2, (stderr, "TRUE\n")); 832203926Suqs 833203926Suqs return (TRUE); 8342490Sjkh} 8352490Sjkh 8362490Sjkh/* 8372490Sjkh * copy: 8382490Sjkh * Return a malloc()'ed copy of the string 8392490Sjkh */ 8402490Sjkhchar * 841203922Suqscopy(const char *str, unsigned int len) 8422490Sjkh{ 843203926Suqs char *new, *sp; 8442490Sjkh 8452490Sjkh new = do_malloc(len + 1); 8462490Sjkh sp = new; 8472490Sjkh do { 8482490Sjkh *sp++ = *str; 8492490Sjkh } while (*str++); 850203926Suqs 851203926Suqs return (new); 8522490Sjkh} 8532490Sjkh 8542490Sjkh/* 8552490Sjkh * do_malloc: 8562490Sjkh * Do a malloc, checking for NULL return. 8572490Sjkh */ 8582490Sjkhvoid * 859203922Suqsdo_malloc(unsigned int size) 8602490Sjkh{ 861203926Suqs void *new; 8622490Sjkh 8632490Sjkh if ((new = malloc(size)) == NULL) { 8642490Sjkh (void) fprintf(stderr, "fortune: out of memory.\n"); 8652490Sjkh exit(1); 8662490Sjkh } 867203926Suqs 868203926Suqs return (new); 8692490Sjkh} 8702490Sjkh 8712490Sjkh/* 8722490Sjkh * do_free: 8732490Sjkh * Free malloc'ed space, if any. 8742490Sjkh */ 8752490Sjkhvoid 876203922Suqsdo_free(void *ptr) 8772490Sjkh{ 8782490Sjkh if (ptr != NULL) 8792490Sjkh free(ptr); 8802490Sjkh} 8812490Sjkh 8822490Sjkh/* 8832490Sjkh * init_prob: 8842490Sjkh * Initialize the fortune probabilities. 8852490Sjkh */ 8862490Sjkhvoid 887203922Suqsinit_prob(void) 8882490Sjkh{ 88953210Sbillf FILEDESC *fp, *last = NULL; 89053210Sbillf int percent, num_noprob, frac; 8912490Sjkh 8922490Sjkh /* 8932490Sjkh * Distribute the residual probability (if any) across all 8942490Sjkh * files with unspecified probability (i.e., probability of 0) 8952490Sjkh * (if any). 8962490Sjkh */ 8972490Sjkh 8982490Sjkh percent = 0; 8992490Sjkh num_noprob = 0; 9002490Sjkh for (fp = File_tail; fp != NULL; fp = fp->prev) 9012490Sjkh if (fp->percent == NO_PROB) { 9022490Sjkh num_noprob++; 9032490Sjkh if (Equal_probs) 9042490Sjkh last = fp; 905203926Suqs } else 9062490Sjkh percent += fp->percent; 9072490Sjkh DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's", 9082490Sjkh percent, num_noprob)); 9092490Sjkh if (percent > 100) { 9102490Sjkh (void) fprintf(stderr, 91115957Sache "fortune: probabilities sum to %d%% > 100%%!\n", percent); 9122490Sjkh exit(1); 913203926Suqs } else if (percent < 100 && num_noprob == 0) { 9142490Sjkh (void) fprintf(stderr, 91515957Sache "fortune: no place to put residual probability (%d%% < 100%%)\n", 9162490Sjkh percent); 9172490Sjkh exit(1); 918203926Suqs } else if (percent == 100 && num_noprob != 0) { 9192490Sjkh (void) fprintf(stderr, 92015957Sache "fortune: no probability left to put in residual files (100%%)\n"); 9212490Sjkh exit(1); 9222490Sjkh } 9232490Sjkh percent = 100 - percent; 92449040Sbillf if (Equal_probs) { 9252490Sjkh if (num_noprob != 0) { 9262490Sjkh if (num_noprob > 1) { 9272490Sjkh frac = percent / num_noprob; 9282490Sjkh DPRINTF(1, (stderr, ", frac = %d%%", frac)); 929173396Sedwin for (fp = File_tail; fp != last; fp = fp->prev) 9302490Sjkh if (fp->percent == NO_PROB) { 9312490Sjkh fp->percent = frac; 9322490Sjkh percent -= frac; 9332490Sjkh } 9342490Sjkh } 9352490Sjkh last->percent = percent; 9362490Sjkh DPRINTF(1, (stderr, ", residual = %d%%", percent)); 9372490Sjkh } 938203922Suqs else 939203926Suqs DPRINTF(1, (stderr, 9402490Sjkh ", %d%% distributed over remaining fortunes\n", 9412490Sjkh percent)); 9422490Sjkh } 9432490Sjkh DPRINTF(1, (stderr, "\n")); 9442490Sjkh 9452490Sjkh#ifdef DEBUG 9462490Sjkh if (Debug >= 1) 9472490Sjkh print_file_list(); 9482490Sjkh#endif 9492490Sjkh} 9502490Sjkh 9512490Sjkh/* 9522490Sjkh * get_fort: 9532490Sjkh * Get the fortune data file's seek pointer for the next fortune. 9542490Sjkh */ 9552490Sjkhvoid 956203922Suqsget_fort(void) 9572490Sjkh{ 95853210Sbillf FILEDESC *fp; 95953210Sbillf int choice; 9602490Sjkh 9612490Sjkh if (File_list->next == NULL || File_list->percent == NO_PROB) 9622490Sjkh fp = File_list; 9632490Sjkh else { 964181385Sache choice = arc4random_uniform(100); 9652490Sjkh DPRINTF(1, (stderr, "choice = %d\n", choice)); 9662490Sjkh for (fp = File_list; fp->percent != NO_PROB; fp = fp->next) 9672490Sjkh if (choice < fp->percent) 9682490Sjkh break; 9692490Sjkh else { 9702490Sjkh choice -= fp->percent; 9712490Sjkh DPRINTF(1, (stderr, 9722490Sjkh " skip \"%s\", %d%% (choice = %d)\n", 9732490Sjkh fp->name, fp->percent, choice)); 9742490Sjkh } 9752490Sjkh DPRINTF(1, (stderr, 9762490Sjkh "using \"%s\", %d%% (choice = %d)\n", 9772490Sjkh fp->name, fp->percent, choice)); 9782490Sjkh } 9792490Sjkh if (fp->percent != NO_PROB) 9802490Sjkh get_tbl(fp); 9812490Sjkh else { 9822490Sjkh if (fp->next != NULL) { 9832490Sjkh sum_noprobs(fp); 984181385Sache choice = arc4random_uniform(Noprob_tbl.str_numstr); 985142022Sru DPRINTF(1, (stderr, "choice = %d (of %u) \n", choice, 9862490Sjkh Noprob_tbl.str_numstr)); 987203922Suqs while ((unsigned int)choice >= fp->tbl.str_numstr) { 9882490Sjkh choice -= fp->tbl.str_numstr; 9892490Sjkh fp = fp->next; 9902490Sjkh DPRINTF(1, (stderr, 991142022Sru " skip \"%s\", %u (choice = %d)\n", 9922490Sjkh fp->name, fp->tbl.str_numstr, 9932490Sjkh choice)); 9942490Sjkh } 995142022Sru DPRINTF(1, (stderr, "using \"%s\", %u\n", fp->name, 9962490Sjkh fp->tbl.str_numstr)); 9972490Sjkh } 9982490Sjkh get_tbl(fp); 9992490Sjkh } 10002490Sjkh if (fp->child != NULL) { 10012490Sjkh DPRINTF(1, (stderr, "picking child\n")); 10022490Sjkh fp = pick_child(fp); 10032490Sjkh } 10042490Sjkh Fortfile = fp; 10052490Sjkh get_pos(fp); 10062490Sjkh open_dat(fp); 1007203926Suqs lseek(fp->datfd, 1008203926Suqs (off_t) (sizeof fp->tbl + fp->pos * sizeof Seekpts[0]), SEEK_SET); 10092490Sjkh read(fp->datfd, Seekpts, sizeof Seekpts); 1010142022Sru Seekpts[0] = be64toh(Seekpts[0]); 1011142022Sru Seekpts[1] = be64toh(Seekpts[1]); 10122490Sjkh} 10132490Sjkh 10142490Sjkh/* 10152490Sjkh * pick_child 10162490Sjkh * Pick a child from a chosen parent. 10172490Sjkh */ 10182490SjkhFILEDESC * 1019203922Suqspick_child(FILEDESC *parent) 10202490Sjkh{ 102153210Sbillf FILEDESC *fp; 102253210Sbillf int choice; 10232490Sjkh 10242490Sjkh if (Equal_probs) { 1025181385Sache choice = arc4random_uniform(parent->num_children); 10262490Sjkh DPRINTF(1, (stderr, " choice = %d (of %d)\n", 10272490Sjkh choice, parent->num_children)); 10282490Sjkh for (fp = parent->child; choice--; fp = fp->next) 10292490Sjkh continue; 10302490Sjkh DPRINTF(1, (stderr, " using %s\n", fp->name)); 1031203926Suqs return (fp); 10322490Sjkh } 10332490Sjkh else { 10342490Sjkh get_tbl(parent); 1035181385Sache choice = arc4random_uniform(parent->tbl.str_numstr); 1036142022Sru DPRINTF(1, (stderr, " choice = %d (of %u)\n", 10372490Sjkh choice, parent->tbl.str_numstr)); 1038203922Suqs for (fp = parent->child; (unsigned)choice >= fp->tbl.str_numstr; 10392490Sjkh fp = fp->next) { 10402490Sjkh choice -= fp->tbl.str_numstr; 1041142022Sru DPRINTF(1, (stderr, "\tskip %s, %u (choice = %d)\n", 10422490Sjkh fp->name, fp->tbl.str_numstr, choice)); 10432490Sjkh } 1044142022Sru DPRINTF(1, (stderr, " using %s, %u\n", fp->name, 10452490Sjkh fp->tbl.str_numstr)); 1046203926Suqs return (fp); 10472490Sjkh } 10482490Sjkh} 10492490Sjkh 10502490Sjkh/* 10512490Sjkh * sum_noprobs: 10522490Sjkh * Sum up all the noprob probabilities, starting with fp. 10532490Sjkh */ 10542490Sjkhvoid 1055203922Suqssum_noprobs(FILEDESC *fp) 10562490Sjkh{ 10572490Sjkh static bool did_noprobs = FALSE; 10582490Sjkh 10592490Sjkh if (did_noprobs) 10602490Sjkh return; 10612490Sjkh zero_tbl(&Noprob_tbl); 10622490Sjkh while (fp != NULL) { 10632490Sjkh get_tbl(fp); 10642490Sjkh sum_tbl(&Noprob_tbl, &fp->tbl); 10652490Sjkh fp = fp->next; 10662490Sjkh } 10672490Sjkh did_noprobs = TRUE; 10682490Sjkh} 10692490Sjkh 10702490Sjkhint 1071203922Suqsmax(int i, int j) 10722490Sjkh{ 10732490Sjkh return (i >= j ? i : j); 10742490Sjkh} 10752490Sjkh 10762490Sjkh/* 10772490Sjkh * open_fp: 10782490Sjkh * Assocatiate a FILE * with the given FILEDESC. 10792490Sjkh */ 10802490Sjkhvoid 1081203922Suqsopen_fp(FILEDESC *fp) 10822490Sjkh{ 10832490Sjkh if (fp->inf == NULL && (fp->inf = fdopen(fp->fd, "r")) == NULL) { 10842490Sjkh perror(fp->path); 10852490Sjkh exit(1); 10862490Sjkh } 10872490Sjkh} 10882490Sjkh 10892490Sjkh/* 10902490Sjkh * open_dat: 10912490Sjkh * Open up the dat file if we need to. 10922490Sjkh */ 10932490Sjkhvoid 1094203922Suqsopen_dat(FILEDESC *fp) 10952490Sjkh{ 1096203926Suqs if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, O_RDONLY)) < 0) { 10972490Sjkh perror(fp->datfile); 10982490Sjkh exit(1); 10992490Sjkh } 11002490Sjkh} 11012490Sjkh 11022490Sjkh/* 11032490Sjkh * get_pos: 11042490Sjkh * Get the position from the pos file, if there is one. If not, 11052490Sjkh * return a random number. 11062490Sjkh */ 11072490Sjkhvoid 1108203922Suqsget_pos(FILEDESC *fp) 11092490Sjkh{ 11102490Sjkh int fd; 11112490Sjkh 11122490Sjkh assert(fp->read_tbl); 11132490Sjkh if (fp->pos == POS_UNKNOWN) { 1114242687Seadler if (WriteToDisk) { 1115242687Seadler if ((fd = open(fp->posfile, O_RDONLY)) < 0 || 1116242687Seadler read(fd, &fp->pos, sizeof fp->pos) != sizeof fp->pos) 1117242687Seadler fp->pos = arc4random_uniform(fp->tbl.str_numstr); 1118242687Seadler else if (fp->pos >= fp->tbl.str_numstr) 1119242687Seadler fp->pos %= fp->tbl.str_numstr; 1120242687Seadler if (fd >= 0) 1121242687Seadler close(fd); 1122242687Seadler } 1123242687Seadler else 1124181385Sache fp->pos = arc4random_uniform(fp->tbl.str_numstr); 11252490Sjkh } 11262490Sjkh if (++(fp->pos) >= fp->tbl.str_numstr) 11272490Sjkh fp->pos -= fp->tbl.str_numstr; 1128142022Sru DPRINTF(1, (stderr, "pos for %s is %ld\n", fp->name, (long)fp->pos)); 11292490Sjkh} 11302490Sjkh 11312490Sjkh/* 11322490Sjkh * get_tbl: 11332490Sjkh * Get the tbl data file the datfile. 11342490Sjkh */ 11352490Sjkhvoid 1136203922Suqsget_tbl(FILEDESC *fp) 11372490Sjkh{ 1138203926Suqs int fd; 113953210Sbillf FILEDESC *child; 11402490Sjkh 11412490Sjkh if (fp->read_tbl) 11422490Sjkh return; 11432490Sjkh if (fp->child == NULL) { 1144203926Suqs if ((fd = open(fp->datfile, O_RDONLY)) < 0) { 11452490Sjkh perror(fp->datfile); 11462490Sjkh exit(1); 11472490Sjkh } 11482490Sjkh if (read(fd, (char *) &fp->tbl, sizeof fp->tbl) != sizeof fp->tbl) { 11492490Sjkh (void)fprintf(stderr, 11502490Sjkh "fortune: %s corrupted\n", fp->path); 11512490Sjkh exit(1); 11522490Sjkh } 1153142022Sru /* fp->tbl.str_version = be32toh(fp->tbl.str_version); */ 1154142022Sru fp->tbl.str_numstr = be32toh(fp->tbl.str_numstr); 1155142022Sru fp->tbl.str_longlen = be32toh(fp->tbl.str_longlen); 1156142022Sru fp->tbl.str_shortlen = be32toh(fp->tbl.str_shortlen); 1157142022Sru fp->tbl.str_flags = be32toh(fp->tbl.str_flags); 11582490Sjkh (void) close(fd); 11592490Sjkh } 11602490Sjkh else { 11612490Sjkh zero_tbl(&fp->tbl); 11622490Sjkh for (child = fp->child; child != NULL; child = child->next) { 11632490Sjkh get_tbl(child); 11642490Sjkh sum_tbl(&fp->tbl, &child->tbl); 11652490Sjkh } 11662490Sjkh } 11672490Sjkh fp->read_tbl = TRUE; 11682490Sjkh} 11692490Sjkh 11702490Sjkh/* 11712490Sjkh * zero_tbl: 11722490Sjkh * Zero out the fields we care about in a tbl structure. 11732490Sjkh */ 11742490Sjkhvoid 1175203922Suqszero_tbl(STRFILE *tp) 11762490Sjkh{ 11772490Sjkh tp->str_numstr = 0; 11782490Sjkh tp->str_longlen = 0; 1179142022Sru tp->str_shortlen = ~0; 11802490Sjkh} 11812490Sjkh 11822490Sjkh/* 11832490Sjkh * sum_tbl: 11842490Sjkh * Merge the tbl data of t2 into t1. 11852490Sjkh */ 11862490Sjkhvoid 1187203922Suqssum_tbl(STRFILE *t1, STRFILE *t2) 11882490Sjkh{ 11892490Sjkh t1->str_numstr += t2->str_numstr; 11902490Sjkh if (t1->str_longlen < t2->str_longlen) 11912490Sjkh t1->str_longlen = t2->str_longlen; 11922490Sjkh if (t1->str_shortlen > t2->str_shortlen) 11932490Sjkh t1->str_shortlen = t2->str_shortlen; 11942490Sjkh} 11952490Sjkh 11962490Sjkh#define STR(str) ((str) == NULL ? "NULL" : (str)) 11972490Sjkh 11982490Sjkh/* 11992490Sjkh * print_file_list: 12002490Sjkh * Print out the file list 12012490Sjkh */ 12022490Sjkhvoid 1203203922Suqsprint_file_list(void) 12042490Sjkh{ 12052490Sjkh print_list(File_list, 0); 12062490Sjkh} 12072490Sjkh 12082490Sjkh/* 12092490Sjkh * print_list: 12102490Sjkh * Print out the actual list, recursively. 12112490Sjkh */ 12122490Sjkhvoid 1213203922Suqsprint_list(FILEDESC *list, int lev) 12142490Sjkh{ 12152490Sjkh while (list != NULL) { 12162490Sjkh fprintf(stderr, "%*s", lev * 4, ""); 12172490Sjkh if (list->percent == NO_PROB) 12182490Sjkh fprintf(stderr, "___%%"); 12192490Sjkh else 12202490Sjkh fprintf(stderr, "%3d%%", list->percent); 12212490Sjkh fprintf(stderr, " %s", STR(list->name)); 122215957Sache DPRINTF(1, (stderr, " (%s, %s, %s)", STR(list->path), 12232490Sjkh STR(list->datfile), STR(list->posfile))); 122415957Sache fprintf(stderr, "\n"); 12252490Sjkh if (list->child != NULL) 12262490Sjkh print_list(list->child, lev + 1); 12272490Sjkh list = list->next; 12282490Sjkh } 12292490Sjkh} 12302490Sjkh 12312490Sjkh/* 12322490Sjkh * conv_pat: 12332490Sjkh * Convert the pattern to an ignore-case equivalent. 12342490Sjkh */ 12352490Sjkhchar * 1236203922Suqsconv_pat(char *orig) 12372490Sjkh{ 123853210Sbillf char *sp; 123953210Sbillf unsigned int cnt; 124053210Sbillf char *new; 12412490Sjkh 12422490Sjkh cnt = 1; /* allow for '\0' */ 12432490Sjkh for (sp = orig; *sp != '\0'; sp++) 124415944Sache if (isalpha((unsigned char)*sp)) 12452490Sjkh cnt += 4; 12462490Sjkh else 12472490Sjkh cnt++; 12482490Sjkh if ((new = malloc(cnt)) == NULL) { 12492490Sjkh fprintf(stderr, "pattern too long for ignoring case\n"); 12502490Sjkh exit(1); 12512490Sjkh } 12522490Sjkh 12532490Sjkh for (sp = new; *orig != '\0'; orig++) { 125415944Sache if (islower((unsigned char)*orig)) { 12552490Sjkh *sp++ = '['; 12562490Sjkh *sp++ = *orig; 125715944Sache *sp++ = toupper((unsigned char)*orig); 12582490Sjkh *sp++ = ']'; 12592490Sjkh } 126015944Sache else if (isupper((unsigned char)*orig)) { 12612490Sjkh *sp++ = '['; 12622490Sjkh *sp++ = *orig; 126315944Sache *sp++ = tolower((unsigned char)*orig); 12642490Sjkh *sp++ = ']'; 12652490Sjkh } 12662490Sjkh else 12672490Sjkh *sp++ = *orig; 12682490Sjkh } 12692490Sjkh *sp = '\0'; 1270203926Suqs 1271203926Suqs return (new); 12722490Sjkh} 12732490Sjkh 12742490Sjkh/* 12752490Sjkh * find_matches: 12762490Sjkh * Find all the fortunes which match the pattern we've been given. 12772490Sjkh */ 12782490Sjkhint 1279203922Suqsfind_matches(void) 12802490Sjkh{ 12812490Sjkh Fort_len = maxlen_in_list(File_list); 12822490Sjkh DPRINTF(2, (stderr, "Maximum length is %d\n", Fort_len)); 12832490Sjkh /* extra length, "%\n" is appended */ 12842490Sjkh Fortbuf = do_malloc((unsigned int) Fort_len + 10); 12852490Sjkh 12862490Sjkh Found_one = FALSE; 12872490Sjkh matches_in_list(File_list); 1288203926Suqs 1289203926Suqs return (Found_one); 12902490Sjkh} 12912490Sjkh 12922490Sjkh/* 12932490Sjkh * maxlen_in_list 12942490Sjkh * Return the maximum fortune len in the file list. 12952490Sjkh */ 12962490Sjkhint 1297203922Suqsmaxlen_in_list(FILEDESC *list) 12982490Sjkh{ 129953210Sbillf FILEDESC *fp; 130053210Sbillf int len, maxlen; 13012490Sjkh 13022490Sjkh maxlen = 0; 13032490Sjkh for (fp = list; fp != NULL; fp = fp->next) { 13042490Sjkh if (fp->child != NULL) { 13052490Sjkh if ((len = maxlen_in_list(fp->child)) > maxlen) 13062490Sjkh maxlen = len; 13072490Sjkh } 13082490Sjkh else { 13092490Sjkh get_tbl(fp); 1310203926Suqs if (fp->tbl.str_longlen > (unsigned int)maxlen) 13112490Sjkh maxlen = fp->tbl.str_longlen; 13122490Sjkh } 13132490Sjkh } 1314203926Suqs 1315203926Suqs return (maxlen); 13162490Sjkh} 13172490Sjkh 13182490Sjkh/* 13192490Sjkh * matches_in_list 13202490Sjkh * Print out the matches from the files in the list. 13212490Sjkh */ 13222490Sjkhvoid 1323203922Suqsmatches_in_list(FILEDESC *list) 13242490Sjkh{ 132553210Sbillf char *sp, *p; 132653210Sbillf FILEDESC *fp; 1327203926Suqs int in_file; 1328203926Suqs unsigned char ch; 13292490Sjkh 13302490Sjkh for (fp = list; fp != NULL; fp = fp->next) { 13312490Sjkh if (fp->child != NULL) { 13322490Sjkh matches_in_list(fp->child); 13332490Sjkh continue; 13342490Sjkh } 13352490Sjkh DPRINTF(1, (stderr, "searching in %s\n", fp->path)); 13362490Sjkh open_fp(fp); 13372490Sjkh sp = Fortbuf; 13382490Sjkh in_file = FALSE; 13392490Sjkh while (fgets(sp, Fort_len, fp->inf) != NULL) 134051863Sdcs if (fp->tbl.str_flags & STR_COMMENTS 134151863Sdcs && sp[0] == fp->tbl.str_delim 134251863Sdcs && sp[1] == fp->tbl.str_delim) 134351863Sdcs continue; 134451863Sdcs else if (!STR_ENDSTRING(sp, fp->tbl)) 13452490Sjkh sp += strlen(sp); 13462490Sjkh else { 13472490Sjkh *sp = '\0'; 134815944Sache if (fp->tbl.str_flags & STR_ROTATED) 134915944Sache for (p = Fortbuf; (ch = *p) != '\0'; ++p) { 135015944Sache if (isascii(ch)) { 135115944Sache if (isupper(ch)) 135215944Sache *p = 'A' + (ch - 'A' + 13) % 26; 135315944Sache else if (islower(ch)) 135415944Sache *p = 'a' + (ch - 'a' + 13) % 26; 135515944Sache } 135615944Sache } 1357130851Sphk if (regexec(&Re_pat, Fortbuf, 0, NULL, 0) != REG_NOMATCH) { 13582490Sjkh printf("%c%c", fp->tbl.str_delim, 13592490Sjkh fp->tbl.str_delim); 13602490Sjkh if (!in_file) { 13612490Sjkh printf(" (%s)", fp->name); 13622490Sjkh Found_one = TRUE; 13632490Sjkh in_file = TRUE; 13642490Sjkh } 13652490Sjkh putchar('\n'); 13662490Sjkh (void) fwrite(Fortbuf, 1, (sp - Fortbuf), stdout); 13672490Sjkh } 13682490Sjkh sp = Fortbuf; 13692490Sjkh } 13702490Sjkh } 13712490Sjkh} 13722490Sjkh 13732490Sjkhvoid 1374203922Suqsusage(void) 13752490Sjkh{ 13762490Sjkh (void) fprintf(stderr, "fortune [-a"); 13772490Sjkh#ifdef DEBUG 13782490Sjkh (void) fprintf(stderr, "D"); 13792490Sjkh#endif /* DEBUG */ 1380141581Sru (void) fprintf(stderr, "efilosw]"); 13812490Sjkh (void) fprintf(stderr, " [-m pattern]"); 1382141581Sru (void) fprintf(stderr, " [[N%%] file/directory/all]\n"); 13832490Sjkh exit(1); 13842490Sjkh} 1385173396Sedwin 1386173396Sedwin/* 1387173396Sedwin * getpath 1388173396Sedwin * Set up file search patch from environment var FORTUNE_PATH; 1389173396Sedwin * if not set, use the compiled in FORTDIR. 1390173396Sedwin */ 1391173396Sedwin 1392173396Sedwinvoid 1393173396Sedwingetpath(void) 1394173396Sedwin{ 1395173401Sedwin int nstr, foundenv; 1396173401Sedwin char *pch, **ppch, *str, *path; 1397173396Sedwin 1398173401Sedwin foundenv = 1; 1399173396Sedwin Fortune_path = getenv("FORTUNE_PATH"); 1400173401Sedwin if (Fortune_path == NULL) { 1401173401Sedwin Fortune_path = FORTDIR; 1402173401Sedwin foundenv = 0; 1403173401Sedwin } 1404173396Sedwin path = strdup(Fortune_path); 1405173396Sedwin 1406173396Sedwin for (nstr = 2, pch = path; *pch != '\0'; pch++) { 1407173396Sedwin if (*pch == ':') 1408173396Sedwin nstr++; 1409173396Sedwin } 1410173396Sedwin 1411173396Sedwin ppch = Fortune_path_arr = (char **)calloc(nstr, sizeof(char *)); 1412173396Sedwin 1413173396Sedwin nstr = 0; 1414173396Sedwin str = strtok(path, ":"); 1415173396Sedwin while (str) { 1416173396Sedwin if (is_dir(str)) { 1417173396Sedwin nstr++; 1418173396Sedwin *ppch++ = str; 1419173396Sedwin } 1420173396Sedwin str = strtok(NULL, ":"); 1421173396Sedwin } 1422173401Sedwin 1423173396Sedwin if (nstr == 0) { 1424173401Sedwin if (foundenv == 1) { 1425173401Sedwin fprintf(stderr, 1426173401Sedwin "fortune: FORTUNE_PATH: None of the specified " 1427173401Sedwin "directories found.\n"); 1428173401Sedwin exit(1); 1429173401Sedwin } 1430173396Sedwin free(path); 1431203922Suqs Fortune_path_arr[0] = strdup(FORTDIR); 1432173396Sedwin } 1433173396Sedwin} 1434