/* -*- Mode:Text -*- */
#ifndef lint
static char Rcs_Id[] =
    "$Id: ispell.c,v 1.92 1991/12/09 00:40:40 geoff Exp $";
#endif

#define MAIN

/*
 * ispell.c - An interactive spelling corrector.
 *
 * Copyright (c), 1983, by Pace Willisson
 *
 * Copyright 1987, 1988, 1989, by Geoff Kuenning, Manhattan Beach, CA
 * Permission for non-profit use is hereby granted.
 * All other rights reserved.
 * See "version.h" for a more complete copyright notice.
 *
 * 1987, Robert McQueer, added:
 *	-w option & handling of extra legal word characters
 *	-d option for alternate dictionary file
 *	-p option & WORDLIST variable for alternate personal dictionary
 *	-x option to suppress .bak files.
 *	8 bit text & config.h parameters
 * 1987, Geoff Kuenning, added:
 *	-c option for creating suffix suggestions from raw words
 *	suffixes in personal dictionary file
 *	hashed personal dictionary file
 *	-S option for unsorted word lists
 *	table driven flag handling
 *	-e option for expanding affixes
 *	-D flag for dumping flag tables
 * 1987, Greg Schaffer, added:
 *	-T option (for TeX and LaTeX instead of troff) [later changed to -t]
 *	   passes over \ till next whitespace.
 *	   does not recognize % (comment)
 * 1987, George M. Sipe, added:
 *	backup extension configurable, root filename truncation for
 *	backup extension, config.X external overrides, fast
 *	shellescape() handling, automatic use of look, next command
 *	entry accepted at "---Type..." prompts, improved ispell.1
 *	manual page, misc. minor corrections and improvements
 * 1987, Paul Placeway:
 *	changed the order of makepossibilities to be in the correct order
 * 	as per CACM aricle.
 * 1987, Ken Stevens, fixed:
 *	-t option didn't work when inside -A or -a; improved LaTeX parsing.
 *		      added:
 *	-a or -A modes now take lines rather than words. This facilitates
 *	  the -t mode parsing as well as improves performance in -a mode
 *	  from gnumacs.
 *	   The personal dictionary is now saved under user control.
 *	   *<word> inserts into personal dictionary.
 *	   @<word> accepts word for this session (I).
 *	   # saves the personal dictionary
 *	   + places ispell in tex/latex mode (tflag set).
 *	   - returns ispell to normal "troff" mode (tflag cleared).
 *		This allows ispell to be run as a background process from
 *		an editor, and dynamically check both latex and troff files.
 *	   <line of text> checked returning one flag per word.  The following
 *	      codes are used, terminated by a blank line.  The misspelled word
 *	      is included to avoid ambiguity in a multi-word line.
 *		* Word accepted.
 *		+ <rootword> Accepted because of rootword.
 *		& <misspelled-word> <possible corrections> Misspelling
 *		# <misspelled-word> Misspelling, no possible corrections.
 *
 * Ole Bjorn Hessen, 16th April 1988.
 * Let ispell read the file ~/.ispell<libhashfile> if (no personal dictionary
 * is specified) and (a libdir hash file is specified) and (the file exists)
 * else as before. Purpose: Make life more easily for those using more than
 * one language.
 *
 * Geoff Kuenning, 6 January 1989.
 * Add support for "terse" mode (Ken Steven's idea) in askmode().  "!" turns
 * terse mode on;  "%" turns it back off.
 */

