]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ms2html.c
vmx: reset virtio queue state on device reset
[plan9front.git] / sys / src / cmd / ms2html.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <bio.h>
5
6 enum
7 {
8         SSIZE = 10,
9
10         Maxnh=  8,              /* highest NH level */
11         HH=     4,              /* heading level used for SH and NH */
12         Maxmstack=      10,     /* deepest macro/string nesting */
13         Narg=   20,             /* max args to a macro */
14         Maxsstack=      5,      /* deepest nesting of .so's */
15         Nline=  1024,
16         Maxget= 10,
17         Maxif = 20,
18         Maxfsp = 100,
19
20         /* list types */
21         Lordered = 1,
22         Lunordered,
23         Ldef,
24         Lother,
25 };
26
27 char *delim = "$$";
28 char *basename;
29 char *title;
30 int eqnmode;
31
32 int     quiet;
33 float   indent; /* from .in */
34 Biobuf  bout;
35 int     isup;
36 int     isdown;
37 int     debug;
38
39 int nh[Maxnh];
40 int ifwastrue[Maxif];
41
42 int list, listnum, example;
43 int hangingau, hangingdt, hanginghead, hangingcenter;
44 int indirective, paragraph, sol, titleseen, ignore_nl, weBref;
45 void dohangingcenter(void);
46
47 typedef struct Goobie Goobie;
48 typedef struct Goobieif Goobieif;
49 struct Goobie
50 {
51         char *name;
52         void (*f)(int, char**);
53 };
54
55 typedef void F(int, char**);
56 typedef void Fif(char*, char*);
57
58 struct Goobieif
59 {
60         char *name;
61         Fif *f;
62 };
63
64 /* if, ie */
65 Fif g_as, g_ds, g_el, g_ie, g_if;
66 Goobieif gtabif[] = {
67         { "as", g_as },
68         { "ds", g_ds },
69         { "if", g_if },
70         { "ie", g_ie },
71         { "el", g_el },
72         { nil, nil },
73         };
74
75 /* pseudo ops */
76 F g_notyet, g_ignore, g_hrule, g_startgif;
77
78 /* ms macros */
79 F g_AU, g_B, g_BI, g_CW, g_I, g_IP, g_LP, g_PP, g_SH, g_NH;
80 F g_P1, g_P2, g_TL, g_R, g_AB, g_AE, g_EQ, g_TS, g_TE, g_FS, g_FE;
81 F g_PY, g_IH, g_MH, g_HO, g_BX, g_QS, g_QE, g_RS, g_RE;
82
83 /* pictures macro */
84 F g_BP;
85
86 /* real troff */
87 F g_br, g_ft, g_sp, g_de, g_lf, g_so, g_rm, g_in;
88 F g_nr, g_ig, g_RT, g_BS, g_BE, g_LB, g_ta;
89
90 /* macros to include ML in output */
91 F g__H, g__T;
92
93 Goobie gtab[] =
94 {
95         { "_T", g__T, },
96         { "_H", g__H, },
97         { "1C", g_ignore, },
98         { "2C", g_ignore, },
99         { "AB", g_AB, },
100         { "AE", g_AE, },
101         { "AI", g_ignore, },
102         { "AU", g_AU, },
103         { "B",  g_B, },
104         { "B1", g_hrule, },
105         { "B2", g_hrule, },
106         { "BI", g_BI, },
107         { "BP", g_BP, },
108         { "BT", g_ignore, },
109         { "BX", g_BX, },
110         { "CW", g_CW, },
111         { "CT", g_ignore, },
112         { "DA", g_ignore, },
113         { "DE", g_P2, },
114         { "DS", g_P1, },
115         { "EG", g_ignore, },
116         { "EN", g_ignore, },
117         { "EQ", g_startgif, },
118         { "FE", g_FE, },
119         { "FP", g_ignore, },
120         { "FS", g_FS, },
121         { "HO", g_HO, },
122         { "I",  g_I, },
123         { "IH", g_IH, },
124         { "IM", g_ignore, },
125         { "IP", g_IP, },
126         { "KE", g_ignore, },
127         { "KF", g_ignore, },
128         { "KS", g_ignore, },
129         { "LG", g_ignore, },
130         { "LP", g_LP, },
131         { "LT", g_ignore, },
132         { "MF", g_ignore, },
133         { "MH", g_MH, },
134         { "MR", g_ignore, },
135         { "ND", g_ignore, },
136         { "NH", g_NH, },
137         { "NL", g_ignore, },
138         { "P1", g_P1, },
139         { "P2", g_P2, },
140         { "PE", g_ignore, },
141         { "PF", g_ignore, },
142         { "PP", g_PP, },
143         { "PS", g_startgif, },
144         { "PY", g_PY, },
145         { "QE", g_QE, },
146         { "QP", g_QS, },
147         { "QS", g_QS, },
148         { "R",          g_R, },
149         { "RE", g_RE, },
150         { "RP", g_ignore, },
151         { "RS", g_RS, },
152         { "SG", g_ignore, },
153         { "SH", g_SH, },
154         { "SM", g_ignore, },
155         { "TA", g_ignore, },
156         { "TE", g_ignore, },
157         { "TH", g_TL, },
158         { "TL", g_TL, },
159         { "TM", g_ignore, },
160         { "TR", g_ignore, },
161         { "TS", g_startgif, },
162         { "UL", g_I, },
163         { "UX", g_ignore, },
164         { "WH", g_ignore, },
165         { "RT",         g_RT, },
166
167         { "br", g_br, },
168         { "ti",         g_br, },
169         { "nf", g_P1, },
170         { "fi",         g_P2, },
171         { "ft",         g_ft, },
172         { "sp",         g_sp, },
173         { "rm",         g_rm, },
174         { "de",         g_de, },
175         { "am",         g_de, },
176         { "lf",         g_lf, },
177         { "so",         g_so, },
178         { "ps",         g_ignore },
179         { "vs",         g_ignore },
180         { "nr",         g_nr },
181         { "in",         g_in },
182         { "ne",         g_ignore },
183         { "ig",         g_ig },
184         { "BS",         g_BS },
185         { "BE",         g_BE },
186         { "LB",         g_LB },
187         { nil, nil },
188 };
189
190 typedef struct Entity Entity;
191 struct Entity
192 {
193         char *name;
194         int value;
195 };
196
197 Entity entity[] =
198 {
199         { "&#SPACE;",   L' ', },
200         { "&#RS;",              L'\n', },
201         { "&#RE;",              L'\r', },
202         { "&quot;",             L'"', },
203         { "&AElig;",    L'Æ', },
204         { "&Aacute;",   L'Á', },
205         { "&Acirc;",    L'Â', },
206         { "&Agrave;",   L'À', },
207         { "&Aring;",    L'Å', },
208         { "&Atilde;",   L'Ã', },
209         { "&Auml;",     L'Ä', },
210         { "&Ccedil;",   L'Ç', },
211         { "&ETH;",              L'Ð', },
212         { "&Eacute;",   L'É', },
213         { "&Ecirc;",    L'Ê', },
214         { "&Egrave;",   L'È', },
215         { "&Euml;",     L'Ë', },
216         { "&Iacute;",   L'Í', },
217         { "&Icirc;",            L'Î', },
218         { "&Igrave;",   L'Ì', },
219         { "&Iuml;",             L'Ï', },
220         { "&Ntilde;",   L'Ñ', },
221         { "&Oacute;",   L'Ó', },
222         { "&Ocirc;",    L'Ô', },
223         { "&Ograve;",   L'Ò', },
224         { "&Oslash;",   L'Ø', },
225         { "&Otilde;",   L'Õ', },
226         { "&Ouml;",     L'Ö', },
227         { "&THORN;",    L'Þ', },
228         { "&Uacute;",   L'Ú', },
229         { "&Ucirc;",    L'Û', },
230         { "&Ugrave;",   L'Ù', },
231         { "&Uuml;",     L'Ü', },
232         { "&Yacute;",   L'Ý', },
233         { "&aacute;",   L'á', },
234         { "&acirc;",    L'â', },
235         { "&aelig;",    L'æ', },
236         { "&agrave;",   L'à', },
237         { "&amp;",              L'&', },
238         { "&aring;",    L'å', },
239         { "&atilde;",   L'ã', },
240         { "&auml;",     L'ä', },
241         { "&ccedil;",   L'ç', },
242         { "&eacute;",   L'é', },
243         { "&ecirc;",    L'ê', },
244         { "&egrave;",   L'è', },
245         { "&eth;",              L'ð', },
246         { "&euml;",     L'ë', },
247         { "&gt;",               L'>', },
248         { "&iacute;",   L'í', },
249         { "&icirc;",            L'î', },
250         { "&igrave;",   L'ì', },
251         { "&iuml;",             L'ï', },
252         { "&lt;",               L'<', },
253         { "&ntilde;",   L'ñ', },
254         { "&oacute;",   L'ó', },
255         { "&ocirc;",    L'ô', },
256         { "&ograve;",   L'ò', },
257         { "&oslash;",   L'ø', },
258         { "&otilde;",   L'õ', },
259         { "&ouml;",     L'ö', },
260         { "&szlig;",            L'ß', },
261         { "&thorn;",    L'þ', },
262         { "&uacute;",   L'ú', },
263         { "&ucirc;",    L'û', },
264         { "&ugrave;",   L'ù', },
265         { "&uuml;",     L'ü', },
266         { "&yacute;",   L'ý', },
267         { "&yuml;",     L'ÿ', },
268         { "&#161;",     L'¡', },
269         { "&#162;",     L'¢', },
270         { "&#163;",     L'£', },
271         { "&#164;",     L'¤', },
272         { "&#165;",     L'¥', },
273         { "&#166;",     L'¦', },
274         { "&#167;",     L'§', },
275         { "&#168;",     L'¨', },
276         { "&#169;",     L'©', },
277         { "&#170;",     L'ª', },
278         { "&#171;",     L'«', },
279         { "&#172;",     L'¬', },
280         { "&#173;",     L'­', },
281         { "&#174;",     L'®', },
282         { "&#175;",     L'¯', },
283         { "&#176;",     L'°', },
284         { "&#177;",     L'±', },
285         { "&#178;",     L'²', },
286         { "&#179;",     L'³', },
287         { "&#180;",     L'´', },
288         { "&#181;",     L'µ', },
289         { "&#182;",     L'¶', },
290         { "&#183;",     L'·', },
291         { "&#184;",     L'¸', },
292         { "&#185;",     L'¹', },
293         { "&#186;",     L'º', },
294         { "&#187;",     L'»', },
295         { "&#188;",     L'¼', },
296         { "&#189;",     L'½', },
297         { "&#190;",     L'¾', },
298         { "&#191;",     L'¿', },
299
300         { "*",                  L'•', },
301         { "&#164;",     L'□', },
302         { "&#186;",     L'◊', },
303         { "(tm)",               L'™', },
304         {"&#913;",              L'Α',},
305         {"&#914;",              L'Β',},
306         {"&#915;",              L'Γ',},
307         {"&#916;",              L'Δ',},
308         {"&#917;",              L'Ε',},
309         {"&#918;",              L'Ζ',},
310         {"&#919;",              L'Η',},
311         {"&#920;",              L'Θ',},
312         {"&#921;",              L'Ι',},
313         {"&#922;",              L'Κ',},
314         {"&#923;",              L'Λ',},
315         {"&#924;",              L'Μ',},
316         {"&#925;",              L'Ν',},
317         {"&#926;",              L'Ξ',},
318         {"&#927;",              L'Ο',},
319         {"&#928;",              L'Π',},
320         {"&#929;",              L'Ρ',},
321         {"&#930;",              L'΢',},
322         {"&#931;",              L'Σ',},
323         {"&#932;",              L'Τ',},
324         {"&#933;",              L'Υ',},
325         {"&#934;",              L'Φ',},
326         {"&#935;",              L'Χ',},
327         {"&#936;",              L'Ψ',},
328         {"&#937;",              L'Ω',},
329         {"&#945;",              L'α',},
330         {"&#946;",              L'β',},
331         {"&#947;",              L'γ',},
332         {"&#948;",              L'δ',},
333         {"&#949;",              L'ε',},
334         {"&#950;",              L'ζ',},
335         {"&#951;",              L'η',},
336         {"&#952;",              L'θ',},
337         {"&#953;",              L'ι',},
338         {"&#954;",              L'κ',},
339         {"&#955;",              L'λ',},
340         {"&#956;",              L'μ',},
341         {"&#957;",              L'ν',},
342         {"&#958;",              L'ξ',},
343         {"&#959;",              L'ο',},
344         {"&#960;",              L'π',},
345         {"&#961;",              L'ρ',},
346         {"&#962;",              L'ς',},
347         {"&#963;",              L'σ',},
348         {"&#964;",              L'τ',},
349         {"&#965;",              L'υ',},
350         {"&#966;",              L'φ',},
351         {"&#967;",              L'χ',},
352         {"&#968;",              L'ψ',},
353         {"&#969;",              L'ω',},
354
355         { "<-",         L'←', },
356         { "^",                  L'↑', },
357         { "->",         L'→', },
358         { "v",                  L'↓', },
359         { "!=",         L'≠', },
360         { "<=",         L'≤', },
361         { "...",                L'⋯', },
362         {"&isin;",              L'∈', },
363
364         {"&#8211;",     L'–', },
365         {"&#8212;",     L'—', },
366
367         { "CYRILLIC XYZZY",     L'й', },
368         { "CYRILLIC XYZZY",     L'ъ', },
369         { "CYRILLIC Y",         L'ь', },
370         { "CYRILLIC YA",        L'я', },
371         { "CYRILLIC YA",        L'ё', },
372         { "&#191;",             L'ℱ', },
373
374         { nil, 0 },
375 };
376
377 typedef struct Troffspec Troffspec;
378 struct Troffspec
379 {
380         char *name;
381         char *value;
382 };
383
384 Troffspec tspec[] =
385 {
386         { "A*", "&Aring;", },
387         { "o\"", "&ouml;", },
388         { "ff", "ff", },
389         { "fi", "fi", },
390         { "fl", "fl", },
391         { "Fi", "ffi", },
392         { "ru", "_", },
393         { "em", "&#173;", },
394         { "14", "&#188;", },
395         { "12", "&#189;", },
396         { "co", "&#169;", },
397         { "de", "&#176;", },
398         { "dg", "&#161;", },
399         { "fm", "&#180;", },
400         { "rg", "&#174;", },
401         { "bu", "*", },
402         { "sq", "&#164;", },
403         { "hy", "-", },
404         { "pl", "+", },
405         { "mi", "-", },
406         { "mu", "&#215;", },
407         { "di", "&#247;", },
408         { "eq", "=", },
409         { "==", "==", },
410         { ">=", ">=", },
411         { "<=", "<=", },
412         { "!=", "!=", },
413         { "+-", "&#177;", },
414         { "no", "&#172;", },
415         { "sl", "/", },
416         { "ap", "&", },
417         { "~=", "~=", },
418         { "pt", "oc", },
419         { "gr", "GRAD", },
420         { "->", "->", },
421         { "<-", "<-", },
422         { "ua", "^", },
423         { "da", "v", },
424         { "is", "Integral", },
425         { "pd", "DIV", },
426         { "if", "oo", },
427         { "sr", "-/", },
428         { "sb", "(~", },
429         { "sp", "~)", },
430         { "cu", "U", },
431         { "ca", "(^)", },
432         { "ib", "(=", },
433         { "ip", "=)", },
434         { "mo", "C", },
435         { "es", "&Oslash;", },
436         { "aa", "&#180;", },
437         { "ga", "`", },
438         { "ci", "O", },
439         { "L1", "DEATHSTAR", },
440         { "sc", "&#167;", },
441         { "dd", "++", },
442         { "lh", "<=", },
443         { "rh", "=>", },
444         { "lt", "(", },
445         { "rt", ")", },
446         { "lc", "|", },
447         { "rc", "|", },
448         { "lb", "(", },
449         { "rb", ")", },
450         { "lf", "|", },
451         { "rf", "|", },
452         { "lk", "|", },
453         { "rk", "|", },
454         { "bv", "|", },
455         { "ts", "s", },
456         { "br", "|", },
457         { "or", "|", },
458         { "ul", "_", },
459         { "rn", " ", },
460         { "**", "*", },
461         { "tm", "&#153", },
462         { nil, nil, },
463 };
464
465 typedef struct Font Font;
466 struct Font
467 {
468         char    *start;
469         char    *end;
470 };
471 Font bfont = { "<B>", "</B>" };
472 Font ifont = { "<I>", "</I>" };
473 Font bifont = { "<B><I>", "</I></B>" };
474 Font cwfont = { "<TT>", "</TT>" };
475 Font *fstack[Maxfsp];
476 int fsp = -1;
477
478 typedef struct String String;
479 struct String
480 {
481         String *next;
482         char *name;
483         char *val;
484 };
485 String *numregs, *strings;
486 char *strstack[Maxmstack];
487 char *mustfree[Maxmstack];
488 int strsp = -1;
489 int elsetop = -1;
490
491 typedef struct Mstack Mstack;
492 struct Mstack
493 {
494         char *ptr;
495         char *argv[Narg+1];
496 };
497 String *macros;
498 Mstack mstack[Maxmstack];
499 int msp = -1;
500
501 typedef struct Srcstack Srcstack;
502 struct Srcstack
503 {
504         char    filename[256];
505         int     fd;
506         int     lno;
507         int     rlno;
508         Biobuf  in;
509 };
510 Srcstack sstack[Maxsstack];
511 Srcstack *ssp = &sstack[-1];
512
513 char token[128];
514
515 void    closel(void);
516 void    closefont(void);
517
518 void*
519 emalloc(uint n)
520 {
521         void *p;
522
523         p = mallocz(n, 1);
524         if(p == nil){
525                 fprint(2, "ms2html: malloc failed: %r\n");
526                 exits("malloc");
527         }
528         return p;
529 }
530
531
532 /* define a string variable */
533 void
534 dsnr(char *name, char *val, String **l)
535 {
536         String *s;
537
538         for(s = *l; s != nil; s = *l){
539                 if(strcmp(s->name, name) == 0)
540                         break;
541                 l = &s->next;
542         }
543         if(s == nil){
544                 s = emalloc(sizeof(String));
545                 *l = s;
546                 s->name = strdup(name);
547         } else
548                 free(s->val);
549         s->val = strdup(val);
550 }
551
552 void
553 ds(char *name, char *val)
554 {
555         dsnr(name, val, &strings);
556 }
557
558 /* look up a defined string */
559 char*
560 getds(char *name)
561 {
562         String *s;
563
564         for(s = strings; s != nil; s = s->next)
565                 if(strcmp(name, s->name) == 0)
566                         break;
567         if(s != nil)
568                 return s->val;
569         return "";
570 }
571
572 char *
573 getnr(char *name)
574 {
575         String *s;
576
577         for(s = numregs; s != nil; s = s->next)
578                 if(strcmp(name, s->name) == 0)
579                         break;
580         if(s != nil)
581                 return s->val;
582         return "0";
583 }
584
585 void
586 pushstr(char *p)
587 {
588         if(p == nil)
589                 return;
590         if(strsp >= Maxmstack - 1)
591                 return;
592         strstack[++strsp] = p;
593 }
594
595
596 /* lookup a defined macro */
597 char*
598 getmacro(char *name)
599 {
600         String *s;
601
602         for(s = macros; s != nil; s = s->next)
603                 if(strcmp(name, s->name) == 0)
604                         return s->val;
605         return nil;
606 }
607
608 enum
609 {
610         Dstring,
611         Macro,
612         Input,
613 };
614 int lastsrc;
615
616 void
617 pushsrc(char *name)
618 {
619         Dir *d;
620         int fd;
621
622         if(ssp == &sstack[Maxsstack-1]){
623                 fprint(2, "ms2html: .so's too deep\n");
624                 return;
625         }
626         d = nil;
627         if(name == nil){
628                 d = dirfstat(0);
629                 if(d == nil){
630                         fprint(2, "ms2html: can't stat %s: %r\n", name);
631                         return;
632                 }
633                 name = d->name;
634                 fd = 0;
635         } else {
636                 fd = open(name, OREAD);
637                 if(fd < 0){
638                         fprint(2, "ms2html: can't open %s: %r\n", name);
639                         return;
640                 }
641         }
642         ssp++;
643         ssp->fd = fd;
644         Binit(&ssp->in, fd, OREAD);
645         snprint(ssp->filename, sizeof(ssp->filename), "%s", name);
646         ssp->lno = ssp->rlno = 1;
647         free(d);
648 }
649
650 /* get next logical byte.  from stdin or a defined string */
651 int
652 getrune(void)
653 {
654         int i;
655         Rune r;
656         int c;
657         Mstack *m;
658
659         while(strsp >= 0){
660                 i = chartorune(&r, strstack[strsp]);
661                 if(r != 0){
662                         strstack[strsp] += i;
663                         lastsrc = Dstring;
664                         return r;
665                 }
666                 if (mustfree[strsp]) {
667                         free(mustfree[strsp]);
668                         mustfree[strsp] = nil;
669                 }
670                 strsp--;
671         }
672         while(msp >= 0){
673                 m = &mstack[msp];
674                 i = chartorune(&r, m->ptr);
675                 if(r != 0){
676                         m->ptr += i;
677                         lastsrc = Macro;
678                         return r;
679                 }
680                 for(i = 0; m->argv[i] != nil; i++)
681                         free(m->argv[i]);
682                 msp--;
683         }
684
685         lastsrc = Input;
686         for(;;) {
687                 if(ssp < sstack)
688                         return -1;
689                 c = Bgetrune(&ssp->in);
690                 if(c >= 0){
691                         r = c;
692                         break;
693                 }
694                 close(ssp->fd);
695                 ssp--;
696         }
697
698         return r;
699 }
700
701 void
702 ungetrune(void)
703 {
704         switch(lastsrc){
705         case Dstring:
706                 if(strsp >= 0)
707                         strstack[strsp]--;
708                 break;
709         case Macro:
710                 if(msp >= 0)
711                         mstack[msp].ptr--;
712                 break;
713         case Input:
714                 if(ssp >= sstack)
715                         Bungetrune(&ssp->in);
716                 break;
717         }
718 }
719
720 int vert;
721
722 char*
723 changefont(Font *f)
724 {
725         token[0] = 0;
726         if(fsp == Maxfsp)
727                 return token;
728         if(fsp >= 0 && fstack[fsp])
729                 strcpy(token, fstack[fsp]->end);
730         if(f != nil)
731                 strcat(token, f->start);
732         fstack[++fsp] = f;
733         return token;
734 }
735
736 char*
737 changebackfont(void)
738 {
739         token[0] = 0;
740         if(fsp >= 0){
741                 if(fstack[fsp])
742                         strcpy(token, fstack[fsp]->end);
743                 fsp--;
744         }
745         if(fsp >= 0 && fstack[fsp])
746                 strcat(token, fstack[fsp]->start);
747         return token;
748 }
749
750 char*
751 changesize(int amount)
752 {
753         static int curamount;
754         static char buf[200];
755         int i;
756
757         buf[0] = 0;
758         if (curamount >= 0)
759                 for (i = 0; i < curamount; i++)
760                         strcat(buf, "</big>");
761         else
762                 for (i = 0; i < -curamount; i++)
763                         strcat(buf, "</small>");
764         curamount = 0;
765         if (amount >= 0)
766                 for (i = 0; i < amount; i++)
767                         strcat(buf, "<big>");
768         else
769                 for (i = 0; i < -amount; i++)
770                         strcat(buf, "<small>");
771         curamount = amount;
772         return buf;
773 }
774
775 /* get next logical character.  expand it with escapes */
776 char*
777 getnext(void)
778 {
779         int r;
780         Entity *e;
781         Troffspec *t;
782         Rune R;
783         char str[4];
784         static char buf[8];
785
786         r = getrune();
787         if(r < 0)
788                 return nil;
789         if(r > 128 || r == '<' || r == '>'){
790                 for(e = entity; e->name; e++)
791                         if(e->value == r)
792                                 return e->name;
793                 sprint(buf, "&#%d;", r);
794                 return buf;
795         }
796
797         if (r == delim[eqnmode]){
798                 if (eqnmode == 0){
799                         eqnmode = 1;
800                         return changefont(&ifont);
801                 }
802                 eqnmode = 0;
803                 return changebackfont();
804         }
805         switch(r){
806         case '\\':
807                 r = getrune();
808                 if(r < 0)
809                         return nil;
810                 switch(r){
811                 case ' ':
812                         return " ";
813
814                 /* chars to ignore */
815                 case '&':
816                 case '|':
817                 case '%':
818                         return "";
819
820                 /* small space in troff, nothing in nroff */
821                 case '^':
822                         return getnext();
823
824                 /* ignore arg */
825                 case 'k':
826                         getrune();
827                         return getnext();
828
829                 /* comment */
830                 case '"':
831                         while(getrune() != '\n')
832                                 ;
833                         return "\n";
834                 /* ignore line */
835                 case '!':
836                         while(getrune() != '\n')
837                                 ;
838                         ungetrune();
839                         return getnext();
840
841                 /* defined strings */
842                 case '*':
843                         r = getrune();
844                         if(r == '('){
845                                 str[0] = getrune();
846                                 str[1] = getrune();
847                                 str[2] = 0;
848                         } else {
849                                 str[0] = r;
850                                 str[1] = 0;
851                         }
852                         pushstr(getds(str));
853                         return getnext();
854
855                 /* macro args */
856                 case '$':
857                         r = getrune();
858                         if(r < '1' || r > '9'){
859                                 token[0] = '\\';
860                                 token[1] = '$';
861                                 token[2] = r;
862                                 token[3] = 0;
863                                 return token;
864                         }
865                         r -= '0';
866                         if(msp >= 0) 
867                                 pushstr(mstack[msp].argv[r]);
868                         return getnext();
869
870                 /* special chars */
871                 case '(':
872                         token[0] = getrune();
873                         token[1] = getrune();
874                         token[2] = 0;
875                         for(t = tspec; t->name; t++)
876                                 if(strcmp(token, t->name) == 0)
877                                         return t->value;
878                         return "&#191;";
879
880                 /* ignore immediately following newline */
881                 case 'c':
882                         r = getrune();
883                         if (r == '\n') {
884                                 sol = ignore_nl = 1;
885                                 if (indirective)
886                                         break;
887                                 }
888                         else
889                                 ungetrune();
890                         return getnext();
891
892                 /* escape backslash */
893                 case 'e':
894                         return "\\";
895
896                 /* font change */
897                 case 'f':
898                         r = getrune();
899                         switch(r){
900                         case '(':
901                                 str[0] = getrune();
902                                 str[1] = getrune();
903                                 str[2] = 0;
904                                 token[0] = 0;
905                                 if(strcmp("BI", str) == 0)
906                                         return changefont(&bifont);
907                                 else if(strcmp("CW", str) == 0)
908                                         return changefont(&cwfont);
909                                 else
910                                         return changefont(nil);
911                         case '3':
912                         case 'B':
913                                 return changefont(&bfont);
914                         case '2':
915                         case 'I':
916                                 return changefont(&ifont);
917                         case '4':
918                                 return changefont(&bifont);
919                         case '5':
920                                 return changefont(&cwfont);
921                         case 'P':
922                                 return changebackfont();
923                         case 'R':
924                         default:
925                                 return changefont(nil);
926                         }
927
928                 /* number register */
929                 case 'n':
930                         r = getrune();
931                         if (r == '(') /*)*/ {
932                                 r = getrune();
933                                 if (r < 0)
934                                         return nil;
935                                 str[0] = r;
936                                 r = getrune();
937                                 if (r < 0)
938                                         return nil;
939                                 str[1] = r;
940                                 str[2] = 0;
941                                 }
942                         else {
943                                 str[0] = r;
944                                 str[1] = 0;
945                                 }
946                         pushstr(getnr(str));
947                         return getnext();
948
949                 /* font size */
950                 case 's':
951                         r = getrune();
952                         switch(r){
953                         case '0':
954                                 return changesize(0);
955                         case '-':
956                                 r = getrune();
957                                 if (!isdigit(r))
958                                         return getnext();
959                                 return changesize(-(r - '0'));
960                         case '+':
961                                 r = getrune();
962                                 if (!isdigit(r))
963                                         return getnext();
964                                 return changesize(r - '0');
965                         }
966                         return getnext();
967                 /* vertical movement */
968                 case 'v':
969                         r = getrune();
970                         if(r != '\''){
971                                 ungetrune();
972                                 return getnext();
973                         }
974                         r = getrune();
975                         if(r != '-')
976                                 vert--;
977                         else
978                                 vert++;
979                         while(r != '\'' && r != '\n')
980                                 r = getrune();
981                         if(r != '\'')
982                                 ungetrune();
983                         
984                         if(vert > 0)
985                                 return "^";
986                         return getnext();
987                         
988
989                 /* horizontal line */
990                 case 'l':
991                         r = getrune();
992                         if(r != '\''){
993                                 ungetrune();
994                                 return "<HR>";
995                         }
996                         while(getrune() != '\'')
997                                 ;
998                         return "<HR>";
999
1000                 /* character height and slant */
1001                 case 'S':
1002                 case 'H':
1003                         r = getrune();
1004                         if(r != '\''){
1005                                 ungetrune();
1006                                 return "<HR>";
1007                         }
1008                         while(getrune() != '\'')
1009                                 ;
1010                         return getnext();
1011
1012                 /* digit-width space */
1013                 case '0':
1014                         return " ";
1015
1016                 /*for .if, .ie, .el */
1017                 case '{':
1018                         return "\\{"; /*}*/
1019                 case '}':
1020                         return "";
1021                 /* up and down */
1022                 case 'u':
1023                         if (isdown) {
1024                                 isdown = 0;
1025                                 return "</sub>";
1026                         }
1027                         isup = 1;
1028                         return "<sup>";
1029                 case 'd':
1030                         if (isup) {
1031                                 isup = 0;
1032                                 return "</sup>";
1033                         }
1034                         isdown = 1;
1035                         return "<sub>";
1036                 }
1037                 break;
1038         case '&':
1039                 if(msp >= 0 || strsp >= 0)
1040                         return "&";
1041                 return "&amp;";
1042         case '<':
1043                 if(msp >= 0 || strsp >= 0)
1044                         return "<";
1045                 return "&#60;";
1046         case '>':
1047                 if(msp >= 0 || strsp >= 0)
1048                         return ">";
1049                 return "&#62;";
1050         }
1051         if (r < Runeself) {
1052                 token[0] = r;
1053                 token[1] = 0;
1054                 }
1055         else {
1056                 R = r;
1057                 token[runetochar(token,&R)] = 0;
1058         }
1059         return token;
1060 }
1061
1062 /* if arg0 is set, read up to (and expand) to the next whitespace, else to the end of line */
1063 char*
1064 copyline(char *p, char *e, int arg0)
1065 {
1066         int c;
1067         Rune r;
1068         char *p1;
1069
1070         while((c = getrune()) == ' ' || c == '\t')
1071                 ;
1072         for(indirective = 1; p < e; c = getrune()) {
1073                 if (c < 0)
1074                         goto done;
1075                 switch(c) {
1076                 case '\\':
1077                         break;
1078                 case '\n':
1079                         if (arg0)
1080                                 ungetrune();
1081                         goto done;
1082                 case ' ':
1083                 case '\t':
1084                         if (arg0)
1085                                 goto done;
1086                 default:
1087                         r = c;
1088                         p += runetochar(p,&r);
1089                         continue;
1090                 }
1091                 ungetrune();
1092                 p1 = getnext();
1093                 if (p1 == nil)
1094                         goto done;
1095                 if (*p1 == '\n') {
1096                         if (arg0)
1097                                 ungetrune();
1098                         break;
1099                 }
1100                 while((*p = *p1++) && p < e)
1101                         p++;
1102         }
1103 done:
1104         indirective = 0;
1105         *p++ = 0;
1106         return p;
1107 }
1108
1109 char*
1110 copyarg(char *p, char *e, int *nullarg)
1111 {
1112         int c, quoted, last;
1113         Rune r;
1114
1115         *nullarg = 0;
1116         quoted = 0;
1117         do{
1118                 c = getrune();
1119         } while(c == ' ' || c == '\t');
1120
1121         if(c == '"'){
1122                 quoted = 1;
1123                 *nullarg = 1;
1124                 c = getrune();
1125         }
1126
1127         if(c == '\n')
1128                 goto done;
1129
1130         last = 0;
1131         for(; p < e; c = getrune()) {
1132                 if (c < 0)
1133                         break;
1134                 switch(c) {
1135                 case '\n':
1136                         ungetrune();
1137                         goto done;
1138                 case '\\':
1139                         r = c;
1140                         p += runetochar(p,&r);
1141                         if(last == '\\')
1142                                 r = 0;
1143                         break;
1144                 case ' ':
1145                 case '\t':
1146                         if(!quoted && last != '\\')
1147                                 goto done;
1148                         r = c;
1149                         p += runetochar(p,&r);
1150                         break;
1151                 case '"':
1152                         if(quoted && last != '\\')
1153                                 goto done;
1154                         r = c;
1155                         p += runetochar(p,&r);
1156                         break;
1157                 default:
1158                         r = c;
1159                         p += runetochar(p,&r);
1160                         break;
1161                 }
1162                 last = r;
1163         }
1164 done:
1165         *p++ = 0;
1166         return p;
1167
1168 }
1169
1170 int
1171 parseargs(char *p, char *e, char **argv)
1172 {
1173         int argc;
1174         char *np;
1175         int nullarg;
1176
1177         indirective = 1;
1178         *p++ = 0;
1179         for(argc = 1; argc < Narg; argc++){
1180                 np = copyarg(p, e, &nullarg);
1181                 if(nullarg==0 && np == p+1)
1182                         break;
1183                 argv[argc] = p;
1184                 p = np;
1185         }
1186         argv[argc] = nil;
1187         indirective = 0;
1188
1189
1190         return argc;
1191 }
1192
1193 void
1194 dodirective(void)
1195 {
1196         char *p, *e;
1197         Goobie *g;
1198         Goobieif *gif;
1199         char line[Nline], *line1;
1200         int i, argc;
1201         char *argv[Narg];
1202         Mstack *m;
1203
1204         /* read line, translate special bytes */
1205         e = line + sizeof(line) - UTFmax - 1;
1206         line1 = copyline(line, e, 1);
1207         if (!line[0])
1208                 return;
1209         argv[0] = line;
1210
1211         /* first look through user defined macros */
1212         p = getmacro(argv[0]);
1213         if(p != nil){
1214                 if(msp == Maxmstack-1){
1215                         fprint(2, "ms2html: macro stack overflow\n");
1216                         return;
1217                 }
1218                 argc = parseargs(line1, e, argv);
1219                 m = &mstack[++msp];
1220                 m->ptr = p;
1221                 memset(m->argv, 0, sizeof(m->argv));
1222                 for(i = 0; i < argc; i++)
1223                         m->argv[i] = strdup(argv[i]);
1224                 return;
1225         }
1226
1227         /* check for .if or .ie */
1228         for(gif = gtabif; gif->name; gif++)
1229                 if(strcmp(gif->name, argv[0]) == 0){
1230                         (*gif->f)(line1, e);
1231                         return;
1232                 }
1233
1234         argc = parseargs(line1, e, argv);
1235
1236         /* try standard ms macros */
1237         for(g = gtab; g->name; g++)
1238                 if(strcmp(g->name, argv[0]) == 0){
1239                         (*g->f)(argc, argv);
1240                         return;
1241                 }
1242
1243         if(debug)
1244                 fprint(2, "stdin %d(%s:%d): unknown directive %s\n",
1245                         ssp->lno, ssp->filename, ssp->rlno, line);
1246 }
1247
1248 void
1249 printarg(char *a)
1250 {
1251         char *e, *p;
1252         
1253         e = a + strlen(a);
1254         pushstr(a);
1255         while(strsp >= 0 && strstack[strsp] >= a && strstack[strsp] < e){
1256                 p = getnext();
1257                 if(p == nil)
1258                         return;
1259                 Bprint(&bout, "%s", p);
1260         }
1261 }
1262
1263 void
1264 printargs(int argc, char **argv)
1265 {
1266         argc--;
1267         argv++;
1268         while(--argc > 0){
1269                 printarg(*argv++);
1270                 Bprint(&bout, " ");
1271         }
1272         if(argc == 0)
1273                 printarg(*argv);
1274 }
1275
1276 void
1277 dohangingdt(void)
1278 {
1279         switch(hangingdt){
1280         case 3:
1281                 hangingdt--;
1282                 break;
1283         case 2:
1284                 Bprint(&bout, "<dd>");
1285                 hangingdt = 0;
1286                 break;
1287         }
1288
1289 }
1290
1291 void
1292 dohangingau(void)
1293 {
1294         if(hangingau == 0)
1295                 return;
1296         Bprint(&bout, "</I></DL>\n");
1297         hangingau = 0;
1298 }
1299
1300 void
1301 dohanginghead(void)
1302 {
1303         if(hanginghead == 0)
1304                 return;
1305         Bprint(&bout, "</H%d>\n", hanginghead);
1306         hanginghead = 0;
1307 }
1308
1309 /*
1310  *  convert a man page to html and output
1311  */
1312 void
1313 doconvert(void)
1314 {
1315         char c, *p;
1316
1317         pushsrc(nil);
1318
1319         sol = 1;
1320         Bprint(&bout, "<html>\n");
1321         Bflush(&bout);
1322         for(;;){
1323                 p = getnext();
1324                 if(p == nil)
1325                         break;
1326                 c = *p;
1327                 if(c == '.' && sol){
1328                         dodirective();
1329                         dohangingdt();
1330                         ssp->lno++;
1331                         ssp->rlno++;
1332                         sol = 1;
1333                 } else if(c == '\n'){
1334                         if (ignore_nl)
1335                                 ignore_nl = 0;
1336                         else {
1337                                 if(hangingau)
1338                                         Bprint(&bout, "<br>\n");
1339                                 else
1340                                         Bprint(&bout, "%s", p);
1341                                 dohangingdt();
1342                                 }
1343                         ssp->lno++;
1344                         ssp->rlno++;
1345                         sol = 1;
1346                 } else{
1347                         Bprint(&bout, "%s", p);
1348                         ignore_nl = sol = 0;
1349                 }
1350         }
1351         dohanginghead();
1352         dohangingdt();
1353         closel();
1354         if(fsp >= 0 && fstack[fsp])
1355                 Bprint(&bout, "%s", fstack[fsp]->end);
1356         Bprint(&bout, "<br>&#32;<br>\n");
1357         Bprint(&bout, "</body></html>\n");
1358 }
1359
1360 static void
1361 usage(void)
1362 {
1363         sysfatal("usage: ms2html [-q] [-b basename] [-d '$$'] [-t title]");
1364 }
1365
1366 void
1367 main(int argc, char **argv)
1368 {
1369         quiet = 1;
1370         ARGBEGIN {
1371         case 't':
1372                 title = EARGF(usage());
1373                 break;
1374         case 'b':
1375                 basename = EARGF(usage());
1376                 break;
1377         case 'q':
1378                 quiet = 0;
1379                 break;
1380         case 'd':
1381                 delim = EARGF(usage());
1382                 break;
1383         case '?':
1384         default:
1385                 usage();
1386         } ARGEND;
1387
1388         Binit(&bout, 1, OWRITE);
1389
1390         ds("R", "&#174;");
1391
1392         doconvert();
1393         exits(nil);
1394 }
1395
1396 void
1397 g_notyet(int, char **argv)
1398 {
1399         fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
1400 }
1401
1402 void
1403 g_ignore(int, char **argv)
1404 {
1405         if(quiet)
1406                 return;
1407         fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
1408 }
1409
1410 void
1411 g_PP(int, char**)
1412 {
1413         dohanginghead();
1414         closel();
1415         closefont();
1416         Bprint(&bout, "<P>\n");
1417         paragraph = 1;
1418 }
1419
1420 void
1421 g_LP(int, char**)
1422 {
1423         dohanginghead();
1424         closel();
1425         closefont();
1426         Bprint(&bout, "<br>&#32;<br>\n");
1427 }
1428
1429 /* close a list */
1430 void
1431 closel(void)
1432 {
1433         g_P2(1, nil);
1434         dohangingau();
1435         if(paragraph){
1436                 Bprint(&bout, "</P>\n");
1437                 paragraph = 0;
1438         }
1439         switch(list){
1440         case Lordered:
1441                 Bprint(&bout, "</ol>\n");
1442                 break;
1443         case Lunordered:
1444                 Bprint(&bout, "</ul>\n");
1445                 break;
1446         case Lother:
1447         case Ldef:
1448                 Bprint(&bout, "</dl>\n");
1449                 break;
1450         }
1451         list = 0;
1452         
1453 }
1454
1455
1456 void
1457 g_IP(int argc, char **argv)
1458 {
1459         switch(list){
1460         default:
1461                 closel();
1462                 if(argc > 1){
1463                         if(strcmp(argv[1], "1") == 0){
1464                                 list = Lordered;
1465                                 listnum = 1;
1466                                 Bprint(&bout, "<OL>\n");
1467                         } else if(strcmp(argv[1], "\\(bu") == 0){
1468                                 list = Lunordered;
1469                                 Bprint(&bout, "<UL>\n");
1470                         } else {
1471                                 list = Lother;
1472                                 Bprint(&bout, "<DL COMPACT>\n");
1473                         }
1474                 } else {
1475                         list = Lother;
1476                         Bprint(&bout, "<DL>\n");
1477                 }
1478                 break;
1479         case Lother:
1480         case Lordered:
1481         case Lunordered:
1482                 break;
1483         }
1484
1485         switch(list){
1486         case Lother:
1487                 Bprint(&bout, "<DT>");
1488                 if(argc > 1)
1489                         printarg(argv[1]);
1490                 else
1491                         Bprint(&bout, "<DT>&#32;");
1492                 Bprint(&bout, "<DD>\n");
1493                 break;
1494         case Lordered:
1495         case Lunordered:
1496                 Bprint(&bout, "<LI>\n");
1497                 break;
1498         }
1499 }
1500
1501 /*
1502  *  .5i is one <DL><DT><DD>
1503  */
1504 void
1505 g_in(int argc, char **argv)
1506 {
1507         float   f;
1508         int     delta, x;
1509         char    *p;
1510
1511         f = indent/0.5;
1512         delta = f;
1513         if(argc <= 1){
1514                 indent = 0.0;
1515         } else {
1516                 f = strtod(argv[1], &p);
1517                 switch(*p){
1518                 case 'i':
1519                         break;
1520                 case 'c':
1521                         f = f / 2.54;
1522                         break;
1523                 case 'P':
1524                         f = f / 6;
1525                         break;
1526                 default:
1527                 case 'u':
1528                 case 'm':
1529                         f = f * (12 / 72);
1530                         break;
1531                 case 'n':
1532                         f = f * (6 / 72);
1533                         break;
1534                 case 'p':
1535                         f = f / 72.0;
1536                         break;
1537                 }
1538                 switch(argv[1][0]){
1539                 case '+':
1540                 case '-':
1541                         indent += f;
1542                         break;
1543                 default:
1544                         indent = f;
1545                         break;
1546                 }
1547         }
1548         if(indent < 0.0)
1549                 indent = 0.0;
1550         f = (indent/0.5);
1551         x = f;
1552         delta = x - delta;
1553         while(delta < 0){
1554                 Bprint(&bout, "</DL>\n");
1555                 delta++;
1556         }
1557         while(delta > 0){
1558                 Bprint(&bout, "<DL><DT><DD>\n");
1559                 delta--;
1560         }
1561 }
1562
1563 void
1564 g_HP(int, char**)
1565 {
1566         switch(list){
1567         default:
1568                 closel();
1569                 list = Ldef;
1570                 hangingdt = 1;
1571                 Bprint(&bout, "<DL><DT>\n");
1572                 break;
1573         case Ldef:
1574                 if(hangingdt)
1575                         Bprint(&bout, "<DD>");
1576                 Bprint(&bout, "<DT>");
1577                 hangingdt = 1;
1578                 break;
1579         }
1580 }
1581
1582 void
1583 g_SH(int, char**)
1584 {
1585         dohanginghead();
1586         dohangingcenter();
1587         closel();
1588         closefont();
1589         Bprint(&bout, "<H%d>", HH);
1590         hanginghead = HH;
1591 }
1592
1593 void
1594 g_NH(int argc, char **argv)
1595 {
1596         int i, level;
1597
1598         closel();
1599         closefont();
1600
1601         dohangingcenter();
1602         if(argc == 1)
1603                 level = 0;
1604         else {
1605                 level = atoi(argv[1])-1;
1606                 if(level < 0 || level >= Maxnh)
1607                         level = Maxnh - 1;
1608         }
1609         nh[level]++;
1610
1611         Bprint(&bout, "<H%d>", HH);
1612         hanginghead = HH;
1613
1614         Bprint(&bout, "%d", nh[0]);
1615         for(i = 1; i <= level; i++)
1616                 Bprint(&bout, ".%d", nh[i]);
1617         Bprint(&bout, " ");
1618
1619         for(i = level+1; i < Maxnh; i++)
1620                 nh[i] = 0;
1621 }
1622
1623 void
1624 g_TL(int, char**)
1625 {
1626         char *p, *np;
1627         char name[128];
1628
1629         closefont();
1630
1631         if(!titleseen){
1632                 if(!title){
1633                         /* get base part of filename */
1634                         p = strrchr(ssp->filename, '/');
1635                         if(p == nil)
1636                                 p = ssp->filename;
1637                         else
1638                                 p++;
1639                         strncpy(name, p, sizeof(name));
1640                         name[sizeof(name)-1] = 0;
1641                 
1642                         /* dump any extensions */
1643                         np = strchr(name, '.');
1644                         if(np)
1645                                 *np = 0;
1646                         title = p;
1647                 }
1648                 Bprint(&bout, "<title>\n");
1649                 Bprint(&bout, "%s\n", title);
1650                 Bprint(&bout, "</title>\n");
1651                 Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n");
1652                 titleseen = 1;
1653         }
1654
1655         Bprint(&bout, "<center>");
1656         hangingcenter = 1;
1657         Bprint(&bout, "<H%d>", 1);
1658         hanginghead = 1;
1659 }
1660
1661 void
1662 dohangingcenter(void)
1663 {
1664         if(hangingcenter){
1665                 Bprint(&bout, "</center>");
1666                 hangingcenter = 1;
1667         }
1668 }
1669
1670 void
1671 g_AU(int, char**)
1672 {
1673         closel();
1674         dohanginghead();
1675         Bprint(&bout, "<DL><DD><I>");
1676         hangingau = 1;
1677 }
1678
1679 void
1680 pushfont(Font *f)
1681 {
1682         if(fsp == Maxfsp)
1683                 return;
1684         if(fsp >= 0 && fstack[fsp])
1685                 Bprint(&bout, "%s", fstack[fsp]->end);
1686         if(f != nil)
1687                 Bprint(&bout, "%s", f->start);
1688         fstack[++fsp] = f;
1689 }
1690
1691 void
1692 popfont(void)
1693 {
1694         if(fsp >= 0){
1695                 if(fstack[fsp])
1696                         Bprint(&bout, "%s", fstack[fsp]->end);
1697                 fsp--;
1698         }
1699 }
1700
1701 /*
1702  *  for 3 args print arg3 \fxarg1\fP arg2
1703  *  for 2 args print arg1 \fxarg2\fP
1704  *  for 1 args print \fxarg1\fP
1705  */
1706 void
1707 font(Font *f, int argc, char **argv)
1708 {
1709         if(argc == 1){
1710                 pushfont(nil);
1711                 return;
1712         }
1713         if(argc > 3)
1714                 printarg(argv[3]);
1715         pushfont(f);
1716         printarg(argv[1]);
1717         popfont();
1718         if(argc > 2)
1719                 printarg(argv[2]);
1720         Bprint(&bout, "\n");
1721 }
1722
1723 void
1724 closefont(void)
1725 {
1726         if(fsp >= 0 && fstack[fsp])
1727                 Bprint(&bout, "%s", fstack[fsp]->end);
1728         fsp = -1;
1729 }
1730
1731 void
1732 g_B(int argc, char **argv)
1733 {
1734         font(&bfont, argc, argv);
1735 }
1736
1737 void
1738 g_R(int argc, char **argv)
1739 {
1740         font(nil, argc, argv);
1741 }
1742
1743 void
1744 g_BI(int argc, char **argv)
1745 {
1746         font(&bifont, argc, argv);
1747 }
1748
1749 void
1750 g_CW(int argc, char **argv)
1751 {
1752         font(&cwfont, argc, argv);
1753 }
1754
1755 char*
1756 lower(char *p)
1757 {
1758         char *x;
1759
1760         for(x = p; *x; x++)
1761                 if(*x >= 'A' && *x <= 'Z')
1762                         *x -= 'A' - 'a';
1763         return p;
1764 }
1765
1766 void
1767 g_I(int argc, char **argv)
1768 {
1769         int anchor;
1770         char *p;
1771
1772         anchor = 0;
1773         if(argc > 2){
1774                 p = argv[2];
1775                 if(p[0] == '(')
1776                 if(p[1] >= '0' && p[1] <= '9')
1777                 if(p[2] == ')'){
1778                         anchor = 1;
1779                         Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">",
1780                                 p[1], lower(argv[1]));
1781                 }
1782         }
1783         font(&ifont, argc, argv);
1784         if(anchor)
1785                 Bprint(&bout, "</A>");
1786 }
1787
1788 void
1789 g_br(int, char**)
1790 {
1791         if(hangingdt){
1792                 Bprint(&bout, "<dd>");
1793                 hangingdt = 0;
1794         }else
1795                 Bprint(&bout, "<br>\n");
1796 }
1797
1798 void
1799 g_P1(int, char**)
1800 {
1801         if(example == 0){
1802                 example = 1;
1803                 Bprint(&bout, "<DL><DT><DD><TT><PRE>\n");
1804         }
1805 }
1806
1807 void
1808 g_P2(int, char**)
1809 {
1810         if(example){
1811                 example = 0;
1812                 Bprint(&bout, "</PRE></TT></DL>\n");
1813         }
1814 }
1815
1816 void
1817 g_SM(int, char **argv)
1818 {
1819         Bprint(&bout, "%s", argv[1]);
1820 }
1821
1822 void
1823 g_ft(int argc, char **argv)
1824 {
1825         if(argc < 2){
1826                 pushfont(nil);
1827                 return;
1828         }
1829
1830         switch(argv[1][0]){
1831         case '3':
1832         case 'B':
1833                 pushfont(&bfont);
1834                 break;
1835         case '2':
1836         case 'I':
1837                 pushfont(&ifont);
1838                 break;
1839         case '4':
1840                 pushfont(&bifont);
1841                 break;
1842         case '5':
1843                 pushfont(&cwfont);
1844                 break;
1845         case 'P':
1846                 popfont();
1847                 break;
1848         case 'R':
1849         default:
1850                 pushfont(nil);
1851                 break;
1852         }
1853 }
1854
1855 void
1856 g_sp(int argc, char **argv)
1857 {
1858         int n;
1859
1860         n = 1;
1861         if(argc > 1){
1862                 n = atoi(argv[1]);
1863                 if(n < 1)
1864                         n = 1;
1865                 if(argv[1][strlen(argv[1])-1] == 'i')
1866                         n *= 4;
1867         }
1868         if(n > 5){
1869                 Bprint(&bout, "<br>&#32;<br>\n");
1870                 Bprint(&bout, "<HR>\n");
1871                 Bprint(&bout, "<br>&#32;<br>\n");
1872         } else
1873                 for(; n > 0; n--)
1874                         Bprint(&bout, "<br>&#32;<br>\n");
1875 }
1876
1877  void
1878 rm_loop(char *name, String **l)
1879 {
1880         String *s;
1881         for(s = *l; s != nil; s = *l){
1882                 if(strcmp(name, s->name) == 0){
1883                         *l = s->next;
1884                         free(s->name);
1885                         free(s->val);
1886                         free(s);
1887                         break;
1888                         }
1889                 l = &s->next;
1890                 }
1891         }
1892
1893 void
1894 g_rm(int argc, char **argv)
1895 {
1896         Goobie *g;
1897         char *name;
1898         int i;
1899
1900         for(i = 1; i < argc; i++) {
1901                 name = argv[i];
1902                 rm_loop(name, &strings);
1903                 rm_loop(name, &macros);
1904                 for(g = gtab; g->name; g++)
1905                         if (strcmp(g->name, name) == 0) {
1906                                 g->f = g_ignore;
1907                                 break;
1908                                 }
1909                 }
1910         }
1911
1912 void
1913 g_AB(int, char**)
1914 {
1915         closel();
1916         dohangingcenter();
1917         Bprint(&bout, "<center><H4>ABSTRACT</H4></center><DL><DD>\n");
1918 }
1919
1920 void
1921 g_AE(int, char**)
1922 {
1923         Bprint(&bout, "</DL>\n");
1924 }
1925
1926 void
1927 g_FS(int, char **)
1928 {
1929         char *argv[3];
1930
1931         argv[0] = "IP";
1932         argv[1] = nil;
1933         argv[2] = nil;
1934         g_IP(1, argv);
1935         Bprint(&bout, "NOTE:<I> ");
1936 }
1937
1938 void
1939 g_FE(int, char **)
1940 {
1941         Bprint(&bout, "</I><DT>&#32;<DD>");
1942         closel();
1943         Bprint(&bout, "<br>\n");
1944 }
1945
1946 void
1947 g_de(int argc, char **argv)
1948 {
1949         int r;
1950         char *p, *cp;
1951         String *m;
1952         int len;
1953
1954         if(argc < 2)
1955                 return;
1956
1957         m = nil;
1958         len = 0;
1959         if(strcmp(argv[0], "am") == 0){
1960                 for(m = macros; m != nil; m = m->next)
1961                         if(strcmp(argv[1], m->name) == 0){
1962                                 len = strlen(m->val);
1963                                 break;
1964                         }
1965
1966                 if(m == nil){
1967                         /* nothing to append to */
1968                         for(;;){
1969                                 p = Brdline(&ssp->in, '\n');
1970                                 if(p == nil)
1971                                         break;
1972                                 p[Blinelen(&ssp->in)-1] = 0;
1973                                 if(strcmp(p, "..") == 0)
1974                                         break;
1975                         }
1976                         return;
1977                 }
1978         }
1979
1980         if(m == nil){
1981                 m = emalloc(sizeof(*m));
1982                 m->next = macros;
1983                 macros = m;
1984                 m->name = strdup(argv[1]);
1985                 m->val = nil;
1986                 len = 0;
1987         }
1988
1989         /* read up to a .. removing double backslashes */
1990         for(;;){
1991                 p = Brdline(&ssp->in, '\n');
1992                 if(p == nil)
1993                         break;
1994                 p[Blinelen(&ssp->in)-1] = 0;
1995                 if(strcmp(p, "..") == 0)
1996                         break;
1997                 m->val = realloc(m->val, len + Blinelen(&ssp->in)+1);
1998                 cp = m->val + len;
1999                 while(*p){
2000                         r = *p++;
2001                         if(r == '\\' && *p == '\\')
2002                                 p++;
2003                         *cp++ = r;
2004                 }
2005                 *cp++ = '\n';
2006                 len = cp - m->val;
2007                 *cp = 0;
2008         }
2009 }
2010
2011 void
2012 g_hrule(int, char**)
2013 {
2014         Bprint(&bout, "<HR>\n");
2015 }
2016
2017 void
2018 g_BX(int argc, char **argv)
2019 {
2020         Bprint(&bout, "<HR>\n");
2021         printargs(argc, argv);
2022         Bprint(&bout, "<HR>\n");
2023 }
2024
2025 void
2026 g_IH(int, char**)
2027 {
2028         Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
2029 }
2030
2031 void
2032 g_MH(int, char**)
2033 {
2034         Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
2035 }
2036
2037 void
2038 g_PY(int, char**)
2039 {
2040         Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
2041 }
2042
2043 void
2044 g_HO(int, char**)
2045 {
2046         Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
2047 }
2048
2049 void
2050 g_QS(int, char**)
2051 {
2052         Bprint(&bout, "<BLOCKQUOTE>\n");
2053 }
2054
2055 void
2056 g_QE(int, char**)
2057 {
2058         Bprint(&bout, "</BLOCKQUOTE>\n");
2059 }
2060
2061 void
2062 g_RS(int, char**)
2063 {
2064         Bprint(&bout, "<DL><DD>\n");
2065 }
2066
2067 void
2068 g_RE(int, char**)
2069 {
2070         Bprint(&bout, "</DL>\n");
2071 }
2072
2073 int gif;
2074
2075 void
2076 g_startgif(int, char **argv)
2077 {
2078         int fd;
2079         int pfd[2];
2080         char *e, *p;
2081         char name[32];
2082         Dir *d;
2083
2084         if(strcmp(argv[0], "EQ") == 0)
2085                 e = ".EN";
2086         else if(strcmp(argv[0], "TS") == 0)
2087                 e = ".TE";
2088         else if(strcmp(argv[0], "PS") == 0)
2089                 e = ".PE";
2090         else
2091                 return;
2092
2093         if(basename)
2094                 p = basename;
2095         else{
2096                 p = strrchr(sstack[0].filename, '/');
2097                 if(p != nil)
2098                         p++;
2099                 else
2100                         p = sstack[0].filename;
2101         }
2102         snprint(name, sizeof(name), "%s.%d.gif", p, gif++);
2103         fd = create(name, OWRITE, 0664);
2104         if(fd < 0){
2105                 fprint(2, "ms2html: can't create %s: %r\n", name);
2106                 return;
2107         }
2108
2109         if(pipe(pfd) < 0){
2110                 fprint(2, "ms2html: can't create pipe: %r\n");
2111                 close(fd);
2112                 return;
2113         }
2114         switch(rfork(RFFDG|RFPROC)){
2115         case -1:
2116                 fprint(2, "ms2html: can't fork: %r\n");
2117                 close(fd);
2118                 return;
2119         case 0:
2120                 dup(fd, 1);
2121                 close(fd);
2122                 dup(pfd[0], 0);
2123                 close(pfd[0]);
2124                 close(pfd[1]);
2125                 execl("/bin/troff2gif", "troff2gif", nil);
2126                 fprint(2, "ms2html: couldn't exec troff2gif: %r\n");
2127                 _exits(nil);
2128         default:
2129                 close(fd);
2130                 close(pfd[0]);
2131                 fprint(pfd[1], ".ll 7i\n");
2132         /*      fprint(pfd[1], ".EQ\ndelim %s\n.EN\n", delim); */
2133         /*      fprint(pfd[1], ".%s\n", argv[0]); */
2134                 for(;;){
2135                         p = Brdline(&ssp->in, '\n');
2136                         if(p == nil)
2137                                 break;
2138                         ssp->lno++;
2139                         ssp->rlno++;
2140                         if(write(pfd[1], p, Blinelen(&ssp->in)) < 0)
2141                                 break;
2142                         if(strncmp(p, e, 3) == 0)
2143                                 break;
2144                 }
2145                 close(pfd[1]);
2146                 waitpid();
2147                 d = dirstat(name);
2148                 if(d == nil)
2149                         break;
2150                 if(d->length == 0){
2151                         remove(name);
2152                         free(d);
2153                         break;
2154                 }
2155                 free(d);
2156                 fprint(2, "ms2html: created auxiliary file %s\n", name);
2157                 Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2158                 break;
2159         }
2160 }
2161
2162 void
2163 g_lf(int argc, char **argv)
2164 {
2165         if(argc > 2)
2166                 snprint(ssp->filename, sizeof(ssp->filename), argv[2]);
2167         if(argc > 1)
2168                 ssp->rlno = atoi(argv[1]);
2169 }
2170
2171 void
2172 g_so(int argc, char **argv)
2173 {
2174         ssp->lno++;
2175         ssp->rlno++;
2176         if(argc > 1)
2177                 pushsrc(argv[1]);
2178 }
2179
2180
2181 void
2182 g_BP(int argc, char **argv)
2183 {
2184         int fd;
2185         char *p, *ext;
2186         char name[32];
2187         Dir *d;
2188
2189         if(argc < 2)
2190                 return;
2191
2192         p = strrchr(argv[1], '/');
2193         if(p != nil)
2194                 p++;
2195         else
2196                 p = argv[1];
2197
2198
2199         ext = strrchr(p, '.');
2200         if(ext){
2201                 if(strcmp(ext, ".jpeg") == 0
2202                 || strcmp(ext, ".gif") == 0){
2203                         Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]);
2204                         return;
2205                 }
2206         }
2207
2208
2209         snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
2210         fd = create(name, OWRITE, 0664);
2211         if(fd < 0){
2212                 fprint(2, "ms2html: can't create %s: %r\n", name);
2213                 return;
2214         }
2215
2216         switch(rfork(RFFDG|RFPROC)){
2217         case -1:
2218                 fprint(2, "ms2html: can't fork: %r\n");
2219                 close(fd);
2220                 return;
2221         case 0:
2222                 dup(fd, 1);
2223                 close(fd);
2224                 execl("/bin/ps2gif", "ps2gif", argv[1], nil);
2225                 fprint(2, "ms2html: couldn't exec ps2gif: %r\n");
2226                 _exits(nil);
2227         default:
2228                 close(fd);
2229                 waitpid();
2230                 d = dirstat(name);
2231                 if(d == nil)
2232                         break;
2233                 if(d->length == 0){
2234                         remove(name);
2235                         free(d);
2236                         break;
2237                 }
2238                 free(d);
2239                 fprint(2, "ms2html: created auxiliary file %s\n", name);
2240                 Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2241                 break;
2242         }
2243 }
2244
2245 /* insert straight HTML into output */
2246 void
2247 g__H(int argc, char **argv)
2248 {
2249         int i;
2250
2251         for(i = 1; i < argc; i++)
2252                 Bprint(&bout, "%s ", argv[i]);
2253         Bprint(&bout, "\n");
2254 }
2255
2256 /* HTML page title */
2257 void
2258 g__T(int argc, char **argv)
2259 {
2260         if(titleseen)
2261                 return;
2262
2263         Bprint(&bout, "<title>\n");
2264         printargs(argc, argv);
2265         Bprint(&bout, "</title></head><body>\n");
2266         titleseen = 1;
2267 }
2268
2269 void
2270 g_nr(int argc, char **argv)
2271 {
2272         char *val;
2273
2274         if (argc > 1) {
2275                 if (argc == 2)
2276                         val = "0";
2277                 else
2278                         val = argv[2];
2279                 dsnr(argv[1], val, &numregs);
2280         }
2281 }
2282
2283 void
2284 zerodivide(void)
2285 {
2286         fprint(2, "stdin %d(%s:%d): division by 0\n",
2287                 ssp->lno, ssp->filename, ssp->rlno);
2288 }
2289
2290 int
2291 numval(char **pline, int recur)
2292 {
2293         char *p;
2294         int neg, x, y;
2295
2296         x = neg = 0;
2297         p = *pline;
2298         while(*p == '-') {
2299                 neg = 1 - neg;
2300                 p++;
2301         }
2302         if (*p == '(') {
2303                 p++;
2304                 x = numval(&p, 1);
2305                 if (*p != ')')
2306                         goto done;
2307                 p++;
2308         }
2309         else while(*p >= '0' && *p <= '9')
2310                 x = 10*x + *p++ - '0';
2311         if (neg)
2312                 x = -x;
2313         if (recur)
2314             for(;;) {
2315                 switch(*p++) {
2316                 case '+':
2317                         x += numval(&p, 0);
2318                         continue;
2319                 case '-':
2320                         x -= numval(&p, 0);
2321                         continue;
2322                 case '*':
2323                         x *= numval(&p, 0);
2324                         continue;
2325                 case '/':
2326                         y = numval(&p, 0);
2327                         if (y == 0) {
2328                                 zerodivide();
2329                                 x = 0;
2330                                 goto done;
2331                         }
2332                         x /= y;
2333                         continue;
2334                 case '<':
2335                         if (*p == '=') {
2336                                 p++;
2337                                 x = x <= numval(&p, 0);
2338                                 continue;
2339                         }
2340                         x = x < numval(&p, 0);
2341                         continue;
2342                 case '>':
2343                         if (*p == '=') {
2344                                 p++;
2345                                 x = x >= numval(&p, 0);
2346                                 continue;
2347                         }
2348                         x = x > numval(&p, 0);
2349                         continue;
2350                 case '=':
2351                         if (*p == '=')
2352                                 p++;
2353                         x = x == numval(&p, 0);
2354                         continue;
2355                 case '&':
2356                         x &= numval(&p, 0);
2357                         continue;
2358                 case ':':
2359                         x |= numval(&p, 0);
2360                         continue;
2361                 case '%':
2362                         y = numval(&p, 0);
2363                         if (!y) {
2364                                 zerodivide();
2365                                 goto done;
2366                         }
2367                         x %= y;
2368                         continue;
2369                 }
2370                 --p;
2371                 break;
2372         }
2373  done:
2374         *pline = p;
2375         return x;
2376 }
2377
2378 int
2379 iftest(char *p, char **bp)
2380 {
2381         char *p1;
2382         int c, neg, rv;
2383
2384         rv = neg = 0;
2385         if (*p == '!') {
2386                 neg = 1;
2387                 p++;
2388         }
2389         c = *p;
2390         if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) {
2391                 if (numval(&p,1) >= 1)
2392                         rv = 1;
2393                 goto done;
2394         }
2395         switch(c) {
2396         case 't':
2397         case 'o':
2398                 rv = 1;
2399         case 'n':
2400         case 'e':
2401                 p++;
2402                 goto done;
2403         }
2404         for(p1 = ++p; *p != c; p++)
2405                 if (!*p)
2406                         goto done;
2407         for(p++;;) {
2408                 if (*p != *p1++) {
2409                         while(*p && *p++ != c);
2410                         goto done;
2411                 }
2412                 if (*p++ == c)
2413                         break;
2414         }
2415         rv = 1;
2416  done:
2417         if (neg)
2418                 rv = 1 - rv;
2419         while(*p == ' ' || *p == '\t')
2420                 p++;
2421         *bp = p;
2422         return rv;
2423 }
2424
2425 void
2426 scanline(char *p, char *e, int wantnl)
2427 {
2428         int c;
2429         Rune r;
2430
2431         while((c = getrune()) == ' ' || c == '\t') ;
2432         while(p < e) {
2433                 if (c < 0)
2434                         break;
2435                 if (c < Runeself) {
2436                         if (c == '\n') {
2437                                 if (wantnl)
2438                                         *p++ = c;
2439                                 break;
2440                         }
2441                         *p++ = c;
2442                 }
2443                 else {
2444                         r = c;
2445                         p += runetochar(p, &r);
2446                 }
2447                 c = getrune();
2448         }
2449         *p = 0;
2450 }
2451
2452 void
2453 pushbody(char *line)
2454 {
2455         char *b;
2456
2457         if (line[0] == '\\' && line[1] == '{' /*}*/ )
2458                 line += 2;
2459         if (strsp < Maxmstack - 1) {
2460                 pushstr(b = strdup(line));
2461                 mustfree[strsp] = b;
2462         }
2463 }
2464
2465 void
2466 skipbody(char *line)
2467 {
2468         int c, n;
2469
2470         if (line[0] != '\\' || line[1] != '{' /*}*/ )
2471                 return;
2472         for(n = 1;;) {
2473                 while((c = getrune()) != '\\')
2474                         if (c < 0)
2475                                 return;
2476                 c = getrune();
2477                 if (c == '{')
2478                         n++;
2479                 else if ((c == '}' && (c = getrune()) == '\n' && !--n)
2480                         || c < 0)
2481                         return;
2482         }
2483 }
2484
2485 int
2486 ifstart(char *line, char *e, char **bp)
2487 {
2488         int it;
2489         char *b;
2490
2491         b = copyline(line, e, 1);
2492         ungetrune();
2493         b[-1] = getrune();
2494         scanline(b, e, 1);
2495         it = iftest(line, bp);
2496         return it;
2497 }
2498
2499 void
2500 g_ie(char *line, char *e)
2501 {
2502         char *b;
2503
2504         if (elsetop >= Maxif-1) {
2505                 fprint(2, "ms2html: .ie's too deep\n");
2506                 return;
2507         }
2508         if (ifwastrue[++elsetop] = ifstart(line, e, &b))
2509                 pushbody(b);
2510         else
2511                 skipbody(b);
2512 }
2513
2514 void
2515 g_if(char *line, char *e)
2516 {
2517         char *b;
2518
2519         if (ifstart(line, e, &b))
2520                 pushbody(b);
2521         else
2522                 skipbody(b);
2523 }
2524
2525 void
2526 g_el(char *line, char *e)
2527 {
2528         if (elsetop < 0)
2529                 return;
2530         scanline(line, e, 1);
2531         if (ifwastrue[elsetop--])
2532                 skipbody(line);
2533         else
2534                 pushbody(line);
2535 }
2536
2537 void
2538 g_ig(int argc, char **argv)
2539 {
2540         char *p, *s;
2541
2542         s = "..";
2543         if (argc > 1)
2544                 s = argv[1];
2545         for(;;) {
2546                 p = Brdline(&ssp->in, '\n');
2547                 if(p == nil)
2548                         break;
2549                 p[Blinelen(&ssp->in)-1] = 0;
2550                 if(strcmp(p, s) == 0)
2551                         break;
2552         }
2553 }
2554
2555 void
2556 g_ds(char *line, char *e)
2557 {
2558         char *b;
2559
2560         b = copyline(line, e, 1);
2561         if (b > line) {
2562                 copyline(b, e, 0);
2563                 if (*b == '"')
2564                         b++;
2565                 ds(line, b);
2566         }
2567 }
2568
2569 void
2570 g_as(char *line, char *e)
2571 {
2572         String *s;
2573         char *b;
2574
2575         b = copyline(line, e, 1);
2576         if (b == line)
2577                 return;
2578         copyline(b, e, 0);
2579         if (*b == '"')
2580                 b++;
2581         for(s = strings; s != nil; s = s->next)
2582                 if(strcmp(line, s->name) == 0)
2583                         break;
2584
2585         if(s == nil){
2586                 ds(line, b);
2587                 return;
2588         }
2589
2590         s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1);
2591         strcat(s->val, b);
2592 }
2593
2594 void
2595 g_BS(int argc, char **argv)
2596 {
2597         int i;
2598
2599         if (argc > 1 && !weBref) {
2600                 Bprint(&bout, "<a href=\"%s\"", argv[1]);
2601                 for(i = 2; i < argc; i++)
2602                         Bprint(&bout, " %s", argv[i]);
2603                 Bprint(&bout, ">");
2604                 weBref = 1;
2605         }
2606 }
2607
2608 void
2609 g_BE(int, char**)
2610 {
2611         if (weBref) {
2612                 Bprint(&bout, "</a>");
2613                 weBref = 0;
2614         }
2615 }
2616
2617 void
2618 g_LB(int argc, char **argv)
2619 {
2620         if (argc > 1) {
2621                 if (weBref)
2622                         g_BE(0,nil);
2623                 Bprint(&bout, "<a name=\"%s\"></a>", argv[1]);
2624         }
2625 }
2626
2627 void
2628 g_RT(int, char**)
2629 {
2630         g_BE(0,nil);
2631         dohanginghead();
2632         closel();
2633         closefont();
2634 }