This commit is contained in:
rsc 2006-02-21 18:37:05 +00:00
parent 49a1496cbb
commit c42a1d3d61
31 changed files with 4745 additions and 2 deletions

148
src/cmd/htmlroff/a.h Normal file
View file

@ -0,0 +1,148 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
enum
{
Unbsp = 0x00A0,
Uprivate = 0xF000,
Uempty, /* \& */
Uamp, /* raw & */
Ult, /* raw < */
Ugt, /* raw > */
Utick, /* raw ' */
Ubtick, /* raw ` */
Uminus, /* raw - */
Uspace, /* raw space */
Upl, /* symbol + */
Ueq, /* symbol = */
Umi, /* symbol - */
Uformatted, /* start diverted output */
Uunformatted, /* end diverted output */
UPI = 720, /* units per inch */
UPX = 10, /* units per pixel */
/* special input modes */
CopyMode = 1<<1,
ExpandMode = 1<<2,
ArgMode = 1<<3,
HtmlMode = 1<<4,
MaxLine = 1024,
};
Rune* L(char*);
void addesc(Rune, int (*)(void), int);
void addraw(Rune*, void(*)(Rune*));
void addreq(Rune*, void(*)(int, Rune**), int);
void af(Rune*, Rune*);
void as(Rune*, Rune*);
void br(void);
void closehtml(void);
Rune* copyarg(void);
void delraw(Rune*);
void delreq(Rune*);
void ds(Rune*, Rune*);
int dv(int);
int e_nop(void);
int e_warn(void);
void* emalloc(uint);
void* erealloc(void*, uint);
Rune* erunesmprint(char*, ...);
Rune* erunestrdup(Rune*);
char* esmprint(char*, ...);
char* estrdup(char*);
int eval(Rune*);
int evalscale(Rune*, int);
Rune* getname(void);
int getnext(void);
Rune* getds(Rune*);
Rune* _getnr(Rune*);
int getnr(Rune*);
int getnrr(Rune*);
int getrune(void);
Rune* getqarg(void);
Rune* getline(void);
void hideihtml(void);
void html(Rune*, Rune*);
void htmlinit(void);
void ihtml(Rune*, Rune*);
void inputnotify(void(*)(void));
void itrap(void);
void itrapset(void);
int linefmt(Fmt*);
void nr(Rune*, int);
void _nr(Rune*, Rune*);
void out(Rune*);
void (*outcb)(Rune);
void outhtml(Rune*);
void outrune(Rune);
void outtrap(void);
int popinput(void);
void printds(int);
int pushinputfile(Rune*);
void pushinputstring(Rune*);
int pushstdin(void);
int queueinputfile(Rune*);
int queuestdin(void);
void r_nop(int, Rune**);
void r_warn(int, Rune**);
Rune *readline(int);
void reitag(void);
void renraw(Rune*, Rune*);
void renreq(Rune*, Rune*);
void run(void);
void runinput(void);
int runmacro(int, int, Rune**);
void runmacro1(Rune*);
Rune* rune2html(Rune);
void setlinenumber(Rune*, int);
void showihtml(void);
void sp(int);
void t1init(void);
void t2init(void);
void t3init(void);
void t4init(void);
void t5init(void);
void t6init(void);
void t7init(void);
void t8init(void);
void t9init(void);
void t10init(void);
void t11init(void);
void t12init(void);
void t13init(void);
void t14init(void);
void t15init(void);
void t16init(void);
void t17init(void);
void t18init(void);
void t19init(void);
void t20init(void);
Rune troff2rune(Rune*);
void unfont(void);
void ungetnext(Rune);
void ungetrune(Rune);
void unitag(void);
void warn(char*, ...);
extern int backslash;
extern int bol;
extern Biobuf bout;
extern int broke;
extern int dot;
extern int inputmode;
extern int inrequest;
extern int tick;
extern int utf8;
extern int verbose;
extern int linepos;
#define runemalloc(n) (Rune*)emalloc((n)*sizeof(Rune))
#define runerealloc(r, n) (Rune*)erealloc(r, (n)*sizeof(Rune))
#define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune))
#pragma varargck type "L" void

116
src/cmd/htmlroff/char.c Normal file
View file

@ -0,0 +1,116 @@
#include "a.h"
/*
* Translate Unicode to HTML by asking tcs(1).
* This way we don't have yet another table.
*/
Rune*
rune2html(Rune r)
{
static Biobuf b;
static int fd = -1;
static Rune **tcscache[256];
int p[2];
char *q;
if(r == '\n')
return L("\n");
if(tcscache[r>>8] && tcscache[r>>8][r&0xFF])
return tcscache[r>>8][r&0xFF];
if(fd < 0){
if(pipe(p) < 0)
sysfatal("pipe: %r");
switch(fork()){
case -1:
sysfatal("fork: %r");
case 0:
dup(p[0], 0);
dup(p[0], 1);
close(p[1]);
execl("tcs", "tcs", "-t", "html", nil);
_exits(0);
default:
close(p[0]);
fd = p[1];
Binit(&b, fd, OREAD);
break;
}
}
fprint(fd, "%C\n", r);
q = Brdline(&b, '\n');
if(q == nil)
sysfatal("tcs: early eof");
q[Blinelen(&b)-1] = 0;
if(tcscache[r>>8] == nil)
tcscache[r>>8] = emalloc(256*sizeof tcscache[0][0]);
tcscache[r>>8][r&0xFF] = erunesmprint("%s", q);
return tcscache[r>>8][r&0xFF];
}
/*
* Translate troff to Unicode by looking in troff's utfmap.
* This way we don't have yet another hard-coded table.
*/
typedef struct Trtab Trtab;
struct Trtab
{
char t[3];
Rune r;
};
static Trtab trtab[200];
int ntrtab;
static Trtab trinit[] =
{
"pl", Upl,
"eq", Ueq,
"em", 0x2014,
"en", 0x2013,
"mi", Umi,
"fm", 0x2032,
};
Rune
troff2rune(Rune *rs)
{
char *file, *f[10], *p, s[3];
int i, nf;
Biobuf *b;
if(rs[0] >= Runeself || rs[1] >= Runeself)
return Runeerror;
s[0] = rs[0];
s[1] = rs[1];
s[2] = 0;
if(ntrtab == 0){
for(i=0; i<nelem(trinit) && ntrtab < nelem(trtab); i++){
trtab[ntrtab] = trinit[i];
ntrtab++;
}
file = "/sys/lib/troff/font/devutf/utfmap";
if((b = Bopen(file, OREAD)) == nil)
sysfatal("open %s: %r", file);
while((p = Brdline(b, '\n')) != nil){
p[Blinelen(b)-1] = 0;
nf = getfields(p, f, nelem(f), 0, "\t");
for(i=0; i+2<=nf && ntrtab<nelem(trtab); i+=2){
chartorune(&trtab[ntrtab].r, f[i]);
memmove(trtab[ntrtab].t, f[i+1], 2);
ntrtab++;
}
}
Bterm(b);
if(ntrtab >= nelem(trtab))
fprint(2, "%s: trtab too small\n", argv0);
}
for(i=0; i<ntrtab; i++)
if(strcmp(s, trtab[i].t) == 0)
return trtab[i].r;
return Runeerror;
}

287
src/cmd/htmlroff/html.c Normal file
View file

@ -0,0 +1,287 @@
/*
* Emit html. Keep track of tags so that user doesn't have to.
*/
#include "a.h"
typedef struct Tag Tag;
struct Tag
{
Tag *next;
Rune *id;
Rune *open;
Rune *close;
};
Tag *tagstack;
Tag *tagset;
int hidingset;
static Rune*
closingtag(Rune *s)
{
Rune *t;
Rune *p0, *p;
t = runemalloc(sizeof(Rune));
if(s == nil)
return t;
for(p=s; *p; p++){
if(*p == Ult){
p++;
if(*p == '/'){
while(*p && *p != Ugt)
p++;
goto close;
}
p0 = p;
while(*p && !isspacerune(*p) && *p != Uspace && *p != Ugt)
p++;
t = runerealloc(t, 1+(p-p0)+2+runestrlen(t)+1);
runemove(t+(p-p0)+3, t, runestrlen(t)+1);
t[0] = Ult;
t[1] = '/';
runemove(t+2, p0, p-p0);
t[2+(p-p0)] = Ugt;
}
if(*p == Ugt && p>s && *(p-1) == '/'){
close:
for(p0=t+1; *p0 && *p0 != Ult; p0++)
;
runemove(t, p0, runestrlen(p0)+1);
}
}
return t;
}
void
html(Rune *id, Rune *s)
{
Rune *es;
Tag *t, *tt, *next;
br();
hideihtml(); /* br already did, but be paranoid */
for(t=tagstack; t; t=t->next){
if(runestrcmp(t->id, id) == 0){
for(tt=tagstack;; tt=next){
next = tt->next;
free(tt->id);
free(tt->open);
out(tt->close);
outrune('\n');
free(tt->close);
free(tt);
if(tt == t){
tagstack = next;
goto cleared;
}
}
}
}
cleared:
if(s == nil || s[0] == 0)
return;
out(s);
outrune('\n');
es = closingtag(s);
if(es[0] == 0){
free(es);
return;
}
if(runestrcmp(id, L("-")) == 0){
out(es);
outrune('\n');
free(es);
return;
}
t = emalloc(sizeof *t);
t->id = erunestrdup(id);
t->close = es;
t->next = tagstack;
tagstack = t;
}
void
closehtml(void)
{
Tag *t, *next;
br();
hideihtml();
for(t=tagstack; t; t=next){
next = t->next;
out(t->close);
outrune('\n');
free(t->id);
free(t->close);
free(t);
}
}
static void
rshow(Tag *t, Tag *end)
{
if(t == nil || t == end)
return;
rshow(t->next, end);
out(t->open);
}
void
ihtml(Rune *id, Rune *s)
{
Tag *t, *tt, **l;
for(t=tagset; t; t=t->next){
if(runestrcmp(t->id, id) == 0){
if(s && t->open && runestrcmp(t->open, s) == 0)
return;
for(l=&tagset; (tt=*l); l=&tt->next){
if(!hidingset)
out(tt->close);
if(tt == t)
break;
}
*l = t->next;
free(t->id);
free(t->close);
free(t->open);
free(t);
if(!hidingset)
rshow(tagset, *l);
goto cleared;
}
}
cleared:
if(s == nil || s[0] == 0)
return;
t = emalloc(sizeof *t);
t->id = erunestrdup(id);
t->open = erunestrdup(s);
t->close = closingtag(s);
if(!hidingset)
out(s);
t->next = tagset;
tagset = t;
}
void
hideihtml(void)
{
Tag *t;
if(hidingset)
return;
hidingset = 1;
for(t=tagset; t; t=t->next)
out(t->close);
}
void
showihtml(void)
{
if(!hidingset)
return;
hidingset = 0;
rshow(tagset, nil);
}
int
e_lt(void)
{
return Ult;
}
int
e_gt(void)
{
return Ugt;
}
int
e_at(void)
{
return Uamp;
}
int
e_tick(void)
{
return Utick;
}
int
e_btick(void)
{
return Ubtick;
}
int
e_minus(void)
{
return Uminus;
}
void
r_html(Rune *name)
{
Rune *id, *line, *p;
id = copyarg();
line = readline(HtmlMode);
for(p=line; *p; p++){
switch(*p){
case '<':
*p = Ult;
break;
case '>':
*p = Ugt;
break;
case '&':
*p = Uamp;
break;
case ' ':
*p = Uspace;
break;
}
}
if(name[0] == 'i')
ihtml(id, line);
else
html(id, line);
free(id);
free(line);
}
char defaultfont[] =
".ihtml f1\n"
".ihtml f\n"
".ihtml f <span style=\"font-size=\\n(.spt\">\n"
".if \\n(.f==2 .ihtml f1 <i>\n"
".if \\n(.f==3 .ihtml f1 <b>\n"
".if \\n(.f==4 .ihtml f1 <b><i>\n"
".if \\n(.f==5 .ihtml f1 <tt>\n"
".if \\n(.f==6 .ihtml f1 <tt><i>\n"
"..\n"
;
void
htmlinit(void)
{
addraw(L("html"), r_html);
addraw(L("ihtml"), r_html);
addesc('<', e_lt, CopyMode);
addesc('>', e_gt, CopyMode);
addesc('\'', e_tick, CopyMode);
addesc('`', e_btick, CopyMode);
addesc('-', e_minus, CopyMode);
addesc('@', e_at, CopyMode);
ds(L("font"), L(defaultfont));
}

