]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ms2html.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[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         Tm *t;
1317
1318         pushsrc(nil);
1319
1320         sol = 1;
1321         Bprint(&bout, "<html>\n");
1322         Bflush(&bout);
1323         for(;;){
1324                 p = getnext();
1325                 if(p == nil)
1326                         break;
1327                 c = *p;
1328                 if(c == '.' && sol){
1329                         dodirective();
1330                         dohangingdt();
1331                         ssp->lno++;
1332                         ssp->rlno++;
1333                         sol = 1;
1334                 } else if(c == '\n'){
1335                         if (ignore_nl)
1336                                 ignore_nl = 0;
1337                         else {
1338                                 if(hangingau)
1339                                         Bprint(&bout, "<br>\n");
1340                                 else
1341                                         Bprint(&bout, "%s", p);
1342                                 dohangingdt();
1343                                 }
1344                         ssp->lno++;
1345                         ssp->rlno++;
1346                         sol = 1;
1347                 } else{
1348                         Bprint(&bout, "%s", p);
1349                         ignore_nl = sol = 0;
1350                 }
1351         }
1352         dohanginghead();
1353         dohangingdt();
1354         closel();
1355         if(fsp >= 0 && fstack[fsp])
1356                 Bprint(&bout, "%s", fstack[fsp]->end);
1357         Bprint(&bout, "<br>&#32;<br>\n");
1358 /*
1359         Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n");
1360         t = localtime(time(nil));
1361         Bprint(&bout, "Copyright</A> &#169; %d Alcatel-Lucent Inc.  All rights reserved.\n",
1362                         t->year+1900);
1363 */
1364         Bprint(&bout, "</body></html>\n");
1365 }
1366
1367 static void
1368 usage(void)
1369 {
1370         sysfatal("usage: ms2html [-q] [-b basename] [-d '$$'] [-t title]");
1371 }
1372
1373 void
1374 main(int argc, char **argv)
1375 {
1376         quiet = 1;
1377         ARGBEGIN {
1378         case 't':
1379                 title = EARGF(usage());
1380                 break;
1381         case 'b':
1382                 basename = EARGF(usage());
1383                 break;
1384         case 'q':
1385                 quiet = 0;
1386                 break;
1387         case 'd':
1388                 delim = EARGF(usage());
1389                 break;
1390         case '?':
1391         default:
1392                 usage();
1393         } ARGEND;
1394
1395         Binit(&bout, 1, OWRITE);
1396
1397         ds("R", "&#174;");
1398
1399         doconvert();
1400         exits(nil);
1401 }
1402
1403 void
1404 g_notyet(int, char **argv)
1405 {
1406         fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
1407 }
1408
1409 void
1410 g_ignore(int, char **argv)
1411 {
1412         if(quiet)
1413                 return;
1414         fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
1415 }
1416
1417 void
1418 g_PP(int, char**)
1419 {
1420         dohanginghead();
1421         closel();
1422         closefont();
1423         Bprint(&bout, "<P>\n");
1424         paragraph = 1;
1425 }
1426
1427 void
1428 g_LP(int, char**)
1429 {
1430         dohanginghead();
1431         closel();
1432         closefont();
1433         Bprint(&bout, "<br>&#32;<br>\n");
1434 }
1435
1436 /* close a list */
1437 void
1438 closel(void)
1439 {
1440         g_P2(1, nil);
1441         dohangingau();
1442         if(paragraph){
1443                 Bprint(&bout, "</P>\n");
1444                 paragraph = 0;
1445         }
1446         switch(list){
1447         case Lordered:
1448                 Bprint(&bout, "</ol>\n");
1449                 break;
1450         case Lunordered:
1451                 Bprint(&bout, "</ul>\n");
1452                 break;
1453         case Lother:
1454         case Ldef:
1455                 Bprint(&bout, "</dl>\n");
1456                 break;
1457         }
1458         list = 0;
1459         
1460 }
1461
1462
1463 void
1464 g_IP(int argc, char **argv)
1465 {
1466         switch(list){
1467         default:
1468                 closel();
1469                 if(argc > 1){
1470                         if(strcmp(argv[1], "1") == 0){
1471                                 list = Lordered;
1472                                 listnum = 1;
1473                                 Bprint(&bout, "<OL>\n");
1474                         } else if(strcmp(argv[1], "\\(bu") == 0){
1475                                 list = Lunordered;
1476                                 Bprint(&bout, "<UL>\n");
1477                         } else {
1478                                 list = Lother;
1479                                 Bprint(&bout, "<DL COMPACT>\n");
1480                         }
1481                 } else {
1482                         list = Lother;
1483                         Bprint(&bout, "<DL>\n");
1484                 }
1485                 break;
1486         case Lother:
1487         case Lordered:
1488         case Lunordered:
1489                 break;
1490         }
1491
1492         switch(list){
1493         case Lother:
1494                 Bprint(&bout, "<DT>");
1495                 if(argc > 1)
1496                         printarg(argv[1]);
1497                 else
1498                         Bprint(&bout, "<DT>&#32;");
1499                 Bprint(&bout, "<DD>\n");
1500                 break;
1501         case Lordered:
1502         case Lunordered:
1503                 Bprint(&bout, "<LI>\n");
1504                 break;
1505         }
1506 }
1507
1508 /*
1509  *  .5i is one <DL><DT><DD>
1510  */
1511 void
1512 g_in(int argc, char **argv)
1513 {
1514         float   f;
1515         int     delta, x;
1516         char    *p;
1517
1518         f = indent/0.5;
1519         delta = f;
1520         if(argc <= 1){
1521                 indent = 0.0;
1522         } else {
1523                 f = strtod(argv[1], &p);
1524                 switch(*p){
1525                 case 'i':
1526                         break;
1527                 case 'c':
1528                         f = f / 2.54;
1529                         break;
1530                 case 'P':
1531                         f = f / 6;
1532                         break;
1533                 default:
1534                 case 'u':
1535                 case 'm':
1536                         f = f * (12 / 72);
1537                         break;
1538                 case 'n':
1539                         f = f * (6 / 72);
1540                         break;
1541                 case 'p':
1542                         f = f / 72.0;
1543                         break;
1544                 }
1545                 switch(argv[1][0]){
1546                 case '+':
1547                 case '-':
1548                         indent += f;
1549                         break;
1550                 default:
1551                         indent = f;
1552                         break;
1553                 }
1554         }
1555         if(indent < 0.0)
1556                 indent = 0.0;
1557         f = (indent/0.5);
1558         x = f;
1559         delta = x - delta;
1560         while(delta < 0){
1561                 Bprint(&bout, "</DL>\n");
1562                 delta++;
1563         }
1564         while(delta > 0){
1565                 Bprint(&bout, "<DL><DT><DD>\n");
1566                 delta--;
1567         }
1568 }
1569
1570 void
1571 g_HP(int, char**)
1572 {
1573         switch(list){
1574         default:
1575                 closel();
1576                 list = Ldef;
1577                 hangingdt = 1;
1578                 Bprint(&bout, "<DL><DT>\n");
1579                 break;
1580         case Ldef:
1581                 if(hangingdt)
1582                         Bprint(&bout, "<DD>");
1583                 Bprint(&bout, "<DT>");
1584                 hangingdt = 1;
1585                 break;
1586         }
1587 }
1588
1589 void
1590 g_SH(int, char**)
1591 {
1592         dohanginghead();
1593         dohangingcenter();
1594         closel();
1595         closefont();
1596         Bprint(&bout, "<H%d>", HH);
1597         hanginghead = HH;
1598 }
1599
1600 void
1601 g_NH(int argc, char **argv)
1602 {
1603         int i, level;
1604
1605         closel();
1606         closefont();
1607
1608         dohangingcenter();
1609         if(argc == 1)
1610                 level = 0;
1611         else {
1612                 level = atoi(argv[1])-1;
1613                 if(level < 0 || level >= Maxnh)
1614                         level = Maxnh - 1;
1615         }
1616         nh[level]++;
1617
1618         Bprint(&bout, "<H%d>", HH);
1619         hanginghead = HH;
1620
1621         Bprint(&bout, "%d", nh[0]);
1622         for(i = 1; i <= level; i++)
1623                 Bprint(&bout, ".%d", nh[i]);
1624         Bprint(&bout, " ");
1625
1626         for(i = level+1; i < Maxnh; i++)
1627                 nh[i] = 0;
1628 }
1629
1630 void
1631 g_TL(int, char**)
1632 {
1633         char *p, *np;
1634         char name[128];
1635
1636         closefont();
1637
1638         if(!titleseen){
1639                 if(!title){
1640                         /* get base part of filename */
1641                         p = strrchr(ssp->filename, '/');
1642                         if(p == nil)
1643                                 p = ssp->filename;
1644                         else
1645                                 p++;
1646                         strncpy(name, p, sizeof(name));
1647                         name[sizeof(name)-1] = 0;
1648                 
1649                         /* dump any extensions */
1650                         np = strchr(name, '.');
1651                         if(np)
1652                                 *np = 0;
1653                         title = p;
1654                 }
1655                 Bprint(&bout, "<title>\n");
1656                 Bprint(&bout, "%s\n", title);
1657                 Bprint(&bout, "</title>\n");
1658                 Bprint(&bout, "<body BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#0000FF\" VLINK=\"#330088\" ALINK=\"#FF0044\">\n");
1659                 titleseen = 1;
1660         }
1661
1662         Bprint(&bout, "<center>");
1663         hangingcenter = 1;
1664         Bprint(&bout, "<H%d>", 1);
1665         hanginghead = 1;
1666 }
1667
1668 void
1669 dohangingcenter(void)
1670 {
1671         if(hangingcenter){
1672                 Bprint(&bout, "</center>");
1673                 hangingcenter = 1;
1674         }
1675 }
1676
1677 void
1678 g_AU(int, char**)
1679 {
1680         closel();
1681         dohanginghead();
1682         Bprint(&bout, "<DL><DD><I>");
1683         hangingau = 1;
1684 }
1685
1686 void
1687 pushfont(Font *f)
1688 {
1689         if(fsp == Maxfsp)
1690                 return;
1691         if(fsp >= 0 && fstack[fsp])
1692                 Bprint(&bout, "%s", fstack[fsp]->end);
1693         if(f != nil)
1694                 Bprint(&bout, "%s", f->start);
1695         fstack[++fsp] = f;
1696 }
1697
1698 void
1699 popfont(void)
1700 {
1701         if(fsp >= 0){
1702                 if(fstack[fsp])
1703                         Bprint(&bout, "%s", fstack[fsp]->end);
1704                 fsp--;
1705         }
1706 }
1707
1708 /*
1709  *  for 3 args print arg3 \fxarg1\fP arg2
1710  *  for 2 args print arg1 \fxarg2\fP
1711  *  for 1 args print \fxarg1\fP
1712  */
1713 void
1714 font(Font *f, int argc, char **argv)
1715 {
1716         if(argc == 1){
1717                 pushfont(nil);
1718                 return;
1719         }
1720         if(argc > 3)
1721                 printarg(argv[3]);
1722         pushfont(f);
1723         printarg(argv[1]);
1724         popfont();
1725         if(argc > 2)
1726                 printarg(argv[2]);
1727         Bprint(&bout, "\n");
1728 }
1729
1730 void
1731 closefont(void)
1732 {
1733         if(fsp >= 0 && fstack[fsp])
1734                 Bprint(&bout, "%s", fstack[fsp]->end);
1735         fsp = -1;
1736 }
1737
1738 void
1739 g_B(int argc, char **argv)
1740 {
1741         font(&bfont, argc, argv);
1742 }
1743
1744 void
1745 g_R(int argc, char **argv)
1746 {
1747         font(nil, argc, argv);
1748 }
1749
1750 void
1751 g_BI(int argc, char **argv)
1752 {
1753         font(&bifont, argc, argv);
1754 }
1755
1756 void
1757 g_CW(int argc, char **argv)
1758 {
1759         font(&cwfont, argc, argv);
1760 }
1761
1762 char*
1763 lower(char *p)
1764 {
1765         char *x;
1766
1767         for(x = p; *x; x++)
1768                 if(*x >= 'A' && *x <= 'Z')
1769                         *x -= 'A' - 'a';
1770         return p;
1771 }
1772
1773 void
1774 g_I(int argc, char **argv)
1775 {
1776         int anchor;
1777         char *p;
1778
1779         anchor = 0;
1780         if(argc > 2){
1781                 p = argv[2];
1782                 if(p[0] == '(')
1783                 if(p[1] >= '0' && p[1] <= '9')
1784                 if(p[2] == ')'){
1785                         anchor = 1;
1786                         Bprint(&bout, "<A href=\"/magic/man2html/%c/%s\">",
1787                                 p[1], lower(argv[1]));
1788                 }
1789         }
1790         font(&ifont, argc, argv);
1791         if(anchor)
1792                 Bprint(&bout, "</A>");
1793 }
1794
1795 void
1796 g_br(int, char**)
1797 {
1798         if(hangingdt){
1799                 Bprint(&bout, "<dd>");
1800                 hangingdt = 0;
1801         }else
1802                 Bprint(&bout, "<br>\n");
1803 }
1804
1805 void
1806 g_P1(int, char**)
1807 {
1808         if(example == 0){
1809                 example = 1;
1810                 Bprint(&bout, "<DL><DT><DD><TT><PRE>\n");
1811         }
1812 }
1813
1814 void
1815 g_P2(int, char**)
1816 {
1817         if(example){
1818                 example = 0;
1819                 Bprint(&bout, "</PRE></TT></DL>\n");
1820         }
1821 }
1822
1823 void
1824 g_SM(int, char **argv)
1825 {
1826         Bprint(&bout, "%s", argv[1]);
1827 }
1828
1829 void
1830 g_ft(int argc, char **argv)
1831 {
1832         if(argc < 2){
1833                 pushfont(nil);
1834                 return;
1835         }
1836
1837         switch(argv[1][0]){
1838         case '3':
1839         case 'B':
1840                 pushfont(&bfont);
1841                 break;
1842         case '2':
1843         case 'I':
1844                 pushfont(&ifont);
1845                 break;
1846         case '4':
1847                 pushfont(&bifont);
1848                 break;
1849         case '5':
1850                 pushfont(&cwfont);
1851                 break;
1852         case 'P':
1853                 popfont();
1854                 break;
1855         case 'R':
1856         default:
1857                 pushfont(nil);
1858                 break;
1859         }
1860 }
1861
1862 void
1863 g_sp(int argc, char **argv)
1864 {
1865         int n;
1866
1867         n = 1;
1868         if(argc > 1){
1869                 n = atoi(argv[1]);
1870                 if(n < 1)
1871                         n = 1;
1872                 if(argv[1][strlen(argv[1])-1] == 'i')
1873                         n *= 4;
1874         }
1875         if(n > 5){
1876                 Bprint(&bout, "<br>&#32;<br>\n");
1877                 Bprint(&bout, "<HR>\n");
1878                 Bprint(&bout, "<br>&#32;<br>\n");
1879         } else
1880                 for(; n > 0; n--)
1881                         Bprint(&bout, "<br>&#32;<br>\n");
1882 }
1883
1884  void
1885 rm_loop(char *name, String **l)
1886 {
1887         String *s;
1888         for(s = *l; s != nil; s = *l){
1889                 if(strcmp(name, s->name) == 0){
1890                         *l = s->next;
1891                         free(s->name);
1892                         free(s->val);
1893                         free(s);
1894                         break;
1895                         }
1896                 l = &s->next;
1897                 }
1898         }
1899
1900 void
1901 g_rm(int argc, char **argv)
1902 {
1903         Goobie *g;
1904         char *name;
1905         int i;
1906
1907         for(i = 1; i < argc; i++) {
1908                 name = argv[i];
1909                 rm_loop(name, &strings);
1910                 rm_loop(name, &macros);
1911                 for(g = gtab; g->name; g++)
1912                         if (strcmp(g->name, name) == 0) {
1913                                 g->f = g_ignore;
1914                                 break;
1915                                 }
1916                 }
1917         }
1918
1919 void
1920 g_AB(int, char**)
1921 {
1922         closel();
1923         dohangingcenter();
1924         Bprint(&bout, "<center><H4>ABSTRACT</H4></center><DL><DD>\n");
1925 }
1926
1927 void
1928 g_AE(int, char**)
1929 {
1930         Bprint(&bout, "</DL>\n");
1931 }
1932
1933 void
1934 g_FS(int, char **)
1935 {
1936         char *argv[3];
1937
1938         argv[0] = "IP";
1939         argv[1] = nil;
1940         argv[2] = nil;
1941         g_IP(1, argv);
1942         Bprint(&bout, "NOTE:<I> ");
1943 }
1944
1945 void
1946 g_FE(int, char **)
1947 {
1948         Bprint(&bout, "</I><DT>&#32;<DD>");
1949         closel();
1950         Bprint(&bout, "<br>\n");
1951 }
1952
1953 void
1954 g_de(int argc, char **argv)
1955 {
1956         int r;
1957         char *p, *cp;
1958         String *m;
1959         int len;
1960
1961         if(argc < 2)
1962                 return;
1963
1964         m = nil;
1965         len = 0;
1966         if(strcmp(argv[0], "am") == 0){
1967                 for(m = macros; m != nil; m = m->next)
1968                         if(strcmp(argv[1], m->name) == 0){
1969                                 len = strlen(m->val);
1970                                 break;
1971                         }
1972
1973                 if(m == nil){
1974                         /* nothing to append to */
1975                         for(;;){
1976                                 p = Brdline(&ssp->in, '\n');
1977                                 if(p == nil)
1978                                         break;
1979                                 p[Blinelen(&ssp->in)-1] = 0;
1980                                 if(strcmp(p, "..") == 0)
1981                                         break;
1982                         }
1983                         return;
1984                 }
1985         }
1986
1987         if(m == nil){
1988                 m = emalloc(sizeof(*m));
1989                 m->next = macros;
1990                 macros = m;
1991                 m->name = strdup(argv[1]);
1992                 m->val = nil;
1993                 len = 0;
1994         }
1995
1996         /* read up to a .. removing double backslashes */
1997         for(;;){
1998                 p = Brdline(&ssp->in, '\n');
1999                 if(p == nil)
2000                         break;
2001                 p[Blinelen(&ssp->in)-1] = 0;
2002                 if(strcmp(p, "..") == 0)
2003                         break;
2004                 m->val = realloc(m->val, len + Blinelen(&ssp->in)+1);
2005                 cp = m->val + len;
2006                 while(*p){
2007                         r = *p++;
2008                         if(r == '\\' && *p == '\\')
2009                                 p++;
2010                         *cp++ = r;
2011                 }
2012                 *cp++ = '\n';
2013                 len = cp - m->val;
2014                 *cp = 0;
2015         }
2016 }
2017
2018 void
2019 g_hrule(int, char**)
2020 {
2021         Bprint(&bout, "<HR>\n");
2022 }
2023
2024 void
2025 g_BX(int argc, char **argv)
2026 {
2027         Bprint(&bout, "<HR>\n");
2028         printargs(argc, argv);
2029         Bprint(&bout, "<HR>\n");
2030 }
2031
2032 void
2033 g_IH(int, char**)
2034 {
2035         Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
2036 }
2037
2038 void
2039 g_MH(int, char**)
2040 {
2041         Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
2042 }
2043
2044 void
2045 g_PY(int, char**)
2046 {
2047         Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
2048 }
2049
2050 void
2051 g_HO(int, char**)
2052 {
2053         Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
2054 }
2055
2056 void
2057 g_QS(int, char**)
2058 {
2059         Bprint(&bout, "<BLOCKQUOTE>\n");
2060 }
2061
2062 void
2063 g_QE(int, char**)
2064 {
2065         Bprint(&bout, "</BLOCKQUOTE>\n");
2066 }
2067
2068 void
2069 g_RS(int, char**)
2070 {
2071         Bprint(&bout, "<DL><DD>\n");
2072 }
2073
2074 void
2075 g_RE(int, char**)
2076 {
2077         Bprint(&bout, "</DL>\n");
2078 }
2079
2080 int gif;
2081
2082 void
2083 g_startgif(int, char **argv)
2084 {
2085         int fd;
2086         int pfd[2];
2087         char *e, *p;
2088         char name[32];
2089         Dir *d;
2090
2091         if(strcmp(argv[0], "EQ") == 0)
2092                 e = ".EN";
2093         else if(strcmp(argv[0], "TS") == 0)
2094                 e = ".TE";
2095         else if(strcmp(argv[0], "PS") == 0)
2096                 e = ".PE";
2097         else
2098                 return;
2099
2100         if(basename)
2101                 p = basename;
2102         else{
2103                 p = strrchr(sstack[0].filename, '/');
2104                 if(p != nil)
2105                         p++;
2106                 else
2107                         p = sstack[0].filename;
2108         }
2109         snprint(name, sizeof(name), "%s.%d.gif", p, gif++);
2110         fd = create(name, OWRITE, 0664);
2111         if(fd < 0){
2112                 fprint(2, "ms2html: can't create %s: %r\n", name);
2113                 return;
2114         }
2115
2116         if(pipe(pfd) < 0){
2117                 fprint(2, "ms2html: can't create pipe: %r\n");
2118                 close(fd);
2119                 return;
2120         }
2121         switch(rfork(RFFDG|RFPROC)){
2122         case -1:
2123                 fprint(2, "ms2html: can't fork: %r\n");
2124                 close(fd);
2125                 return;
2126         case 0:
2127                 dup(fd, 1);
2128                 close(fd);
2129                 dup(pfd[0], 0);
2130                 close(pfd[0]);
2131                 close(pfd[1]);
2132                 execl("/bin/troff2gif", "troff2gif", nil);
2133                 fprint(2, "ms2html: couldn't exec troff2gif: %r\n");
2134                 _exits(nil);
2135         default:
2136                 close(fd);
2137                 close(pfd[0]);
2138                 fprint(pfd[1], ".ll 7i\n");
2139         /*      fprint(pfd[1], ".EQ\ndelim %s\n.EN\n", delim); */
2140         /*      fprint(pfd[1], ".%s\n", argv[0]); */
2141                 for(;;){
2142                         p = Brdline(&ssp->in, '\n');
2143                         if(p == nil)
2144                                 break;
2145                         ssp->lno++;
2146                         ssp->rlno++;
2147                         if(write(pfd[1], p, Blinelen(&ssp->in)) < 0)
2148                                 break;
2149                         if(strncmp(p, e, 3) == 0)
2150                                 break;
2151                 }
2152                 close(pfd[1]);
2153                 waitpid();
2154                 d = dirstat(name);
2155                 if(d == nil)
2156                         break;
2157                 if(d->length == 0){
2158                         remove(name);
2159                         free(d);
2160                         break;
2161                 }
2162                 free(d);
2163                 fprint(2, "ms2html: created auxiliary file %s\n", name);
2164                 Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2165                 break;
2166         }
2167 }
2168
2169 void
2170 g_lf(int argc, char **argv)
2171 {
2172         if(argc > 2)
2173                 snprint(ssp->filename, sizeof(ssp->filename), argv[2]);
2174         if(argc > 1)
2175                 ssp->rlno = atoi(argv[1]);
2176 }
2177
2178 void
2179 g_so(int argc, char **argv)
2180 {
2181         ssp->lno++;
2182         ssp->rlno++;
2183         if(argc > 1)
2184                 pushsrc(argv[1]);
2185 }
2186
2187
2188 void
2189 g_BP(int argc, char **argv)
2190 {
2191         int fd;
2192         char *p, *ext;
2193         char name[32];
2194         Dir *d;
2195
2196         if(argc < 2)
2197                 return;
2198
2199         p = strrchr(argv[1], '/');
2200         if(p != nil)
2201                 p++;
2202         else
2203                 p = argv[1];
2204
2205
2206         ext = strrchr(p, '.');
2207         if(ext){
2208                 if(strcmp(ext, ".jpeg") == 0
2209                 || strcmp(ext, ".gif") == 0){
2210                         Bprint(&bout, "<br><img src=\"%s\"><br>\n", argv[1]);
2211                         return;
2212                 }
2213         }
2214
2215
2216         snprint(name, sizeof(name), "%s.%d%d.gif", p, getpid(), gif++);
2217         fd = create(name, OWRITE, 0664);
2218         if(fd < 0){
2219                 fprint(2, "ms2html: can't create %s: %r\n", name);
2220                 return;
2221         }
2222
2223         switch(rfork(RFFDG|RFPROC)){
2224         case -1:
2225                 fprint(2, "ms2html: can't fork: %r\n");
2226                 close(fd);
2227                 return;
2228         case 0:
2229                 dup(fd, 1);
2230                 close(fd);
2231                 execl("/bin/ps2gif", "ps2gif", argv[1], nil);
2232                 fprint(2, "ms2html: couldn't exec ps2gif: %r\n");
2233                 _exits(nil);
2234         default:
2235                 close(fd);
2236                 waitpid();
2237                 d = dirstat(name);
2238                 if(d == nil)
2239                         break;
2240                 if(d->length == 0){
2241                         remove(name);
2242                         free(d);
2243                         break;
2244                 }
2245                 free(d);
2246                 fprint(2, "ms2html: created auxiliary file %s\n", name);
2247                 Bprint(&bout, "<br><img src=\"%s\"><br>\n", name);
2248                 break;
2249         }
2250 }
2251
2252 /* insert straight HTML into output */
2253 void
2254 g__H(int argc, char **argv)
2255 {
2256         int i;
2257
2258         for(i = 1; i < argc; i++)
2259                 Bprint(&bout, "%s ", argv[i]);
2260         Bprint(&bout, "\n");
2261 }
2262
2263 /* HTML page title */
2264 void
2265 g__T(int argc, char **argv)
2266 {
2267         if(titleseen)
2268                 return;
2269
2270         Bprint(&bout, "<title>\n");
2271         printargs(argc, argv);
2272         Bprint(&bout, "</title></head><body>\n");
2273         titleseen = 1;
2274 }
2275
2276 void
2277 g_nr(int argc, char **argv)
2278 {
2279         char *val;
2280
2281         if (argc > 1) {
2282                 if (argc == 2)
2283                         val = "0";
2284                 else
2285                         val = argv[2];
2286                 dsnr(argv[1], val, &numregs);
2287         }
2288 }
2289
2290 void
2291 zerodivide(void)
2292 {
2293         fprint(2, "stdin %d(%s:%d): division by 0\n",
2294                 ssp->lno, ssp->filename, ssp->rlno);
2295 }
2296
2297 int
2298 numval(char **pline, int recur)
2299 {
2300         char *p;
2301         int neg, x, y;
2302
2303         x = neg = 0;
2304         p = *pline;
2305         while(*p == '-') {
2306                 neg = 1 - neg;
2307                 p++;
2308         }
2309         if (*p == '(') {
2310                 p++;
2311                 x = numval(&p, 1);
2312                 if (*p != ')')
2313                         goto done;
2314                 p++;
2315         }
2316         else while(*p >= '0' && *p <= '9')
2317                 x = 10*x + *p++ - '0';
2318         if (neg)
2319                 x = -x;
2320         if (recur)
2321             for(;;) {
2322                 switch(*p++) {
2323                 case '+':
2324                         x += numval(&p, 0);
2325                         continue;
2326                 case '-':
2327                         x -= numval(&p, 0);
2328                         continue;
2329                 case '*':
2330                         x *= numval(&p, 0);
2331                         continue;
2332                 case '/':
2333                         y = numval(&p, 0);
2334                         if (y == 0) {
2335                                 zerodivide();
2336                                 x = 0;
2337                                 goto done;
2338                         }
2339                         x /= y;
2340                         continue;
2341                 case '<':
2342                         if (*p == '=') {
2343                                 p++;
2344                                 x = x <= numval(&p, 0);
2345                                 continue;
2346                         }
2347                         x = x < numval(&p, 0);
2348                         continue;
2349                 case '>':
2350                         if (*p == '=') {
2351                                 p++;
2352                                 x = x >= numval(&p, 0);
2353                                 continue;
2354                         }
2355                         x = x > numval(&p, 0);
2356                         continue;
2357                 case '=':
2358                         if (*p == '=')
2359                                 p++;
2360                         x = x == numval(&p, 0);
2361                         continue;
2362                 case '&':
2363                         x &= numval(&p, 0);
2364                         continue;
2365                 case ':':
2366                         x |= numval(&p, 0);
2367                         continue;
2368                 case '%':
2369                         y = numval(&p, 0);
2370                         if (!y) {
2371                                 zerodivide();
2372                                 goto done;
2373                         }
2374                         x %= y;
2375                         continue;
2376                 }
2377                 --p;
2378                 break;
2379         }
2380  done:
2381         *pline = p;
2382         return x;
2383 }
2384
2385 int
2386 iftest(char *p, char **bp)
2387 {
2388         char *p1;
2389         int c, neg, rv;
2390
2391         rv = neg = 0;
2392         if (*p == '!') {
2393                 neg = 1;
2394                 p++;
2395         }
2396         c = *p;
2397         if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '('/*)*/) {
2398                 if (numval(&p,1) >= 1)
2399                         rv = 1;
2400                 goto done;
2401         }
2402         switch(c) {
2403         case 't':
2404         case 'o':
2405                 rv = 1;
2406         case 'n':
2407         case 'e':
2408                 p++;
2409                 goto done;
2410         }
2411         for(p1 = ++p; *p != c; p++)
2412                 if (!*p)
2413                         goto done;
2414         for(p++;;) {
2415                 if (*p != *p1++) {
2416                         while(*p && *p++ != c);
2417                         goto done;
2418                 }
2419                 if (*p++ == c)
2420                         break;
2421         }
2422         rv = 1;
2423  done:
2424         if (neg)
2425                 rv = 1 - rv;
2426         while(*p == ' ' || *p == '\t')
2427                 p++;
2428         *bp = p;
2429         return rv;
2430 }
2431
2432 void
2433 scanline(char *p, char *e, int wantnl)
2434 {
2435         int c;
2436         Rune r;
2437
2438         while((c = getrune()) == ' ' || c == '\t') ;
2439         while(p < e) {
2440                 if (c < 0)
2441                         break;
2442                 if (c < Runeself) {
2443                         if (c == '\n') {
2444                                 if (wantnl)
2445                                         *p++ = c;
2446                                 break;
2447                         }
2448                         *p++ = c;
2449                 }
2450                 else {
2451                         r = c;
2452                         p += runetochar(p, &r);
2453                 }
2454                 c = getrune();
2455         }
2456         *p = 0;
2457 }
2458
2459 void
2460 pushbody(char *line)
2461 {
2462         char *b;
2463
2464         if (line[0] == '\\' && line[1] == '{' /*}*/ )
2465                 line += 2;
2466         if (strsp < Maxmstack - 1) {
2467                 pushstr(b = strdup(line));
2468                 mustfree[strsp] = b;
2469         }
2470 }
2471
2472 void
2473 skipbody(char *line)
2474 {
2475         int c, n;
2476
2477         if (line[0] != '\\' || line[1] != '{' /*}*/ )
2478                 return;
2479         for(n = 1;;) {
2480                 while((c = getrune()) != '\\')
2481                         if (c < 0)
2482                                 return;
2483                 c = getrune();
2484                 if (c == '{')
2485                         n++;
2486                 else if ((c == '}' && (c = getrune()) == '\n' && !--n)
2487                         || c < 0)
2488                         return;
2489         }
2490 }
2491
2492 int
2493 ifstart(char *line, char *e, char **bp)
2494 {
2495         int it;
2496         char *b;
2497
2498         b = copyline(line, e, 1);
2499         ungetrune();
2500         b[-1] = getrune();
2501         scanline(b, e, 1);
2502         it = iftest(line, bp);
2503         return it;
2504 }
2505
2506 void
2507 g_ie(char *line, char *e)
2508 {
2509         char *b;
2510
2511         if (elsetop >= Maxif-1) {
2512                 fprint(2, "ms2html: .ie's too deep\n");
2513                 return;
2514         }
2515         if (ifwastrue[++elsetop] = ifstart(line, e, &b))
2516                 pushbody(b);
2517         else
2518                 skipbody(b);
2519 }
2520
2521 void
2522 g_if(char *line, char *e)
2523 {
2524         char *b;
2525
2526         if (ifstart(line, e, &b))
2527                 pushbody(b);
2528         else
2529                 skipbody(b);
2530 }
2531
2532 void
2533 g_el(char *line, char *e)
2534 {
2535         if (elsetop < 0)
2536                 return;
2537         scanline(line, e, 1);
2538         if (ifwastrue[elsetop--])
2539                 skipbody(line);
2540         else
2541                 pushbody(line);
2542 }
2543
2544 void
2545 g_ig(int argc, char **argv)
2546 {
2547         char *p, *s;
2548
2549         s = "..";
2550         if (argc > 1)
2551                 s = argv[1];
2552         for(;;) {
2553                 p = Brdline(&ssp->in, '\n');
2554                 if(p == nil)
2555                         break;
2556                 p[Blinelen(&ssp->in)-1] = 0;
2557                 if(strcmp(p, s) == 0)
2558                         break;
2559         }
2560 }
2561
2562 void
2563 g_ds(char *line, char *e)
2564 {
2565         char *b;
2566
2567         b = copyline(line, e, 1);
2568         if (b > line) {
2569                 copyline(b, e, 0);
2570                 if (*b == '"')
2571                         b++;
2572                 ds(line, b);
2573         }
2574 }
2575
2576 void
2577 g_as(char *line, char *e)
2578 {
2579         String *s;
2580         char *b;
2581
2582         b = copyline(line, e, 1);
2583         if (b == line)
2584                 return;
2585         copyline(b, e, 0);
2586         if (*b == '"')
2587                 b++;
2588         for(s = strings; s != nil; s = s->next)
2589                 if(strcmp(line, s->name) == 0)
2590                         break;
2591
2592         if(s == nil){
2593                 ds(line, b);
2594                 return;
2595         }
2596
2597         s->val = realloc(s->val, strlen(s->val) + strlen(b) + 1);
2598         strcat(s->val, b);
2599 }
2600
2601 void
2602 g_BS(int argc, char **argv)
2603 {
2604         int i;
2605
2606         if (argc > 1 && !weBref) {
2607                 Bprint(&bout, "<a href=\"%s\"", argv[1]);
2608                 for(i = 2; i < argc; i++)
2609                         Bprint(&bout, " %s", argv[i]);
2610                 Bprint(&bout, ">");
2611                 weBref = 1;
2612         }
2613 }
2614
2615 void
2616 g_BE(int, char**)
2617 {
2618         if (weBref) {
2619                 Bprint(&bout, "</a>");
2620                 weBref = 0;
2621         }
2622 }
2623
2624 void
2625 g_LB(int argc, char **argv)
2626 {
2627         if (argc > 1) {
2628                 if (weBref)
2629                         g_BE(0,nil);
2630                 Bprint(&bout, "<a name=\"%s\"></a>", argv[1]);
2631         }
2632 }
2633
2634 void
2635 g_RT(int, char**)
2636 {
2637         g_BE(0,nil);
2638         dohanginghead();
2639         closel();
2640         closefont();
2641 }