/*
 * $Log: ispell.c,v $
 * Revision 1.92  1991/12/09  00:40:40  geoff
 * Add some protection against bogus filenames given in command-line
 * switches.  Don't chmod the temporary file until after it has been
 * opened; this keeps ispell from accidentally chmod'ing the current
 * directory instead.
 *
 * Revision 1.91  91/09/11  23:22:41  geoff
 * Fix the initialization of the Try array so that it is correct for
 * characters with values larger than 256.  Make sure that "defdupchar"
 * (the preferred string-cahracter type) is set to the default for files
 * with no extension.
 * 
 * Revision 1.90  91/09/04  14:35:38  geoff
 * Fix the options dumping to be configurable for language.
 * 
 * Revision 1.89  91/09/03  20:14:14  geoff
 * Add support for the -vv (print compilation options) switch.
 * 
 * Revision 1.88  91/08/24  03:21:10  geoff
 * Unlink temp file even if no changes were made
 * 
 * Revision 1.87  91/08/10  16:38:26  geoff
 * Add support for the -V flag.
 * 
 * Revision 1.86  91/07/27  20:48:23  geoff
 * Rearrange the includes (again) so that ispell will compile without
 * errors on Suns.  Add code to set LibDict even if the default hash file
 * is in use.
 * 
 * Revision 1.85  91/07/15  19:26:58  geoff
 * Provide the "canonical" parameter to all strtoichar calls.
 * 
 * Revision 1.84  91/07/11  19:52:10  geoff
 * Remove the include of stdio.h, since ispell.h now does this.  Change
 * the priority of the -T switch so that it overrides the string
 * character type selected from the file suffix.
 * 
 * Revision 1.83  91/07/05  19:51:41  geoff
 * Fix some lint complaints.
 * 
 * Revision 1.82  91/07/04  00:04:14  geoff
 * Move findfiletype() to makedent.c.
 * 
 * Revision 1.81  91/07/03  18:20:43  geoff
 * Don't include types.h, since config.h now does that.  Replace all
 * conversions of chars to ichar_t's with a macro call which (a) does the
 * conversion correctly and (b) makes future changes easy in case (a) is
 * false.
 * 
 * Revision 1.80  91/06/23  22:08:17  geoff
 * When casting to ichar_t, cast to unsigned first to avoid sign-extension
 * problems.
 * 
 * Revision 1.79  91/06/12  19:15:14  geoff
 * Range-check the mask bit before setting it.
 * 
 * Revision 1.78  91/05/27  21:47:55  geoff
 * Add support for deducing the preferred string character type from the
 * file extension, and for the "-T" switch.  Add the "findfiletype"
 * routine.
 * 
 * Revision 1.77  91/01/24  02:27:56  geoff
 * Always write out personal dictionary on exit from ask (-a) mode.
 * 
 * Revision 1.76  90/12/31  00:59:16  geoff
 * Reformat to follow a consistent convention throughout ispell
 * 
 * Revision 1.75  90/10/14  01:51:34  geoff
 * Add Guy Shaw's changes to support readonly files and only update the
 * file if it has changed.  Also add code which makes sure that at least
 * one of the specified files exists before going to the expense of loading
 * the dictionary;  this is in response to an ancient suggestion by somebody
 * whose name I have forgotten.
 * 
 * Revision 1.74  89/12/27  22:27:52  geoff
 * Add the -N and -W options.
 * 
 * Revision 1.73  89/12/27  03:20:51  geoff
 * Move all messages to msgs.h so they can be reconfigured
 * 
 * Revision 1.72  89/07/11  00:32:35  geoff
 * Add Amiga support from luis@rice.edu.
 * 
 * Revision 1.71  89/06/27  01:54:38  geoff
 * Break ispell.c up into smaller files
 * 
 * Revision 1.70  89/06/25  15:15:46  geoff
 * Add a couple of suggestions from Ken Stevens:  Don't make a ".tex"
 * suffix force TeX mode for all subsequent files, and make LOOK optional.
 * 
 * Revision 1.69  89/06/09  15:53:26  geoff
 * Add support for the internal "character" type, ichar_t.
 * 
 * Revision 1.68  89/04/28  01:10:31  geoff
 * Change Header to Id;  nobody cares about my pathnames.
 * 
 * Revision 1.67  89/04/27  23:31:25  geoff
 * Fix extraletter() to work properly with string characters (it didn't
 * actually delete the extra letter).  Get rid of a couple of duplicate
 * tests for backslashing the flag marker in dumpmode().
 * 
 * Revision 1.66  89/04/03  01:55:37  geoff
 * Add support for string characters and the selectable flag marker, and
 * fix some lint complaints.
 * 
 * Revision 1.65  89/02/27  02:22:06  geoff
 * Rohit Aggarwal's changes -- call givehelp() from Usage(), and add
 * the -M option and the mini-menu.
 * 
 * Revision 1.64  89/02/22  23:14:21  geoff
 * Add the -L switch and support for a variable-sized context.  Also fix the
 * bug that would unlink a file if it had a 14-character filename.  Finally,
 * fix a bug in ins_root_cap when CAPITALIZATION was turned off.
 * 
 * Revision 1.63  89/02/17  14:10:31  geoff
 * Improve the DeTex processing some more (Ken Stevens).
 * 
 * Revision 1.62  89/02/16  00:35:40  geoff
 * Add support for 'terse' mode as per Ken Stevens' suggestion.
 * 
 * Revision 1.61  88/12/26  02:28:21  geoff
 * Update and complete the copyright notice.
 * 
 * Revision 1.60  88/12/12  02:18:16  geoff
 * Correctly handle null additions in dump mode
 * 
 * Revision 1.59  88/11/30  01:37:31  geoff
 * Fix a stupid bug in revision 1.57
 * 
 * Revision 1.58  88/11/27  00:05:01  geoff
 * Interchange the 'q' and 'x' commands;  this makes the command more
 * consistent with other Unix programs (quit means give up;  exit
 * means I'm done but this work is valid).
 * 
 * Revision 1.57  88/11/25  19:51:02  geoff
 * Fix casecmp to generate the same result regardless of argument order when
 * one string is a leading substring of the other.
 * 
 * Revision 1.56  88/11/17  23:15:25  geoff
 * Get rid of a minor warning from some compilers
 * 
 * Revision 1.55  88/11/16  01:46:41  geoff
 * Make some improvements and simplifications to the processing of the
 * "-w" option.  Remember to set "outfile" in list ("-l") mode.
 * 
 * Revision 1.54  88/10/20  22:35:36  geoff
 * Fix a dumb bug in the last version.
 * 
 * Revision 1.53  88/10/20  20:52:05  geoff
 * Integrate Ole Bjoern Hessen's changes that support automatically
 * associating a different personal dictionary when the -d switch is used.
 * 
 * Revision 1.52  88/06/25  17:48:26  geoff
 * Add support for the new switches -n, -b, -B, -C, -P, and -m, for the
 * keeping of TeX/nroff special characters in the hash header, and for
 * setting certain defaults in the hash header.  Note that the -B/-C switches
 * add the new routine "compoundgood", and the -P/-m switches replace
 * "nopossibilities" with "easypossibilities" and change the emacs output
 * format.
 * 
 * Revision 1.51  88/04/30  22:12:42  geoff
 * Fix a bunch of lint complaints, including a few honest bugs.
 * 
 * Revision 1.50  88/04/11  01:34:46  geoff
 * Fix checkline to do all output onto outfile (this is for cleanliness
 * only).  Change askmode output to include the character offset within
 * the line.  Change xgets calls to reflect the new calling sequence.  In
 * askmode, don't trim the input *line* to maximum *word* length!
 * 
 * Revision 1.49  88/04/10  20:49:48  geoff
 * Integrate Ken Stevens' changes into the main branch
 * 
 * Revision 1.48  88/04/03  23:09:22  geoff
 * Fix the processing of the -w option to operate after the hash header is
 * read in, not before, so the work doesn't get clobbered by the header.
 * 
 * Revision 1.47  88/03/27  00:59:17  geoff
 * Get rid of some spurious declarations that are already taken care of in
 * ispell.h.  Add ".hash" to end of hash file names if it's not already there.
 * Fix ins_root_cap to use the old-style capitalization rules if CAPITALIZATION
 * isn't defined.
 * 
 * Revision 1.46  88/02/20  23:11:30  geoff
 * Fix a bug that caused multiple single quotes (e.g., ``xx'' as in many
 * troff source files) to be considered as part of the word.  Major changes
 * to add support for the new capitalization handling.  If a word has wrong
 * capitalization, don't try other corrections.  Fix entdump to handle
 * flags with null affixes.
 * 
 * Revision 1.45  87/09/30  23:30:01  geoff
 * Move some globals to ispell.h.  Add the "nopossibilities" (tryveryhard)
 * code and stuff to support it.  Add "missingspace".  Improve "ins_cap"
 * to deal with affixes better.
 * 
 * Revision 1.44  87/09/26  15:41:40  geoff
 * Add code to limit input word lengths, and make the buffer sizes
 * consistent and reasonable.
 * 
 * Revision 1.43  87/09/09  00:17:44  geoff
 * Replace a lot of #ifdef's on NO8BIT with use of SET_SIZE and NOPARITY.  Fix
 * an error in the help messages.  Fix a bug in casecmp when two words matched
 * except for case.
 * 
 * Revision 1.42  87/09/04  22:35:50  geoff
 * Oops, don't put two dots in the backup filename.
 * 
 * Revision 1.41  87/09/03  23:43:45  geoff
 * Add Paul Placeway's fix to generate possibilites in the most likely order.
 * 
 * Revision 1.40  87/09/03  23:00:45  geoff
 * Integrate George Sipes latest changes:  dynamically determine availability
 * of look(1), accept commands at "type space" prompts, clean up help, add
 * TRUNCATEBAK code, make ^Z spawn a shell on USG.
 * 
 * Revision 1.39  87/08/28  21:20:40  geoff
 * Fix some errors in the usage messages.  Fancify the -v switch to
 * print a multi-line message.
 * 
 * Revision 1.38  87/08/18  21:19:51  geoff
 * Handle both forward and reverse quotes (accents) identically.  Assume that
 * all word characters (iswordch) occupy one screen position on the display.
 * 
 * Revision 1.37  87/08/13  20:46:58  geoff
 * Fix the apostrophe handling to work correctly independently of whether
 * the apostrophe is a member of "iswordch".
 * 
 * Revision 1.36  87/07/20  23:21:51  geoff
 * Add table-driving support and the -e and -D flags.  Also add support
 * for nodictflag.
 * 
 * Revision 1.35  87/06/27  12:19:29  geoff
 * Fix some minor help problems.  Add support for the "sg" option for
 * brain-damaged terminals (Michael Wester).
 * 
 * Revision 1.34  87/06/07  15:25:09  geoff
 * At Don Kark's suggestion, improve ISTEXTERM
 * 
 * Revision 1.33  87/06/07  15:15:24  geoff
 * Integrate Don Karks's TeX brace-handling
 * 
 * Revision 1.32  87/06/02  23:39:38  geoff
 * Rewrite rev 31 to be somewhat more efficient and elegant
 * 
 * Revision 1.31  87/06/02  01:53:10  geoff
 * Change the way wrongletter() handles case, so that it will work under
 * all circumstances.
 * 
 * Revision 1.30  87/05/25  21:58:44  geoff
 * Add the -v option.
 * 
 * Revision 1.29  87/05/25  21:10:33  geoff
 * Add my interpretation of Mark Davies' changes.
 * 
 * Revision 1.28  87/05/14  23:18:05  geoff
 * Add support for backslashes in the -w option
 * 
 * Revision 1.27  87/05/14  00:00:08  geoff
 * Fix tab handling in show_char
 * 
 * Revision 1.26  87/04/19  22:52:11  geoff
 * Add BAKEXT (George Sipe).  Add capitalization handling.
 * 
 * Revision 1.25  87/04/06  23:41:02  geoff
 * Get rid of some minor excess statements.  Calculate token length every
 * pass through correct's main loop.  Add an ifdef for EQUAL_COLUMNS.
 * Handle non-word characters correctly in inserttoken.
 * 
 * Revision 1.24  87/04/06  22:43:33  geoff
 * Fix a couple of errors in the usage message
 * 
 * Revision 1.23  87/04/02  12:25:14  geoff
 * Make stdout buffered in normal mode, to improve performance.
 * 
 * Revision 1.22  87/04/01  17:23:47  geoff
 * Improve the long-line handling to deal with control chars
 * 
 * Revision 1.21  87/04/01  15:22:14  geoff
 * Integrate Joe Orost's V7/register changes into the main branch
 * 
 * Revision 1.20  87/03/31  18:40:06  geoff
 * Do the previous change correctly
 * 
 * Revision 1.19  87/03/31  17:51:20  geoff
 * Fix some syntax errors introduced during integration
 * 
 * Revision 1.18  87/03/31  16:40:59  geoff
 * Integrate Steve Kelem's changes into the main branch:  really wait for
 * a space in givehelp();  accept +/- in troff size strings, and automatically
 * set TeX mode if the file extension is ".tex".
 * 
 * Revision 1.17  87/03/31  15:32:55  geoff
 * Improve troff backslash handling some more (isaac@mulga.oz)
 * 
 * Revision 1.16  87/03/29  01:58:16  geoff
 * Duplicate the original file's modes on temp and output files.  Really
 * generate 100 possibilities, not 99.  Don't assume 80 columns.  Handle
 * input lines wider than the terminal properly.
 * 
 * Revision 1.15  87/03/28  23:17:26  geoff
 * Make LOOK configurable
 * 
 * Revision 1.14  87/03/28  20:15:37  geoff
 * Integrate Gary Puckering's changes into the main branch
 * 
 * Revision 1.13  87/03/28  20:01:46  geoff
 * Change -T to -t (per Don Kark, among others)
 * 
 * Revision 1.12  87/03/27  17:21:08  geoff
 * Add the -S option.  Greatly expand the size of the substitution list,
 * including multiple columns of suggetsions.
 * 
 * Revision 1.11  87/03/23  00:03:48  geoff
 * Let's try again:  Greg Schaffer's fixed TeX stuff
 * 
 * Revision 1.9  87/03/22  23:00:55  geoff
 * Integrate Perry Smith's emacs-support changes
 * 
 * Revision 1.8  87/03/08  20:30:29  geoff
 * Don't output the tree if cflag.  Add commentary about my changes.
 * 
 * Revision 1.7  87/03/01  00:56:34  geoff
 * Add a declaration of treeinsert.
 * 
 * Revision 1.6  87/02/28  14:58:02  geoff
 * Add the -c flag.
 * 
 * Revision 1.5  87/02/28  01:05:47  geoff
 * Fix a typo in a mask;  also move the -i switch to egrep into config.h
 * (EGREPCMD) so it's changeable.
 * 
 * Revision 1.4  87/02/26  00:37:01  geoff
 * Integrate McQueer's enhancements into the main branch
 * 
 * Revision 1.3  87/01/19  00:05:46  geoff
 * Add USG support;  mostly #ifdef'ing out calls to stop().
 * 
 * Revision 1.2  87/01/17  13:11:42  geoff
 * Add RCS ID keywords
 * 
 */

