#ifndef lint
static char Rcs_Id[] =
    "$Id: ispell.c,v 1.161 2005/05/25 14:13:53 geoff Exp $";
#endif

#define MAIN

/*
 * ispell.c - An interactive spelling corrector.
 *
 * Copyright (c), 1983, by Pace Willisson
 *
 * Copyright 1992, 1993, 1999, 2001, 2005, Geoff Kuenning, Claremont, CA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All modifications to the source code must be clearly marked as
 *    such.  Binary redistributions based on modified source code
 *    must be clearly marked as modified versions in the documentation
 *    and/or other materials provided with the distribution.
 * 4. The code that causes the 'ispell -v' command to display a prominent
 *    link to the official ispell Web site may not be removed.
 * 5. The name of Geoff Kuenning may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * $Log: ispell.c,v $
 * Revision 1.161  2005/05/25 14:13:53  geoff
 * Report the value of EXEEXT in "ispell -vv".
 *
 * Revision 1.160  2005/04/28 14:46:51  geoff
 * Open a correction log file in command mode.
 *
 * Revision 1.159  2005/04/28 00:26:06  geoff
 * Don't print the count-file suffix, since it's no longer used.
 *
 * Revision 1.158  2005/04/20 23:16:32  geoff
 * Rename some variables to make them more meaningful.
 *
 * Revision 1.157  2005/04/14 23:11:36  geoff
 * Move initckch to makedent.c.
 *
 * Revision 1.156  2005/04/14 21:25:52  geoff
 * Make DICTIONARYVAR configurable, add CHARSETVAR, and add both to ispell -vv.
 *
 * Revision 1.155  2005/04/14 15:19:37  geoff
 * Make sure all current config.X variables are represented in ispell -vv
 * (with the exception of a couple that are function macros), and clean
 * up the sorting of the printout.
 *
 * Revision 1.154  2005/04/14 14:38:23  geoff
 * Update license.  Incorporate Ed Avis's changes.  Add new config.X
 * variables to -vv output.  For backwards compatibility, always put
 * !NO8BIT in config.X output.  Add the -o switch.  Add -e5.  Be
 * intelligent about inferring the output file mode if there is an
 * external deformatter.
 *
 * Revision 1.153  2001/09/06 00:30:28  geoff
 * Many changes from Eli Zaretskii to support DJGPP compilation.
 *
 * Revision 1.152  2001/07/25 21:51:46  geoff
 * Minor license update.
 *
 * Revision 1.151  2001/07/23 20:24:03  geoff
 * Update the copyright and the license.
 *
 * Revision 1.150  2001/07/19 07:01:46  geoff
 * Fix a bug that effectively disabled the -w switch.
 *
 * Revision 1.149  2001/06/14 09:11:11  geoff
 * Use a non-conflicting macro for bzero to avoid compilation problems on
 * smarter compilers.
 *
 * Revision 1.148  2001/05/30 21:14:47  geoff
 * Invert the fcntl/mkstemp options so they will default to being used.
 *
 * Revision 1.147  2001/05/30 21:04:25  geoff
 * Add fcntl.h support (needed for the previous change), security
 * commentary, and mkstemp support.
 *
 * Revision 1.146  2001/05/30 09:38:26  geoff
 * Werner Fink's patch to get rid of a temp-file race condition.
 *
 * Revision 1.145  2001/05/07 04:47:18  geoff
 * Flush stdout after expansion, for use in pipes (Ed Avis)
 *
 * Revision 1.144  2000/08/22 10:52:25  geoff
 * Fix some compiler warnings.
 *
 * Revision 1.143  1999/01/13 01:34:21  geoff
 * Get rid of some obsolete variables in the -vv switch.
 *
 * Revision 1.142  1999/01/08  05:45:03  geoff
 * Allow shtml files to force HTML mode.
 *
 * Revision 1.141  1999/01/07  01:22:47  geoff
 * Update the copyright.
 *
 * Revision 1.140  1999/01/03  01:46:35  geoff
 * Add support for external deformatters.
 *
 * Revision 1.139  1998/07/12  20:42:18  geoff
 * Change the -i switch to -k, and make it take the name of a keyword
 * table.
 *
 * Revision 1.138  1998/07/06  06:55:16  geoff
 * Use the new general-keyword-lookup support to initialize HTML and TEX
 * tags from environment variables or defaults.
 *
 * Revision 1.137  1997/12/02  06:24:48  geoff
 * Get rid of some compile options that really shouldn't be optional.
 * Add HTML support ('i' switch).
 *
 * Revision 1.136  1995/11/08  05:09:14  geoff
 * Set "aflag" and "askverbose" (new interactive mode) if invoked without
 * filenames or mode arguments.
 *
 * Revision 1.135  1995/11/08  04:27:59  geoff
 * Improve the HTML support to interoperate better with nroff/troff, clean
 * it up, and use '-H' for the switch.
 *
 * Revision 1.134  1995/10/25  04:05:23  geoff
 * Modifications made by Gerry Tierney <gtierney@nova.ucd.ie> to allow
 * checking of html code.  Adds -h switch and checking for html files by
 * .html or .htm extension.  14th of October 1995.
 *
 * Revision 1.133  1995/10/11  04:30:29  geoff
 * Get rid of an unused variable.
 *
 * Revision 1.132  1995/08/05  23:19:36  geoff
 * If the DICTIONARY environment variable is set, derive the default
 * personal-dictionary name from it.
 *
 * Revision 1.131  1995/01/08  23:23:39  geoff
 * Support variable hashfile suffixes for DOS purposes.  Report all the
 * new configuration variables in the -vv switch.  Do some better error
 * checking for mktemp failures.  Support the rename system call.  All of
 * this is to help make DOS porting easier.
 *
 * Revision 1.130  1995/01/03  19:24:08  geoff
 * When constructing a personal-dictioary name from the hash file name,
 * don't stupidly include path directory components.
 *
 * Revision 1.129  1995/01/03  02:23:19  geoff
 * Disable the setbuf call on BSDI systems, sigh.
 *
 * Revision 1.128  1994/10/26  05:12:28  geoff
 * Include boundary characters in the list of characters to be tried in
 * corrections.
 *
 * Revision 1.127  1994/10/25  05:46:07  geoff
 * Allow the default dictionary to be specified by an environment
 * variable (DICTIONARY) as well as a switch.
 *
 * Revision 1.126  1994/09/16  03:32:34  geoff
 * Issue an error message for bad affix flags
 *
 * Revision 1.125  1994/07/28  05:11:36  geoff
 * Log message for previous revision: fix backup-file checks to correctly
 * test for exceeding MAXNAMLEN.
 *
 * Revision 1.124  1994/07/28  04:53:39  geoff
 *
 * Revision 1.123  1994/05/17  06:44:12  geoff
 * Add support for controlled compound formation and the COMPOUNDONLY
 * option to affix flags.
 *
 * Revision 1.122  1994/04/27  01:50:37  geoff
 * Print MAX_CAPS in -vv mode.
 *
 * Revision 1.121  1994/03/16  03:49:10  geoff
 * Fix -vv to display the value of NO_STDLIB_H.
 *
 * Revision 1.120  1994/03/15  06:24:28  geoff
 * Allow the -t, -n, and -T switches to override each other, as follows:
 * if no switches are given, the deformatter and string characters are
 * chosen based on the file suffix.  If only -t/-n are given, the
 * deformatter is forced but string characters come from the file suffix.
 * If only -T is given, the deformatter is chosen based on the value
 * given in the -T switch.  Finally, if both -T and -t/-n are given,
 * string characters are controlled by -T and the deformatter by -t/-n.
 *
 * Revision 1.119  1994/03/15  05:58:07  geoff
 * Get rid of a gcc warning
 *
 * Revision 1.118  1994/03/15  05:30:37  geoff
 * Get rid of an unused-variable complaint by proper ifdeffing
 *
 * Revision 1.117  1994/03/12  21:26:48  geoff
 * Correctly limit maximum name lengths for files that have directory paths
 * included.  Also don't use a wired-in 256 for the size of the backup file
 * name.
 *
 * Revision 1.116  1994/02/07  08:10:44  geoff
 * Print GENERATE_LIBRARY_PROTOS in the -vv switch.
 *
 * Revision 1.115  1994/01/26  07:44:47  geoff
 * Make yacc configurable through local.h.
 *
 * Revision 1.114  1994/01/25  07:11:44  geoff
 * Get rid of all old RCS log lines in preparation for the 3.1 release.
 *
 */

