/* -*- Mode:Text -*- */
/*
 * ispell.c - An interactive spelling corrector.
 *
 * Copyright (c), 1983, by Pace Willisson
 * Permission for non-profit use is hereby granted.
 * All other rights reserved.
 */

#include <stdio.h>
#include <ctype.h>
#include "ispell.h"

#define NOPARITY 0x7f 

FILE *infile;
FILE *outfile;

char rootword[BUFSIZ];

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


char *getline();

int lflag = 0;
int aflag = 0;
int fflag = 0;
int sflag = 0;

char *askfilename;

usage ()
{
	fprintf (stderr, "Usage: spell [ file ... | -a | -l | -f file | -s ]\n");
	exit (1);
}

main (argc, argv)
char **argv;
{
	argv++;
	argc--;
	while (argc && **argv == '-') {
		switch ((*argv)[1]) {
		case 'a':
			aflag++;
			break;
		case 'f':
			fflag++;
			argv++; argc--;
			if (argc == 0)
				usage ();
			askfilename = *argv;
			break;
		case 'l':
			lflag++;
			break;
		case 's':
			sflag++;
			break;
		}
		argv++; argc--;
	}

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

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

	treeinit ();

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

	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;

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

	currentfile = filename;

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

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

#ifdef unx
	strcpy (tempfile, "/usr/tmp/spellXXXXXX");
	mktemp (tempfile);
#else
	strcpy (tempfile, "t:ISpelltemp");  /* Needs fix to make uniq!! (fnf) */
#endif
	if ((outfile = fopen (tempfile, "w")) == NULL) {
		fprintf (stderr, "Can't create %s\r\n", tempfile);
		sleep (2);
		return;
	}

	quit = 0;

	checkfile ();

	fclose (infile);
	fclose (outfile);

	treeoutput ();

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

	sprintf(bakfile, "%s.bak", filename);

#ifdef unix
	if(link(filename, bakfile) == 0)
		unlink(filename);
#else
	if(rename(filename, bakfile) == -1) {
		fprintf (stderr, "sorry, can't rename %s as %s\n",
			filename, bakfile);
		sleep (2);
		return;
	}
#endif

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

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

	fclose (infile);
	fclose (outfile);

	unlink (tempfile);
}

checkfile ()
{
	int c;
	char *p;
	int len;

	secondbuf[0] = 0;
	currentchar = secondbuf;

	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;
		
		len = strlen (secondbuf) - 1;
		if (secondbuf [ len ] == '\n')
			secondbuf [ len ] = 0;

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

		while (1) {
			while (*currentchar && !isalpha (*currentchar)) {
				/* formatting escape sequences */
				if (*currentchar == '\\') {
				    if(currentchar[1] == 'f') {
					/* font change: \fX */
					copyout(&currentchar, 3);
					continue;
				    }
				    else if(currentchar[1] == 's') {
					/* size change */
					if(currentchar[2] < 6 &&
					   currentchar[2] != 0)
						/* two digit size */
						copyout(&currentchar, 4);
					else
						/* one digit size */
						copyout(&currentchar, 3);
					continue;
				    }
				    else if(currentchar[1] == '(') {
					/* extended char set escape: \(XX */
					copyout(&currentchar, 4);
					continue;
				    }
				}

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

			if (*currentchar == 0)
				break;

			p = token;
			while (isalpha (*currentchar) ||
			       (*currentchar == '\'' &&
				(isalpha (*(currentchar + 1)))))
			  *p++ = *currentchar++;
			*p = 0;
			if (lflag) {
				if (!good (token))
					printf ("%s\r\n", token);
			} else {
				if (!quit)
				correct (token, &currentchar);
			}
			if (!lflag)
				fprintf (outfile, "%s", token);
		}
		putc ('\n', outfile);
	}
}

char possibilities[10][BUFSIZ];
int pcount;

