1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1996-2012 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * troff to html filter
26 *
27 * NOTE: not handled in state.groff mode
28 *
29 * \?anything\?
30 */
31
32 static const char usage[] =
33 "[-?\n@(#)$Id: troff2html (AT&T Research) 2004-04-26 $\n]"
34 USAGE_LICENSE
35 "[+NAME?troff2html - convert troff/groff input to html]"
36 "[+DESCRIPTION?\btroff2html\b converts \btroff\b(1) (or \bgroff\b(1),"
37 " depending on the processing mode) input documents to an \bhtml\b"
38 " document on the standard output. Although a full \atroff\a parse"
39 " is done, many features are ignored in the mapping to \bhtml\b.]"
40 "[+?The \atroff\a \bt\b condition test evaluates \btrue\b, the \bn\b"
41 " condition tests evaluates \bfalse\b, and the \agroff\a compatibility"
42 " register \b\\\\n[.C]]\b evaluates to 0 (enable \agroff\a parsing)"
43 " if it is referenced before the first \agroff\a \b.cp\b (compatibility"
44 " mode) request.]"
45 "[+?The generated \bhtml\b has properly nested begin/end tags, even though most"
46 " browsers don't care.]"
47
48 "[i:identify?Reads identification options from \afile\a. Unknown options"
49 " are silently ignored. See the \b.xx\b request below for a description"
50 " of the options.]:[file]"
51 "[I:include?Appends \adirectory\a to the list of directories searched"
52 " for \b--macros\b and \b.so\b request files.]:[directory]"
53 "[m:macros?Locates and reads the macro package file \apackage\a. In order"
54 " to accomodate different \atroff\a installation styles the file search"
55 " order is fairly involved:]:[package]{"
56 " [+./package?]"
57 " [+directory/package?for all \b--include\b directories]"
58 " [+directory/tmac.package?for all \b--include\b directories]"
59 " [+../lib/tmac/tmac.package?for all directories on \b$PATH\b]"
60 " [+../lib/html/package?for all directories on \b$PATH\b]"
61 " [+../lib/html/mpackage.tr?for all directories on \b$PATH\b]"
62 " [+../share/groff/tmac/tmac.package?for all directories on \b$PATH\b]"
63 " [+../groff/share/groff/tmac/tmac.package?for all directories on \b$PATH\b]"
64 "}"
65 "[r:register?Initializes the number \aregister\a to \aN\a.]:[registerN]"
66 "[s:script?Reads \ascript\a as if it came from a file. \b--script='.nr A 123'\b"
67 " is equivalent to \b-rA123\b.]:[script]"
68 "[v:verbose?Enables verbose error and warning messages. Following \atroff\a"
69 " tradition, \btroff2html\b by default does not warn about unknown"
70 " requests; \b--verbose\b enables such warnings.]"
71
72 "[+EXTENSIONS?\b.xx\b \aname\a[=\avalue\a]] is a special \btroff2html\b"
73 " request that handles program tracing, \bhtml\b extensions and \atroff\a"
74 " macro package magic that went way past the author's willingness"
75 " to understand. Supported operations are:]{"
76 " [+author=text?Specifies the contact information for the document"
77 " HEAD section.]"
78 " [+background=URL?Specifies the document background URL.]"
79 " [+debug=level?The debug trace \alevel\a; higher levels produce"
80 " more output.]"
81 " [+get=+-register?Traces each \bget\b for the named number"
82 " \aregister\a. \b-\b turns tracing off.]"
83 " [+hot='word ...'?Adds (\b+\b) or removes (\b-\b) \aword\a ... from"
84 " the hot-word list. Constructs that match (\ahot-word\a ..."
85 " \aSWITCH-FONT\a text \aSWITCH-FONT\a ... ) adds \atext\a as"
86 " a hot link to another portion of the document. \brefer\b and"
87 " \bsee\b are the default hot words. Case is ignored when"
88 " matching hot words.]"
89 " [+logo=URL?Specifies the logo/banner image URL that is centered"
90 " at the top of the document.]"
91 " [+mailto=address?Sets the email \aaddress\a to send comments and"
92 " suggestions.]"
93 " [+meta.name?Emits the \bhtml\b tag \b<META name=\b\"\aname\a\""
94 " \bcontent=\b\"\acontent\a\"\b>\b.]"
95 " [+package=text?\atext\a is prepended to the \bhtml\b document title.]"
96 " [+set=+-register?Traces each \bset\b for the named number"
97 " \aregister\a. \b-\b turns tracing off.]"
98 " [+title=text?Sets the document title.]"
99 "}"
100 "[+?Local URL links are generated for all top level headings. These can be"
101 " referenced by embedding the benign (albeit convoluted) \atroff\a"
102 " construct \\h'0*\\w\"label\"'text\\g'0', where \alabel\a is the"
103 " local link label and \atext\a is the hot link text. If \alabel\a"
104 " and \atext\a are the same then use \\h'0*1'text\\h'0'.]"
105
106 "\n"
107 "\n[ file ... ]\n"
108 "\n"
109 "[+SEE ALSO?\bgroff\b(1), \bhtml2rtf\b(1), \bman\b(1), \bmm\b(1),"
110 " \bmm2html\b(1), \btroff\b(1)]"
111 ;
112
113 #include "troff2html.h"
114
115 #include <error.h>
116 #include <hashkey.h>
117 #include <ls.h>
118 #include <tm.h>
119
120 /*
121 * intermediate code
122 *
123 * CODE_0
124 * CODE_1 <op>
125 * CODE_2 <data> <op>
126 * CODE_n <length> <op> <length-data>
127 */
128
129 #define DEFAULT_cc '.'
130 #define DEFAULT_c2 '\''
131 #define DEFAULT_ec '\\'
132 #define DEFAULT_ft 1
133 #define DEFAULT_pc '%'
134 #define DEFAULT_ps 10
135 #define DEFAULT_vs 12
136
137 #define CODE_1 1
138 #define CODE_2 2
139 #define CODE_n 3
140 #define CODE_0 4
141
142 #define END(x) ((x)|OP_END)
143 #define LABEL(x) ((x)|OP_LABEL)
144 #define OP(x) ((x)&077)
145
146 #define OP_END 0200
147 #define OP_LABEL 0100
148
149 #define OP_a 1
150 #define OP_body 2
151 #define OP_br 3
152 #define OP_cc 4
153 #define OP_center 5
154 #define OP_comment 6
155 #define OP_dd 7
156 #define OP_div 8
157 #define OP_dl 9
158 #define OP_dt 10
159 #define OP_fn 11
160 #define OP_ft1 12
161 #define OP_ft2 13
162 #define OP_ft3 14
163 #define OP_ft4 15
164 #define OP_ft5 16
165 #define OP_h2 17
166 #define OP_h3 18
167 #define OP_h4 19
168 #define OP_head 20
169 #define OP_hr 21
170 #define OP_html 22
171 #define OP_link 23
172 #define OP_p 24
173 #define OP_pre 25
174 #define OP_ps 26
175 #define OP_sub 27
176 #define OP_sup 28
177 #define OP_ta 29
178 #define OP_tab 30
179 #define OP_tab_data 31
180 #define OP_tab_head 32
181 #define OP_tab_row 33
182 #define OP_title 34
183 #define OP_RAW 35
184
185 #define OP_ft (OP_ft1-1)
186
187 #define ARG_ALIGN(a) (((a)&3)-1)
188 #define ARG_ATTR(a) ((a)&0x0fff)
189
190 #define ARG_left 0x0001
191 #define ARG_center 0x0002
192 #define ARG_right 0x0003
193
194 #define ARG_compact 0x0010
195 #define ARG_wide 0x0020
196
197 #define ATT_INDEX(a) (((a)&0x0fff)-1)
198
199 #define ATT_NUMBER 0x8000
200
201 #define ATT_background 1
202 #define ATT_href 2
203 #define ATT_lref 3
204 #define ATT_id 4
205 #define ATT_name 5
206 #define ATT_size (ATT_NUMBER|6)
207 #define ATT_src 7
208
209 #define LINE 0x2000
210 #define LIST 0x4000
211 #define STACK 0x8000
212
213 #define DIVERTED() (state.out_top>state.out_stack)
214 #undef EOF
215 #define EOF 0
216 #define INDEX(a,b) (((a)<<8)|(b))
217 #define ISFILE() ((state.in_top-1)->ip)
218 #define GETC() (*state.in++)
219 #define GROFFINIT() do{if(!state.groff_init)state.groff|=state.groff_init=1;}while(0)
220 #define UNGETC(c) (*--state.in=(c))
221
222 #define UNGET_MAX (SF_BUFSIZE/8)
223
224 State_t state;
225
226 /*
227 * sfstruse(sp) with fatal error check
228 */
229
230 static char*
use(Sfio_t * sp)231 use(Sfio_t* sp)
232 {
233 char* s;
234
235 if (!(s = sfstruse(sp)))
236 error(ERROR_SYSTEM|3, "out of space");
237 return s;
238 }
239
240 /*
241 * push input file/string
242 */
243
244 static void
pushin(char * name,int line,Sfio_t * ip,char * data,Arg_t * arg)245 pushin(char* name, int line, Sfio_t* ip, char* data, Arg_t* arg)
246 {
247 register Pushin_t* pp;
248 int n;
249
250 if (state.in_top >= &state.in_stack[elementsof(state.in_stack)])
251 error(3, "input stack overflow");
252 pp = state.in_top++;
253 pp->in = state.in;
254 if (!(pp->ip = ip))
255 {
256 if (!(state.in = (unsigned char*)data) || !*state.in)
257 {
258 state.in = pp->in;
259 --state.in_top;
260 return;
261 }
262 }
263 else if (!(pp->buf = oldof(0, unsigned char, SF_BUFSIZE, UNGET_MAX + 1)))
264 error(ERROR_SYSTEM|3, "out of space [file pushin]");
265 else
266 {
267 pp->buf += UNGET_MAX;
268 if ((n = sfread(ip, pp->buf, SF_BUFSIZE)) <= 0)
269 {
270 if (ip && ip != sfstdin)
271 sfclose(ip);
272 state.in_top--;
273 return;
274 }
275 pp->end = pp->buf + n;
276 *pp->end++ = EOF;
277 state.in = pp->buf;
278 }
279 pp->file = error_info.file;
280 if (name)
281 {
282 if (!state.original.file)
283 {
284 state.original.file = error_info.file;
285 state.original.line = error_info.line;
286 }
287 error_info.file = name;
288 }
289 pp->line = error_info.line;
290 if (line < 0)
291 state.noline++;
292 else
293 {
294 if (line > 0)
295 error_info.line = line;
296 if (state.noline > 0)
297 state.noline++;
298 }
299 if (arg)
300 {
301 pp->mac = state.mac;
302 state.mac = arg;
303 if (pp->top.sp)
304 sfstrseek(pp->top.sp, 0, SEEK_SET);
305 else if (!(pp->top.sp = sfstropen()))
306 error(ERROR_SYSTEM|3, "out of space [macro call]");
307 pp->tag = state.tag;
308 state.tag = &pp->top;
309 }
310 else
311 pp->tag = 0;
312 }
313
314 static long expression(char*, char**, int);
315
316 /*
317 * return the next expression operand
318 */
319
320 static long
operand(register char * s,char ** e,int scale)321 operand(register char* s, char** e, int scale)
322 {
323 register int c;
324 register long n;
325 register long f;
326 int abs;
327 int neg;
328 long d;
329 char* x;
330
331 while (isspace(*s))
332 s++;
333 if (abs = *s == '|')
334 s++;
335 if (neg = *s == '-')
336 s++;
337 else if (*s == '+')
338 s++;
339 d = 1;
340 if (*s == '(')
341 {
342 n = (state.groff && isalpha(*(s + 1)) && *(s + 2) == ';') ?
343 expression(s + 3, &x, *(s + 1)) :
344 expression(s + 1, &x, scale);
345 s = x;
346 if (*s == ')')
347 s++;
348 c = *s++;
349 }
350 else
351 {
352 n = 0;
353 while ((c = *s++) >= '0' && c <= '9')
354 n = n * 10 + c - '0';
355 if (c == '.')
356 {
357 f = 0;
358 while ((c = *s++) >= '0' && c <= '9')
359 {
360 d *= 10;
361 n *= 10;
362 f = f * 10 + c - '0';
363 }
364 n += f;
365 }
366 }
367 for (;;)
368 {
369 switch (c)
370 {
371 case 'P':
372 n *= 72;
373 break;
374 case 'c':
375 n *= 170;
376 break;
377 case 'i':
378 n *= 432;
379 break;
380 case 'm':
381 n *= 8 * state.env->ps.current;
382 break;
383 case 'n':
384 case 'w':
385 n *= 6 * state.env->ps.current;
386 break;
387 case 'p':
388 n *= 6;
389 break;
390 case 's':
391 case 'u':
392 case 'z':
393 break;
394 case 'v':
395 n *= 6 * state.env->vs.current;
396 break;
397 default:
398 s--;
399 if (c = scale)
400 {
401 scale = 0;
402 continue;
403 }
404 break;
405 }
406 break;
407 }
408 n /= d;
409 if (e)
410 {
411 while (isspace(*s))
412 s++;
413 *e = s;
414 }
415 if (abs)
416 return 1;
417 if (neg)
418 n = -n;
419 return n;
420 }
421
422 /*
423 * convert units to scale
424 */
425
426 static long
convert(long n,int up,int scale)427 convert(long n, int up, int scale)
428 {
429 long m;
430
431 m = n;
432 switch (scale)
433 {
434 case 'M':
435 if (up)
436 n *= state.env->ps.current / 12;
437 else
438 n /= state.env->ps.current / 12;
439 break;
440 case 'P':
441 if (up)
442 n *= 72;
443 else
444 n /= 72;
445 break;
446 case 'c':
447 if (up)
448 n *= 170;
449 else
450 n /= 170;
451 break;
452 case 'i':
453 if (up)
454 n *= 432;
455 else
456 n /= 432;
457 break;
458 case 'm':
459 if (up)
460 n *= 8 * state.env->ps.current;
461 else
462 n /= 8 * state.env->ps.current;
463 break;
464 case 'n':
465 case 'w':
466 if (up)
467 n *= 6 * state.env->ps.current;
468 else
469 n /= 6 * state.env->ps.current;
470 break;
471 case 'p':
472 if (up)
473 n *= 6;
474 else
475 n /= 6;
476 break;
477 case 's':
478 case 'u':
479 case 'z':
480 break;
481 case 'v':
482 if (up)
483 n *= 6 * state.env->vs.current;
484 else
485 n /= 6 * state.env->vs.current;
486 break;
487 }
488 return n ? n : (m > 0) ? 1 : 0;
489 }
490
491 /*
492 * evaluate numeric expression in s
493 */
494
495 static long
expression(char * s,char ** e,int scale)496 expression(char* s, char** e, int scale)
497 {
498 long n;
499 long m;
500
501 n = operand(s, &s, scale);
502 for (;;)
503 {
504 switch (*s++)
505 {
506 case CODE_2:
507 s++;
508 /*FALLTHROUGH*/
509 case CODE_1:
510 s++;
511 /*FALLTHROUGH*/
512 case CODE_0:
513 continue;
514 case '+':
515 n += operand(s, &s, scale);
516 continue;
517 case '-':
518 n -= operand(s, &s, scale);
519 continue;
520 case '/':
521 if (m = operand(s, &s, scale))
522 n /= m;
523 else
524 n = 0;
525 continue;
526 case '*':
527 n *= operand(s, &s, scale);
528 continue;
529 case '%':
530 if (m = operand(s, &s, scale))
531 n %= m;
532 else
533 n = 0;
534 continue;
535 case '<':
536 if (state.groff && *s == '?')
537 {
538 m = operand(s + 1, &s, scale);
539 if (m < n)
540 n = m;
541 }
542 else if (*s == '=')
543 n = n <= operand(s + 1, &s, scale);
544 else
545 n = n < operand(s, &s, scale);
546 continue;
547 case '>':
548 if (state.groff && *s == '?')
549 {
550 m = operand(s + 1, &s, scale);
551 if (m > n)
552 n = m;
553 }
554 else if (*s == '=')
555 n = n >= operand(s + 1, &s, scale);
556 else
557 n = n > operand(s, &s, scale);
558 continue;
559 case '=':
560 if (*s == '=')
561 s++;
562 n = n == operand(s, &s, scale);
563 continue;
564 case '&':
565 if (*s == '&')
566 s++;
567 n = (operand(s, &s, scale) > 0) && (n > 0);
568 continue;
569 case ':':
570 if (*s == ':')
571 s++;
572 n = (operand(s, &s, scale) > 0) || (n > 0);
573 continue;
574 default:
575 s--;
576 break;
577 }
578 break;
579 }
580 if (e)
581 {
582 while (isspace(*s))
583 s++;
584 *e = s;
585 }
586 if (scale)
587 n = convert(n, 0, scale);
588 return n;
589 }
590
591 /*
592 * evaluate a conditional expression
593 */
594
595 static long
cond(register char * s,char ** e)596 cond(register char* s, char** e)
597 {
598 register int c;
599 register int i;
600 register int q;
601 register long v;
602 register char* t;
603 char* u;
604
605 while (isspace(*s))
606 s++;
607 if (i = *s == '!')
608 s++;
609 while (isspace(*s))
610 s++;
611 switch (c = *s)
612 {
613 case '0':
614 case '1':
615 case '2':
616 case '3':
617 case '4':
618 case '5':
619 case '6':
620 case '7':
621 case '8':
622 case '9':
623 case '-':
624 case '+':
625 case '(':
626 v = expression(s, &u, 0) > 0;
627 s = u;
628 break;
629 case 'c':
630 s++;
631 if (*s == 'c' && *(s + 1) == 'h')
632 s += 2;
633 v = 0;
634 break;
635 case 'd':
636 case 'r':
637 t = s;
638 while (*++s && !isspace(*s));
639 q = *s;
640 *s = 0;
641 *t = c == 'd' ? '.' : 'n';
642 v = hashget(state.symbols, t) != 0;
643 *t = c;
644 *s = q;
645 break;
646 case 'g':
647 GROFFINIT();
648 v = state.groff != 0;
649 s++;
650 break;
651 case 'o':
652 case 't':
653 v = 1;
654 s++;
655 break;
656 default:
657 if (isalpha(c))
658 {
659 v = 0;
660 s++;
661 }
662 else
663 {
664 for (u = ++s; *s && *s != c; s++);
665 if (*s)
666 {
667 *s++ = 0;
668 for (t = s; *s && *s != c; s++);
669 if (*s)
670 *s++ = 0;
671 v = !strcmp(u, t);
672 }
673 else
674 v = 0;
675 }
676 break;
677 }
678 if (i)
679 v = !v;
680 while (isspace(*s))
681 s++;
682 if (e)
683 *e = s;
684 return v;
685 }
686
687 static void expand(Sfio_t*, char*);
688
689 /*
690 * pop input stream
691 * return non-0 on EOF
692 */
693
694 static int
popin(void)695 popin(void)
696 {
697 register Pushin_t* pp;
698 int n;
699
700 if (state.in_top <= state.in_stack)
701 {
702 state.in--;
703 return 1;
704 }
705 pp = state.in_top;
706 if (pp->loop)
707 {
708 state.in = (unsigned char*)sfstrbase(pp->loop);
709 return 0;
710 }
711 if (--pp == state.in_stack)
712 {
713 if (state.cond.level > 1 && state.verbose)
714 error(1, "%d missing .el request%s", state.cond.level - 1, state.cond.level == 2 ? "" : "s");
715 state.cond.level = 0;
716 }
717 if (pp->ip)
718 {
719 if (state.in < pp->end)
720 return 0;
721 if ((n = sfread(pp->ip, pp->buf, SF_BUFSIZE)) > 0)
722 {
723 pp->end = pp->buf + n;
724 *pp->end++ = EOF;
725 state.in = pp->buf;
726 return 0;
727 }
728 if (pp->ip && pp->ip != sfstdin)
729 sfclose(pp->ip);
730 free(pp->buf - UNGET_MAX);
731 }
732 state.in_top = pp;
733 if (state.noline > 0)
734 state.noline--;
735 if ((error_info.file = pp->file) == state.original.file)
736 {
737 state.original.file = 0;
738 state.original.line = 0;
739 }
740 error_info.line = pp->line;
741 if (pp->tag)
742 {
743 state.tag = pp->tag;
744 state.mac = pp->mac;
745 }
746 state.in = pp->in;
747 return 0;
748 }
749
750 /*
751 * copy <type><name> into buf of size n
752 */
753
754 static char*
nam(int type,register char * name,char * buf,size_t n)755 nam(int type, register char* name, char* buf, size_t n)
756 {
757 register char* t = buf;
758 register char* e = t + n - 1;
759
760 *t++ = type;
761 if ((*t++ = *name++) && (*t++ = *name))
762 {
763 if (state.groff)
764 while (t < e && (*t++ = *++name));
765 else
766 *t = 0;
767 }
768 return buf;
769 }
770
771 /*
772 * return number register pointer given name
773 * pointer created if not found
774 */
775
776 static Num_t*
num(register char * s)777 num(register char* s)
778 {
779 register Num_t* np;
780 char buf[MAXNAME];
781
782 if (!(np = (Num_t*)hashget(state.symbols, nam('n', s, buf, sizeof(buf)))))
783 {
784 if (!(np = newof(0, Num_t, 1, 0)))
785 error(ERROR_SYSTEM|3, "out of space [number]");
786 np->name = hashput(state.symbols, NiL, np);
787 np->format = '1';
788 np->increment = 1;
789 }
790 return np;
791 }
792
793 /*
794 * lookup or create (force!=0) io stream handle
795 */
796
797 static Stream_t*
iop(char * s,int force)798 iop(char* s, int force)
799 {
800 register Stream_t* sp;
801 char buf[MAXNAME];
802
803 if (!(sp = (Stream_t*)hashget(state.symbols, nam('s', s, buf, sizeof(buf)))))
804 {
805 if (!force) error(1, "iop %s lookup failed", buf);
806 if (!force)
807 return 0;
808 if (!(sp = newof(0, Stream_t, 1, 0)))
809 error(ERROR_SYSTEM|3, "out of space [stream]");
810 hashput(state.symbols, NiL, sp);
811 }
812 return sp;
813 }
814
815 /*
816 * push output stream np
817 */
818
819 static void
pushout(Sfio_t * np)820 pushout(Sfio_t* np)
821 {
822 if (state.out_top >= &state.out_stack[elementsof(state.out_stack)])
823 error(3, "output stack overflow");
824 *state.out_top++ = state.out;
825 iop("stdout", 1)->sp = state.out = np;
826 }
827
828 /*
829 * pop ouput stream
830 * if retain>0 then strdup() buffer
831 * append an extra 0 for easy arg '\n' append by if/ie/el
832 */
833
834 static char*
popout(void)835 popout(void)
836 {
837 char* s;
838
839 if (!DIVERTED())
840 return 0;
841 if (state.out == state.tag->sp)
842 sfputc(state.out, 0);
843 if (state.out == state.nul)
844 s = 0;
845 else
846 s = use(state.out);
847 iop("stdout", 1)->sp = state.out = *--state.out_top;
848 return s;
849 }
850
851 /*
852 * return next character with possible line join
853 */
854
855 static int
nextchar(void)856 nextchar(void)
857 {
858 int c;
859 int n;
860
861 for (;;)
862 {
863 if ((c = GETC()) == EOF)
864 {
865 if (popin())
866 break;
867 continue;
868 }
869 if (c == state.ec)
870 {
871 escape:
872 switch (n = GETC())
873 {
874 case EOF:
875 if (popin())
876 break;
877 goto escape;
878 case '\n':
879 error_info.line++;
880 continue;
881 case 't':
882 c = '\t';
883 break;
884 case '"':
885 for (;;)
886 {
887 switch (c = GETC())
888 {
889 case EOF:
890 if (popin())
891 break;
892 continue;
893 case '\n':
894 break;
895 default:
896 if (c == state.ec)
897 {
898 escape_2:
899 switch (n = GETC())
900 {
901 case EOF:
902 if (popin())
903 break;
904 goto escape_2;
905 case '\n':
906 error_info.line++;
907 continue;
908 default:
909 continue;
910 }
911 break;
912 }
913 continue;
914 }
915 break;
916 }
917 break;
918 default:
919 UNGETC(n);
920 break;
921 }
922 break;
923 }
924 break;
925 }
926 return c;
927 }
928
929 /*
930 * call a macro
931 */
932
933 static void
macro(Tag_t * tp,Arg_t * ap)934 macro(Tag_t* tp, Arg_t* ap)
935 {
936 if (tp->body)
937 pushin(tp->file, tp->line, NiL, tp->body, ap);
938 }
939
940 /*
941 * set macro value
942 */
943
944 static void
set(Tag_t * mp,char * value,int append)945 set(Tag_t* mp, char* value, int append)
946 {
947 int n;
948
949 if (append && mp->body)
950 {
951 sfputr(state.tmp, mp->body, -1);
952 sfputr(state.tmp, value, -1);
953 value = use(state.tmp);
954 }
955 n = strlen(value) + 1;
956 if (mp->size < n)
957 {
958 if (mp->size)
959 free(mp->body);
960 mp->size = n = roundof(n, 64);
961 if (!(mp->body = newof(0, char, n, 0)))
962 error(ERROR_SYSTEM|3, "out of space [set value]");
963 }
964 strcpy(mp->body, value);
965 mp->call = macro;
966 mp->flags |= TAG_PASS;
967 if (!mp->line)
968 {
969 mp->line = error_info.line;
970 mp->file = error_info.file;
971 }
972 if (mp->flags & TAG_TRACE_SET)
973 error(2, "set macro %s `%s'", mp->name + 1, mp->body);
974 }
975
976 /*
977 * return macro pointer given name
978 * pointer created if not found
979 */
980
981 static Tag_t*
mac(char * name)982 mac(char* name)
983 {
984 register Tag_t* mp;
985 char buf[MAXNAME];
986
987 if (!(mp = (Tag_t*)hashget(state.symbols, nam('.', name, buf, sizeof(buf)))))
988 {
989 if (!(mp = newof(0, Tag_t, 1, 0)))
990 error(ERROR_SYSTEM|3, "out of space [mac]");
991 mp->name = hashput(state.symbols, NiL, mp);
992 }
993 return mp;
994 }
995
996 static Env_t*
env(char * s)997 env(char* s)
998 {
999 register Env_t* v;
1000 char* e;
1001 long n;
1002 char buf[MAXNAME];
1003
1004 n = expression(s, &e, 0);
1005 if (*e)
1006 sfsprintf(buf, sizeof(buf), "E%s", s);
1007 else
1008 sfsprintf(buf, sizeof(buf), "E%d", n);
1009 if (!(v = (Env_t*)hashget(state.symbols, buf)))
1010 {
1011 if (!(v = newof(0, Env_t, 1, 0)))
1012 error(ERROR_SYSTEM|3, "out of space [environment]");
1013 v->name = hashput(state.symbols, NiL, v);
1014 }
1015 if (v->generation < state.generation)
1016 {
1017 v->generation = state.generation;
1018 v->c2 = DEFAULT_c2;
1019 v->cc = DEFAULT_cc;
1020 v->ft.current = v->ft.previous = DEFAULT_ft;
1021 v->nf = 0;
1022 v->ps.current = v->ps.previous = DEFAULT_ps;
1023 v->ss = 0;
1024 v->vs.current = v->vs.previous = DEFAULT_vs;
1025 }
1026 return v;
1027 }
1028
1029 /*
1030 * open name for reading
1031 * looking in state.dirs if needed
1032 * verbose<0 for -m option
1033 */
1034
1035 static Sfio_t*
find(char * name,char ** found,int verbose)1036 find(char* name, char** found, int verbose)
1037 {
1038 register char* s;
1039 register Sfio_t* sp;
1040 char* path;
1041 char* t;
1042 char* x;
1043 int i;
1044 Dir_t* dp;
1045 char buf[PATH_MAX];
1046
1047 static const char* pathdirs[] =
1048 {
1049 "lib/tmac/tmac.%s",
1050 "lib/tmac/%s",
1051 "lib/html/%s",
1052 "lib/html/m%s.tr",
1053 "share/groff/tmac/tmac.%s",
1054 "share/groff/tmac/%s",
1055 "groff/share/groff/tmac/tmac.%s",
1056 "groff/share/groff/tmac/%s",
1057 };
1058
1059 if (verbose >= 0 && (sp = sfopen(NiL, name, "r")))
1060 {
1061 sfprintf(state.tmp, "/%s", name);
1062 path = use(state.tmp);
1063 goto hit;
1064 }
1065 if (*name != '/')
1066 {
1067 if (*name == '.' && (x = getenv("HOME")))
1068 {
1069 sfprintf(state.tmp, "/%s/%s", x, name);
1070 path = use(state.tmp);
1071 if (sp = sfopen(NiL, path + 1, "r"))
1072 goto hit;
1073 }
1074 for (dp = state.dirs; dp; dp = dp->next)
1075 {
1076 if (verbose >= 0)
1077 {
1078 sfprintf(state.tmp, "/%s/%s", dp->name, name);
1079 path = use(state.tmp);
1080 if (sp = sfopen(NiL, path + 1, "r"))
1081 goto hit;
1082 }
1083 sfprintf(state.tmp, "/%s/tmac.%s", dp->name, name);
1084 path = use(state.tmp);
1085 if (sp = sfopen(NiL, path + 1, "r"))
1086 goto hit;
1087 }
1088 for (i = 0; i < elementsof(pathdirs); i++)
1089 {
1090 sfprintf(state.tmp, pathdirs[i], name);
1091 path = use(state.tmp);
1092 if (pathpath(path, "", PATH_REGULAR|PATH_READ, buf + 1, sizeof(buf) - 1) && (sp = sfopen(NiL, buf + 1, "r")))
1093 {
1094 *(path = buf) = '/';
1095 goto hit;
1096 }
1097 }
1098 }
1099 if (verbose > 0)
1100 {
1101 /*
1102 * some systems provide
1103 * /usr/lib/tmac.mXXX
1104 * that references
1105 * /usr/lib/macros/mXXX[tn]
1106 * that is not provided
1107 */
1108
1109 if (*(s = name) == '/')
1110 while (s = strchr(s, '/'))
1111 if (!strncmp(++s, "macros/", 7) && *(s += 7))
1112 {
1113 t = s + strlen(s) - 1;
1114 x = strchr(s, '.') ? "" : ".tr";
1115 while (*s)
1116 {
1117 sfprintf(state.tmp, "lib/html/%s%s", s, x);
1118 path = use(state.tmp);
1119 if (pathpath(path, "", PATH_REGULAR|PATH_READ, buf + 1, sizeof(buf) - 1) && (sp = sfopen(NiL, buf + 1, "r")))
1120 {
1121 *(path = buf) = '/';
1122 goto hit;
1123 }
1124 if (*t != 't')
1125 break;
1126 *t-- = 0;
1127 }
1128 }
1129 error(ERROR_SYSTEM|2, "%s: cannot read", name);
1130 }
1131 else if (verbose < 0)
1132 error(ERROR_SYSTEM|3, "-m%s: cannot find macro package", name);
1133 return 0;
1134 hit:
1135 message((-1, "FIND %s => %s", name, path + 1));
1136 if (found)
1137 *found = hashput(state.symbols, path, path + 1) + 1;
1138 s = path + strlen(path);
1139 while (s > path + 2)
1140 if (*--s == '/')
1141 {
1142 if (!strcmp(s, "/lib"))
1143 {
1144 *s = 0;
1145 set(mac(".P"), path + 1, 0);
1146 break;
1147 }
1148 *s = 0;
1149 }
1150 return sp;
1151 }
1152
1153 /*
1154 * generate intermediate CODE_0
1155 */
1156
1157 static void
code_0(void)1158 code_0(void)
1159 {
1160 sfputc(state.out, CODE_0);
1161 }
1162
1163 /*
1164 * generate intermediate CODE_1
1165 */
1166
1167 static void
code_1(int code)1168 code_1(int code)
1169 {
1170 sfputc(state.out, CODE_1);
1171 sfputc(state.out, code);
1172 }
1173
1174 /*
1175 * generate intermediate CODE_2
1176 */
1177
1178 static void
code_2(int code,int data)1179 code_2(int code, int data)
1180 {
1181 sfputc(state.out, CODE_2);
1182 sfputc(state.out, data);
1183 sfputc(state.out, code);
1184 }
1185
1186 /*
1187 * generate intermediate CODE_n
1188 */
1189
1190 static void
code_n(int code,char * s)1191 code_n(int code, char* s)
1192 {
1193 int n;
1194
1195 sfputc(state.out, CODE_n);
1196 n = s ? (strlen(s) + 1) : 1;
1197 if (n > 0177)
1198 sfputc(state.out, ((n >> 8) & 0177) | 0200);
1199 sfputc(state.out, n & 0177);
1200 sfputc(state.out, code);
1201 if (s)
1202 sfputr(state.out, s, 0);
1203 else
1204 sfputc(state.out, 0);
1205 }
1206
1207 /*
1208 * join all args from n on into 1 arg
1209 */
1210
1211 static char*
join(Arg_t * ap,int n)1212 join(Arg_t* ap, int n)
1213 {
1214 if (n < 0 || ap->argc < n)
1215 return 0;
1216 n++;
1217 while (ap->argc >= n)
1218 *(ap->argv[ap->argc--] - 1) = ' ';
1219 return ap->argv[n - 1];
1220 }
1221
1222 /*
1223 * output error message line
1224 */
1225
1226 static void
notify(register char * s)1227 notify(register char* s)
1228 {
1229 register int c;
1230 Sfoff_t p;
1231
1232 p = sftell(sfstderr);
1233 for (;;)
1234 {
1235 switch (c = *s++)
1236 {
1237 case 0:
1238 break;
1239 case CODE_0:
1240 continue;
1241 case CODE_1:
1242 s++;
1243 continue;
1244 case CODE_2:
1245 c = *s++;
1246 if (*s++ == OP_cc)
1247 sfputc(sfstderr, c);
1248 continue;
1249 default:
1250 sfputc(sfstderr, c);
1251 continue;
1252 }
1253 break;
1254 }
1255 if (sftell(sfstderr) != p)
1256 sfputc(sfstderr, '\n');
1257 }
1258
1259 /*
1260 * add trap s to xp
1261 */
1262
1263 static void
trap(Trap_t ** xp,char * s)1264 trap(Trap_t** xp, char* s)
1265 {
1266 Trap_t* ip;
1267 Trap_t* pp;
1268
1269 for (pp = 0, ip = *xp; ip; pp = ip, ip = ip->next)
1270 if (streq(ip->name + 1, s))
1271 return;
1272 if (!(ip = newof(0, Trap_t, 1, strlen(s) + 1)))
1273 error(ERROR_SYSTEM|3, "out of space [trap]");
1274 *ip->name = '.';
1275 strcpy(ip->name + 1, s);
1276 if (pp)
1277 pp->next = ip;
1278 else
1279 *xp = ip;
1280 }
1281
1282 /*
1283 * trigger traps on xp
1284 */
1285
1286 static void
trigger(Trap_t ** xp)1287 trigger(Trap_t** xp)
1288 {
1289 Trap_t* ip;
1290 Trap_t* pp;
1291
1292 if (!DIVERTED() && (ip = *xp))
1293 {
1294 state.t = 1;
1295 do
1296 {
1297 sfputr(state.req, ip->name, '\n');
1298 pp = ip;
1299 ip = ip->next;
1300 free(pp);
1301 } while (ip);
1302 *xp = 0;
1303 pushin(xp == &state.fini ? "[*EOF*]" : "[*TRAP*]", 1, NiL, use(state.req), NiL);
1304 }
1305 }
1306
1307 /*
1308 * return value for register name with type
1309 * and increment/decrement i={0,'+','-'}
1310 */
1311
1312 static char*
value(char * name,int type,int i)1313 value(char* name, int type, int i)
1314 {
1315 register Num_t* np;
1316 register char* b;
1317 register char* x;
1318 register long n;
1319 register long m;
1320 Tag_t* tp;
1321 char buf[8];
1322
1323 b = hashget(state.symbols, name);
1324 switch (type)
1325 {
1326 case 'g':
1327 if (!(np = (Num_t*)b))
1328 return "";
1329 b = np->value;
1330 if (isalnum(np->format))
1331 *b++ = np->format;
1332 else
1333 {
1334 for (n = 0; n < np->format; n++)
1335 *b++ = '0';
1336 *b++ = '1';
1337 }
1338 *b = 0;
1339 return np->value;
1340 case 'j':
1341 return "";
1342 case 'n':
1343 break;
1344 case '.':
1345 if (!(tp = (Tag_t*)b))
1346 return 0;
1347 if (tp->flags & TAG_TRACE_GET)
1348 error(2, "get macro %s `%s'", tp->name + 1, tp->body);
1349 return tp->body;
1350 default:
1351 return b;
1352 }
1353 if (!b)
1354 {
1355 name = strcpy(buf, "n");
1356 if (!(b = (char*)hashget(state.symbols, name)))
1357 return "0";
1358 }
1359 np = (Num_t*)b;
1360 if (np->flags & TAG_TRACE_GET)
1361 error(2, "get register %s %d %d", np->name + 1, np->number, np->increment);
1362 if (np->internal)
1363 {
1364 switch (INDEX(name[1], name[2]))
1365 {
1366 case INDEX('%',0):
1367 n = np->number;
1368 break;
1369 case INDEX('.','$'):
1370 n = state.mac ? state.mac->argc : 0;
1371 break;
1372 case INDEX('.','C'):
1373 GROFFINIT();
1374 n = state.groff == 0;
1375 break;
1376 case INDEX('.','F'):
1377 return state.original.file ? state.original.file : error_info.file;
1378 case INDEX('.','H'):
1379 n = 3000;
1380 break;
1381 case INDEX('.','L'):
1382 n = 1;
1383 break;
1384 case INDEX('.','P'):
1385 n = 1;
1386 break;
1387 case INDEX('.','R'):
1388 n = 256;
1389 break;
1390 case INDEX('.','V'):
1391 n = 6000;
1392 break;
1393 case INDEX('.','c'):
1394 if (name[3] == 'e')
1395 {
1396 n = state.it.center;
1397 break;
1398 }
1399 /*FALLTHROUGH*/
1400 case INDEX('c','.'):
1401 n = state.original.line ? state.original.line : error_info.line;
1402 break;
1403 case INDEX('.','e'):
1404 if (name[3] == 'v')
1405 n = state.it.center; /*GROFF: string valued?*/
1406 break;
1407 case INDEX('.','f'):
1408 n = state.env->ft.current;
1409 break;
1410 case INDEX('.','g'):
1411 GROFFINIT();
1412 n = state.groff != 0;
1413 break;
1414 case INDEX('.','i'):
1415 n = state.env->in.current;
1416 break;
1417 case INDEX('.','k'):
1418 n = 100;
1419 break;
1420 case INDEX('.','l'):
1421 n = state.env->ll.current;
1422 break;
1423 case INDEX('.','n'):
1424 n = state.n;
1425 break;
1426 case INDEX('.','p'):
1427 if (name[3] == 'n')
1428 n = 2;
1429 else if (name[3] == 's' && name[4] == 'r')
1430 n = state.env->ps.previous;
1431 else
1432 n = state.env->ps.current;
1433 break;
1434 case INDEX('.','r'):
1435 n = 0;
1436 break;
1437 case INDEX('.','s'):
1438 if (name[3] == 'r')
1439 n = state.env->ps.previous;
1440 else
1441 n = state.env->ps.current;
1442 break;
1443 case INDEX('.','t'):
1444 n = state.t;
1445 break;
1446 case INDEX('.','u'):
1447 n = !state.env->nf;
1448 break;
1449 case INDEX('.','v'):
1450 if (name[3] == 'p')
1451 n = 1;
1452 else
1453 n = 6 * state.env->vs.current;
1454 break;
1455 case INDEX('.','w'):
1456 if (name[3] == 'a')
1457 n = 0;
1458 else
1459 n = 6 * state.env->ps.current;
1460 break;
1461 case INDEX('.','x'):
1462 n = 19980401;
1463 break;
1464 case INDEX('.','y'):
1465 n = 1;
1466 break;
1467 case INDEX('.','z'):
1468 return state.divert ? state.divert->tag->name : "";
1469 case INDEX('d','l'):
1470 if (state.test & 0x1000) n = 0; else
1471 n = state.dl;
1472 break;
1473 case INDEX('d','n'):
1474 if (state.test & 0x2000) n = 0; else
1475 n = state.dn;
1476 break;
1477 case INDEX('l','n'):
1478 if (state.test & 0x4000) n = 0; else
1479 n = state.ln;
1480 break;
1481 case INDEX('n','l'):
1482 if (!(state.test & 0x8000)) n = 0; else
1483 n = state.nl;
1484 break;
1485 default:
1486 n = 0;
1487 break;
1488 }
1489 np->number = n;
1490 }
1491 if (i)
1492 {
1493 switch (i)
1494 {
1495 case '+':
1496 np->number += np->increment;
1497 break;
1498 case '-':
1499 np->number -= np->increment;
1500 break;
1501 }
1502 if (np->flags & TAG_TRACE_SET)
1503 error(2, "set register %s %d %d", np->name + 1, np->number, np->increment);
1504 }
1505 n = np->number;
1506 b = np->value;
1507 switch (np->format)
1508 {
1509 case '1':
1510 sfsprintf(b, sizeof(np->value), "%ld", np->number);
1511 break;
1512 case 'A':
1513 case 'a':
1514 x = islower(np->format) ? "abcdefghijklmnopqrstuvwxyz" : "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1515 if (n < 0)
1516 {
1517 n = -n;
1518 *b++ = '-';
1519 }
1520 for (m = 1; m > 0 && m < n; m *= 26);
1521 if (m <= 0)
1522 sfsprintf(b, sizeof(buf) - 1, "<%ld>", n);
1523 else
1524 {
1525 for (; m > 0; m /= 26)
1526 {
1527 i = n / m;
1528 n -= m * i;
1529 *b++ = x[i];
1530 }
1531 *b = 0;
1532 }
1533 break;
1534 case 'I':
1535 case 'i':
1536 x = islower(np->format) ? "zwmdclxvi" : "ZWMDCLXVI";
1537 if (n <= -40000 || n >= 40000)
1538 sfsprintf(b, sizeof(buf), "<%ld>", n);
1539 else if (!n)
1540 sfsprintf(b, sizeof(buf), "0");
1541 else
1542 {
1543 if (n < 0)
1544 {
1545 n = -n;
1546 *b++ = '-';
1547 }
1548 while (n >= 10000)
1549 {
1550 n -= 10000;
1551 *b++ = x[0];
1552 }
1553 for (i = 1000; i > 0; i /= 10, x += 2)
1554 {
1555 m = n / i;
1556 n -= m * i;
1557 switch (m)
1558 {
1559 case 9:
1560 *b++ = x[2];
1561 *b++ = x[0];
1562 break;
1563 case 8:
1564 *b++ = x[1];
1565 *b++ = x[2];
1566 *b++ = x[2];
1567 *b++ = x[2];
1568 break;
1569 case 7:
1570 *b++ = x[1];
1571 *b++ = x[2];
1572 *b++ = x[2];
1573 break;
1574 case 6:
1575 *b++ = x[1];
1576 *b++ = x[2];
1577 break;
1578 case 5:
1579 *b++ = x[1];
1580 break;
1581 case 4:
1582 *b++ = x[2];
1583 *b++ = x[1];
1584 break;
1585 case 3:
1586 *b++ = x[2];
1587 /*FALLTHROUGH*/
1588 case 2:
1589 *b++ = x[2];
1590 /*FALLTHROUGH*/
1591 case 1:
1592 *b++ = x[2];
1593 break;
1594 }
1595 }
1596 *b = 0;
1597 }
1598 break;
1599 default:
1600 sfsprintf(b, sizeof(np->value), "%0*ld", np->format, np->number);
1601 break;
1602 }
1603 return np->value;
1604 }
1605
1606 /*
1607 * return the value for typed register
1608 */
1609
1610 static char*
interpolate(int type)1611 interpolate(int type)
1612 {
1613 register int c;
1614 register char* b;
1615 register char* x;
1616 register char* t;
1617 int i;
1618 int k;
1619 char buf[MAXNAME];
1620
1621 i = k = 0;
1622 b = buf;
1623 *b++ = (type == 'g' || type == 'j') ? 'f' : type;
1624 x = buf + 2;
1625 do
1626 {
1627 switch (c = GETC())
1628 {
1629 case EOF:
1630 if (popin())
1631 return 0;
1632 continue;
1633 case '\n':
1634 error_info.line++;
1635 break;
1636 case '(':
1637 if (x == buf + 2)
1638 {
1639 x++;
1640 continue;
1641 }
1642 break;
1643 case '[':
1644 if (state.groff && x == buf + 2)
1645 {
1646 x = buf + sizeof(buf) - 2;
1647 k = 1;
1648 continue;
1649 }
1650 break;
1651 case ']':
1652 if (k)
1653 goto done;
1654 break;
1655 case '-':
1656 case '+':
1657 if (type == 'n' && b == buf + 1)
1658 {
1659 i = c;
1660 continue;
1661 }
1662 break;
1663 default:
1664 if (c == state.ec)
1665 {
1666 escape:
1667 switch (c = GETC())
1668 {
1669 case EOF:
1670 if (popin())
1671 return 0;
1672 goto escape;
1673 case '\n':
1674 error_info.line++;
1675 continue;
1676 case '*':
1677 c = '.';
1678 /*FALLTHROUGH*/
1679 case 'n':
1680 if (t = interpolate(c))
1681 while (b < x && (*b = *t++))
1682 b++;
1683 continue;
1684 }
1685 }
1686 break;
1687 }
1688 *b++ = c;
1689 } while (b < x);
1690 done:
1691 *b = 0;
1692 return value(buf, type, i);
1693 }
1694
1695 /*
1696 * switch to font s
1697 */
1698
1699 static void
ft(char * s)1700 ft(char* s)
1701 {
1702 int c;
1703
1704 if (!s || (c = *s) < '0' || c > '5')
1705 c = 0;
1706 else
1707 c -= '0';
1708 if (!c)
1709 c = state.env->ft.previous;
1710 state.env->ft.previous = state.env->ft.current;
1711 state.env->ft.current = c;
1712 code_1(OP_ft + c);
1713 }
1714
1715 /*
1716 * switch to point size n
1717 */
1718
1719 static void
ps(int n)1720 ps(int n)
1721 {
1722 state.env->ps.previous = state.env->ps.current;
1723 state.env->ps.current = n;
1724 code_2(OP_ps, n);
1725 }
1726
1727 /*
1728 * sync list state
1729 */
1730
1731 static void
li(int force)1732 li(int force)
1733 {
1734 List_t* list = state.list;
1735
1736 state.it.dc = 0;
1737 if (state.divert || state.env->nf)
1738 {
1739 if (state.env->nf)
1740 state.it.dl = 0;
1741 return;
1742 }
1743 if (force)
1744 {
1745 while (state.env->in.current < state.list->in)
1746 {
1747 message((-2, "DROP %d %d %d:%d %d:%d", state.list - state.list_stack, state.list->dl, state.list->ti, state.list->in, force ? state.env->ti.current : 0, state.env->in.current));
1748 if (state.list->dl)
1749 code_1(END(OP_dl));
1750 state.list--;
1751 }
1752 }
1753 else
1754 message((-2, "TEST %d %d %d:%d %d:%d ops=%d df=%d dc=%d dl=%d", state.list - state.list_stack, state.list->dl, state.list->ti, state.list->in, force ? state.env->ti.current : 0, state.env->in.current, state.list - list, force, state.it.dc, state.it.dl));
1755 if (state.env->in.current > state.list->in || force > 0 && state.env->ti.current > state.list->in)
1756 {
1757 if (state.list >= &state.list_stack[elementsof(state.list_stack)-1])
1758 error(2, "list stack overflow");
1759 else
1760 state.list++;
1761 state.list->dl = 0;
1762 state.list->in = state.env->in.current;
1763 state.list->ti = state.env->in.current > state.list->in ? state.env->in.current : state.env->ti.current;
1764 state.it.dl++;
1765 }
1766 else if (!force && state.env->in.current < state.list->in)
1767 state.it.dc = 1;
1768 message((-2, "LIST %d %d %d:%d %d:%d ops=%d df=%d dc=%d dl=%d", state.list - state.list_stack, state.list->dl, state.list->ti, state.list->in, force ? state.env->ti.current : 0, state.env->in.current, state.list - list, force, state.it.dc, state.it.dl));
1769 }
1770
1771 /*
1772 * pop inline traps
1773 */
1774
1775 static void
it(void)1776 it(void)
1777 {
1778 register List_t* p;
1779
1780 if (state.env->nf)
1781 {
1782 if (state.env->in.current > 0)
1783 sfputc(state.out, '\t');
1784 }
1785 else if (!DIVERTED())
1786 {
1787 if (state.it.dc && ISFILE())
1788 li(-1);
1789 p = state.list;
1790 while (state.it.dl > 0)
1791 {
1792 state.it.dl--;
1793 for (; p >= state.list_stack; p--)
1794 if (!p->dl)
1795 {
1796 p->dl = 1;
1797 code_1(OP_dl);
1798 break;
1799 }
1800 }
1801 if (state.it.dt)
1802 {
1803 state.it.dt = 0;
1804 code_1((state.list - state.list_stack) <= 1 ? LABEL(OP_dt) : OP_dt);
1805 state.it.dd = 1;
1806 }
1807 else if (state.it.dd)
1808 {
1809 state.it.dd = 0;
1810 code_1(OP_dd);
1811 }
1812 }
1813 }
1814
1815 /*
1816 * initialize number register
1817 */
1818
1819 static void
nr(char * s,int v,int format,int internal)1820 nr(char* s, int v, int format, int internal)
1821 {
1822 register Num_t* np;
1823
1824 np = num(s);
1825 np->number = v;
1826 np->format = format;
1827 if (internal > 0)
1828 np->internal = internal;
1829 }
1830
1831 /*
1832 * set time vars to t
1833 */
1834
1835 static void
tm(time_t t)1836 tm(time_t t)
1837 {
1838 Tm_t* xp;
1839
1840 state.date = t;
1841 xp = tmmake(&t);
1842 nr("dw", xp->tm_wday + 1, 0, 0);
1843 nr("dy", xp->tm_mday, 0, 0);
1844 nr("mo", xp->tm_mon + 1, 0, 0);
1845 nr("yr", xp->tm_year % 100, 2, 0);
1846 nr("YR", 1900 + xp->tm_year, 4, 0);
1847 }
1848
1849 /*
1850 * expand if expression s into op
1851 */
1852
1853 static void
expand(register Sfio_t * op,register char * s)1854 expand(register Sfio_t* op, register char* s)
1855 {
1856 int c;
1857 int d;
1858 int q;
1859 int v;
1860 int w;
1861 int z;
1862 char* t;
1863 char* b;
1864 char buf[MAXNAME];
1865
1866 for (;;)
1867 {
1868 switch (c = *s++)
1869 {
1870 case 0:
1871 break;
1872 case CODE_2:
1873 sfputc(op, c);
1874 c = *s++;
1875 /*FALLTHROUGH*/
1876 case CODE_1:
1877 sfputc(op, c);
1878 c = *s++;
1879 /*FALLTHROUGH*/
1880 case CODE_0:
1881 sfputc(op, c);
1882 continue;
1883 default:
1884 if (c == state.ec)
1885 switch (w = c = *s++)
1886 {
1887 case 0:
1888 s--;
1889 break;
1890 case 'A':
1891 case 'C':
1892 case 'L':
1893 case 'N':
1894 case 'R':
1895 case 'V':
1896 case 'X':
1897 case 'Y':
1898 case 'Z':
1899 case 'b':
1900 case 'h':
1901 case 'l':
1902 case 'o':
1903 case 'w':
1904 case 'x':
1905 v = 0;
1906 c = *s++;
1907 if (c == '(')
1908 z = 2;
1909 else
1910 {
1911 z = -1;
1912 if (state.groff && c == '[')
1913 c = ']';
1914 }
1915 b = s;
1916 for (;;)
1917 {
1918 switch (d = *s++)
1919 {
1920 case 0:
1921 s--;
1922 break;
1923 case CODE_2:
1924 s++;
1925 /*FALLTHROUGH*/
1926 case CODE_1:
1927 if (*s++ == OP_cc)
1928 v++;
1929 /*FALLTHROUGH*/
1930 case CODE_0:
1931 continue;
1932 default:
1933 if (d == state.ec)
1934 {
1935 switch (d = *s++)
1936 {
1937 case 0:
1938 s--;
1939 break;
1940 case 'A':
1941 case 'C':
1942 case 'L':
1943 case 'N':
1944 case 'R':
1945 case 'V':
1946 case 'X':
1947 case 'Y':
1948 case 'Z':
1949 case 'b':
1950 case 'h':
1951 case 'l':
1952 case 'o':
1953 case 'w':
1954 case 'x':
1955 q = *s++;
1956 for (;;)
1957 {
1958 switch (d = *s++)
1959 {
1960 case 0:
1961 s--;
1962 break;
1963 case CODE_2:
1964 s++;
1965 /*FALLTHROUGH*/
1966 case CODE_1:
1967 s++;
1968 /*FALLTHROUGH*/
1969 case CODE_0:
1970 continue;
1971 default:
1972 if (d == q)
1973 break;
1974 continue;
1975 }
1976 break;
1977 }
1978 v++;
1979 break;
1980 case 'f':
1981 case 's':
1982 if (*s)
1983 s++;
1984 break;
1985 case 'g':
1986 case 'j':
1987 t = buf;
1988 *t++ = 'n';
1989 if ((*t++ = *s++) == '(')
1990 {
1991 *(t - 1) = *s++;
1992 *t++ = *s++;
1993 }
1994 *t = 0;
1995 if (t = value(buf, d, 0))
1996 v += strlen(t);
1997 break;
1998 }
1999 }
2000 else if (z > 0)
2001 z--;
2002 else if (d == c || z == 0)
2003 break;
2004 v++;
2005 continue;
2006 }
2007 break;
2008 }
2009 switch (w)
2010 {
2011 case 'A':
2012 sfprintf(op, "1");
2013 break;
2014 case 'R':
2015 case 'X':
2016 case 'V':
2017 c = *(s - 1);
2018 *(s - 1) = 0;
2019 switch (w)
2020 {
2021 case 'R':
2022 /*HERE*/
2023 break;
2024 case 'V':
2025 if (t = getenv(b))
2026 sfprintf(op, "%s", t);
2027 break;
2028 case 'X':
2029 /*HERE*/
2030 break;
2031 }
2032 *(s - 1) = c;
2033 break;
2034 case 'w':
2035 sfprintf(op, "%d", convert(v, 1, w));
2036 break;
2037 }
2038 break;
2039 case 'g':
2040 case 'j':
2041 t = buf;
2042 *t++ = 'n';
2043 if ((*t++ = *s++) == '(')
2044 {
2045 *(t - 1) = *s++;
2046 *t++ = *s++;
2047 }
2048 *t = 0;
2049 if (t = value(buf, c, 0))
2050 sfputr(op, t, -1);
2051 break;
2052 default:
2053 sfputc(op, state.ec);
2054 sfputc(op, c);
2055 break;
2056 }
2057 else
2058 sfputc(op, c);
2059 continue;
2060 }
2061 break;
2062 }
2063 }
2064
2065 /*
2066 * remove macro/register s
2067 */
2068
2069 static void
rm(char * s)2070 rm(char* s)
2071 {
2072 register Tag_t* mp;
2073 char buf[MAXNAME];
2074
2075 if (mp = (Tag_t*)hashget(state.symbols, nam('.', s, buf, sizeof(buf))))
2076 {
2077 if (mp->flags & TAG_TRACE_SET)
2078 error(2, "remove macro %s", mp->name + 1);
2079 if (mp->size)
2080 free(mp->body);
2081 if (!(mp->flags & TAG_STATIC))
2082 free(mp);
2083 hashput(state.symbols, NiL, NiL);
2084 }
2085 if (mp = (Tag_t*)hashget(state.symbols, nam('n', s, buf, sizeof(buf))))
2086 {
2087 if (mp->flags & TAG_TRACE_SET)
2088 error(2, "remove register %s", mp->name + 1);
2089 if (mp->size)
2090 free(mp->body);
2091 if (!(mp->flags & TAG_STATIC))
2092 free(mp);
2093 hashput(state.symbols, NiL, NiL);
2094 }
2095 }
2096
2097 /*
2098 * skip one conditional block
2099 * if op!=0 then copy block data to s
2100 */
2101
2102 static void
skip(char * s,int f,register Sfio_t * op)2103 skip(char* s, int f, register Sfio_t* op)
2104 {
2105 register int c;
2106 register int n;
2107
2108 n = (f & COND_BLOCK) != 0;
2109 if (s)
2110 pushin(NiL, 0, NiL, s, NiL);
2111 else if (!n)
2112 return;
2113 for (;;)
2114 {
2115 switch (c = GETC())
2116 {
2117 case EOF:
2118 if (s && n <= 0)
2119 break;
2120 if (popin())
2121 break;
2122 continue;
2123 case CODE_2:
2124 if (op)
2125 sfputc(op, c);
2126 c = GETC();
2127 /*FALLTHROUGH*/
2128 case CODE_1:
2129 if (op)
2130 sfputc(op, c);
2131 c = GETC();
2132 /*FALLTHROUGH*/
2133 case CODE_0:
2134 if (op)
2135 sfputc(op, c);
2136 continue;
2137 case '\n':
2138 if (op)
2139 sfputc(op, c);
2140 error_info.line++;
2141 if (n <= 0)
2142 break;
2143 continue;
2144 default:
2145 if (op)
2146 sfputc(op, c);
2147 if (c == state.ec)
2148 {
2149 escape:
2150 switch (c = GETC())
2151 {
2152 case EOF:
2153 if (popin())
2154 break;
2155 goto escape;
2156 case '\n':
2157 if (op)
2158 sfputc(op, c);
2159 error_info.line++;
2160 continue;
2161 case '{':
2162 if (op)
2163 sfputc(op, c);
2164 n++;
2165 continue;
2166 case '}':
2167 if (op)
2168 sfputc(op, c);
2169 if (--n <= 0)
2170 {
2171 switch (c = nextchar())
2172 {
2173 case '\n':
2174 if (op)
2175 sfputc(op, c);
2176 error_info.line++;
2177 break;
2178 default:
2179 UNGETC(c);
2180 break;
2181 }
2182 break;
2183 }
2184 continue;
2185 default:
2186 if (op)
2187 sfputc(op, c);
2188 continue;
2189 }
2190 break;
2191 }
2192 continue;
2193 }
2194 break;
2195 }
2196 if (n > 0 && state.verbose)
2197 error(2, "unbalanced \\{ ... \\} block");
2198 }
2199
2200 /*
2201 * internal groff requests
2202 */
2203
2204 static void
groff_aln(Tag_t * tp,Arg_t * ap)2205 groff_aln(Tag_t* tp, Arg_t* ap)
2206 {
2207 Tag_t* xp;
2208 char buf[MAXNAME];
2209
2210 if (ap->argc != 2)
2211 error(1, "%s: two arguments expected", ap->argv[0]);
2212 else if (!(xp = (Tag_t*)hashget(state.symbols, nam('n', ap->argv[2], buf, sizeof(buf)))))
2213 error(1, "%s: not defined", buf);
2214 else
2215 hashput(state.symbols, nam('n', ap->argv[1], buf, sizeof(buf)), xp);
2216 }
2217
2218 static void
groff_als(Tag_t * tp,Arg_t * ap)2219 groff_als(Tag_t* tp, Arg_t* ap)
2220 {
2221 Tag_t* xp;
2222 char buf[MAXNAME];
2223
2224 if (ap->argc != 2)
2225 error(1, "%s: two arguments expected", ap->argv[0]);
2226 else if (!(xp = (Tag_t*)hashget(state.symbols, nam('.', ap->argv[2], buf, sizeof(buf)))))
2227 error(1, "%s: not defined", buf);
2228 else
2229 hashput(state.symbols, nam('.', ap->argv[1], buf, sizeof(buf)), xp);
2230 }
2231
2232 static void
groff_asciify(Tag_t * tp,Arg_t * ap)2233 groff_asciify(Tag_t* tp, Arg_t* ap)
2234 {
2235 static int warned;
2236
2237 if (!warned++)
2238 error(1, "%s: not implemented yet", tp->name);
2239 }
2240
2241 static void
groff_break(Tag_t * tp,Arg_t * ap)2242 groff_break(Tag_t* tp, Arg_t* ap)
2243 {
2244 register Pushin_t* pp;
2245
2246 for (pp = state.in_top; pp >= state.in_stack; pp--)
2247 if (pp->loop)
2248 {
2249 sfclose(pp->loop);
2250 pp->loop = 0;
2251 return;
2252 }
2253 error(1, "%s: outside of loop", tp->name);
2254 }
2255
2256 static void
groff_chop(Tag_t * tp,Arg_t * ap)2257 groff_chop(Tag_t* tp, Arg_t* ap)
2258 {
2259 register char* s;
2260 register Tag_t* mp;
2261
2262 if (ap->argc >= 1)
2263 {
2264 mp = mac(ap->argv[1]);
2265 if ((s = mp->body) && *s)
2266 *(s + strlen(s) - 1) = 0;
2267 }
2268 }
2269
2270 static void
groff_close(Tag_t * tp,Arg_t * ap)2271 groff_close(Tag_t* tp, Arg_t* ap)
2272 {
2273 Stream_t* sp;
2274
2275 if (ap->argc < 1)
2276 {
2277 error(1, "%s: one argument expected", ap->argv[0]);
2278 return;
2279 }
2280 if (!(sp = iop(ap->argv[1], 0)))
2281 {
2282 error(1, "%s: %s: stream not open", ap->argv[0], ap->argv[1]);
2283 return;
2284 }
2285 sfclose(sp->sp);
2286 sp->sp = 0;
2287 }
2288
2289 static void
groff_continue(Tag_t * tp,Arg_t * ap)2290 groff_continue(Tag_t* tp, Arg_t* ap)
2291 {
2292 register Pushin_t* pp;
2293
2294 for (pp = state.in_top; pp >= state.in_stack; pp--)
2295 if (pp->loop)
2296 {
2297 pp->in = (unsigned char*)sfstrbase(pp->loop);
2298 return;
2299 }
2300 error(1, "%s: outside of loop", tp->name);
2301 }
2302
2303 static void
groff_cp(Tag_t * tp,Arg_t * ap)2304 groff_cp(Tag_t* tp, Arg_t* ap)
2305 {
2306 NoP(tp);
2307 if (ap->argc < 1 || expression(ap->argv[1], NiL, 0))
2308 state.groff &= 2;
2309 else
2310 state.groff |= 1;
2311 }
2312
2313 static void
groff_open(Tag_t * tp,Arg_t * ap)2314 groff_open(Tag_t* tp, Arg_t* ap)
2315 {
2316 char* path;
2317 char* mode;
2318 Stream_t* sp;
2319
2320 if (ap->argc < 2)
2321 {
2322 error(1, "%s: two arguments expected", ap->argv[0]);
2323 return;
2324 }
2325 mode = strlen(ap->argv[0]) > 5 ? (ap->argv[0] + 5) : "w";
2326 path = ap->argv[2];
2327 sp = iop(ap->argv[1], 1);
2328 if (*mode == 'w' || !streq(sp->path, path))
2329 {
2330 if (sp->sp)
2331 {
2332 sfclose(sp->sp);
2333 sp->sp = 0;
2334 }
2335 if (sp->path)
2336 free(sp->path);
2337 if (!(sp->path = strdup(path)))
2338 error(ERROR_SYSTEM|3, "out of space [stream path]");
2339 }
2340 if (!sp->sp && !(sp->sp = sfopen(NiL, path, mode)))
2341 error(ERROR_SYSTEM|2, "%s: %s: \"%s\" mode open error", ap->argv[0], path, mode);
2342 }
2343
2344 static void
groff_pso(Tag_t * tp,Arg_t * ap)2345 groff_pso(Tag_t* tp, Arg_t* ap)
2346 {
2347 char* s;
2348 Sfio_t* sp;
2349
2350 if (s = join(ap, 1))
2351 {
2352 if (!(sp = sfpopen(NiL, s, "r")))
2353 error(ERROR_SYSTEM|2, "%s: %s: command error", ap->argv[0], s);
2354 else
2355 pushin(NiL, 0, sp, NiL, NiL);
2356 }
2357 }
2358
2359 static void
groff_rnn(Tag_t * tp,Arg_t * ap)2360 groff_rnn(Tag_t* tp, Arg_t* ap)
2361 {
2362 Num_t* mp;
2363 char* s;
2364
2365 NoP(tp);
2366 if (ap->argc >= 2)
2367 {
2368 s = ap->argv[2];
2369 rm(s);
2370 *--s = 'n';
2371 mp = num(ap->argv[1]);
2372 hashput(state.symbols, NiL, NiL);
2373 mp->name = hashput(state.symbols, s, mp);
2374 }
2375 }
2376
2377 static void
groff_shift(Tag_t * tp,Arg_t * ap)2378 groff_shift(Tag_t* tp, Arg_t* ap)
2379 {
2380 register int n;
2381 register int i;
2382 register Arg_t* pp;
2383
2384 if (pp = state.mac)
2385 {
2386 n = (ap->argc >= 1) ? expression(ap->argv[1], NiL, 0) : 1;
2387 i = 0;
2388 while (n < pp->argc)
2389 pp->argv[++i] = pp->argv[++n];
2390 pp->argc = i;
2391 }
2392 }
2393
2394 static void
groff_sy(Tag_t * tp,Arg_t * ap)2395 groff_sy(Tag_t* tp, Arg_t* ap)
2396 {
2397 char* s;
2398
2399 nr("systat", (long)((s = join(ap, 1)) ? system(s) : 0), 0, 0);
2400 }
2401
2402 static void
groff_while(Tag_t * tp,Arg_t * ap)2403 groff_while(Tag_t* tp, Arg_t* ap)
2404 {
2405 register char* s;
2406 register char* t;
2407 Sfio_t* op;
2408
2409 if (s = join(ap, 1))
2410 {
2411 if (!(op = sfstropen()))
2412 error(ERROR_SYSTEM|3, "out of space [%s]", tp->name);
2413 sfputr(op, ".ie", ' ');
2414 sfputr(op, s, '\n');
2415 t = s + strlen(s);
2416 while (t > s && isspace(*--t));
2417 if (t > s && *t == '{' && *(t - 1) == state.ec)
2418 skip(NiL, COND_BLOCK, op);
2419 sfputr(op, "\n.el .break\n", 0);
2420 pushin(NiL, 0, NiL, sfstrbase(op), NiL);
2421 state.in_top->loop = op;
2422 }
2423 }
2424
2425 static void
groff_write(Tag_t * tp,Arg_t * ap)2426 groff_write(Tag_t* tp, Arg_t* ap)
2427 {
2428 char* s;
2429 Stream_t* sp;
2430
2431 if (ap->argc < 1)
2432 {
2433 error(1, "%s: at least one argument expected", ap->argv[0]);
2434 return;
2435 }
2436 if (!(sp = iop(ap->argv[1], 0)))
2437 {
2438 error(1, "%s: %s: stream not open", ap->argv[0], ap->argv[1]);
2439 return;
2440 }
2441 if (!(s = join(ap, 2)))
2442 s = "";
2443 if (*s == '"')
2444 s++;
2445 if (sfputr(sp->sp, s, '\n') < 0)
2446 error(ERROR_SYSTEM|2, "%s: %s: write error", ap->argv[0], ap->argv[1]);
2447 }
2448
2449 /*
2450 * internal troff requests
2451 */
2452
2453 static void
troff_ab(Tag_t * tp,Arg_t * ap)2454 troff_ab(Tag_t* tp, Arg_t* ap)
2455 {
2456 char* s;
2457
2458 NoP(tp);
2459 if (!(s = join(ap, 1)))
2460 s = "User abort";
2461 notify(s);
2462 exit(1);
2463 }
2464
2465 static void
troff_ad(Tag_t * tp,Arg_t * ap)2466 troff_ad(Tag_t* tp, Arg_t* ap)
2467 {
2468 NoP(tp);
2469 if (ap->argc >= 1)
2470 switch (ap->argv[1][0])
2471 {
2472 case 'b':
2473 case 'B':
2474 case 'n':
2475 case 'N':
2476 break;
2477 case 'c':
2478 case 'C':
2479 break;
2480 case 'l':
2481 case 'L':
2482 break;
2483 case 'r':
2484 case 'R':
2485 break;
2486 }
2487 }
2488
2489 static void
troff_af(Tag_t * tp,Arg_t * ap)2490 troff_af(Tag_t* tp, Arg_t* ap)
2491 {
2492 Num_t* np;
2493 char* s;
2494
2495 NoP(tp);
2496 if (ap->argc >= 2)
2497 {
2498 np = num(ap->argv[1]);
2499 switch (*(s = ap->argv[2]))
2500 {
2501 case '0':
2502 for (np->format = 0; *s++ == '0'; np->format++);
2503 break;
2504 case 'a':
2505 case 'A':
2506 case 'i':
2507 case 'I':
2508 np->format = *s;
2509 break;
2510 case '1':
2511 default:
2512 np->format = '1';
2513 break;
2514 }
2515 }
2516 }
2517
2518 static void
troff_br(Tag_t * tp,Arg_t * ap)2519 troff_br(Tag_t* tp, Arg_t* ap)
2520 {
2521 NoP(tp);
2522 NoP(ap);
2523 if (state.it.dd)
2524 {
2525 state.it.dd = 0;
2526 code_1(OP_dd);
2527 }
2528 code_1(OP_br);
2529 }
2530
2531 static void
troff_c2(Tag_t * tp,Arg_t * ap)2532 troff_c2(Tag_t* tp, Arg_t* ap)
2533 {
2534 NoP(tp);
2535 state.env->c2 = (ap->argc >= 1) ? ap->argv[1][0] : DEFAULT_c2;
2536 }
2537
2538 static void
troff_cc(Tag_t * tp,Arg_t * ap)2539 troff_cc(Tag_t* tp, Arg_t* ap)
2540 {
2541 NoP(tp);
2542 state.env->cc = (ap->argc >= 1) ? ap->argv[1][0] : DEFAULT_cc;
2543 }
2544
2545 static void
troff_ce(Tag_t * tp,Arg_t * ap)2546 troff_ce(Tag_t* tp, Arg_t* ap)
2547 {
2548 int n;
2549 int r;
2550
2551 NoP(tp);
2552 n = (ap->argc >= 1) ? expression(ap->argv[1], NiL, 0) : 1;
2553 r = *(ap->argv[0] + 1) == 'r';
2554 if (n >= 1)
2555 {
2556 if (state.it.center && state.it.right != r)
2557 {
2558 state.it.center = 0;
2559 code_1(state.it.right ? END(OP_p) : END(OP_center));
2560 }
2561 if (!state.it.center)
2562 {
2563 if (r)
2564 code_2(OP_p, ARG_right);
2565 else
2566 code_1(OP_center);
2567 }
2568 }
2569 else
2570 {
2571 if (state.it.right)
2572 code_1(END(OP_p));
2573 else if (state.it.center)
2574 code_1(END(OP_center));
2575 n = 0;
2576 r = 0;
2577 }
2578 state.it.center = n;
2579 state.it.right = r;
2580 }
2581
2582 static void
troff_cf(Tag_t * tp,Arg_t * ap)2583 troff_cf(Tag_t* tp, Arg_t* ap)
2584 {
2585 Sfio_t* sp;
2586
2587 if (ap->argc >= 1 && (sp = find(ap->argv[1], NiL, 1)))
2588 {
2589 sfmove(sp, state.out, SF_UNBOUND, -1);
2590 sfclose(sp);
2591 }
2592 }
2593
2594 static void troff_wh(Tag_t*, Arg_t*);
2595
2596 static void
troff_ch(Tag_t * tp,Arg_t * ap)2597 troff_ch(Tag_t* tp, Arg_t* ap)
2598 {
2599 char* s;
2600 Trap_t* xp;
2601
2602 NoP(tp);
2603 NoP(ap);
2604 if (ap->argc == 1)
2605 {
2606 for (xp = state.trap; xp; xp = xp->next)
2607 if (streq(xp->name + 1, ap->argv[1]))
2608 xp->name[0] = 0;
2609 }
2610 else if (ap->argc >= 2)
2611 {
2612 s = ap->argv[1];
2613 ap->argv[1] = (state.test & 1) ? "1" : ap->argv[2];
2614 ap->argv[2] = s;
2615 troff_wh(tp, ap);
2616 }
2617 }
2618
2619 static void
troff_de(Tag_t * tp,Arg_t * ap)2620 troff_de(Tag_t* tp, Arg_t* ap)
2621 {
2622 register Tag_t* mp;
2623
2624 if (ap->argc >= 1)
2625 {
2626 mp = mac(ap->argv[1]);
2627 if (mp->body && tp->name[1] == 'a')
2628 sfputr(state.tmp, mp->body, -1);
2629 mp->file = error_info.file;
2630 mp->line = error_info.line;
2631 mp->flags |= TAG_PASS;
2632 mp->call = macro;
2633 state.define = mp;
2634 state.end = (Tag_t*)hashget(state.symbols, "..");
2635 state.pass = 1;
2636 pushout(state.tmp);
2637 }
2638 }
2639
2640 static void
troff_di(Tag_t * tp,Arg_t * ap)2641 troff_di(Tag_t* tp, Arg_t* ap)
2642 {
2643 Divert_t* dp;
2644 int n;
2645
2646 if (!ap->argc)
2647 {
2648 if (dp = state.divert)
2649 {
2650 state.dl = state.env->dl;
2651 state.dn = state.env->dn;
2652 if (dp->env->ft.current != state.env->ft.current)
2653 code_1(OP_ft + dp->env->ft.current);
2654 if (dp->env->ps.current != state.env->ps.current)
2655 code_2(OP_ps, dp->env->ps.current);
2656 state.env = dp->env;
2657 state.pass = 0;
2658 state.divert = dp->next;
2659 if ((n = sfstrtell(state.out)) > 0 && *(sfstrbase(state.out) + n - 1) != '\n')
2660 sfputc(state.out, '\n');
2661 set(dp->tag, popout(), 0);
2662 message((-2, "%s: pop diversion `%s'", dp->tag->name, dp->tag->body));
2663 sfstrclose(dp->sp);
2664 free(dp);
2665 }
2666 }
2667 else if (!(dp = newof(0, Divert_t, 1, 0)) || !(dp->sp = sfstropen()))
2668 error(ERROR_SYSTEM|3, "out of space [divert]");
2669 else
2670 {
2671 dp->tag = mac(ap->argv[1]);
2672 if (dp->tag->body && tp->name[2] == 'a')
2673 sfputr(dp->sp, dp->tag->body, -1);
2674 dp->next = state.divert;
2675 state.divert = dp;
2676 state.pass = 1;
2677 pushout(dp->sp);
2678 dp->env = state.env;
2679 dp->environment = *state.env;
2680 state.env = &dp->environment;
2681 state.env->dl = 0;
2682 state.env->dn = 0;
2683 }
2684 }
2685
2686 static void
troff_ds(Tag_t * tp,Arg_t * ap)2687 troff_ds(Tag_t* tp, Arg_t* ap)
2688 {
2689 char* v;
2690
2691 if (v = join(ap, 2))
2692 set(mac(ap->argv[1]), v, tp->name[1] == 'a');
2693 }
2694
2695 static void
troff_ec(Tag_t * tp,Arg_t * ap)2696 troff_ec(Tag_t* tp, Arg_t* ap)
2697 {
2698 NoP(tp);
2699 state.ec = state.eo = (ap->argc >= 1) ? ap->argv[1][0] : DEFAULT_ec;
2700 }
2701
2702 static void
troff_eo(Tag_t * tp,Arg_t * ap)2703 troff_eo(Tag_t* tp, Arg_t* ap)
2704 {
2705 NoP(tp);
2706 if (state.ec)
2707 {
2708 state.eo = state.ec;
2709 state.ec = 0;
2710 }
2711 }
2712
2713 static void
troff_ev(Tag_t * tp,Arg_t * ap)2714 troff_ev(Tag_t* tp, Arg_t* ap)
2715 {
2716 register Env_t* oe;
2717 register Env_t* ne;
2718
2719 if (ap->argc < 1)
2720 {
2721 if (state.env_sp <= state.env_stack)
2722 error(2, "%s: stack underflow", tp->name);
2723 else
2724 state.env_sp--;
2725 }
2726 else if (state.env_sp >= &state.env_stack[elementsof(state.env_stack)])
2727 error(2, "%s: stack overflow", tp->name);
2728 else
2729 *++state.env_sp = env(ap->argv[1]);
2730 oe = state.env;
2731 ne = state.env = *state.env_sp;
2732 if (oe != ne)
2733 {
2734 if (oe->ft.current != ne->ft.current)
2735 code_1(OP_ft + ne->ft.current);
2736 if (oe->nf != ne->nf)
2737 code_1(ne->nf ? OP_pre : END(OP_pre));
2738 if (oe->ps.current != ne->ps.current)
2739 code_2(OP_ps, ne->ps.current);
2740 }
2741 }
2742
2743 static void
troff_em(Tag_t * tp,Arg_t * ap)2744 troff_em(Tag_t* tp, Arg_t* ap)
2745 {
2746 int i;
2747
2748 NoP(tp);
2749 for (i = 1; i <= ap->argc; i++)
2750 trap(&state.fini, ap->argv[i]);
2751 }
2752
2753 static void
troff_fi(Tag_t * tp,Arg_t * ap)2754 troff_fi(Tag_t* tp, Arg_t* ap)
2755 {
2756 NoP(tp);
2757 NoP(ap);
2758 if (state.env->nf)
2759 {
2760 state.env->nf = 0;
2761 code_1(END(OP_pre));
2762 }
2763 }
2764
2765 static void
troff_fp(Tag_t * tp,Arg_t * ap)2766 troff_fp(Tag_t* tp, Arg_t* ap)
2767 {
2768 char* s;
2769 char* t;
2770
2771 NoP(tp);
2772 if (ap->argc >= 2)
2773 {
2774 s = ap->argv[1];
2775 *--s = 'f';
2776 t = ap->argv[1];
2777 *--t = 'f';
2778 hashput(state.symbols, s, hashget(state.symbols, t));
2779 }
2780 }
2781
2782 static void
troff_ft(Tag_t * tp,Arg_t * ap)2783 troff_ft(Tag_t* tp, Arg_t* ap)
2784 {
2785 char* s;
2786
2787 NoP(tp);
2788 if (ap->argc >= 1)
2789 {
2790 s = ap->argv[1];
2791 *--s = 'f';
2792 s = (char*)hashget(state.symbols, s);
2793 }
2794 else s = 0;
2795 ft(s);
2796 }
2797
2798 static void
troff_ie(Tag_t * tp,Arg_t * ap)2799 troff_ie(Tag_t* tp, Arg_t* ap)
2800 {
2801 int f;
2802 int q;
2803 long v;
2804 char* s;
2805 char* t;
2806 char* e;
2807
2808 if (tp->name[1] == 'e')
2809 {
2810 if (!state.cond.level || !(state.cond.flags[state.cond.level] & COND_IE))
2811 {
2812 if (state.verbose)
2813 error(2, "%s: no matching .ie", tp->name);
2814 if (!state.cond.level)
2815 state.cond.level++;
2816 state.cond.flags[state.cond.level] = COND_IE|COND_KEPT;
2817 }
2818 f = COND_EL;
2819 }
2820 else
2821 {
2822 if (state.cond.level >= elementsof(state.cond.flags) - 1)
2823 error(3, "%s: conditional stack too deep", tp->name);
2824 f = tp->name[2] == 'f' ? COND_IF : COND_IE;
2825 }
2826 if (error_info.trace <= -5)
2827 {
2828 int g = state.cond.flags[state.cond.level];
2829
2830 error(-5, "IE +++ %s level=%d |%s%s%s%s%s", tp->name, state.cond.level, (g & COND_IF) ? "IF|" : "", (g & COND_IE) ? "IE|" : "", (g & COND_BLOCK) ? "BLOCK|" : "", (g & COND_SKIP) ? "SKIP|" : "", (g & COND_KEPT) ? "KEPT|" : "");
2831 }
2832 if (s = join(ap, 1))
2833 {
2834 if (state.ec)
2835 {
2836 t = s;
2837 while (t = strchr(t, state.ec))
2838 if (*++t != '{' && *t != '}')
2839 break;
2840 if (t)
2841 {
2842 if (state.test & 0x100)
2843 error(2, "EXPAND +++ `%s'", s);
2844 expand(state.arg, s);
2845 sfputr(ap->sp, use(state.arg), 0);
2846 s = use(ap->sp);
2847 if (state.test & 0x100)
2848 error(2, "EXPAND --- `%s'", s);
2849 }
2850 }
2851 for (;;)
2852 {
2853 if (f & COND_EL)
2854 v = !(state.cond.flags[state.cond.level] & COND_KEPT);
2855 else
2856 {
2857 v = cond(s, &e);
2858 s = e;
2859 }
2860 if (v || s[0] != state.env->cc && s[0] != state.env->c2 || s[1] != 'i')
2861 break;
2862 q = f;
2863 if (s[2] == 'e')
2864 f = COND_IE|COND_KEPT|COND_SKIP;
2865 else if (s[2] == 'f')
2866 f = COND_IF|COND_SKIP;
2867 else
2868 break;
2869 if (q & COND_EL)
2870 state.cond.level--;
2871 }
2872 if (*s == state.ec && state.ec && *(s + 1) == '{')
2873 {
2874 s += 2;
2875 f |= COND_BLOCK;
2876 }
2877 while (isspace(*s))
2878 s++;
2879 if (!*s)
2880 s = 0;
2881 else
2882 *(s + strlen(s)) = '\n';
2883 }
2884 if (!v)
2885 {
2886 f |= COND_SKIP;
2887 skip(s, f, NiL);
2888 }
2889 else
2890 {
2891 if (s)
2892 {
2893 pushin(NiL, 0, NiL, s, ap);
2894 error_info.line--;
2895 }
2896 if (f & (COND_EL|COND_IE))
2897 f |= COND_KEPT;
2898 }
2899 if (f & COND_EL)
2900 {
2901 if ((f & (COND_SKIP|COND_BLOCK)) == COND_BLOCK)
2902 state.cond.flags[state.cond.level] = COND_IE|COND_EL|COND_KEPT|COND_BLOCK;
2903 else
2904 state.cond.level--;
2905 }
2906 else if ((f & COND_IE) || (f & (COND_BLOCK|COND_SKIP)) == COND_BLOCK)
2907 state.cond.flags[++state.cond.level] = f;
2908 if (error_info.trace <= -5)
2909 {
2910 int g = state.cond.flags[state.cond.level];
2911
2912 error(-5, "IE --- %s level=%d |%s%s%s%s%s", tp->name, state.cond.level, (g & COND_IF) ? "IF|" : "", (g & COND_IE) ? "IE|" : "", (g & COND_BLOCK) ? "BLOCK|" : "", (g & COND_SKIP) ? "SKIP|" : "", (g & COND_KEPT) ? "KEPT|" : "");
2913 }
2914 }
2915
2916 static void
troff_ignore(Tag_t * tp,Arg_t * ap)2917 troff_ignore(Tag_t* tp, Arg_t* ap)
2918 {
2919 char* s;
2920
2921 if (!state.end)
2922 {
2923 *tp->name = 'e';
2924 state.end = (s = (char*)hashget(state.symbols, tp->name)) ? (Tag_t*)hashget(state.symbols, s) : (Tag_t*)0;
2925 *tp->name = '.';
2926 if (state.end)
2927 {
2928 sfprintf(state.out, "<BR>%s ... %s OMITTED<BR>\n", tp->name, state.end->name);
2929 state.pass = 1;
2930 pushout(state.nul);
2931 }
2932 }
2933 }
2934
2935 static void
troff_in(Tag_t * tp,Arg_t * ap)2936 troff_in(Tag_t* tp, Arg_t* ap)
2937 {
2938 int n;
2939
2940 NoP(tp);
2941 if (ap->argc < 1)
2942 n = state.env->in.previous;
2943 else
2944 {
2945 n = expression(ap->argv[1], NiL, 'u');
2946 switch (ap->argv[1][0])
2947 {
2948 case '-':
2949 case '+':
2950 n += state.env->in.current;
2951 break;
2952 }
2953 }
2954 state.env->in.previous = state.env->in.current;
2955 state.env->in.current = n;
2956 li(0);
2957 }
2958
2959 static void
troff_it(Tag_t * tp,Arg_t * ap)2960 troff_it(Tag_t* tp, Arg_t* ap)
2961 {
2962 char* s;
2963 char* t;
2964
2965 NoP(tp);
2966 state.it.count = 0;
2967 if (ap->argc >= 2 && (state.it.count = expression(ap->argv[1], NiL, 0)) > 0 && *(s = ap->argv[2]))
2968 {
2969 t = state.it.trap;
2970 *t++ = '.';
2971 *t++ = *s++;
2972 if (*s)
2973 {
2974 *t++ = *s;
2975 if (state.groff)
2976 while (t < &state.it.trap[sizeof(state.it.trap)-2] && (*t = *++s))
2977 t++;
2978 }
2979 *t++ = '\n';
2980 *t = 0;
2981 }
2982 }
2983
2984 static void
troff_ll(Tag_t * tp,Arg_t * ap)2985 troff_ll(Tag_t* tp, Arg_t* ap)
2986 {
2987 int n;
2988
2989 NoP(tp);
2990 if (ap->argc < 1)
2991 n = state.env->ll.previous;
2992 else
2993 {
2994 n = expression(ap->argv[1], NiL, 'u');
2995 switch (ap->argv[1][0])
2996 {
2997 case '-':
2998 case '+':
2999 n += state.env->ll.current;
3000 break;
3001 }
3002 }
3003 state.env->ll.previous = state.env->ll.current;
3004 state.env->ll.current = n;
3005 li(0);
3006 }
3007
3008 static void
troff_ne(Tag_t * tp,Arg_t * ap)3009 troff_ne(Tag_t* tp, Arg_t* ap)
3010 {
3011 NoP(tp);
3012 state.t = (ap->argc < 1) ? 1 : INT_MAX;
3013 }
3014
3015 static void
troff_nf(Tag_t * tp,Arg_t * ap)3016 troff_nf(Tag_t* tp, Arg_t* ap)
3017 {
3018 NoP(tp);
3019 NoP(ap);
3020 if (!state.env->nf)
3021 {
3022 if (!state.it.interrupt)
3023 it();
3024 li(-1);
3025 state.env->nf = 1;
3026 code_1(OP_pre);
3027 }
3028 }
3029
3030 static void
troff_nr(Tag_t * tp,Arg_t * ap)3031 troff_nr(Tag_t* tp, Arg_t* ap)
3032 {
3033 int i;
3034 long n;
3035 char* s;
3036 Num_t* np;
3037
3038 NoP(tp);
3039 if (ap->argc >= 2)
3040 {
3041 np = num(ap->argv[1]);
3042 if ((i = *(s = ap->argv[2])) == '+' || i == '-')
3043 s++;
3044 n = expression(s, NiL, 0);
3045 switch (i)
3046 {
3047 case '-':
3048 n = np->number - n;
3049 break;
3050 case '+':
3051 n = np->number + n;
3052 break;
3053 }
3054 np->number = n;
3055 if (np->internal)
3056 switch (INDEX(np->name[1], np->name[2]))
3057 {
3058 case INDEX('c','.'):
3059 if (state.original.line)
3060 state.original.line = n;
3061 else
3062 error_info.line = n;
3063 break;
3064 case INDEX('d','l'):
3065 state.dl = n;
3066 break;
3067 case INDEX('d','n'):
3068 state.dn = n;
3069 break;
3070 case INDEX('l','n'):
3071 state.ln = n;
3072 break;
3073 case INDEX('n','l'):
3074 state.nl = n;
3075 break;
3076 }
3077 if (ap->argc >= 3)
3078 np->increment = expression(ap->argv[3], NiL, 0);
3079 if (np->flags & TAG_TRACE_SET)
3080 error(2, "set register %s %d %d", np->name + 1, np->number, np->increment);
3081 }
3082 }
3083
3084 static void
troff_pc(Tag_t * tp,Arg_t * ap)3085 troff_pc(Tag_t* tp, Arg_t* ap)
3086 {
3087 NoP(tp);
3088 state.pc = (ap->argc >= 1) ? ap->argv[1][0] : 0;
3089 }
3090
3091 static void
troff_ps(Tag_t * tp,Arg_t * ap)3092 troff_ps(Tag_t* tp, Arg_t* ap)
3093 {
3094 int n;
3095
3096 NoP(tp);
3097 if (ap->argc < 1)
3098 n = state.env->ps.previous;
3099 else
3100 {
3101 n = expression(ap->argv[1], NiL, 'p');
3102 switch (ap->argv[1][0])
3103 {
3104 case '-':
3105 case '+':
3106 n += state.env->ps.current;
3107 break;
3108 default:
3109 if (!n)
3110 n = state.env->ps.previous;
3111 break;
3112 }
3113 }
3114 if (n > 0)
3115 ps(n);
3116 }
3117
3118 static void
troff_rm(Tag_t * tp,Arg_t * ap)3119 troff_rm(Tag_t* tp, Arg_t* ap)
3120 {
3121 int i;
3122
3123 NoP(tp);
3124 for (i = 1; i <= ap->argc; i++)
3125 rm(ap->argv[i]);
3126 }
3127
3128 static void
troff_rn(Tag_t * tp,Arg_t * ap)3129 troff_rn(Tag_t* tp, Arg_t* ap)
3130 {
3131 Tag_t* mp;
3132 char* s;
3133
3134 NoP(tp);
3135 if (ap->argc >= 2)
3136 {
3137 s = ap->argv[2];
3138 rm(s);
3139 *--s = '.';
3140 mp = mac(ap->argv[1]);
3141 hashput(state.symbols, NiL, NiL);
3142 mp->name = hashput(state.symbols, s, mp);
3143 }
3144 }
3145
3146 static void
rr(char * name)3147 rr(char* name)
3148 {
3149 register Num_t* np;
3150 char buf[MAXNAME];
3151
3152 if (np = (Num_t*)hashget(state.symbols, nam('.', name, buf, sizeof(buf))))
3153 {
3154 if (np->internal)
3155 return;
3156 if (np->flags & TAG_TRACE_SET)
3157 error(2, "remove register %s", np->name + 1);
3158 free(np);
3159 hashput(state.symbols, NiL, NiL);
3160 }
3161 }
3162
3163 static void
troff_rr(Tag_t * tp,Arg_t * ap)3164 troff_rr(Tag_t* tp, Arg_t* ap)
3165 {
3166 int i;
3167
3168 NoP(tp);
3169 for (i = 1; i <= ap->argc; i++)
3170 rr(ap->argv[i]);
3171 }
3172
3173 static void
troff_so(Tag_t * tp,Arg_t * ap)3174 troff_so(Tag_t* tp, Arg_t* ap)
3175 {
3176 Sfio_t* sp;
3177 char* path;
3178
3179 NoP(tp);
3180 if (ap->argc >= 1 && (sp = find(ap->argv[1], &path, 1)))
3181 pushin(path, 1, sp, NiL, NiL);
3182 }
3183
3184 static void
troff_sp(Tag_t * tp,Arg_t * ap)3185 troff_sp(Tag_t* tp, Arg_t* ap)
3186 {
3187 NoP(tp);
3188 if (state.it.dd)
3189 {
3190 state.it.dd = 0;
3191 code_1(OP_dd);
3192 }
3193 code_1((ap->argc < 1 || expression(ap->argv[1], NiL, 'v') > 0) ? OP_p : OP_br);
3194 }
3195
3196 static void
troff_ta(Tag_t * tp,Arg_t * ap)3197 troff_ta(Tag_t* tp, Arg_t* ap)
3198 {
3199 int i;
3200 unsigned char ta[elementsof(state.ta)];
3201
3202 NoP(tp);
3203 if (ap->argc < 1)
3204 {
3205 state.ta[0] = 8;
3206 i = 1;
3207 }
3208 else
3209 {
3210 if (ap->argc >= elementsof(ta))
3211 ap->argc = elementsof(ta) - 1;
3212 for (i = 0; i < ap->argc; i++)
3213 ta[i] = expression(ap->argv[i+1], NiL, 'u');
3214 }
3215 ta[i] = 0;
3216 code_n(OP_ta, (char*)ta);
3217 }
3218
3219 static void
troff_ti(Tag_t * tp,Arg_t * ap)3220 troff_ti(Tag_t* tp, Arg_t* ap)
3221 {
3222 int n;
3223
3224 NoP(tp);
3225 if (ap->argc < 1)
3226 n = state.env->ti.previous;
3227 else
3228 {
3229 n = expression(ap->argv[1], NiL, 'u');
3230 switch (ap->argv[1][0])
3231 {
3232 case '-':
3233 case '+':
3234 n += state.env->ti.current;
3235 break;
3236 }
3237 }
3238 state.env->ti.previous = state.env->ti.current;
3239 state.env->ti.current = n;
3240 li(1);
3241 if (state.list > state.list_stack && state.env->ti.current < state.list->in)
3242 state.it.dt = 1;
3243 }
3244
3245 static void
troff_tl(Tag_t * tp,Arg_t * ap)3246 troff_tl(Tag_t* tp, Arg_t* ap)
3247 {
3248 register int c;
3249 register int q;
3250 register char* s;
3251 register char* t;
3252 int n;
3253
3254 NoP(tp);
3255 if (s = join(ap, 1))
3256 {
3257 if (state.head)
3258 state.footer = 1;
3259 else
3260 {
3261 state.head = 1;
3262 code_1(END(OP_head));
3263 code_1(OP_body);
3264 }
3265 code_1(OP_h3);
3266 code_2(OP_tab, ARG_wide);
3267 code_1(OP_tab_row);
3268 if (q = *s++)
3269 for (n = 1; n <= 3; n++)
3270 {
3271 code_2(OP_tab_head, n);
3272 if (n == 1)
3273 code_2(OP_cc, ' ');
3274 while ((c = *s++) != q)
3275 {
3276 if (!c)
3277 {
3278 s--;
3279 break;
3280 }
3281 if (c != state.pc)
3282 sfputc(state.out, c);
3283 else if (t = value("n%", 'n', 0))
3284 sfputr(state.out, t, -1);
3285 }
3286 if (n == 1)
3287 code_2(OP_cc, ' ');
3288 }
3289 code_1(END(OP_tab));
3290 code_1(END(OP_h3));
3291 sfputc(state.out, '\n');
3292 }
3293 }
3294
3295 static void
troff_tm(Tag_t * tp,Arg_t * ap)3296 troff_tm(Tag_t* tp, Arg_t* ap)
3297 {
3298 char* s;
3299
3300 NoP(tp);
3301 if (s = join(ap, 1))
3302 notify(s);
3303 }
3304
3305 static void
troff_vs(Tag_t * tp,Arg_t * ap)3306 troff_vs(Tag_t* tp, Arg_t* ap)
3307 {
3308 int n;
3309
3310 NoP(tp);
3311 if (ap->argc < 1)
3312 n = state.env->vs.previous;
3313 else
3314 {
3315 n = expression(ap->argv[1], NiL, 'p');
3316 switch (ap->argv[1][0])
3317 {
3318 case '-':
3319 case '+':
3320 n += state.env->vs.current;
3321 break;
3322 default:
3323 if (!n)
3324 n = state.env->vs.previous;
3325 break;
3326 }
3327 }
3328 if (n > 0)
3329 {
3330 state.env->vs.previous = state.env->vs.current;
3331 state.env->vs.current = n;
3332 }
3333 }
3334
3335 /*
3336 * return 1 if any non-diverted text escaped
3337 */
3338
3339 static int
text(void)3340 text(void)
3341 {
3342 register char* s;
3343 register char* e;
3344
3345 if (state.it.text)
3346 return 1;
3347 s = sfstrbase(state.out);
3348 e = sfstrseek(state.out, 0, SEEK_CUR);
3349 while (s < e)
3350 {
3351 switch (*s++)
3352 {
3353 case CODE_2:
3354 s++;
3355 /*FALLTHROUGH*/
3356 case CODE_1:
3357 s++;
3358 /*FALLTHROUGH*/
3359 case ' ':
3360 case '\t':
3361 case '\n':
3362 case CODE_0:
3363 continue;
3364 default:
3365 message((-9, "TEXT begin"));
3366 return 1;
3367 }
3368 }
3369 return 0;
3370 }
3371
3372 static void
troff_wh(Tag_t * tp,Arg_t * ap)3373 troff_wh(Tag_t* tp, Arg_t* ap)
3374 {
3375 int i;
3376 Trap_t** xp;
3377
3378 if (ap->argc > 1)
3379 {
3380 if (!(i = expression(ap->argv[1], NiL, 'u')))
3381 {
3382 if (!state.it.text && text())
3383 state.it.text = 1;
3384 if (state.it.text)
3385 i = -1;
3386 }
3387 xp = (i < 0) ? &state.fini : &state.trap;
3388 for (i = 2; i <= ap->argc; i++)
3389 trap(xp, ap->argv[i]);
3390 }
3391 }
3392
3393 static void
hot(register char * s,int add)3394 hot(register char* s, int add)
3395 {
3396 register Dir_t* x;
3397 register Dir_t* p;
3398
3399 for (p = 0, x = state.hot; x; p = x, x = x->next)
3400 if (streq(s, x->name))
3401 {
3402 if (!add)
3403 {
3404 if (p)
3405 p->next = x->next;
3406 else
3407 state.hot = state.hot->next;
3408 free(x);
3409 }
3410 return;
3411 }
3412 if (add)
3413 {
3414 if (!(x = newof(0, Dir_t, 1, strlen(s) + 1)))
3415 error(ERROR_SYSTEM|3, "out of space [hot]");
3416 strcpy(x->name = (char*)x + sizeof(*x), s);
3417 x->next = state.hot;
3418 state.hot = x;
3419 }
3420 }
3421
3422 #define OPT_SWITCH 1
3423 #define OPT_begin 2
3424 #define OPT_debug 3
3425 #define OPT_end 4
3426 #define OPT_footnote 5
3427 #define OPT_get 6
3428 #define OPT_hot 7
3429 #define OPT_label 8
3430 #define OPT_link 9
3431 #define OPT_test 10
3432 #define OPT_toolbar 11
3433 #define OPT_set 12
3434
3435 typedef struct
3436 {
3437 const char* name;
3438 char** value;
3439 int index;
3440 } Option_t;
3441
3442 static Option_t options[] =
3443 {
3444 "author", &state.author, 0,
3445 "background", &state.background, 0,
3446 "begin", 0, OPT_begin,
3447 "company", &state.company, 0,
3448 "corporation", &state.corporation, 0,
3449 "debug", 0, OPT_debug,
3450 "end", 0, OPT_end,
3451 "footnote", 0, OPT_footnote,
3452 "get", 0, OPT_get,
3453 "hot", 0, OPT_hot,
3454 "label", 0, OPT_label,
3455 "link", 0, OPT_link,
3456 "location", &state.location, 0,
3457 "logo", &state.logo, 0,
3458 "mailto", &state.mailto, 0,
3459 "organization", &state.organization, 0,
3460 "package", &state.package, 0,
3461 "set", 0, OPT_set,
3462 "test", 0, OPT_test,
3463 "title", &state.title, 0,
3464 "toolbar", &state.toolbar, OPT_toolbar,
3465 "verbose", (char**)&state.verbose, OPT_SWITCH,
3466 0, 0, 0
3467 };
3468
3469 /*
3470 * called by stropt() to set name=value
3471 */
3472
3473 static int
setopt(void * a,const void * x,register int n,const char * v)3474 setopt(void* a, const void* x, register int n, const char* v)
3475 {
3476 register char* s;
3477 register char* t;
3478 register Option_t* p = (Option_t*)x;
3479 register int f;
3480
3481 if (p)
3482 switch (p->index)
3483 {
3484 case OPT_begin:
3485 sfprintf(state.tmp, "<!-- %s -->", v + n);
3486 code_n(OP_RAW, use(state.tmp));
3487 break;
3488 case OPT_debug:
3489 error_info.trace = n ? -expression((char*)v, NiL, 'u') : 0;
3490 break;
3491 case OPT_end:
3492 sfprintf(state.tmp, "<!-- /%s -->", v + n);
3493 code_n(OP_RAW, use(state.tmp));
3494 break;
3495 case OPT_footnote:
3496 if (!state.out)
3497 error(1, "%s: option valid from document body only", p->name);
3498 else
3499 {
3500 /*
3501 * NOTE: mm .FS/.FE is so convoluted that I
3502 * punted to this; trying to decipher
3503 * it prompted .xx [debug|get|set] in
3504 * the first place; I'll get back to a
3505 * real solution on a meetingless day.
3506 */
3507
3508 if (n)
3509 {
3510 register char* b;
3511 long m;
3512
3513 m = sfstrtell(state.out);
3514 b = sfstrbase(state.out);
3515 s = b + m;
3516 while (s > b)
3517 if (!isspace(*--s))
3518 {
3519 s++;
3520 break;
3521 }
3522 t = s;
3523 while (s > b)
3524 if (isspace(*--s))
3525 {
3526 s++;
3527 break;
3528 }
3529 sfprintf(state.tmp, "FN%ld\t%-.*s", m, t - s, s);
3530 sfstrseek(state.out, s - b, SEEK_SET);
3531 code_n(OP_link, use(state.tmp));
3532 sfprintf(state.tmp, "FN%ld", m);
3533 code_n(OP_fn, use(state.tmp));
3534 }
3535 else
3536 code_1(END(OP_fn));
3537 }
3538 break;
3539 case OPT_get:
3540 case OPT_hot:
3541 case OPT_set:
3542 switch (p->index)
3543 {
3544 case OPT_get:
3545 f = TAG_TRACE_GET;
3546 break;
3547 case OPT_hot:
3548 f = 0;
3549 break;
3550 case OPT_set:
3551 f = TAG_TRACE_SET;
3552 break;
3553 }
3554 s = (char*)v;
3555 do
3556 {
3557 while (isspace(*s))
3558 s++;
3559 for (t = s;; t++)
3560 if (!*t)
3561 {
3562 t = 0;
3563 break;
3564 }
3565 else if (isspace(*t))
3566 {
3567 *t++ = 0;
3568 break;
3569 }
3570 if (!s[0])
3571 break;
3572 if (s[0] == '+' && !s[1])
3573 n = 1;
3574 else if (s[0] == '-' && !s[1])
3575 n = 0;
3576 else if (!f)
3577 hot(s, n);
3578 else if (n)
3579 {
3580 num(s)->flags |= f;
3581 mac(s)->flags |= f;
3582 }
3583 else
3584 {
3585 num(s)->flags &= ~f;
3586 mac(s)->flags &= ~f;
3587 }
3588 } while (s = t);
3589 break;
3590 case OPT_label:
3591 if (!state.out)
3592 error(1, "%s: option valid from document body only", p->name);
3593 else if (n)
3594 {
3595 sfprintf(state.tmp, "%s\t", v);
3596 code_n(LABEL(OP_link), use(state.tmp));
3597 }
3598 break;
3599 case OPT_link:
3600 if (!state.out)
3601 error(1, "%s: option valid from document body only", p->name);
3602 else if (n)
3603 code_n(OP_link, (char*)v);
3604 break;
3605 case OPT_test:
3606 if (n)
3607 state.test |= expression((char*)v, NiL, 'u');
3608 else
3609 state.test &= ~expression((char*)v, NiL, 'u');
3610 break;
3611 case OPT_SWITCH:
3612 *((int*)p->value) = n && expression((char*)v, NiL, 'u');
3613 break;
3614 default:
3615 if (*p->value)
3616 free(*p->value);
3617 *p->value = n && v ? strdup(v) : (char*)0;
3618 break;
3619 }
3620 else if (a)
3621 {
3622 if (strneq(v, "meta.", 5))
3623 {
3624 sfprintf(state.tmp, "<META name=\"%s\" content=\"%s\">", v + 5, v + n);
3625 code_n(OP_RAW, use(state.tmp));
3626 }
3627 else
3628 error(1, "%s: unknown option", v);
3629 }
3630 return 0;
3631 }
3632
3633 static void
troff_xx(Tag_t * tp,Arg_t * ap)3634 troff_xx(Tag_t* tp, Arg_t* ap)
3635 {
3636 NoP(tp);
3637 stropt(join(ap, 1), options, sizeof(*options), setopt, options);
3638 }
3639
3640 #define COMMENT 001
3641 #define COPY 002
3642 #define EAT 004
3643 #define RAW 010
3644 #define STRING 020
3645
3646 #define ONE() \
3647 do \
3648 {\
3649 if (cc <= 0) \
3650 { \
3651 if (cc < 0) \
3652 cc++; \
3653 if (!argc && !state.it.interrupt) \
3654 it(); \
3655 } \
3656 cc++; \
3657 } while (0)
3658
3659 /*
3660 * convert troff file ip to intermediate file op
3661 */
3662
3663 static void
process(char * file,Sfio_t * ip,Sfio_t * op)3664 process(char* file, Sfio_t* ip, Sfio_t* op)
3665 {
3666 register int c;
3667 register int cc;
3668 register int lastc;
3669 register char* s;
3670 int argc;
3671 int quote;
3672 int n;
3673 int m;
3674 unsigned char* map;
3675 Tag_t* tp;
3676 int argv[ARGS];
3677 struct stat st;
3678 char buf[MAXNAME];
3679
3680 pushin(file, 1, ip, NiL, NiL);
3681 state.generation++;
3682 *(state.env_sp = state.env_stack) = state.env = env("0");
3683 state.it.dd = state.it.dl = state.it.dt = 0;
3684 state.link = 0;
3685 iop("stdout", 1)->sp = state.out = op;
3686 state.pass = 0;
3687 if (ip && !fstat(sffileno(ip), &st))
3688 tm(st.st_mtime);
3689 argc = 0;
3690 argv[0] = 0;
3691 cc = 0;
3692 lastc = 0;
3693 quote = 0;
3694 map = ccmap(CC_NATIVE, CC_ASCII);
3695 for (;;)
3696 {
3697 switch (c = GETC())
3698 {
3699 case EOF:
3700 if (popin())
3701 goto done;
3702 continue;
3703 case CODE_n:
3704 if (ISFILE())
3705 {
3706 cc++;
3707 code_2(OP_cc, c);
3708 }
3709 else
3710 {
3711 sfputc(state.out, c);
3712 n = GETC();
3713 sfputc(state.out, n);
3714 if (n & 0200)
3715 {
3716 n = (n & 0177) << 8;
3717 c = GETC();
3718 sfputc(state.out, c);
3719 n |= c;
3720 }
3721 do
3722 {
3723 c = GETC();
3724 sfputc(state.out, c);
3725 } while (n-- > 0);
3726 }
3727 continue;
3728 case CODE_2:
3729 if (ISFILE())
3730 {
3731 cc++;
3732 code_2(OP_cc, c);
3733 }
3734 else
3735 {
3736 m = GETC();
3737 n = GETC();
3738 if (cc <= 0 && (n == OP_cc || n >= OP_ft1 && n <= OP_ft5 || n == OP_ps))
3739 ONE();
3740 sfputc(state.out, c);
3741 sfputc(state.out, m);
3742 sfputc(state.out, n);
3743 }
3744 continue;
3745 case CODE_1:
3746 if (ISFILE())
3747 {
3748 cc++;
3749 code_2(OP_cc, c);
3750 }
3751 else
3752 {
3753 sfputc(state.out, c);
3754 switch (c = GETC())
3755 {
3756 case OP_br:
3757 case OP_p:
3758 cc = 0;
3759 break;
3760 }
3761 sfputc(state.out, c);
3762 }
3763 continue;
3764 case CODE_0:
3765 if (ISFILE())
3766 {
3767 cc++;
3768 code_2(OP_cc, c);
3769 }
3770 else
3771 {
3772 ONE();
3773 sfputc(state.out, c);
3774 }
3775 continue;
3776 case '"':
3777 if (argc && !(quote & RAW))
3778 {
3779 lastc = c;
3780 switch ((quote & STRING) ? (c = nextchar()) : EOF)
3781 {
3782 case '"':
3783 break;
3784 default:
3785 UNGETC(c);
3786 /*FALLTHROUGH*/
3787 case EOF:
3788 quote ^= STRING;
3789 continue;
3790 }
3791 }
3792 break;
3793 case '\n':
3794 if (state.noline)
3795 {
3796 if (isspace(lastc))
3797 continue;
3798 c = ' ';
3799 break;
3800 }
3801 if (quote & COMMENT)
3802 {
3803 quote &= ~COMMENT;
3804 if (quote & EAT)
3805 {
3806 quote &= ~EAT;
3807 c = ' ';
3808 }
3809 popout();
3810 }
3811 if (argc)
3812 {
3813 cc = 0;
3814 lastc = c;
3815 quote = 0;
3816 state.groff &= 1;
3817 state.pass = 0;
3818 if (!(s = popout()))
3819 {
3820 argc = 0;
3821 continue;
3822 }
3823 if (!s[argv[--argc]] && argc > 0)
3824 argc--;
3825 for (state.tag->argc = argc; argc >= 0; argc--)
3826 state.tag->argv[argc] = s + argv[argc];
3827 argc = 0;
3828 if (error_info.trace <= -4)
3829 {
3830 if (state.end)
3831 sfprintf(state.tmp, " ");
3832 sfprintf(state.tmp, "%s", state.tag->argv[0]);
3833 if (!tp)
3834 sfprintf(state.tmp, " [UNKNOWN]");
3835 for (n = 1; n <= state.tag->argc; n++)
3836 sfprintf(state.tmp, " `%s'", state.tag->argv[n]);
3837 error(-4, "%s:%d: %s", error_info.file, error_info.line, use(state.tmp));
3838 }
3839 if (tp)
3840 {
3841 error_info.line++;
3842 if (tp->call)
3843 (*tp->call)(tp, state.tag);
3844 if (tp->flags & TAG_TRIGGER)
3845 {
3846 tp->flags &= ~TAG_TRIGGER;
3847 trigger(&state.trap);
3848 }
3849 }
3850 else if (state.verbose)
3851 {
3852 switch (state.tag->argv[0][1])
3853 {
3854 case 0:
3855 break;
3856 case '#':
3857 if (!state.tag->argv[0][2])
3858 break;
3859 /*FALLTHROUGH*/
3860 default:
3861 if (error_info.trace >= 0)
3862 error(1, "%s: unknown request", state.tag->argv[0]);
3863 sfprintf(state.tmp, "UNKNOWN REQUEST %s", join(state.tag, 0));
3864 code_n(OP_comment, use(state.tmp));
3865 break;
3866 }
3867 error_info.line++;
3868 }
3869 continue;
3870 }
3871 error_info.line++;
3872 n = 6 * state.env->vs.current;
3873 state.nl += n;
3874 state.env->dn += n;
3875 n = 6 * state.env->ps.current;
3876 if (n > state.env->dl)
3877 state.env->dl = n;
3878 if (!DIVERTED())
3879 {
3880 state.ln++;
3881 if (state.it.text || cc > 1)
3882 state.it.text++;
3883 else
3884 {
3885 cc = 0;
3886 continue;
3887 }
3888 }
3889 state.it.interrupt = 0;
3890 if (state.it.center > 0)
3891 {
3892 if (!--state.it.center)
3893 code_1(state.it.right ? END(OP_p) : END(OP_center));
3894 else
3895 code_1(OP_br);
3896 }
3897 if (state.it.count > 0 && !--state.it.count)
3898 {
3899 sfputc(state.out, '\n');
3900 pushin(NiL, 0, NiL, state.it.trap, NiL);
3901 cc = 0;
3902 continue;
3903 }
3904 cc = -2;
3905 break;
3906 case ' ':
3907 case '\t':
3908 if (argc)
3909 {
3910 if (!quote || (quote == RAW || quote == (COPY|RAW)) && argc == 1)
3911 {
3912 if (lastc != ' ' && argc < elementsof(argv))
3913 {
3914 sfputc(state.out, 0);
3915 argv[argc++] = sftell(state.out);
3916 lastc = ' ';
3917 }
3918 continue;
3919 }
3920 }
3921 break;
3922 default:
3923 if (c == state.ec)
3924 {
3925 escape:
3926 switch (c = GETC())
3927 {
3928 case EOF:
3929 if (popin())
3930 {
3931 c = '\n';
3932 break;
3933 }
3934 goto escape;
3935 case '\n':
3936 if (ISFILE())
3937 error_info.line++;
3938 else
3939 UNGETC(c);
3940 continue;
3941 case 'a':
3942 if (state.pass)
3943 goto passthrough;
3944 c = 1;
3945 break;
3946 case 'c':
3947 if (state.pass || (quote & RAW))
3948 goto passthrough;
3949 if ((c = nextchar()) == '\n')
3950 {
3951 message((-9, "INTERRUPT %s:%d: it.center=%d it.count=%d it.dt", error_info.file, error_info.line, state.it.center, state.it.count));
3952 state.it.interrupt = 1;
3953 error_info.line++;
3954 cc = 0;
3955 continue;
3956 }
3957 UNGETC(c);
3958 continue;
3959 case 'd':
3960 if (state.env->ss <= 0)
3961 {
3962 state.env->ss--;
3963 code_1(OP_sub);
3964 }
3965 else
3966 {
3967 state.env->ss++;
3968 code_1(END(OP_sup));
3969 }
3970 continue;
3971 case 'e':
3972 case 'E':
3973 if (state.pass)
3974 goto passthrough;
3975 ONE();
3976 code_2(OP_cc, state.eo);
3977 lastc = state.eo;
3978 continue;
3979 case 'f':
3980 if (state.pass || (quote & RAW))
3981 goto passthrough;
3982 ft(interpolate('f'));
3983 continue;
3984 case 'g':
3985 case 'j':
3986 if (state.pass || (quote & RAW))
3987 goto passthrough;
3988 pushin(NiL, 0, NiL, interpolate(c), NiL);
3989 continue;
3990 case 'k':
3991 nextchar();
3992 continue;
3993 case '*':
3994 c = '.';
3995 goto interp;
3996 case '[':
3997 if (!state.groff)
3998 goto passthrough;
3999 case '(':
4000 if (state.pass)
4001 goto passthrough;
4002 UNGETC(c);
4003 /*FALLTHROUGH*/
4004 case 'n':
4005 interp:
4006 if (quote & COPY)
4007 goto passthrough;
4008 if (s = interpolate(c))
4009 pushin(NiL, -1, NiL, s, NiL);
4010 continue;
4011 case 'p':
4012 case 'r':
4013 case 'z':
4014 continue;
4015 case 's':
4016 if (state.pass || (quote & RAW))
4017 goto passthrough;
4018 switch (c = nextchar())
4019 {
4020 case '-':
4021 case '+':
4022 m = c;
4023 c = nextchar();
4024 break;
4025 default:
4026 m = 0;
4027 break;
4028 }
4029 switch (c)
4030 {
4031 case '(':
4032 n = 1;
4033 sfputc(state.tmp, c);
4034 break;
4035 case '\'':
4036 n = c;
4037 goto size_long;
4038 case '[':
4039 n = ']';
4040 size_long:
4041 c = nextchar();
4042 if (!m)
4043 switch (c)
4044 {
4045 case '-':
4046 case '+':
4047 m = c;
4048 c = nextchar();
4049 break;
4050 }
4051 while (c != EOF && c != n)
4052 {
4053 sfputc(state.tmp, c);
4054 c = nextchar();
4055 }
4056 goto size_eval;
4057 default:
4058 n = 0;
4059 break;
4060 }
4061 if (c == '0' && m)
4062 {
4063 if ((n = nextchar()) == '\'')
4064 {
4065 while ((c = nextchar()) != EOF && c != '\'')
4066 sfputc(state.tmp, c);
4067 code_n(m == '+' ? OP_link : LABEL(OP_link), use(state.tmp));
4068 continue;
4069 }
4070 UNGETC(n);
4071 n = 0;
4072 }
4073 if (c != EOF)
4074 {
4075 if (n)
4076 {
4077 for (;; c = nextchar())
4078 {
4079 switch (c)
4080 {
4081 case EOF:
4082 break;
4083 case '(':
4084 sfputc(state.tmp, c);
4085 n++;
4086 continue;
4087 case ')':
4088 sfputc(state.tmp, c);
4089 if (--n <= 0)
4090 break;
4091 continue;
4092 default:
4093 sfputc(state.tmp, c);
4094 continue;
4095 }
4096 break;
4097 }
4098 size_eval:
4099 n = expression(use(state.tmp), NiL, 'p');
4100 }
4101 else
4102 n = isdigit(c) ? (c - '0') : 0;
4103 if (!n)
4104 n = state.env->ps.previous;
4105 else switch (m)
4106 {
4107 case '-':
4108 n = state.env->ps.current - n;
4109 break;
4110 case '+':
4111 n = state.env->ps.current + n;
4112 break;
4113 }
4114 if (n > 0)
4115 ps(n);
4116 }
4117 continue;
4118 case 't':
4119 if (state.pass)
4120 goto passthrough;
4121 c = '\t';
4122 break;
4123 case 'u':
4124 if (state.env->ss >= 0)
4125 {
4126 state.env->ss++;
4127 code_1(OP_sup);
4128 }
4129 else
4130 {
4131 state.env->ss--;
4132 code_1(END(OP_sub));
4133 }
4134 continue;
4135 case 'v':
4136 case 'w':
4137 if (state.pass || (quote & RAW))
4138 goto passthrough;
4139 /*FALLTHROUGH*/
4140 case 'b':
4141 case 'h':
4142 case 'l':
4143 case 'L':
4144 case 'o':
4145 case 'x':
4146 if ((n = nextchar()) != EOF)
4147 {
4148 while ((m = nextchar()) != n)
4149 sfputc(state.arg, m);
4150 s = use(state.arg);
4151 switch (c)
4152 {
4153 case 'h':
4154 if (*s++ == '0')
4155 {
4156 /*UNDENT...*/
4157 if ((c = *s++) == '*' || c == '/')
4158 {
4159 state.link = c == '*' ? OP_link : LABEL(OP_link);
4160 if (*s++ == '\\' && *s++ == 'w' && *s++ == '"')
4161 {
4162 if ((c = strlen(s)) > 0 && s[--c] == '"')
4163 s[c] = 0;
4164 sfputr(state.ref, s, '\t');
4165 }
4166 pushout(state.ref);
4167 }
4168 else if (!c && state.link)
4169 {
4170 code_n(state.link, popout());
4171 state.link = 0;
4172 }
4173 /*...INDENT*/
4174 }
4175 /* yep, this is a grade A hack, even for this file */
4176 if ((c = nextchar()) == state.ec)
4177 {
4178 if ((n = nextchar()) == 'c')
4179 break;
4180 UNGETC(n);
4181 }
4182 UNGETC(c);
4183 break;
4184 case 'v':
4185 c = expression(s, NiL, 0) >= 0 ? 'd' : 'u';
4186 sfprintf(state.arg, "%c%c", state.ec, c);
4187 pushin(NiL, 0, NiL, use(state.arg), NiL);
4188 break;
4189 case 'w':
4190 n = convert(strlen(s), 1, 'n');
4191 sfprintf(state.arg, "%d", n);
4192 pushin(NiL, 0, NiL, use(state.arg), NiL);
4193 break;
4194 }
4195 }
4196 continue;
4197 case '$':
4198 if (state.mac)
4199 {
4200 c = nextchar();
4201 if (c == '(')
4202 {
4203 if ((c = nextchar()) != EOF)
4204 {
4205 sfputc(state.tmp, c);
4206 if ((c = nextchar()) != EOF)
4207 sfputc(state.tmp, c);
4208 }
4209 goto arg_eval;
4210 }
4211 else if (c == '[')
4212 {
4213 while ((c = nextchar()) != EOF && c != ']')
4214 sfputc(state.tmp, c);
4215 arg_eval:
4216 c = expression(use(state.tmp), NiL, 0);
4217 if (c >= 0 && c <= state.mac->argc)
4218 pushin(NiL, -argc, NiL, state.mac->argv[c], NiL);
4219 }
4220 else if (c == '@')
4221 {
4222 for (c = 1; c < state.mac->argc; c++)
4223 sfprintf(state.tmp, "\"%s\" ", state.mac->argv[c]);
4224 if (c == state.mac->argc)
4225 sfprintf(state.tmp, "\"%s\"", state.mac->argv[c]);
4226 pushin(NiL, -argc, NiL, use(state.tmp), NiL);
4227 }
4228 else if (c < '0' || c > '9')
4229 {
4230 for (c = 1; c < state.mac->argc; c++)
4231 sfputr(state.tmp, state.mac->argv[c], ' ');
4232 if (c == state.mac->argc)
4233 sfputr(state.tmp, state.mac->argv[c], -1);
4234 pushin(NiL, -argc, NiL, use(state.tmp), NiL);
4235 }
4236 else if ((c -= '0') <= state.mac->argc)
4237 pushin(NiL, -argc, NiL, state.mac->argv[c], NiL);
4238 }
4239 continue;
4240 case '{':
4241 for (;;)
4242 {
4243 if ((n = GETC()) == EOF)
4244 {
4245 if (popin())
4246 break;
4247 continue;
4248 }
4249 if (n == state.ec)
4250 {
4251 escape_splice:
4252 switch (m = GETC())
4253 {
4254 case EOF:
4255 if (popin())
4256 break;
4257 goto escape_splice;
4258 case '\n':
4259 UNGETC(m);
4260 break;
4261 default:
4262 UNGETC(m);
4263 UNGETC(n);
4264 break;
4265 }
4266 break;
4267 }
4268 else
4269 UNGETC(n);
4270 break;
4271 }
4272 if (state.pass)
4273 goto passthrough;
4274 continue;
4275 case '}':
4276 if (state.end || !(state.cond.flags[state.cond.level] & COND_BLOCK))
4277 goto passthrough;
4278 if (state.cond.flags[state.cond.level] & (COND_EL|COND_IF))
4279 state.cond.level--;
4280 else
4281 state.cond.flags[state.cond.level] &= ~COND_BLOCK;
4282 continue;
4283 case '0':
4284 case '|':
4285 case '^':
4286 case ' ':
4287 case '/':
4288 case ',':
4289 case '~':
4290 if (state.pass)
4291 goto passthrough;
4292 ONE();
4293 code_2(OP_cc, ' ');
4294 continue;
4295 case '&':
4296 if (state.pass || (quote & RAW))
4297 goto passthrough;
4298 if (!cc)
4299 cc = -1;
4300 code_0();
4301 continue;
4302 case '-':
4303 case '%':
4304 if (state.pass)
4305 goto passthrough;
4306 ONE();
4307 code_2(OP_cc, 45);
4308 continue;
4309 case '"':
4310 if (!quote)
4311 {
4312 quote |= COMMENT;
4313 pushout(state.nul);
4314 }
4315 cc++;
4316 continue;
4317 case '#':
4318 if (!quote)
4319 {
4320 quote |= COMMENT|EAT;
4321 pushout(state.nul);
4322 }
4323 cc++;
4324 continue;
4325 case '!':
4326 if (!cc)
4327 {
4328 cc++;
4329 continue;
4330 }
4331 break;
4332 default:
4333 if (c == state.ec)
4334 break;
4335 if ((c == state.env->cc || c == state.env->c2) && !cc)
4336 goto request;
4337 if (state.pass)
4338 goto passthrough;
4339 break;
4340 }
4341 break;
4342 passthrough:
4343 ONE();
4344 sfputc(state.out, state.ec);
4345 break;
4346 }
4347 else if ((c == state.env->cc || c == state.env->c2) && !cc)
4348 {
4349 request:
4350 n = c;
4351 s = buf;
4352 *s++ = '.';
4353 if ((c = nextchar()) != EOF)
4354 {
4355 while (c == ' ' || c == '\t')
4356 c = nextchar();
4357 if (c == state.ec || c == '\n')
4358 UNGETC(c);
4359 else if (c != EOF)
4360 {
4361 *s++ = c;
4362 while (s < &buf[sizeof(buf)-1] && (c = nextchar()) != EOF)
4363 {
4364 if (c == state.ec || isspace(c))
4365 {
4366 UNGETC(c);
4367 break;
4368 }
4369 *s++ = c;
4370 if (!state.groff)
4371 {
4372 if ((c = nextchar()) != EOF)
4373 {
4374 UNGETC(c);
4375 if (!isspace(c))
4376 pushin(NiL, 0, NiL, " ", NiL);
4377 }
4378 break;
4379 }
4380 }
4381 }
4382 }
4383 *s = 0;
4384 tp = (Tag_t*)hashget(state.symbols, buf);
4385 if (tp && (tp->flags & TAG_DO))
4386 {
4387 state.groff |= 2;
4388 c = n;
4389 goto request;
4390 }
4391 if (state.end)
4392 {
4393 if (tp == state.end)
4394 {
4395 state.end = 0;
4396 state.pass = 0;
4397 s = popout();
4398 if (tp = state.define)
4399 {
4400 state.define = 0;
4401 set(tp, s, 0);
4402 }
4403 quote |= COMMENT;
4404 pushout(state.nul);
4405 }
4406 else
4407 {
4408 pushin(NiL, 0, NiL, buf + 1, NiL);
4409 c = n;
4410 }
4411 break;
4412 }
4413 if (tp)
4414 {
4415 if ((tp->flags & TAG_BREAK) && n == state.env->cc)
4416 {
4417 if (!DIVERTED() && (state.it.text || text()))
4418 state.it.text++;
4419 if (state.it.interrupt)
4420 {
4421 message((-9, "BREAK %s:%d: it.center=%d it.count=%d", error_info.file, error_info.line, state.it.center, state.it.count));
4422 state.it.interrupt = 0;
4423 sfputc(state.out, '\n');
4424 ONE();
4425 }
4426 tp->flags |= TAG_TRIGGER;
4427 }
4428 if (tp->flags & TAG_COPY)
4429 quote |= COPY|RAW;
4430 else if (tp->flags & TAG_RAW)
4431 quote |= RAW;
4432 if (tp->flags & TAG_PASS)
4433 state.pass = 1;
4434 }
4435 else
4436 state.pass = 1;
4437 argc = 1;
4438 pushout(state.tag->sp);
4439 sfputr(state.out, buf, -1);
4440 cc = s - buf;
4441 continue;
4442 }
4443 else if ((n = ccmapchr(map, c)) > 0177)
4444 {
4445 ONE();
4446 code_2(OP_cc, n & 0377);
4447 continue;
4448 }
4449 break;
4450 }
4451 ONE();
4452 sfputc(state.out, c);
4453 lastc = c;
4454 }
4455 done:
4456 if (state.end)
4457 {
4458 if (state.define)
4459 {
4460 error(2, "%s macro definition end tag %s not found", state.define->name, state.end->name);
4461 state.define = 0;
4462 }
4463 else
4464 error(2, "group end tag %s not found", state.end->name);
4465 state.end = 0;
4466 }
4467 while (DIVERTED())
4468 popout();
4469 }
4470
4471 static Tag_t tags[] =
4472 {
4473 ".", 0, 0, 0,0,0,0,
4474 ".'", 0, 0, 0,0,0,0,
4475 ".''", 0, 0, 0,0,0,0,
4476 "..", 0, 0, 0,0,0,0,
4477 ".EN", 0, 0, 0,0,0,0,
4478 ".EQ", troff_ignore, 0, 0,0,0,0,
4479 ".TE", 0, 0, 0,0,0,0,
4480 ".TS", troff_ignore, 0, 0,0,0,0,
4481 ".\"", 0, 0, 0,0,0,0,
4482 ".\\\"",0, 0, 0,0,0,0,
4483 ".ab", troff_ab, 0, 0,0,0,0,
4484 ".ad", troff_ad, TAG_PASS, 0,0,0,0,
4485 ".af", troff_af, 0, 0,0,0,0,
4486 ".al", 0, 0, 0,0,0,0,
4487 ".aln", groff_aln, 0, 0,0,0,0,
4488 ".als", groff_als, 0, 0,0,0,0,
4489 ".am", troff_de, TAG_PASS, 0,0,0,0,
4490 ".as", troff_ds, TAG_PASS, 0,0,0,0,
4491 ".asciify", groff_asciify, 0, 0,0,0,0,
4492 ".backtrace", 0, 0, 0,0,0,0,
4493 ".bd", 0, 0, 0,0,0,0,
4494 ".blm", 0, 0, 0,0,0,0,
4495 ".bp", 0, TAG_BREAK, 0,0,0,0,
4496 ".br", troff_br, TAG_BREAK, 0,0,0,0,
4497 ".break", groff_break, 0, 0,0,0,0,
4498 ".c2", troff_c2, 0, 0,0,0,0,
4499 ".cc", troff_cc, 0, 0,0,0,0,
4500 ".ce", troff_ce, TAG_BREAK, 0,0,0,0,
4501 ".cf", troff_cf, 0, 0,0,0,0,
4502 ".cflags", 0, 0, 0,0,0,0,
4503 ".ch", troff_ch, 0, 0,0,0,0,
4504 ".char", 0, 0, 0,0,0,0,
4505 ".chop", groff_chop, 0, 0,0,0,0,
4506 ".close", groff_close, 0, 0,0,0,0,
4507 ".continue", groff_continue, 0, 0,0,0,0,
4508 ".cp", groff_cp, 0, 0,0,0,0,
4509 ".cs", 0, 0, 0,0,0,0,
4510 ".cu", 0, 0, 0,0,0,0,
4511 ".da", troff_di, TAG_PASS, 0,0,0,0,
4512 ".de", troff_de, TAG_PASS, 0,0,0,0,
4513 ".di", troff_di, TAG_PASS, 0,0,0,0,
4514 ".do", 0, TAG_DO, 0,0,0,0,
4515 ".ds", troff_ds, TAG_PASS, 0,0,0,0,
4516 ".dt", troff_wh, TAG_PASS, 0,0,0,0,
4517 ".ec", troff_ec, 0, 0,0,0,0,
4518 ".eo", troff_eo, 0, 0,0,0,0,
4519 ".el", troff_ie, TAG_PASS|TAG_RAW, 0,0,0,0,
4520 ".em", troff_em, TAG_PASS, 0,0,0,0,
4521 ".ev", troff_ev, TAG_PASS, 0,0,0,0,
4522 ".fam", 0, 0, 0,0,0,0,
4523 ".fc", 0, 0, 0,0,0,0,
4524 ".fi", troff_fi, TAG_BREAK, 0,0,0,0,
4525 ".fl", 0, TAG_BREAK, 0,0,0,0,
4526 ".fp", troff_fp, 0, 0,0,0,0,
4527 ".fspecial", 0, 0, 0,0,0,0,
4528 ".ft", troff_ft, 0, 0,0,0,0,
4529 ".ftr", 0, 0, 0,0,0,0,
4530 ".hcode", 0, 0, 0,0,0,0,
4531 ".hla", 0, 0, 0,0,0,0,
4532 ".hlm", 0, 0, 0,0,0,0,
4533 ".hpf", 0, 0, 0,0,0,0,
4534 ".hw", 0, 0, 0,0,0,0,
4535 ".hy", 0, 0, 0,0,0,0,
4536 ".hym", 0, 0, 0,0,0,0,
4537 ".hys", 0, 0, 0,0,0,0,
4538 ".ie", troff_ie, TAG_PASS|TAG_RAW, 0,0,0,0,
4539 ".if", troff_ie, TAG_PASS|TAG_RAW, 0,0,0,0,
4540 ".ig", troff_ignore, TAG_PASS, 0,0,0,0,
4541 ".in", troff_in, TAG_BREAK, 0,0,0,0,
4542 ".it", troff_it, 0, 0,0,0,0,
4543 ".kern", 0, 0, 0,0,0,0,
4544 ".lc", 0, 0, 0,0,0,0,
4545 ".lf", 0, 0, 0,0,0,0,
4546 ".ll", troff_ll, 0, 0,0,0,0,
4547 ".ls", 0, 0, 0,0,0,0,
4548 ".lt", 0, 0, 0,0,0,0,
4549 ".mk", 0, 0, 0,0,0,0,
4550 ".mso", troff_so, 0, 0,0,0,0,
4551 ".na", 0, 0, 0,0,0,0,
4552 ".ne", troff_ne, 0, 0,0,0,0,
4553 ".nf", troff_nf, TAG_BREAK, 0,0,0,0,
4554 ".nh", 0, 0, 0,0,0,0,
4555 ".nm", 0, 0, 0,0,0,0,
4556 ".nn", 0, 0, 0,0,0,0,
4557 ".nr", troff_nr, 0, 0,0,0,0,
4558 ".nroff", 0, 0, 0,0,0,0,
4559 ".ns", 0, 0, 0,0,0,0,
4560 ".open", groff_open, 0, 0,0,0,0,
4561 ".opena", groff_open, 0, 0,0,0,0,
4562 ".os", 0, 0, 0,0,0,0,
4563 ".pc", troff_pc, 0, 0,0,0,0,
4564 ".pl", 0, 0, 0,0,0,0,
4565 ".pm", 0, 0, 0,0,0,0,
4566 ".pn", 0, 0, 0,0,0,0,
4567 ".pnr", 0, 0, 0,0,0,0,
4568 ".po", 0, 0, 0,0,0,0,
4569 ".ps", troff_ps, 0, 0,0,0,0,
4570 ".pso", groff_pso, 0, 0,0,0,0,
4571 ".ptr", 0, 0, 0,0,0,0,
4572 ".rchar", 0, 0, 0,0,0,0,
4573 ".rj", troff_ce, 0, 0,0,0,0,
4574 ".rm", troff_rm, 0, 0,0,0,0,
4575 ".rn", troff_rn, 0, 0,0,0,0,
4576 ".rnn", groff_rnn, 0, 0,0,0,0,
4577 ".rr", troff_rr, 0, 0,0,0,0,
4578 ".rs", 0, 0, 0,0,0,0,
4579 ".rt", 0, 0, 0,0,0,0,
4580 ".shc", 0, 0, 0,0,0,0,
4581 ".shift", groff_shift, 0, 0,0,0,0,
4582 ".so", troff_so, 0, 0,0,0,0,
4583 ".sp", troff_sp, TAG_BREAK, 0,0,0,0,
4584 ".special", 0, 0, 0,0,0,0,
4585 ".ss", 0, 0, 0,0,0,0,
4586 ".sty", 0, 0, 0,0,0,0,
4587 ".sv", 0, 0, 0,0,0,0,
4588 ".sy", groff_sy, 0, 0,0,0,0,
4589 ".ta", troff_ta, TAG_BREAK, 0,0,0,0,
4590 ".tc", 0, 0, 0,0,0,0,
4591 ".ti", troff_ti, TAG_BREAK, 0,0,0,0,
4592 ".tkf", 0, 0, 0,0,0,0,
4593 ".tl", troff_tl, 0, 0,0,0,0,
4594 ".tm", troff_tm, 0, 0,0,0,0,
4595 ".tr", 0, 0, 0,0,0,0,
4596 ".trf", 0, 0, 0,0,0,0,
4597 ".trnt", 0, 0, 0,0,0,0,
4598 ".troff", 0, 0, 0,0,0,0,
4599 ".uf", 0, 0, 0,0,0,0,
4600 ".ul", 0, 0, 0,0,0,0,
4601 ".vpt", 0, 0, 0,0,0,0,
4602 ".vs", troff_vs, 0, 0,0,0,0,
4603 ".warn", 0, 0, 0,0,0,0,
4604 ".wh", troff_wh, TAG_PASS, 0,0,0,0,
4605 ".while", groff_while, TAG_PASS|TAG_COPY, 0,0,0,0,
4606 ".write", groff_write, 0, 0,0,0,0,
4607 ".xx", troff_xx, TAG_RAW, 0,0,0,0,
4608 };
4609
4610 static Var_t vars[] =
4611 {
4612 "(**", "*",
4613 "(+-", "\261",
4614 "(ap", "~",
4615 "(bu", "\267",
4616 "(bv", "|",
4617 "(co", "\251",
4618 "(dg", "\247",
4619 "(fm", "'",
4620 "(lq", "``",
4621 "(rg", "\256",
4622 "(rq", "''",
4623 "(sq", "\244",
4624 "eEQ", ".EN",
4625 "eTS", ".TE",
4626 "eig", "..",
4627 "f", "",
4628 "f1", "1",
4629 "f2", "2",
4630 "f3", "3",
4631 "f5", "5",
4632 "fB", "3",
4633 "fCW", "5",
4634 "fF", "5",
4635 "fI", "2",
4636 "fL", "5",
4637 "fM", "5",
4638 "fR", "1",
4639 "fX", "5",
4640 };
4641
4642 static Dir_t dot =
4643 {
4644 0, ".",
4645 };
4646
4647 /*
4648 * initialize the global data
4649 */
4650
4651 static void
init(void)4652 init(void)
4653 {
4654 register int i;
4655
4656 state.groff |= 2;
4657 state.tag = &state.top;
4658 if (!(state.top.sp = sfstropen()))
4659 error(ERROR_SYSTEM|3, "out of space [top buffer]");
4660 if (!(state.arg = sfstropen()))
4661 error(ERROR_SYSTEM|3, "out of space [arg buffer]");
4662 if (!(state.nul = sfopen(NiL, "/dev/null", "w")))
4663 error(ERROR_SYSTEM|3, "out of space [nul buffer]");
4664 if (!(state.ref = sfstropen()))
4665 error(ERROR_SYSTEM|3, "out of space [ref buffer]");
4666 if (!(state.req = sfstropen()))
4667 error(ERROR_SYSTEM|3, "out of space [req buffer]");
4668 if (!(state.tmp = sfstropen()))
4669 error(ERROR_SYSTEM|3, "out of space [tmp buffer]");
4670 if (!(state.symbols = hashalloc(NiL, HASH_name, "symbols", 0)))
4671 error(ERROR_SYSTEM|3, "out of space [symbol hash]");
4672 for (i = 0; i < elementsof(vars); i++)
4673 if (!hashput(state.symbols, vars[i].name, vars[i].value))
4674 error(ERROR_SYSTEM|3, "out of space [var hash put]");
4675 for (i = 0; i < elementsof(tags); i++)
4676 {
4677 tags[i].flags |= TAG_STATIC;
4678 if (!hashput(state.symbols, tags[i].name, &tags[i]))
4679 error(ERROR_SYSTEM|3, "out of space [tag hash put]");
4680 }
4681 hashset(state.symbols, HASH_ALLOCATE);
4682 nr("%", 1, 0, 1);
4683 nr(".$", 0, 0, 1);
4684 nr(".A", 0, 0, 1);
4685 nr(".C", 0, 0, 1);
4686 nr(".F", 0, 0, 1);
4687 nr(".H", 0, 0, 1);
4688 nr(".L", 0, 0, 1);
4689 nr(".P", 0, 0, 1);
4690 nr(".T", 0, 0, 1);
4691 nr(".R", 0, 0, 1);
4692 nr(".V", 0, 0, 1);
4693 nr(".a", 0, 0, 1);
4694 nr(".b", 0, 0, 1);
4695 nr(".c", 0, 0, 1);
4696 nr(".ce", 0, 0, 1);
4697 nr(".d", 0, 0, 1);
4698 nr(".ev", 0, 0, 1);
4699 nr(".f", 0, 0, 1);
4700 nr(".g", 0, 0, 1);
4701 nr(".i", 0, 0, 1);
4702 nr(".in", 0, 0, 1);
4703 nr(".j", 0, 0, 1);
4704 nr(".k", 0, 0, 1);
4705 nr(".l", 0, 0, 1);
4706 nr(".ll", 0, 0, 1);
4707 nr(".n", 0, 0, 1);
4708 nr(".pn", 0, 0, 1);
4709 nr(".ps", 0, 0, 1);
4710 nr(".psr", 0, 0, 1);
4711 nr(".s", 0, 0, 1);
4712 nr(".sr", 0, 0, 1);
4713 nr(".t", 0, 0, 1);
4714 nr(".u", 0, 0, 1);
4715 nr(".v", 0, 0, 1);
4716 nr(".vpt", 0, 0, 1);
4717 nr(".w", 0, 0, 1);
4718 nr(".warn", 0, 0, 1);
4719 nr(".x", 0, 0, 1);
4720 nr(".y", 0, 0, 1);
4721 nr(".z", 0, 0, 1);
4722 nr("c.", 0, 0, 1);
4723 nr("dl", 0, 0, 1);
4724 nr("dn", 0, 0, 1);
4725 nr("ln", 0, 0, 1);
4726 nr("nl", 0, 0, 1);
4727 nr("systat", 0, 0, 0);
4728 tm(NiL);
4729 hot("see", 1);
4730 hot("refer", 1);
4731 state.ec = state.eo = DEFAULT_ec;
4732 state.in = (unsigned char*)"";
4733 state.in_top = state.in_stack;
4734 state.out_top = state.out_stack;
4735 state.tag_top = state.tag_stack;
4736 state.pc = DEFAULT_pc;
4737 state.list = state.list_stack;
4738 state.ta[0] = 8;
4739 state.ta[1] = 0;
4740 state.t = 1;
4741 iop("stderr", 1)->sp = sfstderr;
4742 state.groff &= 1;
4743 }
4744
4745 /*
4746 * convert intermediate code in s to html on op
4747 */
4748
4749 static const char* opt_align[] =
4750 {
4751 "LEFT", "CENTER", "RIGHT",
4752 };
4753
4754 static const char* opt_attribute[] =
4755 {
4756 "BACKGROUND", "HREF", "HREF", "ID", "NAME", "SIZE", "SRC",
4757 };
4758
4759 static const char* tag_name[] =
4760 {
4761 0, /* unknown */
4762 "A", /* OP_a */
4763 "BODY", /* OP_body */
4764 "BR", /* OP_br */
4765 0, /* OP_cc */
4766 "CENTER", /* OP_center */
4767 0, /* OP_comment */
4768 "DD", /* OP_dd */
4769 "DIV", /* OP_div */
4770 "DL", /* OP_dl */
4771 "DT", /* OP_dt */
4772 "FN", /* OP_fn */
4773 0, /* OP_ft1 */
4774 "EM", /* OP_ft2 */
4775 "STRONG", /* OP_ft3 */
4776 0, /* OP_ft4 */
4777 "TT", /* OP_ft5 */
4778 "H2", /* OP_h2 */
4779 "H3", /* OP_h3 */
4780 "H4", /* OP_h4 */
4781 "HEAD", /* OP_head */
4782 "HR", /* OP_hr */
4783 "HTML", /* OP_html */
4784 0, /* OP_link */
4785 "P", /* OP_p */
4786 "PRE", /* OP_pre */
4787 "FONT", /* OP_ps */
4788 "SUB", /* OP_sub */
4789 "SUP", /* OP_sup */
4790 "TABLE", /* OP_tab */
4791 "TD", /* OP_tab_data */
4792 "TH", /* OP_tab_head */
4793 "TR", /* OP_tab_row */
4794 "TITLE", /* OP_title */
4795 };
4796
4797 /*
4798 * emit tag and optionally check stack
4799 */
4800
4801 static void
tag(Sfio_t * op,int index,register int flags,int att,char * att_str,int att_num)4802 tag(Sfio_t* op, int index, register int flags, int att, char* att_str, int att_num)
4803 {
4804 register int n;
4805 register int m;
4806 register unsigned char* sp;
4807
4808 if (index & OP_END)
4809 {
4810 index |= OP_LABEL;
4811 sp = state.tag_top;
4812 m = 1;
4813 for (;;)
4814 {
4815 if (sp <= state.tag_stack)
4816 {
4817 error(2, "tag stack underflow trying to match <%s>", tag_name[OP(index)]);
4818 sfprintf(sfstderr, "stack contents:\n");
4819 sp = state.tag_top;
4820 while (--sp >= state.tag_stack)
4821 sfprintf(sfstderr, "\t<%s%s%s>\n", (*sp & OP_END) ? "/" : "", tag_name[OP(*sp)], (*sp & OP_LABEL) ? " label=1" : "");
4822 return;
4823 }
4824 n = *--sp;
4825 if (!(n & OP_END))
4826 {
4827 if (n == OP_pre && OP(index) != OP_pre)
4828 {
4829 m = 0;
4830 break;
4831 }
4832 n |= OP_END|OP_LABEL;
4833 if (tag_name[OP(n)])
4834 {
4835 flags |= LIST;
4836 sfprintf(op, "</%s>", tag_name[OP(n)]);
4837 }
4838 *sp = n;
4839 }
4840 if (n == index)
4841 {
4842 *sp &= ~OP_LABEL;
4843 break;
4844 }
4845 m = 0;
4846 }
4847 if (m)
4848 {
4849 if ((flags & (LINE|LIST)) == (LINE|LIST))
4850 sfputc(op, '\n');
4851 state.tag_top = sp;
4852 }
4853 }
4854 else
4855 {
4856 if (flags & STACK)
4857 {
4858 if (state.tag_top >= &state.tag_stack[elementsof(state.tag_stack)])
4859 error(3, "tag stack overflow");
4860 *state.tag_top++ = tag_name[index] ? index : END(index);
4861 }
4862 if (tag_name[index])
4863 {
4864 sfprintf(op, "<%s", tag_name[index]);
4865 if (att && ((att & ATT_NUMBER) || att_str))
4866 {
4867 sfprintf(op, " %s=", opt_attribute[ATT_INDEX(att)]);
4868 if (att & ATT_NUMBER)
4869 {
4870 if (att == ATT_size)
4871 {
4872 if (att_num < 0)
4873 {
4874 att_num = -att_num;
4875 sfputc(op, '-');
4876 }
4877 else
4878 sfputc(op, '+');
4879 att_num = (att_num + 5) / 6;
4880 }
4881 sfprintf(op, "%d", att_num);
4882 }
4883 else
4884 sfprintf(op, "\"%s%s\"", att == ATT_lref ? "#" : "", att_str);
4885 }
4886 if (ARG_ATTR(flags))
4887 {
4888 if ((n = ARG_ALIGN(flags)) >= 0)
4889 sfprintf(op, " align=%s", opt_align[n]);
4890 if (flags & ARG_compact)
4891 sfputr(op, " COMPACT", -1);
4892 if (flags & ARG_wide)
4893 sfputr(op, " width=100%", -1);
4894 }
4895 if (index == OP_body)
4896 {
4897 if (state.background)
4898 sfprintf(op, " background=\"%s\"", state.background);
4899 if (state.logo)
4900 sfprintf(op, ">\n<CENTER><IMG src=\"%s\"></CENTER", state.logo);
4901 }
4902 sfputc(op, '>');
4903 if (flags & LINE)
4904 sfputc(op, '\n');
4905 }
4906 }
4907 }
4908
4909 /*
4910 * if OP_dl follows OP_ft,OP_ps then do it now
4911 */
4912
4913 static void
peeklist(Sfio_t * op,register char * s)4914 peeklist(Sfio_t* op, register char* s)
4915 {
4916 for (;;)
4917 {
4918 switch (*s++)
4919 {
4920 case 0:
4921 break;
4922 case CODE_0:
4923 continue;
4924 case CODE_2:
4925 s++;
4926 /*FALLTHROUGH*/
4927 case CODE_1:
4928 switch (*s++)
4929 {
4930 case OP_ft1:
4931 case OP_ft2:
4932 case OP_ft3:
4933 case OP_ft4:
4934 case OP_ft5:
4935 case OP_ps:
4936 continue;
4937 case OP_dl:
4938 *--s = CODE_0;
4939 *--s = CODE_0;
4940 tag(op, OP_dl, STACK|ARG_compact, 0, NiL, 0);
4941 continue;
4942 }
4943 break;
4944 case ' ':
4945 case '\t':
4946 case '\n':
4947 continue;
4948 default:
4949 break;
4950 }
4951 break;
4952 }
4953 }
4954
4955 #define P() \
4956 col = br = p = 0
4957
4958 #define DATA() \
4959 do \
4960 { \
4961 if (li) \
4962 { \
4963 if (li == 1) \
4964 li = 0; \
4965 P(); \
4966 } \
4967 else if (p) \
4968 { \
4969 P(); \
4970 sfputr(op, "<P>", '\n'); \
4971 } \
4972 else if (br) \
4973 { \
4974 P(); \
4975 sfputr(op, "<BR>", '\n'); \
4976 } \
4977 } while (0)
4978
4979 static void
html(register unsigned char * s,Sfio_t * op)4980 html(register unsigned char* s, Sfio_t* op)
4981 {
4982 register int c;
4983 register int br = 0;
4984 register int col = 0;
4985 register int li = 0;
4986 register int p = 0;
4987 register int nf = 0;
4988 register unsigned char* v;
4989 unsigned char* t;
4990 unsigned char* u;
4991 int n;
4992 int m;
4993 int ta;
4994 int ts;
4995 int ft = 1;
4996 int hot = 0;
4997 int label = 0;
4998 int ps = DEFAULT_ps;
4999 int ps_set = 0;
5000 Dir_t* x;
5001 Sfio_t* subject;
5002 char a[2];
5003
5004 sfputr(op, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">", '\n');
5005 tag(op, OP_html, STACK|LINE, 0, NiL, 0);
5006 tag(op, OP_head, STACK|LINE, 0, NiL, 0);
5007 t = (unsigned char*)strchr(usage, '\n') + 5;
5008 sfprintf(op, "<META name=\"generator\" content=\"%-.*s", strchr((char*)t, '\n') - (char*)t, t);
5009 for (x = state.macros; x; x = x->next)
5010 sfprintf(op, " -m%s", x->name);
5011 sfputr(op, "\">\n", -1);
5012 tag(op, OP_title, STACK, 0, NiL, 0);
5013 if (!(subject = sfstropen()))
5014 error(ERROR_SYSTEM|3, "out of space [subject]");
5015 if (state.package)
5016 sfputr(subject, state.package, ' ');
5017 if (state.title)
5018 sfputr(subject, state.title, -1);
5019 else
5020 {
5021 if (state.input)
5022 {
5023 if (t = (unsigned char*)strrchr(state.input, '/'))
5024 t++;
5025 else
5026 t = (unsigned char*)state.input;
5027 sfputr(subject, (char*)t, -1);
5028 }
5029 if (state.macros)
5030 sfprintf(subject, " m%s document", state.macros->name);
5031 }
5032 sfputr(op, use(subject), -1);
5033 tag(op, END(OP_title), STACK|LINE, 0, NiL, 0);
5034 if (state.author)
5035 sfprintf(op, "<AUTHOR>%s</AUTHOR>\n", state.author);
5036 if (!state.head)
5037 {
5038 tag(op, END(OP_head), STACK|LINE, 0, NiL, 0);
5039 tag(op, OP_body, STACK|LINE, 0, NiL, 0);
5040 }
5041 for (;;)
5042 {
5043 switch (*s++)
5044 {
5045 case 0:
5046 break;
5047 case '&':
5048 DATA();
5049 col++;
5050 sfputr(op, "&", -1);
5051 continue;
5052 case '<':
5053 DATA();
5054 col++;
5055 sfputr(op, "<", -1);
5056 continue;
5057 case '>':
5058 DATA();
5059 col++;
5060 sfputr(op, ">", -1);
5061 continue;
5062 case CODE_0:
5063 continue;
5064 case CODE_2:
5065 c = *s++;
5066 /*FALLTHROUGH*/
5067 case CODE_1:
5068 if (!nf)
5069 {
5070 n = *s ^ OP_END;
5071 for (v = s + 1;;)
5072 {
5073 switch (*v++)
5074 {
5075 case '\n':
5076 case ' ':
5077 case '\t':
5078 continue;
5079 case CODE_0:
5080 continue;
5081 case CODE_1:
5082 if ((m = *v++) == n)
5083 {
5084 n = 0;
5085 s = v;
5086 break;
5087 }
5088 else if (m != OP_br && m != OP_p)
5089 break;
5090 continue;
5091 case CODE_2:
5092 if (*v++ != ' ' || *v++ != OP_cc)
5093 break;
5094 continue;
5095 default:
5096 break;
5097 }
5098 break;
5099 }
5100 if (!n)
5101 continue;
5102 }
5103 switch (m = *s++)
5104 {
5105 case END(OP_a):
5106 tag(op, m, STACK, 0, NiL, 0);
5107 *--s = a[1];
5108 *--s = a[0];
5109 break;
5110 case OP_body:
5111 tag(op, m, STACK|LINE, 0, NiL, 0);
5112 tag(op, OP_hr, LINE, 0, NiL, 0);
5113 col = 0;
5114 goto compact;
5115 case OP_br:
5116 while (*s == ' ')
5117 s++;
5118 if (!li)
5119 {
5120 if (nf)
5121 goto compact;
5122 else
5123 br++;
5124 }
5125 break;
5126 case OP_cc:
5127 DATA();
5128 sfputc(op, '&');
5129 switch (c)
5130 {
5131 case '&':
5132 sfputr(op, "amp", -1);
5133 break;
5134 case '<':
5135 sfputr(op, "lt", -1);
5136 break;
5137 case '>':
5138 sfputr(op, "gt", -1);
5139 break;
5140 case ' ':
5141 sfputr(op, "nbsp", -1);
5142 break;
5143 default:
5144 sfprintf(op, "#%03d", c);
5145 break;
5146 }
5147 sfputc(op, ';');
5148 col++;
5149 break;
5150 case OP_center:
5151 if (col)
5152 {
5153 col = 0;
5154 sfputc(op, '\n');
5155 }
5156 tag(op, m, STACK|c, 0, NiL, 0);
5157 if (!state.head)
5158 {
5159 tag(op, OP_h2, STACK, 0, NiL, 0);
5160 for (;;)
5161 {
5162 switch (n = *s++)
5163 {
5164 case 0:
5165 case '\n':
5166 case CODE_1:
5167 case CODE_2:
5168 case CODE_n:
5169 s--;
5170 break;
5171 default:
5172 sfputc(op, n);
5173 continue;
5174 }
5175 break;
5176 }
5177 tag(op, END(OP_h2), STACK, 0, NiL, 0);
5178 tag(op, OP_h4, STACK, 0, NiL, 0);
5179 }
5180 break;
5181 case END(OP_center):
5182 col = 0;
5183 if (!state.head)
5184 {
5185 state.head = 1;
5186 tag(op, END(OP_h4), STACK, 0, NiL, 0);
5187 }
5188 tag(op, m, STACK, 0, NiL, 0);
5189 break;
5190 case LABEL(OP_dd):
5191 tag(op, END(OP_a), STACK, 0, NiL, 0);
5192 tag(op, END(OP_h3), STACK, 0, NiL, 0);
5193 /*FALLTHROUGH*/
5194 case OP_dd:
5195 li = 1;
5196 if (col)
5197 {
5198 col = 0;
5199 sfputc(op, '\n');
5200 }
5201 tag(op, OP_dd, 0, 0, NiL, 0);
5202 break;
5203 case OP_div:
5204 if (col)
5205 {
5206 col = 0;
5207 sfputc(op, '\n');
5208 }
5209 tag(op, m, STACK|c, 0, NiL, 0);
5210 break;
5211 case END(OP_div):
5212 col = 0;
5213 tag(op, m, STACK, 0, NiL, 0);
5214 break;
5215 case OP_dl:
5216 li = 0;
5217 if (col)
5218 {
5219 col = 0;
5220 sfputc(op, '\n');
5221 }
5222 tag(op, m, STACK|LINE|ARG_compact, 0, NiL, 0);
5223 break;
5224 case END(OP_dl):
5225 v = s;
5226 for (;;)
5227 {
5228 switch (*v++)
5229 {
5230 case CODE_0:
5231 continue;
5232 case CODE_1:
5233 switch (*v++)
5234 {
5235 case OP_dl:
5236 *(v - 2) = *(v - 1) = CODE_0;
5237 v = 0;
5238 break;
5239 default:
5240 continue;
5241 }
5242 break;
5243 case CODE_2:
5244 v += 2;
5245 continue;
5246 default:
5247 break;
5248 }
5249 break;
5250 }
5251 if (v)
5252 {
5253 li = 0;
5254 col = 0;
5255 tag(op, m, STACK|LINE, 0, NiL, 0);
5256 }
5257 break;
5258 case LABEL(OP_dt):
5259 label = 1;
5260 n = 1;
5261 /*FALLTHROUGH*/
5262 case OP_dt:
5263 if (col)
5264 {
5265 col = 0;
5266 sfputc(op, '\n');
5267 }
5268 if (p)
5269 {
5270 P();
5271 tag(op, OP_p, LINE, 0, NiL, 0);
5272 }
5273 v = s;
5274 for (;;)
5275 {
5276 switch (*v++)
5277 {
5278 case CODE_0:
5279 continue;
5280 case CODE_2:
5281 v++;
5282 /*FALLTHROUGH*/
5283 case CODE_1:
5284 switch (*v++)
5285 {
5286 case OP_dl:
5287 *(v - 2) = *(v - 1) = CODE_0;
5288 tag(op, OP_dl, STACK|LINE|ARG_compact, 0, NiL, 0);
5289 if (!label)
5290 break;
5291 continue;
5292 case OP_dd:
5293 if (label)
5294 *(v - 1) = LABEL(OP_dd);
5295 break;
5296 default:
5297 continue;
5298 }
5299 break;
5300 case ' ':
5301 case '\t':
5302 case '\n':
5303 if (!label)
5304 break;
5305 if (!n)
5306 {
5307 n = 1;
5308 sfputc(state.tmp, ' ');
5309 }
5310 continue;
5311 case '"':
5312 if (!label)
5313 break;
5314 continue;
5315 default:
5316 if (!label)
5317 break;
5318 n = 0;
5319 sfputc(state.tmp, *(v - 1));
5320 continue;
5321 }
5322 break;
5323 }
5324 li = 2;
5325 tag(op, OP_dt, LINE, 0, NiL, 0);
5326 if (label)
5327 {
5328 label = 0;
5329 n = sfstrtell(state.tmp);
5330 v = (unsigned char*)use(state.tmp);
5331 while (--n > 0 && (isspace(v[n]) || v[n] == '.'));
5332 v[n + 1] = 0;
5333 if (isdigit(*v))
5334 tag(op, OP_hr, LINE, 0, NiL, 0);
5335 tag(op, OP_h3, STACK, 0, NiL, 0);
5336 tag(op, OP_a, STACK, ATT_name, (char*)v, 0);
5337 }
5338 break;
5339 case END(OP_fn):
5340 tag(op, m, STACK, 0, NiL, 0);
5341 break;
5342 case OP_ft1:
5343 case OP_ft2:
5344 case OP_ft3:
5345 case OP_ft4:
5346 case OP_ft5:
5347 if (hot)
5348 {
5349 int ob = 0;
5350 int cb = 0;
5351 int p = 0;
5352 int r = ATT_lref;
5353 int z = 0;
5354
5355 v = s;
5356 for (;;)
5357 {
5358 switch (n = *v++)
5359 {
5360 case 0:
5361 hot = 0;
5362 break;
5363 case CODE_0:
5364 continue;
5365 case CODE_2:
5366 v++;
5367 /*FALLTHROUGH*/
5368 case CODE_1:
5369 n = *v++;
5370 if (!p && n >= OP_ft1 && n <= OP_ft5)
5371 p = -1;
5372 continue;
5373 case '(':
5374 case '[':
5375 case '{':
5376 case '<':
5377 if (n == ob)
5378 p++;
5379 else if (p <= 0)
5380 {
5381 r = ATT_href;
5382 p = 1;
5383 switch (ob = n)
5384 {
5385 case '(':
5386 cb = ')';
5387 break;
5388 case '[':
5389 cb = ']';
5390 break;
5391 case '{':
5392 cb = '}';
5393 break;
5394 case '<':
5395 cb = '>';
5396 break;
5397 }
5398 }
5399 z = 0;
5400 sfputc(state.tmp, n);
5401 continue;
5402 case ')':
5403 case ']':
5404 case '}':
5405 case '>':
5406 if (p <= 0)
5407 {
5408 v--;
5409 break;
5410 }
5411 z = 0;
5412 sfputc(state.tmp, n);
5413 if (n == cb && !--p)
5414 break;
5415 continue;
5416 case ' ':
5417 case '\t':
5418 case '\n':
5419 if (!z)
5420 {
5421 z = 1;
5422 sfputc(state.tmp, ' ');
5423 }
5424 continue;
5425 case '"':
5426 continue;
5427 default:
5428 if (p < 0)
5429 {
5430 v--;
5431 break;
5432 }
5433 z = 0;
5434 sfputc(state.tmp, n);
5435 continue;
5436 }
5437 break;
5438 }
5439 n = sfstrtell(state.tmp);
5440 if (!*(t = (unsigned char*)use(state.tmp)))
5441 hot = 0;
5442 if (hot)
5443 {
5444 hot = 0;
5445 while (--n > 0 && (isspace(t[n]) || t[n] == '.'));
5446 t[n + 1] = 0;
5447 tag(op, OP_a, STACK, r, (char*)t, 0);
5448 a[0] = v[0];
5449 v[0] = CODE_1;
5450 a[1] = v[1];
5451 v[1] = END(OP_a);
5452 }
5453 }
5454 c = m - OP_ft;
5455 if (c != ft)
5456 {
5457 peeklist(op, (char*)s);
5458 if (ft != 1)
5459 tag(op, END(OP_ft + ft), STACK, 0, NiL, 0);
5460 ft = c;
5461 if (ft != 1)
5462 tag(op, OP_ft + ft, STACK, 0, NiL, 0);
5463 }
5464 break;
5465 case OP_h2:
5466 case OP_h3:
5467 case OP_h4:
5468 case END(OP_h2):
5469 case END(OP_h3):
5470 case END(OP_h4):
5471 tag(op, m, STACK, 0, NiL, 0);
5472 break;
5473 case OP_head:
5474 case END(OP_head):
5475 col = 0;
5476 tag(op, m, STACK|LINE, 0, NiL, 0);
5477 goto compact;
5478 case OP_hr:
5479 col = 0;
5480 tag(op, OP_hr, LINE, 0, NiL, 0);
5481 goto compact;
5482 case OP_p:
5483 while (*s == ' ')
5484 s++;
5485 if (c)
5486 {
5487 if (col)
5488 {
5489 col = 0;
5490 sfputc(op, '\n');
5491 }
5492 P();
5493 tag(op, m, STACK|c, 0, NiL, 0);
5494 }
5495 else if (!li)
5496 {
5497 if (nf)
5498 goto compact;
5499 else
5500 p++;
5501 }
5502 break;
5503 case END(OP_p):
5504 col = 0;
5505 tag(op, m, STACK, 0, NiL, 0);
5506 break;
5507 case OP_pre:
5508 if (!nf)
5509 {
5510 nf = 03;
5511 tag(op, m, STACK, 0, NiL, 0);
5512 }
5513 goto compact;
5514 case END(OP_pre):
5515 if (nf)
5516 {
5517 nf = 02;
5518 tag(op, m, STACK, 0, NiL, 0);
5519 }
5520 goto compact;
5521 case OP_ps:
5522 if (c != ps)
5523 {
5524 peeklist(op, (char*)s);
5525 if (ps_set)
5526 {
5527 ps = ps_set;
5528 ps_set = 0;
5529 tag(op, END(OP_ps), STACK, 0, NiL, 0);
5530 }
5531 if (n = c - ps)
5532 {
5533 ps_set = ps;
5534 ps = c;
5535 tag(op, OP_ps, STACK, ATT_size, NiL, n);
5536 }
5537 }
5538 break;
5539 case OP_sub:
5540 case END(OP_sub):
5541 case OP_sup:
5542 case END(OP_sup):
5543 tag(op, m, STACK, 0, NiL, 0);
5544 break;
5545 case OP_tab:
5546 if (col)
5547 {
5548 col = 0;
5549 sfputc(op, '\n');
5550 }
5551 tag(op, m, STACK|c, 0, NiL, 0);
5552 goto compact;
5553 case END(OP_tab):
5554 tag(op, m, STACK|LINE, 0, NiL, 0);
5555 break;
5556 case OP_tab_data:
5557 case OP_tab_head:
5558 case OP_tab_row:
5559 tag(op, m, c, 0, NiL, 0);
5560 break;
5561 default:
5562 if (!(v = (unsigned char*)tag_name[OP(m)]))
5563 {
5564 sfprintf(state.tmp, "(%d)", OP(m));
5565 v = (unsigned char*)use(state.tmp);
5566 }
5567 error(2, "internal error: <%s%s%s %d> ignored", (m & OP_END) ? "/" : "", v, (m & OP_LABEL) ? " label=" : "", c);
5568 break;
5569 compact:
5570 if (col)
5571 sfputc(op, '\n');
5572 P();
5573 v = s;
5574 for (;;)
5575 {
5576 switch (*s++)
5577 {
5578 case 0:
5579 break;
5580 case '\n':
5581 p++;
5582 col = 0;
5583 v = s;
5584 continue;
5585 case ' ':
5586 case '\t':
5587 if (!nf)
5588 break;
5589 col = 1;
5590 continue;
5591 case CODE_0:
5592 continue;
5593 case CODE_1:
5594 switch (*s)
5595 {
5596 case OP_pre:
5597 if (!nf)
5598 break;
5599 /*FALLTHROUGH*/
5600 case OP_br:
5601 case OP_p:
5602 s++;
5603 p++;
5604 col = 0;
5605 v = s;
5606 continue;
5607 case OP_body:
5608 p = 0;
5609 col = 0;
5610 break;
5611 }
5612 break;
5613 }
5614 if (col)
5615 {
5616 col = 0;
5617 s = v;
5618 }
5619 else
5620 s--;
5621 break;
5622 }
5623 if (nf > 1)
5624 nf &= 01;
5625 else if (nf && p)
5626 sfputc(op, '\n');
5627 p = 0;
5628 break;
5629 }
5630 c = 0;
5631 continue;
5632 case CODE_n:
5633 n = *s++;
5634 if (n & 0200)
5635 {
5636 n = (n & 0177) << 8;
5637 n |= *s++;
5638 }
5639 c = *s++;
5640 v = s;
5641 s += n;
5642 switch (c)
5643 {
5644 case OP_comment:
5645 if (col)
5646 {
5647 col = 0;
5648 sfputc(op, '\n');
5649 }
5650 sfprintf(op, "<!--\"%s\"-->\n", v);
5651 break;
5652 case OP_fn:
5653 tag(op, c, STACK, ATT_id, (char*)v, 0);
5654 break;
5655 case OP_link:
5656 DATA();
5657 if (u = (unsigned char*)strchr((char*)v, '\t'))
5658 *u++ = 0;
5659 else
5660 u = 0;
5661 t = v;
5662 while (isdigit(*v))
5663 v++;
5664 while (isalpha(*v))
5665 v++;
5666 if (*v == ':' || *v == '/' || *v == '.' || *(v + 1) == '/')
5667 {
5668 if (!u)
5669 u = v + 1;
5670 v = (unsigned char*)"";
5671 }
5672 else
5673 {
5674 if (!u)
5675 u = t;
5676 v = (unsigned char*)"#";
5677 }
5678 sfprintf(op, "<A href=\"%s%s\">%s</A>", v, t, u);
5679 break;
5680 case LABEL(OP_link):
5681 DATA();
5682 if (u = (unsigned char*)strchr((char*)v, '\t'))
5683 *u++ = 0;
5684 else
5685 u = v;
5686 sfprintf(op, "<A name=\"%s\">%s</A>", v, u);
5687 break;
5688 case OP_ta:
5689 strcpy((char*)state.ta, (char*)v);
5690 break;
5691 case OP_RAW:
5692 DATA();
5693 if (col)
5694 {
5695 col = 0;
5696 sfputc(op, '\n');
5697 }
5698 sfputr(op, (char*)v, '\n');
5699 break;
5700 }
5701 continue;
5702 case ' ':
5703 if (nf)
5704 {
5705 col++;
5706 sfputc(op, *(s - 1));
5707 }
5708 else
5709 {
5710 while (isspace(*s))
5711 s++;
5712 if (col >= 70)
5713 {
5714 col = 0;
5715 sfputc(op, '\n');
5716 }
5717 else if (col > 0)
5718 {
5719 col++;
5720 sfputc(op, ' ');
5721 }
5722 }
5723 continue;
5724 case '\t':
5725 if (nf)
5726 {
5727 ta = state.ta[ts = 0];
5728 while (col >= ta)
5729 {
5730 if (state.ta[ts+1])
5731 ts++;
5732 ta += state.ta[ts];
5733 }
5734 do
5735 {
5736 sfputc(op, ' ');
5737 } while (++col < ta);
5738 }
5739 else
5740 {
5741 col++;
5742 sfputc(op, '\t');
5743 }
5744 continue;
5745 case '\n':
5746 if (nf)
5747 {
5748 v = s;
5749 col = 0;
5750 for (;;)
5751 {
5752 switch (*v++)
5753 {
5754 case 0:
5755 break;
5756 case '\n':
5757 continue;
5758 case ' ':
5759 case '\t':
5760 continue;
5761 case CODE_0:
5762 continue;
5763 case CODE_1:
5764 switch (*v++)
5765 {
5766 case OP_br:
5767 case OP_p:
5768 case OP_pre:
5769 continue;
5770 case END(OP_pre):
5771 col = 1;
5772 s = v - 2;
5773 break;
5774 }
5775 break;
5776 }
5777 break;
5778 }
5779 if (col)
5780 {
5781 col = 0;
5782 continue;
5783 }
5784 sfputc(op, '\n');
5785 }
5786 else
5787 {
5788 while (isspace(*s))
5789 s++;
5790 if (col >= 70)
5791 {
5792 col = 0;
5793 sfputc(op, '\n');
5794 }
5795 else if (col > 0)
5796 {
5797 col++;
5798 sfputc(op, ' ');
5799 }
5800 }
5801 continue;
5802 case '(':
5803 if (hot)
5804 hot = 0;
5805 else
5806 for (x = state.hot; x; x = x->next)
5807 {
5808 v = s;
5809 u = (unsigned char*)x->name;
5810 do
5811 {
5812 if (!*u)
5813 {
5814 if (isspace(*v))
5815 {
5816 hot = 1;
5817 goto data;
5818 }
5819 break;
5820 }
5821 } while (*u++ == *v++);
5822 }
5823 goto data;
5824 case ')':
5825 hot = 0;
5826 goto data;
5827 case '.':
5828 if (!nf && isspace(*s))
5829 {
5830 while (isspace(*s))
5831 s++;
5832 col = 0;
5833 sfputc(op, '.');
5834 sfputc(op, '\n');
5835 continue;
5836 }
5837 /*FALLTHROUGH*/
5838 default:
5839 data:
5840 DATA();
5841 col++;
5842 sfputc(op, *(s - 1));
5843 continue;
5844 }
5845 break;
5846 }
5847 if (col)
5848 sfputc(op, '\n');
5849 if (state.mailto)
5850 sfprintf(op, "<P>Send comments and suggestions to <A href=\"mailto:%s?subject=%s\">%s</A>.\n", state.mailto, state.mailto, sfstrbase(subject));
5851 if (state.author || state.corporation || state.company || state.location)
5852 {
5853 t = (unsigned char*)"<P>";
5854 u = (unsigned char*)"<BR>";
5855 if (state.author)
5856 {
5857 sfprintf(op, "%s%s\n", t, state.author);
5858 t = u;
5859 }
5860 if (state.organization)
5861 {
5862 sfprintf(op, "%s%s\n", t, state.organization);
5863 t = u;
5864 }
5865 if (state.corporation || state.company)
5866 {
5867 sfputr(op, (char*)t, -1);
5868 t = u;
5869 if (state.corporation)
5870 sfputr(op, state.corporation, state.company ? ' ' : '\n');
5871 if (state.company)
5872 sfputr(op, state.company, '\n');
5873 }
5874 if (state.address)
5875 {
5876 sfprintf(op, "%s%s\n", t, state.address);
5877 t = u;
5878 }
5879 if (state.location)
5880 {
5881 sfprintf(op, "%s%s\n", t, state.location);
5882 t = u;
5883 }
5884 if (state.phone)
5885 {
5886 sfprintf(op, "%s%s\n", t, state.phone);
5887 t = u;
5888 }
5889 }
5890 sfstrclose(subject);
5891 if (!state.footer)
5892 sfprintf(op, "<P>%s\n", fmttime("%B %d, %Y", state.date));
5893 if (state.toolbar && (subject = find(state.toolbar, NiL, 1)))
5894 {
5895 sfmove(subject, sfstdout, SF_UNBOUND, -1);
5896 sfclose(subject);
5897 }
5898 tag(op, END(OP_body), STACK, 0, NiL, 0);
5899 tag(op, END(OP_html), STACK, 0, NiL, 0);
5900 sfputc(op, '\n');
5901 }
5902
5903 int
main(int argc,char ** argv)5904 main(int argc, char** argv)
5905 {
5906 register int n;
5907 register char* s;
5908 char* v;
5909 Dir_t* x;
5910 Dir_t* lastdir;
5911 Dir_t* lastmac;
5912 Sfio_t* ip;
5913 Sfio_t* op;
5914 Sfio_t* script;
5915
5916 NoP(argc);
5917 error_info.id = "troff2html";
5918 state.dirs = lastdir = ˙
5919 init();
5920 script = 0;
5921 for (;;)
5922 {
5923 switch (optget(argv, usage))
5924 {
5925 case 0:
5926 break;
5927 case 'i':
5928 if (!(op = sfopen(NiL, opt_info.arg, "r")))
5929 error(ERROR_SYSTEM|2, "%s: cannot read", opt_info.arg);
5930 else
5931 {
5932 if (!(s = sfreserve(op, SF_UNBOUND, 0)) || (n = sfvalue(op)) <= 0 || s[n - 1] != '\n')
5933 error(1, "%s: invalid info file", opt_info.arg);
5934 else
5935 {
5936 s[n] = 0;
5937 stropt(s, options, sizeof(*options), setopt, NiL);
5938 }
5939 sfclose(op);
5940 }
5941 continue;
5942 case 'm':
5943 if (!(x = newof(0, Dir_t, 1, 0)))
5944 error(ERROR_SYSTEM|3, "out of space [macros]");
5945 x->name = opt_info.arg;
5946 if (state.macros)
5947 lastmac = lastmac->next = x;
5948 else
5949 state.macros = lastmac = x;
5950 continue;
5951 case 'r':
5952 if (*(s = opt_info.arg))
5953 {
5954 opt_info.num = expression(s + 1, NiL, 0);
5955 s[1] = 0;
5956 nr(s, opt_info.num, 0, 0);
5957 }
5958 continue;
5959 case 's':
5960 if (!script && !(script = sfstropen()))
5961 error(ERROR_SYSTEM|3, "out of space [script]");
5962 sfputr(script, opt_info.arg, '\n');
5963 continue;
5964 case 'v':
5965 state.verbose = 1;
5966 continue;
5967 case 'I':
5968 if (streq(opt_info.arg, "-"))
5969 dot.name[0] = 0;
5970 else if (!(x = newof(0, Dir_t, 1, 0)))
5971 error(ERROR_SYSTEM|3, "out of space [dir]");
5972 else
5973 {
5974 x->name = opt_info.arg;
5975 lastdir = lastdir->next = x;
5976 }
5977 continue;
5978 case '?':
5979 error(ERROR_USAGE|4, "%s", opt_info.arg);
5980 continue;
5981 case ':':
5982 if (opt_info.name[1] != '-')
5983 {
5984 error(2, "%s", opt_info.arg);
5985 continue;
5986 }
5987 if (!(v = strchr(argv[opt_info.index - 1], '=')))
5988 v = opt_info.name + 2;
5989 else if (!(v = sfprints("%s=%s", opt_info.name + 2, fmtquote(v + 1, "\"", "\"", strlen(v + 1), FMT_ALWAYS))))
5990 error(ERROR_SYSTEM|3, "out of space");
5991 stropt(v, options, sizeof(*options), setopt, options);
5992 continue;
5993 }
5994 break;
5995 }
5996 if (!dot.name[0])
5997 state.dirs = state.dirs->next;
5998 argv += opt_info.index;
5999 if (error_info.errors)
6000 error(ERROR_USAGE|4, "%s", optusage(NiL));
6001 if (!(op = sfstropen()))
6002 error(ERROR_SYSTEM|3, "out of space [output]");
6003 if (script)
6004 {
6005 pushin("script", 1, NiL, use(script), NiL);
6006 process(NiL, NiL, op);
6007 sfstrclose(script);
6008 }
6009 for (x = state.macros; x; x = x->next)
6010 if (ip = find(x->name, &v, -1))
6011 process(v, ip, op);
6012 if (!(state.input = *argv))
6013 process(NiL, sfstdin, op);
6014 else
6015 while (s = *argv++)
6016 if (ip = find(s, &v, 1))
6017 process(v, ip, op);
6018 if (state.out)
6019 {
6020 if (state.it.center)
6021 {
6022 state.it.center = 0;
6023 code_1(END(OP_center));
6024 }
6025 while (state.list > state.list_stack)
6026 {
6027 if (state.list->dl)
6028 code_1(END(OP_dl));
6029 state.list--;
6030 }
6031 code_1(OP_hr);
6032 trigger(&state.fini);
6033 process(NiL, NiL, op);
6034 html((unsigned char*)use(op), sfstdout);
6035 }
6036 exit(error_info.errors != 0);
6037 }
6038