#include "config.h"
#include "ispell.h"
#include "fields.h"
#include "proto.h"
#include "msgs.h"
#include "version.h"
#include <ctype.h>
#ifndef NO_FCNTL_H
#include <fcntl.h>
#endif /* NO_FCNTL_H */
#include <sys/stat.h>
#undef putchar

static void	usage P ((void));
int		main P ((int argc, char * argv[]));
static void	dofile P ((char * filename));
static FILE *	setupdefmt P ((char * filename, struct stat * statbuf));
static void	update_file P ((char * filename, struct stat * statbuf));
static void	expandmode P ((int printorig));
char *		last_slash P ((char * file));

static char     Cmdname[32];
static char *	LibDict = NULL;		/* Pointer to name of $(LIBDIR)/dict */

static void usage ()
    {
    (void) fprintf (stderr, ISPELL_C_USAGE1, Cmdname);
    (void) fprintf (stderr, ISPELL_C_USAGE2, Cmdname);
    (void) fprintf (stderr, ISPELL_C_USAGE3, Cmdname);
    (void) fprintf (stderr, ISPELL_C_USAGE4, Cmdname);
    (void) fprintf (stderr, ISPELL_C_USAGE5, Cmdname);
    (void) fprintf (stderr, ISPELL_C_USAGE6, Cmdname);
    (void) fprintf (stderr, ISPELL_C_USAGE7, Cmdname);
    givehelp (0);
    exit (1);
    }

#ifdef WIN32
#include <fcntl.h>
#include <io.h>
char libdir[128];
/* for Japanese support (-j) --ak 2001/01/23 */
int jptrue = 0;
/* ----------------------------------------- */
#endif

