mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Notable changes: - Tie up some loose ends re:3de4da5a
and7ba2c685
. - comp/omitted.c: Header include fix for Cygwin. - misc/optget.c: - args(): When printing options for the uage line, use a local pointer for the 'if' block instead of reusing the 'b' pointer. That variable is used to output blanks later. - The above fix allows re-enabling the AST translation-aware macros and deleting the astsa fallback without causing usage message corruption in multibyte locales. Maybe someday we'll make ksh actually translatable. - Remove code to reinitialise _error_infop_ and _opt_info_ 'because these are not initialised by all DLLs'. In 2022, hopefully the buggy dynamic linkers are fixed. If not, we're not going to find out by keeping the workaround. I suspect that those bugs may have been triggered by the Microsoft/Cygwin import/export obfuscation removed in3de4da5a
. - ksh93: - Remove unused sh.st.var_local variable. This was a leftover of a 93v- attempt to implement the bash 'local' command. It used static scoping, so it's not actually compatible. - Add a few regression tests for miscellaneous breakage that I caused in experiments (the breakage never made it to git; the tests are just to keep it that way).
5718 lines
114 KiB
C
5718 lines
114 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1985-2012 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2022 Contributors to ksh 93u+m *
|
|
* 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> *
|
|
* *
|
|
***********************************************************************/
|
|
/*
|
|
* 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;
|
|
|
|
#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);
|
|
}
|
|
|
|
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");
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
|
|
static Opt_t _opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state };
|
|
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 (!ast.locale.serial)
|
|
setlocale(LC_ALL, "");
|
|
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))
|
|
{
|
|
char *cp;
|
|
sfwrite(ip, p, i);
|
|
if (cp = sfstruse(ip))
|
|
sfputr(sp, T(id, catalog, cp), -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 != OG)
|
|
{
|
|
if (!(tsp = psp))
|
|
break;
|
|
s = psp->ob;
|
|
psp = psp->next;
|
|
free(tsp);
|
|
}
|
|
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 (!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);
|
|
}
|