1223a736eSDavid du Colombier #include <u.h>
2223a736eSDavid du Colombier #include <libc.h>
3223a736eSDavid du Colombier #include <bio.h>
4223a736eSDavid du Colombier
5223a736eSDavid du Colombier enum{
6223a736eSDavid du Colombier Nfont = 11,
7223a736eSDavid du Colombier Wid = 20, /* tmac.anhtml sets page width to 20" so we can recognize .nf text */
8223a736eSDavid du Colombier };
9223a736eSDavid du Colombier
1073e742d7SDavid du Colombier typedef uintptr Char;
11223a736eSDavid du Colombier typedef struct Troffchar Troffchar;
12223a736eSDavid du Colombier typedef struct Htmlchar Htmlchar;
13223a736eSDavid du Colombier typedef struct Font Font;
14223a736eSDavid du Colombier typedef struct HTMLfont HTMLfont;
15223a736eSDavid du Colombier
1673e742d7SDavid du Colombier /*
1773e742d7SDavid du Colombier * a Char is >= 32 bits. low 16 bits are the rune. higher are attributes.
1873e742d7SDavid du Colombier * must be able to hold a pointer.
1973e742d7SDavid du Colombier */
20223a736eSDavid du Colombier enum
21223a736eSDavid du Colombier {
22223a736eSDavid du Colombier Italic = 16,
230fb05a45SDavid du Colombier Bold,
240fb05a45SDavid du Colombier CW,
250fb05a45SDavid du Colombier Indent1,
260fb05a45SDavid du Colombier Indent2,
270fb05a45SDavid du Colombier Indent3,
28223a736eSDavid du Colombier Heading = 25,
29223a736eSDavid du Colombier Anchor = 26, /* must be last */
30223a736eSDavid du Colombier };
31223a736eSDavid du Colombier
320fb05a45SDavid du Colombier enum /* magic emissions */
330fb05a45SDavid du Colombier {
340fb05a45SDavid du Colombier Estring = 0,
350fb05a45SDavid du Colombier Epp = 1<<16,
360fb05a45SDavid du Colombier };
370fb05a45SDavid du Colombier
380fb05a45SDavid du Colombier int attrorder[] = { Indent1, Indent2, Indent3, Heading, Anchor, Italic, Bold, CW };
390fb05a45SDavid du Colombier
400fb05a45SDavid du Colombier int nest[10];
410fb05a45SDavid du Colombier int nnest;
420fb05a45SDavid du Colombier
43223a736eSDavid du Colombier struct Troffchar
44223a736eSDavid du Colombier {
45223a736eSDavid du Colombier char *name;
46223a736eSDavid du Colombier char *value;
47223a736eSDavid du Colombier };
48223a736eSDavid du Colombier
49223a736eSDavid du Colombier struct Htmlchar
50223a736eSDavid du Colombier {
510fb05a45SDavid du Colombier char *utf;
52223a736eSDavid du Colombier char *name;
530fb05a45SDavid du Colombier int value;
54223a736eSDavid du Colombier };
55223a736eSDavid du Colombier
56223a736eSDavid du Colombier #include "chars.h"
57223a736eSDavid du Colombier
58223a736eSDavid du Colombier struct Font{
59223a736eSDavid du Colombier char *name;
60223a736eSDavid du Colombier HTMLfont *htmlfont;
61223a736eSDavid du Colombier };
62223a736eSDavid du Colombier
63223a736eSDavid du Colombier struct HTMLfont{
64223a736eSDavid du Colombier char *name;
65223a736eSDavid du Colombier char *htmlname;
66223a736eSDavid du Colombier int bit;
67223a736eSDavid du Colombier };
68223a736eSDavid du Colombier
69223a736eSDavid du Colombier /* R must be first; it's the default representation for fonts we don't recognize */
70223a736eSDavid du Colombier HTMLfont htmlfonts[] =
71223a736eSDavid du Colombier {
72223a736eSDavid du Colombier "R", nil, 0,
73223a736eSDavid du Colombier "LucidaSans", nil, 0,
740fb05a45SDavid du Colombier "I", "i", Italic,
750fb05a45SDavid du Colombier "LucidaSansI", "i", Italic,
760fb05a45SDavid du Colombier "CW", "tt", CW,
770fb05a45SDavid du Colombier "LucidaCW", "tt", CW,
78223a736eSDavid du Colombier nil, nil,
79223a736eSDavid du Colombier };
80223a736eSDavid du Colombier
810fb05a45SDavid du Colombier #define TABLE "<table border=0 cellpadding=0 cellspacing=0>"
820fb05a45SDavid du Colombier
83223a736eSDavid du Colombier char*
8473e742d7SDavid du Colombier onattr[8*sizeof(int)] =
850fb05a45SDavid du Colombier {
860fb05a45SDavid du Colombier 0, 0, 0, 0, 0, 0, 0, 0,
870fb05a45SDavid du Colombier 0, 0, 0, 0, 0, 0, 0, 0,
880fb05a45SDavid du Colombier "<i>", /* italic */
890fb05a45SDavid du Colombier "<b>", /* bold */
900fb05a45SDavid du Colombier "<tt><font size=+1>", /* cw */
910fb05a45SDavid du Colombier "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n", /* indent1 */
920fb05a45SDavid du Colombier "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n", /* indent2 */
930fb05a45SDavid du Colombier "<+table border=0 cellpadding=0 cellspacing=0><tr height=2><td><tr><td width=20><td>\n", /* indent3 */
940fb05a45SDavid du Colombier 0,
950fb05a45SDavid du Colombier 0,
960fb05a45SDavid du Colombier 0,
970fb05a45SDavid du Colombier "<p><font size=+1><b>", /* heading 25 */
980fb05a45SDavid du Colombier "<unused>", /* anchor 26 */
99223a736eSDavid du Colombier };
100223a736eSDavid du Colombier
101223a736eSDavid du Colombier char*
10273e742d7SDavid du Colombier offattr[8*sizeof(int)] =
1030fb05a45SDavid du Colombier {
1040fb05a45SDavid du Colombier 0, 0, 0, 0, 0, 0, 0, 0,
1050fb05a45SDavid du Colombier 0, 0, 0, 0, 0, 0, 0, 0,
1060fb05a45SDavid du Colombier "</i>", /* italic */
1070fb05a45SDavid du Colombier "</b>", /* bold */
1080fb05a45SDavid du Colombier "</font></tt>", /* cw */
1090fb05a45SDavid du Colombier "<-/table>", /* indent1 */
1100fb05a45SDavid du Colombier "<-/table>", /* indent2 */
1110fb05a45SDavid du Colombier "<-/table>", /* indent3 */
1120fb05a45SDavid du Colombier 0,
1130fb05a45SDavid du Colombier 0,
1140fb05a45SDavid du Colombier 0,
1150fb05a45SDavid du Colombier "</b></font>", /* heading 25 */
1160fb05a45SDavid du Colombier "</a>", /* anchor 26 */
117223a736eSDavid du Colombier };
118223a736eSDavid du Colombier
119223a736eSDavid du Colombier Font *font[Nfont];
120223a736eSDavid du Colombier
121223a736eSDavid du Colombier Biobuf bout;
122223a736eSDavid du Colombier int debug = 0;
123223a736eSDavid du Colombier
124223a736eSDavid du Colombier /* troff state */
125223a736eSDavid du Colombier int page = 1;
126223a736eSDavid du Colombier int ft = 1;
127223a736eSDavid du Colombier int vp = 0;
128223a736eSDavid du Colombier int hp = 0;
129223a736eSDavid du Colombier int ps = 1;
130223a736eSDavid du Colombier int res = 720;
131223a736eSDavid du Colombier
132223a736eSDavid du Colombier int didP = 0;
133223a736eSDavid du Colombier int atnewline = 1;
134223a736eSDavid du Colombier int prevlineH = 0;
13573e742d7SDavid du Colombier Char attr = 0; /* or'ed into each Char */
136223a736eSDavid du Colombier
137223a736eSDavid du Colombier Char *chars;
138223a736eSDavid du Colombier int nchars;
139223a736eSDavid du Colombier int nalloc;
140223a736eSDavid du Colombier char** anchors; /* allocated in order */
141223a736eSDavid du Colombier int nanchors;
142223a736eSDavid du Colombier
143223a736eSDavid du Colombier char *filename;
144223a736eSDavid du Colombier int cno;
145223a736eSDavid du Colombier char buf[8192];
146223a736eSDavid du Colombier char *title = "Plan 9 man page";
147223a736eSDavid du Colombier
148223a736eSDavid du Colombier void process(Biobuf*, char*);
149223a736eSDavid du Colombier void mountfont(int, char*);
150223a736eSDavid du Colombier void switchfont(int);
151223a736eSDavid du Colombier void header(char*);
152223a736eSDavid du Colombier void flush(void);
153223a736eSDavid du Colombier void trailer(void);
154223a736eSDavid du Colombier
155223a736eSDavid du Colombier void*
emalloc(ulong n)156223a736eSDavid du Colombier emalloc(ulong n)
157223a736eSDavid du Colombier {
158223a736eSDavid du Colombier void *p;
159223a736eSDavid du Colombier
160223a736eSDavid du Colombier p = malloc(n);
161223a736eSDavid du Colombier if(p == nil)
162223a736eSDavid du Colombier sysfatal("malloc failed: %r");
163223a736eSDavid du Colombier return p;
164223a736eSDavid du Colombier }
165223a736eSDavid du Colombier
166223a736eSDavid du Colombier void*
erealloc(void * p,ulong n)167223a736eSDavid du Colombier erealloc(void *p, ulong n)
168223a736eSDavid du Colombier {
169223a736eSDavid du Colombier
170223a736eSDavid du Colombier p = realloc(p, n);
171223a736eSDavid du Colombier if(p == nil)
172223a736eSDavid du Colombier sysfatal("realloc failed: %r");
173223a736eSDavid du Colombier return p;
174223a736eSDavid du Colombier }
175223a736eSDavid du Colombier
176223a736eSDavid du Colombier char*
estrdup(char * s)177223a736eSDavid du Colombier estrdup(char *s)
178223a736eSDavid du Colombier {
179223a736eSDavid du Colombier char *t;
180223a736eSDavid du Colombier
181223a736eSDavid du Colombier t = strdup(s);
182223a736eSDavid du Colombier if(t == nil)
183223a736eSDavid du Colombier sysfatal("strdup failed: %r");
184223a736eSDavid du Colombier return t;
185223a736eSDavid du Colombier }
186223a736eSDavid du Colombier
187223a736eSDavid du Colombier void
usage(void)188223a736eSDavid du Colombier usage(void)
189223a736eSDavid du Colombier {
1900fb05a45SDavid du Colombier fprint(2, "usage: troff2html [-d] [-t title] [file ...]\n");
191223a736eSDavid du Colombier exits("usage");
192223a736eSDavid du Colombier }
193223a736eSDavid du Colombier
1940fb05a45SDavid du Colombier int
hccmp(const void * va,const void * vb)1950fb05a45SDavid du Colombier hccmp(const void *va, const void *vb)
1960fb05a45SDavid du Colombier {
1970fb05a45SDavid du Colombier Htmlchar *a, *b;
1980fb05a45SDavid du Colombier
1990fb05a45SDavid du Colombier a = (Htmlchar*)va;
2000fb05a45SDavid du Colombier b = (Htmlchar*)vb;
2010fb05a45SDavid du Colombier return a->value - b->value;
2020fb05a45SDavid du Colombier }
2030fb05a45SDavid du Colombier
204223a736eSDavid du Colombier void
main(int argc,char * argv[])205223a736eSDavid du Colombier main(int argc, char *argv[])
206223a736eSDavid du Colombier {
207223a736eSDavid du Colombier int i;
208223a736eSDavid du Colombier Biobuf in, *inp;
2090fb05a45SDavid du Colombier Rune r;
2100fb05a45SDavid du Colombier
2110fb05a45SDavid du Colombier for(i=0; i<nelem(htmlchars); i++){
2120fb05a45SDavid du Colombier chartorune(&r, htmlchars[i].utf);
2130fb05a45SDavid du Colombier htmlchars[i].value = r;
2140fb05a45SDavid du Colombier }
2150fb05a45SDavid du Colombier qsort(htmlchars, nelem(htmlchars), sizeof(htmlchars[0]), hccmp);
216223a736eSDavid du Colombier
217223a736eSDavid du Colombier ARGBEGIN{
218223a736eSDavid du Colombier case 't':
219223a736eSDavid du Colombier title = ARGF();
220223a736eSDavid du Colombier if(title == nil)
221223a736eSDavid du Colombier usage();
222223a736eSDavid du Colombier break;
223223a736eSDavid du Colombier case 'd':
224223a736eSDavid du Colombier debug++;
225223a736eSDavid du Colombier break;
226223a736eSDavid du Colombier default:
227223a736eSDavid du Colombier usage();
228223a736eSDavid du Colombier }ARGEND
2290fb05a45SDavid du Colombier
230223a736eSDavid du Colombier Binit(&bout, 1, OWRITE);
231223a736eSDavid du Colombier if(argc == 0){
232223a736eSDavid du Colombier header(title);
233223a736eSDavid du Colombier Binit(&in, 0, OREAD);
234223a736eSDavid du Colombier process(&in, "<stdin>");
235223a736eSDavid du Colombier }else{
236223a736eSDavid du Colombier header(title);
237223a736eSDavid du Colombier for(i=0; i<argc; i++){
238223a736eSDavid du Colombier inp = Bopen(argv[i], OREAD);
239223a736eSDavid du Colombier if(inp == nil)
240223a736eSDavid du Colombier sysfatal("can't open %s: %r", argv[i]);
241223a736eSDavid du Colombier process(inp, argv[i]);
242223a736eSDavid du Colombier Bterm(inp);
243223a736eSDavid du Colombier }
244223a736eSDavid du Colombier }
245223a736eSDavid du Colombier flush();
246223a736eSDavid du Colombier trailer();
247223a736eSDavid du Colombier exits(nil);
248223a736eSDavid du Colombier }
249223a736eSDavid du Colombier
250223a736eSDavid du Colombier void
emitchar(Char c)25173e742d7SDavid du Colombier emitchar(Char c)
252223a736eSDavid du Colombier {
253223a736eSDavid du Colombier if(nalloc == nchars){
254223a736eSDavid du Colombier nalloc += 10000;
255223a736eSDavid du Colombier chars = realloc(chars, nalloc*sizeof(chars[0]));
256223a736eSDavid du Colombier if(chars == nil)
257223a736eSDavid du Colombier sysfatal("malloc failed: %r");
258223a736eSDavid du Colombier }
25973e742d7SDavid du Colombier chars[nchars++] = c;
260223a736eSDavid du Colombier }
261223a736eSDavid du Colombier
262223a736eSDavid du Colombier void
emit(Rune r)263223a736eSDavid du Colombier emit(Rune r)
264223a736eSDavid du Colombier {
26573e742d7SDavid du Colombier emitchar(r | attr);
2660fb05a45SDavid du Colombier /*
2670fb05a45SDavid du Colombier * Close man page references early, so that
2680fb05a45SDavid du Colombier * .IR proof (1),
2690fb05a45SDavid du Colombier * doesn't make the comma part of the link.
2700fb05a45SDavid du Colombier */
2710fb05a45SDavid du Colombier if(r == ')')
2720fb05a45SDavid du Colombier attr &= ~(1<<Anchor);
273223a736eSDavid du Colombier }
274223a736eSDavid du Colombier
275223a736eSDavid du Colombier void
emitstr(char * s)276223a736eSDavid du Colombier emitstr(char *s)
277223a736eSDavid du Colombier {
27873e742d7SDavid du Colombier emitchar(Estring);
27973e742d7SDavid du Colombier emitchar((Char)s);
280223a736eSDavid du Colombier }
281223a736eSDavid du Colombier
2820fb05a45SDavid du Colombier int indentlevel;
2830fb05a45SDavid du Colombier int linelen;
2840fb05a45SDavid du Colombier
2850fb05a45SDavid du Colombier void
iputrune(Biobuf * b,Rune r)2860fb05a45SDavid du Colombier iputrune(Biobuf *b, Rune r)
2870fb05a45SDavid du Colombier {
2880fb05a45SDavid du Colombier int i;
2890fb05a45SDavid du Colombier
2900fb05a45SDavid du Colombier if(linelen++ > 60 && r == ' ')
2910fb05a45SDavid du Colombier r = '\n';
2920fb05a45SDavid du Colombier Bputrune(b, r);
2930fb05a45SDavid du Colombier if(r == '\n'){
2940fb05a45SDavid du Colombier for(i=0; i<indentlevel; i++)
2950fb05a45SDavid du Colombier Bprint(b, " ");
2960fb05a45SDavid du Colombier linelen = 0;
2970fb05a45SDavid du Colombier }
2980fb05a45SDavid du Colombier }
2990fb05a45SDavid du Colombier
3000fb05a45SDavid du Colombier void
iputs(Biobuf * b,char * s)3010fb05a45SDavid du Colombier iputs(Biobuf *b, char *s)
3020fb05a45SDavid du Colombier {
3030fb05a45SDavid du Colombier if(s[0]=='<' && s[1]=='+'){
3040fb05a45SDavid du Colombier iputrune(b, '\n');
3050fb05a45SDavid du Colombier Bprint(b, "<%s", s+2);
3060fb05a45SDavid du Colombier indentlevel++;
3070fb05a45SDavid du Colombier iputrune(b, '\n');
3080fb05a45SDavid du Colombier }else if(s[0]=='<' && s[1]=='-'){
3090fb05a45SDavid du Colombier indentlevel--;
3100fb05a45SDavid du Colombier iputrune(b, '\n');
3110fb05a45SDavid du Colombier Bprint(b, "<%s", s+2);
3120fb05a45SDavid du Colombier iputrune(b, '\n');
3130fb05a45SDavid du Colombier }else
3140fb05a45SDavid du Colombier Bprint(b, "%s", s);
3150fb05a45SDavid du Colombier }
3160fb05a45SDavid du Colombier
3170fb05a45SDavid du Colombier void
setattr(Char a)31873e742d7SDavid du Colombier setattr(Char a)
3190fb05a45SDavid du Colombier {
32073e742d7SDavid du Colombier Char on, off;
32173e742d7SDavid du Colombier int i, j;
3220fb05a45SDavid du Colombier
3230fb05a45SDavid du Colombier on = a & ~attr;
3240fb05a45SDavid du Colombier off = attr & ~a;
3250fb05a45SDavid du Colombier
3260fb05a45SDavid du Colombier /* walk up the nest stack until we reach something we need to turn off. */
3270fb05a45SDavid du Colombier for(i=0; i<nnest; i++)
3280fb05a45SDavid du Colombier if(off&(1<<nest[i]))
3290fb05a45SDavid du Colombier break;
3300fb05a45SDavid du Colombier
3310fb05a45SDavid du Colombier /* turn off everything above that */
3320fb05a45SDavid du Colombier for(j=nnest-1; j>=i; j--)
3330fb05a45SDavid du Colombier iputs(&bout, offattr[nest[j]]);
3340fb05a45SDavid du Colombier
3350fb05a45SDavid du Colombier /* turn on everything we just turned off but didn't want to */
3360fb05a45SDavid du Colombier for(j=i; j<nnest; j++)
3370fb05a45SDavid du Colombier if(a&(1<<nest[j]))
3380fb05a45SDavid du Colombier iputs(&bout, onattr[nest[j]]);
3390fb05a45SDavid du Colombier else
3400fb05a45SDavid du Colombier nest[j] = 0;
3410fb05a45SDavid du Colombier
3420fb05a45SDavid du Colombier /* shift the zeros (turned off things) up */
3430fb05a45SDavid du Colombier for(i=j=0; i<nnest; i++)
3440fb05a45SDavid du Colombier if(nest[i] != 0)
3450fb05a45SDavid du Colombier nest[j++] = nest[i];
3460fb05a45SDavid du Colombier nnest = j;
3470fb05a45SDavid du Colombier
3480fb05a45SDavid du Colombier /* now turn on the new attributes */
3490fb05a45SDavid du Colombier for(i=0; i<nelem(attrorder); i++){
3500fb05a45SDavid du Colombier j = attrorder[i];
3510fb05a45SDavid du Colombier if(on&(1<<j)){
3520fb05a45SDavid du Colombier if(j == Anchor)
3530fb05a45SDavid du Colombier onattr[j] = anchors[nanchors++];
3540fb05a45SDavid du Colombier iputs(&bout, onattr[j]);
35573e742d7SDavid du Colombier if(nnest >= nelem(nest))
35673e742d7SDavid du Colombier sysfatal("nesting too deep");
3570fb05a45SDavid du Colombier nest[nnest++] = j;
3580fb05a45SDavid du Colombier }
3590fb05a45SDavid du Colombier }
3600fb05a45SDavid du Colombier attr = a;
3610fb05a45SDavid du Colombier }
3620fb05a45SDavid du Colombier
363223a736eSDavid du Colombier void
flush(void)364223a736eSDavid du Colombier flush(void)
365223a736eSDavid du Colombier {
3660fb05a45SDavid du Colombier int i;
36773e742d7SDavid du Colombier Char c, a;
368223a736eSDavid du Colombier
3690fb05a45SDavid du Colombier nanchors = 0;
370223a736eSDavid du Colombier for(i=0; i<nchars; i++){
371223a736eSDavid du Colombier c = chars[i];
3720fb05a45SDavid du Colombier if(c == Estring){
373223a736eSDavid du Colombier /* next word is string to print */
3740fb05a45SDavid du Colombier iputs(&bout, (char*)chars[++i]);
375223a736eSDavid du Colombier continue;
376223a736eSDavid du Colombier }
3770fb05a45SDavid du Colombier if(c == Epp){
3780fb05a45SDavid du Colombier iputrune(&bout, '\n');
3790fb05a45SDavid du Colombier iputs(&bout, TABLE "<tr height=5><td></table>");
3800fb05a45SDavid du Colombier iputrune(&bout, '\n');
3810fb05a45SDavid du Colombier continue;
382223a736eSDavid du Colombier }
3830fb05a45SDavid du Colombier a = c & ~0xFFFF;
3840fb05a45SDavid du Colombier c &= 0xFFFF;
3850fb05a45SDavid du Colombier /*
3860fb05a45SDavid du Colombier * If we're going to something off after a space,
3870fb05a45SDavid du Colombier * let's just turn it off before.
3880fb05a45SDavid du Colombier */
3890fb05a45SDavid du Colombier if(c == ' ' && i<nchars-1 && (chars[i+1]&0xFFFF) >= 32)
3900fb05a45SDavid du Colombier a ^= a & ~chars[i+1];
3910fb05a45SDavid du Colombier setattr(a);
3920fb05a45SDavid du Colombier iputrune(&bout, c & 0xFFFF);
393223a736eSDavid du Colombier }
394223a736eSDavid du Colombier }
395223a736eSDavid du Colombier
396223a736eSDavid du Colombier void
header(char * s)397223a736eSDavid du Colombier header(char *s)
398223a736eSDavid du Colombier {
3990fb05a45SDavid du Colombier Bprint(&bout, "<head>\n");
4000fb05a45SDavid du Colombier Bprint(&bout, "<title>%s</title>\n", s);
4010fb05a45SDavid du Colombier Bprint(&bout, "<meta content=\"text/html; charset=utf-8\" http-equiv=Content-Type>\n");
4020fb05a45SDavid du Colombier Bprint(&bout, "</head>\n");
4030fb05a45SDavid du Colombier Bprint(&bout, "<body bgcolor=#ffffff>\n");
404223a736eSDavid du Colombier }
405223a736eSDavid du Colombier
406223a736eSDavid du Colombier void
trailer(void)407223a736eSDavid du Colombier trailer(void)
408223a736eSDavid du Colombier {
4099a747e4fSDavid du Colombier
4100fb05a45SDavid du Colombier #ifdef LUCENT
411daafdc08SDavid du Colombier Tm *t;
4129a747e4fSDavid du Colombier t = localtime(time(nil));
4130fb05a45SDavid du Colombier Bprint(&bout, TABLE "<tr height=20><td></table>\n");
4140fb05a45SDavid du Colombier Bprint(&bout, "<font size=-1><a href=\"http://www.lucent.com/copyright.html\">\n");
415*b09c09c5SDavid du Colombier Bprint(&bout, "Copyright</A> © %d Alcatel-Lucent. All rights reserved.</font>\n", t->year+1900);
4160fb05a45SDavid du Colombier #endif
4170fb05a45SDavid du Colombier Bprint(&bout, "</body></html>\n");
418223a736eSDavid du Colombier }
419223a736eSDavid du Colombier
420223a736eSDavid du Colombier int
getc(Biobuf * b)421223a736eSDavid du Colombier getc(Biobuf *b)
422223a736eSDavid du Colombier {
423223a736eSDavid du Colombier cno++;
424223a736eSDavid du Colombier return Bgetrune(b);
425223a736eSDavid du Colombier }
426223a736eSDavid du Colombier
427223a736eSDavid du Colombier void
ungetc(Biobuf * b)428223a736eSDavid du Colombier ungetc(Biobuf *b)
429223a736eSDavid du Colombier {
430223a736eSDavid du Colombier cno--;
431223a736eSDavid du Colombier Bungetrune(b);
432223a736eSDavid du Colombier }
433223a736eSDavid du Colombier
434223a736eSDavid du Colombier char*
getline(Biobuf * b)435223a736eSDavid du Colombier getline(Biobuf *b)
436223a736eSDavid du Colombier {
437223a736eSDavid du Colombier int i, c;
438223a736eSDavid du Colombier
439223a736eSDavid du Colombier for(i=0; i<sizeof buf; i++){
440223a736eSDavid du Colombier c = getc(b);
441223a736eSDavid du Colombier if(c == Beof)
442223a736eSDavid du Colombier return nil;
443223a736eSDavid du Colombier buf[i] = c;
444223a736eSDavid du Colombier if(c == '\n'){
445223a736eSDavid du Colombier buf[i] = '\0';
446223a736eSDavid du Colombier break;
447223a736eSDavid du Colombier }
448223a736eSDavid du Colombier }
449223a736eSDavid du Colombier return buf;
450223a736eSDavid du Colombier }
451223a736eSDavid du Colombier
452223a736eSDavid du Colombier int
getnum(Biobuf * b)453223a736eSDavid du Colombier getnum(Biobuf *b)
454223a736eSDavid du Colombier {
455223a736eSDavid du Colombier int i, c;
456223a736eSDavid du Colombier
457223a736eSDavid du Colombier i = 0;
458223a736eSDavid du Colombier for(;;){
459223a736eSDavid du Colombier c = getc(b);
460223a736eSDavid du Colombier if(c<'0' || '9'<c){
461223a736eSDavid du Colombier ungetc(b);
462223a736eSDavid du Colombier break;
463223a736eSDavid du Colombier }
464223a736eSDavid du Colombier i = i*10 + (c-'0');
465223a736eSDavid du Colombier }
466223a736eSDavid du Colombier return i;
467223a736eSDavid du Colombier }
468223a736eSDavid du Colombier
469223a736eSDavid du Colombier char*
getstr(Biobuf * b)470223a736eSDavid du Colombier getstr(Biobuf *b)
471223a736eSDavid du Colombier {
472223a736eSDavid du Colombier int i, c;
473223a736eSDavid du Colombier
474223a736eSDavid du Colombier for(i=0; i<sizeof buf; i++){
475223a736eSDavid du Colombier /* must get bytes not runes */
476223a736eSDavid du Colombier cno++;
477223a736eSDavid du Colombier c = Bgetc(b);
478223a736eSDavid du Colombier if(c == Beof)
479223a736eSDavid du Colombier return nil;
480223a736eSDavid du Colombier buf[i] = c;
481223a736eSDavid du Colombier if(c == '\n' || c==' ' || c=='\t'){
482223a736eSDavid du Colombier ungetc(b);
483223a736eSDavid du Colombier buf[i] = '\0';
484223a736eSDavid du Colombier break;
485223a736eSDavid du Colombier }
486223a736eSDavid du Colombier }
487223a736eSDavid du Colombier return buf;
488223a736eSDavid du Colombier }
489223a736eSDavid du Colombier
490223a736eSDavid du Colombier int
setnum(Biobuf * b,char * name,int min,int max)491223a736eSDavid du Colombier setnum(Biobuf *b, char *name, int min, int max)
492223a736eSDavid du Colombier {
493223a736eSDavid du Colombier int i;
494223a736eSDavid du Colombier
495223a736eSDavid du Colombier i = getnum(b);
496223a736eSDavid du Colombier if(debug > 2)
497223a736eSDavid du Colombier fprint(2, "set %s = %d\n", name, i);
498223a736eSDavid du Colombier if(min<=i && i<max)
499223a736eSDavid du Colombier return i;
500223a736eSDavid du Colombier sysfatal("value of %s is %d; min %d max %d at %s:#%d", name, i, min, max, filename, cno);
501223a736eSDavid du Colombier return i;
502223a736eSDavid du Colombier }
503223a736eSDavid du Colombier
504223a736eSDavid du Colombier void
xcmd(Biobuf * b)505223a736eSDavid du Colombier xcmd(Biobuf *b)
506223a736eSDavid du Colombier {
5070fb05a45SDavid du Colombier char *p, *fld[16], buf[1024];
5080fb05a45SDavid du Colombier
509223a736eSDavid du Colombier int i, nfld;
510223a736eSDavid du Colombier
511223a736eSDavid du Colombier p = getline(b);
512223a736eSDavid du Colombier if(p == nil)
513223a736eSDavid du Colombier sysfatal("xcmd error: %r");
514223a736eSDavid du Colombier if(debug)
515223a736eSDavid du Colombier fprint(2, "x command '%s'\n", p);
516223a736eSDavid du Colombier nfld = tokenize(p, fld, nelem(fld));
517223a736eSDavid du Colombier if(nfld == 0)
518223a736eSDavid du Colombier return;
519223a736eSDavid du Colombier switch(fld[0][0]){
520223a736eSDavid du Colombier case 'f':
521223a736eSDavid du Colombier /* mount font */
522223a736eSDavid du Colombier if(nfld != 3)
523223a736eSDavid du Colombier break;
524223a736eSDavid du Colombier i = atoi(fld[1]);
525223a736eSDavid du Colombier if(i<0 || Nfont<=i)
526223a736eSDavid du Colombier sysfatal("font %d out of range at %s:#%d", i, filename, cno);
527223a736eSDavid du Colombier mountfont(i, fld[2]);
528223a736eSDavid du Colombier return;
529223a736eSDavid du Colombier case 'i':
530223a736eSDavid du Colombier /* init */
531223a736eSDavid du Colombier return;
532223a736eSDavid du Colombier case 'r':
533223a736eSDavid du Colombier if(nfld<2 || atoi(fld[1])!=res)
534223a736eSDavid du Colombier sysfatal("typesetter has unexpected resolution %s", fld[1]? fld[1] : "<unspecified>");
535223a736eSDavid du Colombier return;
536223a736eSDavid du Colombier case 's':
537223a736eSDavid du Colombier /* stop */
538223a736eSDavid du Colombier return;
539223a736eSDavid du Colombier case 't':
540223a736eSDavid du Colombier /* trailer */
541223a736eSDavid du Colombier return;
542223a736eSDavid du Colombier case 'T':
543223a736eSDavid du Colombier if(nfld!=2 || strcmp(fld[1], "utf")!=0)
544223a736eSDavid du Colombier sysfatal("output for unknown typesetter type %s", fld[1]);
545223a736eSDavid du Colombier return;
546223a736eSDavid du Colombier case 'X':
547223a736eSDavid du Colombier if(nfld<3 || strcmp(fld[1], "html")!=0)
548223a736eSDavid du Colombier break;
549223a736eSDavid du Colombier /* is it a man reference of the form cp(1)? */
550223a736eSDavid du Colombier /* X manref start/end cp (1) */
551223a736eSDavid du Colombier if(nfld==6 && strcmp(fld[2], "manref")==0){
552223a736eSDavid du Colombier /* was the right macro; is it the right form? */
553223a736eSDavid du Colombier if(strlen(fld[5])>=3 &&
554223a736eSDavid du Colombier fld[5][0]=='(' && fld[5][2]==')' &&
555223a736eSDavid du Colombier '0'<=fld[5][1] && fld[5][1]<='9'){
556223a736eSDavid du Colombier if(strcmp(fld[3], "start") == 0){
557223a736eSDavid du Colombier /* set anchor attribute and remember string */
558223a736eSDavid du Colombier attr |= (1<<Anchor);
559223a736eSDavid du Colombier snprint(buf, sizeof buf,
5600fb05a45SDavid du Colombier "<a href=\"/magic/man2html/%c/%s\">",
561223a736eSDavid du Colombier fld[5][1], fld[4]);
562223a736eSDavid du Colombier nanchors++;
563223a736eSDavid du Colombier anchors = erealloc(anchors, nanchors*sizeof(char*));
564223a736eSDavid du Colombier anchors[nanchors-1] = estrdup(buf);
565223a736eSDavid du Colombier }else if(strcmp(fld[3], "end") == 0)
566223a736eSDavid du Colombier attr &= ~(1<<Anchor);
567223a736eSDavid du Colombier }
5680fb05a45SDavid du Colombier }else if(strcmp(fld[2], "manPP") == 0){
5690fb05a45SDavid du Colombier didP = 1;
57073e742d7SDavid du Colombier emitchar(Epp);
571223a736eSDavid du Colombier }else if(nfld<4 || strcmp(fld[2], "manref")!=0){
572223a736eSDavid du Colombier if(nfld>2 && strcmp(fld[2], "<P>")==0){ /* avoid triggering extra <br> */
573223a736eSDavid du Colombier didP = 1;
574223a736eSDavid du Colombier /* clear all font attributes before paragraph */
57573e742d7SDavid du Colombier emitchar(' ' | (attr & ~(0xFFFF|((1<<Italic)|(1<<Bold)|(1<<CW)))));
576223a736eSDavid du Colombier emitstr("<P>");
577223a736eSDavid du Colombier /* next emittec char will turn font attributes back on */
578223a736eSDavid du Colombier }else if(nfld>2 && strcmp(fld[2], "<H4>")==0)
579223a736eSDavid du Colombier attr |= (1<<Heading);
580223a736eSDavid du Colombier else if(nfld>2 && strcmp(fld[2], "</H4>")==0)
581223a736eSDavid du Colombier attr &= ~(1<<Heading);
582223a736eSDavid du Colombier else if(debug)
583223a736eSDavid du Colombier fprint(2, "unknown in-line html %s... at %s:%#d\n",
584223a736eSDavid du Colombier fld[2], filename, cno);
585223a736eSDavid du Colombier }
586223a736eSDavid du Colombier return;
587223a736eSDavid du Colombier }
588223a736eSDavid du Colombier if(debug)
589223a736eSDavid du Colombier fprint(2, "unknown or badly formatted x command %s\n", fld[0]);
590223a736eSDavid du Colombier }
591223a736eSDavid du Colombier
592223a736eSDavid du Colombier int
lookup(int c,Htmlchar tab[],int ntab)593223a736eSDavid du Colombier lookup(int c, Htmlchar tab[], int ntab)
594223a736eSDavid du Colombier {
595223a736eSDavid du Colombier int low, high, mid;
596223a736eSDavid du Colombier
597223a736eSDavid du Colombier low = 0;
598223a736eSDavid du Colombier high = ntab - 1;
599223a736eSDavid du Colombier while(low <= high){
600223a736eSDavid du Colombier mid = (low+high)/2;
601223a736eSDavid du Colombier if(c < tab[mid].value)
602223a736eSDavid du Colombier high = mid - 1;
603223a736eSDavid du Colombier else if(c > tab[mid].value)
604223a736eSDavid du Colombier low = mid + 1;
605223a736eSDavid du Colombier else
606223a736eSDavid du Colombier return mid;
607223a736eSDavid du Colombier }
608223a736eSDavid du Colombier return -1; /* no match */
609223a736eSDavid du Colombier }
610223a736eSDavid du Colombier
611223a736eSDavid du Colombier void
emithtmlchar(int r)612223a736eSDavid du Colombier emithtmlchar(int r)
613223a736eSDavid du Colombier {
614223a736eSDavid du Colombier static char buf[10];
615223a736eSDavid du Colombier int i;
616223a736eSDavid du Colombier
617223a736eSDavid du Colombier i = lookup(r, htmlchars, nelem(htmlchars));
618223a736eSDavid du Colombier if(i >= 0)
619223a736eSDavid du Colombier emitstr(htmlchars[i].name);
620223a736eSDavid du Colombier else
621223a736eSDavid du Colombier emit(r);
622223a736eSDavid du Colombier }
623223a736eSDavid du Colombier
624223a736eSDavid du Colombier char*
troffchar(char * s)625223a736eSDavid du Colombier troffchar(char *s)
626223a736eSDavid du Colombier {
627223a736eSDavid du Colombier int i;
628223a736eSDavid du Colombier
629223a736eSDavid du Colombier for(i=0; troffchars[i].name!=nil; i++)
630223a736eSDavid du Colombier if(strcmp(s, troffchars[i].name) == 0)
631223a736eSDavid du Colombier return troffchars[i].value;
632223a736eSDavid du Colombier return "??";
633223a736eSDavid du Colombier }
634223a736eSDavid du Colombier
635223a736eSDavid du Colombier void
indent(void)636223a736eSDavid du Colombier indent(void)
637223a736eSDavid du Colombier {
638223a736eSDavid du Colombier int nind;
639223a736eSDavid du Colombier
640223a736eSDavid du Colombier didP = 0;
641223a736eSDavid du Colombier if(atnewline){
642223a736eSDavid du Colombier if(hp != prevlineH){
643223a736eSDavid du Colombier prevlineH = hp;
644223a736eSDavid du Colombier /* these most peculiar numbers appear in the troff -man output */
645223a736eSDavid du Colombier nind = ((prevlineH-1*res)+323)/324;
646223a736eSDavid du Colombier attr &= ~((1<<Indent1)|(1<<Indent2)|(1<<Indent3));
647223a736eSDavid du Colombier if(nind >= 1)
648223a736eSDavid du Colombier attr |= (1<<Indent1);
649223a736eSDavid du Colombier if(nind >= 2)
650223a736eSDavid du Colombier attr |= (1<<Indent2);
6510fb05a45SDavid du Colombier if(nind >= 3)
652223a736eSDavid du Colombier attr |= (1<<Indent3);
653223a736eSDavid du Colombier }
654223a736eSDavid du Colombier atnewline = 0;
655223a736eSDavid du Colombier }
656223a736eSDavid du Colombier }
657223a736eSDavid du Colombier
658223a736eSDavid du Colombier void
process(Biobuf * b,char * name)659223a736eSDavid du Colombier process(Biobuf *b, char *name)
660223a736eSDavid du Colombier {
661223a736eSDavid du Colombier int c, r, v, i;
662223a736eSDavid du Colombier char *p;
663223a736eSDavid du Colombier
664223a736eSDavid du Colombier cno = 0;
665223a736eSDavid du Colombier prevlineH = res;
666223a736eSDavid du Colombier filename = name;
667223a736eSDavid du Colombier for(;;){
668223a736eSDavid du Colombier c = getc(b);
669223a736eSDavid du Colombier switch(c){
670223a736eSDavid du Colombier case Beof:
671223a736eSDavid du Colombier /* go to ground state */
672223a736eSDavid du Colombier attr = 0;
673223a736eSDavid du Colombier emit('\n');
674223a736eSDavid du Colombier return;
675223a736eSDavid du Colombier case '\n':
676223a736eSDavid du Colombier break;
677223a736eSDavid du Colombier case '0': case '1': case '2': case '3': case '4':
678223a736eSDavid du Colombier case '5': case '6': case '7': case '8': case '9':
679223a736eSDavid du Colombier v = c-'0';
680223a736eSDavid du Colombier c = getc(b);
681223a736eSDavid du Colombier if(c<'0' || '9'<c)
682223a736eSDavid du Colombier sysfatal("illegal character motion at %s:#%d", filename, cno);
683223a736eSDavid du Colombier v = v*10 + (c-'0');
684223a736eSDavid du Colombier hp += v;
685223a736eSDavid du Colombier /* fall through to character case */
686223a736eSDavid du Colombier case 'c':
687223a736eSDavid du Colombier indent();
688223a736eSDavid du Colombier r = getc(b);
689223a736eSDavid du Colombier emithtmlchar(r);
690223a736eSDavid du Colombier break;
691b7b24591SDavid du Colombier case 'D':
692b7b24591SDavid du Colombier /* draw line; ignore */
693b7b24591SDavid du Colombier do
694b7b24591SDavid du Colombier c = getc(b);
695b7b24591SDavid du Colombier while(c!='\n' && c!= Beof);
696b7b24591SDavid du Colombier break;
697223a736eSDavid du Colombier case 'f':
698223a736eSDavid du Colombier v = setnum(b, "font", 0, Nfont);
699223a736eSDavid du Colombier switchfont(v);
700223a736eSDavid du Colombier break;
701223a736eSDavid du Colombier case 'h':
702223a736eSDavid du Colombier v = setnum(b, "hpos", -20000, 20000);
703223a736eSDavid du Colombier /* generate spaces if motion is large and within a line */
704223a736eSDavid du Colombier if(!atnewline && v>2*72)
705223a736eSDavid du Colombier for(i=0; i<v; i+=72)
706223a736eSDavid du Colombier emitstr(" ");
707223a736eSDavid du Colombier hp += v;
708223a736eSDavid du Colombier break;
709223a736eSDavid du Colombier case 'n':
710223a736eSDavid du Colombier setnum(b, "n1", -10000, 10000);
711223a736eSDavid du Colombier //Bprint(&bout, " N1=%d", v);
712223a736eSDavid du Colombier getc(b); /* space separates */
713223a736eSDavid du Colombier setnum(b, "n2", -10000, 10000);
714223a736eSDavid du Colombier atnewline = 1;
715223a736eSDavid du Colombier if(!didP && hp < (Wid-1)*res) /* if line is less than 19" long, probably need a line break */
716223a736eSDavid du Colombier emitstr("<br>");
717223a736eSDavid du Colombier emit('\n');
718223a736eSDavid du Colombier break;
719223a736eSDavid du Colombier case 'p':
720223a736eSDavid du Colombier page = setnum(b, "ps", -10000, 10000);
721223a736eSDavid du Colombier break;
722223a736eSDavid du Colombier case 's':
723223a736eSDavid du Colombier ps = setnum(b, "ps", 1, 1000);
724223a736eSDavid du Colombier break;
725223a736eSDavid du Colombier case 'v':
726223a736eSDavid du Colombier vp += setnum(b, "vpos", -10000, 10000);
727223a736eSDavid du Colombier /* BUG: ignore motion */
728223a736eSDavid du Colombier break;
729223a736eSDavid du Colombier case 'x':
730223a736eSDavid du Colombier xcmd(b);
731223a736eSDavid du Colombier break;
732223a736eSDavid du Colombier case 'w':
733223a736eSDavid du Colombier emit(' ');
734223a736eSDavid du Colombier break;
735223a736eSDavid du Colombier case 'C':
736223a736eSDavid du Colombier indent();
737223a736eSDavid du Colombier p = getstr(b);
738223a736eSDavid du Colombier emitstr(troffchar(p));
739223a736eSDavid du Colombier break;
740223a736eSDavid du Colombier case 'H':
741223a736eSDavid du Colombier hp = setnum(b, "hpos", 0, 20000);
742223a736eSDavid du Colombier //Bprint(&bout, " H=%d ", hp);
743223a736eSDavid du Colombier break;
744223a736eSDavid du Colombier case 'V':
745223a736eSDavid du Colombier vp = setnum(b, "vpos", 0, 10000);
746223a736eSDavid du Colombier break;
747223a736eSDavid du Colombier default:
748223a736eSDavid du Colombier fprint(2, "dhtml: unknown directive %c(0x%.2ux) at %s:#%d\n", c, c, filename, cno);
749223a736eSDavid du Colombier return;
750223a736eSDavid du Colombier }
751223a736eSDavid du Colombier }
752223a736eSDavid du Colombier }
753223a736eSDavid du Colombier
754223a736eSDavid du Colombier HTMLfont*
htmlfont(char * name)755223a736eSDavid du Colombier htmlfont(char *name)
756223a736eSDavid du Colombier {
757223a736eSDavid du Colombier int i;
758223a736eSDavid du Colombier
759223a736eSDavid du Colombier for(i=0; htmlfonts[i].name!=nil; i++)
760223a736eSDavid du Colombier if(strcmp(name, htmlfonts[i].name) == 0)
761223a736eSDavid du Colombier return &htmlfonts[i];
762223a736eSDavid du Colombier return &htmlfonts[0];
763223a736eSDavid du Colombier }
764223a736eSDavid du Colombier
765223a736eSDavid du Colombier void
mountfont(int pos,char * name)766223a736eSDavid du Colombier mountfont(int pos, char *name)
767223a736eSDavid du Colombier {
768223a736eSDavid du Colombier if(debug)
769223a736eSDavid du Colombier fprint(2, "mount font %s on %d\n", name, pos);
770223a736eSDavid du Colombier if(font[pos] != nil){
771223a736eSDavid du Colombier free(font[pos]->name);
772223a736eSDavid du Colombier free(font[pos]);
773223a736eSDavid du Colombier }
774223a736eSDavid du Colombier font[pos] = emalloc(sizeof(Font));
775223a736eSDavid du Colombier font[pos]->name = estrdup(name);
776223a736eSDavid du Colombier font[pos]->htmlfont = htmlfont(name);
777223a736eSDavid du Colombier }
778223a736eSDavid du Colombier
779223a736eSDavid du Colombier void
switchfont(int pos)780223a736eSDavid du Colombier switchfont(int pos)
781223a736eSDavid du Colombier {
782223a736eSDavid du Colombier HTMLfont *hf;
783223a736eSDavid du Colombier
784223a736eSDavid du Colombier if(debug)
785223a736eSDavid du Colombier fprint(2, "font change from %d (%s) to %d (%s)\n", ft, font[ft]->name, pos, font[pos]->name);
786223a736eSDavid du Colombier if(pos == ft)
787223a736eSDavid du Colombier return;
788223a736eSDavid du Colombier hf = font[ft]->htmlfont;
789223a736eSDavid du Colombier if(hf->bit != 0)
790223a736eSDavid du Colombier attr &= ~(1<<hf->bit);
791223a736eSDavid du Colombier ft = pos;
792223a736eSDavid du Colombier hf = font[ft]->htmlfont;
793223a736eSDavid du Colombier if(hf->bit != 0)
794223a736eSDavid du Colombier attr |= (1<<hf->bit);
795223a736eSDavid du Colombier }
796