241
src/cmd/htmlroff/input.c Normal file
View file

@ -0,0 +1,241 @@
/*
* Read input files.
*/
#include "a.h"
typedef struct Istack Istack;
struct Istack
{
Rune unget[3];
int nunget;
Biobuf *b;
Rune *p;
Rune *ep;
Rune *s;
int lineno;
Rune *name;
Istack *next;
void (*fn)(void);
};
Istack *istack;
Istack *ibottom;
static void
setname(void)
{
Rune *r, *p;
if(istack == nil || istack->name == nil)
return;
_nr(L(".F"), istack->name);
r = erunestrdup(istack->name);
p = runestrchr(r, '.');
if(p)
*p = 0;
_nr(L(".B"), r);
free(r);
}
static void
ipush(Istack *is)
{
if(istack == nil)
ibottom = is;
else
is->next = istack;
istack = is;
setname();
}
static void
iqueue(Istack *is)
{
if(ibottom == nil){
istack = is;
setname();
}else
ibottom->next = is;
ibottom = is;
}
int
_inputfile(Rune *s, void (*push)(Istack*))
{
Istack *is;
Biobuf *b;
char *t;
t = esmprint("%S", s);
if((b = Bopen(t, OREAD)) == nil){
free(t);
fprint(2, "%s: open %S: %r\n", argv0, s);
return -1;
}
free(t);
is = emalloc(sizeof *is);
is->b = b;
is->name = erunestrdup(s);
is->lineno = 1;
push(is);
return 0;
}
int
pushinputfile(Rune *s)
{
return _inputfile(s, ipush);
}
int
queueinputfile(Rune *s)
{
return _inputfile(s, iqueue);
}
int
_inputstdin(void (*push)(Istack*))
{
Biobuf *b;
Istack *is;
if((b = Bopen("/dev/null", OREAD)) == nil){
fprint(2, "%s: open /dev/null: %r\n", argv0);
return -1;
}
dup(0, b->fid);
is = emalloc(sizeof *is);
is->b = b;
is->name = erunestrdup(L("stdin"));
is->lineno = 1;
push(is);
return 0;
}
int
pushstdin(void)
{
return _inputstdin(ipush);
}
int
queuestdin(void)
{
return _inputstdin(iqueue);
}
void
_inputstring(Rune *s, void (*push)(Istack*))
{
Istack *is;
is = emalloc(sizeof *is);
is->s = erunestrdup(s);
is->p = is->s;
is->ep = is->p+runestrlen(is->p);
push(is);
}
void
pushinputstring(Rune *s)
{
_inputstring(s, ipush);
}
void
inputnotify(void (*fn)(void))
{
if(istack)
istack->fn = fn;
}
int
popinput(void)
{
Istack *is;
is = istack;
if(is == nil)
return 0;
istack = istack->next;
if(is->b)
Bterm(is->b);
free(is->s);
free(is->name);
if(is->fn)
is->fn();
free(is);
setname();
return 1;
}
int
getrune(void)
{
Rune r;
int c;
top:
if(istack == nil)
return -1;
if(istack->nunget)
return istack->unget[--istack->nunget];
else if(istack->p){
if(istack->p >= istack->ep){
popinput();
goto top;
}
r = *istack->p++;
}else if(istack->b){
if((c = Bgetrune(istack->b)) < 0){
popinput();
goto top;
}
r = c;
}else{
r = 0;
sysfatal("getrune - can't happen");
}
if(r == '\n')
istack->lineno++;
return r;
}
void
ungetrune(Rune r)
{
if(istack == nil || istack->nunget >= nelem(istack->unget))
pushinputstring(L(""));
istack->unget[istack->nunget++] = r;
}
int
linefmt(Fmt *f)
{
Istack *is;
for(is=istack; is && !is->b; is=is->next)
;
if(is)
return fmtprint(f, "%S:%d", is->name, is->lineno);
else
return fmtprint(f, "<no input>");
}
void
setlinenumber(Rune *s, int n)
{
Istack *is;
for(is=istack; is && !is->name; is=is->next)
;
if(is){
if(s){
free(is->name);
is->name = erunestrdup(s);
}
is->lineno = n;
}
}

72
src/cmd/htmlroff/main.c Normal file
View file

@ -0,0 +1,72 @@
/*
* Convert troff -ms input to HTML.
*/
#include "a.h"
Biobuf bout;
char* tmacdir;
int verbose;
int utf8 = 0;
void
usage(void)
{
fprint(2, "usage: htmlroff [-iuv] [-m mac] [-r an] [file...]\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int i, dostdin;
char *p;
Rune *r;
Rune buf[2];
Binit(&bout, 1, OWRITE);
fmtinstall('L', linefmt);
quotefmtinstall();
tmacdir = unsharp("#9/tmac");
dostdin = 0;
ARGBEGIN{
case 'i':
dostdin = 1;
break;
case 'm':
r = erunesmprint("%s/tmac.%s", tmacdir, EARGF(usage()));
if(queueinputfile(r) < 0)
fprint(2, "%S: %r\n", r);
break;
case 'r':
p = EARGF(usage());
p += chartorune(buf, p);
buf[1] = 0;
_nr(buf, erunesmprint("%s", p+1));
break;
case 'u':
utf8 = 1;
break;
case 'v':
verbose = 1;
break;
default:
usage();
}ARGEND
for(i=0; i<argc; i++){
if(strcmp(argv[i], "-") == 0)
queuestdin();
else
queueinputfile(erunesmprint("%s", argv[i]));
}
if(argc == 0 || dostdin)
queuestdin();
run();
Bprint(&bout, "\n");
Bterm(&bout);
exits(nil);
}

58
src/cmd/htmlroff/mkfile Normal file
View file

@ -0,0 +1,58 @@
<$PLAN9/src/mkhdr
TARG=htmlroff
OFILES=\
char.$O\
html.$O\
input.$O\
main.$O\
roff.$O\
t1.$O\
t2.$O\
t3.$O\
t4.$O\
t5.$O\
t6.$O\
t7.$O\
t8.$O\
# t9.$O\
t10.$O\
t11.$O\
# t12.$O\
t13.$O\
t14.$O\
t15.$O\
t16.$O\
t17.$O\
t18.$O\
t19.$O\
t20.$O\
util.$O\
HFILES=a.h
<$PLAN9/src/mkone
auth:V: auth.html
web auth.html
auth.html: o.htmlroff auth.ms htmlmac.s
9 pic auth.ms | 9 eqn | ./o.htmlroff -ms >auth.html
# 9 pic auth.ms | 9 eqn | ./o.htmlroff htmlmac.s /usr/local/plan9/tmac/tmac.skeep - >auth.html
test%.html: o.htmlroff test.% htmlmac.s
./o.htmlroff htmlmac.s test.$stem - >$target
eqn:V: eqn.html
web eqn.html
eqn.html: o.htmlroff htmlmac.s eqn.ms
9 eqn eqn.ms | ./o.htmlroff htmlmac.s - >eqn.html
eqn0.html: o.htmlroff htmlmac.s eqn0.ms
./o.htmlroff htmlmac.s eqn0.ms - >eqn0.html
rc.html: o.htmlroff rc.ms htmlmac.s
9 tbl rc.ms | ./o.htmlroff -ms >rc.html

750
src/cmd/htmlroff/roff.c Normal file
View file

@ -0,0 +1,750 @@
#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;
void
addraw(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;
}
void
delraw(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;
}
}
}
void
renraw(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;
}
}
void
addreq(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;
}
void
delreq(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;
}
}
}
void
renreq(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;
}
}
void
addesc(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.
*/
int
getnext(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;
}
void
ungetnext(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.
*/
int
parseargs(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.
*/
void
dotline(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;
void
newline(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');
}
void
startoutput(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);
}
void
br(void)
{
if(!isoutput)
return;
isoutput = 0;
nr(L(".dv"), 0);
dv(0);
hideihtml();
if(getnr(L(".paragraph")))
outhtml(L("</p>"));
}
void
r_margin(int argc, Rune **argv)
{
USED(argc);
nr(L(".margin"), eval(argv[1]));
}
int inrequest;
void
runinput(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;
}
}
}
void
run(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();
}
void
out(Rune *s)
{
if(s == nil)
return;
for(; *s; s++)
outrune(*s);
}
void (*outcb)(Rune);
void
inroman(Rune r)
{
int f;
f = getnr(L(".f"));
nr(L(".f"), 1);
runmacro1(L("font"));
outrune(r);
nr(L(".f"), f);
runmacro1(L("font"));
}
void
Brune(Rune r)
{
if(r == '&')
Bprint(&bout, "&amp;");
else if(r == '<')
Bprint(&bout, "&lt;");
else if(r == '>')
Bprint(&bout, "&gt;");
else if(r < Runeself || utf8)
Bprint(&bout, "%C", r);
else
Bprint(&bout, "%S", rune2html(r));
}
void
outhtml(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);
}
}
void
outrune(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, "&rsquo;");
return;
case '`':
Bprint(&bout, "&lsquo;");
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);
}
void
r_nop(int argc, Rune **argv)
{
USED(argc);
USED(argv);
}
void
r_warn(int argc, Rune **argv)
{
USED(argc);
warn("ignoring %C%S", dot, argv[0]);
}
int
e_warn(void)
{
/* dispatch loop prints a warning for us */
return 0;
}
int
e_nop(void)
{
return 0;
}

186
src/cmd/htmlroff/t1.c Normal file
View file

