#define KBD_DEL		595
#define KBD_INS		594
#define KBD_HOME	583
#define KBD_END		591
#define KBD_LEFT	587
#define KBD_RIGHT	589

#define KBD_N_DEL	339
#define KBD_N_INS	338
#define KBD_N_HOME	327
#define KBD_N_END	335
#define KBD_N_LEFT	331
#define KBD_N_RIGHT	333


#include <ctype.h>
#include "ispell.h"
#include "msgs.h"
#include "version.h"

Local ins_flag = True;
Local void	inserttoken (char * buf, char * start, char * tok,
							  char ** curchar);
Global void	givehelp (int interactive);
Global void	checkfile (void);
Global void	correct (char * ctok, int ctokl, ichar_t * itok, int itokl,
							      char ** curchar);
Local void	show_line (char * line, char * invstart, int invlen);
Local int	show_char (char ** cp, int linew, int output, int maxw);
Local int	line_size (char * buf, char * bufend);
Local int	posscmp (char * a, char * b);
Global void	makepossibilities (ichar_t * word);
#ifndef NO_CAPITALIZATION_SUPPORT
Local void	wrongcapital (ichar_t * word);
#endif /* NO_CAPITALIZATION_SUPPORT */
Local void	wrongletter (ichar_t * word);
Local void	extraletter (ichar_t * word);
Local void	missingletter (ichar_t * word);
Local void	missingspace (ichar_t * word);
Global int	compoundgood (ichar_t * word, int pfxopts);
Local void	transposedletter (ichar_t * word);
Local void	tryveryhard (ichar_t * word);
Local int	ins_cap (ichar_t * word, ichar_t * pattern);
Local int	save_cap (ichar_t * word, ichar_t * pattern,
		      ichar_t savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN]);
Local char *	getline (char * buf, int bufsize, int first_pos);
Global void	askmode (void);
Global void	copyout (char ** cc, int cnt);
Local void	lookharder (char * string);
#ifdef REGEX_LOOKUP
Local void	regex_dict_lookup (char * cmd, char * grepstr);
#endif /* REGEX_LOOKUP */

Public void 
givehelp (int interactive)
                         	/* NZ for interactive-mode help */
{
#ifdef COMMANDFORSPACE
    char ch;
#endif
    register FILE *helpout;	/* File to write help to */

    if (interactive)
	{
	erase ();
	helpout = stdout;
	}
    else
	helpout = stderr;

    (void) fprintf (helpout, CORR_C_HELP_1);
    (void) fprintf (helpout, CORR_C_HELP_2);
    (void) fprintf (helpout, CORR_C_HELP_3);
    (void) fprintf (helpout, CORR_C_HELP_4);
    (void) fprintf (helpout, CORR_C_HELP_5);
    (void) fprintf (helpout, CORR_C_HELP_6);
    (void) fprintf (helpout, CORR_C_HELP_7);
    (void) fprintf (helpout, CORR_C_HELP_8);
    (void) fprintf (helpout, CORR_C_HELP_9);

    (void) fprintf (helpout, CORR_C_HELP_COMMANDS);

    (void) fprintf (helpout, CORR_C_HELP_R_CMD);
    (void) fprintf (helpout, CORR_C_HELP_BLANK);
    (void) fprintf (helpout, CORR_C_HELP_A_CMD);
    (void) fprintf (helpout, CORR_C_HELP_I_CMD);
    (void) fprintf (helpout, CORR_C_HELP_U_CMD);
    (void) fprintf (helpout, CORR_C_HELP_0_CMD);
    (void) fprintf (helpout, CORR_C_HELP_L_CMD);
    (void) fprintf (helpout, CORR_C_HELP_X_CMD);
    (void) fprintf (helpout, CORR_C_HELP_Q_CMD);
    (void) fprintf (helpout, CORR_C_HELP_BANG);
    (void) fprintf (helpout, CORR_C_HELP_REDRAW);
    (void) fprintf (helpout, CORR_C_HELP_SUSPEND);
    (void) fprintf (helpout, CORR_C_HELP_HELP);

    if (interactive)
	{
	(void) fprintf (helpout, "\r\n");
	move (li - 1, 0);  /* bottom line, no matter what the screen size is */
	(void) fprintf (helpout, CORR_C_HELP_TYPE_SPACE);
	(void) fflush (helpout);
#ifdef COMMANDFORSPACE
	ch = GETKEYSTROKE ();
	if (ch != ' ' && ch != '\n' && ch != '\r')
	    (void) ungetc (ch, stdin);
#else
	while (GETKEYSTROKE () != ' ')
	    ;
#endif
	}
}

