xref: /titanic_52/usr/src/contrib/ast/src/lib/libast/misc/optget.c (revision 906afcb89d0412cc073b95c2d701a804a8cdb62c)
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*
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*
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*
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*
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*
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*
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*
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*
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
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*
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*
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*
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
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
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*
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*
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*
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
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
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
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*
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*
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
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*
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 nospace;
3511 		return opt_info.msg = p;
3512 	}
3513 	sp = sp_text;
3514 	if (sfstrtell(sp) && style != STYLE_posix)
3515 		sfputc(sp, ']');
3516 	if (style == STYLE_nroff)
3517 	{
3518 		char	rd[64];
3519 		char	ud[64];
3520 
3521 		s = o->id;
3522 		t = ud;
3523 		while (t < &ud[sizeof(ud)-2] && (c = *s++))
3524 		{
3525 			if (islower(c))
3526 				c = toupper(c);
3527 			*t++ = c;
3528 		}
3529 		*t = 0;
3530 		t = rd;
3531 		if (s = o->release)
3532 		{
3533 			*t++ = ' ';
3534 			while (t < &rd[sizeof(rd)-2] && (c = *s++) && c != ']')
3535 				*t++ = c;
3536 		}
3537 		*t = 0;
3538 		sfprintf(sp, "\
3539 .\\\" format with nroff|troff|groff -man\n\
3540 .TH %s %s%s\n\
3541 .fp 5 CW\n\
3542 .nr mH 5\n\
3543 .de H0\n\
3544 .nr mH 0\n\
3545 .in 5n\n\
3546 \\fB\\\\$1\\fP\n\
3547 .in 7n\n\
3548 ..\n\
3549 .de H1\n\
3550 .nr mH 1\n\
3551 .in 7n\n\
3552 \\fB\\\\$1\\fP\n\
3553 .in 9n\n\
3554 ..\n\
3555 .de H2\n\
3556 .nr mH 2\n\
3557 .in 11n\n\
3558 \\fB\\\\$1\\fP\n\
3559 .in 13n\n\
3560 ..\n\
3561 .de H3\n\
3562 .nr mH 3\n\
3563 .in 15n\n\
3564 \\fB\\\\$1\\fP\n\
3565 .in 17n\n\
3566 ..\n\
3567 .de H4\n\
3568 .nr mH 4\n\
3569 .in 19n\n\
3570 \\fB\\\\$1\\fP\n\
3571 .in 21n\n\
3572 ..\n\
3573 .de OP\n\
3574 .nr mH 0\n\
3575 .ie !'\\\\$1'-' \\{\n\
3576 .ds mO \\\\fB\\\\-\\\\$1\\\\fP\n\
3577 .ds mS ,\\\\0\n\
3578 .\\}\n\
3579 .el \\{\n\
3580 .ds mO \\\\&\n\
3581 .ds mS \\\\&\n\
3582 .\\}\n\
3583 .ie '\\\\$2'-' \\{\n\
3584 .if !'\\\\$4'-' .as mO \\\\0\\\\fI\\\\$4\\\\fP\n\
3585 .\\}\n\
3586 .el \\{\n\
3587 .as mO \\\\*(mS\\\\fB%s\\\\$2\\\\fP\n\
3588 .if !'\\\\$4'-' .as mO =\\\\fI\\\\$4\\\\fP\n\
3589 .\\}\n\
3590 .in 5n\n\
3591 \\\\*(mO\n\
3592 .in 9n\n\
3593 ..\n\
3594 .de SP\n\
3595 .if \\\\n(mH==2 .in 9n\n\
3596 .if \\\\n(mH==3 .in 13n\n\
3597 .if \\\\n(mH==4 .in 17n\n\
3598 ..\n\
3599 .de FN\n\
3600 .nr mH 0\n\
3601 .in 5n\n\
3602 \\\\$1 \\\\$2\n\
3603 .in 9n\n\
3604 ..\n\
3605 .de DS\n\
3606 .in +3n\n\
3607 .ft 5\n\
3608 .nf\n\
3609 ..\n\
3610 .de DE\n\
3611 .fi\n\
3612 .ft R\n\
3613 .in -3n\n\
3614 ..\n\
3615 "
3616 , ud
3617 , section
3618 , rd
3619 , o->prefix == 2 ? "\\\\-\\\\-" : o->prefix == 1 ? "\\\\-" : ""
3620 );
3621 	}
3622 	if (style == STYLE_match)
3623 	{
3624 		if (!matched)
3625 		{
3626 			if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what))
3627 			{
3628 				if (!sp_help && !(sp_help = sfstropen()))
3629 					goto nospace;
3630 				sfprintf(sp_help, "[-][:%s?%s]", hp->match, hp->text);
3631 				if (!(opts = sfstruse(sp_help)))
3632 					goto nospace;
3633 				goto again;
3634 			}
3635 			s = (char*)unknown;
3636 			goto nope;
3637 		}
3638 		else if (matched < 0)
3639 			x = 0;
3640 	}
3641 	if (sp_plus)
3642 	{
3643 		if (sfstrtell(sp_plus))
3644 		{
3645 			if (sfstrtell(sp))
3646 				sfputc(sp, ' ');
3647 			if (!(t = sfstruse(sp_plus)))
3648 				goto nospace;
3649 			sfputr(sp, t, ']');
3650 		}
3651 		sfclose(sp_plus);
3652 	}
3653 	if (style >= STYLE_man)
3654 	{
3655 		if (sp_head)
3656 		{
3657 			if (!(t = sfstruse(sp_head)))
3658 				goto nospace;
3659 			for (; *t == '\n'; t++);
3660 			sfputr(sp, t, '\n');
3661 			sfclose(sp_head);
3662 			sp_head = 0;
3663 		}
3664 		if (x)
3665 			item(sp, C("SYNOPSIS"), 0, 0, style, sp_info, version, id, ID, &hflags);
3666 	}
3667 	if (x)
3668 	{
3669 		for (t = x + xl; t > x && (*(t - 1) == '\n' || *(t - 1) == '\r'); t--);
3670 		xl = t - x;
3671 		if (style >= STYLE_match)
3672 		{
3673 			u = id;
3674 			if (o->flags & OPT_functions)
3675 				t = 0;
3676 			else if (t = strchr(u, ':'))
3677 			{
3678 				if ((o->flags & OPT_module) && *(t + 1) == ':' && *(t + 2))
3679 				{
3680 					u = t + 2;
3681 					t = 0;
3682 				}
3683 				else
3684 					*t = 0;
3685 			}
3686 			args(sp, x, xl, o->flags, style, sp_info, version, u, catalog);
3687 			if (t)
3688 				*t = ':';
3689 			x = 0;
3690 		}
3691 	}
3692 	if (sp_body)
3693 	{
3694 		if (sfstrtell(sp_body))
3695 		{
3696 			if (style < STYLE_match && sfstrtell(sp))
3697 				sfputc(sp, ' ');
3698 			if (!(t = sfstruse(sp_body)))
3699 				goto nospace;
3700 			if (style == STYLE_html && !(dflags & HELP_head) && (bflags & HELP_head))
3701 				sfputr(sp, "\n</DIV>", '\n');
3702 			sfputr(sp, t, -1);
3703 		}
3704 		sfclose(sp_body);
3705 		sp_body = 0;
3706 	}
3707 	if (x && style != STYLE_posix)
3708 		args(sp, x, xl, flags, style, sp_info, version, id, catalog);
3709 	if (sp_info)
3710 	{
3711 		sfclose(sp_info);
3712 		sp_info = 0;
3713 	}
3714 	if (sp_misc)
3715 	{
3716 		sfclose(sp_misc);
3717 		sp_misc = 0;
3718 	}
3719 	if (!(p = sfstruse(sp)))
3720 		goto nospace;
3721 	astwinsize(1, NiL, &state.width);
3722 	if (state.width < 20)
3723 		state.width = OPT_WIDTH;
3724 	m = strlen((style <= STYLE_long && error_info.id && !strchr(error_info.id, '/')) ? error_info.id : id) + 1;
3725 	margin = style == STYLE_api ? (8 * 1024) : (state.width - 1);
3726 	if (!(state.flags & OPT_preformat))
3727 	{
3728 		if (style >= STYLE_man || matched < 0)
3729 		{
3730 			sfputc(mp, '\f');
3731 			ts = 0;
3732 		}
3733 		else
3734 			ts = OPT_USAGE + m;
3735 		if (style == STYLE_html)
3736 		{
3737 			char	ud[64];
3738 
3739 			s = id;
3740 			t = ud;
3741 			while (t < &ud[sizeof(ud)-2] && (c = *s++))
3742 			{
3743 				if (islower(c))
3744 					c = toupper(c);
3745 				*t++ = c;
3746 			}
3747 			*t = 0;
3748 			sfprintf(mp, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n<HTML>\n<HEAD>\n<META name=\"generator\" content=\"optget (AT&T Research) 2011-11-11\">\n%s<TITLE>%s man document</TITLE>\n<STYLE type=\"text/css\">\ndiv.SH { padding-left:2em; text-indent:0em; }\ndiv.SY { padding-left:4em; text-indent:-2em; }\ndt { float:left; clear:both; }\ndd { margin-left:3em; }\n</STYLE>\n</HEAD>\n<BODY bgcolor=white>\n", (state.flags & OPT_proprietary) ? "<!--INTERNAL-->\n" : "", id);
3749 			sfprintf(mp, "<H4><TABLE width=100%%><TR><TH align=left>%s&nbsp;(&nbsp;%s&nbsp;)&nbsp;<TH align=center><A href=\".\" title=\"Index\">%s</A><TH align=right>%s&nbsp;(&nbsp;%s&nbsp;)</TR></TABLE></H4>\n<HR>\n", ud, section, T(NiL, ID, secname(section)), ud, section);
3750 			co = 2;
3751 			pt = ptstk;
3752 			pt->level = 0;
3753 			pt->id = TAG_DIV;
3754 		}
3755 		else
3756 			co = 0;
3757 		if ((rm = margin - ts) < OPT_MARGIN)
3758 			rm = OPT_MARGIN;
3759 		ip = indent;
3760 		ip->stop = (ip+1)->stop = style >= STYLE_html ? 0 : 2;
3761 		tp = 0;
3762 		n = 0;
3763 		head = 1;
3764 		while (*p == '\n')
3765 			p++;
3766 		while (c = *p++)
3767 		{
3768 			if (c == '\n')
3769 			{
3770 				ip = indent;
3771 				n = 0;
3772 				tp = 0;
3773 				sfputc(mp, '\n');
3774 				co = 0;
3775 				rm = margin;
3776 				ts = ip->stop;
3777 				if (*p == '\n')
3778 				{
3779 					while (*++p == '\n');
3780 					if ((style == STYLE_man || style == STYLE_html) && (!head || *p != ' ' && *p != '\t'))
3781 					{
3782 						if (style == STYLE_man)
3783 							p--;
3784 						else
3785 							sfprintf(mp, "<P>\n");
3786 					}
3787 				}
3788 				head = *p != ' ' && *p != '\t';
3789 				if (style == STYLE_html && (*p != '<' || !strneq(p, "<BR>", 4) && !strneq(p, "<P>", 3)))
3790 				{
3791 					y = p;
3792 					while (*p == '\t')
3793 						p++;
3794 					if (*p == '\n')
3795 						continue;
3796 					j = p - y;
3797 					if (j > pt->level)
3798 					{
3799 						pt++;
3800 						pt->level = j;
3801 						pt->id = TAG_NONE;
3802 						for (y = p; *y && *y != '\n'; y++)
3803 							if (*y == '\t')
3804 							{
3805 								pt->id = TAG_DL;
3806 								sfprintf(mp, "<DL>\n");
3807 								break;
3808 							}
3809 					}
3810 					else
3811 						while (j < pt->level && pt > ptstk)
3812 						{
3813 							sfprintf(mp, "%s", end[pt->id]);
3814 							pt--;
3815 						}
3816 					if (pt->id == TAG_DL)
3817 					{
3818 						dt = p;
3819 						sfprintf(mp, "<DT>");
3820 					}
3821 					else
3822 						dt = 0;
3823 				}
3824 			}
3825 			else if (c == '\t')
3826 			{
3827 				if (style == STYLE_html)
3828 				{
3829 					while (*p == '\t')
3830 						p++;
3831 					if (*p != '\n')
3832 					{
3833 						co += sfprintf(mp, "<DD>");
3834 						if (dt)
3835 						{
3836 							c = 0;
3837 							m = 0;
3838 							for (;;)
3839 							{
3840 								switch (*dt++)
3841 								{
3842 								case '\t':
3843 									break;
3844 								case '<':
3845 									c = '>';
3846 									continue;
3847 								case '>':
3848 									if (c == '>')
3849 										c = 0;
3850 									else
3851 										m++;
3852 									continue;
3853 								case '&':
3854 									c = ';';
3855 									continue;
3856 								case ';':
3857 									if (c == ';')
3858 										c = 0;
3859 									m++;
3860 									continue;
3861 								default:
3862 									if (!c)
3863 										m++;
3864 									continue;
3865 								}
3866 								break;
3867 							}
3868 							if (m >= 5)
3869 								co += sfprintf(mp, "<BR>");
3870 						}
3871 					}
3872 				}
3873 				else
3874 				{
3875 					if ((ip+1)->stop)
3876 					{
3877 						do
3878 						{
3879 							ip++;
3880 							if (*p != '\t')
3881 								break;
3882 							p++;
3883 						} while ((ip+1)->stop);
3884 						if (*p == '\n')
3885 							continue;
3886 						ts = ip->stop;
3887 						if (co >= ts)
3888 						{
3889 							sfputc(mp, '\n');
3890 							co = 0;
3891 							rm = margin;
3892 							ts = ip->stop;
3893 						}
3894 					}
3895 					while (co < ts)
3896 					{
3897 						sfputc(mp, ' ');
3898 						co++;
3899 					}
3900 				}
3901 			}
3902 			else
3903 			{
3904 				if (c == ' ' && !n)
3905 				{
3906 					if (co >= rm)
3907 						tp = 0;
3908 					else
3909 					{
3910 						tp = sfstrtell(mp);
3911 						pp = p;
3912 					}
3913 					if (style == STYLE_nroff && !co)
3914 						continue;
3915 				}
3916 				else if (style == STYLE_html)
3917 				{
3918 					if (c == '<')
3919 					{
3920 						if (strneq(p, "NOBR>", 5))
3921 							n++;
3922 						else if (n && strneq(p, "/NOBR>", 6) && !--n)
3923 						{
3924 							for (y = p += 6; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++)
3925 								if (c == '[')
3926 									sfputr(mp, "&#0091;", -1);
3927 								else if (c == ']')
3928 									sfputr(mp, "&#0093;", -1);
3929 								else
3930 									sfputc(mp, c);
3931 							sfwrite(mp, "</NOBR", 6);
3932 							c = '>';
3933 							co += p - y + 6;
3934 						}
3935 					}
3936 					else if (c == '>' && !n)
3937 					{
3938 						for (y = --p; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++)
3939 							if (c == '[')
3940 								sfputr(mp, "&#0091;", -1);
3941 							else if (c == ']')
3942 								sfputr(mp, "&#0093;", -1);
3943 							else
3944 								sfputc(mp, c);
3945 						c = *sfstrseek(mp, -1, SEEK_CUR);
3946 						if (p > y + 1)
3947 						{
3948 							tp = 0;
3949 							co += p - y - 1;
3950 						}
3951 						if (co >= rm)
3952 							tp = 0;
3953 						else
3954 						{
3955 							tp = sfstrtell(mp);
3956 							pp = p;
3957 						}
3958 					}
3959 					else if (c == '[')
3960 					{
3961 						sfputr(mp, "&#0091", -1);
3962 						c = ';';
3963 					}
3964 					else if (c == ']')
3965 					{
3966 						sfputr(mp, "&#0093", -1);
3967 						c = ';';
3968 					}
3969 					else if (c == 'h')
3970 					{
3971 						y = p;
3972 						if (*y++ == 't' && *y++ == 't' && *y++ == 'p' && (*y == ':' || *y++ == 's' && *y == ':') && *y++ == ':' && *y++ == '/' && *y++ == '/')
3973 						{
3974 							while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.')
3975 								y++;
3976 							if (*y == '?')
3977 								while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.' || *y == '?' || *y == '=' || *y == '%' || *y == '&' || *y == ';' || *y == '#')
3978 									y++;
3979 							if (*(y - 1) == '.')
3980 								y--;
3981 							p--;
3982 							sfprintf(mp, "<A href=\"%-.*s\">%-.*s</A", y - p, p, y - p, p);
3983 							p = y;
3984 							c = '>';
3985 						}
3986 					}
3987 					else if (c == 'C')
3988 					{
3989 						y = p;
3990 						if (*y++ == 'o' && *y++ == 'p' && *y++ == 'y' && *y++ == 'r' && *y++ == 'i' && *y++ == 'g' && *y++ == 'h' && *y++ == 't' && *y++ == ' ' && *y++ == '(' && (*y++ == 'c' || *(y - 1) == 'C') && *y++ == ')')
3991 						{
3992 							sfputr(mp, "Copyright &copy", -1);
3993 							p = y;
3994 							c = ';';
3995 						}
3996 					}
3997 				}
3998 				else if (c == ']')
3999 				{
4000 					if (n)
4001 						n--;
4002 				}
4003 				else if (c == '[')
4004 					n++;
4005 				if (c == CC_esc)
4006 				{
4007 					sfputc(mp, c);
4008 					do
4009 					{
4010 						if (!(c = *p++))
4011 						{
4012 							p--;
4013 							break;
4014 						}
4015 						sfputc(mp, c);
4016 					} while (c < 'a' || c > 'z');
4017 				}
4018 				else if (co++ >= rm && !n)
4019 				{
4020 					if (tp)
4021 					{
4022 						if (*sfstrseek(mp, tp, SEEK_SET) != ' ')
4023 							sfstrseek(mp, 1, SEEK_CUR);
4024 						tp = 0;
4025 						p = pp;
4026 						n = 0;
4027 					}
4028 					else if (c != ' ' && c != '\n')
4029 						sfputc(mp, c);
4030 					if (*p == ' ')
4031 						p++;
4032 					if (*p != '\n')
4033 					{
4034 						sfputc(mp, '\n');
4035 						for (co = 0; co < ts; co++)
4036 							sfputc(mp, ' ');
4037 						rm = margin;
4038 					}
4039 				}
4040 				else
4041 					sfputc(mp, c);
4042 			}
4043 		}
4044 		for (d = sfstrbase(mp), t = sfstrseek(mp, 0, SEEK_CUR); t > d && ((c = *(t - 1)) == '\n' || c == '\r' || c == ' ' || c == '\t'); t--);
4045 		sfstrseek(mp, t - d, SEEK_SET);
4046 		if (style == STYLE_html)
4047 		{
4048 			sfprintf(mp, "\n");
4049 			while (pt > ptstk)
4050 			{
4051 				sfprintf(mp, "%s", end[pt->id]);
4052 				pt--;
4053 			}
4054 			sfprintf(mp, "</DIV>\n</BODY>\n</HTML>");
4055 		}
4056 	}
4057 	else
4058 		sfputr(mp, p, 0);
4059 	if (!(p = sfstruse(mp)))
4060 		goto nospace;
4061 	if (sp)
4062 		sfclose(sp);
4063 	return opt_info.msg = p;
4064  nospace:
4065 	s = T(NiL, ID, "[* out of space *]");
4066  nope:
4067 	if (psp)
4068 		pop(psp);
4069 	if (sp_help)
4070 		sfclose(sp_help);
4071 	if (sp_text)
4072 		sfclose(sp_text);
4073 	if (sp_plus)
4074 		sfclose(sp_plus);
4075 	if (sp_info)
4076 		sfclose(sp_info);
4077 	if (sp_head)
4078 		sfclose(sp_head);
4079 	if (sp_body)
4080 		sfclose(sp_body);
4081 	if (sp_misc)
4082 		sfclose(sp_misc);
4083 	return s;
4084 }
4085 
4086 /*
4087  * compatibility wrapper to opthelp()
4088  */
4089 
4090 char*
4091 optusage(const char* opts)
4092 {
4093 	return opthelp(opts, NiL);
4094 }
4095 
4096 /*
4097  * convert number using strtonll() *except* that
4098  * 0*[[:digit:]].* is treated as [[:digit:]].*
4099  * i.e., it looks octal but isn't, to meet
4100  * posix Utility Argument Syntax -- use
4101  * 0x.* or <base>#* for alternate bases
4102  */
4103 
4104 static intmax_t
4105 optnumber(const char* s, char** t, int* e)
4106 {
4107 	intmax_t	n;
4108 	int		oerrno;
4109 
4110 	while (*s == '0' && isdigit(*(s + 1)))
4111 		s++;
4112 	oerrno = errno;
4113 	errno = 0;
4114 	n = strtonll(s, t, NiL, 0);
4115 	if (e)
4116 		*e = errno;
4117 	errno = oerrno;
4118 	return n;
4119 }
4120 
4121 /*
4122  * point opt_info.arg to an error/info message for opt_info.name
4123  * p points to opts location for opt_info.name
4124  * optget() return value is returned
4125  */
4126 
4127 static int
4128 opterror(register char* p, int err, int version, char* id, char* catalog)
4129 {
4130 	register Sfio_t*	mp;
4131 	register Sfio_t*	tp;
4132 	register char*		s;
4133 	register int		c;
4134 
4135 	if (opt_info.num != LONG_MIN)
4136 		opt_info.num = (long)(opt_info.number = 0);
4137 	if (!p || !(mp = state.mp) && !(mp = state.mp = sfstropen()))
4138 		goto nospace;
4139 	s = *p == '-' ? p : opt_info.name;
4140 	if (*p == '!')
4141 	{
4142 		while (*s == '-')
4143 			sfputc(mp, *s++);
4144 		sfputc(mp, 'n');
4145 		sfputc(mp, 'o');
4146 	}
4147 	sfputr(mp, s, ':');
4148 	sfputc(mp, ' ');
4149 	if (*p == '#' || *p == ':')
4150 	{
4151 		if (*p == '#')
4152 		{
4153 			s = T(NiL, ID, "numeric");
4154 			sfputr(mp, s, ' ');
4155 		}
4156 		if (*(p = next(p + 1, version)) == '[')
4157 		{
4158 			p = skip(s = p + 1, ':', '?', 0, 1, 0, 0, version);
4159 			tp = X(catalog) ? state.xp : mp;
4160 			while (s < p)
4161 			{
4162 				if ((c = *s++) == '?' || c == ']')
4163 					s++;
4164 				sfputc(tp, c);
4165 			}
4166 			if (!X(catalog))
4167 				sfputc(mp, ' ');
4168 			else if (p = sfstruse(tp))
4169 				sfputr(mp, T(id, catalog, p), ' ');
4170 			else
4171 				goto nospace;
4172 		}
4173 		p = opt_info.name[2] ? C("value expected") : C("argument expected");
4174 	}
4175 	else if (*p == '*' || *p == '&')
4176 	{
4177 		sfputr(mp, opt_info.arg, ':');
4178 		sfputc(mp, ' ');
4179 		p = *p == '&' ? C("ambiguous option argument value") : C("unknown option argument value");
4180 	}
4181 	else if (*p == '=' || *p == '!')
4182 		p = C("value not expected");
4183 	else if (*p == '?')
4184 		p = *(p + 1) == '?' ? C("optget: option not supported") : C("ambiguous option");
4185 	else if (*p == '+')
4186 		p = C("section not found");
4187 	else
4188 	{
4189 		if (opt_info.option[0] != '?' && opt_info.option[0] != '-' || opt_info.option[1] != '?' && opt_info.option[1] != '-')
4190 			opt_info.option[0] = 0;
4191 		p = C("unknown option");
4192 	}
4193 	p = T(NiL, ID, p);
4194 	sfputr(mp, p, -1);
4195 	if (err)
4196 		sfputr(mp, " -- out of range", -1);
4197 	if (opt_info.arg = sfstruse(mp))
4198 		return ':';
4199  nospace:
4200 	opt_info.arg = T(NiL, ID, "[* out of space *]");
4201 	return ':';
4202 }
4203 
4204 /*
4205  * argv:	command line argv where argv[0] is command name
4206  *
4207  * opts:	option control string
4208  *
4209  *	'[' [flag][=][index][:<long-name>[|<alias-name>...]['?'description]] ']'
4210  *			long option name, index, description; -index returned
4211  *	':'		option takes string arg
4212  *	'#'		option takes numeric arg (concat option may follow)
4213  *	'?'		(option) following options not in usage
4214  *			(following # or :) optional arg
4215  *	'[' '[' ... ] ... '[' ... ']' ']'
4216  *			mutually exclusive option grouping
4217  *	'[' name [:attr]* [?description] ']'
4218  *			(following # or :) optional option arg description
4219  *	'\n'[' '|'\t']*	ignored for legibility
4220  *	' ' ...		optional argument(s) description (to end of string)
4221  *			or after blank line
4222  *	']]'		literal ']' within '[' ... ']'
4223  *
4224  * return:
4225  *	0		no more options
4226  *	'?'		usage: opt_info.arg points to message sans
4227  *			`Usage: command '
4228  *	':'		error: opt_info.arg points to message sans `command: '
4229  *
4230  * ':'  '#'  ' '  '['  ']'
4231  *			invalid option chars
4232  *
4233  * -- terminates option list and returns 0
4234  *
4235  * + as first opts char makes + equivalent to -
4236  *
4237  * if any # option is specified then numeric options (e.g., -123)
4238  * are associated with the leftmost # option in opts
4239  *
4240  * usage info in placed opt_info.arg when '?' returned
4241  * see help_text[] (--???) for more info
4242  */
4243 
4244 int
4245 optget(register char** argv, const char* oopts)
4246 {
4247 	register int	c;
4248 	register char*	s;
4249 	char*		a;
4250 	char*		b;
4251 	char*		e;
4252 	char*		f;
4253 	char*		g;
4254 	char*		v;
4255 	char*		w;
4256 	char*		p;
4257 	char*		q;
4258 	char*		t;
4259 	char*		y;
4260 	char*		numopt;
4261 	char*		opts;
4262 	char*		id;
4263 	char*		catalog;
4264 	int		n;
4265 	int		m;
4266 	int		k;
4267 	int		j;
4268 	int		x;
4269 	int		err;
4270 	int		no;
4271 	int		nov;
4272 	int		num;
4273 	int		numchr;
4274 	int		prefix;
4275 	int		version;
4276 	Help_t*		hp;
4277 	Push_t*		psp;
4278 	Push_t*		tsp;
4279 	Sfio_t*		vp;
4280 	Sfio_t*		xp;
4281 	Optcache_t*	cache;
4282 	Optcache_t*	pcache;
4283 	Optpass_t*	pass;
4284 
4285 #if !_PACKAGE_astsa && !_YOU_FIGURED_OUT_HOW_TO_GET_ALL_DLLS_TO_DO_THIS_
4286 	/*
4287 	 * these are not initialized by all dlls!
4288 	 */
4289 
4290 	extern Error_info_t	_error_info_;
4291 	extern Opt_t		_opt_info_;
4292 
4293 	if (!_error_infop_)
4294 		_error_infop_ = &_error_info_;
4295 	if (!_opt_infop_)
4296 		_opt_infop_ = &_opt_info_;
4297 #endif
4298 	if (!oopts)
4299 		return 0;
4300 	state.pindex = opt_info.index;
4301 	state.poffset = opt_info.offset;
4302 	if (!opt_info.index)
4303 	{
4304 		opt_info.index = 1;
4305 		opt_info.offset = 0;
4306 		if (state.npass)
4307 		{
4308 			state.npass = 0;
4309 			state.join = 0;
4310 		}
4311 	}
4312 	if (!argv)
4313 		cache = 0;
4314 	else
4315 		for (pcache = 0, cache = state.cache; cache; pcache = cache, cache = cache->next)
4316 			if (cache->pass.oopts == (char*)oopts)
4317 				break;
4318 	if (cache)
4319 	{
4320 		if (pcache)
4321 		{
4322 			pcache->next = cache->next;
4323 			cache->next = state.cache;
4324 			state.cache = cache;
4325 		}
4326 		pass = &cache->pass;
4327 		state.npass = -1;
4328 	}
4329 	else
4330 	{
4331 		if (!argv)
4332 			n = state.npass ? state.npass : 1;
4333 		else if ((n = state.join - 1) < 0)
4334 			n = 0;
4335 		if (n >= state.npass || state.pass[n].oopts != (char*)oopts)
4336 		{
4337 			for (m = 0; m < state.npass && state.pass[m].oopts != (char*)oopts; m++);
4338 			if (m < state.npass)
4339 				n = m;
4340 			else
4341 			{
4342 				if (n >= elementsof(state.pass))
4343 					n = elementsof(state.pass) - 1;
4344 				init((char*)oopts, &state.pass[n]);
4345 				if (state.npass <= n)
4346 					state.npass = n + 1;
4347 			}
4348 		}
4349 		if (!argv)
4350 			return 0;
4351 		pass = &state.pass[n];
4352 	}
4353 	opts = pass->opts;
4354 	prefix = pass->prefix;
4355 	version = pass->version;
4356 	id = pass->id;
4357 	if (!(xp = state.xp) || (catalog = pass->catalog) && !X(catalog))
4358 		catalog = 0;
4359 	else /* if (!error_info.catalog) */
4360 		error_info.catalog = catalog;
4361  again:
4362 	psp = 0;
4363 
4364 	/*
4365 	 * check if any options remain and determine if the
4366 	 * next option is short or long
4367 	 */
4368 
4369 	opt_info.assignment = 0;
4370 	num = 1;
4371 	w = v = 0;
4372 	x = 0;
4373 	for (;;)
4374 	{
4375 		if (!opt_info.offset)
4376 		{
4377 			/*
4378 			 * finished with the previous arg
4379 			 */
4380 
4381 			if (opt_info.index == 1 && opt_info.argv != state.strv)
4382 			{
4383 				opt_info.argv = 0;
4384 				state.argv[0] = 0;
4385 				if (argv[0] && (state.argv[0] = save(argv[0], strlen(argv[0]), 0, 0, 0, 0)))
4386 					opt_info.argv = state.argv;
4387 				state.style = STYLE_short;
4388 			}
4389 			if (!(s = argv[opt_info.index]))
4390 				return 0;
4391 			if (!prefix)
4392 			{
4393 				/*
4394 				 * long with no prefix (dd style)
4395 				 */
4396 
4397 				n = 2;
4398 				if ((c = *s) != '-' && c != '+')
4399 					c = '-';
4400 				else if (*++s == c)
4401 				{
4402 					if (!*++s)
4403 					{
4404 						opt_info.index++;
4405 						return 0;
4406 					}
4407 					else if (*s == c)
4408 						return 0;
4409 				}
4410 				else if (*s == '?')
4411 					n = 1;
4412 			}
4413 			else if ((c = *s++) != '-' && (c != '+' || !(pass->flags & OPT_plus) && (!(pass->flags & OPT_numeric) || !isdigit(*s))))
4414 			{
4415 				if (!(pass->flags & OPT_old) || !isalpha(c))
4416 					return 0;
4417 				s--;
4418 				n = 1;
4419 				opt_info.offset--;
4420 			}
4421 			else if (*s == c)
4422 			{
4423 				if (!*++s)
4424 				{
4425 					/*
4426 					 * -- or ++ end of options
4427 					 */
4428 
4429 					opt_info.index++;
4430 					return 0;
4431 				}
4432 				else if (*s == c)
4433 				{
4434 					/*
4435 					 * ---* or +++* are operands
4436 					 */
4437 
4438 					return 0;
4439 				}
4440 				if (version || *s == '?' || !(pass->flags & OPT_minus))
4441 				{
4442 					/*
4443 					 * long with double prefix
4444 					 */
4445 
4446 					n = 2;
4447 				}
4448 				else
4449 				{
4450 					/*
4451 					 * short option char '-'
4452 					 */
4453 
4454 					s--;
4455 					n = 1;
4456 				}
4457 			}
4458 			else if (prefix == 1 && *s != '?')
4459 			{
4460 				/*
4461 				 * long with single prefix (find style)
4462 				 */
4463 
4464 				n = 2;
4465 			}
4466 			else
4467 			{
4468 				/*
4469 				 * short (always with single prefix)
4470 				 */
4471 
4472 				n = 1;
4473 			}
4474 
4475 			/*
4476 			 * just a prefix is an option (e.g., `-' == stdin)
4477 			 */
4478 
4479 			if (!*s)
4480 				return 0;
4481 			if (c == '+')
4482 				opt_info.arg = 0;
4483 			if (n == 2)
4484 			{
4485 				x = 0;
4486 				state.style = STYLE_long;
4487 				opt_info.option[0] = opt_info.name[0] = opt_info.name[1] = c;
4488 				w = &opt_info.name[prefix];
4489 				if ((*s == 'n' || *s == 'N') && (*(s + 1) == 'o' || *(s + 1) == 'O') && *(s + 2) && *(s + 2) != '=')
4490 					no = *(s + 2) == '-' ? 3 : 2;
4491 				else
4492 					no = 0;
4493 				for (c = *s; *s; s++)
4494 				{
4495 					if (*s == '=')
4496 					{
4497 						if (*(s + 1) == '=')
4498 							s++;
4499 						if (!isalnum(*(s - 1)) && *(w - 1) == (opt_info.assignment = *(s - 1)))
4500 							w--;
4501 						v = ++s;
4502 						break;
4503 					}
4504 					if (w < &opt_info.name[elementsof(opt_info.name) - 1] && *s != ':' && *s != '|' && *s != '[' && *s != ']')
4505 						*w++ = *s;
4506 				}
4507 				*w = 0;
4508 				w = &opt_info.name[prefix];
4509 				c = *w;
4510 				opt_info.offset = 0;
4511 				opt_info.index++;
4512 				break;
4513 			}
4514 			opt_info.offset++;
4515 		}
4516 		if (!argv[opt_info.index])
4517 			return 0;
4518 		if (c = argv[opt_info.index][opt_info.offset++])
4519 		{
4520 			if ((k = argv[opt_info.index][0]) != '-' && k != '+')
4521 				k = '-';
4522 			opt_info.option[0] = opt_info.name[0] = k;
4523 			opt_info.option[1] = opt_info.name[1] = c;
4524 			opt_info.option[2] = opt_info.name[2] = 0;
4525 			break;
4526 		}
4527 		opt_info.offset = 0;
4528 		opt_info.index++;
4529 	}
4530 
4531 	/*
4532 	 * at this point:
4533 	 *
4534 	 *	c	the first character of the option
4535 	 *	w	long option name if != 0, otherwise short
4536 	 *	v	long option value (via =) if w != 0
4537 	 */
4538 
4539 	if (c == '?')
4540 	{
4541 		/*
4542 		 * ? always triggers internal help
4543 		 */
4544 
4545 		if (!state.msgdict)
4546 			initdict();
4547 		if (w)
4548 		{
4549 			if (!v && (*(w + 1) || !(v = argv[opt_info.index]) || !++opt_info.index))
4550 				v = w + 1;
4551 			else if (w[0] != '?' || w[1])
4552 			{
4553 				s = w;
4554 				w = v;
4555 				v = s + 1;
4556 			}
4557 		}
4558 		opt_info.option[1] = c;
4559 		opt_info.option[2] = 0;
4560 		if (!w)
4561 		{
4562 			opt_info.name[1] = c;
4563 			opt_info.name[2] = 0;
4564 		}
4565 		goto help;
4566 	}
4567 	else if (w && !state.msgdict)
4568 		initdict();
4569 	numopt = 0;
4570 	f = 0;
4571 	s = opts;
4572 
4573 	/*
4574 	 * no option can start with these characters
4575 	 */
4576 
4577 	if (c == ':' || c == '#' || c == ' ' || c == '[' || c == ']')
4578 	{
4579 		if (c != *s)
4580 			s = "";
4581 	}
4582 	else
4583 	{
4584 		a = 0;
4585 		if (!w && (pass->flags & OPT_cache))
4586 		{
4587 			if (cache)
4588 			{
4589 				if (k = cache->flags[map[c]])
4590 				{
4591 					opt_info.arg = 0;
4592 
4593 					/*
4594 					 * this is a ksh getopts workaround
4595 					 */
4596 
4597 					if (opt_info.num != LONG_MIN)
4598 						opt_info.num = (long)(opt_info.number = !(k & OPT_cache_invert));
4599 					if (!(k & (OPT_cache_string|OPT_cache_numeric)))
4600 						return c;
4601 					if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset]))
4602 					{
4603 						if (!(k & OPT_cache_numeric))
4604 						{
4605 							opt_info.offset = 0;
4606 							return c;
4607 						}
4608 						opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
4609 						if (err || e == opt_info.arg)
4610 						{
4611 							opt_info.num = (long)(opt_info.number = 0);
4612 							if (!err && (k & OPT_cache_optional))
4613 							{
4614 								opt_info.arg = 0;
4615 								opt_info.index--;
4616 								return c;
4617 							}
4618 						}
4619 						else if (*e)
4620 						{
4621 							opt_info.offset += e - opt_info.arg;
4622 							opt_info.index--;
4623 							return c;
4624 						}
4625 						else
4626 						{
4627 							opt_info.offset = 0;
4628 							return c;
4629 						}
4630 					}
4631 					else if (opt_info.arg = argv[opt_info.index])
4632 					{
4633 						opt_info.index++;
4634 						if ((k & OPT_cache_optional) && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1))
4635 						{
4636 							opt_info.arg = 0;
4637 							opt_info.index--;
4638 							opt_info.offset = 0;
4639 							opt_info.num = (long)(opt_info.number = 0);
4640 							return c;
4641 						}
4642 						if (k & OPT_cache_string)
4643 						{
4644 							opt_info.offset = 0;
4645 							return c;
4646 						}
4647 						opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
4648 						if (!err)
4649 						{
4650 							if (!*e)
4651 							{
4652 								opt_info.offset = 0;
4653 								return c;
4654 							}
4655 							if (k & OPT_cache_optional)
4656 							{
4657 								opt_info.arg = 0;
4658 								opt_info.index--;
4659 								opt_info.offset = 0;
4660 								return c;
4661 							}
4662 						}
4663 					}
4664 					else if (k & OPT_cache_optional)
4665 					{
4666 						opt_info.offset = 0;
4667 						return c;
4668 					}
4669 					opt_info.index--;
4670 				}
4671 				cache = 0;
4672 			}
4673 			else if (cache = newof(0, Optcache_t, 1, 0))
4674 			{
4675 				cache->caching = c;
4676 				c = 0;
4677 				cache->pass = *pass;
4678 				cache->next = state.cache;
4679 				state.cache = cache;
4680 			}
4681 		}
4682 		else
4683 			cache = 0;
4684 		for (;;)
4685 		{
4686 			if (!(*(s = next(s, version))) || *s == '\n' || *s == ' ')
4687 			{
4688 				if (!(tsp = psp))
4689 				{
4690 					if (cache)
4691 					{
4692 						/*
4693 						 * the first loop pass
4694 						 * initialized the cache
4695 						 * so one more pass to
4696 						 * check the cache or
4697 						 * bail for a full scan
4698 						 */
4699 
4700 						cache->flags[0] = 0;
4701 						c = cache->caching;
4702 						cache->caching = 0;
4703 						cache = 0;
4704 						s = opts;
4705 						continue;
4706 					}
4707 					if (!x && catalog)
4708 					{
4709 						/*
4710 						 * the first loop pass
4711 						 * translated long
4712 						 * options and there
4713 						 * were no matches so
4714 						 * one more pass for C
4715 						 * locale
4716 						 */
4717 
4718 						catalog = 0;
4719 						s = opts;
4720 						continue;
4721 					}
4722 					s = "";
4723 					break;
4724 				}
4725 				s = psp->ob;
4726 				psp = psp->next;
4727 				free(tsp);
4728 				continue;
4729 			}
4730 			if (*s == '\f')
4731 			{
4732 				psp = info(psp, s + 1, NiL, xp, id);
4733 				if (psp->nb)
4734 					s = psp->nb;
4735 				else
4736 				{
4737 					s = psp->ob;
4738 					psp = psp->next;
4739 				}
4740 				continue;
4741 			}
4742 			message((-20, "optget: opt %s  c %c  w %s  num %ld", show(s), c, w, num));
4743 			if (*s == c && !w)
4744 				break;
4745 			else if (*s == '[')
4746 			{
4747 				s = next(s + 1, version);
4748 				if (*s == '(')
4749 				{
4750 					s = nest(f = s);
4751 					if (!conformance(f, s - f))
4752 						goto disable;
4753 				}
4754 				k = *(f = s);
4755 				if (k == '+' || k == '-')
4756 					/* ignore */;
4757 				else if (k == '[' || version < 1)
4758 					continue;
4759 				else if (w && !cache)
4760 				{
4761 					nov = no;
4762 					if (*(s + 1) == '\f' && (vp = state.vp))
4763 					{
4764 						sfputc(vp, k);
4765 						s = expand(s + 2, NiL, &t, vp, id);
4766 						if (*s)
4767 							*(f = s - 1) = k;
4768 						else
4769 						{
4770 							f = sfstrbase(vp);
4771 							if (s = strrchr(f, ':'))
4772 								f = s - 1;
4773 							else
4774 								s = f + 1;
4775 						}
4776 					}
4777 					else
4778 						t = 0;
4779 					if (*s != ':')
4780 						s = skip(s, ':', '?', 0, 1, 0, 0, version);
4781 					if (*s == ':')
4782 					{
4783 						if (catalog)
4784 						{
4785 							p = skip(s + 1, '?', 0, 0, 1, 0, 0, version);
4786 							e = sfprints("%-.*s", p - (s + 1), s + 1);
4787 							g = T(id, catalog, e);
4788 							if (g == e)
4789 								p = 0;
4790 							else
4791 							{
4792 								sfprintf(xp, ":%s|%s?", g, e);
4793 								if (!(s = sfstruse(xp)))
4794 									goto nospace;
4795 							}
4796 						}
4797 						else
4798 							p = 0;
4799 						y = w;
4800 						for (;;)
4801 						{
4802 							n = m = 0;
4803 							e = s + 1;
4804 							while (*++s)
4805 							{
4806 								if (*s == '*' || *s == '\a')
4807 								{
4808 									if (*s == '\a')
4809 										do
4810 										{
4811 											if (!*++s)
4812 											{
4813 												s--;
4814 												break;
4815 											}
4816 										} while (*s != '\a');
4817 									j = *(s + 1);
4818 									if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
4819 									{
4820 										while (*w)
4821 											w++;
4822 										m = 0;
4823 										break;
4824 									}
4825 									m = 1;
4826 								}
4827 								else if (*s == *w || SEP(*s) && SEP(*w))
4828 									w++;
4829 								else if (*w == 0)
4830 									break;
4831 								else if (!SEP(*s))
4832 								{
4833 									if (SEP(*w))
4834 									{
4835 										if (*++w == *s)
4836 										{
4837 											w++;
4838 											continue;
4839 										}
4840 									}
4841 									else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
4842 										break;
4843 									for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
4844 									if (!SEP(*q))
4845 										break;
4846 									for (s = q; w > y && *w != *(s + 1); w--);
4847 								}
4848 								else if (*w != *(s + 1))
4849 									break;
4850 							}
4851 							if (!*w)
4852 							{
4853 								nov = 0;
4854 								break;
4855 							}
4856 							if (n = no)
4857 							{
4858 								m = 0;
4859 								s = e - 1;
4860 								w = y + n;
4861 								while (*++s)
4862 								{
4863 									if (*s == '*' || *s == '\a')
4864 									{
4865 										if (*s == '\a')
4866 											do
4867 											{
4868 												if (!*++s)
4869 												{
4870 													s--;
4871 													break;
4872 												}
4873 											} while (*s != '\a');
4874 										j = *(s + 1);
4875 										if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
4876 										{
4877 											while (*w)
4878 												w++;
4879 											m = 0;
4880 											break;
4881 										}
4882 										m = 1;
4883 									}
4884 									else if (*s == *w || SEP(*s) && SEP(*w))
4885 										w++;
4886 									else if (*w == 0)
4887 										break;
4888 									else if (!SEP(*s))
4889 									{
4890 										if (SEP(*w))
4891 										{
4892 											if (*++w == *s)
4893 											{
4894 												w++;
4895 												continue;
4896 											}
4897 										}
4898 										else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
4899 											break;
4900 										for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
4901 										if (!SEP(*q))
4902 											break;
4903 										for (s = q; w > y && *w != *(s + 1); w--);
4904 									}
4905 									else if (*w != *(s + 1))
4906 										break;
4907 								}
4908 								if (!*w)
4909 									break;
4910 							}
4911 							if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|')
4912 								break;
4913 							w = y;
4914 						}
4915 						if (p)
4916 							s = p;
4917 						if (!*w)
4918 						{
4919 							if (n)
4920 								num = 0;
4921 							if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']' || *s == 0)) && x)
4922 							{
4923 								psp = pop(psp);
4924 								return opterror("?", 0, version, id, catalog);
4925 							}
4926 							for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2);
4927 							if (*f == ':')
4928 							{
4929 								x = -1;
4930 								opt_info.option[1] = '-';
4931 								opt_info.option[2] = 0;
4932 							}
4933 							else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':')
4934 							{
4935 								opt_info.option[1] = x;
4936 								opt_info.option[2] = 0;
4937 							}
4938 							else
4939 							{
4940 								a = f;
4941 								if (*a == '=')
4942 									a++;
4943 								else
4944 								{
4945 									if (*(a + 1) == '!')
4946 										a++;
4947 									if (*(a + 1) == '=')
4948 										a += 2;
4949 								}
4950 								x = -strtol(a, &b, 0);
4951 								if ((b - a) > sizeof(opt_info.option) - 2)
4952 									b = a + sizeof(opt_info.option) - 2;
4953 								memcpy(&opt_info.option[1], a, b - a);
4954 								opt_info.option[b - a + 1] = 0;
4955 							}
4956 							b = e;
4957 							if (t)
4958 							{
4959 								s = t;
4960 								t = 0;
4961 							}
4962 							a = s = skip(s, 0, 0, 0, 1, 0, 0, version);
4963 							if (n)
4964 							{
4965 								w = y;
4966 								break;
4967 							}
4968 						}
4969 						w = y;
4970 					}
4971 					else if (k == c && prefix == 1)
4972 					{
4973 						w = 0;
4974 						opt_info.name[1] = c;
4975 						opt_info.name[2] = 0;
4976 						opt_info.offset = 2;
4977 						opt_info.index--;
4978 						break;
4979 					}
4980 					if (t)
4981 					{
4982 						s = t;
4983 						if (a)
4984 							a = t;
4985 					}
4986 				}
4987 			disable:
4988 				s = skip(s, 0, 0, 0, 1, 0, 1, version);
4989 				if (*s == GO)
4990 					s = skip(s + 1, 0, 0, 0, 0, 1, 1, version);
4991 				if (cache)
4992 				{
4993 					m = OPT_cache_flag;
4994 					v = s;
4995 					if (*v == '#')
4996 					{
4997 						v++;
4998 						m |= OPT_cache_numeric;
4999 					}
5000 					else if (*v == ':')
5001 					{
5002 						v++;
5003 						m |= OPT_cache_string;
5004 					}
5005 					if (*v == '?')
5006 					{
5007 						v++;
5008 						m |= OPT_cache_optional;
5009 					}
5010 					else if (*v == *(v - 1))
5011 						v++;
5012 					if (*(v = next(v, version)) == '[')
5013 						v = skip(v + 1, 0, 0, 0, 1, 0, 1, version);
5014 					if (*v != GO)
5015 					{
5016 						v = f;
5017 						for (;;)
5018 						{
5019 							if (isdigit(*f) && isdigit(*(f + 1)))
5020 								while (isdigit(*(f + 1)))
5021 									f++;
5022 							else if (*(f + 1) == '=')
5023 								break;
5024 							else
5025 								cache->flags[map[*f]] = m;
5026 							j = 0;
5027 							while (*(f + 1) == '|')
5028 							{
5029 								f += 2;
5030 								if (!(j = *f) || j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
5031 									break;
5032 								cache->flags[map[j]] = m;
5033 							}
5034 							if (j != '!' || (m & OPT_cache_invert))
5035 								break;
5036 							f = v;
5037 							m |= OPT_cache_invert;
5038 						}
5039 					}
5040 				}
5041 				else
5042 				{
5043 					m = 0;
5044 					if (!w)
5045 					{
5046 						if (isdigit(*f) && isdigit(*(f + 1)))
5047 							k = -1;
5048 						if (c == k)
5049 							m = 1;
5050 						while (*(f + 1) == '|')
5051 						{
5052 							f += 2;
5053 							if (!(j = *f))
5054 							{
5055 								m = 0;
5056 								break;
5057 							}
5058 							else if (j == c)
5059 								m = 1;
5060 							else if (j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
5061 								break;
5062 						}
5063 					}
5064 					if (m)
5065 					{
5066 						s--;
5067 						if (*++f == '!')
5068 						{
5069 							f++;
5070 							num = 0;
5071 						}
5072 						if (*f == '=')
5073 						{
5074 							c = -strtol(++f, &b, 0);
5075 							if ((b - f) > sizeof(opt_info.option) - 2)
5076 								b = f + sizeof(opt_info.option) - 2;
5077 							memcpy(&opt_info.option[1], f, b - f);
5078 							opt_info.option[b - f + 1] = 0;
5079 						}
5080 						else
5081 							c = k;
5082 						break;
5083 					}
5084 				}
5085 				if (*s == '#')
5086 				{
5087 					if (!numopt && s > opts)
5088 					{
5089 						numopt = s - 1;
5090 						numchr = k;
5091 						if (*f == ':')
5092 							numchr = -1;
5093 						else if (*(f + 1) != ':' && *(f + 1) != '!' && *(f + 1) != ']')
5094 						{
5095 							a = f;
5096 							if (*a == '=')
5097 								a++;
5098 							else
5099 							{
5100 								if (*(a + 1) == '!')
5101 									a++;
5102 								if (*(a + 1) == '=')
5103 									a += 2;
5104 							}
5105 							numchr = -strtol(a, NiL, 0);
5106 						}
5107 					}
5108 				}
5109 				else if (*s != ':')
5110 					continue;
5111 			}
5112 			else if (*s == ']')
5113 			{
5114 				s++;
5115 				continue;
5116 			}
5117 			else if (*s == '#')
5118 			{
5119 				if (!numopt && s > opts)
5120 					numchr = *(numopt = s - 1);
5121 			}
5122 			else if (*s != ':')
5123 			{
5124 				if (cache)
5125 				{
5126 					m = OPT_cache_flag;
5127 					if (*(s + 1) == '#')
5128 					{
5129 						m |= OPT_cache_numeric;
5130 						if (*(s + 2) == '?')
5131 							m |= OPT_cache_optional;
5132 					}
5133 					else if (*(s + 1) == ':')
5134 					{
5135 						m |= OPT_cache_string;
5136 						if (*(s + 2) == '?')
5137 							m |= OPT_cache_optional;
5138 					}
5139 					cache->flags[map[*s]] = m;
5140 				}
5141 				s++;
5142 				continue;
5143 			}
5144 			message((-21, "optget: opt %s", show(s)));
5145 			if (*++s == '?' || *s == *(s - 1))
5146 				s++;
5147 			if (*(s = next(s, version)) == '[')
5148 			{
5149 				s = skip(s + 1, 0, 0, 0, 1, 0, 1, version);
5150 				if (*s == GO)
5151 					s = skip(s + 1, 0, 0, 0, 0, 1, 1, version);
5152 			}
5153 			message((-21, "optget: opt %s", show(s)));
5154 		}
5155 		if (w && x)
5156 		{
5157 			s = skip(b, '|', '?', 0, 1, 0, 0, version);
5158 			if (v && (a == 0 || *a == 0 || *(a + 1) != ':' && *(a + 1) != '#') && (*v == '0' || *v == '1') && !*(v + 1))
5159 			{
5160 				if (*v == '0')
5161 					num = !num;
5162 				v = 0;
5163 			}
5164 			if ((s - b) >= elementsof(opt_info.name))
5165 				s = b + elementsof(opt_info.name) - 1;
5166 			for (;;)
5167 			{
5168 				if (b >= s)
5169 				{
5170 					*w = 0;
5171 					break;
5172 				}
5173 				if (*b == '*')
5174 					break;
5175 				*w++ = *b++;
5176 			}
5177 			if (!num && v)
5178 				return opterror(no ? "!" : "=", 0, version, id, catalog);
5179 			w = &opt_info.name[prefix];
5180 			c = x;
5181 			s = a;
5182 		}
5183 	}
5184 	if (!*s)
5185 	{
5186 		if (w)
5187 		{
5188 			if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), w))
5189 			{
5190 				if (!v)
5191 					v = (char*)hp->name;
5192 				goto help;
5193 			}
5194 			if (!v)
5195 			{
5196 				v = opt_info.name;
5197 				goto help;
5198 			}
5199 		}
5200 		if (w || !isdigit(c) || !numopt || !(pass->flags & OPT_numeric))
5201 		{
5202 			pop(psp);
5203 			return opterror("", 0, version, id, catalog);
5204 		}
5205 		s = numopt;
5206 		c = opt_info.option[1] = numchr;
5207 		opt_info.offset--;
5208 	}
5209 	opt_info.arg = 0;
5210 
5211 	/*
5212 	 * this is a ksh getopts workaround
5213 	 */
5214 
5215 	if (opt_info.num != LONG_MIN)
5216 		opt_info.num = (long)(opt_info.number = num);
5217 	if ((n = *++s == '#') || *s == ':' || w && !nov && v && (optnumber(v, &e, NiL), n = !*e))
5218 	{
5219 		if (w)
5220 		{
5221 			if (nov)
5222 			{
5223 				if (v)
5224 				{
5225 					pop(psp);
5226 					return opterror("!", 0, version, id, catalog);
5227 				}
5228 				opt_info.num = (long)(opt_info.number = 0);
5229 			}
5230 			else
5231 			{
5232 				if (!v && *(s + 1) != '?' && (v = argv[opt_info.index]))
5233 				{
5234 					opt_info.index++;
5235 					opt_info.offset = 0;
5236 				}
5237 				if (!(opt_info.arg = v) || (*v == '0' || *v == '1') && !*(v + 1))
5238 				{
5239 					if (*(s + 1) != '?')
5240 					{
5241 						if (!opt_info.arg)
5242 						{
5243 							pop(psp);
5244 							return opterror(s, 0, version, id, catalog);
5245 						}
5246 					}
5247 					else if (*(t = next(s + 2, version)) == '[')
5248 						while (*(t = skip(t, ':', 0, 0, 1, 0, 0, version)) == ':')
5249 							if (*++t == '!')
5250 							{
5251 								if (!v || *v == '1')
5252 								{
5253 									e = skip(t, ':', '?', ']', 1, 0, 0, version);
5254 									opt_info.arg = sfprints("%-.*s", e - t - 1, t + 1);
5255 								}
5256 								else
5257 								{
5258 									opt_info.arg = 0;
5259 									opt_info.num = (long)(opt_info.number = 0);
5260 								}
5261 								break;
5262 							}
5263 				}
5264 				if (opt_info.arg && n)
5265 				{
5266 					opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
5267 					if (err || e == opt_info.arg)
5268 					{
5269 						pop(psp);
5270 						return opterror(s, err, version, id, catalog);
5271 					}
5272 				}
5273 			}
5274 			goto optarg;
5275 		}
5276 		else if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset]))
5277 		{
5278 			if (*s == '#')
5279 			{
5280 				opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
5281 				if (err || e == opt_info.arg)
5282 				{
5283 					if (!err && *(s + 1) == '?')
5284 					{
5285 						opt_info.arg = 0;
5286 						opt_info.index--;
5287 					}
5288 					else
5289 					{
5290 						opt_info.offset = 0;
5291 						c = opterror(s, err, version, id, catalog);
5292 					}
5293 					pop(psp);
5294 					return c;
5295 				}
5296 				else if (*e)
5297 				{
5298 					opt_info.offset += e - opt_info.arg;
5299 					opt_info.index--;
5300 					pop(psp);
5301 					return c;
5302 				}
5303 			}
5304 		}
5305 		else if (opt_info.arg = argv[opt_info.index])
5306 		{
5307 			opt_info.index++;
5308 			if (*(s + 1) == '?' && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1))
5309 			{
5310 				opt_info.num = (long)(opt_info.number = 0);
5311 				opt_info.index--;
5312 				opt_info.arg = 0;
5313 			}
5314 			else if (*s == '#')
5315 			{
5316 				opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
5317 				if (err || *e)
5318 				{
5319 					if (!err && *(s + 1) == '?')
5320 					{
5321 						opt_info.arg = 0;
5322 						opt_info.index--;
5323 					}
5324 					else
5325 					{
5326 						pop(psp);
5327 						opt_info.offset = 0;
5328 						return opterror(s, err, version, id, catalog);
5329 					}
5330 				}
5331 			}
5332 		}
5333 		else if (*(s + 1) != '?')
5334 		{
5335 			opt_info.index--;
5336 			pop(psp);
5337 			return opterror(s, 0, version, id, catalog);
5338 		}
5339 		opt_info.offset = 0;
5340 	optarg:
5341 		if (*s == ':' && *(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == GO && *(s = next(s + 1, version)) == '[' && isalnum(*(s + 1)))
5342 		{
5343 			x = 0;
5344 			if (opt_info.arg)
5345 			{
5346 				do
5347 				{
5348 					w = y = opt_info.arg;
5349 					f = s = next(s + 1, version);
5350 					k = *f;
5351 					if (k == *w && isalpha(k) && !*(w + 1))
5352 					{
5353 						x = k;
5354 						break;
5355 					}
5356 					if (*s == '+' || *s == '-')
5357 						continue;
5358 					else if (*s == '[' || version < 1)
5359 						continue;
5360 					else
5361 					{
5362 						if (*s != ':')
5363 							s = skip(s, ':', '?', 0, 1, 0, 0, version);
5364 						if (*s == ':')
5365 						{
5366 							if (catalog)
5367 							{
5368 								p = skip(s + 1, '?', 0, 0, 1, 0, 0, version);
5369 								e = sfprints("%-.*s", p - (s + 1), s + 1);
5370 								b = T(id, catalog, e);
5371 								if (b == e)
5372 									p = 0;
5373 								else
5374 								{
5375 									sfprintf(xp, ":%s|%s?", b, e);
5376 									if (!(s = sfstruse(xp)))
5377 										goto nospace;
5378 								}
5379 							}
5380 							else
5381 								p = 0;
5382 							for (;;)
5383 							{
5384 								n = m = 0;
5385 								e = s + 1;
5386 								while (*++s)
5387 								{
5388 									if (*s == '*' || *s == '\a')
5389 									{
5390 										if (*s == '\a')
5391 											do
5392 											{
5393 												if (!*++s)
5394 												{
5395 													s--;
5396 													break;
5397 												}
5398 											} while (*s != '\a');
5399 										j = *(s + 1);
5400 										if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
5401 										{
5402 											while (*w)
5403 												w++;
5404 											m = 0;
5405 											break;
5406 										}
5407 										m = 1;
5408 									}
5409 									else if (*s == *w || SEP(*s) && SEP(*w))
5410 										w++;
5411 									else if (*w == 0)
5412 										break;
5413 									else if (!SEP(*s))
5414 									{
5415 										if (SEP(*w))
5416 										{
5417 											if (*++w == *s)
5418 											{
5419 												w++;
5420 												continue;
5421 											}
5422 										}
5423 										else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
5424 											break;
5425 										for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
5426 										if (!SEP(*q))
5427 											break;
5428 										for (s = q; w > y && *w != *(s + 1); w--);
5429 									}
5430 									else if (*w != *(s + 1))
5431 										break;
5432 								}
5433 								if (!*w)
5434 								{
5435 									nov = 0;
5436 									break;
5437 								}
5438 								if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|')
5439 									break;
5440 								w = y;
5441 							}
5442 							if (p)
5443 								s = p;
5444 							if (!*w)
5445 							{
5446 								if (n)
5447 									num = 0;
5448 								if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']')) && x)
5449 								{
5450 									pop(psp);
5451 									return opterror("&", 0, version, id, catalog);
5452 								}
5453 								for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2);
5454 								if (*f == ':')
5455 									x = -1;
5456 								else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':')
5457 									/* ok */;
5458 								else
5459 								{
5460 									a = f;
5461 									if (*a == '=')
5462 										a++;
5463 									else
5464 									{
5465 										if (*(a + 1) == '!')
5466 											a++;
5467 										if (*(a + 1) == '=')
5468 											a += 2;
5469 									}
5470 									x = -strtol(a, &b, 0);
5471 								}
5472 								b = e;
5473 								a = s = skip(s, 0, 0, 0, 1, 0, 0, version);
5474 								if (n)
5475 									break;
5476 							}
5477 						}
5478 					}
5479 				} while (*(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == '[');
5480 				if (!(opt_info.num = (long)(opt_info.number = x)))
5481 				{
5482 					pop(psp);
5483 					return opterror("*", 0, version, id, catalog);
5484 				}
5485 			}
5486 		}
5487 	}
5488 	else if (w && v)
5489 	{
5490 		pop(psp);
5491 		return opterror("=", 0, version, id, catalog);
5492 	}
5493 	else
5494 	{
5495 		opt_info.num = (long)(opt_info.number = num);
5496 		if (!w && !argv[opt_info.index][opt_info.offset])
5497 		{
5498 			opt_info.offset = 0;
5499 			opt_info.index++;
5500 		}
5501 	}
5502 	pop(psp);
5503 	return c;
5504  help:
5505 	if (v && *v == '?' && *(v + 1) == '?' && *(v + 2))
5506 	{
5507 		s = v + 2;
5508 		if ((s[0] == 'n' || s[0] == 'N') && (s[1] == 'o' || s[1] == 'O'))
5509 		{
5510 			s += 2;
5511 			n = -1;
5512 		}
5513 		else
5514 			n = 1;
5515 		if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), s))
5516 		{
5517 			if (hp->style < STYLE_man || !(s = argv[opt_info.index]) || s[0] != '-' || s[1] != '-' || !s[2])
5518 			{
5519 				opt_info.arg = sfprints("\fversion=%d", version);
5520 				pop(psp);
5521 				return '?';
5522 			}
5523 			state.force = hp->style;
5524 		}
5525 		else if (match(s, "CONFORMANCE", -1, ID, NiL))
5526 		{
5527 			opt_info.arg = sfprints("\f%s", conformance(w, 0));
5528 			pop(psp);
5529 			return '?';
5530 		}
5531 		else if (match(s, "ESC", -1, ID, NiL) || match(s, "EMPHASIS", -1, ID, NiL))
5532 			state.emphasis = n;
5533 		else if (match(s, "MAN", -1, ID, NiL))
5534 		{
5535 			opt_info.arg = sfprints("\f%s", secname(*w != '?' ? w : pass->section));
5536 			pop(psp);
5537 			return '?';
5538 		}
5539 		else if (match(s, "PREFORMAT", -1, ID, NiL))
5540 			state.flags |= OPT_preformat;
5541 		else if (match(s, "SECTION", -1, ID, NiL))
5542 		{
5543 			opt_info.arg = sfprints("\f%s", pass->section);
5544 			pop(psp);
5545 			return '?';
5546 		}
5547 		else if (match(s, "TEST", -1, ID, NiL))
5548 		{
5549 			state.width = OPT_WIDTH;
5550 			state.emphasis = 1;
5551 		}
5552 		else
5553 		{
5554 			pop(psp);
5555 			return opterror(v, 0, version, id, catalog);
5556 		}
5557 		psp = pop(psp);
5558 		if (argv == state.strv)
5559 			return '#';
5560 		goto again;
5561 	}
5562 	if ((opt_info.arg = opthelp(NiL, v)) == (char*)unknown)
5563 	{
5564 		pop(psp);
5565 		return opterror(v, 0, version, id, catalog);
5566 	}
5567 	pop(psp);
5568 	return '?';
5569  nospace:
5570 	pop(psp);
5571 	return opterror(NiL, 0, 0, NiL, NiL);
5572 }
5573 
5574 /*
5575  * parse long options with 0,1,2 leading '-' or '+' from string and pass to optget()
5576  * syntax is the unquoted
5577  *
5578  *	<length> [-|+|--|++]<name>[[-+:|&=]=<value>\n (or \0 for the last)
5579  *
5580  * or the quoted
5581  *
5582  *	[-|+|--|++][no]name[[-+:|&=]=['"{(]value[)}"']][, ]...
5583  *
5584  * with \x escapes passed to chresc()
5585  *
5586  * return '#' for `label:', with opt_info.name==label
5587  * str[opt_info.offset]	next arg
5588  *
5589  *	optstr(s, 0)
5590  *		return '-' if arg, 0 otherwise
5591  *	optstr(0, opts)
5592  *		use previous parsed str
5593  */
5594 
5595 int
5596 optstr(const char* str, const char* opts)
5597 {
5598 	register char*		s = (char*)str;
5599 	register Sfio_t*	mp;
5600 	register int		c;
5601 	register int		ql;
5602 	register int		qr;
5603 	register int		qc;
5604 	int			v;
5605 	char*			e;
5606 
5607  again:
5608 	if (s)
5609 	{
5610 		if (!(mp = state.strp) && !(mp = state.strp = sfstropen()))
5611 			return 0;
5612 		if (state.str != s)
5613 			state.str = s;
5614 		else if (opt_info.index == 1)
5615 			s += opt_info.offset;
5616 		while (*s == ',' || *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
5617 			s++;
5618 		if (!*s)
5619 		{
5620 			state.str = 0;
5621 			return 0;
5622 		}
5623 		if (*s == '-' || *s == '+')
5624 		{
5625 			c = *s++;
5626 			sfputc(mp, c);
5627 			if (*s == c)
5628 			{
5629 				sfputc(mp, c);
5630 				s++;
5631 			}
5632 		}
5633 		else
5634 		{
5635 			sfputc(mp, '-');
5636 			sfputc(mp, '-');
5637 		}
5638 		if (isdigit(*s) && (v = (int)strtol(s, &e, 10)) > 1 && isspace(*e) && --v <= strlen(s) && (s[v] == 0 || s[v] == '\n'))
5639 		{
5640 			s += v;
5641 			while (isspace(*++e));
5642 			sfwrite(mp, e, s - e);
5643 		}
5644 		else
5645 		{
5646 			while (*s && *s != ',' && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' && *s != '=' && *s != ':')
5647 				sfputc(mp, *s++);
5648 			if ((c = *s) == ':' && *(s + 1) != '=')
5649 			{
5650 				opt_info.index = 1;
5651 				opt_info.offset = ++s - (char*)str;
5652 				if (!(s = sfstruse(mp)))
5653 					goto nospace;
5654 				s += 2;
5655 				e = opt_info.name;
5656 				while (e < &opt_info.name[sizeof(opt_info.name)-1] && (*e++ = *s++));
5657 				opt_info.arg = 0;
5658 				opt_info.num = (long)(opt_info.number = 0);
5659 				opt_info.option[0] = ':';
5660 				opt_info.option[1] = 0;
5661 				return '#';
5662 			}
5663 			if (c == ':' || c == '=')
5664 			{
5665 				sfputc(mp, c);
5666 				ql = qr = 0;
5667 				while (c = *++s)
5668 				{
5669 					if (c == '\\')
5670 					{
5671 						sfputc(mp, chresc(s, &e));
5672 						s = e - 1;
5673 					}
5674 					else if (c == qr)
5675 					{
5676 						if (qr != ql)
5677 							sfputc(mp, c);
5678 						if (--qc <= 0)
5679 							qr = ql = 0;
5680 					}
5681 					else if (c == ql)
5682 					{
5683 						sfputc(mp, c);
5684 						qc++;
5685 					}
5686 					else if (qr)
5687 						sfputc(mp, c);
5688 					else if (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r')
5689 						break;
5690 					else if (c == '"' || c == '\'')
5691 					{
5692 						ql = qr = c;
5693 						qc = 1;
5694 					}
5695 					else
5696 					{
5697 						sfputc(mp, c);
5698 						if (c == GO)
5699 						{
5700 							ql = c;
5701 							qr = OG;
5702 							qc = 1;
5703 						}
5704 						else if (c == '(')
5705 						{
5706 							ql = c;
5707 							qr = ')';
5708 							qc = 1;
5709 						}
5710 					}
5711 				}
5712 			}
5713 		}
5714 		opt_info.argv = state.strv;
5715 		state.strv[0] = T(NiL, ID, "option");
5716 		if (!(state.strv[1] = sfstruse(mp)))
5717 			goto nospace;
5718 		state.strv[2] = 0;
5719 		opt_info.offset = s - (char*)str;
5720 	}
5721 	if (opts)
5722 	{
5723 		if (!state.strv[1])
5724 		{
5725 			state.str = 0;
5726 			return 0;
5727 		}
5728 		opt_info.index = 1;
5729 		v = opt_info.offset;
5730 		opt_info.offset = 0;
5731 		c = optget(state.strv, opts);
5732 		opt_info.index = 1;
5733 		opt_info.offset = v;
5734 		if (c == '#')
5735 		{
5736 			s = state.str;
5737 			goto again;
5738 		}
5739 		if ((c == '?' || c == ':') && (opt_info.arg[0] == '-' && opt_info.arg[1] == '-'))
5740 			opt_info.arg += 2;
5741 		s = opt_info.name;
5742 		if (*s++ == '-' && *s++ == '-' && *s)
5743 		{
5744 			e = opt_info.name;
5745 			while (*e++ = *s++);
5746 		}
5747 	}
5748 	else
5749 		c = '-';
5750 	return c;
5751  nospace:
5752 	return opterror(NiL, 0, 0, NiL, NiL);
5753 }
5754