@ -0,0 +1,186 @@
#include "a.h"
/*
* Section 1 - General Explanation.
*/
/* 1.3 - Numerical parameter input. */
char *units = "icPmnpuvx";
int
scale2units(char c)
{
int x;
switch(c){
case 'i': /* inch */
return UPI;
case 'c': /* centimeter */
return 0.3937008 * UPI;
case 'P': /* pica = 1/6 inch */
return UPI / 6;
case 'm': /* em = S points */
return UPI / 72.0 * getnr(L(".s"));
case 'n': /* en = em/2 */
return UPI / 72.0 * getnr(L(".s")) / 2;
case 'p': /* point = 1/72 inch */
return UPI / 72;
case 'u': /* basic unit */
return 1;
case 'v': /* vertical line space V */
x = getnr(L(".v"));
if(x == 0)
x = 12 * UPI / 72;
return x;
case 'x': /* pixel (htmlroff addition) */
return UPX;
default:
return 1;
}
}
/* 1.4 - Numerical expressions. */
int eval0(Rune**, int, int);
int
eval(Rune *s)
{
return eval0(&s, 1, 1);
}
long
runestrtol(Rune *a, Rune **p)
{
long n;
n = 0;
while('0' <= *a && *a <= '9'){
n = n*10 + *a-'0';
a++;
}
*p = a;
return n;
}
int
evalscale(Rune *s, int c)
{
return eval0(&s, scale2units(c), 1);
}
int
eval0(Rune **pline, int scale, int recur)
{
Rune *p;
int neg;
double f, p10;
int x, y;
neg = 0;
p = *pline;
while(*p == '-'){
neg = 1 - neg;
p++;
}
if(*p == '('){
p++;
x = eval0(&p, scale, 1);
if (*p != ')'){
*pline = p;
return x;
}
p++;
}else{
f = runestrtol(p, &p);
if(*p == '.'){
p10 = 1.0;
p++;
while('0' <= *p && *p <= '9'){
p10 /= 10;
f += p10*(*p++ - '0');
}
}
if(*p && strchr(units, *p)){
if(scale)
f *= scale2units(*p);
p++;
}else if(scale)
f *= scale;
x = f;
}
if(neg)
x = -x;
if(!recur){
*pline = p;
return x;
}
while(*p){
switch(*p++) {
case '+':
x += eval0(&p, scale, 0);
continue;
case '-':
x -= eval0(&p, scale, 0);
continue;
case '*':
x *= eval0(&p, scale, 0);
continue;
case '/':
y = eval0(&p, scale, 0);
if (y == 0) {
fprint(2, "%L: divide by zero %S\n", p);
y = 1;
}
x /= y;
continue;
case '%':
y = eval0(&p, scale, 0);
if (!y) {
fprint(2, "%L: modulo by zero %S\n", p);
y = 1;
}
x %= y;
continue;
case '<':
if (*p == '=') {
p++;
x = x <= eval0(&p, scale, 0);
continue;
}
x = x < eval0(&p, scale, 0);
continue;
case '>':
if (*p == '=') {
p++;
x = x >= eval0(&p, scale, 0);
continue;
}
x = x > eval0(&p, scale, 0);
continue;
case '=':
if (*p == '=')
p++;
x = x == eval0(&p, scale, 0);
continue;
case '&':
x &= eval0(&p, scale, 0);
continue;
case ':':
x |= eval0(&p, scale, 0);
continue;
}
}
*pline = p;
return x;
}
void
t1init(void)
{
Tm tm;
tm = *localtime(time(0));
nr(L("dw"), tm.wday+1);
nr(L("dy"), tm.mday);
nr(L("mo"), tm.mon);
nr(L("yr"), tm.year%100);
}

140
src/cmd/htmlroff/t10.c Normal file
View file

@ -0,0 +1,140 @@
#include "a.h"
/*
* 10. Input and Output Conventions and Character Translation.
*/
/* set escape character */
void
r_ec(int argc, Rune **argv)
{
if(argc == 1)
backslash = '\\';
else
backslash = argv[1][0];
}
/* turn off escape character */
void
r_eo(int argc, Rune **argv)
{
USED(argc);
USED(argv);
backslash = -2;
}
/* continuous underline (same as ul in troff) for the next N lines */
/* set underline font */
void
g_uf(int argc, Rune **argv)
{
USED(argc);
USED(argv);
}
/* set control character */
void
r_cc(int argc, Rune **argv)
{
if(argc == 1)
dot = '.';
else
dot = argv[1][0];
}
/* set no-break control character */
void
r_c2(int argc, Rune **argv)
{
if(argc == 1)
tick = '\'';
else
tick = argv[1][0];
}
/* output translation */
int
e_bang(void)
{
Rune *line;
line = readline(CopyMode);
out(line);
outrune('\n');
free(line);
return 0;
}
int
e_X(void)
{
int c;
while((c = getrune()) >= 0 && c != '\'' && c != '\n')
outrune(c);
if(c == '\n'){
warn("newline in %CX'...'", backslash);
outrune(c);
}
if(c < 0)
warn("eof in %CX'...'", backslash);
return 0;
}
int
e_quote(void)
{
int c;
if(inputmode&ArgMode){
/* Leave \" around for argument parsing */
ungetrune('"');
return '\\';
}
while((c = getrune()) >= 0 && c != '\n')
;
return '\n';
}
int
e_newline(void)
{
return 0;
}
int
e_e(void)
{
return backslash;
}
void
r_comment(Rune *name)
{
int c;
USED(name);
while((c = getrune()) >= 0 && c != '\n')
;
}
void
t10init(void)
{
addreq(L("ec"), r_ec, -1);
addreq(L("eo"), r_eo, 0);
addreq(L("lg"), r_nop, -1);
addreq(L("cc"), r_cc, -1);
addreq(L("c2"), r_c2, -1);
addreq(L("tr"), r_warn, -1);
addreq(L("ul"), r_nop, -1);
addraw(L("\\\""), r_comment);
addesc('!', e_bang, 0);
addesc('X', e_X, 0);
addesc('\"', e_quote, CopyMode|ArgMode);
addesc('\n', e_newline, CopyMode|ArgMode|HtmlMode);
addesc('e', e_e, 0);
}

107
src/cmd/htmlroff/t11.c Normal file
View file

@ -0,0 +1,107 @@
#include "a.h"
/*
* 11. Local Horizontal and Vertical Motions, and the Width Function.
*/
int
e_0(void)
{
/* digit-width space */
return ' ';
}
int
dv(int d)
{
Rune sub[6];
d += getnr(L(".dv"));
nr(L(".dv"), d);
runestrcpy(sub, L("<sub>"));
sub[0] = Ult;
sub[4] = Ugt;
if(d < 0){
sub[3] = 'p';
ihtml(L(".dv"), sub);
}else if(d > 0)
ihtml(L(".dv"), sub);
else
ihtml(L(".dv"), nil);
return 0;
}
int
e_v(void)
{
dv(eval(getqarg()));
return 0;
}
int
e_u(void)
{
dv(eval(L("-0.5m")));
return 0;
}
int
e_d(void)
{
dv(eval(L("0.5m")));
return 0;
}
int
e_r(void)
{
dv(eval(L("-1m")));
return 0;
}
int
e_h(void)
{
getqarg();
return 0;
}
int
e_w(void)
{
Rune *a;
Rune buf[40];
a = getqarg();
runesnprint(buf, sizeof buf, "%ld", runestrlen(a));
pushinputstring(buf);
nr(L("st"), 0);
nr(L("sb"), 0);
nr(L("ct"), 0);
return 0;
}
int
e_k(void)
{
getname();
warn("%Ck not available", backslash);
return 0;
}
void
t11init(void)
{
addesc('|', e_nop, 0);
addesc('^', e_nop, 0);
addesc('v', e_v, 0);
addesc('h', e_h, 0);
addesc('w', e_w, 0);
addesc('0', e_0, 0);
addesc('u', e_u, 0);
addesc('d', e_d, 0);
addesc('r', e_r, 0);
addesc('k', e_k, 0);
}

67
src/cmd/htmlroff/t12.c Normal file
View file

@ -0,0 +1,67 @@
#include "a.h"
/*
* 12. Overstrike, bracket, line-drawing, graphics, and zero-width functions.
*/
/*
\o'asdf'
\zc
\b'asdf'
\l'Nc'
\L'Nc'
\D'xxx'
*/
int
e_o(void)
{
pushinputstring(getqarg());
return 0;
}
int
e_z(void)
{
getnext();
return 0;
}
int
e_b(void)
{
pushinputstring(getqarg());
return 0;
}
int
e_l(void)
{
getqarg();
return 0;
}
int
e_L(void)
{
getqarg();
return 0;
}
int
e_D(void)
{
getqarg();
return 0;
}
void
t12init(void)
{
addesc('o', e_o, 0);
addesc('z', e_z, 0);
addesc('b', e_b, 0);
addesc('l', e_l, 0);
addesc('L', e_L, 0);
addesc('D', e_D, 0);
}

17
src/cmd/htmlroff/t13.c Normal file
View file

@ -0,0 +1,17 @@
#include "a.h"
/*
* 13. Hyphenation.
*/
void
t13init(void)
{
addreq(L("nh"), r_nop, -1);
addreq(L("hy"), r_nop, -1);
addreq(L("hc"), r_nop, -1);
addreq(L("hw"), r_nop, -1);
addesc('%', e_nop, 0);
}

33
src/cmd/htmlroff/t14.c Normal file
View file

@ -0,0 +1,33 @@
#include "a.h"
/*
* 14. Three-part titles.
*/
void
r_lt(int argc, Rune **argv)
{
Rune *p;
if(argc < 2)
nr(L(".lt"), evalscale(L("6.5i"), 'm'));
else{
if(argc > 2)
warn("too many arguments for .lt");
p = argv[1];
if(p[0] == '-')
nr(L(".lt"), getnr(L(".lt"))-evalscale(p+1, 'm'));
else if(p[0] == '+')
nr(L(".lt"), getnr(L(".lt"))+evalscale(p+1, 'm'));
else
nr(L(".lt"), evalscale(p, 'm'));
}
}
void
t14init(void)
{
addreq(L("tl"), r_warn, -1);
addreq(L("pc"), r_nop, -1); /* page number char */
addreq(L("lt"), r_lt, -1);
}

13
src/cmd/htmlroff/t15.c Normal file
View file

@ -0,0 +1,13 @@
#include "a.h"
/*
* 15. Output line numbering.
*/
void
t15init(void)
{
addreq(L("nm"), r_warn, -1);
addreq(L("nn"), r_warn, -1);
}

156
src/cmd/htmlroff/t16.c Normal file
View file

