[comp.sources.atari.st] v03i010: nroff -- Partial clone of UNIX "nroff" formatter part03/03

koreth@panarthea.ebay.sun.com (Steven Grimm) (11/15/89)

Submitted-by: rosenkra@hall.cray.com (Bill Rosenkranz)
Posting-number: Volume 3, Issue 10
Archive-name: nroff/part03


part03/03

-bill
rosenkra@hall.cray.com
rosenkra@boston.cray.com

-------------cut here 0---------------------cut here-----------------------
#!/bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by hall!rosenkra on Sun Nov 12 16:35:37 CST 1989
# Contents:  escape.c low.c macros.c strings.c
 
echo x - escape.c
sed 's/^@//' > "escape.c" <<'@//E*O*F escape.c//'
/*
 *	escape.c - Escape and special character input processing portion of
 *	           nroff word processor
 *
 *	adapted for atariST/TOS by Bill Rosenkranz 11/89
 *	net:	rosenkra@hall.cray.com
 *	CIS:	71460,17
 *	GENIE:	W.ROSENKRANZ
 *
 *	original author:
 *
 *	Stephen L. Browning
 *	5723 North Parker Avenue
 *	Indianapolis, Indiana 46220
 *
 *	history:
 *
 *	- Originally written in BDS C;
 *	- Adapted for standard C by W. N. Paul
 *	- Heavily hacked up to conform to "real" nroff by Bill Rosenkranz
 */

#undef NRO_MAIN					/* extern globals */

#include <stdio.h>
#include "nroff.h"