int main (argc, argv)
    int		argc;
    char *	argv[];
    {
    char *	p = NULL;
    char	libdir[MAXPATHLEN];
#ifdef WIN32
    char *      pp;
    char *      ap;
    char *      ppp;
#endif
    char *	cpd;
    field_t *	extra_args;	/* Extra arguments from OPTIONVAR */
    char **	versionp;
    char *	wchars = NULL;
    char *	preftype = NULL;
    static char	libdictname[sizeof DEFHASH];
    char	logfilename[MAXPATHLEN];
    static char	outbuf[BUFSIZ];
    int		argno;
    int		arglen;

#ifdef WIN32
    strcpy(argv[0], "ispell");
#endif

    strcpy(Cmdname, argv[0]);
    Trynum = 0;

#ifdef WIN32
    ppp = getenv (LIBRARYVAR);
    if (ppp != NULL)
       p = (char *)_strdup(ppp);
#else
    p = getenv (LIBRARYVAR);
#endif
    if (p == NULL)
	(void) strcpy (libdir, LIBDIR);
    else
	{
#ifdef WIN32
	   for(ap=p; *ap; ap++) {
	      if(isknj(*ap)) {
		 ap++; continue;
	      }
	      if(*ap == '\\') *ap = '/';
	   }
#endif
	(void) strncpy (libdir, p, sizeof libdir);
	libdir[sizeof libdir - 1] = '\0';
	}

#ifdef WIN32
    p = NULL;
    ppp = getenv (DICTIONARYVAR);
    if (ppp)
       p = (char *)_strdup(ppp);
#else
    p = getenv (DICTIONARYVAR);
#endif
    if (p != NULL)
      {
#ifdef WIN32
        for(pp = p; *pp; pp++) {
	  if(isknj(*pp)) {
	    pp++; continue;
	  }
	  if(*pp == '\\')
	     *pp = '/';
	}
#endif
	if (last_slash (p) != NULL)
	    (void) strcpy (hashname, p);
	else
	    (void) sprintf (hashname, "%s/%s", libdir, p);
	(void) strcpy (libdictname, p);
	p = rindex (p, '.');
	if (p == NULL  ||  stricmp (p, HASHSUFFIX) != 0)
	    (void) strcat (hashname, HASHSUFFIX);
	LibDict = last_slash (libdictname);
	if (LibDict != NULL)
	    LibDict++;
	else
	    LibDict = libdictname;
	p = rindex (LibDict, '.');
	if (p != NULL)
	    *p = '\0';
      }
   else
	(void) sprintf (hashname, "%s/%s", libdir, DEFHASH);

    cpd = NULL;

    /*
    ** If any options were given in OPTIONVAR, prepend them to the
    ** command-line arguments.  We prepend so that the command-line
    ** arguments can override those from the environment.
    */
    p = getenv (OPTIONVAR);
    if (p != NULL)
	{
	char **		newargv;

	extra_args = fieldmake (p, 0, " \t",
	  FLD_RUNS | FLD_SNGLQUOTES | FLD_DBLQUOTES | FLD_SHQUOTES
	    | FLD_STRIPQUOTES | FLD_BACKSLASH,
	  0);
	if (extra_args == NULL)
	    {
	    (void) fprintf (stderr, ISPELL_C_NO_OPTIONS_SPACE);
	    return 1;
	    }
	else
	    {
	    newargv =
	      (char **) calloc (argc + extra_args->nfields, sizeof (char *));
	    if (newargv == NULL)
		{
		(void) fprintf (stderr, ISPELL_C_NO_OPTIONS_SPACE);
		return 1;
		}

	    /* Copy arguments over */
	    newargv[0] = argv[0];
	    for (argno = 0;  argno < (int) extra_args->nfields;  argno++)
		newargv[argno + 1] = extra_args->fields[argno];
	    for (argc += extra_args->nfields, argno++;  argno < argc;  argno++)
		newargv[argno] = argv[argno - extra_args->nfields];
	    }

	argv = newargv;
	}

    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
	 *	^^^^^^  ^   ^^^ ^ ^^^ ^^^
	 */
	arglen = strlen (*argv);
	switch ((*argv)[1])
	    {
	    case 'v':
		if (arglen > 3)
		    usage ();
		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);
		    /*
		     * We print USG first because it's mis-set so often.
		     * All others are in alphabetical order.
		     */
#ifdef USG
		    (void) printf ("\tUSG\n");
#else /* USG */
		    (void) printf ("\t!USG (BSD)\n");
#endif /* USG */
		    (void) printf ("\tBAKEXT = \"%s\"\n", BAKEXT);
		    (void) printf ("\tBINDIR = \"%s\"\n", BINDIR);
#ifdef BOTTOMCONTEXT
		    (void) printf ("\tBOTTOMCONTEXT\n");
#else /* BOTTOMCONTEXT */
		    (void) printf ("\t!BOTTOMCONTEXT\n");
#endif /* BOTTOMCONTEXT */
		    (void) printf ("\tCC = \"%s\"\n", CC);
		    (void) printf ("\tCFLAGS = \"%s\"\n", CFLAGS);
		    (void) printf ("\tCHARSETVAR = \"%s\"\n", CHARSETVAR);
#ifdef COMMANDFORSPACE
		    (void) printf ("\tCOMMANDFORSPACE\n");
#else /* COMMANDFORSPACE */
		    (void) printf ("\t!COMMANDFORSPACE\n");
#endif /* COMMANDFORSPACE */
		    (void) printf ("\tCONTEXTPCT = %d\n", CONTEXTPCT);
#ifdef CONTEXTROUNDUP
		    (void) printf ("\tCONTEXTROUNDUP\n");
#else /* CONTEXTROUNDUP */
		    (void) printf ("\t!CONTEXTROUNDUP\n");
#endif /* CONTEXTROUNDUP */
		    (void) printf ("\tDEFAULT_FILE_MODE = 0%3.3o\n",
		      DEFAULT_FILE_MODE);
		    (void) printf ("\tDEFHASH = \"%s\"\n", DEFHASH);
		    (void) printf ("\tDEFINCSTR = \"%s\"\n", DEFINCSTR);
		    (void) printf ("\tDEFLANG = \"%s\"\n", DEFLANG);
		    (void) printf ("\tDEFNOBACKUPFLAG = %d\n",
		      DEFNOBACKUPFLAG);
		    (void) printf ("\tDEFPAFF = \"%s\"\n", DEFPAFF);
		    (void) printf ("\tDEFPDICT = \"%s\"\n", DEFPDICT);
		    (void) printf ("\tDEFTEXFLAG = %d\n", DEFTEXFLAG);
		    (void) printf ("\tDICTIONARYVAR = \"%s\"\n",
		      DICTIONARYVAR);
		    (void) printf ("\tEGREPCMD = \"%s\"\n", EGREPCMD);
#ifdef EQUAL_COLUMNS
		    (void) printf ("\tEQUAL_COLUMNS\n");
#else /* EQUAL_COLUMNS */
		    (void) printf ("\t!EQUAL_COLUMNS\n");
#endif /* EQUAL_COLUMNS */
		    (void) printf ("\tEXEEXT = \"%s\"\n", EXEEXT);
#ifdef GENERATE_LIBRARY_PROTOS
		    (void) printf ("\tGENERATE_LIBRARY_PROTOS\n");
#else /* GENERATE_LIBRARY_PROTOS */
		    (void) printf ("\t!GENERATE_LIBRARY_PROTOS\n");
#endif /* GENERATE_LIBRARY_PROTOS */
		    (void) printf ("\tHASHSUFFIX = \"%s\"\n", HASHSUFFIX);
#ifdef HAS_RENAME
		    (void) printf ("\tHAS_RENAME\n");
#else /* HAS_RENAME */
		    (void) printf ("\t!HAS_RENAME\n");
#endif /* HAS_RENAME */
		    (void) printf ("\tHOME = \"%s\"\n", HOME);
		    (void) printf ("\tHTMLCHECK = \"%s\"\n", HTMLCHECK);
		    (void) printf ("\tHTMLCHECKVAR = \"%s\"\n", HTMLCHECKVAR);
		    (void) printf ("\tHTMLIGNORE = \"%s\"\n", HTMLIGNORE);
		    (void) printf ("\tHTMLIGNOREVAR = \"%s\"\n",
		      HTMLIGNOREVAR);
#ifdef IGNOREBIB
		    (void) printf ("\tIGNOREBIB\n");
#else /* IGNOREBIB */
		    (void) printf ("\t!IGNOREBIB\n");
#endif /* IGNOREBIB */
		    (void) printf ("\tINCSTRVAR = \"%s\"\n", INCSTRVAR);
		    (void) printf ("\tINPUTWORDLEN = %d\n", INPUTWORDLEN);
		    (void) printf ("\tINSTALL = \"%s\"\n", INSTALL);
		    (void) printf ("\tLANGUAGES = \"%s\"\n", LANGUAGES);
		    (void) printf ("\tLIBDIR = \"%s\"\n", libdir);
		    (void) printf ("\tLIBES = \"%s\"\n", LIBES);
		    (void) printf ("\tLIBRARYVAR = \"%s\"\n", LIBRARYVAR);
		    (void) printf ("\tLINK = \"%s\"\n",  LINK);
		    (void) printf ("\tLINT = \"%s\"\n", LINT);
		    (void) printf ("\tLINTFLAGS = \"%s\"\n", LINTFLAGS);
#ifndef REGEX_LOOKUP
		    (void) printf ("\tLOOK = \"%s\"\n", LOOK);
#endif /* REGEX_LOOKUP */
		    (void) printf ("\tLOOK_XREF = \"%s\"\n", LOOK_XREF);
		    (void) printf ("\tMAKE_SORTTMP = \"%s\"\n", MAKE_SORTTMP);
		    (void) printf ("\tMALLOC_INCREMENT = %d\n",
		      MALLOC_INCREMENT);
		    (void) printf ("\tMAN1DIR = \"%s\"\n", MAN1DIR);
		    (void) printf ("\tMAN1EXT = \"%s\"\n", MAN1EXT);
		    (void) printf ("\tMAN45DIR = \"%s\"\n", MAN45DIR);
		    (void) printf ("\tMAN45EXT = \"%s\"\n", MAN45EXT);
		    (void) printf ("\tMAN45SECT = \"%s\"\n", MAN45SECT);
		    (void) printf ("\tMASKBITS = %d\n", MASKBITS);
		    (void) printf ("\tMASKTYPE = \"%s\"\n", MASKTYPE_STRING);
		    (void) printf ("\tMASKTYPE_WIDTH = %d\n", MASKTYPE_WIDTH);
		    (void) printf ("\tMASTERHASH = \"%s\"\n", MASTERHASH);
		    (void) printf ("\tMAXAFFIXLEN = %d\n", MAXAFFIXLEN);
#ifdef MAXBASENAMELEN
		    (void) printf ("\tMAXBASENAMELEN = %d\n", MAXBASENAMELEN);
#endif
		    (void) printf ("\tMAXCONTEXT = %d\n", MAXCONTEXT);
#ifdef MAXEXTLEN
		    (void) printf ("\tMAXEXTLEN = %d\n", MAXEXTLEN);
#endif
		    (void) printf ("\tMAXINCLUDEFILES = %d\n",
		      MAXINCLUDEFILES);
		    (void) printf ("\tMAXNAMLEN = %d\n", MAXNAMLEN);
		    (void) printf ("\tMAXPATHLEN = %d\n", MAXPATHLEN);
		    (void) printf ("\tMAXPCT = %d\n", MAXPCT);
		    (void) printf ("\tMAXSEARCH = %d\n", MAXSEARCH);
		    (void) printf ("\tMAXSTRINGCHARLEN = %d\n",
		      MAXSTRINGCHARLEN);
		    (void) printf ("\tMAXSTRINGCHARS = %d\n", MAXSTRINGCHARS);
		    (void) printf ("\tMAX_CAPS = %d\n", MAX_CAPS);
		    (void) printf ("\tMAX_HITS = %d\n", MAX_HITS);
		    (void) printf ("\tMAX_SCREEN_SIZE = %d\n",
		      MAX_SCREEN_SIZE);
		    (void) printf ("\tMINCONTEXT = %d\n", MINCONTEXT);
#ifdef MINIMENU
		    (void) printf ("\tMINIMENU\n");
#else /* MINIMENU */
		    (void) printf ("\t!MINIMENU\n");
#endif /* MINIMENU */
		    (void) printf ("\tMINWORD = %d\n", MINWORD);
#ifdef MSDOS
		    (void) printf ("\tMSDOS\n");
#else /* MSDOS */
		    (void) printf ("\t!MSDOS\n");
#endif /* MSDOS */
		    (void) printf ("\tMSDOS_BINARY_OPEN = 0x%x\n",
		      (unsigned int) MSDOS_BINARY_OPEN);
		    (void) printf ("\tMSGLANG = \"%s\"\n", MSGLANG);
		    /*
		     * NO8BIT is an obsolete option, but some people depend
		     * on it being in the ispell -vv output.
		     */
		    (void) printf ("\t!NO8BIT (8BIT)\n");
#ifdef NO_FCNTL_H
		    (void) printf ("\tNO_FCNTL_H\n");
#else /* NO_FCNTL_H */
		    (void) printf ("\t!NO_FCNTL_H (FCNTL_H)\n");
#endif /* NO_FCNTL_H */
#ifdef NO_MKSTEMP
		    (void) printf ("\tNO_MKSTEMP\n");
#else /* NO_MKSTEMP */
		    (void) printf ("\t!NO_MKSTEMP (MKSTEMP)\n");
#endif /* NO_STDLIB_H */
#ifdef NO_STDLIB_H
		    (void) printf ("\tNO_STDLIB_H\n");
#else /* NO_STDLIB_H */
		    (void) printf ("\t!NO_STDLIB_H (STDLIB_H)\n");
#endif /* NO_STDLIB_H */
		    (void) printf ("\tNRSPECIAL = \"%s\"\n", NRSPECIAL);
		    (void) printf ("\tOLDPAFF = \"%s\"\n", OLDPAFF);
		    (void) printf ("\tOLDPDICT = \"%s\"\n", OLDPDICT);
		    (void) printf ("\tOPTIONVAR = \"%s\"\n", OPTIONVAR);
#ifdef PDICTHOME
		    (void) printf ("\tPDICTHOME = \"%s\"\n", PDICTHOME);
#else /* PDICTHOME */
		    (void) printf ("\tPDICTHOME = (undefined)\n");
#endif /* PDICTHOME */
		    (void) printf ("\tPDICTVAR = \"%s\"\n", PDICTVAR);
#ifdef PIECEMEAL_HASH_WRITES
		    (void) printf ("\tPIECEMEAL_HASH_WRITES\n");
#else /* PIECEMEAL_HASH_WRITES */
		    (void) printf ("\t!PIECEMEAL_HASH_WRITES\n");
#endif /* PIECEMEAL_HASH_WRITES */
		    (void) printf ("\tPOUNDBANG = \"%s\"\n", POUNDBANG);
#ifdef REGEX_LOOKUP
		    (void) printf ("\tREGEX_LOOKUP\n");
#else /* REGEX_LOOKUP */
		    (void) printf ("\t!REGEX_LOOKUP\n");
#endif /* REGEX_LOOKUP */
		    (void) printf ("\tREGLIB = \"%s\"\n", REGLIB);
		    (void) printf ("\tR_OK = %d\n", R_OK);
		    (void) printf ("\tSIGNAL_TYPE = \"%s\"\n",
		      SIGNAL_TYPE_STRING);
		    (void) printf ("\tSORTPERSONAL = %d\n", SORTPERSONAL);
		    (void) printf ("\tSORTTMP = \"%s\"\n", SORTTMP);
		    (void) printf ("\tSPELL_XREF = \"%s\"\n", SPELL_XREF);
		    (void) printf ("\tSTATSUFFIX = \"%s\"\n", STATSUFFIX);
		    (void) printf ("\tTEMPNAME = \"%s\"\n", TEMPNAME);
		    (void) printf ("\tTERMLIB = \"%s\"\n", TERMLIB);
#if TERM_MODE == CBREAK
		    (void) printf ("\tTERM_MODE = CBREAK\n");
#else /* TERM_MODE */
		    (void) printf ("\tTERM_MODE = RAW\n");
#endif /* TERM_MODE */
		    (void) printf ("\tTEXSKIP1 = \"%s\"\n", TEXSKIP1);
		    (void) printf ("\tTEXSKIP1VAR = \"%s\"\n", TEXSKIP1VAR);
		    (void) printf ("\tTEXSKIP2 = \"%s\"\n", TEXSKIP2);
		    (void) printf ("\tTEXSKIP2VAR = \"%s\"\n", TEXSKIP2VAR);
		    (void) printf ("\tTEXSPECIAL = \"%s\"\n", TEXSPECIAL);
		    (void) printf ("\tTIB_XREF = \"%s\"\n", TIB_XREF);
#ifdef TRUNCATEBAK
		    (void) printf ("\tTRUNCATEBAK\n");
#else /* TRUNCATEBAK */
		    (void) printf ("\t!TRUNCATEBAK\n");
#endif /* TRUNCATEBAK */
#ifdef USESH
		    (void) printf ("\tUSESH\n");
#else /* USESH */
		    (void) printf ("\t!USESH\n");
#endif /* USESH */
		    (void) printf ("\tWORDS = \"%s\"\n", WORDS);
		    (void) printf ("\tW_OK = %d\n", W_OK);
		    (void) printf ("\tYACC = \"%s\"\n", YACC);
		    }
		exit (0);
		break;
	    case 'n':
		if (arglen > 2)
		    usage ();
		tflag = DEFORMAT_NROFF;	/* nroff/troff mode */
		deftflag = DEFORMAT_NROFF;
		if (preftype == NULL)
		    preftype = "nroff";
		break;
	    case 't':			/* TeX mode */
		if (arglen > 2)
		    usage ();
		tflag = DEFORMAT_TEX;
		deftflag = DEFORMAT_TEX;
		if (preftype == NULL)
		    preftype = "tex";
		break;
	    case 'H':			/* HTML mode */
		if (arglen > 2)
		    usage ();
		tflag = DEFORMAT_SGML;	/* non-TeX mode */
		deftflag = DEFORMAT_SGML;
		if (preftype == NULL)
		    preftype = "sgml";
		break;
	    case 'o':                   /* Ordinary text mode */
		if (arglen > 2)
		    usage ();
		tflag = DEFORMAT_NONE;
		deftflag = DEFORMAT_NONE;
		if (preftype == NULL)
		    preftype = "plain";
		break;
	    case 'k':			/* Set keyword tables */
		p = (*argv) + 2;
		argv++;
		argc--;
		if (argc == 0)
		    usage ();
		if (strcmp (p, "texskip1") == 0)
		    {
		    if (init_keyword_table (*argv, TEXSKIP1VAR, TEXSKIP1,
		      0, &texskip1list))
			usage ();
		    }
		else if (strcmp (p, "texskip2") == 0)
		    {
		    if (init_keyword_table (*argv, TEXSKIP2VAR, TEXSKIP2,
		      0, &texskip2list))
			usage ();
		    }
		else if (strcmp (p, "htmlignore") == 0)
		    {
		    if (init_keyword_table (*argv, HTMLIGNOREVAR, HTMLIGNORE,
		      1, &htmlignorelist))
			usage ();
		    }
		else if (strcmp (p, "htmlcheck") == 0)
		    {
		    if (init_keyword_table (*argv, HTMLCHECKVAR, HTMLCHECK,
		      1, &htmlchecklist))
			usage ();
		    }
		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 'F':			/* Set external deformatting program */
		p = (*argv) + 2;
		if (*p == '\0')
		    {
		    argv++;
		    argc--;
		    if (argc == 0)
			usage ();
		    p = *argv;
		    }
		defmtpgm = p;
		tflag = DEFORMAT_NONE;
		deftflag = DEFORMAT_NONE;
		if (preftype == NULL)
		    preftype = "plain";
		break;
	    case 'A':
		if (arglen > 2)
		    usage ();
		incfileflag = 1;
		aflag = 1;
		break;
	    case 'a':
		if (arglen > 2)
		    usage ();
		aflag++;
		break;
	    case 'D':
		if (arglen > 2)
		    usage ();
		dumpflag++;
		nodictflag++;
		break;
	    case 'e':
		if (arglen > 3)
		    usage ();
		eflag = 1;
		if ((*argv)[2] == 'e')
		    eflag = 2;
		else if ((*argv)[2] >= '1'  &&  (*argv)[2] <= '5')
		    eflag = (*argv)[2] - '0';
		else if ((*argv)[2] != '\0')
		    usage ();
		nodictflag++;
		break;
	    case 'c':
		if (arglen > 2)
		    usage ();
		cflag++;
		lflag++;
		nodictflag++;
		break;
	    case 'b':
		if (arglen > 2)
		    usage ();
		xflag = 0;		/* Keep a backup file */
		break;
	    case 'x':
		if (arglen > 2)
		    usage ();
		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':
		if (arglen > 2)
		    usage ();
		lflag++;
		break;
