/*
 * printdef.c - print a definition.
 *
 * We get one definition at a time, but have to get cross references from
 * all of them.  So, we "output" the definitions into a buffer, and the
 * cross references into answers.  Then we dump the stuff out.
 *
 * David A. Curry
 * Purdue University
 * Engineering Computer Network
 * April, 1986
 *
 * $Log:	printdef.c,v $
 * Revision 1.14  91/11/28  00:12:53  jik
 * Fix ^A's in output.
 * 
 * Revision 1.13  91/11/27  23:59:29  jik
 * Changes from ambar.
 * 
 * Revision 1.12  91/07/09  01:08:04  geoff
 * cb -sj, and other clean up
 * 
 * Revision 1.11  91/07/08  22:49:29  ambar
 * changed where stdio.h is included from.  some reformatting.  much
 * deletion of silly #ifdef SUNRPC stuff.
 * 
 * Revision 1.10  90/04/04  12:31:24  jik
 * Deleted a superfluous #else
 * 
 * Revision 1.9  89/04/09  01:37:34  jik
 * runons were not being handled correctly -- only one was getting printed.
 * Fixed so that all are now printed.
 * 
 * Revision 1.8  88/10/02  02:07:04  jik
 * fixed the bug which caused the server to barf when trying to read the
 * last definition in a definition file
 * 
 * Revision 1.7  88/03/01  06:09:15  ambar
 * I *think* I've solved the problem of printing synonyms.
 * 
 * Revision 1.6  88/02/29  05:05:03  ambar
 * tried to nuke all instances of '\r';
 * changed *stash to *curstash.
 * 
 * Revision 1.5  88/02/25  22:45:09  ambar
 * changed location of header files;
 * changed the definitions of outbuf, output, and ob, to be
 * (respectively) larger, no larger than necessary, and larger.
 * the definition of 'run' needs a 10K buffer, and larger definitions
 * may still be lurking.
 * 
 * Revision 1.4  88/02/23  23:52:53  ambar
 * changed definition of MAXLINE from 64 to 300.
 * 
 * Revision 1.3  88/02/23  23:24:51  ambar
 * 
 * initialized the array prefsuf in doprefsuf.
 * 
 * Revision 1.2  88/02/20  07:02:31  ambar
 * ifdef'ed for SUNRPC
 * 
 * Revision 1.1  87/06/16  20:26:15  jtkohl
 * Initial revision
 * 
 * Revision 1.2  86/12/26  22:02:49  davy
 * Changed to work with DBM files.
 */
#ifndef lint
static char	rcsid_printdef_c[] = "$Header: /afs/sipb.mit.edu/project/sipbsrc/src/webster/src/server/RCS/printdef.c,v 1.14 91/11/28 00:12:53 jik Exp $";
#endif

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <index.h>
#include <webster.h>
#ifdef SUNRPC
#include <sunrpcweb.h>
#endif SUNRPC

/*
 * largest definition I know of is 224 lines ('run'), so 300 for some
 * slop.
 */
#define MAXLINE		300		/* Max. lines in a definition */
#define LINELEN		128		/* Max. linelen of definition */
#define COLS		75		/* Max. cols to print on a line */
#define NEWLINE		'\001'		/* Newline marker (internal) */

/* imports */
extern char *strdup();

#ifndef SUNRPC
extern FILE *output;			/* output file pointer */
#else
char	output[BUFSIZ];			/* buffer for random output */
#endif !SUNRPC
extern char	*wordfiles[];		/* names of dictionary files */
extern char	*wordfiledir;		/* where the files live */

extern int	nanswers;		/* number of answers in answers */
extern int	answers_valid;		/* non-zero if numbers valid */
extern struct answer answers[MAXANSWERS]; /* the answers to searches */

int	nsaved;				/* number of lines saved */
char	*outch;				/* current "output" character */
char sacrificial[] = "  x x x x x x ";	/* for dumpoutput to stomp */
char	outbuf[10 * BUFSIZ];		/* output buffer */
char	*save[MAXLINE];			/* saved lines of defs. */

/*
 * printdef - get a definition from a file and return it as a string.
 */