#include "config.h"
#include "ispell.h"
#include "msgs.h"
#include "version.h"
#include <ctype.h>
#ifdef AMIGA
#include <stat.h>
#else /* AMIGA */
#include <sys/stat.h>
#endif /* AMIGA */

#ifdef OS2
#include <stdlib.h>
#include <sys/param.h>
#endif /* OS/2 */

extern void	dumpmode ();	/* Execute -D flag */

extern char *	xgets ();

extern void	exit ();
extern char *	mktemp ();
extern unsigned	sleep ();
extern char *	index ();
extern char *	rindex ();
extern char *	strcpy ();
extern char *	strcat ();

#ifdef OS2
extern void     getsetup (int printoption, char *argv[]);
#endif  /* OS/2 */

static char *	Cmd;

usage ()
    {

    (void) fprintf (stderr, ISPELL_C_USAGE1, Cmd);
    (void) fprintf (stderr, ISPELL_C_USAGE2, Cmd);
    (void) fprintf (stderr, ISPELL_C_USAGE3, Cmd);
    (void) fprintf (stderr, ISPELL_C_USAGE4, Cmd);
    (void) fprintf (stderr, ISPELL_C_USAGE5, Cmd);
    (void) fprintf (stderr, ISPELL_C_USAGE6, Cmd);
    (void) fprintf (stderr, ISPELL_C_USAGE7, Cmd);
    givehelp (0);
    exit (1);
    }

