[net.sources] Public Domain NRO - nrocmd.c

jordan@mddc.UUCP (08/15/83)

/*
 *      Command processor for NRO text processor
 *
 *      Stephen L. Browning
 *      5723 North Parker Avenue
 *      Indianapolis, Indiana 46220
 */

#include <stdio.h>
#include "nro.h"
#include "nroxtrn.c"

char *skipbl();
char *skipwd();

comand(p)
char *p;
{
        int ct, val;
        int spval;
        int index;
        char argtyp;
        char name[MAXLINE];
        char macexp[MXMLEN];

        ct = comtyp(p,macexp);
        if (ct == UNKNOWN) {
                printf("*** nro: unrecognized command %s\n",p);
                return;
        }
        expesc(p,name);
        val = getval(p,&argtyp);
        switch (ct) {
        case BO: /* bold face */
                set(&dc.boval,val,argtyp,1,0,HUGE);
                dc.cuval = dc.ulval = 0;
                break;
        case BP: /* begin page */
                if(pg.lineno > 0) space(HUGE);
                set(&pg.curpag,val,argtyp,pg.curpag+1,-HUGE,HUGE);
                pg.newpag = pg.curpag;
                break;
        case BR: /* break */
                brk();
                break;
        case BS: /* backspaces in output */
                set(&dc.bsflg,val,argtyp,1,0,1);
                break;
        case CC: /* command character */
                if (argtyp == '\r' || argtyp == '\n') dc.cmdchr = '.';
                else dc.cmdchr = argtyp;
                break;
        case CE: /* center */
                brk();
                set(&dc.ceval,val,argtyp,1,0,HUGE);
                break;
        case CU: /* continuous underline */
                set(&dc.cuval,val,argtyp,1,0,HUGE);
                dc.ulval = dc.boval = 0;
                break;
        case DE: /* define macro */
                defmac(p,sofile[dc.flevel]);
                break;
        case EF: /* even footer */
                gettl(p,pg.efoot,&pg.eflim[0]);
                break;
        case EH: /* even header */
                gettl(p,pg.ehead,&pg.ehlim[0]);
                break;
        case EN: /* end macro definition */
                puts("***nro: missing .de command\n");
                break;
        case FI: /* fill */
                brk();
                dc.fill = YES;
                break;
        case FO: /* footer */
                gettl(p,pg.efoot,&pg.eflim[0]);
                gettl(p,pg.ofoot,&pg.oflim[0]);
                break;
        case HE: /* header */
                gettl(p,pg.ehead,&pg.ehlim[0]);
                gettl(p,pg.ohead,&pg.ohlim[0]);
                break;
        case IN: /* indenting */
                set(&dc.inval,val,argtyp,0,0,dc.rmval-1);
                dc.tival = dc.inval;
                break;
        case JU: /* justify */
                dc.juval = YES;
                break;
        case LS: /* line spacing */
                set(&dc.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(p,macexp);
                break;
        case NE: /* need n lines */
                brk();
                if ((pg.bottom-pg.lineno+1) < (val*dc.lsval)) {
                        space(HUGE);
                }
                break;
        case NF: /* no fill */
                brk();
                dc.fill = NO;
                break;
        case NJ: /* no justify */
                dc.juval = NO;
                break;
        case NR: /* set number register */
                p = skipwd(p);
                p = skipbl(p);
                if (!isalpha(*p)) {
                        puts("***nro: invalid or missing number register name\n");
                }
                else {
                        index = tolower(*p) - 'a';
                        p = skipwd(p);
                        val = getval(p,&argtyp);
                        set(&dc.nr[index],val,argtyp,0,-HUGE,HUGE);
                }
                break;
        case OF: /* odd footer */
                gettl(p,pg.ofoot,&pg.oflim[0]);
                break;
        case OH: /* odd header */
                gettl(p,pg.ohead,&pg.ohlim[0]);
                break;
        case PC: /* page number character */
                if (argtyp == '\r' || argtyp == '\n') dc.pgchr = EOS;
                else dc.pgchr = argtyp;
                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 PO: /* page offset */
                set(&pg.offset,val,argtyp,0,0,HUGE);
                break;
        case RM: /* right margin */
                set(&dc.rmval,val,argtyp,PAGEWIDTH,dc.tival+1,HUGE);
                break;
        case SO: /* source file */
                p = skipwd(p);
                p = skipbl(p);
                if (getwrd(p,name) == 0) break;
                if (dc.flevel+1 >= NFILES) {
                        puts("***nro: .so commands nested too deeply\n");
                        exit(-1);
                }
                if ((sofile[dc.flevel+1] = fopen(name,"r")) == NULL) {
                        printf("***nro: unable to open %s\n",name);
                        exit(-1);
                }
                ++dc.flevel;
                break;
        case SP: /* space */
                set(&spval,val,argtyp,1,0,HUGE);
                space(spval);
                break;
        case TI: /* temporary indent */
                brk();
                set(&dc.tival,val,argtyp,0,0,dc.rmval);
                break;
        case UL: /* underline */
                set(&dc.ulval,val,argtyp,0,1,HUGE);
                dc.cuval = dc.boval = 0;
                break;
        }
}



/*
 *      convert ascii character to decimal.
 */

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



/*
 *      end current filled line
 */

brk()
{
        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;
}


/*
 *      Collect macro definition from input stream
 */

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




/*
 *      decodes nro command and returns its associated
 *      value.
 */

comtyp(p,m)
char *p;
char *m;
{
        char c1, c2;
        char macnam[MNLEN];
        char *s;

        p++;
        /*
        *       First check to see if the command is a macro.
        *       If it is, truncate to two characters and return
        *       expansion in m.  Note that upper and lower case
        *       characters are handled differently for macro names,
        *       but not for normal command names.
        */
        getwrd(p,macnam);
        macnam[2] = EOS;
        if ((s = getmac(macnam)) != NULL) {
                strcpy(m,s);
                return(MACRO);
        }
        c1 = tolower(*p++);
        c2 = tolower(*p);
        if (c1 == 'b' && c2 == 'o') return(BO);
        if (c1 == 'b' && c2 == 'p') return(BP);
        if (c1 == 'b' && c2 == 'r') return(BR);
        if (c1 == 'b' && c2 == 's') return(BS);
        if (c1 == 'c' && c2 == 'c') return(CC);
        if (c1 == 'c' && c2 == 'e') return(CE);
        if (c1 == 'c' && c2 == 'u') return(CU);
        if (c1 == 'd' && c2 == 'e') return(DE);
        if (c1 == 'e' && c2 == 'f') return(EF);
        if (c1 == 'e' && c2 == 'h') return(EH);
        if (c1 == 'e' && c2 == 'n') return(EN);
        if (c1 == 'f' && c2 == 'i') return(FI);
        if (c1 == 'f' && c2 == 'o') return(FO);
        if (c1 == 'h' && c2 == 'e') return(HE);
        if (c1 == 'i' && c2 == 'n') return(IN);
        if (c1 == 'j' && c2 == 'u') return(JU);
        if (c1 == 'l' && c2 == 's') return(LS);
        if (c1 == 'm' && c2 == '1') return(M1);
        if (c1 == 'm' && c2 == '2') return(M2);
        if (c1 == 'm' && c2 == '3') return(M3);
        if (c1 == 'm' && c2 == '4') return(M4);
        if (c1 == 'n' && c2 == 'e') return(NE);
        if (c1 == 'n' && c2 == 'f') return(NF);
        if (c1 == 'n' && c2 == 'j') return(NJ);
        if (c1 == 'n' && c2 == 'r') return(NR);
        if (c1 == 'o' && c2 == 'f') return(OF);
        if (c1 == 'o' && c2 == 'h') return(OH);
        if (c1 == 'p' && c2 == 'c') return(PC);
        if (c1 == 'p' && c2 == 'l') return(PL);
        if (c1 == 'p' && c2 == 'o') return(PO);
        if (c1 == 'r' && c2 == 'm') return(RM);
        if (c1 == 's' && c2 == 'o') return(SO);
        if (c1 == 's' && c2 == 'p') return(SP);
        if (c1 == 't' && c2 == 'i') return(TI);
        if (c1 == 'u' && c2 == 'l') return(UL);
        return(UNKNOWN);
}



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

ctod(p)
char *p;
{
        int val, d;

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


/*
 *      Define a macro
 */

defmac(p,infp)
char *p;
FILE *infp;
{
        int i;
        char name[MNLEN];
        char defn[MXMLEN];
        char *q;

        q = skipwd(p);
        q = skipbl(q);
        i = getwrd(q,name);
        if (!isalpha(*name)) {
                puts("***nro: missing or illegal macro definition name\n");
                exit(-1);
        }
        if (i > 2) name[2] = EOS;
        i = 0;
        while (getlin(p,infp) != EOF) {
                if (p[0] == dc.cmdchr && tolower(p[1]) == 'e' && tolower(p[2]) == 'n') {
                        break;
                }
                if ((i = colmac(p,defn,i)) == ERR) {
                        puts("***nro: macro definition too long\n");
                        exit(-1);
                }
        }
        if (putmac(name,defn) == ERR) {
                puts("***nro: macro definition table full\n");
                exit(-1);
        }
}


/*
 *      Expand escape sequences
 */

expesc(p,q)
char *p;
char *q;
{
        char *s, *t;

        s = p;
        t = q;
        while (*s != EOS) {
                if (*s != '@') {
                        *t++ = *s++;
                }
                else if (*(s+1) == '@') {
                        *t++ = *s++;
                        ++s;
                }
                else if (tolower(*(s+1)) == 'n' && isalpha(*(s+2))) {
                        s += 2;
                        t += itoda(dc.nr[tolower(*s)-'a'],t,6) - 1;
                        ++s;
                }
                else {
                        *t++ = *s++;
                }
        }
        *t = EOS;
        strcpy(p,q);
}



/*
 *      Get macro definition from table
 */

char *getmac(name)
char *name;
{
        int i;

        for (i = mac.lastp; i >= 0; --i) {
                if (!strcmp(name,mac.mnames[i])) {
                        return(mac.mnames[i] + 3);
                }
        }
        return(NULL);
}




/*
 *      get header or footer title
 */

gettl(p,q,limit)
char *p;
char *q;
int limit[];
{
        p = skipwd(p);
        p = skipbl(p);
        strcpy(q,p);
        limit[LEFT] = dc.inval;
        limit[RIGHT] = dc.rmval;
}



/*
 *      retrieves optional argument following nro command.
 *      returns positive integer value with sign (if any)
 *      saved in character addressed by p_argt.
 */

getval(p,p_argt)
char *p;
char *p_argt;
{
        p = skipwd(p);
        p = skipbl(p);
        *p_argt = *p;
        if((*p == '+') || (*p == '-')) ++p;
        return(ctod(p));
}


/*
 *      Evaluate macro expansion
 */

maceval(p,m)
char *p;
char m[];
{
        int i, j;
        char *argp[10];
        char c;

        *p++ = EOS;             /* replace command char with EOS */
        /*
        *       initialize argp array to substitute command
        *       string for any undefined argument
        */
        for (i=0; i<10; ++i) argp[i] = p;
        p = skipwd(p);
        *p++ = EOS;
        for (i=0; i<10; ++i) {
                p = skipbl(p);
                if (*p == '\r' || *p == '\n' || *p == EOS) break;
                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;
                }
        }
        for (i=strlen(m)-1; i>=0; --i) {
                if (i > 0 && m[i-1] == '$') {
                        if (!isdigit(m[i])) {
                                putbak(m[i]);
                        }
                        else {
                                pbstr(argp[m[i]-'0']);
                                --i;
                        }
                }
                else {
                        putbak(m[i]);
                }
        }
}


/*
 *      Push back string into input stream
 */

pbstr(p)
char p[];
{
        int i;

        for (i=strlen(p)-1; i>=0; --i) {
                putbak(p[i]);
        }
}



/*
 *      Push character back into input stream
 */

putbak(c)
char c;
{
        if (mac.ppb < &mac.pbb[0]) {
                mac.ppb = &mac.pbb[0];
                *mac.ppb = c;
        }
        else {
                if (mac.ppb >= &mac.pbb[MAXLINE-1]) {
                        puts("***nro: push back buffer overflow\n");
                        exit(-1);
                }
                *++mac.ppb = c;
        }
}




/*
 *      Put macro definition into table
 */

putmac(name,p)
char *name;
char *p;
{
        if (mac.lastp >= MXMDEF) return(ERR);
        if (mac.emb + strlen(name) + strlen(p) + 1 > &mac.mb[MACBUF]) {
                return(ERR);
        }
        ++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);
}




/*
 *      set parameter and check range
 */

set(param,val,type,defval,minval,maxval)
int *param;
int val;
char type;
int defval,minval,maxval;
{
        switch(type) {
        case '\r':
        case '\n':
                *param = defval;
                break;
        case '+':
                *param += val;
                break;
        case '-':
                *param -= val;
                break;
        default:
                *param = val;
                break;
        }
        *param = min(*param,maxval);
        *param = max(*param,minval);
}



/*
 *      skip blanks and tabs in character buffer.
 *      return number of characters skipped.
 */

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


/*
 *      skip over word and punctuation
 */

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



/*
 *      space vertically n lines
 */

space(n)
int n;
{
        brk();
        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();
}