#ifndef USG
	    case 's':
		if (arglen > 2)
		    usage ();
		sflag++;
		break;
#endif
	    case 'S':
		if (arglen > 2)
		    usage ();
		sortit = 0;
		break;
	    case 'B':		/* -B:  report missing blanks */
		if (arglen > 2)
		    usage ();
		compoundflag = COMPOUND_NEVER;
		break;
	    case 'C':		/* -C:  compound words are acceptable */
		if (arglen > 2)
		    usage ();
		compoundflag = COMPOUND_ANYTIME;
		break;
	    case 'P':		/* -P:  don't gen non-dict poss's */
		if (arglen > 2)
		    usage ();
		tryhardflag = 0;
		break;
	    case 'm':		/* -m:  make all poss affix combos */
		if (arglen > 2)
		    usage ();
		tryhardflag = 1;
		break;
	    case 'N':		/* -N:  suppress minimenu */
		if (arglen > 2)
		    usage ();
		minimenusize = 0;
		break;
	    case 'M':		/* -M:  force minimenu */
		if (arglen > 2)
		    usage ();
		minimenusize = 2;
		break;
	    case 'p':
		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;
		    }
#ifdef WIN32
		for(pp=p; *pp; pp++) {
		   if(isknj(*pp)) {
		      pp++; continue;
		   }
		   if(*pp == '\\') *pp = '/';
		}
