xref: /titanic_41/usr/src/lib/libast/common/misc/optget.c (revision c10c16dec587a0662068f6e2991c29ed3a9db943)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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 sep(c)		((c)=='-'||(c)=='_')
80 
81 typedef struct Attr_s
82 {
83 	const char*	name;
84 	int		flag;
85 } Attr_t;
86 
87 typedef struct Help_s
88 {
89 	const char*	match;		/* builtin help match name	*/
90 	const char*	name;		/* builtin help name		*/
91 	int		style;		/* STYLE_*			*/
92 	const char*	text;		/* --? text			*/
93 	unsigned int	size;		/* strlen text			*/
94 } Help_t;
95 
96 typedef struct Font_s
97 {
98 	const char*	html[2];
99 	const char*	nroff[2];
100 	const char*	term[2];
101 } Font_t;
102 
103 typedef struct List_s
104 {
105 	int		type;		/* { - + : }			*/
106 	const char*	name;		/* list name			*/
107 	const char*	text;		/* help text			*/
108 } List_t;
109 
110 typedef struct Msg_s
111 {
112 	const char*	text;		/* default message text		*/
113 	Dtlink_t	link;		/* cdt link			*/
114 } Msg_t;
115 
116 typedef struct Save_s
117 {
118 	Dtlink_t	link;		/* cdt link			*/
119 	char		text[1];	/* saved text text		*/
120 } Save_t;
121 
122 typedef struct Push_s
123 {
124 	struct Push_s*	next;		/* next string			*/
125 	char*		ob;		/* next char in old string	*/
126 	char*		oe;		/* end of old string		*/
127 	char*		nb;		/* next char in new string	*/
128 	char*		ne;		/* end of new string		*/
129 	int		ch;		/* localize() translation	*/
130 } Push_t;
131 
132 typedef struct Indent_s
133 {
134 	int		stop;		/* tab column position		*/
135 } Indent_t;
136 
137 static Indent_t		indent[] =
138 {
139 	0,2,	4,10,	12,18,	20,26,	28,34,	36,42,	44,50,	0,0
140 };
141 
142 static const char	term_off[] =	{CC_esc,'[','0','m',0};
143 static const char	term_B_on[] =	{CC_esc,'[','1','m',0};
144 static const char	term_I_on[] =	{CC_esc,'[','1',';','4','m',0};
145 
146 static const Font_t	fonts[] =
147 {
148 	"",	"",	"",	"",	"",			"",
149 	"</B>",	"<B>", "\\fP",	"\\fB",	&term_off[0],	&term_B_on[0],
150 	"</I>",	"<I>", "\\fP",	"\\fI",	&term_off[0],	&term_I_on[0],
151 	"",	"",	"",	"",	"",			"",
152 	"</TT>","<TT>","\\fP",	"\\f5",	"",			"",
153 };
154 
155 static char		native[] = "";
156 
157 #if !_PACKAGE_astsa
158 
159 #define ID		ast.id
160 
161 #define C(s)		ERROR_catalog(s)
162 #define D(s)		(opt_info.state->msgdict && dtmatch(opt_info.state->msgdict, (s)))
163 #define T(i,c,m)	(X(c)?translate(i,c,C(m)):(m))
164 #define X(c)		(ERROR_translating()&&(c)!=native)
165 #define Z(x)		C(x),sizeof(x)-1
166 
167 /*
168  * translate with C_LC_MESSAGES_libast[] check
169  */
170 
171 static char*
172 translate(const char* cmd, const char* cat, const char* msg)
173 {
174 	if (!X(cat))
175 		return (char*)msg;
176 	if (cat != (const char*)ID && D(msg))
177 		cat = (const char*)ID;
178 	return errorx(NiL, cmd, cat, msg);
179 }
180 
181 #else
182 
183 static char		ID[] = "ast";
184 
185 #define C(s)		s
186 #define D(s)		(opt_info.state->msgdict && dtmatch(opt_info.state->msgdict, (s)))
187 #define T(i,c,m)	m
188 #define X(c)		0
189 #define Z(x)		C(x),sizeof(x)-1
190 
191 #endif
192 
193 static const List_t	help_head[] =
194 {
195 	'-',	0,
196 		0,
197 	'+',	C("NAME"),
198 		C("options available to all \bast\b commands"),
199 	'+',	C("DESCRIPTION"),
200 		C("\b-?\b and \b--?\b* options are the same \
201 for all \bast\b commands. For any \aitem\a below, if \b--\b\aitem\a is not \
202 supported by a given command then it is equivalent to \b--\?\?\b\aitem\a. The \
203 \b--\?\?\b form should be used for portability. All output is written to the \
204 standard error."),
205 };
206 
207 static const Help_t	styles[] =
208 {
209 	C("about"),	"-",		STYLE_match,
210 	Z("List all implementation info."),
211 	C("api"),	"?api",		STYLE_api,
212 	Z("List detailed info in program readable form."),
213 	C("help"),	"",		-1,
214 	Z("List detailed help option info."),
215 	C("html"),	"?html",	STYLE_html,
216 	Z("List detailed info in html."),
217 	C("keys"),	"?keys",	STYLE_keys,
218 	Z("List the usage translation key strings with C style escapes."),
219 	C("long"),	"?long",	STYLE_long,
220 	Z("List long option usage."),
221 	C("man"),	"?man",		STYLE_man,
222 	Z("List detailed info in displayed man page form."),
223 	C("nroff"),	"?nroff",	STYLE_nroff,
224 	Z("List detailed info in nroff."),
225 	C("options"),	"?options",	STYLE_options,
226 	Z("List short and long option details."),
227 	C("posix"),	"?posix",	STYLE_posix,
228 	Z("List posix getopt usage."),
229 	C("short"),	"?short",	STYLE_short,
230 	Z("List short option usage."),
231 	C("usage"),	"?usage",	STYLE_usage,
232 	Z("List the usage string with C style escapes."),
233 };
234 
235 static const List_t	help_tail[] =
236 {
237 	':',	C("\?\?-\alabel\a"),
238 		C("List implementation info matching \alabel\a*."),
239 	':',	C("\?\?\aname\a"),
240 		C("Equivalent to \b--help=\b\aname\a."),
241 	':',	C("\?\?"),
242 		C("Equivalent to \b--\?\?options\b."),
243 	':',	C("\?\?\?\?"),
244 		C("Equivalent to \b--\?\?man\b."),
245 	':',	C("\?\?\?\?\?\?"),
246 		C("Equivalent to \b--\?\?help\b."),
247 	':',	C("\?\?\?\?\?\?\aitem\a"),
248 		C("If the next argument is \b--\b\aoption\a then list \
249 the \aoption\a output in the \aitem\a style. Otherwise print \
250 \bversion=\b\an\a where \an\a>0 if \b--\?\?\b\aitem\a is supported, \b0\b \
251 if not."),
252 	':',	C("\?\?\?\?\?\?ESC"),
253 		C("Emit escape codes even if output is not a terminal."),
254 	':',	C("\?\?\?\?\?\?TEST"),
255 		C("Massage the output for regression testing."),
256 };
257 
258 static const Attr_t	attrs[] =
259 {
260 	"flag",		OPT_flag,
261 	"hidden",	OPT_hidden,
262 	"ignorecase",	OPT_ignorecase,
263 	"invert",	OPT_invert,
264 	"listof",	OPT_listof,
265 	"number",	OPT_number,
266 	"oneof",	OPT_oneof,
267 	"optional",	OPT_optional,
268 	"string",	OPT_string,
269 };
270 
271 static const char	unknown[] = C("unknown option or attribute");
272 
273 static const char*	heading[] =
274 {
275 	C("INDEX"),
276 	C("USER COMMANDS"),
277 	C("SYSTEM LIBRARY"),
278 	C("USER LIBRARY"),
279 	C("FILE FORMATS"),
280 	C("MISCELLANEOUS"),
281 	C("GAMES and DEMOS"),
282 	C("SPECIAL FILES"),
283 	C("ADMINISTRATIVE COMMANDS"),
284 	C("GUIs"),
285 };
286 
287 /*
288  * list of common man page strings
289  * NOTE: add but do not delete from this table
290  */
291 
292 static Msg_t		C_LC_MESSAGES_libast[] =
293 {
294 	{ C("APPLICATION USAGE") },
295 	{ C("ASYNCHRONOUS EVENTS") },
296 	{ C("BUGS") },
297 	{ C("CAVEATS") },
298 	{ C("CONSEQUENCES OF ERRORS") },
299 	{ C("DESCRIPTION") },
300 	{ C("ENVIRONMENT VARIABLES") },
301 	{ C("EXAMPLES") },
302 	{ C("EXIT STATUS") },
303 	{ C("EXTENDED DESCRIPTION") },
304 	{ C("INPUT FILES") },
305 	{ C("LIBRARY") },
306 	{ C("NAME") },
307 	{ C("OPERANDS") },
308 	{ C("OPTIONS") },
309 	{ C("OUTPUT FILES") },
310 	{ C("SEE ALSO") },
311 	{ C("STDERR") },
312 	{ C("STDIN") },
313 	{ C("STDOUT") },
314 	{ C("SYNOPSIS") },
315 	{ C("author") },
316 	{ C("copyright") },
317 	{ C("license") },
318 	{ C("name") },
319 	{ C("path") },
320 	{ C("version") },
321 };
322 
323 static unsigned char	map[UCHAR_MAX];
324 
325 static Optstate_t	state;
326 
327 /*
328  * 2007-03-19 move opt_info from _opt_info_ to (*_opt_data_)
329  *	      to allow future Opt_t growth
330  *            by 2009 _opt_info_ can be static
331  */
332 
333 #if _BLD_ast && defined(__EXPORT__)
334 #define extern		extern __EXPORT__
335 #endif
336 
337 extern Opt_t	_opt_info_;
338 
339 Opt_t		_opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state };
340 
341 #undef	extern
342 
343 __EXTERN__(Opt_t, _opt_info_);
344 
345 __EXTERN__(Opt_t*, _opt_infop_);
346 
347 Opt_t*		_opt_infop_ = &_opt_info_;
348 
349 #if _BLD_DEBUG
350 
351 /*
352  * debug usage string segment format
353  */
354 
355 static char*
356 show(register char* s)
357 {
358 	register int	c;
359 	register char*	t;
360 	register char*	e;
361 
362 	static char	buf[32];
363 
364 	if (!s)
365 		return "(null)";
366 	t = buf;
367 	e = buf + sizeof(buf) - 2;
368 	while (t < e)
369 	{
370 		switch (c = *s++)
371 		{
372 		case 0:
373 			goto done;
374 		case '\a':
375 			*t++ = '\\';
376 			c = 'a';
377 			break;
378 		case '\b':
379 			*t++ = '\\';
380 			c = 'b';
381 			break;
382 		case '\f':
383 			*t++ = '\\';
384 			c = 'f';
385 			break;
386 		case '\n':
387 			*t++ = '\\';
388 			c = 'n';
389 			break;
390 		case '\t':
391 			*t++ = '\\';
392 			c = 't';
393 			break;
394 		case '\v':
395 			*t++ = '\\';
396 			c = 'v';
397 			break;
398 		}
399 		*t++ = c;
400 	}
401  done:
402 	*t = 0;
403 	return buf;
404 }
405 
406 #endif
407 
408 /*
409  * pop the push stack
410  */
411 
412 static Push_t*
413 pop(register Push_t* psp)
414 {
415 	register Push_t*	tsp;
416 
417 	while (tsp = psp)
418 	{
419 		psp = psp->next;
420 		free(tsp);
421 	}
422 	return 0;
423 }
424 
425 /*
426  * skip over line space to the next token
427  */
428 
429 static char*
430 next(register char* s, int version)
431 {
432 	register char*	b;
433 
434 	while (*s == '\t' || *s == '\r' || version >= 1 && *s == ' ')
435 		s++;
436 	if (*s == '\n')
437 	{
438 		b = s;
439 		while (*++s == ' ' || *s == '\t' || *s == '\r');
440 		if (*s == '\n')
441 			return b;
442 	}
443 	return s;
444 }
445 
446 /*
447  * skip to t1 or t2 or t3, whichever first, in s
448  *	n==0	outside [...]
449  *	n==1	inside [...] before ?
450  *	n==2	inside [...] after ?
451  *	b==0	outside {...}
452  *	b==1	inside {...}
453  * past skips past the terminator to the next token
454  * otherwise a pointer to the terminator is returned
455  *
456  * ]] for ] inside [...]
457  * ?? for ? inside [...] before ?
458  * :: for : inside [...] before ?
459  */
460 
461 static char*
462 skip(register char* s, register int t1, register int t2, register int t3, register int n, register int b, int past, int version)
463 {
464 	register int	c;
465 	register int	on = n;
466 	register int	ob = b;
467 
468 	if (version < 1)
469 	{
470 		n = n >= 1;
471 		for (;;)
472 		{
473 			switch (*s++)
474 			{
475 			case 0:
476 				break;
477 			case '[':
478 				n++;
479 				continue;
480 			case ']':
481 				if (--n <= 0)
482 					break;
483 				continue;
484 			default:
485 				continue;
486 			}
487 			break;
488 		}
489 	}
490 	else while (c = *s++)
491 	{
492 		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)));
493 		if (c == '[')
494 		{
495 			if (!n)
496 				n = 1;
497 		}
498 		else if (c == ']')
499 		{
500 			if (n)
501 			{
502 				if (*s == ']')
503 					s++;
504 				else if (on == 1)
505 					break;
506 				else
507 					n = 0;
508 			}
509 		}
510 		else if (c == GO)
511 		{
512 			if (n == 0)
513 				b++;
514 		}
515 		else if (c == OG)
516 		{
517 			if (n == 0 && b-- == ob)
518 				break;
519 		}
520 		else if (c == '?')
521 		{
522 			if (n == 1)
523 			{
524 				if (*s == '?')
525 					s++;
526 				else
527 				{
528 					if (n == on && (c == t1 || c == t2 || c == t3))
529 						break;
530 					n = 2;
531 				}
532 			}
533 		}
534 		else if (n == on && (c == t1 || c == t2 || c == t3))
535 		{
536 			if (n == 1 && c == ':' && *s == c)
537 				s++;
538 			else
539 				break;
540 		}
541 	}
542 	return past && *(s - 1) ? next(s, version) : s - 1;
543 }
544 
545 /*
546  * match s with t
547  * t translated if possible
548  * imbedded { - _ ' } ignored
549  * * separates required prefix from optional suffix
550  * otherwise prefix match
551  */
552 
553 static int
554 match(char* s, char* t, int version, const char* id, const char* catalog)
555 {
556 	register char*	w;
557 	register char*	x;
558 	char*		xw;
559 	char*		ww;
560 	int		n;
561 	int		v;
562 	int		j;
563 
564 	for (n = 0; n < 2; n++)
565 	{
566 		if (n)
567 			x = t;
568 		else
569 		{
570 			if (catalog)
571 			{
572 				w = skip(t, ':', '?', 0, 1, 0, 0, version);
573 				w = sfprints("%-.*s", w - t, t);
574 				x = T(id, catalog, w);
575 				if (x == w)
576 					continue;
577 			}
578 			x = T(NiL, ID, t);
579 			if (x == t)
580 				continue;
581 		}
582 		do
583 		{
584 			v = 0;
585 			xw = x;
586 			w = ww = s;
587 			while (*x && *w)
588 			{
589 				if (isupper(*x))
590 					xw = x;
591 				if (isupper(*w))
592 					ww = w;
593 				if (*x == '*' && !v++ || *x == '\a')
594 				{
595 					if (*x == '\a')
596 						do
597 						{
598 							if (!*++x)
599 							{
600 								x--;
601 								break;
602 							}
603 						} while (*x != '\a');
604 					j = *(x + 1);
605 					if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
606 						while (*w)
607 							w++;
608 				}
609 				else if (sep(*x))
610 					xw = ++x;
611 				else if (sep(*w) && w != s)
612 					ww = ++w;
613 				else if (*x == *w)
614 				{
615 					x++;
616 					w++;
617 				}
618 				else if (w == ww && x == xw)
619 					break;
620 				else
621 				{
622 					if (x != xw)
623 					{
624 						while (*x && !sep(*x) && !isupper(*x))
625 							x++;
626 						if (!*x)
627 							break;
628 						if (sep(*x))
629 							x++;
630 						xw = x;
631 					}
632 					while (w > ww && *w != *x)
633 						w--;
634 				}
635 			}
636 			if (!*w)
637 			{
638 				if (!v)
639 				{
640 					for (;;)
641 					{
642 						switch (*x++)
643 						{
644 						case 0:
645 						case ':':
646 						case '|':
647 						case '?':
648 						case ']':
649 							return 1;
650 						case '*':
651 							break;
652 						default:
653 							continue;
654 						}
655 						break;
656 					}
657 					break;
658 				}
659 				return 1;
660 			}
661 		} while (*(x = skip(x, '|', 0, 0, 1, 0, 0, version)) == '|' && x++);
662 	}
663 	return 0;
664 }
665 
666 /*
667  * prefix search for s in tab with num elements of size
668  * with optional translation
669  */
670 
671 static void*
672 search(const void* tab, size_t num, size_t siz, char* s)
673 {
674 	register char*	p;
675 	register char*	e;
676 
677 	for (e = (p = (char*)tab) + num * siz; p < e; p += siz)
678 		if (match(s, *((char**)p), -1, NiL, NiL))
679 			return (void*)p;
680 	return 0;
681 }
682 
683 /*
684  * save s and return the saved pointer
685  */
686 
687 static char*
688 save(const char* s)
689 {
690 	Save_t*		p;
691 	Dtdisc_t*	d;
692 
693 	static Dt_t*	dict;
694 
695 	if (!dict)
696 	{
697 		if (!(d = newof(0, Dtdisc_t, 1, 0)))
698 			return (char*)s;
699 		d->key = offsetof(Save_t, text);
700 		if (!(dict = dtopen(d, Dthash)))
701 			return (char*)s;
702 	}
703 	if (!(p = (Save_t*)dtmatch(dict, s)))
704 	{
705 		if (!(p = newof(0, Save_t, 1, strlen(s))))
706 			return (char*)s;
707 		strcpy(p->text, s);
708 		dtinsert(dict, p);
709 	}
710 	return p->text;
711 }
712 
713 /*
714  * initialize the attributes for pass p from opt string s
715  */
716 
717 static int
718 init(register char* s, Optpass_t* p)
719 {
720 	register char*	t;
721 	register char*	u;
722 	register int	c;
723 	register int	a;
724 	register int	n;
725 
726 	if (!opt_info.state->msgdict)
727 	{
728 #if !_PACKAGE_astsa
729 		if (!ast.locale.serial)
730 			setlocale(LC_ALL, "");
731 #endif
732 		opt_info.state->vp = sfstropen();
733 		opt_info.state->xp = sfstropen();
734 		opt_info.state->msgdisc.key = offsetof(Msg_t, text);
735 		opt_info.state->msgdisc.size = -1;
736 		opt_info.state->msgdisc.link = offsetof(Msg_t, link);
737 		if (opt_info.state->msgdict = dtopen(&opt_info.state->msgdisc, Dthash))
738 			for (n = 0; n < elementsof(C_LC_MESSAGES_libast); n++)
739 				dtinsert(opt_info.state->msgdict, C_LC_MESSAGES_libast + n);
740 		if (!map[OPT_FLAGS[0]])
741 			for (n = 0, t = OPT_FLAGS; *t; t++)
742 				map[*t] = ++n;
743 	}
744 #if _BLD_DEBUG
745 	error(-1, "optget debug");
746 #endif
747 	p->oopts = s;
748 	p->version = 0;
749 	p->prefix = 2;
750 	p->section = 1;
751 	p->flags = 0;
752 	p->id = error_info.id;
753 	p->catalog = 0;
754 	s = next(s, 0);
755 	if (*s == ':')
756 		s++;
757 	if (*s == '+')
758 		s++;
759 	s = next(s, 0);
760 	if (*s++ == '[')
761 	{
762 		if (*s == '+')
763 			p->version = 1;
764 		else if (*s++ == '-')
765 		{
766 			if (*s == '?' || *s == ']')
767 				p->version = 1;
768 			else
769 			{
770 				if (!isdigit(*s))
771 					p->version = 1;
772 				else
773 					while (isdigit(*s))
774 						p->version = p->version * 10 + (*s++ - '0');
775 				while (*s && *s != '?' && *s != ']')
776 				{
777 					c = *s++;
778 					if (!isdigit(*s))
779 						n = 1;
780 					else
781 					{
782 						n = 0;
783 						while (isdigit(*s))
784 							n = n * 10 + (*s++ - '0');
785 					}
786 					switch (c)
787 					{
788 					case '+':
789 						p->flags |= OPT_plus;
790 						break;
791 					case 'c':
792 						p->flags |= OPT_cache;
793 						break;
794 					case 'i':
795 						p->flags |= OPT_ignore;
796 						break;
797 					case 'l':
798 						p->flags |= OPT_long;
799 						break;
800 					case 'n':
801 						p->flags |= OPT_numeric;
802 						break;
803 					case 'o':
804 						p->flags |= OPT_old;
805 						break;
806 					case 'p':
807 						p->prefix = n;
808 						break;
809 					case 's':
810 						p->section = n;
811 						if (n > 1 && n < 6)
812 						{
813 							p->flags |= OPT_functions;
814 							p->prefix = 0;
815 						}
816 						break;
817 					}
818 				}
819 			}
820 		}
821 		while (*s)
822 			if (*s++ == ']')
823 			{
824 				while (isspace(*s))
825 					s++;
826 				if (*s++ == '[')
827 				{
828 					if (*s++ != '-')
829 					{
830 						if (strneq(s - 1, "+NAME?", 6))
831 						{
832 							for (s += 5; *s == '\a' || *s == '\b' || *s == '\v' || *s == ' '; s++);
833 							if (*s != '\f')
834 							{
835 								for (t = s; *t && *t != ' ' && *t != ']'; t++);
836 								if (t > s)
837 								{
838 									u = t;
839 									if (*(t - 1) == '\a' || *(t - 1) == '\b' || *(t - 1) == '\v')
840 										t--;
841 									if (t > s)
842 									{
843 										while (*u == ' ' || *u == '\\')
844 											u++;
845 										if (*u == '-' || *u == ']')
846 											p->id = save(sfprints("%-.*s", t - s, s));
847 									}
848 								}
849 							}
850 						}
851 						break;
852 					}
853 					if (*s == '-')
854 						s++;
855 					if (strneq(s, "catalog?", 8))
856 						p->catalog = s += 8;
857 				}
858 			}
859 	}
860 	if (!error_info.id)
861 	{
862 		if (!(error_info.id = p->id))
863 			p->id = "command";
864 	}
865 	else if (p->id == error_info.id)
866 		p->id = save(p->id);
867 	if (s = p->catalog)
868 		p->catalog = ((t = strchr(s, ']')) && (!p->id || (t - s) != strlen(p->id) || !strneq(s, p->id, t - s))) ? save(sfprints("%-.*s", t - s, s)) : (char*)0;
869 	if (!p->catalog)
870 	{
871 		if (opt_info.disc && opt_info.disc->catalog && (!p->id || !streq(opt_info.disc->catalog, p->id)))
872 			p->catalog = opt_info.disc->catalog;
873 		else
874 			p->catalog = ID;
875 	}
876 	s = p->oopts;
877 	if (*s == ':')
878 		s++;
879 	if (*s == '+')
880 	{
881 		s++;
882 		p->flags |= OPT_plus;
883 	}
884 	s = next(s, 0);
885 	if (*s != '[')
886 		for (t = s, a = 0; *t; t++)
887 			if (!a && *t == '-')
888 			{
889 				p->flags |= OPT_minus;
890 				break;
891 			}
892 			else if (*t == '[')
893 				a++;
894 			else if (*t == ']')
895 				a--;
896 	if (!p->version && (t = strchr(s, '(')) && strchr(t, ')') && (opt_info.state->cp || (opt_info.state->cp = sfstropen())))
897 	{
898 		/*
899 		 * solaris long option compatibility
900 		 */
901 
902 		p->version = 1;
903 		for (t = p->oopts; t < s; t++)
904 			sfputc(opt_info.state->cp, *t);
905 		n = t - p->oopts;
906 		sfputc(opt_info.state->cp, '[');
907 		sfputc(opt_info.state->cp, '-');
908 		sfputc(opt_info.state->cp, ']');
909 		c = *s++;
910 		while (c)
911 		{
912 			sfputc(opt_info.state->cp, '[');
913 			sfputc(opt_info.state->cp, c);
914 			if (a = (c = *s++) == ':')
915 				c = *s++;
916 			if (c == '(')
917 			{
918 				sfputc(opt_info.state->cp, ':');
919 				for (;;)
920 				{
921 					while ((c = *s++) && c != ')')
922 						sfputc(opt_info.state->cp, c);
923 					if (!c || (c = *s++) != '(')
924 						break;
925 					sfputc(opt_info.state->cp, '|');
926 				}
927 			}
928 			sfputc(opt_info.state->cp, ']');
929 			if (a)
930 				sfputr(opt_info.state->cp, ":[string]", -1);
931 		}
932 		if (!(p->oopts = s = sfstruse(opt_info.state->cp)))
933 			return -1;
934 		s += n;
935 	}
936 	p->opts = s;
937 	message((-1, "version=%d prefix=%d section=%d flags=%04x id=%s catalog=%s", p->version, p->prefix, p->section, p->flags, p->id, p->catalog));
938 	return 0;
939 }
940 
941 /*
942  * return the bold set/unset sequence for style
943  */
944 
945 static const char*
946 font(int f, int style, int set)
947 {
948 	switch (style)
949 	{
950 	case STYLE_html:
951 		return fonts[f].html[set];
952 	case STYLE_nroff:
953 		return fonts[f].nroff[set];
954 	case STYLE_short:
955 	case STYLE_long:
956 	case STYLE_posix:
957 	case STYLE_api:
958 		break;
959 	default:
960 		if (opt_info.state->emphasis > 0)
961 			return fonts[f].term[set];
962 		break;
963 	}
964 	return "";
965 }
966 
967 /*
968  * expand \f...\f info
969  * *p set to next char after second \f
970  * expanded value returned
971  */
972 
973 static char*
974 expand(register char* s, register char* e, char** p, Sfio_t* ip, char* id)
975 {
976 	register int	c;
977 	register char*	b = s;
978 	int		n;
979 
980 	n = sfstrtell(ip);
981 	c = 1;
982 	while ((!e || s < e) && (c = *s++) && c != '\f');
983 	sfwrite(ip, b, s - b - 1);
984 	sfputc(ip, 0);
985 	b = sfstrbase(ip) + n;
986 	n = sfstrtell(ip);
987 	if (!c)
988 		s--;
989 	if (*b == '?')
990 	{
991 		if (!*++b || streq(b, "NAME"))
992 		{
993 			if (!(b = id))
994 				b = "command";
995 			sfstrseek(ip, 0, SEEK_SET);
996 			sfputr(ip, b, -1);
997 			n = 0;
998 		}
999 		else
1000 			n = 1;
1001 	}
1002 	else if (!opt_info.disc || !opt_info.disc->infof || (*opt_info.disc->infof)(&opt_info, ip, b, opt_info.disc) < 0)
1003 		n = 0;
1004 	*p = s;
1005 	if (s = sfstruse(ip))
1006 		s += n;
1007 	else
1008 		s = "error";
1009 	return s;
1010 }
1011 
1012 /*
1013  * push \f...\f info
1014  */
1015 
1016 static Push_t*
1017 info(Push_t* psp, char* s, char* e, Sfio_t* ip, char* id)
1018 {
1019 	register char*	b;
1020 	int		n;
1021 	Push_t*		tsp;
1022 
1023 	static Push_t	push;
1024 
1025 	b = expand(s, e, &s, ip, id);
1026 	n = strlen(b);
1027 	if (tsp = newof(0, Push_t, 1, n + 1))
1028 	{
1029 		tsp->nb = (char*)(tsp + 1);
1030 		tsp->ne = tsp->nb + n;
1031 		strcpy(tsp->nb, b);
1032 	}
1033 	else
1034 		tsp = &push;
1035 	tsp->next = psp;
1036 	tsp->ob = s;
1037 	tsp->oe = e;
1038 	return tsp;
1039 }
1040 
1041 /*
1042  * push translation
1043  */
1044 
1045 static Push_t*
1046 localize(Push_t* psp, char* s, char* e, int term, int n, Sfio_t* ip, int version, char* id, char* catalog)
1047 {
1048 	char*		t;
1049 	char*		u;
1050 	Push_t*		tsp;
1051 	int		c;
1052 
1053 	t = skip(s, term, 0, 0, n, 0, 0, version);
1054 	if (e && t > e)
1055 		t = e;
1056 	while (s < t)
1057 	{
1058 		switch (c = *s++)
1059 		{
1060 		case ':':
1061 		case '?':
1062 			if (term && *s == c)
1063 				s++;
1064 			break;
1065 		case ']':
1066 			if (*s == c)
1067 				s++;
1068 			break;
1069 		}
1070 		sfputc(ip, c);
1071 	}
1072 	if (!(s = sfstruse(ip)) || (u = T(id, catalog, s)) == s)
1073 		return 0;
1074 	n = strlen(u);
1075 	if (tsp = newof(0, Push_t, 1, n + 1))
1076 	{
1077 		tsp->nb = (char*)(tsp + 1);
1078 		tsp->ne = tsp->nb + n;
1079 		strcpy(tsp->nb, u);
1080 		tsp->ob = t;
1081 		tsp->oe = e;
1082 		tsp->ch = 1;
1083 	}
1084 	tsp->next = psp;
1085 	return tsp;
1086 }
1087 
1088 /*
1089  * output label s from [ ...label...[?...] ] to sp
1090  * 1 returned if the label was translated
1091  */
1092 
1093 static int
1094 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)
1095 {
1096 	register int	c;
1097 	register char*	t;
1098 	register char*	e;
1099 	int		ostyle;
1100 	int		a;
1101 	int		i;
1102 	char*		p;
1103 	char*		w;
1104 	char*		y;
1105 	int		va;
1106 	Push_t*		tsp;
1107 
1108 	int		r = 0;
1109 	int		n = 1;
1110 	Push_t*		psp = 0;
1111 
1112 	if ((ostyle = style) > (STYLE_nroff - (sep <= 0)) && f != FONT_LITERAL)
1113 		style = 0;
1114 	if (z < 0)
1115 		e = s + strlen(s);
1116 	else
1117 		e = s + z;
1118 	if (sep > 0)
1119 	{
1120 		if (sep == ' ' && style == STYLE_nroff)
1121 			sfputc(sp, '\\');
1122 		sfputc(sp, sep);
1123 	}
1124 	sep = !sep || z < 0;
1125 	va = 0;
1126 	y = 0;
1127 	if (about)
1128 		sfputc(sp, '(');
1129 	if (version < 1)
1130 	{
1131 		a = 0;
1132 		for (;;)
1133 		{
1134 			if (s >= e)
1135 				return r;
1136 			switch (c = *s++)
1137 			{
1138 			case '[':
1139 				a++;
1140 				break;
1141 			case ']':
1142 				if (--a < 0)
1143 					return r;
1144 				break;
1145 			}
1146 			sfputc(sp, c);
1147 		}
1148 	}
1149 	else if (level && (*(p = skip(s, 0, 0, 0, 1, level, 1, version)) == ':' || *p == '#'))
1150 	{
1151 		va = 0;
1152 		if (*++p == '?' || *p == *(p - 1))
1153 		{
1154 			p++;
1155 			va |= OPT_optional;
1156 		}
1157 		if (*(p = next(p, version)) == '[')
1158 			y = p + 1;
1159 	}
1160 	if (X(catalog) && (!level || *s == '\a' || *(s - 1) != '+') &&
1161 	    (tsp = localize(psp, s, e, (sep || level) ? '?' : 0, sep || level, ip, version, id, catalog)))
1162 	{
1163 		psp= tsp;
1164 		s = psp->nb;
1165 		e = psp->ne;
1166 		r = psp->ch > 0;
1167 	}
1168 	switch (*s)
1169 	{
1170 	case '\a':
1171 		if (f == FONT_ITALIC)
1172 			s++;
1173 		f = 0;
1174 		break;
1175 	case '\b':
1176 		if (f == FONT_BOLD)
1177 			s++;
1178 		f = 0;
1179 		break;
1180 	case '\v':
1181 		if (f == FONT_LITERAL)
1182 			s++;
1183 		f = 0;
1184 		break;
1185 	default:
1186 		if (f)
1187 			sfputr(sp, font(f, style, 1), -1);
1188 		break;
1189 	}
1190 	for (;;)
1191 	{
1192 		if (s >= e)
1193 		{
1194 			if (!(tsp = psp))
1195 				goto restore;
1196 			s = psp->ob;
1197 			e = psp->oe;
1198 			psp = psp->next;
1199 			free(tsp);
1200 			continue;
1201 		}
1202 		switch (c = *s++)
1203 		{
1204 		case '(':
1205 			if (n)
1206 			{
1207 				n = 0;
1208 				if (f)
1209 				{
1210 					sfputr(sp, font(f, style, 0), -1);
1211 					f = 0;
1212 				}
1213 			}
1214 			break;
1215 		case '?':
1216 		case ':':
1217 		case ']':
1218 			if (psp && psp->ch)
1219 				break;
1220 			if (y)
1221 			{
1222 				if (va & OPT_optional)
1223 					sfputc(sp, '[');
1224 				sfputc(sp, '=');
1225 				label(sp, 0, y, 0, -1, 0, style, FONT_ITALIC, ip, version, id, catalog);
1226 				if (va & OPT_optional)
1227 					sfputc(sp, ']');
1228 				y = 0;
1229 			}
1230 			switch (c)
1231 			{
1232 			case '?':
1233 				if (*s == '?')
1234 					s++;
1235 				else if (*s == ']' && *(s + 1) != ']')
1236 					continue;
1237 				else if (sep)
1238 					goto restore;
1239 				else if (X(catalog) && (tsp = localize(psp, s, e, 0, 1, ip, version, id, catalog)))
1240 				{
1241 					psp = tsp;
1242 					s = psp->nb;
1243 					e = psp->ne;
1244 				}
1245 				break;
1246 			case ']':
1247 				if (sep && *s++ != ']')
1248 					goto restore;
1249 				break;
1250 			case ':':
1251 				if (sep && *s++ != ':')
1252 					goto restore;
1253 				break;
1254 			}
1255 			break;
1256 		case '\a':
1257 			a = FONT_ITALIC;
1258 		setfont:
1259 			if (f & ~a)
1260 			{
1261 				sfputr(sp, font(f, style, 0), -1);
1262 				f = 0;
1263 			}
1264 			if (!f && style == STYLE_html)
1265 			{
1266 				for (t = s; t < e && !isspace(*t) && !iscntrl(*t); t++);
1267 				if (*t == c && *++t == '(')
1268 				{
1269 					w = t;
1270 					while (++t < e && isdigit(*t));
1271 					if (t < e && *t == ')' && t > w + 1)
1272 					{
1273 						sfprintf(sp, "<NOBR><A href=\"../man%-.*s/%-.*s.html\">%s%-.*s%s</A>%-.*s</NOBR>"
1274 							, t - w - 1, w + 1
1275 							, w - s - 1, s
1276 							, font(a, style, 1)
1277 							, w - s - 1, s
1278 							, font(a, style, 0)
1279 							, t - w + 1, w
1280 							);
1281 						s = t + 1;
1282 						continue;
1283 					}
1284 				}
1285 			}
1286 			sfputr(sp, font(a, style, !!(f ^= a)), -1);
1287 			continue;
1288 		case '\b':
1289 			a = FONT_BOLD;
1290 			goto setfont;
1291 		case '\f':
1292 			psp = info(psp, s, e, ip, id);
1293 			if (psp->nb)
1294 			{
1295 				s = psp->nb;
1296 				e = psp->ne;
1297 			}
1298 			else
1299 			{
1300 				s = psp->ob;
1301 				psp = psp->next;
1302 			}
1303 			continue;
1304 		case '\n':
1305 			sfputc(sp, c);
1306 			for (i = 0; i < level; i++)
1307 				sfputc(sp, '\t');
1308 			continue;
1309 		case '\v':
1310 			a = FONT_LITERAL;
1311 			goto setfont;
1312 		case '<':
1313 			if (style == STYLE_html)
1314 			{
1315 				sfputr(sp, "&lt;", -1);
1316 				c = 0;
1317 				for (t = s; t < e; t++)
1318 					if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-')
1319 					{
1320 						if (*t == '@')
1321 						{
1322 							if (c)
1323 								break;
1324 							c = 1;
1325 						}
1326 						else if (*t == '>')
1327 						{
1328 							if (c)
1329 							{
1330 								sfprintf(sp, "<A href=\"mailto:%-.*s>%-.*s</A>&gt;", t - s, s, t - s, s);
1331 								s = t + 1;
1332 							}
1333 							break;
1334 						}
1335 						else
1336 							break;
1337 					}
1338 				continue;
1339 			}
1340 			break;
1341 		case '>':
1342 			if (style == STYLE_html)
1343 			{
1344 				sfputr(sp, "&gt;", -1);
1345 				continue;
1346 			}
1347 			break;
1348 		case '&':
1349 			if (style == STYLE_html)
1350 			{
1351 				sfputr(sp, "&amp;", -1);
1352 				continue;
1353 			}
1354 			break;
1355 		case '-':
1356 			if (ostyle == STYLE_nroff)
1357 				sfputc(sp, '\\');
1358 			break;
1359 		case '.':
1360 			if (ostyle == STYLE_nroff)
1361 			{
1362 				sfputc(sp, '\\');
1363 				sfputc(sp, '&');
1364 			}
1365 			break;
1366 		case '\\':
1367 			if (ostyle == STYLE_nroff)
1368 			{
1369 				c = 'e';
1370 				sfputc(sp, '\\');
1371 			}
1372 			break;
1373 		case ' ':
1374 			if (ostyle == STYLE_nroff)
1375 				sfputc(sp, '\\');
1376 			break;
1377 		}
1378 		sfputc(sp, c);
1379 	}
1380  restore:
1381 	if (f)
1382 		sfputr(sp, font(f, style, 0), -1);
1383 	if (about)
1384 		sfputc(sp, ')');
1385 	if (psp)
1386 		pop(psp);
1387 	return r;
1388 }
1389 
1390 /*
1391  * output args description to sp from p of length n
1392  */
1393 
1394 static void
1395 args(register Sfio_t* sp, register char* p, register int n, int flags, int style, Sfio_t* ip, int version, char* id, char* catalog)
1396 {
1397 	register int	i;
1398 	register char*	t;
1399 	register char*	o;
1400 	register char*	a = 0;
1401 	char*		b;
1402 	int		sep;
1403 
1404 	if (flags & OPT_functions)
1405 		sep = '\t';
1406 	else
1407 	{
1408 		sep = ' ';
1409 		o = T(NiL, ID, "options");
1410 		b = style == STYLE_nroff ? "\\ " : " ";
1411 		for (;;)
1412 		{
1413 			t = (char*)memchr(p, '\n', n);
1414 			if (style >= STYLE_man)
1415 			{
1416 				if (!(a = id))
1417 					a = "...";
1418 				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);
1419 			}
1420 			else if (a)
1421 				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);
1422 			else
1423 			{
1424 				if (!(a = error_info.id) && !(a = id))
1425 					a = "...";
1426 				if (!sfstrtell(sp))
1427 					sfprintf(sp, "[%s%s%s]", b, o, b);
1428 			}
1429 			if (!t)
1430 				break;
1431 			i = ++t - p;
1432 			if (i)
1433 			{
1434 				sfputr(sp, b, -1);
1435 				if (X(catalog))
1436 				{
1437 					sfwrite(ip, p, i);
1438 					if (b = sfstruse(ip))
1439 						sfputr(sp, T(id, catalog, b), -1);
1440 					else
1441 						sfwrite(sp, p, i);
1442 				}
1443 				else
1444 					sfwrite(sp, p, i);
1445 			}
1446 			if (style == STYLE_html)
1447 				sfputr(sp, "<BR>", '\n');
1448 			else if (style == STYLE_nroff)
1449 				sfputr(sp, ".br", '\n');
1450 			else if (style == STYLE_api)
1451 				sfputr(sp, ".BR", '\n');
1452 			p = t;
1453 			n -= i;
1454 			while (n > 0 && (*p == ' ' || *p == '\t'))
1455 			{
1456 				p++;
1457 				n--;
1458 			}
1459 		}
1460 	}
1461 	if (n)
1462 		label(sp, sep, p, 0, n, 0, style, 0, ip, version, id, catalog);
1463 }
1464 
1465 /*
1466  * output [+-...label...?...] label s to sp
1467  * according to {...} level and style
1468  * return 0:header 1:paragraph
1469  */
1470 
1471 static int
1472 item(Sfio_t* sp, char* s, int about, int level, int style, Sfio_t* ip, int version, char* id, char* catalog)
1473 {
1474 	register char*	t;
1475 	int		n;
1476 	int		par;
1477 
1478 	sfputc(sp, '\n');
1479 	if (*s == '\n')
1480 	{
1481 		par = 0;
1482 		if (style >= STYLE_nroff)
1483 			sfprintf(sp, ".DS\n");
1484 		else
1485 		{
1486 			if (style == STYLE_html)
1487 				sfprintf(sp, "<PRE>\n");
1488 			else
1489 				sfputc(sp, '\n');
1490 			for (n = 0; n < level; n++)
1491 				sfputc(sp, '\t');
1492 		}
1493 		label(sp, 0, s + 1, about, -1, level, style, FONT_LITERAL, ip, version, id, catalog);
1494 		sfputc(sp, '\n');
1495 		if (style >= STYLE_nroff)
1496 			sfprintf(sp, ".DE");
1497 		else if (style == STYLE_html)
1498 			sfprintf(sp, "</PRE>");
1499 	}
1500 	else if (*s != ']' && (*s != '?' || *(s + 1) == '?'))
1501 	{
1502 		par = 0;
1503 		if (level)
1504 		{
1505 			if (style >= STYLE_nroff)
1506 				sfprintf(sp, ".H%d ", (level - (level > 2)) / 2);
1507 			else
1508 				for (n = 0; n < level; n++)
1509 					sfputc(sp, '\t');
1510 		}
1511 		if (style == STYLE_html)
1512 		{
1513 			if (!level)
1514 				sfputr(sp, "<H4>", -1);
1515 			sfputr(sp, "<A name=\"", -1);
1516 			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] == '?')
1517 				for (t = s + 8; *t && *t != ']'; t++)
1518 					if (t[0] == 'p' && (!strncmp(t, "proprietary", 11) || !strncmp(t, "private", 7)) || t[0] == 'n' && !strncmp(t, "noncommercial", 13))
1519 					{
1520 						opt_info.state->flags |= OPT_proprietary;
1521 						break;
1522 					}
1523 			label(sp, 0, s, about, -1, level, 0, 0, ip, version, id, catalog);
1524 			sfputr(sp, "\">", -1);
1525 			label(sp, 0, s, about, -1, level, style, level ? FONT_BOLD : 0, ip, version, id, catalog);
1526 			sfputr(sp, "</A>", -1);
1527 			if (!level)
1528 				sfputr(sp, "</H4>", -1);
1529 		}
1530 		else
1531 		{
1532 			if (!level)
1533 			{
1534 				if (style >= STYLE_nroff)
1535 					sfprintf(sp, ".SH ");
1536 				else if (style == STYLE_man)
1537 					sfputc(sp, '\n');
1538 				else if (style != STYLE_options && style != STYLE_match || *s == '-' || *s == '+')
1539 					sfputc(sp, '\t');
1540 			}
1541 			label(sp, 0, s, about, -1, level, style, FONT_BOLD, ip, version, id, catalog);
1542 		}
1543 	}
1544 	else
1545 	{
1546 		par = 1;
1547 		if (style >= STYLE_nroff)
1548 			sfputr(sp, level ? ".SP" : ".PP", -1);
1549 	}
1550 	if (style >= STYLE_nroff || !level)
1551 		sfputc(sp, '\n');
1552 	if (par && style < STYLE_nroff)
1553 		for (n = 0; n < level; n++)
1554 			sfputc(sp, '\t');
1555 	return par;
1556 }
1557 
1558 /*
1559  * output text to sp from p according to style
1560  */
1561 
1562 #if _BLD_DEBUG
1563 
1564 static char*	textout(Sfio_t*, char*, int, int, int, Sfio_t*, int, char*, char*);
1565 
1566 static char*
1567 trace_textout(Sfio_t* sp, register char* p, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog, int line)
1568 {
1569 	static int	depth = 0;
1570 
1571 	message((-21, "opthelp: txt#%d +++ %2d \"%s\" style=%d level=%d bump=%d", line, ++depth, show(p), style, level, bump));
1572 	p = textout(sp, p, style, level, bump, ip, version, id, catalog);
1573 	message((-21, "opthelp: txt#%d --- %2d \"%s\"", line, depth--, show(p)));
1574 	return p;
1575 }
1576 
1577 #endif
1578 
1579 static char*
1580 textout(Sfio_t* sp, register char* p, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog)
1581 {
1582 #if _BLD_DEBUG
1583 #define textout(sp,p,style,level,bump,ip,version,id,catalog)	trace_textout(sp,p,style,level,bump,ip,version,id,catalog,__LINE__)
1584 #endif
1585 	register char*	t;
1586 	register int	c;
1587 	register int	n;
1588 	char*		e;
1589 	int		a;
1590 	int		f;
1591 	int		par;
1592 	int		about;
1593 	Push_t*		tsp;
1594 
1595 	int		ident = 0;
1596 	int		lev = level;
1597 	Push_t*		psp = 0;
1598 
1599  again:
1600 	about = 0;
1601 	if ((c = *p) == GO)
1602 	{
1603 		for (;;)
1604 		{
1605 			while (*(p = next(p + 1, version)) == '\n');
1606 			if (*p == GO)
1607 			{
1608 				if (level > 1)
1609 					level++;
1610 				level++;
1611 			}
1612 			else if (*p != OG)
1613 			{
1614 				if (level <= 1 || *p != '[' || *(p + 1) != '-' || style == STYLE_man && *(p + 2) == '?' || isalpha(*(p + 2)))
1615 					break;
1616 				p = skip(p, 0, 0, 0, 1, level, 0, version);
1617 			}
1618 			else if ((level -= 2) <= lev)
1619 				return p + 1;
1620 		}
1621 		if (*p == '\f')
1622 		{
1623 			psp = info(psp, p + 1, NiL, ip, id);
1624 			if (psp->nb)
1625 				p = psp->nb;
1626 			else
1627 			{
1628 				p = psp->ob;
1629 				psp = psp->next;
1630 			}
1631 		}
1632 		if (*p != '[')
1633 			return p;
1634 		c = *++p;
1635 		if (level > 1)
1636 			level++;
1637 		level++;
1638 	}
1639 	if (c == '-' && level > 1)
1640 	{
1641 		if (style == STYLE_man)
1642 		{
1643 			about = 1;
1644 			if (*(p + 1) == '-')
1645 				p++;
1646 		}
1647 		else
1648 			for (;;)
1649 			{
1650 				p = skip(p, 0, 0, 0, 1, level, 0, version);
1651 				while (*(p = next(p + 1, version)) == '\n');
1652 				if (*p == '[')
1653 				{
1654 					if ((c = *++p) != '-')
1655 						break;
1656 				}
1657 				else if (*p == GO)
1658 					goto again;
1659 				else if (*p == OG)
1660 					return p + 1;
1661 			}
1662 	}
1663 	if (c == '+' || c == '-' && (bump = 3) || c != ' ' && level > 1)
1664 	{
1665 		p = skip(t = p + 1, '?', 0, 0, 1, level, 0, version);
1666 		if (c == '-' && (*t == '?' || isdigit(*t) || *p == '?' && *(p + 1) == '\n'))
1667 		{
1668 			if ((c = *p) != '?')
1669 				return skip(p, 0, 0, 0, 1, level, 1, version);
1670 			e = C("version");
1671 			par = item(sp, e, about, level, style, ip, version, id, ID);
1672 			for (;;)
1673 			{
1674 				while (isspace(*(p + 1)))
1675 					p++;
1676 				e = p;
1677 				if (e[1] == '@' && e[2] == '(' && e[3] == '#' && e[4] == ')')
1678 					p = e + 4;
1679 				else if (e[1] == '$' && e[2] == 'I' && e[3] == 'd' && e[4] == ':' && e[5] == ' ')
1680 				{
1681 					p = e + 5;
1682 					ident = 1;
1683 				}
1684 				else
1685 					break;
1686 			}
1687 		}
1688 		else
1689 		{
1690 			if (isdigit(c) && isdigit(*t))
1691 			{
1692 				while (isdigit(*t))
1693 					t++;
1694 				if (*t == ':')
1695 					t++;
1696 			}
1697 			else if (isalnum(c) && *t-- == ':')
1698 			{
1699 				if (X(catalog) || *t == *(t + 2))
1700 					t += 2;
1701 				else
1702 				{
1703 					sfprintf(ip, "%s", t);
1704 					if (e = sfstruse(ip))
1705 						*((t = e) + 1) = '|';
1706 				}
1707 			}
1708 			par = item(sp, t, about, level, style, ip, version, id, catalog);
1709 			c = *p;
1710 		}
1711 		if (!about && level)
1712 			par = 0;
1713 	}
1714 	else
1715 	{
1716 		if (style >= STYLE_nroff)
1717 			sfputc(sp, '\n');
1718 		else if (c == '?')
1719 			for (n = 0; n < level; n++)
1720 				sfputc(sp, '\t');
1721 		par = 0;
1722 	}
1723 	if (c == ':')
1724 		c = *(p = skip(p, '?', 0, 0, 1, 0, 0, version));
1725 	if ((c == ']' || c == '?' && *(p + 1) == ']' && *(p + 2) != ']' && p++) && (c = *(p = next(p + 1, version))) == GO)
1726 	{
1727 		p = textout(sp, p, style, level + bump + par + 1, 0, ip, version, id, catalog);
1728 		if (level > lev && *p && *(p = next(p, version)) == '[')
1729 		{
1730 			p++;
1731 			message((-21, "textout#%d p=%s", __LINE__, show(p)));
1732 			goto again;
1733 		}
1734 	}
1735 	else if (c == '?' || c == ' ')
1736 	{
1737 		p++;
1738 		if (c == ' ')
1739 			sfputc(sp, c);
1740 		else
1741 		{
1742 			if (X(catalog) && (tsp = localize(psp, p, NiL, 0, 1, ip, version, id, catalog)))
1743 			{
1744 				psp = tsp;
1745 				p = psp->nb;
1746 			}
1747 			if (style < STYLE_nroff)
1748 				for (n = 0; n < bump + 1; n++)
1749 					sfputc(sp, '\t');
1750 		}
1751 		f = 0;
1752 		for (;;)
1753 		{
1754 			switch (c = *p++)
1755 			{
1756 			case 0:
1757 				if (!(tsp = psp))
1758 				{
1759 					if (f)
1760 						sfputr(sp, font(f, style, 0), -1);
1761 					return p - 1;
1762 				}
1763 				p = psp->ob;
1764 				psp = psp->next;
1765 				free(tsp);
1766 				continue;
1767 			case ']':
1768 				if (psp && psp->ch)
1769 					break;
1770 				if (*p != ']')
1771 				{
1772 					if (f)
1773 					{
1774 						sfputr(sp, font(f, style, 0), -1);
1775 						f = 0;
1776 					}
1777 					for (;;)
1778 					{
1779 						if ((*p == '#' || *p == ':') && level > lev)
1780 						{
1781 							char*	o;
1782 							char*	v;
1783 							int	j;
1784 							int	m;
1785 							int	ol;
1786 							int	vl;
1787 
1788 							a = 0;
1789 							o = 0;
1790 							v = 0;
1791 							if (*++p == '?' || *p == *(p - 1))
1792 							{
1793 								p++;
1794 								a |= OPT_optional;
1795 							}
1796 							if (*(p = next(p, version)) == '[')
1797 							{
1798 								p = skip(p + 1, ':', '?', 0, 1, 0, 0, version);
1799 								while (*p == ':')
1800 								{
1801 									p = skip(t = p + 1, ':', '?', 0, 1, 0, 0, version);
1802 									m = p - t;
1803 									if (*t == '!')
1804 									{
1805 										o = t + 1;
1806 										ol = m - 1;
1807 									}
1808 									else if (*t == '=')
1809 									{
1810 										v = t + 1;
1811 										vl = m - 1;
1812 									}
1813 									else
1814 										for (j = 0; j < elementsof(attrs); j++)
1815 											if (strneq(t, attrs[j].name, m))
1816 											{
1817 												a |= attrs[j].flag;
1818 												break;
1819 											}
1820 								}
1821 							}
1822 							if (a & OPT_optional)
1823 							{
1824 								if (o)
1825 								{
1826 									sfprintf(sp, " %s ", T(NiL, ID, "If the option value is omitted then"));
1827 									sfputr(sp, font(FONT_BOLD, style, 1), -1);
1828 									t = o + ol;
1829 									while (o < t)
1830 									{
1831 										if (((c = *o++) == ':' || c == '?') && *o == c)
1832 											o++;
1833 										sfputc(sp, c);
1834 									}
1835 									sfputr(sp, font(FONT_BOLD, style, 0), -1);
1836 									sfprintf(sp, " %s.", T(NiL, ID, "is assumed"));
1837 								}
1838 								else
1839 									sfprintf(sp, " %s", T(NiL, ID, "The option value may be omitted."));
1840 							}
1841 							if (v)
1842 							{
1843 								sfprintf(sp, " %s ", T(NiL, ID, "The default value is"));
1844 								sfputr(sp, font(FONT_BOLD, style, 1), -1);
1845 								t = v + vl;
1846 								while (v < t)
1847 								{
1848 									if (((c = *v++) == ':' || c == '?') && *v == c)
1849 										v++;
1850 									sfputc(sp, c);
1851 								}
1852 								sfputr(sp, font(FONT_BOLD, style, 0), -1);
1853 								sfputc(sp, '.');
1854 							}
1855 							p = skip(p, 0, 0, 0, 1, 0, 1, version);
1856 						}
1857 						if (*(p = next(p, version)) == GO)
1858 						{
1859 							p = textout(sp, p, style, level + bump + !level, 0, ip, version, id, catalog);
1860 							if (*p && *(p = next(p, version)) == '[' && !isalnum(*(p + 1)))
1861 							{
1862 								p++;
1863 								message((-21, "textout#%d p=%s", __LINE__, show(p)));
1864 								goto again;
1865 							}
1866 						}
1867 						else if (*p == '[' && level > lev)
1868 						{
1869 							p++;
1870 							goto again;
1871 						}
1872 						else if (*p == '\f')
1873 						{
1874 							p++;
1875 							if (style != STYLE_keys)
1876 							{
1877 								psp = info(psp, p, NiL, ip, id);
1878 								if (psp->nb)
1879 									p = psp->nb;
1880 								else
1881 								{
1882 									p = psp->ob;
1883 									psp = psp->next;
1884 								}
1885 							}
1886 						}
1887 						else if (!*p)
1888 						{
1889 							if (!(tsp = psp))
1890 								break;
1891 							p = psp->ob;
1892 							psp = psp->next;
1893 							free(tsp);
1894 						}
1895 						else if (*p != OG)
1896 							break;
1897 						else
1898 						{
1899 							p++;
1900 							if ((level -= 2) <= lev)
1901 								break;
1902 						}
1903 					}
1904 					return p;
1905 				}
1906 				p++;
1907 				break;
1908 			case '\a':
1909 				a = FONT_ITALIC;
1910 			setfont:
1911 				if (f & ~a)
1912 				{
1913 					sfputr(sp, font(f, style, 0), -1);
1914 					f = 0;
1915 				}
1916 				if (!f && style == STYLE_html)
1917 				{
1918 					for (t = p; *t && !isspace(*t) && !iscntrl(*t); t++);
1919 					if (*t == c && *++t == '(')
1920 					{
1921 						e = t;
1922 						while (isdigit(*++t));
1923 						if (*t == ')' && t > e + 1)
1924 						{
1925 							sfprintf(sp, "<NOBR><A href=\"../man%-.*s/%-.*s.html\">%s%-.*s%s</A>%-.*s</NOBR>"
1926 								, t - e - 1, e + 1
1927 								, e - p - 1, p
1928 								, font(a, style, 1)
1929 								, e - p - 1, p
1930 								, font(a, style, 0)
1931 								, t - e + 1, e
1932 								);
1933 							p = t + 1;
1934 							continue;
1935 						}
1936 					}
1937 				}
1938 				sfputr(sp, font(a, style, !!(f ^= a)), -1);
1939 				continue;
1940 			case '\b':
1941 				a = FONT_BOLD;
1942 				goto setfont;
1943 			case '\f':
1944 				if (style != STYLE_keys)
1945 				{
1946 					psp = info(psp, p, NiL, ip, id);
1947 					if (psp->nb)
1948 						p = psp->nb;
1949 					else
1950 					{
1951 						p = psp->ob;
1952 						psp = psp->next;
1953 					}
1954 				}
1955 				continue;
1956 			case '\v':
1957 				a = FONT_LITERAL;
1958 				goto setfont;
1959 			case ' ':
1960 				if (ident && *p == '$')
1961 				{
1962 					while (*++p)
1963 						if (*p == ']')
1964 						{
1965 							if (*(p + 1) != ']')
1966 								break;
1967 							p++;
1968 						}
1969 					continue;
1970 				}
1971 			case '\n':
1972 			case '\r':
1973 			case '\t':
1974 				while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
1975 					p++;
1976 				if (*p == ']' && *(p + 1) != ']' && (!psp || !psp->ch))
1977 					continue;
1978 				c = ' ';
1979 				break;
1980 			case '<':
1981 				if (style == STYLE_html)
1982 				{
1983 					sfputr(sp, "&lt;", -1);
1984 					c = 0;
1985 					for (t = p; *t; t++)
1986 						if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-')
1987 						{
1988 							if (*t == '@')
1989 							{
1990 								if (c)
1991 									break;
1992 								c = 1;
1993 							}
1994 							else if (*t == '>')
1995 							{
1996 								if (c)
1997 								{
1998 									sfprintf(sp, "<A href=\"mailto:%-.*s\">%-.*s</A>&gt;", t - p, p, t - p, p);
1999 									p = t + 1;
2000 								}
2001 								break;
2002 							}
2003 							else
2004 								break;
2005 						}
2006 					continue;
2007 				}
2008 				break;
2009 			case '>':
2010 				if (style == STYLE_html)
2011 				{
2012 					sfputr(sp, "&gt;", -1);
2013 					continue;
2014 				}
2015 				break;
2016 			case '&':
2017 				if (style == STYLE_html)
2018 				{
2019 					sfputr(sp, "&amp;", -1);
2020 					continue;
2021 				}
2022 				break;
2023 			case '-':
2024 				if (style == STYLE_nroff)
2025 					sfputc(sp, '\\');
2026 				break;
2027 			case '.':
2028 				if (style == STYLE_nroff)
2029 				{
2030 					sfputc(sp, '\\');
2031 					sfputc(sp, '&');
2032 				}
2033 				break;
2034 			case '\\':
2035 				if (style == STYLE_nroff)
2036 				{
2037 					sfputc(sp, c);
2038 					c = 'e';
2039 				}
2040 				break;
2041 			}
2042 			sfputc(sp, c);
2043 		}
2044 	}
2045 	else if (c == '[' && level > lev)
2046 	{
2047 		p++;
2048 		goto again;
2049 	}
2050 	return p;
2051 }
2052 
2053 /*
2054  * generate optget() help [...] list from lp
2055  */
2056 
2057 static void
2058 list(Sfio_t* sp, register const List_t* lp)
2059 {
2060 	sfprintf(sp, "[%c", lp->type);
2061 	if (lp->name)
2062 	{
2063 		sfprintf(sp, "%s", lp->name);
2064 		if (lp->text)
2065 			sfprintf(sp, "?%s", lp->text);
2066 	}
2067 	sfputc(sp, ']');
2068 }
2069 
2070 /*
2071  * return pointer to help message sans `Usage: command'
2072  * if oopts is 0 then opt_info.state->pass is used
2073  * what:
2074  *	0	?short by default, ?long if any long options used
2075  *	*	otherwise see help_text[] (--???)
2076  * external formatter:
2077  *	\a...\a	italic
2078  *	\b...\b	bold
2079  *	\f...\f	discipline infof callback on ...
2080  *	\v...\v	literal
2081  * internal formatter:
2082  *	\t	indent
2083  *	\n	newline
2084  * margin flush pops to previous indent
2085  */
2086 
2087 char*
2088 opthelp(const char* oopts, const char* what)
2089 {
2090 	register Sfio_t*	sp;
2091 	register Sfio_t*	mp;
2092 	register int		c;
2093 	register char*		p;
2094 	register Indent_t*	ip;
2095 	char*			t;
2096 	char*			x;
2097 	char*			w;
2098 	char*			u;
2099 	char*			y;
2100 	char*			s;
2101 	char*			d;
2102 	char*			v;
2103 	char*			ov;
2104 	char*			pp;
2105 	char*			rb;
2106 	char*			re;
2107 	int			f;
2108 	int			i;
2109 	int			j;
2110 	int			m;
2111 	int			n;
2112 	int			a;
2113 	int			sl;
2114 	int			vl;
2115 	int			ol;
2116 	int			wl;
2117 	int			xl;
2118 	int			rm;
2119 	int			ts;
2120 	int			co;
2121 	int			z;
2122 	int			style;
2123 	int			head;
2124 	int			margin;
2125 	int			mode;
2126 	int			mutex;
2127 	int			prefix;
2128 	int			version;
2129 	long			tp;
2130 	char*			id;
2131 	char*			catalog;
2132 	Optpass_t*		o;
2133 	Optpass_t*		q;
2134 	Optpass_t*		e;
2135 	Optpass_t		one;
2136 	Help_t*			hp;
2137 	short			ptstk[elementsof(indent) + 2];
2138 	short*			pt;
2139 	Sfio_t*			vp;
2140 	Push_t*			tsp;
2141 
2142 	char*			opts = (char*)oopts;
2143 	int			flags = 0;
2144 	int			matched = 0;
2145 	int			paragraph = 0;
2146 	int			section = 1;
2147 	Push_t*			psp = 0;
2148 	Sfio_t*			sp_help = 0;
2149 	Sfio_t*			sp_text = 0;
2150 	Sfio_t*			sp_plus = 0;
2151 	Sfio_t*			sp_head = 0;
2152 	Sfio_t*			sp_body = 0;
2153 	Sfio_t*			sp_info = 0;
2154 	Sfio_t*			sp_misc = 0;
2155 
2156 	if (!(mp = opt_info.state->mp) && !(mp = opt_info.state->mp = sfstropen()))
2157 		goto nospace;
2158 	if (!what)
2159 		style = opt_info.state->style;
2160 	else if (!*what)
2161 		style = STYLE_options;
2162 	else if (*what != '?')
2163 		style = STYLE_match;
2164 	else if (!*(what + 1))
2165 		style = STYLE_man;
2166 	else if ((hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what + 1)) && hp->style >= 0)
2167 	{
2168 		style = hp->style;
2169 		if (*hp->name != '?')
2170 			what = hp->name;
2171 	}
2172 	else
2173 	{
2174 		if ((style = opt_info.state->force) < STYLE_man)
2175 			style = STYLE_man;
2176 		if (!(sp_help = sfstropen()))
2177 			goto nospace;
2178 		for (i = 0; i < elementsof(help_head); i++)
2179 			list(sp_help, &help_head[i]);
2180 		for (i = 0; i < elementsof(styles); i++)
2181 			sfprintf(sp_help, "[:%s?%s]", styles[i].match, styles[i].text);
2182 		for (i = 0; i < elementsof(help_tail); i++)
2183 			list(sp_help, &help_tail[i]);
2184 		if (!(opts = sfstruse(sp_help)))
2185 			goto nospace;
2186 	}
2187  again:
2188 	if (opts)
2189 	{
2190 		for (i = 0; i < opt_info.state->npass; i++)
2191 			if (opt_info.state->pass[i].oopts == opts)
2192 			{
2193 				o = &opt_info.state->pass[i];
2194 				break;
2195 			}
2196 		if (i >= opt_info.state->npass)
2197 		{
2198 			o = &one;
2199 			if (init((char*)opts, o))
2200 				goto nospace;
2201 		}
2202 		e = o + 1;
2203 	}
2204 	else if (opt_info.state->npass > 0)
2205 	{
2206 		o = opt_info.state->pass;
2207 		e = o + opt_info.state->npass;
2208 	}
2209 	else if (opt_info.state->npass < 0)
2210 	{
2211 		o = &opt_info.state->cache->pass;
2212 		e = o + 1;
2213 	}
2214 	else
2215 		return T(NiL, ID, "[* call optget() before opthelp() *]");
2216 	if (style <= STYLE_usage)
2217 	{
2218 		if (!(sp_text = sfstropen()) || !(sp_info = sfstropen()))
2219 			goto nospace;
2220 		if (style >= STYLE_match && style < STYLE_keys && !(sp_body = sfstropen()))
2221 			goto nospace;
2222 	}
2223 	switch (style)
2224 	{
2225 	case STYLE_api:
2226 	case STYLE_html:
2227 	case STYLE_nroff:
2228 		opt_info.state->emphasis = 0;
2229 		break;
2230 	case STYLE_usage:
2231 	case STYLE_keys:
2232 		for (q = o; q < e; q++)
2233 			if (!(q->flags & OPT_ignore) && !streq(q->catalog, o->catalog))
2234 				o = q;
2235 		/*FALLTHROUGH*/
2236 	case STYLE_posix:
2237 		sfputc(mp, '\f');
2238 		break;
2239 	default:
2240 		if (!opt_info.state->emphasis)
2241 		{
2242 			if (x = getenv("ERROR_OPTIONS"))
2243 			{
2244 				if (strmatch(x, "*noemphasi*"))
2245 					break;
2246 				if (strmatch(x, "*emphasi*"))
2247 				{
2248 					opt_info.state->emphasis = 1;
2249 					break;
2250 				}
2251 			}
2252 			if ((x = getenv("TERM")) && strmatch(x, "(ansi|vt100|xterm)*") && isatty(sffileno(sfstderr)))
2253 				opt_info.state->emphasis = 1;
2254 		}
2255 		break;
2256 	}
2257 	x = "";
2258 	xl = 0;
2259 	for (q = o; q < e; q++)
2260 	{
2261 		if (q->flags & OPT_ignore)
2262 			continue;
2263 		if (section < q->section)
2264 			section = q->section;
2265 		section = q->section;
2266 		flags |= q->flags;
2267 		p = q->opts;
2268 		prefix = q->prefix;
2269 		version = q->version;
2270 		id = q->id;
2271 		catalog = q->catalog;
2272 		switch (style)
2273 		{
2274 		case STYLE_usage:
2275 			if (xl)
2276 				sfputc(mp, '\n');
2277 			else
2278 				xl = 1;
2279 			psp = 0;
2280 			for (;;)
2281 			{
2282 				switch (c = *p++)
2283 				{
2284 				case 0:
2285 					if (!(tsp = psp))
2286 						goto style_usage;
2287 					p = psp->ob;
2288 					psp = psp->next;
2289 					free(tsp);
2290 					continue;
2291 				case '\a':
2292 					c = 'a';
2293 					break;
2294 				case '\b':
2295 					c = 'b';
2296 					break;
2297 				case '\f':
2298 					psp = info(psp, p, NiL, sp_info, id);
2299 					if (psp->nb)
2300 						p = psp->nb;
2301 					else
2302 					{
2303 						p = psp->ob;
2304 						psp = psp->next;
2305 					}
2306 					continue;
2307 				case '\n':
2308 					c = 'n';
2309 					break;
2310 				case '\r':
2311 					c = 'r';
2312 					break;
2313 				case '\t':
2314 					c = 't';
2315 					break;
2316 				case '\v':
2317 					c = 'v';
2318 					break;
2319 				case '"':
2320 					c = '"';
2321 					break;
2322 				case '\'':
2323 					c = '\'';
2324 					break;
2325 				case '\\':
2326 					c = '\\';
2327 					break;
2328 				default:
2329 					sfputc(mp, c);
2330 					continue;
2331 				}
2332 				sfputc(mp, '\\');
2333 				sfputc(mp, c);
2334 			}
2335 		style_usage:
2336 			continue;
2337 		case STYLE_keys:
2338 			a = 0;
2339 			psp = 0;
2340 			vl = 0;
2341 			for (;;)
2342 			{
2343 				if (!(c = *p++))
2344 				{
2345 					if (!(tsp = psp))
2346 						break;
2347 					p = psp->ob;
2348 					psp = psp->next;
2349 					free(tsp);
2350 					continue;
2351 				}
2352 				if (c == '\f')
2353 				{
2354 					psp = info(psp, p, NiL, sp_info, id);
2355 					if (psp->nb)
2356 						p = psp->nb;
2357 					else
2358 					{
2359 						p = psp->ob;
2360 						psp = psp->next;
2361 					}
2362 					continue;
2363 				}
2364 				f = z = 1;
2365 				t = 0;
2366 				if (a == 0 && (c == ' ' || c == '\n' && *p == '\n'))
2367 				{
2368 					if (c == ' ' && *p == ']')
2369 					{
2370 						p++;
2371 						continue;
2372 					}
2373 					if (*p == '\n')
2374 						p++;
2375 					a = c;
2376 				}
2377 				else if (c == '\n')
2378 				{
2379 					if (a == ' ')
2380 						a = -1;
2381 					else if (a == '\n' || *p == '\n')
2382 					{
2383 						a = -1;
2384 						p++;
2385 					}
2386 					continue;
2387 				}
2388 				else if ((c == ':' || c == '#') && (*p == '[' || *p == '?' && *(p + 1) == '[' && p++))
2389 					p++;
2390 				else if (c != '[')
2391 				{
2392 					if (c == GO)
2393 						vl++;
2394 					else if (c == OG)
2395 						vl--;
2396 					continue;
2397 				}
2398 				else if (*p == ' ')
2399 				{
2400 					p++;
2401 					continue;
2402 				}
2403 				else if (*p == '-')
2404 				{
2405 					z = 0;
2406 					if (*++p == '-')
2407 					{
2408 						p = skip(p, 0, 0, 0, 1, 0, 1, version);
2409 						continue;
2410 					}
2411 				}
2412 				else if (*p == '+')
2413 				{
2414 					p++;
2415 					if (vl > 0 && *p != '\a')
2416 					{
2417 						f = 0;
2418 						p = skip(p, '?', 0, 0, 1, 0, 0, version);
2419 						if (*p == '?')
2420 							p++;
2421 					}
2422 				}
2423 				else
2424 				{
2425 					if (*(p + 1) == '\f' && (vp = opt_info.state->vp))
2426 						p = expand(p + 2, NiL, &t, vp, id);
2427 					p = skip(p, ':', '?', 0, 1, 0, 0, version);
2428 					if (*p == ':')
2429 						p++;
2430 				}
2431 				if (f && *p == '?' && *(p + 1) != '?')
2432 				{
2433 					f = 0;
2434 					if (z)
2435 						p++;
2436 					else
2437 						p = skip(p, 0, 0, 0, 1, 0, 0, version);
2438 				}
2439 				if (*p == ']' && *(p + 1) != ']')
2440 				{
2441 					p++;
2442 					continue;
2443 				}
2444 				if (!*p)
2445 				{
2446 					if (!t)
2447 						break;
2448 					p = t;
2449 					t = 0;
2450 				}
2451 				m = sfstrtell(mp);
2452 				sfputc(mp, '"');
2453 				xl = 1;
2454 				/*UNDENT...*/
2455 
2456 	for (;;)
2457 	{
2458 		if (!(c = *p++))
2459 		{
2460 			if (t)
2461 			{
2462 				p = t;
2463 				t = 0;
2464 			}
2465 			if (!(tsp = psp))
2466 			{
2467 				p--;
2468 				break;
2469 			}
2470 			p = psp->ob;
2471 			psp = psp->next;
2472 			free(tsp);
2473 			continue;
2474 		}
2475 		if (a > 0)
2476 		{
2477 			if (c == '\n')
2478 			{
2479 				if (a == ' ')
2480 				{
2481 					a = -1;
2482 					break;
2483 				}
2484 				if (a == '\n' || *p == '\n')
2485 				{
2486 					a = -1;
2487 					p++;
2488 					break;
2489 				}
2490 			}
2491 		}
2492 		else if (c == ']')
2493 		{
2494 			if (*p != ']')
2495 			{
2496 				sfputc(mp, 0);
2497 				y = sfstrbase(mp) + m + 1;
2498 				if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT))
2499 				{
2500 					sfstrseek(mp, m, SEEK_SET);
2501 					xl = 0;
2502 				}
2503 				else
2504 					sfstrseek(mp, -1, SEEK_CUR);
2505 				break;
2506 			}
2507 			sfputc(mp, *p++);
2508 			continue;
2509 		}
2510 		switch (c)
2511 		{
2512 		case '?':
2513 			if (f)
2514 			{
2515 				if (*p == '?')
2516 				{
2517 					p++;
2518 					sfputc(mp, c);
2519 				}
2520 				else
2521 				{
2522 					f = 0;
2523 					sfputc(mp, 0);
2524 					y = sfstrbase(mp) + m + 1;
2525 					if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT))
2526 					{
2527 						sfstrseek(mp, m, SEEK_SET);
2528 						xl = 0;
2529 					}
2530 					else
2531 						sfstrseek(mp, -1, SEEK_CUR);
2532 					if (z && (*p != ']' || *(p + 1) == ']'))
2533 					{
2534 						if (xl)
2535 						{
2536 							sfputc(mp, '"');
2537 							sfputc(mp, '\n');
2538 						}
2539 						m = sfstrtell(mp);
2540 						sfputc(mp, '"');
2541 						xl = 1;
2542 					}
2543 					else
2544 					{
2545 						p = skip(p, 0, 0, 0, 1, 0, 0, version);
2546 						if (*p == '?')
2547 							p++;
2548 					}
2549 				}
2550 			}
2551 			else
2552 				sfputc(mp, c);
2553 			continue;
2554 		case ':':
2555 			if (f && *p == ':')
2556 				p++;
2557 			sfputc(mp, c);
2558 			continue;
2559 		case '\a':
2560 			c = 'a';
2561 			break;
2562 		case '\b':
2563 			c = 'b';
2564 			break;
2565 		case '\f':
2566 			c = 'f';
2567 			break;
2568 		case '\n':
2569 			c = 'n';
2570 			break;
2571 		case '\r':
2572 			c = 'r';
2573 			break;
2574 		case '\t':
2575 			c = 't';
2576 			break;
2577 		case '\v':
2578 			c = 'v';
2579 			break;
2580 		case '"':
2581 			c = '"';
2582 			break;
2583 		case '\\':
2584 			c = '\\';
2585 			break;
2586 		case CC_esc:
2587 			c = 'E';
2588 			break;
2589 		default:
2590 			sfputc(mp, c);
2591 			continue;
2592 		}
2593 		sfputc(mp, '\\');
2594 		sfputc(mp, c);
2595 	}
2596 
2597 				/*...INDENT*/
2598 				if (xl)
2599 				{
2600 					sfputc(mp, '"');
2601 					sfputc(mp, '\n');
2602 				}
2603 			}
2604 			continue;
2605 		}
2606 		z = 0;
2607 		head = 0;
2608 		mode = 0;
2609 		mutex = 0;
2610 		if (style > STYLE_short && style < STYLE_nroff && version < 1)
2611 		{
2612 			style = STYLE_short;
2613 			if (sp_body)
2614 			{
2615 				sfclose(sp_body);
2616 				sp_body = 0;
2617 			}
2618 		}
2619 		else if (style == STYLE_short && prefix < 2)
2620 			style = STYLE_long;
2621 		if (*p == ':')
2622 			p++;
2623 		if (*p == '+')
2624 		{
2625 			p++;
2626 			if (!(sp = sp_plus) && !(sp = sp_plus = sfstropen()))
2627 				goto nospace;
2628 		}
2629 		else if (style >= STYLE_match)
2630 			sp = sp_body;
2631 		else
2632 			sp = sp_text;
2633 		psp = 0;
2634 		for (;;)
2635 		{
2636 			if (!(*(p = next(p, version))))
2637 			{
2638 				if (!(tsp = psp))
2639 					break;
2640 				p = psp->ob;
2641 				psp = psp->next;
2642 				free(tsp);
2643 				continue;
2644 			}
2645 			if (*p == '\f')
2646 			{
2647 				psp = info(psp, p + 1, NiL, sp_info, id);
2648 				if (psp->nb)
2649 					p = psp->nb;
2650 				else
2651 				{
2652 					p = psp->ob;
2653 					psp = psp->next;
2654 				}
2655 				continue;
2656 			}
2657 			if (*p == '\n' || *p == ' ')
2658 			{
2659 				if (*(x = p = next(p + 1, version)))
2660 					while (*++p)
2661 						if (*p == '\n')
2662 						{
2663 							while (*++p == ' ' || *p == '\t' || *p == '\r');
2664 							if (*p == '\n')
2665 								break;
2666 						}
2667 				xl = p - x;
2668 				if (!*p)
2669 					break;
2670 				continue;
2671 			}
2672 			if (*p == OG)
2673 			{
2674 				p++;
2675 				continue;
2676 			}
2677 			message((-20, "opthelp: opt %s", show(p)));
2678 			if (z < 0)
2679 				z = 0;
2680 			a = 0;
2681 			f = 0;
2682 			w = 0;
2683 			d = 0;
2684 			s = 0;
2685 			rb = re = 0;
2686 			sl = 0;
2687 			vl = 0;
2688 			if (*p == '[')
2689 			{
2690 				if ((c = *(p = next(p + 1, version))) == '-')
2691 				{
2692 					if (style >= STYLE_man)
2693 					{
2694 						if (*(p + 1) != '-')
2695 						{
2696 							if (!sp_misc && !(sp_misc = sfstropen()))
2697 								goto nospace;
2698 							else
2699 								p = textout(sp_misc, p, style, 1, 3, sp_info, version, id, catalog);
2700 							continue;
2701 						}
2702 					}
2703 					else if (style == STYLE_match && *what == '-')
2704 					{
2705 						if (*(p + 1) == '?' || isdigit(*(p + 1)))
2706 							s = C("version");
2707 						else
2708 							s = p + 1;
2709 						w = (char*)what;
2710 						if (*s != '-' || *(w + 1) == '-')
2711 						{
2712 							if (*s == '-')
2713 								s++;
2714 							if (*(w + 1) == '-')
2715 								w++;
2716 							if (match(w + 1, s, version, id, catalog))
2717 							{
2718 								if (*(p + 1) == '-')
2719 									p++;
2720 								p = textout(sp, p, style, 1, 3, sp_info, version, id, catalog);
2721 								matched = -1;
2722 								continue;
2723 							}
2724 						}
2725 					}
2726 					if (!z)
2727 						z = -1;
2728 				}
2729 				else if (c == '+')
2730 				{
2731 					if (style >= STYLE_man)
2732 					{
2733 						p = textout(sp_body, p, style, 0, 0, sp_info, version, id, catalog);
2734 						if (!sp_head)
2735 						{
2736 							sp_head = sp_body;
2737 							if (!(sp_body = sfstropen()))
2738 								goto nospace;
2739 						}
2740 						continue;
2741 					}
2742 					else if (style == STYLE_match && *what == '+')
2743 					{
2744 						if (paragraph)
2745 						{
2746 							if (p[1] == '?')
2747 							{
2748 								p = textout(sp, p, style, 1, 3, sp_info, version, id, catalog);
2749 								continue;
2750 							}
2751 							paragraph = 0;
2752 						}
2753 						if (match((char*)what + 1, p + 1, version, id, catalog))
2754 						{
2755 							p = textout(sp, p, style, 1, 3, sp_info, version, id, catalog);
2756 							matched = -1;
2757 							paragraph = 1;
2758 							continue;
2759 						}
2760 					}
2761 					if (!z)
2762 						z = -1;
2763 				}
2764 				else if (c == '[' || version < 1)
2765 				{
2766 					mutex++;
2767 					continue;
2768 				}
2769 				else
2770 				{
2771 					if (c == '!')
2772 					{
2773 						a |= OPT_invert;
2774 						p++;
2775 					}
2776 					rb = p;
2777 					if (*p != ':')
2778 					{
2779 						s = p;
2780 						if (*(p + 1) == '|')
2781 						{
2782 							while (*++p && *p != '=' && *p != '!' && *p != ':' && *p != '?');
2783 							if ((p - s) > 1)
2784 								sl = p - s;
2785 							if (*p == '!')
2786 								a |= OPT_invert;
2787 						}
2788 						if (*(p + 1) == '\f')
2789 							p++;
2790 						else
2791 							p = skip(p, ':', '?', 0, 1, 0, 0, version);
2792 						if (sl || (p - s) == 1 || *(s + 1) == '=' || *(s + 1) == '!' && (a |= OPT_invert) || *(s + 1) == '|')
2793 							f = *s;
2794 					}
2795 					re = p;
2796 					if (style <= STYLE_short)
2797 					{
2798 						if (!z && !f)
2799 							z = -1;
2800 					}
2801 					else
2802 					{
2803 						if (*p == '\f' && (vp = opt_info.state->vp))
2804 							p = expand(p + 1, NiL, &t, vp, id);
2805 						else
2806 							t = 0;
2807 						if (*p == ':')
2808 						{
2809 							p = skip(w = p + 1, ':', '?', 0, 1, 0, 0, version);
2810 							if (!(wl = p - w))
2811 								w = 0;
2812 						}
2813 						else
2814 							wl = 0;
2815 						if (*p == ':' || *p == '?')
2816 						{
2817 							d = p;
2818 							p = skip(p, 0, 0, 0, 1, 0, 0, version);
2819 						}
2820 						else
2821 							d = 0;
2822 						if (style == STYLE_match)
2823 						{
2824 							if (wl && !match((char*)what, w, version, id, catalog))
2825 								wl = 0;
2826 							if ((!wl || *w == ':' || *w == '?') && (what[1] || sl && !memchr(s, what[0], sl) || !sl && what[0] != f))
2827 							{
2828 								w = 0;
2829 								if (!z)
2830 									z = -1;
2831 							}
2832 							else
2833 								matched = 1;
2834 						}
2835 						if (t)
2836 						{
2837 							p = t;
2838 							if (*p == ':' || *p == '?')
2839 							{
2840 								d = p;
2841 								p = skip(p, 0, 0, 0, 1, 0, 0, version);
2842 							}
2843 						}
2844 					}
2845 				}
2846 				p = skip(p, 0, 0, 0, 1, 0, 1, version);
2847 				if (*p == GO)
2848 					p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
2849 			}
2850 			else if (*p == ']')
2851 			{
2852 				if (mutex)
2853 				{
2854 					if (style >= STYLE_nroff)
2855 						sfputr(sp_body, "\n.OP - - anyof", '\n');
2856 					if (!(mutex & 1))
2857 					{
2858 						mutex--;
2859 						if (style <= STYLE_long)
2860 						{
2861 							sfputc(sp_body, ' ');
2862 							sfputc(sp_body, ']');
2863 						}
2864 					}
2865 					mutex--;
2866 				}
2867 				p++;
2868 				continue;
2869 			}
2870 			else if (*p == '?')
2871 			{
2872 				if (style < STYLE_match)
2873 					z = 1;
2874 				mode |= OPT_hidden;
2875 				p++;
2876 				continue;
2877 			}
2878 			else if (*p == '\\' && style==STYLE_posix)
2879 			{
2880 				if (*++p)
2881 					p++;
2882 				continue;
2883 			}
2884 			else
2885 			{
2886 				f = *p++;
2887 				s = 0;
2888 				if (style == STYLE_match && !z)
2889 					z = -1;
2890 			}
2891 			if (!z)
2892 			{
2893 				if (style == STYLE_long || prefix < 2 || (q->flags & OPT_long))
2894 					f = 0;
2895 				else if (style <= STYLE_short)
2896 					w = 0;
2897 				if (!f && !w)
2898 					z = -1;
2899 			}
2900 			ov = 0;
2901 			u = v = y = 0;
2902 			if (*p == ':' && (a |= OPT_string) || *p == '#' && (a |= OPT_number))
2903 			{
2904 				message((-21, "opthelp: arg %s", show(p)));
2905 				if (*++p == '?' || *p == *(p - 1))
2906 				{
2907 					p++;
2908 					a |= OPT_optional;
2909 				}
2910 				if (*(p = next(p, version)) == '[')
2911 				{
2912 					if (!z)
2913 					{
2914 						p = skip(y = p + 1, ':', '?', 0, 1, 0, 0, version);
2915 						while (*p == ':')
2916 						{
2917 							p = skip(t = p + 1, ':', '?', 0, 1, 0, 0, version);
2918 							m = p - t;
2919 							if (*t == '!')
2920 							{
2921 								ov = t + 1;
2922 								ol = m - 1;
2923 							}
2924 							else if (*t == '=')
2925 							{
2926 								v = t + 1;
2927 								vl = m - 1;
2928 							}
2929 							else
2930 								for (j = 0; j < elementsof(attrs); j++)
2931 									if (strneq(t, attrs[j].name, m))
2932 									{
2933 										a |= attrs[j].flag;
2934 										break;
2935 									}
2936 						}
2937 						if (*p == '?')
2938 							u = p;
2939 						p = skip(p, 0, 0, 0, 1, 0, 1, version);
2940 					}
2941 					else
2942 						p = skip(p + 1, 0, 0, 0, 1, 0, 1, version);
2943 				}
2944 				else
2945 					y = (a & OPT_number) ? T(NiL, ID, "#") : T(NiL, ID, "arg");
2946 			}
2947 			else
2948 				a |= OPT_flag;
2949 			if (!z)
2950 			{
2951 				if (style <= STYLE_short && !y && !mutex || style == STYLE_posix)
2952 				{
2953 					if (style != STYLE_posix && !sfstrtell(sp))
2954 					{
2955 						sfputc(sp, '[');
2956 						if (sp == sp_plus)
2957 							sfputc(sp, '+');
2958 						sfputc(sp, '-');
2959 					}
2960 					if (!sl)
2961 						sfputc(sp, f);
2962 					else
2963 						for (c = 0; c < sl; c++)
2964 							if (s[c] != '|')
2965 								sfputc(sp, s[c]);
2966 					if (style == STYLE_posix && y)
2967 						sfputc(sp, ':');
2968 				}
2969 				else
2970 				{
2971 					if (style >= STYLE_match)
2972 					{
2973 						sfputc(sp_body, '\n');
2974 						if (!head)
2975 						{
2976 							head = 1;
2977 							item(sp_body, (flags & OPT_functions) ? C("FUNCTIONS") : C("OPTIONS"), 0, 0, style, sp_info, version, id, ID);
2978 						}
2979 						if (style >= STYLE_nroff)
2980 						{
2981 							if (mutex & 1)
2982 							{
2983 								mutex++;
2984 								sfputr(sp_body, "\n.OP - - oneof", '\n');
2985 							}
2986 						}
2987 						else
2988 							sfputc(sp_body, '\t');
2989 					}
2990 					else
2991 					{
2992 						if (sp_body)
2993 							sfputc(sp_body, ' ');
2994 						else if (!(sp_body = sfstropen()))
2995 							goto nospace;
2996 						if (mutex)
2997 						{
2998 							if (mutex & 1)
2999 							{
3000 								mutex++;
3001 								sfputc(sp_body, '[');
3002 							}
3003 							else
3004 								sfputc(sp_body, '|');
3005 							sfputc(sp_body, ' ');
3006 						}
3007 						else
3008 							sfputc(sp_body, '[');
3009 					}
3010 					if (style >= STYLE_nroff)
3011 					{
3012 						if (flags & OPT_functions)
3013 						{
3014 							sfputr(sp_body, ".FN", ' ');
3015 							if (re > rb)
3016 								sfwrite(sp_body, rb, re - rb);
3017 							else
3018 								sfputr(sp, "void", -1);
3019 							if (w)
3020 								label(sp_body, ' ', w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog);
3021 						}
3022 						else
3023 						{
3024 							sfputr(sp_body, ".OP", ' ');
3025 							if (sl)
3026 								sfwrite(sp_body, s, sl);
3027 							else
3028 								sfputc(sp_body, f ? f : '-');
3029 							sfputc(sp_body, ' ');
3030 							if (w)
3031 							{
3032 								if (label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, catalog))
3033 								{
3034 									sfputc(sp_body, '|');
3035 									label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, native);
3036 								}
3037 							}
3038 							else
3039 								sfputc(sp_body, '-');
3040 							sfputc(sp_body, ' ');
3041 							m = a & OPT_TYPE;
3042 							for (j = 0; j < elementsof(attrs); j++)
3043 								if (m & attrs[j].flag)
3044 								{
3045 									sfputr(sp_body, attrs[j].name, -1);
3046 									break;
3047 								}
3048 							if (m = (a & ~m) | mode)
3049 								for (j = 0; j < elementsof(attrs); j++)
3050 									if (m & attrs[j].flag)
3051 									{
3052 										sfputc(sp_body, ':');
3053 										sfputr(sp_body, attrs[j].name, -1);
3054 									}
3055 							sfputc(sp_body, ' ');
3056 							if (y)
3057 								label(sp_body, 0, y, 0, -1, 0, style, 0, sp_info, version, id, catalog);
3058 							else
3059 								sfputc(sp_body, '-');
3060 							if (v)
3061 								sfprintf(sp_body, " %-.*s", vl, v);
3062 						}
3063 					}
3064 					else
3065 					{
3066 						if (f)
3067 						{
3068 							if (sp_body == sp_plus)
3069 								sfputc(sp_body, '+');
3070 							sfputc(sp_body, '-');
3071 							sfputr(sp_body, font(FONT_BOLD, style, 1), -1);
3072 							if (!sl)
3073 							{
3074 								sfputc(sp_body, f);
3075 								if (f == '-' && y)
3076 								{
3077 									y = 0;
3078 									sfputr(sp_body, C("long-option[=value]"), -1);
3079 								}
3080 							}
3081 							else
3082 								sfwrite(sp_body, s, sl);
3083 							sfputr(sp_body, font(FONT_BOLD, style, 0), -1);
3084 							if (w)
3085 							{
3086 								sfputc(sp_body, ',');
3087 								sfputc(sp_body, ' ');
3088 							}
3089 						}
3090 						else if ((flags & OPT_functions) && re > rb)
3091 						{
3092 							sfwrite(sp_body, rb, re - rb);
3093 							sfputc(sp_body, ' ');
3094 						}
3095 						if (w)
3096 						{
3097 							if (prefix > 0)
3098 							{
3099 								sfputc(sp_body, '-');
3100 								if (prefix > 1)
3101 									sfputc(sp_body, '-');
3102 							}
3103 							if (label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog))
3104 							{
3105 								sfputc(sp_body, '|');
3106 								label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, native);
3107 							}
3108 						}
3109 						if (y)
3110 						{
3111 							if (a & OPT_optional)
3112 								sfputc(sp_body, '[');
3113 							else if (!w)
3114 								sfputc(sp_body, ' ');
3115 							if (w)
3116 								sfputc(sp_body, prefix == 1 ? ' ' : '=');
3117 							label(sp_body, 0, y, 0, -1, 0, style, FONT_ITALIC, sp_info, version, id, catalog);
3118 							if (a & OPT_optional)
3119 								sfputc(sp_body, ']');
3120 						}
3121 					}
3122 					if (style >= STYLE_match)
3123 					{
3124 						if (d)
3125 							textout(sp_body, d, style, 0, 3, sp_info, version, id, catalog);
3126 						if (u)
3127 							textout(sp_body, u, style, 0, 3, sp_info, version, id, catalog);
3128 						if ((a & OPT_invert) && w && (d || u))
3129 						{
3130 							u = skip(w, ':', '?', 0, 1, 0, 0, version);
3131 							if (f)
3132 								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);
3133 							else
3134 								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"));
3135 							if (!(t = sfstruse(sp_info)))
3136 								goto nospace;
3137 							textout(sp_body, t, style, 0, 0, sp_info, version, NiL, NiL);
3138 						}
3139 						if (*p == GO)
3140 						{
3141 							p = u ? skip(p + 1, 0, 0, 0, 0, 1, 1, version) : textout(sp_body, p, style, 4, 0, sp_info, version, id, catalog);
3142 							y = "+?";
3143 						}
3144 						else
3145 							y = " ";
3146 						if (a & OPT_optional)
3147 						{
3148 							if (ov)
3149 							{
3150 								sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "If the option value is omitted then"));
3151 								t = ov + ol;
3152 								while (ov < t)
3153 								{
3154 									if (((c = *ov++) == ':' || c == '?') && *ov == c)
3155 										ov++;
3156 									sfputc(sp_info, c);
3157 								}
3158 								sfprintf(sp_info, "\b %s.", T(NiL, ID, "is assumed"));
3159 							}
3160 							else
3161 								sfprintf(sp_info, "%s%s", y, T(NiL, ID, "The option value may be omitted."));
3162 							if (!(t = sfstruse(sp_info)))
3163 								goto nospace;
3164 							textout(sp_body, t, style, 4, 0, sp_info, version, NiL, NiL);
3165 							y = " ";
3166 						}
3167 						if (v)
3168 						{
3169 							sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "The default value is"));
3170 							t = v + vl;
3171 							while (v < t)
3172 							{
3173 								if (((c = *v++) == ':' || c == '?') && *v == c)
3174 									v++;
3175 								sfputc(sp_info, c);
3176 							}
3177 							sfputc(sp_info, '\b');
3178 							sfputc(sp_info, '.');
3179 							if (!(t = sfstruse(sp_info)))
3180 								goto nospace;
3181 							textout(sp_body, t, style, 4, 0, sp_info, version, NiL, NiL);
3182 						}
3183 					}
3184 					else if (!mutex)
3185 						sfputc(sp_body, ']');
3186 				}
3187 				if (*p == GO)
3188 				{
3189 					if (style >= STYLE_match)
3190 						p = textout(sp_body, p, style, 4, 0, sp_info, version, id, catalog);
3191 					else
3192 						p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
3193 				}
3194 			}
3195 			else if (*p == GO)
3196 				p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
3197 		}
3198 		psp = pop(psp);
3199 		if (sp_misc)
3200 		{
3201 			if (!(p = sfstruse(sp_misc)))
3202 				goto nospace;
3203 			for (t = p; *t == '\t' || *t == '\n'; t++);
3204 			if (*t)
3205 			{
3206 				item(sp_body, C("IMPLEMENTATION"), 0, 0, style, sp_info, version, id, ID);
3207 				sfputr(sp_body, p, -1);
3208 			}
3209 		}
3210 	}
3211 	version = o->version;
3212 	id = o->id;
3213 	catalog = o->catalog;
3214 	if (style >= STYLE_keys)
3215 	{
3216 		if (sp_info)
3217 			sfclose(sp_info);
3218 		if (style == STYLE_keys && sfstrtell(mp) > 1)
3219 			sfstrseek(mp, -1, SEEK_CUR);
3220 		if (!(p = sfstruse(mp)))
3221 			goto nospace;
3222 		return opt_info.msg = p;
3223 	}
3224 	sp = sp_text;
3225 	if (sfstrtell(sp) && style != STYLE_posix)
3226 		sfputc(sp, ']');
3227 	if (style == STYLE_nroff)
3228 	{
3229 		char	ud[64];
3230 
3231 		s = o->id;
3232 		t = ud;
3233 		while (t < &ud[sizeof(ud)-2] && (c = *s++))
3234 		{
3235 			if (islower(c))
3236 				c = toupper(c);
3237 			*t++ = c;
3238 		}
3239 		*t = 0;
3240 		sfprintf(sp, "\
3241 .\\\" format with nroff|troff|groff -man\n\
3242 .fp 5 CW\n\
3243 .nr mH 5\n\
3244 .de H0\n\
3245 .nr mH 0\n\
3246 .in 5n\n\
3247 \\fB\\\\$1\\fP\n\
3248 .in 7n\n\
3249 ..\n\
3250 .de H1\n\
3251 .nr mH 1\n\
3252 .in 7n\n\
3253 \\fB\\\\$1\\fP\n\
3254 .in 9n\n\
3255 ..\n\
3256 .de H2\n\
3257 .nr mH 2\n\
3258 .in 11n\n\
3259 \\fB\\\\$1\\fP\n\
3260 .in 13n\n\
3261 ..\n\
3262 .de H3\n\
3263 .nr mH 3\n\
3264 .in 15n\n\
3265 \\fB\\\\$1\\fP\n\
3266 .in 17n\n\
3267 ..\n\
3268 .de H4\n\
3269 .nr mH 4\n\
3270 .in 19n\n\
3271 \\fB\\\\$1\\fP\n\
3272 .in 21n\n\
3273 ..\n\
3274 .de OP\n\
3275 .nr mH 0\n\
3276 .ie !'\\\\$1'-' \\{\n\
3277 .ds mO \\\\fB\\\\-\\\\$1\\\\fP\n\
3278 .ds mS ,\\\\0\n\
3279 .\\}\n\
3280 .el \\{\n\
3281 .ds mO \\\\&\n\
3282 .ds mS \\\\&\n\
3283 .\\}\n\
3284 .ie '\\\\$2'-' \\{\n\
3285 .if !'\\\\$4'-' .as mO \\\\0\\\\fI\\\\$4\\\\fP\n\
3286 .\\}\n\
3287 .el \\{\n\
3288 .as mO \\\\*(mS\\\\fB%s\\\\$2\\\\fP\n\
3289 .if !'\\\\$4'-' .as mO =\\\\fI\\\\$4\\\\fP\n\
3290 .\\}\n\
3291 .in 5n\n\
3292 \\\\*(mO\n\
3293 .in 9n\n\
3294 ..\n\
3295 .de SP\n\
3296 .if \\\\n(mH==2 .in 9n\n\
3297 .if \\\\n(mH==3 .in 13n\n\
3298 .if \\\\n(mH==4 .in 17n\n\
3299 ..\n\
3300 .de FN\n\
3301 .nr mH 0\n\
3302 .in 5n\n\
3303 \\\\$1 \\\\$2\n\
3304 .in 9n\n\
3305 ..\n\
3306 .de DS\n\
3307 .in +3n\n\
3308 .ft 5\n\
3309 .nf\n\
3310 ..\n\
3311 .de DE\n\
3312 .fi\n\
3313 .ft R\n\
3314 .in -3n\n\
3315 ..\n\
3316 .TH %s %d\n\
3317 "
3318 , o->prefix == 2 ? "\\\\-\\\\-" : o->prefix == 1 ? "\\\\-" : ""
3319 , ud
3320 , section
3321 );
3322 	}
3323 	if (style == STYLE_match)
3324 	{
3325 		if (!matched)
3326 		{
3327 			if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what))
3328 			{
3329 				if (!sp_help && !(sp_help = sfstropen()))
3330 					goto nospace;
3331 				sfprintf(sp_help, "[-][:%s?%s]", hp->match, hp->text);
3332 				if (!(opts = sfstruse(sp_help)))
3333 					goto nospace;
3334 				goto again;
3335 			}
3336 			s = (char*)unknown;
3337 			goto nope;
3338 		}
3339 		else if (matched < 0)
3340 			x = 0;
3341 	}
3342 	if (sp_plus)
3343 	{
3344 		if (sfstrtell(sp_plus))
3345 		{
3346 			if (sfstrtell(sp))
3347 				sfputc(sp, ' ');
3348 			if (!(t = sfstruse(sp_plus)))
3349 				goto nospace;
3350 			sfputr(sp, t, ']');
3351 		}
3352 		sfclose(sp_plus);
3353 	}
3354 	if (style >= STYLE_man)
3355 	{
3356 		if (sp_head)
3357 		{
3358 			if (!(t = sfstruse(sp_head)))
3359 				goto nospace;
3360 			for (; *t == '\n'; t++);
3361 			sfputr(sp, t, '\n');
3362 			sfclose(sp_head);
3363 			sp_head = 0;
3364 		}
3365 		item(sp, C("SYNOPSIS"), 0, 0, style, sp_info, version, id, ID);
3366 	}
3367 	if (x)
3368 	{
3369 		for (t = x + xl; t > x && (*(t - 1) == '\n' || *(t - 1) == '\r'); t--);
3370 		xl = t - x;
3371 		if (style >= STYLE_match)
3372 		{
3373 			args(sp, x, xl, flags, style, sp_info, version, id, catalog);
3374 			x = 0;
3375 		}
3376 	}
3377 	if (sp_body)
3378 	{
3379 		if (sfstrtell(sp_body))
3380 		{
3381 			if (style < STYLE_match && sfstrtell(sp))
3382 				sfputc(sp, ' ');
3383 			if (!(t = sfstruse(sp_body)))
3384 				goto nospace;
3385 			sfputr(sp, t, -1);
3386 		}
3387 		sfclose(sp_body);
3388 		sp_body = 0;
3389 	}
3390 	if (x && style != STYLE_posix)
3391 		args(sp, x, xl, flags, style, sp_info, version, id, catalog);
3392 	if (sp_info)
3393 	{
3394 		sfclose(sp_info);
3395 		sp_info = 0;
3396 	}
3397 	if (sp_misc)
3398 	{
3399 		sfclose(sp_misc);
3400 		sp_misc = 0;
3401 	}
3402 	if (!(p = sfstruse(sp)))
3403 		goto nospace;
3404 	astwinsize(1, NiL, &opt_info.state->width);
3405 	if (opt_info.state->width < 20)
3406 		opt_info.state->width = OPT_WIDTH;
3407 	m = strlen((style <= STYLE_long && error_info.id && !strchr(error_info.id, '/')) ? error_info.id : id) + 1;
3408 	margin = style == STYLE_api ? (8 * 1024) : (opt_info.state->width - 1);
3409 	if (!(opt_info.state->flags & OPT_preformat))
3410 	{
3411 		if (style >= STYLE_man || matched < 0)
3412 		{
3413 			sfputc(mp, '\f');
3414 			ts = 0;
3415 		}
3416 		else
3417 			ts = OPT_USAGE + m;
3418 		if (style == STYLE_html)
3419 		{
3420 			char	ud[64];
3421 
3422 			s = id;
3423 			t = ud;
3424 			while (t < &ud[sizeof(ud)-2] && (c = *s++))
3425 			{
3426 				if (islower(c))
3427 					c = toupper(c);
3428 				*t++ = c;
3429 			}
3430 			*t = 0;
3431 			sfprintf(mp, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n<HTML>\n<HEAD>\n<META name=\"generator\" content=\"optget (AT&T Research) 2000-04-01\">\n%s<TITLE>%s man document</TITLE>\n</HEAD>\n<BODY bgcolor=white>\n", (opt_info.state->flags & OPT_proprietary) ? "<!--INTERNAL-->\n" : "", id);
3432 			sfprintf(mp, "<H4><TABLE width=100%%><TR><TH align=left>&nbsp;%s&nbsp;(&nbsp;%d&nbsp;)&nbsp;<TH align=center><A href=\".\" title=\"Index\">%s</A><TH align=right>%s&nbsp;(&nbsp;%d&nbsp;)</TR></TABLE></H4>\n<HR>\n", ud, section, T(NiL, ID, heading[section % 10]), ud, section);
3433 			sfprintf(mp, "<DL compact>\n<DT>");
3434 			co = 2;
3435 			*(pt = ptstk) = 0;
3436 		}
3437 		else
3438 			co = 0;
3439 		if ((rm = margin - ts) < OPT_MARGIN)
3440 			rm = OPT_MARGIN;
3441 		ip = indent;
3442 		ip->stop = (ip+1)->stop = style >= STYLE_html ? 0 : 2;
3443 		tp = 0;
3444 		n = 0;
3445 		head = 1;
3446 		while (*p == '\n')
3447 			p++;
3448 		while (c = *p++)
3449 		{
3450 			if (c == '\n')
3451 			{
3452 				ip = indent;
3453 				n = 0;
3454 				tp = 0;
3455 				sfputc(mp, '\n');
3456 				co = 0;
3457 				rm = margin;
3458 				ts = ip->stop;
3459 				if (*p == '\n')
3460 				{
3461 					while (*++p == '\n');
3462 					if ((style == STYLE_man || style == STYLE_html) && (!head || *p != ' ' && *p != '\t'))
3463 					{
3464 						if (style == STYLE_man)
3465 							p--;
3466 						else
3467 							sfprintf(mp, "<P>\n");
3468 					}
3469 				}
3470 				head = *p != ' ' && *p != '\t';
3471 				if (style == STYLE_html && (*p != '<' || !strneq(p, "<BR>", 4) && !strneq(p, "<P>", 3)))
3472 				{
3473 					y = p;
3474 					while (*p == '\t')
3475 						p++;
3476 					if (*p == '\n')
3477 						continue;
3478 					j = p - y;
3479 					if (j > *pt)
3480 					{
3481 						if (pt > ptstk)
3482 							sfprintf(mp, "<DL compact>\n");
3483 						*++pt = j;
3484 						sfprintf(mp, "<DL compact>\n");
3485 					}
3486 					else while (j < *pt)
3487 					{
3488 						if (--pt > ptstk)
3489 							sfprintf(mp, "</DL>\n");
3490 						sfprintf(mp, "</DL>\n");
3491 					}
3492 					co += sfprintf(mp, "<DT>");
3493 				}
3494 			}
3495 			else if (c == '\t')
3496 			{
3497 				if (style == STYLE_html)
3498 				{
3499 					while (*p == '\t')
3500 						p++;
3501 					if (*p != '\n')
3502 						co += sfprintf(mp, "<DD>");
3503 				}
3504 				else
3505 				{
3506 					if ((ip+1)->stop)
3507 					{
3508 						do
3509 						{
3510 							ip++;
3511 							if (*p != '\t')
3512 								break;
3513 							p++;
3514 						} while ((ip+1)->stop);
3515 						if (*p == '\n')
3516 							continue;
3517 						ts = ip->stop;
3518 						if (co >= ts)
3519 						{
3520 							sfputc(mp, '\n');
3521 							co = 0;
3522 							rm = margin;
3523 							ts = ip->stop;
3524 						}
3525 					}
3526 					while (co < ts)
3527 					{
3528 						sfputc(mp, ' ');
3529 						co++;
3530 					}
3531 				}
3532 			}
3533 			else
3534 			{
3535 				if (c == ' ' && !n)
3536 				{
3537 					if (co >= rm)
3538 						tp = 0;
3539 					else
3540 					{
3541 						tp = sfstrtell(mp);
3542 						pp = p;
3543 					}
3544 					if (style == STYLE_nroff && !co)
3545 						continue;
3546 				}
3547 				else if (style == STYLE_html)
3548 				{
3549 					if (c == '<')
3550 					{
3551 						if (strneq(p, "NOBR>", 5))
3552 							n++;
3553 						else if (n && strneq(p, "/NOBR>", 6) && !--n)
3554 						{
3555 							for (y = p += 6; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++)
3556 								if (c == '[')
3557 									sfputr(mp, "&#0091;", -1);
3558 								else if (c == ']')
3559 									sfputr(mp, "&#0093;", -1);
3560 								else
3561 									sfputc(mp, c);
3562 							sfwrite(mp, "</NOBR", 6);
3563 							c = '>';
3564 							tp = 0;
3565 							co += p - y + 6;
3566 						}
3567 					}
3568 					else if (c == '>' && !n)
3569 					{
3570 						for (y = --p; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++)
3571 							if (c == '[')
3572 								sfputr(mp, "&#0091;", -1);
3573 							else if (c == ']')
3574 								sfputr(mp, "&#0093;", -1);
3575 							else
3576 								sfputc(mp, c);
3577 						c = *sfstrseek(mp, -1, SEEK_CUR);
3578 						if (p > y + 1)
3579 						{
3580 							tp = 0;
3581 							co += p - y - 1;
3582 						}
3583 						if (co >= rm)
3584 							tp = 0;
3585 						else
3586 						{
3587 							tp = sfstrtell(mp);
3588 							pp = p;
3589 						}
3590 					}
3591 					else if (c == '[')
3592 					{
3593 						sfputr(mp, "&#0091", -1);
3594 						c = ';';
3595 					}
3596 					else if (c == ']')
3597 					{
3598 						sfputr(mp, "&#0093", -1);
3599 						c = ';';
3600 					}
3601 					else if (c == 'h')
3602 					{
3603 						y = p;
3604 						if (*y++ == 't' && *y++ == 't' && *y++ == 'p' && (*y == ':' || *y++ == 's' && *y == ':') && *y++ == ':' && *y++ == '/' && *y++ == '/')
3605 						{
3606 							while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.')
3607 								y++;
3608 							if (*y == '?')
3609 								while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.' || *y == '?' || *y == '=' || *y == '%' || *y == '&' || *y == ';' || *y == '#')
3610 									y++;
3611 							if (*(y - 1) == '.')
3612 								y--;
3613 							p--;
3614 							sfprintf(mp, "<A href=\"%-.*s\">%-.*s</A", y - p, p, y - p, p);
3615 							p = y;
3616 							c = '>';
3617 						}
3618 					}
3619 					else if (c == 'C')
3620 					{
3621 						y = p;
3622 						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++ == ')')
3623 						{
3624 							sfputr(mp, "Copyright &copy", -1);
3625 							p = y;
3626 							c = ';';
3627 						}
3628 					}
3629 				}
3630 				else if (c == ']')
3631 				{
3632 					if (n)
3633 						n--;
3634 				}
3635 				else if (c == '[')
3636 					n++;
3637 				if (c == CC_esc)
3638 				{
3639 					sfputc(mp, c);
3640 					do
3641 					{
3642 						if (!(c = *p++))
3643 						{
3644 							p--;
3645 							break;
3646 						}
3647 						sfputc(mp, c);
3648 					} while (c < 'a' || c > 'z');
3649 				}
3650 				else if (co++ >= rm && !n)
3651 				{
3652 					if (tp)
3653 					{
3654 						if (*sfstrseek(mp, tp, SEEK_SET) != ' ')
3655 							sfstrseek(mp, 1, SEEK_CUR);
3656 						tp = 0;
3657 						p = pp;
3658 						n = 0;
3659 					}
3660 					else if (c != ' ' && c != '\n')
3661 						sfputc(mp, c);
3662 					if (*p == ' ')
3663 						p++;
3664 					if (*p != '\n')
3665 					{
3666 						sfputc(mp, '\n');
3667 						for (co = 0; co < ts; co++)
3668 							sfputc(mp, ' ');
3669 						rm = margin;
3670 					}
3671 				}
3672 				else
3673 					sfputc(mp, c);
3674 			}
3675 		}
3676 		for (d = sfstrbase(mp), t = sfstrseek(mp, 0, SEEK_CUR); t > d && ((c = *(t - 1)) == '\n' || c == '\r' || c == ' ' || c == '\t'); t--);
3677 		sfstrseek(mp, t - d, SEEK_SET);
3678 		if (style == STYLE_html)
3679 		{
3680 			while (pt > ptstk)
3681 			{
3682 				if (--pt > ptstk)
3683 					sfprintf(mp, "\n</DL>");
3684 				sfprintf(mp, "\n</DL>");
3685 			}
3686 			sfprintf(mp, "</DL>\n</BODY>\n</HTML>");
3687 		}
3688 	}
3689 	else
3690 		sfputr(mp, p, 0);
3691 	if (!(p = sfstruse(mp)))
3692 		goto nospace;
3693 	if (sp)
3694 		sfclose(sp);
3695 	return opt_info.msg = p;
3696  nospace:
3697 	s = T(NiL, ID, "[* out of space *]");
3698  nope:
3699 	if (psp)
3700 		pop(psp);
3701 	if (sp_help)
3702 		sfclose(sp_help);
3703 	if (sp_text)
3704 		sfclose(sp_text);
3705 	if (sp_plus)
3706 		sfclose(sp_plus);
3707 	if (sp_info)
3708 		sfclose(sp_info);
3709 	if (sp_head)
3710 		sfclose(sp_head);
3711 	if (sp_body)
3712 		sfclose(sp_body);
3713 	if (sp_misc)
3714 		sfclose(sp_misc);
3715 	return s;
3716 }
3717 
3718 /*
3719  * compatibility wrapper to opthelp()
3720  */
3721 
3722 char*
3723 optusage(const char* opts)
3724 {
3725 	return opthelp(opts, NiL);
3726 }
3727 
3728 /*
3729  * convert number using strtonll() *except* that
3730  * 0*[[:digit:]].* is treated as [[:digit:]].*
3731  * i.e., it looks octal but isn't, to meet
3732  * posix Utility Argument Syntax -- use
3733  * 0x.* or <base>#* for alternate bases
3734  */
3735 
3736 static intmax_t
3737 optnumber(const char* s, char** t, int* e)
3738 {
3739 	intmax_t	n;
3740 	int		oerrno;
3741 
3742 	while (*s == '0' && isdigit(*(s + 1)))
3743 		s++;
3744 	oerrno = errno;
3745 	errno = 0;
3746 	n = strtonll(s, t, NiL, 0);
3747 	if (e)
3748 		*e = errno;
3749 	errno = oerrno;
3750 	return n;
3751 }
3752 
3753 /*
3754  * point opt_info.arg to an error/info message for opt_info.name
3755  * p points to opts location for opt_info.name
3756  * optget() return value is returned
3757  */
3758 
3759 static int
3760 opterror(register char* p, int err, int version, char* id, char* catalog)
3761 {
3762 	register Sfio_t*	mp;
3763 	register Sfio_t*	tp;
3764 	register char*		s;
3765 	register int		c;
3766 
3767 	if (opt_info.num != LONG_MIN)
3768 		opt_info.num = (long)(opt_info.number = 0);
3769 	if (!p || !(mp = opt_info.state->mp) && !(mp = opt_info.state->mp = sfstropen()))
3770 		goto nospace;
3771 	s = *p == '-' ? p : opt_info.name;
3772 	if (*p == '!')
3773 	{
3774 		while (*s == '-')
3775 			sfputc(mp, *s++);
3776 		sfputc(mp, 'n');
3777 		sfputc(mp, 'o');
3778 	}
3779 	sfputr(mp, s, ':');
3780 	sfputc(mp, ' ');
3781 	if (*p == '#' || *p == ':')
3782 	{
3783 		if (*p == '#')
3784 		{
3785 			s = T(NiL, ID, "numeric");
3786 			sfputr(mp, s, ' ');
3787 		}
3788 		if (*(p = next(p + 1, version)) == '[')
3789 		{
3790 			p = skip(s = p + 1, ':', '?', 0, 1, 0, 0, version);
3791 			tp = X(catalog) ? opt_info.state->xp : mp;
3792 			while (s < p)
3793 			{
3794 				if ((c = *s++) == '?' || c == ']')
3795 					s++;
3796 				sfputc(tp, c);
3797 			}
3798 			if (!X(catalog))
3799 				sfputc(mp, ' ');
3800 			else if (p = sfstruse(tp))
3801 				sfputr(mp, T(id, catalog, p), ' ');
3802 			else
3803 				goto nospace;
3804 		}
3805 		p = opt_info.name[2] ? C("value expected") : C("argument expected");
3806 	}
3807 	else if (*p == '*' || *p == '&')
3808 	{
3809 		sfputr(mp, opt_info.arg, ':');
3810 		sfputc(mp, ' ');
3811 		p = *p == '&' ? C("ambiguous option argument value") : C("unknown option argument value");
3812 	}
3813 	else if (*p == '=' || *p == '!')
3814 		p = C("value not expected");
3815 	else if (*p == '?')
3816 		p = *(p + 1) == '?' ? C("optget: option not supported") : C("ambiguous option");
3817 	else if (*p == '+')
3818 		p = C("section not found");
3819 	else
3820 	{
3821 		if (opt_info.option[0] != '?' && opt_info.option[0] != '-' || opt_info.option[1] != '?' && opt_info.option[1] != '-')
3822 			opt_info.option[0] = 0;
3823 		p = C("unknown option");
3824 	}
3825 	p = T(NiL, ID, p);
3826 	sfputr(mp, p, -1);
3827 	if (err)
3828 		sfputr(mp, " -- out of range", -1);
3829 	if (opt_info.arg = sfstruse(mp))
3830 		return ':';
3831  nospace:
3832 	opt_info.arg = T(NiL, ID, "[* out of space *]");
3833 	return ':';
3834 }
3835 
3836 /*
3837  * argv:	command line argv where argv[0] is command name
3838  *
3839  * opts:	option control string
3840  *
3841  *	'[' [flag][=][index][:<long-name>[|<alias-name>...]['?'description]] ']'
3842  *			long option name, index, description; -index returned
3843  *	':'		option takes string arg
3844  *	'#'		option takes numeric arg (concat option may follow)
3845  *	'?'		(option) following options not in usage
3846  *			(following # or :) optional arg
3847  *	'[' '[' ... ] ... '[' ... ']' ']'
3848  *			mutually exclusive option grouping
3849  *	'[' name [:attr]* [?description] ']'
3850  *			(following # or :) optional option arg description
3851  *	'\n'[' '|'\t']*	ignored for legibility
3852  *	' ' ...		optional argument(s) description (to end of string)
3853  *			or after blank line
3854  *	']]'		literal ']' within '[' ... ']'
3855  *
3856  * return:
3857  *	0		no more options
3858  *	'?'		usage: opt_info.arg points to message sans
3859  *			`Usage: command '
3860  *	':'		error: opt_info.arg points to message sans `command: '
3861  *
3862  * ':'  '#'  ' '  '['  ']'
3863  *			invalid option chars
3864  *
3865  * -- terminates option list and returns 0
3866  *
3867  * + as first opts char makes + equivalent to -
3868  *
3869  * if any # option is specified then numeric options (e.g., -123)
3870  * are associated with the leftmost # option in opts
3871  *
3872  * usage info in placed opt_info.arg when '?' returned
3873  * see help_text[] (--???) for more info
3874  */
3875 
3876 int
3877 optget(register char** argv, const char* oopts)
3878 {
3879 	register int	c;
3880 	register char*	s;
3881 	char*		a;
3882 	char*		b;
3883 	char*		e;
3884 	char*		f;
3885 	char*		g;
3886 	char*		v;
3887 	char*		w;
3888 	char*		p;
3889 	char*		q;
3890 	char*		t;
3891 	char*		y;
3892 	char*		numopt;
3893 	char*		opts;
3894 	char*		id;
3895 	char*		catalog;
3896 	int		n;
3897 	int		m;
3898 	int		k;
3899 	int		j;
3900 	int		x;
3901 	int		err;
3902 	int		no;
3903 	int		nov;
3904 	int		num;
3905 	int		numchr;
3906 	int		prefix;
3907 	int		version;
3908 	Help_t*		hp;
3909 	Push_t*		psp;
3910 	Push_t*		tsp;
3911 	Sfio_t*		vp;
3912 	Sfio_t*		xp;
3913 	Optcache_t*	cache;
3914 	Optcache_t*	pcache;
3915 	Optpass_t*	pass;
3916 
3917 #if !_PACKAGE_astsa && !_YOU_FIGURED_OUT_HOW_TO_GET_ALL_DLLS_TO_DO_THIS_
3918 	/*
3919 	 * these are not initialized by all dlls!
3920 	 */
3921 
3922 	extern Error_info_t	_error_info_;
3923 	extern Opt_t		_opt_info_;
3924 
3925 	if (!_error_infop_)
3926 		_error_infop_ = &_error_info_;
3927 	if (!_opt_infop_)
3928 		_opt_infop_ = &_opt_info_;
3929 	if (!opt_info.state)
3930 		opt_info.state = &state;
3931 #endif
3932 	if (!oopts)
3933 		return 0;
3934 	opt_info.state->pindex = opt_info.index;
3935 	opt_info.state->poffset = opt_info.offset;
3936 	if (!opt_info.index)
3937 	{
3938 		opt_info.index = 1;
3939 		opt_info.offset = 0;
3940 		if (opt_info.state->npass)
3941 		{
3942 			opt_info.state->npass = 0;
3943 			opt_info.state->join = 0;
3944 		}
3945 	}
3946 	if (!argv)
3947 		cache = 0;
3948 	else
3949 		for (pcache = 0, cache = opt_info.state->cache; cache; pcache = cache, cache = cache->next)
3950 			if (cache->pass.oopts == (char*)oopts)
3951 				break;
3952 	if (cache)
3953 	{
3954 		if (pcache)
3955 		{
3956 			pcache->next = cache->next;
3957 			cache->next = opt_info.state->cache;
3958 			opt_info.state->cache = cache;
3959 		}
3960 		pass = &cache->pass;
3961 		opt_info.state->npass = -1;
3962 	}
3963 	else
3964 	{
3965 		if (!argv)
3966 			n = opt_info.state->npass ? opt_info.state->npass : 1;
3967 		else if ((n = opt_info.state->join - 1) < 0)
3968 			n = 0;
3969 		if (n >= opt_info.state->npass || opt_info.state->pass[n].oopts != (char*)oopts)
3970 		{
3971 			for (m = 0; m < opt_info.state->npass && opt_info.state->pass[m].oopts != (char*)oopts; m++);
3972 			if (m < opt_info.state->npass)
3973 				n = m;
3974 			else
3975 			{
3976 				if (n >= elementsof(opt_info.state->pass))
3977 					n = elementsof(opt_info.state->pass) - 1;
3978 				init((char*)oopts, &opt_info.state->pass[n]);
3979 				if (opt_info.state->npass <= n)
3980 					opt_info.state->npass = n + 1;
3981 			}
3982 		}
3983 		if (!argv)
3984 			return 0;
3985 		pass = &opt_info.state->pass[n];
3986 	}
3987 	opts = pass->opts;
3988 	prefix = pass->prefix;
3989 	version = pass->version;
3990 	id = pass->id;
3991 	if (!(xp = opt_info.state->xp) || (catalog = pass->catalog) && !X(catalog))
3992 		catalog = 0;
3993 	else /* if (!error_info.catalog) */
3994 		error_info.catalog = catalog;
3995  again:
3996 	psp = 0;
3997 
3998 	/*
3999 	 * check if any options remain and determine if the
4000 	 * next option is short or long
4001 	 */
4002 
4003 	opt_info.assignment = 0;
4004 	num = 1;
4005 	w = v = 0;
4006 	x = 0;
4007 	for (;;)
4008 	{
4009 		if (!opt_info.offset)
4010 		{
4011 			/*
4012 			 * finished with the previous arg
4013 			 */
4014 
4015 			if (opt_info.index == 1 && opt_info.argv != opt_info.state->strv)
4016 			{
4017 				opt_info.argv = 0;
4018 				opt_info.state->argv[0] = 0;
4019 				if (argv[0] && (opt_info.state->argv[0] = save(argv[0])))
4020 					opt_info.argv = opt_info.state->argv;
4021 				opt_info.state->style = STYLE_short;
4022 			}
4023 			if (!(s = argv[opt_info.index]))
4024 				return 0;
4025 			if (!prefix)
4026 			{
4027 				/*
4028 				 * long with no prefix (dd style)
4029 				 */
4030 
4031 				n = 2;
4032 				if ((c = *s) != '-' && c != '+')
4033 					c = '-';
4034 				else if (*++s == c)
4035 				{
4036 					if (!*++s)
4037 					{
4038 						opt_info.index++;
4039 						return 0;
4040 					}
4041 					else if (*s == c)
4042 						return 0;
4043 				}
4044 				else if (*s == '?')
4045 					n = 1;
4046 			}
4047 			else if ((c = *s++) != '-' && (c != '+' || !(pass->flags & OPT_plus) && (!(pass->flags & OPT_numeric) || !isdigit(*s))))
4048 			{
4049 				if (!(pass->flags & OPT_old) || !isalpha(c))
4050 					return 0;
4051 				s--;
4052 				n = 1;
4053 				opt_info.offset--;
4054 			}
4055 			else if (*s == c)
4056 			{
4057 				if (!*++s)
4058 				{
4059 					/*
4060 					 * -- or ++ end of options
4061 					 */
4062 
4063 					opt_info.index++;
4064 					return 0;
4065 				}
4066 				else if (*s == c)
4067 				{
4068 					/*
4069 					 * ---* or +++* are operands
4070 					 */
4071 
4072 					return 0;
4073 				}
4074 				if (version || *s == '?' || !(pass->flags & OPT_minus))
4075 				{
4076 					/*
4077 					 * long with double prefix
4078 					 */
4079 
4080 					n = 2;
4081 				}
4082 				else
4083 				{
4084 					/*
4085 					 * short option char '-'
4086 					 */
4087 
4088 					s--;
4089 					n = 1;
4090 				}
4091 			}
4092 			else if (prefix == 1 && *s != '?')
4093 			{
4094 				/*
4095 				 * long with single prefix (find style)
4096 				 */
4097 
4098 				n = 2;
4099 			}
4100 			else
4101 			{
4102 				/*
4103 				 * short (always with single prefix)
4104 				 */
4105 
4106 				n = 1;
4107 			}
4108 
4109 			/*
4110 			 * just a prefix is an option (e.g., `-' == stdin)
4111 			 */
4112 
4113 			if (!*s)
4114 				return 0;
4115 			if (c == '+')
4116 				opt_info.arg = 0;
4117 			message((-2, "c='%c' n=%d", c, n));
4118 			if (n == 2)
4119 			{
4120 				x = 0;
4121 				opt_info.state->style = STYLE_long;
4122 				opt_info.option[0] = opt_info.name[0] = opt_info.name[1] = c;
4123 				w = &opt_info.name[prefix];
4124 				if ((*s == 'n' || *s == 'N') && (*(s + 1) == 'o' || *(s + 1) == 'O') && *(s + 2) && *(s + 2) != '=')
4125 					no = *(s + 2) == '-' ? 3 : 2;
4126 				else
4127 					no = 0;
4128 				for (c = *s; *s; s++)
4129 				{
4130 					if (*s == '=')
4131 					{
4132 						if (*(s + 1) == '=')
4133 							s++;
4134 						if (!isalnum(*(s - 1)) && *(w - 1) == (opt_info.assignment = *(s - 1)))
4135 							w--;
4136 						v = ++s;
4137 						break;
4138 					}
4139 					if (w < &opt_info.name[elementsof(opt_info.name) - 1] && *s != ':' && *s != '|' && *s != '[' && *s != ']')
4140 						*w++ = *s;
4141 				}
4142 				*w = 0;
4143 				w = &opt_info.name[prefix];
4144 				c = *w;
4145 				opt_info.offset = 0;
4146 				opt_info.index++;
4147 				break;
4148 			}
4149 			opt_info.offset++;
4150 		}
4151 		if (!argv[opt_info.index])
4152 			return 0;
4153 		if (c = argv[opt_info.index][opt_info.offset++])
4154 		{
4155 			if ((k = argv[opt_info.index][0]) != '-' && k != '+')
4156 				k = '-';
4157 			opt_info.option[0] = opt_info.name[0] = k;
4158 			opt_info.option[1] = opt_info.name[1] = c;
4159 			opt_info.option[2] = opt_info.name[2] = 0;
4160 			break;
4161 		}
4162 		opt_info.offset = 0;
4163 		opt_info.index++;
4164 	}
4165 
4166 	/*
4167 	 * at this point:
4168 	 *
4169 	 *	c	the first character of the option
4170 	 *	w	long option name if != 0, otherwise short
4171 	 *	v	long option value (via =) if w != 0
4172 	 */
4173 
4174 	if (c == '?')
4175 	{
4176 		/*
4177 		 * ? always triggers internal help
4178 		 */
4179 
4180 		if (w && !v && (*(w + 1) || !(v = argv[opt_info.index]) || !++opt_info.index))
4181 			v = w + 1;
4182 		opt_info.option[1] = c;
4183 		opt_info.option[2] = 0;
4184 		if (!w)
4185 		{
4186 			opt_info.name[1] = c;
4187 			opt_info.name[2] = 0;
4188 		}
4189 		goto help;
4190 	}
4191 	numopt = 0;
4192 	f = 0;
4193 	s = opts;
4194 
4195 	/*
4196 	 * no option can start with these characters
4197 	 */
4198 
4199 	if (c == ':' || c == '#' || c == ' ' || c == '[' || c == ']')
4200 	{
4201 		if (c != *s)
4202 			s = "";
4203 	}
4204 	else
4205 	{
4206 		a = 0;
4207 		if (!w && (pass->flags & OPT_cache))
4208 		{
4209 			if (cache)
4210 			{
4211 				if (k = cache->flags[map[c]])
4212 				{
4213 					opt_info.arg = 0;
4214 
4215 					/*
4216 					 * this is a ksh getopts workaround
4217 					 */
4218 
4219 					if (opt_info.num != LONG_MIN)
4220 						opt_info.num = (long)(opt_info.number = !(k & OPT_cache_invert));
4221 					if (!(k & (OPT_cache_string|OPT_cache_numeric)))
4222 						return c;
4223 					if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset]))
4224 					{
4225 						if (!(k & OPT_cache_numeric))
4226 						{
4227 							opt_info.offset = 0;
4228 							return c;
4229 						}
4230 						opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
4231 						if (err || e == opt_info.arg)
4232 						{
4233 							if (!err && (k & OPT_cache_optional))
4234 							{
4235 								opt_info.arg = 0;
4236 								opt_info.index--;
4237 								return c;
4238 							}
4239 						}
4240 						else if (*e)
4241 						{
4242 							opt_info.offset += e - opt_info.arg;
4243 							opt_info.index--;
4244 							return c;
4245 						}
4246 						else
4247 						{
4248 							opt_info.offset = 0;
4249 							return c;
4250 						}
4251 					}
4252 					else if (opt_info.arg = argv[opt_info.index])
4253 					{
4254 						opt_info.index++;
4255 						if ((k & OPT_cache_optional) && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1))
4256 						{
4257 							opt_info.arg = 0;
4258 							opt_info.index--;
4259 							opt_info.offset = 0;
4260 							return c;
4261 						}
4262 						if (k & OPT_cache_string)
4263 						{
4264 							opt_info.offset = 0;
4265 							return c;
4266 						}
4267 						opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
4268 						if (!err)
4269 						{
4270 							if (!*e)
4271 							{
4272 								opt_info.offset = 0;
4273 								return c;
4274 							}
4275 							if (k & OPT_cache_optional)
4276 							{
4277 								opt_info.arg = 0;
4278 								opt_info.index--;
4279 								opt_info.offset = 0;
4280 								return c;
4281 							}
4282 						}
4283 					}
4284 					else if (k & OPT_cache_optional)
4285 					{
4286 						opt_info.offset = 0;
4287 						return c;
4288 					}
4289 					opt_info.index--;
4290 				}
4291 				cache = 0;
4292 			}
4293 			else if (cache = newof(0, Optcache_t, 1, 0))
4294 			{
4295 				cache->caching = c;
4296 				c = 0;
4297 				cache->pass = *pass;
4298 				cache->next = opt_info.state->cache;
4299 				opt_info.state->cache = cache;
4300 			}
4301 		}
4302 		else
4303 			cache = 0;
4304 		for (;;)
4305 		{
4306 			if (!(*(s = next(s, version))) || *s == '\n' || *s == ' ')
4307 			{
4308 				if (!(tsp = psp))
4309 				{
4310 					if (cache)
4311 					{
4312 						/*
4313 						 * the first loop pass
4314 						 * initialized the cache
4315 						 * so one more pass to
4316 						 * check the cache or
4317 						 * bail for a full scan
4318 						 */
4319 
4320 						cache->flags[0] = 0;
4321 						c = cache->caching;
4322 						cache->caching = 0;
4323 						cache = 0;
4324 						s = opts;
4325 						continue;
4326 					}
4327 					if (!x && catalog)
4328 					{
4329 						/*
4330 						 * the first loop pass
4331 						 * translated long
4332 						 * options and there
4333 						 * were no matches so
4334 						 * one more pass for C
4335 						 * locale
4336 						 */
4337 
4338 						catalog = 0;
4339 						s = opts;
4340 						continue;
4341 					}
4342 					s = "";
4343 					break;
4344 				}
4345 				s = psp->ob;
4346 				psp = psp->next;
4347 				free(tsp);
4348 				continue;
4349 			}
4350 			if (*s == '\f')
4351 			{
4352 				psp = info(psp, s + 1, NiL, opt_info.state->xp, id);
4353 				if (psp->nb)
4354 					s = psp->nb;
4355 				else
4356 				{
4357 					s = psp->ob;
4358 					psp = psp->next;
4359 				}
4360 				continue;
4361 			}
4362 			message((-20, "optget: opt %s  c %c  w %s  num %ld", show(s), c, w, num));
4363 			if (*s == c && !w)
4364 				break;
4365 			else if (*s == '[')
4366 			{
4367 				f = s = next(s + 1, version);
4368 				k = *f;
4369 				if (k == '+' || k == '-')
4370 					/* ignore */;
4371 				else if (k == '[' || version < 1)
4372 					continue;
4373 				else if (w && !cache)
4374 				{
4375 					nov = no;
4376 					if (*(s + 1) == '\f' && (vp = opt_info.state->vp))
4377 					{
4378 						sfputc(vp, k);
4379 						s = expand(s + 2, NiL, &t, vp, id);
4380 						if (*s)
4381 							*(f = s - 1) = k;
4382 						else
4383 						{
4384 							f = sfstrbase(vp);
4385 							if (s = strrchr(f, ':'))
4386 								f = s - 1;
4387 							else
4388 								s = f + 1;
4389 						}
4390 					}
4391 					else
4392 						t = 0;
4393 					if (*s != ':')
4394 						s = skip(s, ':', '?', 0, 1, 0, 0, version);
4395 					if (*s == ':')
4396 					{
4397 						if (catalog)
4398 						{
4399 							p = skip(s + 1, '?', 0, 0, 1, 0, 0, version);
4400 							e = sfprints("%-.*s", p - (s + 1), s + 1);
4401 							g = T(id, catalog, e);
4402 							if (g == e)
4403 								p = 0;
4404 							else
4405 							{
4406 								sfprintf(xp, ":%s|%s?", g, e);
4407 								if (!(s = sfstruse(xp)))
4408 									goto nospace;
4409 							}
4410 						}
4411 						else
4412 							p = 0;
4413 						y = w;
4414 						for (;;)
4415 						{
4416 							n = m = 0;
4417 							e = s + 1;
4418 							while (*++s)
4419 							{
4420 								if (*s == '*' || *s == '\a')
4421 								{
4422 									if (*s == '\a')
4423 										do
4424 										{
4425 											if (!*++s)
4426 											{
4427 												s--;
4428 												break;
4429 											}
4430 										} while (*s != '\a');
4431 									j = *(s + 1);
4432 									if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
4433 									{
4434 										while (*w)
4435 											w++;
4436 										m = 0;
4437 										break;
4438 									}
4439 									m = 1;
4440 								}
4441 								else if (*s == *w || sep(*s) && sep(*w))
4442 									w++;
4443 								else if (*w == 0)
4444 									break;
4445 								else if (!sep(*s))
4446 								{
4447 									if (sep(*w))
4448 									{
4449 										if (*++w == *s)
4450 										{
4451 											w++;
4452 											continue;
4453 										}
4454 									}
4455 									else if (w == y || sep(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
4456 										break;
4457 									for (q = s; *q && !sep(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
4458 									if (!sep(*q))
4459 										break;
4460 									for (s = q; w > y && *w != *(s + 1); w--);
4461 								}
4462 								else if (*w != *(s + 1))
4463 									break;
4464 							}
4465 							if (!*w)
4466 							{
4467 								nov = 0;
4468 								break;
4469 							}
4470 							if (n = no)
4471 							{
4472 								m = 0;
4473 								s = e - 1;
4474 								w = y + n;
4475 								while (*++s)
4476 								{
4477 									if (*s == '*' || *s == '\a')
4478 									{
4479 										if (*s == '\a')
4480 											do
4481 											{
4482 												if (!*++s)
4483 												{
4484 													s--;
4485 													break;
4486 												}
4487 											} while (*s != '\a');
4488 										j = *(s + 1);
4489 										if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
4490 										{
4491 											while (*w)
4492 												w++;
4493 											m = 0;
4494 											break;
4495 										}
4496 										m = 1;
4497 									}
4498 									else if (*s == *w || sep(*s) && sep(*w))
4499 										w++;
4500 									else if (*w == 0)
4501 										break;
4502 									else if (!sep(*s))
4503 									{
4504 										if (sep(*w))
4505 										{
4506 											if (*++w == *s)
4507 											{
4508 												w++;
4509 												continue;
4510 											}
4511 										}
4512 										else if (w == y || sep(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
4513 											break;
4514 										for (q = s; *q && !sep(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
4515 										if (!sep(*q))
4516 											break;
4517 										for (s = q; w > y && *w != *(s + 1); w--);
4518 									}
4519 									else if (*w != *(s + 1))
4520 										break;
4521 								}
4522 								if (!*w)
4523 									break;
4524 							}
4525 							if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|')
4526 								break;
4527 							w = y;
4528 						}
4529 						if (p)
4530 							s = p;
4531 						if (!*w)
4532 						{
4533 							if (n)
4534 								num = 0;
4535 							if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']' || *s == 0)) && x)
4536 							{
4537 								psp = pop(psp);
4538 								return opterror("?", 0, version, id, catalog);
4539 							}
4540 							for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2);
4541 							if (*f == ':')
4542 							{
4543 								x = -1;
4544 								opt_info.option[1] = '-';
4545 								opt_info.option[2] = 0;
4546 							}
4547 							else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':')
4548 							{
4549 								opt_info.option[1] = x;
4550 								opt_info.option[2] = 0;
4551 							}
4552 							else
4553 							{
4554 								a = f;
4555 								if (*a == '=')
4556 									a++;
4557 								else
4558 								{
4559 									if (*(a + 1) == '!')
4560 										a++;
4561 									if (*(a + 1) == '=')
4562 										a += 2;
4563 								}
4564 								x = -strtol(a, &b, 0);
4565 								if ((b - a) > sizeof(opt_info.option) - 2)
4566 									b = a + sizeof(opt_info.option) - 2;
4567 								memcpy(&opt_info.option[1], a, b - a);
4568 								opt_info.option[b - a + 1] = 0;
4569 							}
4570 							b = e;
4571 							if (t)
4572 							{
4573 								s = t;
4574 								t = 0;
4575 							}
4576 							a = s = skip(s, 0, 0, 0, 1, 0, 0, version);
4577 							if (n)
4578 							{
4579 								w = y;
4580 								break;
4581 							}
4582 						}
4583 						w = y;
4584 					}
4585 					else if (k == c && prefix == 1)
4586 					{
4587 						w = 0;
4588 						opt_info.name[1] = c;
4589 						opt_info.name[2] = 0;
4590 						opt_info.offset = 2;
4591 						opt_info.index--;
4592 						break;
4593 					}
4594 					if (t)
4595 					{
4596 						s = t;
4597 						if (a)
4598 							a = t;
4599 					}
4600 				}
4601 				s = skip(s, 0, 0, 0, 1, 0, 1, version);
4602 				if (*s == GO)
4603 					s = skip(s + 1, 0, 0, 0, 0, 1, 1, version);
4604 				if (cache)
4605 				{
4606 					m = OPT_cache_flag;
4607 					v = s;
4608 					if (*v == '#')
4609 					{
4610 						v++;
4611 						m |= OPT_cache_numeric;
4612 					}
4613 					else if (*v == ':')
4614 					{
4615 						v++;
4616 						m |= OPT_cache_string;
4617 					}
4618 					if (*v == '?')
4619 					{
4620 						v++;
4621 						m |= OPT_cache_optional;
4622 					}
4623 					else if (*v == *(v - 1))
4624 						v++;
4625 					if (*(v = next(v, version)) == '[')
4626 						v = skip(v + 1, 0, 0, 0, 1, 0, 1, version);
4627 					if (*v != GO)
4628 					{
4629 						v = f;
4630 						for (;;)
4631 						{
4632 							if (isdigit(*f) && isdigit(*(f + 1)))
4633 								while (isdigit(*(f + 1)))
4634 									f++;
4635 							else if (*(f + 1) == '=')
4636 								break;
4637 							else
4638 								cache->flags[map[*f]] = m;
4639 							j = 0;
4640 							while (*(f + 1) == '|')
4641 							{
4642 								f += 2;
4643 								if (!(j = *f) || j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
4644 									break;
4645 								cache->flags[map[j]] = m;
4646 							}
4647 							if (j != '!' || (m & OPT_cache_invert))
4648 								break;
4649 							f = v;
4650 							m |= OPT_cache_invert;
4651 						}
4652 					}
4653 				}
4654 				else
4655 				{
4656 					m = 0;
4657 					if (!w)
4658 					{
4659 						if (isdigit(*f) && isdigit(*(f + 1)))
4660 							k = -1;
4661 						if (c == k)
4662 							m = 1;
4663 						while (*(f + 1) == '|')
4664 						{
4665 							f += 2;
4666 							if (!(j = *f))
4667 							{
4668 								m = 0;
4669 								break;
4670 							}
4671 							else if (j == c)
4672 								m = 1;
4673 							else if (j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
4674 								break;
4675 						}
4676 					}
4677 					if (m)
4678 					{
4679 						s--;
4680 						if (*++f == '!')
4681 						{
4682 							f++;
4683 							num = 0;
4684 						}
4685 						if (*f == '=')
4686 						{
4687 							c = -strtol(++f, &b, 0);
4688 							if ((b - f) > sizeof(opt_info.option) - 2)
4689 								b = f + sizeof(opt_info.option) - 2;
4690 							memcpy(&opt_info.option[1], f, b - f);
4691 							opt_info.option[b - f + 1] = 0;
4692 						}
4693 						else
4694 							c = k;
4695 						break;
4696 					}
4697 				}
4698 				if (*s == '#')
4699 				{
4700 					if (!numopt && s > opts)
4701 					{
4702 						numopt = s - 1;
4703 						numchr = k;
4704 						if (*f == ':')
4705 							numchr = -1;
4706 						else if (*(f + 1) != ':' && *(f + 1) != '!' && *(f + 1) != ']')
4707 						{
4708 							a = f;
4709 							if (*a == '=')
4710 								a++;
4711 							else
4712 							{
4713 								if (*(a + 1) == '!')
4714 									a++;
4715 								if (*(a + 1) == '=')
4716 									a += 2;
4717 							}
4718 							numchr = -strtol(a, NiL, 0);
4719 						}
4720 					}
4721 				}
4722 				else if (*s != ':')
4723 					continue;
4724 			}
4725 			else if (*s == ']')
4726 			{
4727 				s++;
4728 				continue;
4729 			}
4730 			else if (*s == '#')
4731 			{
4732 				if (!numopt && s > opts)
4733 					numchr = *(numopt = s - 1);
4734 			}
4735 			else if (*s != ':')
4736 			{
4737 				if (cache)
4738 				{
4739 					m = OPT_cache_flag;
4740 					if (*(s + 1) == '#')
4741 					{
4742 						m |= OPT_cache_numeric;
4743 						if (*(s + 2) == '?')
4744 							m |= OPT_cache_optional;
4745 					}
4746 					else if (*(s + 1) == ':')
4747 					{
4748 						m |= OPT_cache_string;
4749 						if (*(s + 2) == '?')
4750 							m |= OPT_cache_optional;
4751 					}
4752 					cache->flags[map[*s]] = m;
4753 				}
4754 				s++;
4755 				continue;
4756 			}
4757 			message((-21, "optget: opt %s", show(s)));
4758 			if (*++s == '?' || *s == *(s - 1))
4759 				s++;
4760 			if (*(s = next(s, version)) == '[')
4761 			{
4762 				s = skip(s + 1, 0, 0, 0, 1, 0, 1, version);
4763 				if (*s == GO)
4764 					s = skip(s + 1, 0, 0, 0, 0, 1, 1, version);
4765 			}
4766 			message((-21, "optget: opt %s", show(s)));
4767 		}
4768 		if (w && x)
4769 		{
4770 			s = skip(b, '|', '?', 0, 1, 0, 0, version);
4771 			if (v && (a == 0 || *a == 0 || *(a + 1) != ':' && *(a + 1) != '#') && (*v == '0' || *v == '1') && !*(v + 1))
4772 			{
4773 				if (*v == '0')
4774 					num = !num;
4775 				v = 0;
4776 			}
4777 			if ((s - b) >= elementsof(opt_info.name))
4778 				s = b + elementsof(opt_info.name) - 1;
4779 			for (;;)
4780 			{
4781 				if (b >= s)
4782 				{
4783 					*w = 0;
4784 					break;
4785 				}
4786 				if (*b == '*')
4787 					break;
4788 				*w++ = *b++;
4789 			}
4790 			if (!num && v)
4791 				return opterror(no ? "!" : "=", 0, version, id, catalog);
4792 			w = &opt_info.name[prefix];
4793 			c = x;
4794 			s = a;
4795 		}
4796 	}
4797 	if (!*s)
4798 	{
4799 		if (w)
4800 		{
4801 			if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), w))
4802 			{
4803 				if (!v)
4804 					v = (char*)hp->name;
4805 				goto help;
4806 			}
4807 			if (!v)
4808 			{
4809 				v = opt_info.name;
4810 				goto help;
4811 			}
4812 		}
4813 		if (w || !isdigit(c) || !numopt || !(pass->flags & OPT_numeric))
4814 		{
4815 			pop(psp);
4816 			return opterror("", 0, version, id, catalog);
4817 		}
4818 		s = numopt;
4819 		c = opt_info.option[1] = numchr;
4820 		opt_info.offset--;
4821 	}
4822 	opt_info.arg = 0;
4823 
4824 	/*
4825 	 * this is a ksh getopts workaround
4826 	 */
4827 
4828 	if (opt_info.num != LONG_MIN)
4829 		opt_info.num = (long)(opt_info.number = num);
4830 	if ((n = *++s == '#') || *s == ':' || w && !nov && v && (optnumber(v, &e, NiL), n = !*e))
4831 	{
4832 		if (w)
4833 		{
4834 			if (nov)
4835 			{
4836 				if (v)
4837 				{
4838 					pop(psp);
4839 					return opterror("!", 0, version, id, catalog);
4840 				}
4841 				opt_info.num = (long)(opt_info.number = 0);
4842 			}
4843 			else
4844 			{
4845 				if (!v && *(s + 1) != '?' && (v = argv[opt_info.index]))
4846 				{
4847 					opt_info.index++;
4848 					opt_info.offset = 0;
4849 				}
4850 				if (!(opt_info.arg = v) || (*v == '0' || *v == '1') && !*(v + 1))
4851 				{
4852 					if (*(s + 1) != '?')
4853 					{
4854 						if (!opt_info.arg)
4855 						{
4856 							pop(psp);
4857 							return opterror(s, 0, version, id, catalog);
4858 						}
4859 					}
4860 					else if (*(t = next(s + 2, version)) == '[')
4861 						while (*(t = skip(t, ':', 0, 0, 1, 0, 0, version)) == ':')
4862 							if (*++t == '!')
4863 							{
4864 								if (!v || *v == '1')
4865 								{
4866 									e = skip(t, ':', '?', ']', 1, 0, 0, version);
4867 									opt_info.arg = sfprints("%-.*s", e - t - 1, t + 1);
4868 								}
4869 								else
4870 								{
4871 									opt_info.arg = 0;
4872 									opt_info.num = (long)(opt_info.number = 0);
4873 								}
4874 								break;
4875 							}
4876 				}
4877 				if (opt_info.arg && n)
4878 				{
4879 					opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
4880 					if (err || e == opt_info.arg)
4881 					{
4882 						pop(psp);
4883 						return opterror(s, err, version, id, catalog);
4884 					}
4885 				}
4886 			}
4887 			goto optarg;
4888 		}
4889 		else if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset]))
4890 		{
4891 			if (*s == '#')
4892 			{
4893 				opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
4894 				if (err || e == opt_info.arg)
4895 				{
4896 					if (!err && *(s + 1) == '?')
4897 					{
4898 						opt_info.arg = 0;
4899 						opt_info.index--;
4900 					}
4901 					else
4902 					{
4903 						opt_info.offset = 0;
4904 						c = opterror(s, err, version, id, catalog);
4905 					}
4906 					pop(psp);
4907 					return c;
4908 				}
4909 				else if (*e)
4910 				{
4911 					opt_info.offset += e - opt_info.arg;
4912 					opt_info.index--;
4913 					pop(psp);
4914 					return c;
4915 				}
4916 			}
4917 		}
4918 		else if (opt_info.arg = argv[opt_info.index])
4919 		{
4920 			opt_info.index++;
4921 			if (*(s + 1) == '?' && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1))
4922 			{
4923 				opt_info.index--;
4924 				opt_info.arg = 0;
4925 			}
4926 			else if (*s == '#')
4927 			{
4928 				opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
4929 				if (err || *e)
4930 				{
4931 					if (!err && *(s + 1) == '?')
4932 					{
4933 						opt_info.arg = 0;
4934 						opt_info.index--;
4935 					}
4936 					else
4937 					{
4938 						pop(psp);
4939 						opt_info.offset = 0;
4940 						return opterror(s, err, version, id, catalog);
4941 					}
4942 				}
4943 			}
4944 		}
4945 		else if (*(s + 1) != '?')
4946 		{
4947 			opt_info.index--;
4948 			pop(psp);
4949 			return opterror(s, 0, version, id, catalog);
4950 		}
4951 		opt_info.offset = 0;
4952 	optarg:
4953 		if (*s == ':' && *(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == GO && *(s = next(s + 1, version)) == '[' && isalnum(*(s + 1)))
4954 		{
4955 			x = 0;
4956 			if (opt_info.arg)
4957 			{
4958 				do
4959 				{
4960 					w = y = opt_info.arg;
4961 					f = s = next(s + 1, version);
4962 					k = *f;
4963 					if (k == *w && isalpha(k) && !*(w + 1))
4964 					{
4965 						x = k;
4966 						break;
4967 					}
4968 					if (*s == '+' || *s == '-')
4969 						continue;
4970 					else if (*s == '[' || version < 1)
4971 						continue;
4972 					else
4973 					{
4974 						if (*s != ':')
4975 							s = skip(s, ':', '?', 0, 1, 0, 0, version);
4976 						if (*s == ':')
4977 						{
4978 							if (catalog)
4979 							{
4980 								p = skip(s + 1, '?', 0, 0, 1, 0, 0, version);
4981 								e = sfprints("%-.*s", p - (s + 1), s + 1);
4982 								b = T(id, catalog, e);
4983 								if (b == e)
4984 									p = 0;
4985 								else
4986 								{
4987 									sfprintf(xp, ":%s|%s?", b, e);
4988 									if (!(s = sfstruse(xp)))
4989 										goto nospace;
4990 								}
4991 							}
4992 							else
4993 								p = 0;
4994 							for (;;)
4995 							{
4996 								n = m = 0;
4997 								e = s + 1;
4998 								while (*++s)
4999 								{
5000 									if (*s == '*' || *s == '\a')
5001 									{
5002 										if (*s == '\a')
5003 											do
5004 											{
5005 												if (!*++s)
5006 												{
5007 													s--;
5008 													break;
5009 												}
5010 											} while (*s != '\a');
5011 										j = *(s + 1);
5012 										if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
5013 										{
5014 											while (*w)
5015 												w++;
5016 											m = 0;
5017 											break;
5018 										}
5019 										m = 1;
5020 									}
5021 									else if (*s == *w || sep(*s) && sep(*w))
5022 										w++;
5023 									else if (*w == 0)
5024 										break;
5025 									else if (!sep(*s))
5026 									{
5027 										if (sep(*w))
5028 										{
5029 											if (*++w == *s)
5030 											{
5031 												w++;
5032 												continue;
5033 											}
5034 										}
5035 										else if (w == y || sep(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
5036 											break;
5037 										for (q = s; *q && !sep(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
5038 										if (!sep(*q))
5039 											break;
5040 										for (s = q; w > y && *w != *(s + 1); w--);
5041 									}
5042 									else if (*w != *(s + 1))
5043 										break;
5044 								}
5045 								if (!*w)
5046 								{
5047 									nov = 0;
5048 									break;
5049 								}
5050 								if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|')
5051 									break;
5052 								w = y;
5053 							}
5054 							if (p)
5055 								s = p;
5056 							if (!*w)
5057 							{
5058 								if (n)
5059 									num = 0;
5060 								if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']')) && x)
5061 								{
5062 									pop(psp);
5063 									return opterror("&", 0, version, id, catalog);
5064 								}
5065 								for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2);
5066 								if (*f == ':')
5067 									x = -1;
5068 								else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':')
5069 									/* ok */;
5070 								else
5071 								{
5072 									a = f;
5073 									if (*a == '=')
5074 										a++;
5075 									else
5076 									{
5077 										if (*(a + 1) == '!')
5078 											a++;
5079 										if (*(a + 1) == '=')
5080 											a += 2;
5081 									}
5082 									x = -strtol(a, &b, 0);
5083 								}
5084 								b = e;
5085 								a = s = skip(s, 0, 0, 0, 1, 0, 0, version);
5086 								if (n)
5087 									break;
5088 							}
5089 						}
5090 					}
5091 				} while (*(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == '[');
5092 				if (!(opt_info.num = (long)(opt_info.number = x)))
5093 				{
5094 					pop(psp);
5095 					return opterror("*", 0, version, id, catalog);
5096 				}
5097 			}
5098 		}
5099 	}
5100 	else if (w && v)
5101 	{
5102 		pop(psp);
5103 		return opterror("=", 0, version, id, catalog);
5104 	}
5105 	else
5106 	{
5107 		opt_info.num = (long)(opt_info.number = num);
5108 		if (!w && !argv[opt_info.index][opt_info.offset])
5109 		{
5110 			opt_info.offset = 0;
5111 			opt_info.index++;
5112 		}
5113 	}
5114 	pop(psp);
5115 	return c;
5116  help:
5117 	if (v && *v == '?' && *(v + 1) == '?' && *(v + 2))
5118 	{
5119 		s = v + 2;
5120 		if ((s[0] == 'n' || s[0] == 'N') && (s[1] == 'o' || s[1] == 'O'))
5121 		{
5122 			s += 2;
5123 			n = -1;
5124 		}
5125 		else
5126 			n = 1;
5127 		if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), s))
5128 		{
5129 			if (hp->style < STYLE_man || !(s = argv[opt_info.index]) || s[0] != '-' || s[1] != '-' || !s[2])
5130 			{
5131 				opt_info.arg = sfprints("\fversion=%d", version);
5132 				pop(psp);
5133 				return '?';
5134 			}
5135 			opt_info.state->force = hp->style;
5136 		}
5137 		else if (match(s, "ESC", -1, ID, NiL) || match(s, "EMPHASIS", -1, ID, NiL))
5138 			opt_info.state->emphasis = n;
5139 		else if (match(s, "PREFORMAT", -1, ID, NiL))
5140 			opt_info.state->flags |= OPT_preformat;
5141 		else if (match(s, "TEST", -1, ID, NiL))
5142 		{
5143 			opt_info.state->width = OPT_WIDTH;
5144 			opt_info.state->emphasis = 1;
5145 		}
5146 		else
5147 		{
5148 			pop(psp);
5149 			return opterror(v, 0, version, id, catalog);
5150 		}
5151 		psp = pop(psp);
5152 		if (argv == opt_info.state->strv)
5153 			return '#';
5154 		goto again;
5155 	}
5156 	if ((opt_info.arg = opthelp(NiL, v)) == (char*)unknown)
5157 	{
5158 		pop(psp);
5159 		return opterror(v, 0, version, id, catalog);
5160 	}
5161 	pop(psp);
5162 	return '?';
5163  nospace:
5164 	pop(psp);
5165 	return opterror(NiL, 0, 0, NiL, NiL);
5166 }
5167 
5168 /*
5169  * parse long options with 0,1,2 leading '-' or '+' from string and pass to optget()
5170  * syntax is the unquoted
5171  *
5172  *	<length> [-|+|--|++]<name>[[-+:|&=]=<value>\n (or \0 for the last)
5173  *
5174  * or the quoted
5175  *
5176  *	[-|+|--|++][no]name[[-+:|&=]=['"{(]value[)}"']][, ]...
5177  *
5178  * with \x escapes passed to chresc()
5179  *
5180  * return '#' for `label:', with opt_info.name==label
5181  * str[opt_info.offset]	next arg
5182  *
5183  *	optstr(s, 0)
5184  *		return '-' if arg, 0 otherwise
5185  *	optstr(0, opts)
5186  *		use previous parsed str
5187  */
5188 
5189 int
5190 optstr(const char* str, const char* opts)
5191 {
5192 	register char*		s = (char*)str;
5193 	register Sfio_t*	mp;
5194 	register int		c;
5195 	register int		ql;
5196 	register int		qr;
5197 	register int		qc;
5198 	int			v;
5199 	char*			e;
5200 
5201  again:
5202 	if (s)
5203 	{
5204 		if (!(mp = opt_info.state->strp) && !(mp = opt_info.state->strp = sfstropen()))
5205 			return 0;
5206 		if (opt_info.state->str != s)
5207 			opt_info.state->str = s;
5208 		else if (opt_info.index == 1)
5209 			s += opt_info.offset;
5210 		while (*s == ',' || *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
5211 			s++;
5212 		if (!*s)
5213 		{
5214 			opt_info.state->str = 0;
5215 			return 0;
5216 		}
5217 		if (*s == '-' || *s == '+')
5218 		{
5219 			c = *s++;
5220 			sfputc(mp, c);
5221 			if (*s == c)
5222 			{
5223 				sfputc(mp, c);
5224 				s++;
5225 			}
5226 		}
5227 		else
5228 		{
5229 			sfputc(mp, '-');
5230 			sfputc(mp, '-');
5231 		}
5232 		if (isdigit(*s) && (v = (int)strtol(s, &e, 10)) > 1 && isspace(*e) && --v <= strlen(s) && (s[v] == 0 || s[v] == '\n'))
5233 		{
5234 			s += v;
5235 			while (isspace(*++e));
5236 			sfwrite(mp, e, s - e);
5237 		}
5238 		else
5239 		{
5240 			while (*s && *s != ',' && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' && *s != '=' && *s != ':')
5241 				sfputc(mp, *s++);
5242 			if ((c = *s) == ':' && *(s + 1) != '=')
5243 			{
5244 				opt_info.index = 1;
5245 				opt_info.offset = ++s - (char*)str;
5246 				if (!(s = sfstruse(mp)))
5247 					goto nospace;
5248 				s += 2;
5249 				e = opt_info.name;
5250 				while (e < &opt_info.name[sizeof(opt_info.name)-1] && (*e++ = *s++));
5251 				opt_info.arg = 0;
5252 				opt_info.num = (long)(opt_info.number = 0);
5253 				opt_info.option[0] = ':';
5254 				opt_info.option[1] = 0;
5255 				return '#';
5256 			}
5257 			if (c == ':' || c == '=')
5258 			{
5259 				sfputc(mp, c);
5260 				ql = qr = 0;
5261 				while (c = *++s)
5262 				{
5263 					if (c == '\\')
5264 					{
5265 						sfputc(mp, chresc(s, &e));
5266 						s = e - 1;
5267 					}
5268 					else if (c == qr)
5269 					{
5270 						if (qr != ql)
5271 							sfputc(mp, c);
5272 						if (--qc <= 0)
5273 							qr = ql = 0;
5274 					}
5275 					else if (c == ql)
5276 					{
5277 						sfputc(mp, c);
5278 						qc++;
5279 					}
5280 					else if (qr)
5281 						sfputc(mp, c);
5282 					else if (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r')
5283 						break;
5284 					else if (c == '"' || c == '\'')
5285 					{
5286 						ql = qr = c;
5287 						qc = 1;
5288 					}
5289 					else
5290 					{
5291 						sfputc(mp, c);
5292 						if (c == GO)
5293 						{
5294 							ql = c;
5295 							qr = OG;
5296 							qc = 1;
5297 						}
5298 						else if (c == '(')
5299 						{
5300 							ql = c;
5301 							qr = ')';
5302 							qc = 1;
5303 						}
5304 					}
5305 				}
5306 			}
5307 		}
5308 		opt_info.argv = opt_info.state->strv;
5309 		opt_info.state->strv[0] = T(NiL, ID, "option");
5310 		if (!(opt_info.state->strv[1] = sfstruse(mp)))
5311 			goto nospace;
5312 		opt_info.state->strv[2] = 0;
5313 		opt_info.offset = s - (char*)str;
5314 	}
5315 	if (opts)
5316 	{
5317 		if (!opt_info.state->strv[1])
5318 		{
5319 			opt_info.state->str = 0;
5320 			return 0;
5321 		}
5322 		opt_info.index = 1;
5323 		v = opt_info.offset;
5324 		opt_info.offset = 0;
5325 		c = optget(opt_info.state->strv, opts);
5326 		opt_info.index = 1;
5327 		opt_info.offset = v;
5328 		if (c == '#')
5329 		{
5330 			s = opt_info.state->str;
5331 			goto again;
5332 		}
5333 		if ((c == '?' || c == ':') && (opt_info.arg[0] == '-' && opt_info.arg[1] == '-'))
5334 			opt_info.arg += 2;
5335 		s = opt_info.name;
5336 		if (*s++ == '-' && *s++ == '-' && *s)
5337 		{
5338 			e = opt_info.name;
5339 			while (*e++ = *s++);
5340 		}
5341 	}
5342 	else
5343 		c = '-';
5344 	return c;
5345  nospace:
5346 	return opterror(NiL, 0, 0, NiL, NiL);
5347 }
5348