/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2007 AT&T Knowledge Ventures * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Knowledge Ventures * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * Phong Vo * * * ***********************************************************************/ #pragma prototyped /* * Glenn Fowler * AT&T Research * * command line option parser and usage formatter * its a monster but its all in one place * widen your window while you're at it */ #include #include #include #include #include #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_minus 0x021 /* '-' is an option flag */ #define OPT_number 0x040 /* arg is strtonll() number */ #define OPT_oneof 0x080 /* arg may be set once */ #define OPT_optional 0x100 /* arg is optional */ #define OPT_string 0x200 /* arg is string */ #define OPT_preformat 0001 /* output preformat string */ #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 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 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 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[] = { "", "", "", "", "", "", "", "", "\\fP", "\\fB", &term_off[0], &term_B_on[0], "", "", "\\fP", "\\fI", &term_off[0], &term_I_on[0], "", "", "", "", "", "", "","","\\fP", "\\f5", "", "", }; static char native[] = ""; #if !_PACKAGE_astsa #define ID ast.id #define C(s) ERROR_catalog(s) #define D(s) (opt_info.state->msgdict && dtmatch(opt_info.state->msgdict, (s))) #define T(i,c,m) (X(c)?translate(i,c,C(m)):(m)) #define X(c) (ERROR_translating()&&(c)!=native) #define Z(x) C(x),sizeof(x)-1 /* * translate with C_LC_MESSAGES_libast[] check */ static char* translate(const char* cmd, const char* cat, const char* msg) { if (!X(cat)) return (char*)msg; if (cat != (const char*)ID && D(msg)) cat = (const char*)ID; return errorx(NiL, cmd, cat, msg); } #else static char ID[] = "ast"; #define C(s) s #define D(s) (opt_info.state->msgdict && dtmatch(opt_info.state->msgdict, (s))) #define T(i,c,m) m #define X(c) 0 #define Z(x) C(x),sizeof(x)-1 #endif static const List_t help_head[] = { '-', 0, 0, '+', C("NAME"), C("options available to all \bast\b commands"), '+', C("DESCRIPTION"), C("\b-?\b and \b--?\b* options are the same \ for all \bast\b commands. For any \aitem\a below, if \b--\b\aitem\a is not \ supported by a given command then it is equivalent to \b--\?\?\b\aitem\a. The \ \b--\?\?\b form should be used for portability. All output is written to the \ standard error."), }; static const Help_t styles[] = { C("about"), "-", STYLE_match, Z("List all implementation info."), C("api"), "?api", STYLE_api, Z("List detailed info in program readable form."), C("help"), "", -1, Z("List detailed help option info."), C("html"), "?html", STYLE_html, Z("List detailed info in html."), C("keys"), "?keys", STYLE_keys, Z("List the usage translation key strings with C style escapes."), C("long"), "?long", STYLE_long, Z("List long option usage."), C("man"), "?man", STYLE_man, Z("List detailed info in displayed man page form."), C("nroff"), "?nroff", STYLE_nroff, Z("List detailed info in nroff."), C("options"), "?options", STYLE_options, Z("List short and long option details."), C("posix"), "?posix", STYLE_posix, Z("List posix getopt usage."), C("short"), "?short", STYLE_short, Z("List short option usage."), C("usage"), "?usage", STYLE_usage, Z("List the usage string with C style escapes."), }; 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("\?\?\?\?\?\?TEST"), C("Massage the output for regression testing."), }; static const Attr_t attrs[] = { "flag", OPT_flag, "hidden", OPT_hidden, "ignorecase", OPT_ignorecase, "invert", OPT_invert, "listof", OPT_listof, "number", OPT_number, "oneof", OPT_oneof, "optional", OPT_optional, "string", OPT_string, }; static const char unknown[] = C("unknown option or attribute"); static const char* heading[] = { C("INDEX"), C("USER COMMANDS"), C("SYSTEM LIBRARY"), C("USER LIBRARY"), C("FILE FORMATS"), C("MISCELLANEOUS"), C("GAMES and DEMOS"), C("SPECIAL FILES"), C("ADMINISTRATIVE COMMANDS"), C("GUIs"), }; /* * list of common man page strings * NOTE: add but do not delete from this table */ static Msg_t C_LC_MESSAGES_libast[] = { { C("APPLICATION USAGE") }, { C("ASYNCHRONOUS EVENTS") }, { C("BUGS") }, { C("CAVEATS") }, { C("CONSEQUENCES OF ERRORS") }, { C("DESCRIPTION") }, { C("ENVIRONMENT VARIABLES") }, { C("EXAMPLES") }, { C("EXIT STATUS") }, { C("EXTENDED DESCRIPTION") }, { C("INPUT FILES") }, { C("LIBRARY") }, { C("NAME") }, { C("OPERANDS") }, { C("OPTIONS") }, { C("OUTPUT FILES") }, { C("SEE ALSO") }, { C("STDERR") }, { C("STDIN") }, { C("STDOUT") }, { C("SYNOPSIS") }, { C("author") }, { C("copyright") }, { C("license") }, { C("name") }, { C("path") }, { C("version") }, }; static unsigned char map[UCHAR_MAX]; static Optstate_t state; /* * 2007-03-19 move opt_info from _opt_info_ to (*_opt_data_) * to allow future Opt_t growth * by 2009 _opt_info_ can be static */ #if _BLD_ast && defined(__EXPORT__) #define extern extern __EXPORT__ #endif extern Opt_t _opt_info_; Opt_t _opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state }; #undef extern __EXTERN__(Opt_t, _opt_info_); __EXTERN__(Opt_t*, _opt_infop_); Opt_t* _opt_infop_ = &_opt_info_; #if _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 '\v': *t++ = '\\'; c = 'v'; break; } *t++ = c; } done: *t = 0; return buf; } #endif /* * 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; } /* * match s with t * t translated if possible * imbedded { - _ ' } ignored * * separates required prefix from optional suffix * otherwise prefix match */ static int match(char* s, char* t, int version, 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(error_info.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)) return (void*)p; return 0; } /* * save s and return the saved pointer */ static char* save(const char* s) { Save_t* p; static Dtdisc_t disc; static Dt_t* dict; if (!dict) { disc.key = offsetof(Save_t, text); if (!(dict = dtopen(&disc, Dthash))) return (char*)s; } if (!(p = (Save_t*)dtmatch(dict, s))) { if (!(p = newof(0, Save_t, 1, strlen(s)))) return (char*)s; strcpy(p->text, s); dtinsert(dict, p); } return p->text; } /* * initialize the attributes for pass p from opt string s */ static int init(register char* s, Optpass_t* p) { register char* t; register int c; register int a; register int n; if (!opt_info.state->msgdict) { #if !_PACKAGE_astsa if (!ast.locale.serial) setlocale(LC_ALL, ""); #endif opt_info.state->vp = sfstropen(); opt_info.state->xp = sfstropen(); opt_info.state->msgdisc.key = offsetof(Msg_t, text); opt_info.state->msgdisc.size = -1; opt_info.state->msgdisc.link = offsetof(Msg_t, link); if (opt_info.state->msgdict = dtopen(&opt_info.state->msgdisc, Dthash)) for (n = 0; n < elementsof(C_LC_MESSAGES_libast); n++) dtinsert(opt_info.state->msgdict, C_LC_MESSAGES_libast + n); if (!map[OPT_FLAGS[0]]) for (n = 0, t = OPT_FLAGS; *t; t++) map[*t] = ++n; } #if _BLD_DEBUG error(-1, "optget debug"); #endif p->oopts = s; p->version = 0; p->prefix = 2; p->section = 1; p->flags = 0; 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 (*s < '0' || *s > '9') p->version = 1; else while (*s >= '0' && *s <= '9') p->version = p->version * 10 + (*s++ - '0'); while (*s && *s != '?' && *s != ']') { c = *s++; if (*s < '0' || *s > '9') n = 1; else { n = 0; while (*s >= '0' && *s <= '9') n = n * 10 + (*s++ - '0'); } switch (c) { case 'c': p->flags |= OPT_cache; break; case 'i': p->flags |= OPT_ignore; break; case 'l': p->flags |= OPT_long; break; case 'o': p->flags |= OPT_old; break; case 'p': p->prefix = n; break; case 's': p->section = n; if (n > 1 && n < 6) { p->flags |= OPT_functions; p->prefix = 0; } break; } } } } while (*s) if (*s++ == ']' && *s++ == '[') { if (*s++ != '-') { if (!error_info.id && strneq(s - 1, "+NAME?", 6)) { for (t = s += 5; *t && *t != ' ' && *t != ']'; t++); error_info.id = save(sfprints("%-.*s", t - s, s)); } break; } if (*s == '-') s++; if (strneq(s, "catalog?", 8)) { s += 8; if ((t = strchr(s, ']')) && (!error_info.id || (t - s) != strlen(error_info.id) || !strneq(s, error_info.id, t - s))) p->catalog = save(sfprints("%-.*s", t - s, s)); if (error_info.id) break; } } } if (!p->catalog) { if (opt_info.disc && opt_info.disc->catalog && (!error_info.id || !streq(opt_info.disc->catalog, error_info.id))) p->catalog = opt_info.disc->catalog; else p->catalog = ID; } s = p->oopts; if (*s == ':') s++; if (*s == '+') { s++; p->flags |= OPT_plus; } 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, ')') && (opt_info.state->cp || (opt_info.state->cp = sfstropen()))) { /* * solaris long option compatibility */ p->version = 1; for (t = p->oopts; t < s; t++) sfputc(opt_info.state->cp, *t); n = t - p->oopts; sfputc(opt_info.state->cp, '['); sfputc(opt_info.state->cp, '-'); sfputc(opt_info.state->cp, ']'); while (c = *s++) { sfputc(opt_info.state->cp, '['); sfputc(opt_info.state->cp, c); if (a = (c = *s++) == ':') c = *s++; if (c == '(') { sfputc(opt_info.state->cp, ':'); for (;;) { while ((c = *s++) && c != ')') sfputc(opt_info.state->cp, c); if (!c || *s != '(') break; sfputc(opt_info.state->cp, '|'); s++; } } sfputc(opt_info.state->cp, ']'); if (a) sfputr(opt_info.state->cp, ":[string]", -1); if (!c) break; } if (!(p->oopts = s = sfstruse(opt_info.state->cp))) return -1; s += n; } p->opts = s; 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 (opt_info.state->emphasis > 0) return fonts[f].term[set]; break; } return ""; } /* * 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) { register int c; register char* b = s; int n; message((-23, "AHA#%d expand(%s)", __LINE__, show(s))); 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; message((-23, "AHA#%d expand(%s)", __LINE__, b)); n = sfstrtell(ip); if (!c) s--; if (*b == '?') { if (!*++b || streq(b, "NAME")) { if (!(b = error_info.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; } /* * push \f...\f info */ static Push_t* info(Push_t* psp, char* s, char* e, Sfio_t* ip) { register char* b; int n; Push_t* tsp; static Push_t push; b = expand(s, e, &s, ip); 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, char* catalog, int version, Sfio_t* ip) { 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(error_info.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 z, int level, int style, int f, Sfio_t* ip, int version, char* catalog) { register int c; register char* t; register char* e; int ostyle; int a; int i; char* p; 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) 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 (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, catalog, version, ip))) { psp= tsp; s = psp->nb; e = psp->ne; r = psp->ch > 0; } switch (*s) { case '\a': if (f == FONT_ITALIC) s++; f = 0; break; case '\b': if (f == FONT_BOLD) s++; f = 0; break; case '\v': if (f == FONT_LITERAL) s++; f = 0; break; default: if (f) 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) { 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, -1, 0, style, FONT_ITALIC, ip, version, 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, catalog, version, ip))) { 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 & ~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; while (++t < e && isdigit(*t)); if (t < e && *t == ')' && t > w + 1) { sfprintf(sp, "%s%-.*s%s%-.*s" , t - w - 1, w + 1 , w - s - 1, s , 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); 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, "%-.*s>", 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 (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) sfputr(sp, font(f, style, 0), -1); 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* 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) { if (!(a = error_info.id)) a = "..."; sfprintf(sp, "\t%s%s%s%s[%s%s%s%s%s]", font(FONT_BOLD, style, 1), a, font(FONT_BOLD, style, 0), b, b, font(FONT_ITALIC, style, 1), o, font(FONT_ITALIC, style, 0), b); } else if (a) sfprintf(sp, "%*.*s%s%s%s[%s%s%s]", OPT_USAGE - 1, OPT_USAGE - 1, T(NiL, ID, "Or:"), b, a, b, b, o, b); else { if (!(a = error_info.id)) a = "..."; if (!sfstrtell(sp)) sfprintf(sp, "[%s%s%s]", b, o, b); } if (!t) break; i = ++t - p; if (i) { sfputr(sp, b, -1); if (X(catalog)) { sfwrite(ip, p, i); if (b = sfstruse(ip)) sfputr(sp, T(error_info.id, catalog, b), -1); else sfwrite(sp, p, i); } else sfwrite(sp, p, i); } if (style == STYLE_html) sfputr(sp, "
", '\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--; } } } if (n) label(sp, sep, p, n, 0, style, 0, ip, version, catalog); } /* * 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 level, int style, Sfio_t* ip, int version, char* catalog) { 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, "
\n");
			else
				sfputc(sp, '\n');
			for (n = 0; n < level; n++)
				sfputc(sp, '\t');
		}
		label(sp, 0, s + 1, -1, level, style, FONT_LITERAL, ip, version, catalog);
		sfputc(sp, '\n');
		if (style >= STYLE_nroff)
			sfprintf(sp, ".DE");
		else if (style == STYLE_html)
			sfprintf(sp, "
"); } else if (*s != ']' && (*s != '?' || *(s + 1) == '?')) { par = 0; if (level) { if (style >= STYLE_nroff) sfprintf(sp, ".H%d ", (level + 1) / 2); else for (n = 0; n < level; n++) sfputc(sp, '\t'); } if (style == STYLE_html) { if (!level) sfputr(sp, "

", -1); sfputr(sp, "flags |= OPT_proprietary; break; } label(sp, 0, s, -1, level, 0, 0, ip, version, catalog); sfputr(sp, "\">", -1); label(sp, 0, s, -1, level, style, level ? FONT_BOLD : 0, ip, version, catalog); sfputr(sp, "", -1); if (!level) sfputr(sp, "

", -1); } 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, -1, level, style, FONT_BOLD, ip, version, catalog); } } else { par = 1; if (style >= STYLE_nroff) sfputr(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 */ static char* textout(Sfio_t* sp, register char* p, int style, int level, int bump, Sfio_t* ip, int version, char* catalog) { #if 0 #define textout(a,b,c,d,e,f,g,h) (sfprintf(a,"(%d)",__LINE__),textout(a,b,c,d,e,f,g,h)) #endif register char* t; register int c; register int n; char* e; int a; int f; int par; Push_t* tsp; int ident = 0; int lev = level; Push_t* psp = 0; again: if ((c = *p) == GO) { for (;;) { while (*(p = next(p + 1, version)) == '\n'); if (*p == GO) { if (level > 1) level++; level++; } else if (*p != OG) { if (level <= 1 || *p != '[' || *(p + 1) != '-') break; p = skip(p, 0, 0, 0, 1, level, 0, version); } else if ((level -= 2) <= lev) return p + 1; } if (*p == '\f') { psp = info(psp, p + 1, NiL, ip); if (psp->nb) p = psp->nb; else { p = psp->ob; psp = psp->next; } } if (*p != '[') return p; c = *++p; if (level > 1) level++; level++; } if (c == '-' && level > 1) return skip(p, 0, 0, 0, 1, level, 1, version); if (c == '+' || c == '-' && (bump = 3) || c != ' ' && level > 1) { p = skip(t = p + 1, '?', 0, 0, 1, level, 0, version); if (c == '-' && (*t == '?' || *t >= '0' && *t <= '9')) { if ((c = *p) != '?') return skip(p, 0, 0, 0, 1, level, 1, version); par = item(sp, C("version"), level, style, ip, version, ID); for (;;) { while (isspace(*(p + 1))) p++; e = p; if (e[1] == '@' && e[2] == '(' && e[3] == '#' && e[4] == ')') p = e + 4; else if (e[1] == '$' && e[2] == 'I' && e[3] == 'd' && e[4] == ':' && e[5] == ' ') { p = e + 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 (e = sfstruse(ip)) *((t = e) + 1) = '|'; } } par = item(sp, t, level, style, ip, version, catalog); c = *p; } if (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 = *(p = skip(p, '?', 0, 0, 1, 0, 0, version)); if ((c == ']' || c == '?' && *(p + 1) == ']' && *(p + 2) != ']' && p++) && (c = *(p = next(p + 1, version))) == GO) p = textout(sp, p, style, level + bump + par + 1, 0, ip, version, catalog); else if (c == '?' || c == ' ') { p++; if (c == ' ') sfputc(sp, c); else { if (X(catalog) && (tsp = localize(psp, p, NiL, 0, 1, catalog, version, ip))) { psp = tsp; p = psp->nb; } if (style < STYLE_nroff) for (n = 0; n < bump + 1; n++) sfputc(sp, '\t'); } f = 0; for (;;) { switch (c = *p++) { case 0: if (!(tsp = psp)) { if (f) sfputr(sp, font(f, style, 0), -1); return p - 1; } p = psp->ob; psp = psp->next; free(tsp); continue; case ']': if (psp && psp->ch) break; if (*p != ']') { if (f) { sfputr(sp, font(f, style, 0), -1); f = 0; } for (;;) { if ((*p == '#' || *p == ':') && level > lev) { char* o; char* v; int j; int m; int ol; int vl; a = 0; o = 0; v = 0; if (*++p == '?' || *p == *(p - 1)) { p++; a |= OPT_optional; } if (*(p = next(p, version)) == '[') { p = skip(p + 1, ':', '?', 0, 1, 0, 0, version); while (*p == ':') { p = skip(t = p + 1, ':', '?', 0, 1, 0, 0, version); m = p - 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, '.'); } p = skip(p, 0, 0, 0, 1, 0, 1, version); } if (*(p = next(p, version)) == GO) p = textout(sp, p, style, level + bump + !level, 0, ip, version, catalog); else if (*p == '[' && level > lev) { p++; goto again; } else if (*p == '\f') { p++; if (style != STYLE_keys) { psp = info(psp, p, NiL, ip); if (psp->nb) p = psp->nb; else { p = psp->ob; psp = psp->next; } } } else if (!*p) { if (!(tsp = psp)) break; p = psp->ob; psp = psp->next; free(tsp); } else if (*p != OG) break; else { p++; if ((level -= 2) <= lev) break; } } return p; } p++; 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 = p; *t && !isspace(*t) && !iscntrl(*t); t++); if (*t == c && *++t == '(') { e = t; while (isdigit(*++t)); if (*t == ')' && t > e + 1) { sfprintf(sp, "%s%-.*s%s%-.*s" , t - e - 1, e + 1 , e - p - 1, p , font(a, style, 1) , e - p - 1, p , font(a, style, 0) , t - e + 1, e ); p = 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, p, NiL, ip); if (psp->nb) p = psp->nb; else { p = psp->ob; psp = psp->next; } } continue; case '\v': a = FONT_LITERAL; goto setfont; case ' ': if (ident && *p == '$') { while (*++p) if (*p == ']') { if (*(p + 1) != ']') break; p++; } continue; } case '\n': case '\r': case '\t': while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++; if (*p == ']' && *(p + 1) != ']' && (!psp || !psp->ch)) continue; c = ' '; break; case '<': if (style == STYLE_html) { sfputr(sp, "<", -1); c = 0; for (t = p; *t; t++) if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-') { if (*t == '@') { if (c) break; c = 1; } else if (*t == '>') { if (c) { sfprintf(sp, "%-.*s>", t - p, p, t - p, p); p = 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) { p++; goto again; } return p; } /* * 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 opt_info.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* ov; char* name; char* pp; char* rb; char* re; int f; int i; int j; int m; int n; int a; int sl; int vl; int ol; int wl; int xl; int rm; int ts; int co; int z; int style; int head; int mode; int mutex; int prefix; int version; long tp; char* catalog; Optpass_t* o; Optpass_t* q; Optpass_t* e; Optpass_t one; Help_t* hp; short ptstk[elementsof(indent) + 2]; short* pt; Sfio_t* vp; Push_t* tsp; char* opts = (char*)oopts; int flags = 0; int matched = 0; int paragraph = 0; int section = 1; 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 = opt_info.state->mp) && !(mp = opt_info.state->mp = sfstropen())) goto nospace; if (!what) style = opt_info.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 = opt_info.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; } message((-20, "AHA#%d style=%d", __LINE__, style)); again: if (opts) { for (i = 0; i < opt_info.state->npass; i++) if (opt_info.state->pass[i].oopts == opts) { o = &opt_info.state->pass[i]; break; } if (i >= opt_info.state->npass) { o = &one; if (init((char*)opts, o)) goto nospace; } e = o + 1; } else if (opt_info.state->npass > 0) { o = opt_info.state->pass; e = o + opt_info.state->npass; } else if (opt_info.state->npass < 0) { o = &opt_info.state->cache->pass; e = o + 1; } else return T(NiL, ID, "[* call optget() before opthelp() *]"); 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: opt_info.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: if (!opt_info.state->emphasis) { if (x = getenv("ERROR_OPTIONS")) { if (strmatch(x, "*noemphasi*")) break; if (strmatch(x, "*emphasi*")) { opt_info.state->emphasis = 1; break; } } if ((x = getenv("TERM")) && strmatch(x, "(ansi|vt100|xterm)*") && isatty(sffileno(sfstderr))) opt_info.state->emphasis = 1; } break; } x = ""; xl = 0; for (q = o; q < e; q++) { if (q->flags & OPT_ignore) continue; if (section < q->section) section = q->section; section = q->section; flags |= q->flags; p = q->opts; prefix = q->prefix; version = q->version; catalog = q->catalog; switch (style) { case STYLE_usage: if (xl) sfputc(mp, '\n'); else xl = 1; while (c = *p++) { switch (c) { 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 '\\': c = '\\'; break; default: sfputc(mp, c); continue; } sfputc(mp, '\\'); sfputc(mp, c); } 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); 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 == '{') vl++; else if (c == '}') 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 = opt_info.state->vp)) p = expand(p + 2, NiL, &t, vp); 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); 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 == '}') { p++; continue; } message((-20, "opthelp: opt %s", show(p))); if (z < 0) z = 0; a = 0; f = 0; w = 0; d = 0; s = 0; sl = 0; if (*p == '[') { if ((c = *(p = next(p + 1, version))) == '-') { if (style >= STYLE_man) { if (*(p + 1) != '-') { if (!sp_misc && !(sp_misc = sfstropen())) goto nospace; else p = textout(sp_misc, p, style, 1, 3, sp_info, version, catalog); continue; } } else if (style == STYLE_match && *what == '-') { if (*(p + 1) == '?' || *(p + 1) >= '0' && *(p + 1) <= '9') 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, catalog)) { if (*(p + 1) == '-') p++; p = textout(sp, p, style, 1, 3, sp_info, version, catalog); matched = -1; continue; } } } if (!z) z = -1; } else if (c == '+') { if (style >= STYLE_man) { p = textout(sp_body, p, style, 0, 0, sp_info, version, catalog); if (!sp_head) { sp_head = sp_body; if (!(sp_body = sfstropen())) goto nospace; } continue; } else if (style == STYLE_match && *what == '+') { if (paragraph) { if (p[1] == '?') { p = textout(sp, p, style, 1, 3, sp_info, version, catalog); continue; } paragraph = 0; } if (match((char*)what + 1, p + 1, version, catalog)) { p = textout(sp, p, style, 1, 3, sp_info, version, catalog); 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 = opt_info.state->vp)) p = expand(p + 1, NiL, &t, vp); 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, 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 = 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, style, sp_info, version, ID); } 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, -1, 0, style, FONT_BOLD, sp_info, version, 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, -1, 0, style, 0, sp_info, version, catalog)) { sfputc(sp_body, '|'); label(sp_body, 0, w, -1, 0, style, 0, sp_info, version, 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, -1, 0, style, 0, sp_info, version, 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, -1, 0, style, FONT_BOLD, sp_info, version, catalog)) { sfputc(sp_body, '|'); label(sp_body, 0, w, -1, 0, style, FONT_BOLD, sp_info, version, 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, -1, 0, style, FONT_ITALIC, sp_info, version, catalog); if (a & OPT_optional) sfputc(sp_body, ']'); } } if (style >= STYLE_match) { if (d) textout(sp_body, d, style, 0, 3, sp_info, version, catalog); if (u) textout(sp_body, u, style, 0, 3, sp_info, version, catalog); 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, style, 0, 0, sp_info, version, NiL); } if (*p == GO) { p = u ? skip(p + 1, 0, 0, 0, 0, 1, 1, version) : textout(sp_body, p, style, 4, 0, sp_info, version, catalog); 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, style, 4, 0, sp_info, version, NiL); 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, style, 4, 0, sp_info, version, NiL); } } else if (!mutex) sfputc(sp_body, ']'); } if (*p == GO) { if (style >= STYLE_match) p = textout(sp_body, p, style, 4, 0, sp_info, version, catalog); 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, style, sp_info, version, ID); sfputr(sp_body, p, -1); } } } version = o->version; 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) { sfprintf(sp, "\ .\\\" format with nroff|troff|groff -man\n\ .fp 5 CW\n\ .nr mI 0\n\ .de mI\n\ .if \\\\n(mI>\\\\$1 \\{\n\ . nr mI \\\\n(mI-1\n\ . RE\n\ .mI \\\\$1\n\ .\\}\n\ .if \\\\n(mI<\\\\$1 \\{\n\ . nr mI \\\\n(mI+1\n\ . RS\n\ .mI \\\\$1\n\ .\\}\n\ ..\n\ .de H1\n\ .mI 1\n\ .TP\n\ \\fB\\\\$1\\fP\n\ ..\n\ .de H2\n\ .mI 2\n\ .TP\n\ \\fB\\\\$1\\fP\n\ ..\n\ .de H3\n\ .mI 3\n\ .TP\n\ \\fB\\\\$1\\fP\n\ ..\n\ .de H4\n\ .mI 4\n\ .TP\n\ \\fB\\\\$1\\fP\n\ ..\n\ .de OP\n\ .mI 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\ .TP\n\ \\\\*(mO\n\ ..\n\ .de FN\n\ .mI 0\n\ .TP\n\ \\\\$1 \\\\$2\n\ ..\n\ .TH %s %d\n\ " , o->prefix == 2 ? "\\\\-\\\\-" : o->prefix == 1 ? "\\\\-" : "" , error_info.id , section ); } 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; } item(sp, C("SYNOPSIS"), 0, style, sp_info, version, ID); } if (x) { for (t = x + xl; t > x && (*(t - 1) == '\n' || *(t - 1) == '\r'); t--); xl = t - x; if (style >= STYLE_match) { args(sp, x, xl, flags, style, sp_info, version, catalog); x = 0; } } if (sp_body) { if (sfstrtell(sp_body)) { if (style < STYLE_match && sfstrtell(sp)) sfputc(sp, ' '); if (!(t = sfstruse(sp_body))) goto nospace; sfputr(sp, t, -1); } sfclose(sp_body); sp_body = 0; } if (x && style != STYLE_posix) args(sp, x, xl, flags, style, sp_info, version, 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; name = error_info.id ? error_info.id : "command"; m = strlen(name) + 1; if (!opt_info.state->width) { astwinsize(1, NiL, &opt_info.state->width); if (opt_info.state->width < 20) opt_info.state->width = OPT_WIDTH; } if (!(opt_info.state->flags & OPT_preformat)) { if (style >= STYLE_man || matched < 0) { sfputc(mp, '\f'); ts = 0; } else ts = OPT_USAGE + m; if (style == STYLE_html) { sfprintf(mp, "\n\n\n\n%s%s man document\n\n\n", (opt_info.state->flags & OPT_proprietary) ? "\n" : "", name); sfprintf(mp, "

 %s ( %d ) %s%s ( %d )

\n
\n", name, section, T(NiL, ID, heading[section % 10]), name, section); sfprintf(mp, "
\n
"); co = 2; *(pt = ptstk) = 0; } else co = 0; if ((rm = opt_info.state->width - ts - 1) < 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 = opt_info.state->width - 1; 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, "

\n"); } } head = *p != ' ' && *p != '\t'; if (style == STYLE_html && (*p != '<' || !strneq(p, "
", 4) && !strneq(p, "

", 3))) { y = p; while (*p == '\t') p++; if (*p == '\n') continue; j = p - y; if (j > *pt) { if (pt > ptstk) sfprintf(mp, "

\n"); *++pt = j; sfprintf(mp, "
\n"); } else while (j < *pt) { if (--pt > ptstk) sfprintf(mp, "
\n"); sfprintf(mp, "
\n"); } co += sfprintf(mp, "
"); } } else if (c == '\t') { if (style == STYLE_html) { while (*p == '\t') p++; if (*p != '\n') co += sfprintf(mp, "
"); } 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 = opt_info.state->width - 1; 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, "' && !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, "%-.*s '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 = opt_info.state->width - 1; } } 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) { while (pt > ptstk) { if (--pt > ptstk) sfprintf(mp, "\n
"); sfprintf(mp, "\n"); } sfprintf(mp, "\n\n"); } } 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 #* 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 version, char* catalog, int err) { register Sfio_t* mp; register Sfio_t* tp; register char* s; register int c; if (opt_info.num != LONG_MIN) opt_info.num = opt_info.number = 0; if (!p || !(mp = opt_info.state->mp) && !(mp = opt_info.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) ? opt_info.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(error_info.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][:[|...]['?'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* 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 !_YOU_FIGURED_OUT_HOW_TO_GET_ALL_DLLS_TO_DO_THIS_ /* * these are not initialized by all dlls! */ extern Error_info_t _error_info_; extern Opt_t _opt_info_; if (!_error_infop_) _error_infop_ = &_error_info_; if (!_opt_infop_) _opt_infop_ = &_opt_info_; if (!opt_info.state) opt_info.state = &state; #endif if (!oopts) return 0; opt_info.state->pindex = opt_info.index; opt_info.state->poffset = opt_info.offset; if (!opt_info.index) { opt_info.index = 1; opt_info.offset = 0; if (opt_info.state->npass) { opt_info.state->npass = 0; opt_info.state->join = 0; } } if (!argv) cache = 0; else for (pcache = 0, cache = opt_info.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 = opt_info.state->cache; opt_info.state->cache = cache; } pass = &cache->pass; opt_info.state->npass = -1; } else { if (!argv) n = opt_info.state->npass ? opt_info.state->npass : 1; else if ((n = opt_info.state->join - 1) < 0) n = 0; if (n >= opt_info.state->npass || opt_info.state->pass[n].oopts != (char*)oopts) { for (m = 0; m < opt_info.state->npass && opt_info.state->pass[m].oopts != (char*)oopts; m++); if (m < opt_info.state->npass) n = m; else { if (n >= elementsof(opt_info.state->pass)) n = elementsof(opt_info.state->pass) - 1; init((char*)oopts, &opt_info.state->pass[n]); if (opt_info.state->npass <= n) opt_info.state->npass = n + 1; } } if (!argv) return 0; pass = &opt_info.state->pass[n]; } opts = pass->opts; prefix = pass->prefix; version = pass->version; if (!(xp = opt_info.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; num = 1; w = v = 0; x = 0; for (;;) { if (!opt_info.offset) { /* * finished with the previous arg */ if (opt_info.index == 1 && opt_info.argv != opt_info.state->strv) { opt_info.argv = 0; opt_info.state->argv[0] = 0; if (argv[0] && (opt_info.state->argv[0] = save(argv[0]))) opt_info.argv = opt_info.state->argv; opt_info.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 == '?') n = 1; } else if ((c = *s++) != '-' && (c != '+' || !(pass->flags & OPT_plus) && (*s < '0' || *s > '9' || !strmatch(opts, version ? "*\\]#\\[*" : "*#*")))) { 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; } 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; opt_info.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 (w && !v && (*(w + 1) || !(v = argv[opt_info.index]) || !++opt_info.index)) v = w + 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; } 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 = 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) { 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; 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 = opt_info.state->cache; opt_info.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, opt_info.state->xp); if (psp->nb) s = psp->nb; else { s = psp->ob; psp = psp->next; } continue; } message((-20, "optget: opt %s w %s num %ld", show(s), w, num)); if (*s == c && !w) break; else if (*s == '[') { f = s = next(s + 1, version); k = *f; if (k == '+' || k == '-') /* ignore */; else if (k == '[' || version < 1) continue; else if (w && !cache) { nov = no; if (*(s + 1) == '\f' && (vp = opt_info.state->vp)) { sfputc(vp, k); s = expand(s + 2, NiL, &t, vp); 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(error_info.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("?", version, catalog, 0); } 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; } } 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); } } 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 ? "!" : "=", version, catalog, 0); 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 || c < '0' || c > '9' || !numopt) { pop(psp); return opterror("", version, catalog, 0); } 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 = 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("!", version, catalog, 0); } opt_info.num = 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, version, catalog, 0); } } 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 = 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, version, catalog, err); } } } 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, version, catalog, err); } 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.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, version, catalog, err); } } } } else if (*(s + 1) != '?') { opt_info.index--; pop(psp); return opterror(s, version, catalog, 0); } 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(error_info.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("&", version, catalog, 0); } 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 = opt_info.number = x)) { pop(psp); return opterror("*", version, catalog, 0); } } } } else if (w && v) { pop(psp); return opterror("=", version, catalog, 0); } else { opt_info.num = 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 '?'; } opt_info.state->force = hp->style; } else if (match(s, "ESC", -1, NiL) || match(s, "EMPHASIS", -1, NiL)) opt_info.state->emphasis = n; else if (match(s, "PREFORMAT", -1, NiL)) opt_info.state->flags |= OPT_preformat; else if (match(s, "TEST", -1, NiL)) { opt_info.state->width = OPT_WIDTH; opt_info.state->emphasis = 1; } else { pop(psp); return opterror(v, version, catalog, 0); } psp = pop(psp); if (argv == opt_info.state->strv) return '#'; goto again; } if ((opt_info.arg = opthelp(NiL, v)) == (char*)unknown) { pop(psp); return opterror(v, version, catalog, 0); } pop(psp); return '?'; nospace: pop(psp); return opterror(NiL, 0, NiL, 0); } /* * parse long options with 0,1,2 leading '-' or '+' from string and pass to optget() * syntax is the unquoted * * [-|+|--|++][[-+:|&=]=\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 = opt_info.state->strp) && !(mp = opt_info.state->strp = sfstropen())) return 0; if (opt_info.state->str != s) opt_info.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) { opt_info.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 = 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 = opt_info.state->strv; opt_info.state->strv[0] = T(NiL, ID, "option"); if (!(opt_info.state->strv[1] = sfstruse(mp))) goto nospace; opt_info.state->strv[2] = 0; opt_info.offset = s - (char*)str; } if (opts) { if (!opt_info.state->strv[1]) { opt_info.state->str = 0; return 0; } opt_info.index = 1; v = opt_info.offset; opt_info.offset = 0; c = optget(opt_info.state->strv, opts); opt_info.index = 1; opt_info.offset = v; if (c == '#') { s = opt_info.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, NiL, 0); }