1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-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 <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24  * Glenn Fowler
25  * AT&T Research
26  *
27  * command line option parser and usage formatter
28  * its a monster but its all in one place
29  * widen your window while you're at it
30  */
31 
32 #include <optlib.h>
33 #include <debug.h>
34 #include <ccode.h>
35 #include <ctype.h>
36 #include <errno.h>
37 
38 #define KEEP		"*[A-Za-z][A-Za-z]*"
39 #define OMIT		"*@(\\[[-+]*\\?*\\]|\\@\\(#\\)|Copyright \\(c\\)|\\$\\I\\d\\: )*"
40 
41 #define GO		'{'		/* group nest open		*/
42 #define OG		'}'		/* group nest close		*/
43 
44 #define OPT_WIDTH	80		/* default help text width	*/
45 #define OPT_MARGIN	10		/* default help text margin	*/
46 #define OPT_USAGE	7		/* usage continuation indent	*/
47 
48 #define OPT_flag	0x001		/* flag ( 0 or 1 )		*/
49 #define OPT_hidden	0x002		/* remaining are hidden		*/
50 #define OPT_ignorecase	0x004		/* arg match ignores case	*/
51 #define OPT_invert	0x008		/* flag inverts long sense	*/
52 #define OPT_listof	0x010		/* arg is ' ' or ',' list	*/
53 #define OPT_number	0x020		/* arg is strtonll() number	*/
54 #define OPT_oneof	0x040		/* arg may be set once		*/
55 #define OPT_optional	0x080		/* arg is optional		*/
56 #define OPT_string	0x100		/* arg is string		*/
57 
58 #define OPT_preformat	0001		/* output preformat string	*/
59 #define OPT_proprietary	0002		/* proprietary docs		*/
60 
61 #define OPT_TYPE	(OPT_flag|OPT_number|OPT_string)
62 
63 #define STYLE_posix	0		/* posix getopt usage		*/
64 #define STYLE_short	1		/* [default] short usage	*/
65 #define STYLE_long	2		/* long usage			*/
66 #define STYLE_match	3		/* long description of matches	*/
67 #define STYLE_options	4		/* short and long descriptions	*/
68 #define STYLE_man	5		/* pretty details		*/
69 #define STYLE_html	6		/* html details			*/
70 #define STYLE_nroff	7		/* nroff details		*/
71 #define STYLE_api	8		/* program details		*/
72 #define STYLE_keys	9		/* translation key strings	*/
73 #define STYLE_usage	10		/* escaped usage string		*/
74 
75 #define FONT_BOLD	1
76 #define FONT_ITALIC	2
77 #define FONT_LITERAL	4
78 
79 #define HELP_head	0x01
80 #define HELP_index	0x02
81 
82 #define TAG_NONE	0
83 #define TAG_DIV		1
84 #define TAG_DL		2
85 
86 #define SEP(c)		((c)=='-'||(c)=='_')
87 
88 typedef struct Attr_s
89 {
90 	const char*	name;
91 	int		flag;
92 } Attr_t;
93 
94 typedef struct Help_s
95 {
96 	const char*	match;		/* builtin help match name	*/
97 	const char*	name;		/* builtin help name		*/
98 	int		style;		/* STYLE_*			*/
99 	const char*	text;		/* --? text			*/
100 	unsigned int	size;		/* strlen text			*/
101 } Help_t;
102 
103 typedef struct Font_s
104 {
105 	const char*	html[2];
106 	const char*	nroff[2];
107 	const char*	term[2];
108 } Font_t;
109 
110 typedef struct List_s
111 {
112 	int		type;		/* { - + : }			*/
113 	const char*	name;		/* list name			*/
114 	const char*	text;		/* help text			*/
115 } List_t;
116 
117 typedef struct Msg_s
118 {
119 	const char*	text;		/* default message text		*/
120 	Dtlink_t	link;		/* cdt link			*/
121 } Msg_t;
122 
123 typedef struct Save_s
124 {
125 	Dtlink_t	link;		/* cdt link			*/
126 	char		text[1];	/* saved text text		*/
127 } Save_t;
128 
129 typedef struct Push_s
130 {
131 	struct Push_s*	next;		/* next string			*/
132 	char*		ob;		/* next char in old string	*/
133 	char*		oe;		/* end of old string		*/
134 	char*		nb;		/* next char in new string	*/
135 	char*		ne;		/* end of new string		*/
136 	int		ch;		/* localize() translation	*/
137 } Push_t;
138 
139 typedef struct Tag_s
140 {
141 	unsigned char	level;		/* indent level			*/
142 	unsigned char	id;		/* TAG_* id			*/
143 } Tag_t;
144 
145 typedef struct Indent_s
146 {
147 	int		stop;		/* tab column position		*/
148 } Indent_t;
149 
150 static Indent_t		indent[] =
151 {
152 	0,2,	4,10,	12,18,	20,26,	28,34,	36,42,	44,50,	0,0
153 };
154 
155 static const char*	end[] =
156 {
157 	"", "</DIV>\n", "</DL>\n"
158 };
159 
160 static const char	term_off[] =	{CC_esc,'[','0','m',0};
161 static const char	term_B_on[] =	{CC_esc,'[','1','m',0};
162 static const char	term_I_on[] =	{CC_esc,'[','1',';','4','m',0};
163 
164 static const Font_t	fonts[] =
165 {
166 	"",	"",	"",	"",	"",			"",
167 	"</B>",	"<B>", "\\fP",	"\\fB",	&term_off[0],	&term_B_on[0],
168 	"</I>",	"<I>", "\\fP",	"\\fI",	&term_off[0],	&term_I_on[0],
169 	"",	"",	"",	"",	"",			"",
170 	"</TT>","<TT>","\\fP",	"\\f5",	"",			"",
171 };
172 
173 static char		native[] = "";
174 
175 static unsigned char	map[UCHAR_MAX];
176 
177 static Optstate_t	state;
178 
179 #if !_PACKAGE_astsa
180 
181 #define ID		ast.id
182 
183 #define C(s)		ERROR_catalog(s)
184 #define D(s)		(state.msgdict && dtmatch(state.msgdict, (s)))
185 #define T(i,c,m)	(X(c)?translate(i,c,C(m)):(m))
186 #define X(c)		(ERROR_translating()&&(c)!=native)
187 #define Z(x)		C(x),sizeof(x)-1
188 
189 /*
190  * translate with C_LC_MESSAGES_libast[] check
191  */
192 
193 static char*
translate(const char * cmd,const char * cat,const char * msg)194 translate(const char* cmd, const char* cat, const char* msg)
195 {
196 	if (!X(cat))
197 		return (char*)msg;
198 	if (cat != (const char*)ID && D(msg))
199 		cat = (const char*)ID;
200 	return errorx(NiL, cmd, cat, msg);
201 }
202 
203 #else
204 
205 static char		ID[] = "ast";
206 
207 #define C(s)		s
208 #define D(s)		(state.msgdict && dtmatch(state.msgdict, (s)))
209 #define T(i,c,m)	m
210 #define X(c)		0
211 #define Z(x)		C(x),sizeof(x)-1
212 
213 #endif
214 
215 static const List_t	help_head[] =
216 {
217 	'-',	0,
218 		0,
219 	'+',	C("NAME"),
220 		C("options available to all \bast\b commands"),
221 	'+',	C("DESCRIPTION"),
222 		C("\b-?\b and \b--?\b* options are the same \
223 for all \bast\b commands. For any \aitem\a below, if \b--\b\aitem\a is not \
224 supported by a given command then it is equivalent to \b--\?\?\b\aitem\a. The \
225 \b--\?\?\b form should be used for portability. All output is written to the \
226 standard error."),
227 };
228 
229 static const Help_t	styles[] =
230 {
231 	C("about"),	"-",		STYLE_match,
232 	Z("List all implementation info."),
233 	C("api"),	"?api",		STYLE_api,
234 	Z("List detailed info in program readable form."),
235 	C("help"),	"",		-1,
236 	Z("List detailed help option info."),
237 	C("html"),	"?html",	STYLE_html,
238 	Z("List detailed info in html."),
239 	C("keys"),	"?keys",	STYLE_keys,
240 	Z("List the usage translation key strings with C style escapes."),
241 	C("long"),	"?long",	STYLE_long,
242 	Z("List long option usage."),
243 	C("man"),	"?man",		STYLE_man,
244 	Z("List detailed info in displayed man page form."),
245 	C("nroff"),	"?nroff",	STYLE_nroff,
246 	Z("List detailed info in nroff."),
247 	C("options"),	"?options",	STYLE_options,
248 	Z("List short and long option details."),
249 	C("posix"),	"?posix",	STYLE_posix,
250 	Z("List posix getopt usage."),
251 	C("short"),	"?short",	STYLE_short,
252 	Z("List short option usage."),
253 	C("usage"),	"?usage",	STYLE_usage,
254 	Z("List the usage string with C style escapes."),
255 };
256 
257 static const List_t	help_tail[] =
258 {
259 	':',	C("\?\?-\alabel\a"),
260 		C("List implementation info matching \alabel\a*."),
261 	':',	C("\?\?\aname\a"),
262 		C("Equivalent to \b--help=\b\aname\a."),
263 	':',	C("\?\?"),
264 		C("Equivalent to \b--\?\?options\b."),
265 	':',	C("\?\?\?\?"),
266 		C("Equivalent to \b--\?\?man\b."),
267 	':',	C("\?\?\?\?\?\?"),
268 		C("Equivalent to \b--\?\?help\b."),
269 	':',	C("\?\?\?\?\?\?\aitem\a"),
270 		C("If the next argument is \b--\b\aoption\a then list \
271 the \aoption\a output in the \aitem\a style. Otherwise print \
272 \bversion=\b\an\a where \an\a>0 if \b--\?\?\b\aitem\a is supported, \b0\b \
273 if not."),
274 	':',	C("\?\?\?\?\?\?ESC"),
275 		C("Emit escape codes even if output is not a terminal."),
276 	':',	C("\?\?\?\?\?\?MAN[=\asection\a]]"),
277 		C("List the \bman\b(1) section title for \asection\a [the \
278 current command]]."),
279 	':',	C("\?\?\?\?\?\?SECTION"),
280 		C("List the \bman\b(1) section number for the current command."),
281 	':',	C("\?\?\?\?\?\?TEST"),
282 		C("Massage the output for regression testing."),
283 };
284 
285 static const Attr_t	attrs[] =
286 {
287 	"flag",		OPT_flag,
288 	"hidden",	OPT_hidden,
289 	"ignorecase",	OPT_ignorecase,
290 	"invert",	OPT_invert,
291 	"listof",	OPT_listof,
292 	"number",	OPT_number,
293 	"oneof",	OPT_oneof,
294 	"optional",	OPT_optional,
295 	"string",	OPT_string,
296 };
297 
298 static const char	unknown[] = C("unknown option or attribute");
299 
300 static const char*	heading[] =
301 {
302 	C("INDEX"),
303 	C("USER COMMANDS"),
304 	C("SYSTEM LIBRARY"),
305 	C("USER LIBRARY"),
306 	C("FILE FORMATS"),
307 	C("MISCELLANEOUS"),
308 	C("GAMES and DEMOS"),
309 	C("SPECIAL FILES"),
310 	C("ADMINISTRATIVE COMMANDS"),
311 	C("GUIs"),
312 };
313 
314 /*
315  * list of common man page strings
316  * NOTE: add but do not delete from this table
317  */
318 
319 static Msg_t		C_LC_MESSAGES_libast[] =
320 {
321 	{ C("APPLICATION USAGE") },
322 	{ C("ASYNCHRONOUS EVENTS") },
323 	{ C("BUGS") },
324 	{ C("CAVEATS") },
325 	{ C("CONSEQUENCES OF ERRORS") },
326 	{ C("DESCRIPTION") },
327 	{ C("ENVIRONMENT VARIABLES") },
328 	{ C("EXAMPLES") },
329 	{ C("EXIT STATUS") },
330 	{ C("EXTENDED DESCRIPTION") },
331 	{ C("INPUT FILES") },
332 	{ C("LIBRARY") },
333 	{ C("NAME") },
334 	{ C("OPERANDS") },
335 	{ C("OPTIONS") },
336 	{ C("OUTPUT FILES") },
337 	{ C("PLUGIN") },
338 	{ C("SEE ALSO") },
339 	{ C("STDERR") },
340 	{ C("STDIN") },
341 	{ C("STDOUT") },
342 	{ C("SYNOPSIS") },
343 	{ C("author") },
344 	{ C("copyright") },
345 	{ C("license") },
346 	{ C("name") },
347 	{ C("path") },
348 	{ C("version") },
349 };
350 
351 /*
352  * 2007-03-19 move opt_info from _opt_info_ to (*_opt_data_)
353  *	      to allow future Opt_t growth
354  *            by 2009 _opt_info_ can be static
355  */
356 
357 #if _BLD_ast && defined(__EXPORT__)
358 #define extern		extern __EXPORT__
359 #endif
360 
361 extern Opt_t	_opt_info_;
362 
363 Opt_t		_opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state };
364 
365 #undef	extern
366 
367 __EXTERN__(Opt_t, _opt_info_);
368 
369 __EXTERN__(Opt_t*, _opt_infop_);
370 
371 Opt_t*		_opt_infop_ = &_opt_info_;
372 
373 Optstate_t*
optstate(Opt_t * p)374 optstate(Opt_t* p)
375 {
376 	return &state;
377 }
378 
379 #if DEBUG || _BLD_DEBUG
380 
381 /*
382  * debug usage string segment format
383  */
384 
385 static char*
show(register char * s)386 show(register char* s)
387 {
388 	register int	c;
389 	register char*	t;
390 	register char*	e;
391 
392 	static char	buf[32];
393 
394 	if (!s)
395 		return "(null)";
396 	t = buf;
397 	e = buf + sizeof(buf) - 2;
398 	while (t < e)
399 	{
400 		switch (c = *s++)
401 		{
402 		case 0:
403 			goto done;
404 		case '\a':
405 			*t++ = '\\';
406 			c = 'a';
407 			break;
408 		case '\b':
409 			*t++ = '\\';
410 			c = 'b';
411 			break;
412 		case '\f':
413 			*t++ = '\\';
414 			c = 'f';
415 			break;
416 		case '\n':
417 			*t++ = '\\';
418 			c = 'n';
419 			break;
420 		case '\t':
421 			*t++ = '\\';
422 			c = 't';
423 			break;
424 		case '\v':
425 			*t++ = '\\';
426 			c = 'v';
427 			break;
428 		}
429 		*t++ = c;
430 	}
431  done:
432 	*t = 0;
433 	return buf;
434 }
435 
436 #endif
437 
438 typedef struct Section_s
439 {
440 	const char	section[4];
441 	const char*	name;
442 } Section_t;
443 
444 static const Section_t	sections[] =
445 {
446 	"1M",	"MAKE ASSERTION OPERATORS AND RULES",
447 	"1",	"USER COMMANDS",
448 	"2",	"SYSTEM CALLS",
449 	"3F",	"FORTRAN LIBRARY ROUTINES",
450 	"3K",	"KERNEL VM LIBRARY FUNCTIONS",
451 	"3L",	"LIGHTWEIGHT PROCESSES LIBRARY",
452 	"3M",	"MATHEMATICAL LIBRARY",
453 	"3N",	"NETWORK FUNCTIONS",
454 	"3R",	"RPC SERVICES LIBRARY",
455 	"3S",	"STANDARD I/O FUNCTIONS",
456 	"3V",	"SYSTEM V LIBRARY",
457 	"3",	"C LIBRARY FUNCTIONS",
458 	"4F",	"PROTOCOL FAMILIES",
459 	"4P",	"PROTOCOLS",
460 	"4",	"DEVICES AND NETWORK INTERFACES",
461 	"5P",	"PLUGINS",
462 	"5",	"FILE FORMATS",
463 	"6",	"GAMES AND DEMOS",
464 	"7",	"PUBLIC FILES AND TABLES",
465 	"8",	"ADMINISTRATIVE COMMANDS",
466 	"L",	"LOCAL COMMANDS",
467 };
468 
469 /*
470  * return section name given abbreviation
471  */
472 
473 static char*
secname(char * section)474 secname(char* section)
475 {
476 	int		i;
477 	char*		b;
478 	char*		t;
479 	const char*	s;
480 
481 	b = t = fmtbuf(64);
482 	if (section[1])
483 	{
484 		switch (section[2] ? section[2] : section[1])
485 		{
486 		case 'C':
487 			s = "COMPATIBILITY ";
488 			break;
489 		case 'U':
490 			s = "UWIN ";
491 			break;
492 		case 'X':
493 			s = "MISCELLANEOUS ";
494 			break;
495 		default:
496 			s = 0;
497 			break;
498 		}
499 		if (s)
500 			t = strcopy(t, s);
501 	}
502 	s = 0;
503 	for (i = 0; i < elementsof(sections); i++)
504 		if (section[0] == sections[i].section[0] && (section[1] == sections[i].section[1] || !sections[i].section[1]))
505 		{
506 			s = sections[i].name;
507 			break;
508 		}
509 	if (!s)
510 	{
511 		t = strcopy(t, "SECTION ");
512 		s = section;
513 	}
514 	strcopy(t, s);
515 	return b;
516 }
517 
518 /*
519  * pop the push stack
520  */
521 
522 static Push_t*
pop(register Push_t * psp)523 pop(register Push_t* psp)
524 {
525 	register Push_t*	tsp;
526 
527 	while (tsp = psp)
528 	{
529 		psp = psp->next;
530 		free(tsp);
531 	}
532 	return 0;
533 }
534 
535 /*
536  * skip over line space to the next token
537  */
538 
539 static char*
next(register char * s,int version)540 next(register char* s, int version)
541 {
542 	register char*	b;
543 
544 	while (*s == '\t' || *s == '\r' || version >= 1 && *s == ' ')
545 		s++;
546 	if (*s == '\n')
547 	{
548 		b = s;
549 		while (*++s == ' ' || *s == '\t' || *s == '\r');
550 		if (*s == '\n')
551 			return b;
552 	}
553 	return s;
554 }
555 
556 /*
557  * skip to t1 or t2 or t3, whichever first, in s
558  *	n==0	outside [...]
559  *	n==1	inside [...] before ?
560  *	n==2	inside [...] after ?
561  *	b==0	outside {...}
562  *	b==1	inside {...}
563  * past skips past the terminator to the next token
564  * otherwise a pointer to the terminator is returned
565  *
566  * ]] for ] inside [...]
567  * ?? for ? inside [...] before ?
568  * :: for : inside [...] before ?
569  */
570 
571 static char*
skip(register char * s,register int t1,register int t2,register int t3,register int n,register int b,int past,int version)572 skip(register char* s, register int t1, register int t2, register int t3, register int n, register int b, int past, int version)
573 {
574 	register int	c;
575 	register int	on = n;
576 	register int	ob = b;
577 
578 	if (version < 1)
579 	{
580 		n = n >= 1;
581 		for (;;)
582 		{
583 			switch (*s++)
584 			{
585 			case 0:
586 				break;
587 			case '[':
588 				n++;
589 				continue;
590 			case ']':
591 				if (--n <= 0)
592 					break;
593 				continue;
594 			default:
595 				continue;
596 			}
597 			break;
598 		}
599 	}
600 	else while (c = *s++)
601 	{
602 		message((-22, "optget: skip t1=%c t2=%c t3=%c n=%d b=%d `%s'", t1 ? t1 : '@', t2 ? t2 : '@', t3 ? t3 : '@', n, b, show(s - 1)));
603 		if (c == '[')
604 		{
605 			if (!n)
606 				n = 1;
607 		}
608 		else if (c == ']')
609 		{
610 			if (n)
611 			{
612 				if (*s == ']')
613 					s++;
614 				else if (on == 1)
615 					break;
616 				else
617 					n = 0;
618 			}
619 		}
620 		else if (c == GO)
621 		{
622 			if (n == 0)
623 				b++;
624 		}
625 		else if (c == OG)
626 		{
627 			if (n == 0 && b-- == ob)
628 				break;
629 		}
630 		else if (c == '?')
631 		{
632 			if (n == 1)
633 			{
634 				if (*s == '?')
635 					s++;
636 				else
637 				{
638 					if (n == on && (c == t1 || c == t2 || c == t3))
639 						break;
640 					n = 2;
641 				}
642 			}
643 		}
644 		else if (n == on && (c == t1 || c == t2 || c == t3))
645 		{
646 			if (n == 1 && c == ':' && *s == c)
647 				s++;
648 			else
649 				break;
650 		}
651 	}
652 	return past && *(s - 1) ? next(s, version) : s - 1;
653 }
654 
655 /*
656  * *s points to '(' on input
657  * return is one past matching ')'
658  */
659 
660 static char*
nest(register char * s)661 nest(register char* s)
662 {
663 	int	n;
664 
665 	n = 0;
666 	for (;;)
667 	{
668 		switch (*s++)
669 		{
670 		case '(':
671 			n++;
672 			continue;
673 		case ')':
674 			if (!--n)
675 				break;
676 			continue;
677 		default:
678 			continue;
679 		}
680 		break;
681 	}
682 	return s;
683 }
684 
685 /*
686  * match s with t
687  * t translated if possible
688  * embedded { - _ ' } ignored
689  * * separates required prefix from optional suffix
690  * otherwise prefix match
691  */
692 
693 static int
match(char * s,char * t,int version,const char * id,const char * catalog)694 match(char* s, char* t, int version, const char* id, const char* catalog)
695 {
696 	register char*	w;
697 	register char*	x;
698 	char*		xw;
699 	char*		ww;
700 	int		n;
701 	int		v;
702 	int		j;
703 
704 	for (n = 0; n < 2; n++)
705 	{
706 		if (n)
707 			x = t;
708 		else
709 		{
710 			if (catalog)
711 			{
712 				w = skip(t, ':', '?', 0, 1, 0, 0, version);
713 				w = sfprints("%-.*s", w - t, t);
714 				x = T(id, catalog, w);
715 				if (x == w)
716 					continue;
717 			}
718 			x = T(NiL, ID, t);
719 			if (x == t)
720 				continue;
721 		}
722 		do
723 		{
724 			v = 0;
725 			xw = x;
726 			w = ww = s;
727 			while (*x && *w)
728 			{
729 				if (isupper(*x))
730 					xw = x;
731 				if (isupper(*w))
732 					ww = w;
733 				if (*x == '*' && !v++ || *x == '\a')
734 				{
735 					if (*x == '\a')
736 						do
737 						{
738 							if (!*++x)
739 							{
740 								x--;
741 								break;
742 							}
743 						} while (*x != '\a');
744 					j = *(x + 1);
745 					if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
746 						while (*w)
747 							w++;
748 				}
749 				else if (SEP(*x))
750 					xw = ++x;
751 				else if (SEP(*w) && w != s)
752 					ww = ++w;
753 				else if (*x == *w)
754 				{
755 					x++;
756 					w++;
757 				}
758 				else if (w == ww && x == xw)
759 					break;
760 				else
761 				{
762 					if (x != xw)
763 					{
764 						while (*x && !SEP(*x) && !isupper(*x))
765 							x++;
766 						if (!*x)
767 							break;
768 						if (SEP(*x))
769 							x++;
770 						xw = x;
771 					}
772 					while (w > ww && *w != *x)
773 						w--;
774 				}
775 			}
776 			if (!*w)
777 			{
778 				if (!v)
779 				{
780 					for (;;)
781 					{
782 						switch (*x++)
783 						{
784 						case 0:
785 						case ':':
786 						case '|':
787 						case '?':
788 						case ']':
789 							return 1;
790 						case '*':
791 							break;
792 						default:
793 							continue;
794 						}
795 						break;
796 					}
797 					break;
798 				}
799 				return 1;
800 			}
801 		} while (*(x = skip(x, '|', 0, 0, 1, 0, 0, version)) == '|' && x++);
802 	}
803 	return 0;
804 }
805 
806 /*
807  * prefix search for s in tab with num elements of size
808  * with optional translation
809  */
810 
811 static void*
search(const void * tab,size_t num,size_t siz,char * s)812 search(const void* tab, size_t num, size_t siz, char* s)
813 {
814 	register char*	p;
815 	register char*	e;
816 
817 	for (e = (p = (char*)tab) + num * siz; p < e; p += siz)
818 		if (match(s, *((char**)p), -1, NiL, NiL))
819 			return (void*)p;
820 	return 0;
821 }
822 
823 /*
824  * save ap+bp+cp and return the saved pointer
825  */
826 
827 static char*
save(const char * ap,size_t az,const char * bp,size_t bz,const char * cp,size_t cz)828 save(const char* ap, size_t az, const char* bp, size_t bz, const char* cp, size_t cz)
829 {
830 	char*		b;
831 	char*		e;
832 	const char*	ep;
833 	Save_t*		p;
834 	Dtdisc_t*	d;
835 	char		buf[1024];
836 
837 	static Dt_t*	dict;
838 
839 	if (!dict)
840 	{
841 		if (!(d = newof(0, Dtdisc_t, 1, 0)))
842 			return (char*)ap;
843 		d->key = offsetof(Save_t, text);
844 		if (!(dict = dtopen(d, Dtset)))
845 			return (char*)ap;
846 	}
847 	b = buf;
848 	e = b + sizeof(buf) - 1;
849 	for (ep = ap + az; b < e && ap < ep; *b++ = *ap++);
850 	if (bp)
851 	{
852 		for (ep = bp + bz; b < e && bp < ep; *b++ = *bp++);
853 		if (cp)
854 			for (ep = cp + cz; b < e && cp < ep; *b++ = *cp++);
855 	}
856 	*b = 0;
857 	if (!(p = (Save_t*)dtmatch(dict, buf)))
858 	{
859 		if (!(p = newof(0, Save_t, 1, b - buf)))
860 			return (char*)ap;
861 		strcpy(p->text, buf);
862 		dtinsert(dict, p);
863 	}
864 	return p->text;
865 }
866 
867 /*
868  * expand \f...\f info
869  * *p set to next char after second \f
870  * expanded value returned
871  */
872 
873 static char*
expand(register char * s,register char * e,char ** p,Sfio_t * ip,char * id)874 expand(register char* s, register char* e, char** p, Sfio_t* ip, char* id)
875 {
876 	register int	c;
877 	register char*	b = s;
878 	int		n;
879 
880 	n = sfstrtell(ip);
881 	c = 1;
882 	while ((!e || s < e) && (c = *s++) && c != '\f');
883 	sfwrite(ip, b, s - b - 1);
884 	sfputc(ip, 0);
885 	b = sfstrbase(ip) + n;
886 	n = sfstrtell(ip);
887 	if (!c)
888 		s--;
889 	if (*b == '?')
890 	{
891 		if (!*++b || streq(b, "NAME"))
892 		{
893 			if (!(b = id))
894 				b = "command";
895 			sfstrseek(ip, 0, SEEK_SET);
896 			sfputr(ip, b, -1);
897 			n = 0;
898 		}
899 		else
900 			n = 1;
901 	}
902 	else if (!opt_info.disc || !opt_info.disc->infof || (*opt_info.disc->infof)(&opt_info, ip, b, opt_info.disc) < 0)
903 		n = 0;
904 	*p = s;
905 	if (s = sfstruse(ip))
906 		s += n;
907 	else
908 		s = "error";
909 	return s;
910 }
911 
912 /*
913  * initialize the translation dictionary and flag maps
914  */
915 
916 static void
initdict(void)917 initdict(void)
918 {
919 	register int	n;
920 
921 	state.vp = sfstropen();
922 	state.msgdisc.key = offsetof(Msg_t, text);
923 	state.msgdisc.size = -1;
924 	state.msgdisc.link = offsetof(Msg_t, link);
925 	if (state.msgdict = dtopen(&state.msgdisc, Dtset))
926 		for (n = 0; n < elementsof(C_LC_MESSAGES_libast); n++)
927 			dtinsert(state.msgdict, C_LC_MESSAGES_libast + n);
928 }
929 
930 /*
931  * initialize the attributes for pass p from opt string s
932  */
933 
934 static int
init(register char * s,Optpass_t * p)935 init(register char* s, Optpass_t* p)
936 {
937 	register char*	t;
938 	register char*	u;
939 	register int	c;
940 	register int	a;
941 	register int	n;
942 	char*		e;
943 	int		l;
944 
945 	if (!state.localized)
946 	{
947 		state.localized = 1;
948 #if !_PACKAGE_astsa
949 		if (!ast.locale.serial)
950 			setlocale(LC_ALL, "");
951 #endif
952 		state.xp = sfstropen();
953 		if (!map[OPT_FLAGS[0]])
954 			for (n = 0, t = OPT_FLAGS; *t; t++)
955 				map[*t] = ++n;
956 	}
957 #if _BLD_DEBUG
958 	error(-2, "optget debug");
959 #endif
960 	p->oopts = s;
961 	p->version = 0;
962 	p->prefix = 2;
963 	p->section[0] = '1';
964 	p->section[1] = 0;
965 	p->flags = 0;
966 	p->id = error_info.id;
967 	p->catalog = 0;
968 	s = next(s, 0);
969 	if (*s == ':')
970 		s++;
971 	if (*s == '+')
972 		s++;
973 	s = next(s, 0);
974 	if (*s++ == '[')
975 	{
976 		if (*s == '+')
977 			p->version = 1;
978 		else if (*s++ == '-')
979 		{
980 			if (*s == '?' || *s == ']')
981 				p->version = 1;
982 			else
983 			{
984 				if (!isdigit(*s))
985 					p->version = 1;
986 				else
987 					while (isdigit(*s))
988 						p->version = p->version * 10 + (*s++ - '0');
989 				while (*s && *s != ']')
990 				{
991 					if ((c = *s++) == '?')
992 					{
993 						p->release = s;
994 						while (*s && *s != ']')
995 							if (isspace(*s++))
996 								p->release = s;
997 						break;
998 					}
999 					else if (!isdigit(*s))
1000 						n = 1;
1001 					else
1002 					{
1003 						n = 0;
1004 						while (isdigit(*s))
1005 							n = n * 10 + (*s++ - '0');
1006 					}
1007 					switch (c)
1008 					{
1009 					case '+':
1010 						p->flags |= OPT_plus;
1011 						break;
1012 					case 'a':
1013 						p->flags |= OPT_append;
1014 						break;
1015 					case 'c':
1016 						p->flags |= OPT_cache;
1017 						break;
1018 					case 'i':
1019 						p->flags |= OPT_ignore;
1020 						break;
1021 					case 'l':
1022 						p->flags |= OPT_long;
1023 						break;
1024 					case 'm':
1025 						p->flags |= OPT_module;
1026 						break;
1027 					case 'n':
1028 						p->flags |= OPT_numeric;
1029 						break;
1030 					case 'o':
1031 						p->flags |= OPT_old;
1032 						break;
1033 					case 'p':
1034 						p->prefix = n;
1035 						break;
1036 					case 's':
1037 						if (n > 1 && n < 5)
1038 						{
1039 							p->flags |= OPT_functions;
1040 							p->prefix = 0;
1041 						}
1042 						p->section[0] = '0' + (n % 10);
1043 						n = 1;
1044 						if (isupper(*s))
1045 							p->section[n++] = *s++;
1046 						if (isupper(*s))
1047 							p->section[n++] = *s++;
1048 						p->section[n] = 0;
1049 						break;
1050 					}
1051 				}
1052 			}
1053 		}
1054 		while (*s)
1055 			if (*s++ == ']')
1056 			{
1057 				while (isspace(*s))
1058 					s++;
1059 				if (*s++ == '[')
1060 				{
1061 					if (*s++ != '-')
1062 					{
1063 						l = 0;
1064 						if (strneq(s - 1, "+NAME?", 6) && (s += 5) || strneq(s - 1, "+LIBRARY?", 9) && (s += 8) && (l = 1) || strneq(s - 1, "+PLUGIN?", 8) && (s += 7) && (l = 1))
1065 						{
1066 							for (; *s == '\a' || *s == '\b' || *s == '\v' || *s == ' '; s++);
1067 							if (*s == '\f')
1068 							{
1069 								if (*(s + 1) == '?' && *(s + 2) == '\f')
1070 									break;
1071 								s = expand(s + 1, NiL, &e, state.xp, p->id);
1072 							}
1073 							for (t = s; *t && *t != ' ' && *t != ']'; t++);
1074 							if (t > s)
1075 							{
1076 								u = t;
1077 								if (*(t - 1) == '\a' || *(t - 1) == '\b' || *(t - 1) == '\v')
1078 									t--;
1079 								if (t > s)
1080 								{
1081 									while (*u == ' ' || *u == '\\')
1082 										u++;
1083 									if (*u == '-' || *u == ']')
1084 									{
1085 										if (!l)
1086 											p->id = save(s, t - s, 0, 0, 0, 0);
1087 										else if ((a = strlen(p->id)) <= (n = t - s) || strncmp(p->id + a - n, s, n) || *(p->id + a - n - 1) != ':')
1088 											p->id = save(p->id, strlen(p->id), "::", 2, s, t - s);
1089 									}
1090 								}
1091 							}
1092 						}
1093 						break;
1094 					}
1095 					if (*s == '-')
1096 						s++;
1097 					if (strneq(s, "catalog?", 8))
1098 						p->catalog = s += 8;
1099 				}
1100 			}
1101 	}
1102 	if (!error_info.id)
1103 	{
1104 		if (!(error_info.id = p->id))
1105 			p->id = "command";
1106 	}
1107 	else if (p->id == error_info.id)
1108 		p->id = save(p->id, strlen(p->id), 0, 0, 0, 0);
1109 	if (s = p->catalog)
1110 		p->catalog = ((t = strchr(s, ']')) && (!p->id || (t - s) != strlen(p->id) || !strneq(s, p->id, t - s))) ? save(s, t - s, 0, 0, 0, 0) : (char*)0;
1111 	if (!p->catalog)
1112 	{
1113 		if (opt_info.disc && opt_info.disc->catalog && (!p->id || !streq(opt_info.disc->catalog, p->id)))
1114 			p->catalog = opt_info.disc->catalog;
1115 		else
1116 			p->catalog = ID;
1117 	}
1118 	s = p->oopts;
1119 	if (*s == ':')
1120 		s++;
1121 	if (*s == '+')
1122 	{
1123 		s++;
1124 		p->flags |= OPT_plus;
1125 	}
1126 	s = next(s, 0);
1127 	if (*s != '[')
1128 		for (t = s, a = 0; *t; t++)
1129 			if (!a && *t == '-')
1130 			{
1131 				p->flags |= OPT_minus;
1132 				break;
1133 			}
1134 			else if (*t == '[')
1135 				a++;
1136 			else if (*t == ']')
1137 				a--;
1138 	if (!p->version && (t = strchr(s, '(')) && strchr(t, ')') && (state.cp || (state.cp = sfstropen())))
1139 	{
1140 		/*
1141 		 * solaris long option compatibility
1142 		 */
1143 
1144 		p->version = 1;
1145 		for (t = p->oopts; t < s; t++)
1146 			sfputc(state.cp, *t);
1147 		n = t - p->oopts;
1148 		sfputc(state.cp, '[');
1149 		sfputc(state.cp, '-');
1150 		sfputc(state.cp, ']');
1151 		c = *s++;
1152 		while (c)
1153 		{
1154 			sfputc(state.cp, '[');
1155 			sfputc(state.cp, c);
1156 			if (a = (c = *s++) == ':')
1157 				c = *s++;
1158 			if (c == '(')
1159 			{
1160 				sfputc(state.cp, ':');
1161 				for (;;)
1162 				{
1163 					while ((c = *s++) && c != ')')
1164 						sfputc(state.cp, c);
1165 					if (!c || (c = *s++) != '(')
1166 						break;
1167 					sfputc(state.cp, '|');
1168 				}
1169 			}
1170 			sfputc(state.cp, ']');
1171 			if (a)
1172 				sfputr(state.cp, ":[string]", -1);
1173 		}
1174 		if (!(p->oopts = s = sfstruse(state.cp)))
1175 			return -1;
1176 		s += n;
1177 	}
1178 	p->opts = s;
1179 	message((-2, "version=%d prefix=%d section=%s flags=%04x id=%s catalog=%s oopts=%p", p->version, p->prefix, p->section, p->flags, p->id, p->catalog, p->oopts));
1180 	return 0;
1181 }
1182 
1183 /*
1184  * return the bold set/unset sequence for style
1185  */
1186 
1187 static const char*
font(int f,int style,int set)1188 font(int f, int style, int set)
1189 {
1190 	switch (style)
1191 	{
1192 	case STYLE_html:
1193 		return fonts[f].html[set];
1194 	case STYLE_nroff:
1195 		return fonts[f].nroff[set];
1196 	case STYLE_short:
1197 	case STYLE_long:
1198 	case STYLE_posix:
1199 	case STYLE_api:
1200 		break;
1201 	default:
1202 		if (state.emphasis > 0)
1203 			return fonts[f].term[set];
1204 		break;
1205 	}
1206 	return "";
1207 }
1208 
1209 /*
1210  * push \f...\f info
1211  */
1212 
1213 static Push_t*
info(Push_t * psp,char * s,char * e,Sfio_t * ip,char * id)1214 info(Push_t* psp, char* s, char* e, Sfio_t* ip, char* id)
1215 {
1216 	register char*	b;
1217 	int		n;
1218 	Push_t*		tsp;
1219 
1220 	static Push_t	push;
1221 
1222 	b = expand(s, e, &s, ip, id);
1223 	n = strlen(b);
1224 	if (tsp = newof(0, Push_t, 1, n + 1))
1225 	{
1226 		tsp->nb = (char*)(tsp + 1);
1227 		tsp->ne = tsp->nb + n;
1228 		strcpy(tsp->nb, b);
1229 	}
1230 	else
1231 		tsp = &push;
1232 	tsp->next = psp;
1233 	tsp->ob = s;
1234 	tsp->oe = e;
1235 	return tsp;
1236 }
1237 
1238 /*
1239  * push translation
1240  */
1241 
1242 static Push_t*
localize(Push_t * psp,char * s,char * e,int term,int n,Sfio_t * ip,int version,char * id,char * catalog)1243 localize(Push_t* psp, char* s, char* e, int term, int n, Sfio_t* ip, int version, char* id, char* catalog)
1244 {
1245 	char*		t;
1246 	char*		u;
1247 	Push_t*		tsp;
1248 	int		c;
1249 
1250 	t = skip(s, term, 0, 0, n, 0, 0, version);
1251 	if (e && t > e)
1252 		t = e;
1253 	while (s < t)
1254 	{
1255 		switch (c = *s++)
1256 		{
1257 		case ':':
1258 		case '?':
1259 			if (term && *s == c)
1260 				s++;
1261 			break;
1262 		case ']':
1263 			if (*s == c)
1264 				s++;
1265 			break;
1266 		}
1267 		sfputc(ip, c);
1268 	}
1269 	if (!(s = sfstruse(ip)) || (u = T(id, catalog, s)) == s)
1270 		return 0;
1271 	n = strlen(u);
1272 	if (tsp = newof(0, Push_t, 1, n + 1))
1273 	{
1274 		tsp->nb = (char*)(tsp + 1);
1275 		tsp->ne = tsp->nb + n;
1276 		strcpy(tsp->nb, u);
1277 		tsp->ob = t;
1278 		tsp->oe = e;
1279 		tsp->ch = 1;
1280 	}
1281 	tsp->next = psp;
1282 	return tsp;
1283 }
1284 
1285 /*
1286  * output label s from [ ...label...[?...] ] to sp
1287  * 1 returned if the label was translated
1288  */
1289 
1290 static int
label(register Sfio_t * sp,int sep,register char * s,int about,int z,int level,int style,int f,Sfio_t * ip,int version,char * id,char * catalog)1291 label(register Sfio_t* sp, int sep, register char* s, int about, int z, int level, int style, int f, Sfio_t* ip, int version, char* id, char* catalog)
1292 {
1293 	register int	c;
1294 	register char*	t;
1295 	register char*	e;
1296 	int		ostyle;
1297 	int		a;
1298 	int		i;
1299 	char*		p;
1300 	char*		q;
1301 	char*		w;
1302 	char*		y;
1303 	int		va;
1304 	Push_t*		tsp;
1305 
1306 	int		r = 0;
1307 	int		n = 1;
1308 	Push_t*		psp = 0;
1309 
1310 	if ((ostyle = style) > (STYLE_nroff - (sep <= 0)) && f != FONT_LITERAL && f >= 0)
1311 		style = 0;
1312 	if (z < 0)
1313 		e = s + strlen(s);
1314 	else
1315 		e = s + z;
1316 	if (sep > 0)
1317 	{
1318 		if (sep == ' ' && style == STYLE_nroff)
1319 			sfputc(sp, '\\');
1320 		sfputc(sp, sep);
1321 	}
1322 	sep = !sep || z < 0;
1323 	va = 0;
1324 	y = 0;
1325 	if (about)
1326 		sfputc(sp, '(');
1327 	if (version < 1)
1328 	{
1329 		a = 0;
1330 		for (;;)
1331 		{
1332 			if (s >= e)
1333 				return r;
1334 			switch (c = *s++)
1335 			{
1336 			case '[':
1337 				a++;
1338 				break;
1339 			case ']':
1340 				if (--a < 0)
1341 					return r;
1342 				break;
1343 			}
1344 			sfputc(sp, c);
1345 		}
1346 	}
1347 	else if (level && (*(p = skip(s, 0, 0, 0, 1, level, 1, version)) == ':' || *p == '#'))
1348 	{
1349 		va = 0;
1350 		if (*++p == '?' || *p == *(p - 1))
1351 		{
1352 			p++;
1353 			va |= OPT_optional;
1354 		}
1355 		if (*(p = next(p, version)) == '[')
1356 			y = p + 1;
1357 	}
1358 	if (X(catalog) && (!level || *s == '\a' || *(s - 1) != '+') &&
1359 	    (tsp = localize(psp, s, e, (sep || level) ? '?' : 0, sep || level, ip, version, id, catalog)))
1360 	{
1361 		psp = tsp;
1362 		s = psp->nb;
1363 		e = psp->ne;
1364 		r = psp->ch > 0;
1365 	}
1366 	switch (*s)
1367 	{
1368 	case '\a':
1369 		if (f == FONT_ITALIC || f < 0)
1370 			s++;
1371 		if (f > 0)
1372 			f = 0;
1373 		break;
1374 	case '\b':
1375 		if (f == FONT_BOLD || f < 0)
1376 			s++;
1377 		if (f > 0)
1378 			f = 0;
1379 		break;
1380 	case '\v':
1381 		if (f == FONT_LITERAL || f < 0)
1382 			s++;
1383 		if (f > 0)
1384 			f = 0;
1385 		break;
1386 	default:
1387 		if (f > 0)
1388 			sfputr(sp, font(f, style, 1), -1);
1389 		break;
1390 	}
1391 	for (;;)
1392 	{
1393 		if (s >= e)
1394 		{
1395 			if (!(tsp = psp))
1396 				goto restore;
1397 			s = psp->ob;
1398 			e = psp->oe;
1399 			psp = psp->next;
1400 			free(tsp);
1401 			continue;
1402 		}
1403 		switch (c = *s++)
1404 		{
1405 		case '(':
1406 			if (n)
1407 			{
1408 				n = 0;
1409 				if (f > 0)
1410 				{
1411 					sfputr(sp, font(f, style, 0), -1);
1412 					f = 0;
1413 				}
1414 			}
1415 			break;
1416 		case '?':
1417 		case ':':
1418 		case ']':
1419 			if (psp && psp->ch)
1420 				break;
1421 			if (y)
1422 			{
1423 				if (va & OPT_optional)
1424 					sfputc(sp, '[');
1425 				sfputc(sp, '=');
1426 				label(sp, 0, y, 0, -1, 0, style, f >= 0 ? FONT_ITALIC : f, ip, version, id, catalog);
1427 				if (va & OPT_optional)
1428 					sfputc(sp, ']');
1429 				y = 0;
1430 			}
1431 			switch (c)
1432 			{
1433 			case '?':
1434 				if (*s == '?')
1435 					s++;
1436 				else if (*s == ']' && *(s + 1) != ']')
1437 					continue;
1438 				else if (sep)
1439 					goto restore;
1440 				else if (X(catalog) && (tsp = localize(psp, s, e, 0, 1, ip, version, id, catalog)))
1441 				{
1442 					psp = tsp;
1443 					s = psp->nb;
1444 					e = psp->ne;
1445 				}
1446 				break;
1447 			case ']':
1448 				if (sep && *s++ != ']')
1449 					goto restore;
1450 				break;
1451 			case ':':
1452 				if (sep && *s++ != ':')
1453 					goto restore;
1454 				break;
1455 			}
1456 			break;
1457 		case '\a':
1458 			a = FONT_ITALIC;
1459 		setfont:
1460 			if (f >= 0)
1461 			{
1462 				if (f & ~a)
1463 				{
1464 					sfputr(sp, font(f, style, 0), -1);
1465 					f = 0;
1466 				}
1467 				if (!f && style == STYLE_html)
1468 				{
1469 					for (t = s; t < e && !isspace(*t) && !iscntrl(*t); t++);
1470 					if (*t == c && *++t == '(')
1471 					{
1472 						w = t;
1473 						if (++t < e && isdigit(*t))
1474 							while (++t < e && isupper(*t));
1475 						if (t < e && *t == ')' && t > w + 1)
1476 						{
1477 							sfprintf(sp, "<NOBR><A href=\"../man%-.*s/"
1478 								, t - w - 1, w + 1
1479 								);
1480 							for (q = s; q < w - 1; q++)
1481 								if (*q == ':' && q < w - 2 && *(q + 1) == ':')
1482 								{
1483 									sfputc(sp, '-');
1484 									q++;
1485 								}
1486 								else
1487 									sfputc(sp, *q);
1488 							sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>"
1489 								, font(a, style, 1)
1490 								, w - s - 1, s
1491 								, font(a, style, 0)
1492 								, t - w + 1, w
1493 								);
1494 							s = t + 1;
1495 							continue;
1496 						}
1497 					}
1498 				}
1499 				sfputr(sp, font(a, style, !!(f ^= a)), -1);
1500 			}
1501 			continue;
1502 		case '\b':
1503 			a = FONT_BOLD;
1504 			goto setfont;
1505 		case '\f':
1506 			psp = info(psp, s, e, ip, id);
1507 			if (psp->nb)
1508 			{
1509 				s = psp->nb;
1510 				e = psp->ne;
1511 			}
1512 			else
1513 			{
1514 				s = psp->ob;
1515 				psp = psp->next;
1516 			}
1517 			continue;
1518 		case '\n':
1519 			sfputc(sp, c);
1520 			for (i = 0; i < level; i++)
1521 				sfputc(sp, '\t');
1522 			continue;
1523 		case '\v':
1524 			a = FONT_LITERAL;
1525 			goto setfont;
1526 		case '<':
1527 			if (style == STYLE_html)
1528 			{
1529 				sfputr(sp, "&lt;", -1);
1530 				c = 0;
1531 				for (t = s; t < e; t++)
1532 					if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-')
1533 					{
1534 						if (*t == '@')
1535 						{
1536 							if (c)
1537 								break;
1538 							c = 1;
1539 						}
1540 						else if (*t == '>')
1541 						{
1542 							if (c)
1543 							{
1544 								sfprintf(sp, "<A href=\"mailto:%-.*s>%-.*s</A>&gt;", t - s, s, t - s, s);
1545 								s = t + 1;
1546 							}
1547 							break;
1548 						}
1549 						else
1550 							break;
1551 					}
1552 				continue;
1553 			}
1554 			break;
1555 		case '>':
1556 			if (style == STYLE_html)
1557 			{
1558 				sfputr(sp, "&gt;", -1);
1559 				continue;
1560 			}
1561 			break;
1562 		case '&':
1563 			if (style == STYLE_html)
1564 			{
1565 				sfputr(sp, "&amp;", -1);
1566 				continue;
1567 			}
1568 			break;
1569 		case '"':
1570 			if (style == STYLE_html)
1571 			{
1572 				sfputr(sp, "&quot;", -1);
1573 				continue;
1574 			}
1575 			break;
1576 		case '-':
1577 			if (ostyle == STYLE_nroff)
1578 				sfputc(sp, '\\');
1579 			break;
1580 		case '.':
1581 			if (ostyle == STYLE_nroff)
1582 			{
1583 				sfputc(sp, '\\');
1584 				sfputc(sp, '&');
1585 			}
1586 			break;
1587 		case '\\':
1588 			if (ostyle == STYLE_nroff)
1589 			{
1590 				c = 'e';
1591 				sfputc(sp, '\\');
1592 			}
1593 			break;
1594 		case ' ':
1595 			if (ostyle == STYLE_nroff)
1596 				sfputc(sp, '\\');
1597 			break;
1598 		}
1599 		sfputc(sp, c);
1600 	}
1601  restore:
1602 	if (f > 0)
1603 		sfputr(sp, font(f, style, 0), -1);
1604 	if (about)
1605 		sfputc(sp, ')');
1606 	if (psp)
1607 		pop(psp);
1608 	return r;
1609 }
1610 
1611 /*
1612  * output args description to sp from p of length n
1613  */
1614 
1615 static void
args(register Sfio_t * sp,register char * p,register int n,int flags,int style,Sfio_t * ip,int version,char * id,char * catalog)1616 args(register Sfio_t* sp, register char* p, register int n, int flags, int style, Sfio_t* ip, int version, char* id, char* catalog)
1617 {
1618 	register int	i;
1619 	register char*	t;
1620 	register char*	o;
1621 	register char*	a = 0;
1622 	char*		b;
1623 	int		sep;
1624 
1625 	if (flags & OPT_functions)
1626 		sep = '\t';
1627 	else
1628 	{
1629 		sep = ' ';
1630 		o = T(NiL, ID, "options");
1631 		b = style == STYLE_nroff ? "\\ " : " ";
1632 		for (;;)
1633 		{
1634 			t = (char*)memchr(p, '\n', n);
1635 			if (style >= STYLE_man)
1636 			{
1637 				if (!(a = id))
1638 					a = "...";
1639 				sfprintf(sp, "\t%s%s%s%s[%s%s%s%s%s]", font(FONT_BOLD, style, 1), a, font(FONT_BOLD, style, 0), b, b, font(FONT_ITALIC, style, 1), o, font(FONT_ITALIC, style, 0), b);
1640 			}
1641 			else if (a)
1642 				sfprintf(sp, "%*.*s%s%s%s[%s%s%s]", OPT_USAGE - 1, OPT_USAGE - 1, T(NiL, ID, "Or:"), b, a, b, b, o, b);
1643 			else
1644 			{
1645 				if (!(a = error_info.id) && !(a = id))
1646 					a = "...";
1647 				if (!sfstrtell(sp))
1648 					sfprintf(sp, "[%s%s%s]", b, o, b);
1649 			}
1650 			if (!t)
1651 				break;
1652 			i = ++t - p;
1653 			if (i)
1654 			{
1655 				sfputr(sp, b, -1);
1656 				if (X(catalog))
1657 				{
1658 					sfwrite(ip, p, i);
1659 					if (b = sfstruse(ip))
1660 						sfputr(sp, T(id, catalog, b), -1);
1661 					else
1662 						sfwrite(sp, p, i);
1663 				}
1664 				else
1665 					sfwrite(sp, p, i);
1666 			}
1667 			if (style == STYLE_html)
1668 				sfputr(sp, "<BR>", '\n');
1669 			else if (style == STYLE_nroff)
1670 				sfputr(sp, ".br", '\n');
1671 			else if (style == STYLE_api)
1672 				sfputr(sp, ".BR", '\n');
1673 			p = t;
1674 			n -= i;
1675 			while (n > 0 && (*p == ' ' || *p == '\t'))
1676 			{
1677 				p++;
1678 				n--;
1679 			}
1680 		}
1681 	}
1682 	if (n)
1683 		label(sp, sep, p, 0, n, 0, style, 0, ip, version, id, catalog);
1684 }
1685 
1686 /*
1687  * output [+-...label...?...] label s to sp
1688  * according to {...} level and style
1689  * return 0:header 1:paragraph
1690  */
1691 
1692 static int
item(Sfio_t * sp,char * s,int about,int level,int style,Sfio_t * ip,int version,char * id,char * catalog,int * hflags)1693 item(Sfio_t* sp, char* s, int about, int level, int style, Sfio_t* ip, int version, char* id, char* catalog, int* hflags)
1694 {
1695 	register char*	t;
1696 	int		n;
1697 	int		par;
1698 
1699 	sfputc(sp, '\n');
1700 	if (*s == '\n')
1701 	{
1702 		par = 0;
1703 		if (style >= STYLE_nroff)
1704 			sfprintf(sp, ".DS\n");
1705 		else
1706 		{
1707 			if (style == STYLE_html)
1708 				sfprintf(sp, "<PRE>\n");
1709 			else
1710 				sfputc(sp, '\n');
1711 			for (n = 0; n < level; n++)
1712 				sfputc(sp, '\t');
1713 		}
1714 		label(sp, 0, s + 1, about, -1, level, style, FONT_LITERAL, ip, version, id, catalog);
1715 		sfputc(sp, '\n');
1716 		if (style >= STYLE_nroff)
1717 			sfprintf(sp, ".DE");
1718 		else if (style == STYLE_html)
1719 			sfprintf(sp, "</PRE>");
1720 	}
1721 	else if (*s != ']' && (*s != '?' || *(s + 1) == '?'))
1722 	{
1723 		par = 0;
1724 		if (level)
1725 		{
1726 			if (style >= STYLE_nroff)
1727 				sfprintf(sp, ".H%d ", (level - (level > 2)) / 2);
1728 			else
1729 				for (n = 0; n < level; n++)
1730 					sfputc(sp, '\t');
1731 		}
1732 		if (style == STYLE_html)
1733 		{
1734 			if (!level)
1735 			{
1736 				if (*hflags & HELP_head)
1737 					sfputr(sp, "</DIV>", '\n');
1738 				else
1739 					*hflags |= HELP_head;
1740 				sfputr(sp, "<H4>", -1);
1741 			}
1742 			sfputr(sp, "<A name=\"", -1);
1743 			if (s[-1] == '-' && s[0] == 'l' && s[1] == 'i' && s[2] == 'c' && s[3] == 'e' && s[4] == 'n' && s[5] == 's' && s[6] == 'e' && s[7] == '?')
1744 				for (t = s + 8; *t && *t != ']'; t++)
1745 					if (t[0] == 'p' && (!strncmp(t, "proprietary", 11) || !strncmp(t, "private", 7)) || t[0] == 'n' && !strncmp(t, "noncommercial", 13))
1746 					{
1747 						state.flags |= OPT_proprietary;
1748 						break;
1749 					}
1750 			label(sp, 0, s, about, -1, level, style, -1, ip, version, id, catalog);
1751 			sfputr(sp, "\">", -1);
1752 			label(sp, 0, s, about, -1, level, style, level ? FONT_BOLD : 0, ip, version, id, catalog);
1753 			sfputr(sp, "</A>", -1);
1754 			if (!level)
1755 			{
1756 				if (!strncmp(s, C("SYNOPSIS"), strlen(C("SYNOPSIS"))))
1757 					sfputr(sp, "</H4>\n<DIV class=SY>", -1);
1758 				else
1759 				{
1760 					sfputr(sp, "</H4>\n<DIV class=SH>", -1);
1761 					if (!strncmp(s, C("NAME"), strlen(C("NAME"))) || !strncmp(s, C("PLUGIN"), strlen(C("PLUGIN"))))
1762 						*hflags |= HELP_index;
1763 				}
1764 			}
1765 		}
1766 		else
1767 		{
1768 			if (!level)
1769 			{
1770 				if (style >= STYLE_nroff)
1771 					sfprintf(sp, ".SH ");
1772 				else if (style == STYLE_man)
1773 					sfputc(sp, '\n');
1774 				else if (style != STYLE_options && style != STYLE_match || *s == '-' || *s == '+')
1775 					sfputc(sp, '\t');
1776 			}
1777 			label(sp, 0, s, about, -1, level, style, FONT_BOLD, ip, version, id, catalog);
1778 		}
1779 	}
1780 	else
1781 	{
1782 		par = 1;
1783 		if (style >= STYLE_nroff)
1784 			sfputr(sp, level ? ".SP" : ".PP", -1);
1785 	}
1786 	if (style >= STYLE_nroff || !level)
1787 		sfputc(sp, '\n');
1788 	if (par && style < STYLE_nroff)
1789 		for (n = 0; n < level; n++)
1790 			sfputc(sp, '\t');
1791 	return par;
1792 }
1793 
1794 /*
1795  * output text to sp from p according to style
1796  */
1797 
1798 #if _BLD_DEBUG
1799 
1800 static char*	textout(Sfio_t*, char*, char*, int, int, int, int, Sfio_t*, int, char*, char*, int*);
1801 
1802 static char*
trace_textout(Sfio_t * sp,register char * p,char * conform,int conformlen,int style,int level,int bump,Sfio_t * ip,int version,char * id,char * catalog,int * hflags,int line)1803 trace_textout(Sfio_t* sp, register char* p, char* conform, int conformlen, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog, int* hflags, int line)
1804 {
1805 	static int	depth = 0;
1806 
1807 	message((-21, "opthelp: txt#%d +++ %2d \"%s\" style=%d level=%d bump=%d", line, ++depth, show(p), style, level, bump));
1808 	p = textout(sp, p, conform, conformlen, style, level, bump, ip, version, id, catalog, hflags);
1809 	message((-21, "opthelp: txt#%d --- %2d \"%s\"", line, depth--, show(p)));
1810 	return p;
1811 }
1812 
1813 #endif
1814 
1815 static char*
textout(Sfio_t * sp,register char * s,char * conform,int conformlen,int style,int level,int bump,Sfio_t * ip,int version,char * id,char * catalog,int * hflags)1816 textout(Sfio_t* sp, register char* s, char* conform, int conformlen, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog, int* hflags)
1817 {
1818 #if _BLD_DEBUG
1819 #define textout(sp,s,conform,conformlen,style,level,bump,ip,version,id,catalog,hflags)	trace_textout(sp,s,conform,conformlen,style,level,bump,ip,version,id,catalog,hflags,__LINE__)
1820 #endif
1821 	register char*	t;
1822 	register int	c;
1823 	register int	n;
1824 	char*		w;
1825 	char*		q;
1826 	int		a;
1827 	int		f;
1828 	int		par;
1829 	int		about;
1830 	Push_t*		tsp;
1831 
1832 	int		ident = 0;
1833 	int		lev = level;
1834 	Push_t*		psp = 0;
1835 
1836  again:
1837 	about = 0;
1838 	if ((c = *s) == GO)
1839 	{
1840 		for (;;)
1841 		{
1842 			while (*(s = next(s + 1, version)) == '\n');
1843 			if (*s == GO)
1844 			{
1845 				if (level > 1)
1846 					level++;
1847 				level++;
1848 			}
1849 			else if (*s != OG)
1850 			{
1851 				if (level <= 1 || *s != '[' || *(s + 1) != '-' || style == STYLE_man && *(s + 2) == '?' || isalpha(*(s + 2)))
1852 					break;
1853 				s = skip(s, 0, 0, 0, 1, level, 0, version);
1854 			}
1855 			else if ((level -= 2) <= lev)
1856 				return s + 1;
1857 		}
1858 		if (*s == '\f')
1859 		{
1860 			psp = info(psp, s + 1, NiL, ip, id);
1861 			if (psp->nb)
1862 				s = psp->nb;
1863 			else
1864 			{
1865 				s = psp->ob;
1866 				psp = psp->next;
1867 			}
1868 		}
1869 		if (*s != '[')
1870 			return s;
1871 		c = *++s;
1872 		if (level > 1)
1873 			level++;
1874 		level++;
1875 	}
1876 	if (c == '-' && level > 1)
1877 	{
1878 		if (style == STYLE_man)
1879 		{
1880 			about = 1;
1881 			if (*(s + 1) == '-')
1882 				s++;
1883 		}
1884 		else
1885 			for (;;)
1886 			{
1887 				s = skip(s, 0, 0, 0, 1, level, 0, version);
1888 				while (*(s = next(s + 1, version)) == '\n');
1889 				if (*s == '[')
1890 				{
1891 					if ((c = *++s) != '-')
1892 						break;
1893 				}
1894 				else if (*s == GO)
1895 					goto again;
1896 				else if (*s == OG)
1897 					return s + 1;
1898 			}
1899 	}
1900 	if (c == '+' || c == '-' && (bump = 3) || c != ' ' && level > 1)
1901 	{
1902 		s = skip(t = s + 1, '?', 0, 0, 1, level, 0, version);
1903 		if (c == '-' && (*t == '?' || isdigit(*t) || *s == '?' && *(s + 1) == '\n'))
1904 		{
1905 			if ((c = *s) != '?')
1906 				return skip(s, 0, 0, 0, 1, level, 1, version);
1907 			w = C("version");
1908 			par = item(sp, w, about, level, style, ip, version, id, ID, hflags);
1909 			for (;;)
1910 			{
1911 				while (isspace(*(s + 1)))
1912 					s++;
1913 				w = s;
1914 				if (w[1] == '@' && w[2] == '(' && w[3] == '#' && w[4] == ')')
1915 					s = w + 4;
1916 				else if (w[1] == '$' && w[2] == 'I' && w[3] == 'd' && w[4] == ':' && w[5] == ' ')
1917 				{
1918 					s = w + 5;
1919 					ident = 1;
1920 				}
1921 				else
1922 					break;
1923 			}
1924 		}
1925 		else
1926 		{
1927 			if (isdigit(c) && isdigit(*t))
1928 			{
1929 				while (isdigit(*t))
1930 					t++;
1931 				if (*t == ':')
1932 					t++;
1933 			}
1934 			else if (isalnum(c) && *t-- == ':')
1935 			{
1936 				if (X(catalog) || *t == *(t + 2))
1937 					t += 2;
1938 				else
1939 				{
1940 					sfprintf(ip, "%s", t);
1941 					if (w = sfstruse(ip))
1942 						*((t = w) + 1) = '|';
1943 				}
1944 			}
1945 			par = item(sp, t, about, level, style, ip, version, id, catalog, hflags);
1946 			c = *s;
1947 		}
1948 		if (!about && level)
1949 			par = 0;
1950 	}
1951 	else
1952 	{
1953 		if (style >= STYLE_nroff)
1954 			sfputc(sp, '\n');
1955 		else if (c == '?')
1956 			for (n = 0; n < level; n++)
1957 				sfputc(sp, '\t');
1958 		par = 0;
1959 	}
1960 	if (c == ':')
1961 		c = *(s = skip(s, '?', 0, 0, 1, 0, 0, version));
1962 	if ((c == ']' || c == '?' && *(s + 1) == ']' && *(s + 2) != ']' && s++) && (c = *(s = next(s + 1, version))) == GO)
1963 	{
1964 		s = textout(sp, s, conform, conformlen, style, level + bump + par + 1, 0, ip, version, id, catalog, hflags);
1965 		if (level > lev && *s && *(s = next(s, version)) == '[')
1966 		{
1967 			s++;
1968 			message((-21, "textout#%d s=%s", __LINE__, show(s)));
1969 			goto again;
1970 		}
1971 	}
1972 	else if (c == '?' || c == ' ')
1973 	{
1974 		s++;
1975 		if (c == ' ')
1976 			sfputc(sp, c);
1977 		else
1978 		{
1979 			if (X(catalog) && (tsp = localize(psp, s, NiL, 0, 1, ip, version, id, catalog)))
1980 			{
1981 				psp = tsp;
1982 				s = psp->nb;
1983 			}
1984 			if (style < STYLE_nroff)
1985 				for (n = 0; n < bump + 1; n++)
1986 					sfputc(sp, '\t');
1987 		}
1988 		if (conform)
1989 		{
1990 			sfprintf(sp, "[%-.*s %s] ", conformlen, conform, T(NiL, ID, "conformance"));
1991 			conform = 0;
1992 		}
1993 		if (*hflags & HELP_index)
1994 		{
1995 			*hflags &= ~HELP_index;
1996 			sfputr(sp, "<!--MAN-INDEX-->", -1);
1997 		}
1998 		f = 0;
1999 		for (;;)
2000 		{
2001 			switch (c = *s++)
2002 			{
2003 			case 0:
2004 				if (!(tsp = psp))
2005 				{
2006 					if (f)
2007 						sfputr(sp, font(f, style, 0), -1);
2008 					return s - 1;
2009 				}
2010 				s = psp->ob;
2011 				psp = psp->next;
2012 				free(tsp);
2013 				continue;
2014 			case ']':
2015 				if (psp && psp->ch)
2016 					break;
2017 				if (*s != ']')
2018 				{
2019 					if (f)
2020 					{
2021 						sfputr(sp, font(f, style, 0), -1);
2022 						f = 0;
2023 					}
2024 					for (;;)
2025 					{
2026 						if ((*s == '#' || *s == ':') && level > lev)
2027 						{
2028 							char*	o;
2029 							char*	v;
2030 							int	j;
2031 							int	m;
2032 							int	ol;
2033 							int	vl;
2034 
2035 							a = 0;
2036 							o = 0;
2037 							v = 0;
2038 							if (*++s == '?' || *s == *(s - 1))
2039 							{
2040 								s++;
2041 								a |= OPT_optional;
2042 							}
2043 							if (*(s = next(s, version)) == '[')
2044 							{
2045 								s = skip(s + 1, ':', '?', 0, 1, 0, 0, version);
2046 								while (*s == ':')
2047 								{
2048 									s = skip(t = s + 1, ':', '?', 0, 1, 0, 0, version);
2049 									m = s - t;
2050 									if (*t == '!')
2051 									{
2052 										o = t + 1;
2053 										ol = m - 1;
2054 									}
2055 									else if (*t == '=')
2056 									{
2057 										v = t + 1;
2058 										vl = m - 1;
2059 									}
2060 									else
2061 										for (j = 0; j < elementsof(attrs); j++)
2062 											if (strneq(t, attrs[j].name, m))
2063 											{
2064 												a |= attrs[j].flag;
2065 												break;
2066 											}
2067 								}
2068 							}
2069 							if (a & OPT_optional)
2070 							{
2071 								if (o)
2072 								{
2073 									sfprintf(sp, " %s ", T(NiL, ID, "If the option value is omitted then"));
2074 									sfputr(sp, font(FONT_BOLD, style, 1), -1);
2075 									t = o + ol;
2076 									while (o < t)
2077 									{
2078 										if (((c = *o++) == ':' || c == '?') && *o == c)
2079 											o++;
2080 										sfputc(sp, c);
2081 									}
2082 									sfputr(sp, font(FONT_BOLD, style, 0), -1);
2083 									sfprintf(sp, " %s.", T(NiL, ID, "is assumed"));
2084 								}
2085 								else
2086 									sfprintf(sp, " %s", T(NiL, ID, "The option value may be omitted."));
2087 							}
2088 							if (v)
2089 							{
2090 								sfprintf(sp, " %s ", T(NiL, ID, "The default value is"));
2091 								sfputr(sp, font(FONT_BOLD, style, 1), -1);
2092 								t = v + vl;
2093 								while (v < t)
2094 								{
2095 									if (((c = *v++) == ':' || c == '?') && *v == c)
2096 										v++;
2097 									sfputc(sp, c);
2098 								}
2099 								sfputr(sp, font(FONT_BOLD, style, 0), -1);
2100 								sfputc(sp, '.');
2101 							}
2102 							s = skip(s, 0, 0, 0, 1, 0, 1, version);
2103 						}
2104 						if (*(s = next(s, version)) == GO)
2105 						{
2106 							s = textout(sp, s, 0, 0, style, level + bump + !level, 0, ip, version, id, catalog, hflags);
2107 							if (*s && *(s = next(s, version)) == '[' && !isalnum(*(s + 1)))
2108 							{
2109 								s++;
2110 								message((-21, "textout#%d s=%s", __LINE__, show(s)));
2111 								goto again;
2112 							}
2113 						}
2114 						else if (*s == '[' && level > lev)
2115 						{
2116 							s++;
2117 							goto again;
2118 						}
2119 						else if (*s == '\f')
2120 						{
2121 							s++;
2122 							if (style != STYLE_keys)
2123 							{
2124 								psp = info(psp, s, NiL, ip, id);
2125 								if (psp->nb)
2126 									s = psp->nb;
2127 								else
2128 								{
2129 									s = psp->ob;
2130 									psp = psp->next;
2131 								}
2132 							}
2133 						}
2134 						else if (!*s)
2135 						{
2136 							if (!(tsp = psp))
2137 								break;
2138 							s = psp->ob;
2139 							psp = psp->next;
2140 							free(tsp);
2141 						}
2142 						else if (*s != OG)
2143 							break;
2144 						else
2145 						{
2146 							s++;
2147 							if ((level -= 2) <= lev)
2148 								break;
2149 						}
2150 					}
2151 					return s;
2152 				}
2153 				s++;
2154 				break;
2155 			case '\a':
2156 				a = FONT_ITALIC;
2157 			setfont:
2158 				if (f & ~a)
2159 				{
2160 					sfputr(sp, font(f, style, 0), -1);
2161 					f = 0;
2162 				}
2163 				if (!f && style == STYLE_html)
2164 				{
2165 					for (t = s; *t && !isspace(*t) && !iscntrl(*t); t++);
2166 					if (*t == c && *++t == '(')
2167 					{
2168 						w = t;
2169 						if (isdigit(*++t))
2170 							while (isupper(*++t));
2171 						if (*t == ')' && t > w + 1)
2172 						{
2173 							sfprintf(sp, "<NOBR><A href=\"../man%-.*s/"
2174 								, t - w - 1, w + 1
2175 								);
2176 							for (q = s; q < w - 1; q++)
2177 								if (*q == ':' && q < w - 2 && *(q + 1) == ':')
2178 								{
2179 									sfputc(sp, '-');
2180 									q++;
2181 								}
2182 								else
2183 									sfputc(sp, *q);
2184 							sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>"
2185 								, font(a, style, 1)
2186 								, w - s - 1, s
2187 								, font(a, style, 0)
2188 								, t - w + 1, w
2189 								);
2190 							s = t + 1;
2191 							continue;
2192 						}
2193 					}
2194 				}
2195 				sfputr(sp, font(a, style, !!(f ^= a)), -1);
2196 				continue;
2197 			case '\b':
2198 				a = FONT_BOLD;
2199 				goto setfont;
2200 			case '\f':
2201 				if (style != STYLE_keys)
2202 				{
2203 					psp = info(psp, s, NiL, ip, id);
2204 					if (psp->nb)
2205 						s = psp->nb;
2206 					else
2207 					{
2208 						s = psp->ob;
2209 						psp = psp->next;
2210 					}
2211 				}
2212 				continue;
2213 			case '\v':
2214 				a = FONT_LITERAL;
2215 				goto setfont;
2216 			case ' ':
2217 				if (ident && *s == '$')
2218 				{
2219 					while (*++s)
2220 						if (*s == ']')
2221 						{
2222 							if (*(s + 1) != ']')
2223 								break;
2224 							s++;
2225 						}
2226 					continue;
2227 				}
2228 			case '\n':
2229 			case '\r':
2230 			case '\t':
2231 				while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
2232 					s++;
2233 				if (*s == ']' && *(s + 1) != ']' && (!psp || !psp->ch))
2234 					continue;
2235 				c = ' ';
2236 				break;
2237 			case '<':
2238 				if (style == STYLE_html)
2239 				{
2240 					sfputr(sp, "&lt;", -1);
2241 					c = 0;
2242 					for (t = s; *t; t++)
2243 						if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-')
2244 						{
2245 							if (*t == '@')
2246 							{
2247 								if (c)
2248 									break;
2249 								c = 1;
2250 							}
2251 							else if (*t == '>')
2252 							{
2253 								if (c)
2254 								{
2255 									sfprintf(sp, "<A href=\"mailto:%-.*s\">%-.*s</A>&gt;", t - s, s, t - s, s);
2256 									s = t + 1;
2257 								}
2258 								break;
2259 							}
2260 							else
2261 								break;
2262 						}
2263 					continue;
2264 				}
2265 				break;
2266 			case '>':
2267 				if (style == STYLE_html)
2268 				{
2269 					sfputr(sp, "&gt;", -1);
2270 					continue;
2271 				}
2272 				break;
2273 			case '&':
2274 				if (style == STYLE_html)
2275 				{
2276 					sfputr(sp, "&amp;", -1);
2277 					continue;
2278 				}
2279 				break;
2280 			case '-':
2281 				if (style == STYLE_nroff)
2282 					sfputc(sp, '\\');
2283 				break;
2284 			case '.':
2285 				if (style == STYLE_nroff)
2286 				{
2287 					sfputc(sp, '\\');
2288 					sfputc(sp, '&');
2289 				}
2290 				break;
2291 			case '\\':
2292 				if (style == STYLE_nroff)
2293 				{
2294 					sfputc(sp, c);
2295 					c = 'e';
2296 				}
2297 				break;
2298 			}
2299 			sfputc(sp, c);
2300 		}
2301 	}
2302 	else if (c == '[' && level > lev)
2303 	{
2304 		s++;
2305 		goto again;
2306 	}
2307 	return s;
2308 }
2309 
2310 /*
2311  * generate optget() help [...] list from lp
2312  */
2313 
2314 static void
list(Sfio_t * sp,register const List_t * lp)2315 list(Sfio_t* sp, register const List_t* lp)
2316 {
2317 	sfprintf(sp, "[%c", lp->type);
2318 	if (lp->name)
2319 	{
2320 		sfprintf(sp, "%s", lp->name);
2321 		if (lp->text)
2322 			sfprintf(sp, "?%s", lp->text);
2323 	}
2324 	sfputc(sp, ']');
2325 }
2326 
2327 /*
2328  * return pointer to help message sans `Usage: command'
2329  * if oopts is 0 then state.pass is used
2330  * what:
2331  *	0	?short by default, ?long if any long options used
2332  *	*	otherwise see help_text[] (--???)
2333  * external formatter:
2334  *	\a...\a	italic
2335  *	\b...\b	bold
2336  *	\f...\f	discipline infof callback on ...
2337  *	\v...\v	literal
2338  * internal formatter:
2339  *	\t	indent
2340  *	\n	newline
2341  * margin flush pops to previous indent
2342  */
2343 
2344 char*
opthelp(const char * oopts,const char * what)2345 opthelp(const char* oopts, const char* what)
2346 {
2347 	register Sfio_t*	sp;
2348 	register Sfio_t*	mp;
2349 	register int		c;
2350 	register char*		p;
2351 	register Indent_t*	ip;
2352 	char*			t;
2353 	char*			x;
2354 	char*			w;
2355 	char*			u;
2356 	char*			y;
2357 	char*			s;
2358 	char*			d;
2359 	char*			v;
2360 	char*			cb;
2361 	char*			dt;
2362 	char*			ov;
2363 	char*			pp;
2364 	char*			rb;
2365 	char*			re;
2366 	int			f;
2367 	int			i;
2368 	int			j;
2369 	int			m;
2370 	int			n;
2371 	int			a;
2372 	int			cl;
2373 	int			sl;
2374 	int			vl;
2375 	int			ol;
2376 	int			wl;
2377 	int			xl;
2378 	int			rm;
2379 	int			ts;
2380 	int			co;
2381 	int			z;
2382 	int			style;
2383 	int			head;
2384 	int			margin;
2385 	int			mode;
2386 	int			mutex;
2387 	int			prefix;
2388 	int			version;
2389 	long			tp;
2390 	char*			id;
2391 	char*			catalog;
2392 	Optpass_t*		o;
2393 	Optpass_t*		q;
2394 	Optpass_t*		e;
2395 	Optpass_t		one;
2396 	Optpass_t		top;
2397 	Help_t*			hp;
2398 	Tag_t			ptstk[elementsof(indent) + 2];
2399 	Tag_t*			pt;
2400 	Sfio_t*			vp;
2401 	Push_t*			tsp;
2402 
2403 	char*			opts = (char*)oopts;
2404 	char*			section = "1";
2405 	int			flags = 0;
2406 	int			bflags = 0;
2407 	int			dflags = 0;
2408 	int			hflags = 0;
2409 	int			matched = 0;
2410 	int			paragraph = 0;
2411 	Push_t*			psp = 0;
2412 	Sfio_t*			sp_help = 0;
2413 	Sfio_t*			sp_text = 0;
2414 	Sfio_t*			sp_plus = 0;
2415 	Sfio_t*			sp_head = 0;
2416 	Sfio_t*			sp_body = 0;
2417 	Sfio_t*			sp_info = 0;
2418 	Sfio_t*			sp_misc = 0;
2419 
2420 	if (!(mp = state.mp) && !(mp = state.mp = sfstropen()))
2421 		goto nospace;
2422 	if (!what)
2423 		style = state.style;
2424 	else if (!*what)
2425 		style = STYLE_options;
2426 	else if (*what != '?')
2427 		style = STYLE_match;
2428 	else if (!*(what + 1))
2429 		style = STYLE_man;
2430 	else if ((hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what + 1)) && hp->style >= 0)
2431 	{
2432 		style = hp->style;
2433 		if (*hp->name != '?')
2434 			what = hp->name;
2435 	}
2436 	else
2437 	{
2438 		if ((style = state.force) < STYLE_man)
2439 			style = STYLE_man;
2440 		if (!(sp_help = sfstropen()))
2441 			goto nospace;
2442 		for (i = 0; i < elementsof(help_head); i++)
2443 			list(sp_help, &help_head[i]);
2444 		for (i = 0; i < elementsof(styles); i++)
2445 			sfprintf(sp_help, "[:%s?%s]", styles[i].match, styles[i].text);
2446 		for (i = 0; i < elementsof(help_tail); i++)
2447 			list(sp_help, &help_tail[i]);
2448 		if (!(opts = sfstruse(sp_help)))
2449 			goto nospace;
2450 	}
2451 
2452 	/*
2453 	 * this is a workaround for static optjoin() data
2454 	 * clobbered by plugins/builtins that may be called
2455 	 * evaluating \f...\f -- it would be good to hide
2456 	 * optjoin() interactions a bit more ...
2457 	 */
2458 
2459 	top = state.pass[0];
2460  again:
2461 	if (opts)
2462 	{
2463 		for (i = 0; i < state.npass; i++)
2464 			if (state.pass[i].oopts == opts)
2465 			{
2466 				o = &state.pass[i];
2467 				break;
2468 			}
2469 		if (i >= state.npass)
2470 		{
2471 			o = &one;
2472 			if (init((char*)opts, o))
2473 				goto nospace;
2474 		}
2475 		e = o + 1;
2476 	}
2477 	else
2478 	{
2479 		if (state.npass > 0)
2480 		{
2481 			o = state.pass;
2482 			e = o + state.npass;
2483 		}
2484 		else if (state.npass < 0)
2485 		{
2486 			o = &state.cache->pass;
2487 			e = o + 1;
2488 		}
2489 		else
2490 			return T(NiL, ID, "[* call optget() before opthelp() *]");
2491 		oopts = (const char*)state.pass[0].oopts;
2492 	}
2493 	if (style <= STYLE_usage)
2494 	{
2495 		if (!(sp_text = sfstropen()) || !(sp_info = sfstropen()))
2496 			goto nospace;
2497 		if (style >= STYLE_match && style < STYLE_keys && !(sp_body = sfstropen()))
2498 			goto nospace;
2499 	}
2500 	switch (style)
2501 	{
2502 	case STYLE_api:
2503 	case STYLE_html:
2504 	case STYLE_nroff:
2505 		state.emphasis = 0;
2506 		break;
2507 	case STYLE_usage:
2508 	case STYLE_keys:
2509 		for (q = o; q < e; q++)
2510 			if (!(q->flags & OPT_ignore) && !streq(q->catalog, o->catalog))
2511 				o = q;
2512 		/*FALLTHROUGH*/
2513 	case STYLE_posix:
2514 		sfputc(mp, '\f');
2515 		break;
2516 	default:
2517 		if (!state.emphasis)
2518 		{
2519 			if (x = getenv("ERROR_OPTIONS"))
2520 			{
2521 				if (strmatch(x, "*noemphasi*"))
2522 					break;
2523 				if (strmatch(x, "*emphasi*"))
2524 				{
2525 					state.emphasis = 1;
2526 					break;
2527 				}
2528 			}
2529 			if ((x = getenv("TERM")) && strmatch(x, "(ansi|vt100|xterm)*") && isatty(sffileno(sfstderr)))
2530 				state.emphasis = 1;
2531 		}
2532 		break;
2533 	}
2534 	x = "";
2535 	xl = 0;
2536 	for (q = o; q < e; q++)
2537 	{
2538 		if (q->flags & OPT_ignore)
2539 			continue;
2540 		section = q->section;
2541 		flags |= q->flags;
2542 		p = q->opts;
2543 		prefix = q->prefix;
2544 		version = q->version;
2545 		id = q->id;
2546 		catalog = q->catalog;
2547 		switch (style)
2548 		{
2549 		case STYLE_usage:
2550 			if (xl)
2551 				sfputc(mp, '\n');
2552 			else
2553 				xl = 1;
2554 			psp = 0;
2555 			for (;;)
2556 			{
2557 				switch (c = *p++)
2558 				{
2559 				case 0:
2560 					if (!(tsp = psp))
2561 						goto style_usage;
2562 					p = psp->ob;
2563 					psp = psp->next;
2564 					free(tsp);
2565 					continue;
2566 				case '\a':
2567 					c = 'a';
2568 					break;
2569 				case '\b':
2570 					c = 'b';
2571 					break;
2572 				case '\f':
2573 					psp = info(psp, p, NiL, sp_info, id);
2574 					if (psp->nb)
2575 						p = psp->nb;
2576 					else
2577 					{
2578 						p = psp->ob;
2579 						psp = psp->next;
2580 					}
2581 					continue;
2582 				case '\n':
2583 					c = 'n';
2584 					break;
2585 				case '\r':
2586 					c = 'r';
2587 					break;
2588 				case '\t':
2589 					c = 't';
2590 					break;
2591 				case '\v':
2592 					c = 'v';
2593 					break;
2594 				case '"':
2595 					c = '"';
2596 					break;
2597 				case '\'':
2598 					c = '\'';
2599 					break;
2600 				case '\\':
2601 					c = '\\';
2602 					break;
2603 				default:
2604 					sfputc(mp, c);
2605 					continue;
2606 				}
2607 				sfputc(mp, '\\');
2608 				sfputc(mp, c);
2609 			}
2610 		style_usage:
2611 			continue;
2612 		case STYLE_keys:
2613 			a = 0;
2614 			psp = 0;
2615 			vl = 0;
2616 			for (;;)
2617 			{
2618 				if (!(c = *p++))
2619 				{
2620 					if (!(tsp = psp))
2621 						break;
2622 					p = psp->ob;
2623 					psp = psp->next;
2624 					free(tsp);
2625 					continue;
2626 				}
2627 				if (c == '\f')
2628 				{
2629 					psp = info(psp, p, NiL, sp_info, id);
2630 					if (psp->nb)
2631 						p = psp->nb;
2632 					else
2633 					{
2634 						p = psp->ob;
2635 						psp = psp->next;
2636 					}
2637 					continue;
2638 				}
2639 				f = z = 1;
2640 				t = 0;
2641 				if (a == 0 && (c == ' ' || c == '\n' && *p == '\n'))
2642 				{
2643 					if (c == ' ' && *p == ']')
2644 					{
2645 						p++;
2646 						continue;
2647 					}
2648 					if (*p == '\n')
2649 						p++;
2650 					a = c;
2651 				}
2652 				else if (c == '\n')
2653 				{
2654 					if (a == ' ')
2655 						a = -1;
2656 					else if (a == '\n' || *p == '\n')
2657 					{
2658 						a = -1;
2659 						p++;
2660 					}
2661 					continue;
2662 				}
2663 				else if ((c == ':' || c == '#') && (*p == '[' || *p == '?' && *(p + 1) == '[' && p++))
2664 					p++;
2665 				else if (c != '[')
2666 				{
2667 					if (c == GO)
2668 						vl++;
2669 					else if (c == OG)
2670 						vl--;
2671 					continue;
2672 				}
2673 				else if (*p == ' ')
2674 				{
2675 					p++;
2676 					continue;
2677 				}
2678 				else if (*p == '-')
2679 				{
2680 					z = 0;
2681 					if (*++p == '-')
2682 					{
2683 						p = skip(p, 0, 0, 0, 1, 0, 1, version);
2684 						continue;
2685 					}
2686 				}
2687 				else if (*p == '+')
2688 				{
2689 					p++;
2690 					if (vl > 0 && *p != '\a')
2691 					{
2692 						f = 0;
2693 						p = skip(p, '?', 0, 0, 1, 0, 0, version);
2694 						if (*p == '?')
2695 							p++;
2696 					}
2697 				}
2698 				else
2699 				{
2700 					if (*(p + 1) == '\f' && (vp = state.vp))
2701 						p = expand(p + 2, NiL, &t, vp, id);
2702 					p = skip(p, ':', '?', 0, 1, 0, 0, version);
2703 					if (*p == ':')
2704 						p++;
2705 				}
2706 				if (f && *p == '?' && *(p + 1) != '?')
2707 				{
2708 					f = 0;
2709 					if (z)
2710 						p++;
2711 					else
2712 						p = skip(p, 0, 0, 0, 1, 0, 0, version);
2713 				}
2714 				if (*p == ']' && *(p + 1) != ']')
2715 				{
2716 					p++;
2717 					continue;
2718 				}
2719 				if (!*p)
2720 				{
2721 					if (!t)
2722 						break;
2723 					p = t;
2724 					t = 0;
2725 				}
2726 				m = sfstrtell(mp);
2727 				sfputc(mp, '"');
2728 				xl = 1;
2729 				/*UNDENT...*/
2730 
2731 	for (;;)
2732 	{
2733 		if (!(c = *p++))
2734 		{
2735 			if (t)
2736 			{
2737 				p = t;
2738 				t = 0;
2739 			}
2740 			if (!(tsp = psp))
2741 			{
2742 				p--;
2743 				break;
2744 			}
2745 			p = psp->ob;
2746 			psp = psp->next;
2747 			free(tsp);
2748 			continue;
2749 		}
2750 		if (a > 0)
2751 		{
2752 			if (c == '\n')
2753 			{
2754 				if (a == ' ')
2755 				{
2756 					a = -1;
2757 					break;
2758 				}
2759 				if (a == '\n' || *p == '\n')
2760 				{
2761 					a = -1;
2762 					p++;
2763 					break;
2764 				}
2765 			}
2766 		}
2767 		else if (c == ']')
2768 		{
2769 			if (*p != ']')
2770 			{
2771 				sfputc(mp, 0);
2772 				y = sfstrbase(mp) + m + 1;
2773 				if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT))
2774 				{
2775 					sfstrseek(mp, m, SEEK_SET);
2776 					xl = 0;
2777 				}
2778 				else
2779 					sfstrseek(mp, -1, SEEK_CUR);
2780 				break;
2781 			}
2782 			sfputc(mp, *p++);
2783 			continue;
2784 		}
2785 		switch (c)
2786 		{
2787 		case '?':
2788 			if (f)
2789 			{
2790 				if (*p == '?')
2791 				{
2792 					p++;
2793 					sfputc(mp, c);
2794 				}
2795 				else
2796 				{
2797 					f = 0;
2798 					sfputc(mp, 0);
2799 					y = sfstrbase(mp) + m + 1;
2800 					if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT))
2801 					{
2802 						sfstrseek(mp, m, SEEK_SET);
2803 						xl = 0;
2804 					}
2805 					else
2806 						sfstrseek(mp, -1, SEEK_CUR);
2807 					if (z && (*p != ']' || *(p + 1) == ']'))
2808 					{
2809 						if (xl)
2810 						{
2811 							sfputc(mp, '"');
2812 							sfputc(mp, '\n');
2813 						}
2814 						m = sfstrtell(mp);
2815 						sfputc(mp, '"');
2816 						xl = 1;
2817 					}
2818 					else
2819 					{
2820 						p = skip(p, 0, 0, 0, 1, 0, 0, version);
2821 						if (*p == '?')
2822 							p++;
2823 					}
2824 				}
2825 			}
2826 			else
2827 				sfputc(mp, c);
2828 			continue;
2829 		case ':':
2830 			if (f && *p == ':')
2831 				p++;
2832 			sfputc(mp, c);
2833 			continue;
2834 		case '\a':
2835 			c = 'a';
2836 			break;
2837 		case '\b':
2838 			c = 'b';
2839 			break;
2840 		case '\f':
2841 			c = 'f';
2842 			break;
2843 		case '\n':
2844 			c = 'n';
2845 			break;
2846 		case '\r':
2847 			c = 'r';
2848 			break;
2849 		case '\t':
2850 			c = 't';
2851 			break;
2852 		case '\v':
2853 			c = 'v';
2854 			break;
2855 		case '"':
2856 			c = '"';
2857 			break;
2858 		case '\\':
2859 			c = '\\';
2860 			break;
2861 		case CC_esc:
2862 			c = 'E';
2863 			break;
2864 		default:
2865 			sfputc(mp, c);
2866 			continue;
2867 		}
2868 		sfputc(mp, '\\');
2869 		sfputc(mp, c);
2870 	}
2871 
2872 				/*...INDENT*/
2873 				if (xl)
2874 				{
2875 					sfputc(mp, '"');
2876 					sfputc(mp, '\n');
2877 				}
2878 			}
2879 			continue;
2880 		}
2881 		z = 0;
2882 		head = 0;
2883 		mode = 0;
2884 		mutex = 0;
2885 		if (style > STYLE_short && style < STYLE_nroff && version < 1)
2886 		{
2887 			style = STYLE_short;
2888 			if (sp_body)
2889 			{
2890 				sfclose(sp_body);
2891 				sp_body = 0;
2892 			}
2893 		}
2894 		else if (style == STYLE_short && prefix < 2)
2895 			style = STYLE_long;
2896 		if (*p == ':')
2897 			p++;
2898 		if (*p == '+')
2899 		{
2900 			p++;
2901 			if (!(sp = sp_plus) && !(sp = sp_plus = sfstropen()))
2902 				goto nospace;
2903 		}
2904 		else if (style >= STYLE_match)
2905 			sp = sp_body;
2906 		else
2907 			sp = sp_text;
2908 		psp = 0;
2909 		for (;;)
2910 		{
2911 			if (!(*(p = next(p, version))))
2912 			{
2913 				if (!(tsp = psp))
2914 					break;
2915 				p = psp->ob;
2916 				psp = psp->next;
2917 				free(tsp);
2918 				continue;
2919 			}
2920 			if (*p == '\f')
2921 			{
2922 				psp = info(psp, p + 1, NiL, sp_info, id);
2923 				if (psp->nb)
2924 					p = psp->nb;
2925 				else
2926 				{
2927 					p = psp->ob;
2928 					psp = psp->next;
2929 				}
2930 				continue;
2931 			}
2932 			if (*p == '\n' || *p == ' ')
2933 			{
2934 				if (*(x = p = next(p + 1, version)))
2935 					while (*++p)
2936 						if (*p == '\n')
2937 						{
2938 							while (*++p == ' ' || *p == '\t' || *p == '\r');
2939 							if (*p == '\n')
2940 								break;
2941 						}
2942 				xl = p - x;
2943 				if (!*p)
2944 					break;
2945 				continue;
2946 			}
2947 			if (*p == OG)
2948 			{
2949 				p++;
2950 				continue;
2951 			}
2952 			message((-20, "opthelp: opt %s", show(p)));
2953 			if (z < 0)
2954 				z = 0;
2955 			a = 0;
2956 			f = 0;
2957 			w = 0;
2958 			d = 0;
2959 			s = 0;
2960 			rb = re = 0;
2961 			sl = 0;
2962 			vl = 0;
2963 			if (*p == '[')
2964 			{
2965 				if ((c = *(p = next(p + 1, version))) == '(')
2966 				{
2967 					p = nest(cb = p);
2968 					cl = p - cb;
2969 					c = *p;
2970 				}
2971 				else
2972 					cb = 0;
2973 				if (c == '-')
2974 				{
2975 					if (style >= STYLE_man)
2976 					{
2977 						if (*(p + 1) != '-')
2978 						{
2979 							if (!sp_misc && !(sp_misc = sfstropen()))
2980 								goto nospace;
2981 							else
2982 								p = textout(sp_misc, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
2983 							continue;
2984 						}
2985 					}
2986 					else if (style == STYLE_match && *what == '-')
2987 					{
2988 						if (*(p + 1) == '?' || *(s = skip(p + 1, ':', '?', 0, 1, 0, 0, version)) == '?' && isspace(*(s + 1)))
2989 							s = C("version");
2990 						else
2991 							s = p + 1;
2992 						w = (char*)what;
2993 						if (*s != '-' || *(w + 1) == '-')
2994 						{
2995 							if (*s == '-')
2996 								s++;
2997 							if (*(w + 1) == '-')
2998 								w++;
2999 							if (match(w + 1, s, version, id, catalog))
3000 							{
3001 								if (*(p + 1) == '-')
3002 									p++;
3003 								p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
3004 								matched = -1;
3005 								continue;
3006 							}
3007 						}
3008 					}
3009 					if (!z)
3010 						z = -1;
3011 				}
3012 				else if (c == '+')
3013 				{
3014 					if (style >= STYLE_man)
3015 					{
3016 						p = textout(sp_body, p, cb, cl, style, 0, 0, sp_info, version, id, catalog, &bflags);
3017 						if (!sp_head)
3018 						{
3019 							sp_head = sp_body;
3020 							hflags = dflags = bflags;
3021 							if (!(sp_body = sfstropen()))
3022 								goto nospace;
3023 						}
3024 						continue;
3025 					}
3026 					else if (style == STYLE_match && *what == '+')
3027 					{
3028 						if (paragraph)
3029 						{
3030 							if (p[1] == '?')
3031 							{
3032 								p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
3033 								continue;
3034 							}
3035 							paragraph = 0;
3036 						}
3037 						if (match((char*)what + 1, p + 1, version, id, catalog))
3038 						{
3039 							p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
3040 							matched = -1;
3041 							paragraph = 1;
3042 							continue;
3043 						}
3044 					}
3045 					if (!z)
3046 						z = -1;
3047 				}
3048 				else if (c == '[' || version < 1)
3049 				{
3050 					mutex++;
3051 					continue;
3052 				}
3053 				else
3054 				{
3055 					if (c == '!')
3056 					{
3057 						a |= OPT_invert;
3058 						p++;
3059 					}
3060 					rb = p;
3061 					if (*p != ':')
3062 					{
3063 						s = p;
3064 						if (*(p + 1) == '|')
3065 						{
3066 							while (*++p && *p != '=' && *p != '!' && *p != ':' && *p != '?');
3067 							if ((p - s) > 1)
3068 								sl = p - s;
3069 							if (*p == '!')
3070 								a |= OPT_invert;
3071 						}
3072 						if (*(p + 1) == '\f')
3073 							p++;
3074 						else
3075 							p = skip(p, ':', '?', 0, 1, 0, 0, version);
3076 						if (sl || (p - s) == 1 || *(s + 1) == '=' || *(s + 1) == '!' && (a |= OPT_invert) || *(s + 1) == '|')
3077 							f = *s;
3078 					}
3079 					re = p;
3080 					if (style <= STYLE_short)
3081 					{
3082 						if (!z && !f)
3083 							z = -1;
3084 					}
3085 					else
3086 					{
3087 						if (*p == '\f' && (vp = state.vp))
3088 							p = expand(p + 1, NiL, &t, vp, id);
3089 						else
3090 							t = 0;
3091 						if (*p == ':')
3092 						{
3093 							p = skip(w = p + 1, ':', '?', 0, 1, 0, 0, version);
3094 							if (!(wl = p - w))
3095 								w = 0;
3096 						}
3097 						else
3098 							wl = 0;
3099 						if (*p == ':' || *p == '?')
3100 						{
3101 							d = p;
3102 							p = skip(p, 0, 0, 0, 1, 0, 0, version);
3103 						}
3104 						else
3105 							d = 0;
3106 						if (style == STYLE_match)
3107 						{
3108 							if (wl && !match((char*)what, w, version, id, catalog))
3109 								wl = 0;
3110 							if ((!wl || *w == ':' || *w == '?') && (what[1] || sl && !memchr(s, what[0], sl) || !sl && what[0] != f))
3111 							{
3112 								w = 0;
3113 								if (!z)
3114 									z = -1;
3115 							}
3116 							else
3117 								matched = 1;
3118 						}
3119 						if (t)
3120 						{
3121 							p = t;
3122 							if (*p == ':' || *p == '?')
3123 							{
3124 								d = p;
3125 								p = skip(p, 0, 0, 0, 1, 0, 0, version);
3126 							}
3127 						}
3128 					}
3129 				}
3130 				p = skip(p, 0, 0, 0, 1, 0, 1, version);
3131 				if (*p == GO)
3132 					p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
3133 			}
3134 			else if (*p == ']')
3135 			{
3136 				if (mutex)
3137 				{
3138 					if (style >= STYLE_nroff)
3139 						sfputr(sp_body, "\n.OP - - anyof", '\n');
3140 					if (!(mutex & 1))
3141 					{
3142 						mutex--;
3143 						if (style <= STYLE_long)
3144 						{
3145 							sfputc(sp_body, ' ');
3146 							sfputc(sp_body, ']');
3147 						}
3148 					}
3149 					mutex--;
3150 				}
3151 				p++;
3152 				continue;
3153 			}
3154 			else if (*p == '?')
3155 			{
3156 				if (style < STYLE_match)
3157 					z = 1;
3158 				mode |= OPT_hidden;
3159 				p++;
3160 				continue;
3161 			}
3162 			else if (*p == '\\' && style==STYLE_posix)
3163 			{
3164 				if (*++p)
3165 					p++;
3166 				continue;
3167 			}
3168 			else
3169 			{
3170 				f = *p++;
3171 				s = 0;
3172 				if (style == STYLE_match && !z)
3173 					z = -1;
3174 			}
3175 			if (!z)
3176 			{
3177 				if (style == STYLE_long || prefix < 2 || (q->flags & OPT_long))
3178 					f = 0;
3179 				else if (style <= STYLE_short)
3180 					w = 0;
3181 				if (!f && !w)
3182 					z = -1;
3183 			}
3184 			ov = 0;
3185 			u = v = y = 0;
3186 			if (*p == ':' && (a |= OPT_string) || *p == '#' && (a |= OPT_number))
3187 			{
3188 				message((-21, "opthelp: arg %s", show(p)));
3189 				if (*++p == '?' || *p == *(p - 1))
3190 				{
3191 					p++;
3192 					a |= OPT_optional;
3193 				}
3194 				if (*(p = next(p, version)) == '[')
3195 				{
3196 					if (!z)
3197 					{
3198 						p = skip(y = p + 1, ':', '?', 0, 1, 0, 0, version);
3199 						while (*p == ':')
3200 						{
3201 							p = skip(t = p + 1, ':', '?', 0, 1, 0, 0, version);
3202 							m = p - t;
3203 							if (*t == '!')
3204 							{
3205 								ov = t + 1;
3206 								ol = m - 1;
3207 							}
3208 							else if (*t == '=')
3209 							{
3210 								v = t + 1;
3211 								vl = m - 1;
3212 							}
3213 							else
3214 								for (j = 0; j < elementsof(attrs); j++)
3215 									if (strneq(t, attrs[j].name, m))
3216 									{
3217 										a |= attrs[j].flag;
3218 										break;
3219 									}
3220 						}
3221 						if (*p == '?')
3222 							u = p;
3223 						p = skip(p, 0, 0, 0, 1, 0, 1, version);
3224 					}
3225 					else
3226 						p = skip(p + 1, 0, 0, 0, 1, 0, 1, version);
3227 				}
3228 				else
3229 					y = (a & OPT_number) ? T(NiL, ID, "#") : T(NiL, ID, "arg");
3230 			}
3231 			else
3232 				a |= OPT_flag;
3233 			if (!z)
3234 			{
3235 				if (style <= STYLE_short && !y && !mutex || style == STYLE_posix)
3236 				{
3237 					if (style != STYLE_posix && !sfstrtell(sp))
3238 					{
3239 						sfputc(sp, '[');
3240 						if (sp == sp_plus)
3241 							sfputc(sp, '+');
3242 						sfputc(sp, '-');
3243 					}
3244 					if (!sl)
3245 						sfputc(sp, f);
3246 					else
3247 						for (c = 0; c < sl; c++)
3248 							if (s[c] != '|')
3249 								sfputc(sp, s[c]);
3250 					if (style == STYLE_posix && y)
3251 						sfputc(sp, ':');
3252 				}
3253 				else
3254 				{
3255 					if (style >= STYLE_match)
3256 					{
3257 						sfputc(sp_body, '\n');
3258 						if (!head)
3259 						{
3260 							head = 1;
3261 							item(sp_body, (flags & OPT_functions) ? C("FUNCTIONS") : C("OPTIONS"), 0, 0, style, sp_info, version, id, ID, &bflags);
3262 						}
3263 						if (style >= STYLE_nroff)
3264 						{
3265 							if (mutex & 1)
3266 							{
3267 								mutex++;
3268 								sfputr(sp_body, "\n.OP - - oneof", '\n');
3269 							}
3270 						}
3271 						else
3272 							sfputc(sp_body, '\t');
3273 					}
3274 					else
3275 					{
3276 						if (sp_body)
3277 							sfputc(sp_body, ' ');
3278 						else if (!(sp_body = sfstropen()))
3279 							goto nospace;
3280 						if (mutex)
3281 						{
3282 							if (mutex & 1)
3283 							{
3284 								mutex++;
3285 								sfputc(sp_body, '[');
3286 							}
3287 							else
3288 								sfputc(sp_body, '|');
3289 							sfputc(sp_body, ' ');
3290 						}
3291 						else
3292 							sfputc(sp_body, '[');
3293 					}
3294 					if (style >= STYLE_nroff)
3295 					{
3296 						if (flags & OPT_functions)
3297 						{
3298 							sfputr(sp_body, ".FN", ' ');
3299 							if (re > rb)
3300 								sfwrite(sp_body, rb, re - rb);
3301 							else
3302 								sfputr(sp, "void", -1);
3303 							if (w)
3304 								label(sp_body, ' ', w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog);
3305 						}
3306 						else
3307 						{
3308 							sfputr(sp_body, ".OP", ' ');
3309 							if (sl)
3310 								sfwrite(sp_body, s, sl);
3311 							else
3312 								sfputc(sp_body, f ? f : '-');
3313 							sfputc(sp_body, ' ');
3314 							if (w)
3315 							{
3316 								if (label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, catalog))
3317 								{
3318 									sfputc(sp_body, '|');
3319 									label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, native);
3320 								}
3321 							}
3322 							else
3323 								sfputc(sp_body, '-');
3324 							sfputc(sp_body, ' ');
3325 							m = a & OPT_TYPE;
3326 							for (j = 0; j < elementsof(attrs); j++)
3327 								if (m & attrs[j].flag)
3328 								{
3329 									sfputr(sp_body, attrs[j].name, -1);
3330 									break;
3331 								}
3332 							if (m = (a & ~m) | mode)
3333 								for (j = 0; j < elementsof(attrs); j++)
3334 									if (m & attrs[j].flag)
3335 									{
3336 										sfputc(sp_body, ':');
3337 										sfputr(sp_body, attrs[j].name, -1);
3338 									}
3339 							sfputc(sp_body, ' ');
3340 							if (y)
3341 								label(sp_body, 0, y, 0, -1, 0, style, 0, sp_info, version, id, catalog);
3342 							else
3343 								sfputc(sp_body, '-');
3344 							if (v)
3345 								sfprintf(sp_body, " %-.*s", vl, v);
3346 						}
3347 					}
3348 					else
3349 					{
3350 						if (f)
3351 						{
3352 							if (sp_body == sp_plus)
3353 								sfputc(sp_body, '+');
3354 							sfputc(sp_body, '-');
3355 							sfputr(sp_body, font(FONT_BOLD, style, 1), -1);
3356 							if (!sl)
3357 							{
3358 								sfputc(sp_body, f);
3359 								if (f == '-' && y)
3360 								{
3361 									y = 0;
3362 									sfputr(sp_body, C("long-option[=value]"), -1);
3363 								}
3364 							}
3365 							else
3366 								sfwrite(sp_body, s, sl);
3367 							sfputr(sp_body, font(FONT_BOLD, style, 0), -1);
3368 							if (w)
3369 							{
3370 								sfputc(sp_body, ',');
3371 								sfputc(sp_body, ' ');
3372 							}
3373 						}
3374 						else if ((flags & OPT_functions) && re > rb)
3375 						{
3376 							sfwrite(sp_body, rb, re - rb);
3377 							sfputc(sp_body, ' ');
3378 						}
3379 						if (w)
3380 						{
3381 							if (prefix > 0)
3382 							{
3383 								sfputc(sp_body, '-');
3384 								if (prefix > 1)
3385 									sfputc(sp_body, '-');
3386 							}
3387 							if (label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog))
3388 							{
3389 								sfputc(sp_body, '|');
3390 								label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, native);
3391 							}
3392 						}
3393 						if (y)
3394 						{
3395 							if (a & OPT_optional)
3396 								sfputc(sp_body, '[');
3397 							else if (!w)
3398 								sfputc(sp_body, ' ');
3399 							if (w)
3400 								sfputc(sp_body, prefix == 1 ? ' ' : '=');
3401 							label(sp_body, 0, y, 0, -1, 0, style, FONT_ITALIC, sp_info, version, id, catalog);
3402 							if (a & OPT_optional)
3403 								sfputc(sp_body, ']');
3404 						}
3405 					}
3406 					if (style >= STYLE_match)
3407 					{
3408 						if (d)
3409 						{
3410 							textout(sp_body, d, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags);
3411 							cb = 0;
3412 						}
3413 						if (u)
3414 							textout(sp_body, u, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags);
3415 						if ((a & OPT_invert) && w && (d || u))
3416 						{
3417 							u = skip(w, ':', '?', 0, 1, 0, 0, version);
3418 							if (f)
3419 								sfprintf(sp_info, " %s; -\b%c\b %s --\bno%-.*s\b.", T(NiL, ID, "On by default"), f, T(NiL, ID, "means"), u - w, w);
3420 							else
3421 								sfprintf(sp_info, " %s %s\bno%-.*s\b %s.", T(NiL, ID, "On by default; use"), "--"+2-prefix, u - w, w, T(NiL, ID, "to turn off"));
3422 							if (!(t = sfstruse(sp_info)))
3423 								goto nospace;
3424 							textout(sp_body, t, 0, 0, style, 0, 0, sp_info, version, NiL, NiL, &bflags);
3425 						}
3426 						if (*p == GO)
3427 						{
3428 							p = u ? skip(p + 1, 0, 0, 0, 0, 1, 1, version) : textout(sp_body, p, 0, 0, style, 4, 0, sp_info, version, id, catalog, &bflags);
3429 							y = "+?";
3430 						}
3431 						else
3432 							y = " ";
3433 						if (a & OPT_optional)
3434 						{
3435 							if (ov)
3436 							{
3437 								sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "If the option value is omitted then"));
3438 								t = ov + ol;
3439 								while (ov < t)
3440 								{
3441 									if (((c = *ov++) == ':' || c == '?') && *ov == c)
3442 										ov++;
3443 									sfputc(sp_info, c);
3444 								}
3445 								sfprintf(sp_info, "\b %s.", T(NiL, ID, "is assumed"));
3446 							}
3447 							else
3448 								sfprintf(sp_info, "%s%s", y, T(NiL, ID, "The option value may be omitted."));
3449 							if (!(t = sfstruse(sp_info)))
3450 								goto nospace;
3451 							textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags);
3452 							y = " ";
3453 						}
3454 						if (v)
3455 						{
3456 							sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "The default value is"));
3457 							t = v + vl;
3458 							while (v < t)
3459 							{
3460 								if (((c = *v++) == ':' || c == '?') && *v == c)
3461 									v++;
3462 								sfputc(sp_info, c);
3463 							}
3464 							sfputc(sp_info, '\b');
3465 							sfputc(sp_info, '.');
3466 							if (!(t = sfstruse(sp_info)))
3467 								goto nospace;
3468 							textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags);
3469 						}
3470 					}
3471 					else if (!mutex)
3472 						sfputc(sp_body, ']');
3473 				}
3474 				if (*p == GO)
3475 				{
3476 					if (style >= STYLE_match)
3477 						p = textout(sp_body, p, 0, 0, style, 4, 0, sp_info, version, id, catalog, &bflags);
3478 					else
3479 						p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
3480 				}
3481 			}
3482 			else if (*p == GO)
3483 				p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
3484 		}
3485 		psp = pop(psp);
3486 		if (sp_misc)
3487 		{
3488 			if (!(p = sfstruse(sp_misc)))
3489 				goto nospace;
3490 			for (t = p; *t == '\t' || *t == '\n'; t++);
3491 			if (*t)
3492 			{
3493 				item(sp_body, C("IMPLEMENTATION"), 0, 0, style, sp_info, version, id, ID, &bflags);
3494 				sfputr(sp_body, p, -1);
3495 			}
3496 		}
3497 	}
3498 	if (oopts != o->oopts && oopts == top.oopts)
3499 		state.pass[0] = top;
3500 	version = o->version;
3501 	id = o->id;
3502 	catalog = o->catalog;
3503 	if (style >= STYLE_keys)
3504 	{
3505 		if (sp_info)
3506 			sfclose(sp_info);
3507 		if (style == STYLE_keys && sfstrtell(mp) > 1)
3508 			sfstrseek(mp, -1, SEEK_CUR);
3509 		if (!(p = sfstruse(mp)))
3510 			goto