[alt.sources] cookie - C fortune cookie program for use with "%%" cookie files

karl@sugar.hackercorp.com (Karl Lehenbauer) (10/21/90)

Cookie is a program to randomly retrieve cookies from a fortune cookie file.
Each cookie can contain an arbitrary number of lines of text.  By generating
a small "hash" file containing the addresses of each cookie in the cookie
file, cookie has a high-performance way to look up cookies, plus it is "fair,"
which is to say that short cookies are equally as likely to be selected as 
long ones.

The code is again released into the public domain without restriction.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of shell archive."
# Contents:  README cookie.1 Makefile cookie.h cookie.c cookhash.c
# Wrapped by karl@sugar on Sat Oct 20 13:06:08 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(3858 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
XThe third release of "cookie"   20-Oct-1990
X-------------------------------------------
X
XCookie is a program to randomly retrieve cookies from a fortune cookie file.
XEach cookie can contain an arbitrary number of lines of text.  By generating
Xa small "hash" file containing the addresses of each cookie in the cookie
Xfile, cookie has a high-performance way to look up cookies, plus it is "fair,"
Xwhich is to say that short cookies are equally as likely to be selected as 
Xlong ones.  (The technique of simply randomly seeking into the cookie file
Xand looking backwards and forwards from there to find the beginning and
Xend of the cookie text would bias the cookie program toward large cookies.)
X
XThis is the same code as in my second release, but it's been almost two years
Xsince that release and I've gotten numerous requests for the program, plus
XI'm about to pump out a batch of new cookies, plus there wern't alt.sources
Xarchives (that I'm aware of) then, plus there's a manpage now, blah blah
Xblah.  Flames via email or alt.sources.d if you must...
X
XThe code is again released into the public domain without restriction.
XI ask that you retain my name in the source code if you redistribute this stuff,
Xand that you redistribute source along with binaries.
X
XNo warranties are expressed or implied -- this is free code.  We do not have
Xa contract.
X
XThe code is written for System V but the only area of incompatibility should be
Xthe rand() function.  Only minor hacking should be necessary to port to BSD,
Xfor example.  (If someone gets the urge, please make a version that works
Xwith #ifdefs for both, test the BSD version and forward it back to me, OK?)
X
XIt should be possible to make cookie work pretty painlessly on MS-DOS, Minix,
Xetc.  Tested updates for popular and not-overwhelmingly-twisted systems, with
X#ifdefs, are solicited.  I seem to recall that cookie works as-is on the Amiga.
X
XTo use, unshar this archive and do a 'make' to compile 'cookie' and 'cookhash'.
X
XThen collect a bunch of cookies.  Eric Townsend just posted quite a few to
Xalt.sources.  I will be posting a couple of new batches.  I hope to post
Xinformation soon to alt.sources.d as to where an archive site for all my
Xcookies (pushing 500 KB) can be found.
X
XCookies are separated by lines containing two percent-signs and nothing
Xelse, for example:
X
X"I just thought of something funny, your mother."
X-- Cheech Marin
X%%
X"He can shout, don't hear you."
X-- The Firesign Theatre
X%%
X
X...and so forth.
X
XThe include file "cookie.h" defines the location of the cookie file as being
X"/usr/local/lib/sayings".  If you want to put it elsewhere, change cookie.h
Xand rebuild 'cookie'.  
X
XCookie needs a hash file for the cookie file, by default called
X"/usr/local/lib/sayhash".  This is created by the 'cookhash' program.
XCookhash is simply a filter that reads a cookie file in as stdin and writes
Xa cookie hash file to stdout.  Thus, if you've moved the cookie file to
X/usr/local/lib, 'cd' there and do a "cookhash <sayings >sayhash" to create the 
Xhash file.
X
XAfter that, 'cookie' should produce a cookie.  Cookie can also be executed
Xwith two arguments, the name of a cookie file followed by the name of
Xits hash file, useful for creating aliases for alternate cookie files so
Xyou can get just Zippy the Pinhead quotes, for example.
X
XIf you find quotes in the file that are unattributed and you know the
Xattributions, please mail them to karl@sugar.hackercorp.com or
Xuunet!sugar!karl
X
XI also collect cookies.  If you see good ones, please forward them.  (If you
Xgot them from me, please don't!)
X
XA lot of people think it's fun to get a cookie every time they log in, so 
Xthey put "cookie" in their .profile or .login file.
X
XRegards,
XKarl (karl@sugar.hackercorp.com) @ The Hacker's Haven, Missouri City, Texas 
X(The name will be changing because hacker has lost its old meaning -- sigh)
END_OF_FILE
if test 3858 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'cookie.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cookie.1'\"
else
echo shar: Extracting \"'cookie.1'\" \(1033 characters\)
sed "s/^X//" >'cookie.1' <<'END_OF_FILE'
X.TH COOKIE 6
X.SH NAME
Xcookie \- show a fortune cookie
X.SH SYNOPSIS
X.B cookie
X[ cookie-file hash-file ]
X.sp
X.B /usr/local/lib/cookhash
X< sayings > hash-file
X.SH DESCRIPTION
X.I Cookie
Xshows the user a randomly chosen quote from a file containing
Xfortune cookies, sayings, jokes, aphorisms, quotes and so on.
X.P
XCookie can also be executed with two arguments, the name of a cookie file
Xfollowed by the name of its hash file.
X.P
XTo create the hash file, use
X.I /usr/local/lib/cookhash
Xwhich reads the sayings from standard input and writes the hash values to
Xstandard output. (The hash file contains the ASCII-formatted addresses of
Xthe cookies found in the cookie file.  It is used by 
X.I cookie
Xto look up cookies quickly and to help to insure that all cookies are
Xequally likely to be chosen.)
X.P
XSayings within the input file are separated by lines containing 
X.B %% .
X.SH SEE ALSO
Xfortune(6)
X.SH FILES
X/usr/local/lib/sayings, /usr/local/lib/sayhash
X.SH AUTHOR
XKarl Lehenbauer (karl@sugar.hackercorp.com),
XMissouri City, Texas, USA
X
END_OF_FILE
if test 1033 -ne `wc -c <'cookie.1'`; then
    echo shar: \"'cookie.1'\" unpacked with wrong size!
fi
# end of 'cookie.1'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(200 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# makefile for karl's PD fortune cookie program
X
Xall:	cookie cookhash
X
Xcookie:	cookie.h
X	cc -O -o cookie cookie.c
X
Xcookhash:
X	cc -O -o cookhash cookhash.c
X
Xinstall:
X	cp cookie cookhash /usr/local/bin
END_OF_FILE
if test 200 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'cookie.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cookie.h'\"
else
echo shar: Extracting \"'cookie.h'\" \(263 characters\)
sed "s/^X//" >'cookie.h' <<'END_OF_FILE'
X/* cookie.h - include file for karl's PD fortune cookie program 
X * by Karl Lehenbauer (karl@sugar.uu.net, uunet!sugar!karl)
X * cookie.h 1.1 1/12/89
X */
X
X#define COOKIEFILE "/usr/local/lib/sayings"
X#define HASHFILE "/usr/local/lib/sayhash"
X
X/* end of cookie.h */
END_OF_FILE
if test 263 -ne `wc -c <'cookie.h'`; then
    echo shar: \"'cookie.h'\" unpacked with wrong size!
fi
# end of 'cookie.h'
fi
if test -f 'cookie.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cookie.c'\"
else
echo shar: Extracting \"'cookie.c'\" \(2702 characters\)
sed "s/^X//" >'cookie.c' <<'END_OF_FILE'
X/* cookie - print out an entry from the sayings file
X * by Karl Lehenbauer (karl@sugar.uu.net, uunet!sugar!karl)
X *  cookie.c  1.1  1/12/89
X */
X
X#include <stdio.h>
X#include "cookie.h"
X
X#define ENTSIZE 7L
X#define METACHAR '%'
X#define YES 1
X#define NO 0
X
Xchar *sccs_id = "@(#) fortune cookie program 1.1 1/12/89 by K. Lehenbauer";
X
Xextern long lseek(), time();
Xextern int rand();
X
Xchar *cookiename = COOKIEFILE;
Xchar *hashname = HASHFILE;
X
X/* really_random - insure a good random return for a range, unlike an arbitrary
X * random() % n, thanks to Ken Arnold, Unix Review, October 1987
X * ...likely needs a little hacking to run under Berkely
X */
X#define RANDOM_RANGE ((1 << 15) - 1)
Xint really_random(my_range)
Xint my_range;
X{
X	int max_multiple, rnum;
X
X	max_multiple = RANDOM_RANGE / my_range;
X	max_multiple *= my_range;
X	while ((rnum = rand()) >= max_multiple)
X		continue;
X	return(rnum % my_range);
X}
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X	int nentries, oneiwant, c, sawmeta = 0;
X	FILE *hashf, *cookief;
X	long cookiepos;
X
X	/* if we got exactly three arguments, use the cookie and hash
X	 * files specified
X	 */
X	if (argc == 3)
X	{
X		cookiename = argv[1];
X		hashname = argv[2];
X	}
X	/* otherwise if argc isn't one (no arguments, specifying the
X	 * default cookie file), barf
X	 */
X	else if (argc != 1)
X	{
X		fputs("usage: cookie cookiefile hashfile\n",stderr);
X		exit(1);
X	}
X
X	/* open the cookie file for read */
X	if ((cookief = fopen(cookiename,"r")) == NULL)
X	{
X		perror(cookiename);
X		exit(2);
X	}
X
X	/* open the hash file for read */
X	if ((hashf = fopen(hashname,"r")) == NULL)
X	{
X		perror(hashname);
X		exit(2);
X	}
X
X	/* compute number of cookie addresses in the hash file by
X	 * dividing the file length by the size of a cookie address
X	 */
X	if (fseek(hashf,0L,2) != 0)
X	{
X		perror(hashname);
X		exit(3);
X	}
X	nentries = ftell(hashf) / 7L;
X
X	/* seed the random number generator with time in seconds plus
X	 * the program's process ID - it yields a pretty good seed
X	 * again, thanks to Ken Arnold
X	 */
X	srand(getpid() + time(NULL));
X
X	/* generate a not really random number */
X	oneiwant = really_random(nentries);
X
X	/* locate the one I want in the hash file and read the
X	 * address found there
X	 */
X	fseek(hashf,(long)oneiwant * ENTSIZE, 0);
X	fscanf(hashf,"%lx",&cookiepos);
X
X	/* seek cookie file to cookie starting at address read from hash */
X	fseek(cookief,cookiepos,0);
X
X	/* get characters from the cookie file and write them out
X	 * until finding the end-of-fortune sequence, '%%'
X	 */
X	while ((c = fgetc(cookief)) != EOF && sawmeta < 2)
X	{
X		if (c != METACHAR)
X		{
X			if (sawmeta)
X				putchar(METACHAR);
X			putchar(c);
X			sawmeta = 0;
X		}
X		else
X			sawmeta++;
X	}
X	exit(0);
X}
X
X/* end of cookie.c */
END_OF_FILE
if test 2702 -ne `wc -c <'cookie.c'`; then
    echo shar: \"'cookie.c'\" unpacked with wrong size!
fi
# end of 'cookie.c'
fi
if test -f 'cookhash.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cookhash.c'\"
else
echo shar: Extracting \"'cookhash.c'\" \(943 characters\)
sed "s/^X//" >'cookhash.c' <<'END_OF_FILE'
X/* cookhash - read a sayings file and generate an index file
X * by Karl Lehenbauer (karl@sugar.uu.net, uunet!sugar!karl)
X *  cookhash.c  1.1  1/12/89
X */
X
X#include <stdio.h>
X
X#define YES 1
X#define NO 0
X#define METACHAR '%'
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X	int c, sawmeta;
X	long charpos = 0;
X
X	if (argc != 1)
X	{
X		fprintf(stderr,"usage: cookhash <cookiefile >hashfile\n");
X		exit(1);
X	}
X
X	/* write out the "address" of the first cookie */
X	puts("000000");
X
X	/* read the cookie until the end,
X	 *   whenever the end-of-cookie ("%%") sequence is found,
X	 *   the "address" (file position) of the first byte following
X	 *   it (start of next cookie) is written to the index (hash) file
X	 */
X	while ((c = getchar()) != EOF)
X	{
X		if (c == METACHAR)
X		{
X			if (sawmeta)
X			{
X				printf("%06lx\n",charpos+2);
X				sawmeta = NO;
X			}
X			else
X				sawmeta = YES;
X		}
X		else
X			sawmeta = NO;
X		charpos++;
X	}
X	exit(0);
X}
X
X/* end of cookhash.c */
END_OF_FILE
if test 943 -ne `wc -c <'cookhash.c'`; then
    echo shar: \"'cookhash.c'\" unpacked with wrong size!
fi
# end of 'cookhash.c'
fi
echo shar: End of shell archive.
exit 0
-- 
-- uunet!sugar!karl
-- Usenet access: (713) 438-5018