2 * Copyright (c) 1986, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * @(#) Copyright (c) 1986, 1993 The Regents of the University of California. All rights reserved.
33 * @(#)fortune.c 8.1 (Berkeley) 5/31/93
34 * $FreeBSD: src/games/fortune/fortune/fortune.c,v 1.18.2.1 2001/07/02 00:35:27 dd Exp $
36 /* $FreeBSD: src/games/fortune/fortune/fortune.c,v 1.18.2.1 2001/07/02 00:35:27 dd Exp $ */
37 /* $DragonFly: src/games/fortune/fortune/fortune.c,v 1.6 2006/08/08 16:58:59 pavalos Exp $ */
39 #include <sys/param.h>
41 #include <netinet/in.h>
57 #include "pathnames.h"
59 #define MINW 6 /* minimum wait if desired */
60 #define CPERS 20 /* # of chars for each sec */
61 #define SLEN 160 /* # of chars in short fortune */
63 #define POS_UNKNOWN ((long)-1) /* pos for file unknown */
64 #define NO_PROB (-1) /* no prob specified for file */
67 #define DPRINTF(l,x) { if (Debug >= l) fprintf x; }
81 char *datfile
, *posfile
;
86 struct fd
*child
, *parent
;
87 struct fd
*next
, *prev
;
90 bool Found_one
; /* did we find a match? */
91 bool Find_files
= false; /* just find a list of proper fortune files */
92 bool Fortunes_only
= false; /* check only "fortunes" files */
93 bool Wait
= false; /* wait desired after fortune */
94 bool Short_only
= false; /* short fortune desired */
95 bool Long_only
= false; /* long fortune desired */
96 bool Offend
= false; /* offensive fortunes only */
97 bool All_forts
= false; /* any fortune allowed */
98 bool Equal_probs
= false; /* scatter un-allocted prob equally */
100 bool Match
= false; /* dump fortunes matching a pattern */
103 bool Debug
= false; /* print debug messages */
106 char *Fortbuf
= NULL
; /* fortune buffer for -m */
110 long Seekpts
[2]; /* seek pointers to fortunes */
112 FILEDESC
*File_list
= NULL
, /* Head of file list */
113 *File_tail
= NULL
; /* Tail of file list */
114 FILEDESC
*Fortfile
; /* Fortune file to use */
116 STRFILE Noprob_tbl
; /* sum of data for all no prob files */
118 bool add_dir(FILEDESC
*);
120 const char *, const char *, FILEDESC
**, FILEDESC
**, FILEDESC
*);
121 void all_forts(FILEDESC
*, char *);
122 char *copy(const char *, u_int
);
123 void display(FILEDESC
*);
124 void do_free(void *);
125 void *do_malloc(u_int
);
126 bool form_file_list(char **, int);
129 void get_pos(FILEDESC
*);
130 void get_tbl(FILEDESC
*);
131 void getargs(int, char *[]);
132 void init_prob(void);
133 bool is_dir(const char *);
134 bool is_fortfile(const char *, char **, char **, int);
135 bool is_off_name(const char *);
137 FILEDESC
*new_fp(void);
138 char *off_name(const char *);
139 void open_dat(FILEDESC
*);
140 void open_fp(FILEDESC
*);
141 FILEDESC
*pick_child(FILEDESC
*);
142 void print_file_list(void);
143 void print_list(FILEDESC
*, int);
144 void sum_noprobs(FILEDESC
*);
145 void sum_tbl(STRFILE
*, STRFILE
*);
147 void zero_tbl(STRFILE
*);
150 char *conv_pat(char *);
151 int find_matches(void);
152 void matches_in_list(FILEDESC
*);
153 int maxlen_in_list(FILEDESC
*);
158 #define RE_COMP(p) (Re_pat = regcmp(p, NULL))
159 #define BAD_COMP(f) ((f) == NULL)
160 #define RE_EXEC(p) regex(Re_pat, (p))
163 char *regcmp(), *regex();
165 #define RE_COMP(p) (p = re_comp(p))
166 #define BAD_COMP(f) ((f) != NULL)
167 #define RE_EXEC(p) re_exec(p)
172 main(int ac
, char *av
[])
174 #ifdef OK_TO_WRITE_DISK
176 #endif /* OK_TO_WRITE_DISK */
178 setlocale(LC_ALL
, "");
184 exit(find_matches() != 0);
191 } while ((Short_only
&& fortlen() > SLEN
) ||
192 (Long_only
&& fortlen() <= SLEN
));
196 #ifdef OK_TO_WRITE_DISK
197 if ((fd
= creat(Fortfile
->posfile
, 0666)) < 0) {
198 perror(Fortfile
->posfile
);
203 * if we can, we exclusive lock, but since it isn't very
204 * important, we just punt if we don't have easy locking
209 write(fd
, (char *) &Fortfile
->pos
, sizeof Fortfile
->pos
);
210 if (!Fortfile
->was_pos_file
)
211 chmod(Fortfile
->path
, 0666);
215 #endif /* OK_TO_WRITE_DISK */
219 sleep((unsigned int)max(Fort_len
/ CPERS
, MINW
));
226 display(FILEDESC
*fp
)
233 fseek(fp
->inf
, Seekpts
[0], SEEK_SET
);
234 for (Fort_len
= 0; fgets(line
, sizeof line
, fp
->inf
) != NULL
&&
235 !STR_ENDSTRING(line
, fp
->tbl
); Fort_len
++) {
236 if (fp
->tbl
.str_flags
& STR_ROTATED
)
237 for (p
= line
; (ch
= *p
) != '\0'; ++p
) {
240 *p
= 'A' + (ch
- 'A' + 13) % 26;
241 else if (islower(ch
))
242 *p
= 'a' + (ch
- 'a' + 13) % 26;
245 if (fp
->tbl
.str_flags
& STR_COMMENTS
246 && line
[0] == fp
->tbl
.str_delim
247 && line
[1] == fp
->tbl
.str_delim
)
256 * Return the length of the fortune.
264 if (!(Fortfile
->tbl
.str_flags
& (STR_RANDOM
| STR_ORDERED
)))
265 nchar
= (Seekpts
[1] - Seekpts
[0] <= SLEN
);
268 fseek(Fortfile
->inf
, Seekpts
[0], SEEK_SET
);
270 while (fgets(line
, sizeof line
, Fortfile
->inf
) != NULL
&&
271 !STR_ENDSTRING(line
, Fortfile
->tbl
))
272 nchar
+= strlen(line
);
280 * This routine evaluates the arguments on the command line
283 getargs(int argc
, char **argv
)
288 #endif /* NO_REGEX */
294 #endif /* NO_REGEX */
297 while ((ch
= getopt(argc
, argv
, "aDefilm:osw")) != -1)
299 while ((ch
= getopt(argc
, argv
, "aefilm:osw")) != -1)
302 case 'a': /* any fortune */
311 Equal_probs
++; /* scatter un-allocted prob equally */
313 case 'f': /* find fortune files */
316 case 'l': /* long ones only */
320 case 'o': /* offensive ones only */
323 case 's': /* short ones only */
327 case 'w': /* give time to read */
331 case 'i': /* case-insensitive match */
332 case 'm': /* dump out the fortunes */
334 "fortune: can't match fortunes on this system (Sorry)\n");
337 case 'm': /* dump out the fortunes */
341 case 'i': /* case-insensitive match */
344 #endif /* NO_REGEX */
352 if (!form_file_list(argv
, argc
))
353 exit(1); /* errors printed through form_file_list() */
367 if (BAD_COMP(RE_COMP(pat
))) {
369 fprintf(stderr
, "%s\n", pat
);
371 fprintf(stderr
, "bad pattern: %s\n", pat
);
375 #endif /* NO_REGEX */
380 * Form the file list from the file specifications.
383 form_file_list(char **files
, int file_cnt
)
391 Fortunes_only
= true;
392 i
= add_file(NO_PROB
, FORTDIR
, NULL
, &File_list
,
394 Fortunes_only
= false;
397 return (add_file(NO_PROB
, "fortunes", FORTDIR
,
398 &File_list
, &File_tail
, NULL
));
400 for (i
= 0; i
< file_cnt
; i
++) {
402 if (!isdigit((unsigned char)files
[i
][0]))
406 for (sp
= files
[i
]; isdigit((unsigned char)*sp
); sp
++)
407 percent
= percent
* 10 + *sp
- '0';
409 fprintf(stderr
, "percentages must be <= 100\n");
413 fprintf(stderr
, "percentages must be integers\n");
417 * If the number isn't followed by a '%', then
418 * it was not a percentage, just the first part
419 * of a file name which starts with digits.
425 else if (*++sp
== '\0') {
426 if (++i
>= file_cnt
) {
427 fprintf(stderr
, "percentages must precede files\n");
433 if (strcmp(sp
, "all") == 0)
435 if (!add_file(percent
, sp
, NULL
, &File_list
, &File_tail
, NULL
))
444 * Add a file to the file list.
447 add_file(int percent
, const char *file
, const char *dir
,
448 FILEDESC
**head
, FILEDESC
**tail
, FILEDESC
*parent
)
453 char *tpath
, *offensive
;
463 tpath
= do_malloc((unsigned int)(strlen(dir
) + strlen(file
) + 2));
464 strcat(strcat(strcpy(tpath
, dir
), "/"), file
);
468 if ((isdir
= is_dir(path
)) && parent
!= NULL
) {
471 return (false); /* don't recurse */
474 if (!isdir
&& parent
== NULL
&& (All_forts
|| Offend
) &&
475 !is_off_name(path
)) {
476 offensive
= off_name(path
);
483 DPRINTF(1, (stderr
, "\ttrying \"%s\"\n", path
));
484 file
= off_name(file
);
488 DPRINTF(1, (stderr
, "adding file \"%s\"\n", path
));
490 if ((fd
= open(path
, O_RDONLY
)) < 0) {
492 * This is a sneak. If the user said -a, and if the
493 * file we're given isn't a file, we check to see if
494 * there is a -o version. If there is, we treat it as
495 * if *that* were the file given. We only do this for
496 * individual files -- if we're scanning a directory,
497 * we'll pick up the -o file anyway.
499 if (All_forts
&& offensive
!= NULL
) {
505 DPRINTF(1, (stderr
, "\ttrying \"%s\"\n", path
));
506 file
= off_name(file
);
509 if (dir
== NULL
&& file
[0] != '/')
510 return (add_file(percent
, file
, FORTDIR
, head
, tail
,
519 DPRINTF(2, (stderr
, "path = \"%s\"\n", path
));
523 fp
->percent
= percent
;
528 if ((isdir
&& !add_dir(fp
)) ||
530 !is_fortfile(path
, &fp
->datfile
, &fp
->posfile
, (parent
!= NULL
))))
534 "fortune:%s not a fortune file or directory\n",
538 do_free(fp
->datfile
);
539 do_free(fp
->posfile
);
545 * If the user said -a, we need to make this node a pointer to
546 * both files, if there are two. We don't need to do this if
547 * we are scanning a directory, since the scan will pick up the
550 if (All_forts
&& parent
== NULL
&& !is_off_name(path
))
551 all_forts(fp
, offensive
);
554 else if (fp
->percent
== NO_PROB
) {
564 #ifdef OK_TO_WRITE_DISK
565 fp
->was_pos_file
= (access(fp
->posfile
, W_OK
) >= 0);
566 #endif /* OK_TO_WRITE_DISK */
573 * Return a pointer to an initialized new FILEDESC.
580 fp
= (FILEDESC
*)do_malloc(sizeof(*fp
));
582 fp
->pos
= POS_UNKNOWN
;
585 fp
->percent
= NO_PROB
;
586 fp
->read_tbl
= false;
599 * Return a pointer to the offensive version of a file of this name.
602 off_name(const char *file
)
606 new = copy(file
, (unsigned int)(strlen(file
) + 2));
608 return (strcat(new, "-o"));
613 * Is the file an offensive-style name?
616 is_off_name(const char *file
)
622 return (len
>= 3 && file
[len
- 2] == '-' && file
[len
- 1] == 'o');
627 * Modify a FILEDESC element to be the parent of two children if
628 * there are two children to be a parent of.
631 all_forts(FILEDESC
*fp
, char *offensive
)
634 FILEDESC
*scene
, *obscene
;
636 char *datfile
, *posfile
;
638 if (fp
->child
!= NULL
) /* this is a directory, not a file */
640 if (!is_fortfile(offensive
, &datfile
, &posfile
, false))
642 if ((fd
= open(offensive
, O_RDONLY
)) < 0)
644 DPRINTF(1, (stderr
, "adding \"%s\" because of -a\n", offensive
));
649 fp
->num_children
= 2;
651 scene
->next
= obscene
;
652 obscene
->next
= NULL
;
653 scene
->child
= obscene
->child
= NULL
;
654 scene
->parent
= obscene
->parent
= fp
;
657 scene
->percent
= obscene
->percent
= NO_PROB
;
661 obscene
->path
= offensive
;
662 if ((sp
= rindex(offensive
, '/')) == NULL
)
663 obscene
->name
= offensive
;
665 obscene
->name
= ++sp
;
666 obscene
->datfile
= datfile
;
667 obscene
->posfile
= posfile
;
668 obscene
->read_tbl
= false;
669 #ifdef OK_TO_WRITE_DISK
670 obscene
->was_pos_file
= (access(obscene
->posfile
, W_OK
) >= 0);
671 #endif /* OK_TO_WRITE_DISK */
676 * Add the contents of an entire directory.
679 add_dir(FILEDESC
*fp
)
682 struct dirent
*dirent
;
688 if ((dir
= opendir(fp
->path
)) == NULL
) {
693 DPRINTF(1, (stderr
, "adding dir \"%s\"\n", fp
->path
));
694 fp
->num_children
= 0;
695 while ((dirent
= readdir(dir
)) != NULL
) {
696 if ((name
= strdup(dirent
->d_name
)) == NULL
)
697 err(1, "strdup failed");
698 if (add_file(NO_PROB
, name
, fp
->path
, &fp
->child
, &tailp
, fp
))
703 if (fp
->num_children
== 0) {
705 "fortune: %s: No fortune files in directory.\n", fp
->path
);
714 * Return true if the file is a directory, false otherwise.
717 is_dir(const char *file
)
721 if (stat(file
, &sbuf
) < 0)
724 return (sbuf
.st_mode
& S_IFDIR
);
729 * Return true if the file is a fortune database file. We try and
730 * exclude files without reading them if possible to avoid
731 * overhead. Files which start with ".", or which have "illegal"
732 * suffixes, as contained in suflist[], are ruled out.
736 is_fortfile(const char *file
, char **datp
, char **posp
, int check_for_offend
)
741 static const char *suflist
[] = {
742 /* list of "illegal" suffixes" */
743 "dat", "pos", "c", "h", "p", "i", "f",
744 "pas", "ftn", "ins.c", "ins,pas",
749 DPRINTF(2, (stderr
, "is_fortfile(%s) returns ", file
));
752 * Preclude any -o files for offendable people, and any non -o
753 * files for completely offensive people.
755 if (check_for_offend
&& !All_forts
) {
757 if (Offend
^ (file
[i
- 2] == '-' && file
[i
- 1] == 'o')) {
758 DPRINTF(2, (stderr
, "false (offending file)\n"));
763 if ((sp
= rindex(file
, '/')) == NULL
)
768 DPRINTF(2, (stderr
, "false (file starts with '.')\n"));
771 if (Fortunes_only
&& strncmp(sp
, "fortunes", 8) != 0) {
772 DPRINTF(2, (stderr
, "false (check fortunes only)\n"));
775 if ((sp
= rindex(sp
, '.')) != NULL
) {
777 for (i
= 0; suflist
[i
] != NULL
; i
++)
778 if (strcmp(sp
, suflist
[i
]) == 0) {
779 DPRINTF(2, (stderr
, "false (file has suffix \".%s\")\n", sp
));
784 datfile
= copy(file
, (unsigned int)(strlen(file
) + 4)); /* +4 for ".dat" */
785 strcat(datfile
, ".dat");
786 if (access(datfile
, R_OK
) < 0) {
787 DPRINTF(2, (stderr
, "false (no readable \".dat\" file)\n"));
790 DPRINTF(0, (stderr
, "Warning: file \"%s\" unreadable\n", datfile
));
799 #ifdef OK_TO_WRITE_DISK
801 *posp
= copy(file
, (unsigned int)(strlen(file
) + 4)); /* +4 for ".dat" */
802 strcat(*posp
, ".pos");
808 #endif /* OK_TO_WRITE_DISK */
809 DPRINTF(2, (stderr
, "true\n"));
816 * Return a malloc()'ed copy of the string
819 copy(const char *str
, unsigned int len
)
823 new = do_malloc(len
+ 1);
834 * Do a malloc, checking for NULL return.
837 do_malloc(unsigned int size
)
841 if ((new = malloc(size
)) == NULL
) {
842 fprintf(stderr
, "fortune: out of memory.\n");
851 * Free malloc'ed space, if any.
862 * Initialize the fortune probabilities.
868 int percent
, num_noprob
, frac
;
872 * Distribute the residual probability (if any) across all
873 * files with unspecified probability (i.e., probability of 0)
879 for (fp
= File_tail
; fp
!= NULL
; fp
= fp
->prev
)
880 if (fp
->percent
== NO_PROB
) {
885 percent
+= fp
->percent
;
886 DPRINTF(1, (stderr
, "summing probabilities:%d%% with %d NO_PROB's",
887 percent
, num_noprob
));
890 "fortune: probabilities sum to %d%% > 100%%!\n", percent
);
892 } else if (percent
< 100 && num_noprob
== 0) {
894 "fortune: no place to put residual probability (%d%% < 100%%)\n",
897 } else if (percent
== 100 && num_noprob
!= 0) {
899 "fortune: no probability left to put in residual files (100%%)\n");
902 percent
= 100 - percent
;
904 if (num_noprob
!= 0) {
905 if (num_noprob
> 1) {
906 frac
= percent
/ num_noprob
;
907 DPRINTF(1, (stderr
, ", frac = %d%%", frac
));
908 for (fp
= File_list
; fp
!= last
; fp
= fp
->next
)
909 if (fp
->percent
== NO_PROB
) {
914 last
->percent
= percent
;
915 DPRINTF(1, (stderr
, ", residual = %d%%", percent
));
919 ", %d%% distributed over remaining fortunes\n",
922 DPRINTF(1, (stderr
, "\n"));
932 * Get the fortune data file's seek pointer for the next fortune.
940 if (File_list
->next
== NULL
|| File_list
->percent
== NO_PROB
)
943 choice
= random() % 100;
944 DPRINTF(1, (stderr
, "choice = %d\n", choice
));
945 for (fp
= File_list
; fp
->percent
!= NO_PROB
; fp
= fp
->next
)
946 if (choice
< fp
->percent
)
949 choice
-= fp
->percent
;
951 " skip \"%s\", %d%% (choice = %d)\n",
952 fp
->name
, fp
->percent
, choice
));
955 "using \"%s\", %d%% (choice = %d)\n",
956 fp
->name
, fp
->percent
, choice
));
958 if (fp
->percent
!= NO_PROB
)
961 if (fp
->next
!= NULL
) {
963 choice
= random() % Noprob_tbl
.str_numstr
;
964 DPRINTF(1, (stderr
, "choice = %d (of %ld) \n", choice
,
965 Noprob_tbl
.str_numstr
));
966 while ((unsigned int)choice
>= fp
->tbl
.str_numstr
) {
967 choice
-= fp
->tbl
.str_numstr
;
970 " skip \"%s\", %ld (choice = %d)\n",
971 fp
->name
, fp
->tbl
.str_numstr
,
974 DPRINTF(1, (stderr
, "using \"%s\", %ld\n", fp
->name
,
975 fp
->tbl
.str_numstr
));
979 if (fp
->child
!= NULL
) {
980 DPRINTF(1, (stderr
, "picking child\n"));
987 (off_t
) (sizeof fp
->tbl
+ fp
->pos
* sizeof Seekpts
[0]), SEEK_SET
);
988 read(fp
->datfd
, Seekpts
, sizeof Seekpts
);
989 Seekpts
[0] = ntohl(Seekpts
[0]);
990 Seekpts
[1] = ntohl(Seekpts
[1]);
995 * Pick a child from a chosen parent.
998 pick_child(FILEDESC
*parent
)
1004 choice
= random() % parent
->num_children
;
1005 DPRINTF(1, (stderr
, " choice = %d (of %d)\n",
1006 choice
, parent
->num_children
));
1007 for (fp
= parent
->child
; choice
--; fp
= fp
->next
)
1009 DPRINTF(1, (stderr
, " using %s\n", fp
->name
));
1014 choice
= random() % parent
->tbl
.str_numstr
;
1015 DPRINTF(1, (stderr
, " choice = %d (of %ld)\n",
1016 choice
, parent
->tbl
.str_numstr
));
1017 for (fp
= parent
->child
; (unsigned int)choice
>= fp
->tbl
.str_numstr
;
1019 choice
-= fp
->tbl
.str_numstr
;
1020 DPRINTF(1, (stderr
, "\tskip %s, %ld (choice = %d)\n",
1021 fp
->name
, fp
->tbl
.str_numstr
, choice
));
1023 DPRINTF(1, (stderr
, " using %s, %ld\n", fp
->name
,
1024 fp
->tbl
.str_numstr
));
1031 * Sum up all the noprob probabilities, starting with fp.
1034 sum_noprobs(FILEDESC
*fp
)
1036 static bool did_noprobs
= false;
1040 zero_tbl(&Noprob_tbl
);
1041 while (fp
!= NULL
) {
1043 sum_tbl(&Noprob_tbl
, &fp
->tbl
);
1052 return (i
>= j
? i
: j
);
1057 * Assocatiate a FILE * with the given FILEDESC.
1060 open_fp(FILEDESC
*fp
)
1062 if (fp
->inf
== NULL
&& (fp
->inf
= fdopen(fp
->fd
, "r")) == NULL
) {
1070 * Open up the dat file if we need to.
1073 open_dat(FILEDESC
*fp
)
1075 if (fp
->datfd
< 0 && (fp
->datfd
= open(fp
->datfile
, O_RDONLY
)) < 0) {
1076 perror(fp
->datfile
);
1083 * Get the position from the pos file, if there is one. If not,
1084 * return a random number.
1087 get_pos(FILEDESC
*fp
)
1089 #ifdef OK_TO_WRITE_DISK
1091 #endif /* OK_TO_WRITE_DISK */
1093 assert(fp
->read_tbl
);
1094 if (fp
->pos
== POS_UNKNOWN
) {
1095 #ifdef OK_TO_WRITE_DISK
1096 if ((fd
= open(fp
->posfile
, O_RDONLY
)) < 0 ||
1097 read(fd
, &fp
->pos
, sizeof fp
->pos
) != sizeof fp
->pos
)
1098 fp
->pos
= random() % fp
->tbl
.str_numstr
;
1099 else if (fp
->pos
>= fp
->tbl
.str_numstr
)
1100 fp
->pos
%= fp
->tbl
.str_numstr
;
1104 fp
->pos
= random() % fp
->tbl
.str_numstr
;
1105 #endif /* OK_TO_WRITE_DISK */
1107 if ((unsigned int)++(fp
->pos
) >= fp
->tbl
.str_numstr
)
1108 fp
->pos
-= fp
->tbl
.str_numstr
;
1109 DPRINTF(1, (stderr
, "pos for %s is %ld\n", fp
->name
, fp
->pos
));
1114 * Get the tbl data file the datfile.
1117 get_tbl(FILEDESC
*fp
)
1124 if (fp
->child
== NULL
) {
1125 if ((fd
= open(fp
->datfile
, O_RDONLY
)) < 0) {
1126 perror(fp
->datfile
);
1129 if (read(fd
, (char *) &fp
->tbl
, sizeof fp
->tbl
) != sizeof fp
->tbl
) {
1131 "fortune: %s corrupted\n", fp
->path
);
1134 /* fp->tbl.str_version = ntohl(fp->tbl.str_version); */
1135 fp
->tbl
.str_numstr
= ntohl(fp
->tbl
.str_numstr
);
1136 fp
->tbl
.str_longlen
= ntohl(fp
->tbl
.str_longlen
);
1137 fp
->tbl
.str_shortlen
= ntohl(fp
->tbl
.str_shortlen
);
1138 fp
->tbl
.str_flags
= ntohl(fp
->tbl
.str_flags
);
1143 for (child
= fp
->child
; child
!= NULL
; child
= child
->next
) {
1145 sum_tbl(&fp
->tbl
, &child
->tbl
);
1148 fp
->read_tbl
= true;
1153 * Zero out the fields we care about in a tbl structure.
1156 zero_tbl(STRFILE
*tp
)
1159 tp
->str_longlen
= 0;
1160 tp
->str_shortlen
= ~((unsigned long)0);
1165 * Merge the tbl data of t2 into t1.
1168 sum_tbl(STRFILE
*t1
, STRFILE
*t2
)
1170 t1
->str_numstr
+= t2
->str_numstr
;
1171 if (t1
->str_longlen
< t2
->str_longlen
)
1172 t1
->str_longlen
= t2
->str_longlen
;
1173 if (t1
->str_shortlen
> t2
->str_shortlen
)
1174 t1
->str_shortlen
= t2
->str_shortlen
;
1177 #define STR(str) ((str) == NULL ? "NULL" : (str))
1181 * Print out the file list
1184 print_file_list(void)
1186 print_list(File_list
, 0);
1191 * Print out the actual list, recursively.
1194 print_list(FILEDESC
*list
, int lev
)
1196 while (list
!= NULL
) {
1197 fprintf(stderr
, "%*s", lev
* 4, "");
1198 if (list
->percent
== NO_PROB
)
1199 fprintf(stderr
, "___%%");
1201 fprintf(stderr
, "%3d%%", list
->percent
);
1202 fprintf(stderr
, " %s", STR(list
->name
));
1203 DPRINTF(1, (stderr
, " (%s, %s, %s)", STR(list
->path
),
1204 STR(list
->datfile
), STR(list
->posfile
)));
1205 fprintf(stderr
, "\n");
1206 if (list
->child
!= NULL
)
1207 print_list(list
->child
, lev
+ 1);
1215 * Convert the pattern to an ignore-case equivalent.
1218 conv_pat(char *orig
)
1224 cnt
= 1; /* allow for '\0' */
1225 for (sp
= orig
; *sp
!= '\0'; sp
++)
1226 if (isalpha((unsigned char)*sp
))
1230 if ((new = malloc(cnt
)) == NULL
) {
1231 fprintf(stderr
, "pattern too long for ignoring case\n");
1235 for (sp
= new; *orig
!= '\0'; orig
++) {
1236 if (islower((unsigned char)*orig
)) {
1239 *sp
++ = toupper((unsigned char)*orig
);
1242 else if (isupper((unsigned char)*orig
)) {
1245 *sp
++ = tolower((unsigned char)*orig
);
1258 * Find all the fortunes which match the pattern we've been given.
1263 Fort_len
= maxlen_in_list(File_list
);
1264 DPRINTF(2, (stderr
, "Maximum length is %d\n", Fort_len
));
1265 /* extra length, "%\n" is appended */
1266 Fortbuf
= do_malloc((unsigned int)Fort_len
+ 10);
1269 matches_in_list(File_list
);
1276 * Return the maximum fortune len in the file list.
1279 maxlen_in_list(FILEDESC
*list
)
1285 for (fp
= list
; fp
!= NULL
; fp
= fp
->next
) {
1286 if (fp
->child
!= NULL
) {
1287 if ((len
= maxlen_in_list(fp
->child
)) > maxlen
)
1292 if (fp
->tbl
.str_longlen
> (unsigned int)maxlen
)
1293 maxlen
= fp
->tbl
.str_longlen
;
1302 * Print out the matches from the files in the list.
1305 matches_in_list(FILEDESC
*list
)
1312 for (fp
= list
; fp
!= NULL
; fp
= fp
->next
) {
1313 if (fp
->child
!= NULL
) {
1314 matches_in_list(fp
->child
);
1317 DPRINTF(1, (stderr
, "searching in %s\n", fp
->path
));
1321 while (fgets(sp
, Fort_len
, fp
->inf
) != NULL
)
1322 if (fp
->tbl
.str_flags
& STR_COMMENTS
1323 && sp
[0] == fp
->tbl
.str_delim
1324 && sp
[1] == fp
->tbl
.str_delim
)
1326 else if (!STR_ENDSTRING(sp
, fp
->tbl
))
1330 if (fp
->tbl
.str_flags
& STR_ROTATED
)
1331 for (p
= Fortbuf
; (ch
= *p
) != '\0'; ++p
) {
1334 *p
= 'A' + (ch
- 'A' + 13) % 26;
1335 else if (islower(ch
))
1336 *p
= 'a' + (ch
- 'a' + 13) % 26;
1339 if (RE_EXEC(Fortbuf
)) {
1340 printf("%c%c", fp
->tbl
.str_delim
,
1343 printf(" (%s)", fp
->name
);
1348 fwrite(Fortbuf
, 1, (sp
- Fortbuf
), stdout
);
1354 #endif /* NO_REGEX */
1359 fprintf(stderr
, "fortune [-a");
1361 fprintf(stderr
, "D");
1363 fprintf(stderr
, "f");
1365 fprintf(stderr
, "i");
1367 fprintf(stderr
, "losw]");
1369 fprintf(stderr
, " [-m pattern]");
1371 fprintf(stderr
, "[[#%%] file/directory/all]\n");