/*
 * ispell.c - An interactive spelling corrector.
 *
 * Copyright (c), 1983, by Pace Willisson
 * Permission for non-profit use is hereby granted.
 * All other rights reserved.
 *
 * 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
 * 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)
 * 1989, Tomas Rokicki, added:
 *      ispell'local'words to indicate rest of line for local words
 *      -L option to write local words when done
 *      fixed `sleep' so some error messages could be seen.
 * 1989, Loren J. Rittle, added:
 *	Fixed ISpell to use proper STDOUT/STDIN streams when used in
 *	   non-interactive modes.
 *	ARexx Server Mode, invoke with -r option ( for rexx )
 *	Thanks to Tomas Rokicki for MinRexx! and example code.
 * 1990, Loren J. Rittle, reworked:
 *	Converted to SAS/C v5.10.
 *	Added full prototype checking.
 *	Fixed more bugs, many memory related.
 *	Finally left UNIX behind completely.
 *	Got rid of alot of (what I would call) kludge code, left
 *	   over from the original port job.  See amiga.c for more details.
 * 1991, Loren J. Rittle, added:
 *	many new ARexx Server Mode commands.
 *	better WorkBench startup support, will read tool types and
 *	   process files that were selected with the ISpell icon, or
 *	   whose default tools were set to ISpell.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <libraries/dosextens.h>
#include <proto/all.h>
#include "minrexx.h"
#include "config.h"
#include "ispell.h"
#include "version.h"

struct rexxCommandList rcl[] =
{
  {"add", &rexxadd},
  {"quickadd", &rexxquickadd},
  {"check", &rexxcheck},
  {"quickcheck", &rexxquickcheck},
  {"lookup", &rexxlookup},
  {"filecheck", &rexxfilecheck},
  {"version", &rexxversion},
  {"exit", &rexxexit},
  {NULL, NULL}
};

SHORT KeepGoing = TRUE;	/* main loop control value */

char rootword[BUFSIZ];
struct dent *lastdent;

char *hashstrings;
struct hashheader hashheader;

char tempfile[200];

#define ISTEXTERM(c)   (((c) == '{') || \
			((c) == '}') || \
			((c) == '[') || \
			((c) == ']'))

int li, co;	/* lines, columns */

FILE *infile;
FILE *outfile;

char hashname[MAXPATHLEN];

/*
** we use extended character set range specifically to allow intl.
** character set characters.  We are being REALLY paranoid about indexing
** this array - explicitly cast into unsigned INTEGER, then mask
** If NO8BIT is set, text will be masked to ascii range.
*/
     static int Trynum;
#ifdef NO8BIT
     static char Try[128];
     static char Checkch[128];
#define iswordch(X) (Checkch[((unsigned)(X))&0x7f])
#else
     static char Try[256];
     static char Checkch[256];
#define iswordch(X) (Checkch[((unsigned)(X))&0xff])
#endif

     static int sortit = 1;

void givehelp (void)
{
  erase ();
  printcon (Version_ID);
  printcon ("\n");
  printcon ("Whenever a word is found that is not in the dictionary,\n");
  printcon ("it is printed on the first line of the screen.  If the dictionary\n");
  printcon ("contains any similar words, they are listed with a single digit\n");
  printcon ("next to each one.  You have the option of replacing the word\n");
  printcon ("completely, or choosing one of the suggested words.\n");
  printcon ("\n");
  printcon ("Commands are:\n\n");
  printcon ("R       Replace the misspelled word completely.\n");
  printcon ("Space   Accept the word this time only\n");
  printcon ("A       Accept the word for the rest of this file.\n");
  printcon ("I       Accept the word, and put it in your private dictionary.\n");
  printcon ("0-9     Replace with one of the suggested words.\n");
  printcon ("L       Look up words via regular expression.\n");
  printcon ("Q       Write the rest of this file, ignoring misspellings, ");
  printcon ("and start next file.\n");
  printcon ("X       Exit immediately.  Asks for confirmation.  ");
  printcon ("Leaves file unchanged.\n");
  printcon ("^L      Redraw screen.\n");
  printcon ("\n\n");
  printcon ("-- Type space to continue --");
  while (getccon () != ' ')
    ;
}

char *getline ();

int cflag = 0;
int lflag = 0;
int incfileflag = 0;
int aflag = 0;
int fflag = 0;
int sflag = 0;
int xflag = 0;
int tflag = 0;
int llflag = 0;
int rflag = 0;

#define MAXPOSSIBLE	100	/* Max no. of possibilities to generate */

char possibilities[MAXPOSSIBLE][BUFSIZ];
int pcount;
int maxposslen;

char *askfilename;

static char *Cmd;