#ifdef SUNRPC
char	*
#endif SUNRPC
printdef(ans)
struct answer *ans;
{
	FILE * fp;
	char	buf[BUFSIZ];
	register int	i, j;
#ifdef SUNRPC
	char	*dumpoutput();
#endif SUNRPC

	nsaved = 0;
	nanswers = 0;
	outch = outbuf;
	(void) sprintf(buf, "%s/%s", wordfiledir, wordfiles[ans->a_idx.i_file]);

	/*
	 * Open the file and seek to the start of the definition.
	 */
	if ((fp = fopen(buf, "r")) == NULL)
		return error(1, "Cannot open \"%s\".", buf);

	if (fseek(fp, (long)ans->a_idx.i_filepos, 0) == -1)
		return error(1, "Cannot find definition\n", (char *)NULL);

	if (fgets(buf, BUFSIZ, fp) == (char *) NULL)
		return error(1, "Error while reading definition file\n",
		     (char *)NULL);

	/*
	 * Read all the definitions for this word.  Save each one and
	 * process it before going to the next.
	 */
	do {
		save[nsaved++] = strdup(buf);
		save[nsaved] = "";
		if (fgets(buf, BUFSIZ, fp) == NULL && !feof(fp))
			return(error(1, "Error while reading definition file\n",
			     (char *) NULL));
		if (*buf == 'F') {
			process();
			if (sameword(ans->a_word, buf) == 0)
				break;
		}
	} while (!feof(fp));

	/*
	 * Catch the one condition where we don't format the word inside
	 * the loop.
	 */
	if (feof(fp) && (nsaved != 0)) {
		process();
	}

	/*
	 * Print the definition.
	 */
#ifdef SUNRPC
	(void) sprintf(output, "DEFINITION %d\n", nanswers);
	addtodef(output);
#else !SUNRPC
	fprintf(output, "DEFINITION %d\n", nanswers);
#endif !SUNRPC

	/*
	 * Cross references.
	 */
	j = 1;
	for (i = 0; i < nanswers; i++) {
		if (i > 0 &&
		    strcmp(answers[i].a_word, answers[i-1].a_word) == 0)
			continue;
#ifdef SUNRPC
		(void) sprintf(output, "%d %s\n", j++, answers[i].a_word);
		addtodef(output);
#else !SUNRPC
		fprintf(output, "%d %s\n", j++, answers[i].a_word);
#endif !SUNRPC
	}

	/*
	 * Text.
	 *
	 * so close, we're not going to sweat about this, since, after
	 * all, we weren't writing to that file.
	 */
	(void) fclose(fp);
	return(dumpoutput());
}

/*
 * formatword - format the definition from the stored format.
 */
formatword()
{
	doentry();			/* dotted entry name */
	dovariants(0);			/* level 0 variants */
	dopronunciation();		/* pronunciation */
	dopartofspeech();		/* part of speech */
	doprefsuf();			/* prefix/suffix */
	dovariants(1);			/* other variants */
	doetymology();			/* etymology */
	dolabelsanddefs();		/* labels and definitions */
	dorunons();			/* run-ons */
}

/*
 * doentry - print the entry name
 */
doentry()
{
	register int i;
	register char	*s, *t;
	char	dots[16], entname[64], homono[8], prefsuf[8];

	for (i = 0; i < nsaved; i++)
		if (save[i][0] == 'F')
			break;
	if (i >= nsaved)
		return;

	s = &save[i][2];
	t = entname;
	while (*s != ';')
		*t++ = *s++;
	*t = NULL;
	s++;
	t = homono;
	while (*s != ';')
		*t++ = *s++;
	*t = NULL;
	s++;
	t = prefsuf;
	while (*s != ';')
		*t++ = *s++;
	*t = NULL;
	s++;
	t = dots;
	while (*s != ';')
		*t++ = *s++;
	*t = NULL;

	if (atoi(homono) > 0) {
		if (atoi(homono) > 1)
			outchar(NEWLINE);

		outstr(homono);
		outstr(". ");
	}
	dodots(entname, dots);
}

/*
 * dovariants - print variants.
 */