Public void 
checkfile (void)
{
    int		bufno;
    int		bufsize;
    int		ch;

    for (bufno = 0;  bufno < contextsize;  bufno++)
	contextbufs[bufno][0] = '\0';

    for (  ;  ;  )
	{
	for (bufno = contextsize;  --bufno > 0;  )
	    (void) strcpy (contextbufs[bufno], contextbufs[bufno - 1]);
	if (quit)	/* quit can't be set in l mode */
	    {
	    while (fgets (contextbufs[0],
			      sizeof contextbufs[0], infile) != NULL)
		(void) fputs (contextbufs[0], outfile);
	    break;
	    }
	/*
	 * Only read in enough characters to fill half this buffer so that any
	 * corrections we make are not likely to cause an overflow.
	 */
	if (fgets (contextbufs[0], (sizeof contextbufs[0]) / 2, infile)
								  == NULL)
	    break;
	/*
	 * If we didn't read to end-of-line, we may have ended the
	 * buffer in the middle of a word.  So keep reading until we
	 * see some sort of character that can't possibly be part of a
	 * word. (or until the buffer is full, which fortunately isn't
	 * all that likely).
	 */
	bufsize = strlen (contextbufs[0]);
	if (bufsize == (sizeof contextbufs[0]) / 2 - 1)
	    {
	    ch = (unsigned char) contextbufs[0][bufsize - 1];
	    while (bufsize < sizeof contextbufs[0] - 1
	      &&  (iswordch ((ichar_t) ch)  ||  isboundarych ((ichar_t) ch)
						      ||  isstringstart (ch)))
		{
		ch = getc (infile);
		if (ch == EOF)
		    break;
		contextbufs[0][bufsize++] = (char) ch;
		contextbufs[0][bufsize] = '\0';
		}
	    }
	checkline (outfile);
	}
}