@ -0,0 +1,156 @@
#include "a.h"
/*
* 16. Conditional acceptance of input.
*
* conditions are
* c - condition letter (o, e, t, n)
* !c - not c
* N - N>0
* !N - N <= 0
* 'a'b' - if a==b
* !'a'b' - if a!=b
*
* \{xxx\} can be used for newline in bodies
*
* .if .ie .el
*
*/
int iftrue[20];
int niftrue;
void
startbody(void)
{
int c;
while((c = getrune()) == ' ' || c == '\t')
;
ungetrune(c);
}
void
skipbody(void)
{
int c, cc, nbrace;
nbrace = 0;
for(cc=0; (c = getrune()) >= 0; cc=c){
if(c == '\n' && nbrace <= 0)
break;
if(cc == '\\' && c == '{')
nbrace++;
if(cc == '\\' && c == '}')
nbrace--;
}
}
int
ifeval(void)
{
int c, cc, neg, nc;
Rune line[MaxLine], *p, *e, *q;
Rune *a;
while((c = getnext()) == ' ' || c == '\t')
;
neg = 0;
while(c == '!'){
neg = !neg;
c = getnext();
}
if('0' <= c && c <= '9'){
ungetnext(c);
a = copyarg();
c = (eval(a)>0) ^ neg;
free(a);
return c;
}
switch(c){
case ' ':
case '\n':
ungetnext(c);
return !neg;
case 'o': /* odd page */
case 't': /* troff */
case 'h': /* htmlroff */
while((c = getrune()) != ' ' && c != '\t' && c != '\n' && c >= 0)
;
return 1 ^ neg;
case 'n': /* nroff */
case 'e': /* even page */
while((c = getnext()) != ' ' && c != '\t' && c != '\n' && c >= 0)
;
return 0 ^ neg;
}
/* string comparison 'string1'string2' */
p = line;
e = p+nelem(line);
nc = 0;
q = nil;
while((cc=getnext()) >= 0 && cc != '\n' && p<e){
if(cc == c){
if(++nc == 2)
break;
q = p;
}
*p++ = cc;
}
if(cc != c){
ungetnext(cc);
return 0;
}
if(nc < 2){
return 0;
}
*p = 0;
return (q-line == p-(q+1)
&& memcmp(line, q+1, (q-line)*sizeof(Rune))==0) ^ neg;
}
void
r_if(Rune *name)
{
int n;
n = ifeval();
if(runestrcmp(name, L("ie")) == 0){
if(niftrue >= nelem(iftrue))
sysfatal("%Cie overflow", dot);
iftrue[niftrue++] = n;
}
if(n)
startbody();
else
skipbody();
}
void
r_el(Rune *name)
{
USED(name);
if(niftrue <= 0){
warn("%Cel underflow", dot);
return;
}
if(iftrue[--niftrue])
skipbody();
else
startbody();
}
void
t16init(void)
{
addraw(L("if"), r_if);
addraw(L("ie"), r_if);
addraw(L("el"), r_el);
addesc('{', e_nop, HtmlMode|ArgMode);
addesc('}', e_nop, HtmlMode|ArgMode);
}

131
src/cmd/htmlroff/t17.c Normal file
View file

@ -0,0 +1,131 @@
#include "a.h"
/*
* 17. Environment switching.
*/
typedef struct Env Env;
struct Env
{
int s;
int s0;
int f;
int f0;
int fi;
int ad;
int ce;
int v;
int v0;
int ls;
int ls0;
int it;
/* - ta */
/* - tc */
/* - lc */
/* - ul */
/* - cu */
/* - cc */
/* - c2 */
/* - nh */
/* - hy */
/* - hc */
/* - lt */
/* - nm */
/* - nn */
/* - mc */
};
Env defenv =
{
10,
10,
1,
1,
1,
1,
0,
12,
12,
0,
0,
0,
};
Env env[3];
Env *evstack[20];
int nevstack;
void
saveenv(Env *e)
{
e->s = getnr(L(".s"));
e->s0 = getnr(L(".s0"));
e->f = getnr(L(".f"));
e->f0 = getnr(L(".f0"));
e->fi = getnr(L(".fi"));
e->ad = getnr(L(".ad"));
e->ce = getnr(L(".ce"));
e->v = getnr(L(".v"));
e->v0 = getnr(L(".v0"));
e->ls = getnr(L(".ls"));
e->ls0 = getnr(L(".ls0"));
e->it = getnr(L(".it"));
}
void
restoreenv(Env *e)
{
nr(L(".s"), e->s);
nr(L(".s0"), e->s0);
nr(L(".f"), e->f);
nr(L(".f0"), e->f0);
nr(L(".fi"), e->fi);
nr(L(".ad"), e->ad);
nr(L(".ce"), e->ce);
nr(L(".v"), e->v);
nr(L(".v0"), e->v0);
nr(L(".ls"), e->ls);
nr(L(".ls0"), e->ls0);
nr(L(".it"), e->it);
nr(L(".ev"), e-env);
runmacro1(L("font"));
}
void
r_ev(int argc, Rune **argv)
{
int i;
Env *e;
if(argc == 1){
if(nevstack <= 0){
if(verbose) warn(".ev stack underflow");
return;
}
restoreenv(evstack[--nevstack]);
return;
}
if(nevstack >= nelem(evstack))
sysfatal(".ev stack overflow");
i = eval(argv[1]);
if(i < 0 || i > 2){
warn(".ev bad environment %d", i);
i = 0;
}
e = &env[getnr(L(".ev"))];
saveenv(e);
evstack[nevstack++] = e;
restoreenv(&env[i]);
}
void
t17init(void)
{
int i;
for(i=0; i<nelem(env); i++)
env[i] = defenv;
addreq(L("ev"), r_ev, -1);
}

67
src/cmd/htmlroff/t18.c Normal file
View file

@ -0,0 +1,67 @@
#include "a.h"
/*
* 18. Insertions from the standard input
*/
void
r_rd(int argc, Rune **argv)
{
char *s;
Rune *p;
Fmt fmt;
static int didstdin;
static Biobuf bstdin;
/*
* print prompt, then read until double newline,
* then run the text just read as though it were
* a macro body, using the remaining arguments.
*/
if(isatty(0)){
if(argc > 1)
fprint(2, "%S", argv[1]);
else
fprint(2, "%c", 7/*BEL*/);
}
if(!didstdin){
Binit(&bstdin, 0, OREAD);
didstdin = 1;
}
runefmtstrinit(&fmt);
while((s = Brdstr(&bstdin, '\n', 0)) != nil){
if(s[0] == '\n'){
free(s);
break;
}
fmtprint(&fmt, "%s", s);
free(s);
}
p = runefmtstrflush(&fmt);
if(p == nil)
warn("out of memory in %Crd", dot);
ds(L(".rd"), p);
argc--;
argv++;
argv[0] = L(".rd");
runmacro('.', argc, argv);
ds(L(".rd"), nil);
}
/* terminate exactly as if input had ended */
void
r_ex(int argc, Rune **argv)
{
USED(argc);
USED(argv);
while(popinput())
;
}
void
t18init(void)
{
addreq(L("rd"), r_rd, -1);
addreq(L("ex"), r_ex, 0);
}

142
src/cmd/htmlroff/t19.c Normal file
View file

@ -0,0 +1,142 @@
#include "a.h"
/*
* 19. Input/output file switching.
*/
/* .so - push new source file */
void
r_so(int argc, Rune **argv)
{
USED(argc);
pushinputfile(erunesmprint("%s", unsharp(esmprint("%S", argv[1]))));
}
/* .nx - end this file, switch to arg */
void
r_nx(int argc, Rune **argv)
{
int n;
if(argc == 1){
while(popinput())
;
}else{
if(argc > 2)
warn("too many arguments for .nx");
while((n=popinput()) && n != 2)
;
pushinputfile(argv[1]);
}
}
/* .sy - system: run string */
void
r_sy(Rune *name)
{
USED(name);
warn(".sy not implemented");
}
/* .pi - pipe output to string */
void
r_pi(Rune *name)
{
USED(name);
warn(".pi not implemented");
}
/* .cf - copy contents of filename to output */
void
r_cf(int argc, Rune **argv)
{
int c;
char *p;
Biobuf *b;
USED(argc);
p = esmprint("%S", argv[1]);
if((b = Bopen(p, OREAD)) == nil){
fprint(2, "%L: open %s: %r\n", p);
free(p);
return;
}
free(p);
while((c = Bgetrune(b)) >= 0)
outrune(c);
Bterm(b);
}
void
r_inputpipe(Rune *name)
{
Rune *cmd, *stop, *line;
int n, pid, p[2], len;
Waitmsg *w;
USED(name);
if(pipe(p) < 0){
warn("pipe: %r");
return;
}
stop = copyarg();
cmd = readline(CopyMode);
pid = fork();
switch(pid){
case 0:
if(p[0] != 0){
dup(p[0], 0);
close(p[0]);
}
close(p[1]);
execl(unsharp("#9/bin/rc"), "rc", "-c", esmprint("%S", cmd), nil);
warn("%Cdp %S: %r", dot, cmd);
_exits(nil);
case -1:
warn("fork: %r");
default:
close(p[0]);
len = runestrlen(stop);
fprint(p[1], ".ps %d\n", getnr(L(".s")));
fprint(p[1], ".vs %du\n", getnr(L(".v")));
fprint(p[1], ".ft %d\n", getnr(L(".f")));
fprint(p[1], ".ll 8i\n");
fprint(p[1], ".pl 30i\n");
while((line = readline(~0)) != nil){
if(runestrncmp(line, stop, len) == 0
&& (line[len]==' ' || line[len]==0 || line[len]=='\t'
|| (line[len]=='\\' && line[len+1]=='}')))
break;
n = runestrlen(line);
line[n] = '\n';
fprint(p[1], "%.*S", n+1, line);
free(line);
}
free(stop);
close(p[1]);
w = wait();
if(w == nil){
warn("wait: %r");
return;
}
if(w->msg[0])
sysfatal("%C%S %S: %s", dot, name, cmd, w->msg);
free(cmd);
free(w);
}
}
void
t19init(void)
{
addreq(L("so"), r_so, 1);
addreq(L("nx"), r_nx, -1);
addraw(L("sy"), r_sy);
addraw(L("inputpipe"), r_inputpipe);
addraw(L("pi"), r_pi);
addreq(L("cf"), r_cf, 1);
nr(L("$$"), getpid());
}

274
src/cmd/htmlroff/t2.c Normal file
View file

