1 /* $NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $ */
4 * Copyright (c) 1986, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 /* Modified September, 1995, Amy A. Lewis
40 * 1: removed all file-locking dreck. Unnecessary
41 * 2: Fixed bug that made fortune -f report a different list than
42 * fortune with any other parameters, or none, and which forced
43 * the program to read only one file (named 'fortunes')
44 * 3: removed the unnecessary print_file_list()
45 * 4: Added "OFFDIR" to pathnames.h as the directory in which offensive
46 * fortunes are kept. This considerably simplifies our life by
47 * permitting us to dispense with a lot of silly tests for the string
48 * "-o" at the end of a filename.
49 * 5: I think the problems with trying to find filenames were fixed by
50 * the change in the way that offensive files are defined. Two birds,
52 * 6: Calculated probabilities for all files, so that -f will print them.
55 /* Changes Copyright (c) 1997 Dennis L. Clark. All rights reserved.
57 * The changes in this file may be freely redistributed, modified or
58 * included in other software, as long as both the above copyright
59 * notice and these conditions appear intact.
62 /* Modified May 1997, Dennis L. Clark (dbugger@progsoc.uts.edu.au)
63 * + Various portability fixes
64 * + Percent selection of files with -a now works on datafiles which
65 * appear in both unoffensive and offensive directories (see man page
67 * + The -s and -l options are now more consistant in their
68 * interpretation of fortune length
69 * + The -s and -l options can now be combined wit the -m option
72 /* Modified Jul 1999, Pablo Saratxaga <srtxg@chanae.alphanet.ch>
73 * - added use of the LANG variables; now if called without argument
74 * it will choose (if they exist) fortunes in the users' language.
75 * (that is, under a directory $LANG/ under the main fortunes directory
77 * Added to debian by Alastair McKinstry, <mckinstry@computer.org>, 2002-07-31
80 #if 0 /* comment out the stuff here, and get rid of silly warnings */
82 static char copyright[] =
83 "@(#) Copyright (c) 1986, 1993\n\
84 The Regents of the University of California. All rights reserved.\n";
90 static char sccsid[] = "@(#)fortune.c 8.1 (Berkeley) 5/31/93";
93 static char rcsid[] = "$NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $";
97 #endif /* killing warnings */
99 #define PROGRAM_NAME "fortune-mod"
100 #define PROGRAM_VERSION "9708"
102 #ifdef HAVE_STDBOOL_H
104 #else /* ! HAVE_STDBOOL_H */
106 /* stdbool.h for GNU. */
108 /* The type `bool' must promote to `int' or `unsigned int'. The constants
109 `true' and `false' must have the value 0 and 1 respectively. */
116 /* The names `true' and `false' must also be made available as macros. */
120 /* Signal that all the definitions are present. */
121 #define __bool_true_false_are_defined 1
123 #endif /* HAVE_STDBOOL_H */
125 #include <sys/types.h>
126 #include <sys/time.h>
127 #include <sys/param.h>
128 #include <sys/stat.h>
129 #include <netinet/in.h>
142 #include <langinfo.h>
146 /* This makes GNU libc to prototype the BSD regex functions */
148 #define _REGEX_RE_COMP
167 #define MINW 6 /* minimum wait if desired */
168 #define CPERS 20 /* # of chars for each sec */
170 #define POS_UNKNOWN ((int32_t) -1) /* pos for file unknown */
171 #define NO_PROB (-1) /* no prob specified for file */
174 #define DPRINTF(l,x) if (Debug >= l) fprintf x;
189 char *datfile, *posfile;
195 struct fd *child, *parent;
196 struct fd *next, *prev;
200 bool Found_one; /* did we find a match? */
201 bool Find_files = FALSE; /* just find a list of proper fortune files */
202 bool Wait = FALSE; /* wait desired after fortune */
203 bool Short_only = FALSE; /* short fortune desired */
204 bool Long_only = FALSE; /* long fortune desired */
205 bool Offend = FALSE; /* offensive fortunes only */
206 bool All_forts = FALSE; /* any fortune allowed */
207 bool Equal_probs = FALSE; /* scatter un-allocated prob equally */
208 bool Show_filename = FALSE;
210 bool ErrorMessage = FALSE; /* Set to true if an error message has been displayed */
213 bool Match = FALSE; /* dump fortunes matching a pattern */
217 bool Debug = FALSE; /* print debug messages */
221 unsigned char *Fortbuf = NULL; /* fortune buffer for -m */
223 int Fort_len = 0, Spec_prob = 0, /* total prob specified on cmd line */
224 Num_files, Num_kids, /* totals of files and children. */
225 SLEN = 160; /* max. characters in a "short" fortune */
227 int32_t Seekpts[2]; /* seek pointers to fortunes */
229 FILEDESC *File_list = NULL, /* Head of file list */
230 *File_tail = NULL; /* Tail of file list */
231 FILEDESC *Fortfile; /* Fortune file to use */
233 STRFILE Noprob_tbl; /* sum of data for all no prob files */
237 #define RE_COMP(p) re_comp(p)
238 #define BAD_COMP(f) ((f) != NULL)
239 #define RE_EXEC(p) re_exec(p)
244 #define RE_COMP(p) regcomp(&Re_pat, (p), REG_NOSUB)
245 #define BAD_COMP(f) ((f) != 0)
246 #define RE_EXEC(p) (regexec(&Re_pat, (p), 0, NULL, 0) == 0)
251 #endif /* POSIX_REGEX */
253 #endif /* BSD_REGEX */
255 RECODE_REQUEST request;
258 int add_dir(register FILEDESC *);
260 char *program_version(void)
262 static char buf[BUFSIZ];
263 (void) sprintf(buf, "%s version %s", PROGRAM_NAME, PROGRAM_VERSION);
269 (void) fprintf(stderr, "%s\n",program_version());
270 (void) fprintf(stderr, "fortune [-a");
272 (void) fprintf(stderr, "D");
274 (void) fprintf(stderr, "f");
276 (void) fprintf(stderr, "i");
277 #endif /* NO_REGEX */
278 (void) fprintf(stderr, "losw]");
280 (void) fprintf(stderr, " [-m pattern]");
281 #endif /* NO_REGEX */
282 (void) fprintf(stderr, " [-n number] [ [#%%] file/directory/all]\n");
286 #define STR(str) ((str) == NULL ? "NULL" : (str))
291 * Set the global values for number of files/children, to be used
292 * in printing probabilities when listing files
294 void calc_equal_probs(void)
296 FILEDESC *fiddlylist;
298 Num_files = Num_kids = 0;
299 fiddlylist = File_list;
300 while (fiddlylist != NULL)
303 Num_kids += fiddlylist->num_children;
304 fiddlylist = fiddlylist->next;
310 * Print out the actual list, recursively.
312 void print_list(register FILEDESC * list, int lev)
316 fprintf(stderr, "%*s", lev * 4, "");
317 if (list->percent == NO_PROB)
319 /* This, with some changes elsewhere, gives proper percentages for every case
320 * fprintf(stderr, "___%%"); */
321 fprintf(stderr, "%5.2f%%", (100.0 - Spec_prob) *
322 list->tbl.str_numstr / Noprob_tbl.str_numstr);
324 fprintf(stderr, "%5.2f%%", 100.0 / Num_files);
326 fprintf(stderr, "%5.2f%%", 100.0 / Num_kids);
328 fprintf(stderr, "%5.2f%%", 1.0 * list->percent);
329 fprintf(stderr, " %s", STR(list->name));
330 DPRINTF(1, (stderr, " (%s, %s, %s)\n", STR(list->path),
331 STR(list->datfile), STR(list->posfile)));
333 if (list->child != NULL)
334 print_list(list->child, lev + 1);
342 * Convert the pattern to an ignore-case equivalent.
344 char *conv_pat(register char *orig)
347 register unsigned int cnt;
350 cnt = 1; /* allow for '\0' */
351 for (sp = orig; *sp != '\0'; sp++)
356 if ((new = malloc(cnt)) == NULL)
358 fprintf(stderr, "pattern too long for ignoring case\n");
362 for (sp = new; *orig != '\0'; orig++)
368 *sp++ = toupper(*orig);
371 else if (isupper(*orig))
375 *sp++ = tolower(*orig);
384 #endif /* NO_REGEX */
388 * Do a malloc, checking for NULL return.
390 void *do_malloc(unsigned int size)
394 if ((new = malloc(size)) == NULL)
396 (void) fprintf(stderr, "fortune: out of memory.\n");
404 * Free malloc'ed space, if any.
406 void do_free(void *ptr)
414 * Return a malloc()'ed copy of the string
416 char *copy(char *str, unsigned int len)
420 new = do_malloc(len + 1);
432 * Return a pointer to an initialized new FILEDESC.
434 FILEDESC *new_fp(void)
436 register FILEDESC *fp;
438 fp = (FILEDESC *) do_malloc(sizeof *fp);
440 fp->pos = POS_UNKNOWN;
443 fp->percent = NO_PROB;
444 fp->read_tbl = FALSE;
445 fp->tbl.str_version = 0;
446 fp->tbl.str_numstr = 0;
447 fp->tbl.str_longlen = 0;
448 fp->tbl.str_shortlen = 0;
449 fp->tbl.str_flags = 0;
450 fp->tbl.stuff[0] = 0;
451 fp->tbl.stuff[1] = 0;
452 fp->tbl.stuff[2] = 0;
453 fp->tbl.stuff[3] = 0;
465 * Return TRUE if the file is a directory, FALSE otherwise.
467 int is_dir(char *file)
469 auto struct stat sbuf;
471 if (stat(file, &sbuf) < 0)
473 return (sbuf.st_mode & S_IFDIR);
478 * Return TRUE if the file exists, FALSE otherwise.
480 int is_existant(char *file)
484 if (stat(file, &staat) == 0)
492 perror("fortune: bad juju in is_existant");
499 * Return TRUE if the file is a fortune database file. We try and
500 * exclude files without reading them if possible to avoid
501 * overhead. Files which start with ".", or which have "illegal"
502 * suffixes, as contained in suflist[], are ruled out.
504 int is_fortfile(char *file, char **datp, char **posp)
508 register char *datfile;
509 static char *suflist[] =
510 { /* list of "illegal" suffixes" */
511 "dat", "pos", "c", "h", "p", "i", "f",
512 "pas", "ftn", "ins.c", "ins,pas",
517 DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file));
519 if ((sp = strrchr(file, '/')) == NULL)
525 DPRINTF(2, (stderr, "FALSE (file starts with '.')\n"));
528 if ((sp = strrchr(sp, '.')) != NULL)
531 for (i = 0; suflist[i] != NULL; i++)
532 if (strcmp(sp, suflist[i]) == 0)
534 DPRINTF(2, (stderr, "FALSE (file has suffix \".%s\")\n", sp));
539 datfile = copy(file, (unsigned int) (strlen(file) + 4)); /* +4 for ".dat" */
540 strcat(datfile, ".dat");
541 if (access(datfile, R_OK) < 0)
544 DPRINTF(2, (stderr, "FALSE (no \".dat\" file)\n"));
551 DPRINTF(2, (stderr, "TRUE\n"));
557 * Add a file to the file list.
559 int add_file(int percent, register char *file, char *dir,
560 FILEDESC ** head, FILEDESC ** tail, FILEDESC * parent)
562 register FILEDESC *fp;
564 register char *path, *testpath;
565 register bool was_malloc;
578 path = do_malloc((unsigned int) (strlen(dir) + strlen(file) + 2));
579 (void) strcat(strcat(strcpy(path, dir), "/"), file);
582 if (*path == '/' && !is_existant(path)) /* If doesn't exist, don't do anything. */
588 if ((isdir = is_dir(path)) && parent != NULL)
592 return FALSE; /* don't recurse */
595 DPRINTF(1, (stderr, "trying to add file \"%s\"\n", path));
596 if ((fd = open(path, O_RDONLY)) < 0 || *path != '/')
599 if (dir == NULL && (strchr(file,'/') == NULL))
601 if ( ((sp = strrchr(file,'-')) != NULL) && (strcmp(sp,"-o") == 0) )
603 /* BSD-style '-o' offensive file suffix */
605 found = (add_file(percent, file, LOCOFFDIR, head, tail, parent))
606 || add_file(percent, file, OFFDIR, head, tail, parent);
607 /* put the suffix back in for better identification later */
611 found = (add_file(percent, file, LOCFORTDIR, head, tail, parent)
612 || add_file(percent, file, LOCOFFDIR, head, tail, parent)
613 || add_file(percent, file, FORTDIR, head, tail, parent)
614 || add_file(percent, file, OFFDIR, head, tail, parent));
616 found = (add_file(percent, file, LOCOFFDIR, head, tail, parent)
617 || add_file(percent, file, OFFDIR, head, tail, parent));
619 found = (add_file(percent, file, LOCFORTDIR, head, tail, parent)
620 || add_file(percent, file, FORTDIR, head, tail, parent));
622 if (!found && parent == NULL && dir == NULL)
623 { /* don't display an error when trying language specific files */
626 lang=getenv("LC_ALL");
627 if (!lang) lang=getenv("LC_MESSAGES");
628 if (!lang) lang=getenv("LANGUAGE");
629 if (!lang) lang=getenv("LANG");
636 strncpy(llang,lang,sizeof(llang));
639 /* the language string can be like "es:fr_BE:ga" */
640 while (!ret && lang && (*lang)) {
643 snprintf(langdir,sizeof(langdir),"%s/%s",
646 if (strncmp(path,lang,2) == 0)
648 else if (strncmp(path,langdir,strlen(FORTDIR)+3) == 0)
664 DPRINTF(2, (stderr, "path = \"%s\"\n", path));
668 fp->percent = percent;
670 fp->name = do_malloc (strlen (file) + 1);
671 strncpy (fp->name, file, strlen (file) + 1);
673 fp->path = do_malloc (strlen (path) + 1);
674 strncpy (fp->path, path, strlen (path) + 1);
677 fp->utf8_charset = FALSE;
678 testpath = do_malloc(strlen (path) + 4);
679 sprintf(testpath, "%s.u8", path);
680 // fprintf(stderr, "State mal: %s\n", testpath);
681 if(stat(testpath, &statbuf) == 0)
682 fp->utf8_charset = TRUE;
683 // fprintf(stderr, "Is utf8?: %i\n", fp->utf8_charset );
687 if ((isdir && !add_dir(fp)) ||
689 !is_fortfile(path, &fp->datfile, &fp->posfile)))
693 "fortune:%s not a fortune file or directory\n",
697 do_free(fp->datfile);
698 do_free(fp->posfile);
699 if (fp->fd >= 0) close(fp->fd);
704 /* This is a hack to come around another hack - add_dir returns success
705 * if the directory is allowed to be empty, but we can not handle an
706 * empty directory... */
707 if (isdir && fp->num_children == 0) {
710 do_free(fp->datfile);
711 do_free(fp->posfile);
712 if(fp->fd >= 0) close(fp->fd);
720 else if (fp->percent == NO_PROB)
738 * Add the contents of an entire directory.
740 int add_dir(register FILEDESC * fp)
743 register struct dirent *dirent;
744 auto FILEDESC *tailp;
749 if ((dir = opendir(fp->path)) == NULL)
755 DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path));
756 fp->num_children = 0;
757 while ((dirent = readdir(dir)) != NULL)
759 if (dirent->d_name[0] == 0)
761 name = strdup(dirent->d_name);
762 if (add_file(NO_PROB, name, fp->path, &fp->child, &tailp, fp))
767 if (fp->num_children == 0)
770 * Only the local fortune dir and the local offensive dir are
771 * allowed to be empty.
772 * - Brian Bassett (brianb@debian.org) 1999/07/31
774 if (strcmp(LOCFORTDIR, fp->path) == 0 || strcmp(LOCOFFDIR, fp->path) == 0)
779 "fortune: %s: No fortune files in directory.\n", fp->path);
787 * Form the file list from the file specifications.
789 int form_file_list(register char **files, register int file_cnt)
791 register int i, percent;
795 char fullpathname[512],locpathname[512];
800 return (add_file(NO_PROB, LOCFORTDIR, NULL, &File_list,
802 | add_file(NO_PROB, LOCOFFDIR, NULL, &File_list,
804 | add_file(NO_PROB, FORTDIR, NULL, &File_list,
806 | add_file(NO_PROB, OFFDIR, NULL, &File_list,
809 return (add_file(NO_PROB, LOCOFFDIR, NULL, &File_list,
811 | add_file(NO_PROB, OFFDIR, NULL, &File_list,
816 lang=getenv("LC_ALL");
817 if (!lang) lang=getenv("LC_MESSAGES");
818 if (!lang) lang=getenv("LANGUAGE");
819 if (!lang) lang=getenv("LANG");
825 strncpy(llang,lang,sizeof(llang));
828 /* the language string can be like "es:fr_BE:ga" */
829 while ( lang && (*lang)) {
833 /* first try full locale */
834 ret=add_file(NO_PROB, lang, NULL, &File_list,
837 /* if not try language name only (two first chars) */
843 ret=add_file(NO_PROB, ll, NULL,
844 &File_list, &File_tail, NULL);
847 /* if we have found one we have finished */
853 return (add_file(NO_PROB, LOCFORTDIR, NULL, &File_list,
855 | add_file(NO_PROB, FORTDIR, NULL, &File_list,
860 /* no locales available, use default */
861 return (add_file(NO_PROB, LOCFORTDIR, NULL, &File_list,
863 | add_file(NO_PROB, FORTDIR, NULL, &File_list,
869 for (i = 0; i < file_cnt; i++)
872 if (!isdigit(files[i][0]))
877 for (sp = files[i]; isdigit(*sp); sp++)
878 percent = percent * 10 + *sp - '0';
881 fprintf(stderr, "percentages must be <= 100\n");
887 fprintf(stderr, "percentages must be integers\n");
892 * If the number isn't followed by a '%', then
893 * it was not a percentage, just the first part
894 * of a file name which starts with digits.
901 else if (*++sp == '\0')
905 fprintf(stderr, "percentages must precede files\n");
912 if (strcmp(sp, "all") == 0)
914 snprintf(fullpathname,sizeof(fullpathname),"%s",FORTDIR);
915 snprintf(locpathname,sizeof(locpathname),"%s",LOCFORTDIR);
917 /* if it isn't an absolute path or relative to . or ..
918 make it an absolute path relative to FORTDIR */
921 if (strncmp(sp,"/",1)!=0 && strncmp(sp,"./",2)!=0 &&
922 strncmp(sp,"../",3)!=0)
924 snprintf(fullpathname,sizeof(fullpathname),
926 snprintf(locpathname,sizeof(locpathname),
927 "%s/%s",LOCFORTDIR,sp);
931 snprintf(fullpathname,sizeof(fullpathname),"%s",sp);
932 snprintf(locpathname,sizeof(locpathname),"%s",sp);
936 lang=getenv("LC_ALL");
937 if (!lang) lang=getenv("LC_MESSAGES");
938 if (!lang) lang=getenv("LANGUAGE");
939 if (!lang) lang=getenv("LANG");
945 strncpy(llang,lang,sizeof(llang));
948 /* the language string can be like "es:fr_BE:ga" */
949 while (!ret && lang && (*lang)) {
953 /* first try full locale */
954 snprintf(langdir,sizeof(langdir),"%s/%s/%s",
956 ret=add_file(percent, langdir, NULL, &File_list,
959 /* if not try language name only (two first chars) */
965 snprintf(langdir,sizeof(langdir),
966 "%s/%s/%s", FORTDIR, ll, sp);
967 ret=add_file(percent, langdir, NULL,
968 &File_list, &File_tail, NULL);
975 ret=add_file(percent, fullpathname, NULL, &File_list,
977 if ( (!ret && fullpathname != locpathname) || strcmp(sp, "all") == 0 )
978 ret=add_file(percent, locpathname, NULL, &File_list,
982 snprintf (locpathname, sizeof (locpathname), "%s/%s", getenv ("PWD"), sp);
984 ret=add_file (percent, locpathname, NULL, &File_list, &File_tail, NULL);
991 if (!add_file(percent, fullpathname, NULL, &File_list,
999 * This routine evaluates the arguments on the command line
1001 void getargs(int argc, char **argv)
1003 register int ignore_case;
1006 register char *pat = NULL;
1008 #endif /* NO_REGEX */
1009 extern char *optarg;
1013 ignore_case = FALSE;
1016 while ((ch = getopt(argc, argv, "acDefilm:n:osvw")) != EOF)
1018 while ((ch = getopt(argc, argv, "acefilm:n:osvw")) != EOF)
1022 case 'a': /* any fortune */
1031 Equal_probs++; /* scatter un-allocted prob equally */
1033 case 'f': /* find fortune files */
1036 case 'l': /* long ones only */
1041 SLEN = atoi(optarg);
1043 case 'o': /* offensive ones only */
1046 case 's': /* short ones only */
1050 case 'w': /* give time to read */
1054 case 'i': /* case-insensitive match */
1055 case 'm': /* dump out the fortunes */
1056 (void) fprintf(stderr,
1057 "fortune: can't match fortunes on this system (Sorry)\n");
1059 #else /* NO_REGEX */
1060 case 'm': /* dump out the fortunes */
1064 case 'i': /* case-insensitive match */
1067 #endif /* NO_REGEX */
1069 (void) printf("%s\n", program_version());
1081 if (!form_file_list(argv, argc))
1083 if (!ErrorMessage) fprintf (stderr, "No fortunes found\n");
1084 exit(1); /* errors printed through form_file_list() */
1088 * print_list(File_list, 0); */
1090 /* If (Find_files) print_list() moved to main */
1095 pat = conv_pat(pat);
1096 if (BAD_COMP(RE_COMP(pat)))
1098 fprintf(stderr, "bad pattern: %s\n", pat);
1102 #endif /* NO_REGEX */
1107 * Initialize the fortune probabilities.
1109 void init_prob(void)
1111 register FILEDESC *fp, *last;
1112 register int percent, num_noprob, frac;
1115 * Distribute the residual probability (if any) across all
1116 * files with unspecified probability (i.e., probability of 0)
1123 for (fp = File_tail; fp != NULL; fp = fp->prev)
1124 if (fp->percent == NO_PROB)
1131 percent += fp->percent;
1132 DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's\n",
1133 percent, num_noprob));
1137 "fortune: probabilities sum to %d%%!\n", percent);
1140 else if (percent < 100 && num_noprob == 0)
1143 "fortune: no place to put residual probability (%d%%)\n",
1147 else if (percent == 100 && num_noprob != 0)
1150 "fortune: no probability left to put in residual files\n");
1153 Spec_prob = percent; /* this is for -f when % is specified on cmd line */
1154 percent = 100 - percent;
1157 if (num_noprob != 0)
1161 frac = percent / num_noprob;
1162 DPRINTF(1, (stderr, ", frac = %d%%", frac));
1163 for (fp = File_tail; fp != last; fp = fp->prev)
1164 if (fp->percent == NO_PROB)
1170 last->percent = percent;
1171 DPRINTF(1, (stderr, ", residual = %d%%", percent));
1176 ", %d%% distributed over remaining fortunes\n",
1180 DPRINTF(1, (stderr, "\n"));
1184 * print_list(File_list, 0); *//* Causes crash with new %% code */
1190 * Zero out the fields we care about in a tbl structure.
1192 void zero_tbl(register STRFILE * tp)
1195 tp->str_longlen = 0;
1196 tp->str_shortlen = -1;
1201 * Merge the tbl data of t2 into t1.
1203 void sum_tbl(register STRFILE * t1, register STRFILE * t2)
1205 t1->str_numstr += t2->str_numstr;
1206 if (t1->str_longlen < t2->str_longlen)
1207 t1->str_longlen = t2->str_longlen;
1208 if (t1->str_shortlen > t2->str_shortlen)
1209 t1->str_shortlen = t2->str_shortlen;
1214 * Get the tbl data file the datfile.
1216 void get_tbl(FILEDESC * fp)
1219 register FILEDESC *child;
1223 if (fp->child == NULL)
1226 /* This should not be needed anymore since add_file takes care of
1227 * empty directories now (Torsten Landschoff <torsten@debian.org>)
1231 * Only the local fortune dir and the local offensive dir are
1232 * allowed to be empty. Don't try and fetch their tables if
1233 * they have no children (i.e. are empty).
1234 * - Brian Bassett (brianb@debian.org) 1999/07/31
1236 if (strcmp(LOCFORTDIR, fp->path) == 0 || strcmp(LOCOFFDIR, fp->path) == 0)
1238 fp->read_tbl = TRUE; /* Make it look like we've read it. */
1243 if ((fd = open(fp->datfile, O_RDONLY)) < 0)
1245 perror(fp->datfile);
1248 if (read(fd, &fp->tbl.str_version, sizeof fp->tbl.str_version) !=
1249 sizeof fp->tbl.str_version)
1252 "fortune: %s corrupted\n", fp->path);
1255 if (read(fd, &fp->tbl.str_numstr, sizeof fp->tbl.str_numstr) !=
1256 sizeof fp->tbl.str_numstr)
1259 "fortune: %s corrupted\n", fp->path);
1262 if (read(fd, &fp->tbl.str_longlen, sizeof fp->tbl.str_longlen) !=
1263 sizeof fp->tbl.str_longlen)
1266 "fortune: %s corrupted\n", fp->path);
1269 if (read(fd, &fp->tbl.str_shortlen, sizeof fp->tbl.str_shortlen) !=
1270 sizeof fp->tbl.str_shortlen)
1273 "fortune: %s corrupted\n", fp->path);
1276 if (read(fd, &fp->tbl.str_flags, sizeof fp->tbl.str_flags) !=
1277 sizeof fp->tbl.str_flags)
1280 "fortune: %s corrupted\n", fp->path);
1283 if (read(fd, &fp->tbl.stuff, sizeof fp->tbl.stuff) !=
1284 sizeof fp->tbl.stuff)
1287 "fortune: %s corrupted\n", fp->path);
1290 fp->tbl.str_version = ntohl(fp->tbl.str_version);
1291 fp->tbl.str_numstr = ntohl(fp->tbl.str_numstr);
1292 fp->tbl.str_longlen = ntohl(fp->tbl.str_longlen);
1293 fp->tbl.str_shortlen = ntohl(fp->tbl.str_shortlen);
1294 fp->tbl.str_flags = ntohl(fp->tbl.str_flags);
1300 for (child = fp->child; child != NULL; child = child->next)
1303 sum_tbl(&fp->tbl, &child->tbl);
1306 fp->read_tbl = TRUE;
1311 * Sum up all the noprob probabilities, starting with fp.
1313 void sum_noprobs(register FILEDESC * fp)
1315 static bool did_noprobs = FALSE;
1319 zero_tbl(&Noprob_tbl);
1323 /* This conditional should help us return correct values for -f
1324 * when a percentage is specified */
1325 if (fp->percent == NO_PROB)
1326 sum_tbl(&Noprob_tbl, &fp->tbl);
1334 * Pick a child from a chosen parent.
1336 FILEDESC *pick_child(FILEDESC * parent)
1338 register FILEDESC *fp;
1339 register int choice;
1343 choice = random() % parent->num_children;
1344 DPRINTF(1, (stderr, " choice = %d (of %d)\n",
1345 choice, parent->num_children));
1346 for (fp = parent->child; choice--; fp = fp->next)
1348 DPRINTF(1, (stderr, " using %s\n", fp->name));
1354 choice = random() % parent->tbl.str_numstr;
1355 DPRINTF(1, (stderr, " choice = %d (of %ld)\n",
1356 choice, parent->tbl.str_numstr));
1357 for (fp = parent->child; choice >= fp->tbl.str_numstr;
1360 choice -= fp->tbl.str_numstr;
1361 DPRINTF(1, (stderr, "\tskip %s, %ld (choice = %d)\n",
1362 fp->name, fp->tbl.str_numstr, choice));
1364 DPRINTF(1, (stderr, " using %s, %ld\n", fp->name,
1365 fp->tbl.str_numstr));
1372 * Open up the dat file if we need to.
1374 void open_dat(FILEDESC * fp)
1376 if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, O_RDONLY)) < 0)
1378 perror(fp->datfile);
1385 * Get the position from the pos file, if there is one. If not,
1386 * return a random number.
1388 void get_pos(FILEDESC * fp)
1390 assert(fp->read_tbl);
1391 if (fp->pos == POS_UNKNOWN)
1393 fp->pos = random() % fp->tbl.str_numstr;
1395 if (++(fp->pos) >= fp->tbl.str_numstr)
1396 fp->pos -= fp->tbl.str_numstr;
1397 DPRINTF(1, (stderr, "pos for %s is %ld\n", fp->name, fp->pos));
1402 * Get the fortune data file's seek pointer for the next fortune.
1406 register FILEDESC *fp;
1407 register int choice;
1409 if (File_list->next == NULL || File_list->percent == NO_PROB)
1413 choice = random() % 100;
1414 DPRINTF(1, (stderr, "choice = %d\n", choice));
1415 for (fp = File_list; fp->percent != NO_PROB; fp = fp->next)
1416 if (choice < fp->percent)
1420 choice -= fp->percent;
1422 " skip \"%s\", %d%% (choice = %d)\n",
1423 fp->name, fp->percent, choice));
1426 "using \"%s\", %d%% (choice = %d)\n",
1427 fp->name, fp->percent, choice));
1429 if (fp->percent != NO_PROB)
1433 if (fp->next != NULL)
1436 choice = random() % Noprob_tbl.str_numstr;
1437 DPRINTF(1, (stderr, "choice = %d (of %ld) \n", choice,
1438 Noprob_tbl.str_numstr));
1439 while (choice >= fp->tbl.str_numstr)
1441 choice -= fp->tbl.str_numstr;
1444 " skip \"%s\", %ld (choice = %d)\n",
1445 fp->name, fp->tbl.str_numstr,
1448 DPRINTF(1, (stderr, "using \"%s\", %ld\n", fp->name,
1449 fp->tbl.str_numstr));
1453 if (fp->tbl.str_numstr == 0)
1455 fprintf(stderr, "fortune: no fortune found\n");
1458 if (fp->child != NULL)
1460 DPRINTF(1, (stderr, "picking child\n"));
1461 fp = pick_child(fp);
1467 (off_t) (sizeof fp->tbl + fp->pos * sizeof Seekpts[0]), 0);
1468 read(fp->datfd, &Seekpts[0], sizeof Seekpts[0]);
1469 read(fp->datfd, &Seekpts[1], sizeof Seekpts[1]);
1470 Seekpts[0] = ntohl(Seekpts[0]);
1471 Seekpts[1] = ntohl(Seekpts[1]);
1476 * Assocatiate a FILE * with the given FILEDESC.
1478 void open_fp(FILEDESC * fp)
1480 if (fp->inf == NULL && (fp->inf = fdopen(fp->fd, "r")) == NULL)
1490 * Return the maximum fortune len in the file list.
1492 int maxlen_in_list(FILEDESC * list)
1494 register FILEDESC *fp;
1495 register int len, maxlen;
1498 for (fp = list; fp != NULL; fp = fp->next)
1500 if (fp->child != NULL)
1502 if ((len = maxlen_in_list(fp->child)) > maxlen)
1508 if (fp->tbl.str_longlen > maxlen)
1509 maxlen = fp->tbl.str_longlen;
1517 * Print out the matches from the files in the list.
1519 void matches_in_list(FILEDESC * list)
1522 unsigned char *p; /* -allover */
1523 unsigned char ch; /* -allover */
1524 register FILEDESC *fp;
1528 for (fp = list; fp != NULL; fp = fp->next)
1530 if (fp->child != NULL)
1532 matches_in_list(fp->child);
1535 DPRINTF(1, (stderr, "searching in %s\n", fp->path));
1539 while (fgets(sp, Fort_len, fp->inf) != NULL)
1540 if (!STR_ENDSTRING(sp, fp->tbl))
1545 nchar = sp - Fortbuf;
1547 if (fp->utf8_charset)
1549 output = recode_string (request, Fortbuf);
1553 /* Should maybe rot13 Fortbuf -allover */
1555 if(fp->tbl.str_flags & STR_ROTATED)
1557 for (p = output; (ch = *p); ++p)
1559 if (isupper(ch) && isascii(ch))
1560 *p = 'A' + (ch - 'A' + 13) % 26;
1561 else if (islower(ch) && isascii(ch))
1562 *p = 'a' + (ch - 'a' + 13) % 26;
1566 DPRINTF(1, (stdout, "nchar = %d\n", nchar));
1567 if ( (nchar < SLEN || !Short_only) &&
1568 (nchar > SLEN || !Long_only) &&
1573 fprintf(stderr, "(%s)\n%c\n", fp->name, fp->tbl.str_delim);
1577 fputs (output, stdout);
1578 printf("%c\n", fp->tbl.str_delim);
1581 if (fp->utf8_charset)
1591 * Find all the fortunes which match the pattern we've been given.
1593 int find_matches(void)
1595 Fort_len = maxlen_in_list(File_list);
1596 DPRINTF(2, (stderr, "Maximum length is %d\n", Fort_len));
1597 /* extra length, "%\n" is appended */
1598 Fortbuf = do_malloc((unsigned int) Fort_len + 10);
1601 matches_in_list(File_list);
1605 #endif /* NO_REGEX */
1607 void display(FILEDESC * fp)
1609 register char *p, ch;
1610 unsigned char line[BUFSIZ];
1613 fseek(fp->inf, (long) Seekpts[0], 0);
1615 printf ("(%s)\n%%\n", fp->name);
1616 for (Fort_len = 0; fgets(line, sizeof line, fp->inf) != NULL &&
1617 !STR_ENDSTRING(line, fp->tbl); Fort_len++)
1619 if (fp->tbl.str_flags & STR_ROTATED)
1621 for (p = line; (ch = *p); ++p)
1623 if (isupper(ch) && isascii(ch))
1624 *p = 'A' + (ch - 'A' + 13) % 26;
1625 else if (islower(ch) && isascii (ch))
1626 *p = 'a' + (ch - 'a' + 13) % 26;
1629 if(fp->utf8_charset) {
1631 output = recode_string (request, line);
1632 fputs(output, stdout);
1636 fputs(line, stdout);
1640 if(fp->utf8_charset) {
1641 recode_delete_request(request);
1647 * Return the length of the fortune.
1652 unsigned char line[BUFSIZ];
1654 if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED)))
1655 nchar = (Seekpts[1] - Seekpts[0]) - 2; /* for %^J delimiter */
1659 fseek(Fortfile->inf, (long) Seekpts[0], 0);
1661 while (fgets(line, sizeof line, Fortfile->inf) != NULL &&
1662 !STR_ENDSTRING(line, Fortfile->tbl))
1663 nchar += strlen(line);
1669 int max(register int i, register int j)
1671 return (i >= j ? i : j);
1674 int main(int ac, char *av[])
1676 char *ctype, *crequest;
1679 outer = recode_new_outer(true);
1680 request = recode_new_request (outer);
1682 setlocale(LC_ALL,"");
1683 ctype = nl_langinfo(CODESET);
1684 if(strcmp(ctype,"ANSI_X3.4-1968") == 0)
1687 crequest = malloc(strlen(ctype) + 7 + 1);
1688 sprintf(crequest, "UTF-8..%s", ctype);
1689 recode_scan_request (request, crequest);
1694 exit(find_matches() != 0);
1699 sum_noprobs(File_list);
1702 print_list(File_list, 0);
1705 srandom((int) (time((time_t *) NULL) + getpid()));
1710 while ((Short_only && fortlen() > SLEN) ||
1711 (Long_only && fortlen() <= SLEN));
1718 sleep((unsigned int) max(Fort_len / CPERS, MINW));