Public void 
correct(
    char *		ctok,
    int			ctokl,
    ichar_t *		itok,
    int			itokl,
    char **		curchar)
{
    register int	c;
    register int	i, iii;
    int			col_ht;
    int			ncols;
    char *		start_l2;
    char *		begintoken;

    begintoken = *curchar - strlen (ctok);

    if (icharlen (itok) <= minword)
	return;			/* Accept very short words */

checkagain:
    if (good (itok, 0, 0, 0, 0)  ||  compoundgood (itok, 0))
	return;

    erase ();
    (void) printf ("    %s", ctok);
    if (currentfile)
	(void) printf (CORR_C_FILE_LABEL, currentfile);
    if (readonly)
	(void) printf (" %s", CORR_C_READONLY);
    (void) printf ("\r\n\r\n");

    makepossibilities (itok);

    /*
     * Make sure we have enough room on the screen to hold the
     * possibilities.  Reduce the list if necessary.  co / (maxposslen + 8)
     * is the maximum number of columns that will fit.  col_ht is the
     * height of the columns.  The constant 4 allows 2 lines (1 blank) at
     * the top of the screen, plus another blank line between the
     * columns and the context, plus a final blank line at the bottom
     * of the screen for command entry (R, L, etc).
     */
    col_ht = li - contextsize - 4 - minimenusize;
    ncols = co / (maxposslen + 8);
    if (pcount > ncols * col_ht)
	pcount = ncols * col_ht;

#ifdef EQUAL_COLUMNS
    /*
     * Equalize the column sizes.  The last column will be short.
     */
    col_ht = (pcount + ncols - 1) / ncols;
#endif

    for (i = 0; i < pcount; i++)
	{
#ifdef BOTTOMCONTEXT
	move (2 + (i % col_ht), (maxposslen + 8) * (i / col_ht));
#else /* BOTTOMCONTEXT */
	move (3 + contextsize + (i % col_ht), (maxposslen + 8) * (i / col_ht));
#endif /* BOTTOMCONTEXT */
	if (i >= easypossibilities)
	    (void) printf ("??: %s", possibilities[i]);
	else if (easypossibilities >= 10  &&  i < 10)
	    (void) printf ("0%d: %s", i, possibilities[i]);
	else
	    (void) printf ("%2d: %s", i, possibilities[i]);
	}

#ifdef BOTTOMCONTEXT
    move (li - contextsize - 1 - minimenusize, 0);
#else /* BOTTOMCONTEXT */
    move (2, 0);
#endif /* BOTTOMCONTEXT */
    for (i = contextsize;  --i > 0;  )
	show_line (contextbufs[i], contextbufs[i], 0);

    start_l2 = contextbufs[0];
    if (line_size (contextbufs[0], *curchar) > co - (sg << 1) - 1)
	{
	start_l2 = begintoken - (co / 2);
	while (start_l2 < begintoken)
	    {
	    i = line_size (start_l2, *curchar) + 1;
	    if (i + (sg << 1) <= co)
		break;
	    start_l2 += i - co;
	    }
	if (start_l2 > begintoken)
	    start_l2 = begintoken;
	if (start_l2 < contextbufs[0])
	    start_l2 = contextbufs[0];
	}
    show_line (start_l2, begintoken, (int) strlen (ctok));

    if (minimenusize != 0)
	{
	move (li - 2, 0);
	(void) printf (CORR_C_MINI_MENU);
	}

    while(True)
	{
	(void) fflush (stdout);
	switch (c = (GETKEYSTROKE () & S_NOPARITY))
	    {
	    case 'Z' & 037:
		stop ();
		erase ();
		goto checkagain;
	    case ' ':
		erase ();
		(void) fflush (stdout);
		return;
	    case 'q': 
	    case 'Q':
		if (changes)
		    {
		    (void) printf (CORR_C_CONFIRM_QUIT);
		    (void) fflush (stdout);
		    c = (GETKEYSTROKE () & S_NOPARITY);
		    }
		else
		    c = 'y';
		if (c == 'y' || c == 'Y')
		    {
		    erase ();
		    (void) fflush (stdout);
		    if (tempfile[0] != '\0')
			fclose (outfile); /* so `done' may unlink it safely */
		    done (0);
		    }
		goto checkagain;
	    case 'i': 
	    case 'I':
		treeinsert (ichartosstr (strtosichar (ctok, 0), 1),
						 ICHARTOSSTR_SIZE, 1);
		erase ();
		(void) fflush (stdout);
		changes = 1;
		return;
	    case 'u': 
	    case 'U':
		itok = strtosichar (ctok, 0);
		lowcase (itok);
		treeinsert (ichartosstr (itok, 1), ICHARTOSSTR_SIZE, 1);
		erase ();
		(void) fflush (stdout);
		changes = 1;
		return;
	    case 'a': 
	    case 'A':
		treeinsert (ichartosstr (strtosichar (ctok, 0), 1),
		  ICHARTOSSTR_SIZE, 0);
		erase ();
		(void) fflush (stdout);
		return;
	    case 'L' & 037:
		goto checkagain;
	    case '?':
		givehelp (1);
		goto checkagain;
	    case '!':
		{
		char	buf[200];

		move (li - 1, 0);
		(void) putchar ('!');
		if (getline (buf, sizeof (buf), 1) == NULL)
		    {
		    (void) putchar (7);
		    erase ();
		    (void) fflush (stdout);
		    goto checkagain;
		    }
		(void) printf ("\r\n");
		(void) fflush (stdout);
#ifdef	USESH
		shescape (buf);
#else
		(void) shellescape (buf);
#endif
		erase ();
		goto checkagain;
		}
	    case 'r': 
	    case 'R':
		move (li - 1, 0);
		if (readonly)
		    {
		    (void) putchar (7);
		    (void) printf ("%s ", CORR_C_READONLY);
		    }
		(void) printf (CORR_C_REPLACE_WITH);
		(void) printf ("%s", ctok);
		move (li - 1, strlen(CORR_C_REPLACE_WITH));
		if (getline (ctok, ctokl, strlen(CORR_C_REPLACE_WITH)) 
								    == NULL)
		    {
		    (void) putchar (7);
		    /* Put it back */
		    (void) ichartostr (ctok, itok, ctokl, 0);
		    }
		else
		    {
		    inserttoken (contextbufs[0], begintoken, ctok, curchar);
		    if (strtoichar (itok, ctok, itokl, 0))
			{
			(void) putchar (7);
			(void) printf (WORD_TOO_LONG (ctok));
			}
		    changes = 1;
		    }
		erase ();
		if (icharlen (itok) <= minword)
		    return;		/* Accept very short replacements */
		goto checkagain;
	    case '0': 
	    case '1': 
	    case '2': 
	    case '3': 
	    case '4':
	    case '5': 
	    case '6': 
	    case '7': 
	    case '8': 
	    case '9':
		i = c - '0';
		if (easypossibilities >= 10)
		    {
		    c = GETKEYSTROKE () & S_NOPARITY;
		    if (c >= '0'  &&  c <= '9')
			i = i * 10 + c - '0';
		    else if (c != '\r'  &&  c != '\n')
			{
			(void) putchar (7);
			break;
			}
		    }
		if (i < easypossibilities)
		    {
		    (void) strcpy (ctok, possibilities[i]);
		    changes = 1;
		    inserttoken (contextbufs[0],
			begintoken, ctok, curchar);
		    erase ();
		    if (readonly)
			{
			move (li - 1, 0);
			(void) putchar (7);
			(void) printf ("%s", CORR_C_READONLY);
			(void) fflush (stdout);
			(void) sleep ((unsigned) 2);
			}
		    return;
		    }
		(void) putchar (7);
		break;
	    case '\r':	/* This makes typing \n after single digits */
	    case '\n':	/* ..less obnoxious */
		break;
	    case 'l': 
	    case 'L':
		{
		char	buf[100];
		move (li - 1, 0);
		(void) printf (CORR_C_LOOKUP_PROMPT);
		if (getline (buf, sizeof (buf), 
					strlen (CORR_C_LOOKUP_PROMPT)) == NULL)
		    {
		    (void) putchar (7);
		    erase ();
		    goto checkagain;
		    }
		(void) printf ("\r\n");
		(void) fflush (stdout);
		lookharder (buf);
		erase ();
		goto checkagain;
		}
	    case 'x': 
	    case 'X':
		quit = 1;
		erase ();
		(void) fflush (stdout);
		return;
	    default:
		(void) putchar (7);
		break;
	    }
	}
    }

static void show_line (line, invstart, invlen)
    char *		line;
    register char *	invstart;
    register int	invlen;
    {
    register int	width;

    width = invlen ? (sg << 1) : 0;
    while (line < invstart  &&  width < co - 1)
	width += show_char (&line, width, 1, invstart - line);
    if (invlen)
	{
	inverse ();
	invstart += invlen;
	while (line < invstart  &&  width < co - 1)
	    width += show_char (&line, width, 1, invstart - line);
	normal ();
	}
    while (*line  &&  width < co - 1)
	width += show_char (&line, width, 1, 0);
    (void) printf ("\r\n");
    }

