File: | src/games/fortune/fortune/fortune.c |
Warning: | line 424, column 12 Use of memory after it is freed |
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 && !add_dir(fp)) || | ||||||
463 | (!isdir && | ||||||
464 | !is_fortfile(path, &fp->datfile, (parent != NULL((void *)0))))) | ||||||
465 | { | ||||||
466 | if (parent == NULL((void *)0)) | ||||||
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_MAX0x7fffffff; | ||||||
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 | } |