22 # define P99_GETOPT_H_
32 typedef int p00_getopt_process_type(
void*,
char const*);
36 p00_getopt_process_type*
const p00_f;
37 char const*
const p00_a;
38 char const*
const p00_d;
39 char const*
const p00_t;
40 char const*
const p00_n;
41 char const*
const p00_v;
46 #define P00_GETOPT_PROCESS(T) P99_PASTE2(p00_getopt_process_, T)
48 #define P00_GETOPT_CHAR(CHAR) p00_getopt_char## CHAR
49 #define P00_GETOPT_BOOL(CHAR) p00_getopt_bool## CHAR
52 int p00_getopt_comp(
void const* p00_a,
void const* p00_b,
void* p00_context) {
53 register struct p00_getopt const*
const*
const p00_A = p00_a;
54 register struct p00_getopt const*
const*
const p00_B = p00_b;
55 if (p00_A && (*p00_A) && (*p00_A)->p00_a)
56 if (p00_B && (*p00_B) && (*p00_B)->p00_a)
57 return strcmp((*p00_A)->p00_a, (*p00_B)->p00_a);
60 else if (p00_B && (*p00_B) && (*p00_B)->p00_a)
67 int p00_getopt_subcomp(
void const* p00_a,
void const* p00_b,
void* p00_context) {
68 register struct p00_getopt const*
const*
const p00_A = p00_a;
69 register struct p00_getopt const*
const*
const p00_B = p00_b;
70 if (p00_A && (*p00_A) && (*p00_A)->p00_a)
71 if (p00_B && (*p00_B) && (*p00_B)->p00_a) {
72 size_t p00_n = strlen((*p00_A)->p00_a);
73 return strncmp((*p00_A)->p00_a, (*p00_B)->p00_a, p00_n);
76 else if (p00_B && (*p00_B) && (*p00_B)->p00_a)
83 #define P00_GETOPT_SIGNED(T) \
85 int P00_GETOPT_PROCESS(T)(void* p00_o, char const* p00_c) { \
87 if (p00_c && p00_c[0]) { \
89 *p00_O = strtoll(p00_c, &endptr, 0); \
90 if (endptr) return strlen(p00_c); \
103 #define P00_GETOPT_UNSIGNED(T) \
105 int P00_GETOPT_PROCESS(T)(void* p00_o, char const* p00_c) { \
107 if (p00_c && p00_c[0]) { \
109 *p00_O = strtoull(p00_c, &endptr, 0); \
110 if (endptr) return strlen(p00_c); \
123 int P00_GETOPT_PROCESS(
char)(
void* p00_o,
char const*p00_c) {
125 if (p00_c && p00_c[0]) {
133 int P00_GETOPT_PROCESS(_Bool)(
void* p00_o,
char const*p00_c) {
141 int P00_GETOPT_PROCESS(
char_cptr)(
void* p00_o,
char const*p00_c) {
142 char const**p00_O = p00_o;
145 *p00_O = *p00_getopt_allocations;
146 ++p00_getopt_allocations;
147 return strlen(p00_c) + 1;
152 #define P00_GETOPT_FLOAT(T) \
154 int P00_GETOPT_PROCESS(T)(void* p00_o, char const* p00_c) { \
156 if (p00_c && p00_c[0]) { \
158 *p00_O = strtold(p00_c, &endptr); \
159 if (endptr) return strlen(p00_c); \
170 #define P00_GETOPT_PROCESS_CHOOSE_(T) (T, P00_GETOPT_PROCESS(T))
172 #define P00_GETOPT_PROCESS_CHOOSE(...) P99_SEQ(P00_GETOPT_PROCESS_CHOOSE_, __VA_ARGS__)
174 #define P00_GETOPT_DECLARE(CHAR, T, TS, DEFS, NAME, DEF, ALIAS, DOC, ...) \
176 P99_TENTATIVE_DEF(bool const, P00_GETOPT_BOOL(CHAR)) = true; \
177 P99_TENTATIVE_DEF(struct p00_getopt const*const, P00_GETOPT_CHAR(CHAR)) \
178 = &(struct p00_getopt const){ \
180 .p00_f = P99_GENERIC(NAME, 0, __VA_ARGS__), \
188 #define P00_GETOPT_DECLARE_(...) P00_GETOPT_DECLARE(__VA_ARGS__)
258 #define P99_GETOPT_DECLARE(CHAR, T, NAME, DEF, ALIAS, DOC)
273 #define P99_GETOPT_DEFINE(CHAR, T, NAME, DEF, ALIAS, DOC)
277 #define P99_GETOPT_DECLARE(CHAR, T, ...) \
278 P00_GETOPT_DECLARE_(_p00##CHAR, \
282 P99_IF_LT(P99_NARG(__VA_ARGS__), 2) \
283 (__VA_ARGS__, 0, 0, 0) \
284 (P99_IF_LT(P99_NARG(__VA_ARGS__), 3) \
285 (__VA_ARGS__, 0, 0) \
286 (P99_IF_LT(P99_NARG(__VA_ARGS__), 4) \
289 P00_GETOPT_PROCESS_CHOOSE(P99_STD_REAL_TYPES, char_cptr))
292 #define P00_GETOPT_DEFINE(CHAR, T, NAME, DEF, ALIAS, DOC) \
297 #define P99_GETOPT_DEFINE(CHAR, T, ...) \
298 P99_IF_LT(P99_NARG(__VA_ARGS__), 2) \
299 (P00_GETOPT_DEFINE(CHAR, T, __VA_ARGS__, 0, 0, 0)) \
300 (P99_IF_LT(P99_NARG(__VA_ARGS__), 3) \
301 (P00_GETOPT_DEFINE(CHAR, T, __VA_ARGS__, 0, 0)) \
302 (P99_IF_LT(P99_NARG(__VA_ARGS__), 4) \
303 (P00_GETOPT_DEFINE(CHAR, T, __VA_ARGS__, 0)) \
304 (P00_GETOPT_DEFINE(CHAR, T, __VA_ARGS__))))
308 #define P00_GETOPT_STRUCT_DECL(CHAR) \
309 P99_TENTATIVE_DEC(bool const, P00_GETOPT_BOOL(CHAR)); \
310 P99_TENTATIVE_DEC(struct p00_getopt const*const ,P00_GETOPT_CHAR(CHAR))
312 #define P00_GETOPT_CHARS \
313 _p00A, _p00B, _p00C, _p00D, _p00E, _p00F, _p00G, \
314 _p00H, _p00I, _p00J, _p00K, _p00L, _p00M, _p00N, \
315 _p00O, _p00P, _p00Q, _p00R, _p00S, _p00T, _p00U, \
316 _p00V, _p00W, _p00X, _p00Y, _p00Z, \
317 _p00a, _p00b, _p00c, _p00d, _p00e, _p00f, _p00g, \
318 _p00h, _p00i, _p00j, _p00k, _p00l, _p00m, _p00n, \
319 _p00o, _p00p, _p00q, _p00r, _p00s, _p00t, _p00u, \
320 _p00v, _p00w, _p00x, _p00y, _p00z, \
321 _p000, _p001, _p002, _p003, _p004, _p005, _p006, \
322 _p007, _p008, _p009, _p00_, \
368 enum p99_getopt_enum {
369 p99_getopt_enum_p00A =
'A',
370 p99_getopt_enum_p00B =
'B',
371 p99_getopt_enum_p00C =
'C',
372 p99_getopt_enum_p00D =
'D',
373 p99_getopt_enum_p00E =
'E',
374 p99_getopt_enum_p00F =
'F',
375 p99_getopt_enum_p00G =
'G',
376 p99_getopt_enum_p00H =
'H',
377 p99_getopt_enum_p00I =
'I',
378 p99_getopt_enum_p00J =
'J',
379 p99_getopt_enum_p00K =
'K',
380 p99_getopt_enum_p00L =
'L',
381 p99_getopt_enum_p00M =
'M',
382 p99_getopt_enum_p00N =
'N',
383 p99_getopt_enum_p00O =
'O',
384 p99_getopt_enum_p00P =
'P',
385 p99_getopt_enum_p00Q =
'Q',
386 p99_getopt_enum_p00R =
'R',
387 p99_getopt_enum_p00S =
'S',
388 p99_getopt_enum_p00T =
'T',
389 p99_getopt_enum_p00U =
'U',
390 p99_getopt_enum_p00V =
'V',
391 p99_getopt_enum_p00W =
'W',
392 p99_getopt_enum_p00X =
'X',
393 p99_getopt_enum_p00Y =
'Y',
394 p99_getopt_enum_p00Z =
'Z',
395 p99_getopt_enum_p00a =
'a',
396 p99_getopt_enum_p00b =
'b',
397 p99_getopt_enum_p00c =
'c',
398 p99_getopt_enum_p00d =
'd',
399 p99_getopt_enum_p00e =
'e',
400 p99_getopt_enum_p00f =
'f',
401 p99_getopt_enum_p00g =
'g',
402 p99_getopt_enum_p00h =
'h',
403 p99_getopt_enum_p00i =
'i',
404 p99_getopt_enum_p00j =
'j',
405 p99_getopt_enum_p00k =
'k',
406 p99_getopt_enum_p00l =
'l',
407 p99_getopt_enum_p00m =
'm',
408 p99_getopt_enum_p00n =
'n',
409 p99_getopt_enum_p00o =
'o',
410 p99_getopt_enum_p00p =
'p',
411 p99_getopt_enum_p00q =
'q',
412 p99_getopt_enum_p00r =
'r',
413 p99_getopt_enum_p00s =
's',
414 p99_getopt_enum_p00t =
't',
415 p99_getopt_enum_p00u =
'u',
416 p99_getopt_enum_p00v =
'v',
417 p99_getopt_enum_p00w =
'w',
418 p99_getopt_enum_p00x =
'x',
419 p99_getopt_enum_p00y =
'y',
420 p99_getopt_enum_p00z =
'z',
421 p99_getopt_enum_p000 =
'0',
422 p99_getopt_enum_p001 =
'1',
423 p99_getopt_enum_p002 =
'2',
424 p99_getopt_enum_p003 =
'3',
425 p99_getopt_enum_p004 =
'4',
426 p99_getopt_enum_p005 =
'5',
427 p99_getopt_enum_p006 =
'6',
428 p99_getopt_enum_p007 =
'7',
429 p99_getopt_enum_p008 =
'8',
430 p99_getopt_enum_p009 =
'9',
431 p99_getopt_enum_p00_ =
'_',
432 p99_getopt_enum_p00AMPERSAND =
'&',
433 p99_getopt_enum_p00APOSTROPHE =
'\'',
434 p99_getopt_enum_p00ASTERISK =
'*',
435 p99_getopt_enum_p00AT =
'@',
436 p99_getopt_enum_p00BACKSLASH =
'\\',
437 p99_getopt_enum_p00BAR =
'|',
438 p99_getopt_enum_p00BRACELEFT =
'{',
439 p99_getopt_enum_p00BRACERIGHT =
'}',
440 p99_getopt_enum_p00BRACKETLEFT =
'[',
441 p99_getopt_enum_p00BRACKETRIGHT =
']',
442 p99_getopt_enum_p00CARRET =
'^',
443 p99_getopt_enum_p00COLON =
':',
444 p99_getopt_enum_p00COMMA =
',',
445 p99_getopt_enum_p00DOLLAR =
'$',
446 p99_getopt_enum_p00EQUAL =
'=',
447 p99_getopt_enum_p00EXLAM =
'!',
448 p99_getopt_enum_p00GRAVE =
'`',
449 p99_getopt_enum_p00GREATER =
'>',
450 p99_getopt_enum_p00HASH =
'#',
451 p99_getopt_enum_p00HELP =
'?',
452 p99_getopt_enum_p00LESS =
'<',
453 p99_getopt_enum_p00PARENLEFT =
'(',
454 p99_getopt_enum_p00PARENRIGHT =
')',
455 p99_getopt_enum_p00PERCENT =
'%',
456 p99_getopt_enum_p00PERIOD =
'.',
457 p99_getopt_enum_p00PLUS =
'+',
458 p99_getopt_enum_p00QUOTEDBL =
'"',
464 P99_SEP(P00_GETOPT_STRUCT_DECL, P00_GETOPT_CHARS);
467 int P00_GETOPT_PROCESS(help)(
void* p00_o,
char const*p00_c);
469 typedef struct p00_getopt p00_getopt_pair[2];
473 .p00_f = P00_GETOPT_PROCESS(help),
475 .p00_d =
"provide this help text",
478 .p00_f = P00_GETOPT_PROCESS(help),
479 .p00_d =
"provide this help text",
492 void p00_getopt_help_split(
size_t p00_len,
char p00_buf[p00_len],
493 char const**p00_n,
char const** p00_v) {
495 p00_pos += strspn(&p00_buf[p00_pos],
" \t\n");
496 *p00_n = &p00_buf[p00_pos];
497 p00_pos += strcspn(p00_buf,
", \t\n");
498 if (p00_buf[p00_pos]) {
499 p00_buf[p00_pos] = 0;
501 p00_pos += strspn(&p00_buf[p00_pos],
", \t\n");
502 *p00_v = &p00_buf[p00_pos];
503 p00_pos += strcspn(*p00_v,
", \t\n");
504 p00_buf[p00_pos] = 0;
509 void p00_getopt_help_count_(
bool p00_set,
510 enum p99_getopt_enum p00_c,
511 struct p00_getopt
const*p00_pc,
512 struct p00_getopt
const*p00_help,
514 register struct p00_getopt const*
const p00_p
517 : (((p00_c != p99_getopt_enum_p00h) && (p00_c != p99_getopt_enum_p00HELP))
521 if (p00_p->p00_a) p00_ns[0] =
P99_GEN_MAX(p00_ns[0], strlen(p00_p->p00_a));
522 if (p00_p->p00_t) p00_ns[1] =
P99_GEN_MAX(p00_ns[1], strlen(p00_p->p00_t));
523 char const* p00_n = p00_p->p00_n;
524 char const* p00_v =
"";
525 size_t const p00_len = 1 + (p00_p->p00_v ? strlen(p00_p->p00_v) : 0);
526 char p00_buf[p00_len];
528 strcpy(p00_buf, p00_p->p00_v);
529 p00_getopt_help_split(p00_len, p00_buf, &p00_n, &p00_v);
531 if (p00_n) p00_ns[2] =
P99_GEN_MAX(p00_ns[2], strlen(p00_n));
532 if (p00_v) p00_ns[3] =
P99_GEN_MAX(p00_ns[3], strlen(p00_v));
537 #define P00_GETOPT_HELP_COUNT_(CHAR) p00_getopt_help_count_(p00_getopt_bool## CHAR, p99_getopt_enum## CHAR, P00_GETOPT_CHAR(CHAR), p00_getopt_help, p00_ns)
539 #define P00_GETOPT_HELP_COUNT(...) P99_SEP(P00_GETOPT_HELP_COUNT_, __VA_ARGS__)
542 void p00_getopt_help_(
bool p00_set,
enum p99_getopt_enum p00_c,
struct p00_getopt
const*p00_pc,
struct p00_getopt
const*p00_help,
int const p00_ns[4]) {
543 register struct p00_getopt const*
const p00_p
546 : (((p00_c != p99_getopt_enum_p00h) && (p00_c != p99_getopt_enum_p00HELP))
550 char const*
const p00_d = p00_p->p00_d ? p00_p->p00_d :
"(not documented)";
551 char const*
const p00_t = p00_p->p00_t ? p00_p->p00_t :
"";
552 char const* p00_n =
"";
553 char const* p00_v =
"";
554 size_t const p00_len = 1 + (p00_p->p00_v ? strlen(p00_p->p00_v) : 0);
555 char p00_buf[p00_len];
557 strcpy(p00_buf, p00_p->p00_v);
558 p00_getopt_help_split(p00_len, p00_buf, &p00_n, &p00_v);
561 fprintf(stderr,
" -%c --%-*s%-*s%-*s%-*s\t%s\n",
573 fprintf(stderr,
" -%c %-*s%-*s%-*s%-*s \t%s\n",
588 #define P00_GETOPT_HELP_(CHAR) p00_getopt_help_(p00_getopt_bool## CHAR, p99_getopt_enum## CHAR, P00_GETOPT_CHAR(CHAR), p00_getopt_help, p00_ns)
591 #define P00_GETOPT_HELP(...) P99_SEP(P00_GETOPT_HELP_, __VA_ARGS__)
602 #define P99_GETOPT_SYNOPSIS(LINE) \
603 P00_TENTATIVE_DEF(p00_getopt_synopsis) char const*const p00_getopt_synopsis = { LINE }
612 #define P99_GETOPT_CALLBACK(CALLBACK) \
613 P00_TENTATIVE_DEF(p00_getopt_callback) p99_callback_void_func*const p00_getopt_callback = (CALLBACK)
617 int P00_GETOPT_PROCESS(help)(
void* p00_o,
char const*p00_c) {
619 int p00_ns[4] = { 0 };
620 P00_GETOPT_HELP_COUNT(P00_GETOPT_CHARS);
621 if (p00_getopt_progname || p00_getopt_synopsis)
622 fprintf(stderr,
"%s%s%s\n\toptions:\n",
623 (p00_getopt_progname ? p00_getopt_progname :
""),
624 (p00_getopt_progname && p00_getopt_synopsis ?
" -- " :
""),
625 (p00_getopt_synopsis ? p00_getopt_synopsis :
""));
626 fprintf(stderr,
"short %-*s%-*s%-*s%-*s \t%s\n",
636 P00_GETOPT_HELP(P00_GETOPT_CHARS);
637 if (p00_getopt_callback) {
638 fputs(
"-----------------------------------------------------\n", stderr);
639 p00_getopt_callback();
644 #define P00_GETOPT_INITIALIZE_(CHAR) \
645 case p99_getopt_enum## CHAR: { \
646 register struct p00_getopt const*const p00_p \
647 = (((p99_getopt_enum## CHAR != p99_getopt_enum_p00h) && (p99_getopt_enum## CHAR != p99_getopt_enum_p00HELP)) \
648 ? (P00_GETOPT_BOOL(CHAR) ? P00_GETOPT_CHAR(CHAR) : 0) \
649 : (P00_GETOPT_BOOL(CHAR) ? P00_GETOPT_CHAR(CHAR) : p00_getopt_help)); \
651 void* p00_o = p00_p->p00_o; \
652 p00_used = p00_p->p00_f(p00_o, p00_str); \
653 if (p00_used >= 0) break; \
655 p00_err0 = "unparsable argument"; \
659 #define P00_GETOPT_INITIALIZE(...) P99_SER(P00_GETOPT_INITIALIZE_, __VA_ARGS__)
661 #define P00_GETOPT_ARRAY_ASSIGN_(CHAR) p00_A[p99_getopt_enum## CHAR] = (P00_GETOPT_BOOL(CHAR) ? P00_GETOPT_CHAR(CHAR) : 0)
663 #define P00_GETOPT_ARRAY_ASSIGN(...) P99_SEP(P00_GETOPT_ARRAY_ASSIGN_, __VA_ARGS__)
665 #define P00_GETOPT_ARRAY_INIT_(CHAR) [p99_getopt_enum## CHAR] = 0
667 #define P00_GETOPT_ARRAY_INIT(...) P99_SEQ(P00_GETOPT_ARRAY_INIT_, __VA_ARGS__)
669 #define P00_GETOPT_ALLOCATIONS(CHAR) [p99_getopt_enum## CHAR] = 0
674 void p00_getopt_atexit(
void) {
675 size_t p00_len = p00_getopt_allocations - p00_getopt_allocations_base;
676 void** p00_tmp = p00_getopt_allocations_base;
677 p00_getopt_allocations = 0;
678 p00_getopt_allocations_base = 0;
679 P99_DO(
size_t, p00_i, 0, p00_len)
680 free(p00_tmp[p00_i]);
685 struct p00_getopt const*
686 p00_getopt_find_alias(
char const* p00_al,
size_t p00_size,
struct p00_getopt
const* p00_A[p00_size]) {
688 struct p00_getopt const*
const p00_el = &(
struct p00_getopt const) { .p00_a = p00_al, };
689 register struct p00_getopt const*
const* p00_p =
bsearch_s(&p00_el,
695 if (p00_p && (*p00_p)) {
697 while (p00_p != p00_A && !p00_getopt_subcomp(&p00_el, p00_p - 1, 0)) --p00_p;
701 if (!p00_getopt_comp(&p00_el, p00_p, 0) || (p00_getopt_subcomp(&p00_el, p00_p+1, 0) < 0))
709 p00_getopt_diagnostic(
char const* p00_err0,
char const* p00_err1,
char const* p00_err2) {
710 fprintf(stderr,
"Warning: %s for \"--%s\" with \"%s\"\n",
735 void p99_getopt_initialize(
int * p00_argc,
char***p00_argv) {
736 p00_getopt_allocations_base =
P99_CALLOC(
void*, *p00_argc);
737 p00_getopt_allocations = p00_getopt_allocations_base;
738 atexit(p00_getopt_atexit);
739 char const* p00_err0 = 0;
740 char const* p00_err1 = 0;
741 char const* p00_err2 = 0;
744 static struct p00_getopt const* p00_A[] = {
745 P00_GETOPT_ARRAY_INIT(P00_GETOPT_CHARS),
747 memset(p00_A, 0,
sizeof p00_A);
749 P00_GETOPT_ARRAY_ASSIGN(P00_GETOPT_CHARS);
753 register struct p00_getopt const** p00_up = p00_A;
754 while (*p00_up) ++p00_up;
758 if (p00_getopt_find_alias(
"help", p00_up-p00_A, p00_A)) {
759 *p00_up = &p00_getopt_help[1];
761 *p00_up = &p00_getopt_help[0];
764 qsort_s(p00_A, p00_up - p00_A,
sizeof *p00_A, p00_getopt_comp, 0);
766 p00_getopt_progname = (*p00_argv)[0];
772 for (; p00_arg < *p00_argc && (*p00_argv)[p00_arg]; ++p00_arg) {
775 if ((*p00_argv)[p00_arg][0] !=
'-') {
779 if ((*p00_argv)[p00_arg][1] ==
'-' && !(*p00_argv)[p00_arg][2]) {
783 for (
char const* p00_str = (*p00_argv)[p00_arg] + 1; p00_str && p00_str[0];) {
785 bool p00_extra =
false;
786 char p00_C = p00_str[0];
791 p00_str = (*p00_argv)[p00_arg + 1];
792 if (p00_str) p00_extra =
true;
794 static char p00_err[2];
801 P00_GETOPT_INITIALIZE(P00_GETOPT_CHARS)
807 char p00_al[strlen(p00_str) + 1];
808 strcpy(p00_al, p00_str);
809 char* p00_ar = strchr(p00_al,
'=');
816 p00_ar = (*p00_argv)[p00_arg + 1];
822 register struct p00_getopt const*
const p00_p = p00_getopt_find_alias(p00_al, p00_up - p00_A, p00_A);
824 void* p00_o = p00_p->p00_o;
825 p00_used = p00_p->p00_f(p00_o, p00_ar);
826 if (p00_used >= 0)
break;
827 else p00_getopt_diagnostic(
"unparsable argument", p00_err1, p00_err2);
828 }
else p00_getopt_diagnostic(
"no or ambiguous option alias", p00_err1, p00_err2);
835 p00_arg += p00_extra;
841 if (p00_extra)
break;
846 if (p00_err0) p00_getopt_diagnostic(p00_err0, p00_err1, p00_err2);
849 *p00_argc -= (p00_arg - 1);
850 for (
int p00_n = 1; p00_n < *p00_argc; ++p00_n) {
851 (*p00_argv)[p00_n] = (*p00_argv)[p00_n + (p00_arg - 1)];