static int show_char (cp, linew, output, maxw)
    register char **	cp;
    int			linew;
    int			output;		/* NZ to actually do output */
    int			maxw;		/* NZ to limit width shown */
    {
    register int	ch;
    register int	i;
    int			len;
    ichar_t		ichar;
    register int	width;

    ch = (unsigned char) **cp;
    if (l1_isstringch (*cp, len, 0))
	ichar = SET_SIZE + laststringch;
    else
	ichar = chartoichar (ch);
    if (!vflag  &&  iswordch (ichar)  &&  len == 1)
	{
	if (output)
	    (void) putchar (ch);
	(*cp)++;
	return 1;
	}
    if (ch == '\t')
	{
	if (output)
	    (void) putchar ('\t');
	(*cp)++;
	return 8 - (linew & 0x07);
	}
    /*
     * Character is non-printing, or it's ISO and vflag is set.  Display
     * it in "cat -v" form.  For string characters, display every element
     * separately in that form.
     */
    width = 0;
    if (maxw != 0  &&  len > maxw)
	len = maxw;			/* Don't show too much */
    for (i = 0;  i < len;  i++)
	{
	ch = (unsigned char) *(*cp)++;
	if (ch > '\177')
	    {
	    if (output)
		{
		(void) putchar ('M');
		(void) putchar ('-');
		}
	    width += 2;
	    ch &= 0x7f;
	    }
	if (ch < ' '  ||  ch == '\177')
	    {
	    if (output)
		{
		(void) putchar ('^');
		if (ch == '\177')
		    (void) putchar ('?');
		else
		    (void) putchar (ch + 'A' - '\001');
		}
	    width += 2;
	    }
	else
	    {
	    if (output)
		(void) putchar (ch);
	    width += 1;
	    }
	}
    return width;
    }

Local int 
line_size (
    char *		buf,
    register char *	bufend)
{
    register int	width;

    for (width = 0;  buf < bufend  &&  *buf != '\0';  )
	width += show_char (&buf, width, 0, bufend - buf);
    return width;
}

Local void 
inserttoken(
    char *		buf,
    char *		start,
    register char *	tok,
    char **		curchar)
{
    char		copy[BUFSIZ];
    register char *	p;
    register char *	q;
    char *		ew;

    (void) strcpy (copy, buf);

    for (p = buf, q = copy; p != start; p++, q++)
	*p = *q;
    q += *curchar - start;
    ew = skipoverword (tok);
    while (tok < ew)
	*p++ = *tok++;
    *curchar = p;
    if (*tok)
	{

	/*
	** The token changed to two words.  Split it up and save the
	** second one for later.
	*/

	*p++ = *tok;
	*tok++ = '\0';
	while (*tok)
	    *p++ = *tok++;
	}
    while ((*p++ = *q++) != '\0')
	;
}

Local int
posscmp (char *a, char *b)
{

    return casecmp (a, b, 0);
}


Public void 
makepossibilities ( register ichar_t *	word)
{
    register int	i;

    for (i = 0; i < MAXPOSSIBLE; i++)
	possibilities[i][0] = 0;
    pcount = 0;
    maxposslen = 0;
    easypossibilities = 0;

#ifndef NO_CAPITALIZATION_SUPPORT
    wrongcapital (word);
#endif

/* 
 * according to Pollock and Zamora, CACM April 1984 (V. 27, No. 4),
 * page 363, the correct order for this is:
 * OMISSION = TRANSPOSITION > INSERTION > SUBSTITUTION
 * thus, it was exactly backwards in the old version. -- PWP
 */

    if (pcount < MAXPOSSIBLE)
	missingletter (word);		/* omission */
    if (pcount < MAXPOSSIBLE)
	transposedletter (word);	/* transposition */
    if (pcount < MAXPOSSIBLE)
	extraletter (word);		/* insertion */
    if (pcount < MAXPOSSIBLE)
	wrongletter (word);		/* substitution */

    if ((compoundflag != COMPOUND_ANYTIME)  &&  pcount < MAXPOSSIBLE)
	missingspace (word);	/* two words */

    easypossibilities = pcount;
    if (easypossibilities == 0  ||  tryhardflag)
	tryveryhard (word);

    if ((sortit  ||  (pcount > easypossibilities))  &&  pcount)
	{
	if (easypossibilities > 0  &&  sortit)
	    qsort ((char *) possibilities,
	      (unsigned) easypossibilities,
	      sizeof (possibilities[0]),
	      (int (*) (const void *, const void *)) posscmp);
	if (pcount > easypossibilities)
	    qsort ((char *) &possibilities[easypossibilities][0],
	      (unsigned) (pcount - easypossibilities),
	      sizeof (possibilities[0]),
	      (int (*) (const void *, const void *)) posscmp);
	}
}

#ifndef NO_CAPITALIZATION_SUPPORT
Local void 
wrongcapital (register ichar_t *	word)
{
    ichar_t		newword[INPUTWORDLEN + MAXAFFIXLEN];

    /*
    ** When the third parameter to "good" is nonzero, it ignores
    ** case.  If the word matches this way, "ins_cap" will recapitalize
    ** it correctly.
    */
    if (good (word, 0, 1, 0, 0))
	{
	(void) icharcpy (newword, word);
	upcase (newword);
	(void) ins_cap (newword, word);
	}
}
#endif

Local void 
wrongletter(register ichar_t *	word)
{
    register int	i;
    register int	j;
    register int	n;
    ichar_t		savechar;
    ichar_t		newword[INPUTWORDLEN + MAXAFFIXLEN];

    n = icharlen (word);
    (void) icharcpy (newword, word);
#ifndef NO_CAPITALIZATION_SUPPORT
    upcase (newword);
#endif

    for (i = 0; i < n; i++)
	{
	savechar = newword[i];
	for (j=0; j < Trynum; ++j)
	    {
	    if (Try[j] == savechar)
		continue;
	    else if (isboundarych (Try[j])  &&  (i == 0  ||  i == n - 1))
		continue;
	    newword[i] = Try[j];
	    if (good (newword, 0, 1, 0, 0))
		{
		if (ins_cap (newword, word) < 0)
		    return;
		}
	    }
	newword[i] = savechar;
	}
}

Local void 
extraletter (register ichar_t *	word)
{
    ichar_t		newword[INPUTWORDLEN + MAXAFFIXLEN];
    register ichar_t *	p;
    register ichar_t *	r;

    if (icharlen (word) < 2)
	return;

    (void) icharcpy (newword, word + 1);
    for (p = word, r = newword;  *p != 0;  )
	{
	if (good (newword, 0, 1, 0, 0))
	    {
	    if (ins_cap (newword, word) < 0)
		return;
	    }
	*r++ = *p++;
	}
}

Local void 
missingletter (ichar_t *		word)
{
    ichar_t		newword[INPUTWORDLEN + MAXAFFIXLEN + 1];
    register ichar_t *	p;
    register ichar_t *	r;
    register int	i;

    (void) icharcpy (newword + 1, word);
    for (p = word, r = newword;  *p != 0;  )
	{
	for (i = 0;  i < Trynum;  i++)
	    {
	    if (isboundarych (Try[i])  &&  r == newword)
		continue;
	    *r = Try[i];
	    if (good (newword, 0, 1, 0, 0))
		{
		if (ins_cap (newword, word) < 0)
		    return;
		}
	    }
	*r++ = *p++;
	}
    for (i = 0;  i < Trynum;  i++)
	{
	if (isboundarych (Try[i]))
	    continue;
	*r = Try[i];
	if (good (newword, 0, 1, 0, 0))
	    {
	    if (ins_cap (newword, word) < 0)
		return;
	    }
	}
}

Local void 
missingspace( ichar_t *		word)
{
    ichar_t		firsthalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
    int			firstno;	/* Index into first */
    ichar_t *		firstp;		/* Ptr into current firsthalf word */
    ichar_t		newword[INPUTWORDLEN + MAXAFFIXLEN + 1];
    int			nfirsthalf;	/* No. words saved in 1st half */
    int			nsecondhalf;	/* No. words saved in 2nd half */
    register ichar_t *	p;
    ichar_t		secondhalf[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];
    int			secondno;	/* Index into second */

    /*
    ** We don't do words of length less than 3;  this keeps us from
    ** splitting all two-letter words into two single letters.  We
    ** also don't do maximum-length words, since adding the space
    ** would exceed the size of the "possibilities" array.
    */
    nfirsthalf = icharlen (word);
    if (nfirsthalf < 3  ||  nfirsthalf >= INPUTWORDLEN + MAXAFFIXLEN - 1)
	return;
    (void) icharcpy (newword + 1, word);
    for (p = newword + 1;  p[1] != '\0';  p++)
	{
	p[-1] = *p;
	*p = '\0';
	if (good (newword, 0, 1, 0, 0))
	    {
	    /*
	     * Save_cap must be called before good() is called on the
	     * second half, because it uses state left around by
	     * good().  This is unfortunate because it wastes a bit of
	     * time, but I don't think it's a significant performance
	     * problem.
	     */
	    nfirsthalf = save_cap (newword, word, firsthalf);
	    if (good (p + 1, 0, 1, 0, 0))
		{
		nsecondhalf = save_cap (p + 1, p + 1, secondhalf);
		for (firstno = 0;  firstno < nfirsthalf;  firstno++)
		    {
		    firstp = &firsthalf[firstno][p - newword];
		    for (secondno = 0;  secondno < nsecondhalf;  secondno++)
			{
			*firstp = ' ';
			(void) icharcpy (firstp + 1, secondhalf[secondno]);
			if (insert (firsthalf[firstno]) < 0)
			    return;
			*firstp = '-';
			if (insert (firsthalf[firstno]) < 0)
			    return;
			}
		    }
		}
	    }
	}
}

