📄 fortune.c
字号:
/*- * Copyright (c) 1986, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Ken Arnold. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char copyright[] ="@(#) Copyright (c) 1986, 1993\n\ The Regents of the University of California. All rights reserved.\n";#endif /* not lint */#ifndef lintstatic char sccsid[] = "@(#)fortune.c 8.1 (Berkeley) 5/31/93";#endif /* not lint */# include <sys/param.h># include <sys/stat.h># include <sys/dir.h># include <fcntl.h># include <assert.h># include <unistd.h># include <stdio.h># include <ctype.h># include <stdlib.h># include <string.h># include "strfile.h"# include "pathnames.h"# define TRUE 1# define FALSE 0# define bool short# define MINW 6 /* minimum wait if desired */# define CPERS 20 /* # of chars for each sec */# define SLEN 160 /* # of chars in short fortune */# define POS_UNKNOWN ((off_t) -1) /* pos for file unknown */# define NO_PROB (-1) /* no prob specified for file */# ifdef DEBUG# define DPRINTF(l,x) if (Debug >= l) fprintf x; else# undef NDEBUG# else# define DPRINTF(l,x)# define NDEBUG 1# endiftypedef struct fd { int percent; int fd, datfd; off_t pos; FILE *inf; char *name; char *path; char *datfile, *posfile; bool read_tbl; bool was_pos_file; STRFILE tbl; int num_children; struct fd *child, *parent; struct fd *next, *prev;} FILEDESC;bool Found_one; /* did we find a match? */bool Find_files = FALSE; /* just find a list of proper fortune files */bool Wait = FALSE; /* wait desired after fortune */bool Short_only = FALSE; /* short fortune desired */bool Long_only = FALSE; /* long fortune desired */bool Offend = FALSE; /* offensive fortunes only */bool All_forts = FALSE; /* any fortune allowed */bool Equal_probs = FALSE; /* scatter un-allocted prob equally */#ifndef NO_REGEXbool Match = FALSE; /* dump fortunes matching a pattern */#endif#ifdef DEBUGbool Debug = FALSE; /* print debug messages */#endifchar *Fortbuf = NULL; /* fortune buffer for -m */int Fort_len = 0;off_t Seekpts[2]; /* seek pointers to fortunes */FILEDESC *File_list = NULL, /* Head of file list */ *File_tail = NULL; /* Tail of file list */FILEDESC *Fortfile; /* Fortune file to use */STRFILE Noprob_tbl; /* sum of data for all no prob files */int add_dir __P((FILEDESC *));int add_file __P((int, char *, char *, FILEDESC **, FILEDESC **, FILEDESC *));void all_forts __P((FILEDESC *, char *));char *copy __P((char *, u_int));void display __P((FILEDESC *));void do_free __P((void *));void *do_malloc __P((u_int));int form_file_list __P((char **, int));int fortlen __P((void));void get_fort __P((void));void get_pos __P((FILEDESC *));void get_tbl __P((FILEDESC *));void getargs __P((int, char *[]));void init_prob __P((void));int is_dir __P((char *));int is_fortfile __P((char *, char **, char **, int));int is_off_name __P((char *));int max __P((int, int));FILEDESC * new_fp __P((void));char *off_name __P((char *));void open_dat __P((FILEDESC *));void open_fp __P((FILEDESC *));FILEDESC * pick_child __P((FILEDESC *));void print_file_list __P((void));void print_list __P((FILEDESC *, int));void sum_noprobs __P((FILEDESC *));void sum_tbl __P((STRFILE *, STRFILE *));void usage __P((void));void zero_tbl __P((STRFILE *));#ifndef NO_REGEXchar *conv_pat __P((char *));int find_matches __P((void));void matches_in_list __P((FILEDESC *));int maxlen_in_list __P((FILEDESC *));#endif#ifndef NO_REGEX#ifdef REGCMP# define RE_COMP(p) (Re_pat = regcmp(p, NULL))# define BAD_COMP(f) ((f) == NULL)# define RE_EXEC(p) regex(Re_pat, (p))char *Re_pat;char *regcmp(), *regex();#else# define RE_COMP(p) (p = re_comp(p))# define BAD_COMP(f) ((f) != NULL)# define RE_EXEC(p) re_exec(p)#endif#endifintmain(ac, av)int ac;char *av[];{#ifdef OK_TO_WRITE_DISK int fd;#endif /* OK_TO_WRITE_DISK */ getargs(ac, av);#ifndef NO_REGEX if (Match) exit(find_matches() != 0);#endif init_prob(); srandom((int)(time((time_t *) NULL) + getpid())); do { get_fort(); } while ((Short_only && fortlen() > SLEN) || (Long_only && fortlen() <= SLEN)); display(Fortfile);#ifdef OK_TO_WRITE_DISK if ((fd = creat(Fortfile->posfile, 0666)) < 0) { perror(Fortfile->posfile); exit(1); }#ifdef LOCK_EX /* * if we can, we exclusive lock, but since it isn't very * important, we just punt if we don't have easy locking * available. */ (void) flock(fd, LOCK_EX);#endif /* LOCK_EX */ write(fd, (char *) &Fortfile->pos, sizeof Fortfile->pos); if (!Fortfile->was_pos_file) (void) chmod(Fortfile->path, 0666);#ifdef LOCK_EX (void) flock(fd, LOCK_UN);#endif /* LOCK_EX */#endif /* OK_TO_WRITE_DISK */ if (Wait) { if (Fort_len == 0) (void) fortlen(); sleep((unsigned int) max(Fort_len / CPERS, MINW)); } exit(0); /* NOTREACHED */}voiddisplay(fp)FILEDESC *fp;{ register char *p, ch; char line[BUFSIZ]; open_fp(fp); (void) fseek(fp->inf, (long)Seekpts[0], 0); for (Fort_len = 0; fgets(line, sizeof line, fp->inf) != NULL && !STR_ENDSTRING(line, fp->tbl); Fort_len++) { if (fp->tbl.str_flags & STR_ROTATED) for (p = line; ch = *p; ++p) if (isupper(ch)) *p = 'A' + (ch - 'A' + 13) % 26; else if (islower(ch)) *p = 'a' + (ch - 'a' + 13) % 26; fputs(line, stdout); } (void) fflush(stdout);}/* * fortlen: * Return the length of the fortune. */intfortlen(){ register int nchar; char line[BUFSIZ]; if (!(Fortfile->tbl.str_flags & (STR_RANDOM | STR_ORDERED))) nchar = (Seekpts[1] - Seekpts[0] <= SLEN); else { open_fp(Fortfile); (void) fseek(Fortfile->inf, (long)Seekpts[0], 0); nchar = 0; while (fgets(line, sizeof line, Fortfile->inf) != NULL && !STR_ENDSTRING(line, Fortfile->tbl)) nchar += strlen(line); } Fort_len = nchar; return nchar;}/* * This routine evaluates the arguments on the command line */voidgetargs(argc, argv)register int argc;register char **argv;{ register int ignore_case;# ifndef NO_REGEX register char *pat;# endif /* NO_REGEX */ extern char *optarg; extern int optind; int ch; ignore_case = FALSE; pat = NULL;# ifdef DEBUG while ((ch = getopt(argc, argv, "aDefilm:osw")) != EOF)#else while ((ch = getopt(argc, argv, "aefilm:osw")) != EOF)#endif /* DEBUG */ switch(ch) { case 'a': /* any fortune */ All_forts++; break;# ifdef DEBUG case 'D': Debug++; break;# endif /* DEBUG */ case 'e': Equal_probs++; /* scatter un-allocted prob equally */ break; case 'f': /* find fortune files */ Find_files++; break; case 'l': /* long ones only */ Long_only++; Short_only = FALSE; break; case 'o': /* offensive ones only */ Offend++; break; case 's': /* short ones only */ Short_only++; Long_only = FALSE; break; case 'w': /* give time to read */ Wait++; break;# ifdef NO_REGEX case 'i': /* case-insensitive match */ case 'm': /* dump out the fortunes */ (void) fprintf(stderr, "fortune: can't match fortunes on this system (Sorry)\n"); exit(0);# else /* NO_REGEX */ case 'm': /* dump out the fortunes */ Match++; pat = optarg; break; case 'i': /* case-insensitive match */ ignore_case++; break;# endif /* NO_REGEX */ case '?': default: usage(); } argc -= optind; argv += optind; if (!form_file_list(argv, argc)) exit(1); /* errors printed through form_file_list() */#ifdef DEBUG if (Debug >= 1) print_file_list();#endif /* DEBUG */ if (Find_files) { print_file_list(); exit(0); }# ifndef NO_REGEX if (pat != NULL) { if (ignore_case) pat = conv_pat(pat); if (BAD_COMP(RE_COMP(pat))) {#ifndef REGCMP fprintf(stderr, "%s\n", pat);#else /* REGCMP */ fprintf(stderr, "bad pattern: %s\n", pat);#endif /* REGCMP */ } }# endif /* NO_REGEX */}/* * form_file_list: * Form the file list from the file specifications. */intform_file_list(files, file_cnt)register char **files;register int file_cnt;{ register int i, percent; register char *sp; if (file_cnt == 0) if (Find_files) return add_file(NO_PROB, FORTDIR, NULL, &File_list, &File_tail, NULL); else return add_file(NO_PROB, "fortunes", FORTDIR, &File_list, &File_tail, NULL); for (i = 0; i < file_cnt; i++) { percent = NO_PROB; if (!isdigit(files[i][0])) sp = files[i]; else { percent = 0; for (sp = files[i]; isdigit(*sp); sp++) percent = percent * 10 + *sp - '0'; if (percent > 100) { fprintf(stderr, "percentages must be <= 100\n"); return FALSE; } if (*sp == '.') { fprintf(stderr, "percentages must be integers\n"); return FALSE; } /* * If the number isn't followed by a '%', then * it was not a percentage, just the first part * of a file name which starts with digits. */ if (*sp != '%') { percent = NO_PROB; sp = files[i]; } else if (*++sp == '\0') { if (++i >= file_cnt) { fprintf(stderr, "percentages must precede files\n"); return FALSE; } sp = files[i]; } } if (strcmp(sp, "all") == 0) sp = FORTDIR; if (!add_file(percent, sp, NULL, &File_list, &File_tail, NULL)) return FALSE; } return TRUE;}/* * add_file: * Add a file to the file list. */intadd_file(percent, file, dir, head, tail, parent)int percent;register char *file;char *dir;FILEDESC **head, **tail;FILEDESC *parent;{ register FILEDESC *fp; register int fd; register char *path, *offensive; register bool was_malloc; register bool isdir; if (dir == NULL) { path = file; was_malloc = FALSE; } else { path = do_malloc((unsigned int) (strlen(dir) + strlen(file) + 2)); (void) strcat(strcat(strcpy(path, dir), "/"), file); was_malloc = TRUE; } if ((isdir = is_dir(path)) && parent != NULL) { if (was_malloc) free(path); return FALSE; /* don't recurse */ } offensive = NULL; if (!isdir && parent == NULL && (All_forts || Offend) && !is_off_name(path)) { offensive = off_name(path); was_malloc = TRUE; if (Offend) { if (was_malloc) free(path); path = offensive; file = off_name(file); } } DPRINTF(1, (stderr, "adding file \"%s\"\n", path));over: if ((fd = open(path, 0)) < 0) { /* * This is a sneak. If the user said -a, and if the * file we're given isn't a file, we check to see if * there is a -o version. If there is, we treat it as * if *that* were the file given. We only do this for * individual files -- if we're scanning a directory, * we'll pick up the -o file anyway. */ if (All_forts && offensive != NULL) { path = offensive; if (was_malloc) free(path); offensive = NULL; was_malloc = TRUE; DPRINTF(1, (stderr, "\ttrying \"%s\"\n", path)); file = off_name(file); goto over; } if (dir == NULL && file[0] != '/') return add_file(percent, file, FORTDIR, head, tail, parent); if (parent == NULL) perror(path); if (was_malloc) free(path); return FALSE; } DPRINTF(2, (stderr, "path = \"%s\"\n", path)); fp = new_fp(); fp->fd = fd; fp->percent = percent; fp->name = file; fp->path = path; fp->parent = parent; if ((isdir && !add_dir(fp)) || (!isdir && !is_fortfile(path, &fp->datfile, &fp->posfile, (parent != NULL)))) { if (parent == NULL) fprintf(stderr, "fortune:%s not a fortune file or directory\n", path); free((char *) fp); if (was_malloc) free(path); do_free(fp->datfile); do_free(fp->posfile); do_free(offensive); return FALSE; } /* * If the user said -a, we need to make this node a pointer to * both files, if there are two. We don't need to do this if * we are scanning a directory, since the scan will pick up the * -o file anyway. */ if (All_forts && parent == NULL && !is_off_name(path)) all_forts(fp, offensive); if (*head == NULL) *head = *tail = fp; else if (fp->percent == NO_PROB) { (*tail)->next = fp; fp->prev = *tail; *tail = fp; } else { (*head)->prev = fp; fp->next = *head; *head = fp; }#ifdef OK_TO_WRITE_DISK fp->was_pos_file = (access(fp->posfile, W_OK) >= 0);#endif /* OK_TO_WRITE_DISK */ return TRUE;}/* * new_fp: * Return a pointer to an initialized new FILEDESC. */FILEDESC *new_fp(){ register FILEDESC *fp; fp = (FILEDESC *) do_malloc(sizeof *fp); fp->datfd = -1; fp->pos = POS_UNKNOWN; fp->inf = NULL; fp->fd = -1; fp->percent = NO_PROB; fp->read_tbl = FALSE; fp->next = NULL; fp->prev = NULL; fp->child = NULL; fp->parent = NULL; fp->datfile = NULL; fp->posfile = NULL; return fp;}/* * off_name: * Return a pointer to the offensive version of a file of this name. */char *off_name(file)char *file;{ char *new; new = copy(file, (unsigned int) (strlen(file) + 2)); return strcat(new, "-o");}/* * is_off_name: * Is the file an offensive-style name? */intis_off_name(file)char *file;{ int len; len = strlen(file); return (len >= 3 && file[len - 2] == '-' && file[len - 1] == 'o');}/* * all_forts: * Modify a FILEDESC element to be the parent of two children if * there are two children to be a parent of. */voidall_forts(fp, offensive)register FILEDESC *fp;char *offensive;{ register char *sp; register FILEDESC *scene, *obscene; register int fd; auto char *datfile, *posfile; if (fp->child != NULL) /* this is a directory, not a file */ return; if (!is_fortfile(offensive, &datfile, &posfile, FALSE)) return; if ((fd = open(offensive, 0)) < 0) return; DPRINTF(1, (stderr, "adding \"%s\" because of -a\n", offensive)); scene = new_fp(); obscene = new_fp(); *scene = *fp; fp->num_children = 2; fp->child = scene; scene->next = obscene; obscene->next = NULL; scene->child = obscene->child = NULL; scene->parent = obscene->parent = fp; fp->fd = -1; scene->percent = obscene->percent = NO_PROB; obscene->fd = fd; obscene->inf = NULL; obscene->path = offensive; if ((sp = rindex(offensive, '/')) == NULL) obscene->name = offensive; else obscene->name = ++sp; obscene->datfile = datfile; obscene->posfile = posfile; obscene->read_tbl = FALSE;#ifdef OK_TO_WRITE_DISK obscene->was_pos_file = (access(obscene->posfile, W_OK) >= 0);#endif /* OK_TO_WRITE_DISK */}/* * add_dir: * Add the contents of an entire directory. */intadd_dir(fp)register FILEDESC *fp;{ register DIR *dir;#ifdef SYSV register struct dirent *dirent; /* NIH, of course! */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -