| File: | src/games/fortune/fortune/fortune.c |
| Warning: | line 474, column 3 Attempt to free released memory |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: fortune.c,v 1.63 2021/01/03 01:32:13 schwarze Exp $ */ | ||||||
| 2 | /* $NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $ */ | ||||||
| 3 | |||||||
| 4 | /*- | ||||||
| 5 | * Copyright (c) 1986, 1993 | ||||||
| 6 | * The Regents of the University of California. All rights reserved. | ||||||
| 7 | * | ||||||
| 8 | * This code is derived from software contributed to Berkeley by | ||||||
| 9 | * Ken Arnold. | ||||||
| 10 | * | ||||||
| 11 | * Redistribution and use in source and binary forms, with or without | ||||||
| 12 | * modification, are permitted provided that the following conditions | ||||||
| 13 | * are met: | ||||||
| 14 | * 1. Redistributions of source code must retain the above copyright | ||||||
| 15 | * notice, this list of conditions and the following disclaimer. | ||||||
| 16 | * 2. Redistributions in binary form must reproduce the above copyright | ||||||
| 17 | * notice, this list of conditions and the following disclaimer in the | ||||||
| 18 | * documentation and/or other materials provided with the distribution. | ||||||
| 19 | * 3. Neither the name of the University nor the names of its contributors | ||||||
| 20 | * may be used to endorse or promote products derived from this software | ||||||
| 21 | * without specific prior written permission. | ||||||
| 22 | * | ||||||
| 23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||||||
| 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
| 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
| 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||||||
| 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
| 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||
| 29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
| 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||
| 31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||
| 32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||
| 33 | * SUCH DAMAGE. | ||||||
| 34 | */ | ||||||
| 35 | |||||||
| 36 | #include <sys/stat.h> | ||||||
| 37 | |||||||
| 38 | #include <assert.h> | ||||||
| 39 | #include <ctype.h> | ||||||
| 40 | #include <dirent.h> | ||||||
| 41 | #include <err.h> | ||||||
| 42 | #include <fcntl.h> | ||||||
| 43 | #include <limits.h> | ||||||
| 44 | #include <locale.h> | ||||||
| 45 | #include <stdbool.h> | ||||||
| 46 | #include <stdio.h> | ||||||
| 47 | #include <stdlib.h> | ||||||
| 48 | #include <string.h> | ||||||
| 49 | #include <regex.h> | ||||||
| 50 | #include <unistd.h> | ||||||
| 51 | |||||||
| 52 | #include "pathnames.h" | ||||||
| 53 | #include "strfile.h" | ||||||
| 54 | |||||||
| 55 | #define MINW6 6 /* minimum wait if desired */ | ||||||
| 56 | #define CPERS20 20 /* # of chars for each sec */ | ||||||
| 57 | #define SLEN160 160 /* # of chars in short fortune */ | ||||||
| 58 | |||||||
| 59 | #define POS_UNKNOWN((int32_t) -1) ((int32_t) -1) /* pos for file unknown */ | ||||||
| 60 | #define NO_PROB(-1) (-1) /* no prob specified for file */ | ||||||
| 61 | |||||||
| 62 | #ifdef DEBUG | ||||||
| 63 | #define DPRINTF(l,x) if (Debug >= l) fprintf x; else | ||||||
| 64 | #undef NDEBUG1 | ||||||
| 65 | #else | ||||||
| 66 | #define DPRINTF(l,x) | ||||||
| 67 | #define NDEBUG1 1 | ||||||
| 68 | #endif | ||||||
| 69 | |||||||
| 70 | typedef struct fd { | ||||||
| 71 | int percent; | ||||||
| 72 | int fd, datfd; | ||||||
| 73 | int32_t pos; | ||||||
| 74 | FILE *inf; | ||||||
| 75 | char *name; | ||||||
| 76 | char *path; | ||||||
| 77 | char *datfile; | ||||||
| 78 | bool_Bool read_tbl; | ||||||
| 79 | STRFILE tbl; | ||||||
| 80 | int num_children; | ||||||
| 81 | struct fd *child, *parent; | ||||||
| 82 | struct fd *next, *prev; | ||||||
| 83 | } FILEDESC; | ||||||
| 84 | |||||||
| 85 | bool_Bool Found_one = false0; /* did we find a match? */ | ||||||
| 86 | bool_Bool Find_files = false0; /* display a list of fortune files */ | ||||||
| 87 | bool_Bool Wait = false0; /* wait desired after fortune */ | ||||||
| 88 | bool_Bool Short_only = false0; /* short fortune desired */ | ||||||
| 89 | bool_Bool Long_only = false0; /* long fortune desired */ | ||||||
| 90 | bool_Bool Offend = false0; /* offensive fortunes only */ | ||||||
| 91 | bool_Bool All_forts = false0; /* any fortune allowed */ | ||||||
| 92 | bool_Bool Equal_probs = false0; /* scatter un-allocted prob equally */ | ||||||
| 93 | bool_Bool Match = false0; /* dump fortunes matching a pattern */ | ||||||
| 94 | #ifdef DEBUG | ||||||
| 95 | int Debug = 0; /* print debug messages */ | ||||||
| 96 | #endif | ||||||
| 97 | |||||||
| 98 | char *Fortbuf = NULL((void *)0); /* fortune buffer for -m */ | ||||||
| 99 | |||||||
| 100 | size_t Fort_len = 0; | ||||||
| 101 | |||||||
| 102 | int32_t Seekpts[2]; /* seek pointers to fortunes */ | ||||||
| 103 | |||||||
| 104 | FILEDESC *File_list = NULL((void *)0), /* Head of file list */ | ||||||
| 105 | *File_tail = NULL((void *)0); /* Tail of file list */ | ||||||
| 106 | FILEDESC *Fortfile; /* Fortune file to use */ | ||||||
| 107 | |||||||
| 108 | STRFILE Noprob_tbl; /* sum of data for all no prob files */ | ||||||
| 109 | |||||||
| 110 | int add_dir(FILEDESC *); | ||||||
| 111 | int add_file(int, | ||||||
| 112 | char *, char *, FILEDESC **, FILEDESC **, FILEDESC *); | ||||||
| 113 | void all_forts(FILEDESC *, char *); | ||||||
| 114 | char *copy(char *, char *); | ||||||
| 115 | void display(FILEDESC *); | ||||||
| 116 | int form_file_list(char **, int); | ||||||
| 117 | int fortlen(void); | ||||||
| 118 | void get_fort(void); | ||||||
| 119 | void get_pos(FILEDESC *); | ||||||
| 120 | void get_tbl(FILEDESC *); | ||||||
| 121 | void getargs(int, char *[]); | ||||||
| 122 | void init_prob(void); | ||||||
| 123 | int is_dir(char *); | ||||||
| 124 | int is_fortfile(char *, char **, int); | ||||||
| 125 | int is_off_name(char *); | ||||||
| 126 | int max(int, int); | ||||||
| 127 | FILEDESC * | ||||||
| 128 | new_fp(void); | ||||||
| 129 | char *off_name(char *); | ||||||
| 130 | void open_dat(FILEDESC *); | ||||||
| 131 | void open_fp(FILEDESC *); | ||||||
| 132 | FILEDESC * | ||||||
| 133 | pick_child(FILEDESC *); | ||||||
| 134 | void print_file_list(void); | ||||||
| 135 | void print_list(FILEDESC *, int); | ||||||
| 136 | void rot13(char *, size_t); | ||||||
| 137 | void sanitize(unsigned char *cp); | ||||||
| 138 | void sum_noprobs(FILEDESC *); | ||||||
| 139 | void sum_tbl(STRFILE *, STRFILE *); | ||||||
| 140 | __dead__attribute__((__noreturn__)) void usage(void); | ||||||
| 141 | void zero_tbl(STRFILE *); | ||||||
| 142 | |||||||
| 143 | char *conv_pat(char *); | ||||||
| 144 | int find_matches(void); | ||||||
| 145 | void matches_in_list(FILEDESC *); | ||||||
| 146 | int maxlen_in_list(FILEDESC *); | ||||||
| 147 | int minlen_in_list(FILEDESC *); | ||||||
| 148 | regex_t regex; | ||||||
| 149 | |||||||
| 150 | int | ||||||
| 151 | main(int ac, char *av[]) | ||||||
| 152 | { | ||||||
| 153 | setlocale(LC_CTYPE2, ""); | ||||||
| 154 | |||||||
| 155 | if (pledge("stdio rpath", NULL((void *)0)) == -1) { | ||||||
| |||||||
| 156 | perror("pledge"); | ||||||
| 157 | return 1; | ||||||
| 158 | } | ||||||
| 159 | |||||||
| 160 | getargs(ac, av); | ||||||
| 161 | |||||||
| 162 | if (Match) | ||||||
| 163 | return find_matches() == 0; | ||||||
| 164 | |||||||
| 165 | init_prob(); | ||||||
| 166 | if ((Short_only && minlen_in_list(File_list) > SLEN160) || | ||||||
| 167 | (Long_only && maxlen_in_list(File_list) <= SLEN160)) | ||||||
| 168 | return 1; | ||||||
| 169 | |||||||
| 170 | do { | ||||||
| 171 | get_fort(); | ||||||
| 172 | } while ((Short_only && fortlen() > SLEN160) || | ||||||
| 173 | (Long_only && fortlen() <= SLEN160)); | ||||||
| 174 | |||||||
| 175 | display(Fortfile); | ||||||
| 176 | |||||||
| 177 | if (Wait) { | ||||||
| 178 | if (Fort_len == 0) | ||||||
| 179 | (void) fortlen(); | ||||||
| 180 | sleep((unsigned int) max(Fort_len / CPERS20, MINW6)); | ||||||
| 181 | } | ||||||
| 182 | return 0; | ||||||
| 183 | } | ||||||
| 184 | |||||||
| 185 | void | ||||||
| 186 | rot13(char *p, size_t len) | ||||||
| 187 | { | ||||||
| 188 | while (len--) { | ||||||
| 189 | unsigned char ch = *p; | ||||||
| 190 | if (isupper(ch)) | ||||||
| 191 | *p = 'A' + (ch - 'A' + 13) % 26; | ||||||
| 192 | else if (islower(ch)) | ||||||
| 193 | *p = 'a' + (ch - 'a' + 13) % 26; | ||||||
| 194 | p++; | ||||||
| 195 | } | ||||||
| 196 | } | ||||||
| 197 | |||||||
| 198 | void | ||||||
| 199 | sanitize(unsigned char *cp) | ||||||
| 200 | { | ||||||
| 201 | if (MB_CUR_MAX__mb_cur_max() > 1) | ||||||
| 202 | return; | ||||||
| 203 | for (; *cp != '\0'; cp++) | ||||||
| 204 | if (!isprint(*cp) && !isspace(*cp) && *cp != '\b') | ||||||
| 205 | *cp = '?'; | ||||||
| 206 | } | ||||||
| 207 | |||||||
| 208 | void | ||||||
| 209 | display(FILEDESC *fp) | ||||||
| 210 | { | ||||||
| 211 | char line[BUFSIZ1024]; | ||||||
| 212 | |||||||
| 213 | open_fp(fp); | ||||||
| 214 | (void) fseek(fp->inf, (long)Seekpts[0], SEEK_SET0); | ||||||
| 215 | for (Fort_len = 0; fgets(line, sizeof line, fp->inf) != NULL((void *)0) && | ||||||
| 216 | !STR_ENDSTRING(line, fp->tbl)((line)[0] == (fp->tbl).stuff[0] && (line)[1] == '\n' ); Fort_len++) { | ||||||
| 217 | if (fp->tbl.str_flags & STR_ROTATED0x4) | ||||||
| 218 | rot13(line, strlen(line)); | ||||||
| 219 | sanitize(line); | ||||||
| 220 | fputs(line, stdout(&__sF[1])); | ||||||
| 221 | } | ||||||
| 222 | (void) fflush(stdout(&__sF[1])); | ||||||
| 223 | } | ||||||
| 224 | |||||||
| 225 | /* | ||||||
| 226 | * fortlen: | ||||||
| 227 | * Return the length of the fortune. | ||||||
| 228 | */ | ||||||
| 229 | int | ||||||
| 230 | fortlen(void) | ||||||
| 231 | { | ||||||
| 232 | size_t nchar; | ||||||
| 233 | char line[BUFSIZ1024]; | ||||||
| 234 | |||||||
| 235 | if (!(Fortfile->tbl.str_flags & (STR_RANDOM0x1 | STR_ORDERED0x2))) | ||||||
| 236 | nchar = Seekpts[1] - Seekpts[0]; | ||||||
| 237 | else { | ||||||
| 238 | open_fp(Fortfile); | ||||||
| 239 | (void) fseek(Fortfile->inf, (long)Seekpts[0], SEEK_SET0); | ||||||
| 240 | nchar = 0; | ||||||
| 241 | while (fgets(line, sizeof line, Fortfile->inf) != NULL((void *)0) && | ||||||
| 242 | !STR_ENDSTRING(line, Fortfile->tbl)((line)[0] == (Fortfile->tbl).stuff[0] && (line)[1 ] == '\n')) | ||||||
| 243 | nchar += strlen(line); | ||||||
| 244 | } | ||||||
| 245 | Fort_len = nchar; | ||||||
| 246 | return nchar; | ||||||
| 247 | } | ||||||
| 248 | |||||||
| 249 | /* | ||||||
| 250 | * This routine evaluates the arguments on the command line | ||||||
| 251 | */ | ||||||
| 252 | void | ||||||
| 253 | getargs(int argc, char *argv[]) | ||||||
| 254 | { | ||||||
| 255 | int ignore_case; | ||||||
| 256 | char *pat = NULL((void *)0); | ||||||
| 257 | int ch; | ||||||
| 258 | |||||||
| 259 | ignore_case = 0; | ||||||
| 260 | |||||||
| 261 | #ifdef DEBUG | ||||||
| 262 | while ((ch = getopt(argc, argv, "aDefhilm:osw")) != -1) | ||||||
| 263 | #else | ||||||
| 264 | while ((ch = getopt(argc, argv, "aefhilm:osw")) != -1) | ||||||
| 265 | #endif /* DEBUG */ | ||||||
| 266 | switch(ch) { | ||||||
| 267 | case 'a': /* any fortune */ | ||||||
| 268 | All_forts = true1; | ||||||
| 269 | break; | ||||||
| 270 | #ifdef DEBUG | ||||||
| 271 | case 'D': | ||||||
| 272 | Debug++; | ||||||
| 273 | break; | ||||||
| 274 | #endif /* DEBUG */ | ||||||
| 275 | case 'e': /* scatter un-allocted prob equally */ | ||||||
| 276 | Equal_probs = true1; | ||||||
| 277 | break; | ||||||
| 278 | case 'f': /* find fortune files */ | ||||||
| 279 | Find_files = true1; | ||||||
| 280 | break; | ||||||
| 281 | case 'l': /* long ones only */ | ||||||
| 282 | Long_only = true1; | ||||||
| 283 | Short_only = false0; | ||||||
| 284 | break; | ||||||
| 285 | case 'o': /* offensive ones only */ | ||||||
| 286 | Offend = true1; | ||||||
| 287 | break; | ||||||
| 288 | case 's': /* short ones only */ | ||||||
| 289 | Short_only = true1; | ||||||
| 290 | Long_only = false0; | ||||||
| 291 | break; | ||||||
| 292 | case 'w': /* give time to read */ | ||||||
| 293 | Wait = true1; | ||||||
| 294 | break; | ||||||
| 295 | case 'm': /* dump out the fortunes */ | ||||||
| 296 | Match = true1; | ||||||
| 297 | pat = optarg; | ||||||
| 298 | break; | ||||||
| 299 | case 'i': /* case-insensitive match */ | ||||||
| 300 | ignore_case = 1; | ||||||
| 301 | break; | ||||||
| 302 | case 'h': | ||||||
| 303 | default: | ||||||
| 304 | usage(); | ||||||
| 305 | } | ||||||
| 306 | argc -= optind; | ||||||
| 307 | argv += optind; | ||||||
| 308 | |||||||
| 309 | if (!form_file_list(argv, argc)) | ||||||
| 310 | exit(1); /* errors printed through form_file_list() */ | ||||||
| 311 | #ifdef DEBUG | ||||||
| 312 | if (Debug >= 1) | ||||||
| 313 | print_file_list(); | ||||||
| 314 | #endif /* DEBUG */ | ||||||
| 315 | if (Find_files) { | ||||||
| 316 | print_file_list(); | ||||||
| 317 | exit(0); | ||||||
| 318 | } | ||||||
| 319 | |||||||
| 320 | if (pat != NULL((void *)0)) { | ||||||
| 321 | if (regcomp(®ex, pat, ignore_case ? REG_ICASE0002 : 0)) | ||||||
| 322 | fprintf(stderr(&__sF[2]), "bad pattern: %s\n", pat); | ||||||
| 323 | } | ||||||
| 324 | } | ||||||
| 325 | |||||||
| 326 | /* | ||||||
| 327 | * form_file_list: | ||||||
| 328 | * Form the file list from the file specifications. | ||||||
| 329 | */ | ||||||
| 330 | int | ||||||
| 331 | form_file_list(char **files, int file_cnt) | ||||||
| 332 | { | ||||||
| 333 | int i, percent; | ||||||
| 334 | char *sp; | ||||||
| 335 | |||||||
| 336 | if (file_cnt == 0) { | ||||||
| 337 | if (Find_files) | ||||||
| 338 | return add_file(NO_PROB(-1), FORTDIR"/usr/share/games/fortune", NULL((void *)0), &File_list, | ||||||
| 339 | &File_tail, NULL((void *)0)); | ||||||
| 340 | else | ||||||
| 341 | return add_file(NO_PROB(-1), "fortunes", FORTDIR"/usr/share/games/fortune", | ||||||
| 342 | &File_list, &File_tail, NULL((void *)0)); | ||||||
| 343 | } | ||||||
| 344 | for (i = 0; i < file_cnt; i++) { | ||||||
| 345 | percent = NO_PROB(-1); | ||||||
| 346 | |||||||
| 347 | if (isdigit((unsigned char)files[i][0])) { | ||||||
| 348 | int pos = strspn(files[i], "0123456789."); | ||||||
| 349 | |||||||
| 350 | /* | ||||||
| 351 | * Only try to interpret files[i] as a percentage if | ||||||
| 352 | * it ends in '%'. Otherwise assume it's a file name. | ||||||
| 353 | */ | ||||||
| 354 | if (files[i][pos] == '%' && files[i][pos+1] == '\0') { | ||||||
| 355 | const char *errstr; | ||||||
| 356 | char *prefix; | ||||||
| 357 | |||||||
| 358 | if ((prefix = strndup(files[i], pos)) == NULL((void *)0)) | ||||||
| 359 | err(1, NULL((void *)0)); | ||||||
| 360 | if (strchr(prefix, '.') != NULL((void *)0)) | ||||||
| 361 | errx(1, "percentages must be integers"); | ||||||
| 362 | percent = strtonum(prefix, 0, 100, &errstr); | ||||||
| 363 | if (errstr != NULL((void *)0)) | ||||||
| 364 | errx(1, "percentage is %s: %s", errstr, | ||||||
| 365 | prefix); | ||||||
| 366 | free(prefix); | ||||||
| 367 | |||||||
| 368 | if (++i >= file_cnt) | ||||||
| 369 | errx(1, | ||||||
| 370 | "percentages must precede files"); | ||||||
| 371 | } | ||||||
| 372 | } | ||||||
| 373 | sp = files[i]; | ||||||
| 374 | if (strcmp(sp, "all") == 0) | ||||||
| 375 | sp = FORTDIR"/usr/share/games/fortune"; | ||||||
| 376 | if (!add_file(percent, sp, NULL((void *)0), &File_list, &File_tail, NULL((void *)0))) | ||||||
| 377 | return 0; | ||||||
| 378 | } | ||||||
| 379 | return 1; | ||||||
| 380 | } | ||||||
| 381 | |||||||
| 382 | /* | ||||||
| 383 | * add_file: | ||||||
| 384 | * Add a file to the file list. | ||||||
| 385 | */ | ||||||
| 386 | int | ||||||
| 387 | add_file(int percent, char *file, char *dir, FILEDESC **head, FILEDESC **tail, | ||||||
| 388 | FILEDESC *parent) | ||||||
| 389 | { | ||||||
| 390 | FILEDESC *fp; | ||||||
| 391 | int fd; | ||||||
| 392 | char *path, *offensive; | ||||||
| 393 | bool_Bool was_malloc; | ||||||
| 394 | bool_Bool isdir; | ||||||
| 395 | |||||||
| 396 | if (dir
| ||||||
| 397 | path = file; | ||||||
| 398 | was_malloc = false0; | ||||||
| 399 | } else { | ||||||
| 400 | if (asprintf(&path, "%s/%s", dir, file) == -1) | ||||||
| 401 | err(1, NULL((void *)0)); | ||||||
| 402 | was_malloc = true1; | ||||||
| 403 | } | ||||||
| 404 | if ((isdir = is_dir(path)) && parent
| ||||||
| 405 | if (was_malloc) | ||||||
| 406 | free(path); | ||||||
| 407 | return 0; /* don't recurse */ | ||||||
| 408 | } | ||||||
| 409 | offensive = NULL((void *)0); | ||||||
| 410 | if (!isdir
| ||||||
| 411 | !is_off_name(path)) { | ||||||
| 412 | offensive = off_name(path); | ||||||
| 413 | if (Offend) { | ||||||
| 414 | if (was_malloc
| ||||||
| 415 | free(path); | ||||||
| 416 | path = offensive; | ||||||
| 417 | file = off_name(file); | ||||||
| 418 | was_malloc = true1; | ||||||
| 419 | } | ||||||
| 420 | } | ||||||
| 421 | |||||||
| 422 | DPRINTF(1, (stderr, "adding file \"%s\"\n", path)); | ||||||
| 423 | over: | ||||||
| 424 | if ((fd = open(path, O_RDONLY0x0000)) < 0) { | ||||||
| 425 | /* | ||||||
| 426 | * This is a sneak. If the user said -a, and if the | ||||||
| 427 | * file we're given isn't a file, we check to see if | ||||||
| 428 | * there is a -o version. If there is, we treat it as | ||||||
| 429 | * if *that* were the file given. We only do this for | ||||||
| 430 | * individual files -- if we're scanning a directory, | ||||||
| 431 | * we'll pick up the -o file anyway. | ||||||
| 432 | */ | ||||||
| 433 | if (All_forts
| ||||||
| 434 | path = offensive; | ||||||
| 435 | if (was_malloc) | ||||||
| 436 | free(path); | ||||||
| 437 | offensive = NULL((void *)0); | ||||||
| 438 | was_malloc = true1; | ||||||
| 439 | DPRINTF(1, (stderr, "\ttrying \"%s\"\n", path)); | ||||||
| 440 | file = off_name(file); | ||||||
| 441 | goto over; | ||||||
| 442 | } | ||||||
| 443 | if (dir
| ||||||
| 444 | return add_file(percent, file, FORTDIR"/usr/share/games/fortune", head, tail, | ||||||
| 445 | parent); | ||||||
| 446 | if (parent == NULL((void *)0)) | ||||||
| 447 | perror(path); | ||||||
| 448 | if (was_malloc) | ||||||
| 449 | free(path); | ||||||
| 450 | return 0; | ||||||
| 451 | } | ||||||
| 452 | |||||||
| 453 | DPRINTF(2, (stderr, "path = \"%s\"\n", path)); | ||||||
| 454 | |||||||
| 455 | fp = new_fp(); | ||||||
| 456 | fp->fd = fd; | ||||||
| 457 | fp->percent = percent; | ||||||
| 458 | fp->name = file; | ||||||
| 459 | fp->path = path; | ||||||
| 460 | fp->parent = parent; | ||||||
| 461 | |||||||
| 462 | if ((isdir
| ||||||
| 463 | (!isdir
| ||||||
| 464 | !is_fortfile(path, &fp->datfile, (parent != NULL((void *)0))))) | ||||||
| 465 | { | ||||||
| 466 | if (parent
| ||||||
| 467 | fprintf(stderr(&__sF[2]), | ||||||
| 468 | "fortune: %s not a fortune file or directory\n", | ||||||
| 469 | path); | ||||||
| 470 | if (was_malloc
| ||||||
| 471 | free(path); | ||||||
| 472 | free(fp->datfile); | ||||||
| 473 | free((char *) fp); | ||||||
| 474 | free(offensive); | ||||||
| |||||||
| 475 | return 0; | ||||||
| 476 | } | ||||||
| 477 | /* | ||||||
| 478 | * If the user said -a, we need to make this node a pointer to | ||||||
| 479 | * both files, if there are two. We don't need to do this if | ||||||
| 480 | * we are scanning a directory, since the scan will pick up the | ||||||
| 481 | * -o file anyway. | ||||||
| 482 | */ | ||||||
| 483 | if (All_forts && parent == NULL((void *)0) && !is_off_name(path)) | ||||||
| 484 | all_forts(fp, offensive); | ||||||
| 485 | if (*head == NULL((void *)0)) | ||||||
| 486 | *head = *tail = fp; | ||||||
| 487 | else if (fp->percent == NO_PROB(-1)) { | ||||||
| 488 | (*tail)->next = fp; | ||||||
| 489 | fp->prev = *tail; | ||||||
| 490 | *tail = fp; | ||||||
| 491 | } | ||||||
| 492 | else { | ||||||
| 493 | (*head)->prev = fp; | ||||||
| 494 | fp->next = *head; | ||||||
| 495 | *head = fp; | ||||||
| 496 | } | ||||||
| 497 | |||||||
| 498 | return 1; | ||||||
| 499 | } | ||||||
| 500 | |||||||
| 501 | /* | ||||||
| 502 | * new_fp: | ||||||
| 503 | * Return a pointer to an initialized new FILEDESC. | ||||||
| 504 | */ | ||||||
| 505 | FILEDESC * | ||||||
| 506 | new_fp(void) | ||||||
| 507 | { | ||||||
| 508 | FILEDESC *fp; | ||||||
| 509 | |||||||
| 510 | if ((fp = malloc(sizeof *fp)) == NULL((void *)0)) | ||||||
| 511 | err(1, NULL((void *)0)); | ||||||
| 512 | fp->datfd = -1; | ||||||
| 513 | fp->pos = POS_UNKNOWN((int32_t) -1); | ||||||
| 514 | fp->inf = NULL((void *)0); | ||||||
| 515 | fp->fd = -1; | ||||||
| 516 | fp->percent = NO_PROB(-1); | ||||||
| 517 | fp->read_tbl = 0; | ||||||
| 518 | fp->next = NULL((void *)0); | ||||||
| 519 | fp->prev = NULL((void *)0); | ||||||
| 520 | fp->child = NULL((void *)0); | ||||||
| 521 | fp->parent = NULL((void *)0); | ||||||
| 522 | fp->datfile = NULL((void *)0); | ||||||
| 523 | return fp; | ||||||
| 524 | } | ||||||
| 525 | |||||||
| 526 | /* | ||||||
| 527 | * off_name: | ||||||
| 528 | * Return a pointer to the offensive version of a file of this name. | ||||||
| 529 | */ | ||||||
| 530 | char * | ||||||
| 531 | off_name(char *file) | ||||||
| 532 | { | ||||||
| 533 | return (copy(file, "-o")); | ||||||
| 534 | } | ||||||
| 535 | |||||||
| 536 | /* | ||||||
| 537 | * is_off_name: | ||||||
| 538 | * Is the file an offensive-style name? | ||||||
| 539 | */ | ||||||
| 540 | int | ||||||
| 541 | is_off_name(char *file) | ||||||
| 542 | { | ||||||
| 543 | int len; | ||||||
| 544 | |||||||
| 545 | len = strlen(file); | ||||||
| 546 | return (len >= 3 && file[len - 2] == '-' && file[len - 1] == 'o'); | ||||||
| 547 | } | ||||||
| 548 | |||||||
| 549 | /* | ||||||
| 550 | * all_forts: | ||||||
| 551 | * Modify a FILEDESC element to be the parent of two children if | ||||||
| 552 | * there are two children to be a parent of. | ||||||
| 553 | */ | ||||||
| 554 | void | ||||||
| 555 | all_forts(FILEDESC *fp, char *offensive) | ||||||
| 556 | { | ||||||
| 557 | char *sp; | ||||||
| 558 | FILEDESC *scene, *obscene; | ||||||
| 559 | int fd; | ||||||
| 560 | char *datfile; | ||||||
| 561 | |||||||
| 562 | if (fp->child != NULL((void *)0)) /* this is a directory, not a file */ | ||||||
| 563 | return; | ||||||
| 564 | if (!is_fortfile(offensive, &datfile, 0)) | ||||||
| 565 | return; | ||||||
| 566 | if ((fd = open(offensive, O_RDONLY0x0000)) < 0) | ||||||
| 567 | return; | ||||||
| 568 | DPRINTF(1, (stderr, "adding \"%s\" because of -a\n", offensive)); | ||||||
| 569 | scene = new_fp(); | ||||||
| 570 | obscene = new_fp(); | ||||||
| 571 | *scene = *fp; | ||||||
| 572 | |||||||
| 573 | fp->num_children = 2; | ||||||
| 574 | fp->child = scene; | ||||||
| 575 | scene->next = obscene; | ||||||
| 576 | obscene->next = NULL((void *)0); | ||||||
| 577 | scene->child = obscene->child = NULL((void *)0); | ||||||
| 578 | scene->parent = obscene->parent = fp; | ||||||
| 579 | |||||||
| 580 | fp->fd = -1; | ||||||
| 581 | scene->percent = obscene->percent = NO_PROB(-1); | ||||||
| 582 | |||||||
| 583 | obscene->fd = fd; | ||||||
| 584 | obscene->inf = NULL((void *)0); | ||||||
| 585 | obscene->path = offensive; | ||||||
| 586 | if ((sp = strrchr(offensive, '/')) == NULL((void *)0)) | ||||||
| 587 | obscene->name = offensive; | ||||||
| 588 | else | ||||||
| 589 | obscene->name = ++sp; | ||||||
| 590 | obscene->datfile = datfile; | ||||||
| 591 | obscene->read_tbl = 0; | ||||||
| 592 | } | ||||||
| 593 | |||||||
| 594 | /* | ||||||
| 595 | * add_dir: | ||||||
| 596 | * Add the contents of an entire directory. | ||||||
| 597 | */ | ||||||
| 598 | int | ||||||
| 599 | add_dir(FILEDESC *fp) | ||||||
| 600 | { | ||||||
| 601 | DIR *dir; | ||||||
| 602 | struct dirent *dirent; | ||||||
| 603 | FILEDESC *tailp; | ||||||
| 604 | char *name; | ||||||
| 605 | |||||||
| 606 | (void) close(fp->fd); | ||||||
| 607 | fp->fd = -1; | ||||||
| 608 | if ((dir = opendir(fp->path)) == NULL((void *)0)) { | ||||||
| 609 | perror(fp->path); | ||||||
| 610 | return 0; | ||||||
| 611 | } | ||||||
| 612 | tailp = NULL((void *)0); | ||||||
| 613 | DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path)); | ||||||
| 614 | fp->num_children = 0; | ||||||
| 615 | while ((dirent = readdir(dir)) != NULL((void *)0)) { | ||||||
| 616 | if (dirent->d_namlen == 0) | ||||||
| 617 | continue; | ||||||
| 618 | name = copy(dirent->d_name, NULL((void *)0)); | ||||||
| 619 | if (add_file(NO_PROB(-1), name, fp->path, &fp->child, &tailp, fp)) | ||||||
| 620 | fp->num_children++; | ||||||
| 621 | else | ||||||
| 622 | free(name); | ||||||
| 623 | } | ||||||
| 624 | if (fp->num_children == 0) { | ||||||
| 625 | (void) fprintf(stderr(&__sF[2]), | ||||||
| 626 | "fortune: %s: No fortune files in directory.\n", fp->path); | ||||||
| 627 | closedir(dir); | ||||||
| 628 | return 0; | ||||||
| 629 | } | ||||||
| 630 | closedir(dir); | ||||||
| 631 | return 1; | ||||||
| 632 | } | ||||||
| 633 | |||||||
| 634 | /* | ||||||
| 635 | * is_dir: | ||||||
| 636 | * Return 1 if the file is a directory, 0 otherwise. | ||||||
| 637 | */ | ||||||
| 638 | int | ||||||
| 639 | is_dir(char *file) | ||||||
| 640 | { | ||||||
| 641 | struct stat sbuf; | ||||||
| 642 | |||||||
| 643 | if (stat(file, &sbuf) < 0) | ||||||
| 644 | return 0; | ||||||
| 645 | return S_ISDIR(sbuf.st_mode)((sbuf.st_mode & 0170000) == 0040000); | ||||||
| 646 | } | ||||||
| 647 | |||||||
| 648 | /* | ||||||
| 649 | * is_fortfile: | ||||||
| 650 | * Return 1 if the file is a fortune database file. We try and | ||||||
| 651 | * exclude files without reading them if possible to avoid | ||||||
| 652 | * overhead. Files which start with ".", or which have "illegal" | ||||||
| 653 | * suffixes, as contained in suflist[], are ruled out. | ||||||
| 654 | */ | ||||||
| 655 | int | ||||||
| 656 | is_fortfile(char *file, char **datp, int check_for_offend) | ||||||
| 657 | { | ||||||
| 658 | int i; | ||||||
| 659 | char *sp; | ||||||
| 660 | char *datfile; | ||||||
| 661 | static char *suflist[] = { /* list of "illegal" suffixes" */ | ||||||
| 662 | "dat", "pos", "c", "h", "p", "i", "f", | ||||||
| 663 | "pas", "ftn", "ins.c", "ins,pas", | ||||||
| 664 | "ins.ftn", "sml", | ||||||
| 665 | NULL((void *)0) | ||||||
| 666 | }; | ||||||
| 667 | |||||||
| 668 | DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file)); | ||||||
| 669 | |||||||
| 670 | /* | ||||||
| 671 | * Preclude any -o files for offendable people, and any non -o | ||||||
| 672 | * files for completely offensive people. | ||||||
| 673 | */ | ||||||
| 674 | if (check_for_offend && !All_forts) { | ||||||
| 675 | i = strlen(file); | ||||||
| 676 | if (Offend ^ (file[i - 2] == '-' && file[i - 1] == 'o')) | ||||||
| 677 | return 0; | ||||||
| 678 | } | ||||||
| 679 | |||||||
| 680 | if ((sp = strrchr(file, '/')) == NULL((void *)0)) | ||||||
| 681 | sp = file; | ||||||
| 682 | else | ||||||
| 683 | sp++; | ||||||
| 684 | if (*sp == '.') { | ||||||
| 685 | DPRINTF(2, (stderr, "0 (file starts with '.')\n")); | ||||||
| 686 | return 0; | ||||||
| 687 | } | ||||||
| 688 | if ((sp = strrchr(sp, '.')) != NULL((void *)0)) { | ||||||
| 689 | sp++; | ||||||
| 690 | for (i = 0; suflist[i] != NULL((void *)0); i++) | ||||||
| 691 | if (strcmp(sp, suflist[i]) == 0) { | ||||||
| 692 | DPRINTF(2, (stderr, "0 (file has suffix \".%s\")\n", sp)); | ||||||
| 693 | return 0; | ||||||
| 694 | } | ||||||
| 695 | } | ||||||
| 696 | |||||||
| 697 | datfile = copy(file, ".dat"); | ||||||
| 698 | if (access(datfile, R_OK0x04) < 0) { | ||||||
| 699 | free(datfile); | ||||||
| 700 | DPRINTF(2, (stderr, "0 (no \".dat\" file)\n")); | ||||||
| 701 | return 0; | ||||||
| 702 | } | ||||||
| 703 | if (datp != NULL((void *)0)) | ||||||
| 704 | *datp = datfile; | ||||||
| 705 | else | ||||||
| 706 | free(datfile); | ||||||
| 707 | DPRINTF(2, (stderr, "1\n")); | ||||||
| 708 | return 1; | ||||||
| 709 | } | ||||||
| 710 | |||||||
| 711 | /* | ||||||
| 712 | * copy: | ||||||
| 713 | * Return a malloc()'ed copy of the string + an optional suffix | ||||||
| 714 | */ | ||||||
| 715 | char * | ||||||
| 716 | copy(char *str, char *suf) | ||||||
| 717 | { | ||||||
| 718 | char *new; | ||||||
| 719 | |||||||
| 720 | if (asprintf(&new, "%s%s", str, suf ? suf : "") == -1) | ||||||
| 721 | err(1, NULL((void *)0)); | ||||||
| 722 | return new; | ||||||
| 723 | } | ||||||
| 724 | |||||||
| 725 | /* | ||||||
| 726 | * init_prob: | ||||||
| 727 | * Initialize the fortune probabilities. | ||||||
| 728 | */ | ||||||
| 729 | void | ||||||
| 730 | init_prob(void) | ||||||
| 731 | { | ||||||
| 732 | FILEDESC *fp, *last; | ||||||
| 733 | int percent, num_noprob, frac; | ||||||
| 734 | |||||||
| 735 | /* | ||||||
| 736 | * Distribute the residual probability (if any) across all | ||||||
| 737 | * files with unspecified probability (i.e., probability of 0) | ||||||
| 738 | * (if any). | ||||||
| 739 | */ | ||||||
| 740 | |||||||
| 741 | percent = 0; | ||||||
| 742 | num_noprob = 0; | ||||||
| 743 | for (fp = File_tail; fp != NULL((void *)0); fp = fp->prev) | ||||||
| 744 | if (fp->percent == NO_PROB(-1)) { | ||||||
| 745 | num_noprob++; | ||||||
| 746 | if (Equal_probs) | ||||||
| 747 | last = fp; | ||||||
| 748 | } | ||||||
| 749 | else | ||||||
| 750 | percent += fp->percent; | ||||||
| 751 | DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's", | ||||||
| 752 | percent, num_noprob)); | ||||||
| 753 | if (percent > 100) { | ||||||
| 754 | (void) fprintf(stderr(&__sF[2]), | ||||||
| 755 | "fortune: probabilities sum to %d%%!\n", percent); | ||||||
| 756 | exit(1); | ||||||
| 757 | } | ||||||
| 758 | else if (percent < 100 && num_noprob == 0) { | ||||||
| 759 | (void) fprintf(stderr(&__sF[2]), | ||||||
| 760 | "fortune: no place to put residual probability (%d%%)\n", | ||||||
| 761 | percent); | ||||||
| 762 | exit(1); | ||||||
| 763 | } | ||||||
| 764 | else if (percent == 100 && num_noprob != 0) { | ||||||
| 765 | (void) fprintf(stderr(&__sF[2]), | ||||||
| 766 | "fortune: no probability left to put in residual files\n"); | ||||||
| 767 | exit(1); | ||||||
| 768 | } | ||||||
| 769 | percent = 100 - percent; | ||||||
| 770 | if (Equal_probs) { | ||||||
| 771 | if (num_noprob != 0) { | ||||||
| 772 | if (num_noprob > 1) { | ||||||
| 773 | frac = percent / num_noprob; | ||||||
| 774 | DPRINTF(1, (stderr, ", frac = %d%%", frac)); | ||||||
| 775 | for (fp = File_list; fp != last; fp = fp->next) | ||||||
| 776 | if (fp->percent == NO_PROB(-1)) { | ||||||
| 777 | fp->percent = frac; | ||||||
| 778 | percent -= frac; | ||||||
| 779 | } | ||||||
| 780 | } | ||||||
| 781 | last->percent = percent; | ||||||
| 782 | DPRINTF(1, (stderr, ", residual = %d%%", percent)); | ||||||
| 783 | } | ||||||
| 784 | } else { | ||||||
| 785 | DPRINTF(1, (stderr, | ||||||
| 786 | ", %d%% distributed over remaining fortunes\n", | ||||||
| 787 | percent)); | ||||||
| 788 | } | ||||||
| 789 | DPRINTF(1, (stderr, "\n")); | ||||||
| 790 | |||||||
| 791 | #ifdef DEBUG | ||||||
| 792 | if (Debug >= 1) | ||||||
| 793 | print_file_list(); | ||||||
| 794 | #endif | ||||||
| 795 | } | ||||||
| 796 | |||||||
| 797 | /* | ||||||
| 798 | * get_fort: | ||||||
| 799 | * Get the fortune data file's seek pointer for the next fortune. | ||||||
| 800 | */ | ||||||
| 801 | void | ||||||
| 802 | get_fort(void) | ||||||
| 803 | { | ||||||
| 804 | FILEDESC *fp; | ||||||
| 805 | int choice; | ||||||
| 806 | |||||||
| 807 | if (File_list->next == NULL((void *)0) || File_list->percent == NO_PROB(-1)) | ||||||
| 808 | fp = File_list; | ||||||
| 809 | else { | ||||||
| 810 | choice = arc4random_uniform(100); | ||||||
| 811 | DPRINTF(1, (stderr, "choice = %d\n", choice)); | ||||||
| 812 | for (fp = File_list; fp->percent != NO_PROB(-1); fp = fp->next) | ||||||
| 813 | if (choice < fp->percent) | ||||||
| 814 | break; | ||||||
| 815 | else { | ||||||
| 816 | choice -= fp->percent; | ||||||
| 817 | DPRINTF(1, (stderr, | ||||||
| 818 | " skip \"%s\", %d%% (choice = %d)\n", | ||||||
| 819 | fp->name, fp->percent, choice)); | ||||||
| 820 | } | ||||||
| 821 | DPRINTF(1, (stderr, | ||||||
| 822 | "using \"%s\", %d%% (choice = %d)\n", | ||||||
| 823 | fp->name, fp->percent, choice)); | ||||||
| 824 | } | ||||||
| 825 | if (fp->percent != NO_PROB(-1)) | ||||||
| 826 | get_tbl(fp); | ||||||
| 827 | else { | ||||||
| 828 | if (fp->next != NULL((void *)0)) { | ||||||
| 829 | sum_noprobs(fp); | ||||||
| 830 | choice = arc4random_uniform(Noprob_tbl.str_numstr); | ||||||
| 831 | DPRINTF(1, (stderr, "choice = %d (of %d) \n", choice, | ||||||
| 832 | Noprob_tbl.str_numstr)); | ||||||
| 833 | while (choice >= fp->tbl.str_numstr) { | ||||||
| 834 | choice -= fp->tbl.str_numstr; | ||||||
| 835 | fp = fp->next; | ||||||
| 836 | DPRINTF(1, (stderr, | ||||||
| 837 | " skip \"%s\", %d (choice = %d)\n", | ||||||
| 838 | fp->name, fp->tbl.str_numstr, | ||||||
| 839 | choice)); | ||||||
| 840 | } | ||||||
| 841 | DPRINTF(1, (stderr, "using \"%s\", %d\n", fp->name, | ||||||
| 842 | fp->tbl.str_numstr)); | ||||||
| 843 | } | ||||||
| 844 | get_tbl(fp); | ||||||
| 845 | } | ||||||
| 846 | if (fp->child != NULL((void *)0)) { | ||||||
| 847 | DPRINTF(1, (stderr, "picking child\n")); | ||||||
| 848 | fp = pick_child(fp); | ||||||
| 849 | } | ||||||
| 850 | Fortfile = fp; | ||||||
| 851 | get_pos(fp); | ||||||
| 852 | open_dat(fp); | ||||||
| 853 | (void) lseek(fp->datfd, | ||||||
| 854 | (off_t) (sizeof fp->tbl + fp->pos * sizeof Seekpts[0]), 0); | ||||||
| 855 | read(fp->datfd, &Seekpts[0], sizeof Seekpts[0]); | ||||||
| 856 | Seekpts[0] = ntohl(Seekpts[0])(__uint32_t)(__builtin_constant_p(Seekpts[0]) ? (__uint32_t)( ((__uint32_t)(Seekpts[0]) & 0xff) << 24 | ((__uint32_t )(Seekpts[0]) & 0xff00) << 8 | ((__uint32_t)(Seekpts [0]) & 0xff0000) >> 8 | ((__uint32_t)(Seekpts[0]) & 0xff000000) >> 24) : __swap32md(Seekpts[0])); | ||||||
| 857 | read(fp->datfd, &Seekpts[1], sizeof Seekpts[1]); | ||||||
| 858 | Seekpts[1] = ntohl(Seekpts[1])(__uint32_t)(__builtin_constant_p(Seekpts[1]) ? (__uint32_t)( ((__uint32_t)(Seekpts[1]) & 0xff) << 24 | ((__uint32_t )(Seekpts[1]) & 0xff00) << 8 | ((__uint32_t)(Seekpts [1]) & 0xff0000) >> 8 | ((__uint32_t)(Seekpts[1]) & 0xff000000) >> 24) : __swap32md(Seekpts[1])); | ||||||
| 859 | } | ||||||
| 860 | |||||||
| 861 | /* | ||||||
| 862 | * pick_child | ||||||
| 863 | * Pick a child from a chosen parent. | ||||||
| 864 | */ | ||||||
| 865 | FILEDESC * | ||||||
| 866 | pick_child(FILEDESC *parent) | ||||||
| 867 | { | ||||||
| 868 | FILEDESC *fp; | ||||||
| 869 | int choice; | ||||||
| 870 | |||||||
| 871 | if (Equal_probs) { | ||||||
| 872 | choice = arc4random_uniform(parent->num_children); | ||||||
| 873 | DPRINTF(1, (stderr, " choice = %d (of %d)\n", | ||||||
| 874 | choice, parent->num_children)); | ||||||
| 875 | for (fp = parent->child; choice--; fp = fp->next) | ||||||
| 876 | continue; | ||||||
| 877 | DPRINTF(1, (stderr, " using %s\n", fp->name)); | ||||||
| 878 | return fp; | ||||||
| 879 | } | ||||||
| 880 | else { | ||||||
| 881 | get_tbl(parent); | ||||||
| 882 | choice = arc4random_uniform(parent->tbl.str_numstr); | ||||||
| 883 | DPRINTF(1, (stderr, " choice = %d (of %d)\n", | ||||||
| 884 | choice, parent->tbl.str_numstr)); | ||||||
| 885 | for (fp = parent->child; choice >= fp->tbl.str_numstr; | ||||||
| 886 | fp = fp->next) { | ||||||
| 887 | choice -= fp->tbl.str_numstr; | ||||||
| 888 | DPRINTF(1, (stderr, "\tskip %s, %d (choice = %d)\n", | ||||||
| 889 | fp->name, fp->tbl.str_numstr, choice)); | ||||||
| 890 | } | ||||||
| 891 | DPRINTF(1, (stderr, " using %s, %d\n", fp->name, | ||||||
| 892 | fp->tbl.str_numstr)); | ||||||
| 893 | return fp; | ||||||
| 894 | } | ||||||
| 895 | } | ||||||
| 896 | |||||||
| 897 | /* | ||||||
| 898 | * sum_noprobs: | ||||||
| 899 | * Sum up all the noprob probabilities, starting with fp. | ||||||
| 900 | */ | ||||||
| 901 | void | ||||||
| 902 | sum_noprobs(FILEDESC *fp) | ||||||
| 903 | { | ||||||
| 904 | static bool_Bool did_noprobs = false0; | ||||||
| 905 | |||||||
| 906 | if (did_noprobs) | ||||||
| 907 | return; | ||||||
| 908 | zero_tbl(&Noprob_tbl); | ||||||
| 909 | while (fp != NULL((void *)0)) { | ||||||
| 910 | get_tbl(fp); | ||||||
| 911 | sum_tbl(&Noprob_tbl, &fp->tbl); | ||||||
| 912 | fp = fp->next; | ||||||
| 913 | } | ||||||
| 914 | did_noprobs = true1; | ||||||
| 915 | } | ||||||
| 916 | |||||||
| 917 | int | ||||||
| 918 | max(int i, int j) | ||||||
| 919 | { | ||||||
| 920 | return (i >= j ? i : j); | ||||||
| 921 | } | ||||||
| 922 | |||||||
| 923 | /* | ||||||
| 924 | * open_fp: | ||||||
| 925 | * Assocatiate a FILE * with the given FILEDESC. | ||||||
| 926 | */ | ||||||
| 927 | void | ||||||
| 928 | open_fp(FILEDESC *fp) | ||||||
| 929 | { | ||||||
| 930 | if (fp->inf == NULL((void *)0) && (fp->inf = fdopen(fp->fd, "r")) == NULL((void *)0)) { | ||||||
| 931 | perror(fp->path); | ||||||
| 932 | exit(1); | ||||||
| 933 | } | ||||||
| 934 | } | ||||||
| 935 | |||||||
| 936 | /* | ||||||
| 937 | * open_dat: | ||||||
| 938 | * Open up the dat file if we need to. | ||||||
| 939 | */ | ||||||
| 940 | void | ||||||
| 941 | open_dat(FILEDESC *fp) | ||||||
| 942 | { | ||||||
| 943 | if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, O_RDONLY0x0000)) < 0) { | ||||||
| 944 | perror(fp->datfile); | ||||||
| 945 | exit(1); | ||||||
| 946 | } | ||||||
| 947 | } | ||||||
| 948 | |||||||
| 949 | /* | ||||||
| 950 | * get_pos: | ||||||
| 951 | * Get the position from the pos file, if there is one. If not, | ||||||
| 952 | * return a random number. | ||||||
| 953 | */ | ||||||
| 954 | void | ||||||
| 955 | get_pos(FILEDESC *fp) | ||||||
| 956 | { | ||||||
| 957 | assert(fp->read_tbl)((fp->read_tbl) ? (void)0 : __assert2("/usr/src/games/fortune/fortune/fortune.c" , 957, __func__, "fp->read_tbl")); | ||||||
| 958 | if (fp->pos == POS_UNKNOWN((int32_t) -1)) { | ||||||
| 959 | fp->pos = arc4random_uniform(fp->tbl.str_numstr); | ||||||
| 960 | } | ||||||
| 961 | if (++(fp->pos) >= fp->tbl.str_numstr) | ||||||
| 962 | fp->pos -= fp->tbl.str_numstr; | ||||||
| 963 | DPRINTF(1, (stderr, "pos for %s is %d\n", fp->name, fp->pos)); | ||||||
| 964 | } | ||||||
| 965 | |||||||
| 966 | /* | ||||||
| 967 | * get_tbl: | ||||||
| 968 | * Get the tbl data file the datfile. | ||||||
| 969 | */ | ||||||
| 970 | void | ||||||
| 971 | get_tbl(FILEDESC *fp) | ||||||
| 972 | { | ||||||
| 973 | int fd; | ||||||
| 974 | FILEDESC *child; | ||||||
| 975 | |||||||
| 976 | if (fp->read_tbl) | ||||||
| 977 | return; | ||||||
| 978 | if (fp->child == NULL((void *)0)) { | ||||||
| 979 | if ((fd = open(fp->datfile, O_RDONLY0x0000)) < 0) { | ||||||
| 980 | perror(fp->datfile); | ||||||
| 981 | exit(1); | ||||||
| 982 | } | ||||||
| 983 | if (read(fd, &fp->tbl.str_version, sizeof(fp->tbl.str_version)) != | ||||||
| 984 | sizeof(fp->tbl.str_version)) { | ||||||
| 985 | (void)fprintf(stderr(&__sF[2]), | ||||||
| 986 | "fortune: %s corrupted\n", fp->path); | ||||||
| 987 | exit(1); | ||||||
| 988 | } | ||||||
| 989 | if (read(fd, &fp->tbl.str_numstr, sizeof(fp->tbl.str_numstr)) != | ||||||
| 990 | sizeof(fp->tbl.str_numstr)) { | ||||||
| 991 | (void)fprintf(stderr(&__sF[2]), | ||||||
| 992 | "fortune: %s corrupted\n", fp->path); | ||||||
| 993 | exit(1); | ||||||
| 994 | } | ||||||
| 995 | if (read(fd, &fp->tbl.str_longlen, sizeof(fp->tbl.str_longlen)) != | ||||||
| 996 | sizeof(fp->tbl.str_longlen)) { | ||||||
| 997 | (void)fprintf(stderr(&__sF[2]), | ||||||
| 998 | "fortune: %s corrupted\n", fp->path); | ||||||
| 999 | exit(1); | ||||||
| 1000 | } | ||||||
| 1001 | if (read(fd, &fp->tbl.str_shortlen, sizeof(fp->tbl.str_shortlen)) != | ||||||
| 1002 | sizeof(fp->tbl.str_shortlen)) { | ||||||
| 1003 | (void)fprintf(stderr(&__sF[2]), | ||||||
| 1004 | "fortune: %s corrupted\n", fp->path); | ||||||
| 1005 | exit(1); | ||||||
| 1006 | } | ||||||
| 1007 | if (read(fd, &fp->tbl.str_flags, sizeof(fp->tbl.str_flags)) != | ||||||
| 1008 | sizeof(fp->tbl.str_flags)) { | ||||||
| 1009 | (void)fprintf(stderr(&__sF[2]), | ||||||
| 1010 | "fortune: %s corrupted\n", fp->path); | ||||||
| 1011 | exit(1); | ||||||
| 1012 | } | ||||||
| 1013 | if (read(fd, fp->tbl.stuff, sizeof(fp->tbl.stuff)) != | ||||||
| 1014 | sizeof(fp->tbl.stuff)) { | ||||||
| 1015 | (void)fprintf(stderr(&__sF[2]), | ||||||
| 1016 | "fortune: %s corrupted\n", fp->path); | ||||||
| 1017 | exit(1); | ||||||
| 1018 | } | ||||||
| 1019 | |||||||
| 1020 | /* fp->tbl.str_version = ntohl(fp->tbl.str_version); */ | ||||||
| 1021 | fp->tbl.str_numstr = ntohl(fp->tbl.str_numstr)(__uint32_t)(__builtin_constant_p(fp->tbl.str_numstr) ? (__uint32_t )(((__uint32_t)(fp->tbl.str_numstr) & 0xff) << 24 | ((__uint32_t)(fp->tbl.str_numstr) & 0xff00) << 8 | ((__uint32_t)(fp->tbl.str_numstr) & 0xff0000) >> 8 | ((__uint32_t)(fp->tbl.str_numstr) & 0xff000000) >> 24) : __swap32md(fp->tbl.str_numstr)); | ||||||
| 1022 | fp->tbl.str_longlen = ntohl(fp->tbl.str_longlen)(__uint32_t)(__builtin_constant_p(fp->tbl.str_longlen) ? ( __uint32_t)(((__uint32_t)(fp->tbl.str_longlen) & 0xff) << 24 | ((__uint32_t)(fp->tbl.str_longlen) & 0xff00 ) << 8 | ((__uint32_t)(fp->tbl.str_longlen) & 0xff0000 ) >> 8 | ((__uint32_t)(fp->tbl.str_longlen) & 0xff000000 ) >> 24) : __swap32md(fp->tbl.str_longlen)); | ||||||
| 1023 | fp->tbl.str_shortlen = ntohl(fp->tbl.str_shortlen)(__uint32_t)(__builtin_constant_p(fp->tbl.str_shortlen) ? ( __uint32_t)(((__uint32_t)(fp->tbl.str_shortlen) & 0xff ) << 24 | ((__uint32_t)(fp->tbl.str_shortlen) & 0xff00 ) << 8 | ((__uint32_t)(fp->tbl.str_shortlen) & 0xff0000 ) >> 8 | ((__uint32_t)(fp->tbl.str_shortlen) & 0xff000000 ) >> 24) : __swap32md(fp->tbl.str_shortlen)); | ||||||
| 1024 | fp->tbl.str_flags = ntohl(fp->tbl.str_flags)(__uint32_t)(__builtin_constant_p(fp->tbl.str_flags) ? (__uint32_t )(((__uint32_t)(fp->tbl.str_flags) & 0xff) << 24 | ((__uint32_t)(fp->tbl.str_flags) & 0xff00) << 8 | ((__uint32_t)(fp->tbl.str_flags) & 0xff0000) >> 8 | ((__uint32_t)(fp->tbl.str_flags) & 0xff000000) >> 24) : __swap32md(fp->tbl.str_flags)); | ||||||
| 1025 | (void) close(fd); | ||||||
| 1026 | |||||||
| 1027 | if (fp->tbl.str_numstr == 0) { | ||||||
| 1028 | fprintf(stderr(&__sF[2]), "fortune: %s is empty\n", fp->path); | ||||||
| 1029 | exit(1); | ||||||
| 1030 | } | ||||||
| 1031 | } | ||||||
| 1032 | else { | ||||||
| 1033 | zero_tbl(&fp->tbl); | ||||||
| 1034 | for (child = fp->child; child != NULL((void *)0); child = child->next) { | ||||||
| 1035 | get_tbl(child); | ||||||
| 1036 | sum_tbl(&fp->tbl, &child->tbl); | ||||||
| 1037 | } | ||||||
| 1038 | } | ||||||
| 1039 | fp->read_tbl = 1; | ||||||
| 1040 | } | ||||||
| 1041 | |||||||
| 1042 | /* | ||||||
| 1043 | * zero_tbl: | ||||||
| 1044 | * Zero out the fields we care about in a tbl structure. | ||||||
| 1045 | */ | ||||||
| 1046 | void | ||||||
| 1047 | zero_tbl(STRFILE *tp) | ||||||
| 1048 | { | ||||||
| 1049 | tp->str_numstr = 0; | ||||||
| 1050 | tp->str_longlen = 0; | ||||||
| 1051 | tp->str_shortlen = -1; | ||||||
| 1052 | } | ||||||
| 1053 | |||||||
| 1054 | /* | ||||||
| 1055 | * sum_tbl: | ||||||
| 1056 | * Merge the tbl data of t2 into t1. | ||||||
| 1057 | */ | ||||||
| 1058 | void | ||||||
| 1059 | sum_tbl(STRFILE *t1, STRFILE *t2) | ||||||
| 1060 | { | ||||||
| 1061 | t1->str_numstr += t2->str_numstr; | ||||||
| 1062 | if (t1->str_longlen < t2->str_longlen) | ||||||
| 1063 | t1->str_longlen = t2->str_longlen; | ||||||
| 1064 | if (t1->str_shortlen > t2->str_shortlen) | ||||||
| 1065 | t1->str_shortlen = t2->str_shortlen; | ||||||
| 1066 | } | ||||||
| 1067 | |||||||
| 1068 | #define STR(str)((str) == ((void *)0) ? "NULL" : (str)) ((str) == NULL((void *)0) ? "NULL" : (str)) | ||||||
| 1069 | |||||||
| 1070 | /* | ||||||
| 1071 | * print_file_list: | ||||||
| 1072 | * Print out the file list | ||||||
| 1073 | */ | ||||||
| 1074 | void | ||||||
| 1075 | print_file_list(void) | ||||||
| 1076 | { | ||||||
| 1077 | print_list(File_list, 0); | ||||||
| 1078 | } | ||||||
| 1079 | |||||||
| 1080 | /* | ||||||
| 1081 | * print_list: | ||||||
| 1082 | * Print out the actual list, recursively. | ||||||
| 1083 | */ | ||||||
| 1084 | void | ||||||
| 1085 | print_list(FILEDESC *list, int lev) | ||||||
| 1086 | { | ||||||
| 1087 | while (list != NULL((void *)0)) { | ||||||
| 1088 | fprintf(stderr(&__sF[2]), "%*s", lev * 4, ""); | ||||||
| 1089 | if (list->percent == NO_PROB(-1)) | ||||||
| 1090 | fprintf(stderr(&__sF[2]), "___%%"); | ||||||
| 1091 | else | ||||||
| 1092 | fprintf(stderr(&__sF[2]), "%3d%%", list->percent); | ||||||
| 1093 | fprintf(stderr(&__sF[2]), " %s", STR(list->name)((list->name) == ((void *)0) ? "NULL" : (list->name))); | ||||||
| 1094 | DPRINTF(1, (stderr, " (%s, %s)\n", STR(list->path), | ||||||
| 1095 | STR(list->datfile))); | ||||||
| 1096 | putc('\n', stderr)(!__isthreaded ? __sputc('\n', (&__sF[2])) : (putc)('\n', (&__sF[2]))); | ||||||
| 1097 | if (list->child != NULL((void *)0)) | ||||||
| 1098 | print_list(list->child, lev + 1); | ||||||
| 1099 | list = list->next; | ||||||
| 1100 | } | ||||||
| 1101 | } | ||||||
| 1102 | |||||||
| 1103 | |||||||
| 1104 | /* | ||||||
| 1105 | * find_matches: | ||||||
| 1106 | * Find all the fortunes which match the pattern we've been given. | ||||||
| 1107 | */ | ||||||
| 1108 | int | ||||||
| 1109 | find_matches(void) | ||||||
| 1110 | { | ||||||
| 1111 | Fort_len = maxlen_in_list(File_list); | ||||||
| 1112 | DPRINTF(2, (stderr, "Maximum length is %zu\n", Fort_len)); | ||||||
| 1113 | /* extra length, "%\n" is appended */ | ||||||
| 1114 | if ((Fortbuf = malloc(Fort_len + 10)) == NULL((void *)0)) | ||||||
| 1115 | err(1, NULL((void *)0)); | ||||||
| 1116 | |||||||
| 1117 | Found_one = false0; | ||||||
| 1118 | matches_in_list(File_list); | ||||||
| 1119 | return Found_one; | ||||||
| 1120 | } | ||||||
| 1121 | |||||||
| 1122 | /* | ||||||
| 1123 | * maxlen_in_list | ||||||
| 1124 | * Return the maximum fortune len in the file list. | ||||||
| 1125 | */ | ||||||
| 1126 | int | ||||||
| 1127 | maxlen_in_list(FILEDESC *list) | ||||||
| 1128 | { | ||||||
| 1129 | FILEDESC *fp; | ||||||
| 1130 | int len, maxlen; | ||||||
| 1131 | |||||||
| 1132 | maxlen = 0; | ||||||
| 1133 | for (fp = list; fp != NULL((void *)0); fp = fp->next) { | ||||||
| 1134 | if (fp->child != NULL((void *)0)) { | ||||||
| 1135 | if ((len = maxlen_in_list(fp->child)) > maxlen) | ||||||
| 1136 | maxlen = len; | ||||||
| 1137 | } | ||||||
| 1138 | else { | ||||||
| 1139 | get_tbl(fp); | ||||||
| 1140 | if (fp->tbl.str_longlen > maxlen) | ||||||
| 1141 | maxlen = fp->tbl.str_longlen; | ||||||
| 1142 | } | ||||||
| 1143 | } | ||||||
| 1144 | return maxlen; | ||||||
| 1145 | } | ||||||
| 1146 | |||||||
| 1147 | /* | ||||||
| 1148 | * minlen_in_list | ||||||
| 1149 | * Return the minimum fortune len in the file list. | ||||||
| 1150 | */ | ||||||
| 1151 | int | ||||||
| 1152 | minlen_in_list(FILEDESC *list) | ||||||
| 1153 | { | ||||||
| 1154 | FILEDESC *fp; | ||||||
| 1155 | int len, minlen; | ||||||
| 1156 | |||||||
| 1157 | minlen = INT_MAX2147483647; | ||||||
| 1158 | for (fp = list; fp != NULL((void *)0); fp = fp->next) { | ||||||
| 1159 | if (fp->child != NULL((void *)0)) { | ||||||
| 1160 | if ((len = minlen_in_list(fp->child)) < minlen) | ||||||
| 1161 | minlen = len; | ||||||
| 1162 | } else { | ||||||
| 1163 | get_tbl(fp); | ||||||
| 1164 | if (fp->tbl.str_shortlen < minlen) | ||||||
| 1165 | minlen = fp->tbl.str_shortlen; | ||||||
| 1166 | } | ||||||
| 1167 | } | ||||||
| 1168 | return minlen; | ||||||
| 1169 | } | ||||||
| 1170 | |||||||
| 1171 | /* | ||||||
| 1172 | * matches_in_list | ||||||
| 1173 | * Print out the matches from the files in the list. | ||||||
| 1174 | */ | ||||||
| 1175 | void | ||||||
| 1176 | matches_in_list(FILEDESC *list) | ||||||
| 1177 | { | ||||||
| 1178 | char *sp; | ||||||
| 1179 | FILEDESC *fp; | ||||||
| 1180 | int in_file; | ||||||
| 1181 | |||||||
| 1182 | for (fp = list; fp != NULL((void *)0); fp = fp->next) { | ||||||
| 1183 | if (fp->child != NULL((void *)0)) { | ||||||
| 1184 | matches_in_list(fp->child); | ||||||
| 1185 | continue; | ||||||
| 1186 | } | ||||||
| 1187 | DPRINTF(1, (stderr, "searching in %s\n", fp->path)); | ||||||
| 1188 | open_fp(fp); | ||||||
| 1189 | sp = Fortbuf; | ||||||
| 1190 | in_file = 0; | ||||||
| 1191 | while (fgets(sp, Fort_len, fp->inf) != NULL((void *)0)) | ||||||
| 1192 | if (!STR_ENDSTRING(sp, fp->tbl)((sp)[0] == (fp->tbl).stuff[0] && (sp)[1] == '\n')) | ||||||
| 1193 | sp += strlen(sp); | ||||||
| 1194 | else { | ||||||
| 1195 | *sp = '\0'; | ||||||
| 1196 | if (fp->tbl.str_flags & STR_ROTATED0x4) | ||||||
| 1197 | rot13(Fortbuf, sp - Fortbuf); | ||||||
| 1198 | if (regexec(®ex, Fortbuf, 0, NULL((void *)0), 0) == 0) { | ||||||
| 1199 | printf("%c%c", fp->tbl.str_delimstuff[0], | ||||||
| 1200 | fp->tbl.str_delimstuff[0]); | ||||||
| 1201 | if (!in_file) { | ||||||
| 1202 | printf(" (%s)", fp->name); | ||||||
| 1203 | Found_one = true1; | ||||||
| 1204 | in_file = 1; | ||||||
| 1205 | } | ||||||
| 1206 | putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n', (&__sF[1]))); | ||||||
| 1207 | sanitize(Fortbuf); | ||||||
| 1208 | (void) fwrite(Fortbuf, 1, (sp - Fortbuf), stdout(&__sF[1])); | ||||||
| 1209 | } | ||||||
| 1210 | sp = Fortbuf; | ||||||
| 1211 | } | ||||||
| 1212 | } | ||||||
| 1213 | } | ||||||
| 1214 | |||||||
| 1215 | void | ||||||
| 1216 | usage(void) | ||||||
| 1217 | { | ||||||
| 1218 | (void) fprintf(stderr(&__sF[2]), "usage: fortune [-ae"); | ||||||
| 1219 | #ifdef DEBUG | ||||||
| 1220 | (void) fprintf(stderr(&__sF[2]), "D"); | ||||||
| 1221 | #endif /* DEBUG */ | ||||||
| 1222 | (void) fprintf(stderr(&__sF[2]), "f"); | ||||||
| 1223 | (void) fprintf(stderr(&__sF[2]), "i"); | ||||||
| 1224 | (void) fprintf(stderr(&__sF[2]), "losw]"); | ||||||
| 1225 | (void) fprintf(stderr(&__sF[2]), " [-m pattern]"); | ||||||
| 1226 | (void) fprintf(stderr(&__sF[2]), " [[N%%] file/directory/all]\n"); | ||||||
| 1227 | exit(1); | ||||||
| 1228 | } |