void usage (void)
{
  fprintf (stderr,
	"Usage: %s [-dfile | -pfile | -wchars | -t | -x | -S] file .....\n",
	   Cmd);
  fprintf (stderr,
	   "       %s -r\n",
	   Cmd);
  fprintf (stderr,
	   "       %s [-dfile | -pfile | -wchars] -l\n",
	   Cmd);
  fprintf (stderr,
	   "       %s [-dfile | -pfile | -ffile | -s] {-a | -A}\n",
	   Cmd);
  fprintf (stderr, "       %s [-wchars] -c\n", Cmd);
  fprintf (stderr, "       %s -v\n", Cmd);
  exit (1);
}

static void initckch (void)
{
  register int c;

  Trynum = 0;
#ifdef NO8BIT
  for (c = 0; c < 128; ++c)
    {
#else
  for (c = 0; c < 256; ++c)
    {
#endif
      if (myalpha ((char) c))
	{
	  Checkch[c] = (char) 1;
	  if (myupper ((char) c))
	    {
	      Try[Trynum] = (char) c;
	      ++Trynum;
	    }
	}
      else
	Checkch[c] = (char) 0;
    }
}

void main (int argc, char **argv)
{
  char *p;
  char *cpd;
  char num[4];
  unsigned mask;
  static char outbuf[BUFSIZ];

  Cmd = *argv;

  initckch ();
  sprintf (hashname, "%s%s", LIBDIR, DEFHASH);

  cpd = NULL;

  argv++;
  argc--;
  while (argc && **argv == '-')
    {
      switch ((*argv)[1])
	{
	case 'r':
	  rflag++;
	  lflag++;
	  break;
	case 'v':
	  printf ("%s\n", Version_ID);
	  exit (0);
	case 't':
	  tflag++;
	  break;
	case 'A':
	  incfileflag = 1;
	  aflag = 1;
	  break;
	case 'a':
	  aflag++;
	  break;
	case 'c':
	  cflag++;
	  lflag++;
	  break;
	case 'x':
	  xflag++;
	  break;
	case 'f':
	  fflag++;
	  p = (*argv) + 2;
	  if (*p == '\0')
	    {
	      argv++;
	      argc--;
	      if (argc == 0)
		usage ();
	      p = *argv;
	    }
	  askfilename = p;
	  break;
	case 'l':
	  lflag++;
	  break;
	case 'L':
	  llflag++;
	  break;
	case 's':
	  sflag++;
	  break;
	case 'S':
	  sortit = 0;
	  break;
	case 'p':
	  cpd = (*argv) + 2;
	  if (*cpd == '\0')
	    {
	      argv++;
	      argc--;
	      if (argc == 0)
		usage ();
	      cpd = *argv;
	    }
	  break;
	case 'd':
	  p = (*argv) + 2;
	  if (*p == '\0')
	    {
	      argv++;
	      argc--;
	      if (argc == 0)
		usage ();
	      p = *argv;
	    }
	  if (*p == '/')
	    strcpy (hashname, p);
	  else
	    sprintf (hashname, "%s%s", LIBDIR, p);
	  break;
	case 'w':
	  num[3] = '\0';
#ifdef NO8BIT
	  mask = 0x7f;
#else
	  mask = 0xff;
#endif
	  p = (*argv) + 2;
	  if (*p == '\0')
	    {
	      argv++;
	      argc--;
	      if (argc == 0)
		usage ();
	      p = *argv;
	    }
	  while (Trynum <= mask && *p != '\0')
	    {
	      if (*p != 'n' && *p != '\\')
		{
		  Checkch[((unsigned) (*p)) & mask] = (char) 1;
		  Try[Trynum] = *p & mask;
		  ++p;
		}
	      else
		{
		  ++p;
		  num[0] = '\0';
		  num[1] = '\0';
		  num[2] = '\0';
		  num[3] = '\0';
		  if (isdigit (p[0]))
		    num[0] = p[0];
		  if (isdigit (p[1]))
		    num[1] = p[1];
		  if (isdigit (p[2]))
		    num[2] = p[2];
		  if (p[-1] == 'n')
		    {
		      p += strlen (num);
		      num[0] = atoi (num);
		    }
		  else
		    {
		      p += strlen (num);
		      if (num[0])
			num[0] -= '0';
		      if (num[1])
			{
			  num[0] <<= 3;
			  num[0] += num[1] - '0';
			}
		      if (num[2])
			{
			  num[0] <<= 3;
			  num[0] += num[2] - '0';
			}
		    }
		  Try[Trynum] = num[0] & mask;
		  Checkch[num[0] & mask] = 1;
		}
	      ++Trynum;
	    }
	  break;
	default:
	  usage ();
	}
      argv++;
      argc--;
    }

  if (!argc && !lflag && !aflag && !rflag)
    usage ();

  if (linit () < 0)
    exit (0);

  treeinit (cpd);

  if (rflag)
    {
      servermode ();
      exit (0);
    }

  if (aflag)
    {
      askmode ();
      exit (0);
    }

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

  terminit ();

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

  done ();
}

char firstbuf[BUFSIZ], secondbuf[BUFSIZ];
char *currentchar;
char token[BUFSIZ];

int quit;

char *currentfile = NULL;

void dofile (char *filename)
{
  int c;
  char bakfile[256];
  char *cp;

  currentfile = filename;

  if ((infile = fopen (filename, "r")) == NULL)
    {
      fprintf (stderr, "Can't open %s\n", filename);
      sleep (2);
      return;
    }

  if (access (filename, 2) < 0)
    {
      fprintf (stderr, "Can't write to %s\n", filename);
      sleep (2);
      return;
    }

  tmpnam (tempfile);
  if ((outfile = fopen (tempfile, "w")) == NULL)
    {
      fprintf (stderr, "Can't create %s\n", tempfile);
      sleep (2);
      return;
    }

  quit = 0;

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

  fclose (infile);
  fclose (outfile);

  if (!cflag)
    treeoutput ();

  if ((infile = fopen (tempfile, "r")) == NULL)
    {
      fprintf (stderr, "temporary file disappeared (%s)\n", tempfile);
      sleep (2);
      return;
    }

  sprintf (bakfile, "%s%s", filename, BAKEXT);
  remove (bakfile);
  if (rename (filename, bakfile))
    {
      fprintf (stderr, "can't rename %s to %s\n", filename, bakfile);
      sleep (2);
      return;
    }

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

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

  fclose (infile);
  fclose (outfile);

  remove (tempfile);
  if (xflag)
    remove (bakfile);
}

void checkfile (void)
{
  register char *p;
  register int len;
  int addflag;

  secondbuf[0] = 0;

  while (1)
    {
      strcpy (firstbuf, secondbuf);
      if (quit)
	{			/* quit can't be set in l mode */
	  while (fgets (secondbuf, sizeof secondbuf, infile) != NULL)
	    fputs (secondbuf, outfile);
	  break;
	}

      if (fgets (secondbuf, sizeof secondbuf, infile) == NULL)
	break;
      currentchar = secondbuf;
      addflag = 0;
      len = strlen (secondbuf) - 1;

      if (!tflag)
	{
	  /* skip over .if */
	  if (strncmp (currentchar, ".if t", 5) == 0
	      || strncmp (currentchar, ".if n", 5) == 0)
	    {
	      copyout (&currentchar, 5);
	      while (*currentchar && isspace (*currentchar))
		copyout (&currentchar, 1);
	    }

	  /* skip over .ds XX or .nr XX */
	  if (strncmp (currentchar, ".ds ", 4) == 0
	      || strncmp (currentchar, ".de ", 4) == 0
	      || strncmp (currentchar, ".nr ", 4) == 0)
	    {
	      copyout (&currentchar, 3);
	      while (*currentchar && isspace (*currentchar))
		copyout (&currentchar, 1);
	      while (*currentchar && !isspace (*currentchar))
		copyout (&currentchar, 1);
	      if (*currentchar == 0)
		{
		  if (!lflag)
		    putc ('\n', outfile);
		  continue;
		}
	    }
	}

      if (secondbuf[len] == '\n')
	secondbuf[len] = 0;

      /* if this is a formatter command, skip over it */
      if (!tflag && *currentchar == '.')
	{
	  while (*currentchar && !myspace (*currentchar))
	    {
	      if (!lflag)
		putc (*currentchar, outfile);
	      currentchar++;
	    }
	  if (*currentchar == 0)
	    {
	      if (!lflag)
		putc ('\n', outfile);
	      continue;
	    }
	}

      while (1)
	{
	  while (*currentchar && !iswordch (*currentchar))
	    {
	      if (tflag)	/* TeX or LaTeX stuff */
		{
		  if (*currentchar == '\\')
		    {
		      /* skip till whitespace */
		      while (*currentchar &&
			     (!isspace (*currentchar) &&
			      !ISTEXTERM (*currentchar)))
			{
			  if (!lflag)
			    putc (*currentchar, outfile);
			  currentchar++;
			}
		      continue;
		    }
		}
	      else
		{
		  /* formatting escape sequences */
		  if (*currentchar == '\\')
		    {
		      switch (currentchar[1])
			{
			case 'f':
			  if (currentchar[2] != '(')
			    {
			      /* font change: \fX */
			      copyout (&currentchar, 3);
			    }
			  else
			    {
			      /* font change: \f(XY */
			      copyout (&currentchar, 5);
			    }
			  continue;
			case 's':
			  /* size change */
			  p = currentchar + 2;
			  if (*p == '+' || *p == '-')
			    p++;
			  /* This looks wierd 'cause we assume
			  ** *p is now a digit.
			  */
			  if (isdigit (p[1]))
			    p++;
			  copyout (&currentchar,
				   p - currentchar + 1);
			  continue;
			case '(':
			  /* extended char set escape: \(XX */
			  copyout (&currentchar, 4);
			  continue;
			case '*':
			  if (currentchar[2] != '(')
			    copyout (&currentchar, 3);
			  else
			    copyout (&currentchar, 5);
			  continue;
			default:
			  break;
			}
		    }
		}

	      if (!lflag)
		putc (*currentchar, outfile);
	      currentchar++;
	    }

	  if (*currentchar == 0)
	    break;

	  p = token;
	  while (iswordch (*currentchar) ||
		 (*currentchar == '\'' &&
		  iswordch (*(currentchar + 1))))
	    *p++ = *currentchar++;
	  *p = 0;
/*
 *   mods by tom rokicki to handle local document words automagically
 */
	  if (token[0] == 'i' &&
	      strcmp (token, "ispell'local'words") == 0)
	    {
	      addflag = 1;
	    }
	  else
	    {
	      if (addflag)
		{
		  if (!good (token))
		    {
		      treeinsert (token, 0);
		    }
		}
	      if (lflag)
		{
		  if (!good (token) && !cflag)
		    if (rflag)
		      fprintf (outfile, "%s\n", token);
		    else
		      fprintf (stdout, "%s\n", token);
		}
	      else
		{
		  if (!quit)
		    correct (token, &currentchar);
		}
	    }
	  if (!lflag)
	    fprintf (outfile, "%s", token);
	}
      if (!lflag)
	putc ('\n', outfile);
    }
  if (llflag)
    {
      lldump ();
    }
}

void correct (char *token, char **currentchar)
{
  register int c;
  register int i;
  int col_ht;
  int ncols;
  char *start_l2;
  char *begintoken;

  begintoken = *currentchar - strlen (token);

checkagain:
  if (good (token))
    return;

  erase ();
  printcon ("    %s", token);
  if (currentfile)
    printcon ("              File: %s", currentfile);
  printcon ("\n\n");

  makepossibilities (token);

  /*
   * 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 = li - 6;		/* Height of columns of words */
  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++)
    {
      move (2 + (i % col_ht), (maxposslen + 8) * (i / col_ht));
      printcon ("%2d: %s", i, possibilities[i]);
    }

  move (li - 3, 0);
  show_line (firstbuf, firstbuf, 0);

  start_l2 = secondbuf;
  if (line_size (secondbuf, *currentchar) > co - 1)
    {
      start_l2 = begintoken - (co / 2);
      while (start_l2 < begintoken)
	{
	  i = line_size (start_l2, *currentchar) + 1;
	  if (i <= co)
	    break;
	  start_l2 += i - co;
	}
      if (start_l2 > begintoken)
	start_l2 = begintoken;
      if (start_l2 < secondbuf)
	start_l2 = secondbuf;
    }
  show_line (start_l2, begintoken, strlen (token));

  while (1)
    {
      switch (c = (getccon () & NOPARITY))
	{
	case 'Z' & 037:
	  stop ();
	  erase ();
	  goto checkagain;
	case ' ':
	  erase ();
	  return;
	case 'x':
	case 'X':
	  printcon ("Are you sure you want to throw away your changes? ");
	  c = (getccon () & NOPARITY);
	  if (c == 'y' || c == 'Y')
	    {
	      erase ();
	      done ();
	    }
	  putchar (7);
	  goto checkagain;
	case 'i':
	case 'I':
	  treeinsert (token, 1);
	  erase ();
	  return;
	case 'a':
	case 'A':
	  treeinsert (token, 0);
	  if (llflag)
	    llinsert (token);
	  erase ();
	  return;
	case 'L' & 037:
	  goto checkagain;
	case '?':
	  givehelp ();
	  goto checkagain;
	case 'r':
	case 'R':
	  move (li - 1, 0);
	  printcon ("Replace with: ");
	  if (getline (token) == NULL)
	    {
	      putccon (7);
	      erase ();
	      goto checkagain;
	    }
	  inserttoken (secondbuf, begintoken, token, currentchar);
	  erase ();
	  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 (pcount > 10
	      && i > 0 && i <= (pcount - 1) / 10)
	    {
	      c = getccon () & NOPARITY;
	      if (c >= '0' && c <= '9')
		i = i * 10 + c - '0';
	      else if (c != '\n')
		{
		  putccon (7);
		  break;
		}
	    }
	  if (i < pcount)
	    {
	      strcpy (token, possibilities[i]);
	      inserttoken (secondbuf, begintoken,
			   token, currentchar);
	      erase ();
	      return;
	    }
	  putccon (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);
	    printcon ("Lookup string ('*' is wildcard): ");
	    if (getline (buf) == NULL)
	      {
		putccon (7);
		erase ();
		goto checkagain;
	      }
	    printcon ("\n\n");
	    lookharder (buf);
	    erase ();
	    goto checkagain;
	  }
	case 'q':
	case 'Q':
	  quit = 1;
	  erase ();
	  return;
	default:
	  putccon (7);
	  break;
	}
    }
}

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

  width = 0;
  while (line != invstart && width < co - 1)
    width += show_char (*line++, width);
  if (invlen)
    {
      inverse ();
      while (--invlen >= 0 && width < co - 1)
	width += show_char (*line++, width);
      normal ();
    }
  while (*line && width < co - 1)
    width += show_char (*line++, width);
  printcon ("\n");
}

