plan9fox/sys/src/cmd/ms2html.c

2635 lines
40 KiB
C
Raw Normal View History

#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', },
{ "&quot;", L'"', },
{ "&AElig;", L'Æ', },
{ "&Aacute;", L'Á', },
{ "&Acirc;", L'Â', },
{ "&Agrave;", L'À', },
{ "&Aring;", L'Å', },
{ "&Atilde;", L'Ã', },
{ "&Auml;", L'Ä', },
{ "&Ccedil;", L'Ç', },
{ "&ETH;", L'Ð', },
{ "&Eacute;", L'É', },
{ "&Ecirc;", L'Ê', },
{ "&Egrave;", L'È', },
{ "&Euml;", L'Ë', },
{ "&Iacute;", L'Í', },
{ "&Icirc;", L'Î', },
{ "&Igrave;", L'Ì', },
{ "&Iuml;", L'Ï', },
{ "&Ntilde;", L'Ñ', },
{ "&Oacute;", L'Ó', },
{ "&Ocirc;", L'Ô', },
{ "&Ograve;", L'Ò', },
{ "&Oslash;", L'Ø', },
{ "&Otilde;", L'Õ', },
{ "&Ouml;", L'Ö', },
{ "&THORN;", L'Þ', },
{ "&Uacute;", L'Ú', },
{ "&Ucirc;", L'Û', },
{ "&Ugrave;", L'Ù', },
{ "&Uuml;", L'Ü', },
{ "&Yacute;", L'Ý', },
{ "&aacute;", L'á', },
{ "&acirc;", L'â', },
{ "&aelig;", L'æ', },
{ "&agrave;", L'à', },
{ "&amp;", L'&', },
{ "&aring;", L'å', },
{ "&atilde;", L'ã', },
{ "&auml;", L'ä', },
{ "&ccedil;", L'ç', },
{ "&eacute;", L'é', },
{ "&ecirc;", L'ê', },
{ "&egrave;", L'è', },
{ "&eth;", L'ð', },
{ "&euml;", L'ë', },
{ "&gt;", L'>', },
{ "&iacute;", L'í', },
{ "&icirc;", L'î', },
{ "&igrave;", L'ì', },
{ "&iuml;", L'ï', },
{ "&lt;", L'<', },
{ "&ntilde;", L'ñ', },
{ "&oacute;", L'ó', },
{ "&ocirc;", L'ô', },
{ "&ograve;", L'ò', },
{ "&oslash;", L'ø', },
{ "&otilde;", L'õ', },
{ "&ouml;", L'ö', },
{ "&szlig;", L'ß', },
{ "&thorn;", L'þ', },
{ "&uacute;", L'ú', },
{ "&ucirc;", L'û', },
{ "&ugrave;", L'ù', },
{ "&uuml;", L'ü', },
{ "&yacute;", L'ý', },
{ "&yuml;", L'ÿ', },
{ "&#161;", L'¡', },
{ "&#162;", L'¢', },
{ "&#163;", L'£', },
{ "&#164;", L'¤', },
{ "&#165;", L'¥', },
{ "&#166;", L'¦', },
{ "&#167;", L'§', },
{ "&#168;", L'¨', },
{ "&#169;", L'©', },
{ "&#170;", L'ª', },
{ "&#171;", L'«', },
{ "&#172;", L'¬', },
{ "&#173;", L'­', },
{ "&#174;", L'®', },
{ "&#175;", L'¯', },
{ "&#176;", L'°', },
{ "&#177;", L'±', },
{ "&#178;", L'²', },
{ "&#179;", L'³', },
{ "&#180;", L'´', },
{ "&#181;", L'µ', },
{ "&#182;", L'', },
{ "&#183;", L'·', },
{ "&#184;", L'¸', },
{ "&#185;", L'¹', },
{ "&#186;", L'º', },
{ "&#187;", L'»', },
{ "&#188;", L'¼', },
{ "&#189;", L'½', },
{ "&#190;", L'¾', },
{ "&#191;", L'¿', },
{ "*", L'', },
{ "&#164;", L'', },
{ "&#186;", L'', },
{ "(tm)", L'', },
{"&#913;", L'Α',},
{"&#914;", L'Β',},
{"&#915;", L'Γ',},
{"&#916;", L'Δ',},
{"&#917;", L'Ε',},
{"&#918;", L'Ζ',},
{"&#919;", L'Η',},
{"&#920;", L'Θ',},
{"&#921;", L'Ι',},
{"&#922;", L'Κ',},
{"&#923;", L'Λ',},
{"&#924;", L'Μ',},
{"&#925;", L'Ν',},
{"&#926;", L'Ξ',},
{"&#927;", L'Ο',},
{"&#928;", L'Π',},
{"&#929;", L'Ρ',},
{"&#930;", L'΢',},
{"&#931;", L'Σ',},
{"&#932;", L'Τ',},
{"&#933;", L'Υ',},
{"&#934;", L'Φ',},
{"&#935;", L'Χ',},
{"&#936;", L'Ψ',},
{"&#937;", L'Ω',},
{"&#945;", L'α',},
{"&#946;", L'β',},
{"&#947;", L'γ',},
{"&#948;", L'δ',},
{"&#949;", L'ε',},
{"&#950;", L'ζ',},
{"&#951;", L'η',},
{"&#952;", L'θ',},
{"&#953;", L'ι',},
{"&#954;", L'κ',},
{"&#955;", L'λ',},
{"&#956;", L'μ',},
{"&#957;", L'ν',},
{"&#958;", L'ξ',},
{"&#959;", L'ο',},
{"&#960;", L'π',},
{"&#961;", L'ρ',},
{"&#962;", L'ς',},
{"&#963;", L'σ',},
{"&#964;", L'τ',},
{"&#965;", L'υ',},
{"&#966;", L'φ',},
{"&#967;", L'χ',},
{"&#968;", L'ψ',},
{"&#969;", L'ω',},
{ "<-", L'', },
{ "^", L'', },
{ "->", L'', },
{ "v", L'', },
{ "!=", L'', },
{ "<=", L'', },
{ "...", L'', },
{"&isin;", L'', },
{"&#8211;", L'', },
{"&#8212;", L'', },
{ "CYRILLIC XYZZY", L'й', },
{ "CYRILLIC XYZZY", L'ъ', },
{ "CYRILLIC Y", L'ь', },
{ "CYRILLIC YA", L'я', },
{ "CYRILLIC YA", L'ё', },
{ "&#191;", L'', },
{ nil, 0 },
};
typedef struct Troffspec Troffspec;
struct Troffspec
{
char *name;
char *value;
};
Troffspec tspec[] =
{
{ "A*", "&Aring;", },
{ "o\"", "&ouml;", },
{ "ff", "ff", },
{ "fi", "fi", },
{ "fl", "fl", },
{ "Fi", "ffi", },
{ "ru", "_", },
{ "em", "&#173;", },
{ "14", "&#188;", },
{ "12", "&#189;", },
{ "co", "&#169;", },
{ "de", "&#176;", },
{ "dg", "&#161;", },
{ "fm", "&#180;", },
{ "rg", "&#174;", },
{ "bu", "*", },
{ "sq", "&#164;", },
{ "hy", "-", },
{ "pl", "+", },
{ "mi", "-", },
{ "mu", "&#215;", },
{ "di", "&#247;", },
{ "eq", "=", },
{ "==", "==", },
{ ">=", ">=", },
{ "<=", "<=", },
{ "!=", "!=", },
{ "+-", "&#177;", },
{ "no", "&#172;", },
{ "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", "&Oslash;", },
{ "aa", "&#180;", },
{ "ga", "`", },
{ "ci", "O", },
{ "L1", "DEATHSTAR", },
{ "sc", "&#167;", },
{ "dd", "++", },
{ "lh", "<=", },
{ "rh", "=>", },
{ "lt", "(", },
{ "rt", ")", },
{ "lc", "|", },
{ "rc", "|", },
{ "lb", "(", },
{ "rb", ")", },
{ "lf", "|", },
{ "rf", "|", },
{ "lk", "|", },
{ "rk", "|", },
{ "bv", "|", },
{ "ts", "s", },
{ "br", "|", },
{ "or", "|", },
{ "ul", "_", },
{ "rn", " ", },
{ "**", "*", },
{ "tm", "&#153", },
{ 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 "&#191;";
/* 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 "&amp;";
case '<':
if(msp >= 0 || strsp >= 0)
return "<";
return "&#60;";
case '>':
if(msp >= 0 || strsp >= 0)
return ">";
return "&#62;";
}
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>&#32;<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", "&#174;");
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>&#32;<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>&#32;");
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>&#32;<br>\n");
Bprint(&bout, "<HR>\n");
Bprint(&bout, "<br>&#32;<br>\n");
} else
for(; n > 0; n--)
Bprint(&bout, "<br>&#32;<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, &macros);
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>&#32;<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();
}