mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-08-28 17:29:32 +00:00
695 lines
14 KiB
C
695 lines
14 KiB
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
|
|
* - Heavily modified by Devin Reade to avoid memory trashing bugs.
|
|
*
|
|
* $Id: macros.c,v 1.2 1997/03/20 06:40:50 gdr Exp $
|
|
*/
|
|
|
|
#ifdef __ORCAC__
|
|
segment "macros____";
|
|
#pragma noroot
|
|
#pragma optimize 78
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "unix/err.h"
|
|
|
|
#ifdef sparc
|
|
#include "unix/sunos.h"
|
|
#endif
|
|
|
|
#include "nroff.h"
|
|
#include "macros.h"
|
|
#include "io.h"
|
|
|
|
static int colmac (char *p, char *d, int i);
|
|
static int putmac (char *name, char *p);
|
|
|
|
void
|
|
initMacros (void) {
|
|
memset(mac.mnames, 0, MXMDEF * sizeof(char*));
|
|
memset(mac.mb, 0, MACBUF);
|
|
memset(mac.pbb, 0, MAXLINE);
|
|
#if 0
|
|
for (i = 0; i < MXMDEF; ++i) {
|
|
mac.mnames[i] = NULL_CPTR;
|
|
}
|
|
for (i = 0; i < MACBUF; ++i) {
|
|
mac.mb[i] = EOS;
|
|
}
|
|
for (i = 0; i < MAXPBB; ++i) {
|
|
mac.pbb[i] = EOS;
|
|
}
|
|
#endif
|
|
mac.lastp = 0;
|
|
mac.emb = &mac.mb[0];
|
|
mac.ppb = NULL_CPTR;
|
|
|
|
}
|
|
|
|
/*
|
|
* defmac
|
|
*
|
|
* 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...
|
|
*/
|
|
|
|
void
|
|
defmac (char *line, FILE *infp)
|
|
{
|
|
register char *q;
|
|
register int i;
|
|
char name[MNLEN];
|
|
char defn[MXMLEN];
|
|
char newend[10];
|
|
|
|
#undef BORK
|
|
#ifdef BORK
|
|
fprintf(stderr, "DEBUG defmac (%s:%d): \"%s\"\n", __FILE__, __LINE__, line);
|
|
#endif
|
|
|
|
/*
|
|
* terminate defn; if it's still this way when we're ready to putmac(),
|
|
* then the macro definition was empty and we're going to ignore it.
|
|
*/
|
|
defn[0] = '\0';
|
|
|
|
/*
|
|
* skip the .de and get to the name...
|
|
*/
|
|
q = skipwd (line);
|
|
q = skipbl (q);
|
|
|
|
/*
|
|
* q now points to the name. Copy it into the "name" buffer.
|
|
* Make sure it is valid (i.e. first char is alpha...). The
|
|
* getwrd function returns the length of the word.
|
|
*/
|
|
i = getwrd (q, name);
|
|
if (!isprint (*name)) {
|
|
errx(-1, "missing or illegal macro definition name");
|
|
}
|
|
|
|
/*
|
|
* 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 (line);
|
|
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 (line, infp) != EOF) {
|
|
#ifdef BORK
|
|
fprintf(stderr,"DEBUG: %s:%d line is \"%s\"\n", __FILE__, __LINE__,
|
|
line);
|
|
#endif
|
|
if (line[0] == dc.cmdchr && line[1] == '\\' && line[2] == '\"') {
|
|
/*
|
|
* comment, ignore it
|
|
*/
|
|
continue;
|
|
}
|
|
if (line[0] == dc.cmdchr && newend[0] != EOS
|
|
&& line[1] == newend[0] && line[2] == newend[1]) {
|
|
/*
|
|
* replacement end found
|
|
*/
|
|
break;
|
|
}
|
|
if (line[0] == dc.cmdchr && line[1] == 'e' && line[2] == 'n') {
|
|
/*
|
|
* .en found
|
|
*/
|
|
break;
|
|
}
|
|
if (line[0] == dc.cmdchr && line[1] == dc.cmdchr) {
|
|
/*
|
|
* .. found
|
|
*/
|
|
#ifdef BORK
|
|
fprintf(stderr,"DEBUG: %s:%d: dot-dot found\n",__FILE__,__LINE__);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* collect macro from the line we just read. all this does
|
|
* is put it in the string defn.
|
|
*/
|
|
#ifdef BORK
|
|
fprintf(stderr,"DEBUG: %s:%d: collecting macro\n",__FILE__,__LINE__);
|
|
#endif
|
|
if ((i = colmac (line, defn, i)) == ERR) {
|
|
errx(1, "macro definition too long");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* store the macro
|
|
*/
|
|
if (!ignoring && defn[0] != '\0') {
|
|
if (putmac (name, defn) == ERR) {
|
|
errx(-1, "macro definition table full");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* colmac
|
|
*
|
|
* Collect macro definition from input stream
|
|
*/
|
|
static int
|
|
colmac (char *p, char *d, int i) {
|
|
|
|
char *pstart;
|
|
int istart;
|
|
|
|
pstart = p;
|
|
istart = i;
|
|
while (*p != EOS) {
|
|
/*
|
|
* are we over the length limit for a single macro?
|
|
*/
|
|
if (i >= MXMLEN - 1) {
|
|
d[i - 1] = EOS;
|
|
return (ERR);
|
|
}
|
|
|
|
/*
|
|
* "i break for comments..."
|
|
*/
|
|
if (*p == '\\' && *(p+1) == '\"') {
|
|
/*
|
|
* first back over any whitespace between comment
|
|
* start and last character in line. remember to
|
|
* decrement counter i, too...
|
|
*/
|
|
p--;
|
|
while (isspace (*p) && p > pstart && i > istart) {
|
|
p--;
|
|
i--;
|
|
}
|
|
|
|
/*
|
|
* now skip over the comment until we reach the
|
|
* trailing newline
|
|
*/
|
|
while (*p != EOS) {
|
|
if (*p == '\n' || *p == '\r') {
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* skip quoted things
|
|
*/
|
|
if (*p == '\\' && *(p+1) == '\\') {
|
|
p++;
|
|
}
|
|
|
|
/*
|
|
* copy it
|
|
*/
|
|
d[i++] = *p++;
|
|
}
|
|
d[i] = EOS;
|
|
return (i);
|
|
}
|
|
|
|
|
|
/*
|
|
* putmac
|
|
*
|
|
* Put macro definition into table
|
|
*
|
|
* NOTE: any expansions of things like number registers SHOULD
|
|
* have been done already.
|
|
*/
|
|
|
|
static int
|
|
putmac (char *name, char *p) {
|
|
|
|
/*
|
|
* any room left? (did we exceed max number of possible macros)
|
|
*/
|
|
if (mac.lastp >= MXMDEF) {
|
|
return (ERR);
|
|
}
|
|
|
|
/*
|
|
* will new one fit in big buffer?
|
|
*/
|
|
#ifdef DEBUG
|
|
strlen(name);
|
|
strlen(p);
|
|
#endif
|
|
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
|
|
*
|
|
* Get (lookup) macro definition from namespace
|
|
*/
|
|
char *
|
|
getmac (char *name) {
|
|
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])) {
|
|
#if 0 /* !!!debug */
|
|
puts (mac.mnames[i]);
|
|
#endif
|
|
|
|
if (mac.mnames[i][1] == EOS) {
|
|
return (mac.mnames[i] + 2);
|
|
} else {
|
|
return (mac.mnames[i] + 3);
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* none found, return null
|
|
*/
|
|
return (NULL_CPTR);
|
|
}
|
|
|
|
|
|
/*
|
|
* maceval
|
|
*
|
|
* Evaluate macro expansion from p into m
|
|
*/
|
|
void
|
|
maceval (char *p, char *m) {
|
|
register int i;
|
|
char *argp[15];
|
|
char c;
|
|
int xc;
|
|
|
|
/*
|
|
* replace command char with EOS
|
|
*/
|
|
*p++ = EOS;
|
|
|
|
/*
|
|
* initialize argp array to substitute command
|
|
* string for any undefined argument
|
|
*
|
|
* NO!!! this is fixed...
|
|
*/
|
|
#if 0
|
|
for (i = 0; i < 10; ++i) {
|
|
argp[i] = p;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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. using "i" here IS ok...
|
|
*/
|
|
p = skipbl (p);
|
|
if (*p == '\r' || *p == '\n' || *p == EOS) {
|
|
DEBUGGING(("maceval: set_ireg(.$, %d, 0)", i));
|
|
|
|
set_ireg (".$", i, 0);
|
|
for ( ; i < 10; i++) {
|
|
argp[i] = NULL_CPTR;
|
|
}
|
|
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 $1 thru $9. if the arg is
|
|
* not there, argp[n] will be (char *) 0
|
|
* and pbstr will do nothing.
|
|
*/
|
|
xc = m[i] - '1';
|
|
if (argp[xc]) {
|
|
pbstr (argp[xc]);
|
|
}
|
|
--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
|
|
* print macro data:
|
|
* opt print
|
|
* === =====
|
|
* 0 name and size
|
|
* 1 total size
|
|
* 2 full information
|
|
*/
|
|
|
|
void
|
|
printmac (int opt) {
|
|
register int i; /* was long, minix needs int */
|
|
register long localSpace;
|
|
register long totalspace;
|
|
register char *pname;
|
|
register char *pdef;
|
|
|
|
|
|
localSpace = 0L;
|
|
totalspace = 0L;
|
|
|
|
fflush (out_stream);
|
|
fflush (err_stream);
|
|
|
|
for (i = 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;
|
|
}
|
|
|
|
localSpace = (long) strlen (pdef);
|
|
totalspace += localSpace;
|
|
|
|
switch (opt) {
|
|
case 0:
|
|
fprintf (err_stream, "%s %ld\n", pname, localSpace);
|
|
break;
|
|
case 2:
|
|
fprintf (err_stream, "%s %ld\n", pname, localSpace);
|
|
fprintf (err_stream, "%s\n", pdef);
|
|
break;
|
|
case 1:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fprintf (err_stream, "Total space: %ld\n", totalspace);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* putstr
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
int
|
|
putstr (const char *name, const char *p) {
|
|
|
|
/*
|
|
* 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
|
|
* Get (lookup) string definition from namespace
|
|
*/
|
|
char *
|
|
getstr (char *name) {
|
|
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])) {
|
|
#if 0 /* !!!debug */
|
|
puts (mac.mnames[i]);
|
|
#endif
|
|
if (mac.mnames[i][1] == EOS) {
|
|
return (mac.mnames[i] + 2);
|
|
} else {
|
|
return (mac.mnames[i] + 3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* none found, return null
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifdef NOT_USED
|
|
|
|
/*
|
|
* putbak
|
|
*
|
|
* Push character back into input stream. we use the push-back buffer
|
|
* stored with macros.
|
|
*/
|
|
void
|
|
putbak (char c)
|
|
{
|
|
if (mac.ppb == NULL) { /* first time executing this code */
|
|
mac.ppb = mac.pbb;
|
|
*mac.ppb = c;
|
|
} else if (mac.ppb < mac.pbb + MAXPBB) {
|
|
mac.ppb++;
|
|
*(mac.ppb) = c;
|
|
} else {
|
|
errx(-1, "push back buffer overflow (%d chars)", MAXPBB);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* ngetc
|
|
* get a character from the putback buffer or from <infp>
|
|
*/
|
|
int ngetc (FILE *infp)
|
|
{
|
|
register int c;
|
|
|
|
if (mac.ppb >= mac.pbb) {
|
|
c = *mac.ppb--;
|
|
} else {
|
|
c = getc (infp);
|
|
}
|
|
return (c);
|
|
}
|
|
#endif /* NOT_USED */
|