int show_char (int ch, int linew)
{
  if (ch == '\t')
    {
      putccon ('\t');
      return 8 - (linew & 0x07);
    }
  else if (ch < ' ')
    {
      putccon ('^');
      putccon (ch + 'A' - '\001');
      return 2;
    }
  putccon (ch);
  return 1;
}

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

  for (width = 0; buf < bufend && *buf; buf++)
    {
      if (*buf == '\t')
	width = (width + 8) & ~0x07;
      else if (*buf < ' ')
	width += 2;
      else
	width++;
    }
  return width;
}

void inserttoken (char *buf, char *start, char *token, char **currentchar)
{
  char copy[BUFSIZ];
  register char *p, *q;

  strcpy (copy, buf);

  for (p = buf, q = copy; p != start; p++, q++)
    *p = *q;
  q += *currentchar - start;
  while (*token && iswordch (*token))
    *p++ = *token++;
  *currentchar = p;
  if (*token)
    {

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

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

int casecmp (char *a, char *b)
{
  register char *ap;
  register char *bp;

  for (ap = a, bp = b; *ap; ap++, bp++)
    {
      if (mylower (*ap))
	{
	  if (mylower (*bp))
	    {
	      if (*ap != *bp)
		return *ap - *bp;
	    }
	  else
	    {
	      if (toupper (*ap) != *bp)
		return toupper (*ap) - *bp;
	    }
	}
      else
	{
	  if (myupper (*bp))
	    {
	      if (*ap != *bp)
		return *ap - *bp;
	    }
	  else
	    {
	      if (tolower (*ap) != *bp)
		return tolower (*ap) - *bp;
	    }
	}
    }
  if (*bp != '\0')
    return -*bp;
  return strcmp (a, b);
}

void makepossibilities (char *word)
{
  register int i;

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

#ifdef CAPITALIZE
  wrongcapital (word);
#endif
  if (pcount < MAXPOSSIBLE)
    wrongletter (word);
  if (pcount < MAXPOSSIBLE)
    extraletter (word);
  if (pcount < MAXPOSSIBLE)
    missingletter (word);
  if (pcount < MAXPOSSIBLE)
    transposedletter (word);

  if (sortit && pcount)
    qsort ((char *) possibilities, pcount,
	   sizeof (possibilities[0]), casecmp);
}

int insert (char *word)
{
  register int i;

  for (i = 0; i < pcount; i++)
    if (strcmp (possibilities[i], word) == 0)
      return (0);

  strcpy (possibilities[pcount++], word);
  i = strlen (word);
  if (i > maxposslen)
    maxposslen = i;
  if (pcount >= MAXPOSSIBLE)
    return (-1);
  else
    return (0);
}

#ifdef CAPITALIZE
int wrongcapital (char *word)
{
  char newword[BUFSIZ];

  /*
  ** All-uppercase is always legal.  If the word matches, "ins_cap"
  ** will recapitalize it correctly.
  */
  strcpy (newword, word);
  upcase (newword);
  if (good (newword))
    return ins_cap (newword, word);
  return 0;
}

#endif

void wrongletter (char *word)
{
  register int i, j, c, n;
  char newword[BUFSIZ];

  n = strlen (word);
  strcpy (newword, word);
#ifdef CAPITALIZE
  upcase (newword);
#endif

  for (i = 0; i < n; i++)
    {
      for (j = 0; j < Trynum; ++j)
	{
	  newword[i] = Try[j];
	  if (good (newword))
	    {
	      if (ins_cap (newword, word) < 0)
		return;
	    }
	}
#ifdef CAPITALIZE
      c = word[i];
      if (islower (c))
	newword[i] = toupper (c);
      else
	newword[i] = c;
#else
      newword[i] = word[i];
#endif
    }
}

void extraletter (char *word)
{
  char newword[BUFSIZ];
  register char *p, *s, *t;

  if (strlen (word) < 3)
    return;

  for (p = word; *p; p++)
    {
      for (s = word, t = newword; *s; s++)
	if (s != p)
	  *t++ = *s;
      *t = 0;
#ifdef CAPITALIZE
      if (good (upcase (newword)))
	{
	  if (ins_cap (newword, word) < 0)
	    return;
	}
#else
      if (good (newword))
	{
	  if (ins_cap (newword, word) < 0)
	    return;
	}
#endif
    }
}

void missingletter (char *word)
{
  char newword[BUFSIZ];
  register char *p, *r, *s, *t;
  register int i;

  for (p = word; p == word || p[-1]; p++)
    {
      for (s = newword, t = word; t != p; s++, t++)
	*s = *t;
      r = s++;
      while (*t)
	*s++ = *t++;
      *s = 0;
      for (i = 0; i < Trynum; ++i)
	{
	  *r = Try[i];
#ifdef CAPITALIZE
	  if (good (upcase (newword)))
	    {
	      if (ins_cap (newword, word) < 0)
		return;
	    }
#else
	  if (good (newword))
	    {
	      if (ins_cap (newword, word) < 0)
		return;
	    }
#endif
	}
    }
}

void transposedletter (char *word)
{
  char newword[BUFSIZ];
  register int t;
  register char *p;

  strcpy (newword, word);
  for (p = newword; p[1]; p++)
    {
      t = p[0];
      p[0] = p[1];
      p[1] = t;
#ifdef CAPITALIZE
      if (good (upcase (newword)))
	{
	  if (ins_cap (newword, word) < 0)
	    return;
	}
#else
      if (good (newword))
	{
	  if (ins_cap (newword, word) < 0)
	    return;
	}
#endif
      t = p[0];
      p[0] = p[1];
      p[1] = t;
    }
}

/* Insert one or more correctly capitalized versions of pattern */
int ins_cap (char *word, char *pattern)
{
  static char newword[BUFSIZ];
  register char *p;
  char *psave;
  register int wcount;

  if (*word == 0)
    return 0;

  strcpy (newword, word);
#ifdef CAPITALIZE
  if (lastdent->allcaps)
    return insert (upcase (newword));	/* Uppercase required */
  for (p = pattern; *p; p++)
    if (mylower (*p))
      break;
  if (*p == '\0')
    return insert (upcase (newword));	/* Pattern was all caps */
  for (p = pattern; *p; p++)
    if (myupper (*p))
      break;
  if (*p == '\0')
    {				/* Pattern was all lower */
      if (!lastdent->followcase && !lastdent->capitalize)
	return insert (lowcase (newword));
      /*
      ** If there's a followcase version that's all-lower,
      ** insert only that version.
      */
      if (lastdent->followcase)
	{
	  p = lastdent->word;
	  p += strlen (p) + 1;
	  wcount = (*p++ & 0xFF);
	  while (--wcount >= 0)
	    {
	      for (psave = ++p;
		   *p && !myupper (*p);
		   p++)
		;
	      if (*p == '\0')	/* Was it all lowercase? */
		return insert (psave);	/* Yup, quit */
	      while (*p++)
		;		/* Skip to next case sample */
	    }
	}
    }
  /*
  ** The sample wasn't all-upper, and either it wasn't all-lower or
  ** all-lower is illegal.  Insert all legal capitalizations.  In
  ** some cases, this may include all-lowercase.
  */
  if (lastdent->capitalize)
    {
      lowcase (newword);
      if (mylower (newword[0]))
	newword[0] = toupper (newword[0]);
      insert (newword);
    }
  if (lastdent->followcase)
    {
      p = lastdent->word;
      p += strlen (p) + 1;
      wcount = (*p++ & 0xFF);
      while (--wcount >= 0)
	{
	  /* Insert every variation;  it's easier */
	  if (insert (++p) < 0)
	    return -1;
	  while (*p++)
	    ;			/* Skip to end of sample */
	}
      return 0;
    }
  if (lastdent->capitalize)
    return 0;
  /*
  ** We get here only if none of the special capitalization flags are
  ** set.  If first letter of the pattern is capitalized, capitalize
  ** the first letter of the result.  Otherwise produce all lowercase.
  */
  lowcase (newword);
  if (myupper (pattern[0]) && mylower (newword[0]))
    newword[0] = toupper (newword[0]);
  return insert (newword);
#else
  if (myupper (pattern[0]))
    {
      if (myupper (pattern[1]))
	{
	  for (p = word, q = newword; *p; p++, q++)
	    {
	      if (mylower (*p))
		*q = toupper (*p);
	      else
		*q = *p;
	    }
	  *q = 0;
	}
      else
	{
	  if (mylower (word[0]))
	    newword[0] = toupper (word[0]);
	  else
	    newword[0] = word[0];

	  for (p = word + 1, q = newword + 1; *p; p++, q++)
	    if (myupper (*p))
	      *q = tolower (*p);
	    else
	      *q = *p;

	  *q = 0;
	}
    }
  else
    {
      for (p = word, q = newword; *p; p++, q++)
	if (myupper (*p))
	  *q = tolower (*p);
	else
	  *q = *p;
      *q = 0;
    }
  return insert (newword);
#endif
}

char *getline (char *s)
{
  register char *p;
  register int c;

  p = s;

  while (1)
    {
      c = (getccon () & NOPARITY);
      if (c == '\\')
	{
	  putccon ('\\');
	  c = (getccon () & NOPARITY);
	  backup ();
	  putccon (c);
	  *p++ = c;
	}
      else if (c == ('G' & 037))
	{
	  return (NULL);
	}
      else if ((c == '\n') || (c == '\r'))
	{
	  *p = 0;
	  return (s);
	}
      else if (c == '\b')
	{
	  if (p != s)
	    {
	      p--;
	      backup ();
	      putccon (' ');
	      backup ();
	    }
	}
      else
	{
	  *p++ = c;
	  putccon (c);
	}
    }
}

void servermode (void)
{
  long rexxbit;
  long returnbits;
  rexxbit = upRexxPort ("IRexxSpell", rcl, NULL, &disp);

  setbuf (stdin, NULL);
  setbuf (stdout, NULL);

  while (KeepGoing)
    {
      returnbits = Wait (rexxbit | SIGBREAKF_CTRL_C);
      if (returnbits & SIGBREAKF_CTRL_C)
	KeepGoing = 0;
      if (returnbits & rexxbit)
	dispRexxPort ();
    }
/*
 *   With Rexx, we need to bring the port down.  You might make this
 *   part of exit() for programs that have multiple paths to exit.
 */
  dnRexxPort ();
}


/*
 *   Now we get into the actual code necessary for our REXX port; functions
 *   that do the real work.  Note that this program was not structured
 *   particularly nicely for Rexx; I had to write each of these functions.
 *   Many programs have these subroutines already in place; they are called
 *   as part of the event loop.  This progam, however, just has one big
 *   switch statement with different actions . . .
 *
 *   First, our locals.
 */
int userreplied;		/* has the current message been replied to yet? */
/*
 *   This is our main dispatch function.
 */
void disp (struct RexxMsg *msg, struct rexxCommandList *dat, char *p)
{
  dat->userdata(msg, p);
  return;
}

/*
 *   This handler adds a word to the `global personal dictionary.'
 *
 *   Now, in English, the word is added to whatever personal
 *   dictionary was found when ISpell was started up in ARexx
 *   server mode. (Under a multiuser system, we might want to
 *   restrict who can add words to this dictionary.  For now
 *   we just do it!  Yes, the AmigaOS is multiuser, hehe :-).
 *   No protection, the way true hackers like it, you (the 
 *   user) get more rope to hang yourself :-), err the system 
 *   with :-).
 */
void rexxadd (struct RexxMsg *msg, char *p)
{
  userreplied = 1;
  if (p[1])
    treeinsert (&(p[1]), 1);
  treeoutput ();
  replyRexxCmd (msg, 0L, 0L, "ok");
}

/*
 *   The quick version does not write the changes to disk.
 *   Useful if you will be adding many words.  Be sure to
 *   call rexxadd at the end of the list (pass a null word,
 *   or a valid new word to be added).
 */
void rexxquickadd (struct RexxMsg *msg, char *p)
{
  userreplied = 1;
  treeinsert (&(p[1]), 1);
  replyRexxCmd (msg, 0L, 0L, "ok");
}

/*
 *   This handler checks the spelling of a word.
 */
void rexxcheck (struct RexxMsg *msg, char *p)
{
  char buf[512];
  register int i;

  userreplied = 1;
  p++;
  if (good (p))
    {
      if (rootword[0] == 0)
	strcpy (buf, "*");
      else
	sprintf (buf, "+ %s", rootword);
    }
  else
    {
      makepossibilities (p);
      if (possibilities[0][0])
	{
	  int bufend = 1;
	  int possibilitysize;

	  strcpy (buf, "&");
	  for (i = 0; i < MAXPOSSIBLE; i++)
	    {
	      if (possibilities[i][0] == 0)
		break;
	      possibilitysize = strlen(possibilities[i]);
	      if ((possibilitysize + bufend) > 511)
		break;
	      strcpy (&(buf[bufend++]), " ");
	      strcpy (&(buf[bufend]), possibilities[i]);
	      bufend += possibilitysize;
	    }
	}
      else
	strcpy (buf, "#");
    }
  replyRexxCmd (msg, 0L, 0L, buf);
}

/*
 *   This handler checks the spelling of a word, it does
 *   not try to find replacement words as above.  It only
 *   determines whether or not the word is in the dictoinary.
 */
void rexxquickcheck (struct RexxMsg *msg, char *p)
{
  userreplied = 1;
  if (good (&(p[1])))
    {
      if (rootword[0] == 0)
	replyRexxCmd (msg, 0L, 0L, "ok");
      else
	replyRexxCmd (msg, 0L, 0L, "ok");
    }
  else
    replyRexxCmd (msg, 0L, 0L, "bad");
}

void rexxlookup (struct RexxMsg *msg, char *p)
{
  char buf[512];
  char cmd[512];
  char *rval;
  int whence = 0;
  int bufend = 1;
  int lookupsize;

  userreplied = 1;
  strcpy (buf, "&");
  sprintf (cmd, "^%s$", &(p[1]));
  while ((rval = do_regex_lookup (cmd, whence++)) != NULL)
    {
      lookupsize = strlen(rval);
      if ((lookupsize + bufend) > 511)
	break;
      strcpy (&(buf[bufend++]), " ");
      strcpy (&(buf[bufend]), rval);
      bufend += lookupsize;
    }
  replyRexxCmd (msg, 0L, 0L, buf);
}

/*
 *   This handler checks the spelling of a file.
 */
void rexxfilecheck (struct RexxMsg *msg, char *p)
{
  char *cp;

  userreplied = 1;
  currentfile = &(p[1]);

  if ((infile = fopen (&(p[1]), "r")) == NULL)
    {
      replyRexxCmd (msg, 0L, 0L, "Error: Can't open input file");
      return;
    }

  tmpnam (tempfile);

  if ((outfile = fopen (tempfile, "w")) == NULL)
    {
      replyRexxCmd (msg, 0L, 0L, "Error: Can't open output file");
      return;
    }

  quit = 0;

  /* See if the file is a .tex file.  If so, set the appropriate flag. */
  if ((cp = strrchr (&(p[1]), '.')) != NULL && strcmp (cp, ".tex") == 0)
    tflag = 1;
  else
    tflag = 0;

  lflag = 1;

  checkfile ();

  fclose (infile);
  fclose (outfile);

  replyRexxCmd (msg, 0L, 0L, tempfile);
}

/*
 *   This handler returns the version of the program.
 */
void rexxversion (struct RexxMsg *msg, char *p)
{
  char buf[512];
  int bufend = strlen(Version_ID);
  int namesize;
  int i;

  userreplied = 1;
  strcpy(buf, Version_ID);
  buf[bufend++] = '\n';
  for (i = 0; rcl[i].name != NULL; i++)
    {
      namesize = strlen(rcl[i].name);
      if ((namesize + bufend) > 511)
	break;
      strcpy (&(buf[bufend++]), " ");
      strcpy (&(buf[bufend]), rcl[i].name);
      bufend += namesize;
    }
  replyRexxCmd (msg, 0L, 0L, buf);
}

/*
 *   This handler sets the exit flag.
 */
void rexxexit (struct RexxMsg *msg, char *p)
{
  userreplied = 1;
  KeepGoing = 0;
  replyRexxCmd (msg, 0L, 0L, "bye");
}

void askmode (void)
{
  char buf[BUFSIZ];
  register int i;

  if (fflag)
    {
      if (freopen (askfilename, "w", stdout) == NULL)
	{
	  fprintf (stderr, "Can't create %s\n", askfilename);
	  exit (1);
	}
    }

  setbuf (stdin, NULL);
  setbuf (stdout, NULL);

  while (xgets (buf) != NULL)
    {
      /* *line is like `i', @line is like `a' */
      if (buf[0] == '*' || buf[0] == '@')
	{
	  treeinsert (buf + 1, buf[0] == '*');
	  fprintf (stdout, "*\n");
	  treeoutput ();
	}
      else if (good (buf))
	{
	  if (rootword[0] == 0)
	    {
	      fprintf (stdout, "*\n");	/* perfect match */
	    }
	  else
	    {
	      fprintf (stdout, "+ %s\n", rootword);
	    }
	}
      else
	{
	  makepossibilities (buf);
	  if (possibilities[0][0])
	    {
	      fprintf (stdout, "& ");
	      for (i = 0; i < MAXPOSSIBLE; i++)
		{
		  if (possibilities[i][0] == 0)
		    break;
		  fprintf (stdout, "%s ", possibilities[i]);
		}
	      fprintf (stdout, "\n");
	    }
	  else
	    {
	      fprintf (stdout, "#\n");
	    }
	}
      if (sflag)
	{
	  stop ();
	  if (fflag)
	    {
	      rewind (stdout);
	      creat (askfilename, 0666);
	    }
	}
    }
}

/* Copy/ignore "cnt" number of characters pointed to by *cc. */
void copyout (char **cc, int cnt)
{
  while (--cnt >= 0)
    {
      if (*(*cc) == 0)
	break;
      if (!lflag)
	putc (*(*cc), outfile);
      (*cc)++;
    }
}

void lookharder (char *string)
{
  char cmd[150], grepstr[100];
  register char *g, *s;

  g = grepstr;
  for (s = string; *s != '\0'; s++)
    if (*s == '*')
      {
	*g++ = '.';
	*g++ = '*';
      }
    else
      *g++ = *s;
  *g = '\0';
  if (grepstr[0])
    regex_dict_lookup (cmd, grepstr);
}

void regex_dict_lookup (char *cmd, char *grepstr)
{
  char *do_regex_lookup ();
  char *rval;
  int whence = 0;
  int quit = 0;
  int count = 0;
  sprintf (cmd, "^%s$", grepstr);
  while (!quit && (rval = do_regex_lookup (cmd, whence++)) != NULL)
    {
      int ch;
      printcon ("%s\n", rval);;
      if ((count++ % (li - 1)) == 0)
	{
	  inverse ();
	  printcon ("-- more --");
	  normal ();
	  if ((ch = getccon ()) == 'q' || ch == 'Q' || ch == 'x' || ch == 'X')
	    quit = 1;
	  printcon ("\r                             \r");
	}
    }
  if (rval == NULL)
    {
      inverse ();
      printcon ("--eow--");
      normal ();
      getccon ();
    }
}
