/*
** tr2latex - troff to LaTeX converter
** COPYRIGHT (C) 1987 Kamal Al-Yahya, 1991,1992 Christian Engel
**
** Module: subs.c
**
** These subroutines do (in general) small things for the translator.
** They appear in alphabetical order and their names are unique in the
** first six characters.
*/

#include	"setups.h"
#include	"protos.h"
#include	"simil.h"
#include	"greek.h"
#include	"flip.h"
#include	"forbid.h"
#include	"maths.h"
#include	"macros.h"
#include	"special.h"

int get_tbl_txt_entry(char *pin, char *w);

extern int def_count;
extern int mydef_count;

/* compile-time counting of elements */
const int GRK_count = (sizeof(GRK_list) / sizeof(GRK_list[0]));
const int sim_count = (sizeof(sim_list) / sizeof(sim_list[0]));
const int flip_count = (sizeof(flip_list) / sizeof(flip_list[0]));
const int forbd_count = (sizeof(forbid) / sizeof(forbid[0]));
const int mathcom_count = (sizeof(math) / sizeof(struct math_equiv));
const int macro_count = (sizeof(macro) / sizeof(struct macro_table));
const int special_count = (sizeof(special) / sizeof(struct special_table));

/*
** alternate fonts (manual macro)
*/
char *alternate(char *pin, char *pout, char *w)
{
	int which = 1;
	char font[MAXWORD + 1], font1[MAXWORD + 1], font2[MAXWORD + 1], ww[MAXWORD + 1], tmp[MAXWORD + 1];

	tmp[0] = EOS;
	switch (w[1]) {
	case 'R':	strcpy(font1, "\\rm"); break;
	case 'I':	strcpy(font1, "\\it"); break;
	case 'B':	strcpy(font1, "\\bf"); break;
	}
	switch (w[2]) {
	case 'R':	strcpy(font2, "\\rm"); break;
	case 'I':	strcpy(font2, "\\it"); break;
	case 'B':	strcpy(font2, "\\bf"); break;
	}

	strcpy(font, font1);
	while (*pin != '\n' && *pin != EOS) {
		pin += get_arg(pin, ww, 1);
		if (which == 1) {
			snprintf(tmp,sizeof(tmp),"{%s %s}", font1, ww);
			which = 2;
		}
		else {
			snprintf(tmp,sizeof(tmp),"{%s %s}", font2, ww);
			which = 1;
		}
		pout = strapp(pout, tmp);
		while (*pin == ' ' || *pin == '\t')
			pin++;
	}
	return (pout);
}



/*
** check if w is in the GREEK list
*/
int CAP_GREEK(char *w)
{
	int i;

	for (i = 0; i < GRK_count; i++) {
		if (strcmp(GRK_list[i], w) == 0)
			return (1);
	}
	return (-1);
}

int prefix(char *small, char *big)
{
	char c;
	while ((c = *small++) == *big++)
		if (c == EOS)
			return (1);
	return (c == EOS);
}

int expflag, ctrflag, boxflag, dboxflag, tab, linsize, allflag, DELIM1, DELIM2;

struct optstr {
	char *optnam;
	int *optadd;
};
struct optstr options[] = {
	{ "tab",		&tab },
	{ "TAB",		&tab },
	{ "allbox",		&allflag },
	{ "ALLBOX",		&allflag },
	{ "box",		&boxflag },
	{ "BOX",		&boxflag },
	{ "center",		&ctrflag },
	{ "CENTER",		&ctrflag },
	{ "delim",		&DELIM1 },
	{ "DELIM",		&DELIM1 },
	{ "doublebox",  &dboxflag },
	{ "DOUBLEBOX",  &dboxflag },
	{ "expand",		&expflag },
	{ "EXPAND",		&expflag },
	{ "linesize",	&linsize },
	{ "LINESIZE",	&linsize },
	{ EOS,			0 }
};

/*
** get single option
*/
int get_option(char *pin, char *w) {
	char c;
	int i, j;
	for (j=0; *pin == ',' || *pin == ' ' || *pin == '\t'; j++, pin++);
	for (i = 0; (c = *pin++) != EOS && 
		(c != ' ' && c != '\t' && c != ',' && c != ';' && c != '(') && i < MAXWORD; i++) {
		w[i] = c;
	}
	w[i] = EOS;
	return (j+i);
}

