/* * 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 * - Heavily modified by Devin Reade to avoid memory trashing bugs. * * $Id: command.c,v 1.2 1997/03/20 06:40:50 gdr Exp $ */ #ifdef __ORCAC__ segment "command___"; #pragma noroot #pragma optimize 79 #endif #include #include #include #ifdef __GNO__ #include #else #include "unix/err.h" #endif #ifdef sparc #include "unix/sunos.h" #endif #include "nroff.h" #include "escape.h" #include "macros.h" #include "io.h" #define iscond(x) ((x)=='>'||(x)=='<'||(x)=='=') #define isoper(x) ((x)=='+'||(x)=='-'||(x)=='*'||(x)=='/'||(x)=='%') static int comtyp (char *p, char *m); static void read_if (void); static void gettl (char *p, char *q, int *limit); static int getval (char *p, char *p_argtyp); static int getnumeric (char *p); static int do_oper (int first, int oper, int second); /* * comand * main command processor */ void comand (char *p) { register int i; register int ct; register int val; register int indx; int spval; int not_cond; char argtyp; char name[MAXLINE]; char macexp[MXMLEN]; int tmp; char *pfs; char fs[20]; char c; char *ps1; char *ps2; DEBUGGING(("comand: enter, p=|%s|",p)); /* * get command code */ ct = comtyp (p, macexp); /* * error? */ if (ct == UNKNOWN) { warnx("unrecognized command %s", p); return; /* gdr: added in this return */ } /* * ignore comments */ if (ct == COMMENT) { return; } /* * do escape expansion on command line args from p into name */ expesc (p, name, MAXLINE); /* * get value of command */ #if 0 val = getval (p, &argtyp); #endif /* * do the command */ switch (ct) { /* set (¶m, val, type, defval, minval, maxval) */ case FC: /* * field delim/pad chars * * .fc [delim] [pad] */ warnx(".fc not available"); break; case TR: /* * translate * * .tr ab... */ warnx(".tr not available"); break; case AD: /* * adjust * * .ad [mode] */ val = getval (p, &argtyp); 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} */ val = getval (p, &argtyp); p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { warn("invalid or missing number register name"); } 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] */ val = getval (p, &argtyp); set (&dc.boval, val, argtyp, 1, 0, HUGE); dc.cuval = dc.ulval = 0; break; case BP: /* * begin page * * .bp [+/-N] */ val = getval (p, &argtyp); if (pg.lineno > 0) nroffSpace (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] */ val = getval (p, &argtyp); set (&dc.bsflg, val, argtyp, 1, 0, 1); break; case C2: /* * nobreak char * * .c2 [c='] */ val = getval (p, &argtyp); if (argtyp == '\r' || argtyp == '\n') { dc.nobrchr = '\''; } else { dc.nobrchr = argtyp; } break; case CC: /* * command character * * .cc [c=.] */ val = getval (p, &argtyp); if (argtyp == '\r' || argtyp == '\n') { dc.cmdchr = '.'; } else { dc.cmdchr = argtyp; } break; case CE: /* * center * * .ce [N] */ val = getval (p, &argtyp); 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] */ val = getval (p, &argtyp); set (&dc.cuval, val, argtyp, 1, 0, HUGE); dc.ulval = dc.boval = 0; break; case DE: /* * define macro * * .de name [end] */ val = getval (p, &argtyp); ignoring = FALSE; defmac (p, sofile[dc.flevel]); break; case DS: /* * define string * * .ds name string */ val = getval (p, &argtyp); defstr (p); break; case EC: /* * escape char * * .ec [c=\] */ val = getval (p, &argtyp); if (argtyp == '\r' || argtyp == '\n') { dc.escchr = '\\'; } else { dc.escchr = argtyp; } dc.escon = YES; break; case EF: /* * even footer * * .ef "a" "b" "c" */ val = getval (p, &argtyp); gettl (p, pg.efoot, &pg.eflim[0]); break; case EH: /* * even header * * .eh "a" "b" "c" */ val = getval (p, &argtyp); gettl (p, pg.ehead, &pg.ehlim[0]); break; case EN: /* * end macro def (should not get one here...) * * .en or .. */ warnx("missing .de command"); break; case EO: /* * escape off * * .eo */ dc.escon = NO; break; case EX: /* * exit * * .ex */ if (sofile[0] != stdin) { fclose (sofile[0]); } for (i = 1; i <= Nfiles; i++) { if (sofile[i] != NULL_FPTR) { fclose (sofile[i]); } } err_exit(0); 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" */ val = getval (p, &argtyp); 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... */ val = getval (p, &argtyp); p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { warnx("invalid or missing font name"); } 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" */ val = getval (p, &argtyp); gettl (p, pg.ehead, &pg.ehlim[0]); gettl (p, pg.ohead, &pg.ohlim[0]); break; case IE: /* * if of if/else conditional * * .ie condition anything * .el anything * * .ie condition \{\ * ... * ... \} * .el \{\ * ... * ... \} */ warnx(".ie not available"); break; case EL: /* * else of if/else conditional * * .ie condition anything * .el anything * * .ie condition \{\ * ... * ... \} * .el \{\ * ... * ... \} */ warnx(".el not available"); break; case IF: /* * conditional * * .if c command [c=n(roff),t(roff),e(ven),o(dd)] * .if !c command * .if 's1's2' command [s1 == s2] * .if !'s1's2' command [s1 != s2] * .if N command [N > 0] * .if !N command [N <= 0] * * .if cond \{\ * command * ... \} */ p = skipwd (p); p = skipbl (p); not_cond = 0; if (*p == '!') { p++; not_cond = 1; } if (islower (*p) && isspace (*(p+1))) { /* * single char: n=nroff,t=troff,e=evenpage,o=oddpage */ c = *p; switch (c) { case 'n': /* if nroff... (always T) */ p = skipwd (p); p = skipbl (p); DEBUGGING(("comand: p=|%s|", p)); if (*p != EOS && not_cond == 0) { if (*p == '\\' && *(p+1) == '{') { read_if (); } else { if (*p == dc.cmdchr) { comand (p); } else { if (*p == '\"') { p++; } if (*p == ' ') { robrk (); } text (p); } } } break; case 't': /* if troff... (always F) */ p = skipwd (p); p = skipbl (p); DEBUGGING(("comand: p=|%s|", p)); if (*p != EOS && not_cond != 0) { if (*p == '\\' && *(p+1) == '{') { read_if (); } else { if (*p == dc.cmdchr) { comand (p); } else { if (*p == '\"') { p++; } if (*p == ' ') { robrk (); } text (p); } } } break; case 'e': /* if even page... */ p = skipwd (p); p = skipbl (p); DEBUGGING(("comand: p=|%s|", p)); if (((pg.curpag % 2) == 0 && not_cond == 0) || ((pg.curpag % 2) != 0 && not_cond != 0)) { /* could be newpag, too */ if (*p == '\\' && *(p+1) == '{') { read_if (); } else { if (*p == dc.cmdchr) { comand (p); } else { if (*p == '\"') { p++; } if (*p == ' ') { robrk (); } text (p); } } } break; case 'o': /* if odd page... */ p = skipwd (p); p = skipbl (p); DEBUGGING(("comand: p=|%s|", p)); if (((pg.curpag % 2) == 1 && not_cond == 0) || ((pg.curpag % 2) != 1 && not_cond != 0)) { if (*p == '\\' && *(p+1) == '{') { read_if (); } else { if (*p == dc.cmdchr) { comand (p); } else { if (*p == '\"') { p++; } if (*p == ' ') { robrk (); } text (p); } } } break; } } else if (*p == '\'' || *p == '/' || *p == '\"') { /* * compare strings. we need to interpolate here */ c = *p; ps1 = ++p; while (*p != EOS && *p != c) { p++; } *p = EOS; ps2 = ++p; while (*p != EOS && *p != c) { p++; } *p = EOS; DEBUGGING(("comand: strcmp (ps1=|%s|,ps2=|%s|)", ps1, ps2)); if ((!strcmp (ps1, ps2) && not_cond == 0) || ( strcmp (ps1, ps2) && not_cond != 0)) { p++; p = skipbl (p); if (*p == '\\' && *(p+1) == '{') { read_if (); } else { if (*p == dc.cmdchr) { comand (p); } else { if (*p == '\"') { p++; } if (*p == ' ') { robrk (); } text (p); } } } } else { /* * number */ DEBUGGING(("comand: p=|%s|", p)); val = getnumeric (p); if ((val > 0 && not_cond == 0) || (val <= 0 && not_cond != 0)) { p = skipwd (p); p = skipbl (p); if (*p == '\\' && *(p+1) == '{') { read_if (); } else { if (*p == dc.cmdchr) { comand (p); } else { if (*p == '\"') { p++; } if (*p == ' ') { robrk (); } text (p); } } } } break; case IG: /* * ignore input lines * * .ig name */ val = getval (p, &argtyp); ignoring = TRUE; defmac (p, sofile[dc.flevel]); break; case IN: /* * indenting * * .in [+/-N] */ val = getval (p, &argtyp); 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 LG: /* * ligature (IGNORED) * * .lg [N] */ break; case LL: /* * line length * * .ll [+/-N] * .rm [+/-N] */ val = getval (p, &argtyp); 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] */ val = getval (p, &argtyp); set (&dc.lsval, val, argtyp, 1, 1, HUGE); set_ireg (".v", dc.lsval, 0); break; case LT: /* * title length * * .lt N */ val = getval (p, &argtyp); 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 */ val = getval (p, &argtyp); set (&pg.m1val, val, argtyp, 2, 0, HUGE); break; case M2: /* * second top margin * * .m2 N */ val = getval (p, &argtyp); set (&pg.m2val, val, argtyp, 2, 0, HUGE); break; case M3: /* * 1st bottom margin * * .m3 N */ val = getval (p, &argtyp); set (&pg.m3val, val, argtyp, 2, 0, HUGE); pg.bottom = pg.plval - pg.m4val - pg.m3val; break; case M4: /* * bottom-most marg * * .m4 N */ val = getval (p, &argtyp); 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 MC: /* * margin character (change bars) * * .mc [c [N]] * * right margin only, default 0.2i */ val = getval (p, &argtyp); if (argtyp == '\r' || argtyp == '\n') { mc_ing = FALSE; /* turn off... */ } else { mc_ing = TRUE; /* turn on... */ mc_space = 2; /* force these for now... */ mc_char = argtyp; /* single char only!!! */ p = skipwd (p); p = skipbl (p); val = getval (p, &argtyp); set (&mc_space, val, argtyp, 2, 0, dc.llval); } break; case NA: /* * no adjust * * .na */ dc.adjval = ADJ_OFF; dc.juval = NO; break; case NE: /* * need n lines * * .ne N */ val = getval (p, &argtyp); robrk (); if ((pg.bottom - pg.lineno + 1) < (val * dc.lsval)) { nroffSpace (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 */ val = getval (p, &argtyp); p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { warn("invalid or missing number register name"); } 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" */ val = getval (p, &argtyp); gettl (p, pg.ofoot, &pg.oflim[0]); break; case OH: /* * odd header * * .oh "a" "b" "c" */ val = getval (p, &argtyp); gettl (p, pg.ohead, &pg.ohlim[0]); break; case PC: /* * page number char * * .pc [c=NULL] */ val = getval (p, &argtyp); if (argtyp == '\r' || argtyp == '\n') { dc.pgchr = EOS; } else { dc.pgchr = argtyp; } break; case PL: /* * page length * * .pl N */ val = getval (p, &argtyp); 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] */ val = getval (p, &argtyp); 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 */ val = getval (p, &argtyp); 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 */ val = getval (p, &argtyp); 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 */ val = getval (p, &argtyp); p = skipwd (p); p = skipbl (p); if (!isalpha (*p)) { warnx("invalid or missing number register name"); } else { indx = tolower (*p) - 'a'; val = 0; set (&dc.nr[indx], val, argtyp, 0, -HUGE, HUGE); } break; case SO: /* * source file * * .so name */ val = getval (p, &argtyp); p = skipwd (p); p = skipbl (p); if (getwrd (p, name) == 0) { break; } if (dc.flevel + 1 >= Nfiles) { errx(-1, ".so commands nested too deeply"); } if ((sofile[dc.flevel + 1] = fopen (name, "r")) == NULL_FPTR) { errx(-1, "unable to open %s\n", name); } dc.flevel += 1; break; case SP: /* * space * * .sp [N=1] */ val = getval (p, &argtyp); set (&spval, val, argtyp, 1, 0, HUGE); nroffSpace (spval); break; case SS: /* * space char size (IGNORED) * * .ss N */ break; case TI: /* * temporary indent * * .ti [+/-N] */ val = getval (p, &argtyp); robrk (); set (&dc.tival, val, argtyp, 0, 0, dc.rmval); break; case UL: /* * underline * * .ul [N] */ val = getval (p, &argtyp); set (&dc.ulval, val, argtyp, 0, 1, HUGE); dc.cuval = dc.boval = 0; break; } } /* * comtyp * decodes nro command and returns its associated value. * ptr "p" is incremented (and returned) */ static int comtyp (char *p, char *m) { register char c1; register char c2; char *s; char macnam[MNLEN]; int result; /* * quick check: if null, ignore */ if (*p == EOS) { return (COMMENT); } /* * 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 * (i.e. the text of the macro). 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; result = UNKNOWN; switch (c1) { case '\\': switch (c2) { case '"': result = COMMENT; break; } break; case 'a': switch (c2) { case 'd': result = AD; break; case 'f': result = AF; break; } break; case 'b': switch (c2) { case 'd': result = BD; break; case 'o': result = BO; break; case 'p': result = BP; break; case 'r': result = BR; break; case 's': result = BS; break; } break; case 'c': switch (c2) { case '2': result = C2; break; case 'c': result = CC; break; case 'e': result = CE; break; case 's': result = CS; break; case 'u': result = CU; break; } break; case 'd': switch (c2) { case 'e': result = DE; break; case 's': result = DS; break; } break; case 'e': switch (c2) { case 'c': result = EC; break; case 'f': result = EF; break; case 'h': result = EH; break; #if 0 case 'l': result = EL; break; #endif case 'n': result = EN; break; case 'o': result = EO; break; case 'x': result = EX; break; } break; case 'f': switch (c2) { case 'c': result = FC; break; case 'i': result = FI; break; case 'l': result = FL; break; case 'o': result = FO; break; case 't': result = FT; break; } break; case 'h': switch (c2) { case 'e': result = HE; break; } break; case 'i': switch (c2) { case 'f': result = IF; break; #if 0 case 'e': result = IE; break; #endif case 'g': result = IG; break; case 'n': result = IN; break; } break; case 'j': switch (c2) { case 'u': result = JU; break; } break; case 'l': switch (c2) { case 'g': result = LG; break; case 'l': result = LL; break; case 's': result = LS; break; case 't': result = LT; break; } break; case 'm': switch (c2) { case '1': result = M1; break; case '2': result = M2; break; case '3': result = M3; break; case '4': result = M4; break; case 'c': result = MC; break; } break; case 'n': switch (c2) { case 'a': result = NA; break; case 'e': result = NE; break; case 'f': result = NF; break; case 'j': result = NJ; break; case 'r': result = NR; break; } break; case 'o': switch (c2) { case 'f': result = OF; break; case 'h': result = OH; break; } break; case 'p': switch (c2) { case 'c': result = PC; break; case 'l': result = PL; break; case 'm': result = PM; break; case 'n': result = PN; break; case 'o': result = PO; break; case 's': result = PS; break; } break; case 'r': switch (c2) { case 'm': result = RM; break; case 'r': result = RR; break; } break; case 's': switch (c2) { case 'o': result = SO; break; case 'p': result = SP; break; case 's': result = SS; break; } break; case 't': switch (c2) { case 'i': result = TI; break; case 'l': result = TL; break; case 'r': result = TR; break; } break; case 'u': switch (c2) { case 'l': result = UL; break; } break; case '.': result = EN; break; } return result; } /* * gettl * get header or footer title */ static void gettl (char *p, char *q, int *limit) { /* * 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 * retrieves optional argument following command. * returns positive integer value with sign (if any) * saved in character addressed by p_argt. */ static int getval (char *p, char *p_argtyp) { p = skipwd (p); p = skipbl (p); *p_argtyp = *p; if ((*p == '+') || (*p == '-')) { ++p; } return (ctod (p)); } #define N_ADD 0 #define N_SUB 1 #define N_MUL 2 #define N_DIV 3 #define N_MOD 4 #define N_LT 5 #define N_GT 6 #define N_LE 7 #define N_GE 8 #define N_EQ 9 #define N_AND 10 #define N_OR 11 /* * getnumeric * * retrieves numeric argument. will parse for number registers, * constants, operations, and logical comparisons. no imbeded spaces! * start at p (don't skip) */ static int getnumeric (char *p) { char name[10]; int val; int thisval; int autoinc; char buf[256]; char *pbuf; int next_op; int nreg; const char *percent = "%"; char *scratch; val = 0; next_op = N_ADD; while (*p != EOS && !isspace (*p)) { if (!strncmp (p, "\\n", 2)) { DEBUGGING(("getnumeric: found number reg...")); /* * number register */ autoinc = 0; p += 2; if (*p == '+') { autoinc = 1; p++; } else if (*p == '-') { autoinc = -1; p++; } if (isalpha (*p)) { /* * \nx form. find reg (a-z) */ nreg = tolower (*p) - 'a'; p++; /* * 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]; } val = do_oper (val, next_op, dc.nr[nreg]); } else if (*p == '%') { /* * \n% form. find index into reg struct */ FINDREG(percent, nreg, scratch); p++; if (nreg < 0) { errx(-1, "no register match"); } /* * 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; } val = do_oper (val, next_op, rg[nreg].rval); } else if (*p == '(') { /* * \n(xx form. find index into reg struct */ p++; name[0] = *p++; name[1] = *p++; if (name[1] == ' ' || name[1] == '\t' || name[1] == '\n' || name[1] == '\r') { name[1] = '\0'; } name[2] = '\0'; FINDREG(name, nreg, scratch); if (nreg < 0) { errx(-1, "no register match"); } /* * 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; } } val = do_oper (val, next_op, rg[nreg].rval); } } else if (isdigit (*p)) { pbuf = buf; while (1) { if ((*p == EOS || isspace (*p)) || (*p == '\\') || (iscond (*p)) || (isoper (*p))) { break; } *pbuf++ = *p++; } *pbuf = EOS; DEBUGGING(("getnumeric: buf:|%s| next_op:%d val:%d", buf, next_op, val)); thisval = ctod (buf); val = do_oper (val, next_op, thisval); DEBUGGING(("getnumeric: thisval:%d val:%d", thisval, val)); } /* * p should now be at the next thing, either a * space, a null, or an operator */ if (*p == EOS || isspace (*p)) { break; } switch (*p) { case '+': next_op = N_ADD; p++; break; case '-': next_op = N_SUB; p++; break; case '*': next_op = N_MUL; p++; break; case '/': next_op = N_DIV; p++; break; case '%': next_op = N_MOD; p++; break; case '&': next_op = N_AND; p++; break; case ':': next_op = N_OR; p++; break; case '<': p++; if (*p == '=') { p++; next_op = N_LE; } else { next_op = N_LT; } break; case '>': p++; if (*p == '=') { p++; next_op = N_GE; } else { next_op = N_GT; } break; case '=': p++; if (*p == '=') { p++; } next_op = N_EQ; break; } } return (val); } /* * do_oper */ static int do_oper (int first, int oper, int second) { int val; DEBUGGING(("do_oper: first:%d op:%d second:%d", first, oper, second)); switch (oper) { case N_ADD: val = first + second; break; case N_SUB: val = first - second; break; case N_MUL: val = first * second; break; case N_DIV: val = first / second; break; case N_MOD: val = first % second; break; case N_LT: val = ((first < second) ? 1 : 0); break; case N_GT: val = ((first > second) ? 1 : 0); break; case N_LE: val = ((first <= second) ? 1 : 0); break; case N_GE: val = ((first >= second) ? 1 : 0); break; case N_EQ: val = ((first == second) ? 1 : 0); break; case N_AND: val = ((first && second) ? 1 : 0); break; case N_OR: val = ((first || second) ? 1 : 0); break; } return (val); } /* * set * 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) */ void set (int *param, int val, char type, int defval, int minval, int 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); } /* * set_ireg * set internal register "name" to val. ret 0 if ok, else -1 if reg not * found or 1 if read only * * 0=internal, 1=user set */ int set_ireg (const char *name, int val, int opt) { register int nreg; char *p; FINDREG(name, nreg, p); if (nreg < 0) { return (-1); } if ((rg[nreg].rflag & RF_WRITE) || (opt == 0)) { rg[nreg].rval = val; return (0); } return (1); } /* * read_if * read input while in if statement. stop when a line starts with \} */ static void read_if (void) { char ibuf[MAXLINE]; char *pp; while (getlin (ibuf, sofile[dc.flevel]) != EOF) { DEBUGGING(("read_if: ibuf=|%s|", ibuf)); pp = skipbl (ibuf); if (*pp == '\\' && *(pp+1) == '}') { return; } /* * if line is a command or text */ if (ibuf[0] == dc.cmdchr) { comand (ibuf); } else { /* * this is a text line. first see if * first char is space. if it is, break * line. */ if (ibuf[0] == ' ') { robrk (); } text (ibuf); } pp = ibuf; while (*pp != EOS) { if (*pp == '\\' && *(pp+1) == '}') { return; } } } }