]> granicus.if.org Git - fortune-mod/blob - fortune-mod/fortune/fortune.c
Extract a method or a function.
[fortune-mod] / fortune-mod / fortune / fortune.c
1 /*      $NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $  */
2
3 /*-
4  * Copyright (c) 1986, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Ken Arnold.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
25  *
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
36  * SUCH DAMAGE.
37  */
38
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,
51  *    one stone!
52  * 6: Calculated probabilities for all files, so that -f will print them.
53  */
54
55 /* Changes Copyright (c) 1997 Dennis L. Clark.  All rights reserved.
56  *
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.
60  */
61
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
66  *    for details)
67  *  + The -s and -l options are now more consistent in their
68  *    interpretation of fortune length
69  *  + The -s and -l options can now be combined wit the -m option
70  */
71
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
76  *
77  * Added to debian by Alastair McKinstry, <mckinstry@computer.org>, 2002-07-31
78  */
79
80 #if 0                           /* comment out the stuff here, and get rid of silly warnings */
81 #ifndef lint
82 static char copyright[] =
83 "@(#) Copyright (c) 1986, 1993\n\
84         The Regents of the University of California.  All rights reserved.\n";
85
86 #endif /* not lint */
87
88 #ifndef lint
89 #if 0
90 static char sccsid[] = "@(#)fortune.c   8.1 (Berkeley) 5/31/93";
91
92 #else
93 static char rcsid[] = "$NetBSD: fortune.c,v 1.8 1995/03/23 08:28:40 cgd Exp $";
94
95 #endif
96 #endif /* not lint */
97 #endif /* killing warnings */
98
99 #define         PROGRAM_NAME            "fortune-mod"
100
101 #include "fortune-mod-common.h"
102
103 #include        <dirent.h>
104 #include        <fcntl.h>
105 #include        <assert.h>
106 #include        <errno.h>
107 #include        <locale.h>
108 #ifndef _WIN32
109 #include        <langinfo.h>
110 #define O_BINARY 0
111 #define WITH_RECODE
112 #endif
113 #ifdef WITH_RECODE
114 #include        <recode.h>
115 #endif
116
117
118 #ifdef HAVE_REGEX_H
119 #include        <regex.h>
120 #endif
121 #ifdef HAVE_REGEXP_H
122 #include        <regexp.h>
123 #endif
124 #ifdef HAVE_RX_H
125 #include        <rx.h>
126 #endif
127
128 #include        "config.h"
129
130 #define TRUE    1
131 #define FALSE   0
132
133 #define MINW    6               /* minimum wait if desired */
134 #define CPERS   20              /* # of chars for each sec */
135
136 #define POS_UNKNOWN     ((int32_t) -1)  /* pos for file unknown */
137 #define NO_PROB         (-1)    /* no prob specified for file */
138
139 #ifdef DEBUG
140 #define DPRINTF(l,x)    if (Debug >= l) fprintf x;
141 #else
142 #define DPRINTF(l,x)
143 #endif
144
145 typedef struct fd
146 {
147     int percent;
148     int fd, datfd;
149     int32_t pos;
150     FILE *inf;
151     char *name;
152     char *path;
153     char *datfile, *posfile;
154     bool read_tbl;
155     bool was_pos_file;
156     bool utf8_charset;
157     STRFILE tbl;
158     int num_children;
159     struct fd *child, *parent;
160     struct fd *next, *prev;
161 }
162 FILEDESC;
163
164 static char * env_lang;
165
166 static bool Found_one;                 /* did we find a match? */
167 static bool Find_files = FALSE;        /* just find a list of proper fortune files */
168 static bool Wait = FALSE;              /* wait desired after fortune */
169 static bool Short_only = FALSE;        /* short fortune desired */
170 static bool Long_only = FALSE;         /* long fortune desired */
171 static bool Offend = FALSE;            /* offensive fortunes only */
172 static bool All_forts = FALSE;         /* any fortune allowed */
173 static bool Equal_probs = FALSE;       /* scatter un-allocated prob equally */
174 static bool Show_filename = FALSE;
175 static bool No_recode = FALSE; /* Do we want to stop recoding from occuring */
176
177 static bool ErrorMessage = FALSE;      /* Set to true if an error message has been displayed */
178
179 #ifndef NO_REGEX
180 static bool Match = FALSE;             /* dump fortunes matching a pattern */
181
182 #endif
183 #ifdef DEBUG
184 static bool Debug = FALSE;             /* print debug messages */
185
186 #endif
187
188 static unsigned char *Fortbuf = NULL;  /* fortune buffer for -m */
189
190 static int Fort_len = 0, Spec_prob = 0,        /* total prob specified on cmd line */
191   Num_files, Num_kids,          /* totals of files and children. */
192   SLEN = 160;                   /* max. characters in a "short" fortune */
193
194 static int32_t Seekpts[2];             /* seek pointers to fortunes */
195
196 static FILEDESC *File_list = NULL,     /* Head of file list */
197  *File_tail = NULL;             /* Tail of file list */
198 static FILEDESC *Fortfile;             /* Fortune file to use */
199
200 static STRFILE Noprob_tbl;             /* sum of data for all no prob files */
201
202 #ifdef POSIX_REGEX
203 #define RE_COMP(p)      regcomp(&Re_pat, (p), REG_NOSUB)
204 #define BAD_COMP(f)     ((f) != 0)
205 #define RE_EXEC(p)      (regexec(&Re_pat, (p), 0, NULL, 0) == 0)
206
207 static regex_t Re_pat;
208 #else
209 #define NO_REGEX
210 #endif /* POSIX_REGEX */
211
212 #ifdef WITH_RECODE
213 static RECODE_REQUEST request;
214 static RECODE_OUTER outer;
215 #endif
216
217 int add_dir(register FILEDESC *);
218
219 static unsigned long my_random(unsigned long base)
220 {
221     FILE * fp;
222     unsigned long long l = 0;
223     char * hard_coded_val;
224
225     hard_coded_val = getenv("FORTUNE_MOD_RAND_HARD_CODED_VALS");
226     if (hard_coded_val)
227     {
228         return ((unsigned long)atol(hard_coded_val) % base);
229     }
230     if (getenv("FORTUNE_MOD_USE_SRAND"))
231     {
232         goto fallback;
233     }
234     fp = fopen("/dev/urandom", "rb");
235     if (! fp)
236     {
237         goto fallback;
238     }
239     if (fread(&l, sizeof(l), 1, fp) != 1)
240     {
241         fclose(fp);
242         goto fallback;
243     }
244     fclose(fp);
245     return l % base;
246 fallback:
247     return random() % base;
248 }
249
250 static char *program_version(void)
251 {
252     static char buf[BUFSIZ];
253     (void) sprintf(buf, "%s version %s", PROGRAM_NAME, VERSION);
254     return buf;
255 }
256
257 static void __attribute__((noreturn)) usage(void)
258 {
259     (void) fprintf(stderr, "%s\n",program_version());
260     (void) fprintf(stderr, "fortune [-a");
261 #ifdef  DEBUG
262     (void) fprintf(stderr, "D");
263 #endif /* DEBUG */
264     (void) fprintf(stderr, "f");
265 #ifndef NO_REGEX
266     (void) fprintf(stderr, "i");
267 #endif /* NO_REGEX */
268     (void) fprintf(stderr, "l");
269 #ifndef NO_OFFENSIVE
270     (void) fprintf(stderr, "o");
271 #endif
272     (void) fprintf(stderr, "sw]");
273 #ifndef NO_REGEX
274     (void) fprintf(stderr, " [-m pattern]");
275 #endif /* NO_REGEX */
276     (void) fprintf(stderr, " [-n number] [ [#%%] file/directory/all]\n");
277     exit(1);
278 }
279
280 #define STR(str)        ((str) == NULL ? "NULL" : (str))
281
282
283 /*
284  * calc_equal_probs:
285  *      Set the global values for number of files/children, to be used
286  * in printing probabilities when listing files
287  */
288 static void calc_equal_probs(void)
289 {
290     FILEDESC *fiddlylist;
291
292     Num_files = Num_kids = 0;
293     fiddlylist = File_list;
294     while (fiddlylist != NULL)
295     {
296         Num_files++;
297         Num_kids += fiddlylist->num_children;
298         fiddlylist = fiddlylist->next;
299     }
300 }
301
302 /*
303  * print_list:
304  *      Print out the actual list, recursively.
305  */
306 static void print_list(register FILEDESC * list, int lev)
307 {
308     while (list != NULL)
309     {
310         fprintf(stderr, "%*s", lev * 4, "");
311         if (list->percent == NO_PROB)
312             if (!Equal_probs)
313 /* This, with some changes elsewhere, gives proper percentages for every case
314  * fprintf(stderr, "___%%"); */
315                 fprintf(stderr, "%5.2f%%", (100.0 - Spec_prob) *
316                         list->tbl.str_numstr / Noprob_tbl.str_numstr);
317             else if (lev == 0)
318                 fprintf(stderr, "%5.2f%%", 100.0 / Num_files);
319             else
320                 fprintf(stderr, "%5.2f%%", 100.0 / Num_kids);
321         else
322             fprintf(stderr, "%5.2f%%", 1.0 * list->percent);
323         fprintf(stderr, " %s", STR(list->name));
324         DPRINTF(1, (stderr, " (%s, %s, %s)\n", STR(list->path),
325                     STR(list->datfile), STR(list->posfile)));
326         putc('\n', stderr);
327         if (list->child != NULL)
328             print_list(list->child, lev + 1);
329         list = list->next;
330     }
331 }
332
333 #ifndef NO_REGEX
334 /*
335  * conv_pat:
336  *      Convert the pattern to an ignore-case equivalent.
337  */
338 static char *conv_pat(register char *orig)
339 {
340     register char *sp;
341     register unsigned int cnt;
342     register char *new;
343
344     cnt = 1;                    /* allow for '\0' */
345     for (sp = orig; *sp != '\0'; sp++)
346         if (isalpha(*sp))
347             cnt += 4;
348         else
349             cnt++;
350     if ((new = malloc(cnt)) == NULL)
351     {
352         fprintf(stderr, "pattern too long for ignoring case\n");
353         exit(1);
354     }
355
356     for (sp = new; *orig != '\0'; orig++)
357     {
358         if (islower(*orig))
359         {
360             *sp++ = '[';
361             *sp++ = *orig;
362             *sp++ = (char)toupper(*orig);
363             *sp++ = ']';
364         }
365         else if (isupper(*orig))
366         {
367             *sp++ = '[';
368             *sp++ = *orig;
369             *sp++ = (char)tolower(*orig);
370             *sp++ = ']';
371         }
372         else
373             *sp++ = *orig;
374     }
375     *sp = '\0';
376     return new;
377 }
378 #endif /* NO_REGEX */
379
380 /*
381  * do_malloc:
382  *      Do a malloc, checking for NULL return.
383  */
384 static void *do_malloc(size_t size)
385 {
386     void *new;
387
388     if ((new = malloc(size)) == NULL)
389     {
390         (void) fprintf(stderr, "fortune: out of memory.\n");
391         exit(1);
392     }
393     return new;
394 }
395
396 /*
397  * do_free:
398  *      Free malloc'ed space, if any.
399  */
400 static void do_free(void *ptr)
401 {
402     if (ptr != NULL)
403         free(ptr);
404 }
405
406 /*
407  * copy:
408  *      Return a malloc()'ed copy of the string
409  */
410 static char *copy(char *str, unsigned int len)
411 {
412     char *new, *sp;
413
414     new = do_malloc(len + 1);
415     sp = new;
416     do
417     {
418         *sp++ = *str;
419     }
420     while (*str++);
421     return new;
422 }
423
424 /*
425  * new_fp:
426  *      Return a pointer to an initialized new FILEDESC.
427  */
428 static FILEDESC *new_fp(void)
429 {
430     register FILEDESC *fp;
431
432     fp = (FILEDESC *) do_malloc(sizeof *fp);
433     fp->datfd = -1;
434     fp->pos = POS_UNKNOWN;
435     fp->inf = NULL;
436     fp->fd = -1;
437     fp->percent = NO_PROB;
438     fp->read_tbl = FALSE;
439     fp->tbl.str_version = 0;
440     fp->tbl.str_numstr = 0;
441     fp->tbl.str_longlen = 0;
442     fp->tbl.str_shortlen = 0;
443     fp->tbl.str_flags = 0;
444     fp->tbl.stuff[0] = 0;
445     fp->tbl.stuff[1] = 0;
446     fp->tbl.stuff[2] = 0;
447     fp->tbl.stuff[3] = 0;
448     fp->next = NULL;
449     fp->prev = NULL;
450     fp->child = NULL;
451     fp->parent = NULL;
452     fp->datfile = NULL;
453     fp->posfile = NULL;
454     return fp;
455 }
456
457 #ifdef MYDEBUG
458 static inline void debugprint(const char *msg, ...)
459 {
460     va_list ap;
461
462     va_start(ap, msg);
463     vfprintf(stderr, msg, ap);
464     fflush(stderr);
465     va_end(ap);
466 }
467 #else
468 #define debugprint(format, ...) {}
469 #endif
470 /*
471  * is_dir:
472  *      Return TRUE if the file is a directory, FALSE otherwise.
473  */
474 static int is_dir(const char *const file)
475 {
476     auto struct stat sbuf;
477
478     if (stat(file, &sbuf) < 0) {
479         debugprint( "is_dir failed for file=<%s>\n", file);
480         return -1;
481     }
482     const bool ret = ( (sbuf.st_mode & S_IFDIR) ? true : false);
483     debugprint( "is_dir for file=<%s> gave ret=<%d>\n", file, ret);
484     return ret;
485 }
486
487 /*
488  * is_existant:
489  *      Return TRUE if the file exists, FALSE otherwise.
490  */
491 static int is_existant(char *file)
492 {
493     struct stat staat;
494
495     if (stat(file, &staat) == 0)
496         return TRUE;
497     switch(errno)
498     {
499         case ENOENT:
500         case ENOTDIR:
501             return FALSE;
502         default:
503             perror("fortune: bad juju in is_existant");
504             exit(1);
505     }
506 }
507
508 /*
509  * is_fortfile:
510  *      Return TRUE if the file is a fortune database file.  We try and
511  *      exclude files without reading them if possible to avoid
512  *      overhead.  Files which start with ".", or which have "illegal"
513  *      suffixes, as contained in suflist[], are ruled out.
514  */
515 static int is_fortfile(char *file, char **datp)
516 {
517     register int i;
518     register char *sp;
519     register char *datfile;
520     static const char *suflist[] =
521     {                           /* list of "illegal" suffixes" */
522         "dat", "pos", "c", "h", "p", "i", "f",
523         "pas", "ftn", "ins.c", "ins,pas",
524         "ins.ftn", "sml",
525         NULL
526     };
527
528     DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file));
529
530     if ((sp = strrchr(file, '/')) == NULL)
531         sp = file;
532     else
533         sp++;
534     if (*sp == '.')
535     {
536         DPRINTF(2, (stderr, "FALSE (file starts with '.')\n"));
537         return FALSE;
538     }
539     if ((sp = strrchr(sp, '.')) != NULL)
540     {
541         sp++;
542         for (i = 0; suflist[i] != NULL; i++)
543             if (strcmp(sp, suflist[i]) == 0)
544             {
545                 DPRINTF(2, (stderr, "FALSE (file has suffix \".%s\")\n", sp));
546                 return FALSE;
547             }
548     }
549
550     datfile = copy(file, (unsigned int) (strlen(file) + 4));    /* +4 for ".dat" */
551     strcat(datfile, ".dat");
552     if (access(datfile, R_OK) < 0)
553     {
554         free(datfile);
555         DPRINTF(2, (stderr, "FALSE (no \".dat\" file)\n"));
556         return FALSE;
557     }
558     if (datp != NULL)
559         *datp = datfile;
560     else
561         free(datfile);
562     DPRINTF(2, (stderr, "TRUE\n"));
563     return TRUE;
564 }
565
566 static bool path_is_absolute(const char *const path)
567 {
568     if (path[0] == '/')
569     {
570         return true;
571     }
572 #ifdef _WIN32
573     if (isalpha(path[0]) && path[1] == ':' && path[2] == '/')
574     {
575         return true;
576     }
577 #endif
578     return false;
579 }
580 /*
581  * add_file:
582  *      Add a file to the file list.
583  */
584 static int add_file(int percent, register const char *file, const char *dir,
585              FILEDESC ** head, FILEDESC ** tail, FILEDESC * parent)
586 {
587     register FILEDESC *fp;
588     register int fd = -1;
589     register char *path, *testpath;
590     register bool was_malloc;
591     auto char *sp;
592     auto bool found;
593     struct stat statbuf;
594
595     if (dir == NULL)
596     {
597         path = strdup(file);
598         was_malloc = TRUE;
599     }
600     else
601     {
602         path = do_malloc((unsigned int) (strlen(dir) + strlen(file) + 2));
603         (void) strcat(strcat(strcpy(path, dir), "/"), file);
604         was_malloc = TRUE;
605     }
606     if (*path == '/' && !is_existant(path))     /* If doesn't exist, don't do anything. */
607     {
608         if (was_malloc)
609             free(path);
610         return FALSE;
611     }
612     const int isdir = is_dir(path);
613     if ((isdir > 0 && parent != NULL) || (isdir < 0))
614     {
615         if (was_malloc)
616             free(path);
617         return FALSE;           /* don't recurse */
618     }
619
620     DPRINTF(1, (stderr, "trying to add file \"%s\"\n", path));
621     if (
622     (
623 #ifdef _WIN32
624         (!isdir) &&
625 #endif
626        ( (fd = open(path, O_RDONLY|O_BINARY)) < 0)
627     )
628         || !path_is_absolute(path))
629     {
630         debugprint("sarahhhhh fd=%d path=<%s> dir=<%s> file=<%s> percent=%d\n", fd, path, dir, file, percent);
631       found = FALSE;
632         if (dir == NULL && (strchr(file,'/') == NULL))
633         {
634             if ( ((sp = strrchr(file,'-')) != NULL) && (strcmp(sp,"-o") == 0) )
635             {
636                 /* BSD-style '-o' offensive file suffix */
637                 *sp = '\0';
638                 found = (add_file(percent, file, LOCOFFDIR, head, tail, parent))
639                          || add_file(percent, file, OFFDIR, head, tail, parent);
640                 /* put the suffix back in for better identification later */
641                 *sp = '-';
642             }
643             else if (All_forts)
644                 found = (add_file(percent, file, LOCFORTDIR, head, tail, parent)
645                          || add_file(percent, file, LOCOFFDIR, head, tail, parent)
646                          || add_file(percent, file, FORTDIR, head, tail, parent)
647                          || add_file(percent, file, OFFDIR, head, tail, parent));
648             else if (Offend)
649                 found = (add_file(percent, file, LOCOFFDIR, head, tail, parent)
650                          || add_file(percent, file, OFFDIR, head, tail, parent));
651             else
652                 found = (add_file(percent, file, LOCFORTDIR, head, tail, parent)
653                          || add_file(percent, file, FORTDIR, head, tail, parent));
654         }
655         if (!found && parent == NULL && dir == NULL)
656         { /* don't display an error when trying language specific files */
657           if (env_lang) {
658             char *lang;
659             char llang[512];
660             char langdir[1024];
661             int ret=0;
662             char *p;
663
664             strncpy(llang,env_lang,sizeof(llang));
665             llang[sizeof(llang)-1] = '\0';
666             lang=llang;
667
668             /* the language string can be like "es:fr_BE:ga" */
669             while (!ret && lang && (*lang)) {
670               p=strchr(lang,':');
671               if (p) *p++='\0';
672               snprintf(langdir,sizeof(langdir),"%s/%s",
673                        FORTDIR,lang);
674
675               if (strncmp(path,lang,2) == 0)
676                 ret=1;
677               else if (strncmp(path,langdir,strlen(FORTDIR)+3) == 0)
678                 ret=1;
679               lang=p;
680             }
681             if (!ret) {
682               debugprint("moshe\n");
683               perror(path);
684             }
685           } else {
686               debugprint( "abe\n");
687             perror(path);
688           }
689         }
690
691         if (was_malloc)
692             free(path);
693         return found;
694     }
695
696     DPRINTF(2, (stderr, "path = \"%s\"\n", path));
697
698     fp = new_fp();
699     fp->fd = fd;
700     fp->percent = percent;
701
702     fp->name = do_malloc (strlen (file) + (size_t)1);
703     strncpy (fp->name, file, strlen (file) + (size_t)1);
704
705     fp->path = do_malloc (strlen (path) + (size_t)1);
706     strncpy (fp->path, path, strlen (path) + 1UL);
707
708     //FIXME
709     fp->utf8_charset = FALSE;
710     testpath = do_malloc(strlen (path) + 4UL);
711     sprintf(testpath, "%s.u8", path);
712 //    fprintf(stderr, "State mal: %s\n", testpath);
713     if(stat(testpath, &statbuf) == 0)
714         fp->utf8_charset = TRUE;
715
716     free (testpath);
717     testpath = NULL;
718 //    fprintf(stderr, "Is utf8?: %i\n", fp->utf8_charset );
719
720     fp->parent = parent;
721
722     if ((isdir && !add_dir(fp)) ||
723         (!isdir &&
724          !is_fortfile(path, &fp->datfile)))
725     {
726         if (parent == NULL)
727             fprintf(stderr,
728                     "fortune:%s not a fortune file or directory\n",
729                     path);
730         if (was_malloc)
731             free(path);
732         do_free(fp->datfile);
733         do_free(fp->posfile);
734         do_free(fp->name);
735         do_free(fp->path);
736         if (fp->fd >= 0) close(fp->fd);
737         free(fp);
738         return FALSE;
739     }
740
741     /* This is a hack to come around another hack - add_dir returns success
742      * if the directory is allowed to be empty, but we can not handle an
743      * empty directory... */
744     if (isdir && fp->num_children == 0) {
745         if (was_malloc)
746             free(path);
747         do_free(fp->datfile);
748         do_free(fp->posfile);
749         do_free(fp->name);
750         do_free(fp->path);
751         if(fp->fd >= 0) close(fp->fd);
752         free(fp);
753         return TRUE;
754     }
755     /* End hack. */
756
757     if (*head == NULL)
758         *head = *tail = fp;
759     else if (fp->percent == NO_PROB)
760     {
761         (*tail)->next = fp;
762         fp->prev = *tail;
763         *tail = fp;
764     }
765     else
766     {
767         (*head)->prev = fp;
768         fp->next = *head;
769         *head = fp;
770     }
771
772     if (was_malloc)
773     {
774         free(path);
775         path = NULL;
776     }
777
778     return TRUE;
779 }
780
781 static int names_compare(const void *a, const void *b)
782 {
783     return strcmp(*(const char**)a, *(const char**)b);
784 }
785 /*
786  * add_dir:
787  *      Add the contents of an entire directory.
788  */
789 int add_dir(register FILEDESC * fp)
790 {
791     register DIR *dir;
792     register struct dirent *dirent;
793     auto FILEDESC *tailp;
794     auto char *name;
795     char **names;
796     size_t i, count_names, max_count_names;
797
798     close(fp->fd);
799     fp->fd = -1;
800     if ((dir = opendir(fp->path)) == NULL)
801     {
802         debugprint("yonah\n");
803         perror(fp->path);
804         return FALSE;
805     }
806     tailp = NULL;
807     DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path));
808     fp->num_children = 0;
809     max_count_names = 200;
810     count_names = 0;
811     names = malloc(sizeof(names[0])*max_count_names);
812     if (! names)
813     {
814         debugprint( "zach\n");
815         perror("Out of RAM!");
816         exit(-1);
817     }
818     while ((dirent = readdir(dir)) != NULL)
819     {
820         if (dirent->d_name[0] == 0)
821             continue;
822         name = strdup(dirent->d_name);
823         if (count_names == max_count_names)
824         {
825             max_count_names += 200;
826             names = realloc(names, sizeof(names[0])*max_count_names);
827             if (! names)
828             {
829                 debugprint("rebecca\n");
830                 perror("Out of RAM!");
831                 exit(-1);
832             }
833         }
834         names[count_names++] = name;
835     }
836     closedir(dir);
837     qsort(names, count_names, sizeof(names[0]), names_compare);
838
839     for (i=0; i < count_names; ++i)
840     {
841         if (add_file(NO_PROB, names[i], fp->path, &fp->child, &tailp, fp))
842         {
843             fp->num_children++;
844         }
845         free(names[i]);
846     }
847     free(names);
848     dir = NULL;
849     if (fp->num_children == 0)
850     {
851         /*
852          * Only the local fortune dir and the local offensive dir are
853          * allowed to be empty.
854          *  - Brian Bassett (brianb@debian.org) 1999/07/31
855          */
856         if (strcmp(LOCFORTDIR, fp->path) == 0 || strcmp(LOCOFFDIR, fp->path) == 0)
857         {
858             return TRUE;
859         }
860         fprintf(stderr,
861             "fortune: %s: No fortune files in directory.\n", fp->path);
862         return FALSE;
863     }
864     return TRUE;
865 }
866
867 /*
868 * form_file_list:
869 *      Form the file list from the file specifications.
870 */
871
872 static int top_level__add_file(const char *dirpath)
873 {
874     return add_file(NO_PROB, dirpath, NULL, &File_list, &File_tail, NULL);
875 }
876
877 static int cond_top_level__add_file(const char *dirpath, const char*possible_dup)
878 {
879     if (!strcmp(dirpath, possible_dup))
880     {
881         return 0;
882     }
883     return top_level__add_file(dirpath);
884 }
885
886 static int top_level_LOCFORTDIR(void)
887 {
888     return (top_level__add_file(LOCFORTDIR) | cond_top_level__add_file(FORTDIR, LOCFORTDIR));
889 }
890
891 static int form_file_list(register char **files, register int file_cnt)
892 {
893     register int i, percent;
894     register char *sp;
895     char langdir[1024];
896     char fullpathname[512],locpathname[512];
897
898     if (file_cnt == 0)
899     {
900         if (All_forts)
901             return (top_level__add_file(LOCFORTDIR)
902                 | top_level__add_file(LOCOFFDIR)
903                 | cond_top_level__add_file(FORTDIR, LOCFORTDIR)
904                 | cond_top_level__add_file(OFFDIR, LOCOFFDIR));
905         else if (Offend)
906             return ( top_level__add_file(LOCOFFDIR)
907                 | cond_top_level__add_file(OFFDIR, LOCOFFDIR));
908         else {
909             if (env_lang) {
910                 char *lang;
911                 char llang[512];
912                 int ret=0;
913                 char *p;
914
915                 strncpy(llang,env_lang,sizeof(llang));
916                 llang[sizeof(llang)-1] = '\0';
917                 lang=llang;
918
919                 /* the language string can be like "es:fr_BE:ga" */
920                 while ( lang && (*lang)) {
921                     p=strchr(lang,':');
922                     if (p) *p++='\0';
923
924                     /* first try full locale */
925                     ret=add_file(NO_PROB, lang, NULL, &File_list,
926                         &File_tail, NULL);
927
928                     /* if not try language name only (two first chars) */
929                     if (!ret) {
930                         char ll[3];
931
932                         strncpy(ll,lang,2);
933                         ll[2]='\0';
934                         ret=add_file(NO_PROB, ll, NULL,
935                             &File_list, &File_tail, NULL);
936                     }
937
938                     /* if we have found one we have finished */
939                     if (ret)
940                         return ret;
941                     lang=p;
942                 }
943                 /* default */
944                 return top_level_LOCFORTDIR();
945             }
946             else
947                 /* no locales available, use default */
948                 return top_level_LOCFORTDIR();
949         }
950     }
951
952     for (i = 0; i < file_cnt; i++)
953     {
954         percent = NO_PROB;
955         if (!isdigit(files[i][0]))
956             sp = files[i];
957         else
958         {
959             percent = 0;
960             for (sp = files[i]; isdigit(*sp); sp++)
961                 percent = percent * 10 + *sp - '0';
962             if (percent > 100)
963             {
964                 fprintf(stderr, "percentages must be <= 100\n");
965                 ErrorMessage = TRUE;
966                 return FALSE;
967             }
968             if (*sp == '.')
969             {
970                 fprintf(stderr, "percentages must be integers\n");
971                 ErrorMessage = TRUE;
972                 return FALSE;
973             }
974             /*
975              * If the number isn't followed by a '%', then
976              * it was not a percentage, just the first part
977              * of a file name which starts with digits.
978              */
979             if (*sp != '%')
980             {
981                 percent = NO_PROB;
982                 sp = files[i];
983             }
984             else if (*++sp == '\0')
985             {
986                 if (++i >= file_cnt)
987                 {
988                     fprintf(stderr, "percentages must precede files\n");
989                     ErrorMessage = TRUE;
990                     return FALSE;
991                 }
992                 sp = files[i];
993             }
994         }
995         if (strcmp(sp, "all") == 0)
996         {
997             snprintf(fullpathname,sizeof(fullpathname),"%s",FORTDIR);
998             snprintf(locpathname,sizeof(locpathname),"%s",LOCFORTDIR);
999         }
1000         /* if it isn't an absolute path or relative to . or ..
1001            make it an absolute path relative to FORTDIR */
1002         else
1003         {
1004             if (strncmp(sp,"/",1)!=0 && strncmp(sp,"./",2)!=0 &&
1005                 strncmp(sp,"../",3)!=0)
1006             {
1007                 snprintf(fullpathname,sizeof(fullpathname),
1008                     "%s/%s",FORTDIR,sp);
1009                 snprintf(locpathname,sizeof(locpathname),
1010                     "%s/%s",LOCFORTDIR,sp);
1011             }
1012             else
1013             {
1014                 snprintf(fullpathname,sizeof(fullpathname),"%s",sp);
1015                 snprintf(locpathname,sizeof(locpathname),"%s",sp);
1016             }
1017         }
1018
1019         if (env_lang) {
1020             char *lang;
1021             char llang[512];
1022             int ret=0;
1023             char *p;
1024
1025             strncpy(llang,env_lang,sizeof(llang));
1026             llang[sizeof(llang)-1] = '\0';
1027             lang=llang;
1028
1029             /* the language string can be like "es:fr_BE:ga" */
1030             while (!ret && lang && (*lang)) {
1031                 p=strchr(lang,':');
1032                 if (p) *p++='\0';
1033
1034                 /* first try full locale */
1035                 snprintf(langdir,sizeof(langdir),"%s/%s/%s",
1036                     FORTDIR, lang, sp);
1037                 ret=add_file(percent, langdir, NULL, &File_list,
1038                     &File_tail, NULL);
1039
1040                 /* if not try language name only (two first chars) */
1041                 if (!ret) {
1042                     char ll[3];
1043
1044                     strncpy(ll,lang,2);
1045                     ll[2]='\0';
1046                     snprintf(langdir,sizeof(langdir),
1047                         "%s/%s/%s", FORTDIR, ll, sp);
1048                     ret=add_file(percent, langdir, NULL,
1049                         &File_list, &File_tail, NULL);
1050                 }
1051
1052                 lang=p;
1053             }
1054             /* default */
1055             if (!ret)
1056                 ret=add_file(percent, fullpathname, NULL, &File_list,
1057                     &File_tail, NULL);
1058             if ( !ret && strncmp(fullpathname, locpathname, sizeof(fullpathname)))
1059                 ret=add_file(percent, locpathname, NULL, &File_list,
1060                     &File_tail, NULL);
1061
1062             if (!ret) {
1063                 snprintf (locpathname, sizeof (locpathname), "%s/%s", getenv ("PWD"), sp);
1064
1065                 ret = add_file (percent, locpathname, NULL, &File_list, &File_tail, NULL);
1066             }
1067             if (!ret) {
1068                 return FALSE;
1069             }
1070             if (strncmp(fullpathname, locpathname, sizeof(fullpathname)) && strcmp(sp, "all") == 0) {
1071                 add_file(percent, locpathname, NULL, &File_list, &File_tail, NULL);
1072             }
1073         }
1074         else
1075             if (!add_file(percent, fullpathname, NULL, &File_list,
1076                     &File_tail, NULL))
1077                 return FALSE;
1078     }
1079     return TRUE;
1080 }
1081
1082 /*
1083  *    This routine evaluates the arguments on the command line
1084  */
1085 static void getargs(int argc, char **argv)
1086 {
1087     register int ignore_case;
1088
1089 #ifndef NO_REGEX
1090     register char *pat = NULL;
1091
1092 #endif /* NO_REGEX */
1093     int ch;
1094
1095     ignore_case = FALSE;
1096
1097 #ifdef DEBUG
1098 #define DEBUG_GETOPT "D"
1099 #else
1100 #define DEBUG_GETOPT
1101 #endif
1102
1103 #ifdef NO_OFFENSIVE
1104 #define OFFENSIVE_GETOPT
1105 #else
1106 #define OFFENSIVE_GETOPT "o"
1107 #endif
1108
1109     while ((ch = getopt(argc, argv, "ac" DEBUG_GETOPT "efilm:n:" OFFENSIVE_GETOPT "suvw")) != EOF)
1110         switch (ch)
1111           {
1112           case 'a':             /* any fortune */
1113               All_forts = TRUE;
1114               break;
1115 #ifdef DEBUG
1116           case 'D':
1117               Debug++;
1118               break;
1119 #endif /* DEBUG */
1120           case 'e':
1121               Equal_probs = TRUE;    /* scatter un-allocted prob equally */
1122               break;
1123           case 'f':             /* find fortune files */
1124               Find_files = TRUE;
1125               break;
1126           case 'l':             /* long ones only */
1127               Long_only = TRUE;
1128               Short_only = FALSE;
1129               break;
1130           case 'n':
1131               SLEN = atoi(optarg);
1132               break;
1133 #ifndef NO_OFFENSIVE
1134           case 'o':             /* offensive ones only */
1135               Offend = TRUE;
1136               break;
1137 #endif
1138           case 's':             /* short ones only */
1139               Short_only = TRUE;
1140               Long_only = FALSE;
1141               break;
1142           case 'w':             /* give time to read */
1143               Wait = TRUE;
1144               break;
1145 #ifdef  NO_REGEX
1146           case 'i':             /* case-insensitive match */
1147           case 'm':             /* dump out the fortunes */
1148               (void) fprintf(stderr,
1149                   "fortune: can't match fortunes on this system (Sorry)\n");
1150               exit(0);
1151 #else /* NO_REGEX */
1152           case 'm':             /* dump out the fortunes */
1153               Match = TRUE;
1154               pat = optarg;
1155               break;
1156           case 'i':             /* case-insensitive match */
1157               ignore_case++;
1158               break;
1159 #endif /* NO_REGEX */
1160           case 'u': /* Don't recode the fortune */
1161               No_recode = TRUE;
1162               break;
1163           case 'v':
1164               (void) printf("%s\n", program_version());
1165               exit(0);
1166           case 'c':
1167               Show_filename = TRUE;
1168               break;
1169           case '?':
1170           default:
1171               usage();
1172           }
1173     argc -= optind;
1174     argv += optind;
1175
1176     if (!form_file_list(argv, argc))
1177     {
1178         if (!ErrorMessage) fprintf (stderr, "No fortunes found\n");
1179         exit(1);                /* errors printed through form_file_list() */
1180     }
1181 #ifdef DEBUG
1182 /*      if (Debug >= 1)
1183  * print_list(File_list, 0); */
1184 #endif /* DEBUG */
1185 /* If (Find_files) print_list() moved to main */
1186 #ifndef NO_REGEX
1187     if (pat != NULL)
1188     {
1189         if (ignore_case)
1190             pat = conv_pat(pat);
1191         if (BAD_COMP(RE_COMP(pat)))
1192         {
1193             fprintf(stderr, "bad pattern: %s\n", pat);
1194             exit (1);
1195         }
1196         if (ignore_case)
1197         {
1198             free(pat);
1199         }
1200     }
1201 #endif /* NO_REGEX */
1202 }
1203
1204 /*
1205  * init_prob:
1206  *      Initialize the fortune probabilities.
1207  */
1208 static void init_prob(void)
1209 {
1210     register FILEDESC *fp, *last;
1211     register int percent, num_noprob, frac;
1212
1213     /*
1214      * Distribute the residual probability (if any) across all
1215      * files with unspecified probability (i.e., probability of 0)
1216      * (if any).
1217      */
1218
1219     percent = 0;
1220     num_noprob = 0;
1221     last = NULL;
1222     for (fp = File_tail; fp != NULL; fp = fp->prev)
1223         if (fp->percent == NO_PROB)
1224         {
1225             num_noprob++;
1226             if (Equal_probs)
1227                 last = fp;
1228         }
1229         else
1230             percent += fp->percent;
1231     DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's\n",
1232                 percent, num_noprob));
1233     if (percent > 100)
1234     {
1235         fprintf(stderr,
1236                 "fortune: probabilities sum to %d%%!\n", percent);
1237         exit(1);
1238     }
1239     else if (percent < 100 && num_noprob == 0)
1240     {
1241         fprintf(stderr,
1242                 "fortune: no place to put residual probability (%d%%)\n",
1243                 percent);
1244         exit(1);
1245     }
1246     else if (percent == 100 && num_noprob != 0)
1247     {
1248         fprintf(stderr,
1249                 "fortune: no probability left to put in residual files\n");
1250         exit(1);
1251     }
1252     Spec_prob = percent;        /* this is for -f when % is specified on cmd line */
1253     percent = 100 - percent;
1254     if (Equal_probs)
1255     {
1256         if (num_noprob != 0)
1257         {
1258             if (num_noprob > 1)
1259             {
1260                 frac = percent / num_noprob;
1261                 DPRINTF(1, (stderr, ", frac = %d%%", frac));
1262                 for (fp = File_tail; fp != last; fp = fp->prev)
1263                     if (fp->percent == NO_PROB)
1264                     {
1265                         fp->percent = frac;
1266                         percent -= frac;
1267                     }
1268             }
1269             last->percent = percent;
1270             DPRINTF(1, (stderr, ", residual = %d%%", percent));
1271         }
1272         else
1273         {
1274             DPRINTF(1, (stderr,
1275                         ", %d%% distributed over remaining fortunes\n",
1276                         percent));
1277         }
1278     }
1279     DPRINTF(1, (stderr, "\n"));
1280
1281 #ifdef DEBUG
1282 /*      if (Debug >= 1)
1283      * print_list(File_list, 0); *//* Causes crash with new %% code */
1284 #endif
1285 }
1286
1287 /*
1288  * zero_tbl:
1289  *      Zero out the fields we care about in a tbl structure.
1290  */
1291 static void zero_tbl(register STRFILE * tp)
1292 {
1293     tp->str_numstr = 0;
1294     tp->str_longlen = 0;
1295     tp->str_shortlen = (uint32_t)(-1);
1296 }
1297
1298 /*
1299  * sum_tbl:
1300  *      Merge the tbl data of t2 into t1.
1301  */
1302 static void sum_tbl(register STRFILE * t1, register STRFILE * t2)
1303 {
1304     t1->str_numstr += t2->str_numstr;
1305     if (t1->str_longlen < t2->str_longlen)
1306         t1->str_longlen = t2->str_longlen;
1307     if (t1->str_shortlen > t2->str_shortlen)
1308         t1->str_shortlen = t2->str_shortlen;
1309 }
1310
1311 /*
1312  * get_tbl:
1313  *      Get the tbl data file the datfile.
1314  */
1315 static void get_tbl(FILEDESC * fp)
1316 {
1317     auto int fd;
1318     register FILEDESC *child;
1319
1320     if (fp->read_tbl)
1321         return;
1322     if (fp->child == NULL)
1323     {
1324 #if 0
1325         /* This should not be needed anymore since add_file takes care of
1326          * empty directories now (Torsten Landschoff <torsten@debian.org>)
1327          */
1328
1329         /*
1330          * Only the local fortune dir and the local offensive dir are
1331          * allowed to be empty.  Don't try and fetch their tables if
1332          * they have no children (i.e. are empty).
1333          *  - Brian Bassett (brianb@debian.org) 1999/07/31
1334          */
1335         if (strcmp(LOCFORTDIR, fp->path) == 0 || strcmp(LOCOFFDIR, fp->path) == 0)
1336         {
1337             fp->read_tbl = TRUE;        /* Make it look like we've read it. */
1338             return;
1339         }
1340         /* End */
1341 #endif
1342         if ((fd = open(fp->datfile, O_RDONLY|O_BINARY)) < 0)
1343         {
1344             perror(fp->datfile);
1345             exit(1);
1346         }
1347         if (read(fd, &fp->tbl.str_version, sizeof fp->tbl.str_version) !=
1348                 sizeof fp->tbl.str_version)
1349         {
1350             fprintf(stderr,
1351                     "fortune: %s corrupted\n", fp->path);
1352             exit(1);
1353         }
1354         if (read(fd, &fp->tbl.str_numstr, sizeof fp->tbl.str_numstr) !=
1355                 sizeof fp->tbl.str_numstr)
1356         {
1357             fprintf(stderr,
1358                     "fortune: %s corrupted\n", fp->path);
1359             exit(1);
1360         }
1361         if (read(fd, &fp->tbl.str_longlen, sizeof fp->tbl.str_longlen) !=
1362                 sizeof fp->tbl.str_longlen)
1363         {
1364             fprintf(stderr,
1365                     "fortune: %s corrupted\n", fp->path);
1366             exit(1);
1367         }
1368         if (read(fd, &fp->tbl.str_shortlen, sizeof fp->tbl.str_shortlen) !=
1369                 sizeof fp->tbl.str_shortlen)
1370         {
1371             fprintf(stderr,
1372                     "fortune: %s corrupted\n", fp->path);
1373             exit(1);
1374         }
1375         if (read(fd, &fp->tbl.str_flags, sizeof fp->tbl.str_flags) !=
1376                 sizeof fp->tbl.str_flags)
1377         {
1378             fprintf(stderr,
1379                     "fortune: %s corrupted\n", fp->path);
1380             exit(1);
1381         }
1382         if (read(fd, &fp->tbl.stuff, sizeof fp->tbl.stuff) !=
1383                 sizeof fp->tbl.stuff)
1384         {
1385             fprintf(stderr,
1386                     "fortune: %s corrupted\n", fp->path);
1387             exit(1);
1388         }
1389         fp->tbl.str_version = ntohl(fp->tbl.str_version);
1390         fp->tbl.str_numstr = ntohl(fp->tbl.str_numstr);
1391         fp->tbl.str_longlen = ntohl(fp->tbl.str_longlen);
1392         fp->tbl.str_shortlen = ntohl(fp->tbl.str_shortlen);
1393         fp->tbl.str_flags = ntohl(fp->tbl.str_flags);
1394         close(fd);
1395     }
1396     else
1397     {
1398         zero_tbl(&fp->tbl);
1399         for (child = fp->child; child != NULL; child = child->next)
1400         {
1401             get_tbl(child);
1402             sum_tbl(&fp->tbl, &child->tbl);
1403         }
1404     }
1405     fp->read_tbl = TRUE;
1406 }
1407
1408 /*
1409  * sum_noprobs:
1410  *      Sum up all the noprob probabilities, starting with fp.
1411  */
1412 static void sum_noprobs(register FILEDESC * fp)
1413 {
1414     static bool did_noprobs = FALSE;
1415
1416     if (did_noprobs)
1417         return;
1418     zero_tbl(&Noprob_tbl);
1419     while (fp != NULL)
1420     {
1421         get_tbl(fp);
1422         /* This conditional should help us return correct values for -f
1423          * when a percentage is specified */
1424         if (fp->percent == NO_PROB)
1425             sum_tbl(&Noprob_tbl, &fp->tbl);
1426         fp = fp->next;
1427     }
1428     did_noprobs = TRUE;
1429 }
1430
1431 /*
1432  * pick_child
1433  *      Pick a child from a chosen parent.
1434  */
1435 static FILEDESC *pick_child(FILEDESC * parent)
1436 {
1437     register FILEDESC *fp;
1438     register int choice;
1439
1440     if (Equal_probs)
1441     {
1442         choice = my_random(parent->num_children);
1443         DPRINTF(1, (stderr, "    choice = %d (of %d)\n",
1444                     choice, parent->num_children));
1445         for (fp = parent->child; choice--; fp = fp->next)
1446             continue;
1447         DPRINTF(1, (stderr, "    using %s\n", fp->name));
1448         return fp;
1449     }
1450     else
1451     {
1452         get_tbl(parent);
1453         choice = (int)(my_random(parent->tbl.str_numstr));
1454         DPRINTF(1, (stderr, "    choice = %d (of %ld)\n",
1455                     choice, parent->tbl.str_numstr));
1456         for (fp = parent->child; choice >= (int)fp->tbl.str_numstr;
1457              fp = fp->next)
1458         {
1459             choice -= fp->tbl.str_numstr;
1460             DPRINTF(1, (stderr, "\tskip %s, %ld (choice = %d)\n",
1461                         fp->name, fp->tbl.str_numstr, choice));
1462         }
1463         DPRINTF(1, (stderr, "    using %s, %ld\n", fp->name,
1464                     fp->tbl.str_numstr));
1465         return fp;
1466     }
1467 }
1468
1469 /*
1470  * open_dat:
1471  *      Open up the dat file if we need to.
1472  */
1473 static void open_dat(FILEDESC * fp)
1474 {
1475     if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, O_RDONLY|O_BINARY)) < 0)
1476     {
1477         exit(1);
1478     }
1479 }
1480
1481 /*
1482  * get_pos:
1483  *      Get the position from the pos file, if there is one.  If not,
1484  *      return a random number.
1485  */
1486 static void get_pos(FILEDESC * fp)
1487 {
1488     assert(fp->read_tbl);
1489     if (fp->pos == POS_UNKNOWN)
1490     {
1491         fp->pos = (int32_t)(my_random(fp->tbl.str_numstr));
1492     }
1493     if (++(fp->pos) >= (int32_t)fp->tbl.str_numstr)
1494         fp->pos -= fp->tbl.str_numstr;
1495     DPRINTF(1, (stderr, "pos for %s is %ld\n", fp->name, fp->pos));
1496 }
1497
1498 /*
1499  * get_fort:
1500  *      Get the fortune data file's seek pointer for the next fortune.
1501  */
1502 static void get_fort(void)
1503 {
1504     register FILEDESC *fp;
1505     register int choice;
1506
1507     if (File_list->next == NULL || File_list->percent == NO_PROB)
1508         fp = File_list;
1509     else
1510     {
1511         choice = my_random(100);
1512         DPRINTF(1, (stderr, "choice = %d\n", choice));
1513         for (fp = File_list; fp->percent != NO_PROB; fp = fp->next)
1514             if (choice < fp->percent)
1515                 break;
1516             else
1517             {
1518                 choice -= fp->percent;
1519                 DPRINTF(1, (stderr,
1520                             "    skip \"%s\", %d%% (choice = %d)\n",
1521                             fp->name, fp->percent, choice));
1522             }
1523         DPRINTF(1, (stderr,
1524                     "using \"%s\", %d%% (choice = %d)\n",
1525                     fp->name, fp->percent, choice));
1526     }
1527     if (fp->percent != NO_PROB)
1528         get_tbl(fp);
1529     else
1530     {
1531         if (fp->next != NULL)
1532         {
1533             sum_noprobs(fp);
1534             choice = (int)(my_random(Noprob_tbl.str_numstr));
1535             DPRINTF(1, (stderr, "choice = %d (of %ld) \n", choice,
1536                         Noprob_tbl.str_numstr));
1537             while (choice >= (int)fp->tbl.str_numstr)
1538             {
1539                 choice -= (int)fp->tbl.str_numstr;
1540                 fp = fp->next;
1541                 DPRINTF(1, (stderr,
1542                             "    skip \"%s\", %ld (choice = %d)\n",
1543                             fp->name, fp->tbl.str_numstr,
1544                             choice));
1545             }
1546             DPRINTF(1, (stderr, "using \"%s\", %ld\n", fp->name,
1547                         fp->tbl.str_numstr));
1548         }
1549         get_tbl(fp);
1550     }
1551     if (fp->tbl.str_numstr == 0)
1552     {
1553         fprintf(stderr, "fortune: no fortune found\n");
1554         exit(1);
1555     }
1556     if (fp->child != NULL)
1557     {
1558         DPRINTF(1, (stderr, "picking child\n"));
1559         fp = pick_child(fp);
1560     }
1561     Fortfile = fp;
1562     get_pos(fp);
1563     open_dat(fp);
1564     lseek(fp->datfd,
1565           (off_t) (sizeof fp->tbl + (size_t)fp->pos * sizeof Seekpts[0]), 0);
1566     if ((read(fp->datfd, &Seekpts[0], sizeof Seekpts[0]) <0)||
1567     (read(fp->datfd, &Seekpts[1], sizeof Seekpts[1])<0))
1568     {
1569         exit(1);
1570     }
1571     Seekpts[0] = (int32_t)ntohl((uint32_t)Seekpts[0]);
1572     Seekpts[1] = (int32_t)ntohl((uint32_t)Seekpts[1]);
1573 }
1574
1575 /*
1576  * open_fp:
1577  *      Assocatiate a FILE * with the given FILEDESC.
1578  */
1579 static void open_fp(FILEDESC * fp)
1580 {
1581     if (fp->inf == NULL && (fp->inf = fdopen(fp->fd, "r")) == NULL)
1582     {
1583         perror(fp->path);
1584         exit(1);
1585     }
1586 }
1587
1588 #ifndef NO_REGEX
1589 /*
1590  * maxlen_in_list
1591  *      Return the maximum fortune len in the file list.
1592  */
1593 static int maxlen_in_list(FILEDESC * list)
1594 {
1595     register FILEDESC *fp;
1596     register int len, maxlen;
1597
1598     maxlen = 0;
1599     for (fp = list; fp != NULL; fp = fp->next)
1600     {
1601         if (fp->child != NULL)
1602         {
1603             if ((len = maxlen_in_list(fp->child)) > maxlen)
1604                 maxlen = len;
1605         }
1606         else
1607         {
1608             get_tbl(fp);
1609             if ((int)fp->tbl.str_longlen > maxlen)
1610             {
1611                 maxlen = (int)fp->tbl.str_longlen;
1612             }
1613         }
1614     }
1615     return maxlen;
1616 }
1617
1618 /*
1619  * matches_in_list
1620  *      Print out the matches from the files in the list.
1621  */
1622 static void matches_in_list(FILEDESC * list)
1623 {
1624     unsigned char *sp;
1625     unsigned char *p; /* -allover */
1626     unsigned char ch; /* -allover */
1627     register FILEDESC *fp;
1628     int in_file, nchar;
1629     char *output;
1630
1631     for (fp = list; fp != NULL; fp = fp->next)
1632     {
1633         if (fp->child != NULL)
1634         {
1635             matches_in_list(fp->child);
1636             continue;
1637         }
1638         DPRINTF(1, (stderr, "searching in %s\n", fp->path));
1639         open_fp(fp);
1640         sp = Fortbuf;
1641         in_file = FALSE;
1642         while (fgets((char *)sp, Fort_len, fp->inf) != NULL)
1643         {
1644             if (!STR_ENDSTRING(sp, fp->tbl))
1645             {
1646                 sp += strlen((const char *)sp);
1647             }
1648             else
1649             {
1650                 *sp = '\0';
1651                 nchar = (int)(sp - Fortbuf);
1652
1653                 if (fp->utf8_charset && (! No_recode))
1654                 {
1655 #ifdef WITH_RECODE
1656                     output = recode_string (request, (const char *)Fortbuf);
1657 #else
1658                     output = strdup(Fortbuf);
1659 #endif
1660                 }
1661                 else
1662                 {
1663                     output = (char *)Fortbuf;
1664                 }
1665                 /* Should maybe rot13 Fortbuf -allover */
1666
1667                 if(fp->tbl.str_flags & STR_ROTATED)
1668                 {
1669                     for (p = (unsigned char *)output; (ch = *p); ++p)
1670                     {
1671                         if (isupper(ch) && isascii(ch))
1672                             *p = 'A' + (ch - 'A' + 13) % 26;
1673                         else if (islower(ch) && isascii(ch))
1674                             *p = 'a' + (ch - 'a' + 13) % 26;
1675                     }
1676                 }
1677
1678                 DPRINTF(1, (stdout, "nchar = %d\n", nchar));
1679                 if ( (nchar < SLEN || !Short_only) &&
1680                         (nchar > SLEN || !Long_only) &&
1681                         RE_EXEC(output) )
1682                 {
1683                     if (!in_file)
1684                     {
1685                         fprintf(stderr, "(%s)\n%c\n", fp->name, fp->tbl.str_delim);
1686                         Found_one = TRUE;
1687                         in_file = TRUE;
1688                     }
1689                     fputs (output, stdout);
1690                     printf("%c\n", fp->tbl.str_delim);
1691                 }
1692
1693                 if (fp->utf8_charset && (! No_recode))
1694                   free (output);
1695
1696                 sp = Fortbuf;
1697             }
1698         }
1699     }
1700 }
1701
1702 /*
1703  * find_matches:
1704  *      Find all the fortunes which match the pattern we've been given.
1705  */
1706 static int find_matches(void)
1707 {
1708     Fort_len = maxlen_in_list(File_list);
1709     DPRINTF(2, (stderr, "Maximum length is %d\n", Fort_len));
1710     /* extra length, "%\n" is appended */
1711     Fortbuf = do_malloc((unsigned int) Fort_len + 10);
1712
1713     Found_one = FALSE;
1714     matches_in_list(File_list);
1715     return Found_one;
1716     /* NOTREACHED */
1717 }
1718 #endif /* NO_REGEX */
1719
1720 static void display(FILEDESC * fp)
1721 {
1722     register char *p, ch;
1723     unsigned char line[BUFSIZ];
1724
1725     open_fp(fp);
1726     fseek(fp->inf, (long) Seekpts[0], 0);
1727     if (Show_filename)
1728         printf ("(%s)\n%%\n", fp->name);
1729     for (Fort_len = 0; fgets((char *)line, sizeof line, fp->inf) != NULL &&
1730          !STR_ENDSTRING(line, fp->tbl); Fort_len++)
1731     {
1732         if (fp->tbl.str_flags & STR_ROTATED)
1733         {
1734             for (p = (char *)line; (ch = *p); ++p)
1735             {
1736                 if (isupper(ch) && isascii(ch))
1737                     *p = 'A' + (ch - 'A' + 13) % 26;
1738                 else if (islower(ch) && isascii (ch))
1739                     *p = 'a' + (ch - 'a' + 13) % 26;
1740             }
1741         }
1742         if (fp->utf8_charset && (! No_recode)) {
1743             char *output;
1744 #ifdef WITH_RECODE
1745             output = recode_string (request, (const char *)line);
1746 #else
1747             output = strdup(line);
1748 #endif
1749             fputs(output, stdout);
1750             free(output);
1751         }
1752         else
1753             fputs((char *)line, stdout);
1754     }
1755     fflush(stdout);
1756 }
1757
1758 /*
1759  * fortlen:
1760  *      Return the length of the fortune.
1761  */
1762 static int fortlen(void)
1763 {
1764     register int nchar;
1765     char line[BUFSIZ];
1766
1767     if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED)))
1768         nchar = (Seekpts[1] - Seekpts[0]) - 2;  /* for %^J delimiter */
1769     else
1770     {
1771         open_fp(Fortfile);
1772         fseek(Fortfile->inf, (long) Seekpts[0], 0);
1773         nchar = 0;
1774         while (fgets(line, sizeof line, Fortfile->inf) != NULL &&
1775                !STR_ENDSTRING(line, Fortfile->tbl))
1776             nchar += strlen(line);
1777     }
1778     Fort_len = nchar;
1779     return nchar;
1780 }
1781
1782 static int mymax(register int i, register int j)
1783 {
1784     return (i >= j ? i : j);
1785 }
1786
1787 static void free_desc(FILEDESC *ptr)
1788 {
1789     while (ptr)
1790     {
1791         free_desc(ptr->child);
1792         do_free(ptr->datfile);
1793         do_free(ptr->posfile);
1794         do_free(ptr->name);
1795         do_free(ptr->path);
1796         if (ptr->inf)
1797         {
1798             fclose(ptr->inf);
1799             ptr->inf = NULL;
1800         }
1801         FILEDESC *next = ptr->next;
1802         free(ptr);
1803         ptr = next;
1804     }
1805 }
1806
1807 int main(int ac, char *av[])
1808 {
1809     const char *ctype;
1810     char *crequest;
1811     int exit_code = 0;
1812     env_lang=getenv("LC_ALL");
1813     if (!env_lang) env_lang=getenv("LC_MESSAGES");
1814     if (!env_lang) env_lang=getenv("LANGUAGE");
1815     if (!env_lang) env_lang=getenv("LANG");
1816 #ifdef _WIN32
1817     if (!env_lang) {
1818         env_lang="en";
1819     }
1820 #endif
1821
1822
1823 #ifndef DONT_CALL_GETARGS
1824     getargs(ac, av);
1825 #endif
1826
1827 #ifdef WITH_RECODE
1828     outer = recode_new_outer(true);
1829     request = recode_new_request (outer);
1830 #endif
1831
1832     setlocale(LC_ALL,"");
1833 #ifdef _WIN32
1834     ctype = "C";
1835 #else
1836     ctype = nl_langinfo(CODESET);
1837     if (!ctype || !*ctype)
1838     {
1839         ctype = "C";
1840     }
1841     else if(strcmp(ctype,"ANSI_X3.4-1968") == 0)
1842     {
1843         ctype="ISO-8859-1";
1844     }
1845 #endif
1846
1847 #ifdef WITH_RECODE
1848     crequest = malloc(strlen(ctype) + 7 + 1);
1849     sprintf(crequest, "UTF-8..%s", ctype);
1850     recode_scan_request (request, crequest);
1851     free(crequest);
1852 #endif
1853
1854 #ifndef NO_REGEX
1855     if (Match)
1856     {
1857         exit_code = (find_matches() != 0);
1858         regfree(&Re_pat);
1859         goto cleanup;
1860     }
1861 #endif
1862     init_prob();
1863     if (Find_files)
1864     {
1865         sum_noprobs(File_list);
1866         if (Equal_probs)
1867             calc_equal_probs();
1868         print_list(File_list, 0);
1869     }
1870     else
1871     {
1872         srandom((unsigned int) (time((time_t *) NULL) + getpid()));
1873         do
1874         {
1875             get_fort();
1876         }
1877         while ((Short_only && fortlen() > SLEN) ||
1878             (Long_only && fortlen() <= SLEN));
1879
1880         display(Fortfile);
1881
1882         if (Wait)
1883         {
1884             fortlen();
1885             sleep((unsigned int) mymax(Fort_len / CPERS, MINW));
1886         }
1887     }
1888 cleanup:
1889 #ifdef WITH_RECODE
1890     recode_delete_request(request);
1891     recode_delete_outer(outer);
1892 #endif
1893
1894     /* Free the File_list */
1895     free_desc(File_list);
1896     free(Fortbuf);
1897     exit(exit_code);
1898     /* NOTREACHED */
1899 }