/*
** get table options until ';'
*/
int get_tbl_options(char *pin, struct optstr *options)
{
	char line[MAXLINE], *cp, nb[25], *t;
	struct optstr *lp;
	int c, ci, len = 0, found;
	char opt[MAXWORD];

	//tmp[0] = EOS;
	for (lp = options; lp->optnam; lp++)
		*(lp->optadd) = 0;
	tab = '\t';							/* default is tab char */
	len = get_line(pin, line, 0);
	if (strchr(line, ';') == 0)
		return(0);
	cp = line;
	while ((c = *cp) != ';') {
		cp += get_option(cp, opt);
		found = 0;
		for (lp = options; lp->optadd; lp++) {
			if (strcmp(lp->optnam, opt) == 0) {
				*(lp->optadd) = 1;
				while (*cp == ' ') cp++;
				t = nb;
				if (*cp == '(') {
					while ((ci = *++cp) != ')')
						*t++ = ci;
					cp++;
				}
				*t++ = 0; *t = 0;
				if (lp->optadd == &tab) {
					if (nb[0])
						*(lp->optadd) = nb[0];
				} else
				if (lp->optadd == &linsize) {
					*(lp->optadd) = atoi(nb);
				} else
				if (lp->optadd == &DELIM1) {
					DELIM1 = nb[0];
					DELIM2 = nb[1];
				}
				found = 1;
				break;
			}
		}
		if (!found)
			fprintf(stderr, "Illigel option in table definition: %s\n", opt);
	}
	return(len);
}