@ -0,0 +1,274 @@
#include "a.h"
/*
* Section 2 - Font and character size control.
*/
/* 2.1 - Character set */
/* XXX
*
* \C'name' - character named name
* \N'n' - character number
* \(xx - two-letter character
* \-
* \`
* \'
* `
* '
* -
*/
Rune*
getqarg(void)
{
static Rune buf[MaxLine];
int c;
Rune *p, *e;
p = buf;
e = p+sizeof buf-1;
if(getrune() != '\'')
return nil;
while(p < e){
c = getrune();
if(c < 0)
return nil;
if(c == '\'')
break;
*p++ = c;
}
*p = 0;
return buf;
}
int
e_N(void)
{
Rune *a;
if((a = getqarg()) == nil)
goto error;
return eval(a);
error:
warn("malformed %CN'...'", backslash);
return 0;
}
int
e_paren(void)
{
int c, cc;
Rune buf[2], r;
if((c = getrune()) < 0 || c == '\n')
goto error;
if((cc = getrune()) < 0 || cc == '\n')
goto error;
buf[0] = c;
buf[1] = cc;
r = troff2rune(buf);
if(r == Runeerror)
warn("unknown char %C(%C%C", backslash, c, cc);
return r;
error:
warn("malformed %C(xx", backslash);
return 0;
}
/* 2.2 - Fonts */
Rune fonttab[10][100];
/*
* \fx \f(xx \fN - font change
* number register .f - current font
* \f0 previous font (undocumented?)
*/
/* change to font f. also \fx, \f(xx, \fN */
/* .ft LongName is okay - temporarily at fp 0 */
void
ft(Rune *f)
{
int i;
int fn;
if(f && runestrcmp(f, L("P")) == 0)
f = nil;
if(f == nil)
fn = 0;
else if(isdigit(f[0]))
fn = eval(f);
else{
for(i=0; i<nelem(fonttab); i++){
if(runestrcmp(fonttab[i], f) == 0){
fn = i;
goto have;
}
}
warn("unknown font %S", f);
fn = 1;
}
have:
if(fn < 0 || fn >= nelem(fonttab)){
warn("unknown font %d", fn);
fn = 1;
}
if(fn == 0)
fn = getnr(L(".f0"));
nr(L(".f0"), getnr(L(".f")));
nr(L(".f"), fn);
runmacro1(L("font"));
}
/* mount font named f on physical position N */
void
fp(int i, Rune *f)
{
if(i <= 0 || i >= nelem(fonttab)){
warn("bad font position %d", i);
return;
}
runestrecpy(fonttab[i], fonttab[i]+sizeof fonttab[i], f);
}
int
e_f(void)
{
ft(getname());
return 0;
}
void
r_ft(int argc, Rune **argv)
{
if(argc == 1)
ft(nil);
else
ft(argv[1]);
}
void
r_fp(int argc, Rune **argv)
{
if(argc < 3){
warn("missing arguments to %Cfp", dot);
return;
}
fp(eval(argv[1]), argv[2]);
}
/* 2.3 - Character size */
/* \H'±N' sets height */
void
ps(int s)
{
if(s == 0)
s = getnr(L(".s0"));
nr(L(".s0"), getnr(L(".s")));
nr(L(".s"), s);
runmacro1(L("font"));
}
/* set point size */
void
r_ps(int argc, Rune **argv)
{
Rune *p;
if(argc == 1 || argv[1][0] == 0)
ps(0);
else{
p = argv[1];
if(p[0] == '-')
ps(getnr(L(".s"))-eval(p+1));
else if(p[0] == '+')
ps(getnr(L(".s"))+eval(p+1));
else
ps(eval(p));
}
}
int
e_s(void)
{
int c, cc, ccc, n, twodigit;
c = getnext();
if(c < 0)
return 0;
if(c == '+' || c == '-'){
cc = getnext();
if(cc == '('){
cc = getnext();
ccc = getnext();
if(cc < '0' || cc > '9' || ccc < '0' || ccc > '9'){
warn("bad size %Cs%C(%C%C", backslash, c, cc, ccc);
return 0;
}
n = (cc-'0')*10+ccc-'0';
}else{
if(cc < '0' || cc > '9'){
warn("bad size %Cs%C%C", backslash, c, cc);
return 0;
}
n = cc-'0';
}
if(c == '+')
ps(getnr(L(".s"))+n);
else
ps(getnr(L(".s"))-n);
return 0;
}
twodigit = 0;
if(c == '('){
twodigit = 1;
c = getnext();
if(c < 0)
return 0;
}
if(c < '0' || c > '9'){
warn("bad size %Cs%C", backslash, c);
ungetnext(c);
return 0;
}
if(twodigit || (c < '4' && c != '0')){
cc = getnext();
if(c < 0)
return 0;
n = (c-'0')*10+cc-'0';
}else
n = c-'0';
ps(n);
return 0;
}
void
t2init(void)
{
fp(1, L("R"));
fp(2, L("I"));
fp(3, L("B"));
fp(4, L("BI"));
fp(5, L("CW"));
nr(L(".s"), 10);
nr(L(".s0"), 10);
addreq(L("ft"), r_ft, -1);
addreq(L("fp"), r_fp, -1);
addreq(L("ps"), r_ps, -1);
addreq(L("ss"), r_warn, -1);
addreq(L("cs"), r_warn, -1);
addreq(L("bd"), r_warn, -1);
addesc('f', e_f, 0);
addesc('s', e_s, 0);
addesc('(', e_paren, 0); /* ) */
addesc('C', e_warn, 0);
addesc('N', e_N, 0);
/* \- \' \` are handled in html.c */
}

79
src/cmd/htmlroff/t20.c Normal file
View file

@ -0,0 +1,79 @@
#include "a.h"
/*
* 20. Miscellaneous
*/
/* .mc - margin character */
/* .ig - ignore; treated like a macro in t7.c */
/* .pm - print macros and strings */
void
r_pm(int argc, Rune **argv)
{
int i;
if(argc == 1){
printds(0);
return;
}
if(runestrcmp(argv[1], L("t")) == 0){
printds(1);
return;
}
for(i=1; i<argc; i++)
fprint(2, "%S: %S\n", argv[i], getds(argv[i]));
}
void
r_tm(Rune *name)
{
Rune *line;
USED(name);
line = readline(CopyMode);
fprint(2, "%S\n", line);
free(line);
}
void
r_ab(Rune *name)
{
USED(name);
r_tm(L("ab"));
exits(".ab");
}
void
r_lf(int argc, Rune **argv)
{
if(argc == 1)
return;
if(argc == 2)
setlinenumber(nil, eval(argv[1]));
if(argc == 3)
setlinenumber(argv[2], eval(argv[1]));
}
void
r_fl(int argc, Rune **argv)
{
USED(argc);
USED(argv);
Bflush(&bout);
}
void
t20init(void)
{
addreq(L("mc"), r_warn, -1);
addraw(L("tm"), r_tm);
addraw(L("ab"), r_ab);
addreq(L("lf"), r_lf, -1);
addreq(L("pm"), r_pm, -1);
addreq(L("fl"), r_fl, 0);
}

49
src/cmd/htmlroff/t3.c Normal file
View file

@ -0,0 +1,49 @@
#include "a.h"
/*
* Section 3 - page control (mostly irrelevant).
*/
/* page offset */
void
po(int o)
{
nr(L(".o0"), getnr(L(".o")));
nr(L(".o"), o);
}
void
r_po(int argc, Rune **argv)
{
if(argc == 1){
po(getnr(L(".o0")));
return;
}
if(argv[1][0] == '+')
po(getnr(L(".o"))+evalscale(argv[1]+1, 'v'));
else if(argv[1][0] == '-')
po(getnr(L(".o"))-evalscale(argv[1]+1, 'v'));
else
po(evalscale(argv[1], 'v'));
}
/* .ne - need vertical space */
/* .mk - mark current vertical place */
/* .rt - return upward */
void
t3init(void)
{
nr(L(".o"), eval(L("1i")));
nr(L(".o0"), eval(L("1i")));
nr(L(".p"), eval(L("11i")));
addreq(L("pl"), r_warn, -1);
addreq(L("bp"), r_nop, -1);
addreq(L("pn"), r_warn, -1);
addreq(L("po"), r_po, -1);
addreq(L("ne"), r_nop, -1);
addreq(L("mk"), r_nop, -1);
addreq(L("rt"), r_warn, -1);
}

142
src/cmd/htmlroff/t4.c Normal file
View file

@ -0,0 +1,142 @@
#include "a.h"
/*
* 4 - Text filling, centering, and adjusting.
* "\ " - unbreakable space
* .n register - length of last line
* nl register - text baseline position on this page
* .h register - baseline high water mark
* .k register - current horizontal output position
* \p - cause break at end of word, justify
* \& - non-printing zero-width filler
* tr - output translation
* \c - break (but don't) input line in .nf mode
* \c - break (but don't) word in .fi mode
*/
int
e_space(void)
{
return 0xA0; /* non-breaking space */
}
int
e_amp(void)
{
return Uempty;
}
int
e_c(void)
{
getrune();
bol = 1;
return 0;
}
void
r_br(int argc, Rune **argv)
{
USED(argc);
USED(argv);
br();
}
/* fill mode on */
void
r_fi(int argc, Rune **argv)
{
USED(argc);
USED(argv);
nr(L(".fi"), 1);
// warn(".fi");
}
/* no-fill mode */
void
r_nf(int argc, Rune **argv)
{
USED(argc);
USED(argv);
nr(L(".fi"), 0);
}
/* adjust */
void
r_ad(int argc, Rune **argv)
{
int c, n;
nr(L(".j"), getnr(L(".j"))|1);
if(argc < 2)
return;
c = argv[1][0];
switch(c){
default:
fprint(2, "%L: bad adjust %C\n", c);
return;
case 'r':
n = 2*2|1;
break;
case 'l':
n = 0;
break;
case 'c':
n = 1*2|1;
break;
case 'b':
case 'n':
n = 0*2|1;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
n = c-'0';
break;
}
nr(L(".j"), n);
}
/* no adjust */
void
r_na(int argc, Rune **argv)
{
USED(argc);
USED(argv);
nr(L(".j"), getnr(L(".j"))&~1);
}
/* center next N lines */
void
r_ce(int argc, Rune **argv)
{
if(argc < 2)
nr(L(".ce"), 1);
else
nr(L(".ce"), eval(argv[1]));
/* XXX set trap */
}
void
t4init(void)
{
nr(L(".fi"), 1);
nr(L(".j"), 1);
addreq(L("br"), r_br, 0);
addreq(L("fi"), r_fi, 0);
addreq(L("nf"), r_nf, 0);
addreq(L("ad"), r_ad, -1);
addreq(L("na"), r_na, 0);
addreq(L("ce"), r_ce, -1);
addesc(' ', e_space, 0);
addesc('p', e_warn, 0);
addesc('&', e_amp, 0);
addesc('c', e_c, 0);
}

110
src/cmd/htmlroff/t5.c Normal file
View file

@ -0,0 +1,110 @@
#include "a.h"
/*
* 5. Vertical spacing.
*/
/* set vertical baseline spacing */
void
vs(int v)
{
if(v == 0)
v = getnr(L(".v0"));
nr(L(".v0"), getnr(L(".v")));
nr(L(".v"), v);
}
void
r_vs(int argc, Rune **argv)
{
if(argc < 2)
vs(eval(L("12p")));
else if(argv[1][0] == '+')
vs(getnr(L(".v"))+evalscale(argv[1]+1, 'p'));
else if(argv[1][0] == '-')
vs(getnr(L(".v"))-evalscale(argv[1]+1, 'p'));
else
vs(evalscale(argv[1], 'p'));
}
/* set line spacing */
void
ls(int v)
{
if(v == 0)
v = getnr(L(".ls0"));
nr(L(".ls0"), getnr(L(".ls")));
nr(L(".ls"), v);
}
void
r_ls(int argc, Rune **argv)
{
ls(argc < 2 ? 0 : eval(argv[1]));
}
/* .sp - space vertically */
/* .sv - save a contiguous vertical block */
void
sp(int v)
{
Rune buf[100];
double fv;
br();
fv = v * 1.0/UPI;
if(fv > 5)
fv = eval(L("1v")) * 1.0/UPI;
runesnprint(buf, nelem(buf), "<p style=\"margin-top: 0; margin-bottom: %.2fin\"></p>\n", fv);
outhtml(buf);
}
void
r_sp(int argc, Rune **argv)
{
if(getnr(L(".ns")))
return;
if(argc < 2)
sp(eval(L("1v")));
else{
if(argv[1][0] == '|'){
/* XXX if there's no output yet, do the absolute! */
if(verbose)
warn("ignoring absolute .sp %d", eval(argv[1]+1));
return;
}
sp(evalscale(argv[1], 'v'));
}
}
void
r_ns(int argc, Rune **argv)
{
USED(argc);
USED(argv);
nr(L(".ns"), 1);
}
void
r_rs(int argc, Rune **argv)
{
USED(argc);
USED(argv);
nr(L(".ns"), 0);
}
void
t5init(void)
{
addreq(L("vs"), r_vs, -1);
addreq(L("ls"), r_ls, -1);
addreq(L("sp"), r_sp, -1);
addreq(L("sv"), r_sp, -1);
addreq(L("os"), r_nop, -1);
addreq(L("ns"), r_ns, 0);
addreq(L("rs"), r_rs, 0);
nr(L(".v"), eval(L("12p")));
nr(L(".v0"), eval(L("12p")));
nr(L(".ls"), 1);
nr(L(".ls0"), 1);
}