/*------------------------------*/
/*	expesc			*/
/*------------------------------*/
expesc (p, q)
char   *p;
char   *q;
{

/*
 *	Expand escape sequences
 */

	register char  *s;
	register char  *t;
	register char  *pstr;
	register int	i;
	register int	val;
	register int	autoinc;
	char		c;
	char		fs[5];				/* for font change */
	char		nrstr[20];
	char		fmt[20];
	char		name[10];
	int		nreg;
	char	       *pfs;
	int		inc;
	int		tmp;
	char		delim;


	s = p;
	t = q;


	/*
	 *   if escape parsing is not on, just copy string
	 */
	if (dc.escon == NO)
	{
		while (*s != EOS)
		{
			*t++ = *s++;
		}
		*t = EOS;
		strcpy (p, q);

		return;
	}


	/*
	 *   do it...
	 */
	while (*s != EOS)
	{
		if (*s != dc.escchr)
		{
			/*
			 *   not esc, continue...
			 */
			*t++ = *s++;
		}


		else if (*(s + 1) == dc.escchr)
		{
			/*
			 *   \\			escape escape
			 */
			*t++ = *s++;
			++s;
		}


		else if (*(s + 1) == 'n')
		{
			/*
			 *   \nx, \n(xx		register
			 *
			 *   first check for \n+... or \n-... (either form)
			 */
			s += 2;
			autoinc = 0;
			if (*s == '+')
			{
				autoinc = 1;
				s += 1;
			}
			if (*s == '-')
			{
				autoinc = -1;
				s += 1;
			}



			/*
			 *   was this \nx or \n(xx form?
			 */
			if (isalpha (*s))
			{
				/*
				 *   \nx form. find reg (a-z)
				 */
				nreg = tolower (*s) - 'a';


				/*
				 *   was this \n+x or \n-x? if so, do the
				 *   auto incr
				 */
				if (autoinc > 0)
					dc.nr[nreg] += dc.nrauto[nreg];
				else if (autoinc < 0)
					dc.nr[nreg] -= dc.nrauto[nreg];

				/*
				 *   display format
				 */
				if (dc.nrfmt[nreg] == '1')
				{
					/*
					 *   normal decimal digits
					 */
					t += itoda (dc.nr[nreg], t, 6) - 1;
				}
				else if (dc.nrfmt[nreg] == 'i')
				{
					/*
					 *   lower roman
					 */
					t += itoroman (dc.nr[nreg], t, 24) - 1;
				}
				else if (dc.nrfmt[nreg] == 'I')
				{
					/*
					 *   upper roman
					 */
					t += itoROMAN (dc.nr[nreg], t, 24) - 1;
				}
				else if (dc.nrfmt[nreg] == 'a')
				{
					/*
					 *   lower letters
					 */
					t += itoletter (dc.nr[nreg], t, 12) - 1;
				}
				else if (dc.nrfmt[nreg] == 'A')
				{
					/*
					 *   upper letters
					 */
					t += itoLETTER (dc.nr[nreg], t, 12) - 1;
				}
				else if (dc.nrfmt[nreg] & 0x80)
				{
					/*
					 *   zero-filled decimal
					 */
					sprintf (fmt, "%%0%dld",
						(int)(dc.nrfmt[nreg] & 0x7F));
					fmt[5] = '\0';
					sprintf (nrstr, fmt, (long) dc.nr[nreg]);
					tmp = dc.nrfmt[nreg] & 0x7F;
					nrstr[tmp] = '\0';

					strcpy (t, nrstr);
					t += strlen (nrstr);
				}
				else
				{
					/*
					 *   normal (default)
					 */
					t += itoda (dc.nr[nreg], t, 6) - 1;
				}
				++s;
			}
			else if (*s == '%')
			{
				/*
				 *   \n% form. find index into reg struct
				 */
				nreg = findreg ("%");
				if (nreg < 0)
				{
					fprintf (err_stream,
						"***%s: no register match\n",
						myname);
					err_exit (-1);
				}


				/*
				 *   was this \n+% or \n-%? if so, do the
				 *   auto incr
				 */
				if (autoinc > 0)
					rg[nreg].rval += rg[nreg].rauto;
				else if (autoinc < 0)
					rg[nreg].rval -= rg[nreg].rauto;


				/*
				 *   display format
				 */
				if (rg[nreg].rfmt == '1')
				{
					/*
					 *   normal decimal digits
					 */
					t += itoda (rg[nreg].rval, t, 6) - 1;
				}
				else if (rg[nreg].rfmt == 'i')
				{
					/*
					 *   lower roman
					 */
					t += itoroman (rg[nreg].rval, t, 24) - 1;
				}
				else if (rg[nreg].rfmt == 'I')
				{
					/*
					 *   upper roman
					 */
					t += itoROMAN (rg[nreg].rval, t, 24) - 1;
				}
				else if (rg[nreg].rfmt == 'a')
				{
					/*
					 *   lower letters
					 */
					t += itoletter (rg[nreg].rval, t, 12) - 1;
				}
				else if (rg[nreg].rfmt == 'A')
				{
					/*
					 *   upper letters
					 */
					t += itoLETTER (rg[nreg].rval, t, 12) - 1;
				}
				else if (rg[nreg].rfmt & 0x80)
				{
					/*
					 *   zero-filled decimal
					 */
					sprintf (fmt, "%%0%dld",
						(int)(rg[nreg].rfmt & 0x7F));
					fmt[5] = '\0';
					sprintf (nrstr, fmt, (long) rg[nreg].rval);
					tmp = rg[nreg].rfmt & 0x7F;
					nrstr[tmp] = '\0';

					strcpy (t, nrstr);
					t += strlen (nrstr);
				}
				else
				{
					/*
					 *   normal (default)
					 */
					t += itoda (rg[nreg].rval, t, 6) - 1;
				}
				s += 1;
			}
			else if (*s == '(')
			{
				/*
				 *   \n(xx form. find index into reg struct
				 */
				s += 1;
				name[0] = *s;
				name[1] = *(s + 1);
				if (name[1] == ' '  || name[1] == '\t'
				||  name[1] == '\n' || name[1] == '\r')
					name[1] = '\0';
				name[2] = '\0';
				nreg = findreg (name);
				if (nreg < 0)
				{
					fprintf (err_stream,
						"***%s: no register match\n",
						myname);
					err_exit (-1);
				}
				

				/*
				 *   was this \n+(xx or \n-(xx? if so, do the
				 *   auto incr
				 */
				if (rg[nreg].rflag & RF_WRITE)
				{
					if (autoinc > 0)
						rg[nreg].rval += rg[nreg].rauto;
					else if (autoinc < 0)
						rg[nreg].rval -= rg[nreg].rauto;
				}


				/*
				 *   display format
				 */
				if (rg[nreg].rfmt == '1')
				{
					/*
					 *   normal decimal digits
					 */
					t += itoda (rg[nreg].rval, t, 6) - 1;
				}
				else if (rg[nreg].rfmt == 'i')
				{
					/*
					 *   lower roman
					 */
					t += itoroman (rg[nreg].rval, t, 24) - 1;
				}
				else if (rg[nreg].rfmt == 'I')
				{
					/*
					 *   upper roman
					 */
					t += itoROMAN (rg[nreg].rval, t, 24) - 1;
				}
				else if (rg[nreg].rfmt == 'a')
				{
					/*
					 *   lower letters
					 */
					t += itoletter (rg[nreg].rval, t, 12) - 1;
				}
				else if (rg[nreg].rfmt == 'A')
				{
					/*
					 *   upper letters
					 */
					t += itoLETTER (rg[nreg].rval, t, 12) - 1;
				}
				else if (rg[nreg].rfmt & 0x80)
				{
					/*
					 *   zero-filled decimal
					 */
					sprintf (fmt, "%%0%dld",
						(int)(rg[nreg].rfmt & 0x7F));
					fmt[5] = '\0';
					sprintf (nrstr, fmt, (long) rg[nreg].rval);
					tmp = rg[nreg].rfmt & 0x7F;
					nrstr[tmp] = '\0';

					strcpy (t, nrstr);
					t += strlen (nrstr);
				}
				else
				{
					/*
					 *   normal (default)
					 */
					t += itoda (rg[nreg].rval, t, 6) - 1;
				}
				s += 2;
			}
		}


		else if (*(s + 1) == '\"')
		{
			/*
			 *   \"			comment
			 */
			*s = EOS;
			*t = *s;

			return;
		}


		else if (*(s + 1) == '*')
		{
			/*
			 *   \*x, \*(xx		string
			 */
			s += 2;
			if (*s == '(')
			{
				/*
				 *   \*(xx form
				 */
				s += 1;
				name[0] = *s;
				name[1] = *(s + 1);
				name[2] = '\0';
				pstr = getstr (name);
				if (!pstr)
				{
					fprintf (err_stream,
						"***%s: string not found\n",
						myname);
					err_exit (-1);
				}
				while (*pstr)
					*t++ = *pstr++;
				s += 2;
			}
			else
			{
				/*
				 *   \*x form
				 */
				name[0] = *s;
				name[1] = '\0';
				pstr = getstr (name);
				if (!pstr)
				{
					fprintf (err_stream,
						"***%s: string not found\n",
						myname);
					err_exit (-1);
				}
				while (*pstr)
					*t++ = *pstr++;
				s += 1;
			}
		}


		else if (*(s + 1) == 'f')
		{
			/*
			 *   \fx		font
			 */
			s += 2;
			pfs = fs;		/* set up ret string */
			fs[0] = '\0';

			/*
			 *  it parses 1-2 char of s and returns esc seq for
			 *  \fB and \fR (\fI is same as \fB)
			 */
			fontchange (*s, pfs);

			/*
			 *   imbed the atari (vt52) escape seq
			 */
			while (*pfs)
				*t++ = *pfs++;
			++s;			/* skip B,I,R,S,P */
		}


		else if (*(s + 1) == '(')
		{
			/*
			 *   \(xx		special char
			 */
			s  += 2;

			/*
			 *   it returns num char to skip and sets c to
			 *   the ascii value of the char
			 */
			inc = specialchar (s, &c);

			/*
			 *   skip proper num char in s and add c to target
			 */
			if (inc)
			{
				s   += inc;
				*t++ = c;
			}
		}


		else if (*(s + 1) == 'e')
		{
			/*
			 *   \e		printable version of escape
			 */
			*t++ = dc.escchr;
			s   += 2;
		}


		else if (*(s + 1) == '`')
		{
			/*
			 *   \`		grave, like \(ga
			 */
			*t++ = 0x60;
			s  += 2;
		}


		else if (*(s + 1) == '\'')
		{
			/*
			 *   \'		accute, like \(aa
			 */
			s  += 2;
			*t++ = 0xBA;
		}


		else if (*(s + 1) == '-')
		{
			/*
			 *   \-		minus
			 */
			s  += 2;
			*t++ = 0x2D;
		}


		else if (*(s + 1) == '.')
		{
			/*
			 *   \.		period
			 */
			s  += 2;
			*t++ = 0x2E;
		}


		else if (*(s + 1) == ' ')
		{
			/*
			 *   \(space)	space
			 */
			s  += 2;
			*t++ = 0x20;
		}


		else if (*(s + 1) == '0')
		{
			/*
			 *   \0		digital width space
			 */
			s  += 2;
			*t++ = 0x20;
		}


		else if (*(s + 1) == '|')
		{
			/*
			 *   \|		narrow width char (0 in nroff)
			 */
			s  += 2;
		}


		else if (*(s + 1) == '^')
		{
			/*
			 *   \^		narrow width char (0 in nroff)
			 */
			s  += 2;
		}


		else if (*(s + 1) == '&')
		{
			/*
			 *   \&		non-printing zero width
			 */
			s  += 2;
		}


		else if (*(s + 1) == '!')
		{
			/*
			 *   \!		transparent copy line
			 */
			s  += 2;
		}


		else if (*(s + 1) == '$')
		{
			/*
			 *   \$N	interpolate arg 1<=N<=9
			 */
			s  += 2;
		}


		else if (*(s + 1) == '%')
		{
			/*
			 *   \%		hyphen
			 */
			s  += 2;
			*t++ = 0x2D;
			*t++ = 0x2D;
		}


		else if (*(s + 1) == 'a')
		{
			/*
			 *   \a
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'b')
		{
			/*
			 *   \b'abc...'
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'c')
		{
			/*
			 *   \c
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'd')
		{
			/*
			 *   \d
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'h')
		{
			/*
			 *   \h'N'	horiz motion
			 */
			s    += 2;
			delim = *s++;
			val   = atoi (s);
			for (i = 0; i < val; i++)
				*t++ = ' ';
			while (*s != delim)
			{
				if (*s == 0)
					break;
				s++;
			}
			if (*s)
				s++;
			
		}


		else if (*(s + 1) == 'k')
		{
			/*
			 *   \kx
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'l')
		{
			/*
			 *   \l'Nc'
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'L')
		{
			/*
			 *   \L'Nc'
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'o')
		{
			/*
			 *   \o'abc...'		overstrike
			 */
			s  += 2;
			delim = *s++;
			while (*s != EOS && *s != delim)
			{
				*t++ = *s++;
				*t++ = 0x08;
			}
			s++;
		}


		else if (*(s + 1) == 'p')
		{
			/*
			 *   \p
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'r')
		{
			/*
			 *   \r
			 */
			s  += 2;
		}


		else if (*(s + 1) == 's')
		{
			/*
			 *   \sN,\s+-N
			 */
			s  += 2;
		}


		else if (*(s + 1) == 't')
		{
			/*
			 *   \t		horizontal tab
			 */
			s  += 2;
			*t++ = 0x09;
		}


		else if (*(s + 1) == 'u')
		{
			/*
			 *   \u
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'v')
		{
			/*
			 *   \v'N'	vert tab
			 */
			s    += 2;
			delim = *s++;
			val   = atoi (s);
			for (i = 0; i < val; i++)
				*t++ = 0x0A;
			while (*s != delim)
			{
				if (*s == 0)
					break;
				s++;
			}
			if (*s)
				s++;
		}


		else if (*(s + 1) == 'w')
		{
			/*
			 *   \w'str'
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'x')
		{
			/*
			 *   \x'N'
			 */
			s  += 2;
		}


		else if (*(s + 1) == 'z')
		{
			/*
			 *   \zc	print c w/o spacing
			 */
			s  += 2;
			*t++ = *s++;
			*t++ = 0x08;
		}


		else if (*(s + 1) == '{')
		{
			/*
			 *   \{
			 */
			s  += 2;
		}


		else if (*(s + 1) == '}')
		{
			/*
			 *   \}
			 */
			s  += 2;
		}


		else if (*(s + 1) == '\n' || *(s + 1) == '\r')
		{
			/*
			 *   \(newline)	ignore newline
			 */
			s  += 2;
		}


		else
		{
			/*
			 *   \X		any other character not above
			 */
			s   += 1;
			*t++ = *s++;
		}

	}

	/*
	 *   end the string and return it in original buf
	 */
	*t = EOS;
	strcpy (p, q);
}