/*
** translate table
** arguments:
**  pin
**  pout
**	offset    amount to offset pin
*/
char *do_table (char *pin, char *pout, int *offset)
{
	char w[MAXLINE+1], ww[MAXLINE+1], format[MAXWORD+1], tmp[MAXLINE+1];
	char *ptr;
	int i, fc = 0, columns = 0;

	tmp[0] = EOS;
	ptr = pin;							/* remember where we started */

	pin += get_tbl_options(pin, options);
	if (boxflag || dboxflag || allflag)
		format[fc++] = '|';
	if (dboxflag)
		format[fc++] = '|';
	pin = skip_line(pin);
	while (*pin != EOS) {				/* get the LAST format line */
		pin += get_line(pin, w, 0);
		if (w[strlen(w) - 1] == '.')
			break;
		else /* not a format line */
			pin = skip_line(pin);
	}
	for (i = 0; w[i] != '.'; i++) {
		switch (w[i]) {
			case ' ':
			case '\t':
				continue;
			case 'l':
			case 'L':
			case 'n':
			case 'N':
			case 'a':
			case 'A':
				format[fc++] = 'l';
				break;
			case 'r':
			case 'R':
				format[fc++] = 'r';
				break;
			case 'c':
			case 'C':
			case 's':
			case 'S':
				format[fc++] = 'c';
				break;
			case '|':
				format[fc++] = '|';
				break;
			case ',':
				fc = 0;	/* use only last format */
				columns = 0;
				continue;
			default:
				continue;
		}
		if (allflag)
			format[fc++] = '|';
		columns++;
	}
	if ((boxflag || dboxflag) && !allflag)
		format[fc++] = '|';
	if (dboxflag)
		format[fc++] = '|';
	if (columns == 0) {
		fprintf (stderr, "Sorry, I cannot do tables without a format line\n"
						 "Doing plain translation of table, lines will be commented\n"
						 "You need to fix it yourself\n");
		while (*pin != EOS) {
			(void) getword (pin, w);
			if (strcmp (w,".TE") ==  0) {
				pin += 4;
				break;
			}
			pin += get_line (pin, w, 1);
			*pout++ = '%';
			pout = strapp (pout, w);
			pout = strapp (pout, "\n");
			pin++;		/* skip the \n */
		}
		*offset = pin - ptr;
		return (pout);
	}
	format[fc] = EOS;
	pin = skip_line(pin);
	pout = strapp(pout, "\\par\\noindent\n");
	if (linsize > 0) {
		snprintf(tmp, sizeof(tmp), "{\\arrayrulewidth=%dpt\n", linsize);
		pout = strapp(pout, tmp);
	}
	if (ctrflag)
		pout = strapp(pout, "\\begin{center}\n");
	snprintf(tmp, sizeof(tmp), "\\begin{tabular}{%s}\n", format);
	pout = strapp (pout, tmp);
	if (boxflag || dboxflag || allflag)
		pout = strapp(pout, "\\hline\n");
	if (dboxflag)
		pout = strapp(pout, "\\hline\n");
	while (*pin != EOS) {
		(void)getword(pin, w);
		if (strcmp(w, ".sp") == 0) {
			pin += get_line(pin, ww, 1);
			snprintf(tmp, sizeof(tmp), "%s\n\\\\ ", ww);
			pout = strapp(pout, tmp);
			continue;
		} else if (strcmp(w, "_") == 0) {
			pout = strapp(pout, "\\hline\n");
			pin = skip_line(pin);
			continue;
		} else if (strcmp(w, "=") == 0) {
			pout = strapp(pout, "\\hline\n\\hline\n");
			pin = skip_line(pin);
			continue;
		} else if (strcmp(w, "\n") == 0) {
			pin++;
			continue;
		}
		for (i = 0; i < columns - 1; i++) {
			(void) getword (pin, w);
			if (strcmp (w, ".TE") == 0) {
				pin += 4;
				if (i == 0 && !boxflag && !dboxflag && !allflag) {
					pout -= 3;		/* take back the \\ and the \n */
					pout = strapp(pout, "\n");
				}
				if (boxflag)
					pout = strapp(pout, "\\hline\n");
				if (dboxflag)
					pout = strapp(pout, "\\hline\n");
				pout = strapp(pout,"\\end{tabular}\n");
				if (ctrflag)
					pout = strapp(pout, "\\end{center}\n");
				if (linsize > 0)
					pout = strapp(pout, "}\n");
				pout = strapp(pout, "\\par\n");
				*offset = pin - ptr;
				return (pout);
			}
			(void)get_line(pin, w, 0);
			if (strncmp(w, "T{", 2) == 0) {
				pin = skip_line(pin);
				pin += get_tbl_txt_entry(pin, w);
				troff_tex(w, ww, 0, 0);
				snprintf(tmp, sizeof(tmp), "\\parbox[t]{%5.4f\\hsize}{\n%s} & ", 1.0 / columns, ww);
				pin += get_table_entry(pin, w, tab);
			}
			else {
				pin += get_table_entry(pin, w, tab);
				troff_tex(w, ww, 0, 1);
				snprintf(tmp, sizeof(tmp), "%s & ", ww);
			}
			pin++;		/* skip tab */
			pout = strapp(pout,tmp);
		} /* end for */
		pin += skip_white(pin);
		(void) getword (pin, w);
		if (strcmp(w, ".TE") == 0) {
			fprintf(stderr, "Oops! I goofed. I told I you I am not very good at tables\n"
							"I've encountered an unexpected end for the table\n"
							"You need to fix it yourself\n");
			pin = skip_line(pin);
			if (boxflag || allflag)
				pout = strapp(pout, "\\hline\n");
			if (dboxflag)
				pout = strapp(pout, "\\hline\n");
			pout = strapp(pout,"\\end{tabular}\n");
			if (ctrflag)
				pout = strapp(pout, "\\end{center}\n");
			if (linsize > 0)
				pout = strapp(pout, "}\n");
			pout = strapp(pout, "\\par\n");
			*offset = pin - ptr;
			return(pout);
		}
		//pin += skip_white(pin);
		(void)get_line(pin, w, 0);
		if (strncmp(w, "T{", 2) == 0) {
			pin = skip_line(pin);
			pin += get_tbl_txt_entry(pin, w);
			troff_tex(w, ww, 0, 0);
			snprintf(tmp, sizeof(tmp), "\\parbox[t]{%5.4f\\hsize}{\n%s} \\\\\n", 1.0 / columns, ww);
			//pin += get_table_entry(pin, w, tab);
		}
		else {
			pin += get_table_entry(pin, w, '\n');
			troff_tex(w, ww, 0, 1);
			snprintf(tmp, sizeof(tmp), "%s \\\\\n", ww);
		}
		pin++;		/* skip tab */
		pout = strapp (pout, tmp);
		if (allflag)
			pout = strapp(pout, "\\hline\n");
	}
	fprintf (stderr, "Oops! I goofed. I told I you I am not very good at tables\n"
					 "File ended and I haven't finished the table!\n"
					 "You need to fix it yourself\n");
	*offset = pin - ptr;
	pout = strapp (pout, "\\end{tabular}\n\\par\n");
	return (pout);
}


/*
** end current environment
*/
char *end_env (char *pout)
{
	if (IP_stat[RSRE]) {
		IP_stat[RSRE] = 0;
		pout = strapp (pout, "\\end{IPlist}");
	}
	if (QP_stat) {
		QP_stat = 0;
		pout = strapp (pout, "\\end{quotation}");
	}
	if (TP_stat) {
		TP_stat = 0;
		pout = strapp (pout, "\\end{TPlist}");
	}
	return(pout);
}


