#ifdef MSDOS /* { */ #include "grep_msd.h" #endif /* } */ char sccsID[] = "@(#) nroff.c V1.0 Copyright Vector Systems Ltd(pjc) 1987\n"; /* nroff - a unix nroff lookalike */ #include "nroff.h" /* define structures etc. */ /* page parameters */ int curpage; int newpage; int lineno; int plval; int m1val; int m2val; int m3val; int m4val; int bottom; struct hft headers[2]; struct hft footers[2]; /* global parameters */ int fill; int lsval; int spval; int inval; int rmval; int tival; int ceval; int ulval; int embval; int dlval; /* length of last diversion */ /* output */ int out_p; int outw; int outwds; int dir; char inbuf[LINESIZE]; char extrabuf[LINESIZE]; char outbuf[LINESIZE]; /* sourceing */ int nsources; /* sources */ struct source stin; /* standard input file */ struct source stout; /* standard output file */ struct source *macros; /* list of all macros */ struct source *mname; struct source *curmacro; /* current macro being expanded */ struct source *regstrs; /* registers that are known */ struct source *curdiv; /* current diversion */ struct when *whens; /* traps */ struct nregs *numregs; /* numeric registers */ char echar = '\\'; /* escape char */ char pageo = '\f'; char cmdchar1 = '.'; /* command chars, 1 causes break */ char cmdchar2 = '\''; /* command doesn't */ int save_space; char emacro[2]; /* name of end macro */ int inemacro; int adj_type; /* adjustment type */ char **ARGV ; main(argc, argv) char **argv; { #ifdef VSL /* { */ ARGV = argv ; #include "../../include/vsl.h" #endif /* } */ initfmt(); for(argc--, argv++ ; argc && **argv == '-';argc--, argv++){ /* ignore for now */ if(argv[0][1] == '\0') break; } do { if(argc > 0){ if(**argv == '-'){ /* wants stdin */ stin.m_fp = stdin; clearerr(stdin); } else stin.m_fp = fopen(*argv, "r"); } if(stin.m_fp == NULL){ fprintf(stderr, "Cannot open '%s'\n", *argv); exit(1); } argv++; doe:; /* main loop */ while(ngets(inbuf, FALSE)) if(CMD(inbuf[0])) command(inbuf); else text(inbuf); if(stin.m_fp != stdin) fclose(stin.m_fp); } while(--argc > 0); if(!inemacro && emacro[0] != '\0'){ inemacro = TRUE; doemacro(emacro); goto doe; } pageo = 0; page(TRUE); /* flush output */ dflsh(0); /* force out output */ exit(0); } initfmt() { stin.m_fp = stdin; /* set up current input */ stin.m_val = mymalloc(LINESIZE+1, TRUE); stin.m_curp = stin.m_val; curmacro = &stin; stout.m_fp = stdout; stout.m_val = mymalloc(LINESIZE+1, FALSE); stout.m_curp = stout.m_val; stout.m_eptr = stout.m_val + LINESIZE; curdiv = &stout; fill = TRUE; rmval = PAGEWIDTH; lsval = 1; newpage = 1; plval = PAGELEN; m1val = 3; m2val = 2; m3val = 2; m4val = 3; bottom = plval - m3val - m4val; setfonts(); } text(ibuf) char *ibuf; { char *cp; char *getword(); if(ibuf[0] == ' ' || ibuf[0] == '\0') leadbl(ibuf); if(ulval > 0){ ulval--; underln(ibuf); } if(embval > 0){ embval--; embold(ibuf); } if(ceval > 0){ center(ibuf); put(ibuf); ceval--; } else if(ibuf[0] == '\0') put(ibuf); else if(!fill) put(ibuf); else { int len; for(cp = ibuf; (cp = getword(cp, extrabuf, &len));) putword(extrabuf, len); } } char * getword(sp, out, len) char *sp, *out; int *len; { char *sap; if(*sp == ' '){ if(*++sp == ' '){ sap = sp; while(*sp == ' ') *out++ = *sp++; } else sap = sp; } else sap = sp; if(*sp == '\0') return( (char *)0); while(*sp != '\0' && *sp != ' '){ if(*sp == echar) /* non interupted space */ if(*(sp+1) == ' ') *out++ = *sp++; *out++ = *sp++; } *out = '\0'; *len = sp - sap + 1; return(sp); } command(buf) char *buf; { int cmd; int argtype, spval, val; struct cm *getcmd(); struct cm *cp; int tobreak = TRUE; cp = getcmd(buf); if(cp == 0) /* not a built-in */ if(mname == 0) /* macro name ?? */ return; /* nope, I don't know what it is */ if(*buf != cmdchar1) tobreak = FALSE; while(*buf && *buf != ' ') buf++; while(*buf == ' ') buf++; if(cp == 0){ /* it's a macro */ if(mname->m_last != 0){ fprintf(stderr, "Recursive macro\n"); exit(3); } mname->m_last = curmacro; curmacro = mname; curmacro->m_curp = curmacro->m_val; setmargs(curmacro, buf); return; } /* a normal command */ if(cp->sea != 0) val = getval(&buf, &argtype, cp->sea); switch(cmd = cp->sev){ case FI: if(tobreak) fbreak(); fill = TRUE; break; case NF: if(tobreak) fbreak(); fill = FALSE; break; case BR: if(tobreak) fbreak(); break; case LS: setparam(&lsval, val, argtype, 1, 1, HUGE); break; case CE: if(tobreak) fbreak(); setparam(&ceval, val, argtype, 1, 0, HUGE); break; case UL: setparam(&ulval, val, argtype, 1, 0, HUGE); break; case BO: setparam(&embval, val, argtype, 1, 0, HUGE); break; case HE: getftl(buf, &headers[EVEN]); headers[ODD] = headers[EVEN]; break; case FO: getftl(buf, &footers[EVEN]); footers[ODD] = footers[EVEN]; break; case EH: getftl(buf, &headers[EVEN]); break; case OH: getftl(buf, &headers[ODD]); break; case EF: getftl(buf, &footers[EVEN]); break; case OF: getftl(buf, &footers[ODD]); break; case SO: setsource(buf); break; case BP: page(tobreak); setparam(&curpage, val, argtype, curpage+1, -HUGE, HUGE); newpage = curpage; break; case SP: setparam(&spval, val, argtype, 1, 0, HUGE); if(tobreak) fbreak(); val = towhen(lineno); if(val == 0) val = 1; space(MIN(val, spval)); break; case IND: if(tobreak) fbreak(); setparam(&inval, val, argtype, 0, 0, rmval-1); break; case RM: setparam(&rmval, val, argtype, PAGEWIDTH, inval+tival+1, HUGE); break; case TI: if(tobreak) fbreak(); setparam(&tival, val, argtype, 0, -HUGE, rmval); break; case PL: setparam(&plval, val, argtype, PAGELEN, m1val+m2val+m3val+m4val+1, HUGE); if(curdiv->m_last == 0) /* don't set in a diversion */ bottom = plval - m3val -m4val; break; /* * macros */ case DE: case AM: setmdef(buf, cmd); break; case EN: break; case HL: spval = m1val + m2val; setparam(&spval, val, argtype, spval, 0, plval-m3val-m4val-1); m1val = spval/2 + (spval & 1); m2val = spval/2; break; case FL: spval = m3val + m4val; setparam(&spval, val, argtype, spval, 0, plval-m1val-m2val-1); m3val = spval/2; m4val = spval/2 + (spval & 1); if(curdiv->m_last == 0) /* don't set in a diversion */ bottom = plval - m3val -m4val; break; /* define a string */ case DS: case AS: /* append to a string */ defstr(buf, cmd); break; /* set a diversion */ case DI: case DA: /* append to a diversion */ setdiv(buf, cmd); break; /* when at line N, call macro XX */ case WH: setwhen(buf); break; case TL: if(tobreak) fbreak(); titil(buf); break; case NE: spval = 0; setparam(&spval, val, argtype, 1, 0, HUGE); val = towhen(lineno); if(val < spval) save_space = spval; else space(MIN(val, spval)); break; case OS: if(save_space){ val = towhen(lineno); space(MIN(save_space, val)); } save_space = 0; break; case NR: defreg(buf); break; case AF: setnform(buf); break; case IF: case EI: cond(buf, cmd); break; case EL: econd(buf); break; case EC: /* set escape char */ if(*buf == '\0') echar = '\\'; else echar = *buf; break; case EO: /* turn off the escape char */ echar = '\0'; break; case CH: chwhen(buf); break; case PN: setparam(&newpage, val, argtype, newpage, -HUGE, HUGE); break; case AD: case NA: setadtype(buf, cmd); break; case CC: if(*buf) cmdchar1 = *buf; else cmdchar1 = '.'; break; case C2: if(*buf) cmdchar2 = *buf; else cmdchar2 = '\''; break; case EM: emacro[0] = *buf; if(*buf == '\0') break; if(buf[1]) emacro[1] = buf[1]; break; } } setparam(param, val, argtype, defval, minval, maxval) int *param; { switch(argtype){ case '\0': *param = defval; break; case '+': *param += val; break; case '-': *param -= val; break; default: *param = val; break; } if(*param > maxval) *param = maxval; else if(*param < minval) *param = minval; } put(buf) char *buf; { int i; if(lineno <= 0 || lineno > bottom) puthead(); tival += inval; if(*buf != '\0' && tival > 0){ if(curfont->f_putc) for(i = 0 ; i < tival ; i++) (*curfont->f_putc)(' '); else for(i = 0 ; i < tival ; i++) PUT(' '); } tival = 0; putl(buf); i = MIN(lsval - 1, bottom - lineno); lineno++; SPRING(lineno); skip(i); if(lineno > bottom) putfoot(pageo); } /* put out the line, with respect for escape sequences */ putl(buf) char *buf; { register struct font *fp = curfont; char *p, *q; char tbuf[11]; for(; *buf ; buf++){ /* now put out the text */ if(*buf == echar){ switch(*++buf){ default: break; case '&': /* null width char */ continue; case 'e': /* current scape char */ if(echar){ if(fp->f_putc) (*fp->f_putc)(echar); else PUT(echar); } continue; case 'f': buf++; chfont(&buf); fp = curfont; continue; case 'o': p = ++buf; if(*p != '\'') break; p++; q = tbuf; while(*p && *p != '\'' && p < buf + 9) *q++ = *p++; if(*p != '\''){ if(*p == '\0') buf = p -1; continue; } buf = p; *q = '\0'; if(fp->f_putc){ for(q = tbuf ; *q ; q++){ (*fp->f_putc)(*q); if(*(q+1)) (*fp->f_putc)('\b'); } continue; } for(q = tbuf ; *q ; q++){ PUT(*q); if(*(q+1)) PUT('\b'); } continue; } } if(fp->f_putc) (*fp->f_putc)(*buf); else PUT(*buf); } if(fp->f_putc) (*fp->f_putc)('\n'); else PUTNL(); } skip(n) { register struct font *fp = curfont; if(n <= 0) return; for(; n ; n--){ lineno++; if(fp->f_putc) (*fp->f_putc)('\n'); else PUTNL(); if(SPRING(lineno)) break; } } puthead() { if(curdiv->m_last != 0){ /* don't do it in a diversion */ lineno = 1; return; } curpage = newpage; newpage++; lineno = 1; SPRING(0); if(m1val > 0){ skip(m1val -1); puttl(&headers[curpage & 1], curpage); } skip(m2val); } putfoot(c) { skip(m3val); if(m4val > 0){ puttl(&footers[curpage & 1], curpage); skip(m4val-1); } if(c) PUT(c); } space(n) { int sv; fbreak(); if(lineno > bottom) return; if(lineno <= 0) puthead(); n = MIN(n, bottom+1-lineno); skip(n); if(lineno > bottom) putfoot(pageo); } page(tobreak) { if(tobreak) fbreak(); if(lineno > 0 && lineno <= bottom){ skip(bottom+1-lineno); putfoot(pageo); } lineno = 0; } center(buf) char *buf; { int w = rmval - tival - inval; int ww = width(buf); if(ww > w) /* won't fit, left adjust */ tival -= ww-w; else tival += (w - ww)/2; if(tival < 0) tival = 0; } underln(buf) char *buf; { chft(buf, 'I'); } chft(buf, fnt) char *buf; { char name[2]; char *bp = buf; char *tp = extrabuf; static char *ep = extrabuf + LINESIZE -5; int on = FALSE; name[0] = curfont->f_name[0]; name[1] = curfont->f_name[1]; for(; *bp != '\0' && tp < ep; ){ if(*bp == ' '){ if(on){ *tp++ = echar; *tp++ = 'f'; if(name[1]){ *tp++ = '('; *tp++ = name[0]; *tp++ = name[1]; } else *tp++ = name[0]; on = FALSE; } *tp++ = *bp++; continue; } if(!on){ *tp++ = echar; *tp++ = 'f'; *tp++ = fnt; on = TRUE; } if(*bp == echar) *tp++ = *bp++; *tp++ = *bp++; } if(on && tp < ep){ *tp++ = echar; *tp++ = 'f'; if(name[1]){ *tp++ = '('; *tp++ = name[0]; *tp++ = name[1]; } else *tp++ = name[0]; } *tp = '\0'; strcpy(buf, extrabuf); } embold(buf) char *buf; { chft(buf, 'B'); } putword(wordbuf, len) char *wordbuf; { int last, llval, nextra, w; w = width(wordbuf); last = out_p + len; llval = rmval - tival - inval; if(out_p > 0 && (outw + w > llval || last >= LINESIZE)){ last = len; nextra = llval - outw + 1; if(nextra > 0 && outwds > 1 && adj_type >= 0){ spread(outbuf, out_p, nextra, outwds); out_p += nextra; } fbreak(); } strcpy(outbuf + out_p, wordbuf); out_p = last; outbuf[out_p-1] = ' '; outw += w + 1; outwds++; } spread(buf, outp, nextra, outwds) char *buf; { int i,nb, nholes; register char *ip, *jp; if(nextra <= 0 || outwds <= 1) return; dir = 1-dir; nholes = outwds -1; i = outp -1; ip = buf + i - 1; i += nextra; if(i > LINESIZE - 1) i = LINESIZE - 1; jp = buf + i - 1; for(;ip < jp && ip >= buf; ip--, jp--){ *jp = *ip; if(*ip == ' '){ /* * if have multiple space in input don't * play with them, or a no splitable space */ /* * should check, to see * if its not '\\' */ if(*(ip-1) == ' ' && *(ip-2) != echar) continue; if(*(ip-1) == echar) continue; if(dir == 0) nb = (nextra -1) / nholes + 1; else nb = nextra / nholes; nextra -= nb; nholes--; while(nb > 0){ *--jp = ' '; nb--; } } } } width(buf) char *buf; { int w; char *p; for(w = 0; *buf; buf++){ if(*buf == echar){ switch(*++buf){ default: break; case '&': /* a zero width char */ continue; case 'e': if(echar) break; continue; case 'f': /* sizeof font change */ if(*++buf == '(') buf += 2; continue; case 'o': p = ++buf; if(*p != '\'') break; w++; p++; while(*p && *p != '\'' && p < buf + 9) p++; if(*p == '\''){ buf = p; continue; } else if(*p == '\0'){ buf = p - 1; continue; } else continue; } } else if(*buf == '\b'){ w--; continue; } w++; } if(w < 0) w = 0; return(w); } fbreak() { if(out_p > 0){ outbuf[out_p - 1] = '\0'; put(outbuf); } out_p = 0; outw = 0; outwds = 0; } leadbl(buf) register char *buf; { register char *bp = buf; fbreak(); if(*bp == '\0') return; while(*bp == ' ') bp++; if(*bp != '\0'){ tival += bp - buf; while(*buf = *bp) buf++,bp++; } else *buf = '\0'; } /* * set adjustment type */ setadtype(buf, cmd) char *buf; { if(cmd == NA) adj_type = -1; else adj_type = *buf; } struct cm * getcmd(buf) char *buf; { register struct source *mp; register struct cm *cp; char c = buf[2]; if(c == ' ') c = '\0'; mname = 0; for(mp = macros ; mp ; mp = mp->m_next) if(buf[1] == mp->m_name[0] && c == mp->m_name[1]){ mname = mp; return( (struct cm *)0); } for(cp = cmd ; cp->se ; cp++) if(buf[1] == cp->se[0] && c == cp->se[1]) return(cp); return((struct cm *)0); } getval(ptr, argtype, calctype) char **ptr; int *argtype; { char *buf = *ptr; if(*buf == '\0'){ *argtype = '\0'; return(0); } if(calctype == ARG_REL){ *argtype = *buf; if(*argtype == '+' || *argtype == '-') buf++; } else *argtype = '0'; *ptr = buf; return(eval(ptr)); } /* evaluate an expression */ #define LEEQ 1 /* further operators */ #define GEEQ 2 eval(ptr) char **ptr; { char *buf = *ptr; int res = 0; int val; int cmd = 0; int minus = 0; for(;;){ if(*buf == '('){ /* recurse */ buf++; val = eval(&buf); if(*buf == ')') buf++; else break; } else if(*buf == '-'){ buf++; minus = 1; continue; } else if(*buf == '+'){ buf++; continue; } else if(!isdigit(*buf)) /* end of expression */ break; else { /* it's a digit */ val = *buf++ - '0'; while(isdigit(*buf)) val = val * 10 + (*buf++ - '0'); } if(minus){ val = -val; minus = 0; } if(cmd){ /* had a command last time around */ switch(cmd){ case '+': res += val; break; case '-': res -= val; break; case '*': res *= val; break; case '/': res /= val; break; case '%': res %= val; break; case '&': res &= val; break; case ':': res |= val; break; case '=': res = (res == val); break; case '<': res = (res < val); break; case '>': res = (res > val); break; case LEEQ: res = (res <= val); break; case GEEQ: res = (res >= val); break; } cmd = 0; } else res = val; switch(*buf){ case '+': case '-': case '*': case '/': case '%': case '&': case ':': case '=': cmd = *buf++; break; case '<': case '>': if(buf[1] == '='){ if(*buf == '<') cmd = LEEQ; else cmd = GEEQ; break; } cmd = *buf++; break; } if(!cmd) break; } *ptr = buf; return(res); }