static initckch(wchars)
    char *		wchars;		/* Characters in -w option, if any */
    {
    register ichar_t	c;
    char		num[4];

    for (c = 0; c < SET_SIZE + hashheader.nstrchars; ++c)
	{
	if (iswordch (c))
	    {
	    if (!mylower (c))
		{
		Try[Trynum] = c;
		++Trynum;
		}
	    }
	}
    if (wchars != NULL)
	{
	while (Trynum < SET_SIZE  &&  *wchars != '\0')
	    {
	    if (*wchars != 'n'  &&  *wchars != '\\')
		{
		c = *wchars;
		++wchars;
		}
	    else
		{
		++wchars;
		num[0] = '\0'; 
		num[1] = '\0'; 
		num[2] = '\0'; 
		num[3] = '\0';
		if (isdigit (wchars[0]))
		    {
		    num[0] = wchars[0];
		    if (isdigit (wchars[1]))
			{
			num[1] = wchars[1];
			if (isdigit (wchars[2]))
			    num[2] = wchars[2];
			}
		    }
		if (wchars[-1] == 'n')
		    {
		    wchars += strlen (num);
		    c = atoi (num);
		    }
		else
		    {
		    wchars += strlen (num);
		    c = 0;
		    if (num[0])
			c = num[0] - '0';
		    if (num[1])
			{
			c <<= 3;
			c += num[1] - '0';
			}
		    if (num[2])
			{
			c <<= 3;
			c += num[2] - '0';
			}
		    }
		}
	    c &= NOPARITY;
	    if (!hashheader.wordchars[c])
		{
		hashheader.wordchars[c] = 1;
		hashheader.sortorder[c] = hashheader.sortval++;
		Try[Trynum] = c;
		++Trynum;
		}
	    }
	}
    }