/*
** set flag for current environment
*/
void envoke_stat (int par)
{

	switch(par) {
	case 2:
		IP_stat[RSRE] = 1;
		break;
	case 3:
		TP_stat = 1;
		break;
	case 4:
		QP_stat = 1;
		break;
	default:
		break;
	}
}



/*
** do the flipping
*/
char * flip (char *pout, char *w)
{
	int lb=0, rb=0;
	char ww[MAXWORD+1], tmp[MAXWORD+1];

	ww[0] = EOS;
	tmp[0] = EOS;
	pout--;
	while (isspace (*pout))
		pout--;
	while (1) {
		if (*pout == '{') {
			lb++;
			if (lb > rb)
				break;
		}
		if (*pout == '}')
			rb++;
		if (rb == 0) {
			if (! isspace (*pout) && *pout != '$') {
				pout--;
				continue;
			}
			else
				break;
		}
		pout--;
		if (lb == rb && lb != 0)
			break;
	}
	pout++;
	if (*pout == '\\') {
		pout++;
		(void) getword (pout, tmp);
		snprintf(ww, sizeof(ww), "\\%s", tmp);
		pout--;
	}
	else if (*pout == '{')
		(void) get_brace_arg (pout, ww);
	else
		(void) getword (pout, ww);
	*pout = EOS;
	snprintf(tmp,sizeof(tmp),"\\%s %s", w, ww);
	pout = strapp (pout, tmp);
	return (pout);
}



/*
** take care of things like x hat under
*/
char * flip_twice (char *pout, char *w, char *ww)
{
	int lb=0, rb=0;
	char tmp1[MAXWORD+1], tmp2[MAXWORD+1];

	tmp1[0] = EOS;		tmp2[0] = EOS;
	pout--;
	while (*pout == ' ' || *pout == '\t' || *pout == '\n')
		pout--;
	while (1) {
		if (*pout == '{') {
			lb++;
			if (lb > rb)
				break;
		}
		if (*pout == '}')
			rb++;
		if (rb == 0) {
			if (! isspace (*pout) && *pout != '$') {
				pout--;
				continue;
			}
			else
				break;
		}
		pout--;
		if (lb == rb && lb != 0)
			break;
	}
	pout++;
	if (*pout == '\\') {
		pout++;
		(void) getword(pout,tmp2);
		snprintf(tmp1, sizeof(tmp1), "\\%s", tmp2);
		pout--;
	}
	else if (*pout == '{')
		(void) get_brace_arg(pout,tmp1);
	else
		(void) getword(pout,tmp1);
	*pout = EOS;
	snprintf(tmp2, sizeof(tmp2), "\\%s{\\%s %s}", w, ww, tmp1);
	pout = strapp(pout,tmp2);
	return(pout);
}



/*
** get argument
** arguments:
**  rec=1 means recursive
*/
int	get_arg (register char *pin, char *w, int rec)
{
	int c,len,i;
	char ww[MAXWORD+1];
	int delim;
	int escape = 0;

	len=0;
	while ((c = *pin) == ' ' || c == '\t') {	/* skip spaces and tabs */
		pin++;
		len++;
	}
	i = 0;
	if (*pin == '{' || *pin == '\"') {
		if (*pin == '{')
			delim = '}';
		else
			delim = '\"';
		pin++;
		len++;
		while ((c = *pin++) != EOS && (escape || c != delim) && i < MAXWORD) {
			/*if (c == ' ' && delim == '\"')
				ww[i++] = '\\';*/
			if (escape && c == 't')
				ww[i++] = '\t';
			else
				ww[i++] = (char)c;
			if (c == '\\' && !escape)
				escape = 1;
			else {
				escape = 0;
			}
			len++;
		}
		len++;
	}
	else {
		while ((c = *pin++) != EOS && !isspace(c)
			   /* && c != '$' && c != '}' */ && i < MAXWORD) {
			if (math_mode && c == '~')
				break;
			ww[i++] = (char)c;
			len++;
		}
	}
	ww[i] = EOS;
	if (rec == 1)				/* check if recursion is required */
		troff_tex(ww, w, 1, 1);
	else
		strcpy(w, ww);
	return(len);
}


