ref: 75f5fdbb0df902d8d7e16a4489a04d92493eacae
dir: /sys/src/cmd/ms2html.c/
#include <u.h> #include <libc.h> #include <ctype.h> #include <bio.h> enum { SSIZE = 10, Maxnh= 8, /* highest NH level */ HH= 4, /* heading level used for SH and NH */ Maxmstack= 10, /* deepest macro/string nesting */ Narg= 20, /* max args to a macro */ Maxsstack= 5, /* deepest nesting of .so's */ Nline= 1024, Maxget= 10, Maxif = 20, Maxfsp = 100, /* list types */ Lordered = 1, Lunordered, Ldef, Lother, }; char *delim = "$$"; char *basename; char *title; int eqnmode; int quiet; float indent; /* from .in */ Biobuf bout; int isup; int isdown; int debug; int nh[Maxnh]; int ifwastrue[Maxif]; int list, listnum, example; int hangingau, hangingdt, hanginghead, hangingcenter; int indirective, paragraph, sol, titleseen, ignore_nl, weBref; void dohangingcenter(void); typedef struct Goobie Goobie; typedef struct Goobieif Goobieif; struct Goobie { char *name; void (*f)(int, char**); }; typedef void F(int, char**); typedef void Fif(char*, char*); struct Goobieif { char *name; Fif *f; }; /* if, ie */ Fif g_as, g_ds, g_el, g_ie, g_if; Goobieif gtabif[] = { { "as", g_as }, { "ds", g_ds }, { "if", g_if }, { "ie", g_ie }, { "el", g_el }, { nil, nil }, }; /* pseudo ops */ F g_notyet, g_ignore, g_hrule, g_startgif; /* ms macros */ F g_AU, g_B, g_BI, g_CW, g_I, g_IP, g_LP, g_PP, g_SH, g_NH; F g_P1, g_P2, g_TL, g_R, g_AB, g_AE, g_EQ, g_TS, g_TE, g_FS, g_FE; F g_PY, g_IH, g_MH, g_HO, g_BX, g_QS, g_QE, g_RS, g_RE; /* pictures macro */ F g_BP; /* real troff */ F g_br, g_ft, g_sp, g_de, g_lf, g_so, g_rm, g_in; F g_nr, g_ig, g_RT, g_BS, g_BE, g_LB, g_ta; /* macros to include ML in output */ F g__H, g__T; Goobie gtab[] = { { "_T", g__T, }, { "_H", g__H, }, { "1C", g_ignore, }, { "2C", g_ignore, }, { "AB", g_AB, }, { "AE", g_AE, }, { "AI", g_ignore, }, { "AU", g_AU, }, { "B", g_B, }, { "B1", g_hrule, }, { "B2", g_hrule, }, { "BI", g_BI, }, { "BP", g_BP, }, { "BT", g_ignore, }, { "BX", g_BX, }, { "CW", g_CW, }, { "CT", g_ignore, }, { "DA", g_ignore, }, { "DE", g_P2, }, { "DS", g_P1, }, { "EG", g_ignore, }, { "EN", g_ignore, }, { "EQ", g_startgif, }, { "FE", g_FE, }, { "FP", g_ignore, }, { "FS", g_FS, }, { "HO", g_HO, }, { "I", g_I, }, { "IH", g_IH, }, { "IM", g_ignore, }, { "IP", g_IP, }, { "KE", g_ignore, }, { "KF", g_ignore, }, { "KS", g_ignore, }, { "LG", g_ignore, }, { "LP", g_LP, }, { "LT", g_ignore, }, { "MF", g_ignore, }, { "MH", g_MH, }, { "MR", g_ignore, }, { "ND", g_ignore, }, { "NH", g_NH, }, { "NL", g_ignore, }, { "P1", g_P1, }, { "P2", g_P2, }, { "PE", g_ignore, }, { "PF", g_ignore, }, { "PP", g_PP, }, { "PS", g_startgif, }, { "PY", g_PY, }, { "QE", g_QE, }, { "QP", g_QS, }, { "QS", g_QS, }, { "R", g_R, }, { "RE", g_RE, }, { "RP", g_ignore, }, { "RS", g_RS, }, { "SG", g_ignore, }, { "SH", g_SH, }, { "SM", g_ignore, }, { "TA", g_ignore, }, { "TE", g_ignore, }, { "TH", g_TL, }, { "TL", g_TL, }, { "TM", g_ignore, }, { "TR", g_ignore, }, { "TS", g_startgif, }, { "UL", g_I, }, { "UX", g_ignore, }, { "WH", g_ignore, }, { "RT", g_RT, }, { "br", g_br, }, { "ti", g_br, }, { "nf", g_P1, }, { "fi", g_P2, }, { "ft", g_ft, }, { "sp", g_sp, }, { "rm", g_rm, }, { "de", g_de, }, { "am", g_de, }, { "lf", g_lf, }, { "so", g_so, }, { "ps", g_ignore }, { "vs", g_ignore }, { "nr", g_nr }, { "in", g_in }, { "ne", g_ignore }, { "ig", g_ig }, { "BS", g_BS }, { "BE", g_BE }, { "LB", g_LB }, { nil, nil }, }; typedef struct Entity Entity; struct Entity { char *name; int value; }; Entity entity[] = { { "&#SPACE;", L' ', }, { "&#RS;", L'\n', }, { "&#RE;", L'\r', }, { """, L'"', }, { "Æ", L'Æ', }, { "Á", L'Á', }, { "Â", L'Â', }, { "À", L'À', }, { "Å", L'Å', }, { "Ã", L'Ã', }, { "Ä", L'Ä', }, { "Ç", L'Ç', }, { "Ð", L'Ð', }, { "É", L'É', }, { "Ê", L'Ê', }, { "È", L'È', }, { "Ë", L'Ë', }, { "Í", L'Í', }, { "Î", L'Î', }, { "Ì", L'Ì', }, { "Ï", L'Ï', }, { "Ñ", L'Ñ', }, { "Ó", L'Ó', }, { "Ô", L'Ô', }, { "Ò", L'Ò', }, { "Ø", L'Ø', }, { "Õ", L'Õ', }, { "Ö", L'Ö', }, { "Þ", L'Þ', }, { "Ú", L'Ú', }, { "Û", L'Û', }, { "Ù", L'Ù', }, { "Ü", L'Ü', }, { "Ý", L'Ý', }, { "á", L'á', }, { "â", L'â', }, { "æ", L'æ', }, { "à", L'à', }, { "&", L'&', }, { "å", L'å', }, { "ã", L'ã', }, { "ä", L'ä', }, { "ç", L'ç', }, { "é", L'é', }, { "ê", L'ê', }, { "è", L'è', }, { "ð", L'ð', }, { "ë", L'ë', }, { ">", L'>', }, { "í", L'í', }, { "î", L'î', }, { "ì", L'ì', }, { "ï", L'ï', }, { "<", L'<', }, { "ñ", L'ñ', }, { "ó", L'ó', }, { "ô", L'ô', }, { "ò", L'ò', }, { "ø", L'ø', }, { "õ", L'õ', }, { "ö", L'ö', }, { "ß", L'ß', }, { "þ", L'þ', }, { "ú", L'ú', }, { "û", L'û', }, { "ù", L'ù', }, { "ü", L'ü', }, { "ý", L'ý', }, { "ÿ", L'ÿ', }, { "¡", L'¡', }, { "¢", L'¢', }, { "£", L'£', }, { "¤", L'¤', }, { "¥", L'¥', }, { "¦", L'¦', }, { "§", L'§', }, { "¨", L'¨', }, { "©", L'©', }, { "ª", L'ª', }, { "«", L'«', }, { "¬", L'¬', }, { "­", L'', }, { "®", L'®', }, { "¯", L'¯', }, { "°", L'°', }, { "±", L'±', }, { "²", L'²', }, { "³", L'³', }, { "´", L'´', }, { "µ", L'µ', }, { "¶", L'¶', }, { "·", L'·', }, { "¸", L'¸', }, { "¹", L'¹', }, { "º", L'º', }, { "»", L'»', }, { "¼", L'¼', }, { "½", L'½', }, { "¾", L'¾', }, { "¿", L'¿', }, { "*", L'•', }, { "¤", L'□', }, { "º", L'◊', }, { "(tm)", L'™', }, {"Α", L'Α',}, {"Β", L'Β',}, {"Γ", L'Γ',}, {"Δ", L'Δ',}, {"Ε", L'Ε',}, {"Ζ", L'Ζ',}, {"Η", L'Η',}, {"Θ", L'Θ',}, {"Ι", L'Ι',}, {"Κ", L'Κ',}, {"Λ", L'Λ',}, {"Μ", L'Μ',}, {"Ν", L'Ν',}, {"Ξ", L'Ξ',}, {"Ο", L'Ο',}, {"Π", L'Π',}, {"Ρ", L'Ρ',}, {"΢", L'',}, {"Σ", L'Σ',}, {"Τ", L'Τ',}, {"Υ", L'Υ',}, {"Φ", L'Φ',}, {"Χ", L'Χ',}, {"Ψ", L'Ψ',}, {"Ω", L'Ω',}, {"α", L'α',}, {"β", L'β',}, {"γ", L'γ',}, {"δ", L'δ',}, {"ε", L'ε',}, {"ζ", L'ζ',}, {"η", L'η',}, {"θ", L'θ',}, {"ι", L'ι',}, {"κ", L'κ',}, {"λ", L'λ',}, {"μ", L'μ',}, {"ν", L'ν',}, {"ξ", L'ξ',}, {"ο", L'ο',}, {"π", L'π',}, {"ρ", L'ρ',}, {"ς", L'ς',}, {"σ", L'σ',}, {"τ", L'τ',}, {"υ", L'υ',}, {"φ", L'φ',}, {"χ", L'χ',}, {"ψ", L'ψ',}, {"ω", L'ω',}, { "<-", L'←', }, { "^", L'↑', }, { "->", L'→', }, { "v", L'↓', }, { "!=", L'≠', }, { "<=", L'≤', }, { "...", L'⋯', }, {"∈", L'∈', }, {"–", L'–', }, {"—", L'—', }, { "CYRILLIC XYZZY", L'й', }, { "CYRILLIC XYZZY", L'ъ', }, { "CYRILLIC Y", L'ь', }, { "CYRILLIC YA", L'я', }, { "CYRILLIC YA", L'ё', }, { "¿", L'ℱ', }, { nil, 0 }, }; typedef struct Troffspec Troffspec; struct Troffspec { char *name; char *value; }; Troffspec tspec[] = { { "A*", "Å", }, { "o\"", "ö", }, { "ff", "ff", }, { "fi", "fi", }, { "fl", "fl", }, { "Fi", "ffi", }, { "ru", "_", }, { "em", "­", }, { "14", "¼", }, { "12", "½", }, { "co", "©", }, { "de", "°", }, { "dg", "¡", }, { "fm", "´", }, { "rg", "®", }, { "bu", "*", }, { "sq", "¤", }, { "hy", "-", }, { "pl", "+", }, { "mi", "-", }, { "mu", "×", }, { "di", "÷", }, { "eq", "=", }, { "==", "==", }, { ">=", ">=", }, { "<=", "<=", }, { "!=", "!=", }, { "+-", "±", }, { "no", "¬", }, { "sl", "/", }, { "ap", "&", }, { "~=", "~=", }, { "pt", "oc", }, { "gr", "GRAD", }, { "->", "->", }, { "<-", "<-", }, { "ua", "^", }, { "da", "v", }, { "is", "Integral", }, { "pd", "DIV", }, { "if", "oo", }, { "sr", "-/", }, { "sb", "(~", }, { "sp", "~)", }, { "cu", "U", }, { "ca", "(^)", }, { "ib", "(=", }, { "ip", "=)", }, { "mo", "C", }, { "es", "Ø", }, { "aa", "´", }, { "ga", "`", }, { "ci", "O", }, { "L1", "DEATHSTAR", }, { "sc", "§", }, { "dd", "++", }, { "lh", "<=", }, { "rh", "=>", }, { "lt", "(", }, { "rt", ")", }, { "lc", "|", }, { "rc", "|", }, { "lb", "(", }, { "rb", ")", }, { "lf", "|", }, { "rf", "|", }, { "lk", "|", }, { "rk", "|", }, { "bv", "|", }, { "ts", "s", }, { "br", "|", }, { "or", "|", }, { "ul", "_", }, { "rn", " ", }, { "**", "*", }, { "tm", "™", }, { nil, nil, }, }; typedef struct Font Font; struct Font { char *start; char *end; }; Font bfont = { "<B>", "</B>" }; Font ifont = { "<I>", "</I>" }; Font bifont = { "<B><I>", "</I></B>" }; Font cwfont = { "<TT>", "</TT>" }; Font *fstack[Maxfsp]; int fsp = -1; typedef struct String String; struct String { String *next; char *name; char *val; }; String *numregs, *strings; char *strstack[Maxmstack]; char *mustfree[Maxmstack]; int strsp = -1; int elsetop = -1; typedef struct Mstack Mstack; struct Mstack { char *ptr; char *argv[Narg+1]; }; String *macros; Mstack mstack[Maxmstack]; int msp = -1; typedef struct Srcstack Srcstack; struct Srcstack { char filename[256]; int fd; int lno; int rlno; Biobuf in; }; Srcstack sstack[Maxsstack]; Srcstack *ssp = &sstack[-1]; char token[128]; void closel(void); void closefont(void); void* emalloc(uint n) { void *p; p = mallocz(n, 1); if(p == nil){ fprint(2, "ms2html: malloc failed: %r\n"); exits("malloc"); } return p; } /* define a string variable */ void dsnr(char *name, char *val, String **l) { String *s; for(s = *l; s != nil; s = *l){ if(strcmp(s->name, name) == 0) break; l = &s->next; } if(s == nil){ s = emalloc(sizeof(String)); *l = s; s->name = strdup(name); } else free(s->val); s->val = strdup(val); } void ds(char *name, char *val) { dsnr(name, val, &strings); } /* look up a defined string */ char* getds(char *name) { String *s; for(s = strings; s != nil; s = s->next) if(strcmp(name, s->name) == 0) break; if(s != nil) return s->val; return ""; } char * getnr(char *name) { String *s; for(s = numregs; s != nil; s = s->next) if(strcmp(name, s->name) == 0) break; if(s != nil) return s->val; return "0"; } void pushstr(char *p) { if(p == nil) return; if(strsp >= Maxmstack - 1) return; strstack[++strsp] = p; } /* lookup a defined macro */ char* getmacro(char *name) { String *s; for(s = macros; s != nil; s = s->next) if(strcmp(name, s->name) == 0) return s->val; return nil; } enum { Dstring, Macro, Input, }; int lastsrc; void pushsrc(char *name) { Dir *d; int fd; if(ssp == &sstack[Maxsstack-1]){ fprint(2, "ms2html: .so's too deep\n"); return; } d = nil; if(name == nil){ d = dirfstat(0); if(d == nil){ fprint(2, "ms2html: can't stat %s: %r\n", name); return; } name = d->name; fd = 0; } else { fd = open(name, OREAD); if(fd < 0){ fprint(2, "ms2html: can't open %s: %r\n", name); return; } } ssp++; ssp->fd = fd; Binit(&ssp->in, fd, OREAD); snprint(ssp->filename, sizeof(ssp->filename), "%s", name); ssp->lno = ssp->rlno = 1; free(d); } /* get next logical byte. from stdin or a defined string */ int getrune(void) { int i; Rune r; int c; Mstack *m; while(strsp >= 0){ i = chartorune(&r, strstack[strsp]); if(r != 0){ strstack[strsp] += i; lastsrc = Dstring; return r; } if (mustfree[strsp]) { free(mustfree[strsp]); mustfree[strsp] = nil; } strsp--; } while(msp >= 0){ m = &mstack[msp]; i = chartorune(&r, m->ptr); if(r != 0){ m->ptr += i; lastsrc = Macro; return r; } for(i = 0; m->argv[i] != nil; i++) free(m->argv[i]); msp--; } lastsrc = Input; for(;;) { if(ssp < sstack) return -1; c = Bgetrune(&ssp->in); if(c >= 0){ r = c; break; } close(ssp->fd); ssp--; } return r; } void ungetrune(void) { switch(lastsrc){ case Dstring: if(strsp >= 0) strstack[strsp]--; break; case Macro: if(msp >= 0) mstack[msp].ptr--; break; case Input: if(ssp >= sstack) Bungetrune(&ssp->in); break; } } int vert; char* changefont(Font *f) { token[0] = 0; if(fsp == Maxfsp) return token; if(fsp >= 0 && fstack[fsp]) strcpy(token, fstack[fsp]->end); if(f != nil) strcat(token, f->start); fstack[++fsp] = f; return token; } char* changebackfont(void) { token[0] = 0; if(fsp >= 0){ if(fstack[fsp]) strcpy(token, fstack[fsp]->end); fsp--; } if(fsp >= 0 && fstack[fsp]) strcat(token, fstack[fsp]->start); return token; } char* changesize(int amount) { static int curamount; static char buf[200]; int i; buf[0] = 0; if (curamount >= 0) for (i = 0; i < curamount; i++) strcat(buf, "</big>"); else for (i = 0; i < -curamount; i++) strcat(buf, "</small>"); curamount = 0; if (amount >= 0) for (i = 0; i < amount; i++) strcat(buf, "<big>"); else for (i = 0; i < -amount; i++) strcat(buf, "<small>"); curamount = amount; return buf; } /* get next logical character. expand it with escapes */ char* getnext(void) { int r; Entity *e; Troffspec *t; Rune R; char str[4]; static char buf[8]; r = getrune(); if(r < 0) return nil; if(r > 128 || r == '<' || r == '>'){ for(e = entity; e->name; e++) if(e->value == r) return e->name; sprint(buf, "&#%d;", r); return buf; } if (r == delim[eqnmode]){ if (eqnmode == 0){ eqnmode = 1; return changefont(&ifont); } eqnmode = 0; return changebackfont(); } switch(r){ case '\\': r = getrune(); if(r < 0) return nil; switch(r){ case ' ': return " "; /* chars to ignore */ case '&': case '|': case '%': return ""; /* small space in troff, nothing in nroff */ case '^': return getnext(); /* ignore arg */ case 'k': getrune(); return getnext(); /* comment */ case '"': while(getrune() != '\n') ; return "\n"; /* ignore line */ case '!': while(getrune() != '\n') ; ungetrune(); return getnext(); /* defined strings */ case '*': r = getrune(); if(r == '('){ str[0] = getrune(); str[1] = getrune(); str[2] = 0; } else { str[0] = r; str[1] = 0; } pushstr(getds(str)); return getnext(); /* macro args */ case '$': r = getrune(); if(r < '1' || r > '9'){ token[0] = '\\'; token[1] = '$'; token[2] = r; token[3] = 0; return token; } r -= '0'; if(msp >= 0) pushstr(mstack[msp].argv[r]); return getnext(); /* special chars */ case '(': token[0] = getrune(); token[1] = getrune(); token[2] = 0; for(t = tspec; t->name; t++) if(strcmp(token, t->name) == 0) return t->value; return "¿"; /* ignore immediately following newline */ case 'c': r = getrune(); if (r == '\n') { sol = ignore_nl = 1; if (indirective) break; } else ungetrune(); return getnext(); /* escape backslash */ case 'e': return "\\"; /* font change */ case 'f': r = getrune(); switch(r){ case '(': str[0] = getrune(); str[1] = getrune(); str[2] = 0; token[0] = 0; if(strcmp("BI", str) == 0) return changefont(&bifont); else if(strcmp("CW", str) == 0) return changefont(&cwfont); else return changefont(nil); case '3': case 'B': return changefont(&bfont); case '2': case 'I': return changefont(&ifont); case '4': return changefont(&bifont); case '5': return changefont(&cwfont); case 'P': return changebackfont(); case 'R': default: return changefont(nil); } /* number register */ case 'n': r = getrune(); if (r == '(') /*)*/ { r = getrune(); if (r < 0) return nil; str[0] = r; r = getrune(); if (r < 0) return nil; str[1] = r; str[2] = 0; } else { str[0] = r; str[1] = 0; } pushstr(getnr(str)); return getnext(); /* font size */ case 's': r = getrune(); switch(r){ case '0': return changesize(0); case '-': r = getrune(); if (!isdigit(r)) return getnext(); return changesize(-(r - '0')); case '+': r = getrune(); if (!isdigit(r)) return getnext(); return changesize(r - '0'); } return getnext(); /* vertical movement */ case 'v': r = getrune(); if(r != '\''){ ungetrune(); return getnext(); } r = getrune(); if(r != '-') vert--; else vert++; while(r != '\'' && r != '\n') r = getrune(); if(r != '\'') ungetrune(); if(vert > 0) return "^"; return getnext(); /* horizontal line */ case 'l': r = getrune(); if(r != '\''){ ungetrune(); return "<HR>"; } while(getrune() != '\'') ; return "<HR>"; /* character height and slant */ case 'S': case 'H': r = getrune(); if(r != '\''){ ungetrune(); return "<HR>"; } while(getrune() != '\'') ; return getnext(); /* digit-width space */ case '0': return " "; /*for .if, .ie, .el */ case '{': return "\\{"; /*}*/ case '}': return ""; /* up and down */ case 'u': if (isdown) { isdown = 0; return "</sub>"; } isup = 1; return "<sup>"; case 'd': if (isup) { isup = 0; return "</sup>"; } isdown = 1; return "<sub>"; } break; case '&': if(msp >= 0 || strsp >= 0) return "&"; return "&"; case '<': if(msp >= 0 || strsp >= 0) return "<"; return "<"; case '>': if(msp >= 0 || strsp >= 0) return ">"; return ">"; } if (r < Runeself) { token[0] = r; token[1] = 0; } else { R = r; token[runetochar(token,&R)] = 0; } return token; } /* if arg0 is set, read up to (and expand) to the next whitespace, else to the end of line */ char* copyline(char *p, char *e, int arg0) { int c; Rune r; char *p1; while((c = getrune()) == ' ' || c == '\t') ; for(indirective = 1; p < e; c = getrune()) { if (c < 0) goto done; switch(c) { case '\\': break; case '\n': if (arg0) ungetrune(); goto done; case ' ': case '\t': if (arg0) goto done; default: r = c; p += runetochar(p,&r); continue; } ungetrune(); p1 = getnext(); if (p1 == nil) goto done; if (*p1 == '\n') { if (arg0) ungetrune(); break; } while((*p = *p1++) && p < e) p++; } done: indirective = 0; *p++ = 0; return p; } char* copyarg(char *p, char *e, int *nullarg) { int c, quoted, last; Rune r; *nullarg = 0; quoted = 0; do{ c = getrune(); } while(c == ' ' || c == '\t'); if(c == '"'){ quoted = 1; *nullarg = 1; c = getrune(); } if(c == '\n') goto done; last = 0; for(; p < e; c = getrune()) { if (c < 0) break; switch(c) { case '\n': ungetrune(); goto done; case '\\': r = c; p += runetochar(p,&r); if(last == '\\') r = 0; break; case ' ': case '\t': if(!quoted && last != '\\') goto done; r = c; p += runetochar(p,&r); break; case '"': if(quoted && last != '\\') goto done; r = c; p += runetochar(p,&r); break; default: r = c; p += runetochar(p,&r); break; } last = r; } done: *p++ = 0; return p; } int parseargs(char *p, char *e, char **argv) { int argc; char *np; int nullarg; indirective = 1; *p++ = 0; for(argc = 1; argc < Narg; argc++){ np = copyarg(p, e, &nullarg); if(nullarg==0 && np == p+1) break; argv[argc] = p; p = np; } argv[argc] = nil; indirective = 0; return argc; } void dodirective(void) { char *p, *e; Goobie *g; Goobieif *gif; char line[Nline], *line1; int i, argc; char *argv[Narg]; Mstack *m; /* read line, translate special bytes */ e = line + sizeof(line) - UTFmax - 1; line1 = copyline(line, e, 1); if (!line[0]) return; argv[0] = line; /* first look through user defined macros */ p = getmacro(argv[0]); if(p != nil){ if(msp == Maxmstack-1){ fprint(2, "ms2html: macro stack overflow\n"); return; } argc = parseargs(line1, e, argv); m = &mstack[++msp]; m->ptr = p; memset(m->argv, 0, sizeof(m->argv)); for(i = 0; i < argc; i++) m->argv[i] = strdup(argv[i]); return; } /* check for .if or .ie */ for(gif = gtabif; gif->name; gif++) if(strcmp(gif->name, argv[0]) == 0){ (*gif->f)(line1, e); return; } argc = parseargs(line1, e, argv); /* try standard ms macros */ for(g = gtab; g->name; g++) if(strcmp(g->name, argv[0]) == 0){ (*g->f)(argc, argv); return; } if(debug) fprint(2, "stdin %d(%s:%d): unknown directive %s\n", ssp->lno, ssp->filename, ssp->rlno, line); } void printarg(char *a) { char *e, *p; e = a + strlen(a); pushstr(a); while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){ p = getnext(); if(p == nil) return; Bprint(&bout, "%s", p); } } void printargs(int argc, char **argv) { argc--; argv++; while(--argc > 0){ printarg(*argv++); Bprint(&bout, " "); } if(argc == 0) printarg(*argv); } void dohangingdt(void) { switch(hangingdt){ case 3: hangingdt--; break; case 2: Bprint(&bout, "<dd>"); hangingdt = 0; break; } } void dohangingau(void) { if(hangingau == 0) return; Bprint(&bout, "</I></DL>\n"); hangingau = 0; } void dohanginghead(void) { if(hanginghead == 0) return; Bprint(&bout, "</H%d>\n", hanginghead); hanginghead = 0; } /* * convert a man page to html and output */ void doconvert(void) { char c, *p; pushsrc(nil); sol = 1; Bprint(&bout, "<html>\n"); Bflush(&bout); for(;;){ p = getnext(); if(p == nil) break; c = *p; if(c == '.' && sol){ dodirective(); dohangingdt(); ssp->lno++; ssp->rlno++; sol = 1; } else if(c == '\n'){ if (ignore_nl) ignore_nl = 0; else { if(hangingau) Bprint(&bout, "<br>\n"); else Bprint(&bout, "%s", p); dohangingdt(); } ssp->lno++; ssp->rlno++; sol = 1; } else{ Bprint(&bout, "%s", p); ignore_nl = sol = 0; } } dohanginghead(); dohangingdt(); closel(); if(fsp >= 0 && fstack[fsp]) Bprint(&bout, "%s", fstack[fsp]->end); Bprint(&bout, "<br> <br>\n"); Bprint(&bout, "</body></html>\n"); } static void usage(void) { sysfatal("usage: ms2html [-q] [-b basename] [-d '$$'] [-t title]"); } void main(int argc, char **argv) { quiet = 1; ARGBEGIN { case 't': title = EARGF(usage()); break; case 'b': basename = EARGF(usage()); break; case 'q': quiet = 0; break; case 'd': delim = EARGF(usage()); break; case '?': default: usage(); } ARGEND; Binit(&bout, 1, OWRITE); ds("R", "®"); doconvert(); exits(nil); } void g_notyet(int, char **argv) { fprint(2, "ms2html: .%s not yet supported\n", argv[0]); } void g_ignore(int, char **argv) { if(quiet) return; fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]); } void g_PP(int, char**) { dohanginghead(); closel(); closefont(); Bprint(&bout, "<P>\n"); paragraph = 1; } void g_LP(int, char**) { dohanginghead(); closel(); closefont(); Bprint(&bout, "<br> <br>\n"); } /* close a list */ void closel(void) { g_P2(1, nil); dohangingau(); if(paragraph){ Bprint(&bout, "</P>\n"); paragraph = 0; } switch(list){ case Lordered: Bprint(&bout, "</ol>\n"); break; case Lunordered: Bprint(&bout, "</ul>\n"); break; case Lother: case Ldef: Bprint(&bout, "</dl>\n"); break; } list = 0; } void g_IP(int argc, char **argv) { switch(list){ default: closel(); if(argc > 1){ if(strcmp(argv[1], "1") == 0){ list = Lordered; listnum = 1; Bprint(&bout, "<OL>\n"); } else if(strcmp(argv[1], "\\(bu") == 0){ list = Lunordered; Bprint(&bout, "<UL>\n"); } else { list = Lother; Bprint(&bout, "<DL COMPACT>\n"); } } else { list = Lother; Bprint(&bout, "<DL>\n"); } break; case Lother: case Lordered: case Lunordered: break; } switch(list){ case Lother: Bprint(&bout, "<DT>"); if(argc > 1) printarg(argv[1]); else Bprint(&bout, "<DT> "); Bprint(&bout, "<DD>\n"); break; case Lordered: case Lunordered: Bprint(&bout, "<LI>\n"); break; } } /* * .5i is one <DL><DT><DD> */ void g_in(int argc, char **argv) { float f; int delta, x; char *p; f = indent/0.5; delta = f; if(argc <= 1){ indent = 0.0; } else { f = strtod(argv[1], &p); switch(*p){ case 'i': break; case 'c': f = f / 2.54; break; case 'P': f = f / 6; break; default: case 'u': case 'm': f = f * (12 / 72); break; case 'n': f = f * (6 / 72); break; case 'p': f = f / 72.0; break; } switch(argv[1][0]){ case '+': case '-': indent += f; break; default: indent = f; break; } } if(indent < 0.0) indent = 0.0; f = (indent/0.5); x = f; delta = x - delta; while(delta < 0){ Bprint(&bout, "</DL>\n"); delta++; } while(delta > 0){ Bprint(&bout, "<DL><DT><DD>\n"); delta--; } } void g_HP(int, char**) { switch(list){ default: closel(); list = Ldef; hangingdt = 1; Bprint(&bout, "<DL><DT>\n"); break; case Ldef: if(hangingdt) Bprint(&bout, "<DD>"); Bprint(&bout, "<DT>"); hangingdt = 1; break; } } void g_SH(int, char**) { dohanginghead(); dohangingcenter(); closel(); closefont(); Bprint(&bout, "<H%d>", HH); hanginghead = HH; } void g_NH(int argc, char **argv) { int i, level; closel(); closefont(); dohangingcenter(); if(argc == 1) level = 0; else { level = atoi(argv[1])-1; if(level < 0 || level >= Maxnh) level = Maxnh - 1; } nh[level]++; Bprint(&bout, "<H%d>", HH); hanginghead = HH; Bprint(&bout, "%d", nh[0]); for(i = 1; i <= level; i++) Bprint(&bout, ".%d", nh[i]); Bprint(&bout, " "); for(i = level+1; i < Maxnh; i++) nh[i] = 0; } void g_TL(int, char**) { char *p, *np; char name[128]; closefont(); if(!titleseen){ if(!title){ /* get base part of filename */ p = strrchr(ssp->filename, '/'); if(p == nil) p = ssp->filename; else p++; strncpy(name, p, sizeof(name)); name[sizeof(name)-1] = 0; /* dump any extensions */ np = strchr(name, '.'); if(np) *np = 0; title = p; } Bprint(&bout, "<title>\n"); Bprint(&bout, "%s\n", title); Bprint(&bout, "</title>\n"); Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n"); titleseen = 1; } Bprint(&bout, "<center>"); hangingcenter = 1; Bprint(&bout, "<H%d>", 1); hanginghead = 1; } void dohangingcenter(void) { if(hangingcenter){ Bprint(&bout, "</center>"); hangingcenter = 1; } } void g_AU(int, char**) { closel(); dohanginghead(); Bprint(&bout, "<DL><DD><I>"); hangingau = 1; } void pushfont(Font *f) { if(fsp == Maxfsp) return; if(fsp >= 0 && fstack[fsp]) Bprint(&bout, "%s", fstack[fsp]->end); if(f != nil) Bprint(&bout, "%s", f->start); fstack[++fsp] = f; } void popfont(void) { if(fsp >= 0){ if(fstack[fsp]) Bprint(&bout, "%s", fstack[fsp]->end); fsp--; } } /* * for 3 args print arg3 \fxarg1\fP arg2 * for 2 args print arg1 \fxarg2\fP * for 1 args print \fxarg1\fP */ void font(Font *f, int argc, char **argv) { if(argc == 1){ pushfont(nil); return; } if(argc > 3) printarg(argv[3]); pushfont(f); printarg(argv[1]); popfont(); if(argc > 2) printarg(argv[2]); Bprint(&bout, "\n"); } void closefont(void) { if(fsp >= 0 && fstack[fsp]) Bprint(&bout, "%s", fstack[fsp]->end); fsp = -1; } void g_B(int argc, char **argv) { font(&bfont, argc, argv); } void g_R(int argc, char **argv) { font(nil, argc, argv); } void g_BI(int argc, char **argv) { font(&bifont, argc, argv); } void g_CW(int argc, char **argv) { font(&cwfont, argc, argv); } char* lower(char *p) { char *x; for(x = p; *x; x++) if(*x >= 'A' && *x <= 'Z') *x -= 'A' - 'a'; return p; } void g_I(int argc, char **argv) { int anchor; char *p; anchor = 0; if(argc > 2){ p = argv[2]; if(p[0] == '(') if(p[1] >= '0' && p[1] <= '9') if(p[2] == ')'){ anchor = 1; Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">", p[1], lower(argv[1])); } } font(&ifont, argc, argv); if(anchor) Bprint(&bout, "</A>"); } void g_br(int, char**) { if(hangingdt){ Bprint(&bout, "<dd>"); hangingdt = 0; }else Bprint(&bout, "<br>\n"); } void g_P1(int, char**) { if(example == 0){ example = 1; Bprint(&bout, "<DL><DT><DD><TT><PRE>\n"); } } void g_P2(int, char**) { if(example){ example = 0; Bprint(&bout, "</PRE></TT></DL>\n"); } } void g_SM(int, char **argv) { Bprint(&bout, "%s", argv[1]); } void g_ft(int argc, char **argv) { if(argc < 2){ pushfont(nil); return; } switch(argv[1][0]){ case '3': case 'B': pushfont(&bfont); break; case '2': case 'I': pushfont(&ifont); break; case '4': pushfont(&bifont); break; case '5': pushfont(&cwfont); break; case 'P': popfont(); break; case 'R': default: pushfont(nil); break; } } void g_sp(int argc, char **argv) { int n; n = 1; if(argc > 1){ n = atoi(argv[1]); if(n < 1) n = 1; if(argv[1][strlen(argv[1])-1] == 'i') n *= 4; } if(n > 5){ Bprint(&bout, "<br> <br>\n"); Bprint(&bout, "<HR>\n"); Bprint(&bout, "<br> <br>\n"); } else for(; n > 0; n--) Bprint(&bout, "<br> <br>\n"); } void rm_loop(char *name, String **l) { String *s; for(s = *l; s != nil; s = *l){ if(strcmp(name, s->name) == 0){ *l = s->next; free(s->name); free(s->val); free(s); break; } l = &s->next; } } void g_rm(int argc, char **argv) { Goobie *g; char *name; int i; for(i = 1; i < argc; i++) { name = argv[i]; rm_loop(name, &strings); rm_loop(name, ¯os); for(g = gtab; g->name; g++) if (strcmp(g->name, name) == 0) { g->f = g_ignore; break; } } } void g_AB(int, char**) { closel(); dohangingcenter(); Bprint(&bout, "<center><H4>ABSTRACT</H4></center><DL><DD>\n"); } void g_AE(int, char**) { Bprint(&bout, "</DL>\n"); } void g_FS(int, char **) { char *argv[3]; argv[0] = "IP"; argv[1] = nil; argv[2] = nil; g_IP(1, argv); Bprint(&bout, "NOTE:<I> "); } void g_FE(int, char **) { Bprint(&bout, "</I><DT> <DD>"); closel(); Bprint(&bout, "<br>\n"); } void g_de(int argc, char **argv) { int r; char *p, *cp; String *m; int len; if(argc < 2) return; m = nil; len = 0; if(strcmp(argv[0], "am") == 0){ for(m = macros; m != nil; m = m->next) if(strcmp(argv[1], m->name) == 0){ len = strlen(m->val); break; } if(m == nil){ /* nothing to append to */ for(;;){ p = Brdline(&ssp->in, '\n'); if(p == nil) break; p[Blinelen(&ssp->in)-1] = 0; if(strcmp(p, "..") == 0) break; } return; } } if(m == nil){ m = emalloc(sizeof(*m)); m->next = macros; macros = m; m->name = strdup(argv[1]); m->val = nil; len = 0; } /* read up to a .. removing double backslashes */ for(;;){ p = Brdline(&ssp->in, '\n'); if(p == nil) break; p[Blinelen(&ssp->in)-1] = 0; if(strcmp(p, "..") == 0) break; m->val = realloc(m->val, len + Blinelen(&ssp->in)+1); cp = m->val + len; while(*p){ r = *p++; if(r == '\\' && *p == '\\') p++; *cp++ = r; } *cp++ = '\n'; len = cp - m->val; *cp = 0; } } void g_hrule(int, char**) { Bprint(&bout, "<HR>\n"); } void g_BX(int argc, char **argv) { Bprint(&bout, "<HR>\n"); printargs(argc, argv); Bprint(&bout, "<HR>\n"); } void g_IH(int, char**) { Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n"); } void g_MH(int, char**) { Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n"); } void g_PY(int, char**) { Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n"); } void g_HO(int, char**) { Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n"); } void g_QS(int, char**) { Bprint(&bout, "<BLOCKQUOTE>\n"); } void g_QE(int, char**) { Bprint(&bout, "</BLOCKQUOTE>\n"); } void g_RS(int, char**) { Bprint(&bout, "<DL><DD>\n"); } void g_RE(int, char**) { Bprint(&bout, "</DL>\n"); } int gif; void g_startgif(int, char **argv) { int fd; int pfd[2]; char *e, *p; char name[32]; Dir *d; if(strcmp(argv[0], "EQ") == 0) e = ".EN"; else if(strcmp(argv[0], "TS") == 0) e = ".TE"; else if(strcmp(argv[0], "PS") == 0) e = ".PE"; else return; if(basename) p = basename; else{ p = strrchr(sstack[0].filename, '/'); if(p != nil) p++; else p = sstack[0].filename; } snprint(name, sizeof(name), "%s.%d.gif", p, gif++); fd = create(name, OWRITE, 0664); if(fd < 0){ fprint(2, "ms2html: can't create %s: %r\n", name); return; } if(pipe(pfd) < 0){ fprint(2, "ms2html: can't create pipe: %r\n"); close(fd); return; } switch(rfork(RFFDG|RFPROC)){ case -1: fprint(2, "ms2html: can't fork: %r\n"); close(fd); return; case 0: dup(fd, 1); close(fd); dup(pfd[0], 0); close(pfd[0]); close(pfd[1]); execl("/bin/troff2gif", "troff2gif", nil); fprint(2, "ms2html: couldn't exec troff2gif: %r\n"); _exits(nil); default: close(fd); close(pfd[0]); fprint(pfd[1], ".ll 7i\n"); /* fprint(pfd[1], ".EQ\ndelim %s\n.EN\n", delim); */ /* fprint(pfd[1], ".%s\n", argv[0]); */ for(;;){ p = Brdline(&ssp->in, '\n'); if(p == nil) break; ssp->lno++; ssp->rlno++; if(write(pfd[1], p, Blinelen(&ssp->in)) < 0) break; if(strncmp(p, e, 3) == 0) break; } close(pfd[1]); waitpid(); d = dirstat(name); if(d == nil) break; if(d->length == 0){ remove(name); free(d); break; } free(d); fprint(2, "ms2html: created auxiliary file %s\n", name); Bprint(&bout, "<br><img src=\"%s\"><br>\n", name); break; } } void g_lf(int argc, char **argv) { if(argc > 2) snprint(ssp->filename, sizeof(ssp->filename), argv[2]); if(argc > 1) ssp->rlno = atoi(argv[1]); } void g_so(int argc, char **argv) { ssp->lno++; ssp->rlno++; if(argc > 1) pushsrc(argv[1]); } void g_BP(int argc, char **argv) { int fd; char *p, *ext; char name[32]; Dir *d; if(argc < 2) return; p = strrchr(argv[1], '/'); if(p != nil) p++; else p = argv[1]; ext = strrchr(p, '.'); if(ext){ if(strcmp(ext, ".jpeg") == 0 || strcmp(ext, ".gif") == 0){ Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]); return; } } snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++); fd = create(name, OWRITE, 0664); if(fd < 0){ fprint(2, "ms2html: can't create %s: %r\n", name); return; } switch(rfork(RFFDG|RFPROC)){ case -1: fprint(2, "ms2html: can't fork: %r\n"); close(fd); return; case 0: dup(fd, 1); close(fd); execl("/bin/ps2gif", "ps2gif", argv[1], nil); fprint(2, "ms2html: couldn't exec ps2gif: %r\n"); _exits(nil); default: close(fd); waitpid(); d = dirstat(name); if(d == nil) break; if(d->length == 0){ remove(name); free(d); break; } free(d); fprint(2, "ms2html: created auxiliary file %s\n", name); Bprint(&bout, "<br><img src=\"%s\"><br>\n", name); break; } } /* insert straight HTML into output */ void g__H(int argc, char **argv) { int i; for(i = 1; i < argc; i++) Bprint(&bout, "%s ", argv[i]); Bprint(&bout, "\n"); } /* HTML page title */ void g__T(int argc, char **argv) { if(titleseen) return; Bprint(&bout, "<title>\n"); printargs(argc, argv); Bprint(&bout, "</title></head><body>\n"); titleseen = 1; } void g_nr(int argc, char **argv) { char *val; if (argc > 1) { if (argc == 2) val = "0"; else val = argv[2]; dsnr(argv[1], val, &numregs); } } void zerodivide(void) { fprint(2, "stdin %d(%s:%d): division by 0\n", ssp->lno, ssp->filename, ssp->rlno); } int numval(char **pline, int recur) { char *p; int neg, x, y; x = neg = 0; p = *pline; while(*p == '-') { neg = 1 - neg; p++; } if (*p == '(') { p++; x = numval(&p, 1); if (*p != ')') goto done; p++; } else while(*p >= '0' && *p <= '9') x = 10*x + *p++ - '0'; if (neg) x = -x; if (recur) for(;;) { switch(*p++) { case '+': x += numval(&p, 0); continue; case '-': x -= numval(&p, 0); continue; case '*': x *= numval(&p, 0); continue; case '/': y = numval(&p, 0); if (y == 0) { zerodivide(); x = 0; goto done; } x /= y; continue; case '<': if (*p == '=') { p++; x = x <= numval(&p, 0); continue; } x = x < numval(&p, 0); continue; case '>': if (*p == '=') { p++; x = x >= numval(&p, 0); continue; } x = x > numval(&p, 0); continue; case '=': if (*p == '=') p++; x = x == numval(&p, 0); continue; case '&': x &= numval(&p, 0); continue; case ':': x |= numval(&p, 0); continue; case '%': y = numval(&p, 0); if (!y) { zerodivide(); goto done; } x %= y; continue; } --p; break; } done: *pline = p; return x; } int iftest(char *p, char **bp) { char *p1; int c, neg, rv; rv = neg = 0; if (*p == '!') { neg = 1; p++; } c = *p; if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) { if (numval(&p,1) >= 1) rv = 1; goto done; } switch(c) { case 't': case 'o': rv = 1; case 'n': case 'e': p++; goto done; } for(p1 = ++p; *p != c; p++) if (!*p) goto done; for(p++;;) { if (*p != *p1++) { while(*p && *p++ != c); goto done; } if (*p++ == c) break; } rv = 1; done: if (neg) rv = 1 - rv; while(*p == ' ' || *p == '\t') p++; *bp = p; return rv; } void scanline(char *p, char *e, int wantnl) { int c; Rune r; while((c = getrune()) == ' ' || c == '\t') ; while(p < e) { if (c < 0) break; if (c < Runeself) { if (c == '\n') { if (wantnl) *p++ = c; break; } *p++ = c; } else { r = c; p += runetochar(p, &r); } c = getrune(); } *p = 0; } void pushbody(char *line) { char *b; if (line[0] == '\\' && line[1] == '{' /*}*/ ) line += 2; if (strsp < Maxmstack - 1) { pushstr(b = strdup(line)); mustfree[strsp] = b; } } void skipbody(char *line) { int c, n; if (line[0] != '\\' || line[1] != '{' /*}*/ ) return; for(n = 1;;) { while((c = getrune()) != '\\') if (c < 0) return; c = getrune(); if (c == '{') n++; else if ((c == '}' && (c = getrune()) == '\n' && !--n) || c < 0) return; } } int ifstart(char *line, char *e, char **bp) { int it; char *b; b = copyline(line, e, 1); ungetrune(); b[-1] = getrune(); scanline(b, e, 1); it = iftest(line, bp); return it; } void g_ie(char *line, char *e) { char *b; if (elsetop >= Maxif-1) { fprint(2, "ms2html: .ie's too deep\n"); return; } if (ifwastrue[++elsetop] = ifstart(line, e, &b)) pushbody(b); else skipbody(b); } void g_if(char *line, char *e) { char *b; if (ifstart(line, e, &b)) pushbody(b); else skipbody(b); } void g_el(char *line, char *e) { if (elsetop < 0) return; scanline(line, e, 1); if (ifwastrue[elsetop--]) skipbody(line); else pushbody(line); } void g_ig(int argc, char **argv) { char *p, *s; s = ".."; if (argc > 1) s = argv[1]; for(;;) { p = Brdline(&ssp->in, '\n'); if(p == nil) break; p[Blinelen(&ssp->in)-1] = 0; if(strcmp(p, s) == 0) break; } } void g_ds(char *line, char *e) { char *b; b = copyline(line, e, 1); if (b > line) { copyline(b, e, 0); if (*b == '"') b++; ds(line, b); } } void g_as(char *line, char *e) { String *s; char *b; b = copyline(line, e, 1); if (b == line) return; copyline(b, e, 0); if (*b == '"') b++; for(s = strings; s != nil; s = s->next) if(strcmp(line, s->name) == 0) break; if(s == nil){ ds(line, b); return; } s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1); strcat(s->val, b); } void g_BS(int argc, char **argv) { int i; if (argc > 1 && !weBref) { Bprint(&bout, "<a href=\"%s\"", argv[1]); for(i = 2; i < argc; i++) Bprint(&bout, " %s", argv[i]); Bprint(&bout, ">"); weBref = 1; } } void g_BE(int, char**) { if (weBref) { Bprint(&bout, "</a>"); weBref = 0; } } void g_LB(int argc, char **argv) { if (argc > 1) { if (weBref) g_BE(0,nil); Bprint(&bout, "<a name=\"%s\"></a>", argv[1]); } } void g_RT(int, char**) { g_BE(0,nil); dohanginghead(); closel(); closefont(); }