Public int 
compoundgood (
    ichar_t *		word,
    int			pfxopts)	/* Options to apply to prefixes */
{
    ichar_t		newword[INPUTWORDLEN + MAXAFFIXLEN];
    register ichar_t *	p;
    register ichar_t	savech;
    long		secondcap;	/* Capitalization of 2nd half */

    /*
    ** If compoundflag is COMPOUND_NEVER, compound words are never ok.
    */
    if (compoundflag == COMPOUND_NEVER)
	return 0;
    /*
    ** Test for a possible compound word (for languages like German that
    ** form lots of compounds).
    **
    ** This is similar to missingspace, except we quit on the first hit,
    ** and we won't allow either member of the compound to be a single
    ** letter.
    **
    ** We don't do words of length less than 2 * compoundmin, since
    ** both halves must at least compoundmin letters.
    */
    if (icharlen (word) < 2 * hashheader.compoundmin)
	return 0;
    (void) icharcpy (newword, word);
    p = newword + hashheader.compoundmin;
    for (  ;  p[hashheader.compoundmin - 1] != 0;  p++)
	{
	savech = *p;
	*p = 0;
	if (good (newword, 0, 0, pfxopts, FF_COMPOUNDONLY))
	    {
	    *p = savech;
	    if (good (p, 0, 1, FF_COMPOUNDONLY, 0)
	      ||  compoundgood (p, FF_COMPOUNDONLY))
		{
		secondcap = whatcap (p);
		switch (whatcap (newword))
		    {
		    case ANYCASE:
		    case CAPITALIZED:
		    case FOLLOWCASE:	/* Followcase can have l.c. suffix */
			return secondcap == ANYCASE;
		    case ALLCAPS:
			return secondcap == ALLCAPS;
		    }
		}
	    }
	else
	    *p = savech;
	}
    return 0;
}

Local void 
transposedletter(register ichar_t *	word)
{
    ichar_t		newword[INPUTWORDLEN + MAXAFFIXLEN];
    register ichar_t *	p;
    register ichar_t	temp;

    (void) icharcpy (newword, word);
    for (p = newword;  p[1] != 0;  p++)
	{
	temp = *p;
	*p = p[1];
	p[1] = temp;
	if (good (newword, 0, 1, 0, 0))
	    {
	    if (ins_cap (newword, word) < 0)
		return;
	    }
	temp = *p;
	*p = p[1];
	p[1] = temp;
	}
}

Local void 
tryveryhard( ichar_t *		word)
{
    (void) good (word, 1, 0, 0, 0);
}

/* Insert one or more correctly capitalized versions of word */
Local int 
ins_cap(
    ichar_t *		word,
    ichar_t *		pattern)
{
    int			i;		/* Index into savearea */
    int			nsaved;		/* No. of words saved */
    ichar_t		savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN];

    nsaved = save_cap (word, pattern, savearea);
    for (i = 0;  i < nsaved;  i++)
	{
	if (insert (savearea[i]) < 0)
	    return -1;
	}
    return 0;
}

/* Save one or more correctly capitalized versions of word */
Local int 
save_cap (
    ichar_t *		word,		/* Word to save */
    ichar_t *		pattern,	/* Prototype capitalization pattern */
    ichar_t		savearea[MAX_CAPS][INPUTWORDLEN + MAXAFFIXLEN])
					/* Room to save words */
{
    int			hitno;		/* Index into hits array */
    int			nsaved;		/* Number of words saved */
    int			preadd;		/* No. chars added to front of root */
    int			prestrip;	/* No. chars stripped from front */
    int			sufadd;		/* No. chars added to back of root */
    int			sufstrip;	/* No. chars stripped from back */

    if (*word == 0)
	return 0;

    for (hitno = numhits, nsaved = 0;  --hitno >= 0  &&  nsaved < MAX_CAPS;  )
	{
	if (hits[hitno].prefix)
	    {
	    prestrip = hits[hitno].prefix->stripl;
	    preadd = hits[hitno].prefix->affl;
	    }
	else
	    prestrip = preadd = 0;
	if (hits[hitno].suffix)
	    {
	    sufstrip = hits[hitno].suffix->stripl;
	    sufadd = hits[hitno].suffix->affl;
	    }
	else
	    sufadd = sufstrip = 0;
	save_root_cap (word, pattern, prestrip, preadd,
	    sufstrip, sufadd,
	    hits[hitno].dictent, hits[hitno].prefix, hits[hitno].suffix,
	    savearea, &nsaved);
	}
    return nsaved;
}



Local char * 
getline(
	register char *	s,
	register int	len,
	register int	first_pos)