correct (token, currentchar)
char *token;
char **currentchar;
{
	int c;
	int i;
	char *p;
	int len;
	char *begintoken;

	len = strlen (token);
	begintoken = *currentchar - len;

checkagain:
	if (good (token))
		return;

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

	makepossibilities (token);

	for (i = 0; i < 10; i++) {
		if (possibilities[i][0] == 0)
			break;
		printf ("%d: %s\r\n", i, possibilities[i]);
	}

	move (15, 0);
	printf ("%s\r\n", firstbuf);

	for (p = secondbuf; p != begintoken; p++)
		putchar (*p);
	inverse ();
	for (i = strlen (token); i > 0; i--)
		putchar (*p++);
	normal ();
	while (*p)
		putchar (*p++);
	printf ("\r\n");

	while (1) {
		switch (c = (getchar () & NOPARITY)) {
		case 'Z' & 037:
			stop ();
			erase ();
			goto checkagain;
		case ' ':
			erase ();
			return;
		case 'x': case 'X':
			printf ("Are you sure you want to throw away your changes? ");
			c = (getchar () & 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);
			erase ();
			return;
		case 'L' & 037:
			goto checkagain;
		case '?':
			givehelp ();
			goto checkagain;
		case '!':
			{
				char buf[200];
				move (18, 0);
				putchar ('!');
				if (getline (buf) == NULL) {
					putchar (7);
					erase ();
					goto checkagain;
				}
				printf ("\r\n");
				shellescape (buf);
				erase ();
				goto checkagain;
			}
		case 'r': case 'R':
			move (18, 0);
			printf ("Replace with: ");
			if (getline (token) == NULL) {
				putchar (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':
			if (possibilities[c - '0'][0] != 0) {
				strcpy (token, possibilities[c - '0']);
				inserttoken (secondbuf, begintoken, token, currentchar);				erase ();
				return;
			}
			putchar (7);
			break;
		case 'q': case 'Q':
			quit = 1;
			erase ();
			return;
		default:
			putchar (7);
			break;
		}
	}
}

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

	strcpy (copy, buf);

	for (p = buf, q = copy; p != start; p++, q++)
		*p = *q;
	while (*token)
		*p++ = *token++;
	q += *currentchar - start;
	*currentchar = p;
	while (*p++ = *q++)
		;
}


makepossibilities (word)
char word[];
{
	int i;

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

	if (pcount < 10) wrongletter (word);
	if (pcount < 10) extraletter (word);
	if (pcount < 10) missingletter (word);
	if (pcount < 10) transposedletter (word);

}

char *cap();

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

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

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

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

	n = strlen (word);
	strcpy (newword, word);

	for (i = 0; i < n; i++) {
		for (newword[i] = 'A'; newword[i] <= 'Z'; newword[i]++) {
			if (good (newword)) {
				if (insert (cap (newword, word)) < 0)
					return;
			}
		}
		newword[i] = word[i];
	}
}

extraletter (word)
char word[];
{
	char newword[BUFSIZ], *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;
		if (good (newword)) {
			if (insert (cap (newword, word)) < 0)
				return;
		}
	}
}

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

	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 (*r = 'A'; *r <= 'Z'; (*r)++) {
			if (good (newword)) {
				if (insert (cap (newword, word)) < 0)
					return;
			}
		}
	}
}

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

	strcpy (newword, word);
	for (p = newword; p[1]; p++) {
		t = p[0];
		p[0] = p[1];
		p[1] = t;
		if (good (newword)) {
			if (insert (cap (newword, word)) < 0)
				return;
		}
		t = p[0];
		p[0] = p[1];
		p[1] = t;
	}
}

char *
cap (word, pattern)
char word[], pattern[];
{
	static char newword[BUFSIZ];
	char *p, *q;

	if (*word == 0)
		return;

	if (isupper (pattern[0])) {
		if (isupper (pattern[1])) {
			for (p = word, q = newword; *p; p++, q++) {
				if (islower (*p))
					*q = toupper (*p);
				else
					*q = *p;
			}
			*q = 0;
		} else {
			if (islower (word [0]))
				newword[0] = toupper (word[0]);
			else
				newword[0] = word[0];

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

			*q = 0;
		}
	} else {
		for (p = word, q = newword; *p; p++, q++)
			if (isupper (*p))
				*q = tolower (*p);
			else
				*q = *p;
		*q = 0;
	}
	return (newword);
}

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

	p = s;

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

askmode ()
{
	char buf[BUFSIZ];
	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 (gets (buf) != NULL) {
		/* *line is like `i', @line is like `a' */
		if (buf[0] == '*' || buf[0] == '@') {
			treeinsert(buf + 1, buf[0] == '*');
			printf("*\n");
			treeoutput ();
		} else if (good (buf)) {
			if (rootword[0] == 0) {
				printf ("*\n");	/* perfect match */
			} else {
				printf ("+ %s\n", rootword);
			}
		} else {
			makepossibilities (buf);
			if (possibilities[0][0]) {
				printf ("& ");
				for (i = 0; i < 10; i++) {
					if (possibilities[i][0] == 0)
						break;
					printf ("%s ", possibilities[i]);
				}
				printf ("\n");
			} else {
				printf ("#\n");
			}
		}
		if (sflag) {
			stop ();
			if (fflag) {
				rewind (stdout);
				creat (askfilename, 0666);
			}
		}
	}
}


copyout(cc, cnt)
char	**cc;
{
	while (--cnt >= 0) {
		if (*(*cc) == 0)
			break;
		if (!lflag)
			putc (*(*cc), outfile);
		(*cc)++;
	}

}