/*------------------------------*/
/*	specialchar		*/
/*------------------------------*/
specialchar (s, c)
register char  *s;
register char  *c;
{

/*
 *	handles \(xx escape sequences for special characters (atari-specific)
 */

	register char	c1;
	register char	c2;

	c1 = *s;
	c2 = *(s+1);

	/*
	 *   symbols
	 */
	if (c1 == 'c' && c2 == 'o') {*c = 0xBD; return (2);}	/* copyrite */
	if (c1 == 'r' && c2 == 'g') {*c = 0xBE; return (2);}	/* registered */
	if (c1 == 't' && c2 == 'm') {*c = 0xBF; return (2);}	/* trademark */
	if (c1 == '1' && c2 == '2') {*c = 0xAB; return (2);}	/* 1/2 */
	if (c1 == '1' && c2 == '4') {*c = 0xAC; return (2);}	/* 1/4 */
	if (c1 == 'p' && c2 == '2') {*c = 0xFD; return (2);}	/* ^2 */
	if (c1 == 'p' && c2 == '3') {*c = 0xFE; return (2);}	/* ^3 */
	if (c1 == 'p' && c2 == 'n') {*c = 0xFC; return (2);}	/* ^n */
	if (c1 == 'a' && c2 == 'a') {*c = 0xBA; return (2);}	/* acute */
	if (c1 == 'g' && c2 == 'a') {*c = 0x60; return (2);}	/* grave */
	if (c1 == 'd' && c2 == 'e') {*c = 0xF8; return (2);}	/* degree */
	if (c1 == 'd' && c2 == 'g') {*c = 0xBB; return (2);}	/* dagger */
	if (c1 == 'c' && c2 == 't') {*c = 0x9B; return (2);}	/* cent */
	if (c1 == 'b' && c2 == 'u') {*c = 0xF9; return (2);}	/* bullet */
	if (c1 == 'd' && c2 == 't') {*c = 0xFA; return (2);}	/* dot */
	if (c1 == 'p' && c2 == 'p') {*c = 0xBC; return (2);}	/* paragraph */
	if (c1 == '^' && c2 == 'g') {*c = 0x07; return (2);}	/* ring bell */
	if (c1 == 'u' && c2 == 'a') {*c = 0x01; return (2);}	/* up arrow */
	if (c1 == 'd' && c2 == 'a') {*c = 0x02; return (2);}	/* dn arrow */
	if (c1 == '-' && c2 == '>') {*c = 0x03; return (2);}	/* rt arrow */
	if (c1 == '<' && c2 == '-') {*c = 0x04; return (2);}	/* lf arrow */
	if (c1 == 'd' && c2 == 'i') {*c = 0xF6; return (2);}	/* divide */
	if (c1 == 's' && c2 == 'r') {*c = 0xFB; return (2);}	/* sq root */
	if (c1 == '=' && c2 == '=') {*c = 0xF0; return (2);}	/* == */
	if (c1 == '>' && c2 == '=') {*c = 0xF2; return (2);}	/* >= */
	if (c1 == '<' && c2 == '=') {*c = 0xF3; return (2);}	/* <= */
	if (c1 == '+' && c2 == '-') {*c = 0xF1; return (2);}	/* +- */
	if (c1 == '~' && c2 == '=') {*c = 0xF7; return (2);}	/* ~= */
	if (c1 == 'a' && c2 == 'p') {*c = 0x7E; return (2);}	/* approx */
	if (c1 == 'n' && c2 == 'o') {*c = 0xAA; return (2);}	/* not */
	if (c1 == 'm' && c2 == 'o') {*c = 0xEE; return (2);}	/* member */
	if (c1 == 'c' && c2 == 'a') {*c = 0xEF; return (2);}	/* intersect */
	if (c1 == 'c' && c2 == 'u') {*c = 0x55; return (2);}	/* union */
	if (c1 == 'i' && c2 == '1') {*c = 0xF4; return (2);}	/* integral1 */
	if (c1 == 'i' && c2 == '2') {*c = 0xF5; return (2);}	/* integral2 */

	/*
	 *   greek
	 */
	if (c1 == '*' && c2 == 'a') {*c = 0xE0; return (2);}	/* alpha */
	if (c1 == '*' && c2 == 'b') {*c = 0xE1; return (2);}	/* beta */
	if (c1 == '*' && c2 == 'g') {*c = 0xE2; return (2);}	/* gamma */
	if (c1 == '*' && c2 == 'd') {*c = 0x7F; return (2);}	/* delta */
	if (c1 == '*' && c2 == 's') {*c = 0xE4; return (2);}	/* sigma */
	if (c1 == '*' && c2 == 'p') {*c = 0xE3; return (2);}	/* pi */
	if (c1 == '*' && c2 == 'm') {*c = 0xE6; return (2);}	/* mu */

	*c = ' ';
	return (0);	
}