main (argc, argv)
    int		argc;
    char *	argv[];
    {
    char *	p;
    char *	cpd;
    char **	versionp;
    char *	wchars = NULL;
    char *	preftype = NULL;
    static char	libdictname[sizeof DEFHASH];
    static char	outbuf[BUFSIZ];
    int		argno;


#ifdef AMIGA
    _Heapsize = HEAPSIZE;
#endif

#ifdef OS2
    _wildcard (&argc, &argv);   /* unix-like wild card expansion (emx/gcc) */
    getsetup(0, argv);                 /* get info from config file */
#endif /* OS2 */

    Cmd = *argv;

    Trynum = 0;

#ifdef OS2
    if ( cfhashname[0] == '\0')     
            (void) sprintf (hashname,"%s/%s",cflibdir,DEFHASH);
    else 
            (void) strcpy(hashname, cfhashname);
#else
    (void) sprintf (hashname,"%s/%s",LIBDIR,DEFHASH);
#endif  /* OS2 */

    cpd = NULL;

    argv++;
    argc--;
    while (argc && **argv == '-')
	{
	/*
	 * Trying to add a new flag?  Can't remember what's been used?
	 * Here's a handy guide:
	 *
	 * Used:
	 *
	 *	ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789
	 *	^^^^       ^^^ ^  ^^ ^^
	 *	abcdefghijklmnopqrstuvwxyz
	 *	^^^^^^     ^^^ ^  ^^ ^^^
	 */
	switch ((*argv)[1])
	    {
	    case 'v':
		for (versionp = Version_ID;  *versionp;  )
		    {
		    p = *versionp++;
		    if (strncmp (p, "@(#) ", 5) == 0)
		      p += 5;
		    (void) printf ("%s\n", p);
		    }
		if ((*argv)[2] == 'v')
		    {
		    (void) printf (ISPELL_C_OPTIONS_ARE);
#ifdef USG
		    (void) printf ("\tUSG\n");
#else /* USG */
		    (void) printf ("\t!USG (BSD)\n");
#endif /* USG */
#ifdef AMIGA
		    (void) printf ("\tAMIGA\n");
#endif /* AMIGA */
		    (void) printf ("\tLIBDIR = \"%s\"\n", LIBDIR);
		    (void) printf ("\tDEFHASH = \"%s\"\n", DEFHASH);
		    (void) printf ("\tPDICTVAR = \"%s\"\n", PDICTVAR);
		    (void) printf ("\tDEFPDICT = \"%s\"\n", DEFPDICT);
		    (void) printf ("\tDEFPAFF = \"%s\"\n", DEFPAFF);
		    (void) printf ("\tINCSTRVAR = \"%s\"\n", INCSTRVAR);
		    (void) printf ("\tTEMPNAME = \"%s\"\n", TEMPNAME);
		    (void) printf ("\tDEFLANG = \"%s\"\n", DEFLANG);
		    (void) printf ("\tDEFDICT = \"%s\"\n", DEFDICT);
#ifdef REGEX_LOOKUP
		    (void) printf ("\tREGEX_LOOKUP\n");
#else /* REGEX_LOOKUP */
		    (void) printf ("\t!REGEX_LOOKUP\n");
		    (void) printf ("\tLOOK = \"%s\"\n", LOOK);
#endif /* REGEX_LOOKUP */
		    (void) printf ("\tEGREPCMD = \"%s\"\n", EGREPCMD);
		    (void) printf ("\tWORDS = \"%s\"\n", WORDS);
		    (void) printf ("\tMAXPATHLEN = %d\n", MAXPATHLEN);
		    (void) printf ("\tMAXNAMLEN = %d\n", MAXNAMLEN);
#ifdef TRUNCATEBAK
		    (void) printf ("\tTRUNCATEBAK\n");
#else /* TRUNCATEBAK */
		    (void) printf ("\t!TRUNCATEBAK\n");
#endif /* TRUNCATEBAK */
		    (void) printf ("\tINPUTWORDLEN = %d\n", INPUTWORDLEN);
		    (void) printf ("\tMAXAFFIXLEN = %d\n", MAXAFFIXLEN);
#ifdef NO8BIT
		    (void) printf ("\tNO8BIT\n");
#else /* NO8BIT */
		    (void) printf ("\t!NO8BIT (8BIT)\n");
#endif /* NO8BIT */
		    (void) printf ("\tMASKBITS = %d\n", MASKBITS);
		    (void) printf ("\tMAXINCLUDEFILES = %d\n",
		      MAXINCLUDEFILES);
		    (void) printf ("\tMAXPCT = %d\n", MAXPCT);
		    (void) printf ("\tMAXSTRINGCHARS = %d\n", MAXSTRINGCHARS);
		    (void) printf ("\tMAXSTRINGCHARLEN = %d\n",
		      MAXSTRINGCHARLEN);
#if TERM_MODE == CBREAK
		    (void) printf ("\tCBREAK\n");
#else /* TERM_MODE */
		    (void) printf ("\tRAW\n");
#endif /* TERM_MODE */
#ifdef EQUAL_COLUMNS
		    (void) printf ("\tEQUAL_COLUMNS\n");
#else /* EQUAL_COLUMNS */
		    (void) printf ("\t!EQUAL_COLUMNS\n");
#endif /* EQUAL_COLUMNS */
		    (void) printf ("\tBAKEXT = \"%s\"\n", BAKEXT);
#ifdef CAPITALIZATION
		    (void) printf ("\tCAPITALIZATION\n");
#else /* CAPITALIZATION */
		    (void) printf ("\t!CAPITALIZATION\n");
#endif /* CAPITALIZATION */
		    (void) printf ("\tSORTPERSONAL = %d\n", SORTPERSONAL);
#ifdef USESH
		    (void) printf ("\tUSESH\n");
#else /* USESH */
		    (void) printf ("\t!USESH\n");
#endif /* USESH */
		    (void) printf ("\tMAXSEARCH = %d\n", MAXSEARCH);
#ifdef COMMANDFORSPACE
		    (void) printf ("\tCOMMANDFORSPACE\n");
#else /* COMMANDFORSPACE */
		    (void) printf ("\t!COMMANDFORSPACE\n");
#endif /* COMMANDFORSPACE */
		    (void) printf ("\tMALLOC_INCREMENT = %d\n",
		      MALLOC_INCREMENT);
		    (void) printf ("\tMAX_HITS = %d\n", MAX_HITS);
#ifdef IGNOREBIB
		    (void) printf ("\tIGNOREBIB\n");
#else /* IGNOREBIB */
		    (void) printf ("\t!IGNOREBIB\n");
#endif /* IGNOREBIB */
		    (void) printf ("\tTEXSPECIAL = \"%s\"\n", TEXSPECIAL);
		    (void) printf ("\tNRSPECIAL = \"%s\"\n", NRSPECIAL);
		    (void) printf ("\tDEFNOBACKUPFLAG = %d\n",
		      DEFNOBACKUPFLAG);
		    (void) printf ("\tDEFTEXFLAG = %d\n", DEFTEXFLAG);
		    (void) printf ("\tMAX_SCREEN_SIZE = %d\n",
		      MAX_SCREEN_SIZE);
		    (void) printf ("\tCONTEXTPCT = %d\n", CONTEXTPCT);
		    (void) printf ("\tMINCONTEXT = %d\n", MINCONTEXT);
		    (void) printf ("\tMAXCONTEXT = %d\n", MAXCONTEXT);
#ifdef CONTEXTROUNDUP
		    (void) printf ("\tCONTEXTROUNDUP\n");
#else /* CONTEXTROUNDUP */
		    (void) printf ("\t!CONTEXTROUNDUP\n");
#endif /* CONTEXTROUNDUP */
#ifdef MINIMENU
		    (void) printf ("\tMINIMENU\n");
#else /* MINIMENU */
		    (void) printf ("\t!MINIMENU\n");
#endif /* MINIMENU */
		    (void) printf ("\tMINWORD = %d\n", MINWORD);
#ifdef OS2
                    getsetup(1, argv);    
#endif /* OS2 */
		    }
		exit (0);
	    case 'n':
		tflag = 0;		/* nroff/troff mode */
		deftflag = 0;
		if (preftype == NULL)
		    preftype = "nroff";
		break;
	    case 't':			/* TeX mode */
		tflag = 1;
		deftflag = 1;
		if (preftype == NULL)
		    preftype = "tex";
		break;
	    case 'T':			/* Set preferred file type */
		p = (*argv)+2;
		if (*p == '\0')
		    {
		    argv++; argc--;
		    if (argc == 0)
			usage ();
		    p = *argv;
		    }
		preftype = p;
		break;
	    case 'A':
		incfileflag = 1;
		aflag = 1;
		break;
	    case 'a':
		aflag++;
		break;
	    case 'D':
		dumpflag++;
		nodictflag++;
		break;
	    case 'e':
		eflag++;
		if ((*argv)[2] == 'e')
		  eflag++;
		nodictflag++;
		break;
	    case 'c':
		cflag++;
		lflag++;
		nodictflag++;
		break;
	    case 'b':
		xflag = 0;		/* Keep a backup file */
		break;
	    case 'x':
		xflag = 1;		/* Don't keep a backup file */
		break;
	    case 'f':
		fflag++;
		p = (*argv)+2;
		if (*p == '\0')
		    {
		    argv++; argc--;
		    if (argc == 0)
			usage ();
		    p = *argv;
		    }
		askfilename = p;
		if (*askfilename == '\0')
		    askfilename = NULL;
		break;
	    case 'L':
		p = (*argv)+2;
		if (*p == '\0')
		    {
		    argv++; argc--;
		    if (argc == 0)
			usage ();
		    p = *argv;
		    }
		contextsize = atoi (p);
		break;
	    case 'l':
		lflag++;
		break;
#ifndef USG
	    case 's':
		sflag++;
		break;
#endif
	    case 'S':
		sortit = 0;
		break;
	    case 'B':		/* -B:  report missing blanks */
		missingspaceflag = 1;
		break;
	    case 'C':		/* -C:  compound words are acceptable */
		missingspaceflag = 0;
		break;
	    case 'P':		/* -P:  don't gen non-dict poss's */
		tryhardflag = 0;
		break;
	    case 'm':		/* -m:  make all poss affix combos */
		tryhardflag = 1;
		break;
	    case 'N':		/* -N:  suppress minimenu */
		minimenusize = 0;
		break;
	    case 'M':		/* -M:  force minimenu */
		minimenusize = 2;
		break;
	    case 'p':           /* -p:  personal wordlist */
		cpd = (*argv)+2;
		if (*cpd == '\0')
		    {
		    argv++; argc--;
		    if (argc == 0)
			usage ();
		    cpd = *argv;
		    if (*cpd == '\0')
			cpd = NULL;
		    }
		LibDict = NULL;
		break;
	    case 'd':
		p = (*argv)+2;
		if (*p == '\0')
		    {
		    argv++; argc--;
		    if (argc == 0)
			usage ();
		    p = *argv;
		    }
		if (index (p, '/') != NULL)
		    (void) strcpy (hashname, p);
#ifdef OS2
		else
		    (void) sprintf (hashname, "%s/%s", cflibdir, p);
#else
		else
		    (void) sprintf (hashname, "%s/%s", LIBDIR, p);
#endif  /* OS2 */
		if (cpd == NULL  &&  *p != '\0')
		    LibDict = p;
		p = rindex (p, '.');

#ifdef OS2      /* could be on an FAT system - check for .has */
		if (p != NULL  &&  ( (strcmp (p, ".hash") == 0) || (strcmp (p,".has") == 0) ) )
#else
		if (p != NULL  &&  strcmp (p, ".hash") == 0)
#endif  /* OS2 */
		    *p = '\0';	/* Don't want ext. in LibDict */
		else
		    (void) strcat (hashname, ".hash");
		break;
	    case 'V':		/* Display 8-bit characters as M-xxx */
		vflag = 1;
		break;
	    case 'w':
		wchars = (*argv)+2;
		if (*wchars == '\0')
		    {
		    argv++; argc--;
		    if (argc == 0)
			usage ();
		    wchars = *argv;
		    }
		break;
	    case 'W':
		if ((*argv)[2] == '\0')
		    {
		    argv++; argc--;
		    if (argc == 0)
			usage ();
		    minword = atoi (*argv);
		    }
		else
		    minword = atoi (*argv + 2);
		break;
	    default:
		usage();
	    }
	argv++;
	argc--;
	}

    if (!argc  &&  !lflag  &&  !aflag   &&  !eflag  &&  !dumpflag)
	usage ();

    /*
     * Because of the high cost of reading the dictionary, we stat
     * the files specified first to see if they exist.  If at least
     * one exists, we continue.
     */
    for (argno = 0;  argno < argc;  argno++)
	{
	if (access (argv[argno], 4) < 0)
	    break;
	}
    if (argno < argc)
	{
	(void) fprintf (stderr,
	  argc == 1 ? ISPELL_C_NO_FILE : ISPELL_C_NO_FILES);
	exit (1);
	}
    if (linit () < 0)
	exit (1);

    if (preftype != NULL)
	{
	prefstringchar = findfiletype (preftype, 1);
	if (prefstringchar < 0
	  &&  strcmp (preftype, "tex") != 0
	  &&  strcmp (preftype, "nroff") != 0)
	    {
	    (void) fprintf (stderr, ISPELL_C_BAD_TYPE, preftype);
	    exit (1);
	    }
	}
    if (prefstringchar < 0)
	defdupchar = 0;
    else
	defdupchar = prefstringchar;

    if (missingspaceflag < 0)
	missingspaceflag = hashheader.defspaceflag;
    if (tryhardflag < 0)
	tryhardflag = hashheader.defhardflag;

    initckch(wchars);

    if (LibDict == NULL)	
	{
	(void) strcpy (libdictname, DEFHASH);
	LibDict = libdictname;
	p = rindex (libdictname, '.');
	if (p != NULL  &&  strcmp (p, ".hash") == 0)
	    *p = '\0';	/* Don't want ext. in LibDict */
	}
    if (!nodictflag)
	treeinit (cpd, LibDict);

    if (aflag)
	{
	askmode ();
	treeoutput ();
	exit (0);
	}
    else if (eflag)
	{
	expandmode (eflag > 1);
	exit (0);
	}
    else if (dumpflag)
	{
	dumpmode ();
	exit (0);
	}

    setbuf (stdout, outbuf);
    if (lflag)
	{
	infile = stdin;
	outfile = stdout;
	checkfile ();
	exit (0);
	}

    terminit ();

    while (argc--)
	dofile (*argv++);

    done ();
    /* NOTREACHED */
    }

