[comp.sources.misc] Ispell Version 2.0 Beta Part 04/04

allbery@ncoast.UUCP (05/31/87)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	munchlist.X
#	term.c
#	tree.c
#	version.h
#	xgets.c
# This archive created: Sat May 30 17:13:43 1987
export PATH; PATH=/bin:$PATH
echo shar: extracting "'munchlist.X'" '(5932 characters)'
if test -f 'munchlist.X'
then
	echo shar: will not over-write existing file "'munchlist.X'"
else
sed 's/^X //' << \SHAR_EOF > 'munchlist.X'
X : Use /bin/sh
X #
X #	Given a list of words for ispell, generate a reduced list
X #	in which all possible suffixes have been collapsed.  The reduced
X #	list will match the same list as the original.
X #
X #	Usage:
X #
X #	munchlist [ -d hashfile ] [ -e ] [ -w chars ] [ file ] ...
X #
X #	Options:
X #
X #	-d hashfile
X #		Remove any words that are covered by 'hashfile'.  The
X #		default is the default ispell dictionary.  The words
X #		will be removed only if all suffixes are covered by
X #		the hash file.  A hashfile of /dev/null should be
X #		specified when the main dictionary is being munched.
X #	-e	Economical algorithm.  This will use much less temporary
X #		disk space, at the expense of time.  Useful with large files
X #		(such as complete dictionaries).
X #	-w	Passed on to ispell (specify chars that are part of a word)
X #
X #	The given input files are merged, then processed by 'ispell -c'
X #	to generate possible suffix lists;  these are then combined
X #	and reduced.  The final result is written to standard output.
X #
X #	For portability to older systems, I have avoided getopt.
X #
X #		Geoff Kuenning
X #		2/28/87
X #
X LIBDIR=!!LIBDIR!!
X COMBINE=${LIBDIR}/icombine
X EXPAND1=${LIBDIR}/isexp1.sed
X EXPAND2=${LIBDIR}/isexp2.sed
X EXPAND3=${LIBDIR}/isexp3.sed
X EXPAND4=${LIBDIR}/isexp4.sed
X TDIR=${TMPDIR:-/usr/tmp}
X TMP=${TDIR}/munch$$
X 
X cheap=no
X dictopt=
X wchars=
X while [ $# != 0 ]
X do
X     case "$1" in
X 	-d)
X 	    case "$2" in
X 		/dev/null)
X 		    dictopt=NONE
X 		    ;;
X 		*)
X 		    dictopt="-d $2"
X 		    ;;
X 	    esac
X 	    shift
X 	    ;;
X 	-e)
X 	    cheap=yes
X 	    ;;
X 	-w)
X 	    wchars="-w $2"
X 	    shift
X 	    ;;
X 	*)
X 	    break
X     esac
X     shift
X done
X trap "/bin/rm -f ${TMP}*; exit 1" 1 2 15
X #
X # Collect all the input and expand all the suffix options (four sed's),
X # and preserve (sorted) for later joining in ${TMP}a.
X #
X if [ $# -eq 0 ]
X then
X     sed -f $EXPAND1 | sed -f $EXPAND2 \
X       | sed -f $EXPAND3 | sed -f $EXPAND4 | sort -u > ${TMP}a
X else
X     sed -f $EXPAND1 "$@" | sed -f $EXPAND2 \
X       | sed -f $EXPAND3 | sed -f $EXPAND4 | sort -u > ${TMP}a
X fi
X #
X # Unless an explicitly null dictionary was specified, remove all
X # expanded words that are covered by the dictionary.  This produces
X # the final list of expanded words that this dictionary must cover.
X # Leave the list in ${TMP}b.
X #
X if [ "X$dictopt" = "XNONE" ]
X then
X     ln ${TMP}a ${TMP}b
X else
X     ispell -l $dictopt -p /dev/null < ${TMP}a > ${TMP}b
X fi
X #
X # Munch the input to generate roots and suffixes (ispell -c).  We are
X # only interested in words that have at least one suffix (egrep /);  the
X # next step will pick up the rest.  Some of the roots are illegal.  We
X # use join to restrict the output to those root words that are found
X # in the original dictionary.  In cheap mode, we re-sort this for
X # icombine's benefit, and then use icombine to scrunch them together.
X #
X # Note:  one disadvantage of this pipeline is that for a large file,
X # the join and icombine may be sitting around for a long time while ispell
X # and sorts run.  You can get rid of this by splitting the pipe, at
X # the expense of more temp file space.
X #
X if [ $cheap = yes ]
X then
X     ispell $wchars -c -d /dev/null -p /dev/null < ${TMP}b \
X       | egrep / | sort -u -t/ +0 -1 +1 \
X       | join -t/ - ${TMP}a \
X       | sort -u -t/ +0f -1 +0 -1 +1 | $COMBINE > ${TMP}c
X else
X     ispell $wchars -c -d /dev/null -p /dev/null < ${TMP}b \
X       | egrep / | sort -u -t/ +0 -1 +1 \
X       | join -t/ - ${TMP}a > ${TMP}c
X fi
X #
X # There is now one slight problem:  the suffix flags X, J, and Z
X # are simply the addition of an "S" to the suffixes N, G, and R,
X # respectively.  This produces redundant entries in the output file;
X # for example, ABBREVIATE/N/X and ABBREVIATION/S.  We must get rid
X # of the unnecessary duplicates.  The candidates are those words that
X # have only an "S" flag (egrep).  We strip off the "S" (sed), and
X # generate a list of roots that might have made these words (ispell -c).
X # Of these roots, we select those that have the N, G, or R flags,
X # replacing each with the plural equivalent X, J, or Z (sed -n).
X # Using join once again, we select those that have legal roots
X # and put them in ${TMP}d.
X #
X if [ $cheap = yes ]
X then
X     egrep '^[^/]*/S$' ${TMP}c | sed 's@/S$@@' \
X       | ispell $wchars -c -d /dev/null -p /dev/null \
X       | sed -n -e '/\/N/s/N$/X/p' -e '/\/G/s/G$/J/p' -e '/\/R/s/R$/Z/p' \
X       | sort -u -t/ +0 -1 +1 \
X       | join -t/ - ${TMP}a \
X       | sort -u -t/ +0f -1 +0 -1 +1 \
X       | $COMBINE > ${TMP}d
X else
X     egrep '^[^/]*/S$' ${TMP}c | sed 's@/S$@@' \
X       | ispell $wchars -c -d /dev/null -p /dev/null \
X       | sed -n -e '/\/N/s/N$/X/p' -e '/\/G/s/G$/J/p' -e '/\/R/s/R$/Z/p' \
X       | sort -u -t/ +0 -1 +1 \
X       | join -t/ - ${TMP}a > ${TMP}d
X fi
X /bin/rm -f ${TMP}a
X #
X # Now we have to eliminate the stuff covered by ${TMP}d from ${TMP}c.
X # First, we re-expand the suffixes we just made (four sed's), and let
X # ispell re-create the /S version (ispell -c).  We select the /S versions
X # only (egrep), sort them (sort) for comm, and use comm to delete these
X # from ${TMP}c.  The output of comm (i.e., the trimmed version of
X # ${TMP}c) is combined with our special-suffixes file ${TMP}d (sort again)
X # and reduced in size (icombine) to produce a final list of all words
X # that have at least one suffix.
X #
X sed -f $EXPAND1 ${TMP}d | sed -f $EXPAND2 | sed -f $EXPAND3 | sed -f $EXPAND4 \
X   | ispell $wchars -c -d /dev/null -p /dev/null \
X   | egrep '\/S$' | sort -u -t/ +0 -1 +1 | comm -13 - ${TMP}c \
X   | sort -u -t/ +0f -1 +0 -1 +1 - ${TMP}d \
X   | $COMBINE > ${TMP}e
X /bin/rm -f ${TMP}[cd]
X #
X # Now a slick trick.  Use ispell to select those (root) words from the original
X # list (${TMP}b) that are not covered by the suffix list (${TMP}e).  Then we
X # merge these with the suffix list, sort it, and use icombine to strip out
X # unnecessary capitalizations and produce the final output.
X #
X ispell $wchars -d /dev/null -p ${TMP}e -l < ${TMP}b \
X   | sort -t/ +0f -1 +0 -1 +1 - ${TMP}e \
X   | $COMBINE
X /bin/rm -f ${TMP}*
SHAR_EOF
chmod +x 'munchlist.X'
fi # end of overwriting check
echo shar: extracting "'term.c'" '(4678 characters)'
if test -f 'term.c'
then
	echo shar: will not over-write existing file "'term.c'"
else
sed 's/^X //' << \SHAR_EOF > 'term.c'
X /* -*- Mode:Text -*- */
X 
X /*
X  * term.c - deal with termcap, and unix terminal mode settings
X  *
X  * Pace Willisson, 1983
X  */
X 
X #include <stdio.h>
X #ifdef USG
X #include <termio.h>
X #else
X #include <sgtty.h>
X #endif
X #include <signal.h>
X #include "config.h"
X #include "ispell.h"
X 
X int putch();
X 
X erase ()
X {
X 	if (cl)
X 		tputs(cl, li, putch);
X 	else {
X 		if (ho)
X 			tputs(ho, 100, putch);
X 		else if (cm)
X 			tputs(tgoto(cm, 0, 0), 100, putch);
X 		tputs(cd, li, putch);
X 	}
X }
X 
X move (row, col)
X {
X 	tputs (tgoto (cm, col, row), 100, putch);
X }
X 
X inverse ()
X {
X 	tputs (so, 10, putch);
X }
X 
X normal ()
X {
X 	tputs (se, 10, putch);
X }
X 
X backup ()
X {
X 	if (BC)
X 		tputs (BC, 1, putch);
X 	else
X 		putchar ('\b');
X }
X 
X putch (c)
X {
X 	putchar (c);
X }
X 
X #ifdef USG
X struct termio sbuf, osbuf;
X #else
X struct sgttyb sbuf, osbuf;
X struct ltchars ltc, oltc;
X #endif
X static termchanged = 0;
X static int (*oldint) ();
X static int (*oldterm) ();
X 
X terminit ()
X {
X 	int done();
X 
X #ifdef USG
X 	if (!isatty(0)) {
X 		fprintf (stderr, "Can't deal with non interactive use yet.\n");
X 		exit (1);
X 	}
X 	ioctl (0, TCGETA, &osbuf);
X 	termchanged = 1;
X 
X 	sbuf = osbuf;
X 	sbuf.c_lflag &= ~(ECHO | ECHOK | ECHONL | ICANON);
X 	sbuf.c_oflag &= ~(OPOST);
X 	sbuf.c_iflag &= ~(INLCR | IGNCR | ICRNL);
X 	sbuf.c_cc[VMIN] = 1;
X 	sbuf.c_cc[VTIME] = 1;
X 	ioctl (0, TCSETAW, &sbuf);
X 
X 	erasechar = osbuf.c_cc[VERASE];
X 	killchar = osbuf.c_cc[VKILL];
X 
X #else
X 	int tpgrp;
X 	int onstop();
X 	extern short ospeed;
X 
X retry:
X #ifdef SIGTSTP
X 	sigsetmask(1<<(SIGTSTP-1) | 1<<(SIGTTIN-1) | 1<<(SIGTTOU-1));
X #endif
X #ifdef TIOCGPGRP
X 	if (ioctl(0, TIOCGPGRP, &tpgrp) != 0) {
X 		fprintf (stderr, "Can't deal with non interactive use yet.\n");
X 		exit (1);
X 	}
X #endif
X #ifdef SIGTSTP
X 	if (tpgrp != getpgrp(0)) { /* not in foreground */
X 		sigsetmask(1<<(SIGTSTP-1) | 1<<(SIGTTIN-1));
X 		signal(SIGTTOU, SIG_DFL);
X 		kill(0, SIGTTOU);
X 		/* job stops here waiting for SIGCONT */
X 		goto retry;
X 	}
X #endif
X 
X 	ioctl (0, TIOCGETP, &osbuf);
X 	ioctl (0, TIOCGLTC, &oltc);
X 	termchanged = 1;
X 
X 	sbuf = osbuf;
X 	sbuf.sg_flags &= ~ECHO;
X 	sbuf.sg_flags |= TERM_MODE;
X 	ioctl (0, TIOCSETP, &sbuf);
X 
X 	erasechar = sbuf.sg_erase;
X 	killchar = sbuf.sg_kill;
X 	ospeed = sbuf.sg_ospeed;
X 
X 	ltc = oltc;
X 	ltc.t_suspc = -1;
X 	ioctl (0, TIOCSLTC, &ltc);
X 
X 	if ((oldint = signal (SIGINT, SIG_IGN)) != SIG_IGN)
X 		signal (SIGINT, done);
X 	if ((oldterm = signal (SIGTERM, SIG_IGN)) != SIG_IGN)
X 		signal (SIGTERM, done);
X 
X #ifdef SIGTTIN
X 	sigsetmask(0);
X 	if (signal (SIGTTIN, SIG_IGN) != SIG_IGN)
X 		signal(SIGTTIN, onstop);
X 	if (signal (SIGTTOU, SIG_IGN) != SIG_IGN)
X 		signal(SIGTTOU, onstop);
X 	if (signal (SIGTSTP, SIG_IGN) != SIG_IGN)
X 		signal(SIGTSTP, onstop);
X #endif
X #endif
X 
X 	tgetent(termcap, getenv("TERM"));
X 	termptr = termstr;
X 	bs = tgetflag("bs");
X 	BC = tgetstr("bc", &termptr);
X 	UP = tgetstr("up", &termptr);
X 	cd = tgetstr("cd", &termptr);
X 	ce = tgetstr("ce", &termptr);	
X 	cl = tgetstr("cl", &termptr);
X 	cm = tgetstr("cm", &termptr);
X 	dc = tgetstr("dc", &termptr);
X 	dl = tgetstr("dl", &termptr);
X 	dm = tgetstr("dm", &termptr);
X 	ed = tgetstr("ed", &termptr);
X 	ei = tgetstr("ei", &termptr);
X 	ho = tgetstr("ho", &termptr);
X 	ic = tgetstr("ic", &termptr);
X 	il = tgetstr("al", &termptr);
X 	im = tgetstr("im", &termptr);
X 	ip = tgetstr("ip", &termptr);
X 	nd = tgetstr("nd", &termptr);
X 	vb = tgetstr("vb", &termptr);
X 	so = tgetstr("so", &termptr);	/* inverse video on */
X 	se = tgetstr("se", &termptr);	/* inverse video off */
X 	co = tgetnum("co");
X 	li = tgetnum("li");	
X 
X }
X 
X done ()
X {
X 	unlink (tempfile);
X 	if (termchanged)
X #ifdef USG
X 		ioctl (0, TCSETAW, &osbuf);
X #else
X 		ioctl (0, TIOCSETP, &osbuf);
X 		ioctl (0, TIOCSLTC, &oltc);
X #endif
X 	exit (0);
X }
X 
X #ifndef USG
X onstop(signo)
X int signo;
X {
X 	ioctl (0, TIOCSETP, &osbuf);
X 	ioctl (0, TIOCSLTC, &oltc);
X 	signal(signo, SIG_DFL);
X 	sigsetmask(sigblock(0) & ~(1 << (signo-1)));
X 	kill(0, signo);
X 	/* stop here until continued */
X 	signal(signo, onstop);
X 	ioctl (0, TIOCSETP, &sbuf);
X 	ioctl (0, TIOCSLTC, &ltc);
X }
X 
X stop ()
X {
X #ifdef SIGTSTP
X 	onstop (SIGTSTP);
X #endif
X 	;
X }
X #endif
X 
X shellescape (buf)
X char *buf;
X {
X 
X #ifdef USG
X 	ioctl (0, TCSETAW, &osbuf);
X #else
X 	ioctl (0, TIOCSETP, &osbuf);
X 	ioctl (0, TIOCSLTC, &oltc);
X #endif
X 	signal (SIGINT, oldint);
X 	signal (SIGTERM, oldterm);
X #ifdef SIGTTIN
X 	signal(SIGTTIN, SIG_DFL);
X 	signal(SIGTTOU, SIG_DFL);
X 	signal(SIGTSTP, SIG_DFL);
X #endif
X 
X 	system (buf);
X 
X 	if (signal (SIGINT, SIG_IGN) != SIG_IGN)
X 		signal (SIGINT, done);
X 	if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
X 		signal (SIGTERM, done);
X 
X #ifdef SIGTTIN
X 	signal(SIGTTIN, oldttin);
X 	signal(SIGTTOU, oldttou);
X 	signal(SIGTSTP, oldtstp);
X #endif
X 
X #ifdef USG
X 	ioctl (0, TCSETAW, &sbuf);
X #else
X 	ioctl (0, TIOCSETP, &sbuf);
X 	ioctl (0, TIOCSLTC, &ltc);
X #endif
X 	printf ("\n-- Type space to continue --");
X 	fflush (stdout);
X 	getchar ();
X }
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'tree.c'" '(16179 characters)'
if test -f 'tree.c'
then
	echo shar: will not over-write existing file "'tree.c'"
else
sed 's/^X //' << \SHAR_EOF > 'tree.c'
X /* -*- Mode:Text -*- */
X 
X /*
X  * tree.c - a hash style dictionary for user's personal words
X  *
X  * Pace Willisson, 1983
X  * Hash support added by Geoff Kuenning, 1987
X  */
X 
X #include <stdio.h>
X #include <ctype.h>
X #include <sys/param.h>
X #include "config.h"
X #include "ispell.h"
X 
X char *getenv();
X struct dent *lookup();
X char *upcase();
X 
X static int cantexpand = 0;	/* NZ if an expansion fails */
X static struct dent *htab = NULL; /* Hash table for our stuff */
X static int hsize = 0;		/* Space available in hash table */
X static int hcount = 0;		/* Number of items in hash table */
X 
X /*
X  * Hash table sizes.  Prime is probably a good idea, though in truth I
X  * whipped the algorithm up on the spot rather than looking it up, so
X  * who knows what's really best?  If we overflow the table, we just
X  * use a double-and-add-1 algorithm.
X  *
X  * The strange pattern in the table is because this table is sometimes
X  * used with huge dictionaries, and we want to get the table bigger fast.
X  * 23003 just happens to be such that the original dict.191 will fill
X  * the table to just under 70%.  31469 is similarly selected for dict.191
X  * combined with /usr/dict/words.  The other numbers are on 10000-word
X  * intervals starting at 30000.  (The table is still valid if MAXPCT
X  * is changed, but the dictionary sizes will no longer fall on neat
X  * boundaries).
X  */
X static int goodsizes[] = {
X 	53, 223, 907,
X #if ((BIG_DICT * 100) / MAXPCT) <= 23003
X 	23003,				/* ~16000 words */
X #endif
X #if ((BIG_DICT * 100) / MAXPCT) <= 31469
X 	31469,				/* ~22000 words */
X #endif
X #if ((BIG_DICT * 100) / MAXPCT) <= 42859
X 	42859,				/* ~30000 words */
X #endif
X #if ((BIG_DICT * 100) / MAXPCT) <= 57143
X 	57143,				/* ~40000 words */
X #endif
X 	71429				/* ~50000 words */
X };
X 
X struct dent *treeinsert();
X struct dent *tinsert();
X struct dent *treelookup();
X char *upcase ();
X char *lowcase ();
X 
X static char personaldict[MAXPATHLEN];
X static FILE *dictf;
X static newwords = 0;
X 
X extern char *index ();
X extern char *calloc ();
X extern char *malloc ();
X extern char *realloc ();
X 
X extern struct dent *hashtbl;
X extern int hashsize;
X 
X treeinit (p)
X char *p;
X {
X 	register char *h;
X 	char *orig;
X 	char buf[BUFSIZ];
X 	register struct dent *dp;
X 
X 	/*
X 	** if p exists and begins with '/' we don't really need HOME,
X 	** but it's not very likely that HOME isn't set anyway.
X 	*/
X 	orig = p;
X 	if (p == NULL)
X 		p = getenv (PDICTVAR);
X 	if ((h = getenv ("HOME")) == NULL)
X 		return;
X 
X 	if (p == NULL)
X 		sprintf(personaldict,"%s/%s",h,DEFPDICT);
X 	else {
X 		if (*p == '/')
X 			strcpy(personaldict,p);
X 		else {
X 			/*
X 			** The user gave us a relative pathname.  How we
X 			** interpret it depends on how it was given:
X 			**
X 			** -p switch:  as-is first, then $HOME/name
X 			** PDICTVAR:   $HOME/name first, then as-is
X 			**/
X 			if (orig == NULL)
X 				sprintf (personaldict, "%s/%s", h, p);
X 			else			/* -p switch */
X 				strcpy (personaldict, p);
X 		}
X 	}
X 
X 	if ((dictf = fopen (personaldict, "r")) == NULL) {
X 		/* The file doesn't exist. */
X 		if (p != NULL) {
X 			/* If pathname is relative, try another place */
X 			if (*p != '/') {
X 				if (orig == NULL)
X 					strcpy (personaldict, p);
X 				else			/* -p switch */
X 					sprintf (personaldict, "%s/%s", h, p);
X 				dictf = fopen (personaldict, "r");
X 			}
X 			if (dictf == NULL) {
X 				(void) fprintf (stderr, "Couldn't open ");
X 				perror (p);
X 				if (*p != '/') {
X 					/*
X 					** Restore the preferred default, so
X 					** that output will go th the right
X 					** place.
X 					*/
X 					if (orig == NULL)
X 						sprintf (personaldict,
X 						  "%s/%s", h, p);
X 					else			/* -p switch */
X 						strcpy (personaldict, p);
X 				}
X 			}
X 		}
X 		/* If the name wasn't specified explicitly, we don't object */
X 		return;
X 	}
X 
X 	while (fgets (buf, sizeof buf, dictf) != NULL) {
X 		int len = strlen (buf) - 1;
X 
X 		if (buf [ len ] == '\n')
X 			buf [ len-- ] = '\0';
X 		if ((h = index (buf, '/')) != NULL)
X 			*h++ = '\0';
X 		dp = treeinsert (buf, 1);
X 		if (h != NULL) {
X 			while (*h != '\0'  &&  *h != '\n') {
X 				switch (*h++) {
X 				case 'D':
X 				case 'd':
X 					dp->d_flag = 1;
X 					break;
X 				case 'G':
X 				case 'g':
X 					dp->g_flag = 1;
X 					break;
X 				case 'H':
X 				case 'h':
X 					dp->h_flag = 1;
X 					break;
X 				case 'J':
X 				case 'j':
X 					dp->j_flag = 1;
X 					break;
X 				case 'M':
X 				case 'm':
X 					dp->m_flag = 1;
X 					break;
X 				case 'N':
X 				case 'n':
X 					dp->n_flag = 1;
X 					break;
X 				case 'P':
X 				case 'p':
X 					dp->p_flag = 1;
X 					break;
X 				case 'R':
X 				case 'r':
X 					dp->r_flag = 1;
X 					break;
X 				case 'S':
X 				case 's':
X 					dp->s_flag = 1;
X 					break;
X 				case 'T':
X 				case 't':
X 					dp->t_flag = 1;
X 					break;
X 				case 'V':
X 				case 'v':
X 					dp->v_flag = 1;
X 					break;
X 				case 'X':
X 				case 'x':
X 					dp->x_flag = 1;
X 					break;
X 				case 'Y':
X 				case 'y':
X 					dp->y_flag = 1;
X 					break;
X 				case 'Z':
X 				case 'z':
X 					dp->z_flag = 1;
X 					break;
X 				default:
X 					fprintf (stderr,
X 					  "Illegal flag in personal dictionary - %c (word %s)\n",
X 					  h[-1], buf);
X 					break;
X 				}
X 				/* Accept old-format dicts with extra slashes */
X 				if (*h == '/')
X 					h++;
X 			}
X 		}
X 	}
X 
X 	fclose (dictf);
X 
X 	newwords = 0;
X 
X 	if (!lflag && !aflag && access (personaldict, 2) < 0)
X 		fprintf (stderr,
X 		    "Warning: Cannot update personal dictionary (%s)\r\n",
X 		    personaldict);
X }
X 
X struct dent *
X treeinsert (word, keep)
X char *word;
X {
X 	register int i;
X 	register struct dent *dp;
X 	struct dent *olddp;
X 	struct dent *oldhtab;
X 	int oldhsize;
X 	char nword[BUFSIZ];
X #ifdef CAPITALIZE
X 	register char *cp;
X 	char *saveword;
X 	int capspace;
X #endif
X 
X 	strcpy (nword, word);
X 	upcase (nword);
X 	if ((dp = lookup (nword, strlen (nword), 0)) != NULL)
X 		dp->keep = keep;
X 	/*
X 	 * Expand hash table when it is MAXPCT % full.
X 	 */
X 	else if (!cantexpand  &&  (hcount * 100) / MAXPCT >= hsize) {
X 		oldhsize = hsize;
X 		oldhtab = htab;
X 		for (i = 0;  i < sizeof goodsizes / sizeof (goodsizes[0]);  i++)
X 			if (goodsizes[i] > hsize)
X 				break;
X 		if (i >= sizeof goodsizes / sizeof goodsizes[0])
X 			hsize += hsize + 1;
X 		else
X 			hsize = goodsizes[i];
X 		htab = (struct dent *) calloc (hsize, sizeof (struct dent));
X 		if (htab == NULL) {
X 			(void) fprintf (stderr,
X 			    "Ran out of space for personal dictionary\n");
X 			/*
X 			 * Try to continue anyway, since our overflow
X 			 * algorithm can handle an overfull (100%+) table,
X 			 * and the malloc very likely failed because we
X 			 * already have such a huge table, so small mallocs
X 			 * for overflow entries will still work.
X 			 */
X 			if (oldhtab == NULL)
X 				exit (1);	/* No old table, can't go on */
X 			(void) fprintf (stderr,
X 			    "Continuing anyway (with reduced performance).\n");
X 			cantexpand = 1;		/* Suppress further messages */
X 			hsize = oldhsize;	/* Put this back how the were */
X 			htab = oldhtab;		/* ... */
X 			newwords = 1;		/* And pretend it worked */
X 			return tinsert (nword, (struct dent *) NULL, keep);
X 		}
X 		/*
X 		 * Re-insert old entries into new table
X 		 */
X 		for (i = 0;  i < oldhsize;  i++) {
X 			dp = &oldhtab[i];
X 			if (oldhtab[i].used) {
X 				tinsert ((char *) NULL, dp, 0);
X 				dp = dp->next;
X 				while (dp != NULL) {
X 					tinsert ((char *) NULL, dp, 0);
X 					olddp = dp;
X 					dp = dp->next;
X 					free ((char *) olddp);
X 				}
X 			}
X 		}
X 		if (oldhtab != NULL)
X 			free ((char *) oldhtab);
X 		dp = NULL;		/* This will force the insert below */
X 	}
X 	newwords |= keep;
X 	if (dp == NULL)
X 		dp = tinsert (nword, (struct dent *) NULL, keep);
X #ifdef CAPITALIZE
X 	if (dp == NULL)
X 		return NULL;
X 	/*
X 	** Figure out the capitalization rules from the
X 	** capitalization of the sample entry.  If the sample is
X 	** all caps, we don't change the existing flags, since
X 	** all-caps gives us no information.  Tinsert initializes
X 	** new entries with "allcaps" set, so if the word is truly
X 	** required to appear in capitals, the correct result
X 	** will be achieved.
X 	*/
X 	for (cp = word;  *cp;  cp++) {
X 		if (mylower (*cp))
X 			break;
X 	}
X 	if (*cp) {
X 		/*
X 		** Sample entry has at least some lowercase.  See if
X 		** the case is mixed.
X 		*/
X 		for (cp = word;  *cp;  cp++) {
X 			if (myupper (*cp))
X 				break;
X 		}
X 		if (*cp == '\0'  &&  !dp->followcase) {
X 			/*
X 			** Sample entry is all lowercase, and word is not
X 			** followcase.  Clear all of the capitalization flags.
X 			*/
X 			dp->allcaps = 0;
X 			dp->capitalize = 0;
X 			if (keep) {
X 				dp->k_allcaps = 0;
X 				dp->k_capitalize = 0;
X 				dp->k_followcase = 0;
X 			}
X 		}
X 		else {
X 			/*
X 			** The sample entry is mixed case (or all-lower and the
X 			** entry is already followcase).  If it's simply
X 			** capitalized, set the capitalize flag and that's that.
X 			*/
X 			for (cp = word + 1;  *cp  &&  !myupper (*cp);  )
X 				cp++;
X 			if (*cp == 0  &&  myupper (*word)) {
X 				dp->allcaps = 0;
X 				dp->capitalize = 1;
X 				if (keep) {
X 					dp->k_allcaps = 0;
X 					dp->k_capitalize = 1;
X 				}
X 			}
X 			else {
X 				/*
X 				** The sample entry is followcase.  Make the
X 				** dictionary entry followcase if necessary.
X 				*/
X 				if (!dp->followcase) {
X 					dp->followcase = 1;
X 					if (keep)
X 						dp->k_followcase = 1;
X 					capspace = 2 * (strlen (dp->word) + 2);
X 					if (dp->word >= hashstrings
X 					  &&  dp->word <=
X 					    hashstrings
X 					     + hashheader.stringsize) {
X 						cp = dp->word;
X 						dp->word = malloc (capspace);
X 						if (dp->word)
X 							strcpy (dp->word, cp);
X 					}
X 					else
X 						dp->word = realloc (dp->word,
X 								    capspace);
X 					if (dp->word == NULL) {
X 						fprintf (stderr,
X 						  "Ran out of space for personal dictionary\n");
X 						exit (1);
X 					}
X 					cp = dp->word + strlen (dp->word) + 1;
X 					if (dp->capitalize  ||  dp->allcaps)
X 						*cp++ = 0;
X 					else {
X 						*cp++ = 1;
X 						strcpy (cp + 1, dp->word);
X 						lowcase (cp + 1);
X 					}
X 					*cp = dp->keep ? '+' : '-';
X 				}
X 				dp->allcaps = 0;
X 				if (keep)
X 					dp->k_allcaps = 0;
X 				cp = dp->word + strlen (dp->word) + 1;
X 				/* Add a new capitalization */
X 				(*cp)++;
X 				capspace = (cp - dp->word + 1)
X 					    * ((*cp & 0xFF) + 1);
X 				if (dp->word >= hashstrings
X 				  &&  dp->word <=
X 				    hashstrings + hashheader.stringsize) {
X 					saveword = dp->word;
X 					dp->word = malloc (capspace);
X 					if (dp->word) {
X 						cp = dp->word;
X 						while (--capspace >= 0)
X 							*cp++ = *saveword++;
X 					}
X 				}
X 				else
X 					dp->word = realloc (dp->word, capspace);
X 				if (dp->word == NULL) {
X 					fprintf (stderr,
X 					  "Ran out of space for personal dictionary\n");
X 					exit (1);
X 				}
X 				cp = dp->word + strlen (dp->word) + 1;
X 				cp +=
X 				  ((*cp & 0xFF) - 1) * (cp - dp->word + 1) + 1;
X 				*cp++ = keep ? '+' : '-';
X 				strcpy (cp, word);
X 			}
X 		}
X 	}
X #endif
X 	return dp;
X }
X 
X static
X struct dent *
X tinsert (word, proto, keep)
X char *word;			/* One of word/proto must be null */
X struct dent *proto;
X {
X 	register int hcode;
X 	register struct dent *hp; /* Next trial entry in hash table */
X 	register struct dent *php;	/* Previous value of hp, for chaining */
X 	register char *cp;
X 
X 	if (word == NULL)
X 		word = proto->word;
X 	hcode = hash (word, strlen (word), hsize);
X 	php = NULL;
X 	hp = &htab[hcode];
X 	if (hp->used) {
X 		while (hp != NULL) {
X 			if (strcmp (word, hp->word) == 0) {
X 				if (keep)
X 					hp->keep = 1;
X 				return hp;
X 			}
X 			php = hp;
X 			hp = hp->next;
X 		}
X 		hp = (struct dent *) calloc (1, sizeof (struct dent));
X 		if (hp == NULL) {
X 			(void) fprintf (stderr,
X 			    "Ran out of space for personal dictionary\n");
X 			exit (1);
X 		}
X 	}
X 	if (proto != NULL) {
X 		*hp = *proto;
X 		if (php != NULL)
X 			php->next = hp;
X 		hp->next = NULL;
X 		return &htab[hcode];
X 	} else {
X 		if (php != NULL)
X 			php->next = hp;
X 		hp->word = (char *) malloc (strlen (word) + 1);
X 		if (hp->word == NULL) {
X 			(void) fprintf (stderr,
X 			    "Ran out of space for personal dictionary\n");
X 			exit (1);
X 		}
X 		strcpy (hp->word, word);
X 		hp->used = 1;
X 		hp->next = NULL;
X 		hp->d_flag = 0;
X 		hp->g_flag = 0;
X 		hp->h_flag = 0;
X 		hp->j_flag = 0;
X 		hp->m_flag = 0;
X 		hp->n_flag = 0;
X 		hp->p_flag = 0;
X 		hp->r_flag = 0;
X 		hp->s_flag = 0;
X 		hp->t_flag = 0;
X 		hp->v_flag = 0;
X 		hp->x_flag = 0;
X 		hp->y_flag = 0;
X 		hp->z_flag = 0;
X #ifdef CAPITALIZE
X 		hp->allcaps = 1;		/* Assume word is all-caps */
X 		hp->k_allcaps = 1;
X 		hp->capitalize = 0;
X 		hp->k_capitalize = 0;
X 		hp->followcase = 0;
X 		hp->k_followcase = 0;
X #endif
X 		hp->keep = keep;
X 		hcount++;
X 		return (hp);
X 	}
X }
X 
X struct dent *
X treelookup (word)
X char *word;
X {
X 	register int hcode;
X 	register struct dent *hp;
X 	char nword[BUFSIZ];
X 
X 	if (hsize <= 0)
X 		return NULL;
X 	strcpy (nword, word);
X 	hcode = hash (nword, strlen (nword), hsize);
X 	hp = &htab[hcode];
X 	while (hp != NULL  &&  hp->used) {
X 		if (strcmp (nword, hp->word) == 0)
X 			break;
X 		hp = hp->next;
X 	}
X 	if (hp != NULL  &&  hp->used)
X 		return hp;
X 	else
X 		return NULL;
X }
X 
X treeoutput ()
X {
X 	register struct dent *cent;	/* Current entry */
X 	register struct dent *lent;	/* Linked entry */
X #ifdef SORTPERSONAL
X 	register struct dent *lowent;	/* Alphabetically lowest entry */
X 	struct dent *firstent;		/* First entry to be kept */
X #endif
X 	if (newwords == 0)
X 		return;
X 
X 	if ((dictf = fopen (personaldict, "w")) == NULL) {
X 		fprintf (stderr, "Can't create %s\r\n", personaldict);
X 		return;
X 	}
X 
X #ifdef SORTPERSONAL
X 	if (hcount >= SORTPERSONAL) {
X #endif
X 		for (cent = htab;  cent - htab < hsize;  cent++) {
X 			for (lent = cent;  lent != NULL;  lent = lent->next) {
X 				if (lent->used  &&  lent->keep)
X 					toutent (lent);
X 			}
X 		}
X 		for (cent = hashtbl, lent = hashtbl + hashsize;
X 		    cent < lent;
X 		    cent++) {
X 			if (cent->used  &&  cent->keep)
X 				toutent (cent);
X 		}
X #ifdef SORTPERSONAL
X 		return;
X 	}
X 	/*
X 	** Produce dictionary in sorted order.  We use a selection sort,
X 	** which is moderately inefficient, but easy to do in our hash table.
X 	** We start by linking all "keep" entries together to save time.
X 	*/
X 	for (lowent = NULL, cent = hashtbl, lent = hashtbl + hashsize;
X 	    cent < lent;
X 	    cent++) {
X 		if (cent->keep  &&  cent->used) {
X 			cent->next = lowent;
X 			lowent = cent;
X 		}
X 	}
X 	firstent = lowent;
X 	for (cent = htab;  cent - htab < hsize;  cent++) {
X 		for (lent = cent;  lent != NULL;  lent = lowent) {
X 			lowent = lent->next;
X 			if (lent->keep  &&  lent->used) {
X 				lent->next = firstent;
X 				firstent = lent;
X 			}
X 		}
X 	}
X 	/* Now do the sort. */
X 	while (1) {
X 		lowent = NULL;
X 		for (cent = firstent;  cent != NULL;  cent = cent->next) {
X 			if (cent->used  &&  cent->keep) {
X 				if (lowent != NULL) {
X 					if (casecmp (cent->word,
X 						    lowent->word) < 0)
X 						lowent = cent;
X 				}
X 				else
X 					lowent = cent;
X 			}
X 		}
X 		if (lowent == NULL)
X 			break;
X 		else {
X 			toutent (lowent);
X 			lowent->used = 0;
X 		}
X 	}
X #endif
X 
X 	newwords = 0;
X 
X 	fclose (dictf);
X }
X 
X static int hasslash;
X 
X static
X toutent (cent)
X register struct dent *cent;
X {
X 	register char *cp;
X 	int len;
X 	register int wcount;
X 
X 	if (cent->k_followcase) {
X 		if (cent->k_capitalize) {
X 			lowcase (cent->word);
X 			if (mylower (cent->word[0]))
X 				cent->word[0] = toupper (cent->word[0]);
X 			toutword (cent->word, cent);
X 		}
X 		len = strlen (cent->word) + 1;
X 		cp = cent->word + len;
X 		wcount = *cp++ & 0xFF;
X 		while (--wcount >= 0) {
X 			if (*cp++ == '+')
X 				toutword (cp, cent);
X 			cp += len;
X 		}
X 	}
X 	else {
X 		if (!cent->k_allcaps)
X 			lowcase (cent->word);
X 		if (cent->k_capitalize  &&  mylower (cent->word[0]))
X 			cent->word[0] = toupper (cent->word[0]);
X 		toutword (cent->word, cent);
X 	}
X }
X 		
X static
X toutword (word, cent)
X char *word;
X register struct dent *cent;
X {
X 	hasslash = 0;
X 	fprintf (dictf, "%s", word);
X 	if (cent->d_flag)
X 		flagout ('D');
X 	if (cent->g_flag)
X 		flagout ('G');
X 	if (cent->h_flag)
X 		flagout ('H');
X 	if (cent->j_flag)
X 		flagout ('J');
X 	if (cent->m_flag)
X 		flagout ('M');
X 	if (cent->n_flag)
X 		flagout ('N');
X 	if (cent->p_flag)
X 		flagout ('P');
X 	if (cent->r_flag)
X 		flagout ('R');
X 	if (cent->s_flag)
X 		flagout ('S');
X 	if (cent->t_flag)
X 		flagout ('T');
X 	if (cent->v_flag)
X 		flagout ('V');
X 	if (cent->x_flag)
X 		flagout ('X');
X 	if (cent->y_flag)
X 		flagout ('Y');
X 	if (cent->z_flag)
X 		flagout ('Z');
X 	fprintf (dictf, "\n");
X }
X 
X static
X flagout (flag)
X {
X 	if (!hasslash)
X 		putc ('/', dictf);
X 	hasslash = 1;
X 	putc (flag, dictf);
X }
X 
X char *
X upcase (s)
X register char *s;
X {
X 	register char *os = s;
X 
X 	while (*s) {
X 		if (mylower (*s))
X 			*s = toupper (*s);
X 		s++;
X 	}
X 	return (os);
X }
X 
X char *
X lowcase (s)
X register char *s;
X {
X 	register char *os = s;
X 
X 	while (*s) {
X 		if (myupper (*s))
X 			*s = tolower (*s);
X 		s++;
X 	}
X 	return (os);
X }
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'version.h'" '(81 characters)'
if test -f 'version.h'
then
	echo shar: will not over-write existing file "'version.h'"
else
sed 's/^X //' << \SHAR_EOF > 'version.h'
X static char Version_ID[] =
X     "@(#) Ispell Version 2.0, May 1987 Beta posting";
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'xgets.c'" '(1775 characters)'
if test -f 'xgets.c'
then
	echo shar: will not over-write existing file "'xgets.c'"
else
sed 's/^X //' << \SHAR_EOF > 'xgets.c'
X 
X #include "config.h"
X #include <stdio.h>
X 
X #ifndef MAXINCLUDEFILES
X #define MAXINCLUDEFILES	1	/* maximum number of new files in stack */
X #endif
X 
X /*
X  * xgets () acts just like gets () except that if a line matches
X  * "&Include_File&<something>" xgets () will start reading from the
X  * file <something>.
X  *
X  *  Andrew Vignaux -- andrew@vuwcomp  Fri May  8 16:40:23 NZST 1987
X  * modified
X  *  Mark Davies -- mark@vuwcomp  Mon May 11 22:38:10 NZST 1987
X  */
X 
X extern int incfileflag;		/* whether xgets() acts exactly like gets() */
X 
X char *
X xgets (str)
X char str [];
X {
X #if MAXINCLUDFILES == 0
X     return gets (str);
X #else
X     static char * Include_File = DEFINCSTR;
X     static int    Include_Len = 0, strlen ();
X     static FILE * F [MAXINCLUDEFILES+1], ** current_F = F;
X     char * s = str;
X     int c;
X 
X     /* read the environment variable if we havent already */
X     if (Include_Len == 0) {
X 	char * env_variable, * getenv ();
X 
X 	if ((env_variable = getenv (INCSTRVAR) != NULL)
X 	    Include_File = env_variable;
X 	Include_Len = strlen (Include_File);
X 
X 	/* initialise the file stack */
X 	*current_F = stdin;
X     }
X 
X     while (1) {
X         if ((c = getc (*current_F)) != EOF && c != '\n') {
X 	    *s++ = c;
X 	    continue;
X 	}
X 	*s = '\0';		/* end of line */
X 	if (c == EOF)
X 	    if (current_F == F) { /* if end of standard input */
X 		if (s == str) return (NULL);
X 	    } else {
X 	        (void) fclose (*(current_F--));
X 	      	if (s == str) continue;
X 	    }
X 
X 	if (incfileflag != 0 && strncmp (str, Include_File, Include_Len) == 0) {
X 	    char * file_name = str + Include_Len;
X 	    if (current_F - F < MAX_FILES && strlen (file_name) > 0) {
X 		FILE * f;
X 		if (f = fopen (file_name, "r"))
X 		    *(++current_F) = f;
X 	    }
X 	    s = str;
X 	    continue;
X 	}
X 	break;
X     }
X     
X     return (str);
X #endif
X }
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

-- 
Brandon S. Allbery	{decvax,cbatt,cbosgd}!cwruecmp!ncoast!allbery
Tridelta Industries	{ames,mit-eddie,talcott}!necntc!ncoast!allbery
7350 Corporate Blvd.	necntc!ncoast!allbery@harvard.HARVARD.EDU
Mentor, OH 44060	+01 216 255 1080	(also eddie.MIT.EDU)