/*------------------------------*/
/*	fontchange		*/
/*------------------------------*/
fontchange (fnt, s)
char	fnt;
char   *s;
{

/*
 *	handles \fx font change escapes for R,B,I,S,P (atari-specific)
 *	resets current and last font in dc struct (last used for .ft
 *	with no args)
 */

	int	tmp;

	*s = '\0';
	switch (fnt)
	{
	case 'R':				/* Times Roman */
		if (dc.dofnt == YES)
			strcpy (s, "\33q");
		dc.lastfnt = dc.thisfnt;
		dc.thisfnt = 1;
		break;
	case 'I':				/* Times italic */
		if (dc.dofnt == YES)
			strcpy (s, "\33p");
		dc.lastfnt = dc.thisfnt;
		dc.thisfnt = 2;
		break;
	case 'B':				/* Times bold */
		if (dc.dofnt == YES)
			strcpy (s, "\33p");
		dc.lastfnt = dc.thisfnt;
		dc.thisfnt = 3;
		break;
	case 'S':				/* math/special */
		*s = '\0';
		dc.lastfnt = dc.thisfnt;
		dc.thisfnt = 4;
		break;
	case 'P':				/* previous (exchange) */
		if (dc.dofnt == YES)
		{
			if (dc.lastfnt == 1)
				strcpy (s, "\33q");	/* to R */
			else if (dc.lastfnt == 2)
				strcpy (s, "\33p");	/* to I */
			else if (dc.lastfnt == 3)
				strcpy (s, "\33p");	/* to B */
			else
				*s = '\0';		/* nothing */
		}

		tmp        = dc.thisfnt;		/* swap this/last */
		dc.thisfnt = dc.lastfnt;
		dc.lastfnt = tmp;
		break;
	default:
		*s = '\0';
		break;
	}

	set_ireg (".f", dc.thisfnt, 0);
}





/*------------------------------*/
/*	findreg			*/
/*------------------------------*/
findreg (name)
register char  *name;
{

/*
 *	find register named 'name' in pool. return index into array or -1
 *	if not found.
 */

	register int	i;
	register char  *prname;

	for (i = 0; i < MAXREGS; i++)
	{
		prname = rg[i].rname;
		if (*prname == *name && *(prname + 1) == *(name + 1))
			break;
	}

	return ((i < MAXREGS) ? i : -1);
}




@//E*O*F escape.c//
chmod u=rw,g=r,o=r escape.c
 
echo x - low.c
sed 's/^@//' > "low.c" <<'@//E*O*F low.c//'
/*
 *	low.c - misc low-level functions for nroff word processor
 *
 *	adapted for atariST/TOS by Bill Rosenkranz 11/89
 *	net:	rosenkra@hall.cray.com
 *	CIS:	71460,17
 *	GENIE:	W.ROSENKRANZ
 *
 *	original author:
 *
 *	Stephen L. Browning
 *	5723 North Parker Avenue
 *	Indianapolis, Indiana 46220
 *
 *	history:
 *
 *	- Originally written in BDS C;
 *	- Adapted for standard C by W. N. Paul
 *	- Heavily hacked up to conform to "real" nroff by Bill Rosenkranz
 */

#undef NRO_MAIN					/* extern globals */

#include <stdio.h>
#include "nroff.h"



/*------------------------------*/
/*	atod			*/
/*------------------------------*/
atod (c)
char    c;
{

/*
 *	convert ascii character to decimal.
 */

	return (((c < '0') || (c > '9')) ? -1 : c - '0');
}





/*------------------------------*/
/*	robrk			*/
/*------------------------------*/
robrk ()
{

/*
 *	end current filled line
 */

	if (co.outp > 0)
	{
		co.outbuf[co.outp]   = '\r';
		co.outbuf[co.outp+1] = '\n';
		co.outbuf[co.outp+2] = EOS;
		put (co.outbuf);
	}
	co.outp   = 0;
	co.outw   = 0;
	co.outwds = 0;
	co.outesc = 0;
}




/*------------------------------*/
/*	ctod			*/
/*------------------------------*/
ctod (p)
register char  *p;
{

/*
 *	convert string to decimal.
 *	processes only positive values.
 */

	register int	val;
	register int	d;

	val = 0;
	while (*p != EOS)
	{
		d = atod (*p++);
		if (d == -1)
			return (val);
		val = 10 * val + d;
	}
	return (val);
}




/*------------------------------*/
/*	skipbl			*/
/*------------------------------*/
char   *skipbl (p)
register char  *p;
{

/*
 *	skip blanks and tabs in character buffer. return ptr to first
 *	non-space or non-tab char. this could mean EOS or \r or \n.
 *	also increments the arg ptr (side effect).
 */

	while (*p == ' ' || *p == '\t')
		++p;
	return (p);
}




/*------------------------------*/
/*	skipwd			*/
/*------------------------------*/
char   *skipwd (p)
register char  *p;
{

/*
 *	skip over word and punctuation. anything but space,\t,\r,\n, and EOS
 *	is skipped. return ptr to the first of these found. also increments
 *	the arg ptr (side effect).
 */

	while (*p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && *p != EOS)
		++p;
	return (p);
}





/*------------------------------*/
/*	space			*/
/*------------------------------*/
space (n)
int     n;
{

/*
 *	space vertically n lines. this does header and footer also.
 */

	robrk ();
	if (pg.lineno > pg.bottom)
		return;
	if (pg.lineno == 0)
		phead ();
	skip (min (n, pg.bottom + 1 - pg.lineno));
	pg.lineno += n;
	set_ireg ("ln", pg.lineno, 0);
	if (pg.lineno > pg.bottom)
		pfoot ();
}




/*------------------------------*/
/*	getfield		*/
/*------------------------------*/
char   *getfield (p, q, delim)
register char  *p;
register char  *q;
char		delim;
{

/*
 *	get field from title
 */

	while (*p != delim && *p != '\r' && *p != '\n' && *p != EOS)
	{
		*q++ = *p++;
	}
	*q = EOS;
	if (*p == delim)
		++p;
	return (p);
}





