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