74
src/cmd/htmlroff/t6.c Normal file
View file

@ -0,0 +1,74 @@
#include "a.h"
/*
* Section 6 - line length and indenting.
*/
/* set line length */
void
ll(int v)
{
if(v == 0)
v = getnr(L(".l0"));
nr(L(".l0"), getnr(L(".l")));
nr(L(".l"), v);
}
void
r_ll(int argc, Rune **argv)
{
if(argc < 2)
ll(0);
else if(argv[1][0] == '+')
ll(getnr(L(".l"))+evalscale(argv[1]+1, 'v'));
else if(argv[1][0] == '-')
ll(getnr(L(".l"))-evalscale(argv[1]+1, 'v'));
else
ll(evalscale(argv[1], 'm'));
if(argc > 2)
warn("extra arguments to .ll");
}
void
in(int v)
{
nr(L(".i0"), getnr(L(".i")));
nr(L(".i"), v);
/* XXX */
}
void
r_in(int argc, Rune **argv)
{
if(argc < 2)
in(getnr(L(".i0")));
else if(argv[1][0] == '+')
in(getnr(L(".i"))+evalscale(argv[1]+1, 'm'));
else if(argv[1][0] == '-')
in(getnr(L(".i"))-evalscale(argv[1]+1, 'm'));
else
in(evalscale(argv[1], 'm'));
if(argc > 3)
warn("extra arguments to .in");
}
void
ti(int v)
{
nr(L(".ti"), v);
}
void
r_ti(int argc, Rune **argv)
{
USED(argc);
ti(evalscale(argv[1], 'm'));
}
void
t6init(void)
{
addreq(L("ll"), r_ll, -1);
addreq(L("in"), r_in, -1);
addreq(L("ti"), r_ti, 1);
nr(L(".l"), eval(L("6.5i")));
}

543
src/cmd/htmlroff/t7.c Normal file
View file

@ -0,0 +1,543 @@
/*
* 7. Macros, strings, diversion, and position traps.
*
* macros can override builtins
* builtins can be renamed or removed!
*/
#include "a.h"
enum
{
MAXARG = 10,
MAXMSTACK = 40
};
/* macro invocation frame */
typedef struct Mac Mac;
struct Mac
{
int argc;
Rune *argv[MAXARG];
};
Mac mstack[MAXMSTACK];
int nmstack;
void emitdi(void);
void flushdi(void);
/*
* Run a user-defined macro.
*/
void popmacro(void);
int
runmacro(int dot, int argc, Rune **argv)
{
Rune *p;
int i;
Mac *m;
if(verbose && isupperrune(argv[0][0])) fprint(2, "run: %S\n", argv[0]);
p = getds(argv[0]);
if(p == nil){
if(verbose)
warn("ignoring unknown request %C%S", dot, argv[0]);
if(verbose > 1){
for(i=0; i<argc; i++)
fprint(2, " %S", argv[i]);
fprint(2, "\n");
}
return -1;
}
if(nmstack >= nelem(mstack)){
fprint(2, "%L: macro stack overflow:");
for(i=0; i<nmstack; i++)
fprint(2, " %S", mstack[i].argv[0]);
fprint(2, "\n");
return -1;
}
m = &mstack[nmstack++];
m->argc = argc;
for(i=0; i<argc; i++)
m->argv[i] = erunestrdup(argv[i]);
pushinputstring(p);
nr(L(".$"), argc-1);
inputnotify(popmacro);
return 0;
}
void
popmacro(void)
{
int i;
Mac *m;
if(--nmstack < 0){
fprint(2, "%L: macro stack underflow\n");
return;
}
m = &mstack[nmstack];
for(i=0; i<m->argc; i++)
free(m->argv[i]);
if(nmstack > 0)
nr(L(".$"), mstack[nmstack-1].argc-1);
else
nr(L(".$"), 0);
}
void popmacro1(void);
jmp_buf runjb[10];
int nrunjb;
void
runmacro1(Rune *name)
{
Rune *argv[2];
int obol;
if(verbose) fprint(2, "outcb %p\n", outcb);
obol = bol;
argv[0] = name;
argv[1] = nil;
bol = 1;
if(runmacro('.', 1, argv) >= 0){
inputnotify(popmacro1);
if(!setjmp(runjb[nrunjb++]))
runinput();
else
if(verbose) fprint(2, "finished %S\n", name);
}
bol = obol;
}
void
popmacro1(void)
{
popmacro();
if(nrunjb >= 0)
longjmp(runjb[--nrunjb], 1);
}
/*
* macro arguments
*
* "" means " inside " "
* "" empty string
* \newline can be done
* argument separator is space (not tab)
* number register .$ = number of arguments
* no arguments outside macros or in strings
*
* arguments copied in copy mode
*/
/*
* diversions
*
* processed output diverted
* dn dl registers vertical and horizontal size of last diversion
* .z - current diversion name
*/
/*
* traps
*
* skip most
* .t register - distance to next trap
*/
static Rune *trap0;
void
outtrap(void)
{
Rune *t;
if(outcb)
return;
if(trap0){
if(verbose) fprint(2, "trap: %S\n", trap0);
t = trap0;
trap0 = nil;
runmacro1(t);
free(t);
}
}
/* .wh - install trap */
void
r_wh(int argc, Rune **argv)
{
int i;
if(argc < 2)
return;
i = eval(argv[1]);
if(argc == 2){
if(i == 0){
free(trap0);
trap0 = nil;
}else
if(verbose)
warn("not removing trap at %d", i);
}
if(argc > 2){
if(i == 0){
free(trap0);
trap0 = erunestrdup(argv[2]);
}else
if(verbose)
warn("not installing %S trap at %d", argv[2], i);
}
}
void
r_ch(int argc, Rune **argv)
{
int i;
if(argc == 2){
if(trap0 && runestrcmp(argv[1], trap0) == 0){
free(trap0);
trap0 = nil;
}else
if(verbose)
warn("not removing %S trap", argv[1]);
return;
}
if(argc >= 3){
i = eval(argv[2]);
if(i == 0){
free(trap0);
trap0 = erunestrdup(argv[1]);
}else
if(verbose)
warn("not moving %S trap to %d", argv[1], i);
}
}
void
r_dt(int argc, Rune **argv)
{
USED(argc);
USED(argv);
warn("ignoring diversion trap");
}
/* define macro - .de, .am, .ig */
void
r_de(int argc, Rune **argv)
{
Rune *end, *p;
Fmt fmt;
int ignore, len;
delreq(argv[1]);
delraw(argv[1]);
ignore = runestrcmp(argv[0], L("ig")) == 0;
if(!ignore)
runefmtstrinit(&fmt);
end = L("..");
if(argc >= 3)
end = argv[2];
if(runestrcmp(argv[0], L("am")) == 0 && (p=getds(argv[1])) != nil)
fmtrunestrcpy(&fmt, p);
len = runestrlen(end);
while((p = readline(CopyMode)) != nil){
if(runestrncmp(p, end, len) == 0
&& (p[len]==' ' || p[len]==0 || p[len]=='\t'
|| (p[len]=='\\' && p[len+1]=='}'))){
free(p);
goto done;
}
if(!ignore)
fmtprint(&fmt, "%S\n", p);
free(p);
}
warn("eof in %C%S %S - looking for %#Q", dot, argv[0], argv[1], end);
done:
if(ignore)
return;
p = runefmtstrflush(&fmt);
if(p == nil)
sysfatal("out of memory");
ds(argv[1], p);
free(p);
}
/* define string .ds .as */
void
r_ds(Rune *cmd)
{
Rune *name, *line, *p;
name = copyarg();
line = readline(CopyMode);
if(name == nil || line == nil){
free(name);
return;
}
p = line;
if(*p == '"')
p++;
if(cmd[0] == 'd')
ds(name, p);
else
as(name, p);
free(name);
free(line);
}
/* remove request, macro, or string */
void
r_rm(int argc, Rune **argv)
{
int i;
emitdi();
for(i=1; i<argc; i++){
delreq(argv[i]);
delraw(argv[i]);
ds(argv[i], nil);
}
}
/* .rn - rename request, macro, or string */
void
r_rn(int argc, Rune **argv)
{
USED(argc);
renreq(argv[1], argv[2]);
renraw(argv[1], argv[2]);
ds(argv[2], getds(argv[1]));
ds(argv[1], nil);
}
/* .di - divert output to macro xx */
/* .da - divert, appending to macro */
/* page offsetting is not done! */
Fmt difmt;
int difmtinit;
Rune di[20][100];
int ndi;
void
emitdi(void)
{
flushdi();
runefmtstrinit(&difmt);
difmtinit = 1;
fmtrune(&difmt, Uformatted);
}
void
flushdi(void)
{
int n;
Rune *p;
if(ndi == 0 || difmtinit == 0)
return;
fmtrune(&difmt, Uunformatted);
p = runefmtstrflush(&difmt);
memset(&difmt, 0, sizeof difmt);
difmtinit = 0;
if(p == nil)
warn("out of memory in diversion %C%S", dot, di[ndi-1]);
else{
n = runestrlen(p);
if(n > 0 && p[n-1] != '\n'){
p = runerealloc(p, n+2);
p[n] = '\n';
p[n+1] = 0;
}
}
as(di[ndi-1], p);
free(p);
}
void
outdi(Rune r)
{
if(!difmtinit) abort();
if(r == Uempty)
return;
fmtrune(&difmt, r);
}
/* .di, .da */
void
r_di(int argc, Rune **argv)
{
br();
if(argc > 2)
warn("extra arguments to %C%S", dot, argv[0]);
if(argc == 1){
/* end diversion */
if(ndi <= 0){
// warn("unmatched %C%S", dot, argv[0]);
return;
}
flushdi();
if(--ndi == 0){
_nr(L(".z"), nil);
outcb = nil;
}else{
_nr(L(".z"), di[ndi-1]);
runefmtstrinit(&difmt);
fmtrune(&difmt, Uformatted);
difmtinit = 1;
}
return;
}
/* start diversion */
/* various register state should be saved, but it's all useless to us */
flushdi();
if(ndi >= nelem(di))
sysfatal("%Cdi overflow", dot);
if(argv[0][1] == 'i')
ds(argv[1], nil);
_nr(L(".z"), argv[1]);
runestrcpy(di[ndi++], argv[1]);
runefmtstrinit(&difmt);
fmtrune(&difmt, Uformatted);
difmtinit = 1;
outcb = outdi;
}
/* .wh - install trap */
/* .ch - change trap */
/* .dt - install diversion trap */
/* set input-line count trap */
int itrapcount;
int itrapwaiting;
Rune *itrapname;
void
r_it(int argc, Rune **argv)
{
if(argc < 3){
itrapcount = 0;
return;
}
itrapcount = eval(argv[1]);
free(itrapname);
itrapname = erunestrdup(argv[2]);
}
void
itrap(void)
{
itrapset();
if(itrapwaiting){
itrapwaiting = 0;
runmacro1(itrapname);
}
}
void
itrapset(void)
{
if(itrapcount > 0 && --itrapcount == 0)
itrapwaiting = 1;
}
/* .em - invoke macro when all input is over */
void
r_em(int argc, Rune **argv)
{
Rune buf[20];
USED(argc);
runesnprint(buf, nelem(buf), ".%S\n", argv[1]);
as(L("eof"), buf);
}
int
e_star(void)
{
Rune *p;
p = getds(getname());
if(p)
pushinputstring(p);
return 0;
}
int
e_t(void)
{
if(inputmode&CopyMode)
return '\t';
return 0;
}
int
e_a(void)
{
if(inputmode&CopyMode)
return '\a';
return 0;
}
int
e_backslash(void)
{
if(inputmode&ArgMode)
ungetrune('\\');
return backslash;
}
int
e_dot(void)
{
return '.';
}
int
e_dollar(void)
{
int c;
c = getnext();
if(c < '1' || c > '9'){
ungetnext(c);
return 0;
}
c -= '0';
if(nmstack <= 0 || mstack[nmstack-1].argc <= c)
return 0;
pushinputstring(mstack[nmstack-1].argv[c]);
return 0;
}
void
t7init(void)
{
addreq(L("de"), r_de, -1);
addreq(L("am"), r_de, -1);
addreq(L("ig"), r_de, -1);
addraw(L("ds"), r_ds);
addraw(L("as"), r_ds);
addreq(L("rm"), r_rm, -1);
addreq(L("rn"), r_rn, -1);
addreq(L("di"), r_di, -1);
addreq(L("da"), r_di, -1);
addreq(L("it"), r_it, -1);
addreq(L("em"), r_em, 1);
addreq(L("wh"), r_wh, -1);
addreq(L("ch"), r_ch, -1);
addreq(L("dt"), r_dt, -1);
addesc('$', e_dollar, CopyMode|ArgMode|HtmlMode);
addesc('*', e_star, CopyMode|ArgMode|HtmlMode);
addesc('t', e_t, CopyMode|ArgMode);
addesc('a', e_a, CopyMode|ArgMode);
addesc('\\', e_backslash, ArgMode|CopyMode);
addesc('.', e_dot, CopyMode|ArgMode);
ds(L("eof"), L(".sp 0.5i\n"));
ds(L(".."), L(""));
}

