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 { """, L'"', },
203 { "Æ", L'Æ', },
204 { "Á", L'Á', },
205 { "Â", L'Â', },
206 { "À", L'À', },
207 { "Å", L'Å', },
208 { "Ã", L'Ã', },
209 { "Ä", L'Ä', },
210 { "Ç", L'Ç', },
211 { "Ð", L'Ð', },
212 { "É", L'É', },
213 { "Ê", L'Ê', },
214 { "È", L'È', },
215 { "Ë", L'Ë', },
216 { "Í", L'Í', },
217 { "Î", L'Î', },
218 { "Ì", L'Ì', },
219 { "Ï", L'Ï', },
220 { "Ñ", L'Ñ', },
221 { "Ó", L'Ó', },
222 { "Ô", L'Ô', },
223 { "Ò", L'Ò', },
224 { "Ø", L'Ø', },
225 { "Õ", L'Õ', },
226 { "Ö", L'Ö', },
227 { "Þ", L'Þ', },
228 { "Ú", L'Ú', },
229 { "Û", L'Û', },
230 { "Ù", L'Ù', },
231 { "Ü", L'Ü', },
232 { "Ý", L'Ý', },
233 { "á", L'á', },
234 { "â", L'â', },
235 { "æ", L'æ', },
236 { "à", L'à', },
237 { "&", L'&', },
238 { "å", L'å', },
239 { "ã", L'ã', },
240 { "ä", L'ä', },
241 { "ç", L'ç', },
242 { "é", L'é', },
243 { "ê", L'ê', },
244 { "è", L'è', },
245 { "ð", L'ð', },
246 { "ë", L'ë', },
247 { ">", L'>', },
248 { "í", L'í', },
249 { "î", L'î', },
250 { "ì", L'ì', },
251 { "ï", L'ï', },
252 { "<", L'<', },
253 { "ñ", L'ñ', },
254 { "ó", L'ó', },
255 { "ô", L'ô', },
256 { "ò", L'ò', },
257 { "ø", L'ø', },
258 { "õ", L'õ', },
259 { "ö", L'ö', },
260 { "ß", L'ß', },
261 { "þ", L'þ', },
262 { "ú", L'ú', },
263 { "û", L'û', },
264 { "ù", L'ù', },
265 { "ü", L'ü', },
266 { "ý", L'ý', },
267 { "ÿ", L'ÿ', },
268 { "¡", L'¡', },
269 { "¢", L'¢', },
270 { "£", L'£', },
271 { "¤", L'¤', },
272 { "¥", L'¥', },
273 { "¦", L'¦', },
274 { "§", L'§', },
275 { "¨", L'¨', },
276 { "©", L'©', },
277 { "ª", L'ª', },
278 { "«", L'«', },
279 { "¬", L'¬', },
280 { "­", L'', },
281 { "®", L'®', },
282 { "¯", L'¯', },
283 { "°", L'°', },
284 { "±", L'±', },
285 { "²", L'²', },
286 { "³", L'³', },
287 { "´", L'´', },
288 { "µ", L'µ', },
289 { "¶", L'¶', },
290 { "·", L'·', },
291 { "¸", L'¸', },
292 { "¹", L'¹', },
293 { "º", L'º', },
294 { "»", L'»', },
295 { "¼", L'¼', },
296 { "½", L'½', },
297 { "¾", L'¾', },
298 { "¿", L'¿', },
299
300 { "*", L'•', },
301 { "¤", L'□', },
302 { "º", L'◊', },
303 { "(tm)", L'™', },
304 {"Α", L'Α',},
305 {"Β", L'Β',},
306 {"Γ", L'Γ',},
307 {"Δ", L'Δ',},
308 {"Ε", L'Ε',},
309 {"Ζ", L'Ζ',},
310 {"Η", L'Η',},
311 {"Θ", L'Θ',},
312 {"Ι", L'Ι',},
313 {"Κ", L'Κ',},
314 {"Λ", L'Λ',},
315 {"Μ", L'Μ',},
316 {"Ν", L'Ν',},
317 {"Ξ", L'Ξ',},
318 {"Ο", L'Ο',},
319 {"Π", L'Π',},
320 {"Ρ", L'Ρ',},
321 {"΢", L'',},
322 {"Σ", L'Σ',},
323 {"Τ", L'Τ',},
324 {"Υ", L'Υ',},
325 {"Φ", L'Φ',},
326 {"Χ", L'Χ',},
327 {"Ψ", L'Ψ',},
328 {"Ω", L'Ω',},
329 {"α", L'α',},
330 {"β", L'β',},
331 {"γ", L'γ',},
332 {"δ", L'δ',},
333 {"ε", L'ε',},
334 {"ζ", L'ζ',},
335 {"η", L'η',},
336 {"θ", L'θ',},
337 {"ι", L'ι',},
338 {"κ", L'κ',},
339 {"λ", L'λ',},
340 {"μ", L'μ',},
341 {"ν", L'ν',},
342 {"ξ", L'ξ',},
343 {"ο", L'ο',},
344 {"π", L'π',},
345 {"ρ", L'ρ',},
346 {"ς", L'ς',},
347 {"σ", L'σ',},
348 {"τ", L'τ',},
349 {"υ", L'υ',},
350 {"φ", L'φ',},
351 {"χ", L'χ',},
352 {"ψ", L'ψ',},
353 {"ω", L'ω',},
354
355 { "<-", L'←', },
356 { "^", L'↑', },
357 { "->", L'→', },
358 { "v", L'↓', },
359 { "!=", L'≠', },
360 { "<=", L'≤', },
361 { "...", L'⋯', },
362 {"∈", L'∈', },
363
364 {"–", L'–', },
365 {"—", L'—', },
366
367 { "CYRILLIC XYZZY", L'й', },
368 { "CYRILLIC XYZZY", L'ъ', },
369 { "CYRILLIC Y", L'ь', },
370 { "CYRILLIC YA", L'я', },
371 { "CYRILLIC YA", L'ё', },
372 { "¿", 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*", "Å", },
387 { "o\"", "ö", },
388 { "ff", "ff", },
389 { "fi", "fi", },
390 { "fl", "fl", },
391 { "Fi", "ffi", },
392 { "ru", "_", },
393 { "em", "­", },
394 { "14", "¼", },
395 { "12", "½", },
396 { "co", "©", },
397 { "de", "°", },
398 { "dg", "¡", },
399 { "fm", "´", },
400 { "rg", "®", },
401 { "bu", "*", },
402 { "sq", "¤", },
403 { "hy", "-", },
404 { "pl", "+", },
405 { "mi", "-", },
406 { "mu", "×", },
407 { "di", "÷", },
408 { "eq", "=", },
409 { "==", "==", },
410 { ">=", ">=", },
411 { "<=", "<=", },
412 { "!=", "!=", },
413 { "+-", "±", },
414 { "no", "¬", },
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", "Ø", },
436 { "aa", "´", },
437 { "ga", "`", },
438 { "ci", "O", },
439 { "L1", "DEATHSTAR", },
440 { "sc", "§", },
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", "™", },
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*
emalloc(uint n)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
dsnr(char * name,char * val,String ** l)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
ds(char * name,char * val)553 ds(char *name, char *val)
554 {
555 dsnr(name, val, &strings);
556 }
557
558 /* look up a defined string */
559 char*
getds(char * name)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 *
getnr(char * name)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
pushstr(char * p)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*
getmacro(char * name)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
pushsrc(char * name)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
getrune(void)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
ungetrune(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*
changefont(Font * f)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*
changebackfont(void)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*
changesize(int amount)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*
getnext(void)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 "¿";
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 "&";
1042 case '<':
1043 if(msp >= 0 || strsp >= 0)
1044 return "<";
1045 return "<";
1046 case '>':
1047 if(msp >= 0 || strsp >= 0)
1048 return ">";
1049 return ">";
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*
copyline(char * p,char * e,int arg0)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*
copyarg(char * p,char * e,int * nullarg)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
parseargs(char * p,char * e,char ** argv)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
dodirective(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
printarg(char * a)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
printargs(int argc,char ** argv)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
dohangingdt(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
dohangingau(void)1292 dohangingau(void)
1293 {
1294 if(hangingau == 0)
1295 return;
1296 Bprint(&bout, "</I></DL>\n");
1297 hangingau = 0;
1298 }
1299
1300 void
dohanginghead(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
doconvert(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> <br>\n");
1358 Bprint(&bout, "<A href=http://www.lucent.com/copyright.html>\n");
1359 t = localtime(time(nil));
1360 Bprint(&bout, "Copyright</A> © %d Alcatel-Lucent Inc. All rights reserved.\n",
1361 t->year+1900);
1362 Bprint(&bout, "</body></html>\n");
1363 }
1364
1365 static void
usage(void)1366 usage(void)
1367 {
1368 sysfatal("usage: ms2html [-q] [-b basename] [-d '$$'] [-t title]");
1369 }
1370
1371 void
main(int argc,char ** argv)1372 main(int argc, char **argv)
1373 {
1374 quiet = 1;
1375 ARGBEGIN {
1376 case 't':
1377 title = EARGF(usage());
1378 break;
1379 case 'b':
1380 basename = EARGF(usage());
1381 break;
1382 case 'q':
1383 quiet = 0;
1384 break;
1385 case 'd':
1386 delim = EARGF(usage());
1387 break;
1388 case '?':
1389 default:
1390 usage();
1391 } ARGEND;
1392
1393 Binit(&bout, 1, OWRITE);
1394
1395 ds("R", "®");
1396
1397 doconvert();
1398 exits(nil);
1399 }
1400
1401 void
g_notyet(int,char ** argv)1402 g_notyet(int, char **argv)
1403 {
1404 fprint(2, "ms2html: .%s not yet supported\n", argv[0]);
1405 }
1406
1407 void
g_ignore(int,char ** argv)1408 g_ignore(int, char **argv)
1409 {
1410 if(quiet)
1411 return;
1412 fprint(2, "ms2html: line %d: ignoring .%s\n", ssp->lno, argv[0]);
1413 }
1414
1415 void
g_PP(int,char **)1416 g_PP(int, char**)
1417 {
1418 dohanginghead();
1419 closel();
1420 closefont();
1421 Bprint(&bout, "<P>\n");
1422 paragraph = 1;
1423 }
1424
1425 void
g_LP(int,char **)1426 g_LP(int, char**)
1427 {
1428 dohanginghead();
1429 closel();
1430 closefont();
1431 Bprint(&bout, "<br> <br>\n");
1432 }
1433
1434 /* close a list */
1435 void
closel(void)1436 closel(void)
1437 {
1438 g_P2(1, nil);
1439 dohangingau();
1440 if(paragraph){
1441 Bprint(&bout, "</P>\n");
1442 paragraph = 0;
1443 }
1444 switch(list){
1445 case Lordered:
1446 Bprint(&bout, "</ol>\n");
1447 break;
1448 case Lunordered:
1449 Bprint(&bout, "</ul>\n");
1450 break;
1451 case Lother:
1452 case Ldef:
1453 Bprint(&bout, "</dl>\n");
1454 break;
1455 }
1456 list = 0;
1457
1458 }
1459
1460
1461 void
g_IP(int argc,char ** argv)1462 g_IP(int argc, char **argv)
1463 {
1464 dohanginghead();
1465 switch(list){
1466 default:
1467 closel();
1468 if(argc > 1){
1469 if(strcmp(argv[1], "1") == 0){
1470 list = Lordered;
1471 listnum = 1;
1472 Bprint(&bout, "<OL>\n");
1473 } else if(strcmp(argv[1], "\\(bu") == 0){
1474 list = Lunordered;
1475 Bprint(&bout, "<UL>\n");
1476 } else {
1477 list = Lother;
1478 Bprint(&bout, "<DL COMPACT>\n");
1479 }
1480 } else {
1481 list = Lother;
1482 Bprint(&bout, "<DL>\n");
1483 }
1484 break;
1485 case Lother:
1486 case Lordered:
1487 case Lunordered:
1488 break;
1489 }
1490
1491 switch(list){
1492 case Lother:
1493 Bprint(&bout, "<DT>");
1494 if(argc > 1)
1495 printarg(argv[1]);
1496 else
1497 Bprint(&bout, "<DT> ");
1498 Bprint(&bout, "<DD>\n");
1499 break;
1500 case Lordered:
1501 case Lunordered:
1502 Bprint(&bout, "<LI>\n");
1503 break;
1504 }
1505 }
1506
1507 /*
1508 * .5i is one <DL><DT><DD>
1509 */
1510 void
g_in(int argc,char ** argv)1511 g_in(int argc, char **argv)
1512 {
1513 float f;
1514 int delta, x;
1515 char *p;
1516
1517 f = indent/0.5;
1518 delta = f;
1519 if(argc <= 1){
1520 indent = 0.0;
1521 } else {
1522 f = strtod(argv[1], &p);
1523 switch(*p){
1524 case 'i':
1525 break;
1526 case 'c':
1527 f = f / 2.54;
1528 break;
1529 case 'P':
1530 f = f / 6;
1531 break;
1532 default:
1533 case 'u':
1534 case 'm':
1535 f = f * (12 / 72);
1536 break;
1537 case 'n':
1538 f = f * (6 / 72);
1539 break;
1540 case 'p':
1541 f = f / 72.0;
1542 break;
1543 }
1544 switch(argv[1][0]){
1545 case '+':
1546 case '-':
1547 indent += f;
1548 break;
1549 default:
1550 indent = f;
1551 break;
1552 }
1553 }
1554 if(indent < 0.0)
1555 indent = 0.0;
1556 f = (indent/0.5);
1557 x = f;
1558 delta = x - delta;
1559 while(delta < 0){
1560 Bprint(&bout, "</DL>\n");
1561 delta++;
1562 }
1563 while(delta > 0){
1564 Bprint(&bout, "<DL><DT><DD>\n");
1565 delta--;
1566 }
1567 }
1568
1569 void
g_HP(int,char **)1570 g_HP(int, char**)
1571 {
1572 switch(list){
1573 default:
1574 closel();
1575 list = Ldef;
1576 hangingdt = 1;
1577 Bprint(&bout, "<DL><DT>\n");
1578 break;
1579 case Ldef:
1580 if(hangingdt)
1581 Bprint(&bout, "<DD>");
1582 Bprint(&bout, "<DT>");
1583 hangingdt = 1;
1584 break;
1585 }
1586 }
1587
1588 void
g_SH(int,char **)1589 g_SH(int, char**)
1590 {
1591 dohanginghead();
1592 dohangingcenter();
1593 closel();
1594 closefont();
1595 Bprint(&bout, "<H%d>", HH);
1596 hanginghead = HH;
1597 }
1598
1599 void
g_NH(int argc,char ** argv)1600 g_NH(int argc, char **argv)
1601 {
1602 int i, level;
1603
1604 closel();
1605 closefont();
1606
1607 dohanginghead();
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
g_TL(int,char **)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
dohangingcenter(void)1669 dohangingcenter(void)
1670 {
1671 if(hangingcenter){
1672 Bprint(&bout, "</center>");
1673 hangingcenter = 1;
1674 }
1675 }
1676
1677 void
g_AU(int,char **)1678 g_AU(int, char**)
1679 {
1680 closel();
1681 dohanginghead();
1682 Bprint(&bout, "<DL><DD><I>");
1683 hangingau = 1;
1684 }
1685
1686 void
pushfont(Font * f)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
popfont(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
font(Font * f,int argc,char ** argv)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
closefont(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
g_B(int argc,char ** argv)1739 g_B(int argc, char **argv)
1740 {
1741 font(&bfont, argc, argv);
1742 }
1743
1744 void
g_R(int argc,char ** argv)1745 g_R(int argc, char **argv)
1746 {
1747 font(nil, argc, argv);
1748 }
1749
1750 void
g_BI(int argc,char ** argv)1751 g_BI(int argc, char **argv)
1752 {
1753 font(&bifont, argc, argv);
1754 }
1755
1756 void
g_CW(int argc,char ** argv)1757 g_CW(int argc, char **argv)
1758 {
1759 font(&cwfont, argc, argv);
1760 }
1761
1762 char*
lower(char * p)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
g_I(int argc,char ** argv)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
g_br(int,char **)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
g_P1(int,char **)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
g_P2(int,char **)1815 g_P2(int, char**)
1816 {
1817 if(example){
1818 example = 0;
1819 Bprint(&bout, "</PRE></TT></DL>\n");
1820 }
1821 }
1822
1823 void
g_SM(int,char ** argv)1824 g_SM(int, char **argv)
1825 {
1826 Bprint(&bout, "%s", argv[1]);
1827 }
1828
1829 void
g_ft(int argc,char ** argv)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
g_sp(int argc,char ** argv)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> <br>\n");
1877 Bprint(&bout, "<HR>\n");
1878 Bprint(&bout, "<br> <br>\n");
1879 } else
1880 for(; n > 0; n--)
1881 Bprint(&bout, "<br> <br>\n");
1882 }
1883
1884 void
rm_loop(char * name,String ** l)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
g_rm(int argc,char ** argv)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, ¯os);
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
g_AB(int,char **)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
g_AE(int,char **)1928 g_AE(int, char**)
1929 {
1930 Bprint(&bout, "</DL>\n");
1931 }
1932
1933 void
g_FS(int,char **)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
g_FE(int,char **)1946 g_FE(int, char **)
1947 {
1948 Bprint(&bout, "</I><DT> <DD>");
1949 closel();
1950 Bprint(&bout, "<br>\n");
1951 }
1952
1953 void
g_de(int argc,char ** argv)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
g_hrule(int,char **)2019 g_hrule(int, char**)
2020 {
2021 Bprint(&bout, "<HR>\n");
2022 }
2023
2024 void
g_BX(int argc,char ** argv)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
g_IH(int,char **)2033 g_IH(int, char**)
2034 {
2035 Bprint(&bout, "Bell Laboratories, Naperville, Illinois, 60540\n");
2036 }
2037
2038 void
g_MH(int,char **)2039 g_MH(int, char**)
2040 {
2041 Bprint(&bout, "Bell Laboratories, Murray Hill, NJ, 07974\n");
2042 }
2043
2044 void
g_PY(int,char **)2045 g_PY(int, char**)
2046 {
2047 Bprint(&bout, "Bell Laboratories, Piscataway, NJ, 08854\n");
2048 }
2049
2050 void
g_HO(int,char **)2051 g_HO(int, char**)
2052 {
2053 Bprint(&bout, "Bell Laboratories, Holmdel, NJ, 07733\n");
2054 }
2055
2056 void
g_QS(int,char **)2057 g_QS(int, char**)
2058 {
2059 Bprint(&bout, "<BLOCKQUOTE>\n");
2060 }
2061
2062 void
g_QE(int,char **)2063 g_QE(int, char**)
2064 {
2065 Bprint(&bout, "</BLOCKQUOTE>\n");
2066 }
2067
2068 void
g_RS(int,char **)2069 g_RS(int, char**)
2070 {
2071 Bprint(&bout, "<DL><DD>\n");
2072 }
2073
2074 void
g_RE(int,char **)2075 g_RE(int, char**)
2076 {
2077 Bprint(&bout, "</DL>\n");
2078 }
2079
2080 int gif;
2081
2082 void
g_startgif(int,char ** argv)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
g_lf(int argc,char ** argv)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
g_so(int argc,char ** argv)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
g_BP(int argc,char ** argv)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
g__H(int argc,char ** argv)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
g__T(int argc,char ** argv)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
g_nr(int argc,char ** argv)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
zerodivide(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
numval(char ** pline,int recur)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
iftest(char * p,char ** bp)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
scanline(char * p,char * e,int wantnl)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
pushbody(char * line)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
skipbody(char * line)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
ifstart(char * line,char * e,char ** bp)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
g_ie(char * line,char * e)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
g_if(char * line,char * e)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
g_el(char * line,char * e)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
g_ig(int argc,char ** argv)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
g_ds(char * line,char * e)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
g_as(char * line,char * e)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
g_BS(int argc,char ** argv)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
g_BE(int,char **)2616 g_BE(int, char**)
2617 {
2618 if (weBref) {
2619 Bprint(&bout, "</a>");
2620 weBref = 0;
2621 }
2622 }
2623
2624 void
g_LB(int argc,char ** argv)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
g_RT(int,char **)2635 g_RT(int, char**)
2636 {
2637 g_BE(0,nil);
2638 dohanginghead();
2639 closel();
2640 closefont();
2641 }
2642