/*------------------------------*/
/*	getwrd			*/
/*------------------------------*/
getwrd (p0, p1)
register char  *p0;
register char  *p1;
{

/*
 *	get non-blank word from p0 into p1.
 *	return number of characters processed.
 */

	register int	i;
	register char  *p;
	char		c;

	/*
	 *   init counter...
	 */
	i = 0;


	/*
	 *   skip leading whitespace
	 */
	while (*p0 && (*p0 == ' ' || *p0 == '\t'))
	{
		++i;
		++p0;
	}


	/*
	 *   set ptr and start to look for end of word
	 */
	p = p0;
	while (*p0 != ' ' && *p0 != EOS && *p0 != '\t')
	{
		if (*p0 == '\n' || *p0 == '\r')
			break;
		*p1 = *p0++;
		++p1;
		++i;
	}

	c = *(p1 - 1);
	if (c == '"')
		c = *(p1 - 2);
	if (c == '?' || c == '!')
	{
		*p1++ = ' ';
		++i;
	}
	if (c == '.' && (*p0 == '\n' || *p0 == '\r' || islower (*p)))
	{
		*p1++ = ' ';
		++i;
	}
	*p1 = EOS;

	return (i);
}




/*------------------------------*/
/*	countesc		*/
/*------------------------------*/

#define ESC			27

countesc (p)
register char  *p;
{

/*
 *	count atari escape sequence characters in given null-terminated
 *	string
 */

	register char  *pp;
	register int	num;

	pp  = p;
	num = 0;

	while (*pp != EOS)
	{
		if (*pp == ESC)
		{
			/*
			 *   count escape char (atari-specific, vt52)
			 *   generally only p,q,b,and c will show up...
			 */
			switch (*(pp+1))
			{
			case 'A':			/* ESC-a */
			case 'B':
			case 'C':
			case 'D':
			case 'E':
			case 'H':
			case 'I':
			case 'J':
			case 'K':
			case 'L':
			case 'M':
			case 'd':
			case 'e':
			case 'f':
			case 'j':
			case 'k':
			case 'l':
			case 'o':
			case 'p':
			case 'q':
			case 'v':
			case 'w':
				num += 2;
				break;
			case 'b':			/* ESC-a-b */
			case 'c':
				num += 3;
				break;
			case 'Y':			/* ESC-a-b-c */
				num += 4;
				break;
			default:
				num += 1;
				break;
			}
		}
		pp++;
	}

	return (num);
}




/*------------------------------*/
/*	itoda			*/
/*------------------------------*/
itoda (value, p, size)
int		value;
register char  *p;
register int	size;
{

/*
 *	convert integer to decimal ascii string
 */

	register int	i;
	register int    j;
	register int	k;
	register int	aval;
	char		c[20];

	aval = abs (value);
	c[0] = EOS;
	i = 1;
	do
	{
		c[i++] = (aval % 10) + '0';
		aval /= 10;

	} while (aval > 0 && i <= size);

	if (value < 0 && i <= size)
		c[i++] = '-';
	for (j = 0; j < i; ++j)
		*p++ = c[i - j - 1];

	return (i);
}




/*------------------------------*/
/*	itoROMAN		*/
/*------------------------------*/
itoROMAN (value, p, size)
int		value;
register char  *p;
register int	size;
{

/*
 *	convert integer to upper roman. must be positive
 */

	register int	i;
	register int	j;
	register int	k;
	register int	aval;
	char		c[100];
	int		rem;

	aval = abs (value);
	c[0] = EOS;
	i = 1;

	/*
	 *   trivial case:
	 */
	if (aval == 0)
	{
		c[i++] = '0';
		goto done_100;
	}

	/*
	 *   temporarily mod 100...
	 */
	aval = aval % 100;

	if (aval > 0)
	{
		/*
		 *   build backward
		 *
		 *   | I|		1
		 *   | II|		2
		 *   | III|		3
		 *   | VI|		4
		 *   | V|		5
		 *   | IV|		6
		 *   | IIV|		7
		 *   | IIIV|		8
		 *   | XI|		9
		 *   | X|		0
		 *   | IX|		11
		 *   | IIX|		12
		 */
		if ((aval % 5 == 0) && (aval % 10 != 0))/* 5 */
			c[i++] = 'V';
		else
		{
			rem = aval % 10;
			if (rem == 9)			/* 9 */
			{
				c[i++] = 'X';
				c[i++] = 'I';
			}
			else if (rem == 8)		/* 8 */
			{
				c[i++] = 'I';
				c[i++] = 'I';
				c[i++] = 'I';
				c[i++] = 'V';
			}
			else if (rem == 7)		/* 7 */
			{
				c[i++] = 'I';
				c[i++] = 'I';
				c[i++] = 'V';
			}
			else if (rem == 6)		/* 6 */
			{
				c[i++] = 'I';
				c[i++] = 'V';
			}
			else if (rem == 4)		/* 4 */
			{
				c[i++] = 'V';
				c[i++] = 'I';
			}
			else				/* 3,2,1 */
			{
				for (j = 0; j < rem; j++)
					c[i++] = 'I';
			}
		}

		aval /= 10;
		if (aval == 0)
			goto done_100;

		rem = aval % 10;
		if (rem == 4)
		{
			c[i++] = 'L';
			c[i++] = 'X';
		}
		else if (rem == 5)
		{
			c[i++] = 'L';
		}
		else if (rem < 4)
		{
			for (j = 0; j < rem; j++)
				c[i++] = 'X';
		}
		else
		{
			for (j = 0; j < rem - 5; j++)
				c[i++] = 'X';
			c[i++] = 'L';
		}
	}


done_100:
	/*
	 *   divide by 100 (they are done) and temp mod by another 10
	 */
	aval  = abs (value);
	aval /= 100;

	if (aval > 0)
	{
		rem  = aval % 10;
		if (rem == 4)
		{
			c[i++] = 'D';
			c[i++] = 'C';
		}
		if (rem == 5)
		{
			c[i++] = 'D';
		}
		else if (rem < 4)
		{
			for (j = 0; j < rem; j++)
				c[i++] = 'C';
		}
		else if (rem == 9)
		{
			c[i++] = 'M';
			c[i++] = 'C';
		}
		else if (rem < 9)
		{
			for (j = 0; j < rem - 5; j++)
				c[i++] = 'C';
			c[i++] = 'D';
		}
	}


	aval /= 10;

	if (aval > 0)
	{
		rem  = aval % 10;
		if (rem < 4)
		{
			for (j = 0; j < rem; j++)
				c[i++] = 'M';
		}
	}


	if (value < 0)
		c[i++] = '-';

	for (j = 0; j < i; ++j)
		*p++ = c[i - j - 1];

	return (i);
}




/*------------------------------*/
/*	itoroman		*/
/*------------------------------*/
itoroman (value, p, size)
int     value;
char   *p;
int     size;
{

/*
 *	convert integer to lower roman
 */

	register int	i;
	register int	len;
	register int	aval;
	char		c[100];

	c[0] = EOS;
	len = itoROMAN (value, c, size);

	for (i = 0; i < len; i++)
	{
		p[i] = c[i];
		if (isalpha (p[i]))
			p[i] = tolower (c[i]);
	}

	return (len);
}




