home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d1xx
/
d197
/
nro.lha
/
Nro
/
nrocmd.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-03-28
|
34KB
|
1,154 lines
/*
* Command processor for NRO text processor
*
* Originally by Stephen L. Browning, 5723 North Parker Avenue
* Indianapolis, Indiana 46220
*
* Transformed beyond immediate recognition, and
* adapted for Amiga by Olaf Seibert, KosmoSoft
*
* Vossendijk 149-1 (study) Beek 5 (home)
* 5634 TN Nijmegen 5815 CS Merselo
* The Netherlands The Netherlands
* Phone:
* (...-31)80561045 (...-31)4786205
* or 080-561045 04786-205
*
* This program is NOT in the public domain. It may, however
* be distributed only at no charge, and this notice must be
* included unaltered.
*/
#include <stdio.h>
#include "nro.h"
#include "nroxtrn.h"
uchar *skipbl();
uchar *skipwd();
/*
* Communicating the current file through a global
* variable is a bit of a kludge. We could as well use
* it everywhere instead of passing it as a parameter
* all the time. But that would not be very `structured',
* whatever that may be.
*/
comand(word)
uchar *word;
{
int ct, val;
int spval;
uchar argtyp, chr;
uchar *macexp;
val = getcmdwrd(word, infile);
if (word[0] == env.c2chr) {
env.dontbrk = TRUE;
word++;
} else if (word[0] == env.cmdchr) {
word++;
} else if (val > 0) {
putbak(' ');
pbstr(word);
return;
}
ct = comtyp(word, &macexp);
if (ct == UNKNOWN) {
error("nro: unrecognized command %s\n", word);
env.dontbrk = FALSE;
return;
}
if (! (ct & NOARGS))
val = getval(&argtyp, infile); /* Eat more of line */
switch (ct & ~NOARGS) {
case BO: /* Bold face */
set(&env.boval, val, argtyp, 1, -1, HUGE);
if (env.boval) env.reqmode |= FXBO;
else env.reqmode &= ~FXBO;
if (dc.bsflg != BSAMIGA)
env.cuval = env.ulval = 0;
break;
case BP: /* Begin page */
if (pg.lineno > 0) space(pg.plval);
set(&pg.curpag, val, argtyp, pg.curpag+1, -HUGE, HUGE);
pg.newpag = pg.curpag;
break;
case BR: /* Break */
dobrk();
break;
case BS: /* Backspaces in output: 0=No, Amiga; 1=Yes; 2=Use CR */
set(&dc.bsflg, val, argtyp, 1, 0, 2);
break;
case C2: /* No-break command character */
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (iseol(chr)) env.c2chr = C2CHAR;
else { env.c2chr = chr; ct = 0; }
break;
case CC: /* Command character */
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (iseol(chr)) env.cmdchr = CMDCHAR;
else { env.cmdchr = chr; ct = 0; }
break;
case CE: /* Center */
dobrk();
set(&env.ceval, val, argtyp, 1, -1, HUGE);
break;
case COMMENT:
while (chr = ngetc(infile), isnteol(chr));
break;
case CU: /* Continuous underline */
set(&env.cuval, val, argtyp, 1, -1, HUGE);
if (env.cuval) env.reqmode |= FXUL;
else env.reqmode &= ~FXUL;
if (dc.bsflg != BSAMIGA)
env.ulval = env.boval = 0;
break;
case DE: /* Define macro */
defmac(word, infile);
break;
case EF: /* Even footer */
gettl(infile, pg.efoot, NULL, &pg.eflim[0], NULL);
break;
case EH: /* Even header */
gettl(infile, pg.ehead, NULL, &pg.ehlim[0], NULL);
break;
case EL: /* Else */
doel(infile);
break;
case EN: /* End macro definition */
error("nro: missing .de command\n");
break;
case EV: /* Environment switch */
if (isdigit(argtyp)) { /* Supplied argument: push */
if (dc.envsp >= ENVSTACK-1) {
error("nro: cannot push environment.\n"); break;
}
spval = dc.envstack[dc.envsp]; /* Current environment */
dc.envstack[++dc.envsp] = val; /* Save number on stack*/
storenv(spval); /* Save current envir. */
loadenv(val); /* Get new one */
} else { /* Pop an environment */
if ((int) dc.envsp <= 0) {
error("*** nro: cannot pop environment.\n"); break;
}
spval = dc.envstack[dc.envsp]; /* Current environment */
val = dc.envstack[--dc.envsp]; /* Number of old env */
if (argtyp == '-') freenv(spval);/* Dump current environ*/
else storenv(spval); /* ..or save it */
loadenv(val); /* Get old one back */
}
break;
case FI: /* Fill */
dobrk();
env.fill = YES;
break;
case FO: /* Footer */
gettl(infile, pg.efoot, pg.ofoot, &pg.eflim[0], &pg.oflim[0]);
break;
case HE: /* Header */
gettl(infile, pg.ehead, pg.ohead, &pg.ehlim[0], &pg.ohlim[0]);
break;
case IE:
case IF:
doieif(infile, ct & ~NOARGS);
break;
case IN: /* Indenting */
dobrk();
set(&env.inval, val, argtyp, 0, 0, env.tmval-1);
env.tival = env.inval;
break;
case IT: /* Italic face */
set(&env.itval, val, argtyp, 1, -1, HUGE);
if (env.itval) env.reqmode |= FXIT;
else env.reqmode &= ~FXIT;
if (dc.bsflg != BSAMIGA)
error("nro: italics cannot be done by overstrike :-)\n");
break;
case JU: /* Justify */
env.juval = YES;
break;
case LS: /* Line spacing */
set(&env.lsval, val, argtyp, 1, 1, HUGE);
break;
case M1: /* Set topmost margin */
set(&pg.m1val, val, argtyp, 2, 0, HUGE);
break;
case M2: /* Set second top margin */
set(&pg.m2val, val, argtyp, 2, 0, HUGE);
break;
case M3: /* Set first bottom margin */
set(&pg.m3val, val, argtyp, 2, 0, HUGE);
pg.bottom = pg.plval - pg.m4val - pg.m3val;
break;
case M4: /* Set bottom-most margin */
set(&pg.m4val, val, argtyp, 2, 0, HUGE);
pg.bottom = pg.plval - pg.m4val - pg.m3val;
break;
case MACRO: /* Macro expansion */
maceval(macexp, infile);
break;
case NE: /* Need n lines */
/* dobrk(); */
if ((pg.bottom-pg.lineno+1) < (val*env.lsval)) space(pg.plval);
break;
case NF: /* No fill */
dobrk();
env.fill = NO;
break;
case NJ: /* No justify */
env.juval = NO;
break;
case NR: /* Set number register */
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (!isalpha(chr)) {
error("nro: invalid or missing number register name\n");
} else {
ct = tolower(chr) - 'a';
val = getval(&chr, infile);
set(&dc.nr[ct], val, chr, 0, -HUGE, HUGE);
}
ct = 0;
break;
case OF: /* Odd footer */
gettl(infile, pg.ofoot, NULL, &pg.oflim[0], NULL);
break;
case OH: /* Odd header */
gettl(infile, pg.ohead, NULL, &pg.ohlim[0], NULL);
break;
case PC: /* Page number character */
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (iseol(chr)) env.pgchr = EOS;
else { env.pgchr = chr; ct = 0; }
break;
case PL: /* Page length */
set(&pg.plval, val, argtyp, PAGELEN,
pg.m1val+pg.m2val+pg.m3val+pg.m4val+1, HUGE);
pg.bottom = pg.plval - pg.m3val - pg.m4val;
break;
case PN: /* Page numbering mode */
set(&env.pnflg, val, argtyp, PNARABIC, PNARABIC, PNUROMAN);
break;
case PO: /* Page offset */
set(&pg.offset, val, argtyp, 0, 0, HUGE);
break;
case RM: /* Right margin */
set(&env.rmval, val, argtyp, PAGEWIDTH, env.tival+1, HUGE);
env.tmval = env.rmval;
break;
case SO: /* Source file */
if (getcmdwrd(word, infile) == EOF) break;
skipeol(infile);
ct = NOARGS;
if (dc.flevel+1 >= NFILES) {
error("nro: .so commands nested too deeply\n");
break;
}
if ((sofile[dc.flevel+1] = fopen(word, "r")) == NULL) {
error("nro: unable to open %s\n", word);
break;
}
if (verbose > 2) error("nro: processing file '%s'\n", word);
sopbb[dc.flevel] = mac.pbb; /* Stack push back stacks */
mac.pbb = mac.ppb + 1;
dc.flevel++;
infile = sofile[dc.flevel];
break;
case SP: /* Space */
set(&spval, val, argtyp, 1, 0, HUGE);
space(spval * env.lsval);
break;
case TA: /* Tab settings */
tabs(infile);
break;
case TI: /* Temporary indent */
dobrk();
set(&env.tival, val, argtyp, 0, 0, env.tmval);
break;
case TM: /* Terminal Message */
fflush(stdout);
if (env.dontbrk == FALSE || pout != stdout) {
while (val = ngetc(infile), isnteol(val) && val != EOF)
putc(val, stderr);
putc('\n', stderr);
fflush(stderr);
} else ct = 0; /* Skip rest of command line */
break;
case UL: /* Underline */
set(&env.ulval, val, argtyp, -1, 1, HUGE);
if (env.ulval) env.reqmode |= FXUL;
else env.reqmode &= ~FXUL;
if (dc.bsflg != BSAMIGA)
env.cuval = env.boval = 0;
break;
case UN: /* Undefine macro */
undefmac(word, infile);
break;
}
env.dontbrk = FALSE;
if (argtyp != STRINGTYP && !(ct & NOARGS)) skipeol(infile);
return;
}
/*
* Decodes nro command and returns its associated
* value.
*/
comtyp(line, macdef)
uchar *line;
uchar **macdef;
{
uchar c1, c2;
/*
* First check to see if the command is a macro.
* If it is, return expansion in macdef.
* Note that upper and lower case characters are handled
* differently for macro names, but not for normal command names.
*/
if ((*macdef = getmac(line)) != NULL) {
return MACRO | NOARGS;
}
c1 = tolower(*line++);
c2 = tolower(*line );
switch (c1) {
case '"':
case '*':
return COMMENT | NOARGS;
case 'b':
if (c2 == 'o') return BO;
else if (c2 == 'p') return BP;
else if (c2 == 'r') return BR;
else if (c2 == 's') return BS;
break;
case 'c':
if (c2 == '2') return C2 | NOARGS;
else if (c2 == 'c') return CC | NOARGS;
else if (c2 == 'e') return CE;
else if (c2 == 'u') return CU;
break;
case 'd':
if (c2 == 'e') return DE | NOARGS;
break;
case 'e':
if (c2 == 'f') return EF | NOARGS;
else if (c2 == 'h') return EH | NOARGS;
else if (c2 == 'l') return EL | NOARGS;
else if (c2 == 'n') return EN;
else if (c2 == 'v') return EV;
break;
case 'f':
if (c2 == 'i') return FI;
else if (c2 == 'o') return FO | NOARGS;
break;
case 'h':
if (c2 == 'e') return HE | NOARGS;
break;
case 'i':
if (c2 == 'e') return IE | NOARGS;
else if (c2 == 'f') return IF | NOARGS;
else if (c2 == 'n') return IN;
else if (c2 == 't') return IT;
break;
case 'j':
if (c2 == 'u') return JU;
break;
case 'l':
if (c2 == 's') return LS;
break;
case 'm':
if (c2 == '1') return M1;
else if (c2 == '2') return M2;
else if (c2 == '3') return M3;
else if (c2 == '4') return M4;
break;
case 'n':
if (c2 == 'e') return NE;
else if (c2 == 'f') return NF;
else if (c2 == 'j') return NJ;
else if (c2 == 'r') return NR | NOARGS;
break;
case 'o':
if (c2 == 'f') return OF | NOARGS;
else if (c2 == 'h') return OH | NOARGS;
break;
case 'p':
if (c2 == 'c') return PC | NOARGS;
else if (c2 == 'l') return PL;
else if (c2 == 'n') return PN;
else if (c2 == 'o') return PO;
break;
case 'r':
if (c2 == 'm') return RM;
break;
case 's':
if (c2 == 'o') return SO | NOARGS;
else if (c2 == 'p') return SP;
break;
case 't':
if (c2 == 'a') return TA | NOARGS;
else if (c2 == 'i') return TI;
else if (c2 == 'm') return TM | NOARGS;
break;
case 'u':
if (c2 == 'l') return UL;
else if (c2 == 'n') return UN | NOARGS;
}
return UNKNOWN;
}
/*
* Retrieves optional argument following nro command.
* returns positive integer value with sign (if any)
* saved in character addressed by pargtyp.
* In case of a string, puts in a quote.
* It won't read the character it doesn't understand,
* i.e. semicolons and newlines, and garbage.
*/
getval(pargtyp, infile)
uchar *pargtyp;
register FILE *infile;
{
register uchar chr, operator = '+';
short digit;
register int val, cumval;
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
*pargtyp = chr;
if (chr == '+' || chr == '-' || chr == '/' || chr == '*')
chr = ngetc(infile);
else if (chr == STRINGTYP)
return 1;
cumval = 0;
again:
val = 0;
if (chr == '(') { /* Parenthesized subexpression */
val = getval(&digit, infile); /* Dummy argtype pointer */
chr = ngetc(infile);
if (chr != ')')
error("nro: missing close parenthesis in expression\n");
else
chr = ngetc(infile); /* Get next operator or anything */
if (*pargtyp == '(') *pargtyp = '0';
} else { /* Try to collect a number from the input */
while ((digit = atod(chr)) >= 0) {
val = 10 * val + digit;
chr = ngetc(infile);
}
}
/* Check if we need to evaluate an operator */
if (operator) {
switch (operator) {
case '+': cumval += val; break;
case '-': cumval -= val; break;
case '*': cumval *= val; break;
case '/':
if (val != 0) cumval /= val;
else error("nro: division by zero\n");
break;
case '%':
if (val != 0) cumval %= val;
else error("nro: modulo by zero\n");
break;
case '<': cumval = cumval < val; break;
case '=': cumval = cumval == val; break;
case '>': cumval = cumval > val; break;
case '&': cumval = cumval & val; break;
case '|': cumval = cumval | val; break;
}
operator = '\0';
}
/* See if there is more to come */
switch (chr) {
case '+': case '-': case '*': case '/': case '%':
case '<': case '=': case '>':
case '&': case '|':
operator = chr;
chr = ngetc(infile);
goto again;
default:
putbak(chr); /* Put back what we can't interpret */
return cumval;
}
}
/*
* Convert string to decimal.
* processes only positive values.
*/
ctod(p)
uchar *p;
{
int val, d;
val = 0;
while (*p != EOS) {
d = atod(*p++);
if (d == -1) return val;
val = 10 * val + d;
}
return val;
}
/*
* Convert ascii character to decimal.
*/
atod(c)
uchar c;
{
return ((c < '0') || (c > '9')) ? -1 : c-'0';
}
/*
* Get non-blank word from the input file.
* Returns the number of spaces skipped before the word,
* or EOF on end of file.
* It won't read past a newline or semicolon,
* but will skip the first space following the word.
*/
getcmdwrd(to, infile)
register uchar *to;
register FILE *infile;
{
register short chr;
int skipped = 0;
short length = 0;
chr = ngetc(infile);
if (chr == EOF) {
*to = EOS;
return EOF;
}
/* Skip spaces */
while (isspace(chr)) {
chr = ngetc(infile);
skipped++;
}
while (isntspace(chr) && isnteol(chr) && chr != EOF &&
chr != MORETXT && length < MAXWORD-3) {
*to++ = chr;
length++;
chr = ngetc(infile);
}
if (iseol(chr) || chr == MORETXT) putbak(chr);
*to = EOS;
if (length >= MAXWORD-3) error("nro: command word buffer overflow\n");
return skipped;
}
/*
* Skip the rest of the current input line
*/
skipeol(infile)
FILE *infile;
{
int chr;
while ((chr = ngetc(infile)) != '\n' && chr != MORETXT &&
chr != EOF);
}
/*
* Process an IF or IE request
*/
doieif(infile, request)
FILE *infile;
int request;
{
uchar *strp;
short negation;
int chr, val;
uchar delim;
uchar string[MAXWORD];
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (chr == '!') {
negation = TRUE;
chr = ngetc(infile);
} else
negation = FALSE;
switch (chr) {
case 'e': /* Even page number */
val = (pg.curpag % 2) == 0;
break;
case 'o': /* Odd page number */
val = (pg.curpag % 2) != 0;
break;
case 'n': /* Nro(ff) is formatter */
val = TRUE; break;
case 't': /* Troff certainly not */
val = FALSE; break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '+': case '-': case '(':
/* Must be an expression */
putbak(chr);
chr = getval(&string[0], infile);
val = 0;
set(&val, chr, string[0], 0, -HUGE, HUGE);
val = val > 0;
break;
default: /* String comparison */
delim = chr;
strp = string; /* Collect first string */
while ((chr = ngetc(infile)) != delim &&
strp < string + sizeof(string) - 2)
*strp++ = chr;
*strp = EOS;
val = TRUE;
strp = string; /* Compare with second string */
while ((chr = ngetc(infile)) != delim) {
if (*strp++ != chr) val = FALSE;
}
if (*strp != EOS) val = FALSE;
break;
}
if (negation) val = !val;
if (request == IE) env.lastie = val;
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (val) { /* Condition is true. Don't skip any text */
if (chr != BEGIF) putbak(chr);
} else { /* Need to skip some text, maybe even very much */
if (chr == BEGIF) /* Skip until end of line */
dc.iflvl = 1;
while (isnteol(chr)) chr = ngetc(infile);
}
}
/*
* Process an EL request
*/
doel(infile)
FILE *infile;
{
int chr, val;
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
/* Toggle last remembered condition */
val = env.lastie = !env.lastie;
if (val) { /* Condition is true. Don't skip any text */
if (chr != BEGIF) putbak(chr);
} else { /* Need to skip some text, maybe even very much */
if (chr == BEGIF) /* Skip until end of line */
dc.iflvl = 1;
while (isnteol(chr)) chr = ngetc(infile);
}
}
/*
* Process a TA request
*/
tabs(infile)
FILE *infile;
{
int chr, val, i, j;
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (iseol(chr) || chr == EOF) { /* No arguments */
for (i=0; i<MAXTAB; i++)
env.tabstop[i] = NOTAB;
return;
}
i = env.inval;
for (;;) { /* Collect the values */
putbak(chr);
val = getval((uchar *)&chr, infile);
if (*(uchar *)&chr == '+') { /* Saves a temporary uchar :-) */
val += i;
}
/* Find where to insert the tab */
i=0;
while (i<MAXTAB && env.tabstop[i] < val) i++;
if (i < MAXTAB && env.tabstop[i] != val) {
/* Insert the tab */
for (j=MAXTAB-1; j > i; j--)
env.tabstop[j] = env.tabstop[j-1];
env.tabstop[i] = val;
}
i = val;
while ((chr = ngetc(infile)) == ' ' || chr == '\t');
if (iseol(chr) || chr == MORETXT || chr == EOF) break;
}
}
/*
* Loadenv - get an environment
*/
loadenv(number)
int number;
{
struct environ *envp;
short freeit = env.dontbrk;
if (number < 0 || number >= NUMENV) return ERR;
envp = environ[number];
if (envp) { /* It't there. Copy it. */
env = *envp;
if (freeit) { /* We may free the saved image of it, */
freenv(number); /* if we are tight on memory. */
}
} else { /* It isn't. Just use default values. */
initenv();
}
return OK;
}
/*
* Storenv - save an environment
*/
storenv(number)
int number;
{
struct environ *envp;
if (number < 0 || number >= NUMENV) return ERR;
envp = environ[number];
if (envp == NULL) { /* Never saw this guy before */
if ((envp = (struct environ *)malloc(sizeof(*envp))) == NULL) {
error("*** nro: cannot allocate environment #%d\n", number);
return ERR;
} else if (verbose > 2)
error("nro: allocated environment #%d\n", number);
environ[number] = envp;
}
*envp = env;
return OK;
}
/*
* Freenv - throw an environment away
*/
freenv(number)
int number;
{
struct environ *envp;
if (number < 0 || number >= NUMENV) return ERR;
envp = environ[number];
if (envp) {
free(envp);
environ[number] = NULL;
if (verbose > 2) error("nro: freed environment #%d\n", number);
}
return OK;
}
/*
* End current filled line
*/
dobrk()
{
if (!env.dontbrk) { /* Breaks may be disabled by using .'xx */
if (env.outp > 0) {
#ifdef CPM
env.outbuf[env.outp++] = '\r';
env.outbuf[env.outp++] = '\n';
env.outbuf[env.outp ] = EOS;
#else CPM
env.outbuf[env.outp++] = '\n';
env.outbuf[env.outp ] = EOS;
#endif CPM
if (env.ceval != 0) /* Centering */
center(env.outbuf);
put(env.outbuf);
}
env.outp = 0;
env.outw = 0;
env.outwds = 0;
}
}
/*
* Define a macro
*/
defmac(word, infile)
uchar *word;
FILE *infile;
{
uchar line[MAXLINE];
getcmdwrd(word, infile);
skipeol(infile);
if (word[0] == EOS && verbose)
error("nro: appending to macro definition\n");
while (getlin(line, infile) != EOF) {
if (line[0] == env.cmdchr && line[1] == 'e' && line[2] == 'n')
break;
if (putmac(word, line) == ERR) {
error("*** nro: macro definition table full\n");
}
word[0] = EOS;
}
}
/*
* Put macro definition into table.
* If name == "", concatenate this definition to the previous one.
*/
putmac(name, def)
uchar *name;
uchar *def;
{
int lenofname, lenofdef;
lenofname = strlen(name);
lenofdef = strlen(def);
if ((lenofname && mac.lastp >= mac.mxmdef) ||
(mac.emb + lenofname + lenofdef + 2 > &mac.mb[mac.macbuf])) {
return ERR;
}
if (lenofname) {
++mac.lastp;
mac.mnames[mac.lastp] = mac.emb;
strcpy(mac.emb, name);
mac.emb += lenofname + 1;
} else
mac.emb--;
strcpy(mac.emb, def);
mac.emb += lenofdef + 1;
return OK;
}
/*
* Get macro definition from table
*/
uchar *getmac(name)
uchar *name;
{
register int i;
register uchar *name1, *mname;
for (i = mac.lastp; i > 0; --i) { /*V1.5*/
name1 = name;
mname = mac.mnames[i];
while (*(name1++) == *(mname++)) {
if (name1[-1] == EOS) return mname;
}
}
return NULL;
}
/*
* Delete macro definition from table
*/
undefmac(word, infile)
uchar *word;
FILE *infile;
{
register int i;
register uchar *name, *mname;
getcmdwrd(word, infile);
skipeol(infile);
for (i = mac.lastp; i >= 0; --i) {
name = word;
mname = mac.mnames[i];
while (*(name++) == *(mname++)) {
if (name[-1] == EOS) {
mac.lastp = i-1;
mac.emb = mac.mnames[i];
return OK;
}
}
}
return ERR;
}
/*
* Evaluate macro expansion for re-evaluation
*/
maceval(macdef, infile)
register uchar *macdef;
FILE *infile;
{
uchar *argp[10];
int i;
uchar c;
uchar line[MAXLINE];
register uchar *linep = line;
*linep++ = EOS;
getlin(linep, infile);
/*
* Initialize argp array to substitute empty string
* string for any undefined argument
*/
for (i=0; i<10; ++i) argp[i] = line;
for (i=0; i<10; ++i) {
linep = skipbl(linep);
if (iseol(*linep) || *linep == MORETXT || *linep == EOS) break;
if (*linep == '\'' || *linep == '"') {
c = *linep++;
argp[i] = linep;
while (*linep != c && isnteol(*linep) && *linep != EOS)
linep++;
*linep++ = EOS;
} else {
argp[i] = linep;
linep = skipwd(linep);
*linep++ = EOS;
}
}
/* First push back any remaining text or commands */
if (*linep == MORETXT) pbstr(linep+1);
/* Now push back macro body */
for (i=strlen(macdef)-1; i>=0; --i) {
/* Need we substitute an argument? */
if (i > 0 && macdef[i-1] == '$') {
if (!isdigit(macdef[i]) || macdef[i-2] == '$') {
/* Re-evaluate escape characters from macro body */
putbak(macdef[i] | NOGUARD);
} else {
/* but not of any arguments */
pbstr(argp[macdef[i]-'0']);
--i;
}
} else {
putbak(macdef[i] | NOGUARD);
}
}
}
/*
* Push back string into input stream for re-evaluation
*/
pbstr(p)
register uchar p[];
{
register int i;
for (i=strlen(p)-1; i>=0; --i) {
putbak(p[i]);
}
}
/*
* Push character back into input stream
*/
putbak(c)
int c;
{
if (mac.ppb < mac.pbb) {
mac.ppb = mac.pbb;
*mac.ppb = c;
} else {
if (mac.ppb >= mac.pbbend-2) {
error("*** nro: push back buffer overflow\n");
}
*++mac.ppb = c;
}
/* Avoid evaluating escaped escape characters twice */
if (c == ESCCHAR)
*++mac.ppb = ESCCHAR;
}
/*
* Get header or footer title
*/
gettl(infile, line1, line2, limit1, limit2)
FILE *infile;
uchar *line1, *line2;
int limit1[], limit2[];
{
uchar c;
while (c = ngetc(infile), isspace(c));
line1[0] = c;
if (isnteol(c)) getlin(line1+1, infile);
limit1[LEFT] = env.inval;
limit1[RIGHT] = env.rmval;
if (line2) {
strcpy(line2, line1);
limit2[LEFT] = limit1[LEFT];
limit2[RIGHT] = limit1[RIGHT];
}
}
/*
* Set parameter and check range
*/
set(param, val, type, defval, minval, maxval)
int *param;
int val;
uchar type;
int defval, minval, maxval;
{
switch(type) {
case '+':
*param += val; break;
case '-':
*param -= val; break;
case '*':
*param *= val; break;
case '/':
if (val != 0) *param /= val;
else error ("nro: division by zero modification\n");
break;
case '%':
if (val != 0) *param %= val;
else error ("nro: modulo by zero modification\n");
break;
default:
*param = isdigit(type)? val : defval;
break;
}
if (*param < minval)
*param = minval;
else if (*param > maxval)
*param = maxval;
}
/*
* Skip blanks and tabs in character buffer.
* return pointer to first non-space char.
*/
uchar *skipbl(p)
uchar *p;
{
while (isspace(*p)) ++p;
return p;
}
/*
* Skip over word and punctuation
*/
uchar *skipwd(p)
uchar *p;
{
while (isntspace(*p) && isnteol(*p) && *p != EOS)
++p;
return p;
}
/*
* Space vertically n lines
*/
space(n)
int n;
{
dobrk();
if (pg.lineno > pg.bottom) return;
if (pg.lineno == 0) phead();
skip(min(n, pg.bottom+1-pg.lineno));
pg.lineno += n;
if (pg.lineno > pg.bottom) pfoot();
}