dofile (filename)
    char *	filename;
    {
    struct stat	statbuf;
    char *	cp;

    currentfile = filename;
    defdupchar = findfiletype (filename, 0);
    if (defdupchar < 0)
	{
	if (prefstringchar >= 0)
	    defdupchar = prefstringchar;
	else
	    defdupchar = 0;
	}

    if ((infile = fopen (filename, "r")) == NULL)
	{
	(void) fprintf (stderr, CANT_OPEN, filename);
	(void) sleep (2);
	return;
	}

    readonly = access (filename, 2) < 0;
    if (readonly)
	{
	(void) fprintf (stderr, ISPELL_C_CANT_WRITE, filename);
	(void) sleep (2);
	}

#ifndef AMIGA
    (void) fstat (fileno (infile), &statbuf);
#endif /* AMIGA */

#ifdef OS2
    if  ( cftempname[0] == '\0' )
        (void) strcpy (tempfile, TEMPNAME);
    else
        (void) strcpy (tempfile, cftempname);
#else
    (void) strcpy (tempfile, TEMPNAME);
#endif  /* OS2 */

    (void) mktemp (tempfile);
    if ((outfile = fopen (tempfile, "w")) == NULL)
	{
	(void) fprintf (stderr, CANT_CREATE, tempfile);
	(void) sleep (2);
	return;
	}
#ifndef AMIGA
#ifndef OS2
    (void) chmod (tempfile, statbuf.st_mode);
#endif /* OS/2 */
#endif /* AMIGA */

    quit = 0;
    changes = 0;

    /* See if the file is a .tex file.  If so, set the appropriate flag. */
    tflag = deftflag;
    if ((cp = rindex (filename, '.')) != NULL  &&  strcmp (cp, ".tex") == 0)
	tflag = 1;
    checkfile ();

    (void) fclose (infile);
#ifdef OS2      /* try this for DOS */
    fflush(outfile);
#endif  /* OS/2 */
    (void) fclose (outfile);

    if (!cflag)
	treeoutput ();

    if (changes && !readonly)
	update_file (filename, &statbuf);
        (void) unlink (tempfile); 
    }