/*
** get all arguments
** arguments:
**  rec=1 means recursive
*/
int	get_allargs (register char *pin, char ***ppw, int rec)
{
	int c, i;
	static char *ww [MAXARGS];
	char w[MAXWORD+1], *instart;
	int nww;
	int delim;

	instart = pin;
	for (nww = 0; ; nww++) {
		while ((c = *pin) == ' ' || c == '\t')	/* skip spaces and tabs */
			pin++;
		if (c == '\n') {
			pin++;
			ww [nww] = EOS;
			break;
		}
		ww [nww] = pin;
		i=0;
		if (*pin == '{' || *pin == '\"') {
			if (*pin == '{')
				delim = '}';
			else
				delim = '\"';
			ww [nww] = ++pin;
			while ((c = *pin++) != EOS && c != delim && i < MAXWORD)
				/* EMPTY */
				;
			pin [-1] = EOS;
		}
		else {
			while ((c = *pin++) != EOS && !isspace(c)
				   /* && c != '$' && c != '}' */ && i < MAXWORD) {
				if (math_mode && c == '~')
					break;
			}
			pin [-1] = EOS;
			if (c == '\n') {
				ww [nww + 1] = EOS;
				break;
			}
		}
	}
	if (rec == 1) {				/* check if recursion is required */
		for (i = 0; ww [i]; i++) {
			if (ww [i] && *ww [i]) {
				troff_tex (ww [i], w, 1, 1);
				if (strcmp (ww [i], w) != 0)
					ww [i] = strdup(w);
			}
		}
	}
	*ppw = ww;
	return (pin - instart);
}




/*
** get argument surrounded by braces
*/
void get_brace_arg (char *buf, char *w)
{
	int c,i, lb=0, rb=0;

	i=0;
	while ((c = *buf++) != EOS) {
		w[i++] = (char)c;
		if (c == '{')	lb++;
		if (c == '}')	rb++;
		if (lb == rb)	break;
	}
	w[i] = EOS;
}

/*
** get "define" or .de word
** arguments:
**  pin    delimited by space only
**  w      delimited by space only
*/
int get_defword (char *pin, char *w, int *illegal)
{
	int c,i;

	*illegal = 0;
	for (i=0; (c = *pin++) != EOS && !isspace (c) && i < MAXWORD; i++) {
		w[i] = (char)c;
		if (isalpha(c) == 0)
			*illegal = 1;	/* illegal TeX macro */
	}
	w[i] = EOS;
	if (*illegal == 0 && is_forbid(w) >= 0)
		*illegal=1;
	return(i);
}


/*
** get the rest of the line
** arguments:
**  rec=1 means recursion is required
*/
int get_line (char *pin, char *w, int rec)
{
	int c,i,len;
	int escape=0;
	char ww[MAXLINE+1];

	i=0;
	len=0;
	c = *pin;
	while ((c = *pin++) != EOS && c != '\n' && len < MAXLINE) {
		if (de_arg > 0 && !rec && c == '\\' && !escape)
			escape = 1;
		else {
			escape = 0;
			ww[i++] = (char)c;
		}
		len++;
	}
	ww[i] = EOS;
	if (de_arg>0 && ww[0] == '.') {
		/* skip blanks */
		for (i=1; ww[i] == ' '; i++);
		strcpy(ww+1, ww+i);
	}
	//fprintf(stderr, "get_line:%s\n", ww);
	if (rec == 1)
		troff_tex(ww, w, 0, 1);
	else
		strcpy(w, ww);
	//fprintf(stderr, "get_line out:%s\n", w);
	return(len);
}


/*
** get multi-line argument
*/
int get_multi_line (char *pin, char *w)
{
	int len=0,l=0,lines=0;
	char tmp[MAXWORD+1];
	int c1,c2;

	w[0] = EOS;	tmp[0] = EOS;
	while (*pin != EOS) {
		c1 = *pin;
		c2 = *++pin;
		--pin;
		if (c1 == '.' && isupper(c2))
			break;
		lines++;
		if (lines > 1)
			strcat(w," \\\\\n");
		l = get_line(pin,tmp,1);
		strcat(w,tmp);
		len += l+1;
		pin += l+1;
	}
	len--;
	pin--;
	return(len);
}


/*
** get the macro substitution
*/
int get_mydef (char *pin, char *w)
{
	int c1,c2,l,len;
	char tmp[MAXLINE+1];
	char ww[MAXLINE*20+1];

	tmp[0] = EOS;
	ww[0] = EOS;
	len=1;
	while (*pin != EOS) {
		c1 = *pin;
		c2 = *++pin;
		--pin;
		if (c1 == '.' && c2 == '.')
			break;
		l = get_line(pin, tmp, 0);
		if (len+l+1 < MAXLINE*20) {
			strcat(ww, tmp);
			strcat(ww, "\n");
			len += l+1;
			pin += l+1;
		} else break;
	}
	troff_tex(ww, w, 0, 1);
	return(len);
}