449
src/cmd/htmlroff/t8.c Normal file
View file

@ -0,0 +1,449 @@
#include "a.h"
/*
* 8. Number Registers
* (Reg register implementation is also here.)
*/
/*
* \nx N
* \n(xx N
* \n+x N+=M
* \n-x N-=M
*
* .nr R ±N M
* .af R c
*
* formats
* 1 0, 1, 2, 3, ...
* 001 001, 002, 003, ...
* i 0, i, ii, iii, iv, v, ...
* I 0, I, II, III, IV, V, ...
* a 0, a, b, ..., aa, ab, ..., zz, aaa, ...
* A 0, A, B, ..., AA, AB, ..., ZZ, AAA, ...
*
* \gx \g(xx return format of number register
*
* .rr R
*/
typedef struct Reg Reg;
struct Reg
{
Reg *next;
Rune *name;
Rune *val;
Rune *fmt;
int inc;
};
Reg *dslist;
Reg *nrlist;
/*
* Define strings and numbers.
*/
void
dsnr(Rune *name, Rune *val, Reg **l)
{
Reg *s;
for(s = *l; s != nil; s = *l){
if(runestrcmp(s->name, name) == 0)
break;
l = &s->next;
}
if(val == nil){
if(s){
*l = s->next;
free(s->val);
free(s->fmt);
free(s);
}
return;
}
if(s == nil){
s = emalloc(sizeof(Reg));
*l = s;
s->name = erunestrdup(name);
}else
free(s->val);
s->val = erunestrdup(val);
}
Rune*
getdsnr(Rune *name, Reg *list)
{
Reg *s;
for(s=list; s; s=s->next)
if(runestrcmp(name, s->name) == 0)
return s->val;
return nil;
}
void
ds(Rune *name, Rune *val)
{
dsnr(name, val, &dslist);
}
void
as(Rune *name, Rune *val)
{
Rune *p, *q;
p = getds(name);
if(p == nil)
p = L("");
q = runemalloc(runestrlen(p)+runestrlen(val)+1);
runestrcpy(q, p);
runestrcat(q, val);
ds(name, q);
free(q);
}
Rune*
getds(Rune *name)
{
return getdsnr(name, dslist);
}
void
printds(int t)
{
int n, total;
Reg *s;
total = 0;
for(s=dslist; s; s=s->next){
if(s->val)
n = runestrlen(s->val);
else
n = 0;
total += n;
if(!t)
fprint(2, "%S\t%d\n", s->name, n);
}
fprint(2, "total\t%d\n", total);
}
void
nr(Rune *name, int val)
{
Rune buf[20];
runesnprint(buf, nelem(buf), "%d", val);
_nr(name, buf);
}
void
af(Rune *name, Rune *fmt)
{
Reg *s;
if(_getnr(name) == nil)
_nr(name, L("0"));
for(s=nrlist; s; s=s->next)
if(runestrcmp(s->name, name) == 0)
s->fmt = erunestrdup(fmt);
}
Rune*
getaf(Rune *name)
{
Reg *s;
for(s=nrlist; s; s=s->next)
if(runestrcmp(s->name, name) == 0)
return s->fmt;
return nil;
}
void
printnr(void)
{
Reg *r;
for(r=nrlist; r; r=r->next)
fprint(2, "%S %S %d\n", r->name, r->val, r->inc);
}
/*
* Some internal number registers are actually strings,
* so provide _ versions to get at them.
*/
void
_nr(Rune *name, Rune *val)
{
dsnr(name, val, &nrlist);
}
Rune*
_getnr(Rune *name)
{
return getdsnr(name, nrlist);
}
int
getnr(Rune *name)
{
Rune *p;
p = _getnr(name);
if(p == nil)
return 0;
return eval(p);
}
/* new register */
void
r_nr(int argc, Rune **argv)
{
Reg *s;
if(argc < 2)
return;
if(argc < 3)
nr(argv[1], 0);
else{
if(argv[2][0] == '+')
nr(argv[1], getnr(argv[1])+eval(argv[2]+1));
else if(argv[2][0] == '-')
nr(argv[1], getnr(argv[1])-eval(argv[2]+1));
else
nr(argv[1], eval(argv[2]));
}
if(argc > 3){
for(s=nrlist; s; s=s->next)
if(runestrcmp(s->name, argv[1]) == 0)
s->inc = eval(argv[3]);
}
}
/* assign format */
void
r_af(int argc, Rune **argv)
{
USED(argc);
af(argv[1], argv[2]);
}
/* remove register */
void
r_rr(int argc, Rune **argv)
{
int i;
for(i=1; i<argc; i++)
_nr(argv[i], nil);
}
/* fmt integer in base 26 */
void
alpha(Rune *buf, int n, int a)
{
int i, v;
i = 1;
for(v=n; v>0; v/=26)
i++;
if(i == 0)
i = 1;
buf[i] = 0;
while(i > 0){
buf[--i] = a+n%26;
n /= 26;
}
}
struct romanv {
char *s;
int v;
} romanv[] =
{
"m", 1000,
"cm", 900,
"d", 500,
"cd", 400,
"c", 100,
"xc", 90,
"l", 50,
"xl", 40,
"x", 10,
"ix", 9,
"v", 5,
"iv", 4,
"i", 1
};
/* fmt integer in roman numerals! */
void
roman(Rune *buf, int n, int upper)
{
Rune *p;
char *q;
struct romanv *r;
if(upper)
upper = 'A' - 'a';
if(n >= 5000 || n <= 0){
runestrcpy(buf, L("-"));
return;
}
p = buf;
r = romanv;
while(n > 0){
while(n >= r->v){
for(q=r->s; *q; q++)
*p++ = *q + upper;
n -= r->v;
}
r++;
}
*p = 0;
}
Rune*
getname(void)
{
int i, c, cc;
static Rune buf[100];
/* XXX add [name] syntax as in groff */
c = getnext();
if(c < 0)
return L("");
if(c == '\n'){
warn("newline in name\n");
ungetnext(c);
return L("");
}
if(c == '['){
for(i=0; i<nelem(buf)-1; i++){
if((c = getrune()) < 0)
return L("");
if(c == ']'){
buf[i] = 0;
return buf;
}
buf[i] = c;
}
return L("");
}
if(c != '('){
buf[0] = c;
buf[1] = 0;
return buf;
}
c = getnext();
cc = getnext();
if(c < 0 || cc < 0)
return L("");
if(c == '\n' | cc == '\n'){
warn("newline in \\n");
ungetnext(cc);
if(c == '\n')
ungetnext(c);
}
buf[0] = c;
buf[1] = cc;
buf[2] = 0;
return buf;
}
/* \n - return number register */
int
e_n(void)
{
int inc, v, l;
Rune *name, *fmt, buf[100];
Reg *s;
inc = getnext();
if(inc < 0)
return -1;
if(inc != '+' && inc != '-'){
ungetnext(inc);
inc = 0;
}
name = getname();
if(_getnr(name) == nil)
_nr(name, L("0"));
for(s=nrlist; s; s=s->next){
if(runestrcmp(s->name, name) == 0){
if(s->fmt == nil && !inc && s->val[0]){
/* might be a string! */
pushinputstring(s->val);
return 0;
}
v = eval(s->val);
if(inc){
if(inc == '+')
v += s->inc;
else
v -= s->inc;
runesnprint(buf, nelem(buf), "%d", v);
free(s->val);
s->val = erunestrdup(buf);
}
fmt = s->fmt;
if(fmt == nil)
fmt = L("1");
switch(fmt[0]){
case 'i':
case 'I':
roman(buf, v, fmt[0]=='I');
break;
case 'a':
case 'A':
alpha(buf, v, fmt[0]);
break;
default:
l = runestrlen(fmt);
if(l == 0)
l = 1;
runesnprint(buf, sizeof buf, "%0*d", l, v);
break;
}
pushinputstring(buf);
return 0;
}
}
pushinputstring(L(""));
return 0;
}
/* \g - number register format */
int
e_g(void)
{
Rune *p;
p = getaf(getname());
if(p == nil)
p = L("1");
pushinputstring(p);
return 0;
}
void
r_pnr(int argc, Rune **argv)
{
USED(argc);
USED(argv);
printnr();
}
void
t8init(void)
{
addreq(L("nr"), r_nr, -1);
addreq(L("af"), r_af, 2);
addreq(L("rr"), r_rr, -1);
addreq(L("pnr"), r_pnr, 0);
addesc('n', e_n, CopyMode|ArgMode|HtmlMode);
addesc('g', e_g, 0);
}

