📄 roff.c
字号:
#include "a.h"enum{ MAXREQ = 100, MAXRAW = 40, MAXESC = 60, MAXLINE = 1024, MAXIF = 20, MAXARG = 10,};typedef struct Esc Esc;typedef struct Req Req;typedef struct Raw Raw;/* escape sequence handler, like for \c */struct Esc{ Rune r; int (*f)(void); int mode;};/* raw request handler, like for .ie */struct Raw{ Rune *name; void (*f)(Rune*);};/* regular request handler, like for .ft */struct Req{ int argc; Rune *name; void (*f)(int, Rune**);};int dot = '.';int tick = '\'';int backslash = '\\';int inputmode;Req req[MAXREQ];int nreq;Raw raw[MAXRAW];int nraw;Esc esc[MAXESC];int nesc;int iftrue[MAXIF];int niftrue;int isoutput;int linepos;voidaddraw(Rune *name, void (*f)(Rune*)){ Raw *r; if(nraw >= nelem(raw)){ fprint(2, "too many raw requets\n"); return; } r = &raw[nraw++]; r->name = erunestrdup(name); r->f = f;}voiddelraw(Rune *name){ int i; for(i=0; i<nraw; i++){ if(runestrcmp(raw[i].name, name) == 0){ if(i != --nraw){ free(raw[i].name); raw[i] = raw[nraw]; } return; } }}voidrenraw(Rune *from, Rune *to){ int i; delraw(to); for(i=0; i<nraw; i++) if(runestrcmp(raw[i].name, from) == 0){ free(raw[i].name); raw[i].name = erunestrdup(to); return; }}voidaddreq(Rune *s, void (*f)(int, Rune**), int argc){ Req *r; if(nreq >= nelem(req)){ fprint(2, "too many requests\n"); return; } r = &req[nreq++]; r->name = erunestrdup(s); r->f = f; r->argc = argc;}voiddelreq(Rune *name){ int i; for(i=0; i<nreq; i++){ if(runestrcmp(req[i].name, name) == 0){ if(i != --nreq){ free(req[i].name); req[i] = req[nreq]; } return; } }}voidrenreq(Rune *from, Rune *to){ int i; delreq(to); for(i=0; i<nreq; i++) if(runestrcmp(req[i].name, from) == 0){ free(req[i].name); req[i].name = erunestrdup(to); return; }}voidaddesc(Rune r, int (*f)(void), int mode){ Esc *e; if(nesc >= nelem(esc)){ fprint(2, "too many escapes\n"); return; } e = &esc[nesc++]; e->r = r; e->f = f; e->mode = mode;}/* * Get the next logical character in the input stream. */intgetnext(void){ int i, r;next: r = getrune(); if(r < 0) return -1; if(r == Uformatted){ br(); assert(!isoutput); while((r = getrune()) >= 0 && r != Uunformatted){ if(r == Uformatted) continue; outrune(r); } goto next; } if(r == Uunformatted) goto next; if(r == backslash){ r = getrune(); if(r < 0) return -1; for(i=0; i<nesc; i++){ if(r == esc[i].r && (inputmode&esc[i].mode)==inputmode){ if(esc[i].f == e_warn) warn("ignoring %C%C", backslash, r); r = esc[i].f(); if(r <= 0) goto next; return r; } } if(inputmode&(ArgMode|CopyMode)){ ungetrune(r); r = backslash; } } return r;}voidungetnext(Rune r){ /* * really we want to undo the getrunes that led us here, * since the call after ungetnext might be getrune! */ ungetrune(r);}int_readx(Rune *p, int n, int nmode, int line){ int c, omode; Rune *e; while((c = getrune()) == ' ' || c == '\t') ; ungetrune(c); omode = inputmode; inputmode = nmode; e = p+n-1; for(c=getnext(); p<e; c=getnext()){ if(c < 0) break; if(!line && (c == ' ' || c == '\t')) break; if(c == '\n'){ if(!line) ungetnext(c); break; } *p++ = c; } inputmode = omode; *p = 0; if(c < 0) return -1; return 0;}/* * Get the next argument from the current line. */Rune*copyarg(void){ static Rune buf[MaxLine]; int c; Rune *r; if(_readx(buf, sizeof buf, ArgMode, 0) < 0) return nil; r = runestrstr(buf, L("\\\"")); if(r){ *r = 0; while((c = getrune()) >= 0 && c != '\n') ; ungetrune('\n'); } r = erunestrdup(buf); return r;}/* * Read the current line in given mode. Newline not kept. * Uses different buffer from copyarg! */Rune*readline(int m){ static Rune buf[MaxLine]; Rune *r; if(_readx(buf, sizeof buf, m, 1) < 0) return nil; r = erunestrdup(buf); return r;}/* * Given the argument line (already read in copy+arg mode), * parse into arguments. Note that \" has been left in place * during copy+arg mode parsing, so comments still need to be stripped. */intparseargs(Rune *p, Rune **argv){ int argc; Rune *w; for(argc=0; argc<MAXARG; argc++){ while(*p == ' ' || *p == '\t') p++; if(*p == 0) break; argv[argc] = p; if(*p == '"'){ /* quoted argument */ if(*(p+1) == '"'){ /* empty argument */ *p = 0; p += 2; }else{ /* parse quoted string */ w = p++; for(; *p; p++){ if(*p == '"' && *(p+1) == '"') *w++ = '"'; else if(*p == '"'){ p++; break; }else *w++ = *p; } *w = 0; } }else{ /* unquoted argument - need to watch out for \" comment */ for(; *p; p++){ if(*p == ' ' || *p == '\t'){ *p++ = 0; break; } if(*p == '\\' && *(p+1) == '"'){ *p = 0; if(p != argv[argc]) argc++; return argc; } } } } return argc;}/* * Process a dot line. The dot has been read. */voiddotline(int dot){ int argc, i; Rune *a, *argv[1+MAXARG]; /* * Read request/macro name */ a = copyarg(); if(a == nil || a[0] == 0){ free(a); getrune(); /* \n */ return; } argv[0] = a; /* * Check for .if, .ie, and others with special parsing. */ for(i=0; i<nraw; i++){ if(runestrcmp(raw[i].name, a) == 0){ raw[i].f(raw[i].name); free(a); return; } } /* * Read rest of line in copy mode, invoke regular request. */ a = readline(ArgMode); if(a == nil){ free(argv[0]); return; } argc = 1+parseargs(a, argv+1); for(i=0; i<nreq; i++){ if(runestrcmp(req[i].name, argv[0]) == 0){ if(req[i].argc != -1){ if(argc < 1+req[i].argc){ warn("not enough arguments for %C%S", dot, req[i].name); free(argv[0]); free(a); return; } if(argc > 1+req[i].argc) warn("too many arguments for %C%S", dot, req[i].name); } req[i].f(argc, argv); free(argv[0]); free(a); return; } } /* * Invoke user-defined macros. */ runmacro(dot, argc, argv); free(argv[0]); free(a);}/* * newlines are magical in various ways. */int bol;voidnewline(void){ int n; if(bol) sp(eval(L("1v"))); bol = 1; if((n=getnr(L(".ce"))) > 0){ nr(L(".ce"), n-1); br(); } if(getnr(L(".fi")) == 0) br(); outrune('\n');}voidstartoutput(void){ char *align; double ps, vs, lm, rm, ti; Rune buf[200]; if(isoutput) return; isoutput = 1; if(getnr(L(".paragraph")) == 0) return; nr(L(".ns"), 0); isoutput = 1; ps = getnr(L(".s")); if(ps <= 1) ps = 10; ps /= 72.0; USED(ps); vs = getnr(L(".v"))*getnr(L(".ls")) * 1.0/UPI; vs /= (10.0/72.0); /* ps */ if(vs == 0) vs = 1.2; lm = (getnr(L(".o"))+getnr(L(".i"))) * 1.0/UPI; ti = getnr(L(".ti")) * 1.0/UPI; nr(L(".ti"), 0); rm = 8.0 - getnr(L(".l"))*1.0/UPI - getnr(L(".o"))*1.0/UPI; if(rm < 0) rm = 0; switch(getnr(L(".j"))){ default: case 0: align = "left"; break; case 1: align = "justify"; break; case 3: align = "center"; break; case 5: align = "right"; break; } if(getnr(L(".ce"))) align = "center"; if(!getnr(L(".margin"))) runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; text-indent: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n", vs, ti, align); else runesnprint(buf, nelem(buf), "<p style=\"line-height: %.1fem; margin-left: %.2fin; text-indent: %.2fin; margin-right: %.2fin; margin-top: 0; margin-bottom: 0; text-align: %s;\">\n", vs, lm, ti, rm, align); outhtml(buf);}voidbr(void){ if(!isoutput) return; isoutput = 0; nr(L(".dv"), 0); dv(0); hideihtml(); if(getnr(L(".paragraph"))) outhtml(L("</p>"));}voidr_margin(int argc, Rune **argv){ USED(argc); nr(L(".margin"), eval(argv[1]));}int inrequest;voidruninput(void){ int c; bol = 1; for(;;){ c = getnext(); if(c < 0) break; if((c == dot || c == tick) && bol){ inrequest = 1; dotline(c); bol = 1; inrequest = 0; }else if(c == '\n'){ newline(); itrap(); linepos = 0; }else{ outtrap(); startoutput(); showihtml(); if(c == '\t'){ /* XXX do better */ outrune(' '); while(++linepos%4) outrune(' '); }else{ outrune(c); linepos++; } bol = 0; } }}voidrun(void){ t1init(); t2init(); t3init(); t4init(); t5init(); t6init(); t7init(); t8init(); /* t9init(); t9.c */ t10init(); t11init(); /* t12init(); t12.c */ t13init(); t14init(); t15init(); t16init(); t17init(); t18init(); t19init(); t20init(); htmlinit(); hideihtml(); addreq(L("margin"), r_margin, 1); nr(L(".margin"), 1); nr(L(".paragraph"), 1); runinput(); while(popinput()) ; dot = '.'; if(verbose) fprint(2, "eof\n"); runmacro1(L("eof")); closehtml();}voidout(Rune *s){ if(s == nil) return; for(; *s; s++) outrune(*s);}void (*outcb)(Rune);voidinroman(Rune r){ int f; f = getnr(L(".f")); nr(L(".f"), 1); runmacro1(L("font")); outrune(r); nr(L(".f"), f); runmacro1(L("font"));}voidBrune(Rune r){ if(r == '&') Bprint(&bout, "&"); else if(r == '<') Bprint(&bout, "<"); else if(r == '>') Bprint(&bout, ">"); else if(r < Runeself || utf8) Bprint(&bout, "%C", r); else Bprint(&bout, "%S", rune2html(r));}voidouthtml(Rune *s){ Rune r; for(; *s; s++){ switch(r = *s){ case '<': r = Ult; break; case '>': r = Ugt; break; case '&': r = Uamp; break; case ' ': r = Uspace; break; } outrune(r); }}voidoutrune(Rune r){ switch(r){ case ' ': if(getnr(L(".fi")) == 0) r = Unbsp; break; case Uformatted: case Uunformatted: abort(); } if(outcb){ if(r == ' ') r = Uspace; outcb(r); return; } /* writing to bout */ switch(r){ case Uempty: return; case Upl: inroman('+'); return; case Ueq: inroman('='); return; case Umi: inroman(0x2212); return; case Utick: r = '\''; break; case Ubtick: r = '`'; break; case Uminus: r = '-'; break; case '\'': Bprint(&bout, "’"); return; case '`': Bprint(&bout, "‘"); return; case Uamp: Bputrune(&bout, '&'); return; case Ult: Bputrune(&bout, '<'); return; case Ugt: Bputrune(&bout, '>'); return; case Uspace: Bputrune(&bout, ' '); return; case 0x2032: /* * In Firefox, at least, the prime is not * a superscript by default. */ Bprint(&bout, "<sup>"); Brune(r); Bprint(&bout, "</sup>"); return; } Brune(r);}voidr_nop(int argc, Rune **argv){ USED(argc); USED(argv);}voidr_warn(int argc, Rune **argv){ USED(argc); warn("ignoring %C%S", dot, argv[0]);}inte_warn(void){ /* dispatch loop prints a warning for us */ return 0;}inte_nop(void){ return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -