]> granicus.if.org Git - fortune-mod/blob - fortune-mod/fortune/fortune.c
Retab to avoid mixed tabs/spaces.
[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 consistant 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 #define         PROGRAM_VERSION         "9708"
101
102 #ifdef HAVE_STDBOOL_H
103 #include <stdbool.h>
104 #else /* ! HAVE_STDBOOL_H */
105
106 /* stdbool.h for GNU.  */
107
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.  */
110 typedef enum
111   {
112     false = 0,
113     true = 1
114   } bool;
115
116 /* The names `true' and `false' must also be made available as macros.  */
117 #define false   false
118 #define true    true
119
120 /* Signal that all the definitions are present.  */
121 #define __bool_true_false_are_defined   1
122
123 #endif /* HAVE_STDBOOL_H */
124
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>
130
131 #include        <time.h>
132 #include        <dirent.h>
133 #include        <fcntl.h>
134 #include        <assert.h>
135 #include        <unistd.h>
136 #include        <stdio.h>
137 #include        <ctype.h>
138 #include        <stdlib.h>
139 #include        <string.h>
140 #include        <errno.h>
141 #include        <locale.h>
142 #include        <langinfo.h>
143 #include        <recode.h>
144
145
146 /* This makes GNU libc to prototype the BSD regex functions */
147 #ifdef BSD_REGEX
148 #define _REGEX_RE_COMP
149 #endif
150
151 #ifdef HAVE_REGEX_H
152 #include        <regex.h>
153 #endif
154 #ifdef HAVE_REGEXP_H
155 #include        <regexp.h>
156 #endif
157 #ifdef HAVE_RX_H
158 #include        <rx.h>
159 #endif
160
161 #include        "strfile.h"
162
163 #define TRUE    1
164 #define FALSE   0
165 #define bool    short
166
167 #define MINW    6               /* minimum wait if desired */
168 #define CPERS   20              /* # of chars for each sec */
169
170 #define POS_UNKNOWN     ((int32_t) -1)  /* pos for file unknown */
171 #define NO_PROB         (-1)    /* no prob specified for file */
172
173 #ifdef DEBUG
174 #define DPRINTF(l,x)    if (Debug >= l) fprintf x;
175 #undef          NDEBUG
176 #else
177 #define DPRINTF(l,x)
178 #define NDEBUG  1
179 #endif
180
181 typedef struct fd
182 {
183     int percent;
184     int fd, datfd;
185     int32_t pos;
186     FILE *inf;
187     char *name;
188     char *path;
189     char *datfile, *posfile;
190     bool read_tbl;
191     bool was_pos_file;
192     bool utf8_charset;
193     STRFILE tbl;
194     int num_children;
195     struct fd *child, *parent;
196     struct fd *next, *prev;
197 }
198 FILEDESC;
199
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;
209
210 bool ErrorMessage = FALSE;      /* Set to true if an error message has been displayed */
211
212 #ifndef NO_REGEX
213 bool Match = FALSE;             /* dump fortunes matching a pattern */
214
215 #endif
216 #ifdef DEBUG
217 bool Debug = FALSE;             /* print debug messages */
218
219 #endif
220
221 unsigned char *Fortbuf = NULL;  /* fortune buffer for -m */
222
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 */
226
227 int32_t Seekpts[2];             /* seek pointers to fortunes */
228
229 FILEDESC *File_list = NULL,     /* Head of file list */
230  *File_tail = NULL;             /* Tail of file list */
231 FILEDESC *Fortfile;             /* Fortune file to use */
232
233 STRFILE Noprob_tbl;             /* sum of data for all no prob files */
234
235 #ifdef BSD_REGEX
236
237 #define RE_COMP(p)      re_comp(p)
238 #define BAD_COMP(f)     ((f) != NULL)
239 #define RE_EXEC(p)      re_exec(p)
240
241 #else
242
243 #ifdef POSIX_REGEX
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)
247
248 regex_t Re_pat;
249 #else
250 #define NO_REGEX
251 #endif /* POSIX_REGEX */
252
253 #endif /* BSD_REGEX */
254
255 RECODE_REQUEST request;
256 RECODE_OUTER outer;
257
258 int add_dir(register FILEDESC *);
259
260 char *program_version(void)
261 {
262     static char buf[BUFSIZ];
263     (void) sprintf(buf, "%s version %s", PROGRAM_NAME, PROGRAM_VERSION);
264     return buf;
265 }
266
267 void usage(void)
268 {
269     (void) fprintf(stderr, "%s\n",program_version());
270     (void) fprintf(stderr, "fortune [-a");
271 #ifdef  DEBUG
272     (void) fprintf(stderr, "D");
273 #endif /* DEBUG */
274     (void) fprintf(stderr, "f");
275 #ifndef NO_REGEX
276     (void) fprintf(stderr, "i");
277 #endif /* NO_REGEX */
278     (void) fprintf(stderr, "losw]");
279 #ifndef NO_REGEX
280     (void) fprintf(stderr, " [-m pattern]");
281 #endif /* NO_REGEX */
282     (void) fprintf(stderr, " [-n number] [ [#%%] file/directory/all]\n");
283     exit(1);
284 }
285
286 #define STR(str)        ((str) == NULL ? "NULL" : (str))
287
288
289 /*
290  * calc_equal_probs:
291  *      Set the global values for number of files/children, to be used
292  * in printing probabilities when listing files
293  */
294 void calc_equal_probs(void)
295 {
296     FILEDESC *fiddlylist;
297
298     Num_files = Num_kids = 0;
299     fiddlylist = File_list;
300     while (fiddlylist != NULL)
301     {
302         Num_files++;
303         Num_kids += fiddlylist->num_children;
304         fiddlylist = fiddlylist->next;
305     }
306 }
307
308 /*
309  * print_list:
310  *      Print out the actual list, recursively.
311  */
312 void print_list(register FILEDESC * list, int lev)
313 {
314     while (list != NULL)
315     {
316         fprintf(stderr, "%*s", lev * 4, "");
317         if (list->percent == NO_PROB)
318             if (!Equal_probs)
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);
323             else if (lev == 0)
324                 fprintf(stderr, "%5.2f%%", 100.0 / Num_files);
325             else
326                 fprintf(stderr, "%5.2f%%", 100.0 / Num_kids);
327         else
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)));
332         putc('\n', stderr);
333         if (list->child != NULL)
334             print_list(list->child, lev + 1);
335         list = list->next;
336     }
337 }
338
339 #ifndef NO_REGEX
340 /*
341  * conv_pat:
342  *      Convert the pattern to an ignore-case equivalent.
343  */
344 char *conv_pat(register char *orig)
345 {
346     register char *sp;
347     register unsigned int cnt;
348     register char *new;
349
350     cnt = 1;                    /* allow for '\0' */
351     for (sp = orig; *sp != '\0'; sp++)
352         if (isalpha(*sp))
353             cnt += 4;
354         else
355             cnt++;
356     if ((new = malloc(cnt)) == NULL)
357     {
358         fprintf(stderr, "pattern too long for ignoring case\n");
359         exit(1);
360     }
361
362     for (sp = new; *orig != '\0'; orig++)
363     {
364         if (islower(*orig))
365         {
366             *sp++ = '[';
367             *sp++ = *orig;
368             *sp++ = toupper(*orig);
369             *sp++ = ']';
370         }
371         else if (isupper(*orig))
372         {
373             *sp++ = '[';
374             *sp++ = *orig;
375             *sp++ = tolower(*orig);
376             *sp++ = ']';
377         }
378         else
379             *sp++ = *orig;
380     }
381     *sp = '\0';
382     return new;
383 }
384 #endif /* NO_REGEX */
385
386 /*
387  * do_malloc:
388  *      Do a malloc, checking for NULL return.
389  */
390 void *do_malloc(unsigned int size)
391 {
392     void *new;
393
394     if ((new = malloc(size)) == NULL)
395     {
396         (void) fprintf(stderr, "fortune: out of memory.\n");
397         exit(1);
398     }
399     return new;
400 }
401
402 /*
403  * do_free:
404  *      Free malloc'ed space, if any.
405  */
406 void do_free(void *ptr)
407 {
408     if (ptr != NULL)
409         free(ptr);
410 }
411
412 /*
413  * copy:
414  *      Return a malloc()'ed copy of the string
415  */
416 char *copy(char *str, unsigned int len)
417 {
418     char *new, *sp;
419
420     new = do_malloc(len + 1);
421     sp = new;
422     do
423     {
424         *sp++ = *str;
425     }
426     while (*str++);
427     return new;
428 }
429
430 /*
431  * new_fp:
432  *      Return a pointer to an initialized new FILEDESC.
433  */
434 FILEDESC *new_fp(void)
435 {
436     register FILEDESC *fp;
437
438     fp = (FILEDESC *) do_malloc(sizeof *fp);
439     fp->datfd = -1;
440     fp->pos = POS_UNKNOWN;
441     fp->inf = NULL;
442     fp->fd = -1;
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;
454     fp->next = NULL;
455     fp->prev = NULL;
456     fp->child = NULL;
457     fp->parent = NULL;
458     fp->datfile = NULL;
459     fp->posfile = NULL;
460     return fp;
461 }
462
463 /*
464  * is_dir:
465  *      Return TRUE if the file is a directory, FALSE otherwise.
466  */
467 int is_dir(char *file)
468 {
469     auto struct stat sbuf;
470
471     if (stat(file, &sbuf) < 0)
472         return FALSE;
473     return (sbuf.st_mode & S_IFDIR);
474 }
475
476 /*
477  * is_existant:
478  *      Return TRUE if the file exists, FALSE otherwise.
479  */
480 int is_existant(char *file)
481 {
482     struct stat staat;
483
484     if (stat(file, &staat) == 0)
485         return TRUE;
486     switch(errno)
487     {
488         case ENOENT:
489         case ENOTDIR:
490             return FALSE;
491         default:
492             perror("fortune: bad juju in is_existant");
493             exit(1);
494     }
495 }
496
497 /*
498  * is_fortfile:
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.
503  */
504 int is_fortfile(char *file, char **datp, char **posp)
505 {
506     register int i;
507     register char *sp;
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",
513         "ins.ftn", "sml",
514         NULL
515     };
516
517     DPRINTF(2, (stderr, "is_fortfile(%s) returns ", file));
518
519     if ((sp = strrchr(file, '/')) == NULL)
520         sp = file;
521     else
522         sp++;
523     if (*sp == '.')
524     {
525         DPRINTF(2, (stderr, "FALSE (file starts with '.')\n"));
526         return FALSE;
527     }
528     if ((sp = strrchr(sp, '.')) != NULL)
529     {
530         sp++;
531         for (i = 0; suflist[i] != NULL; i++)
532             if (strcmp(sp, suflist[i]) == 0)
533             {
534                 DPRINTF(2, (stderr, "FALSE (file has suffix \".%s\")\n", sp));
535                 return FALSE;
536             }
537     }
538
539     datfile = copy(file, (unsigned int) (strlen(file) + 4));    /* +4 for ".dat" */
540     strcat(datfile, ".dat");
541     if (access(datfile, R_OK) < 0)
542     {
543         free(datfile);
544         DPRINTF(2, (stderr, "FALSE (no \".dat\" file)\n"));
545         return FALSE;
546     }
547     if (datp != NULL)
548         *datp = datfile;
549     else
550         free(datfile);
551     DPRINTF(2, (stderr, "TRUE\n"));
552     return TRUE;
553 }
554
555 /*
556  * add_file:
557  *      Add a file to the file list.
558  */
559 int add_file(int percent, register char *file, char *dir,
560              FILEDESC ** head, FILEDESC ** tail, FILEDESC * parent)
561 {
562     register FILEDESC *fp;
563     register int fd;
564     register char *path, *testpath;
565     register bool was_malloc;
566     register bool isdir;
567     auto char *sp;
568     auto bool found;
569     struct stat statbuf;
570
571     if (dir == NULL)
572     {
573         path = file;
574         was_malloc = FALSE;
575     }
576     else
577     {
578         path = do_malloc((unsigned int) (strlen(dir) + strlen(file) + 2));
579         (void) strcat(strcat(strcpy(path, dir), "/"), file);
580         was_malloc = TRUE;
581     }
582     if (*path == '/' && !is_existant(path))     /* If doesn't exist, don't do anything. */
583     {
584         if (was_malloc)
585             free(path);
586         return FALSE;
587     }
588     if ((isdir = is_dir(path)) && parent != NULL)
589     {
590         if (was_malloc)
591             free(path);
592         return FALSE;           /* don't recurse */
593     }
594
595     DPRINTF(1, (stderr, "trying to add file \"%s\"\n", path));
596     if ((fd = open(path, O_RDONLY)) < 0 || *path != '/')
597     {
598       found = FALSE;
599         if (dir == NULL && (strchr(file,'/') == NULL))
600         {
601             if ( ((sp = strrchr(file,'-')) != NULL) && (strcmp(sp,"-o") == 0) )
602             {
603                 /* BSD-style '-o' offensive file suffix */
604                 *sp = '\0';
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 */
608                 *sp = '-';
609             }
610             else if (All_forts)
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));
615             else if (Offend)
616                 found = (add_file(percent, file, LOCOFFDIR, head, tail, parent)
617                          || add_file(percent, file, OFFDIR, head, tail, parent));
618             else
619                 found = (add_file(percent, file, LOCFORTDIR, head, tail, parent)
620                          || add_file(percent, file, FORTDIR, head, tail, parent));
621         }
622         if (!found && parent == NULL && dir == NULL)
623         { /* don't display an error when trying language specific files */
624           char *lang;
625
626           lang=getenv("LC_ALL");
627           if (!lang) lang=getenv("LC_MESSAGES");
628           if (!lang) lang=getenv("LANGUAGE");
629           if (!lang) lang=getenv("LANG");
630           if (lang) {
631             char llang[512];
632             char langdir[512];
633             int ret=0;
634             char *p;
635
636             strncpy(llang,lang,sizeof(llang));
637             lang=llang;
638
639             /* the language string can be like "es:fr_BE:ga" */
640             while (!ret && lang && (*lang)) {
641               p=strchr(lang,':');
642               if (p) *p++='\0';
643               snprintf(langdir,sizeof(langdir),"%s/%s",
644                        FORTDIR,lang);
645
646               if (strncmp(path,lang,2) == 0)
647                 ret=1;
648               else if (strncmp(path,langdir,strlen(FORTDIR)+3) == 0)
649                 ret=1;
650               lang=p;
651             }
652             if (!ret)
653               perror(path);
654           } else {
655             perror(path);
656           }
657         }
658
659         if (was_malloc)
660             free(path);
661         return found;
662     }
663
664     DPRINTF(2, (stderr, "path = \"%s\"\n", path));
665
666     fp = new_fp();
667     fp->fd = fd;
668     fp->percent = percent;
669
670     fp->name = do_malloc (strlen (file) + 1);
671     strncpy (fp->name, file, strlen (file) + 1);
672
673     fp->path = do_malloc (strlen (path) + 1);
674     strncpy (fp->path, path, strlen (path) + 1);
675
676     //FIXME
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 );
684
685     fp->parent = parent;
686
687     if ((isdir && !add_dir(fp)) ||
688         (!isdir &&
689          !is_fortfile(path, &fp->datfile, &fp->posfile)))
690     {
691         if (parent == NULL)
692             fprintf(stderr,
693                     "fortune:%s not a fortune file or directory\n",
694                     path);
695         if (was_malloc)
696             free(path);
697         do_free(fp->datfile);
698         do_free(fp->posfile);
699         if (fp->fd >= 0) close(fp->fd);
700         free(fp);
701         return FALSE;
702     }
703
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) {
708         if (was_malloc)
709             free(path);
710         do_free(fp->datfile);
711         do_free(fp->posfile);
712         if(fp->fd >= 0) close(fp->fd);
713         free(fp);
714         return TRUE;
715     }
716     /* End hack. */
717
718     if (*head == NULL)
719         *head = *tail = fp;
720     else if (fp->percent == NO_PROB)
721     {
722         (*tail)->next = fp;
723         fp->prev = *tail;
724         *tail = fp;
725     }
726     else
727     {
728         (*head)->prev = fp;
729         fp->next = *head;
730         *head = fp;
731     }
732
733     return TRUE;
734 }
735
736 /*
737  * add_dir:
738  *      Add the contents of an entire directory.
739  */
740 int add_dir(register FILEDESC * fp)
741 {
742     register DIR *dir;
743     register struct dirent *dirent;
744     auto FILEDESC *tailp;
745     auto char *name;
746
747     close(fp->fd);
748     fp->fd = -1;
749     if ((dir = opendir(fp->path)) == NULL)
750     {
751         perror(fp->path);
752         return FALSE;
753     }
754     tailp = NULL;
755     DPRINTF(1, (stderr, "adding dir \"%s\"\n", fp->path));
756     fp->num_children = 0;
757     while ((dirent = readdir(dir)) != NULL)
758     {
759         if (dirent->d_name[0] == 0)
760             continue;
761         name = strdup(dirent->d_name);
762         if (add_file(NO_PROB, name, fp->path, &fp->child, &tailp, fp))
763             fp->num_children++;
764         else
765             free(name);
766     }
767     if (fp->num_children == 0)
768     {
769         /*
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
773          */
774         if (strcmp(LOCFORTDIR, fp->path) == 0 || strcmp(LOCOFFDIR, fp->path) == 0)
775         {
776             return TRUE;
777         }
778         fprintf(stderr,
779                 "fortune: %s: No fortune files in directory.\n", fp->path);
780         return FALSE;
781     }
782     return TRUE;
783 }
784
785 /*
786  * form_file_list:
787  *      Form the file list from the file specifications.
788  */
789 int form_file_list(register char **files, register int file_cnt)
790 {
791     register int i, percent;
792     register char *sp;
793     char *lang;
794     char langdir[512];
795     char fullpathname[512],locpathname[512];
796
797     if (file_cnt == 0)
798     {
799         if (All_forts)
800             return (add_file(NO_PROB, LOCFORTDIR, NULL, &File_list,
801                              &File_tail, NULL)
802                     | add_file(NO_PROB, LOCOFFDIR, NULL, &File_list,
803                                &File_tail, NULL)
804                     | add_file(NO_PROB, FORTDIR, NULL, &File_list,
805                                &File_tail, NULL)
806                     | add_file(NO_PROB, OFFDIR, NULL, &File_list,
807                                &File_tail, NULL));
808         else if (Offend)
809             return (add_file(NO_PROB, LOCOFFDIR, NULL, &File_list,
810                              &File_tail, NULL)
811                     | add_file(NO_PROB, OFFDIR, NULL, &File_list,
812                                &File_tail, NULL));
813         else {
814             char *lang=NULL;
815
816             lang=getenv("LC_ALL");
817             if (!lang) lang=getenv("LC_MESSAGES");
818             if (!lang) lang=getenv("LANGUAGE");
819             if (!lang) lang=getenv("LANG");
820             if (lang) {
821                 char llang[512];
822                 int ret=0;
823                 char *p;
824
825                 strncpy(llang,lang,sizeof(llang));
826                 lang=llang;
827
828                 /* the language string can be like "es:fr_BE:ga" */
829                 while ( lang && (*lang)) {
830                         p=strchr(lang,':');
831                         if (p) *p++='\0';
832
833                         /* first try full locale */
834                         ret=add_file(NO_PROB, lang, NULL, &File_list,
835                                 &File_tail, NULL);
836
837                         /* if not try language name only (two first chars) */
838                         if (!ret) {
839                           char ll[3];
840
841                           strncpy(ll,lang,2);
842                           ll[2]='\0';
843                           ret=add_file(NO_PROB, ll, NULL,
844                                        &File_list, &File_tail, NULL);
845                         }
846
847                         /* if we have found one we have finished */
848                         if (ret)
849                           return ret;
850                         lang=p;
851                 }
852                 /* default */
853                 return (add_file(NO_PROB, LOCFORTDIR, NULL, &File_list,
854                                  &File_tail, NULL)
855                         | add_file(NO_PROB, FORTDIR, NULL, &File_list,
856                                    &File_tail, NULL));
857
858             }
859             else
860               /* no locales available, use default */
861               return (add_file(NO_PROB, LOCFORTDIR, NULL, &File_list,
862                                &File_tail, NULL)
863                       | add_file(NO_PROB, FORTDIR, NULL, &File_list,
864                                  &File_tail, NULL));
865
866         }
867     }
868
869     for (i = 0; i < file_cnt; i++)
870     {
871         percent = NO_PROB;
872         if (!isdigit(files[i][0]))
873             sp = files[i];
874         else
875         {
876             percent = 0;
877             for (sp = files[i]; isdigit(*sp); sp++)
878                 percent = percent * 10 + *sp - '0';
879             if (percent > 100)
880             {
881                 fprintf(stderr, "percentages must be <= 100\n");
882                 ErrorMessage = TRUE;
883                 return FALSE;
884             }
885             if (*sp == '.')
886             {
887                 fprintf(stderr, "percentages must be integers\n");
888                 ErrorMessage = TRUE;
889                 return FALSE;
890             }
891             /*
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.
895              */
896             if (*sp != '%')
897             {
898                 percent = NO_PROB;
899                 sp = files[i];
900             }
901             else if (*++sp == '\0')
902             {
903                 if (++i >= file_cnt)
904                 {
905                     fprintf(stderr, "percentages must precede files\n");
906                     ErrorMessage = TRUE;
907                     return FALSE;
908                 }
909                 sp = files[i];
910             }
911         }
912         if (strcmp(sp, "all") == 0)
913         {
914           snprintf(fullpathname,sizeof(fullpathname),"%s",FORTDIR);
915           snprintf(locpathname,sizeof(locpathname),"%s",LOCFORTDIR);
916         }
917         /* if it isn't an absolute path or relative to . or ..
918            make it an absolute path relative to FORTDIR */
919         else
920         {
921             if (strncmp(sp,"/",1)!=0 && strncmp(sp,"./",2)!=0 &&
922                     strncmp(sp,"../",3)!=0)
923             {
924                 snprintf(fullpathname,sizeof(fullpathname),
925                         "%s/%s",FORTDIR,sp);
926                 snprintf(locpathname,sizeof(locpathname),
927                         "%s/%s",LOCFORTDIR,sp);
928             }
929             else
930             {
931                 snprintf(fullpathname,sizeof(fullpathname),"%s",sp);
932                 snprintf(locpathname,sizeof(locpathname),"%s",sp);
933             }
934         }
935
936         lang=getenv("LC_ALL");
937         if (!lang) lang=getenv("LC_MESSAGES");
938         if (!lang) lang=getenv("LANGUAGE");
939         if (!lang) lang=getenv("LANG");
940         if (lang) {
941           char llang[512];
942           int ret=0;
943           char *p;
944
945           strncpy(llang,lang,sizeof(llang));
946           lang=llang;
947
948           /* the language string can be like "es:fr_BE:ga" */
949           while (!ret && lang && (*lang)) {
950             p=strchr(lang,':');
951             if (p) *p++='\0';
952
953             /* first try full locale */
954             snprintf(langdir,sizeof(langdir),"%s/%s/%s",
955                      FORTDIR, lang, sp);
956             ret=add_file(percent, langdir, NULL, &File_list,
957                          &File_tail, NULL);
958
959             /* if not try language name only (two first chars) */
960             if (!ret) {
961               char ll[3];
962
963               strncpy(ll,lang,2);
964               ll[2]='\0';
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);
969             }
970
971             lang=p;
972           }
973           /* default */
974           if (!ret)
975             ret=add_file(percent, fullpathname, NULL, &File_list,
976                          &File_tail, NULL);
977           if ( (!ret && fullpathname != locpathname) || strcmp(sp, "all") == 0 )
978             ret=add_file(percent, locpathname, NULL, &File_list,
979                          &File_tail, NULL);
980
981           if (!ret){
982                   snprintf (locpathname, sizeof (locpathname), "%s/%s", getenv ("PWD"), sp);
983
984                   ret=add_file (percent, locpathname, NULL, &File_list, &File_tail, NULL);
985           }
986           if (!ret)
987             return FALSE;
988
989         }
990         else
991           if (!add_file(percent, fullpathname, NULL, &File_list,
992                         &File_tail, NULL))
993             return FALSE;
994     }
995     return TRUE;
996 }
997
998 /*
999  *    This routine evaluates the arguments on the command line
1000  */
1001 void getargs(int argc, char **argv)
1002 {
1003     register int ignore_case;
1004
1005 #ifndef NO_REGEX
1006     register char *pat = NULL;
1007
1008 #endif /* NO_REGEX */
1009     extern char *optarg;
1010     extern int optind;
1011     int ch;
1012
1013     ignore_case = FALSE;
1014
1015 #ifdef DEBUG
1016     while ((ch = getopt(argc, argv, "acDefilm:n:osvw")) != EOF)
1017 #else
1018     while ((ch = getopt(argc, argv, "acefilm:n:osvw")) != EOF)
1019 #endif /* DEBUG */
1020         switch (ch)
1021           {
1022           case 'a':             /* any fortune */
1023               All_forts++;
1024               break;
1025 #ifdef DEBUG
1026           case 'D':
1027               Debug++;
1028               break;
1029 #endif /* DEBUG */
1030           case 'e':
1031               Equal_probs++;    /* scatter un-allocted prob equally */
1032               break;
1033           case 'f':             /* find fortune files */
1034               Find_files++;
1035               break;
1036           case 'l':             /* long ones only */
1037               Long_only++;
1038               Short_only = FALSE;
1039               break;
1040           case 'n':
1041               SLEN = atoi(optarg);
1042               break;
1043           case 'o':             /* offensive ones only */
1044               Offend++;
1045               break;
1046           case 's':             /* short ones only */
1047               Short_only++;
1048               Long_only = FALSE;
1049               break;
1050           case 'w':             /* give time to read */
1051               Wait++;
1052               break;
1053 #ifdef  NO_REGEX
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");
1058               exit(0);
1059 #else /* NO_REGEX */
1060           case 'm':             /* dump out the fortunes */
1061               Match++;
1062               pat = optarg;
1063               break;
1064           case 'i':             /* case-insensitive match */
1065               ignore_case++;
1066               break;
1067 #endif /* NO_REGEX */
1068           case 'v':
1069               (void) printf("%s\n", program_version());
1070               exit(0);
1071           case 'c':
1072               Show_filename++;
1073               break;
1074           case '?':
1075           default:
1076               usage();
1077           }
1078     argc -= optind;
1079     argv += optind;
1080
1081     if (!form_file_list(argv, argc))
1082     {
1083         if (!ErrorMessage) fprintf (stderr, "No fortunes found\n");
1084         exit(1);                /* errors printed through form_file_list() */
1085     }
1086 #ifdef DEBUG
1087 /*      if (Debug >= 1)
1088  * print_list(File_list, 0); */
1089 #endif /* DEBUG */
1090 /* If (Find_files) print_list() moved to main */
1091 #ifndef NO_REGEX
1092     if (pat != NULL)
1093     {
1094         if (ignore_case)
1095             pat = conv_pat(pat);
1096         if (BAD_COMP(RE_COMP(pat)))
1097         {
1098             fprintf(stderr, "bad pattern: %s\n", pat);
1099             exit (1);
1100         }
1101     }
1102 #endif /* NO_REGEX */
1103 }
1104
1105 /*
1106  * init_prob:
1107  *      Initialize the fortune probabilities.
1108  */
1109 void init_prob(void)
1110 {
1111     register FILEDESC *fp, *last;
1112     register int percent, num_noprob, frac;
1113
1114     /*
1115      * Distribute the residual probability (if any) across all
1116      * files with unspecified probability (i.e., probability of 0)
1117      * (if any).
1118      */
1119
1120     percent = 0;
1121     num_noprob = 0;
1122     last = NULL;
1123     for (fp = File_tail; fp != NULL; fp = fp->prev)
1124         if (fp->percent == NO_PROB)
1125         {
1126             num_noprob++;
1127             if (Equal_probs)
1128                 last = fp;
1129         }
1130         else
1131             percent += fp->percent;
1132     DPRINTF(1, (stderr, "summing probabilities:%d%% with %d NO_PROB's\n",
1133                 percent, num_noprob));
1134     if (percent > 100)
1135     {
1136         fprintf(stderr,
1137                 "fortune: probabilities sum to %d%%!\n", percent);
1138         exit(1);
1139     }
1140     else if (percent < 100 && num_noprob == 0)
1141     {
1142         fprintf(stderr,
1143                 "fortune: no place to put residual probability (%d%%)\n",
1144                 percent);
1145         exit(1);
1146     }
1147     else if (percent == 100 && num_noprob != 0)
1148     {
1149         fprintf(stderr,
1150                 "fortune: no probability left to put in residual files\n");
1151         exit(1);
1152     }
1153     Spec_prob = percent;        /* this is for -f when % is specified on cmd line */
1154     percent = 100 - percent;
1155     if (Equal_probs)
1156     {
1157         if (num_noprob != 0)
1158         {
1159             if (num_noprob > 1)
1160             {
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)
1165                     {
1166                         fp->percent = frac;
1167                         percent -= frac;
1168                     }
1169             }
1170             last->percent = percent;
1171             DPRINTF(1, (stderr, ", residual = %d%%", percent));
1172         }
1173         else
1174         {
1175             DPRINTF(1, (stderr,
1176                         ", %d%% distributed over remaining fortunes\n",
1177                         percent));
1178         }
1179     }
1180     DPRINTF(1, (stderr, "\n"));
1181
1182 #ifdef DEBUG
1183 /*      if (Debug >= 1)
1184      * print_list(File_list, 0); *//* Causes crash with new %% code */
1185 #endif
1186 }
1187
1188 /*
1189  * zero_tbl:
1190  *      Zero out the fields we care about in a tbl structure.
1191  */
1192 void zero_tbl(register STRFILE * tp)
1193 {
1194     tp->str_numstr = 0;
1195     tp->str_longlen = 0;
1196     tp->str_shortlen = -1;
1197 }
1198
1199 /*
1200  * sum_tbl:
1201  *      Merge the tbl data of t2 into t1.
1202  */
1203 void sum_tbl(register STRFILE * t1, register STRFILE * t2)
1204 {
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;
1210 }
1211
1212 /*
1213  * get_tbl:
1214  *      Get the tbl data file the datfile.
1215  */
1216 void get_tbl(FILEDESC * fp)
1217 {
1218     auto int fd;
1219     register FILEDESC *child;
1220
1221     if (fp->read_tbl)
1222         return;
1223     if (fp->child == NULL)
1224     {
1225 #if 0
1226         /* This should not be needed anymore since add_file takes care of
1227          * empty directories now (Torsten Landschoff <torsten@debian.org>)
1228          */
1229
1230         /*
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
1235          */
1236         if (strcmp(LOCFORTDIR, fp->path) == 0 || strcmp(LOCOFFDIR, fp->path) == 0)
1237         {
1238             fp->read_tbl = TRUE;        /* Make it look like we've read it. */
1239             return;
1240         }
1241         /* End */
1242 #endif
1243         if ((fd = open(fp->datfile, O_RDONLY)) < 0)
1244         {
1245             perror(fp->datfile);
1246             exit(1);
1247         }
1248         if (read(fd, &fp->tbl.str_version, sizeof fp->tbl.str_version) !=
1249                 sizeof fp->tbl.str_version)
1250         {
1251             fprintf(stderr,
1252                     "fortune: %s corrupted\n", fp->path);
1253             exit(1);
1254         }
1255         if (read(fd, &fp->tbl.str_numstr, sizeof fp->tbl.str_numstr) !=
1256                 sizeof fp->tbl.str_numstr)
1257         {
1258             fprintf(stderr,
1259                     "fortune: %s corrupted\n", fp->path);
1260             exit(1);
1261         }
1262         if (read(fd, &fp->tbl.str_longlen, sizeof fp->tbl.str_longlen) !=
1263                 sizeof fp->tbl.str_longlen)
1264         {
1265             fprintf(stderr,
1266                     "fortune: %s corrupted\n", fp->path);
1267             exit(1);
1268         }
1269         if (read(fd, &fp->tbl.str_shortlen, sizeof fp->tbl.str_shortlen) !=
1270                 sizeof fp->tbl.str_shortlen)
1271         {
1272             fprintf(stderr,
1273                     "fortune: %s corrupted\n", fp->path);
1274             exit(1);
1275         }
1276         if (read(fd, &fp->tbl.str_flags, sizeof fp->tbl.str_flags) !=
1277                 sizeof fp->tbl.str_flags)
1278         {
1279             fprintf(stderr,
1280                     "fortune: %s corrupted\n", fp->path);
1281             exit(1);
1282         }
1283         if (read(fd, &fp->tbl.stuff, sizeof fp->tbl.stuff) !=
1284                 sizeof fp->tbl.stuff)
1285         {
1286             fprintf(stderr,
1287                     "fortune: %s corrupted\n", fp->path);
1288             exit(1);
1289         }
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);
1295         close(fd);
1296     }
1297     else
1298     {
1299         zero_tbl(&fp->tbl);
1300         for (child = fp->child; child != NULL; child = child->next)
1301         {
1302             get_tbl(child);
1303             sum_tbl(&fp->tbl, &child->tbl);
1304         }
1305     }
1306     fp->read_tbl = TRUE;
1307 }
1308
1309 /*
1310  * sum_noprobs:
1311  *      Sum up all the noprob probabilities, starting with fp.
1312  */
1313 void sum_noprobs(register FILEDESC * fp)
1314 {
1315     static bool did_noprobs = FALSE;
1316
1317     if (did_noprobs)
1318         return;
1319     zero_tbl(&Noprob_tbl);
1320     while (fp != NULL)
1321     {
1322         get_tbl(fp);
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);
1327         fp = fp->next;
1328     }
1329     did_noprobs = TRUE;
1330 }
1331
1332 /*
1333  * pick_child
1334  *      Pick a child from a chosen parent.
1335  */
1336 FILEDESC *pick_child(FILEDESC * parent)
1337 {
1338     register FILEDESC *fp;
1339     register int choice;
1340
1341     if (Equal_probs)
1342     {
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)
1347             continue;
1348         DPRINTF(1, (stderr, "    using %s\n", fp->name));
1349         return fp;
1350     }
1351     else
1352     {
1353         get_tbl(parent);
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;
1358              fp = fp->next)
1359         {
1360             choice -= fp->tbl.str_numstr;
1361             DPRINTF(1, (stderr, "\tskip %s, %ld (choice = %d)\n",
1362                         fp->name, fp->tbl.str_numstr, choice));
1363         }
1364         DPRINTF(1, (stderr, "    using %s, %ld\n", fp->name,
1365                     fp->tbl.str_numstr));
1366         return fp;
1367     }
1368 }
1369
1370 /*
1371  * open_dat:
1372  *      Open up the dat file if we need to.
1373  */
1374 void open_dat(FILEDESC * fp)
1375 {
1376     if (fp->datfd < 0 && (fp->datfd = open(fp->datfile, O_RDONLY)) < 0)
1377     {
1378         perror(fp->datfile);
1379         exit(1);
1380     }
1381 }
1382
1383 /*
1384  * get_pos:
1385  *      Get the position from the pos file, if there is one.  If not,
1386  *      return a random number.
1387  */
1388 void get_pos(FILEDESC * fp)
1389 {
1390     assert(fp->read_tbl);
1391     if (fp->pos == POS_UNKNOWN)
1392     {
1393         fp->pos = random() % fp->tbl.str_numstr;
1394     }
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));
1398 }
1399
1400 /*
1401  * get_fort:
1402  *      Get the fortune data file's seek pointer for the next fortune.
1403  */
1404 void get_fort(void)
1405 {
1406     register FILEDESC *fp;
1407     register int choice;
1408
1409     if (File_list->next == NULL || File_list->percent == NO_PROB)
1410         fp = File_list;
1411     else
1412     {
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)
1417                 break;
1418             else
1419             {
1420                 choice -= fp->percent;
1421                 DPRINTF(1, (stderr,
1422                             "    skip \"%s\", %d%% (choice = %d)\n",
1423                             fp->name, fp->percent, choice));
1424             }
1425         DPRINTF(1, (stderr,
1426                     "using \"%s\", %d%% (choice = %d)\n",
1427                     fp->name, fp->percent, choice));
1428     }
1429     if (fp->percent != NO_PROB)
1430         get_tbl(fp);
1431     else
1432     {
1433         if (fp->next != NULL)
1434         {
1435             sum_noprobs(fp);
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)
1440             {
1441                 choice -= fp->tbl.str_numstr;
1442                 fp = fp->next;
1443                 DPRINTF(1, (stderr,
1444                             "    skip \"%s\", %ld (choice = %d)\n",
1445                             fp->name, fp->tbl.str_numstr,
1446                             choice));
1447             }
1448             DPRINTF(1, (stderr, "using \"%s\", %ld\n", fp->name,
1449                         fp->tbl.str_numstr));
1450         }
1451         get_tbl(fp);
1452     }
1453     if (fp->tbl.str_numstr == 0)
1454     {
1455         fprintf(stderr, "fortune: no fortune found\n");
1456         exit(1);
1457     }
1458     if (fp->child != NULL)
1459     {
1460         DPRINTF(1, (stderr, "picking child\n"));
1461         fp = pick_child(fp);
1462     }
1463     Fortfile = fp;
1464     get_pos(fp);
1465     open_dat(fp);
1466     lseek(fp->datfd,
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]);
1472 }
1473
1474 /*
1475  * open_fp:
1476  *      Assocatiate a FILE * with the given FILEDESC.
1477  */
1478 void open_fp(FILEDESC * fp)
1479 {
1480     if (fp->inf == NULL && (fp->inf = fdopen(fp->fd, "r")) == NULL)
1481     {
1482         perror(fp->path);
1483         exit(1);
1484     }
1485 }
1486
1487 #ifndef NO_REGEX
1488 /*
1489  * maxlen_in_list
1490  *      Return the maximum fortune len in the file list.
1491  */
1492 int maxlen_in_list(FILEDESC * list)
1493 {
1494     register FILEDESC *fp;
1495     register int len, maxlen;
1496
1497     maxlen = 0;
1498     for (fp = list; fp != NULL; fp = fp->next)
1499     {
1500         if (fp->child != NULL)
1501         {
1502             if ((len = maxlen_in_list(fp->child)) > maxlen)
1503                 maxlen = len;
1504         }
1505         else
1506         {
1507             get_tbl(fp);
1508             if (fp->tbl.str_longlen > maxlen)
1509                 maxlen = fp->tbl.str_longlen;
1510         }
1511     }
1512     return maxlen;
1513 }
1514
1515 /*
1516  * matches_in_list
1517  *      Print out the matches from the files in the list.
1518  */
1519 void matches_in_list(FILEDESC * list)
1520 {
1521     unsigned char *sp;
1522     unsigned char *p; /* -allover */
1523     unsigned char ch; /* -allover */
1524     register FILEDESC *fp;
1525     int in_file, nchar;
1526     char *output;
1527
1528     for (fp = list; fp != NULL; fp = fp->next)
1529     {
1530         if (fp->child != NULL)
1531         {
1532             matches_in_list(fp->child);
1533             continue;
1534         }
1535         DPRINTF(1, (stderr, "searching in %s\n", fp->path));
1536         open_fp(fp);
1537         sp = Fortbuf;
1538         in_file = FALSE;
1539         while (fgets(sp, Fort_len, fp->inf) != NULL)
1540             if (!STR_ENDSTRING(sp, fp->tbl))
1541                 sp += strlen(sp);
1542             else
1543             {
1544                 *sp = '\0';
1545                 nchar = sp - Fortbuf;
1546
1547                 if (fp->utf8_charset)
1548                 {
1549                     output = recode_string (request, Fortbuf);
1550                 } else {
1551                     output = Fortbuf;
1552                 }
1553                 /* Should maybe rot13 Fortbuf -allover */
1554
1555                 if(fp->tbl.str_flags & STR_ROTATED)
1556                 {
1557                     for (p = output; (ch = *p); ++p)
1558                     {
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;
1563                     }
1564                 }
1565
1566                 DPRINTF(1, (stdout, "nchar = %d\n", nchar));
1567                 if ( (nchar < SLEN || !Short_only) &&
1568                         (nchar > SLEN || !Long_only) &&
1569                         RE_EXEC(output) )
1570                 {
1571                     if (!in_file)
1572                     {
1573                         fprintf(stderr, "(%s)\n%c\n", fp->name, fp->tbl.str_delim);
1574                         Found_one = TRUE;
1575                         in_file = TRUE;
1576                     }
1577                     fputs (output, stdout);
1578                     printf("%c\n", fp->tbl.str_delim);
1579                 }
1580
1581                 if (fp->utf8_charset)
1582                   free (output);
1583
1584                 sp = Fortbuf;
1585             }
1586     }
1587 }
1588
1589 /*
1590  * find_matches:
1591  *      Find all the fortunes which match the pattern we've been given.
1592  */
1593 int find_matches(void)
1594 {
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);
1599
1600     Found_one = FALSE;
1601     matches_in_list(File_list);
1602     return Found_one;
1603     /* NOTREACHED */
1604 }
1605 #endif /* NO_REGEX */
1606
1607 void display(FILEDESC * fp)
1608 {
1609     register char *p, ch;
1610     unsigned char line[BUFSIZ];
1611
1612     open_fp(fp);
1613     fseek(fp->inf, (long) Seekpts[0], 0);
1614     if (Show_filename)
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++)
1618     {
1619         if (fp->tbl.str_flags & STR_ROTATED)
1620         {
1621             for (p = line; (ch = *p); ++p)
1622             {
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;
1627             }
1628         }
1629         if(fp->utf8_charset) {
1630             char *output;
1631             output = recode_string (request, line);
1632             fputs(output, stdout);
1633             free(output);
1634         }
1635         else
1636             fputs(line, stdout);
1637     }
1638     fflush(stdout);
1639
1640     if(fp->utf8_charset) {
1641         recode_delete_request(request);
1642     }
1643 }
1644
1645 /*
1646  * fortlen:
1647  *      Return the length of the fortune.
1648  */
1649 int fortlen(void)
1650 {
1651     register int nchar;
1652     unsigned char line[BUFSIZ];
1653
1654     if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED)))
1655         nchar = (Seekpts[1] - Seekpts[0]) - 2;  /* for %^J delimiter */
1656     else
1657     {
1658         open_fp(Fortfile);
1659         fseek(Fortfile->inf, (long) Seekpts[0], 0);
1660         nchar = 0;
1661         while (fgets(line, sizeof line, Fortfile->inf) != NULL &&
1662                !STR_ENDSTRING(line, Fortfile->tbl))
1663             nchar += strlen(line);
1664     }
1665     Fort_len = nchar;
1666     return nchar;
1667 }
1668
1669 int max(register int i, register int j)
1670 {
1671     return (i >= j ? i : j);
1672 }
1673
1674 int main(int ac, char *av[])
1675 {
1676     char *ctype, *crequest;
1677     getargs(ac, av);
1678
1679     outer = recode_new_outer(true);
1680     request = recode_new_request (outer);
1681
1682     setlocale(LC_ALL,"");
1683     ctype = nl_langinfo(CODESET);
1684     if(strcmp(ctype,"ANSI_X3.4-1968") == 0)
1685         ctype="ISO-8859-1";
1686
1687     crequest = malloc(strlen(ctype) + 7 + 1);
1688     sprintf(crequest, "UTF-8..%s", ctype);
1689     recode_scan_request (request, crequest);
1690     free(crequest);
1691
1692 #ifndef NO_REGEX
1693     if (Match)
1694         exit(find_matches() != 0);
1695 #endif
1696     init_prob();
1697     if (Find_files)
1698     {
1699         sum_noprobs(File_list);
1700         if (Equal_probs)
1701             calc_equal_probs();
1702         print_list(File_list, 0);
1703         exit(0);
1704     }
1705     srandom((int) (time((time_t *) NULL) + getpid()));
1706     do
1707     {
1708         get_fort();
1709     }
1710     while ((Short_only && fortlen() > SLEN) ||
1711            (Long_only && fortlen() <= SLEN));
1712
1713     display(Fortfile);
1714
1715     if (Wait)
1716     {
1717         fortlen();
1718         sleep((unsigned int) max(Fort_len / CPERS, MINW));
1719     }
1720     exit(0);
1721     /* NOTREACHED */
1722 }