/*------------------------------*/
/*	itoLETTER		*/
/*------------------------------*/
itoLETTER (value, p, size)
int		value;
register char  *p;
register int	size;
{

/*
 *	convert integer to upper letter value: 0,A,B,C,...,AA,AB,AC,...
 */

	register int	i;
	register int	j;
	register int	k;
	register int	aval;
	int		rem;
	char		c[20];

	aval = abs (value);
	c[0] = EOS;
	i = 1;

	/*
	 *   1 based:
	 *
	 *   0	0
	 *   1	A
	 *   25	Z
	 *   26	AA
	 *   51 AZ
	 *   52 AAA
	 *   ...
	 */
	if (aval == 0)
		c[i++] = '0';
	else if (aval < 27)
	{
		c[i++] = aval - 1 + 'A';
	}
	else
	{
		do
		{
			c[i++] = ((aval - 1) % 26) + 'A';
			aval = (aval - 1)  / 26;
	
		} while (aval > 0 && i <= size);
	}

	if (value < 0 && i <= size)
		c[i++] = '-';

	for (j = 0; j < i; ++j)
		*p++ = c[i - j - 1];

	return (i);
}



/*------------------------------*/
/*	itoletter		*/
/*------------------------------*/
itoletter (value, p, size)
int		value;
register char  *p;
register int	size;
{

/*
 *	convert integer to upper letter value: 0,a,b,c,...,aa,ab,ac,...
 */

	register int	i;
	register int	j;
	register int	k;
	register int	aval;
	char		c[20];
	int		rem;

	aval = abs (value);
	c[0] = EOS;
	i = 1;

	/*
	 *   1 based:
	 *
	 *   0	0
	 *   1	A
	 *   25	Z
	 *   26	AA
	 *   51 AZ
	 *   52 AAA
	 *   ...
	 */
	if (aval == 0)
		c[i++] = '0';
	else if (aval < 27)
	{
		c[i++] = aval - 1 + 'a';
	}
	else
	{
		do
		{
			c[i++] = ((aval - 1) % 26) + 'a';
			aval = (aval - 1)  / 26;
	
		} while (aval > 0 && i <= size);
	}

	if (value < 0 && i <= size)
		c[i++] = '-';

	for (j = 0; j < i; ++j)
		*p++ = c[i - j - 1];

	return (i);
}



/*------------------------------*/
/*	min			*/
/*------------------------------*/

#ifdef min
#undef min
#endif

min (v1, v2)
register int	v1;
register int	v2;
{

/*
 *	find minimum of two integer ONLY
 */

	return ((v1 < v2) ? v1 : v2);
}





/*------------------------------*/
/*	max			*/
/*------------------------------*/

#ifdef max
#undef max
#endif

max (v1, v2)
register int	v1;
register int	v2;
{

/*
 *	find maximum of two integers ONLY
 */

	return ((v1 > v2) ? v1 : v2);
}





/*------------------------------*/
/*	err_exit		*/
/*------------------------------*/
#ifdef ALCYON
#include <osbind.h>
#endif

err_exit (code)
{

/*
 *	exit cleanly on fatal error (close files, etc). also handles normal
 *	exit.
 */

	if (err_stream != stderr && err_stream != (FILE *) 0)
		fclose (err_stream);
	if (dbg_stream != stderr && dbg_stream != (FILE *) 0)
		fclose (dbg_stream);

#ifdef ALCYON
	if (hold_screen)
	{
		printf ("enter any key...");
		Cconin ();
	}
#endif

	exit (code);
}




@//E*O*F low.c//
chmod u=rw,g=r,o=r low.c
 
echo x - macros.c
sed 's/^@//' > "macros.c" <<'@//E*O*F macros.c//'
/*
 *	macros.c - macro input/output processing for nroff word processor
 *
 *	adapted for atariST/TOS by Bill Rosenkranz 11/89
 *	net:	rosenkra@hall.cray.com
 *	CIS:	71460,17
 *	GENIE:	W.ROSENKRANZ
 *
 *	original author:
 *
 *	Stephen L. Browning
 *	5723 North Parker Avenue
 *	Indianapolis, Indiana 46220
 *
 *	history:
 *
 *	- Originally written in BDS C;
 *	- Adapted for standard C by W. N. Paul
 *	- Heavily hacked up to conform to "real" nroff by Bill Rosenkranz
 */

#undef NRO_MAIN					/* extern globals */

#include <stdio.h>
#include "nroff.h"



/*------------------------------*/
/*	defmac			*/
/*------------------------------*/
defmac (p, infp)
register char  *p;
FILE	       *infp;
{

/*
 *	Define a macro. top level, read from stream.
 *
 *	we should read macro without interpretation EXCEPT:
 *
 *	1) number registers are interpolated
 *	2) strings indicated by \* are interpolated
 *	3) arguments indicated by \$ are interpolated
 *	4) concealed newlines indicated by \(newline) are eliminated
 *	5) comments indicated by \" are eliminated
 *	6) \t and \a are interpreted as ASCII h tab and SOH.
 *	7) \\ is interpreted as backslash and \. is interpreted as a period.
 *
 *	currently, we do only 3. a good place to do it would be here before
 *	putmac, after colmac...
 */

	register char  *q;
	register int	i;
	char    	name[MNLEN];
	char    	defn[MXMLEN];
	char		newend[10];


	/*
	 *   skip the .de and get to the name...
	 */
	q = skipwd (p);
	q = skipbl (q);

	/*
	 *   ok, name now holds the name. make sure it is valid (i.e. first
	 *   char is alpha...). getwrd returns the length of the word.
	 */
	i = getwrd (q, name);
	if (!isalpha (*name))
	{
		fprintf (err_stream,
			"***%s: missing or illegal macro definition name\n",
			myname);
		err_exit (-1);
	}

	/*
	 *   truncate to 2 char max name.
	 */
	if (i > 2)
		name[2] = EOS;


	/*
	 *   skip the name and see if we have a new end defined...
	 */
	q = skipwd (p);
	q = skipbl (q);
	for (i = 0; i < 10; i++)
		newend[i] = EOS;

	for (i = 0; (i < 10) && ( isalpha (q[i]) || isdigit (q[i]) ); i++)
	{
		newend[i] = q[i];
	}



	/*
	 *   read a line from input stream until we get the end of macro
	 *   command (.en or ..). actually. we should have read the next
	 *   field just above here to get the .de NA . or .de NA en string
	 *   to be new end of macro.
	 */
	i = 0;
	while (getlin (p, infp) != EOF)
	{
		if (p[0] == dc.cmdchr && newend[0] != EOS
		&&  p[1] == newend[0] && p[2] == newend[1])
		{
			/*
			 *   replacement end found
			 */
			break;
		}
		if (p[0] == dc.cmdchr && p[1] == 'e' && p[2] == 'n')
		{
			/*
			 *   .en found
			 */
			break;
		}
		if (p[0] == dc.cmdchr && p[1] == dc.cmdchr)
		{
			/*
			 *   .. found
			 */
			break;
		}


		/*
		 *   collect macro from the line we just read. all this does
		 *   is put it in the string defn.
		 */
		if ((i = colmac (p, defn, i)) == ERR)
		{
			fprintf (err_stream,
				"***%s: macro definition too long\n", myname);
			err_exit (-1);
		}
	}


	/*
	 *   store the macro
	 */
	if (putmac (name, defn) == ERR)
	{
		fprintf (err_stream,
			"***%s: macro definition table full\n", myname);
		err_exit (-1);
	}
}