/*
** get N lines
*/
int get_N_lines (char *pin, char *w, int N)
{
	int len=0,l=0,lines=0;
	char tmp[MAXWORD+1];

	w[0] = EOS;	tmp[0] = EOS;
	while (*pin != EOS && lines < N) {
		lines++;
		if (lines > 1)
			strcat(w," \\\\\n");
		l = get_line(pin, tmp, 1);
		strcat(w,tmp);
		len += l+1;
		pin += l+1;
	}
	len--;
	pin--;
	return(len);
}


/*
** get text surrounded by quotes in math mode
*/
int get_no_math (char *pin, char *w)
{
	int c,i,len;

	len = 0;
	for (i=0; (c = *pin++) != EOS && c != '\"' && i < MAXWORD; i++) {
		if (c == '{' || c == '}') {
			w[i] = '\\';
			w[++i] = (char)c;
		}
		else
			w[i] = (char)c;
		len++;
	}
	w[i] = EOS;
	return(len);
}


/*
** get the denominator of over
*/
char *get_over_arg (char *pin, char *ww)
{
	char w[MAXWORD+1], tmp1[MAXWORD+1], tmp2[MAXWORD+1];
	int len;

	w[0] = EOS;
	tmp1[0] = EOS;
	tmp2[0] = EOS;
	pin += getword (pin,tmp1);		/* read first word */
	pin += skip_white (pin);
	len = getword (pin, tmp2);		/* read second word */
	strcat(w,tmp1);	strcat(w," ");

	/* as long as there is a sup or sub read the next two words */
	while (strcmp (tmp2, "sub") == 0 || strcmp (tmp2, "sup") == 0) {
		pin += len;
		strcat (w, tmp2);
		strcat (w, " ");
		pin += skip_white (pin);
		pin += getword (pin, tmp1);
		strcat (w, tmp1);
		strcat (w, " ");
		pin += skip_white (pin);
		len = getword (pin, tmp2);
	}
	troff_tex (w, ww, 0, 1);
	return (pin);
}



/*
** get reference
*/
int get_ref (char *pin, char *w)
{
	int len=0, l=0, lines=0;
	char tmp[MAXWORD+1];

	w[0] = EOS;	tmp[0] = EOS;
	while (*pin != EOS) {
		if (*pin == '\n')
			break;
		(void) getword(pin,tmp);
		if (tmp[0] == '.' && isupper(tmp[1])) {
			/* these commands don't cause a break in reference */
			if (strcmp (tmp, ".R") != 0 && strcmp (tmp, ".I") != 0
				&& strcmp(tmp,".B") != 0)
				break;
		}
		else if (tmp[0] == '.' && !(isupper(tmp[1]))) {
			/* these commands don't cause a break in reference */
			if (strcmp (tmp, ".br") != 0 && strcmp (tmp, ".bp") != 0)
				break;
		}
		l = get_line (pin, tmp, 1);
		lines++;
		if (lines > 1)
			strcat (w, " ");
		strcat (w, tmp);
		len += l+1;
		pin += l+1;
	}
	len--;
	pin--;
	return (len);
}


/*
**
*/
void get_size (char *ww, struct measure *PARAMETER)
{
	int sign=0, units=0;
	float value;

	if (ww[0] == EOS) {
		if (PARAMETER->def_value == 0) {
			PARAMETER->value = PARAMETER->old_value;
			strcpy(PARAMETER->units,PARAMETER->old_units);
		}
		else {
			PARAMETER->value = PARAMETER->def_value;
			strcpy(PARAMETER->units,PARAMETER->def_units);
		}
	}
	else {
		PARAMETER->old_value = PARAMETER->value;
		strcpy (PARAMETER->old_units, PARAMETER->units);

		parse_units (ww, &sign, &units, &value);

		if (units == 'p')
			strcpy (PARAMETER->units, "pt");
		else if (units == 'i')
			strcpy (PARAMETER->units, "in");
		else if (units == 'c')
			strcpy (PARAMETER->units, "cm");
		else if (units == 'm')
			strcpy (PARAMETER->units, "em");
		else if (units == 'n') {
			value = .5*value;	/* n is about half the width of m */
			strcpy (PARAMETER->units, "em");
		}
		else if (units == 'v')
			strcpy(PARAMETER->units,"ex");
		else if (units == 0) {
			if (sign == 0 || PARAMETER->old_units[0] == EOS)
				strcpy(PARAMETER->units,PARAMETER->def_units);
			else
				strcpy(PARAMETER->units,PARAMETER->old_units);
		}
		else {
			fprintf(stderr,"unknown units %c, using default units\n", units);
			strcpy(PARAMETER->units,PARAMETER->def_units);
		}
		if (sign == 0)
			PARAMETER->value = value;
		else
			PARAMETER->value = PARAMETER->old_value + sign*value;
	}
}



