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