/*------------------------------*/
/*	colmac			*/
/*------------------------------*/
colmac (p, d, i)
register char  *p;
char		d[];
register int	i;
{

/*
 *	Collect macro definition from input stream
 */

	while (*p != EOS)
	{
		if (i >= MXMLEN - 1)
		{
			d[i - 1] = EOS;
			return (ERR);
		}
		d[i++] = *p++;
	}
	d[i] = EOS;
	return (i);
}





/*------------------------------*/
/*	putmac			*/
/*------------------------------*/
putmac (name, p)
char   *name;
char   *p;
{

/*
 *	Put macro definition into table
 *
 *	NOTE: any expansions of things like number registers SHOULD
 *	have been done already.
 */


	/*
	 *   any room left? (did we exceed max number of possible macros)
	 */
	if (mac.lastp >= MXMDEF)
		return (ERR);

	/*
	 *   will new one fit in big buffer?
	 */
	if (mac.emb + strlen (name) + strlen (p) + 1 > &mac.mb[MACBUF])
	{
		return (ERR);
	}


	/*
	 *   add it...
	 *
	 *   bump counter, set ptr to name, copy name, copy def.
	 *   finally increment end of macro buffer ptr (emb).
	 *
	 *   macro looks like this in mb:
	 *
	 *	mac.mb[MACBUF]		size of total buf
	 *	lastp < MXMDEF		number of macros possible
	 *	*mnames[MXMDEF]		-> names, each max length
	 *	..._____________________________...____________________...
	 *	    / / /|X|X|0|macro definition      |0| / / / / / / /
	 *	.../_/_/_|_|_|_|________________...___|_|/_/_/_/_/_/_/_...
	 *		  ^
	 *		  |
	 *		  \----- mac.mnames[mac.lastp] points here
	 *
	 *   both the 2 char name (XX) and the descripton are null term and
	 *   follow one after the other.
	 */
	++mac.lastp;
	mac.mnames[mac.lastp] = mac.emb;
	strcpy (mac.emb, name);
	strcpy (mac.emb + strlen (name) + 1, p);
	mac.emb += strlen (name) + strlen (p) + 2;

	return (OK);
}






/*------------------------------*/
/*	getmac			*/
/*------------------------------*/
char   *getmac (name)
register char  *name;
{

/*
 *	Get (lookup) macro definition from namespace
 */

	register int	i;

	/*
	 *   loop for all macros, starting with last one
	 */
	for (i = mac.lastp; i >= 0; --i)
	{
		/*
		 *   is this REALLY a macro?
		 */
		if (mac.mnames[i])
		{
			/*
			 *   if it compares, return a ptr to it
			 */
			if (!strcmp (name, mac.mnames[i]))
			{
/*!!!debug			puts (mac.mnames[i]);*/

				if (mac.mnames[i][1] == EOS)
					return (mac.mnames[i] + 2);
				else
					return (mac.mnames[i] + 3);
			}
		}
	}

	/*
	 *   none found, return null
	 */
	return (NULL);
}






/*------------------------------*/
/*	maceval			*/
/*------------------------------*/
maceval (p, m)
register char  *p;
char		m[];
{

/*
 *	Evaluate macro expansion
 */

	register int	i;
	register int	j;
	char	       *argp[10];
	char		c;



	/*
	 *   replace command char with EOS
	 */
	*p++ = EOS;


	/* 
	 *   initialize argp array to substitute command
	 *   string for any undefined argument
	 *
	 *	NO!!! this is fixed...
	 */
	for (i = 0; i < 10; ++i)
		argp[i] = p;

	/*
	 *   skip the command name
	 */
	p = skipwd (p);
	*p++ = EOS;


	/*
	 *   loop for all $n variables...
	 */
	for (i = 0; i < 10; ++i)
	{
		/*
		 *   get to substituted param and if no more, reset remaining
		 *   args to NULL and stop...
		 */
		p = skipbl (p);
		if (*p == '\r' || *p == '\n' || *p == EOS)
		{
			for ( ; i < 10; i++)
				*argp[i] = '\0';
			break;
		}


		/*
		 *   ...otherwise, see if this param is quoted. if it is,
		 *   it is all one parameter, even with blanks (but not
		 *   newlines...). look for another "c" (which is the quote).
		 *
		 *   if no quote, just read the arg as a single word and null
		 *   terminate it.
		 */
		if (*p == '\'' || *p == '"')
		{
			c = *p++;
			argp[i] = p;
			while (*p != c && *p != '\r' && *p != '\n' && *p != EOS)
				++p;
			*p++ = EOS;
		}
		else
		{
			argp[i] = p;
			p = skipwd (p);
			*p++ = EOS;
		}
	}


	/*
	 *   m contains text of the macro. p contained the input line.
	 *   here we start at the end of the macro def and see if there
	 *   are any $n thingies. go backwards.
	 */
	for (i = strlen (m) - 1; i >= 0; --i)
	{
		/*
		 *   found a $.
		 */
		if (i > 0 && m[i - 1] == '$')
		{
			if (!isdigit (m[i]))
			{
				/*
				 *   it wasn't a numeric replacement arg so
				 *   push this char back onto input stream
				 */
				putbak (m[i]);
			}
			else
			{
				/*
				 *   it WAS a numeric replacement arg. so we
				 *   want to push back the appropriate macro
				 *   invocation arg. m[i]-'0' is the numerical
				 *   value of the $0 thru $9. if the arg is
				 *   not there, argp[n] will be (char *) 0
				 *   and pbstr will do nothing.
				 */
				pbstr (argp[m[i] - '0']);
				--i;
			}
		}
		else
		{
			/*
			 *   no $ so push back the char...
			 */
			putbak (m[i]);
		}
	}

	/*
	 *   at this point, the iobuf will hold the new macro command, full
	 *   expanded for $n things. the return gets us right back to the
	 *   main loop in main() and we parse the (new) command just as if
	 *   it were read from a file.
	 */

}