update_file (filename, statbuf)
    char *		filename;
    struct stat *	statbuf;
    {
    char		bakfile[256];
    int			c;

#ifdef OS2
    char    filenamepath[MAXNAMLEN];
    char    drivename[3];
    char    fstype[16];
    register char *top;
#endif  /* OS/2 */

    if ((infile = fopen (tempfile, "r")) == NULL)
	{
	(void) fprintf (stderr, ISPELL_C_TEMP_DISAPPEARED, tempfile);
	(void) sleep (2);
	return;
	}

#ifdef TRUNCATEBAK
    (void) strcpy (bakfile, filename);
    if (strcmp(BAKEXT, filename + strlen(filename) - strlen(BAKEXT)) != 0)
	{
	register char *top;
	register int maxlen;

	maxlen = MAXNAMLEN - strlen(BAKEXT);
	top = (char *) rindex(bakfile, '/');
	top = top ? top + 1 : bakfile;
	if (strlen(top) > maxlen)
	    *(top + maxlen) = '\000';
	(void) strcat (bakfile, BAKEXT);
	}
#else
    (void) sprintf (bakfile, "%s%s", filename, BAKEXT);
#endif

#ifdef OS2 /* create a valid backup file for FAT */
        _abspath(filenamepath, filename, MAXNAMLEN);
        strncpy(drivename, filenamepath, 2);     /* make drive specifier*/
        *(drivename+2) = '\0';
        _filesys(drivename, fstype, sizeof(fstype));    /* get filesystem type */ 
        if (strcmp(fstype, "FAT") == 0)  /* make fat compatible backup file */
                {
                top = (char *) index(bakfile, '.');
                strncpy(top, BAKEXT, sizeof(BAKEXT));
                *(top+4) = '\0';
                }
#endif /* OS/2 */

    if (strncmp(filename, bakfile, MAXNAMLEN) != 0)
      (void) unlink (bakfile);	/* unlink so we can write a new one. */
#ifndef OS2     /* no link function under os/2 */
    if(link(filename, bakfile) == 0)  
	(void) unlink (filename);       
#endif  /* OS2 */

    /* if we can't write new, preserve .bak regardless of xflag */
    if ((outfile = fopen (filename, "w")) == NULL)
	{
	(void) fprintf (stderr, CANT_CREATE, filename);
	(void) sleep (2);
	return;
	}

#ifndef AMIGA
#ifndef OS2
    (void) chmod (filename, statbuf->st_mode);
#endif /* OS/2 */
#endif /* AMIGA */

    while ((c = getc (infile)) != EOF)
	(void) putc (c, outfile);

    (void) fclose (infile);
    (void) fclose (outfile);

    if (xflag  &&  strncmp(filename, bakfile, MAXNAMLEN) != 0)
	(void) unlink (bakfile);
    }

