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