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, "&amp;", -1);
5051 			continue;
5052 		case '<':
5053 			DATA();
5054 			col++;
5055 			sfputr(op, "&lt;", -1);
5056 			continue;
5057 		case '>':
5058 			DATA();
5059 			col++;
5060 			sfputr(op, "&gt;", -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 = &dot;
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