/* * command.c - command input parser/processor for nroff text 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" /*------------------------------*/ /* comand */ /*------------------------------*/ comand (p) register char *p; { /* * main command processor */ register int i; register int ct; register int val; register int indx; int newval; int spval; char argtyp; char name[MAXLINE]; char macexp[MXMLEN]; int tmp; char *pfs; char fs[20]; /* * get command code */ ct = comtyp (p, macexp); /* * error? */ if (ct == UNKNOWN) { fprintf (err_stream, "***%s: unrecognized command %s\n", myname, p); return; } /* * ignore comments */ if (ct == COMMENT) return; /* * do escape expansion on command line args */ expesc (p, name); /* * get value of command */ val = getval (p, &argtyp); /* * do the command */ switch (ct) { /* set (¶m, val, type, defval, minval, maxval) */ case FC: /* * field delim/pad chars * * .fc [delim] [pad] */ fprintf (err_stream, "***%s: .fc not available\n", myname); break; case TR: /* * translate * * .tr ab... */ fprintf (err_stream, "***%s: .tr not available\n", myname); break; case AD: /* * adjust * * .ad [mode] */ p = skipwd (p); p = skipbl (p); switch (*p) { case 'l': dc.adjval = ADJ_LEFT; dc.juval = YES; break; case 'r': dc.adjval = ADJ_RIGHT; dc.juval = YES; break; case 'c': dc.adjval = ADJ_CENTER; dc.juval = YES; break; case 'b': case 'n': dc.adjval = ADJ_BOTH; dc.juval = YES; break; default: break; } break; case AF: /* * assign format to number reg * * .af R {1,a,A,i,I,0...1} */ p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { fprintf (err_stream, "***%s: invalid or missing number register name\n", myname); } else { /* * number register format is 1,a,A,i,I,0...1 * default is 1. for 0001 format, store num dig * or'ed with 0x80, up to 8 digits. */ indx = tolower (*p) - 'a'; p = skipwd (p); p = skipbl (p); if (*p == '1') dc.nrfmt[indx] = '1'; else if (*p == 'a') dc.nrfmt[indx] = 'a'; else if (*p == 'A') dc.nrfmt[indx] = 'A'; else if (*p == 'i') dc.nrfmt[indx] = 'i'; else if (*p == 'I') dc.nrfmt[indx] = 'I'; else if (*p == '0') { for (i = 0; isdigit (p[i]); i++) ; dc.nrfmt[indx] = (char) (i); if (dc.nrfmt[indx] <= 0) dc.nrfmt[indx] = '1'; else if (dc.nrfmt[indx] > 8) { dc.nrfmt[indx] = 8; dc.nrfmt[indx] |= 0x80; } else dc.nrfmt[indx] |= 0x80; } else dc.nrfmt[indx] = '1'; } break; case BD: /* * embolden font (IGNORED) * * .bd [S] F N */ break; case BO: /* * bold face * * .bo [N] */ set (&dc.boval, val, argtyp, 1, 0, HUGE); dc.cuval = dc.ulval = 0; break; case BP: /* * begin page * * .bp [+/-N] */ if (pg.lineno > 0) space (HUGE); set (&pg.curpag, val, argtyp, pg.curpag + 1, -HUGE, HUGE); pg.newpag = pg.curpag; set_ireg ("%", pg.newpag, 0); break; case BR: /* * break (page) * * .br */ robrk (); break; case BS: /* * backspc in output * * .bs [N] */ set (&dc.bsflg, val, argtyp, 1, 0, 1); break; case C2: /* * nobreak char * * .c2 [c='] */ if (argtyp == '\r' || argtyp == '\n') dc.nobrchr = '\''; else dc.nobrchr = argtyp; break; case CC: /* * command character * * .cc [c=.] */ if (argtyp == '\r' || argtyp == '\n') dc.cmdchr = '.'; else dc.cmdchr = argtyp; break; case CE: /* * center * * .ce [N] */ robrk (); set (&dc.ceval, val, argtyp, 1, 0, HUGE); break; case CS: /* * constant space char (IGNORED) * * .cs F N M */ break; case CU: /* * continuous underline * * .cu [N] */ set (&dc.cuval, val, argtyp, 1, 0, HUGE); dc.ulval = dc.boval = 0; break; case DE: /* * define macro * * .de name [end] */ ignoring = FALSE; defmac (p, sofile[dc.flevel]); break; case DS: /* * define string * * .ds name string */ defstr (p); break; case EC: /* * escape char * * .ec [c=\] */ if (argtyp == '\r' || argtyp == '\n') dc.escchr = '\\'; else dc.escchr = argtyp; dc.escon = YES; break; case EF: /* * even footer * * .ef "a" "b" "c" */ gettl (p, pg.efoot, &pg.eflim[0]); break; case EH: /* * even header * * .eh "a" "b" "c" */ gettl (p, pg.ehead, &pg.ehlim[0]); break; case EN: /* * end macro def (should not get one here...) * * .en or .. */ fprintf (err_stream, "***%s: missing .de command\n", myname); break; case EO: /* * escape off * * .eo */ dc.escon = NO; break; case FI: /* * fill * * .fi */ robrk (); dc.fill = YES; break; case FL: /* * flush NOW * * .fl */ fflush (out_stream); break; case FO: /* * footer * * .fo "a" "b" "c" */ gettl (p, pg.efoot, &pg.eflim[0]); gettl (p, pg.ofoot, &pg.oflim[0]); break; case FT: /* * font change * * .ft {R,I,B,S,P} * * the way it's implemented here, it causes a break * rather than be environmental... */ p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { fprintf (err_stream, "***%s: invalid or missing font name\n", myname); } else { pfs = &fs[0]; fontchange (*p, pfs); robrk (); fflush (out_stream); fprintf (out_stream, "%s", pfs); fflush (out_stream); } break; case TL: case HE: /* * header (both are currently identical. .he is -me) * * .tl "a" "b" "c" * .he "a" "b" "c" */ gettl (p, pg.ehead, &pg.ehlim[0]); gettl (p, pg.ohead, &pg.ohlim[0]); break; case IG: /* * ignore input lines * * .ig name */ ignoring = TRUE; defmac (p, sofile[dc.flevel]); break; case IN: /* * indenting * * .in [+/-N] */ set (&dc.inval, val, argtyp, 0, 0, dc.rmval - 1); set_ireg (".i", dc.inval, 0); dc.tival = dc.inval; break; case JU: /* * justify * * .ju */ dc.juval = YES; break; case LL: /* * line length * * .ll [+/-N] * .rm [+/-N] */ set (&dc.rmval, val, argtyp, PAGEWIDTH, dc.tival + 1, HUGE); set (&dc.llval, val, argtyp, PAGEWIDTH, dc.tival + 1, HUGE); set_ireg (".l", dc.llval, 0); break; case LS: /* * line spacing * * .ls [+/-N=+1] */ set (&dc.lsval, val, argtyp, 1, 1, HUGE); set_ireg (".v", dc.lsval, 0); break; case LT: /* * title length * * .lt N */ set (&dc.ltval, val, argtyp, PAGEWIDTH, 0, HUGE); pg.ehlim[RIGHT] = dc.ltval; pg.ohlim[RIGHT] = dc.ltval; break; case M1: /* * topmost margin * * .m1 N */ set (&pg.m1val, val, argtyp, 2, 0, HUGE); break; case M2: /* * second top margin * * .m2 N */ set (&pg.m2val, val, argtyp, 2, 0, HUGE); break; case M3: /* * 1st bottom margin * * .m3 N */ set (&pg.m3val, val, argtyp, 2, 0, HUGE); pg.bottom = pg.plval - pg.m4val - pg.m3val; break; case M4: /* * bottom-most marg * * .m4 N */ set (&pg.m4val, val, argtyp, 2, 0, HUGE); pg.bottom = pg.plval - pg.m4val - pg.m3val; break; case MACRO: /* * macro expansion * * (internal) */ maceval (p, macexp); break; case NA: /* * no adjust * * .na */ dc.adjval = ADJ_OFF; dc.juval = NO; break; case NE: /* * need n lines * * .ne N */ robrk (); if ((pg.bottom - pg.lineno + 1) < (val * dc.lsval)) { space (HUGE); } break; case NF: /* * no fill * * .nf */ robrk (); dc.fill = NO; break; case NJ: /* * no justify * * .nj */ dc.juval = NO; break; case NR: /* * set number reg * * .nr R +/-N M */ p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { fprintf (err_stream, "***%s: invalid or missing number register name\n", myname); } else { /* * indx is the register, R, and val is the final * value (default = 0). getval does skipwd,skipbl */ indx = tolower (*p) - 'a'; val = getval (p, &argtyp); set (&dc.nr[indx], val, argtyp, 0, -INFINITE, INFINITE); /* * now get autoincrement M, if any (default = 1). * getval does skipwd,skipbl */ p = skipwd (p); p = skipbl (p); val = getval (p, &argtyp); set (&dc.nrauto[indx], val, '1', 1, -INFINITE, INFINITE); } break; case OF: /* * odd footer * * .of "a" "b" "c" */ gettl (p, pg.ofoot, &pg.oflim[0]); break; case OH: /* * odd header * * .oh "a" "b" "c" */ gettl (p, pg.ohead, &pg.ohlim[0]); break; case PC: /* * page number char * * .pc [c=NULL] */ if (argtyp == '\r' || argtyp == '\n') dc.pgchr = EOS; else dc.pgchr = argtyp; break; case PL: /* * page length * * .pl N */ set (&pg.plval, val, argtyp, PAGELEN, pg.m1val + pg.m2val + pg.m3val + pg.m4val + 1, HUGE); set_ireg (".p", pg.plval, 0); pg.bottom = pg.plval - pg.m3val - pg.m4val; break; case PM: /* * print macro names and sizes * * .pm [t] */ if (argtyp == '\r' || argtyp == '\n') printmac (0); else if (argtyp == 't') printmac (1); else if (argtyp == 'T') printmac (2); else printmac (0); break; case PN: /* * page number * * .pn N */ tmp = pg.curpag; set (&pg.curpag, val - 1, argtyp, tmp, -HUGE, HUGE); pg.newpag = pg.curpag + 1; set_ireg ("%", pg.newpag, 0); break; case PO: /* * page offset * * .po N */ set (&pg.offset, val, argtyp, 0, 0, HUGE); set_ireg (".o", pg.offset, 0); break; case PS: /* * point size (IGNORED) * * .ps +/-N */ break; case RR: /* * unset number reg * * .rr R */ p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { fprintf (err_stream, "***%s: invalid or missing number register name\n", myname); } else { indx = tolower (*p) - 'a'; val = 0; set (&dc.nr[indx], val, argtyp, 0, -HUGE, HUGE); } break; case SO: /* * source file * * .so name */ p = skipwd (p); p = skipbl (p); if (getwrd (p, name) == 0) break; if (dc.flevel + 1 >= Nfiles) { fprintf (err_stream, "***%s: .so commands nested too deeply\n", myname); err_exit (-1); } if ((sofile[dc.flevel + 1] = fopen (name, "r")) == NULL_FPTR) { fprintf (err_stream, "***%s: unable to open %s\n", myname, name); err_exit (-1); } dc.flevel += 1; break; case SP: /* * space * * .sp [N=1] */ set (&spval, val, argtyp, 1, 0, HUGE); space (spval); break; case SS: /* * space char size (IGNORED) * * .ss N */ break; case TI: /* * temporary indent * * .ti [+/-N] */ robrk (); set (&dc.tival, val, argtyp, 0, 0, dc.rmval); break; case UL: /* * underline * * .ul [N] */ set (&dc.ulval, val, argtyp, 0, 1, HUGE); dc.cuval = dc.boval = 0; break; } } /*------------------------------*/ /* comtyp */ /*------------------------------*/ comtyp (p, m) register char *p; char *m; { /* * decodes nro command and returns its associated value. * ptr "p" is incremented (and returned) */ register char c1; register char c2; char *s; char macnam[MNLEN]; /* * skip past dot and any whitespace */ p++; while (*p && (*p == ' ' || *p == '\t')) p++; if (*p == '\0') return (COMMENT); /* * 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. */ getwrd (p, macnam); macnam[2] = EOS; if ((s = getmac (macnam)) != NULL_CPTR) { strcpy (m, s); return (MACRO); } c1 = *p++; c2 = *p; if (c1 == '\\' && c2 == '\"') return (COMMENT); if (c1 == 'a' && c2 == 'f') return (AF); if (c1 == 'a' && c2 == 'd') return (AD); 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 == 'd' && c2 == 's') return (DS); if (c1 == 'e' && c2 == 'f') return (EF); if (c1 == 'e' && c2 == 'c') return (EC); if (c1 == 'e' && c2 == 'h') return (EH); if (c1 == 'e' && c2 == 'n') return (EN); if (c1 == '.') return (EN); if (c1 == 'e' && c2 == 'o') return (EO); if (c1 == 'f' && c2 == 'i') return (FI); if (c1 == 'f' && c2 == 'l') return (FL); if (c1 == 'f' && c2 == 'o') return (FO); if (c1 == 'f' && c2 == 't') return (FT); 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 == 'l') return (LL); 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 == 'a') return (NA); 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 == 'm') return (PM); if (c1 == 'p' && c2 == 'o') return (PO); if (c1 == 'r' && c2 == 'm') return (RM); if (c1 == 'r' && c2 == 'r') return (RR); if (c1 == 's' && c2 == 'o') return (SO); if (c1 == 's' && c2 == 'p') return (SP); if (c1 == 't' && c2 == 'i') return (TI); if (c1 == 't' && c2 == 'l') return (TL); if (c1 == 'u' && c2 == 'l') return (UL); if (c1 == 'p' && c2 == 'n') return (PN); if (c1 == 'r' && c2 == 'r') return (RR); if (c1 == 'c' && c2 == '2') return (C2); if (c1 == 't' && c2 == 'r') return (TR); if (c1 == 'l' && c2 == 't') return (LT); if (c1 == 'f' && c2 == 'c') return (FC); return (UNKNOWN); } /*------------------------------*/ /* gettl */ /*------------------------------*/ gettl (p, q, limit) register char *p; register char *q; int *limit; { /* * get header or footer title */ /* * skip forward a word... */ p = skipwd (p); p = skipbl (p); /* * copy and set limits */ strcpy (q, p); limit[LEFT] = dc.inval; limit[RIGHT] = dc.rmval; } /*------------------------------*/ /* getval */ /*------------------------------*/ getval (p, p_argt) register char *p; register char *p_argt; { /* * retrieves optional argument following command. * returns positive integer value with sign (if any) * saved in character addressed by p_argt. */ p = skipwd (p); p = skipbl (p); *p_argt = *p; if ((*p == '+') || (*p == '-')) ++p; return (ctod (p)); } /*------------------------------*/ /* set */ /*------------------------------*/ set (param, val, type, defval, minval, maxval) register int *param; register int val; register char type; register int defval; register int minval; register int maxval; { /* * set parameter and check range. this is for basically all commands * which take interger args * * no param (i.e. \r or \n) means reset default * + means param += val (increment) * - means param -= val (decrement) * anything else makes an assignment within the defined numerical limits * * examples: * * .nr a 14 set register 'a' to 14 * .nr a +1 increment register 'a' by 1 * .nr a reset register 'a' to default value (0) */ 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); } /*------------------------------*/ /* set_ireg */ /*------------------------------*/ set_ireg (name, val, opt) register char *name; register int val; register int opt; /* 0=internal, 1=user set */ { /* * set internal register "name" to val. ret 0 if ok, else -1 if reg not * found or 1 if read only */ register int nreg; nreg = findreg (name); if (nreg < 0) return (-1); if ((rg[nreg].rflag & RF_WRITE) || (opt == 0)) { rg[nreg].rval = val; return (0); } return (1); }