Bug Summary

File:src/games/fortune/fortune/fortune.c
Warning:line 424, column 12
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name fortune.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/games/fortune/fortune/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/games/fortune/fortune/../strfile -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/games/fortune/fortune/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/games/fortune/fortune/fortune.c
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
70typedef 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
85bool_Bool Found_one = false0; /* did we find a match? */
86bool_Bool Find_files = false0; /* display a list of fortune files */
87bool_Bool Wait = false0; /* wait desired after fortune */
88bool_Bool Short_only = false0; /* short fortune desired */
89bool_Bool Long_only = false0; /* long fortune desired */
90bool_Bool Offend = false0; /* offensive fortunes only */
91bool_Bool All_forts = false0; /* any fortune allowed */
92bool_Bool Equal_probs = false0; /* scatter un-allocted prob equally */
93bool_Bool Match = false0; /* dump fortunes matching a pattern */
94#ifdef DEBUG
95int Debug = 0; /* print debug messages */
96#endif
97
98char *Fortbuf = NULL((void *)0); /* fortune buffer for -m */
99
100size_t Fort_len = 0;
101
102int32_t Seekpts[2]; /* seek pointers to fortunes */
103
104FILEDESC *File_list = NULL((void *)0), /* Head of file list */
105 *File_tail = NULL((void *)0); /* Tail of file list */
106FILEDESC *Fortfile; /* Fortune file to use */
107
108STRFILE Noprob_tbl; /* sum of data for all no prob files */
109
110int add_dir(FILEDESC *);
111int add_file(int,
112 char *, char *, FILEDESC **, FILEDESC **, FILEDESC *);
113void all_forts(FILEDESC *, char *);
114char *copy(char *, char *);
115void display(FILEDESC *);
116int form_file_list(char **, int);
117int fortlen(void);
118void get_fort(void);
119void get_pos(FILEDESC *);
120void get_tbl(FILEDESC *);
121void getargs(int, char *[]);
122void init_prob(void);
123int is_dir(char *);
124int is_fortfile(char *, char **, int);
125int is_off_name(char *);
126int max(int, int);
127FILEDESC *
128 new_fp(void);
129char *off_name(char *);
130void open_dat(FILEDESC *);
131void open_fp(FILEDESC *);
132FILEDESC *
133 pick_child(FILEDESC *);
134void print_file_list(void);
135void print_list(FILEDESC *, int);
136void rot13(char *, size_t);
137void sanitize(unsigned char *cp);
138void sum_noprobs(FILEDESC *);
139void sum_tbl(STRFILE *, STRFILE *);
140__dead__attribute__((__noreturn__)) void usage(void);
141void zero_tbl(STRFILE *);
142
143char *conv_pat(char *);
144int find_matches(void);
145void matches_in_list(FILEDESC *);
146int maxlen_in_list(FILEDESC *);
147int minlen_in_list(FILEDESC *);
148regex_t regex;
149
150int
151main(int ac, char *av[])
152{
153 setlocale(LC_CTYPE2, "");
154
155 if (pledge("stdio rpath", NULL((void *)0)) == -1) {
1
Assuming the condition is false
2
Taking false branch
156 perror("pledge");
157 return 1;
158 }
159
160 getargs(ac, av);
3
Calling 'getargs'
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
185void
186rot13(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
198void
199sanitize(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
208void
209display(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 */
229int
230fortlen(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 */
252void
253getargs(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)
4
Assuming the condition is false
5
Loop condition is false. Execution continues on line 306
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))
6
Calling 'form_file_list'
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(&regex, 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 */
330int
331form_file_list(char **files, int file_cnt)
332{
333 int i, percent;
334 char *sp;
335
336 if (file_cnt == 0) {
7
Assuming 'file_cnt' is not equal to 0
8
Taking false branch
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++) {
9
Assuming 'i' is < 'file_cnt'
10
Loop condition is true. Entering loop body
15
Assuming 'i' is < 'file_cnt'
16
Loop condition is true. Entering loop body
345 percent = NO_PROB(-1);
346
347 if (isdigit((unsigned char)files[i][0])) {
11
Taking false branch
17
Taking false branch
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)
12
Assuming the condition is false
13
Taking false branch
18
Assuming the condition is false
19
Taking false branch
375 sp = FORTDIR"/usr/share/games/fortune";
376 if (!add_file(percent, sp, NULL((void *)0), &File_list, &File_tail, NULL((void *)0)))
14
Taking false branch
20
Calling 'add_file'
377 return 0;
378 }
379 return 1;
380}
381
382/*
383 * add_file:
384 * Add a file to the file list.
385 */
386int
387add_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
20.1
'dir' is equal to NULL
28.1
'dir' is not equal to NULL
== NULL((void *)0)) {
21
Taking true branch
29
Taking false branch
397 path = file;
398 was_malloc = false0;
399 } else {
400 if (asprintf(&path, "%s/%s", dir, file) == -1)
30
Assuming the condition is false
31
Taking false branch
401 err(1, NULL((void *)0));
402 was_malloc = true1;
403 }
404 if ((isdir = is_dir(path)) && parent
22.1
'parent' is equal to NULL
!= NULL((void *)0)) {
22
Assuming 'isdir' is true
23
Taking false branch
32
Assuming 'isdir' is false
405 if (was_malloc)
406 free(path);
407 return 0; /* don't recurse */
408 }
409 offensive = NULL((void *)0);
410 if (!isdir
23.1
'isdir' is true
32.1
'isdir' is false
&& parent
32.2
'parent' is equal to NULL
== NULL((void *)0) && (All_forts || Offend) &&
33
Assuming 'All_forts' is true
35
Taking true branch
411 !is_off_name(path)) {
34
Assuming the condition is true
412 offensive = off_name(path);
413 if (Offend) {
36
Assuming 'Offend' is false
37
Taking false branch
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));
423over:
424 if ((fd = open(path, O_RDONLY0x0000)) < 0) {
24
Assuming the condition is true
25
Taking true branch
38
Assuming the condition is true
39
Taking true branch
46
Use of memory after it is freed
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
25.1
'All_forts' is false
&& offensive != NULL((void *)0)) {
40
Assuming 'All_forts' is true
41
Assuming 'offensive' is not equal to NULL
42
Taking true branch
434 path = offensive;
435 if (was_malloc
42.1
'was_malloc' is true
)
43
Taking true branch
436 free(path);
44
Memory is released
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;
45
Control jumps to line 424
442 }
443 if (dir
25.2
'dir' is equal to NULL
== NULL((void *)0) && file[0] != '/')
26
Assuming the condition is true
27
Taking true branch
444 return add_file(percent, file, FORTDIR"/usr/share/games/fortune", head, tail,
28
Calling 'add_file'
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 */
505FILEDESC *
506new_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 */
530char *
531off_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 */
540int
541is_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 */
554void
555all_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 */
598int
599add_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 */
638int
639is_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 */
655int
656is_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 */
715char *
716copy(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 */
729void
730init_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 */
801void
802get_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 */
865FILEDESC *
866pick_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 */
901void
902sum_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
917int
918max(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 */
927void
928open_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 */
940void
941open_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 */
954void
955get_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 */
970void
971get_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 */
1046void
1047zero_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 */
1058void
1059sum_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 */
1074void
1075print_file_list(void)
1076{
1077 print_list(File_list, 0);
1078}
1079
1080/*
1081 * print_list:
1082 * Print out the actual list, recursively.
1083 */
1084void
1085print_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 */
1108int
1109find_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 */
1126int
1127maxlen_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 */
1151int
1152minlen_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 */
1175void
1176matches_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(&regex, 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
1215void
1216usage(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}