/*
** get the rest of the line -- Nelson Beebe
** arguments:
**  rec=1 means recursion is required
*/
int get_string (char *pin, char *w, int rec)
{
	int c,i,len;
	char ww[MAXLINE+1];
	char *start;

	if (*pin != '\"')
		return(get_line(pin,w,rec));
	start = pin;				/* remember start so we can find len */
	i=0;
	pin++;						/* point past initial quote */
	while ((c = *pin++) != EOS && c != '\"' && c != '\n' && i < MAXLINE)
		ww[i++] = (char)c;
	ww[i] = EOS;
	if (c != '\n')				/* flush remainder of line */
		while ((c = *pin++) != '\n')
		/* EMPTY */
			;
	len = pin - start - 1;		/* count only up to NL, not past */
	if (rec == 1)
		troff_tex(ww,w,0,1);
	else
		strcpy(w,ww);
	return(len);
}



/*
** get the argument for sub and sup
*/
int get_sub_arg (char *pin, char *w)
{
	int c,len,i;
	char ww[MAXWORD+1], tmp[MAXWORD+1];

	len=0;	tmp[0] = EOS;
	while ((c = *pin) == ' ' || c == '\t') {
		pin++;
		len++;
	}
	i=0;
	while ((c = *pin++) != EOS && c != ' ' && c != '\t' && c != '\n'
		   && c != '$' && c != '}' && c != '~' && i < MAXWORD) {
		ww[i++] = (char)c;
		len++;
	}
	ww[i] = EOS;
	if (strcmp(ww,"roman") == 0  || strcmp(ww,"bold") == 0
		|| strcmp(w,"italic") == 0) {
		(void) get_arg(pin,tmp,0);
		snprintf(ww, sizeof(ww), "%s%c%s", ww, c, tmp);
		len += strlen(tmp)+1;
	}
	troff_tex(ww,w,0,1);		/* recursive */
	return(len);
}


/*
**
*/
int	get_table_entry (char *pin, char *w, int tab)
{
	int i=0, escape=0;
	char c;

	while ((c = *pin++) != EOS && (escape || (c != tab && c != '\n')) && i < MAXLINE) {
		if (escape && c != '\n')
			w[i++] = '\\';
		if (c == '&')
			w[i++] = '\\';
		if (c == '\\' && !escape)
			escape = 1;
		else {
			w[i++] = c;
			escape = 0;
		}
	}
	w[i] = EOS;
	return(i);
}

int	get_tbl_txt_entry(char *pin, char *w) {
	int i;
	char c, tmp[MAXWORD+1];

	for (i = 0; (c = *pin++) != EOS && i < MAXLINE; i++) {
		w[i] = c;
		if (c == '\n') {
			(void)get_line(pin, tmp, 0);
			if (strncmp(tmp, "T}", 2) == 0) {
				w[++i] = EOS;
				return (i + 2);
			}
		}
	}
	w[i] = EOS;
	return (i);
}


/*
** get characters till the next space
*/
int get_till_space (char *pin, char *w)
{
	int c,i;

	for (i=0; (c = *pin++) != EOS && c != ' ' && c != '\n'
		 && c != '\t' && i < MAXWORD; i++)
		w[i] = (char)c;
	w[i] = EOS;
	return(i);
}



/*
** get the define substitution
*/
int getdef (char *pin, char *ww)
{
	int c,i,len;
	int def_delim;
	char w[MAXWORD+1];

	def_delim = *pin++;		/* take first character as delimiter */
	len=1;
	i=0;
	while ((c = *pin++) != EOS && c != def_delim && i < MAXWORD) {
		len++;
		w[i++] = (char)c;
	}
	w[i] = EOS;
	len++;
	if (c != def_delim) {
		fprintf(stderr,
				"WARNING: missing right delimiter in define, define=%s\n",w);
		len--;
	}
	troff_tex(w,ww,0,1);		/* now translate the substitution */
	return(len);
}