#endif
		if (last_slash (p) != NULL)
		    (void) strcpy (hashname, p);
		else
		    (void) sprintf (hashname, "%s/%s", libdir, p);
		if (cpd == NULL  &&  *p != '\0')
		    LibDict = p;
		p = rindex (p, '.');
		if (p != NULL  &&  stricmp (p, HASHSUFFIX) == 0)
		    *p = '\0';	/* Don't want ext. in LibDict */
		else
		    (void) strcat (hashname, HASHSUFFIX);
		if (LibDict != NULL)
		    {
		    p = last_slash (LibDict);
		    if (p != NULL)
			LibDict = p + 1;
		    }
		break;
	    case 'V':		/* Display 8-bit characters as M-xxx */
		if (arglen > 2)
		    usage ();
		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;
/* for Japanese support (-j) --ak 2001/01/23 */
	    case 'j':
		jptrue = 1;
		break;
/* ----------------------------------------- */
	    default:
		usage ();
	      }
	argv++;
	argc--;
      }

    if (!argc  &&  !lflag  &&  !aflag   &&  !eflag  &&  !dumpflag)
	{
	if (argc != 0)
	    usage ();
	else
	    {
	    aflag = 1;
	    askverbose = 1;
	    }
	}

    /*
     * 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], R_OK) >= 0)
	    break;
	}
    if (argno >= argc  &&  !lflag  &&  !aflag  &&  !eflag  &&  !dumpflag)
	{
	(void) fprintf (stderr,
	  argc == 1 ? ISPELL_C_NO_FILE : ISPELL_C_NO_FILES);
	exit (1);
	}
    if (linit () < 0)
	exit (1);

    if (preftype == NULL)
	preftype = getenv (CHARSETVAR);

    if (preftype != NULL)
	{
	prefstringchar =
	  findfiletype (preftype, 1, deftflag < 0 ? &deftflag : (int *) NULL);
	if (prefstringchar < 0
	  &&  stricmp (preftype, "plain") != 0
	  &&  stricmp (preftype, "tex") != 0
	  &&  stricmp (preftype, "nroff") != 0
	  &&  stricmp (preftype, "sgml") != 0)
	    {
	    (void) fprintf (stderr, ISPELL_C_BAD_TYPE, preftype);
	    exit (1);
	    }
	}
    if (prefstringchar < 0)
	defstringgroup = 0;
    else
	defstringgroup = prefstringchar;

    if (compoundflag < 0)
	compoundflag = hashheader.compoundflag;
    if (tryhardflag < 0)
	tryhardflag = hashheader.defhardflag;

    /*
     * Set up the various tables of keywords to be treated specially.
     *
     * TeX/LaTeX mode:
     */
    (void) init_keyword_table (NULL, TEXSKIP1VAR, TEXSKIP1, 0, &texskip1list);
    (void) init_keyword_table (NULL, TEXSKIP2VAR, TEXSKIP2, 0, &texskip2list);
    /*
     * HTML mode:
     */
    (void) init_keyword_table (NULL, HTMLIGNOREVAR, HTMLIGNORE, 1,
      &htmlignorelist);
    (void) init_keyword_table (NULL, HTMLCHECKVAR, HTMLCHECK, 1,
      &htmlchecklist);

    initckch(wchars);

    if (LibDict == NULL)	
	{
	(void) strcpy (libdictname, DEFHASH);
	LibDict = libdictname;
	p = rindex (libdictname, '.');
	if (p != NULL  &&  stricmp (p, HASHSUFFIX) == 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);
	exit (0);
	}
    else if (dumpflag)
	{
	dumpmode ();
	exit (0);
	}