dovariants(level)
int	level;
{
	register int i;
	char level1, level2;
	register char	*s, *t;
	char name[64], dots[16], accents[16];

	for (i = 0; i < nsaved; i++) {
		if (save[i][0] != 'V')
			continue;

		s = &save[i][2];
		t = name;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		s++;
		t = dots;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		s++;
		t = accents;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		s++;
		level1 = *s++;
		level2 = *s++;

		if (level == 0 && level1 != '0' ||
		    level != 0 && level1 == '0')
			continue;

		if (level1 == '2')
			outstr("; ");
		if (level2 == '1')
			outstr(" or ");
		else
			outstr(" also ");

		dodots(name, dots);
	}
}

/*
 * dopronunciation - print the pronunciation.
 */
dopronunciation()
{
	register int i;

	for (i = 0; i < nsaved; i++) {
		if (save[i][0] != 'P')
			continue;

		outstr(" \\");
		outstr(&save[i][2]);
		outstr("\\");
	}
}

/*
 * dopartofspeech - print the part of speech.
 */
dopartofspeech()
{
	register int i;
	register char	*s, *t;
	char	pos[16], posjoin[8], pos2[16];

	for (i = 0; i < nsaved; i++)
		if (save[i][0] == 'F')
			break;
	if (i >= nsaved)
		return;

	s = &save[i][2];
	for (i = 5; i-- > 0; s++)
		while (*s != ';')
			s++;
	t = pos;
	while (*s != ';')
		*t++ = *s++;
	*t = NULL;
	s++;
	t = posjoin;
	while (*s != ';')
		*t++ = *s++;
	*t = NULL;
	s++;
	t = pos2;
	while ((*s != '\r') && (*s != '\n'))
		*t++ = *s++;
	*t = NULL;

	outchar(' ');
	outstr(pos);

	if (*posjoin == '2') {
		outstr("(or ");
		outstr(pos2);
		outchar(')');
	} else if (*posjoin == '_') {
		outstr("or ");
		outstr(pos2);
	}
}

/*
 * doprefsuf - print "prefix" or "suffix".
 */
doprefsuf()
{
	register int i;
	char	prefsuf[8];
	register char	*s, *t;

	/* death to uninitialized variables */
	for (i = 0; i < 8; i++)
		prefsuf[i] = 0;

	for (i = 0; i < nsaved; i++)
		if (save[i][0] == 'F')
			break;
	if (i >= nsaved)
		return;

	s = &save[i][2];
	for (i = 2; i-- > 0; s++)
		while (*s != ';')
			s++;

	t = prefsuf;
	while (*s != ';')
		*t++ = *s++;
	s++;

	if (*prefsuf == 'p')
		outstr(" prefix");
	if (*prefsuf == 's')
		outstr(" suffix");
}

/*
 * doetymology - print etymology.
 */
doetymology()
{
	register int i;

	for (i = 0; i < nsaved; i++) {
		if (save[i][0] != 'E')
			continue;

		outstr(" [");
		outstr(&save[i][2]);
		outchar(']');
	}
}

/*
 * dolabelsanddefs - print labels and definitions.
 */
dolabelsanddefs()
{
	int len = 0;
	register int i, cnt;
	register char	*s, *t;
	char	snsnum[8], snsletter[8], snssubno[8];
	char	lsnsnum[8], lsnsletter[8], lsnssubno[8];

	for (i = 0; i < nsaved; i++) {
		if (save[i][0] != 'L' && save[i][0] != 'D' &&
		    save[i][0] != 'C' && save[i][0] != 'S')
			continue;

		switch (save[i][0]) {
		case 'S':
			dosynonyms();			/* synonyms */
			break;
		case 'C':
			if (len == 79)
				outchar(' ');
			outstr(&save[i][2]);
			break;
		case 'D':
			s = &save[i][2];
			t = snsnum;
			while (*s != ';')
				*t++ = *s++;
			*t = NULL;
			s++;
			t = snsletter;
			while (*s != ';')
				*t++ = *s++;
			*t = NULL;
			s++;
			t = snssubno;
			while (*s != ';')
				*t++ = *s++;
			*t = NULL;
			s++;

			if (strcmp(snsnum, lsnsnum) != 0 ||
			    strcmp(snsletter, lsnsletter) != 0 ||
			    strcmp(snssubno, lsnssubno) != 0) {
				outchar(' ');

				if (atoi(snsnum) != 0) {
					outstr(snsnum);
					outstr(snsletter);
					outstr(snssubno);
				}

				outstr(": ");
			}

			while (*s != ';')
				s++;
			s++;

			outstr(s);
			break;
		case 'L':
			s = &save[i][2];
			for (cnt = 3; cnt-- > 0; s++)
				while (*s != ';')
					s++;

			outchar(' ');
			outstr(s);
			outchar(' ');
			break;
		}

		len = xlen(save[i]);
	}
}