/*------------------------------*/
/*	printmac		*/
/*------------------------------*/
printmac (opt)
int	opt;				/* 0=name&size,1=total size,2=full */
{

/*
 *	print all macros and strings and tabulate sizes
 */

	register long	i;
	register long	space;
 	register long	totalspace;
	register char  *pname;
	register char  *pdef;


	space      = 0L;
	totalspace = 0L;

	fflush (pout);
	fflush (err_stream);

	for (i = (long) mac.lastp; i >= 0; --i)
	{
 		/*
		 *   is this REALLY a macro?
		 */
		if (mac.mnames[i])
		{
			pname = (char *) (mac.mnames[i]);
			pdef  = pname + 3;
			if (*(pname + 1) == '\0')
				pdef = pname + 2;

			space       = (long) strlen (pdef);
			totalspace += space;

			switch (opt)
			{
			case 0:
				fprintf (err_stream, "%s %ld\n", pname, space);
				break;
			case 2:
				fprintf (err_stream, "%s %ld\n", pname, space);
				fprintf (err_stream, "%s\n", pdef);
				break;
			case 1:
			default:
				break;
			}
		}
	}
	fprintf (err_stream, "Total space: %ld\n", totalspace);
	
}

@//E*O*F macros.c//
chmod u=rw,g=r,o=r macros.c
 
echo x - strings.c
sed 's/^@//' > "strings.c" <<'@//E*O*F strings.c//'
/*
 *	strings.c - String input/output processing for nroff word processor
 *
 *	adapted for atariST/TOS by Bill Rosenkranz 11/89
 *	net:	rosenkra@hall.cray.com
 *	CIS:	71460,17
 *	GENIE:	W.ROSENKRANZ
 *
 *	original author:
 *
 *	Stephen L. Browning
 *	5723 North Parker Avenue
 *	Indianapolis, Indiana 46220
 *
 *	history:
 *
 *	- Originally written in BDS C;
 *	- Adapted for standard C by W. N. Paul
 *	- Heavily hacked up to conform to "real" nroff by Bill Rosenkranz
 */

#undef NRO_MAIN					/* extern globals */

#include <stdio.h>
#include "nroff.h"



/*------------------------------*/
/*	defstr			*/
/*------------------------------*/
defstr (p)
register char  *p;
{

/*
 *	Define a string. top level, read from command line.
 *
 *	we should read string without interpretation EXCEPT:
 *
 *	1) number registers are interpolated
 *	2) strings indicated by \* are interpolated
 *	3) arguments indicated by \$ are interpolated
 *	4) concealed newlines indicated by \(newline) are eliminated
 *	5) comments indicated by \" are eliminated
 *	6) \t and \a are interpreted as ASCII h tab and SOH.
 *	7) \\ is interpreted as backslash and \. is interpreted as a period.
 *
 *	currently, we do only 3. a good place to do it would be here before
 *	putstr, after colstr...
 */

	register char  *q;
	register int	i;
	char		name[MNLEN];
	char		defn[MXMLEN];



	name[0] = '\0';
	defn[0] = '\0';


	/*
	 *   skip the .ds and get to the name...
	 */
	q = skipwd (p);
	q = skipbl (q);

	/*
	 *   ok, name now holds the name. make sure it is valid (i.e. first
	 *   char is alpha...). getwrd returns the length of the word.
	 */
	i = getwrd (q, name);
	if (!name[0])
	{
		fprintf (err_stream,
			"***%s: missing or illegal string definition name\n",
			myname);
		err_exit (-1);
	}

	/*
	 *   truncate to 2 char max name.
	 */
	if (i > 2)
		name[2] = EOS;


	/*
	 *   skip the name to get to the string. it CAN start with a " to
	 *   have leading blanks...
	 */
	q = skipwd (q);
	q = skipbl (q);



	/*
	 *   read rest of line from input stream and collect string into
	 *   temp buffer defn
	 */
	if ((i = colstr (q, defn)) == ERR)
	{
		fprintf (err_stream,
			"***%s: string definition too long\n", myname);
		err_exit (-1);
	}


	/*
	 *   store the string
	 */
	if (putstr (name, defn) == ERR)
	{
		fprintf (err_stream,
			"***%s: string definition table full\n", myname);
		err_exit (-1);
	}
}





/*------------------------------*/
/*	colstr			*/
/*------------------------------*/
colstr (p, d)
register char  *p;
char		d[];
{

/*
 *	Collect string definition from input stream
 */

	register int	i = 0;

	if (*p == '\"')
		p++;

	while (*p != EOS)
	{
		if (i >= MXMLEN - 1)
		{
			d[i - 1] = EOS;
			return (ERR);
		}
		d[i++] = *p++;
	}
	d[i] = EOS;
	return (i);
}





/*------------------------------*/
/*	putstr			*/
/*------------------------------*/
putstr (name, p)
register char  *name;
register char  *p;
{

/*
 *	Put string definition into (macro) table
 *
 *	NOTE: any expansions of things like number registers SHOULD
 *	have been done already. strings and macros share mb buffer
 */


	/*
	 *   any room left? (did we exceed max number of possible macros)
	 */
	if (mac.lastp >= MXMDEF)
		return (ERR);

	/*
	 *   will new one fit in big buffer?
	 */
	if (mac.emb + strlen (name) + strlen (p) + 1 > &mac.mb[MACBUF])
	{
		return (ERR);
	}


	/*
	 *   add it...
	 *
	 *   bump counter, set ptr to name, copy name, copy def.
	 *   finally increment end of macro buffer ptr (emb).
	 *
	 *   string looks like this in mb:
	 *
	 *	mac.mb[MACBUF]		size of total buf
	 *	lastp < MXMDEF		number of macros/strings possible
	 *	*mnames[MXMDEF]		-> names, each max length
	 *	...______________________________...____________________...
	 *	    / / /|X|X|0|string definition      |0| / / / / / / /
	 *	.../_/_/_|_|_|_|_________________...___|_|/_/_/_/_/_/_/_...
	 *		    ^
	 *		    |
	 *		    \----- mac.mnames[mac.lastp] points here
	 *
	 *   both the 2 char name (XX) and the descripton are null term and
	 *   follow one after the other.
	 */
	++mac.lastp;
	mac.mnames[mac.lastp] = mac.emb;
	strcpy (mac.emb, name);
	strcpy (mac.emb + strlen (name) + 1, p);
	mac.emb += strlen (name) + strlen (p) + 2;
	return (OK);
}






/*------------------------------*/
/*	getstr			*/
/*------------------------------*/
char   *getstr (name)
register char  *name;
{

/*
 *	Get (lookup) string definition from namespace
 */

	register int	i;

	/*
	 *   loop for all macros, starting with last one
	 */
	for (i = mac.lastp; i >= 0; --i)
	{
		/*
		 *   is this REALLY a macro?
		 */
		if (mac.mnames[i])
		{
			/*
			 *   if it compares, return a ptr to it
			 */
			if (!strcmp (name, mac.mnames[i]))
			{
/*!!!debug			puts (mac.mnames[i]);*/

				if (mac.mnames[i][1] == EOS)
					return (mac.mnames[i] + 2);
				else
					return (mac.mnames[i] + 3);
			}
		}
	}

	/*
	 *   none found, return null
	 */
	return (NULL);
}






@//E*O*F strings.c//
chmod u=rw,g=r,o=r strings.c
 
exit 0