#ifndef __bsdi__
    setbuf (stdout, outbuf);
#endif /* __bsdi__ */
    if (lflag)
	{
	infile = setupdefmt(NULL, NULL);
	outfile = stdout;
	checkfile ();
	exit (0);
	}

    /*
     * If there is a log directory, open a log file.  If the open
     * fails, we just won't log.
     */
    (void) sprintf (logfilename, "%s/%s/%s",
      getenv ("HOME") == NULL ? "" : getenv ("HOME"),
      DEFLOGDIR, LibDict);
#ifdef WIN32
#if DISTPUB
    logfile = fopen (logfilename, "a");
#else
    logfile = fopen (logfilename, "ab");
#endif
#else
    logfile = fopen (logfilename, "a");
#endif /* WIN32 */

    terminit ();

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

    done (0);
    /* NOTREACHED */
    return 0;
    }

static void dofile (filename)
    char *	filename;
    {
    struct stat	statbuf;
    char *	cp;
    int		outfd;			/* Used in opening temp file */
					/* ..might produce not-used warnings */
    char *      pp;
    char *      ap;
    char *      ppp;
    int         len;

    currentfile = filename;

    /* Guess a deformatter based on the file extension */
    tflag = deftflag;
    if (tflag < 0)
	{
	tflag = DEFORMAT_NONE;		/* Default to none */
	cp = rindex (filename, '.');
	if (cp != NULL)
	    {
	    if (stricmp (cp, ".ms") == 0  ||  stricmp (cp, ".mm") == 0
	      ||  stricmp (cp, ".me") == 0  ||  stricmp (cp, ".man") == 0
	      ||  isdigit(*cp))
		tflag = DEFORMAT_NROFF;
	    else if (stricmp (cp, ".tex") == 0)
		tflag = DEFORMAT_TEX;
	    else if (stricmp (cp, ".html") == 0  ||  stricmp (cp, ".htm") == 0
	      ||  stricmp (cp, ".shtml") == 0)
		tflag = DEFORMAT_SGML;
	    }
	}
    if (prefstringchar < 0)
	{
	defstringgroup =
	  findfiletype (filename, 0, deftflag < 0 ? &tflag : (int *) NULL);
	if (defstringgroup < 0)
	    defstringgroup = 0;
	}

    if ((infile = setupdefmt (filename, &statbuf)) == NULL)
	{
	(void) fprintf (stderr, CANT_OPEN, filename, MAYBE_CR (stderr));
	(void) sleep ((unsigned) 2);
	return;
	}

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

    /*
     * Security notes: TEMPNAME must be less than MAXPATHLEN - 1.  If
     * the system has O_EXCL but not mkstemp, the temporary file will
     * be opened securely as in the manner of mkstemp (which
     * unfortunately isn't available anywhere).  In other words, don't
     * worry about the security of this hunk of code.
     */
#ifndef WIN32
    if (last_slash (TEMPNAME) != NULL)
	(void) strcpy (tempfile, TEMPNAME);
    else
	{
	char *tmp = getenv ("TMPDIR");
	int   lastchar;

	if (tmp == NULL)
	    tmp = getenv ("TEMP");
	if (tmp == NULL)
	    tmp = getenv ("TMP");
	if (tmp == NULL)
#ifdef P_tmpdir
	    tmp = P_tmpdir;
#else
	    tmp = "/tmp";
#endif
	lastchar = tmp[strlen (tmp) - 1];
	(void) sprintf (tempfile, "%s%s%s", tmp,
			IS_SLASH (lastchar) ? "" : "/",
			TEMPNAME);
	}
#else
    ppp = getenv("TEMP");
    if(!ppp) ppp = getenv("TMP");
    if(!ppp) ppp = getenv("TMPDIR");
    if(!ppp) {
      fprintf(stderr, "Define TEMP.\n");
      exit(100);
    }
    pp = (char *)_strdup(ppp);
    for(ap=pp; *ap; ap++) {
      if(isknj(*ap)) {
	ap++; continue;
      }
      if(*ap == '\\') *ap = '/';
    }
    (void)strcpy(tempfile, pp);
    len = strlen(tempfile);
    if(tempfile[len-1] != '/') strcat(tempfile, "/");
    (void) strcat(tempfile, TEMPNAME);
#endif /* WIN32 */

#if DISTPUB
#define WRTXT "w"
#else
#define WRTXT "wb"
#endif
#ifdef NO_MKSTEMP
    if (mktemp (tempfile) == NULL  ||  tempfile[0] == '\0'
#ifdef O_EXCL
      ||  (outfd = open (tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0
      ||  (outfile = fdopen (outfd, WRTXT)) == NULL)
#else /* O_EXCL */
      ||  (outfile = fopen (tempfile, WRTXT)) == NULL)
#endif /* O_EXCL */
#else /* NO_MKSTEMP */
    if ((outfd = mkstemp (tempfile)) < 0
      ||  (outfile = fdopen (outfd, WRTXT)) == NULL)
#endif /* NO_MKSTEMP */
	{
	(void) fprintf (stderr, CANT_CREATE,
	  (tempfile == NULL  ||  tempfile[0] == '\0')
	    ? "temporary file" : tempfile,
	  MAYBE_CR (stderr));
	(void) sleep ((unsigned) 2);
	return;
	}
#ifndef MSDOS
    /*
    ** This is usually a no-op on MS-DOS, but with file-sharing
    ** installed, it was reported to produce empty spelled files!
    ** Apparently, the file-sharing module would close the file when
    ** `chmod' is called.
    */
    (void) chmod (tempfile, statbuf.st_mode);
#endif

    quit = 0;
    changes = 0;

    checkfile ();

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

    if (!cflag)
	treeoutput ();

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

/*
 * Set up to externally deformat a file.
 *
 * If no deformatter is provided, we return either standard input (if
 * filename is NULL) or the result of opening the specified file.
 *
 * If a deformatter is provided, but no filename is provided, we
 * assume that the input comes from stdin, and we set up the
 * deformatter to filter that descriptor, and return stdin as our
 * result.  In such a case, there is no dual input: we will read the
 * filtered data via the deformatter and ignore the unfiltered
 * version.
 *
 * If there is a deformatter and we are given a filename, then we set
 * up the deformatter to read the file from its own standard input and
 * write to its standard output.  In addition, we set up "sourcefile"
 * so that it can read the unfiltered data directly from the input
 * file.
 *
 * If "statbuf" is non-NULL, it will be filled in with the result of
 * stat-ing the file.  If the stat fails, statbuf->st_mode will be set
 * to a reasonable default value.  The contents of statbuf are
 * undefined if the input file or the filter cannot be opened.
 *
 * Note that external deformatters do not work with named pipes as input.
 */
static FILE * setupdefmt (filename, statbuf)
    char *		filename;	/* File to open, if non-NULL */
    struct stat *	statbuf;	/* Buffer to hold file status */
    {
    FILE*		filteredfile;	/* Access to the filtered file */
    int			inputfd;	/* Fd for access to file to open */
    int			savedstdin;	/* File descriptor saving stdin */

    sourcefile = NULL;
    if (defmtpgm == NULL)
	{
	/*
	 * There is no deformatter.  Return either stdin or an open file.
	 */
	if (filename == NULL)
	    filteredfile = stdin;
	else
	    filteredfile = fopen (filename, "r");
	if (statbuf != NULL  &&  filteredfile != NULL
	  &&  fstat (fileno (filteredfile), statbuf) == -1)
	    statbuf->st_mode = DEFAULT_FILE_MODE;
	return filteredfile;
	}
    else if (filename == NULL)
	{
	/*
	 * We are reading from standard input.  Switch over to a
	 * filtered version of stdin.
	 */
	if (statbuf != NULL  &&  fstat (fileno (stdin), statbuf) == -1)
	    statbuf->st_mode = DEFAULT_FILE_MODE;
	return popen (defmtpgm, "r");
	}
    else
	{
	/*
	 * This is the tricky case.  We need to get the deformatter to
	 * read from the input file and filter it to us.  Doing so
	 * requires several steps:
	 *
	 *  1.	Preserve file descriptor 0 by duplicating it.
	 *  2.	Close file descriptor 0, and reopen it on the file to be
	 *	filtered.
	 *  3.	Open a pipe to the deformat program.
	 *  4.	Restore file descriptor 0.
	 *
	 * Because we do all of this without ever letting the stdio
	 * library know that we've been mucking around with file
	 * descriptors, we won't bother its access to stdin.
	 */
	sourcefile = fopen (filename, "r");
	if (sourcefile == NULL)
	    return NULL;
	if (statbuf != NULL  &&  fstat (fileno (sourcefile), statbuf) == -1)
	    statbuf->st_mode = DEFAULT_FILE_MODE;
	savedstdin = dup (0);
	inputfd = open (filename, 0);
	if (inputfd < 0)
	    return NULL;		/* Failed to open the file */
	else if (dup2 (inputfd, 0) != 0)
	    {
	    (void) fprintf (stderr, ISPELL_C_UNEXPECTED_FD, filename,
	      MAYBE_CR (stderr));
	    exit (1);
	    }
	filteredfile = popen (defmtpgm, "r");
	if (dup2 (savedstdin, 0) != 0)
	    {
	    (void) fprintf (stderr, ISPELL_C_UNEXPECTED_FD, filename,
	      MAYBE_CR (stderr));
	    exit (1);
	    }
	close (savedstdin);
	return filteredfile;
	}
    }

static void update_file (filename, statbuf)
    char *		filename;
    struct stat *	statbuf;
    {
    char		bakfile[MAXPATHLEN];
    int			c;
    char *		pathtail;
    char *              pp;

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

#ifdef TRUNCATEBAK
    (void) strncpy (bakfile, filename, sizeof bakfile - 1);
    bakfile[sizeof bakfile - 1] = '\0';
#else /* TRUNCATEBAK */
    (void) sprintf (bakfile, "%.*s%s", (int) (sizeof bakfile - sizeof BAKEXT),
      filename, BAKEXT);
#endif /* TRUNCATEBAK */
#ifdef WIN32
    for(pp=bakfile; *pp; pp++) {
      if(isknj(*pp)) {
	pp++; continue;
      }
      if(*pp == '\\') *pp = '/';
    }
#endif
    pathtail = last_slash (bakfile);
    if (pathtail == NULL)
	pathtail = bakfile;
    else
	pathtail++;
#ifdef TRUNCATEBAK
    if (strcmp(BAKEXT, filename + strlen(filename) - sizeof BAKEXT + 1) != 0)
	{
	if (strlen (pathtail) > MAXNAMLEN - sizeof BAKEXT + 1)
	    pathtail[MAXNAMLEN - sizeof BAKEXT + 1] = '\0';
	(void) strcat (pathtail, BAKEXT);
	}
#endif /* TRUNCATEBAK */


#ifdef MSDOS
    if (pathconf (filename, _PC_NAME_MAX) <= MAXBASENAMELEN + MAXEXTLEN + 1)
	{
	/*
	** Excessive characters beyond 8+3 will be truncated by the
	** OS.  Ensure the backup extension won't be truncated, and
	** that we don't create an invalid filename (e.g., more than
	** one dot).  Allow use of BAKEXT without a leading dot (such
	** as "~").
	*/
	char *last_dot = rindex (pathtail, '.');

	/*
	** If no dot in backup filename, make BAKEXT be the extension.
	** This ensures we don't truncate the name more than necessary.
	*/
	if (last_dot == NULL  &&  strlen (pathtail) > MAXBASENAMELEN)
	    {
	    pathtail[MAXBASENAMELEN] = '.';
	    /*
	    ** BAKEXT cannot include a dot here (or we would have
	    ** found it above, and last_dot would not be NULL).
	    */
	    strcpy (pathtail + MAXBASENAMELEN + 1, BAKEXT);
	    }
	else if (last_dot != NULL)
	    {
	    char *p = pathtail;
	    size_t ext_len = strlen (last_dot);

	    /* Convert all dots but the last to underscores. */
	    while (p < last_dot && *p)
		{
		if (*p == '.')
		    *p = '_';
		p++;
		}

	    /* Make sure we preserve as much of BAKEXT as we can. */
	    if (ext_len > MAXEXTLEN && ext_len > sizeof (BAKEXT) - 1)
		strcpy (MAXEXTLEN <= sizeof (BAKEXT) - 1
		    ? last_dot + 1
		    : last_dot + MAXEXTLEN - sizeof (BAKEXT) + 1,
		  BAKEXT);
	    }
	}
#endif /* MSDOS */

    if (strncmp (filename, bakfile, pathtail - bakfile + MAXNAMLEN) != 0)
	(void) unlink (bakfile);	/* unlink so we can write a new one. */
#ifdef HAS_RENAME
    (void) rename (filename, bakfile);
#else /* HAS_RENAME */
    if (link (filename, bakfile) == 0)
	(void) unlink (filename);
#endif /* HAS_RENAME */

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

#ifndef MSDOS
    /*
    ** This is usually a no-op on MS-DOS, but with file-sharing
    ** installed, it was reported to produce empty spelled files!
    ** Apparently, the file-sharing module would close the file when
    ** `chmod' is called.
    */
    (void) chmod (filename, statbuf->st_mode);
#endif

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

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

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

static void expandmode (option)
    int			option;		/* How to print: */
					/* 1 = expansions only */
					/* 2 = original line + expansions */
					/* 3 = original paired w/ expansions */
					/* 4 = add length ratio */
                                        /* 5 = root + flags used, expansion */
    {
    char		buf[BUFSIZ];
    int			explength;	/* Total length of all expansions */
    register char *	flagp;		/* Pointer to next flag char */
    ichar_t		ibuf[BUFSIZ];
    MASKTYPE		mask[MASKSIZE];
    char		origbuf[BUFSIZ]; /* Original contents of buf */
    char		ratiobuf[20];	/* Expansion/root length ratio */
    int			rootlength;	/* Length of root word */
    register int	temp;

    while (xgets (buf, sizeof buf, stdin) != NULL)
	{
	rootlength = strlen (buf);
	if (buf[rootlength - 1] == '\n')
	  buf[--rootlength] = '\0';
	(void) strcpy (origbuf, buf);
	if ((flagp = index (buf, hashheader.flagmarker)) != NULL)
	    {
	    rootlength = flagp - buf;
	    *flagp++ = '\0';
	    }
	if (option == 2  ||  option == 3  ||  option == 4)
	    (void) printf ("%s ", origbuf);
	if (flagp != NULL)
	    {
	    if (flagp - buf > INPUTWORDLEN)
		buf[INPUTWORDLEN] = '\0';
	    }
	else
	    {
	    if ((int) 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')
		{
		temp = CHARTOBIT ((unsigned char) *flagp);
		if (temp >= 0  &&  temp <= LARGESTFLAG)
		    SETMASKBIT (mask, temp);
		else
		    (void) fprintf (stderr, BAD_FLAG, MAYBE_CR (stderr),
		      (unsigned char) *flagp, MAYBE_CR (stderr));
		flagp++;
		/* Accept old-format dicts with extra slashes */
		if (*flagp == hashheader.flagmarker)
		    flagp++;
		}
	    if (strtoichar (ibuf, (unsigned char *) buf, sizeof ibuf, 1))
		(void) fprintf (stderr, WORD_TOO_LONG (buf));
	    explength = expand_pre ((unsigned char *) origbuf, ibuf, mask,
	      option, (unsigned char *) "");
	    explength += expand_suf ((unsigned char *) origbuf, ibuf, mask, 0,
	      option, (unsigned char *) "");
	    explength += rootlength;
	    if (option == 4)
		{
		(void) sprintf (ratiobuf, " %f",
		  (double) explength / (double) rootlength);
		(void) fputs (ratiobuf, stdout);
		(void) expand_pre ((unsigned char *) origbuf, ibuf, mask, 3,
		  (unsigned char *) ratiobuf);
		(void) expand_suf ((unsigned char *) origbuf, ibuf, mask, 0, 3,
		  (unsigned char *) ratiobuf);
		}
	    }
	(void) putchar ('\n');
	(void) fflush (stdout);
	}
    }

/*
** A trivial wrapper for rindex (file '/') on Unix, but
** saves a lot of ifdef-ing on MS-DOS.
*/
char * last_slash (file)
    char *		file;		/* String to search for / or \ */
    {
#ifdef MSDOS
    char *		backslash;	/* Position of last backslash */
#endif /* MSDOS */
    char *		slash;		/* Position of last slash */

    slash = rindex (file, '/');

#ifdef MSDOS
    /*
    ** We can have both forward- and backslashes; find the rightmost
    ** one of either type.
    */
    backslash = rindex (file, '\\');
    if (slash == NULL  ||  (backslash != NULL  &&  backslash > slash))
	slash = backslash;

    /*
    ** If there is no backslash, but the first two characters are a
    ** letter and a colon, the basename begins right after them.
    */
    if (slash == NULL  &&  file[0] != '\0'  &&  file[1] == ':')
	slash = file + 1;
#endif

    return slash;
    }