/*
** get an alphanumeric word (dot also)
*/
int getword (char *pin, char *w)
{
	int c,i;

	for (i=0; (c = *pin++) != EOS
		 && (isalpha(c) || isdigit(c) || c == '.') && i < MAXWORD; i++)
		w[i] = (char)c;
	if (i == 0 && c != EOS)
		w[i++] = (char)c;
	w[i] = EOS;
	return(i);
}



/*
** change GREEK to Greek
*/
void GR_to_Greek (char *w, char *ww)
{
	*ww++ = '\\';
	*ww++ = *w;
	while(*++w != EOS)
		*ww++ = tolower(*w);
	*ww = EOS;
}



/*
** check if w was defined by the user
*/
int is_def (char *w)
{
	int i;

	for (i=0; i < def_count; i++) {
		if (strcmp(def[i].def_macro,w) == 0)
			return(i);
	}
	return(-1);
}



/*
** check if w is in the flip list
*/
int is_flip (char *w)
{
	int i;

	for (i=0; i < flip_count; i++) {
		if (strcmp(flip_list[i],w) == 0)
			return(i);
	}
	return(-1);
}



/*
** check if w is one of those sacred macros
*/
int is_forbid (char *w)
{
	int i;

	for (i=0; i < forbd_count; i++) {
		if (strcmp(forbid[i],w) == 0)
			return(i);
	}
	return(-1);
}



/*
** check if w has a simple correspondence in TeX
*/
int is_mathcom (char *w, char *ww)
{
	int i;

	for (i = 0; i < mathcom_count; i++)
		if (strcmp (math[i].troff_symb, w) == 0) {
			strcpy (ww, math[i].tex_symb);
			return (i);
		}
	return (-1);
}

/*
**
*/
int is_special (char *w, char *ww)
{
	int i;
	//fprintf(stderr, "special_count:%d\n", special_count);
	for (i = 0; i < special_count; i++)
		if (strcmp (special[i].troff_char, w) == 0) {
			strcpy (ww, special[i].tex_char);
			//fprintf(stderr, "Special: %d, %s -> %s\n", i, w, ww);
			return (i);
		}
	return (-1);
}


/*
** check if w is user-defined macro
*/
int is_mydef (char *w)
{
	int i;

	for (i=0; i < mydef_count; i++) {
		if (strcmp(mydef[i].def_macro,w) == 0)
			return(i);
	}
	return(-1);
}



/*
** check if w is a macro or plain troff command
*/
int is_troff_mac (char *w, char *ww, int *arg, int *par)
{
	int i;

	for (i=0; i < macro_count; i++)
		if (strcmp(macro[i].troff_mac,w) == 0) {
			strcpy(ww,macro[i].tex_mac);
			*arg = macro[i].arg;
			*par = macro[i].macpar;
			return(i);
		}
	return(-1);
}


/*
**
*/
void parse_units (char *ww, int *sign, int *units, float *value)
{
	int len, k=0, i;
	char tmp[MAXWORD+1];

	len = strlen(ww);
	if (ww[0] == '-')
		*sign = -1;
	else if (ww[0] == '+')
		*sign = 1;
	if (*sign != 0)
		k++;

	i=0;
	while (k < len) {
		if (isdigit(ww[k]) || ww[k] == '.')
			tmp[i++] = ww[k++];
		else
			break;
	}
	tmp[i] = EOS;
	sscanf(tmp,"%f",value);
	i=0;
	if (k < len) {
		*units = ww[k++];
		if (k < len)
			fprintf(stderr, "Suspect problem in parsing %s, unit used is %c\n",
					ww, *units);
	}
}



/*
** check if w is in the similar list
*/
int similar (char *w)
{
	int i;

	for (i=0; i < sim_count ; i++) {
		if (strcmp(sim_list[i],w) == 0)
			return(1);
	}
	return(-1);
}



/*
** ignore the rest of the line
*/
char * skip_line (char *pin)
{
	while (*pin != '\n' && *pin != EOS)
		pin++;
	if (*pin == EOS)
		return(pin);
	else
		return(++pin);
}



/*
** skip white space
*/
int skip_white (char *pin)
{
	int c,len=0;

	while ((c = *pin++) == ' ' || c == '\t') /* || c == '\n') */
		len++;
	return(len);
}



/*
** copy tail[] to s[], return ptr to terminal EOS in s[]
*/
char * strapp (char *s, char *tail)
{
	while ((*s++ = *tail++) != 0)
	  if (debug_o)
	    fputc(s[-1], stderr);
	return (s-1);			/* pointer to EOS at end of s[] */
}