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