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