/*
 * dorunons - handle run-ons.
 */
dorunons()
{
	register int i;
	register char	*s, *t;
	char accents[16], dots[16], name[64], pos[8], posjoin[8], pos2[8];

	for (i = 0; i < nsaved; i++) {
		for (; i < nsaved; i++)
			if (save[i][0] == 'R')
				break;
		if (i >= nsaved)
			return;

		s = &save[i][2];
		t = name;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		s++;
		t = dots;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		s++;
		t = accents;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		s++;
		t = pos;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		s++;
		t = posjoin;
		while (*s != ';')
			*t++ = *s++;
		*t = NULL;
		t = pos2;
		while ((*s != '\r') && (*s != '\n'))
			*t++ = *s++;
		*t = NULL;

		outstr(" - ");
		dodots(name, dots);

		outchar(' ');
		outstr(pos);

		if (*posjoin == '2') {
			outstr("(or ");
			outstr(pos2);
			outchar(')');
		} else if (*posjoin == '_') {
			outstr("or ");
			outstr(pos2);
		}
	}
}

/*
 * dosynonyms - print synonyms.
 */
dosynonyms()
{
	register int i;

	for (i = 0; i < nsaved; i++) {
		if (save[i][0] != 'S')
			continue;

		outstr(" SYN ");
		outstr(&save[i][2]);
	}
}

/*
 * dodots - print the entry name with dots in the syllables.
 */
dodots(str, dots)
char	*str, *dots;
{
	register int i;
	register char	*s, *t;

	for (s = dots; *s != '\0'; s++)
		if (s == dots)
			*s -= '0';
		else
			*s = *s - '0' + *(s - 1);

	i = 1;
	s = dots;
	t = str;
	while (*t) {
		outchar(*t++);
		if (i++ == *s) {
			outchar('.');
			s++;
		}
	}
}

/*
 * savexrefs - save cross references.
 */
savexrefs()
{
	extern char *index();
	register int i;
	register char	*s;

	for (i = 0; i < nsaved; i++) {
		if (save[i][0] != 'X')
			continue;

		s = index(save[i], ';');
		if (s != NULL)		/* semi-colon? */
			*s = '\0';

		easysearch(save[i] + 2, nanswers);
	}
}

/*
 * sameword - return non-zero if two words are the same.
 */
sameword(word, buf)
register char	*word;
register char	*buf;
{
	char tmp[64];
	register char	*s, *t;

	s = buf + 2;
	for (t = tmp; *s != ';'; s++, t++)
		*t = isupper(*s)? tolower(*s): *s;
	*t = '\0';
	return strcmp(word, tmp) == 0;
}

/*
 * xlen - strlen that doesn't count CRNL.
 */
xlen(s)
register char	*s;
{
	register int len = 0;

	while (*s != '\0' && *s != '\r' && *s != '\n') {
		len++;
		s++;
	}
	return len;
}

outchar(c)
register char c;
{
	*outch++ = c;
}

outstr(s)
register char	*s;
{
	for (; *s != '\0'; s++)
		if (*s != '\r' && *s != '\n')
			*outch++ = *s;
}

/*
 * dumpoutput - print the output.
 */
