mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
This fixes the following: 1. 'set --posix' now works as an equivalent of 'set -o posix'. 2. The posix option turns off braceexpand and turns on letoctal. Any attempt to override that in a single command such as 'set -o posix +o letoctal' was quietly ignored. This now works as long as the overriding option follows the posix option in the command. 3. The --default option to 'set' now stops the 'posix' option, if set or unset in the same 'set' command, from changing other options. This allows the command output by 'set +o' to correctly restore the current options. src/cmd/ksh93/data/builtins.c: - To make 'set --posix' work, we must explicitly list it in sh_set[] as a supported option so that AST optget(3) recognises it and won't override it with its own default --posix option, which converts the optget(3) string to at POSIX getopt(3) string. This means it will appear as a separate entry in --man output, whether we want it to or not. So we might as well use it as an example to document how --optionname == -o optionname, replacing the original documentation that was part of the '-o' description. src/cmd/ksh93/sh/args.c: sh_argopts(): - Add handling for explitit --posix option in data/builtins.c. - Move SH_POSIX syncing SH_BRACEEXPAND and SH_LETOCTAL from sh_applyopts() into the option parsing loop here. This fixes the bug that letoctal was ignored in 'set -o posix +o letoctal'. - Remember if --default was used in a flag, and do not sync options with SH_POSIX if the flag is set. This makes 'set +o' work. src/cmd/ksh93/include/argnod.h, src/cmd/ksh93/data/msg.c, src/cmd/ksh93/sh/args.c: sh_printopts(): - Do not potentially translate the 'on' and 'off' labels in 'set -o' output. No other shell does, and some scripts parse these. src/cmd/ksh93/sh/init.c: sh_init(): - Turn on SH_LETOCTAL early along with SH_POSIX if the shell was invoked as sh; this makes 'sh -o' and 'sh +o' show expected options (not that anyone does this, but correctness is good). src/cmd/ksh93/include/defs.h, src/cmd/ksh93/include/shell.h: - The state flags were in defs.h and most (but not all) of the shell options were in shell.h. Gather all the shell state and option flag definitions into one place in shell.h for clarity. - Remove unused SH_NOPROFILE and SH_XARGS option flags. src/cmd/ksh93/tests/options.sh: - Add tests for these bugs. src/lib/libast/misc/optget.c: styles[]: - Edit default optget(3) option self-documentation for clarity. Several changed files: - Some SHOPT_PFSH fixes to avoid compiling dead code.
5774 lines
115 KiB
C
5774 lines
115 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1985-2012 AT&T Intellectual Property *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 1.0 *
|
|
* by AT&T Intellectual Property *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* http://www.eclipse.org/org/documents/epl-v10.html *
|
|
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
|
|
* *
|
|
* Information and Software Systems Research *
|
|
* AT&T Research *
|
|
* Florham Park NJ *
|
|
* *
|
|
* Glenn Fowler <gsf@research.att.com> *
|
|
* David Korn <dgk@research.att.com> *
|
|
* Phong Vo <kpv@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
#pragma prototyped
|
|
/*
|
|
* Glenn Fowler
|
|
* AT&T Research
|
|
*
|
|
* command line option parser and usage formatter
|
|
* its a monster but its all in one place
|
|
* widen your window while you're at it
|
|
*/
|
|
|
|
#include <optlib.h>
|
|
#include <debug.h>
|
|
#include <ccode.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
|
|
#define KEEP "*[A-Za-z][A-Za-z]*"
|
|
#define OMIT "*@(\\[[-+]*\\?*\\]|\\@\\(#\\)|Copyright \\(c\\)|\\$\\I\\d\\: )*"
|
|
|
|
#define GO '{' /* group nest open */
|
|
#define OG '}' /* group nest close */
|
|
|
|
#define OPT_WIDTH 80 /* default help text width */
|
|
#define OPT_MARGIN 10 /* default help text margin */
|
|
#define OPT_USAGE 7 /* usage continuation indent */
|
|
|
|
#define OPT_flag 0x001 /* flag ( 0 or 1 ) */
|
|
#define OPT_hidden 0x002 /* remaining are hidden */
|
|
#define OPT_ignorecase 0x004 /* arg match ignores case */
|
|
#define OPT_invert 0x008 /* flag inverts long sense */
|
|
#define OPT_listof 0x010 /* arg is ' ' or ',' list */
|
|
#define OPT_number 0x020 /* arg is strtonll() number */
|
|
#define OPT_oneof 0x040 /* arg may be set once */
|
|
#define OPT_optional 0x080 /* arg is optional */
|
|
#define OPT_string 0x100 /* arg is string */
|
|
|
|
#define OPT_preformat 0001 /* output preformat string */
|
|
#define OPT_proprietary 0002 /* proprietary docs */
|
|
|
|
#define OPT_TYPE (OPT_flag|OPT_number|OPT_string)
|
|
|
|
#define STYLE_posix 0 /* posix getopt usage */
|
|
#define STYLE_short 1 /* [default] short usage */
|
|
#define STYLE_long 2 /* long usage */
|
|
#define STYLE_match 3 /* long description of matches */
|
|
#define STYLE_options 4 /* short and long descriptions */
|
|
#define STYLE_man 5 /* pretty details */
|
|
#define STYLE_html 6 /* html details */
|
|
#define STYLE_nroff 7 /* nroff details */
|
|
#define STYLE_api 8 /* program details */
|
|
#define STYLE_keys 9 /* translation key strings */
|
|
#define STYLE_usage 10 /* escaped usage string */
|
|
|
|
#define FONT_BOLD 1
|
|
#define FONT_ITALIC 2
|
|
#define FONT_LITERAL 4
|
|
|
|
#define HELP_head 0x01
|
|
#define HELP_index 0x02
|
|
|
|
#define TAG_NONE 0
|
|
#define TAG_DIV 1
|
|
#define TAG_DL 2
|
|
|
|
#define SEP(c) ((c)=='-'||(c)=='_')
|
|
|
|
typedef struct Attr_s
|
|
{
|
|
const char* name;
|
|
int flag;
|
|
} Attr_t;
|
|
|
|
typedef struct Help_s
|
|
{
|
|
const char* match; /* builtin help match name */
|
|
const char* name; /* builtin help name */
|
|
int style; /* STYLE_* */
|
|
const char* text; /* --? text */
|
|
unsigned int size; /* strlen text */
|
|
} Help_t;
|
|
|
|
typedef struct Font_s
|
|
{
|
|
const char* html[2];
|
|
const char* nroff[2];
|
|
const char* term[2];
|
|
} Font_t;
|
|
|
|
typedef struct List_s
|
|
{
|
|
int type; /* { - + : } */
|
|
const char* name; /* list name */
|
|
const char* text; /* help text */
|
|
} List_t;
|
|
|
|
typedef struct Msg_s
|
|
{
|
|
const char* text; /* default message text */
|
|
Dtlink_t link; /* cdt link */
|
|
} Msg_t;
|
|
|
|
typedef struct Save_s
|
|
{
|
|
Dtlink_t link; /* cdt link */
|
|
char text[1]; /* saved text text */
|
|
} Save_t;
|
|
|
|
typedef struct Push_s
|
|
{
|
|
struct Push_s* next; /* next string */
|
|
char* ob; /* next char in old string */
|
|
char* oe; /* end of old string */
|
|
char* nb; /* next char in new string */
|
|
char* ne; /* end of new string */
|
|
int ch; /* localize() translation */
|
|
} Push_t;
|
|
|
|
typedef struct Tag_s
|
|
{
|
|
unsigned char level; /* indent level */
|
|
unsigned char id; /* TAG_* id */
|
|
} Tag_t;
|
|
|
|
typedef struct Indent_s
|
|
{
|
|
int stop; /* tab column position */
|
|
} Indent_t;
|
|
|
|
static Indent_t indent[] =
|
|
{
|
|
0,2, 4,10, 12,18, 20,26, 28,34, 36,42, 44,50, 0,0
|
|
};
|
|
|
|
static const char* end[] =
|
|
{
|
|
"", "</DIV>\n", "</DL>\n"
|
|
};
|
|
|
|
static const char term_off[] = {CC_esc,'[','0','m',0};
|
|
static const char term_B_on[] = {CC_esc,'[','1','m',0};
|
|
static const char term_I_on[] = {CC_esc,'[','1',';','4','m',0};
|
|
|
|
static const Font_t fonts[] =
|
|
{
|
|
"", "", "", "", "", "",
|
|
"</B>", "<B>", "\\fP", "\\fB", &term_off[0], &term_B_on[0],
|
|
"</I>", "<I>", "\\fP", "\\fI", &term_off[0], &term_I_on[0],
|
|
"", "", "", "", "", "",
|
|
"</TT>","<TT>","\\fP", "\\f5", "", "",
|
|
};
|
|
|
|
static char native[] = "";
|
|
|
|
static unsigned char map[UCHAR_MAX];
|
|
|
|
static Optstate_t state;
|
|
|
|
#if 0 /* #if !_PACKAGE_astsa // this somehow corrupts "Or:" usage mesages, e.g. in 'typeset -\?' */
|
|
|
|
#define ID ast.id
|
|
|
|
#define C(s) ERROR_catalog(s)
|
|
#define D(s) (state.msgdict && dtmatch(state.msgdict, (s)))
|
|
#define T(i,c,m) (X(c)?translate(i,c,C(m)):(m))
|
|
#define X(c) (ERROR_translating()&&(c)!=native)
|
|
#define Z(x) C(x),sizeof(x)-1
|
|
|
|
/*
|
|
* translate with C_LC_MESSAGES_libast[] check
|
|
*/
|
|
|
|
static char*
|
|
translate(const char* cmd, const char* cat, const char* msg)
|
|
{
|
|
if (!X(cat))
|
|
return (char*)msg;
|
|
if (cat != (const char*)ID && D(msg))
|
|
cat = (const char*)ID;
|
|
return errorx(NiL, cmd, cat, msg);
|
|
}
|
|
|
|
#else
|
|
|
|
static char ID[] = "ast";
|
|
|
|
#define C(s) s
|
|
#define D(s) (state.msgdict && dtmatch(state.msgdict, (s)))
|
|
#define T(i,c,m) m
|
|
#define X(c) 0
|
|
#define Z(x) C(x),sizeof(x)-1
|
|
|
|
#endif
|
|
|
|
static const List_t help_head[] =
|
|
{
|
|
'-', 0,
|
|
0,
|
|
'+', C("NAME"),
|
|
C("options available to all \bast\b commands"),
|
|
'+', C("DESCRIPTION"),
|
|
C("\b-?\b and \b--?\b* options are the same \
|
|
for all \bast\b commands. For any \aitem\a below, if \b--\b\aitem\a is not \
|
|
supported by a given command then it is equivalent to \b--\?\?\b\aitem\a. The \
|
|
\b--\?\?\b form should be used for portability. All output is written to the \
|
|
standard error."),
|
|
};
|
|
|
|
static const Help_t styles[] =
|
|
{
|
|
C("about"), "-", STYLE_match,
|
|
Z("Show all implementation info."),
|
|
C("api"), "?api", STYLE_api,
|
|
Z("Output detailed info in program readable form."),
|
|
C("help"), "", -1,
|
|
Z("Show detailed help option info."),
|
|
C("html"), "?html", STYLE_html,
|
|
Z("Output detailed info in HTML."),
|
|
C("keys"), "?keys", STYLE_keys,
|
|
Z("Output usage key strings for translation."),
|
|
C("long"), "?long", STYLE_long,
|
|
Z("Show brief usage with long options."),
|
|
C("man"), "?man", STYLE_man,
|
|
Z("Show detailed info as a manual page."),
|
|
C("nroff"), "?nroff", STYLE_nroff,
|
|
Z("Output detailed info in nroff format."),
|
|
C("options"), "?options", STYLE_options,
|
|
Z("List short and long option details."),
|
|
C("posix"), "?posix", STYLE_posix,
|
|
Z("Output POSIX-compliant getopt(3) short options string."),
|
|
C("short"), "?short", STYLE_short,
|
|
Z("Show brief usage with short options."),
|
|
C("usage"), "?usage", STYLE_usage,
|
|
Z("Output the full AST optget(3) usage string."),
|
|
};
|
|
|
|
static const List_t help_tail[] =
|
|
{
|
|
':', C("\?\?-\alabel\a"),
|
|
C("List implementation info matching \alabel\a*."),
|
|
':', C("\?\?\aname\a"),
|
|
C("Equivalent to \b--help=\b\aname\a."),
|
|
':', C("\?\?"),
|
|
C("Equivalent to \b--\?\?options\b."),
|
|
':', C("\?\?\?\?"),
|
|
C("Equivalent to \b--\?\?man\b."),
|
|
':', C("\?\?\?\?\?\?"),
|
|
C("Equivalent to \b--\?\?help\b."),
|
|
':', C("\?\?\?\?\?\?\aitem\a"),
|
|
C("If the next argument is \b--\b\aoption\a then list \
|
|
the \aoption\a output in the \aitem\a style. Otherwise print \
|
|
\bversion=\b\an\a where \an\a>0 if \b--\?\?\b\aitem\a is supported, \b0\b \
|
|
if not."),
|
|
':', C("\?\?\?\?\?\?ESC"),
|
|
C("Emit escape codes even if output is not a terminal."),
|
|
':', C("\?\?\?\?\?\?MAN[=\asection\a]]"),
|
|
C("List the \bman\b(1) section title for \asection\a [the \
|
|
current command]]."),
|
|
':', C("\?\?\?\?\?\?SECTION"),
|
|
C("List the \bman\b(1) section number for the current command."),
|
|
':', C("\?\?\?\?\?\?TEST"),
|
|
C("Massage the output for regression testing."),
|
|
};
|
|
|
|
static const Attr_t attrs[] =
|
|
{
|
|
"flag", OPT_flag,
|
|
"hidden", OPT_hidden,
|
|
"ignorecase", OPT_ignorecase,
|
|
"invert", OPT_invert,
|
|
"listof", OPT_listof,
|
|
"number", OPT_number,
|
|
"oneof", OPT_oneof,
|
|
"optional", OPT_optional,
|
|
"string", OPT_string,
|
|
};
|
|
|
|
static const char unknown[] = C("unknown option or attribute");
|
|
|
|
static const char* heading[] =
|
|
{
|
|
C("INDEX"),
|
|
C("USER COMMANDS"),
|
|
C("SYSTEM LIBRARY"),
|
|
C("USER LIBRARY"),
|
|
C("FILE FORMATS"),
|
|
C("MISCELLANEOUS"),
|
|
C("GAMES and DEMOS"),
|
|
C("SPECIAL FILES"),
|
|
C("ADMINISTRATIVE COMMANDS"),
|
|
C("GUIs"),
|
|
};
|
|
|
|
/*
|
|
* list of common man page strings
|
|
* NOTE: add but do not delete from this table
|
|
*/
|
|
|
|
static Msg_t C_LC_MESSAGES_libast[] =
|
|
{
|
|
{ C("APPLICATION USAGE") },
|
|
{ C("ASYNCHRONOUS EVENTS") },
|
|
{ C("BUGS") },
|
|
{ C("CAVEATS") },
|
|
{ C("CONSEQUENCES OF ERRORS") },
|
|
{ C("DESCRIPTION") },
|
|
{ C("ENVIRONMENT VARIABLES") },
|
|
{ C("EXAMPLES") },
|
|
{ C("EXIT STATUS") },
|
|
{ C("EXTENDED DESCRIPTION") },
|
|
{ C("INPUT FILES") },
|
|
{ C("LIBRARY") },
|
|
{ C("NAME") },
|
|
{ C("OPERANDS") },
|
|
{ C("OPTIONS") },
|
|
{ C("OUTPUT FILES") },
|
|
{ C("PLUGIN") },
|
|
{ C("SEE ALSO") },
|
|
{ C("STDERR") },
|
|
{ C("STDIN") },
|
|
{ C("STDOUT") },
|
|
{ C("SYNOPSIS") },
|
|
{ C("author") },
|
|
{ C("copyright") },
|
|
{ C("license") },
|
|
{ C("name") },
|
|
{ C("path") },
|
|
{ C("version") },
|
|
};
|
|
|
|
/*
|
|
* 2007-03-19 move opt_info from _opt_info_ to (*_opt_data_)
|
|
* to allow future Opt_t growth
|
|
* by 2009 _opt_info_ can be static
|
|
*/
|
|
|
|
#if _BLD_ast && defined(__EXPORT__)
|
|
#define extern extern __EXPORT__
|
|
#endif
|
|
|
|
extern Opt_t _opt_info_;
|
|
|
|
Opt_t _opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state };
|
|
|
|
#undef extern
|
|
|
|
__EXTERN__(Opt_t, _opt_info_);
|
|
|
|
__EXTERN__(Opt_t*, _opt_infop_);
|
|
|
|
Opt_t* _opt_infop_ = &_opt_info_;
|
|
|
|
Optstate_t*
|
|
optstate(Opt_t* p)
|
|
{
|
|
return &state;
|
|
}
|
|
|
|
#if DEBUG || _BLD_DEBUG
|
|
|
|
/*
|
|
* debug usage string segment format
|
|
*/
|
|
|
|
static char*
|
|
show(register char* s)
|
|
{
|
|
register int c;
|
|
register char* t;
|
|
register char* e;
|
|
|
|
static char buf[32];
|
|
|
|
if (!s)
|
|
return "(null)";
|
|
t = buf;
|
|
e = buf + sizeof(buf) - 2;
|
|
while (t < e)
|
|
{
|
|
switch (c = *s++)
|
|
{
|
|
case 0:
|
|
goto done;
|
|
case '\a':
|
|
*t++ = '\\';
|
|
c = 'a';
|
|
break;
|
|
case '\b':
|
|
*t++ = '\\';
|
|
c = 'b';
|
|
break;
|
|
case '\f':
|
|
*t++ = '\\';
|
|
c = 'f';
|
|
break;
|
|
case '\n':
|
|
*t++ = '\\';
|
|
c = 'n';
|
|
break;
|
|
case '\t':
|
|
*t++ = '\\';
|
|
c = 't';
|
|
break;
|
|
case '\v':
|
|
*t++ = '\\';
|
|
c = 'v';
|
|
break;
|
|
}
|
|
*t++ = c;
|
|
}
|
|
done:
|
|
*t = 0;
|
|
return buf;
|
|
}
|
|
|
|
#endif
|
|
|
|
typedef struct Section_s
|
|
{
|
|
const char section[4];
|
|
const char* name;
|
|
} Section_t;
|
|
|
|
static const Section_t sections[] =
|
|
{
|
|
"1M", "MAKE ASSERTION OPERATORS AND RULES",
|
|
"1", "USER COMMANDS",
|
|
"2", "SYSTEM CALLS",
|
|
"3F", "FORTRAN LIBRARY ROUTINES",
|
|
"3K", "KERNEL VM LIBRARY FUNCTIONS",
|
|
"3L", "LIGHTWEIGHT PROCESSES LIBRARY",
|
|
"3M", "MATHEMATICAL LIBRARY",
|
|
"3N", "NETWORK FUNCTIONS",
|
|
"3R", "RPC SERVICES LIBRARY",
|
|
"3S", "STANDARD I/O FUNCTIONS",
|
|
"3V", "SYSTEM V LIBRARY",
|
|
"3", "C LIBRARY FUNCTIONS",
|
|
"4F", "PROTOCOL FAMILIES",
|
|
"4P", "PROTOCOLS",
|
|
"4", "DEVICES AND NETWORK INTERFACES",
|
|
"5P", "PLUGINS",
|
|
"5", "FILE FORMATS",
|
|
"6", "GAMES AND DEMOS",
|
|
"7", "PUBLIC FILES AND TABLES",
|
|
"8", "ADMINISTRATIVE COMMANDS",
|
|
"L", "LOCAL COMMANDS",
|
|
};
|
|
|
|
/*
|
|
* return section name given abbreviation
|
|
*/
|
|
|
|
static char*
|
|
secname(char* section)
|
|
{
|
|
int i;
|
|
char* b;
|
|
char* t;
|
|
const char* s;
|
|
|
|
b = t = fmtbuf(64);
|
|
if (section[1])
|
|
{
|
|
switch (section[2] ? section[2] : section[1])
|
|
{
|
|
case 'C':
|
|
s = "COMPATIBILITY ";
|
|
break;
|
|
case 'U':
|
|
s = "UWIN ";
|
|
break;
|
|
case 'X':
|
|
s = "MISCELLANEOUS ";
|
|
break;
|
|
default:
|
|
s = 0;
|
|
break;
|
|
}
|
|
if (s)
|
|
t = strcopy(t, s);
|
|
}
|
|
s = 0;
|
|
for (i = 0; i < elementsof(sections); i++)
|
|
if (section[0] == sections[i].section[0] && (section[1] == sections[i].section[1] || !sections[i].section[1]))
|
|
{
|
|
s = sections[i].name;
|
|
break;
|
|
}
|
|
if (!s)
|
|
{
|
|
t = strcopy(t, "SECTION ");
|
|
s = section;
|
|
}
|
|
strcopy(t, s);
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* pop the push stack
|
|
*/
|
|
|
|
static Push_t*
|
|
pop(register Push_t* psp)
|
|
{
|
|
register Push_t* tsp;
|
|
|
|
while (tsp = psp)
|
|
{
|
|
psp = psp->next;
|
|
free(tsp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* skip over line space to the next token
|
|
*/
|
|
|
|
static char*
|
|
next(register char* s, int version)
|
|
{
|
|
register char* b;
|
|
|
|
while (*s == '\t' || *s == '\r' || version >= 1 && *s == ' ')
|
|
s++;
|
|
if (*s == '\n')
|
|
{
|
|
b = s;
|
|
while (*++s == ' ' || *s == '\t' || *s == '\r');
|
|
if (*s == '\n')
|
|
return b;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* skip to t1 or t2 or t3, whichever first, in s
|
|
* n==0 outside [...]
|
|
* n==1 inside [...] before ?
|
|
* n==2 inside [...] after ?
|
|
* b==0 outside {...}
|
|
* b==1 inside {...}
|
|
* past skips past the terminator to the next token
|
|
* otherwise a pointer to the terminator is returned
|
|
*
|
|
* ]] for ] inside [...]
|
|
* ?? for ? inside [...] before ?
|
|
* :: for : inside [...] before ?
|
|
*/
|
|
|
|
static char*
|
|
skip(register char* s, register int t1, register int t2, register int t3, register int n, register int b, int past, int version)
|
|
{
|
|
register int c;
|
|
register int on = n;
|
|
register int ob = b;
|
|
|
|
if (version < 1)
|
|
{
|
|
n = n >= 1;
|
|
for (;;)
|
|
{
|
|
switch (*s++)
|
|
{
|
|
case 0:
|
|
break;
|
|
case '[':
|
|
n++;
|
|
continue;
|
|
case ']':
|
|
if (--n <= 0)
|
|
break;
|
|
continue;
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else while (c = *s++)
|
|
{
|
|
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)));
|
|
if (c == '[')
|
|
{
|
|
if (!n)
|
|
n = 1;
|
|
}
|
|
else if (c == ']')
|
|
{
|
|
if (n)
|
|
{
|
|
if (*s == ']')
|
|
s++;
|
|
else if (on == 1)
|
|
break;
|
|
else
|
|
n = 0;
|
|
}
|
|
}
|
|
else if (c == GO)
|
|
{
|
|
if (n == 0)
|
|
b++;
|
|
}
|
|
else if (c == OG)
|
|
{
|
|
if (n == 0 && b-- == ob)
|
|
break;
|
|
}
|
|
else if (c == '?')
|
|
{
|
|
if (n == 1)
|
|
{
|
|
if (*s == '?')
|
|
s++;
|
|
else
|
|
{
|
|
if (n == on && (c == t1 || c == t2 || c == t3))
|
|
break;
|
|
n = 2;
|
|
}
|
|
}
|
|
}
|
|
else if (n == on && (c == t1 || c == t2 || c == t3))
|
|
{
|
|
if (n == 1 && c == ':' && *s == c)
|
|
s++;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
return past && *(s - 1) ? next(s, version) : s - 1;
|
|
}
|
|
|
|
/*
|
|
* *s points to '(' on input
|
|
* return is one past matching ')'
|
|
*/
|
|
|
|
static char*
|
|
nest(register char* s)
|
|
{
|
|
int n;
|
|
|
|
n = 0;
|
|
for (;;)
|
|
{
|
|
switch (*s++)
|
|
{
|
|
case '(':
|
|
n++;
|
|
continue;
|
|
case ')':
|
|
if (!--n)
|
|
break;
|
|
continue;
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* match s with t
|
|
* t translated if possible
|
|
* embedded { - _ ' } ignored
|
|
* * separates required prefix from optional suffix
|
|
* otherwise prefix match
|
|
*/
|
|
|
|
static int
|
|
match(char* s, char* t, int version, const char* id, const char* catalog)
|
|
{
|
|
register char* w;
|
|
register char* x;
|
|
char* xw;
|
|
char* ww;
|
|
int n;
|
|
int v;
|
|
int j;
|
|
|
|
for (n = 0; n < 2; n++)
|
|
{
|
|
if (n)
|
|
x = t;
|
|
else
|
|
{
|
|
if (catalog)
|
|
{
|
|
w = skip(t, ':', '?', 0, 1, 0, 0, version);
|
|
w = sfprints("%-.*s", w - t, t);
|
|
x = T(id, catalog, w);
|
|
if (x == w)
|
|
continue;
|
|
}
|
|
x = T(NiL, ID, t);
|
|
if (x == t)
|
|
continue;
|
|
}
|
|
do
|
|
{
|
|
v = 0;
|
|
xw = x;
|
|
w = ww = s;
|
|
while (*x && *w)
|
|
{
|
|
if (isupper(*x))
|
|
xw = x;
|
|
if (isupper(*w))
|
|
ww = w;
|
|
if (*x == '*' && !v++ || *x == '\a')
|
|
{
|
|
if (*x == '\a')
|
|
do
|
|
{
|
|
if (!*++x)
|
|
{
|
|
x--;
|
|
break;
|
|
}
|
|
} while (*x != '\a');
|
|
j = *(x + 1);
|
|
if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
|
|
while (*w)
|
|
w++;
|
|
}
|
|
else if (SEP(*x))
|
|
xw = ++x;
|
|
else if (SEP(*w) && w != s)
|
|
ww = ++w;
|
|
else if (*x == *w)
|
|
{
|
|
x++;
|
|
w++;
|
|
}
|
|
else if (w == ww && x == xw)
|
|
break;
|
|
else
|
|
{
|
|
if (x != xw)
|
|
{
|
|
while (*x && !SEP(*x) && !isupper(*x))
|
|
x++;
|
|
if (!*x)
|
|
break;
|
|
if (SEP(*x))
|
|
x++;
|
|
xw = x;
|
|
}
|
|
while (w > ww && *w != *x)
|
|
w--;
|
|
}
|
|
}
|
|
if (!*w)
|
|
{
|
|
if (!v)
|
|
{
|
|
for (;;)
|
|
{
|
|
switch (*x++)
|
|
{
|
|
case 0:
|
|
case ':':
|
|
case '|':
|
|
case '?':
|
|
case ']':
|
|
return 1;
|
|
case '*':
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
} while (*(x = skip(x, '|', 0, 0, 1, 0, 0, version)) == '|' && x++);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* prefix search for s in tab with num elements of size
|
|
* with optional translation
|
|
*/
|
|
|
|
static void*
|
|
search(const void* tab, size_t num, size_t siz, char* s)
|
|
{
|
|
register char* p;
|
|
register char* e;
|
|
|
|
for (e = (p = (char*)tab) + num * siz; p < e; p += siz)
|
|
if (match(s, *((char**)p), -1, NiL, NiL))
|
|
return (void*)p;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* save ap+bp+cp and return the saved pointer
|
|
*/
|
|
|
|
static char*
|
|
save(const char* ap, size_t az, const char* bp, size_t bz, const char* cp, size_t cz)
|
|
{
|
|
char* b;
|
|
char* e;
|
|
const char* ep;
|
|
Save_t* p;
|
|
Dtdisc_t* d;
|
|
char buf[1024];
|
|
|
|
static Dt_t* dict;
|
|
|
|
if (!dict)
|
|
{
|
|
if (!(d = newof(0, Dtdisc_t, 1, 0)))
|
|
return (char*)ap;
|
|
d->key = offsetof(Save_t, text);
|
|
if (!(dict = dtopen(d, Dtset)))
|
|
return (char*)ap;
|
|
}
|
|
b = buf;
|
|
e = b + sizeof(buf) - 1;
|
|
for (ep = ap + az; b < e && ap < ep; *b++ = *ap++);
|
|
if (bp)
|
|
{
|
|
for (ep = bp + bz; b < e && bp < ep; *b++ = *bp++);
|
|
if (cp)
|
|
for (ep = cp + cz; b < e && cp < ep; *b++ = *cp++);
|
|
}
|
|
*b = 0;
|
|
if (!(p = (Save_t*)dtmatch(dict, buf)))
|
|
{
|
|
if (!(p = newof(0, Save_t, 1, b - buf)))
|
|
return (char*)ap;
|
|
strcpy(p->text, buf);
|
|
dtinsert(dict, p);
|
|
}
|
|
return p->text;
|
|
}
|
|
|
|
/*
|
|
* expand \f...\f info
|
|
* *p set to next char after second \f
|
|
* expanded value returned
|
|
*/
|
|
|
|
static char*
|
|
expand(register char* s, register char* e, char** p, Sfio_t* ip, char* id)
|
|
{
|
|
register int c;
|
|
register char* b = s;
|
|
int n;
|
|
|
|
n = sfstrtell(ip);
|
|
c = 1;
|
|
while ((!e || s < e) && (c = *s++) && c != '\f');
|
|
sfwrite(ip, b, s - b - 1);
|
|
sfputc(ip, 0);
|
|
b = sfstrbase(ip) + n;
|
|
n = sfstrtell(ip);
|
|
if (!c)
|
|
s--;
|
|
if (*b == '?')
|
|
{
|
|
if (!*++b || streq(b, "NAME"))
|
|
{
|
|
if (!(b = id))
|
|
b = "command";
|
|
sfstrseek(ip, 0, SEEK_SET);
|
|
sfputr(ip, b, -1);
|
|
n = 0;
|
|
}
|
|
else
|
|
n = 1;
|
|
}
|
|
else if (!opt_info.disc || !opt_info.disc->infof || (*opt_info.disc->infof)(&opt_info, ip, b, opt_info.disc) < 0)
|
|
n = 0;
|
|
*p = s;
|
|
if (s = sfstruse(ip))
|
|
s += n;
|
|
else
|
|
s = "error";
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* initialize the translation dictionary and flag maps
|
|
*/
|
|
|
|
static void
|
|
initdict(void)
|
|
{
|
|
register int n;
|
|
|
|
state.vp = sfstropen();
|
|
state.msgdisc.key = offsetof(Msg_t, text);
|
|
state.msgdisc.size = -1;
|
|
state.msgdisc.link = offsetof(Msg_t, link);
|
|
if (state.msgdict = dtopen(&state.msgdisc, Dtset))
|
|
for (n = 0; n < elementsof(C_LC_MESSAGES_libast); n++)
|
|
dtinsert(state.msgdict, C_LC_MESSAGES_libast + n);
|
|
}
|
|
|
|
/*
|
|
* initialize the attributes for pass p from opt string s
|
|
*/
|
|
|
|
static int
|
|
init(register char* s, Optpass_t* p)
|
|
{
|
|
register char* t;
|
|
register char* u;
|
|
register int c;
|
|
register int a;
|
|
register int n;
|
|
char* e;
|
|
int l;
|
|
|
|
if (!state.localized)
|
|
{
|
|
state.localized = 1;
|
|
#if !_PACKAGE_astsa
|
|
if (!ast.locale.serial)
|
|
setlocale(LC_ALL, "");
|
|
#endif
|
|
state.xp = sfstropen();
|
|
if (!map[OPT_FLAGS[0]])
|
|
for (n = 0, t = OPT_FLAGS; *t; t++)
|
|
map[*t] = ++n;
|
|
}
|
|
#if _BLD_DEBUG
|
|
error(-2, "optget debug");
|
|
#endif
|
|
p->oopts = s;
|
|
p->version = 0;
|
|
p->prefix = 2;
|
|
p->section[0] = '1';
|
|
p->section[1] = 0;
|
|
p->flags = 0;
|
|
p->id = error_info.id;
|
|
p->catalog = 0;
|
|
s = next(s, 0);
|
|
if (*s == ':')
|
|
s++;
|
|
if (*s == '+')
|
|
s++;
|
|
s = next(s, 0);
|
|
if (*s++ == '[')
|
|
{
|
|
if (*s == '+')
|
|
p->version = 1;
|
|
else if (*s++ == '-')
|
|
{
|
|
if (*s == '?' || *s == ']')
|
|
p->version = 1;
|
|
else
|
|
{
|
|
if (!isdigit(*s))
|
|
p->version = 1;
|
|
else
|
|
while (isdigit(*s))
|
|
p->version = p->version * 10 + (*s++ - '0');
|
|
while (*s && *s != ']')
|
|
{
|
|
if ((c = *s++) == '?')
|
|
{
|
|
p->release = s;
|
|
while (*s && *s != ']')
|
|
if (isspace(*s++))
|
|
p->release = s;
|
|
break;
|
|
}
|
|
else if (!isdigit(*s))
|
|
n = 1;
|
|
else
|
|
{
|
|
n = 0;
|
|
while (isdigit(*s))
|
|
n = n * 10 + (*s++ - '0');
|
|
}
|
|
switch (c)
|
|
{
|
|
case '+':
|
|
p->flags |= OPT_plus;
|
|
break;
|
|
case 'a':
|
|
p->flags |= OPT_append;
|
|
break;
|
|
case 'c':
|
|
p->flags |= OPT_cache;
|
|
break;
|
|
case 'i':
|
|
p->flags |= OPT_ignore;
|
|
break;
|
|
case 'l':
|
|
p->flags |= OPT_long;
|
|
break;
|
|
case 'm':
|
|
p->flags |= OPT_module;
|
|
break;
|
|
case 'n':
|
|
p->flags |= OPT_numeric;
|
|
break;
|
|
case 'o':
|
|
p->flags |= OPT_old;
|
|
break;
|
|
case 'p':
|
|
p->prefix = n;
|
|
break;
|
|
case 's':
|
|
if (n > 1 && n < 5)
|
|
{
|
|
p->flags |= OPT_functions;
|
|
p->prefix = 0;
|
|
}
|
|
p->section[0] = '0' + (n % 10);
|
|
n = 1;
|
|
if (isupper(*s))
|
|
p->section[n++] = *s++;
|
|
if (isupper(*s))
|
|
p->section[n++] = *s++;
|
|
p->section[n] = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (*s)
|
|
if (*s++ == ']')
|
|
{
|
|
while (isspace(*s))
|
|
s++;
|
|
if (*s++ == '[')
|
|
{
|
|
if (*s++ != '-')
|
|
{
|
|
l = 0;
|
|
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))
|
|
{
|
|
for (; *s == '\a' || *s == '\b' || *s == '\v' || *s == ' '; s++);
|
|
if (*s == '\f')
|
|
{
|
|
if (*(s + 1) == '?' && *(s + 2) == '\f')
|
|
break;
|
|
s = expand(s + 1, NiL, &e, state.xp, p->id);
|
|
}
|
|
for (t = s; *t && *t != ' ' && *t != ']'; t++);
|
|
if (t > s)
|
|
{
|
|
u = t;
|
|
if (*(t - 1) == '\a' || *(t - 1) == '\b' || *(t - 1) == '\v')
|
|
t--;
|
|
if (t > s)
|
|
{
|
|
while (*u == ' ' || *u == '\\')
|
|
u++;
|
|
if (*u == '-' || *u == ']')
|
|
{
|
|
if (!l)
|
|
p->id = save(s, t - s, 0, 0, 0, 0);
|
|
else if ((a = strlen(p->id)) <= (n = t - s) || strncmp(p->id + a - n, s, n) || *(p->id + a - n - 1) != ':')
|
|
p->id = save(p->id, strlen(p->id), "::", 2, s, t - s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (*s == '-')
|
|
s++;
|
|
if (strneq(s, "catalog?", 8))
|
|
p->catalog = s += 8;
|
|
}
|
|
}
|
|
}
|
|
if (!error_info.id)
|
|
{
|
|
if (!(error_info.id = p->id))
|
|
p->id = "command";
|
|
}
|
|
else if (p->id == error_info.id)
|
|
p->id = save(p->id, strlen(p->id), 0, 0, 0, 0);
|
|
if (s = p->catalog)
|
|
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;
|
|
if (!p->catalog)
|
|
{
|
|
if (opt_info.disc && opt_info.disc->catalog && (!p->id || !streq(opt_info.disc->catalog, p->id)))
|
|
p->catalog = opt_info.disc->catalog;
|
|
else
|
|
p->catalog = ID;
|
|
}
|
|
s = p->oopts;
|
|
if (*s == ':')
|
|
s++;
|
|
if (*s == '+')
|
|
{
|
|
s++;
|
|
p->flags |= OPT_plus;
|
|
}
|
|
s = next(s, 0);
|
|
if (*s != '[')
|
|
for (t = s, a = 0; *t; t++)
|
|
if (!a && *t == '-')
|
|
{
|
|
p->flags |= OPT_minus;
|
|
break;
|
|
}
|
|
else if (*t == '[')
|
|
a++;
|
|
else if (*t == ']')
|
|
a--;
|
|
if (!p->version && (t = strchr(s, '(')) && strchr(t, ')') && (state.cp || (state.cp = sfstropen())))
|
|
{
|
|
/*
|
|
* solaris long option compatibility
|
|
*/
|
|
|
|
p->version = 1;
|
|
for (t = p->oopts; t < s; t++)
|
|
sfputc(state.cp, *t);
|
|
n = t - p->oopts;
|
|
sfputc(state.cp, '[');
|
|
sfputc(state.cp, '-');
|
|
sfputc(state.cp, ']');
|
|
c = *s++;
|
|
while (c)
|
|
{
|
|
sfputc(state.cp, '[');
|
|
sfputc(state.cp, c);
|
|
if (a = (c = *s++) == ':')
|
|
c = *s++;
|
|
if (c == '(')
|
|
{
|
|
sfputc(state.cp, ':');
|
|
for (;;)
|
|
{
|
|
while ((c = *s++) && c != ')')
|
|
sfputc(state.cp, c);
|
|
if (!c || (c = *s++) != '(')
|
|
break;
|
|
sfputc(state.cp, '|');
|
|
}
|
|
}
|
|
sfputc(state.cp, ']');
|
|
if (a)
|
|
sfputr(state.cp, ":[string]", -1);
|
|
}
|
|
if (!(p->oopts = s = sfstruse(state.cp)))
|
|
return -1;
|
|
s += n;
|
|
}
|
|
p->opts = s;
|
|
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));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* return the bold set/unset sequence for style
|
|
*/
|
|
|
|
static const char*
|
|
font(int f, int style, int set)
|
|
{
|
|
switch (style)
|
|
{
|
|
case STYLE_html:
|
|
return fonts[f].html[set];
|
|
case STYLE_nroff:
|
|
return fonts[f].nroff[set];
|
|
case STYLE_short:
|
|
case STYLE_long:
|
|
case STYLE_posix:
|
|
case STYLE_api:
|
|
break;
|
|
default:
|
|
if (state.emphasis > 0)
|
|
return fonts[f].term[set];
|
|
break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
* push \f...\f info
|
|
*/
|
|
|
|
static Push_t*
|
|
info(Push_t* psp, char* s, char* e, Sfio_t* ip, char* id)
|
|
{
|
|
register char* b;
|
|
int n;
|
|
Push_t* tsp;
|
|
|
|
static Push_t push;
|
|
|
|
b = expand(s, e, &s, ip, id);
|
|
n = strlen(b);
|
|
if (tsp = newof(0, Push_t, 1, n + 1))
|
|
{
|
|
tsp->nb = (char*)(tsp + 1);
|
|
tsp->ne = tsp->nb + n;
|
|
strcpy(tsp->nb, b);
|
|
}
|
|
else
|
|
tsp = &push;
|
|
tsp->next = psp;
|
|
tsp->ob = s;
|
|
tsp->oe = e;
|
|
return tsp;
|
|
}
|
|
|
|
/*
|
|
* push translation
|
|
*/
|
|
|
|
static Push_t*
|
|
localize(Push_t* psp, char* s, char* e, int term, int n, Sfio_t* ip, int version, char* id, char* catalog)
|
|
{
|
|
char* t;
|
|
char* u;
|
|
Push_t* tsp;
|
|
int c;
|
|
|
|
t = skip(s, term, 0, 0, n, 0, 0, version);
|
|
if (e && t > e)
|
|
t = e;
|
|
while (s < t)
|
|
{
|
|
switch (c = *s++)
|
|
{
|
|
case ':':
|
|
case '?':
|
|
if (term && *s == c)
|
|
s++;
|
|
break;
|
|
case ']':
|
|
if (*s == c)
|
|
s++;
|
|
break;
|
|
}
|
|
sfputc(ip, c);
|
|
}
|
|
if (!(s = sfstruse(ip)) || (u = T(id, catalog, s)) == s)
|
|
return 0;
|
|
n = strlen(u);
|
|
if (tsp = newof(0, Push_t, 1, n + 1))
|
|
{
|
|
tsp->nb = (char*)(tsp + 1);
|
|
tsp->ne = tsp->nb + n;
|
|
strcpy(tsp->nb, u);
|
|
tsp->ob = t;
|
|
tsp->oe = e;
|
|
tsp->ch = 1;
|
|
}
|
|
tsp->next = psp;
|
|
return tsp;
|
|
}
|
|
|
|
/*
|
|
* output label s from [ ...label...[?...] ] to sp
|
|
* 1 returned if the label was translated
|
|
*/
|
|
|
|
static int
|
|
label(register Sfio_t* sp, int sep, register char* s, int about, int z, int level, int style, int f, Sfio_t* ip, int version, char* id, char* catalog)
|
|
{
|
|
register int c;
|
|
register char* t;
|
|
register char* e;
|
|
int ostyle;
|
|
int a;
|
|
int i;
|
|
char* p;
|
|
char* q;
|
|
char* w;
|
|
char* y;
|
|
int va;
|
|
Push_t* tsp;
|
|
|
|
int r = 0;
|
|
int n = 1;
|
|
Push_t* psp = 0;
|
|
|
|
if ((ostyle = style) > (STYLE_nroff - (sep <= 0)) && f != FONT_LITERAL && f >= 0)
|
|
style = 0;
|
|
if (z < 0)
|
|
e = s + strlen(s);
|
|
else
|
|
e = s + z;
|
|
if (sep > 0)
|
|
{
|
|
if (sep == ' ' && style == STYLE_nroff)
|
|
sfputc(sp, '\\');
|
|
sfputc(sp, sep);
|
|
}
|
|
sep = !sep || z < 0;
|
|
va = 0;
|
|
y = 0;
|
|
if (about)
|
|
sfputc(sp, '(');
|
|
if (version < 1)
|
|
{
|
|
a = 0;
|
|
for (;;)
|
|
{
|
|
if (s >= e)
|
|
return r;
|
|
switch (c = *s++)
|
|
{
|
|
case '[':
|
|
a++;
|
|
break;
|
|
case ']':
|
|
if (--a < 0)
|
|
return r;
|
|
break;
|
|
}
|
|
sfputc(sp, c);
|
|
}
|
|
}
|
|
else if (level && (*(p = skip(s, 0, 0, 0, 1, level, 1, version)) == ':' || *p == '#'))
|
|
{
|
|
va = 0;
|
|
if (*++p == '?' || *p == *(p - 1))
|
|
{
|
|
p++;
|
|
va |= OPT_optional;
|
|
}
|
|
if (*(p = next(p, version)) == '[')
|
|
y = p + 1;
|
|
}
|
|
if (X(catalog) && (!level || *s == '\a' || *(s - 1) != '+') &&
|
|
(tsp = localize(psp, s, e, (sep || level) ? '?' : 0, sep || level, ip, version, id, catalog)))
|
|
{
|
|
psp = tsp;
|
|
s = psp->nb;
|
|
e = psp->ne;
|
|
r = psp->ch > 0;
|
|
}
|
|
switch (*s)
|
|
{
|
|
case '\a':
|
|
if (f == FONT_ITALIC || f < 0)
|
|
s++;
|
|
if (f > 0)
|
|
f = 0;
|
|
break;
|
|
case '\b':
|
|
if (f == FONT_BOLD || f < 0)
|
|
s++;
|
|
if (f > 0)
|
|
f = 0;
|
|
break;
|
|
case '\v':
|
|
if (f == FONT_LITERAL || f < 0)
|
|
s++;
|
|
if (f > 0)
|
|
f = 0;
|
|
break;
|
|
default:
|
|
if (f > 0)
|
|
sfputr(sp, font(f, style, 1), -1);
|
|
break;
|
|
}
|
|
for (;;)
|
|
{
|
|
if (s >= e)
|
|
{
|
|
if (!(tsp = psp))
|
|
goto restore;
|
|
s = psp->ob;
|
|
e = psp->oe;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
continue;
|
|
}
|
|
switch (c = *s++)
|
|
{
|
|
case '(':
|
|
if (n)
|
|
{
|
|
n = 0;
|
|
if (f > 0)
|
|
{
|
|
sfputr(sp, font(f, style, 0), -1);
|
|
f = 0;
|
|
}
|
|
}
|
|
break;
|
|
case '?':
|
|
case ':':
|
|
case ']':
|
|
if (psp && psp->ch)
|
|
break;
|
|
if (y)
|
|
{
|
|
if (va & OPT_optional)
|
|
sfputc(sp, '[');
|
|
sfputc(sp, '=');
|
|
label(sp, 0, y, 0, -1, 0, style, f >= 0 ? FONT_ITALIC : f, ip, version, id, catalog);
|
|
if (va & OPT_optional)
|
|
sfputc(sp, ']');
|
|
y = 0;
|
|
}
|
|
switch (c)
|
|
{
|
|
case '?':
|
|
if (*s == '?')
|
|
s++;
|
|
else if (*s == ']' && *(s + 1) != ']')
|
|
continue;
|
|
else if (sep)
|
|
goto restore;
|
|
else if (X(catalog) && (tsp = localize(psp, s, e, 0, 1, ip, version, id, catalog)))
|
|
{
|
|
psp = tsp;
|
|
s = psp->nb;
|
|
e = psp->ne;
|
|
}
|
|
break;
|
|
case ']':
|
|
if (sep && *s++ != ']')
|
|
goto restore;
|
|
break;
|
|
case ':':
|
|
if (sep && *s++ != ':')
|
|
goto restore;
|
|
break;
|
|
}
|
|
break;
|
|
case '\a':
|
|
a = FONT_ITALIC;
|
|
setfont:
|
|
if (f >= 0)
|
|
{
|
|
if (f & ~a)
|
|
{
|
|
sfputr(sp, font(f, style, 0), -1);
|
|
f = 0;
|
|
}
|
|
if (!f && style == STYLE_html)
|
|
{
|
|
for (t = s; t < e && !isspace(*t) && !iscntrl(*t); t++);
|
|
if (*t == c && *++t == '(')
|
|
{
|
|
w = t;
|
|
if (++t < e && isdigit(*t))
|
|
while (++t < e && isupper(*t));
|
|
if (t < e && *t == ')' && t > w + 1)
|
|
{
|
|
sfprintf(sp, "<NOBR><A href=\"../man%-.*s/"
|
|
, t - w - 1, w + 1
|
|
);
|
|
for (q = s; q < w - 1; q++)
|
|
if (*q == ':' && q < w - 2 && *(q + 1) == ':')
|
|
{
|
|
sfputc(sp, '-');
|
|
q++;
|
|
}
|
|
else
|
|
sfputc(sp, *q);
|
|
sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>"
|
|
, font(a, style, 1)
|
|
, w - s - 1, s
|
|
, font(a, style, 0)
|
|
, t - w + 1, w
|
|
);
|
|
s = t + 1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
sfputr(sp, font(a, style, !!(f ^= a)), -1);
|
|
}
|
|
continue;
|
|
case '\b':
|
|
a = FONT_BOLD;
|
|
goto setfont;
|
|
case '\f':
|
|
psp = info(psp, s, e, ip, id);
|
|
if (psp->nb)
|
|
{
|
|
s = psp->nb;
|
|
e = psp->ne;
|
|
}
|
|
else
|
|
{
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
continue;
|
|
case '\n':
|
|
sfputc(sp, c);
|
|
for (i = 0; i < level; i++)
|
|
sfputc(sp, '\t');
|
|
continue;
|
|
case '\v':
|
|
a = FONT_LITERAL;
|
|
goto setfont;
|
|
case '<':
|
|
if (style == STYLE_html)
|
|
{
|
|
sfputr(sp, "<", -1);
|
|
c = 0;
|
|
for (t = s; t < e; t++)
|
|
if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-')
|
|
{
|
|
if (*t == '@')
|
|
{
|
|
if (c)
|
|
break;
|
|
c = 1;
|
|
}
|
|
else if (*t == '>')
|
|
{
|
|
if (c)
|
|
{
|
|
sfprintf(sp, "<A href=\"mailto:%-.*s>%-.*s</A>>", t - s, s, t - s, s);
|
|
s = t + 1;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
case '>':
|
|
if (style == STYLE_html)
|
|
{
|
|
sfputr(sp, ">", -1);
|
|
continue;
|
|
}
|
|
break;
|
|
case '&':
|
|
if (style == STYLE_html)
|
|
{
|
|
sfputr(sp, "&", -1);
|
|
continue;
|
|
}
|
|
break;
|
|
case '"':
|
|
if (style == STYLE_html)
|
|
{
|
|
sfputr(sp, """, -1);
|
|
continue;
|
|
}
|
|
break;
|
|
case '-':
|
|
if (ostyle == STYLE_nroff)
|
|
sfputc(sp, '\\');
|
|
break;
|
|
case '.':
|
|
if (ostyle == STYLE_nroff)
|
|
{
|
|
sfputc(sp, '\\');
|
|
sfputc(sp, '&');
|
|
}
|
|
break;
|
|
case '\\':
|
|
if (ostyle == STYLE_nroff)
|
|
{
|
|
c = 'e';
|
|
sfputc(sp, '\\');
|
|
}
|
|
break;
|
|
case ' ':
|
|
if (ostyle == STYLE_nroff)
|
|
sfputc(sp, '\\');
|
|
break;
|
|
}
|
|
sfputc(sp, c);
|
|
}
|
|
restore:
|
|
if (f > 0)
|
|
sfputr(sp, font(f, style, 0), -1);
|
|
if (about)
|
|
sfputc(sp, ')');
|
|
if (psp)
|
|
pop(psp);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* output args description to sp from p of length n
|
|
*/
|
|
|
|
static void
|
|
args(register Sfio_t* sp, register char* p, register int n, int flags, int style, Sfio_t* ip, int version, char* id, char* catalog)
|
|
{
|
|
register int i;
|
|
register char* t;
|
|
register char* o;
|
|
register char* a = 0;
|
|
char* b;
|
|
int sep;
|
|
|
|
if (flags & OPT_functions)
|
|
sep = '\t';
|
|
else
|
|
{
|
|
sep = ' ';
|
|
o = T(NiL, ID, "options");
|
|
b = style == STYLE_nroff ? "\\ " : " ";
|
|
for (;;)
|
|
{
|
|
t = (char*)memchr(p, '\n', n);
|
|
if (style >= STYLE_man)
|
|
{
|
|
int firstline = !a;
|
|
/* --man page: print command name */
|
|
if (!(a = id))
|
|
a = "...";
|
|
sfprintf(sp, "\t%s%s%s", font(FONT_BOLD, style, 1), a, font(FONT_BOLD, style, 0));
|
|
if (firstline)
|
|
{
|
|
/* Append "[ options ]" label */
|
|
sfprintf(sp, "%s[%s%s%s%s%s]",
|
|
b, b, font(FONT_ITALIC, style, 1), o, font(FONT_ITALIC, style, 0), b);
|
|
}
|
|
}
|
|
else if (a)
|
|
{
|
|
/* Usage/--help, line 2+: specific usages. Prefix by "Or:" */
|
|
sfprintf(sp, "%*.*s%s%s", OPT_USAGE - 1, OPT_USAGE - 1, T(NiL, ID, "Or:"), b, a);
|
|
}
|
|
else
|
|
{
|
|
/* Usage on unknown long option error: no short options are printed, so print "[ options ]" */
|
|
if (!(a = error_info.id) && !(a = id))
|
|
a = "...";
|
|
if (!sfstrtell(sp))
|
|
sfprintf(sp, "[%s%s%s]", b, o, b);
|
|
}
|
|
if (!t)
|
|
break;
|
|
i = ++t - p;
|
|
if (i)
|
|
{
|
|
/* Print options for usage line */
|
|
sfputr(sp, b, -1);
|
|
if (X(catalog))
|
|
{
|
|
sfwrite(ip, p, i);
|
|
if (b = sfstruse(ip))
|
|
sfputr(sp, T(id, catalog, b), -1);
|
|
else
|
|
sfwrite(sp, p, i);
|
|
}
|
|
else
|
|
sfwrite(sp, p, i);
|
|
}
|
|
/* New line */
|
|
if (style == STYLE_html)
|
|
sfputr(sp, "<BR>", '\n');
|
|
else if (style == STYLE_nroff)
|
|
sfputr(sp, ".br", '\n');
|
|
else if (style == STYLE_api)
|
|
sfputr(sp, ".BR", '\n');
|
|
p = t;
|
|
n -= i;
|
|
while (n > 0 && (*p == ' ' || *p == '\t'))
|
|
{
|
|
p++;
|
|
n--;
|
|
}
|
|
}
|
|
}
|
|
/* Print options for the last usage line */
|
|
if (n)
|
|
label(sp, sep, p, 0, n, 0, style, 0, ip, version, id, catalog);
|
|
/* In usage/--help messages, tell the user how to get more help */
|
|
if (style < STYLE_man)
|
|
{
|
|
sfprintf(sp, "\n%*.*s%s%s [%s--help%s|%s--man%s] 2>&1",
|
|
OPT_USAGE - 1, OPT_USAGE - 1, T(NiL, ID, "Help:"), b, a, b, b, b, b);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* output [+-...label...?...] label s to sp
|
|
* according to {...} level and style
|
|
* return 0:header 1:paragraph
|
|
*/
|
|
|
|
static int
|
|
item(Sfio_t* sp, char* s, int about, int level, int style, Sfio_t* ip, int version, char* id, char* catalog, int* hflags)
|
|
{
|
|
register char* t;
|
|
int n;
|
|
int par;
|
|
|
|
sfputc(sp, '\n');
|
|
if (*s == '\n')
|
|
{
|
|
par = 0;
|
|
if (style >= STYLE_nroff)
|
|
sfprintf(sp, ".DS\n");
|
|
else
|
|
{
|
|
if (style == STYLE_html)
|
|
sfprintf(sp, "<PRE>\n");
|
|
else
|
|
sfputc(sp, '\n');
|
|
for (n = 0; n < level; n++)
|
|
sfputc(sp, '\t');
|
|
}
|
|
label(sp, 0, s + 1, about, -1, level, style, FONT_LITERAL, ip, version, id, catalog);
|
|
sfputc(sp, '\n');
|
|
if (style >= STYLE_nroff)
|
|
sfprintf(sp, ".DE");
|
|
else if (style == STYLE_html)
|
|
sfprintf(sp, "</PRE>");
|
|
}
|
|
else if (*s != ']' && (*s != '?' || *(s + 1) == '?'))
|
|
{
|
|
par = 0;
|
|
if (level)
|
|
{
|
|
if (style >= STYLE_nroff)
|
|
sfprintf(sp, ".H%d ", (level - (level > 2)) / 2);
|
|
else
|
|
for (n = 0; n < level; n++)
|
|
sfputc(sp, '\t');
|
|
}
|
|
if (style == STYLE_html)
|
|
{
|
|
if (!level)
|
|
{
|
|
if (*hflags & HELP_head)
|
|
sfputr(sp, "</DIV>", '\n');
|
|
else
|
|
*hflags |= HELP_head;
|
|
sfputr(sp, "<H4>", -1);
|
|
}
|
|
sfputr(sp, "<A name=\"", -1);
|
|
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] == '?')
|
|
for (t = s + 8; *t && *t != ']'; t++)
|
|
if (t[0] == 'p' && (!strncmp(t, "proprietary", 11) || !strncmp(t, "private", 7)) || t[0] == 'n' && !strncmp(t, "noncommercial", 13))
|
|
{
|
|
state.flags |= OPT_proprietary;
|
|
break;
|
|
}
|
|
label(sp, 0, s, about, -1, level, style, -1, ip, version, id, catalog);
|
|
sfputr(sp, "\">", -1);
|
|
label(sp, 0, s, about, -1, level, style, level ? FONT_BOLD : 0, ip, version, id, catalog);
|
|
sfputr(sp, "</A>", -1);
|
|
if (!level)
|
|
{
|
|
if (!strncmp(s, C("SYNOPSIS"), strlen(C("SYNOPSIS"))))
|
|
sfputr(sp, "</H4>\n<DIV class=SY>", -1);
|
|
else
|
|
{
|
|
sfputr(sp, "</H4>\n<DIV class=SH>", -1);
|
|
if (!strncmp(s, C("NAME"), strlen(C("NAME"))) || !strncmp(s, C("PLUGIN"), strlen(C("PLUGIN"))))
|
|
*hflags |= HELP_index;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!level)
|
|
{
|
|
if (style >= STYLE_nroff)
|
|
sfprintf(sp, ".SH ");
|
|
else if (style == STYLE_man)
|
|
sfputc(sp, '\n');
|
|
else if (style != STYLE_options && style != STYLE_match || *s == '-' || *s == '+')
|
|
sfputc(sp, '\t');
|
|
}
|
|
label(sp, 0, s, about, -1, level, style, FONT_BOLD, ip, version, id, catalog);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
par = 1;
|
|
if (style >= STYLE_nroff)
|
|
sfputr(sp, level ? ".SP" : ".PP", -1);
|
|
}
|
|
if (style >= STYLE_nroff || !level)
|
|
sfputc(sp, '\n');
|
|
if (par && style < STYLE_nroff)
|
|
for (n = 0; n < level; n++)
|
|
sfputc(sp, '\t');
|
|
return par;
|
|
}
|
|
|
|
/*
|
|
* output text to sp from p according to style
|
|
*/
|
|
|
|
#if _BLD_DEBUG
|
|
|
|
static char* textout(Sfio_t*, char*, char*, int, int, int, int, Sfio_t*, int, char*, char*, int*);
|
|
|
|
static char*
|
|
trace_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)
|
|
{
|
|
static int depth = 0;
|
|
|
|
message((-21, "opthelp: txt#%d +++ %2d \"%s\" style=%d level=%d bump=%d", line, ++depth, show(p), style, level, bump));
|
|
p = textout(sp, p, conform, conformlen, style, level, bump, ip, version, id, catalog, hflags);
|
|
message((-21, "opthelp: txt#%d --- %2d \"%s\"", line, depth--, show(p)));
|
|
return p;
|
|
}
|
|
|
|
#endif
|
|
|
|
static char*
|
|
textout(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)
|
|
{
|
|
#if _BLD_DEBUG
|
|
#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__)
|
|
#endif
|
|
register char* t;
|
|
register int c;
|
|
register int n;
|
|
char* w;
|
|
char* q;
|
|
int a;
|
|
int f;
|
|
int par;
|
|
int about;
|
|
Push_t* tsp;
|
|
|
|
int ident = 0;
|
|
int lev = level;
|
|
Push_t* psp = 0;
|
|
|
|
again:
|
|
about = 0;
|
|
if ((c = *s) == GO)
|
|
{
|
|
for (;;)
|
|
{
|
|
while (*(s = next(s + 1, version)) == '\n');
|
|
if (*s == GO)
|
|
{
|
|
if (level > 1)
|
|
level++;
|
|
level++;
|
|
}
|
|
else if (*s != OG)
|
|
{
|
|
if (level <= 1 || *s != '[' || *(s + 1) != '-' || style == STYLE_man && *(s + 2) == '?' || isalpha(*(s + 2)))
|
|
break;
|
|
s = skip(s, 0, 0, 0, 1, level, 0, version);
|
|
}
|
|
else if ((level -= 2) <= lev)
|
|
return s + 1;
|
|
}
|
|
if (*s == '\f')
|
|
{
|
|
psp = info(psp, s + 1, NiL, ip, id);
|
|
if (psp->nb)
|
|
s = psp->nb;
|
|
else
|
|
{
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
}
|
|
if (*s != '[')
|
|
return s;
|
|
c = *++s;
|
|
if (level > 1)
|
|
level++;
|
|
level++;
|
|
}
|
|
if (c == '-' && level > 1)
|
|
{
|
|
if (style == STYLE_man)
|
|
{
|
|
about = 1;
|
|
if (*(s + 1) == '-')
|
|
s++;
|
|
}
|
|
else
|
|
for (;;)
|
|
{
|
|
s = skip(s, 0, 0, 0, 1, level, 0, version);
|
|
while (*(s = next(s + 1, version)) == '\n');
|
|
if (*s == '[')
|
|
{
|
|
if ((c = *++s) != '-')
|
|
break;
|
|
}
|
|
else if (*s == GO)
|
|
goto again;
|
|
else if (*s == OG)
|
|
return s + 1;
|
|
}
|
|
}
|
|
if (c == '+' || c == '-' && (bump = 3) || c != ' ' && level > 1)
|
|
{
|
|
s = skip(t = s + 1, '?', 0, 0, 1, level, 0, version);
|
|
if (c == '-' && (*t == '?' || isdigit(*t) || *s == '?' && *(s + 1) == '\n'))
|
|
{
|
|
if ((c = *s) != '?')
|
|
return skip(s, 0, 0, 0, 1, level, 1, version);
|
|
w = C("version");
|
|
par = item(sp, w, about, level, style, ip, version, id, ID, hflags);
|
|
for (;;)
|
|
{
|
|
while (isspace(*(s + 1)))
|
|
s++;
|
|
w = s;
|
|
if (w[1] == '@' && w[2] == '(' && w[3] == '#' && w[4] == ')')
|
|
s = w + 4;
|
|
else if (w[1] == '$' && w[2] == 'I' && w[3] == 'd' && w[4] == ':' && w[5] == ' ')
|
|
{
|
|
s = w + 5;
|
|
ident = 1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isdigit(c) && isdigit(*t))
|
|
{
|
|
while (isdigit(*t))
|
|
t++;
|
|
if (*t == ':')
|
|
t++;
|
|
}
|
|
else if (isalnum(c) && *t-- == ':')
|
|
{
|
|
if (X(catalog) || *t == *(t + 2))
|
|
t += 2;
|
|
else
|
|
{
|
|
sfprintf(ip, "%s", t);
|
|
if (w = sfstruse(ip))
|
|
*((t = w) + 1) = '|';
|
|
}
|
|
}
|
|
par = item(sp, t, about, level, style, ip, version, id, catalog, hflags);
|
|
c = *s;
|
|
}
|
|
if (!about && level)
|
|
par = 0;
|
|
}
|
|
else
|
|
{
|
|
if (style >= STYLE_nroff)
|
|
sfputc(sp, '\n');
|
|
else if (c == '?')
|
|
for (n = 0; n < level; n++)
|
|
sfputc(sp, '\t');
|
|
par = 0;
|
|
}
|
|
if (c == ':')
|
|
c = *(s = skip(s, '?', 0, 0, 1, 0, 0, version));
|
|
if ((c == ']' || c == '?' && *(s + 1) == ']' && *(s + 2) != ']' && s++) && (c = *(s = next(s + 1, version))) == GO)
|
|
{
|
|
s = textout(sp, s, conform, conformlen, style, level + bump + par + 1, 0, ip, version, id, catalog, hflags);
|
|
if (level > lev && *s && *(s = next(s, version)) == '[')
|
|
{
|
|
s++;
|
|
message((-21, "textout#%d s=%s", __LINE__, show(s)));
|
|
goto again;
|
|
}
|
|
}
|
|
else if (c == '?' || c == ' ')
|
|
{
|
|
s++;
|
|
if (c == ' ')
|
|
sfputc(sp, c);
|
|
else
|
|
{
|
|
if (X(catalog) && (tsp = localize(psp, s, NiL, 0, 1, ip, version, id, catalog)))
|
|
{
|
|
psp = tsp;
|
|
s = psp->nb;
|
|
}
|
|
if (style < STYLE_nroff)
|
|
for (n = 0; n < bump + 1; n++)
|
|
sfputc(sp, '\t');
|
|
}
|
|
if (conform)
|
|
{
|
|
sfprintf(sp, "[%-.*s %s] ", conformlen, conform, T(NiL, ID, "conformance"));
|
|
conform = 0;
|
|
}
|
|
if (*hflags & HELP_index)
|
|
{
|
|
*hflags &= ~HELP_index;
|
|
sfputr(sp, "<!--MAN-INDEX-->", -1);
|
|
}
|
|
f = 0;
|
|
for (;;)
|
|
{
|
|
switch (c = *s++)
|
|
{
|
|
case 0:
|
|
if (!(tsp = psp))
|
|
{
|
|
if (f)
|
|
sfputr(sp, font(f, style, 0), -1);
|
|
return s - 1;
|
|
}
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
continue;
|
|
case ']':
|
|
if (psp && psp->ch)
|
|
break;
|
|
if (*s != ']')
|
|
{
|
|
if (f)
|
|
{
|
|
sfputr(sp, font(f, style, 0), -1);
|
|
f = 0;
|
|
}
|
|
for (;;)
|
|
{
|
|
if ((*s == '#' || *s == ':') && level > lev)
|
|
{
|
|
char* o;
|
|
char* v;
|
|
int j;
|
|
int m;
|
|
int ol;
|
|
int vl;
|
|
|
|
a = 0;
|
|
o = 0;
|
|
v = 0;
|
|
if (*++s == '?' || *s == *(s - 1))
|
|
{
|
|
s++;
|
|
a |= OPT_optional;
|
|
}
|
|
if (*(s = next(s, version)) == '[')
|
|
{
|
|
s = skip(s + 1, ':', '?', 0, 1, 0, 0, version);
|
|
while (*s == ':')
|
|
{
|
|
s = skip(t = s + 1, ':', '?', 0, 1, 0, 0, version);
|
|
m = s - t;
|
|
if (*t == '!')
|
|
{
|
|
o = t + 1;
|
|
ol = m - 1;
|
|
}
|
|
else if (*t == '=')
|
|
{
|
|
v = t + 1;
|
|
vl = m - 1;
|
|
}
|
|
else
|
|
for (j = 0; j < elementsof(attrs); j++)
|
|
if (strneq(t, attrs[j].name, m))
|
|
{
|
|
a |= attrs[j].flag;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (a & OPT_optional)
|
|
{
|
|
if (o)
|
|
{
|
|
sfprintf(sp, " %s ", T(NiL, ID, "If the option value is omitted then"));
|
|
sfputr(sp, font(FONT_BOLD, style, 1), -1);
|
|
t = o + ol;
|
|
while (o < t)
|
|
{
|
|
if (((c = *o++) == ':' || c == '?') && *o == c)
|
|
o++;
|
|
sfputc(sp, c);
|
|
}
|
|
sfputr(sp, font(FONT_BOLD, style, 0), -1);
|
|
sfprintf(sp, " %s.", T(NiL, ID, "is assumed"));
|
|
}
|
|
else
|
|
sfprintf(sp, " %s", T(NiL, ID, "The option value may be omitted."));
|
|
}
|
|
if (v)
|
|
{
|
|
sfprintf(sp, " %s ", T(NiL, ID, "The default value is"));
|
|
sfputr(sp, font(FONT_BOLD, style, 1), -1);
|
|
t = v + vl;
|
|
while (v < t)
|
|
{
|
|
if (((c = *v++) == ':' || c == '?') && *v == c)
|
|
v++;
|
|
sfputc(sp, c);
|
|
}
|
|
sfputr(sp, font(FONT_BOLD, style, 0), -1);
|
|
sfputc(sp, '.');
|
|
}
|
|
s = skip(s, 0, 0, 0, 1, 0, 1, version);
|
|
}
|
|
if (*(s = next(s, version)) == GO)
|
|
{
|
|
s = textout(sp, s, 0, 0, style, level + bump + !level, 0, ip, version, id, catalog, hflags);
|
|
if (*s && *(s = next(s, version)) == '[' && !isalnum(*(s + 1)))
|
|
{
|
|
s++;
|
|
message((-21, "textout#%d s=%s", __LINE__, show(s)));
|
|
goto again;
|
|
}
|
|
}
|
|
else if (*s == '[' && level > lev)
|
|
{
|
|
s++;
|
|
goto again;
|
|
}
|
|
else if (*s == '\f')
|
|
{
|
|
s++;
|
|
if (style != STYLE_keys)
|
|
{
|
|
psp = info(psp, s, NiL, ip, id);
|
|
if (psp->nb)
|
|
s = psp->nb;
|
|
else
|
|
{
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
}
|
|
}
|
|
else if (!*s)
|
|
{
|
|
if (!(tsp = psp))
|
|
break;
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
}
|
|
else if (*s != OG)
|
|
break;
|
|
else
|
|
{
|
|
s++;
|
|
if ((level -= 2) <= lev)
|
|
break;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
s++;
|
|
break;
|
|
case '\a':
|
|
a = FONT_ITALIC;
|
|
setfont:
|
|
if (f & ~a)
|
|
{
|
|
sfputr(sp, font(f, style, 0), -1);
|
|
f = 0;
|
|
}
|
|
if (!f && style == STYLE_html)
|
|
{
|
|
for (t = s; *t && !isspace(*t) && !iscntrl(*t); t++);
|
|
if (*t == c && *++t == '(')
|
|
{
|
|
w = t;
|
|
if (isdigit(*++t))
|
|
while (isupper(*++t));
|
|
if (*t == ')' && t > w + 1)
|
|
{
|
|
sfprintf(sp, "<NOBR><A href=\"../man%-.*s/"
|
|
, t - w - 1, w + 1
|
|
);
|
|
for (q = s; q < w - 1; q++)
|
|
if (*q == ':' && q < w - 2 && *(q + 1) == ':')
|
|
{
|
|
sfputc(sp, '-');
|
|
q++;
|
|
}
|
|
else
|
|
sfputc(sp, *q);
|
|
sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>"
|
|
, font(a, style, 1)
|
|
, w - s - 1, s
|
|
, font(a, style, 0)
|
|
, t - w + 1, w
|
|
);
|
|
s = t + 1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
sfputr(sp, font(a, style, !!(f ^= a)), -1);
|
|
continue;
|
|
case '\b':
|
|
a = FONT_BOLD;
|
|
goto setfont;
|
|
case '\f':
|
|
if (style != STYLE_keys)
|
|
{
|
|
psp = info(psp, s, NiL, ip, id);
|
|
if (psp->nb)
|
|
s = psp->nb;
|
|
else
|
|
{
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
}
|
|
continue;
|
|
case '\v':
|
|
a = FONT_LITERAL;
|
|
goto setfont;
|
|
case ' ':
|
|
if (ident && *s == '$')
|
|
{
|
|
while (*++s)
|
|
if (*s == ']')
|
|
{
|
|
if (*(s + 1) != ']')
|
|
break;
|
|
s++;
|
|
}
|
|
continue;
|
|
}
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
|
|
s++;
|
|
if (*s == ']' && *(s + 1) != ']' && (!psp || !psp->ch))
|
|
continue;
|
|
c = ' ';
|
|
break;
|
|
case '<':
|
|
if (style == STYLE_html)
|
|
{
|
|
sfputr(sp, "<", -1);
|
|
c = 0;
|
|
for (t = s; *t; t++)
|
|
if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-')
|
|
{
|
|
if (*t == '@')
|
|
{
|
|
if (c)
|
|
break;
|
|
c = 1;
|
|
}
|
|
else if (*t == '>')
|
|
{
|
|
if (c)
|
|
{
|
|
sfprintf(sp, "<A href=\"mailto:%-.*s\">%-.*s</A>>", t - s, s, t - s, s);
|
|
s = t + 1;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
case '>':
|
|
if (style == STYLE_html)
|
|
{
|
|
sfputr(sp, ">", -1);
|
|
continue;
|
|
}
|
|
break;
|
|
case '&':
|
|
if (style == STYLE_html)
|
|
{
|
|
sfputr(sp, "&", -1);
|
|
continue;
|
|
}
|
|
break;
|
|
case '-':
|
|
if (style == STYLE_nroff)
|
|
sfputc(sp, '\\');
|
|
break;
|
|
case '.':
|
|
if (style == STYLE_nroff)
|
|
{
|
|
sfputc(sp, '\\');
|
|
sfputc(sp, '&');
|
|
}
|
|
break;
|
|
case '\\':
|
|
if (style == STYLE_nroff)
|
|
{
|
|
sfputc(sp, c);
|
|
c = 'e';
|
|
}
|
|
break;
|
|
}
|
|
sfputc(sp, c);
|
|
}
|
|
}
|
|
else if (c == '[' && level > lev)
|
|
{
|
|
s++;
|
|
goto again;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* generate optget() help [...] list from lp
|
|
*/
|
|
|
|
static void
|
|
list(Sfio_t* sp, register const List_t* lp)
|
|
{
|
|
sfprintf(sp, "[%c", lp->type);
|
|
if (lp->name)
|
|
{
|
|
sfprintf(sp, "%s", lp->name);
|
|
if (lp->text)
|
|
sfprintf(sp, "?%s", lp->text);
|
|
}
|
|
sfputc(sp, ']');
|
|
}
|
|
|
|
/*
|
|
* return pointer to help message sans `Usage: command'
|
|
* if oopts is 0 then state.pass is used
|
|
* what:
|
|
* 0 ?short by default, ?long if any long options used
|
|
* * otherwise see help_text[] (--???)
|
|
* external formatter:
|
|
* \a...\a italic
|
|
* \b...\b bold
|
|
* \f...\f discipline infof callback on ...
|
|
* \v...\v literal
|
|
* internal formatter:
|
|
* \t indent
|
|
* \n newline
|
|
* margin flush pops to previous indent
|
|
*/
|
|
|
|
char*
|
|
opthelp(const char* oopts, const char* what)
|
|
{
|
|
register Sfio_t* sp;
|
|
register Sfio_t* mp;
|
|
register int c;
|
|
register char* p;
|
|
register Indent_t* ip;
|
|
char* t;
|
|
char* x;
|
|
char* w;
|
|
char* u;
|
|
char* y;
|
|
char* s;
|
|
char* d;
|
|
char* v;
|
|
char* cb;
|
|
char* dt;
|
|
char* ov;
|
|
char* pp;
|
|
char* rb;
|
|
char* re;
|
|
int f;
|
|
int i;
|
|
int j;
|
|
int m;
|
|
int n;
|
|
int a;
|
|
int cl;
|
|
int sl;
|
|
int vl;
|
|
int ol;
|
|
int wl;
|
|
int xl;
|
|
int rm;
|
|
int ts;
|
|
int co;
|
|
int z;
|
|
int style;
|
|
int head;
|
|
int margin;
|
|
int mode;
|
|
int mutex;
|
|
int prefix;
|
|
int version;
|
|
long tp;
|
|
char* id;
|
|
char* catalog;
|
|
Optpass_t* o;
|
|
Optpass_t* q;
|
|
Optpass_t* e;
|
|
Optpass_t one;
|
|
Optpass_t top;
|
|
Help_t* hp;
|
|
Tag_t ptstk[elementsof(indent) + 2];
|
|
Tag_t* pt;
|
|
Sfio_t* vp;
|
|
Push_t* tsp;
|
|
|
|
char* opts = (char*)oopts;
|
|
char* section = "1";
|
|
int flags = 0;
|
|
int bflags = 0;
|
|
int dflags = 0;
|
|
int hflags = 0;
|
|
int matched = 0;
|
|
int paragraph = 0;
|
|
Push_t* psp = 0;
|
|
Sfio_t* sp_help = 0;
|
|
Sfio_t* sp_text = 0;
|
|
Sfio_t* sp_plus = 0;
|
|
Sfio_t* sp_head = 0;
|
|
Sfio_t* sp_body = 0;
|
|
Sfio_t* sp_info = 0;
|
|
Sfio_t* sp_misc = 0;
|
|
|
|
if (!(mp = state.mp) && !(mp = state.mp = sfstropen()))
|
|
goto nospace;
|
|
if (!what)
|
|
style = state.style;
|
|
else if (!*what)
|
|
style = STYLE_options;
|
|
else if (*what != '?')
|
|
style = STYLE_match;
|
|
else if (!*(what + 1))
|
|
style = STYLE_man;
|
|
else if ((hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what + 1)) && hp->style >= 0)
|
|
{
|
|
style = hp->style;
|
|
if (*hp->name != '?')
|
|
what = hp->name;
|
|
}
|
|
else
|
|
{
|
|
if ((style = state.force) < STYLE_man)
|
|
style = STYLE_man;
|
|
if (!(sp_help = sfstropen()))
|
|
goto nospace;
|
|
for (i = 0; i < elementsof(help_head); i++)
|
|
list(sp_help, &help_head[i]);
|
|
for (i = 0; i < elementsof(styles); i++)
|
|
sfprintf(sp_help, "[:%s?%s]", styles[i].match, styles[i].text);
|
|
for (i = 0; i < elementsof(help_tail); i++)
|
|
list(sp_help, &help_tail[i]);
|
|
if (!(opts = sfstruse(sp_help)))
|
|
goto nospace;
|
|
}
|
|
|
|
/*
|
|
* this is a workaround for static optjoin() data
|
|
* clobbered by plugins/builtins that may be called
|
|
* evaluating \f...\f -- it would be good to hide
|
|
* optjoin() interactions a bit more ...
|
|
*/
|
|
|
|
top = state.pass[0];
|
|
again:
|
|
if (opts)
|
|
{
|
|
for (i = 0; i < state.npass; i++)
|
|
if (state.pass[i].oopts == opts)
|
|
{
|
|
o = &state.pass[i];
|
|
break;
|
|
}
|
|
if (i >= state.npass)
|
|
{
|
|
o = &one;
|
|
if (init((char*)opts, o))
|
|
goto nospace;
|
|
}
|
|
e = o + 1;
|
|
}
|
|
else
|
|
{
|
|
if (state.npass > 0)
|
|
{
|
|
o = state.pass;
|
|
e = o + state.npass;
|
|
}
|
|
else if (state.npass < 0)
|
|
{
|
|
o = &state.cache->pass;
|
|
e = o + 1;
|
|
}
|
|
else
|
|
return T(NiL, ID, "[* call optget() before opthelp() *]");
|
|
oopts = (const char*)state.pass[0].oopts;
|
|
}
|
|
if (style <= STYLE_usage)
|
|
{
|
|
if (!(sp_text = sfstropen()) || !(sp_info = sfstropen()))
|
|
goto nospace;
|
|
if (style >= STYLE_match && style < STYLE_keys && !(sp_body = sfstropen()))
|
|
goto nospace;
|
|
}
|
|
switch (style)
|
|
{
|
|
case STYLE_api:
|
|
case STYLE_html:
|
|
case STYLE_nroff:
|
|
state.emphasis = 0;
|
|
break;
|
|
case STYLE_usage:
|
|
case STYLE_keys:
|
|
for (q = o; q < e; q++)
|
|
if (!(q->flags & OPT_ignore) && !streq(q->catalog, o->catalog))
|
|
o = q;
|
|
/*FALLTHROUGH*/
|
|
case STYLE_posix:
|
|
sfputc(mp, '\f');
|
|
break;
|
|
default:
|
|
state.emphasis = 0;
|
|
if (x = getenv("ERROR_OPTIONS"))
|
|
{
|
|
if (strmatch(x, "*noemphasi*"))
|
|
break;
|
|
if (strmatch(x, "*emphasi*"))
|
|
{
|
|
state.emphasis = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (isatty(sffileno(sfstderr))
|
|
&& (x = getenv("TERM"))
|
|
&& strmatch(x, "(ansi|cons|dtterm|linux|screen|sun|vt???|wsvt|xterm)*"))
|
|
state.emphasis = 1;
|
|
break;
|
|
}
|
|
x = "";
|
|
xl = 0;
|
|
for (q = o; q < e; q++)
|
|
{
|
|
if (q->flags & OPT_ignore)
|
|
continue;
|
|
section = q->section;
|
|
flags |= q->flags;
|
|
p = q->opts;
|
|
prefix = q->prefix;
|
|
version = q->version;
|
|
id = q->id;
|
|
catalog = q->catalog;
|
|
switch (style)
|
|
{
|
|
case STYLE_usage:
|
|
if (xl)
|
|
sfputc(mp, '\n');
|
|
else
|
|
xl = 1;
|
|
psp = 0;
|
|
for (;;)
|
|
{
|
|
switch (c = *p++)
|
|
{
|
|
case 0:
|
|
if (!(tsp = psp))
|
|
goto style_usage;
|
|
p = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
continue;
|
|
case '\a':
|
|
c = 'a';
|
|
break;
|
|
case '\b':
|
|
c = 'b';
|
|
break;
|
|
case '\f':
|
|
psp = info(psp, p, NiL, sp_info, id);
|
|
if (psp->nb)
|
|
p = psp->nb;
|
|
else
|
|
{
|
|
p = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
continue;
|
|
case '\n':
|
|
c = 'n';
|
|
break;
|
|
case '\r':
|
|
c = 'r';
|
|
break;
|
|
case '\t':
|
|
c = 't';
|
|
break;
|
|
case '\v':
|
|
c = 'v';
|
|
break;
|
|
case '"':
|
|
c = '"';
|
|
break;
|
|
case '\'':
|
|
c = '\'';
|
|
break;
|
|
case '\\':
|
|
c = '\\';
|
|
break;
|
|
default:
|
|
sfputc(mp, c);
|
|
continue;
|
|
}
|
|
sfputc(mp, '\\');
|
|
sfputc(mp, c);
|
|
}
|
|
style_usage:
|
|
continue;
|
|
case STYLE_keys:
|
|
a = 0;
|
|
psp = 0;
|
|
vl = 0;
|
|
for (;;)
|
|
{
|
|
if (!(c = *p++))
|
|
{
|
|
if (!(tsp = psp))
|
|
break;
|
|
p = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
continue;
|
|
}
|
|
if (c == '\f')
|
|
{
|
|
psp = info(psp, p, NiL, sp_info, id);
|
|
if (psp->nb)
|
|
p = psp->nb;
|
|
else
|
|
{
|
|
p = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
continue;
|
|
}
|
|
f = z = 1;
|
|
t = 0;
|
|
if (a == 0 && (c == ' ' || c == '\n' && *p == '\n'))
|
|
{
|
|
if (c == ' ' && *p == ']')
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
if (*p == '\n')
|
|
p++;
|
|
a = c;
|
|
}
|
|
else if (c == '\n')
|
|
{
|
|
if (a == ' ')
|
|
a = -1;
|
|
else if (a == '\n' || *p == '\n')
|
|
{
|
|
a = -1;
|
|
p++;
|
|
}
|
|
continue;
|
|
}
|
|
else if ((c == ':' || c == '#') && (*p == '[' || *p == '?' && *(p + 1) == '[' && p++))
|
|
p++;
|
|
else if (c != '[')
|
|
{
|
|
if (c == GO)
|
|
vl++;
|
|
else if (c == OG)
|
|
vl--;
|
|
continue;
|
|
}
|
|
else if (*p == ' ')
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
else if (*p == '-')
|
|
{
|
|
z = 0;
|
|
if (*++p == '-')
|
|
{
|
|
p = skip(p, 0, 0, 0, 1, 0, 1, version);
|
|
continue;
|
|
}
|
|
}
|
|
else if (*p == '+')
|
|
{
|
|
p++;
|
|
if (vl > 0 && *p != '\a')
|
|
{
|
|
f = 0;
|
|
p = skip(p, '?', 0, 0, 1, 0, 0, version);
|
|
if (*p == '?')
|
|
p++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*(p + 1) == '\f' && (vp = state.vp))
|
|
p = expand(p + 2, NiL, &t, vp, id);
|
|
p = skip(p, ':', '?', 0, 1, 0, 0, version);
|
|
if (*p == ':')
|
|
p++;
|
|
}
|
|
if (f && *p == '?' && *(p + 1) != '?')
|
|
{
|
|
f = 0;
|
|
if (z)
|
|
p++;
|
|
else
|
|
p = skip(p, 0, 0, 0, 1, 0, 0, version);
|
|
}
|
|
if (*p == ']' && *(p + 1) != ']')
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
if (!*p)
|
|
{
|
|
if (!t)
|
|
break;
|
|
p = t;
|
|
t = 0;
|
|
}
|
|
m = sfstrtell(mp);
|
|
sfputc(mp, '"');
|
|
xl = 1;
|
|
/*UNDENT...*/
|
|
|
|
for (;;)
|
|
{
|
|
if (!(c = *p++))
|
|
{
|
|
if (t)
|
|
{
|
|
p = t;
|
|
t = 0;
|
|
}
|
|
if (!(tsp = psp))
|
|
{
|
|
p--;
|
|
break;
|
|
}
|
|
p = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
continue;
|
|
}
|
|
if (a > 0)
|
|
{
|
|
if (c == '\n')
|
|
{
|
|
if (a == ' ')
|
|
{
|
|
a = -1;
|
|
break;
|
|
}
|
|
if (a == '\n' || *p == '\n')
|
|
{
|
|
a = -1;
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (c == ']')
|
|
{
|
|
if (*p != ']')
|
|
{
|
|
sfputc(mp, 0);
|
|
y = sfstrbase(mp) + m + 1;
|
|
if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT))
|
|
{
|
|
sfstrseek(mp, m, SEEK_SET);
|
|
xl = 0;
|
|
}
|
|
else
|
|
sfstrseek(mp, -1, SEEK_CUR);
|
|
break;
|
|
}
|
|
sfputc(mp, *p++);
|
|
continue;
|
|
}
|
|
switch (c)
|
|
{
|
|
case '?':
|
|
if (f)
|
|
{
|
|
if (*p == '?')
|
|
{
|
|
p++;
|
|
sfputc(mp, c);
|
|
}
|
|
else
|
|
{
|
|
f = 0;
|
|
sfputc(mp, 0);
|
|
y = sfstrbase(mp) + m + 1;
|
|
if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT))
|
|
{
|
|
sfstrseek(mp, m, SEEK_SET);
|
|
xl = 0;
|
|
}
|
|
else
|
|
sfstrseek(mp, -1, SEEK_CUR);
|
|
if (z && (*p != ']' || *(p + 1) == ']'))
|
|
{
|
|
if (xl)
|
|
{
|
|
sfputc(mp, '"');
|
|
sfputc(mp, '\n');
|
|
}
|
|
m = sfstrtell(mp);
|
|
sfputc(mp, '"');
|
|
xl = 1;
|
|
}
|
|
else
|
|
{
|
|
p = skip(p, 0, 0, 0, 1, 0, 0, version);
|
|
if (*p == '?')
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
sfputc(mp, c);
|
|
continue;
|
|
case ':':
|
|
if (f && *p == ':')
|
|
p++;
|
|
sfputc(mp, c);
|
|
continue;
|
|
case '\a':
|
|
c = 'a';
|
|
break;
|
|
case '\b':
|
|
c = 'b';
|
|
break;
|
|
case '\f':
|
|
c = 'f';
|
|
break;
|
|
case '\n':
|
|
c = 'n';
|
|
break;
|
|
case '\r':
|
|
c = 'r';
|
|
break;
|
|
case '\t':
|
|
c = 't';
|
|
break;
|
|
case '\v':
|
|
c = 'v';
|
|
break;
|
|
case '"':
|
|
c = '"';
|
|
break;
|
|
case '\\':
|
|
c = '\\';
|
|
break;
|
|
case CC_esc:
|
|
c = 'E';
|
|
break;
|
|
default:
|
|
sfputc(mp, c);
|
|
continue;
|
|
}
|
|
sfputc(mp, '\\');
|
|
sfputc(mp, c);
|
|
}
|
|
|
|
/*...INDENT*/
|
|
if (xl)
|
|
{
|
|
sfputc(mp, '"');
|
|
sfputc(mp, '\n');
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
z = 0;
|
|
head = 0;
|
|
mode = 0;
|
|
mutex = 0;
|
|
if (style > STYLE_short && style < STYLE_nroff && version < 1)
|
|
{
|
|
style = STYLE_short;
|
|
if (sp_body)
|
|
{
|
|
sfclose(sp_body);
|
|
sp_body = 0;
|
|
}
|
|
}
|
|
else if (style == STYLE_short && prefix < 2)
|
|
style = STYLE_long;
|
|
if (*p == ':')
|
|
p++;
|
|
if (*p == '+')
|
|
{
|
|
p++;
|
|
if (!(sp = sp_plus) && !(sp = sp_plus = sfstropen()))
|
|
goto nospace;
|
|
}
|
|
else if (style >= STYLE_match)
|
|
sp = sp_body;
|
|
else
|
|
sp = sp_text;
|
|
psp = 0;
|
|
for (;;)
|
|
{
|
|
if (!(*(p = next(p, version))))
|
|
{
|
|
if (!(tsp = psp))
|
|
break;
|
|
p = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
continue;
|
|
}
|
|
if (*p == '\f')
|
|
{
|
|
psp = info(psp, p + 1, NiL, sp_info, id);
|
|
if (psp->nb)
|
|
p = psp->nb;
|
|
else
|
|
{
|
|
p = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
continue;
|
|
}
|
|
if (*p == '\n' || *p == ' ')
|
|
{
|
|
if (*(x = p = next(p + 1, version)))
|
|
while (*++p)
|
|
if (*p == '\n')
|
|
{
|
|
while (*++p == ' ' || *p == '\t' || *p == '\r');
|
|
if (*p == '\n')
|
|
break;
|
|
}
|
|
xl = p - x;
|
|
if (!*p)
|
|
break;
|
|
continue;
|
|
}
|
|
if (*p == OG)
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
message((-20, "opthelp: opt %s", show(p)));
|
|
if (z < 0)
|
|
z = 0;
|
|
a = 0;
|
|
f = 0;
|
|
w = 0;
|
|
d = 0;
|
|
s = 0;
|
|
rb = re = 0;
|
|
sl = 0;
|
|
vl = 0;
|
|
if (*p == '[')
|
|
{
|
|
if ((c = *(p = next(p + 1, version))) == '(')
|
|
{
|
|
p = nest(cb = p);
|
|
cl = p - cb;
|
|
c = *p;
|
|
}
|
|
else
|
|
cb = 0;
|
|
if (c == '-')
|
|
{
|
|
if (style >= STYLE_man)
|
|
{
|
|
if (*(p + 1) != '-')
|
|
{
|
|
if (!sp_misc && !(sp_misc = sfstropen()))
|
|
goto nospace;
|
|
else
|
|
p = textout(sp_misc, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
|
|
continue;
|
|
}
|
|
}
|
|
else if (style == STYLE_match && *what == '-')
|
|
{
|
|
if (*(p + 1) == '?' || *(s = skip(p + 1, ':', '?', 0, 1, 0, 0, version)) == '?' && isspace(*(s + 1)))
|
|
s = C("version");
|
|
else
|
|
s = p + 1;
|
|
w = (char*)what;
|
|
if (*s != '-' || *(w + 1) == '-')
|
|
{
|
|
if (*s == '-')
|
|
s++;
|
|
if (*(w + 1) == '-')
|
|
w++;
|
|
if (match(w + 1, s, version, id, catalog))
|
|
{
|
|
if (*(p + 1) == '-')
|
|
p++;
|
|
p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
|
|
matched = -1;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (!z)
|
|
z = -1;
|
|
}
|
|
else if (c == '+')
|
|
{
|
|
if (style >= STYLE_man)
|
|
{
|
|
p = textout(sp_body, p, cb, cl, style, 0, 0, sp_info, version, id, catalog, &bflags);
|
|
if (!sp_head)
|
|
{
|
|
sp_head = sp_body;
|
|
hflags = dflags = bflags;
|
|
if (!(sp_body = sfstropen()))
|
|
goto nospace;
|
|
}
|
|
continue;
|
|
}
|
|
else if (style == STYLE_match && *what == '+')
|
|
{
|
|
if (paragraph)
|
|
{
|
|
if (p[1] == '?')
|
|
{
|
|
p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
|
|
continue;
|
|
}
|
|
paragraph = 0;
|
|
}
|
|
if (match((char*)what + 1, p + 1, version, id, catalog))
|
|
{
|
|
p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags);
|
|
matched = -1;
|
|
paragraph = 1;
|
|
continue;
|
|
}
|
|
}
|
|
if (!z)
|
|
z = -1;
|
|
}
|
|
else if (c == '[' || version < 1)
|
|
{
|
|
mutex++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (c == '!')
|
|
{
|
|
a |= OPT_invert;
|
|
p++;
|
|
}
|
|
rb = p;
|
|
if (*p != ':')
|
|
{
|
|
s = p;
|
|
if (*(p + 1) == '|')
|
|
{
|
|
while (*++p && *p != '=' && *p != '!' && *p != ':' && *p != '?');
|
|
if ((p - s) > 1)
|
|
sl = p - s;
|
|
if (*p == '!')
|
|
a |= OPT_invert;
|
|
}
|
|
if (*(p + 1) == '\f')
|
|
p++;
|
|
else
|
|
p = skip(p, ':', '?', 0, 1, 0, 0, version);
|
|
if (sl || (p - s) == 1 || *(s + 1) == '=' || *(s + 1) == '!' && (a |= OPT_invert) || *(s + 1) == '|')
|
|
f = *s;
|
|
}
|
|
re = p;
|
|
if (style <= STYLE_short)
|
|
{
|
|
if (!z && !f)
|
|
z = -1;
|
|
}
|
|
else
|
|
{
|
|
if (*p == '\f' && (vp = state.vp))
|
|
p = expand(p + 1, NiL, &t, vp, id);
|
|
else
|
|
t = 0;
|
|
if (*p == ':')
|
|
{
|
|
p = skip(w = p + 1, ':', '?', 0, 1, 0, 0, version);
|
|
if (!(wl = p - w))
|
|
w = 0;
|
|
}
|
|
else
|
|
wl = 0;
|
|
if (*p == ':' || *p == '?')
|
|
{
|
|
d = p;
|
|
p = skip(p, 0, 0, 0, 1, 0, 0, version);
|
|
}
|
|
else
|
|
d = 0;
|
|
if (style == STYLE_match)
|
|
{
|
|
if (wl && !match((char*)what, w, version, id, catalog))
|
|
wl = 0;
|
|
if ((!wl || *w == ':' || *w == '?') && (what[1] || sl && !memchr(s, what[0], sl) || !sl && what[0] != f))
|
|
{
|
|
w = 0;
|
|
if (!z)
|
|
z = -1;
|
|
}
|
|
else
|
|
matched = 1;
|
|
}
|
|
if (t)
|
|
{
|
|
p = t;
|
|
if (*p == ':' || *p == '?')
|
|
{
|
|
d = p;
|
|
p = skip(p, 0, 0, 0, 1, 0, 0, version);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
p = skip(p, 0, 0, 0, 1, 0, 1, version);
|
|
if (*p == GO)
|
|
p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
|
|
}
|
|
else if (*p == ']')
|
|
{
|
|
if (mutex)
|
|
{
|
|
if (style >= STYLE_nroff)
|
|
sfputr(sp_body, "\n.OP - - anyof", '\n');
|
|
if (!(mutex & 1))
|
|
{
|
|
mutex--;
|
|
if (style <= STYLE_long)
|
|
{
|
|
sfputc(sp_body, ' ');
|
|
sfputc(sp_body, ']');
|
|
}
|
|
}
|
|
mutex--;
|
|
}
|
|
p++;
|
|
continue;
|
|
}
|
|
else if (*p == '?')
|
|
{
|
|
if (style < STYLE_match)
|
|
z = 1;
|
|
mode |= OPT_hidden;
|
|
p++;
|
|
continue;
|
|
}
|
|
else if (*p == '\\' && style==STYLE_posix)
|
|
{
|
|
if (*++p)
|
|
p++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
f = *p++;
|
|
s = 0;
|
|
if (style == STYLE_match && !z)
|
|
z = -1;
|
|
}
|
|
if (!z)
|
|
{
|
|
if (style == STYLE_long || prefix < 2 || (q->flags & OPT_long))
|
|
f = 0;
|
|
else if (style <= STYLE_short)
|
|
w = 0;
|
|
if (!f && !w)
|
|
z = -1;
|
|
}
|
|
ov = 0;
|
|
u = v = y = 0;
|
|
if (*p == ':' && (a |= OPT_string) || *p == '#' && (a |= OPT_number))
|
|
{
|
|
message((-21, "opthelp: arg %s", show(p)));
|
|
if (*++p == '?' || *p == *(p - 1))
|
|
{
|
|
p++;
|
|
a |= OPT_optional;
|
|
}
|
|
if (*(p = next(p, version)) == '[')
|
|
{
|
|
if (!z)
|
|
{
|
|
p = skip(y = p + 1, ':', '?', 0, 1, 0, 0, version);
|
|
while (*p == ':')
|
|
{
|
|
p = skip(t = p + 1, ':', '?', 0, 1, 0, 0, version);
|
|
m = p - t;
|
|
if (*t == '!')
|
|
{
|
|
ov = t + 1;
|
|
ol = m - 1;
|
|
}
|
|
else if (*t == '=')
|
|
{
|
|
v = t + 1;
|
|
vl = m - 1;
|
|
}
|
|
else
|
|
for (j = 0; j < elementsof(attrs); j++)
|
|
if (strneq(t, attrs[j].name, m))
|
|
{
|
|
a |= attrs[j].flag;
|
|
break;
|
|
}
|
|
}
|
|
if (*p == '?')
|
|
u = p;
|
|
p = skip(p, 0, 0, 0, 1, 0, 1, version);
|
|
}
|
|
else
|
|
p = skip(p + 1, 0, 0, 0, 1, 0, 1, version);
|
|
}
|
|
else
|
|
y = (a & OPT_number) ? T(NiL, ID, "#") : T(NiL, ID, "arg");
|
|
}
|
|
else
|
|
a |= OPT_flag;
|
|
if (!z)
|
|
{
|
|
if (style <= STYLE_short && !y && !mutex || style == STYLE_posix)
|
|
{
|
|
if (style != STYLE_posix && !sfstrtell(sp))
|
|
{
|
|
sfputc(sp, '[');
|
|
if (sp == sp_plus)
|
|
sfputc(sp, '+');
|
|
sfputc(sp, '-');
|
|
}
|
|
if (!sl)
|
|
sfputc(sp, f);
|
|
else
|
|
for (c = 0; c < sl; c++)
|
|
if (s[c] != '|')
|
|
sfputc(sp, s[c]);
|
|
if (style == STYLE_posix && y)
|
|
sfputc(sp, ':');
|
|
}
|
|
else
|
|
{
|
|
if (style >= STYLE_match)
|
|
{
|
|
sfputc(sp_body, '\n');
|
|
if (!head)
|
|
{
|
|
head = 1;
|
|
item(sp_body, (flags & OPT_functions) ? C("FUNCTIONS") : C("OPTIONS"), 0, 0, style, sp_info, version, id, ID, &bflags);
|
|
}
|
|
if (style >= STYLE_nroff)
|
|
{
|
|
if (mutex & 1)
|
|
{
|
|
mutex++;
|
|
sfputr(sp_body, "\n.OP - - oneof", '\n');
|
|
}
|
|
}
|
|
else
|
|
sfputc(sp_body, '\t');
|
|
}
|
|
else
|
|
{
|
|
if (sp_body)
|
|
sfputc(sp_body, ' ');
|
|
else if (!(sp_body = sfstropen()))
|
|
goto nospace;
|
|
if (mutex)
|
|
{
|
|
if (mutex & 1)
|
|
{
|
|
mutex++;
|
|
sfputc(sp_body, '[');
|
|
}
|
|
else
|
|
sfputc(sp_body, '|');
|
|
sfputc(sp_body, ' ');
|
|
}
|
|
else
|
|
sfputc(sp_body, '[');
|
|
}
|
|
if (style >= STYLE_nroff)
|
|
{
|
|
if (flags & OPT_functions)
|
|
{
|
|
sfputr(sp_body, ".FN", ' ');
|
|
if (re > rb)
|
|
sfwrite(sp_body, rb, re - rb);
|
|
else
|
|
sfputr(sp, "void", -1);
|
|
if (w)
|
|
label(sp_body, ' ', w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog);
|
|
}
|
|
else
|
|
{
|
|
sfputr(sp_body, ".OP", ' ');
|
|
if (sl)
|
|
sfwrite(sp_body, s, sl);
|
|
else
|
|
sfputc(sp_body, f ? f : '-');
|
|
sfputc(sp_body, ' ');
|
|
if (w)
|
|
{
|
|
if (label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, catalog))
|
|
{
|
|
sfputc(sp_body, '|');
|
|
label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, native);
|
|
}
|
|
}
|
|
else
|
|
sfputc(sp_body, '-');
|
|
sfputc(sp_body, ' ');
|
|
m = a & OPT_TYPE;
|
|
for (j = 0; j < elementsof(attrs); j++)
|
|
if (m & attrs[j].flag)
|
|
{
|
|
sfputr(sp_body, attrs[j].name, -1);
|
|
break;
|
|
}
|
|
if (m = (a & ~m) | mode)
|
|
for (j = 0; j < elementsof(attrs); j++)
|
|
if (m & attrs[j].flag)
|
|
{
|
|
sfputc(sp_body, ':');
|
|
sfputr(sp_body, attrs[j].name, -1);
|
|
}
|
|
sfputc(sp_body, ' ');
|
|
if (y)
|
|
label(sp_body, 0, y, 0, -1, 0, style, 0, sp_info, version, id, catalog);
|
|
else
|
|
sfputc(sp_body, '-');
|
|
if (v)
|
|
sfprintf(sp_body, " %-.*s", vl, v);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (f)
|
|
{
|
|
if (sp_body == sp_plus)
|
|
sfputc(sp_body, '+');
|
|
sfputc(sp_body, '-');
|
|
sfputr(sp_body, font(FONT_BOLD, style, 1), -1);
|
|
if (!sl)
|
|
{
|
|
sfputc(sp_body, f);
|
|
if (f == '-' && y)
|
|
{
|
|
y = 0;
|
|
sfputr(sp_body, C("long-option[=value]"), -1);
|
|
}
|
|
}
|
|
else
|
|
sfwrite(sp_body, s, sl);
|
|
sfputr(sp_body, font(FONT_BOLD, style, 0), -1);
|
|
if (w)
|
|
{
|
|
sfputc(sp_body, ',');
|
|
sfputc(sp_body, ' ');
|
|
}
|
|
}
|
|
else if ((flags & OPT_functions) && re > rb)
|
|
{
|
|
sfwrite(sp_body, rb, re - rb);
|
|
sfputc(sp_body, ' ');
|
|
}
|
|
if (w)
|
|
{
|
|
if (prefix > 0)
|
|
{
|
|
sfputc(sp_body, '-');
|
|
if (prefix > 1)
|
|
sfputc(sp_body, '-');
|
|
}
|
|
if (label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog))
|
|
{
|
|
sfputc(sp_body, '|');
|
|
label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, native);
|
|
}
|
|
}
|
|
if (y)
|
|
{
|
|
if (a & OPT_optional)
|
|
sfputc(sp_body, '[');
|
|
else if (!w)
|
|
sfputc(sp_body, ' ');
|
|
if (w)
|
|
sfputc(sp_body, prefix == 1 ? ' ' : '=');
|
|
label(sp_body, 0, y, 0, -1, 0, style, FONT_ITALIC, sp_info, version, id, catalog);
|
|
if (a & OPT_optional)
|
|
sfputc(sp_body, ']');
|
|
}
|
|
}
|
|
if (style >= STYLE_match)
|
|
{
|
|
if (d)
|
|
{
|
|
textout(sp_body, d, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags);
|
|
cb = 0;
|
|
}
|
|
if (u)
|
|
textout(sp_body, u, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags);
|
|
if ((a & OPT_invert) && w && (d || u))
|
|
{
|
|
u = skip(w, ':', '?', 0, 1, 0, 0, version);
|
|
if (f)
|
|
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);
|
|
else
|
|
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"));
|
|
if (!(t = sfstruse(sp_info)))
|
|
goto nospace;
|
|
textout(sp_body, t, 0, 0, style, 0, 0, sp_info, version, NiL, NiL, &bflags);
|
|
}
|
|
if (*p == GO)
|
|
{
|
|
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);
|
|
y = "+?";
|
|
}
|
|
else
|
|
y = " ";
|
|
if (a & OPT_optional)
|
|
{
|
|
if (ov)
|
|
{
|
|
sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "If the option value is omitted then"));
|
|
t = ov + ol;
|
|
while (ov < t)
|
|
{
|
|
if (((c = *ov++) == ':' || c == '?') && *ov == c)
|
|
ov++;
|
|
sfputc(sp_info, c);
|
|
}
|
|
sfprintf(sp_info, "\b %s.", T(NiL, ID, "is assumed"));
|
|
}
|
|
else
|
|
sfprintf(sp_info, "%s%s", y, T(NiL, ID, "The option value may be omitted."));
|
|
if (!(t = sfstruse(sp_info)))
|
|
goto nospace;
|
|
textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags);
|
|
y = " ";
|
|
}
|
|
if (v)
|
|
{
|
|
sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "The default value is"));
|
|
t = v + vl;
|
|
while (v < t)
|
|
{
|
|
if (((c = *v++) == ':' || c == '?') && *v == c)
|
|
v++;
|
|
sfputc(sp_info, c);
|
|
}
|
|
sfputc(sp_info, '\b');
|
|
sfputc(sp_info, '.');
|
|
if (!(t = sfstruse(sp_info)))
|
|
goto nospace;
|
|
textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags);
|
|
}
|
|
}
|
|
else if (!mutex)
|
|
sfputc(sp_body, ']');
|
|
}
|
|
if (*p == GO)
|
|
{
|
|
if (style >= STYLE_match)
|
|
p = textout(sp_body, p, 0, 0, style, 4, 0, sp_info, version, id, catalog, &bflags);
|
|
else
|
|
p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
|
|
}
|
|
}
|
|
else if (*p == GO)
|
|
p = skip(p + 1, 0, 0, 0, 0, 1, 1, version);
|
|
}
|
|
psp = pop(psp);
|
|
if (sp_misc)
|
|
{
|
|
if (!(p = sfstruse(sp_misc)))
|
|
goto nospace;
|
|
for (t = p; *t == '\t' || *t == '\n'; t++);
|
|
if (*t)
|
|
{
|
|
item(sp_body, C("IMPLEMENTATION"), 0, 0, style, sp_info, version, id, ID, &bflags);
|
|
sfputr(sp_body, p, -1);
|
|
}
|
|
}
|
|
}
|
|
if (oopts != o->oopts && oopts == top.oopts)
|
|
state.pass[0] = top;
|
|
version = o->version;
|
|
id = o->id;
|
|
catalog = o->catalog;
|
|
if (style >= STYLE_keys)
|
|
{
|
|
if (sp_info)
|
|
sfclose(sp_info);
|
|
if (style == STYLE_keys && sfstrtell(mp) > 1)
|
|
sfstrseek(mp, -1, SEEK_CUR);
|
|
if (!(p = sfstruse(mp)))
|
|
goto nospace;
|
|
return opt_info.msg = p;
|
|
}
|
|
sp = sp_text;
|
|
if (sfstrtell(sp) && style != STYLE_posix)
|
|
sfputc(sp, ']');
|
|
if (style == STYLE_nroff)
|
|
{
|
|
char rd[64];
|
|
char ud[64];
|
|
|
|
s = o->id;
|
|
t = ud;
|
|
while (t < &ud[sizeof(ud)-2] && (c = *s++))
|
|
{
|
|
if (islower(c))
|
|
c = toupper(c);
|
|
*t++ = c;
|
|
}
|
|
*t = 0;
|
|
t = rd;
|
|
if (s = o->release)
|
|
{
|
|
*t++ = ' ';
|
|
while (t < &rd[sizeof(rd)-2] && (c = *s++) && c != ']')
|
|
*t++ = c;
|
|
}
|
|
*t = 0;
|
|
sfprintf(sp, "\
|
|
.\\\" format with nroff|troff|groff -man\n\
|
|
.TH %s %s%s\n\
|
|
.fp 5 CW\n\
|
|
.nr mH 5\n\
|
|
.de H0\n\
|
|
.nr mH 0\n\
|
|
.in 5n\n\
|
|
\\fB\\\\$1\\fP\n\
|
|
.in 7n\n\
|
|
..\n\
|
|
.de H1\n\
|
|
.nr mH 1\n\
|
|
.in 7n\n\
|
|
\\fB\\\\$1\\fP\n\
|
|
.in 9n\n\
|
|
..\n\
|
|
.de H2\n\
|
|
.nr mH 2\n\
|
|
.in 11n\n\
|
|
\\fB\\\\$1\\fP\n\
|
|
.in 13n\n\
|
|
..\n\
|
|
.de H3\n\
|
|
.nr mH 3\n\
|
|
.in 15n\n\
|
|
\\fB\\\\$1\\fP\n\
|
|
.in 17n\n\
|
|
..\n\
|
|
.de H4\n\
|
|
.nr mH 4\n\
|
|
.in 19n\n\
|
|
\\fB\\\\$1\\fP\n\
|
|
.in 21n\n\
|
|
..\n\
|
|
.de OP\n\
|
|
.nr mH 0\n\
|
|
.ie !'\\\\$1'-' \\{\n\
|
|
.ds mO \\\\fB\\\\-\\\\$1\\\\fP\n\
|
|
.ds mS ,\\\\0\n\
|
|
.\\}\n\
|
|
.el \\{\n\
|
|
.ds mO \\\\&\n\
|
|
.ds mS \\\\&\n\
|
|
.\\}\n\
|
|
.ie '\\\\$2'-' \\{\n\
|
|
.if !'\\\\$4'-' .as mO \\\\0\\\\fI\\\\$4\\\\fP\n\
|
|
.\\}\n\
|
|
.el \\{\n\
|
|
.as mO \\\\*(mS\\\\fB%s\\\\$2\\\\fP\n\
|
|
.if !'\\\\$4'-' .as mO =\\\\fI\\\\$4\\\\fP\n\
|
|
.\\}\n\
|
|
.in 5n\n\
|
|
\\\\*(mO\n\
|
|
.in 9n\n\
|
|
..\n\
|
|
.de SP\n\
|
|
.if \\\\n(mH==2 .in 9n\n\
|
|
.if \\\\n(mH==3 .in 13n\n\
|
|
.if \\\\n(mH==4 .in 17n\n\
|
|
..\n\
|
|
.de FN\n\
|
|
.nr mH 0\n\
|
|
.in 5n\n\
|
|
\\\\$1 \\\\$2\n\
|
|
.in 9n\n\
|
|
..\n\
|
|
.de DS\n\
|
|
.in +3n\n\
|
|
.ft 5\n\
|
|
.nf\n\
|
|
..\n\
|
|
.de DE\n\
|
|
.fi\n\
|
|
.ft R\n\
|
|
.in -3n\n\
|
|
..\n\
|
|
"
|
|
, ud
|
|
, section
|
|
, rd
|
|
, o->prefix == 2 ? "\\\\-\\\\-" : o->prefix == 1 ? "\\\\-" : ""
|
|
);
|
|
}
|
|
if (style == STYLE_match)
|
|
{
|
|
if (!matched)
|
|
{
|
|
if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what))
|
|
{
|
|
if (!sp_help && !(sp_help = sfstropen()))
|
|
goto nospace;
|
|
sfprintf(sp_help, "[-][:%s?%s]", hp->match, hp->text);
|
|
if (!(opts = sfstruse(sp_help)))
|
|
goto nospace;
|
|
goto again;
|
|
}
|
|
s = (char*)unknown;
|
|
goto nope;
|
|
}
|
|
else if (matched < 0)
|
|
x = 0;
|
|
}
|
|
if (sp_plus)
|
|
{
|
|
if (sfstrtell(sp_plus))
|
|
{
|
|
if (sfstrtell(sp))
|
|
sfputc(sp, ' ');
|
|
if (!(t = sfstruse(sp_plus)))
|
|
goto nospace;
|
|
sfputr(sp, t, ']');
|
|
}
|
|
sfclose(sp_plus);
|
|
}
|
|
if (style >= STYLE_man)
|
|
{
|
|
if (sp_head)
|
|
{
|
|
if (!(t = sfstruse(sp_head)))
|
|
goto nospace;
|
|
for (; *t == '\n'; t++);
|
|
sfputr(sp, t, '\n');
|
|
sfclose(sp_head);
|
|
sp_head = 0;
|
|
}
|
|
if (x)
|
|
item(sp, C("SYNOPSIS"), 0, 0, style, sp_info, version, id, ID, &hflags);
|
|
}
|
|
if (x)
|
|
{
|
|
for (t = x + xl; t > x && (*(t - 1) == '\n' || *(t - 1) == '\r'); t--);
|
|
xl = t - x;
|
|
if (style >= STYLE_match)
|
|
{
|
|
u = id;
|
|
if (o->flags & OPT_functions)
|
|
t = 0;
|
|
else if (t = strchr(u, ':'))
|
|
{
|
|
if ((o->flags & OPT_module) && *(t + 1) == ':' && *(t + 2))
|
|
{
|
|
u = t + 2;
|
|
t = 0;
|
|
}
|
|
else
|
|
*t = 0;
|
|
}
|
|
args(sp, x, xl, o->flags, style, sp_info, version, u, catalog);
|
|
if (t)
|
|
*t = ':';
|
|
x = 0;
|
|
}
|
|
}
|
|
if (sp_body)
|
|
{
|
|
if (sfstrtell(sp_body))
|
|
{
|
|
if (style < STYLE_match && sfstrtell(sp))
|
|
sfputc(sp, ' ');
|
|
if (!(t = sfstruse(sp_body)))
|
|
goto nospace;
|
|
if (style == STYLE_html && !(dflags & HELP_head) && (bflags & HELP_head))
|
|
sfputr(sp, "\n</DIV>", '\n');
|
|
sfputr(sp, t, -1);
|
|
}
|
|
sfclose(sp_body);
|
|
sp_body = 0;
|
|
}
|
|
if (x && style != STYLE_posix)
|
|
args(sp, x, xl, flags, style, sp_info, version, id, catalog);
|
|
if (sp_info)
|
|
{
|
|
sfclose(sp_info);
|
|
sp_info = 0;
|
|
}
|
|
if (sp_misc)
|
|
{
|
|
sfclose(sp_misc);
|
|
sp_misc = 0;
|
|
}
|
|
if (!(p = sfstruse(sp)))
|
|
goto nospace;
|
|
astwinsize(1, NiL, &state.width);
|
|
if (state.width < 20)
|
|
state.width = OPT_WIDTH;
|
|
m = strlen((style <= STYLE_long && error_info.id && !strchr(error_info.id, '/')) ? error_info.id : id) + 1;
|
|
margin = style == STYLE_api ? (8 * 1024) : (state.width - 1);
|
|
if (!(state.flags & OPT_preformat))
|
|
{
|
|
if (style >= STYLE_man || matched < 0)
|
|
{
|
|
sfputc(mp, '\f');
|
|
ts = 0;
|
|
}
|
|
else
|
|
ts = OPT_USAGE + m;
|
|
if (style == STYLE_html)
|
|
{
|
|
char ud[64];
|
|
|
|
s = id;
|
|
t = ud;
|
|
while (t < &ud[sizeof(ud)-2] && (c = *s++))
|
|
{
|
|
if (islower(c))
|
|
c = toupper(c);
|
|
*t++ = c;
|
|
}
|
|
*t = 0;
|
|
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);
|
|
sfprintf(mp, "<H4><TABLE width=100%%><TR><TH align=left>%s ( %s ) <TH align=center><A href=\".\" title=\"Index\">%s</A><TH align=right>%s ( %s )</TR></TABLE></H4>\n<HR>\n", ud, section, T(NiL, ID, secname(section)), ud, section);
|
|
co = 2;
|
|
pt = ptstk;
|
|
pt->level = 0;
|
|
pt->id = TAG_DIV;
|
|
}
|
|
else
|
|
co = 0;
|
|
if ((rm = margin - ts) < OPT_MARGIN)
|
|
rm = OPT_MARGIN;
|
|
ip = indent;
|
|
ip->stop = (ip+1)->stop = style >= STYLE_html ? 0 : 2;
|
|
tp = 0;
|
|
n = 0;
|
|
head = 1;
|
|
while (*p == '\n')
|
|
p++;
|
|
while (c = *p++)
|
|
{
|
|
if (c == '\n')
|
|
{
|
|
ip = indent;
|
|
n = 0;
|
|
tp = 0;
|
|
sfputc(mp, '\n');
|
|
co = 0;
|
|
rm = margin;
|
|
ts = ip->stop;
|
|
if (*p == '\n')
|
|
{
|
|
while (*++p == '\n');
|
|
if ((style == STYLE_man || style == STYLE_html) && (!head || *p != ' ' && *p != '\t'))
|
|
{
|
|
if (style == STYLE_man)
|
|
p--;
|
|
else
|
|
sfprintf(mp, "<P>\n");
|
|
}
|
|
}
|
|
head = *p != ' ' && *p != '\t';
|
|
if (style == STYLE_html && (*p != '<' || !strneq(p, "<BR>", 4) && !strneq(p, "<P>", 3)))
|
|
{
|
|
y = p;
|
|
while (*p == '\t')
|
|
p++;
|
|
if (*p == '\n')
|
|
continue;
|
|
j = p - y;
|
|
if (j > pt->level)
|
|
{
|
|
pt++;
|
|
pt->level = j;
|
|
pt->id = TAG_NONE;
|
|
for (y = p; *y && *y != '\n'; y++)
|
|
if (*y == '\t')
|
|
{
|
|
pt->id = TAG_DL;
|
|
sfprintf(mp, "<DL>\n");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
while (j < pt->level && pt > ptstk)
|
|
{
|
|
sfprintf(mp, "%s", end[pt->id]);
|
|
pt--;
|
|
}
|
|
if (pt->id == TAG_DL)
|
|
{
|
|
dt = p;
|
|
sfprintf(mp, "<DT>");
|
|
}
|
|
else
|
|
dt = 0;
|
|
}
|
|
}
|
|
else if (c == '\t')
|
|
{
|
|
if (style == STYLE_html)
|
|
{
|
|
while (*p == '\t')
|
|
p++;
|
|
if (*p != '\n')
|
|
{
|
|
co += sfprintf(mp, "<DD>");
|
|
if (dt)
|
|
{
|
|
c = 0;
|
|
m = 0;
|
|
for (;;)
|
|
{
|
|
switch (*dt++)
|
|
{
|
|
case '\t':
|
|
break;
|
|
case '<':
|
|
c = '>';
|
|
continue;
|
|
case '>':
|
|
if (c == '>')
|
|
c = 0;
|
|
else
|
|
m++;
|
|
continue;
|
|
case '&':
|
|
c = ';';
|
|
continue;
|
|
case ';':
|
|
if (c == ';')
|
|
c = 0;
|
|
m++;
|
|
continue;
|
|
default:
|
|
if (!c)
|
|
m++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (m >= 5)
|
|
co += sfprintf(mp, "<BR>");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ip+1)->stop)
|
|
{
|
|
do
|
|
{
|
|
ip++;
|
|
if (*p != '\t')
|
|
break;
|
|
p++;
|
|
} while ((ip+1)->stop);
|
|
if (*p == '\n')
|
|
continue;
|
|
ts = ip->stop;
|
|
if (co >= ts)
|
|
{
|
|
sfputc(mp, '\n');
|
|
co = 0;
|
|
rm = margin;
|
|
ts = ip->stop;
|
|
}
|
|
}
|
|
while (co < ts)
|
|
{
|
|
sfputc(mp, ' ');
|
|
co++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (c == ' ' && !n)
|
|
{
|
|
if (co >= rm)
|
|
tp = 0;
|
|
else
|
|
{
|
|
tp = sfstrtell(mp);
|
|
pp = p;
|
|
}
|
|
if (style == STYLE_nroff && !co)
|
|
continue;
|
|
}
|
|
else if (style == STYLE_html)
|
|
{
|
|
if (c == '<')
|
|
{
|
|
if (strneq(p, "NOBR>", 5))
|
|
n++;
|
|
else if (n && strneq(p, "/NOBR>", 6) && !--n)
|
|
{
|
|
for (y = p += 6; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++)
|
|
if (c == '[')
|
|
sfputr(mp, "[", -1);
|
|
else if (c == ']')
|
|
sfputr(mp, "]", -1);
|
|
else
|
|
sfputc(mp, c);
|
|
sfwrite(mp, "</NOBR", 6);
|
|
c = '>';
|
|
co += p - y + 6;
|
|
}
|
|
}
|
|
else if (c == '>' && !n)
|
|
{
|
|
for (y = --p; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++)
|
|
if (c == '[')
|
|
sfputr(mp, "[", -1);
|
|
else if (c == ']')
|
|
sfputr(mp, "]", -1);
|
|
else
|
|
sfputc(mp, c);
|
|
c = *sfstrseek(mp, -1, SEEK_CUR);
|
|
if (p > y + 1)
|
|
{
|
|
tp = 0;
|
|
co += p - y - 1;
|
|
}
|
|
if (co >= rm)
|
|
tp = 0;
|
|
else
|
|
{
|
|
tp = sfstrtell(mp);
|
|
pp = p;
|
|
}
|
|
}
|
|
else if (c == '[')
|
|
{
|
|
sfputr(mp, "[", -1);
|
|
c = ';';
|
|
}
|
|
else if (c == ']')
|
|
{
|
|
sfputr(mp, "]", -1);
|
|
c = ';';
|
|
}
|
|
else if (c == 'h')
|
|
{
|
|
y = p;
|
|
if (*y++ == 't' && *y++ == 't' && *y++ == 'p' && (*y == ':' || *y++ == 's' && *y == ':') && *y++ == ':' && *y++ == '/' && *y++ == '/')
|
|
{
|
|
while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.')
|
|
y++;
|
|
if (*y == '?')
|
|
while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.' || *y == '?' || *y == '=' || *y == '%' || *y == '&' || *y == ';' || *y == '#')
|
|
y++;
|
|
if (*(y - 1) == '.')
|
|
y--;
|
|
p--;
|
|
sfprintf(mp, "<A href=\"%-.*s\">%-.*s</A", y - p, p, y - p, p);
|
|
p = y;
|
|
c = '>';
|
|
}
|
|
}
|
|
else if (c == 'C')
|
|
{
|
|
y = p;
|
|
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++ == ')')
|
|
{
|
|
sfputr(mp, "Copyright ©", -1);
|
|
p = y;
|
|
c = ';';
|
|
}
|
|
}
|
|
}
|
|
else if (c == ']')
|
|
{
|
|
if (n)
|
|
n--;
|
|
}
|
|
else if (c == '[')
|
|
n++;
|
|
if (c == CC_esc)
|
|
{
|
|
sfputc(mp, c);
|
|
do
|
|
{
|
|
if (!(c = *p++))
|
|
{
|
|
p--;
|
|
break;
|
|
}
|
|
sfputc(mp, c);
|
|
} while (c < 'a' || c > 'z');
|
|
}
|
|
else if (co++ >= rm && !n)
|
|
{
|
|
if (tp)
|
|
{
|
|
if (*sfstrseek(mp, tp, SEEK_SET) != ' ')
|
|
sfstrseek(mp, 1, SEEK_CUR);
|
|
tp = 0;
|
|
p = pp;
|
|
n = 0;
|
|
}
|
|
else if (c != ' ' && c != '\n')
|
|
sfputc(mp, c);
|
|
if (*p == ' ')
|
|
p++;
|
|
if (*p != '\n')
|
|
{
|
|
sfputc(mp, '\n');
|
|
for (co = 0; co < ts; co++)
|
|
sfputc(mp, ' ');
|
|
rm = margin;
|
|
}
|
|
}
|
|
else
|
|
sfputc(mp, c);
|
|
}
|
|
}
|
|
for (d = sfstrbase(mp), t = sfstrseek(mp, 0, SEEK_CUR); t > d && ((c = *(t - 1)) == '\n' || c == '\r' || c == ' ' || c == '\t'); t--);
|
|
sfstrseek(mp, t - d, SEEK_SET);
|
|
if (style == STYLE_html)
|
|
{
|
|
sfprintf(mp, "\n");
|
|
while (pt > ptstk)
|
|
{
|
|
sfprintf(mp, "%s", end[pt->id]);
|
|
pt--;
|
|
}
|
|
sfprintf(mp, "</DIV>\n</BODY>\n</HTML>");
|
|
}
|
|
}
|
|
else
|
|
sfputr(mp, p, 0);
|
|
if (!(p = sfstruse(mp)))
|
|
goto nospace;
|
|
if (sp)
|
|
sfclose(sp);
|
|
return opt_info.msg = p;
|
|
nospace:
|
|
s = T(NiL, ID, "[* out of space *]");
|
|
nope:
|
|
if (psp)
|
|
pop(psp);
|
|
if (sp_help)
|
|
sfclose(sp_help);
|
|
if (sp_text)
|
|
sfclose(sp_text);
|
|
if (sp_plus)
|
|
sfclose(sp_plus);
|
|
if (sp_info)
|
|
sfclose(sp_info);
|
|
if (sp_head)
|
|
sfclose(sp_head);
|
|
if (sp_body)
|
|
sfclose(sp_body);
|
|
if (sp_misc)
|
|
sfclose(sp_misc);
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* compatibility wrapper to opthelp()
|
|
*/
|
|
|
|
char*
|
|
optusage(const char* opts)
|
|
{
|
|
return opthelp(opts, NiL);
|
|
}
|
|
|
|
/*
|
|
* convert number using strtonll() *except* that
|
|
* 0*[[:digit:]].* is treated as [[:digit:]].*
|
|
* i.e., it looks octal but isn't, to meet
|
|
* posix Utility Argument Syntax -- use
|
|
* 0x.* or <base>#* for alternate bases
|
|
*/
|
|
|
|
static intmax_t
|
|
optnumber(const char* s, char** t, int* e)
|
|
{
|
|
intmax_t n;
|
|
int oerrno;
|
|
|
|
while (*s == '0' && isdigit(*(s + 1)))
|
|
s++;
|
|
oerrno = errno;
|
|
errno = 0;
|
|
n = strtonll(s, t, NiL, 0);
|
|
if (e)
|
|
*e = errno;
|
|
errno = oerrno;
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* point opt_info.arg to an error/info message for opt_info.name
|
|
* p points to opts location for opt_info.name
|
|
* optget() return value is returned
|
|
*/
|
|
|
|
static int
|
|
opterror(register char* p, int err, int version, char* id, char* catalog)
|
|
{
|
|
register Sfio_t* mp;
|
|
register Sfio_t* tp;
|
|
register char* s;
|
|
register int c;
|
|
|
|
if (opt_info.num != LONG_MIN)
|
|
opt_info.num = (long)(opt_info.number = 0);
|
|
if (!p || !(mp = state.mp) && !(mp = state.mp = sfstropen()))
|
|
goto nospace;
|
|
s = *p == '-' ? p : opt_info.name;
|
|
if (*p == '!')
|
|
{
|
|
while (*s == '-')
|
|
sfputc(mp, *s++);
|
|
sfputc(mp, 'n');
|
|
sfputc(mp, 'o');
|
|
}
|
|
sfputr(mp, s, ':');
|
|
sfputc(mp, ' ');
|
|
if (*p == '#' || *p == ':')
|
|
{
|
|
if (*p == '#')
|
|
{
|
|
s = T(NiL, ID, "numeric");
|
|
sfputr(mp, s, ' ');
|
|
}
|
|
if (*(p = next(p + 1, version)) == '[')
|
|
{
|
|
p = skip(s = p + 1, ':', '?', 0, 1, 0, 0, version);
|
|
tp = X(catalog) ? state.xp : mp;
|
|
while (s < p)
|
|
{
|
|
if ((c = *s++) == '?' || c == ']')
|
|
s++;
|
|
sfputc(tp, c);
|
|
}
|
|
if (!X(catalog))
|
|
sfputc(mp, ' ');
|
|
else if (p = sfstruse(tp))
|
|
sfputr(mp, T(id, catalog, p), ' ');
|
|
else
|
|
goto nospace;
|
|
}
|
|
p = opt_info.name[2] ? C("value expected") : C("argument expected");
|
|
}
|
|
else if (*p == '*' || *p == '&')
|
|
{
|
|
sfputr(mp, opt_info.arg, ':');
|
|
sfputc(mp, ' ');
|
|
p = *p == '&' ? C("ambiguous option argument value") : C("unknown option argument value");
|
|
}
|
|
else if (*p == '=' || *p == '!')
|
|
p = C("value not expected");
|
|
else if (*p == '?')
|
|
p = *(p + 1) == '?' ? C("optget: option not supported") : C("ambiguous option");
|
|
else if (*p == '+')
|
|
p = C("section not found");
|
|
else
|
|
{
|
|
if (opt_info.option[0] != '?' && opt_info.option[0] != '-' || opt_info.option[1] != '?' && opt_info.option[1] != '-')
|
|
opt_info.option[0] = 0;
|
|
p = C("unknown option");
|
|
}
|
|
p = T(NiL, ID, p);
|
|
sfputr(mp, p, -1);
|
|
if (err)
|
|
sfputr(mp, " -- out of range", -1);
|
|
if (opt_info.arg = sfstruse(mp))
|
|
return ':';
|
|
nospace:
|
|
opt_info.arg = T(NiL, ID, "[* out of space *]");
|
|
return ':';
|
|
}
|
|
|
|
/*
|
|
* argv: command line argv where argv[0] is command name
|
|
*
|
|
* opts: option control string
|
|
*
|
|
* '[' [flag][=][index][:<long-name>[|<alias-name>...]['?'description]] ']'
|
|
* long option name, index, description; -index returned
|
|
* ':' option takes string arg
|
|
* '#' option takes numeric arg (concat option may follow)
|
|
* '?' (option) following options not in usage
|
|
* (following # or :) optional arg
|
|
* '[' '[' ... ] ... '[' ... ']' ']'
|
|
* mutually exclusive option grouping
|
|
* '[' name [:attr]* [?description] ']'
|
|
* (following # or :) optional option arg description
|
|
* '\n'[' '|'\t']* ignored for legibility
|
|
* ' ' ... optional argument(s) description (to end of string)
|
|
* or after blank line
|
|
* ']]' literal ']' within '[' ... ']'
|
|
*
|
|
* return:
|
|
* 0 no more options
|
|
* '?' usage: opt_info.arg points to message sans
|
|
* `Usage: command '
|
|
* ':' error: opt_info.arg points to message sans `command: '
|
|
*
|
|
* ':' '#' ' ' '[' ']'
|
|
* invalid option chars
|
|
*
|
|
* -- terminates option list and returns 0
|
|
*
|
|
* + as first opts char makes + equivalent to -
|
|
*
|
|
* if any # option is specified then numeric options (e.g., -123)
|
|
* are associated with the leftmost # option in opts
|
|
*
|
|
* usage info in placed opt_info.arg when '?' returned
|
|
* see help_text[] (--???) for more info
|
|
*/
|
|
|
|
int
|
|
optget(register char** argv, const char* oopts)
|
|
{
|
|
register int c;
|
|
register char* s;
|
|
char* a;
|
|
char* b;
|
|
char* e;
|
|
char* f;
|
|
char* g;
|
|
char* v;
|
|
char* w;
|
|
char* p;
|
|
char* q;
|
|
char* t;
|
|
char* y;
|
|
char* numopt;
|
|
char* opts;
|
|
char* id;
|
|
char* catalog;
|
|
int n;
|
|
int m;
|
|
int k;
|
|
int j;
|
|
int x;
|
|
int err;
|
|
int no;
|
|
int nov;
|
|
int num;
|
|
int numchr;
|
|
int prefix;
|
|
int version;
|
|
Help_t* hp;
|
|
Push_t* psp;
|
|
Push_t* tsp;
|
|
Sfio_t* vp;
|
|
Sfio_t* xp;
|
|
Optcache_t* cache;
|
|
Optcache_t* pcache;
|
|
Optpass_t* pass;
|
|
|
|
#if !_PACKAGE_astsa && !_YOU_FIGURED_OUT_HOW_TO_GET_ALL_DLLS_TO_DO_THIS_
|
|
/*
|
|
* these are not initialized by all dlls!
|
|
*/
|
|
|
|
extern Error_info_t _error_info_;
|
|
extern Opt_t _opt_info_;
|
|
|
|
if (!_error_infop_)
|
|
_error_infop_ = &_error_info_;
|
|
if (!_opt_infop_)
|
|
_opt_infop_ = &_opt_info_;
|
|
#endif
|
|
if (!oopts)
|
|
return 0;
|
|
state.pindex = opt_info.index;
|
|
state.poffset = opt_info.offset;
|
|
if (!opt_info.index)
|
|
{
|
|
opt_info.index = 1;
|
|
opt_info.offset = 0;
|
|
if (state.npass)
|
|
{
|
|
state.npass = 0;
|
|
state.join = 0;
|
|
}
|
|
}
|
|
if (!argv)
|
|
cache = 0;
|
|
else
|
|
for (pcache = 0, cache = state.cache; cache; pcache = cache, cache = cache->next)
|
|
if (cache->pass.oopts == (char*)oopts)
|
|
break;
|
|
if (cache)
|
|
{
|
|
if (pcache)
|
|
{
|
|
pcache->next = cache->next;
|
|
cache->next = state.cache;
|
|
state.cache = cache;
|
|
}
|
|
pass = &cache->pass;
|
|
state.npass = -1;
|
|
}
|
|
else
|
|
{
|
|
if (!argv)
|
|
n = state.npass ? state.npass : 1;
|
|
else if ((n = state.join - 1) < 0)
|
|
n = 0;
|
|
if (n >= state.npass || state.pass[n].oopts != (char*)oopts)
|
|
{
|
|
for (m = 0; m < state.npass && state.pass[m].oopts != (char*)oopts; m++);
|
|
if (m < state.npass)
|
|
n = m;
|
|
else
|
|
{
|
|
if (n >= elementsof(state.pass))
|
|
n = elementsof(state.pass) - 1;
|
|
init((char*)oopts, &state.pass[n]);
|
|
if (state.npass <= n)
|
|
state.npass = n + 1;
|
|
}
|
|
}
|
|
if (!argv)
|
|
return 0;
|
|
pass = &state.pass[n];
|
|
}
|
|
opts = pass->opts;
|
|
prefix = pass->prefix;
|
|
version = pass->version;
|
|
id = pass->id;
|
|
if (!(xp = state.xp) || (catalog = pass->catalog) && !X(catalog))
|
|
catalog = 0;
|
|
else /* if (!error_info.catalog) */
|
|
error_info.catalog = catalog;
|
|
again:
|
|
psp = 0;
|
|
|
|
/*
|
|
* check if any options remain and determine if the
|
|
* next option is short or long
|
|
*/
|
|
|
|
opt_info.assignment = 0;
|
|
nov = no = num = 1;
|
|
e = w = v = 0;
|
|
n = x = 0;
|
|
for (;;)
|
|
{
|
|
if (!opt_info.offset)
|
|
{
|
|
/*
|
|
* finished with the previous arg
|
|
*/
|
|
|
|
if (opt_info.index == 1 && opt_info.argv != state.strv)
|
|
{
|
|
opt_info.argv = 0;
|
|
state.argv[0] = 0;
|
|
if (argv[0] && (state.argv[0] = save(argv[0], strlen(argv[0]), 0, 0, 0, 0)))
|
|
opt_info.argv = state.argv;
|
|
state.style = STYLE_short;
|
|
}
|
|
if (!(s = argv[opt_info.index]))
|
|
return 0;
|
|
if (!prefix)
|
|
{
|
|
/*
|
|
* long with no prefix (dd style)
|
|
*/
|
|
|
|
n = 2;
|
|
if ((c = *s) != '-' && c != '+')
|
|
c = '-';
|
|
else if (*++s == c)
|
|
{
|
|
if (!*++s)
|
|
{
|
|
opt_info.index++;
|
|
return 0;
|
|
}
|
|
else if (*s == c)
|
|
return 0;
|
|
}
|
|
else if (*s == '?')
|
|
n = 1;
|
|
}
|
|
else if ((c = *s++) != '-' && (c != '+' || !(pass->flags & OPT_plus) && (!(pass->flags & OPT_numeric) || !isdigit(*s))))
|
|
{
|
|
if (!(pass->flags & OPT_old) || !isalpha(c))
|
|
return 0;
|
|
s--;
|
|
n = 1;
|
|
opt_info.offset--;
|
|
}
|
|
else if (*s == c)
|
|
{
|
|
if (!*++s)
|
|
{
|
|
/*
|
|
* -- or ++ end of options
|
|
*/
|
|
|
|
opt_info.index++;
|
|
return 0;
|
|
}
|
|
else if (*s == c)
|
|
{
|
|
/*
|
|
* ---* or +++* are operands
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
if (version || *s == '?' || !(pass->flags & OPT_minus))
|
|
{
|
|
/*
|
|
* long with double prefix
|
|
*/
|
|
|
|
n = 2;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* short option char '-'
|
|
*/
|
|
|
|
s--;
|
|
n = 1;
|
|
}
|
|
}
|
|
else if (prefix == 1 && *s != '?')
|
|
{
|
|
/*
|
|
* long with single prefix (find style)
|
|
*/
|
|
|
|
n = 2;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* short (always with single prefix)
|
|
*/
|
|
|
|
n = 1;
|
|
}
|
|
|
|
/*
|
|
* just a prefix is an option (e.g., `-' == stdin)
|
|
*/
|
|
|
|
if (!*s)
|
|
return 0;
|
|
if (c == '+')
|
|
opt_info.arg = 0;
|
|
if (n == 2)
|
|
{
|
|
x = 0;
|
|
state.style = STYLE_long;
|
|
opt_info.option[0] = opt_info.name[0] = opt_info.name[1] = c;
|
|
w = &opt_info.name[prefix];
|
|
if ((*s == 'n' || *s == 'N') && (*(s + 1) == 'o' || *(s + 1) == 'O') && *(s + 2) && *(s + 2) != '=')
|
|
no = *(s + 2) == '-' ? 3 : 2;
|
|
else
|
|
no = 0;
|
|
for (c = *s; *s; s++)
|
|
{
|
|
if (*s == '=')
|
|
{
|
|
if (*(s + 1) == '=')
|
|
s++;
|
|
if (!isalnum(*(s - 1)) && *(w - 1) == (opt_info.assignment = *(s - 1)))
|
|
w--;
|
|
v = ++s;
|
|
break;
|
|
}
|
|
if (w < &opt_info.name[elementsof(opt_info.name) - 1] && *s != ':' && *s != '|' && *s != '[' && *s != ']')
|
|
*w++ = *s;
|
|
}
|
|
*w = 0;
|
|
w = &opt_info.name[prefix];
|
|
c = *w;
|
|
opt_info.offset = 0;
|
|
opt_info.index++;
|
|
break;
|
|
}
|
|
opt_info.offset++;
|
|
}
|
|
if (!argv[opt_info.index])
|
|
return 0;
|
|
if (c = argv[opt_info.index][opt_info.offset++])
|
|
{
|
|
if ((k = argv[opt_info.index][0]) != '-' && k != '+')
|
|
k = '-';
|
|
opt_info.option[0] = opt_info.name[0] = k;
|
|
opt_info.option[1] = opt_info.name[1] = c;
|
|
opt_info.option[2] = opt_info.name[2] = 0;
|
|
break;
|
|
}
|
|
opt_info.offset = 0;
|
|
opt_info.index++;
|
|
}
|
|
|
|
/*
|
|
* at this point:
|
|
*
|
|
* c the first character of the option
|
|
* w long option name if != 0, otherwise short
|
|
* v long option value (via =) if w != 0
|
|
*/
|
|
|
|
if (c == '?')
|
|
{
|
|
/*
|
|
* ? always triggers internal help
|
|
*/
|
|
|
|
if (!state.msgdict)
|
|
initdict();
|
|
if (w)
|
|
{
|
|
if (!v && (*(w + 1) || !(v = argv[opt_info.index]) || !++opt_info.index))
|
|
v = w + 1;
|
|
else if (w[0] != '?' || w[1])
|
|
{
|
|
s = w;
|
|
w = v;
|
|
v = s + 1;
|
|
}
|
|
}
|
|
opt_info.option[1] = c;
|
|
opt_info.option[2] = 0;
|
|
if (!w)
|
|
{
|
|
opt_info.name[1] = c;
|
|
opt_info.name[2] = 0;
|
|
}
|
|
goto help;
|
|
}
|
|
else if (w && !state.msgdict)
|
|
initdict();
|
|
numopt = 0;
|
|
f = 0;
|
|
s = opts;
|
|
|
|
/*
|
|
* no option can start with these characters
|
|
*/
|
|
|
|
if (c == ':' || c == '#' || c == ' ' || c == '[' || c == ']')
|
|
{
|
|
if (c != *s)
|
|
s = "";
|
|
}
|
|
else
|
|
{
|
|
a = 0;
|
|
if (!w && (pass->flags & OPT_cache))
|
|
{
|
|
if (cache)
|
|
{
|
|
if (k = cache->flags[map[c]])
|
|
{
|
|
opt_info.arg = 0;
|
|
|
|
/*
|
|
* this is a ksh getopts workaround
|
|
*/
|
|
|
|
if (opt_info.num != LONG_MIN)
|
|
opt_info.num = (long)(opt_info.number = !(k & OPT_cache_invert));
|
|
if (!(k & (OPT_cache_string|OPT_cache_numeric)))
|
|
return c;
|
|
if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset]))
|
|
{
|
|
if (!(k & OPT_cache_numeric))
|
|
{
|
|
opt_info.offset = 0;
|
|
return c;
|
|
}
|
|
opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
|
|
if (err || e == opt_info.arg)
|
|
{
|
|
opt_info.num = (long)(opt_info.number = 0);
|
|
if (!err && (k & OPT_cache_optional))
|
|
{
|
|
opt_info.arg = 0;
|
|
opt_info.index--;
|
|
return c;
|
|
}
|
|
}
|
|
else if (*e)
|
|
{
|
|
opt_info.offset += e - opt_info.arg;
|
|
opt_info.index--;
|
|
return c;
|
|
}
|
|
else
|
|
{
|
|
opt_info.offset = 0;
|
|
return c;
|
|
}
|
|
}
|
|
else if (opt_info.arg = argv[opt_info.index])
|
|
{
|
|
opt_info.index++;
|
|
if ((k & OPT_cache_optional) && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1))
|
|
{
|
|
opt_info.arg = 0;
|
|
opt_info.index--;
|
|
opt_info.offset = 0;
|
|
opt_info.num = (long)(opt_info.number = 0);
|
|
return c;
|
|
}
|
|
if (k & OPT_cache_string)
|
|
{
|
|
opt_info.offset = 0;
|
|
return c;
|
|
}
|
|
opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
|
|
if (!err)
|
|
{
|
|
if (!*e)
|
|
{
|
|
opt_info.offset = 0;
|
|
return c;
|
|
}
|
|
if (k & OPT_cache_optional)
|
|
{
|
|
opt_info.arg = 0;
|
|
opt_info.index--;
|
|
opt_info.offset = 0;
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
else if (k & OPT_cache_optional)
|
|
{
|
|
opt_info.offset = 0;
|
|
return c;
|
|
}
|
|
opt_info.index--;
|
|
}
|
|
cache = 0;
|
|
}
|
|
else if (cache = newof(0, Optcache_t, 1, 0))
|
|
{
|
|
cache->caching = c;
|
|
c = 0;
|
|
cache->pass = *pass;
|
|
cache->next = state.cache;
|
|
state.cache = cache;
|
|
}
|
|
}
|
|
else
|
|
cache = 0;
|
|
for (;;)
|
|
{
|
|
if (!(*(s = next(s, version))) || *s == '\n' || *s == ' ')
|
|
{
|
|
if (!(tsp = psp))
|
|
{
|
|
if (cache)
|
|
{
|
|
/*
|
|
* the first loop pass
|
|
* initialized the cache
|
|
* so one more pass to
|
|
* check the cache or
|
|
* bail for a full scan
|
|
*/
|
|
|
|
cache->flags[0] = 0;
|
|
c = cache->caching;
|
|
cache->caching = 0;
|
|
cache = 0;
|
|
s = opts;
|
|
continue;
|
|
}
|
|
if (!x && catalog)
|
|
{
|
|
/*
|
|
* the first loop pass
|
|
* translated long
|
|
* options and there
|
|
* were no matches so
|
|
* one more pass for C
|
|
* locale
|
|
*/
|
|
|
|
catalog = 0;
|
|
s = opts;
|
|
continue;
|
|
}
|
|
s = "";
|
|
break;
|
|
}
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
continue;
|
|
}
|
|
if (*s == '\f')
|
|
{
|
|
psp = info(psp, s + 1, NiL, xp, id);
|
|
if (psp->nb)
|
|
s = psp->nb;
|
|
else
|
|
{
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
}
|
|
continue;
|
|
}
|
|
message((-20, "optget: opt %s c %c w %s num %ld", show(s), c, w, num));
|
|
if (*s == c && !w)
|
|
break;
|
|
else if (*s == '[')
|
|
{
|
|
s = next(s + 1, version);
|
|
if (*s == '(')
|
|
{
|
|
s = nest(f = s);
|
|
if (!conformance(f, s - f))
|
|
goto disable;
|
|
}
|
|
k = *(f = s);
|
|
if (k == '+' || k == '-')
|
|
/* ignore */;
|
|
else if (k == '[' || version < 1)
|
|
continue;
|
|
else if (w && !cache)
|
|
{
|
|
nov = no;
|
|
if (*(s + 1) == '\f' && (vp = state.vp))
|
|
{
|
|
sfputc(vp, k);
|
|
s = expand(s + 2, NiL, &t, vp, id);
|
|
if (*s)
|
|
*(f = s - 1) = k;
|
|
else
|
|
{
|
|
f = sfstrbase(vp);
|
|
if (s = strrchr(f, ':'))
|
|
f = s - 1;
|
|
else
|
|
s = f + 1;
|
|
}
|
|
}
|
|
else
|
|
t = 0;
|
|
if (*s != ':')
|
|
s = skip(s, ':', '?', 0, 1, 0, 0, version);
|
|
if (*s == ':')
|
|
{
|
|
if (catalog)
|
|
{
|
|
p = skip(s + 1, '?', 0, 0, 1, 0, 0, version);
|
|
e = sfprints("%-.*s", p - (s + 1), s + 1);
|
|
g = T(id, catalog, e);
|
|
if (g == e)
|
|
p = 0;
|
|
else
|
|
{
|
|
sfprintf(xp, ":%s|%s?", g, e);
|
|
if (!(s = sfstruse(xp)))
|
|
goto nospace;
|
|
}
|
|
}
|
|
else
|
|
p = 0;
|
|
y = w;
|
|
for (;;)
|
|
{
|
|
n = m = 0;
|
|
e = s + 1;
|
|
while (*++s)
|
|
{
|
|
if (*s == '*' || *s == '\a')
|
|
{
|
|
if (*s == '\a')
|
|
do
|
|
{
|
|
if (!*++s)
|
|
{
|
|
s--;
|
|
break;
|
|
}
|
|
} while (*s != '\a');
|
|
j = *(s + 1);
|
|
if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
|
|
{
|
|
while (*w)
|
|
w++;
|
|
m = 0;
|
|
break;
|
|
}
|
|
m = 1;
|
|
}
|
|
else if (*s == *w || SEP(*s) && SEP(*w))
|
|
w++;
|
|
else if (*w == 0)
|
|
break;
|
|
else if (!SEP(*s))
|
|
{
|
|
if (SEP(*w))
|
|
{
|
|
if (*++w == *s)
|
|
{
|
|
w++;
|
|
continue;
|
|
}
|
|
}
|
|
else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
|
|
break;
|
|
for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
|
|
if (!SEP(*q))
|
|
break;
|
|
for (s = q; w > y && *w != *(s + 1); w--);
|
|
}
|
|
else if (*w != *(s + 1))
|
|
break;
|
|
}
|
|
if (!*w)
|
|
{
|
|
nov = 0;
|
|
break;
|
|
}
|
|
if (n = no)
|
|
{
|
|
m = 0;
|
|
s = e - 1;
|
|
w = y + n;
|
|
while (*++s)
|
|
{
|
|
if (*s == '*' || *s == '\a')
|
|
{
|
|
if (*s == '\a')
|
|
do
|
|
{
|
|
if (!*++s)
|
|
{
|
|
s--;
|
|
break;
|
|
}
|
|
} while (*s != '\a');
|
|
j = *(s + 1);
|
|
if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
|
|
{
|
|
while (*w)
|
|
w++;
|
|
m = 0;
|
|
break;
|
|
}
|
|
m = 1;
|
|
}
|
|
else if (*s == *w || SEP(*s) && SEP(*w))
|
|
w++;
|
|
else if (*w == 0)
|
|
break;
|
|
else if (!SEP(*s))
|
|
{
|
|
if (SEP(*w))
|
|
{
|
|
if (*++w == *s)
|
|
{
|
|
w++;
|
|
continue;
|
|
}
|
|
}
|
|
else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
|
|
break;
|
|
for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
|
|
if (!SEP(*q))
|
|
break;
|
|
for (s = q; w > y && *w != *(s + 1); w--);
|
|
}
|
|
else if (*w != *(s + 1))
|
|
break;
|
|
}
|
|
if (!*w)
|
|
break;
|
|
}
|
|
if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|')
|
|
break;
|
|
w = y;
|
|
}
|
|
if (p)
|
|
s = p;
|
|
if (!*w)
|
|
{
|
|
if (n)
|
|
num = 0;
|
|
if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']' || *s == 0)) && x)
|
|
{
|
|
psp = pop(psp);
|
|
return opterror("?", 0, version, id, catalog);
|
|
}
|
|
for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2);
|
|
if (*f == ':')
|
|
{
|
|
x = -1;
|
|
opt_info.option[1] = '-';
|
|
opt_info.option[2] = 0;
|
|
}
|
|
else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':')
|
|
{
|
|
opt_info.option[1] = x;
|
|
opt_info.option[2] = 0;
|
|
}
|
|
else
|
|
{
|
|
a = f;
|
|
if (*a == '=')
|
|
a++;
|
|
else
|
|
{
|
|
if (*(a + 1) == '!')
|
|
a++;
|
|
if (*(a + 1) == '=')
|
|
a += 2;
|
|
}
|
|
x = -strtol(a, &b, 0);
|
|
if ((b - a) > sizeof(opt_info.option) - 2)
|
|
b = a + sizeof(opt_info.option) - 2;
|
|
memcpy(&opt_info.option[1], a, b - a);
|
|
opt_info.option[b - a + 1] = 0;
|
|
}
|
|
b = e;
|
|
if (t)
|
|
{
|
|
s = t;
|
|
t = 0;
|
|
}
|
|
a = s = skip(s, 0, 0, 0, 1, 0, 0, version);
|
|
if (n)
|
|
{
|
|
w = y;
|
|
break;
|
|
}
|
|
}
|
|
w = y;
|
|
}
|
|
else if (k == c && prefix == 1)
|
|
{
|
|
w = 0;
|
|
opt_info.name[1] = c;
|
|
opt_info.name[2] = 0;
|
|
opt_info.offset = 2;
|
|
opt_info.index--;
|
|
break;
|
|
}
|
|
if (t)
|
|
{
|
|
s = t;
|
|
if (a)
|
|
a = t;
|
|
}
|
|
}
|
|
disable:
|
|
s = skip(s, 0, 0, 0, 1, 0, 1, version);
|
|
if (*s == GO)
|
|
s = skip(s + 1, 0, 0, 0, 0, 1, 1, version);
|
|
if (cache)
|
|
{
|
|
m = OPT_cache_flag;
|
|
v = s;
|
|
if (*v == '#')
|
|
{
|
|
v++;
|
|
m |= OPT_cache_numeric;
|
|
}
|
|
else if (*v == ':')
|
|
{
|
|
v++;
|
|
m |= OPT_cache_string;
|
|
}
|
|
if (*v == '?')
|
|
{
|
|
v++;
|
|
m |= OPT_cache_optional;
|
|
}
|
|
else if (*v == *(v - 1))
|
|
v++;
|
|
if (*(v = next(v, version)) == '[')
|
|
v = skip(v + 1, 0, 0, 0, 1, 0, 1, version);
|
|
if (*v != GO)
|
|
{
|
|
v = f;
|
|
for (;;)
|
|
{
|
|
if (isdigit(*f) && isdigit(*(f + 1)))
|
|
while (isdigit(*(f + 1)))
|
|
f++;
|
|
else if (*(f + 1) == '=')
|
|
break;
|
|
else
|
|
cache->flags[map[*f]] = m;
|
|
j = 0;
|
|
while (*(f + 1) == '|')
|
|
{
|
|
f += 2;
|
|
if (!(j = *f) || j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
|
|
break;
|
|
cache->flags[map[j]] = m;
|
|
}
|
|
if (j != '!' || (m & OPT_cache_invert))
|
|
break;
|
|
f = v;
|
|
m |= OPT_cache_invert;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m = 0;
|
|
if (!w)
|
|
{
|
|
if (isdigit(*f) && isdigit(*(f + 1)))
|
|
k = -1;
|
|
if (c == k)
|
|
m = 1;
|
|
while (*(f + 1) == '|')
|
|
{
|
|
f += 2;
|
|
if (!(j = *f))
|
|
{
|
|
m = 0;
|
|
break;
|
|
}
|
|
else if (j == c)
|
|
m = 1;
|
|
else if (j == '!' || j == '=' || j == ':' || j == '?' || j == ']')
|
|
break;
|
|
}
|
|
}
|
|
if (m)
|
|
{
|
|
s--;
|
|
if (*++f == '!')
|
|
{
|
|
f++;
|
|
num = 0;
|
|
}
|
|
if (*f == '=')
|
|
{
|
|
c = -strtol(++f, &b, 0);
|
|
if ((b - f) > sizeof(opt_info.option) - 2)
|
|
b = f + sizeof(opt_info.option) - 2;
|
|
memcpy(&opt_info.option[1], f, b - f);
|
|
opt_info.option[b - f + 1] = 0;
|
|
}
|
|
else
|
|
c = k;
|
|
break;
|
|
}
|
|
}
|
|
if (*s == '#')
|
|
{
|
|
if (!numopt && s > opts)
|
|
{
|
|
numopt = s - 1;
|
|
numchr = k;
|
|
if (*f == ':')
|
|
numchr = -1;
|
|
else if (*(f + 1) != ':' && *(f + 1) != '!' && *(f + 1) != ']')
|
|
{
|
|
a = f;
|
|
if (*a == '=')
|
|
a++;
|
|
else
|
|
{
|
|
if (*(a + 1) == '!')
|
|
a++;
|
|
if (*(a + 1) == '=')
|
|
a += 2;
|
|
}
|
|
numchr = -strtol(a, NiL, 0);
|
|
}
|
|
}
|
|
}
|
|
else if (*s != ':')
|
|
continue;
|
|
}
|
|
else if (*s == ']')
|
|
{
|
|
s++;
|
|
continue;
|
|
}
|
|
else if (*s == '#')
|
|
{
|
|
if (!numopt && s > opts)
|
|
numchr = *(numopt = s - 1);
|
|
}
|
|
else if (*s != ':')
|
|
{
|
|
if (cache)
|
|
{
|
|
m = OPT_cache_flag;
|
|
if (*(s + 1) == '#')
|
|
{
|
|
m |= OPT_cache_numeric;
|
|
if (*(s + 2) == '?')
|
|
m |= OPT_cache_optional;
|
|
}
|
|
else if (*(s + 1) == ':')
|
|
{
|
|
m |= OPT_cache_string;
|
|
if (*(s + 2) == '?')
|
|
m |= OPT_cache_optional;
|
|
}
|
|
cache->flags[map[*s]] = m;
|
|
}
|
|
s++;
|
|
continue;
|
|
}
|
|
message((-21, "optget: opt %s", show(s)));
|
|
if (*++s == '?' || *s == *(s - 1))
|
|
s++;
|
|
if (*(s = next(s, version)) == '[')
|
|
{
|
|
s = skip(s + 1, 0, 0, 0, 1, 0, 1, version);
|
|
if (*s == GO)
|
|
s = skip(s + 1, 0, 0, 0, 0, 1, 1, version);
|
|
}
|
|
message((-21, "optget: opt %s", show(s)));
|
|
}
|
|
if (w && x)
|
|
{
|
|
s = skip(b, '|', '?', 0, 1, 0, 0, version);
|
|
if (v && (a == 0 || *a == 0 || *(a + 1) != ':' && *(a + 1) != '#') && (*v == '0' || *v == '1') && !*(v + 1))
|
|
{
|
|
if (*v == '0')
|
|
num = !num;
|
|
v = 0;
|
|
}
|
|
if ((s - b) >= elementsof(opt_info.name))
|
|
s = b + elementsof(opt_info.name) - 1;
|
|
for (;;)
|
|
{
|
|
if (b >= s)
|
|
{
|
|
*w = 0;
|
|
break;
|
|
}
|
|
if (*b == '*')
|
|
break;
|
|
*w++ = *b++;
|
|
}
|
|
if (!num && v)
|
|
return opterror(no ? "!" : "=", 0, version, id, catalog);
|
|
w = &opt_info.name[prefix];
|
|
c = x;
|
|
s = a;
|
|
}
|
|
}
|
|
if (!*s)
|
|
{
|
|
if (w)
|
|
{
|
|
if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), w))
|
|
{
|
|
if (!v)
|
|
v = (char*)hp->name;
|
|
goto help;
|
|
}
|
|
if (!v)
|
|
{
|
|
v = opt_info.name;
|
|
goto help;
|
|
}
|
|
}
|
|
if (w || !isdigit(c) || !numopt || !(pass->flags & OPT_numeric))
|
|
{
|
|
pop(psp);
|
|
return opterror("", 0, version, id, catalog);
|
|
}
|
|
s = numopt;
|
|
c = opt_info.option[1] = numchr;
|
|
opt_info.offset--;
|
|
}
|
|
opt_info.arg = 0;
|
|
|
|
/*
|
|
* this is a ksh getopts workaround
|
|
*/
|
|
|
|
if (opt_info.num != LONG_MIN)
|
|
opt_info.num = (long)(opt_info.number = num);
|
|
if ((n = *++s == '#') || *s == ':' || w && !nov && v && (optnumber(v, &e, NiL), n = !*e))
|
|
{
|
|
if (w)
|
|
{
|
|
if (nov)
|
|
{
|
|
if (v)
|
|
{
|
|
pop(psp);
|
|
return opterror("!", 0, version, id, catalog);
|
|
}
|
|
opt_info.num = (long)(opt_info.number = 0);
|
|
}
|
|
else
|
|
{
|
|
if (!v && *(s + 1) != '?' && (v = argv[opt_info.index]))
|
|
{
|
|
opt_info.index++;
|
|
opt_info.offset = 0;
|
|
}
|
|
if (!(opt_info.arg = v) || (*v == '0' || *v == '1') && !*(v + 1))
|
|
{
|
|
if (*(s + 1) != '?')
|
|
{
|
|
if (!opt_info.arg)
|
|
{
|
|
pop(psp);
|
|
return opterror(s, 0, version, id, catalog);
|
|
}
|
|
}
|
|
else if (*(t = next(s + 2, version)) == '[')
|
|
while (*(t = skip(t, ':', 0, 0, 1, 0, 0, version)) == ':')
|
|
if (*++t == '!')
|
|
{
|
|
if (!v || *v == '1')
|
|
{
|
|
e = skip(t, ':', '?', ']', 1, 0, 0, version);
|
|
opt_info.arg = sfprints("%-.*s", e - t - 1, t + 1);
|
|
}
|
|
else
|
|
{
|
|
opt_info.arg = 0;
|
|
opt_info.num = (long)(opt_info.number = 0);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (opt_info.arg && n)
|
|
{
|
|
opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
|
|
if (err || e == opt_info.arg)
|
|
{
|
|
pop(psp);
|
|
return opterror(s, err, version, id, catalog);
|
|
}
|
|
}
|
|
}
|
|
goto optarg;
|
|
}
|
|
else if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset]))
|
|
{
|
|
if (*s == '#')
|
|
{
|
|
opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
|
|
if (err || e == opt_info.arg)
|
|
{
|
|
if (!err && *(s + 1) == '?')
|
|
{
|
|
opt_info.arg = 0;
|
|
opt_info.index--;
|
|
}
|
|
else
|
|
{
|
|
opt_info.offset = 0;
|
|
c = opterror(s, err, version, id, catalog);
|
|
}
|
|
pop(psp);
|
|
return c;
|
|
}
|
|
else if (*e)
|
|
{
|
|
opt_info.offset += e - opt_info.arg;
|
|
opt_info.index--;
|
|
pop(psp);
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
else if (opt_info.arg = argv[opt_info.index])
|
|
{
|
|
opt_info.index++;
|
|
if (*(s + 1) == '?' && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1))
|
|
{
|
|
opt_info.num = (long)(opt_info.number = 0);
|
|
opt_info.index--;
|
|
opt_info.arg = 0;
|
|
}
|
|
else if (*s == '#')
|
|
{
|
|
opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err));
|
|
if (err || *e)
|
|
{
|
|
if (!err && *(s + 1) == '?')
|
|
{
|
|
opt_info.arg = 0;
|
|
opt_info.index--;
|
|
}
|
|
else
|
|
{
|
|
pop(psp);
|
|
opt_info.offset = 0;
|
|
return opterror(s, err, version, id, catalog);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (*(s + 1) != '?')
|
|
{
|
|
opt_info.index--;
|
|
pop(psp);
|
|
return opterror(s, 0, version, id, catalog);
|
|
}
|
|
opt_info.offset = 0;
|
|
optarg:
|
|
if (*s == ':' && *(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == GO && *(s = next(s + 1, version)) == '[' && isalnum(*(s + 1)))
|
|
{
|
|
x = 0;
|
|
if (opt_info.arg)
|
|
{
|
|
do
|
|
{
|
|
w = y = opt_info.arg;
|
|
f = s = next(s + 1, version);
|
|
k = *f;
|
|
if (k == *w && isalpha(k) && !*(w + 1))
|
|
{
|
|
x = k;
|
|
break;
|
|
}
|
|
if (*s == '+' || *s == '-')
|
|
continue;
|
|
else if (*s == '[' || version < 1)
|
|
continue;
|
|
else
|
|
{
|
|
if (*s != ':')
|
|
s = skip(s, ':', '?', 0, 1, 0, 0, version);
|
|
if (*s == ':')
|
|
{
|
|
if (catalog)
|
|
{
|
|
p = skip(s + 1, '?', 0, 0, 1, 0, 0, version);
|
|
e = sfprints("%-.*s", p - (s + 1), s + 1);
|
|
b = T(id, catalog, e);
|
|
if (b == e)
|
|
p = 0;
|
|
else
|
|
{
|
|
sfprintf(xp, ":%s|%s?", b, e);
|
|
if (!(s = sfstruse(xp)))
|
|
goto nospace;
|
|
}
|
|
}
|
|
else
|
|
p = 0;
|
|
for (;;)
|
|
{
|
|
n = m = 0;
|
|
e = s + 1;
|
|
while (*++s)
|
|
{
|
|
if (*s == '*' || *s == '\a')
|
|
{
|
|
if (*s == '\a')
|
|
do
|
|
{
|
|
if (!*++s)
|
|
{
|
|
s--;
|
|
break;
|
|
}
|
|
} while (*s != '\a');
|
|
j = *(s + 1);
|
|
if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0)
|
|
{
|
|
while (*w)
|
|
w++;
|
|
m = 0;
|
|
break;
|
|
}
|
|
m = 1;
|
|
}
|
|
else if (*s == *w || SEP(*s) && SEP(*w))
|
|
w++;
|
|
else if (*w == 0)
|
|
break;
|
|
else if (!SEP(*s))
|
|
{
|
|
if (SEP(*w))
|
|
{
|
|
if (*++w == *s)
|
|
{
|
|
w++;
|
|
continue;
|
|
}
|
|
}
|
|
else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w))
|
|
break;
|
|
for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++);
|
|
if (!SEP(*q))
|
|
break;
|
|
for (s = q; w > y && *w != *(s + 1); w--);
|
|
}
|
|
else if (*w != *(s + 1))
|
|
break;
|
|
}
|
|
if (!*w)
|
|
{
|
|
nov = 0;
|
|
break;
|
|
}
|
|
if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|')
|
|
break;
|
|
w = y;
|
|
}
|
|
if (p)
|
|
s = p;
|
|
if (!*w)
|
|
{
|
|
if (n)
|
|
num = 0;
|
|
if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']')) && x)
|
|
{
|
|
pop(psp);
|
|
return opterror("&", 0, version, id, catalog);
|
|
}
|
|
for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2);
|
|
if (*f == ':')
|
|
x = -1;
|
|
else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':')
|
|
/* ok */;
|
|
else
|
|
{
|
|
a = f;
|
|
if (*a == '=')
|
|
a++;
|
|
else
|
|
{
|
|
if (*(a + 1) == '!')
|
|
a++;
|
|
if (*(a + 1) == '=')
|
|
a += 2;
|
|
}
|
|
x = -strtol(a, &b, 0);
|
|
}
|
|
b = e;
|
|
a = s = skip(s, 0, 0, 0, 1, 0, 0, version);
|
|
if (n)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while (*(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == '[');
|
|
if (!(opt_info.num = (long)(opt_info.number = x)))
|
|
{
|
|
pop(psp);
|
|
return opterror("*", 0, version, id, catalog);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (w && v)
|
|
{
|
|
pop(psp);
|
|
return opterror("=", 0, version, id, catalog);
|
|
}
|
|
else
|
|
{
|
|
opt_info.num = (long)(opt_info.number = num);
|
|
if (!w && !argv[opt_info.index][opt_info.offset])
|
|
{
|
|
opt_info.offset = 0;
|
|
opt_info.index++;
|
|
}
|
|
}
|
|
pop(psp);
|
|
return c;
|
|
help:
|
|
if (v && *v == '?' && *(v + 1) == '?' && *(v + 2))
|
|
{
|
|
s = v + 2;
|
|
if ((s[0] == 'n' || s[0] == 'N') && (s[1] == 'o' || s[1] == 'O'))
|
|
{
|
|
s += 2;
|
|
n = -1;
|
|
}
|
|
else
|
|
n = 1;
|
|
if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), s))
|
|
{
|
|
if (hp->style < STYLE_man || !(s = argv[opt_info.index]) || s[0] != '-' || s[1] != '-' || !s[2])
|
|
{
|
|
opt_info.arg = sfprints("\fversion=%d", version);
|
|
pop(psp);
|
|
return '?';
|
|
}
|
|
state.force = hp->style;
|
|
}
|
|
else if (match(s, "CONFORMANCE", -1, ID, NiL))
|
|
{
|
|
opt_info.arg = sfprints("\f%s", conformance(w, 0));
|
|
pop(psp);
|
|
return '?';
|
|
}
|
|
else if (match(s, "ESC", -1, ID, NiL) || match(s, "EMPHASIS", -1, ID, NiL))
|
|
state.emphasis = n;
|
|
else if (match(s, "MAN", -1, ID, NiL))
|
|
{
|
|
opt_info.arg = sfprints("\f%s", secname(*w != '?' ? w : pass->section));
|
|
pop(psp);
|
|
return '?';
|
|
}
|
|
else if (match(s, "PREFORMAT", -1, ID, NiL))
|
|
state.flags |= OPT_preformat;
|
|
else if (match(s, "SECTION", -1, ID, NiL))
|
|
{
|
|
opt_info.arg = sfprints("\f%s", pass->section);
|
|
pop(psp);
|
|
return '?';
|
|
}
|
|
else if (match(s, "TEST", -1, ID, NiL))
|
|
{
|
|
state.width = OPT_WIDTH;
|
|
state.emphasis = 1;
|
|
}
|
|
else
|
|
{
|
|
pop(psp);
|
|
return opterror(v, 0, version, id, catalog);
|
|
}
|
|
psp = pop(psp);
|
|
if (argv == state.strv)
|
|
return '#';
|
|
goto again;
|
|
}
|
|
if ((opt_info.arg = opthelp(NiL, v)) == (char*)unknown)
|
|
{
|
|
pop(psp);
|
|
return opterror(v, 0, version, id, catalog);
|
|
}
|
|
pop(psp);
|
|
return '?';
|
|
nospace:
|
|
pop(psp);
|
|
return opterror(NiL, 0, 0, NiL, NiL);
|
|
}
|
|
|
|
/*
|
|
* parse long options with 0,1,2 leading '-' or '+' from string and pass to optget()
|
|
* syntax is the unquoted
|
|
*
|
|
* <length> [-|+|--|++]<name>[[-+:|&=]=<value>\n (or \0 for the last)
|
|
*
|
|
* or the quoted
|
|
*
|
|
* [-|+|--|++][no]name[[-+:|&=]=['"{(]value[)}"']][, ]...
|
|
*
|
|
* with \x escapes passed to chresc()
|
|
*
|
|
* return '#' for `label:', with opt_info.name==label
|
|
* str[opt_info.offset] next arg
|
|
*
|
|
* optstr(s, 0)
|
|
* return '-' if arg, 0 otherwise
|
|
* optstr(0, opts)
|
|
* use previous parsed str
|
|
*/
|
|
|
|
int
|
|
optstr(const char* str, const char* opts)
|
|
{
|
|
register char* s = (char*)str;
|
|
register Sfio_t* mp;
|
|
register int c;
|
|
register int ql;
|
|
register int qr;
|
|
register int qc;
|
|
int v;
|
|
char* e;
|
|
|
|
again:
|
|
if (s)
|
|
{
|
|
if (!(mp = state.strp) && !(mp = state.strp = sfstropen()))
|
|
return 0;
|
|
if (state.str != s)
|
|
state.str = s;
|
|
else if (opt_info.index == 1)
|
|
s += opt_info.offset;
|
|
while (*s == ',' || *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
|
|
s++;
|
|
if (!*s)
|
|
{
|
|
state.str = 0;
|
|
return 0;
|
|
}
|
|
if (*s == '-' || *s == '+')
|
|
{
|
|
c = *s++;
|
|
sfputc(mp, c);
|
|
if (*s == c)
|
|
{
|
|
sfputc(mp, c);
|
|
s++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sfputc(mp, '-');
|
|
sfputc(mp, '-');
|
|
}
|
|
if (isdigit(*s) && (v = (int)strtol(s, &e, 10)) > 1 && isspace(*e) && --v <= strlen(s) && (s[v] == 0 || s[v] == '\n'))
|
|
{
|
|
s += v;
|
|
while (isspace(*++e));
|
|
sfwrite(mp, e, s - e);
|
|
}
|
|
else
|
|
{
|
|
while (*s && *s != ',' && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' && *s != '=' && *s != ':')
|
|
sfputc(mp, *s++);
|
|
if ((c = *s) == ':' && *(s + 1) != '=')
|
|
{
|
|
opt_info.index = 1;
|
|
opt_info.offset = ++s - (char*)str;
|
|
if (!(s = sfstruse(mp)))
|
|
goto nospace;
|
|
s += 2;
|
|
e = opt_info.name;
|
|
while (e < &opt_info.name[sizeof(opt_info.name)-1] && (*e++ = *s++));
|
|
opt_info.arg = 0;
|
|
opt_info.num = (long)(opt_info.number = 0);
|
|
opt_info.option[0] = ':';
|
|
opt_info.option[1] = 0;
|
|
return '#';
|
|
}
|
|
if (c == ':' || c == '=')
|
|
{
|
|
sfputc(mp, c);
|
|
ql = qr = 0;
|
|
while (c = *++s)
|
|
{
|
|
if (c == '\\')
|
|
{
|
|
sfputc(mp, chresc(s, &e));
|
|
s = e - 1;
|
|
}
|
|
else if (c == qr)
|
|
{
|
|
if (qr != ql)
|
|
sfputc(mp, c);
|
|
if (--qc <= 0)
|
|
qr = ql = 0;
|
|
}
|
|
else if (c == ql)
|
|
{
|
|
sfputc(mp, c);
|
|
qc++;
|
|
}
|
|
else if (qr)
|
|
sfputc(mp, c);
|
|
else if (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
|
break;
|
|
else if (c == '"' || c == '\'')
|
|
{
|
|
ql = qr = c;
|
|
qc = 1;
|
|
}
|
|
else
|
|
{
|
|
sfputc(mp, c);
|
|
if (c == GO)
|
|
{
|
|
ql = c;
|
|
qr = OG;
|
|
qc = 1;
|
|
}
|
|
else if (c == '(')
|
|
{
|
|
ql = c;
|
|
qr = ')';
|
|
qc = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
opt_info.argv = state.strv;
|
|
state.strv[0] = T(NiL, ID, "option");
|
|
if (!(state.strv[1] = sfstruse(mp)))
|
|
goto nospace;
|
|
state.strv[2] = 0;
|
|
opt_info.offset = s - (char*)str;
|
|
}
|
|
if (opts)
|
|
{
|
|
if (!state.strv[1])
|
|
{
|
|
state.str = 0;
|
|
return 0;
|
|
}
|
|
opt_info.index = 1;
|
|
v = opt_info.offset;
|
|
opt_info.offset = 0;
|
|
c = optget(state.strv, opts);
|
|
opt_info.index = 1;
|
|
opt_info.offset = v;
|
|
if (c == '#')
|
|
{
|
|
s = state.str;
|
|
goto again;
|
|
}
|
|
if ((c == '?' || c == ':') && (opt_info.arg[0] == '-' && opt_info.arg[1] == '-'))
|
|
opt_info.arg += 2;
|
|
s = opt_info.name;
|
|
if (*s++ == '-' && *s++ == '-' && *s)
|
|
{
|
|
e = opt_info.name;
|
|
while (*e++ = *s++);
|
|
}
|
|
}
|
|
else
|
|
c = '-';
|
|
return c;
|
|
nospace:
|
|
return opterror(NiL, 0, 0, NiL, NiL);
|
|
}
|