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