6
src/cmd/htmlroff/t9.c Normal file
View file

@ -0,0 +1,6 @@
/*
* 9. Tabs, leaders, and fields.
*/
XXX

123
src/cmd/htmlroff/util.c Normal file
View file

@ -0,0 +1,123 @@
#include "a.h"
void*
emalloc(uint n)
{
void *v;
v = mallocz(n, 1);
if(v == nil)
sysfatal("out of memory");
return v;
}
char*
estrdup(char *s)
{
char *t;
t = strdup(s);
if(t == nil)
sysfatal("out of memory");
return t;
}
Rune*
erunestrdup(Rune *s)
{
Rune *t;
t = emalloc(sizeof(Rune)*(runestrlen(s)+1));
if(t == nil)
sysfatal("out of memory");
runestrcpy(t, s);
return t;
}
void*
erealloc(void *ov, uint n)
{
void *v;
v = realloc(ov, n);
if(v == nil)
sysfatal("out of memory");
return v;
}
Rune*
erunesmprint(char *fmt, ...)
{
Rune *s;
va_list arg;
va_start(arg, fmt);
s = runevsmprint(fmt, arg);
va_end(arg);
if(s == nil)
sysfatal("out of memory");
return s;
}
char*
esmprint(char *fmt, ...)
{
char *s;
va_list arg;
va_start(arg, fmt);
s = vsmprint(fmt, arg);
va_end(arg);
if(s == nil)
sysfatal("out of memory");
return s;
}
void
warn(char *fmt, ...)
{
va_list arg;
fprint(2, "htmlroff: %L: ");
va_start(arg, fmt);
vfprint(2, fmt, arg);
va_end(arg);
fprint(2, "\n");
}
/*
* For non-Unicode compilers, so we can say
* L("asdf") and get a Rune string. Assumes strings
* are identified by their pointers, so no mutable strings!
*/
typedef struct Lhash Lhash;
struct Lhash
{
char *s;
Lhash *next;
Rune r[1];
};
static Lhash *hash[1127];
Rune*
L(char *s)
{
Rune *p;
Lhash *l;
uint h;
h = (uintptr)s%nelem(hash);
for(l=hash[h]; l; l=l->next)
if(l->s == s)
return l->r;
l = emalloc(sizeof *l+(utflen(s)+1)*sizeof(Rune));
p = l->r;
l->s = s;
while(*s)
s += chartorune(p++, s);
*p = 0;
l->next = hash[h];
hash[h] = l;
return l->r;
}

94
tmac/tmac.html Normal file
View file

@ -0,0 +1,94 @@
.de HTML
\! \<?xml version="1.0" encoding="utf-8"?\>
\! \<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
\! "http://www.w3.org/TR/html4/loose.dtd"\>
.html html <html>
.html head <head>
.if !'\\$1'' .html title <title>\\$1</title>
.HEAD
.html head
.html body <body>
..
.de FSFIRST
.de NOTES xx
._NOTES
.rm _NOTES
xx
.em NOTES
.da _NOTES
.sp
.B "Notes
.sp
.da
..
.de FS
.FSFIRST
.rm FSFIRST
.da _NOTES
..
.de FE
.sp
.da
..
.nr png -1 1
.de TS
.ds pngbase "\\*[basename]
.if '\\*[pngbase]'' .ds pngbase \\n(.B
.ds pngfile \\*[pngbase]\\n+[png].png
.html - <center><img src="\\*[pngfile]"></center>
.\" The .inputpipe must be the last line of the macro!
.inputpipe .TE troff2png >\\*[pngfile]
..
.de TE
..
.de PS
.ds pngbase "\\*[basename]
.if '\\*[pngbase]'' .ds pngbase \\n(.B
.ds pngfile \\*[pngbase]\\n+[png].png
.html - <center><img src="\\*[pngfile]"></center>
.inputpipe .PE troff2png >\\*[pngfile]
..
.de PE
..
.de B1
.margin 0
.nr TW 10
.nr TW1 80
.if !'\\$1'' .nr TW \\$1
.if !'\\$2'' .nr TW1 \\$2
.html box \
<center>\
<table width=\\n[TW1]% cellspacing=0 cellpadding=0 border=0>\
<tr height=1>\
<td width=1 bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td width=1 bgcolor=#000000 />\
</tr>\
<tr height=\\n(TW>\
<td width=1 bgcolor=#000000 />\
<td width=\\n(TW />\
<td />\
<td width=\\n(TW />\
<td width=1 bgcolor=#000000 />\
</tr>
.html box0 <tr>
.html box1 <td width=1 bgcolor=#000000 /><td width=\\n(TW /><td>
..
.de B2
.html box1 <td width=\\n(TW /><td width=1 bgcolor=#000000 />
.html box0 <tr height=\\n(TW><td width=1 bgcolor=#000000 />\
<td width=\\n(TW /><td /><td width=\\n(TW />\
<td width=1 bgcolor=#000000 />\
</tr>\
<tr height=1>\
<td width=1 bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td width=1 bgcolor=#000000 />\
</tr>
.html box
.margin 1
..

View file

@ -308,9 +308,11 @@
.di WT .di WT
.na .na
.fi .fi
.ie h .ll \\n(LLu
.el \{\
.ll 5.0i .ll 5.0i
.if n .if \\n(TN .ll 29 .if n .if \\n(TN .ll 29
.if t .if \\n(TN .ll 3.5i .if t .if \\n(TN .ll 3.5i \}
.ft 3 .ft 3
.ps \\n(PS .ps \\n(PS
.if !\\n(TN \{\ .if !\\n(TN \{\
@ -318,6 +320,7 @@
. vs \\n(.s+2 . vs \\n(.s+2
. rm CS\} . rm CS\}
.hy 0 .hy 0
.if h .ce 999
.. ..
.de TX .de TX
.rs .rs
@ -358,6 +361,7 @@
. ft 3 . ft 3
. ll 16\}\} . ll 16\}\}
.ps \\n(PS .ps \\n(PS
.if h .ce 999
.. ..
.de AX .de AX
.ft 1 .ft 1
@ -503,6 +507,7 @@ ABSTRACT
.ie \\n(VS>=41 .vs \\n(VSu .ie \\n(VS>=41 .vs \\n(VSu
.el .vs \\n(VSp .el .vs \\n(VSp
.ti +\\n(PIu .ti +\\n(PIu
.fi
.. ..
. \"AE - end of an abstract . \"AE - end of an abstract
.de AE .de AE
@ -704,6 +709,14 @@ Computing Science Technical Report No. \\*(MN
.if \\$1H .TQ .if \\$1H .TQ
.nr IX 1 .nr IX 1
.. ..
.if h \{\
.de TS
.nr tp -1 1
.ds tp x\\n+(tp.png
.html - <center><img src="\\*(tp"></center>
.dp .TE troff2png >\\*(tp
..
.\}
.de TQ .de TQ
.di TT .di TT
.nr IT 1 .nr IT 1
@ -818,9 +831,18 @@ Computing Science Technical Report No. \\*(MN
.in .in
.if \\n($1>0 .sp .65 .if \\n($1>0 .sp .65
.. ..
.if h \{\
.de PS
.nr tp -1 1
.ds tp x\\n+(tp.png
.html - <center><img src="\\*(tp" /></center>
.dp .PE troff2png >\\*(tp
..
.\}
. \" .P1/.P2 macros for programs . \" .P1/.P2 macros for programs
. .
.nr XP 1 \" delta point size for program .nr XP 1 \" delta point size for program
.if h .nr XP 0
.nr XV 1p \" delta vertical for programs .nr XV 1p \" delta vertical for programs
.nr XT 8 \" delta tab stop for programs .nr XT 8 \" delta tab stop for programs
.nr DV .5v \" space before start of program .nr DV .5v \" space before start of program
@ -832,10 +854,11 @@ Computing Science Technical Report No. \\*(MN
.br .br
.nr v \\n(.v .nr v \\n(.v
.di p1 .di p1
.in \\n(P1u .in +\\n(P1u
.nf .nf
.ps -\\n(XP .ps -\\n(XP
.vs -\\n(XVu .vs -\\n(XVu
.nr xx \\n(.sp
.ft CW .ft CW
.nr t \\n(XT*\\w'x'u .nr t \\n(XT*\\w'x'u
.ta 1u*\\ntu 2u*\\ntu 3u*\\ntu 4u*\\ntu 5u*\\ntu 6u*\\ntu 7u*\\ntu 8u*\\ntu 9u*\\ntu 10u*\\ntu 11u*\\ntu 12u*\\ntu 13u*\\ntu 14u*\\ntu .ta 1u*\\ntu 2u*\\ntu 3u*\\ntu 4u*\\ntu 5u*\\ntu 6u*\\ntu 7u*\\ntu 8u*\\ntu 9u*\\ntu 10u*\\ntu 11u*\\ntu 12u*\\ntu 13u*\\ntu 14u*\\ntu
@ -1565,6 +1588,50 @@ operating system\\$1
.if \\n(BQ .fi .if \\n(BQ .fi
.br .br
.. ..
.if h \{\
.de B1
.margin 0
.nr TW 10
.nr TW1 80
.if !'\\$1'' .nr TW \\$1
.if !'\\$2'' .nr TW1 \\$2
.html pic \
<center>\
<table width=\\n[TW1]% cellspacing=0 cellpadding=0 border=0>\
<tr height=1>\
<td width=1 bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td width=1 bgcolor=#000000 />\
</tr>\
<tr height=\\n(TW>\
<td width=1 bgcolor=#000000 />\
<td width=\\n(TW />\
<td />\
<td width=\\n(TW />\
<td width=1 bgcolor=#000000 />\
</tr>
.html pic0 <tr>
.html pic1 <td width=1 bgcolor=#000000 /><td width=\\n(TW /><td>\}
..
.de B2
.html pic1 <td width=\\n(TW /><td width=1 bgcolor=#000000 />
.html pic0 <tr height=\\n(TW><td width=1 bgcolor=#000000 />\
<td width=\\n(TW /><td /><td width=\\n(TW />\
<td width=1 bgcolor=#000000 />\
</tr>\
<tr height=1>\
<td width=1 bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td bgcolor=#000000 />\
<td width=\\n(TW bgcolor=#000000 />\
<td width=1 bgcolor=#000000 />\
</tr>
.html pic \}
.margin 1
..
.\}
.de AT .de AT
.nf .nf
.sp .sp

View file

@ -71,12 +71,14 @@
.in 0 .in 0
.ls 1 .ls 1
.if \\n(TB=0 .ev .if \\n(TB=0 .ev
.if \\n(TB=0 .KX
.if \\n(TB=0 .br .if \\n(TB=0 .br
.if \\n(TB=0 .ev 2 .if \\n(TB=0 .ev 2
.if \\n(TB=0 .KK .if \\n(TB=0 .KK
.ls .ls
.ce 0 .ce 0
.if \\n(TB=0 .rm KK .if \\n(TB=0 .rm KK
.if \\n(TB=0 .KY
.if \\n(TB .da KJ .if \\n(TB .da KJ
.if \\n(TB \!.KD \\n(dn \\n(KV .if \\n(TB \!.KD \\n(dn \\n(KV
.if \\n(TB .KK .if \\n(TB .KK