{
    register char *	p;
    register int	c;

    p = s;

    c = strlen(s);
    memset(p + c, 0, len - c);
    while(True)
	{
	(void) fflush (stdout);
	c = GETKEYSTROKE (); /* & S_NOPARITY; WM*/
				/*
				** Don't let them overflow the buffer.
				*/
/*	    (void) printf("%d", c); */
	if (p >= s + len - 1)
	    c = '\n';
	if (c == '\\')
	    {
	    (void) putchar ('\\');
	    (void) fflush (stdout);
	    c = (GETKEYSTROKE () & S_NOPARITY);
	    backup ();
	    (void) putchar (c);
	    *p++ = (char) c;
	    }
	else if (c == ('G' & 037))
	    return (NULL);
	else if (c == '\n' || c == '\r')
	    {
	    return (s);
	    }
	else if (c == KBD_DEL || c == KBD_N_DEL)
	    {
	    if (p != s + strlen(s))
		{
		strcpy(p, p + 1);
		move (li - 1, first_pos);
		(void) printf ("%s ", s);
		move (li - 1, p - s + first_pos);
		}
	    }
	else if (c == uerasechar)
	    {
	    if (p != s)
		{
		p--;
		move (li - 1, first_pos);
		strcpy(p, p + 1);
		(void) printf ("%s ", s);
		move (li - 1, p - s + first_pos);
		}
	    }
	else if (c == KBD_INS || c == KBD_N_INS)
	    ins_flag = !ins_flag;
	else if (c == KBD_END || c == KBD_N_END)
	   {
	    move (li - 1, strlen(s) + first_pos);
	    p = s + strlen(s);;
	   }
	else if (c == KBD_HOME || c == KBD_N_HOME)
	   {
	    move (li - 1, first_pos);
	    p = s;
	   }
	else if (c == KBD_LEFT || c == KBD_N_LEFT)
	   {
	    if(*p)
		(void) putchar (*p);
	     else
		(void) putchar (' ');
	    if(p != s)
	       {
		p--;
		move (li - 1, p - s + first_pos);
	       }
	   }
	else if (c == KBD_RIGHT || c == KBD_N_RIGHT)
	   {
	    if (p < s + len - 1)
	      {
	       if(*p)
		   {
		    p++;
		    move (li - 1, p - s + first_pos);
		   }
	       }
	   }
	else if (c == ukillchar)
	    {
	    while (p != s)
		{
		p--;
		backup ();
		(void) putchar (' ');
		backup ();
		}
	    memset(s, 0, len);
	    }
	else
	    {
	     if(strlen(s) < len - 1)
		{
		 if(ins_flag)
		    {
		     register char *x;

		     x = p + strlen(p);
		     while(x >= p)
			{
			 *(x + 1) = *x;
			 x--;
			}
		     *p = (char) c & S_NOPARITY;
		     move (li - 1, first_pos + p - s);
		     (void) printf ("%s", p++);
		     move (li - 1, p - s + first_pos);
		    }
		 else
		    {
		     *p++ = (char) c & S_NOPARITY;
		     (void) putchar (c & S_NOPARITY);
		    }
		}
	    }
	}
}

Public void 
askmode (void)
{
    int			bufsize;	/* Length of contextbufs[0] */
    int			ch;		/* Next character read from input */
    register char *	cp1;
    register char *	cp2;
    ichar_t *		itok;		/* Ichar version of current word */
    int			hadnl;		/* NZ if \n was at end of line */

    if (fflag)
	{
	if (freopen (askfilename, "w", stdout) == NULL)
	    {
	    (void) fprintf (stderr, CANT_CREATE, askfilename);
	    exit (1);
	    }
	}

    (void) printf ("%s\n", Version_ID[0]);

    contextoffset = 0;
    while (True)
	{
	(void) fflush (stdout);
	/*
	 * Only read in enough characters to fill half this buffer so that any
	 * corrections we make are not likely to cause an overflow.
	 */
	if (contextoffset == 0)
	    {
	    if (xgets (contextbufs[0], (sizeof contextbufs[0]) / 2, stdin)
								      == NULL)
		break;
	    }
	else
	    {
	    if (fgets (contextbufs[0], (sizeof contextbufs[0]) / 2, stdin)
	      == NULL)
		break;
	    }
	/*
	 * If we didn't read to end-of-line, we may have ended the
	 * buffer in the middle of a word.  So keep reading until we
	 * see some sort of character that can't possibly be part of a
	 * word. (or until the buffer is full, which fortunately isn't
	 * all that likely).
	 */
	bufsize = strlen (contextbufs[0]);
	if (contextbufs[0][bufsize - 1] == '\n')
	    {
	    hadnl = 1;
	    contextbufs[0][--bufsize] = '\0';
	    }
	else
	    hadnl = 0;
	if (bufsize == (sizeof contextbufs[0]) / 2 - 1)
	    {
	    ch = (unsigned char) contextbufs[0][bufsize - 1];
	    while (bufsize < sizeof contextbufs[0] - 1
	      &&  (iswordch ((ichar_t) ch)  ||  isboundarych ((ichar_t) ch)
	      ||  isstringstart (ch)))
		{
		ch = getc (stdin);
		if (ch == EOF)
		    break;
		contextbufs[0][bufsize++] = (char) ch;
		contextbufs[0][bufsize] = '\0';
		}
	    }
	/*
	** *line is like `i', @line is like `a', &line is like 'u'
	** `#' is like `Q' (writes personal dictionary)
	** `+' sets tflag, `-' clears tflag
	** `!' sets terse mode, `%' clears terse
	** `~' followed by a filename sets parameters according to file name
	** `^' causes rest of line to be checked after stripping 1st char
	*/
	if (contextoffset != 0)
	    checkline (stdout);
	else
	    {
	    if (contextbufs[0][0] == '*'  ||  contextbufs[0][0] == '@')
		treeinsert(ichartosstr (strtosichar (contextbufs[0] + 1, 0), 1),
		  ICHARTOSSTR_SIZE,
		  contextbufs[0][0] == '*');
	    else if (contextbufs[0][0] == '&')
		{
		itok = strtosichar (contextbufs[0] + 1, 0);
		lowcase (itok);
		treeinsert (ichartosstr (itok, 1), ICHARTOSSTR_SIZE, 1);
		}
	    else if (contextbufs[0][0] == '#')
		{
		treeoutput ();
		math_mode = 0;
		LaTeX_Mode = 'P';
		}
	    else if (contextbufs[0][0] == '!')
		terse = 1;
	    else if (contextbufs[0][0] == '%')
		terse = 0;
	    else if (contextbufs[0][0] == '-')
		{
		math_mode = 0;
		LaTeX_Mode = 'P';
		tflag = 0;
		}
	    else if (contextbufs[0][0] == '+')
		{
		math_mode = 0;
		LaTeX_Mode = 'P';
		tflag = strcmp (&contextbufs[0][1], "nroff") != 0
		  &&  strcmp (&contextbufs[0][1], "troff") != 0;
		}
	    else if (contextbufs[0][0] == '~')
		{
		defdupchar = findfiletype (&contextbufs[0][1], 1, (int *) NULL);
		if (defdupchar < 0)
		    defdupchar = 0;
		}
	    else
		{
		if (contextbufs[0][0] == '^')
		    {
		    /* Strip off leading uparrow */
		    for (cp1 = contextbufs[0], cp2 = contextbufs[0] + 1;
		      (*cp1++ = *cp2++) != '\0';
		      )
			;
		    contextoffset++;
		    bufsize--;
		    }
		checkline (stdout);
		}
	    }
	if (hadnl)
	    contextoffset = 0;
	else
	    contextoffset += bufsize;
#ifndef USG
	if (sflag)
	    {
	    stop ();
	    if (fflag)
		{
		rewind (stdout);
		(void) creat (askfilename, 0666);
		}
	    }
#endif
	}
}