expandmode (printorig)
    int			printorig;	/* NZ to print original word */
    {
    char		buf[BUFSIZ];
    ichar_t		ibuf[BUFSIZ];
    register char *	flagp;		/* Pointer to next flag char */
    register int	temp;
    MASKTYPE		mask[MASKSIZE];

    while (xgets (buf, sizeof buf, stdin) != NULL)
	{
	temp = strlen (buf) - 1;
	if (buf[temp] == '\n')
	  buf[temp] = '\0';
	if (printorig)
	  (void) printf ("%s ", buf);
	if ((flagp = index (buf, hashheader.flagmarker)) != NULL)
	    *flagp++ = '\0';
	if (flagp != NULL)
	    {
	    if (flagp - buf > INPUTWORDLEN)
		buf[INPUTWORDLEN] = '\0';
	    }
	else
	    {
	    if (strlen (buf) > INPUTWORDLEN - 1)
		buf[INPUTWORDLEN] = '\0';
	    }
	(void) fputs (buf, stdout);
	if (flagp != NULL)
	    {
	    (void) bzero ((char *) mask, sizeof (mask));
	    while (*flagp != '\0'  &&  *flagp != '\n')
		{
#if MASKBITS <= 32
		temp = CHARTOBIT (mytoupper (chartoichar (*flagp)));
#else
		temp = CHARTOBIT ((unsigned char) *flagp);
#endif
		if (temp >= 0  &&  temp <= LARGESTFLAG)
		    SETMASKBIT (mask, temp);
		flagp++;
		/* Accept old-format dicts with extra slashes */
		if (*flagp == hashheader.flagmarker)
		    flagp++;
		}
	    strtoichar (ibuf, buf, 1);
	    expand_pre (ibuf, mask);
	    expand_suf (ibuf, mask, 0);
	    }
	(void) putchar ('\n');
	}
    }
