/* C O O K I E * * Print a random cookie. * */ /*)BUILD */ #ifdef DOCUMENTATION title cookie Print a Random Message index Print a Random Message synopsis cookie [options] description Cookie prints a fortune cookie. The options are: .lm +8 .s.i- 8;-N Output N cookies. .s.i- 8;N Output the Nth cookie. .s.i- 8;-a Output all cookies in entry order. cookie -a -a appends %% to each cookie (so that the output file may be fed back to coobld). .s.i -8;-f file Read cookies from this file. (Ignore search list.) .s.i- 8;-h Eliminate Heinlien and Lazarus Long cookies .s.i- 8;-v Video mode -- Format for a VT52 or VT100 (VT100 assumed). .s.i- 8;-vt52 Explicitly format for a VT52 or VT100 in VT52 mode .s.i- 8;-vt100 Explicitly format for a VT100 in ANSI mode .s.i- 8;-c Continuous -- give lots of random cookies .s.i- 8;-s N Continuous with specified sleep interval. This may also be specified "-sN". If no number is given, "-s 60" will be assumed. .s.i -8;-w file Write cookies to the file as well as to the screen. .s.i -8;-d Write cookies to the file as well as to the screen. (The file is assumed to be a DECtalk terminal.) .s.i- 8;/HEINLIEN=NO##Same as -h, but for VMS systems .s.lm -8 To enable elimination of cookies by Robert A. Heinlien and his alter ego Lazarus Long, define "NOHEINLIEN". If eliminated, a suitable anticookie will be presented. .s Cookie uses the following search list to locate the cookie file: .s.nf []cookie.fil sys_cookie:cookie.fil public:cookie.fil pub:cookie.fil sys$public:cookie.fil games:cookie.fil sys$games:cookie.fil .s.f author Martin Minow bugs #endif #define NOHEINLIEN 1 #include #include #ifdef vms extern int errno; #define IO_ERROR errno #endif #ifdef rsx #define R_MODE "run" #else #ifdef rt11 #define R_MODE "rn" #else #define R_MODE "r" #endif #endif #ifdef decus int $$narg = 1; /* No argv> prompt */ extern int $$rsts; #else int $$rsts = 0; #define streq(a,b) ((strcmp(a,b) == 0)) #endif extern long rand(); extern long seed; extern long time(); #define EOS 0 int doall = 0; int video = 0; int isvt52 = 0; int notLong = 0; /* nor Heinlien */ int continuous = 0; int long_sleep = 0; FILE *extrafd = NULL; FILE *talkfd = NULL; int cur_line = 0; int linecount = 0; char *old_buff = NULL; char sc_buff[121]; char temp_text[81]; FILE *cookfd = NULL; char *cookfile[] = { /* Cookie search list */ "cookie.fil", /* Current directory first */ "games:cookie.fil", /* Some */ "game:cookie.fil", /* games */ "sys$games:cookie.fil", /* directories */ "public:cookie.fil", /* Various */ "pub:cookie.fil", /* public */ "sys$public:cookie.fil", /* directories */ NULL, }; struct header { long ncookie; /* Number of cookies */ short nindex; /* Dimension of index[] */ short subindex; /* Number of subindex entries */ short sindex; /* Sizeof index for alloc */ char date[28]; /* Date cookie file built */ } header; #define NCOOKIES (header.ncookie) #define TEXTSIZE 2048 #define INDEXMAX (TEXTSIZE / sizeof (long)) union { char text[TEXTSIZE]; /* Cookies stored here */ long index[INDEXMAX]; /* Cookie sub index entries */ } t; /* * If only one cookie is needed, topindex[] can be overlayed over t.index[] */ long topindex[INDEXMAX]; /* Top index entries */ main(argc, argv) int argc; char *argv[]; { register int howmany; /* How many to do */ register char *ap; register int outtime; #ifdef rsx int noargs; extern int $$pos; /* TRUE on P/OS */ #endif long which; long when; long atol(); which = 0; #ifdef rsx noargs = (argc <= 1); /* For P/OS */ #endif while (argc > 1) { ap = argv[1]; if (isdigit(*ap) || (*ap == '-' && isdigit(ap[1]))) which = atol(ap); else if (*ap != '-') { #ifdef NOHEINLIEN if (streq(ap, "/HEINLIEN=NO") || streq(ap, "/heinlien=no")) notLong++; else #endif fprintf(stderr, "?Unknown command \"%s\"\n", ap); } else for (ap++; *ap; ap++) { if (isupper(*ap)) *ap = tolower(*ap); switch (*ap) { case 'a': doall++; break; case 'c': continuous++; break; case 'f': if ((cookfd = fopen(argv[2], R_MODE)) == NULL) { perror(argv[2]); exit(IO_ERROR); } argv++; argc--; goto next_arg; #ifdef NOHEINLIEN case 'h': notLong++; /* No (Lazarus) Longs */ break; #endif case 's': /* Long sleep */ continuous++; if (isdigit(ap[1])) { long_sleep = atoi(&ap[1]); } else if (isdigit(argv[2][0])) { long_sleep = atoi(argv[2]); argv++; /* Skip next argument */ argc--; /* too. */ } else { long_sleep = 60; break; } goto next_arg; case 'v': video = 1; if (ap[1] == 't' || ap[1] == 'T') { video = atoi(&ap[2]); if (video == 52) isvt52 = 1; else if (video == 100) isvt52 = 0; else fprintf(stderr, "?Unknown video \"%s\"\n", ap); goto next_arg; } break; case 'd': if ((talkfd = fopen(argv[2], "w")) == NULL) { perror(argv[2]); exit(IO_ERROR); } argv++; argc--; goto next_arg; case 'w': if ((extrafd = fopen(argv[2], "w")) == NULL) { perror(argv[2]); exit(IO_ERROR); } argv++; argc--; goto next_arg; default: fprintf(stderr, "?Unknown option '%c'\n", *ap); fprintf(stderr, "Option string is \"%s\"\n", ap); } } next_arg: argc--; argv++; } setup_cookie(); if (video) erpage(1, 1); if (doall) { for (which = 1; which <= NCOOKIES; which++) { docookie(which, time(NULL)); } } if (continuous) which = -1; if (which > 0) { if (which > NCOOKIES) { sprintf(t.text, "Misfortune: there are only %ld cookies.\n", NCOOKIES); output(which, t.text, time(NULL)); } else docookie(which, time(NULL)); } else { if ((howmany = -which) == 0) howmany = 1; while (howmany-- > 0) { which = rand(); when = time(NULL); which = ((which ^ when) & 0x3FFFFFFFL) % NCOOKIES; docookie(which + 1, when); if (continuous) howmany = 1; if (howmany > 0) { if (long_sleep > 0) sleep(long_sleep); else { outtime = (linecount + 1) * 2; if (outtime > 0) sleep(outtime); } } } } finish: if (extrafd != NULL) fclose(extrafd); if (talkfd != NULL) fclose(talkfd); } setup_cookie() /* * Initialize cookie file, finding it in the search list */ { register char **namep; register int temp; if (cookfd == NULL) { for (namep = cookfile; *namep != NULL; namep++) { if ((cookfd = fopen(*namep, R_MODE)) != NULL) { break; } } if (cookfd == NULL) { fprintf(stderr, "Can't open cookie file\n"); exit(IO_ERROR); } } #ifdef rsx if ((temp = fget(&header, sizeof header, cookfd)) != sizeof header) rerror(sizeof header, temp, "Reading header file"); if ((temp = fget(&topindex, header.sindex, cookfd)) != header.sindex) rerror(header.sindex, temp, "Reading top-level index"); #else if ((temp = fread(&header, sizeof header, 1, cookfd)) != 1) rerror(sizeof header, temp, "Reading header file"); if ((temp = fread(&topindex, header.sindex, 1, cookfd)) != 1) rerror(header.sindex, temp, "Reading top-level index"); #endif } rerror(expected, got, why) int expected; /* Bytes in item */ int got; /* Unexpectedly isn't == 1 */ char *why; /* * Fatal read error */ { perror(why); #ifdef rsx fprintf(stderr, "expected %d bytes, read %d bytes\n", expected, got); #else fprintf(stderr, "expected 1 item of %d bytes, read %d items\n", expected, got); #endif fprintf(stderr, "Can't continue\n"); exit(IO_ERROR); } docookie(which, when) long which; long when; /* * Read and output the Nth cookie. Note: 1-origin addressing */ { long index; long temp; register int bytect; register int i; register char *tp; which--; temp = header.nindex; temp = which / temp; i = temp; if (i >= header.subindex) { fprintf(stderr, "Bug: Gone too far, index = %d, max = %d\n", i, header.subindex); exit(IO_ERROR); } index = topindex[i]; if (fseek(cookfd, index, 0) != 0) { perror("cookie file"); exit(IO_ERROR); } #ifdef rsx if ((bytect = fget(&t.index, header.sindex, cookfd)) != header.sindex) { #else if ((bytect = fread(&t.index, header.sindex, 1, cookfd)) != 1) { #endif fprintf(stderr, "seek to sub-index %d, %ld. %06o %06o\n", i, index, index); rerror(header.sindex, bytect, "Can't read sub_index"); } temp = header.nindex; temp = which % temp; i = temp; index = t.index[i]; if (fseek(cookfd, index, 0) != 0) { perror("minor index seek"); fprintf(stderr, "?Requesting cookie %ld, max = %ld\n", which, header.ncookie); return; } tp = &t.text[0]; #ifdef rsx while (fget(tp, (sizeof t.text) - (tp - t.text), cookfd) > 0 #else while (fgets(tp, (sizeof t.text) - (tp - t.text), cookfd) != NULL #endif && !feof(cookfd) && tp[0] != '%' && tp[1] != '%') { tp += strlen(tp); } *tp = EOS; /* Remove trailing %% */ if (feof(cookfd)) { rerror(tp - t.text, tp - t.text, "Can't read cookie"); } if (notLong && isLong(t.text)) antiheinlien(which + 1, when); else output(which + 1, &t.text, when); } static char *anti1[] = { "A Heinlien Cookie you do distain,\n\ I'll say no more to soothe your brain.", "I'd rather drink a gallon of overage Rhine wine,\n\ Than read a quote by Robert A. Heinlien.", "I'd rather bite boils from an elephant's behind,\n\ Than read more quotes by Robert A. Heinlien.", "*I'd rather a tax audit found me wrong,", "*I'd rather be dribbled by old King Kong,", "*I'd rather find paraquat in my bong,", "*I'd rather Chuck Barris used me as the gong,", "*I'd rather do updates to Atari Pong,", "*I'd rather write a \"User's Guide to Pong\",", }; static char *anti2[] = { "Than read another quote by Lazarus Long.", "Than have to read more of Lazarus Long.", "Than be forced to consider more Lazarus Long.", }; isLong(text) register char *text; /* * Return TRUE if the text contains "Heinlien" or "Lazarus Long" */ { register char c; while ((c = *text++) != EOS) { switch (c) { case 'H': if (streq(text, "einlien")) return (TRUE); break; case 'L': if (streq(text, "azarus Long")) return (TRUE); break; } } return (FALSE); } static char antibuffer[257]; antiheinlien(which, when) long which; long when; /* * Output a non-Heinlien cookie */ { register char *tp; tp = anti1[(rand() & 32767) % (sizeof anti1) / (sizeof (char *))]; if (*tp == '*') { sprintf(antibuffer, "%s\n%s", tp + 1, anti2[(rand() & 32767) % (sizeof anti2) / (sizeof (char *))]); } else { strcpy(antibuffer, tp); } output(which, antibuffer, when); } static int nvoice; static char *voices[] = { "[:np]", "[:nb]", "[:nh]", "[:nu]", "[:nk]", "[:nf]", "[:nr]", NULL }; output(which, out_text, when) long which; char *out_text; long when; /* * Output one cookie */ { register char *tp; startoutput(which, when); if (extrafd != NULL) fprintf(extrafd, "%s\n", out_text); if (talkfd != NULL) speak(out_text); printf("%s%s", out_text, (doall > 1) ? "%%\n" : ""); linecount = 1; tp = out_text; while ((tp = strchr(tp, '\n')) != NULL) { linecount++; tp++; } endoutput(); } speak(out_text) char *out_text; /* * Drive DECtalk */ { register char *tp; register int c; if (voices[nvoice] == NULL) nvoice = 0; fprintf(talkfd, "%s ", voices[nvoice]); nvoice++; for (tp = out_text; (c = *tp++) != EOS;) { switch (c) { case ',': putc(',', talkfd); if (!isspace(*tp)) putc(' ', talkfd); break; case '[': /* [ => ( */ putc('(', talkfd); break; case ']': putc(')', talkfd); /* ] => ) */ break; case '(': /* (c.a. => ( c.a. */ if (isalpha(*tp) && tp[1] == '.') { putc(c, talkfd); c = ' '; } putc(c, talkfd); break; case ')': /* (44-22 BC)? => */ if (tp > &out_text[1] /* (44-22 BC) */ && !isalpha(tp[-2]) && *tp == '?') { putc(c, talkfd); c = ' '; tp++; } putc(c, talkfd); break; case '.': if (tp > &out_text[3] /* H. L. Menken => */ && isupper(tp[-2]) /* H L Menken */ && isspace(tp[-3]) /* so DECtalk doesn't */ && isspace(*tp)) /* pause. */ continue; if (tp > &out_text[2] /* ...foo => ... foo */ && tp[-2] == '.' && isalpha(*tp)) { putc(c, talkfd); c = ' '; } putc(c, talkfd); break; case '?': if (tp > &out_text[2] && isalpha(tp[-2]) && isspace(*tp)) putc(c, talkfd); break; default: putc(c, talkfd); if (isdigit(c) && !isdigit(*tp)) { switch (*tp) { case '?': /* 1900?-1980 */ if (tp[1] != '-') break; tp++; /* Skip ? */ case '-': /* 1900-1910 */ putc(',', talkfd); putc(' ', talkfd); tp++; if (*tp == '?' && tp[1] == ')') tp += 2; /* 1900-?) */ break; } } } } putc('\f', talkfd); } startoutput(which, when) long which; long when; /* * Initialize output */ { register char *timebuf; extern char *ctime(); if (video) { sprintf(temp_text, "%ld", which); timebuf = ctime(&when); timebuf[24] = EOS; erpage(1, 1); if (isvt52) { /* * VT52 */ vtout(1, 27, timebuf); vtout(3, 36, temp_text); } else { /* * VT100 */ if ($$rsts) { vtout(1, 8, "\233#3"); /* Double high */ vtout(0, 0, timebuf); /* Top half */ vtout(2, 8, "\233#4"); /* Double high */ vtout(0, 0, timebuf); /* Bottom half */ vtout(3, 18, "\233#6"); /* Double wide */ vtout(0, 0, temp_text); } else { vtout(1, 8, "\033#3"); /* Double high */ vtout(0, 0, timebuf); /* Top half */ vtout(2, 8, "\033#4"); /* Double high */ vtout(0, 0, timebuf); /* Bottom half */ vtout(3, 18, "\033#6"); /* Double wide */ vtout(0, 0, temp_text); } } vtout(5, 1, ""); } } endoutput() /* * Clear the rest of the screen */ { if (video) { erpage(0, 0); fflush(stdout); } else putchar('\n'); } vtcurse(row, col) int row, col; { if (row == 0) return; if (isvt52) { printf(($$rsts) ? "\r\233Y%c%c" : "\r\033Y%c%c", row + 040 - 1, col + 040 - 1); } else { printf(($$rsts) ? "\r\233[%d;%dH" : "\r\033[%d;%dH", row, col); } } erpage(row, col) int row, col; { vtcurse(row, col); if (isvt52) fputs(($$rsts) ? "\233J" : "\033J", stdout); else fputs(($$rsts) ? "\233[J" : "\033[J", stdout); } erline(row, col) int row, col; { vtcurse(row, col); if (isvt52) fputs(($$rsts) ? "\233K" : "\033K", stdout); else fputs(($$rsts) ? "\233[K" : "\033[K", stdout); } vtout(row, col, text) int row, col; char *text; { vtcurse(row, col); fputs(text, stdout); }