/* Copy/ignore "cnt" number of characters pointed to by *cc. */
Public void 
copyout(
    register char **	cc,
    register int	cnt)
{

    while (--cnt >= 0)
	{
	if (**cc == '\0')
	    break;
	if (!aflag && !lflag)
	    (void) putc (**cc, outfile);
	(*cc)++;
	}
}

Local void 
lookharder (char *string)

{
    char		cmd[150];
    char		grepstr[100];
    register char *	g;
    register char *	s;
#ifndef REGEX_LOOKUP
    register int	wild = 0;
#ifdef LOOK
    static int		look = -1;
#endif /* LOOK */
#endif /* REGEX_LOOKUP */

    g = grepstr;
    for (s = string; *s != '\0'; s++)
	{
	if (*s == '*')
	    {
#ifndef REGEX_LOOKUP
	    wild++;
#endif /* REGEX_LOOKUP */
	    *g++ = '.';
	    *g++ = '*';
	    }
	else
	    *g++ = *s;
	}
    *g = '\0';
    if (grepstr[0])
	{
#ifdef REGEX_LOOKUP
	regex_dict_lookup (cmd, grepstr);
#else /* REGEX_LOOKUP */
#ifdef LOOK
	/* now supports automatic use of look - gms */
	if (!wild && look)
	    {
	    /* no wild and look(1) is possibly available */
	    (void) sprintf (cmd, "%s %s %s", LOOK, grepstr, WORDS);
	    if (shellescape (cmd))
		return;
	    else
		look = 0;
	    }
#endif /* LOOK */
	/* string has wild card chars or look not avail */
	if (!wild)
	    (void) strcat (grepstr, ".*");	/* work like look */
	(void) sprintf (cmd, "%s ^%s$ %s", EGREPCMD, grepstr, WORDS);
	(void) shellescape (cmd);
#endif /* REGEX_LOOKUP */
	}
}

#ifdef REGEX_LOOKUP
Local void 
regex_dict_lookup(
    char *		cmd,
    char *		grepstr)
{
    char *		rval;
    int			whence = 0;
    int			quitlookup = 0;
    int			count = 0;
    int			ch;

    (void) sprintf (cmd, "^%s$", grepstr);
    while (!quitlookup  &&  (rval = do_regex_lookup (cmd, whence)) != NULL)
	{
	whence = 1;
	(void) printf ("%s\r\n", rval);
	if ((++count % (li - 1)) == 0)
	    {
	    inverse ();
	    (void) printf (CORR_C_MORE_PROMPT);
	    normal ();
	    (void) fflush (stdout);
	    if ((ch = GETKEYSTROKE ()) == 'q'
	      ||  ch == 'Q'  ||  ch == 'x'  ||  ch == 'X' )
		 quitlookup = 1;
	    /*
	     * The following line should blank out the -- more -- even on
	     * magic-cookie terminals.
	     */
	    (void) printf (CORR_C_BLANK_MORE);
	    (void) fflush (stdout);
	    }
	}
    if(rval == NULL )
	{
	inverse ();
	(void) printf (CORR_C_END_LOOK);
	normal ();
	(void) fflush (stdout);
	(void) GETKEYSTROKE ();    
	}
}

#endif /* REGEX_LOOKUP */
