P99
p99_getopt.h
Go to the documentation of this file.
1 /* This may look like nonsense, but it really is -*- mode: C; coding: utf-8 -*- */
2 /* */
3 /* Except for parts copied from previous work and as explicitly stated below, */
4 /* the author and copyright holder for this work is */
5 /* (C) copyright 2013-2014 Jens Gustedt, INRIA, France */
6 /* */
7 /* This file is free software; it is part of the P99 project. */
8 /* */
9 /* Licensed under the Apache License, Version 2.0 (the "License"); */
10 /* you may not use this file except in compliance with the License. */
11 /* You may obtain a copy of the License at */
12 /* */
13 /* http://www.apache.org/licenses/LICENSE-2.0 */
14 /* */
15 /* Unless required by applicable law or agreed to in writing, software */
16 /* distributed under the License is distributed on an "AS IS" BASIS, */
17 /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
18 /* See the License for the specific language governing permissions and */
19 /* limitations under the License. */
20 /* */
21 #ifndef P99_GETOPT_H_
22 # define P99_GETOPT_H_
23 
29 #include "p99_qsort.h"
30 #include "p99_callback.h"
31 
32 typedef int p00_getopt_process_type(void*, char const*);
33 
34 struct p00_getopt {
35  void*const p00_o;
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;
42 };
43 
44 P99_TENTATIVE_DEC(void**, p00_getopt_allocations);
45 
46 #define P00_GETOPT_PROCESS(T) P99_PASTE2(p00_getopt_process_, T)
47 
48 #define P00_GETOPT_CHAR(CHAR) p00_getopt_char## CHAR
49 #define P00_GETOPT_BOOL(CHAR) p00_getopt_bool## CHAR
50 
51 P99_WEAK(p00_getopt_comp)
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);
58  else
59  return -1;
60  else if (p00_B && (*p00_B) && (*p00_B)->p00_a)
61  return 1;
62  else
63  return 0;
64 }
65 
66 P99_WEAK(p00_getopt_subcomp)
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);
74  } else
75  return -1;
76  else if (p00_B && (*p00_B) && (*p00_B)->p00_a)
77  return 1;
78  else
79  return 0;
80 }
81 
82 
83 #define P00_GETOPT_SIGNED(T) \
84 static_inline \
85 int P00_GETOPT_PROCESS(T)(void* p00_o, char const* p00_c) { \
86  T*p00_O = p00_o; \
87  if (p00_c && p00_c[0]) { \
88  char* endptr = 0; \
89  *p00_O = strtoll(p00_c, &endptr, 0); \
90  if (endptr) return strlen(p00_c); \
91  } \
92  return -1; \
93 }
94 
95 P99_SER(P00_GETOPT_SIGNED, \
96  schar, \
97  short, \
98  int, \
99  long, \
100  llong \
101  )
102 
103 #define P00_GETOPT_UNSIGNED(T) \
104 static_inline \
105 int P00_GETOPT_PROCESS(T)(void* p00_o, char const* p00_c) { \
106  T*p00_O = p00_o; \
107  if (p00_c && p00_c[0]) { \
108  char* endptr = 0; \
109  *p00_O = strtoull(p00_c, &endptr, 0); \
110  if (endptr) return strlen(p00_c); \
111  } \
112  return -1; \
113 }
114 
115 P99_SER(P00_GETOPT_UNSIGNED, \
116  uchar, \
117  ushort, \
118  unsigned, \
119  ulong, \
120  ullong)
121 
123 int P00_GETOPT_PROCESS(char)(void* p00_o, char const*p00_c) {
124  char*p00_O = p00_o;
125  if (p00_c && p00_c[0]) {
126  *p00_O = p00_c[0];
127  return 1;
128  }
129  return -1;
130 }
131 
133 int P00_GETOPT_PROCESS(_Bool)(void* p00_o, char const*p00_c) {
134  P99_UNUSED(p00_c);
135  bool*p00_O = p00_o;
136  *p00_O = !*p00_O;
137  return 0;
138 }
139 
141 int P00_GETOPT_PROCESS(char_cptr)(void* p00_o, char const*p00_c) {
142  char const**p00_O = p00_o;
143  if (p00_c) {
144  *p00_getopt_allocations = P99_STRDUP(p00_c);
145  *p00_O = *p00_getopt_allocations;
146  ++p00_getopt_allocations;
147  return strlen(p00_c) + 1;
148  }
149  return -1;
150 }
151 
152 #define P00_GETOPT_FLOAT(T) \
153 static_inline \
154 int P00_GETOPT_PROCESS(T)(void* p00_o, char const* p00_c) { \
155  T*p00_O = p00_o; \
156  if (p00_c && p00_c[0]) { \
157  char* endptr = 0; \
158  *p00_O = strtold(p00_c, &endptr); \
159  if (endptr) return strlen(p00_c); \
160  } \
161  return -1; \
162 }
163 
164 P99_SER(P00_GETOPT_FLOAT, \
165  float, \
166  double, \
167  ldouble)
168 
169 
170 #define P00_GETOPT_PROCESS_CHOOSE_(T) (T, P00_GETOPT_PROCESS(T))
171 
172 #define P00_GETOPT_PROCESS_CHOOSE(...) P99_SEQ(P00_GETOPT_PROCESS_CHOOSE_, __VA_ARGS__)
173 
174 #define P00_GETOPT_DECLARE(CHAR, T, TS, DEFS, NAME, DEF, ALIAS, DOC, ...) \
175  extern T NAME; \
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){ \
179  .p00_o = &(NAME), \
180  .p00_f = P99_GENERIC(NAME, 0, __VA_ARGS__), \
181  .p00_a = (ALIAS), \
182  .p00_d = (DOC), \
183  .p00_t = TS, \
184  .p00_n = #NAME, \
185  .p00_v = DEFS, \
186  }
187 
188 #define P00_GETOPT_DECLARE_(...) P00_GETOPT_DECLARE(__VA_ARGS__)
189 
190 #ifdef P00_DOXYGEN
191 
256 P00_DOCUMENT_TYPE_ARGUMENT(P99_GETOPT_DECLARE, 1)
257 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_GETOPT_DECLARE, 2)
258 #define P99_GETOPT_DECLARE(CHAR, T, NAME, DEF, ALIAS, DOC)
259 
271 P00_DOCUMENT_TYPE_ARGUMENT(P99_GETOPT_DEFINE, 1)
272 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_GETOPT_DEFINE, 2)
273 #define P99_GETOPT_DEFINE(CHAR, T, NAME, DEF, ALIAS, DOC)
274 #else
275 P00_DOCUMENT_TYPE_ARGUMENT(P99_GETOPT_DECLARE, 1)
276 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_GETOPT_DECLARE, 2)
277 #define P99_GETOPT_DECLARE(CHAR, T, ...) \
278 P00_GETOPT_DECLARE_(_p00##CHAR, \
279  T, \
280  #T, \
281  #__VA_ARGS__, \
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) \
287  (__VA_ARGS__, 0) \
288  (__VA_ARGS__))), \
289  P00_GETOPT_PROCESS_CHOOSE(P99_STD_REAL_TYPES, char_cptr))
290 
291 
292 #define P00_GETOPT_DEFINE(CHAR, T, NAME, DEF, ALIAS, DOC) \
293 T NAME = (DEF)
294 
295 P00_DOCUMENT_TYPE_ARGUMENT(P99_GETOPT_DEFINE, 1)
296 P00_DOCUMENT_IDENTIFIER_ARGUMENT(P99_GETOPT_DEFINE, 2)
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__))))
305 #endif
306 
307 
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))
311 
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_, \
323  _p00AMPERSAND, \
324  _p00APOSTROPHE, \
325  _p00ASTERISK, \
326  _p00AT, \
327  _p00BACKSLASH, \
328  _p00BAR, \
329  _p00BRACELEFT, \
330  _p00BRACERIGHT, \
331  _p00BRACKETLEFT, \
332  _p00BRACKETRIGHT, \
333  _p00CARRET, \
334  _p00COLON, \
335  _p00COMMA, \
336  _p00DOLLAR, \
337  _p00EQUAL, \
338  _p00EXLAM, \
339  _p00GRAVE, \
340  _p00GREATER, \
341  _p00HASH, \
342  _p00HELP, \
343  _p00LESS, \
344  _p00PARENLEFT, \
345  _p00PARENRIGHT, \
346  _p00PERCENT, \
347  _p00PERIOD, \
348  _p00PLUS, \
349  _p00QUOTEDBL, \
350  _p00SEMICOLON, \
351  _p00SLASH, \
352  _p00TILDE
353 
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 = '"',
462 };
463 
464 P99_SEP(P00_GETOPT_STRUCT_DECL, P00_GETOPT_CHARS);
465 
467 int P00_GETOPT_PROCESS(help)(void* p00_o, char const*p00_c);
468 
469 typedef struct p00_getopt p00_getopt_pair[2];
470 
471 P99_TENTATIVE_DEC(p00_getopt_pair, const p00_getopt_help) = {
472  {
473  .p00_f = P00_GETOPT_PROCESS(help),
474  .p00_a = "help",
475  .p00_d = "provide this help text",
476  },
477  {
478  .p00_f = P00_GETOPT_PROCESS(help),
479  .p00_d = "provide this help text",
480  }
481 };
482 
483 P99_TENTATIVE_DEC(char const*, p00_getopt_progname);
484 
485 
486 /* Split the "va_arg" argument of the P99_GETOPT macros into the name
487  of the variable and the default value for that variable. We have to
488  take the raw string produced from that argument such that we see
489  the string as the programmer put it, not as the preprocessor
490  expanded it. */
492 void p00_getopt_help_split(size_t p00_len, char p00_buf[p00_len],
493  char const**p00_n, char const** p00_v) {
494  size_t p00_pos = 0;
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;
500  ++p00_pos;
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;
505  } else *p00_v = "0";
506 }
507 
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,
513  int p00_ns[4]) {
514  register struct p00_getopt const*const p00_p
515  = (p00_set
516  ? p00_pc
517  : (((p00_c != p99_getopt_enum_p00h) && (p00_c != p99_getopt_enum_p00HELP))
518  ? 0
519  : p00_help));
520  if (p00_p) {
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];
527  if (p00_p->p00_v) {
528  strcpy(p00_buf, p00_p->p00_v);
529  p00_getopt_help_split(p00_len, p00_buf, &p00_n, &p00_v);
530  }
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));
533  }
534 }
535 
536 
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)
538 
539 #define P00_GETOPT_HELP_COUNT(...) P99_SEP(P00_GETOPT_HELP_COUNT_, __VA_ARGS__)
540 
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
544  = (p00_set
545  ? p00_pc
546  : (((p00_c != p99_getopt_enum_p00h) && (p00_c != p99_getopt_enum_p00HELP))
547  ? 0
548  : p00_help));
549  if (p00_p) {
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];
556  if (p00_p->p00_v) {
557  strcpy(p00_buf, p00_p->p00_v);
558  p00_getopt_help_split(p00_len, p00_buf, &p00_n, &p00_v);
559  }
560  if (p00_p->p00_a)
561  fprintf(stderr, " -%c --%-*s%-*s%-*s%-*s\t%s\n",
562  p00_c,
563  p00_ns[0]+2,
564  p00_p->p00_a,
565  p00_ns[1]+2,
566  p00_t,
567  p00_ns[2]+2,
568  p00_n,
569  p00_ns[3]+2,
570  p00_v,
571  p00_d);
572  else
573  fprintf(stderr, " -%c %-*s%-*s%-*s%-*s \t%s\n",
574  p00_c,
575  p00_ns[0]+2,
576  "",
577  p00_ns[1]+2,
578  p00_t,
579  p00_ns[2]+2,
580  p00_n,
581  p00_ns[3]+2,
582  p00_v,
583  p00_d);
584  }
585 }
586 
587 
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)
589 
590 
591 #define P00_GETOPT_HELP(...) P99_SEP(P00_GETOPT_HELP_, __VA_ARGS__)
592 
593 P99_TENTATIVE_DEC(char const*const, p00_getopt_synopsis);
594 
595 P99_TENTATIVE_DEC(p99_callback_void_func*const, p00_getopt_callback);
596 
602 #define P99_GETOPT_SYNOPSIS(LINE) \
603 P00_TENTATIVE_DEF(p00_getopt_synopsis) char const*const p00_getopt_synopsis = { LINE }
604 
605 
612 #define P99_GETOPT_CALLBACK(CALLBACK) \
613 P00_TENTATIVE_DEF(p00_getopt_callback) p99_callback_void_func*const p00_getopt_callback = (CALLBACK)
614 
615 
617 int P00_GETOPT_PROCESS(help)(void* p00_o, char const*p00_c) {
618  P99_UNUSED(p00_o);
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",
627  p00_ns[0]+4,
628  "long",
629  p00_ns[1]+2,
630  "type",
631  p00_ns[2]+2,
632  "name",
633  p00_ns[3]+2,
634  "default",
635  "");
636  P00_GETOPT_HELP(P00_GETOPT_CHARS);
637  if (p00_getopt_callback) {
638  fputs("-----------------------------------------------------\n", stderr);
639  p00_getopt_callback();
640  }
641  exit(EXIT_FAILURE);
642 }
643 
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)); \
650  if (p00_p) { \
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; \
654  } \
655  p00_err0 = "unparsable argument"; \
656  goto P00_REARANGE; \
657  }
658 
659 #define P00_GETOPT_INITIALIZE(...) P99_SER(P00_GETOPT_INITIALIZE_, __VA_ARGS__)
660 
661 #define P00_GETOPT_ARRAY_ASSIGN_(CHAR) p00_A[p99_getopt_enum## CHAR] = (P00_GETOPT_BOOL(CHAR) ? P00_GETOPT_CHAR(CHAR) : 0)
662 
663 #define P00_GETOPT_ARRAY_ASSIGN(...) P99_SEP(P00_GETOPT_ARRAY_ASSIGN_, __VA_ARGS__)
664 
665 #define P00_GETOPT_ARRAY_INIT_(CHAR) [p99_getopt_enum## CHAR] = 0
666 
667 #define P00_GETOPT_ARRAY_INIT(...) P99_SEQ(P00_GETOPT_ARRAY_INIT_, __VA_ARGS__)
668 
669 #define P00_GETOPT_ALLOCATIONS(CHAR) [p99_getopt_enum## CHAR] = 0
670 
671 P99_TENTATIVE_DEC(void**, p00_getopt_allocations_base);
672 
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]);
681  free(p00_tmp);
682 }
683 
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]) {
687  /* Search for a matching alias in the array */
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,
690  p00_A,
691  p00_size,
692  sizeof *p00_A,
693  p00_getopt_subcomp,
694  0);
695  if (p00_p && (*p00_p)) {
696  /* Now search if there are several matches. */
697  while (p00_p != p00_A && !p00_getopt_subcomp(&p00_el, p00_p - 1, 0)) --p00_p;
698  /* An exact match must always come first and is preferred.
699  If the first is not an exact match, second shouldn't be
700  a partial match. */
701  if (!p00_getopt_comp(&p00_el, p00_p, 0) || (p00_getopt_subcomp(&p00_el, p00_p+1, 0) < 0))
702  return *p00_p;
703  }
704  return 0;
705 }
706 
708 void
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",
711  p00_err0,
712  p00_err1,
713  p00_err2);
714 }
715 
734 static inline
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;
742  /* Create a sorted array with all the aliases, such that we may then
743  search for a matching key. */
744  static struct p00_getopt const* p00_A[] = {
745  P00_GETOPT_ARRAY_INIT(P00_GETOPT_CHARS),
746  };
747  memset(p00_A, 0, sizeof p00_A);
748  {
749  P00_GETOPT_ARRAY_ASSIGN(P00_GETOPT_CHARS);
750  }
751  qsort_s(p00_A, P99_ALEN(p00_A), sizeof *p00_A, p00_getopt_comp, 0);
752 
753  register struct p00_getopt const** p00_up = p00_A;
754  while (*p00_up) ++p00_up;
755 
756  /* If --help is already used by the application code cancel the
757  mention of --help in p00_getopt_help otherwise insert the alias. */
758  if (p00_getopt_find_alias("help", p00_up-p00_A, p00_A)) {
759  *p00_up = &p00_getopt_help[1];
760  } else {
761  *p00_up = &p00_getopt_help[0];
762  }
763  ++p00_up;
764  qsort_s(p00_A, p00_up - p00_A, sizeof *p00_A, p00_getopt_comp, 0);
765 
766  p00_getopt_progname = (*p00_argv)[0];
767 
768  /* Now comes the main processing loop. One character arguments may
769  be aggregated into one option, that is why this loop looks a bit
770  scary. */
771  int p00_arg = 1;
772  for (; p00_arg < *p00_argc && (*p00_argv)[p00_arg]; ++p00_arg) {
773  /* All options must start with a dash, otherwise this finishes
774  option processing. */
775  if ((*p00_argv)[p00_arg][0] != '-') {
776  goto P00_REARANGE;
777  } else {
778  /* A "--" without anything else finishes option processing. */
779  if ((*p00_argv)[p00_arg][1] == '-' && !(*p00_argv)[p00_arg][2]) {
780  ++p00_arg;
781  goto P00_REARANGE;
782  }
783  for (char const* p00_str = (*p00_argv)[p00_arg] + 1; p00_str && p00_str[0];) {
784  int p00_used = 0;
785  bool p00_extra = false;
786  char p00_C = p00_str[0];
787  ++p00_str;
788  /* If there was nothing left in the option, take the next one
789  as an argument. */
790  if (!p00_str[0]) {
791  p00_str = (*p00_argv)[p00_arg + 1];
792  if (p00_str) p00_extra = true;
793  }
794  static char p00_err[2];
795  p00_err[0] = p00_C;
796  p00_err1 = p00_err;
797  p00_err2 = p00_str;
798  switch (p00_C) {
799  /* The cases for the one-character options are hidden
800  here. */
801  P00_GETOPT_INITIALIZE(P00_GETOPT_CHARS)
802  /* If the initial string was "--" this announces a long
803  option. */
804  case '-': {
805  /* First split up the option string into option and
806  argument, if possible. */
807  char p00_al[strlen(p00_str) + 1];
808  strcpy(p00_al, p00_str);
809  char* p00_ar = strchr(p00_al, '=');
810  if (p00_ar) {
811  *p00_ar = 0;
812  ++p00_ar;
813  } else {
814  /* If not, suppose that the argument has been given in
815  the next option. */
816  p00_ar = (*p00_argv)[p00_arg + 1];
817  p00_extra = true;
818  }
819  p00_err1 = p00_al;
820  p00_err2 = p00_ar;
821  /* Search for a matching alias in the array */
822  register struct p00_getopt const*const p00_p = p00_getopt_find_alias(p00_al, p00_up - p00_A, p00_A);
823  if (p00_p) {
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);
829  goto P00_REARANGE;
830  }
831  }
832  /* If this used the option argument and the argument was found
833  in the next element of argv, skip that. */
834  if (p00_used > 0) {
835  p00_arg += p00_extra;
836  break;
837  } else
838  /* Otherwise if this was a flag and we tried to obtain from
839  the next element in argv, check that element again, it
840  might be an option, too. */
841  if (p00_extra) break;
842  }
843  }
844  }
845 P00_REARANGE:
846  if (p00_err0) p00_getopt_diagnostic(p00_err0, p00_err1, p00_err2);
847  /* At the end of the processing, shift all unused options down, such
848  that they appear at front in the argv array. */
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)];
852  }
853 }
854 
855 #endif /* !P99_GETOPT_H_ */
char_cptr
char const * char_cptr
a const pointer to char
Definition: p99_typenames.h:91
P99_GETOPT_DECLARE
#define P99_GETOPT_DECLARE(CHAR, T, NAME, DEF, ALIAS, DOC)
P99_SER
#define P99_SER(MACRO,...)
Apply the macro MACRO to the rest of the argument list.
Definition: p99_for.h:264
P99_SEP
#define P99_SEP(MACRO,...)
Apply the macro MACRO to the rest of the argument list.
Definition: p99_for.h:297
P99_ALEN
#define P99_ALEN(ARR, N)
Produce the length of the argument array ARR in terms of number of elements.
Definition: p99_for.h:678
p99_qsort.h
An implementation of a sorting routine.
ulong
unsigned long ulong
a ‘one token’ abreviation for unsigned long
Definition: p99_typenames.h:38
schar
signed char schar
a ‘one token’ abreviation for signed char
Definition: p99_typenames.h:45
ldouble
long double ldouble
a ‘one token’ abreviation for long double
Definition: p99_typenames.h:49
P99_STRDUP
#define P99_STRDUP(...)
Concatenate all arguments.
Definition: p99_map.h:180
static_inline
#define static_inline
Definition: p99_compiler.h:484
p99_inline
#define p99_inline
Try to force a function always to be inlined.
Definition: p99_compiler.h:496
P99_GETOPT_DEFINE
#define P99_GETOPT_DEFINE(CHAR, T, NAME, DEF, ALIAS, DOC)
P99_UNUSED
#define P99_UNUSED(...)
check if the list of expressions is syntactically valid but don't evaluate it
Definition: p99_enum.h:215
P99_GEN_MAX
#define P99_GEN_MAX(A, B)
Definition: p99_generic.h:1002
P99_WEAK
#define P99_WEAK(...)
Declare a symbol to be weak such that it can be provided several times without error.
Definition: p99_compiler.h:561
P99_CALLOC
#define P99_CALLOC(T, N)
A type oriented replacement for calloc.
Definition: p99_new.h:268
ullong
unsigned long long ullong
a ‘one token’ abreviation for unsigned long long
Definition: p99_typenames.h:48
p99_getopt_enum_p00SLASH
P00_CLAUSE2 p99_getopt_enum_p00SLASH
Definition: p99_getopt.h:451
P99_DO
#define P99_DO(TYPE, VAR, LOW, LEN, INCR)
A fortran like do-loop with bounds that are fixed at the beginning.
Definition: p99_for.h:882
P99_TENTATIVE_DEC
#define P99_TENTATIVE_DEC(T, NAME)
A tentative declaration of an object NAME of type T.
Definition: p99_compiler.h:587
llong
long long llong
a ‘one token’ abreviation for long long
Definition: p99_typenames.h:46
qsort_s
#define qsort_s(B, N, S, CMP, CTX)
A generic sorting routine.
Definition: p99_qsort.h:496
p99_getopt_enum_p00SEMICOLON
P00_CLAUSE2 p99_getopt_enum_p00SEMICOLON
Definition: p99_getopt.h:450
p99_callback_stack::p99_callback_void_func
void p99_callback_void_func(void)
Function type for a callback without arguments.
Definition: p99_callback.h:51
ushort
unsigned short ushort
a ‘one token’ abreviation for unsigned short
Definition: p99_typenames.h:42
p99_callback.h
p99_getopt_enum_p00TILDE
P00_CLAUSE2 p99_getopt_enum_p00TILDE
Definition: p99_getopt.h:452
bsearch_s
#define bsearch_s(...)
uchar
unsigned char uchar
a ‘one token’ abreviation for unsigned char
Definition: p99_typenames.h:44