#ifdef SUNRPC
char	*
#endif SUNRPC
dumpoutput()
{
	extern char *overstrike();
	register char *s, *t, *oc;
	char	ob[10 * BUFSIZ];		/* FINAL output buffer! */

	oc = outch;
	outch = ob;

	/*
	 * Copy to new buffer, handling overstrikes.
	 */
	for (s = outbuf; s < oc; s++)
		if (*s == '\b')
			s = overstrike(s);
		else
			*outch++ = *s;

	/* this is buggy; it can walk past the start of outbuf */
	while (*--outch == ' ')
		;
	*++outch = '\n';

	/*
	 * Print the buffer within COLS columns.
	 */
	s = ob;
	while (s < outch) {
		t = &s[COLS];
		if (t > outch)
			t = outch;

		while (*t != ' ' && *t != '\n' && *t != NEWLINE)
			t--;

		while (s <= t) {
			if (*s == '\n' || *s == NEWLINE)
				break;
#ifdef SUNRPC
			addctodef(*s++);
		}
		addctodef('\n');
		if (*s == NEWLINE)
			s++;
		else
			addtodef("   ");
	}
	*curptr++ = '\0';
	howlong = maxdeflen = 0;
	return(curstash);
#else !SUNRPC
			putc(*s++, output);
		}
		fprintf(output, "\n");
		if (*s == NEWLINE)
			s++;
		else
			fprintf(output, "   ");
	}
	putc(EOFCH, output);
#endif !SUNRPC
}

/*
 * overstrike - try to fake overstrike characters.  You figure it out.
 */
char *
overstrike(s)
char	*s;
{
	extern char *apl(), *bsp(), *greek();

	switch (*--outch) {
	case '(':
		switch (*++s) {
		case 'W':
		case 'X':
		case 'Y':
		case 'M':
		case 'I':
		case 'J':
		case 'K':
		case 'A':
		case 'B':
		case 'C':
			break;
		case 'R':
			s = apl(s);
			break;
		case 'G':
			s = greek(s);
			break;
		case 'Q':
			*outch++ = '<';
			break;
		case '|':
			*outch++ = '[';
			break;
		}
		break;
	case ')':
		switch (*++s) {
		case 'W':
		case 'X':
		case 'Y':
		case 'M':
		case 'I':
		case 'J':
		case 'K':
		case 'A':
		case 'B':
		case 'C':
		case 'G':
		case 'R':
			break;
		case 'Q':
			*outch++ = '>';
			break;
		case '>':
			*outch++ = '}';
			break;
		}
		break;
	case '<':
		switch (*++s) {
		case '(':
			*outch++ = '{';
			break;
		case '\'':
			*outch++ = '`';
			break;
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
			*outch++ = *s;
			*outch++ = '\'';
			break;
		}
		break;
	case '|':
		switch (*++s) {
		case ')':
			*outch++ = ']';
			break;
		case '-':
		case '=':
			*outch++ = *s;
			break;
		case 'q':
		case 'S':
			break;
		case 'B':
			s = bsp(s);
			break;
		}
		break;
	case '+':
		s++;
		*outch++ = '+';
		*outch++ = '/';
		*outch++ = '-';
		break;
	case ';':
		s += 5;
		*outch++ = 'c';
		*outch++ = '~';
		break;
	case '~':
		*outch++ = *++s;
		*outch++ = '^';
		break;
	case '>':
		switch (*++s) {
		case '\'':
			*outch++ = '\'';
			break;
		default:
			*outch++ = *s;
			*outch++ = '`';
			break;
		}
		break;
	case '\'':
		switch (*++s) {
		case '"':
			*outch++ = '\'';
			break;
		case 'b':
			break;
		case 'd':
			*outch++ = 'o';
			break;
		}
		break;
	case '=':
		s++;
		break;
	case '"':
		*outch++ = *++s;
		*outch++ = ':';
		break;
	case '-':
		switch (*++s) {
		case ':':
			*outch++ = '/';
			break;
		case 'm':
		case 'n':
			*outch++ = '-';
			break;
		case '3':
			*outch++ = '.';
			*outch++ = '.';
			*outch++ = '.';
			break;
		}
		break;
	case ':':
		s++;
		*outch++ = ':';
		break;
	}
	return s;
}

/*
 * greek - fake greek characters.
 */
char *
greek(s)
register char	*s;
{
	register char *t = NULL;

	switch (*++s) {
	case '/':
		switch (*++s) {
		case 'a':
			t = "<alpha>";
			break;
		case 'b':
			t = "<beta>";
			break;
		case 'g':
			t = "<gamma>";
			break;
		case 'd':
			t = "<delta>";
			break;
		case 'e':
			t = "<epsilon>";
			break;
		case 'z':
			t = "<zeta>";
			break;
		case 'h':
			t = "<eta>";
			break;
		case 'i':
			t = "<iota>";
			break;
		case 'k':
			t = "<kappa>";
			break;
		case 'l':
			t = "<lambda>";
			break;
		case 'm':
			t = "<mu>";
			break;
		case 'n':
			t = "<nu>";
			break;
		case 'x':
			t = "<xi>";
			break;
		case 'o':
			t = "<omicron>";
			break;
		case 'p':
			t = "<pi>";
			break;
		case 'r':
			t = "<rho>";
			break;
		case 's':
			t = "<sigma>";
			break;
		case 't':
			t = "<tau>";
			break;
		case 'u':
			t = "<upsilon>";
			break;
		case 'c':
			t = "<chi>";
			break;
		case 'v':
			t = "<psi>";
			break;
		case 'w':
			t = "<omega>";
			break;
		case 'A':
			t = "<ALPHA>";
			break;
		case 'B':
			t = "<BETA>";
			break;
		case 'G':
			t = "<GAMMA>";
			break;
		case 'D':
			t = "<DELTA>";
			break;
		case 'E':
			t = "<EPSILON>";
			break;
		case 'Z':
			t = "<ZETA>";
			break;
		case 'H':
			t = "<ETA>";
			break;
		case 'I':
			t = "<IOTA>";
			break;
		case 'K':
			t = "<KAPPA>";
			break;
		case 'L':
			t = "<LAMBDA>";
			break;
		case 'M':
			t = "<MU>";
			break;
		case 'N':
			t = "<NU>";
			break;
		case 'X':
			t = "<XI>";
			break;
		case 'O':
			t = "<OMICRON>";
			break;
		case 'P':
			t = "<PI>";
			break;
		case 'R':
			t = "<RHO>";
			break;
		case 'S':
			t = "<SIGMA>";
			break;
		case 'T':
			t = "<TAU>";
			break;
		case 'U':
			t = "<UPSILON>";
			break;
		case 'C':
			t = "<CHI>";
			break;
		case 'V':
			t = "<PSI>";
			break;
		case 'W':
			t = "<OMEGA>";
			break;
		}
		break;
	case '-':
		switch (*++s) {
		case '0':
			t = "<theta>";
			break;
		case 's':
			t = "<sigma>";
			break;
		case 'O':
			t = "<THETA>";
			break;
		}
		break;
	case '|':
		switch (*++s) {
		case 'q':
			t = "<phi>";
			break;
		}
		break;
	}

	while (*t)
		*outch++ = *t++;
	return s;
}

/*
 * apl - fake APL characters.
 */
char *
apl(s)
char	*s;
{
	switch (*++s) {
	case '-':
		switch (*++s) {
		case '>':
			*outch++ = '-';
			*outch++ = '>';
			break;
		case 'X':
			*outch++ = 'x';
			break;
		case '/':
			*outch++ = '\\';
			break;
		}
		break;
	case '@':
		*outch++ = '~';
		break;
	case '\031':
		*outch++ = ':';
		break;
	case '\032':
		*outch++ = '^';
		break;
	}
	return s;
}

/*
 * bsp - handle backspace characters.
 */
char *
bsp(s)
char	*s;
{
	switch (*++s) {
	case '.':
		*outch++ = '.';
		break;
	case '(':
		*outch++ = '\'';
		break;
	case ')':
		*outch++ = '`';
		break;
	case '-':
		*outch++ = '-';
		break;
	case ',':
		*outch++ = 'c';
		*outch++ = '~';
		break;
	case ':':
		*outch++ = ':';
		break;
	case '\'':
		*outch++ = '^';
		break;
	case '_':
		break;
	default:
		s--;
		break;
	}
	return s;
}

process()
{
	formatword();
	savexrefs();
	while (nsaved > 0) {
		free(save[--nsaved]);
		save[nsaved] = NULL;
	}
}
