1 /* ################################################################### */
2 /* This Source Code Form is subject to the terms of the Mozilla Public */
3 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
4 /* file, You can obtain one at https://mozilla.org/MPL/2.0/.           */
5 /* ################################################################### */
6 
7 #include <errno.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <ctype.h>
13 #include <sys/types.h>
14 #include <regex.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include "ctxopt.h"
18 
19 /* ************************ */
20 /* Static global variables. */
21 /* ************************ */
22 
23 static void * contexts_bst;
24 static void * options_bst;
25 
26 state_t * cur_state;
27 
28 /* Prototypes */
29 
30 /* ************************** */
31 /* Fatal messages prototypes. */
32 /* ************************** */
33 
34 static void (**err_functions)(errors e, state_t * state);
35 
36 static void
37 fatal_internal(const char * format, ...);
38 
39 static void
40 fatal(errors e, char * errmsg);
41 
42 static int    user_rc;      /* Used by various callback functions. */
43 static int    user_value;   /* Used by various callback functions. */
44 static char * user_string;  /* Used by various callback functions. */
45 static char * user_string2; /* Used by various callback functions. */
46 static void * user_object;  /* Used by various callback functions. */
47 
48 /* ************************************ */
49 /* Memory management static prototypes. */
50 /* ************************************ */
51 
52 static void *
53 xmalloc(size_t size);
54 
55 static void *
56 xcalloc(size_t num, size_t size);
57 
58 static void *
59 xrealloc(void * ptr, size_t size);
60 
61 static char *
62 xstrdup(const char * p);
63 
64 static char *
65 xstrndup(const char * str, size_t len);
66 
67 /* ********************** */
68 /* BST static prototypes. */
69 /* ********************** */
70 
71 typedef struct bst_s bst_t;
72 
73 typedef enum
74 {
75   preorder,
76   postorder,
77   endorder,
78   leaf
79 } walk_order_e;
80 
81 #if 0 /* Unused yet. */
82 static void *
83 bst_delete(const void * vkey, void ** vrootp,
84            int (*compar)(const void *, const void *));
85 #endif
86 
87 static void
88 bst_destroy(void * vrootp, void (*clean)(void *));
89 
90 static void *
91 bst_find(const void * vkey, void * const * vrootp,
92          int (*compar)(const void *, const void *));
93 
94 static void *
95 bst_search(void * vkey, void ** vrootp,
96            int (*compar)(const void *, const void *));
97 
98 static void
99 bst_walk_recurse(const bst_t * root,
100                  void (*action)(const void *, walk_order_e, int), int level);
101 
102 static void
103 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int));
104 
105 /* ****************************** */
106 /* Linked list static prototypes. */
107 /* ****************************** */
108 
109 typedef struct ll_node_s ll_node_t;
110 typedef struct ll_s      ll_t;
111 
112 static void
113 ll_append(ll_t * const list, void * const data);
114 
115 static void
116 ll_prepend(ll_t * const list, void * const data);
117 
118 static void
119 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data);
120 
121 static void
122 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data);
123 
124 static int
125 ll_delete(ll_t * const list, ll_node_t * node);
126 
127 #if 0 /* Unused yet. */
128 static ll_node_t *
129 ll_find(ll_t * const, void * const, int (*)(const void *, const void *));
130 #endif
131 
132 static void
133 ll_init(ll_t * list);
134 
135 static ll_node_t *
136 ll_new_node(void);
137 
138 static ll_t *
139 ll_new(void);
140 
141 static void
142 ll_free(ll_t * const list, void (*)(void *));
143 
144 static void
145 ll_destroy(ll_t * const list, void (*)(void *));
146 
147 static int
148 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array);
149 
150 /* ************************** */
151 /* Various static prototypes. */
152 /* ************************** */
153 
154 static void
155 ltrim(char * str, const char * trim_str);
156 
157 static void
158 rtrim(char * str, const char * trim_str, size_t min);
159 
160 static int
161 strchrcount(char * str, char c);
162 
163 static int
164 strpref(char * s1, char * s2);
165 
166 static int
167 stricmp(const char * s1, const char * s2);
168 
169 static char *
170 xstrtok_r(char * str, const char * delim, char ** end);
171 
172 static int
173 eval_yes(char * value, int * invalid);
174 
175 static char *
176 get_word(char * str, char * buf, size_t len);
177 
178 /* ************************* */
179 /* ctxopt static prototypes. */
180 /* ************************* */
181 
182 typedef struct flags_s      flags_t;
183 typedef struct opt_s        opt_t;
184 typedef struct par_s        par_t;
185 typedef struct ctx_s        ctx_t;
186 typedef struct constraint_s constraint_t;
187 typedef struct ctx_inst_s   ctx_inst_t;
188 typedef struct opt_inst_s   opt_inst_t;
189 typedef struct seen_opt_s   seen_opt_t;
190 typedef struct req_s        req_t;
191 
192 static char *
193 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
194 
195 static int
196 ctx_compare(const void * c1, const void * c2);
197 
198 static void
199 ctx_free(void * o);
200 
201 static void
202 ctx_inst_free(void * ci);
203 
204 static void
205 opt_inst_free(void * oi);
206 
207 static int
208 seen_opt_compare(const void * so1, const void * so2);
209 
210 static void
211 incomp_bst_free(void * b);
212 
213 static void
214 req_free(void * r);
215 
216 static void
217 seen_opt_free(void * seen_opt);
218 
219 static int
220 opt_compare(const void * o1, const void * o2);
221 
222 static void
223 opt_free(void * o);
224 
225 static int
226 par_compare(const void * a1, const void * a2);
227 
228 static void
229 par_free(void * p);
230 
231 static void
232 constraint_free(void * cstr);
233 
234 static ctx_t *
235 locate_ctx(char * name);
236 
237 static opt_t *
238 locate_opt(char * name);
239 
240 static par_t *
241 locate_par(char * name, ctx_t * ctx);
242 
243 static void
244 print_before_constraints(ll_t * list);
245 
246 static void
247 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
248               int * has_rule, int * has_generic_arg, int * has_ctx_change,
249               int * has_early_eval);
250 static void
251 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
252                    int has_optional, int has_ellipsis, int has_rule);
253 static void
254 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
255 
256 static void
257 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
258 
259 static void
260 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
261 
262 static void
263 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
264 
265 static void
266 bst_match_par_cb(const void * node, walk_order_e kind, int level);
267 
268 static void
269 match_prefix_cb(const void * node, walk_order_e kind, int level);
270 
271 static int
272 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
273 
274 static int
275 opt_parse(char * s, opt_t ** opt);
276 
277 static int
278 init_opts(char * spec, ctx_t * ctx);
279 
280 static int
281 ctxopt_build_cmdline_list(int nb_words, char ** words);
282 
283 static int
284 opt_set_parms(char * opt_name, char * par_str);
285 
286 static ctx_inst_t *
287 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
288 
289 static void
290 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
291 
292 /* ****************************** */
293 /* Fatal messages implementation. */
294 /* ****************************** */
295 
296 /* =================================================================== */
297 /* Fatal error function used when a fatal condition is encountered.    */
298 /* This function is reserved for the ctxopt internal usage.            */
299 /*                                                                     */
300 /* format : printf like format.                                        */
301 /* ...    : remaining arguments interpreted using the format argument. */
302 /* =================================================================== */
303 static void
fatal_internal(const char * format,...)304 fatal_internal(const char * format, ...)
305 {
306   va_list args;
307 
308   fprintf(stderr, "CTXOPT: ");
309 
310   va_start(args, format);
311   vfprintf(stderr, format, args);
312   fprintf(stderr, "\n");
313   va_end(args);
314 
315   exit(EXIT_FAILURE);
316 }
317 
318 /* ====================================================================== */
319 /* Generic fatal error function. This one uses the global status ctxopt   */
320 /* stored in the cur_state structure and can call custom error functions. */
321 /* registered by the users for a given error identifier if any.           */
322 /*                                                                        */
323 /* e      : Error identifier responsible of the fatal error.              */
324 /* errmsg : Users's provided string specific to the error e.              */
325 /*          Note that errmsg is not used in all cases.                    */
326 /*                                                                        */
327 /*          CTXOPTMISPAR Missing parameter.                               */
328 /*          CTXOPTREQPAR Option: all parameters in a required group are   */
329 /*                               missing.                                 */
330 /*          CTXOPTMISARG Missing argument.                                */
331 /*          CTXOPTUXPARG Unexpected argument.                             */
332 /*          CTXOPTDUPOPT Duplicated option.                               */
333 /*          CTXOPTUNKPAR Unknown parameter.                               */
334 /*          CTXOPTINCOPT Incompatible option.                             */
335 /*          CTXOPTCTEOPT Option: bad number of occurrences.               */
336 /*          CTXOPTCTLOPT Option: not enough occurrences.                  */
337 /*          CTXOPTCTGOPT Option: too many occurrence of.                  */
338 /*          CTXOPTCTEARG Arguments: bad number of occurrences.            */
339 /*          CTXOPTCTLARG Arguments: not enough occurrences.               */
340 /*          CTXOPTCTGARG Arguments: too many occurrences.                 */
341 /* ====================================================================== */
342 static void
fatal(errors e,char * errmsg)343 fatal(errors e, char * errmsg)
344 {
345   if (err_functions[e] != NULL)
346     err_functions[e](e, cur_state);
347   else
348   {
349     switch (e)
350     {
351       case CTXOPTNOERR:
352         break;
353 
354       case CTXOPTMISPAR:
355         if (cur_state->ctx_par_name != NULL)
356           fprintf(stderr,
357                   "the mandatory parameter(s) %s are missing in the context "
358                   "introduced by %s.\n",
359                   errmsg, cur_state->ctx_par_name);
360         else
361           fprintf(stderr,
362                   "The mandatory parameter(s) %s are missing "
363                   "in the main context.\n",
364                   errmsg);
365 
366         free(errmsg);
367         break;
368 
369       case CTXOPTREQPAR:
370         fprintf(stderr, errmsg, cur_state->req_opt_par_needed,
371                 cur_state->req_opt_par);
372         break;
373 
374       case CTXOPTUNXARG:
375         if (cur_state->cur_opt_par_name != NULL)
376           fprintf(stderr,
377                   "The parameter %s takes no arguments "
378                   "or has too many arguments.\n",
379                   cur_state->cur_opt_par_name);
380         break;
381 
382       case CTXOPTMISARG:
383         if (cur_state->pre_opt_par_name != NULL)
384           fprintf(stderr, "%s requires argument(s).\n",
385                   cur_state->pre_opt_par_name);
386         else
387           fprintf(stderr, "%s requires argument(s).\n",
388                   cur_state->cur_opt_par_name);
389         break;
390 
391       case CTXOPTDUPOPT:
392         if (cur_state->pre_opt_par_name != NULL)
393           fprintf(stderr,
394                   "The parameter %s can only appear once in the context "
395                   "introduced by %s.\n",
396                   cur_state->cur_opt_params, cur_state->ctx_par_name);
397         else
398           fprintf(stderr,
399                   "The parameter %s can only appear once "
400                   "in the main context.\n",
401                   cur_state->cur_opt_params);
402         break;
403 
404       case CTXOPTUNKPAR:
405         fprintf(stderr, "Unknown parameter %s.\n%s",
406                 cur_state->cur_opt_par_name, errmsg);
407         break;
408 
409       case CTXOPTINCOPT:
410         fprintf(stderr, "The parameter %s is incompatible with %s.\n",
411                 cur_state->cur_opt_par_name, errmsg);
412         break;
413 
414       case CTXOPTCTEOPT:
415         if (cur_state->ctx_par_name)
416           fprintf(stderr,
417                   "The parameter %s must appear exactly %d times "
418                   "in the context introduced by %s.\n",
419                   cur_state->cur_opt_params, cur_state->opts_count,
420                   cur_state->ctx_par_name);
421         else
422           fprintf(stderr,
423                   "The parameter %s must appear exactly %d times "
424                   "in the main context.\n",
425                   cur_state->cur_opt_params, cur_state->opts_count);
426         break;
427 
428       case CTXOPTCTLOPT:
429         if (cur_state->ctx_par_name)
430           fprintf(stderr,
431                   "The parameter %s must appear less than %d times "
432                   "in the context introduced by %s.\n",
433                   cur_state->cur_opt_params, cur_state->opts_count,
434                   cur_state->ctx_par_name);
435         else
436           fprintf(stderr,
437                   "The parameter %s must appear less than %d times "
438                   "in the main context.\n",
439                   cur_state->cur_opt_params, cur_state->opts_count);
440         break;
441 
442       case CTXOPTCTGOPT:
443         if (cur_state->ctx_par_name)
444           fprintf(stderr,
445                   "The parameter %s must appear more than %d times "
446                   "in the context introduced by %s.\n",
447                   cur_state->cur_opt_params, cur_state->opts_count,
448                   cur_state->ctx_par_name);
449         else
450           fprintf(stderr,
451                   "The parameter %s must appear more than %d times "
452                   "in the main context.\n",
453                   cur_state->cur_opt_params, cur_state->opts_count);
454         break;
455 
456       case CTXOPTCTEARG:
457         fprintf(stderr, "The parameter %s must have exactly %d arguments.\n",
458                 cur_state->cur_opt_par_name, cur_state->opt_args_count);
459         break;
460 
461       case CTXOPTCTLARG:
462         fprintf(stderr, "The parameter %s must have less than %d arguments.\n",
463                 cur_state->cur_opt_par_name, cur_state->opt_args_count);
464         break;
465 
466       case CTXOPTCTGARG:
467         fprintf(stderr, "The parameter %s must have more than %d arguments.\n",
468                 cur_state->cur_opt_par_name, cur_state->opt_args_count);
469         break;
470 
471       case CTXOPTERRSIZ:
472         break;
473     }
474   }
475 
476   /* CTXOPTUNKPAR should display the full usage to help the user follow   */
477   /* the chaining of contexts when several possible contexts have been    */
478   /* identified. Otherwise, errmsg is the empty string and the display of */
479   /* the current usage is enough.                                         */
480   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
481   if (e == CTXOPTUNKPAR && *errmsg != '\0')
482     ctxopt_disp_usage(continue_after);
483   else
484     ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
485 
486   exit(e); /* Exit with the error id e as return code. */
487 }
488 
489 /* ********************************* */
490 /* Memory management implementation. */
491 /* ********************************* */
492 
493 /* ================== */
494 /* Customized malloc. */
495 /* ================== */
496 static void *
xmalloc(size_t size)497 xmalloc(size_t size)
498 {
499   void * allocated;
500   size_t real_size;
501 
502   real_size = (size > 0) ? size : 1;
503   allocated = malloc(real_size);
504   if (allocated == NULL)
505     fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
506                    (unsigned long int)size);
507 
508   return allocated;
509 }
510 
511 /* ================== */
512 /* Customized calloc. */
513 /* ================== */
514 static void *
xcalloc(size_t n,size_t size)515 xcalloc(size_t n, size_t size)
516 {
517   void * allocated;
518 
519   n         = (n > 0) ? n : 1;
520   size      = (size > 0) ? size : 1;
521   allocated = calloc(n, size);
522   if (allocated == NULL)
523     fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
524                    (unsigned long int)size);
525 
526   return allocated;
527 }
528 
529 /* =================== */
530 /* Customized realloc. */
531 /* =================== */
532 static void *
xrealloc(void * p,size_t size)533 xrealloc(void * p, size_t size)
534 {
535   void * allocated;
536 
537   allocated = realloc(p, size);
538   if (allocated == NULL && size > 0)
539     fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
540                    (unsigned long int)size);
541 
542   return allocated;
543 }
544 
545 /* ==================================== */
546 /* strdup implementation using xmalloc. */
547 /* ==================================== */
548 static char *
xstrdup(const char * p)549 xstrdup(const char * p)
550 {
551   char * allocated;
552 
553   allocated = xmalloc(strlen(p) + 1);
554   strcpy(allocated, p);
555 
556   return allocated;
557 }
558 
559 /* =================================================== */
560 /* strndup implementation using xmalloc.               */
561 /* This version guarantees that there is a final '\0'. */
562 /* =================================================== */
563 static char *
xstrndup(const char * str,size_t len)564 xstrndup(const char * str, size_t len)
565 {
566   char * p;
567 
568   p = memchr(str, '\0', len);
569 
570   if (p)
571     len = p - str;
572 
573   p = xmalloc(len + 1);
574   memcpy(p, str, len);
575   p[len] = '\0';
576 
577   return p;
578 }
579 
580 /* *************************** */
581 /* Linked list implementation. */
582 /* *************************** */
583 
584 /* Linked list node structure. */
585 /* """"""""""""""""""""""""""" */
586 struct ll_node_s
587 {
588   void *             data;
589   struct ll_node_s * next;
590   struct ll_node_s * prev;
591 };
592 
593 /* Linked List structure. */
594 /* """""""""""""""""""""" */
595 struct ll_s
596 {
597   ll_node_t * head;
598   ll_node_t * tail;
599   long        len;
600 };
601 
602 /* ========================= */
603 /* Create a new linked list. */
604 /* ========================= */
605 static ll_t *
ll_new(void)606 ll_new(void)
607 {
608   ll_t * ret = xmalloc(sizeof(ll_t));
609   ll_init(ret);
610 
611   return ret;
612 }
613 
614 /* =============================================== */
615 /* Free all the elements of a list (make it empty) */
616 /* NULL or a custom function may be used to free   */
617 /* the sub components of the elements.             */
618 /* =============================================== */
619 static void
ll_free(ll_t * const list,void (* clean)(void *))620 ll_free(ll_t * const list, void (*clean)(void *))
621 {
622   if (list)
623     while (list->head)
624     {
625       /* Apply a custom cleaner if not NULL. */
626       /* """"""""""""""""""""""""""""""""""" */
627       if (clean)
628         clean(list->head->data);
629 
630       ll_delete(list, list->head);
631     }
632 }
633 
634 /* ==================================== */
635 /* Destroy a list and all its elements. */
636 /* ==================================== */
637 static void
ll_destroy(ll_t * list,void (* clean)(void *))638 ll_destroy(ll_t * list, void (*clean)(void *))
639 {
640   if (list)
641   {
642     ll_free(list, clean);
643     free(list);
644   }
645 }
646 
647 /* ========================= */
648 /* Initialize a linked list. */
649 /* ========================= */
650 static void
ll_init(ll_t * list)651 ll_init(ll_t * list)
652 {
653   list->head = NULL;
654   list->tail = NULL;
655   list->len  = 0;
656 }
657 
658 /* ===================================================== */
659 /* Allocate the space for a new node in the linked list. */
660 /* ===================================================== */
661 static ll_node_t *
ll_new_node(void)662 ll_new_node(void)
663 {
664   ll_node_t * ret = xmalloc(sizeof(ll_node_t));
665 
666   return ret;
667 }
668 
669 /* ==================================================================== */
670 /* Append a new node filled with its data at the end of the linked list */
671 /* The user is responsible for the memory management of the data.       */
672 /* ==================================================================== */
673 static void
ll_append(ll_t * const list,void * const data)674 ll_append(ll_t * const list, void * const data)
675 {
676   ll_node_t * node;
677 
678   node = ll_new_node(); /* ll_new_node cannot return NULL because it   *
679                          | uses xmalloc which does not return if there *
680                          | is an allocation error.                     */
681 
682   node->data = data;
683   node->next = NULL;
684 
685   node->prev = list->tail;
686   if (list->tail)
687     list->tail->next = node;
688   else
689     list->head = node;
690 
691   list->tail = node;
692 
693   ++list->len;
694 }
695 
696 /* ================================================================== */
697 /* Put a new node filled with its data at the beginning of the linked */
698 /* list.                                                              */
699 /* The user is responsible for the memory management of the data.     */
700 /* ================================================================== */
701 static void
ll_prepend(ll_t * const list,void * const data)702 ll_prepend(ll_t * const list, void * const data)
703 {
704   ll_node_t * node;
705 
706   node = ll_new_node(); /* ll_new_node cannot return NULL because it   *
707                          | uses xmalloc which does not return if there *
708                          | is an allocation error.                     */
709 
710   node->data = data;
711   node->prev = NULL;
712 
713   node->next = list->head;
714   if (list->head)
715     list->head->prev = node;
716   else
717     list->tail = node;
718 
719   list->head = node;
720 
721   ++list->len;
722 }
723 
724 /* ======================================================== */
725 /* Insert a new node before the specified node in the list. */
726 /* ======================================================== */
727 static void
ll_insert_before(ll_t * const list,ll_node_t * node,void * const data)728 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
729 {
730   ll_node_t * new_node;
731 
732   if (node->prev == NULL)
733     ll_prepend(list, data);
734   else
735   {
736     new_node = ll_new_node(); /* ll_new_node cannot return NULL because it   *
737                                | uses xmalloc which does not return if there *
738                                | is an allocation error.                     */
739 
740     new_node->data   = data;
741     new_node->next   = node;
742     new_node->prev   = node->prev;
743     node->prev->next = new_node;
744     node->prev       = new_node;
745 
746     ++list->len;
747   }
748 }
749 
750 /* ======================================================= */
751 /* Insert a new node after the specified node in the list. */
752 /* ======================================================= */
753 static void
ll_insert_after(ll_t * const list,ll_node_t * node,void * const data)754 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
755 {
756   ll_node_t * new_node;
757 
758   if (node->next == NULL)
759     ll_append(list, data);
760   else
761   {
762     new_node = ll_new_node(); /* ll_new_node cannot return NULL because it   *
763                                | uses xmalloc which does not return if there *
764                                | is an allocation error.                     */
765 
766     new_node->data   = data;
767     new_node->prev   = node;
768     new_node->next   = node->next;
769     node->next->prev = new_node;
770     node->next       = new_node;
771 
772     ++list->len;
773   }
774 }
775 
776 /* ================================================================= */
777 /* Remove a node from a linked list.                                 */
778 /* The memory taken by the deleted node must be freed by the caller. */
779 /* ================================================================= */
780 static int
ll_delete(ll_t * const list,ll_node_t * node)781 ll_delete(ll_t * const list, ll_node_t * node)
782 {
783   if (list->head == list->tail)
784   {
785     if (list->head != NULL)
786       list->head = list->tail = NULL;
787     else
788       return 0;
789   }
790   else if (node->prev == NULL)
791   {
792     list->head       = node->next;
793     list->head->prev = NULL;
794   }
795   else if (node->next == NULL)
796   {
797     list->tail       = node->prev;
798     list->tail->next = NULL;
799   }
800   else
801   {
802     node->next->prev = node->prev;
803     node->prev->next = node->next;
804   }
805 
806   --list->len;
807 
808   free(node);
809 
810   return 1;
811 }
812 
813 #if 0 /* Unused yet */
814 /* ======================================================================== */
815 /* Find a node in the list containing data. Return the node pointer or NULL */
816 /* if not found.                                                            */
817 /* A comparison function must be provided to compare a and b (strcmp like). */
818 /* ======================================================================== */
819 static ll_node_t *
820 ll_find(ll_t * const list, void * const data,
821         int (*cmpfunc)(const void * a, const void * b))
822 {
823   ll_node_t * node;
824 
825   if (NULL == (node = list->head))
826     return NULL;
827 
828   do
829   {
830     if (0 == cmpfunc(node->data, data))
831       return node;
832   } while (NULL != (node = node->next));
833 
834   return NULL;
835 }
836 #endif
837 
838 /* ==================================================================== */
839 /* Allocate and fill an array of strings from a list.                   */
840 /* WARNINGS:                                                            */
841 /*   1) The list node must contain strings (char *).                    */
842 /*   2) The strings in the resulting array MUST NOT be freed as the are */
843 /*      NOT copied from the strings of the list.                        */
844 /*                                                                      */
845 /* IN list       : The list from which the array is generated.          */
846 /* IN start_node : The node of the list which will be the first node to */
847 /*                 consider to create the array.                        */
848 /* OUT: count    : The number of elements of the resulting array.       */
849 /* OUT: array    : The resulting array or NULL if the list is empty.    */
850 /* RC :          : The number of elements of the resulting array.       */
851 /* ==================================================================== */
852 static int
ll_strarray(ll_t * list,ll_node_t * start_node,int * count,char *** array)853 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
854 {
855   int         n = 0;
856   ll_node_t * node;
857 
858   *count = 0;
859 
860   node = start_node;
861 
862   if (list == NULL || node == NULL)
863   {
864     *array = NULL;
865 
866     return 0;
867   }
868 
869   *array = xmalloc((list->len + 1) * sizeof(char *));
870   while (node != NULL)
871   {
872     (*array)[n++] = (char *)(node->data);
873     (*count)++;
874 
875     node = node->next;
876   }
877 
878   (*array)[*count] = NULL;
879 
880   return *count;
881 }
882 
883 /* ******************************************************************* */
884 /* BST (search.h compatible) implementation.                           */
885 /*                                                                     */
886 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like    */
887 /* the AT&T man page says.                                             */
888 /*                                                                     */
889 /* Written by reading the System V Interface Definition, not the code. */
890 /*                                                                     */
891 /* Totally public domain.                                              */
892 /* ******************************************************************* */
893 
894 struct bst_s
895 {
896   void *         key;
897   struct bst_s * llink;
898   struct bst_s * rlink;
899 };
900 
901 #if 0 /* Unused yet. */
902 /* =========================== */
903 /* Delete node with given key. */
904 /* =========================== */
905 static void *
906 bst_delete(const void * vkey, void ** vrootp,
907            int (*compar)(const void *, const void *))
908 {
909   bst_t ** rootp = (bst_t **)vrootp;
910   bst_t *  p, *q, *r;
911   int      cmp;
912 
913   if (rootp == NULL || (p = *rootp) == NULL)
914     return NULL;
915 
916   while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
917   {
918     p     = *rootp;
919     rootp = (cmp < 0) ? &(*rootp)->llink  /* follow llink branch */
920                       : &(*rootp)->rlink; /* follow rlink branch */
921     if (*rootp == NULL)
922       return NULL; /* key not found */
923   }
924   r = (*rootp)->rlink;               /* D1: */
925   if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
926     q = r;
927   else if (r != NULL)
928   { /* Right link is NULL? */
929     if (r->llink == NULL)
930     { /* D2: Find successor */
931       r->llink = q;
932       q        = r;
933     }
934     else
935     { /* D3: Find NULL link */
936       for (q = r->llink; q->llink != NULL; q = r->llink)
937         r = q;
938       r->llink = q->rlink;
939       q->llink = (*rootp)->llink;
940       q->rlink = (*rootp)->rlink;
941     }
942   }
943   if (p != *rootp)
944     free(*rootp); /* D4: Free node */
945   *rootp = q;     /* link parent to new node */
946   return p;
947 }
948 #endif
949 
950 /* ===================================================================== */
951 /* Destroy a tree.                                                       */
952 /* The clean function pointer can be NULL, in this case the node content */
953 /* is not freed.                                                         */
954 /* ===================================================================== */
955 static void
bst_destroy(void * vrootp,void (* clean)(void *))956 bst_destroy(void * vrootp, void (*clean)(void *))
957 {
958   bst_t * root = (bst_t *)vrootp;
959 
960   if (root == NULL)
961     return;
962 
963   bst_destroy(root->llink, clean);
964   bst_destroy(root->rlink, clean);
965 
966   if (clean)
967     clean((void *)root->key);
968 
969   free(root);
970 }
971 
972 /* ========================= */
973 /* Find a node, or return 0. */
974 /* ========================= */
975 static void *
bst_find(const void * vkey,void * const * vrootp,int (* compar)(const void *,const void *))976 bst_find(const void * vkey, void * const * vrootp,
977          int (*compar)(const void *, const void *))
978 {
979   bst_t * const * rootp = (bst_t * const *)vrootp;
980 
981   if (rootp == NULL)
982     return NULL;
983 
984   while (*rootp != NULL)
985   { /* T1: */
986     int r;
987 
988     if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
989       return *rootp;                               /* key found */
990     rootp = (r < 0) ? &(*rootp)->llink             /* T3: follow left branch */
991                     : &(*rootp)->rlink;            /* T4: follow right branch */
992   }
993   return NULL;
994 }
995 
996 /* ======================================= */
997 /* Find or inserts datum into search tree. */
998 /* ======================================= */
999 static void *
bst_search(void * vkey,void ** vrootp,int (* compar)(const void *,const void *))1000 bst_search(void * vkey, void ** vrootp,
1001            int (*compar)(const void *, const void *))
1002 {
1003   bst_t *  q;
1004   bst_t ** rootp = (bst_t **)vrootp;
1005 
1006   if (rootp == NULL)
1007     return NULL;
1008 
1009   while (*rootp != NULL)
1010   { /* Knuth's T1: */
1011     int r;
1012 
1013     if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
1014       return *rootp;                               /* we found it! */
1015 
1016     rootp = (r < 0) ? &(*rootp)->llink  /* T3: follow left branch */
1017                     : &(*rootp)->rlink; /* T4: follow right branch */
1018   }
1019 
1020   q = xmalloc(sizeof(bst_t)); /* T5: key not found */
1021   if (q != 0)
1022   {                  /* make new node */
1023     *rootp   = q;    /* link new node to old */
1024     q->key   = vkey; /* initialize new node */
1025     q->llink = q->rlink = NULL;
1026   }
1027   return q;
1028 }
1029 
1030 /* ========================= */
1031 /* Walk the nodes of a tree. */
1032 /* ========================= */
1033 static void
bst_walk_recurse(const bst_t * root,void (* action)(const void *,walk_order_e,int),int level)1034 bst_walk_recurse(const bst_t * root,
1035                  void (*action)(const void *, walk_order_e, int), int level)
1036 {
1037   if (root->llink == NULL && root->rlink == NULL)
1038     (*action)(root, leaf, level);
1039   else
1040   {
1041     (*action)(root, preorder, level);
1042     if (root->llink != NULL)
1043       bst_walk_recurse(root->llink, action, level + 1);
1044     (*action)(root, postorder, level);
1045     if (root->rlink != NULL)
1046       bst_walk_recurse(root->rlink, action, level + 1);
1047     (*action)(root, endorder, level);
1048   }
1049 }
1050 
1051 static void
bst_walk(const void * vroot,void (* action)(const void *,walk_order_e,int))1052 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
1053 {
1054   if (vroot != NULL && action != NULL)
1055     bst_walk_recurse(vroot, action, 0);
1056 }
1057 
1058 /* ************************ */
1059 /* Various implementations. */
1060 /* ************************ */
1061 
1062 /* ======================== */
1063 /* Trim leading characters. */
1064 /* ======================== */
1065 static void
ltrim(char * str,const char * trim_str)1066 ltrim(char * str, const char * trim_str)
1067 {
1068   size_t len   = strlen(str);
1069   size_t begin = strspn(str, trim_str);
1070   size_t i;
1071 
1072   if (begin > 0)
1073     for (i = begin; i <= len; ++i)
1074       str[i - begin] = str[i];
1075 }
1076 
1077 /* ================================================= */
1078 /* Trim trailing characters.                         */
1079 /* The resulting string will have at least min bytes */
1080 /* even if trailing spaces remain.                   */
1081 /* ================================================= */
1082 static void
rtrim(char * str,const char * trim_str,size_t min)1083 rtrim(char * str, const char * trim_str, size_t min)
1084 {
1085   size_t len = strlen(str);
1086   while (len > min && strchr(trim_str, str[len - 1]))
1087     str[--len] = '\0';
1088 }
1089 
1090 /* ================================================== */
1091 /* Count the number of occurrences of the character c */
1092 /* in the string str.                                 */
1093 /* The str pointer is assumed to be not NULL.         */
1094 /* ================================================== */
1095 static int
strchrcount(char * str,char c)1096 strchrcount(char * str, char c)
1097 {
1098   int count = 0;
1099 
1100   while (*str)
1101     if (*str++ == c)
1102       count++;
1103 
1104   return count;
1105 }
1106 
1107 /* =============================================== */
1108 /* Is the string str2 a prefix of the string str1? */
1109 /* =============================================== */
1110 static int
strpref(char * str1,char * str2)1111 strpref(char * str1, char * str2)
1112 {
1113   while (*str1 != '\0' && *str1 == *str2)
1114   {
1115     str1++;
1116     str2++;
1117   }
1118 
1119   return *str2 == '\0';
1120 }
1121 
1122 /* ========================== */
1123 /* Like strcmp ignoring case. */
1124 /* ========================== */
1125 static int
stricmp(const char * s1,const char * s2)1126 stricmp(const char * s1, const char * s2)
1127 {
1128   while (tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1129   {
1130     if (*s1 == '\0')
1131       return 0;
1132 
1133     s1++;
1134     s2++;
1135   }
1136 
1137   return (int)tolower((unsigned char)*s1) - (int)tolower((unsigned char)*s2);
1138 }
1139 
1140 /* ====================================================================== */
1141 /* Strings concatenation with dynamic memory allocation.                  */
1142 /* IN : a variable number of char * arguments with NULL terminating       */
1143 /*      the sequence.                                                     */
1144 /*      The first one must have been dynamically allocated and is         */
1145 /*      mandatory.                                                        */
1146 /*                                                                        */
1147 /* Returns a new allocated string containing the concatenation of all     */
1148 /* the arguments. It is the caller's responsibility to free the resulting */
1149 /* string.                                                                */
1150 /* ====================================================================== */
1151 static char *
strappend(char * str,...)1152 strappend(char * str, ...)
1153 {
1154   size_t  l;
1155   va_list args;
1156   char *  s;
1157 
1158   l = 1 + strlen(str);
1159   va_start(args, str);
1160 
1161   s = va_arg(args, char *);
1162 
1163   while (s)
1164   {
1165     l += strlen(s);
1166     s = va_arg(args, char *);
1167   }
1168 
1169   va_end(args);
1170 
1171   str = xrealloc(str, l);
1172 
1173   va_start(args, str);
1174   s = va_arg(args, char *);
1175 
1176   while (s)
1177   {
1178     strcat(str, s);
1179     s = va_arg(args, char *);
1180   }
1181   va_end(args);
1182 
1183   return str;
1184 }
1185 
1186 /* ====================================================================== */
1187 /*  Public domain strtok_r() by Charlie Gordon.                           */
1188 /*   from comp.lang.c  9/14/2007                                          */
1189 /*      http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684   */
1190 /*                                                                        */
1191 /*     (Declaration that it's public domain):                             */
1192 /*      http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c   */
1193 /*                                                                        */
1194 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when     */
1195 /* *end == NULL.                                                          */
1196 /* ====================================================================== */
1197 static char *
xstrtok_r(char * str,const char * delim,char ** end)1198 xstrtok_r(char * str, const char * delim, char ** end)
1199 {
1200   char * ret;
1201 
1202   if (str == NULL)
1203     str = *end;
1204 
1205   if (str == NULL)
1206     return NULL;
1207 
1208   str += strspn(str, delim);
1209 
1210   if (*str == '\0')
1211     return NULL;
1212 
1213   ret = str;
1214 
1215   str += strcspn(str, delim);
1216 
1217   if (*str)
1218     *str++ = '\0';
1219 
1220   *end = str;
1221 
1222   return ret;
1223 }
1224 
1225 /* ===================================================================== */
1226 /* Put the first word of str, truncated to len characters, in buf.       */
1227 /* Return a pointer in str pointing just after the word.                 */
1228 /* buf must have been pre-allocated to accept at least len+1 characters. */
1229 /* Note that buf can contains a sting full of spaces is str was not      */
1230 /* trimmed before the call.                                              */
1231 /* ===================================================================== */
1232 char *
get_word(char * str,char * buf,size_t len)1233 get_word(char * str, char * buf, size_t len)
1234 {
1235   char * s = str;
1236 
1237   /* Skip spaces. */
1238   /* """""""""""" */
1239   while (*s && isspace(*s))
1240     s++;
1241 
1242   /* Set the new string start. */
1243   /* """"""""""""""""""""""""" */
1244   str = s;
1245 
1246   /* Get the word. */
1247   /*"""""""""""""" */
1248   while (*s && !isspace(*s) && s - str < len)
1249     s++;
1250 
1251   strncpy(buf, str, s - str);
1252   buf[s - str] = 0;
1253 
1254   return s;
1255 }
1256 
1257 /* ==================================================================== */
1258 /* Return 1 is value is "1" or "yes" (ignoring case).                   */
1259 /* Return 0 is value is "0" or "no" (ignoring case).                    */
1260 /* If value has another value, then set invalid to 1 and also return 0  */
1261 /* invalid is set to 0 in all the other cases.                          */
1262 /* ==================================================================== */
1263 static int
eval_yes(char * value,int * invalid)1264 eval_yes(char * value, int * invalid)
1265 {
1266   *invalid = 0;
1267 
1268   if (strcmp(value, "1") == 0 || stricmp(value, "yes") == 0)
1269     return 1;
1270   else if (strcmp(value, "0") != 0 && stricmp(value, "no") != 0)
1271     *invalid = 1;
1272 
1273   return 0;
1274 }
1275 
1276 /* =========================================================== */
1277 /* Fill an array of strings from the words composing a string. */
1278 /*                                                             */
1279 /* str:    initial string which will be altered.               */
1280 /* args:   array of pointers to the start of the words in str. */
1281 /* max:    maximum number of words used before giving up.      */
1282 /* return: the number of words (<=max).                        */
1283 /* =========================================================== */
1284 static int
str2argv(char * str,char ** args,int max)1285 str2argv(char * str, char ** args, int max)
1286 {
1287   int nb_args = 0;
1288 
1289   while (*str)
1290   {
1291     if (nb_args >= max)
1292       return nb_args;
1293 
1294     while (*str == ' ' || *str == '\t')
1295       *(str++) = '\0';
1296 
1297     if (!*str)
1298       return nb_args;
1299 
1300     args[nb_args] = str;
1301     nb_args++;
1302 
1303     while (*str && (*str != ' ') && (*str != '\t'))
1304       str++;
1305   }
1306 
1307   return nb_args;
1308 }
1309 
1310 /* ********************** */
1311 /* ctxopt implementation. */
1312 /* ********************** */
1313 
1314 static int ctxopt_initialized = 0; /* cap_init has not yet been called. */
1315 
1316 /* Flags structure initialized by ctxopt_init. */
1317 /* """"""""""""""""""""""""""""""""""""""""""" */
1318 struct flags_s
1319 {
1320   int stop_if_non_option;
1321   int allow_abbreviations;
1322   int display_usage_on_error;
1323 };
1324 
1325 static flags_t flags = { 0, 1, 1 };
1326 
1327 /* Context structure. */
1328 /* """""""""""""""""" */
1329 struct ctx_s
1330 {
1331   char * name;
1332   ll_t * opt_list;    /* list of options allowed in this context.      */
1333   ll_t * incomp_list; /* list of strings containing incompatible names *
1334                        | of options separated by spaces or tabs.       */
1335   ll_t * req_list;    /* list of strings containing an option name and *
1336                        | all the option names where at least one of    *
1337                        | them is required to be also present.          */
1338 
1339   int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1340                 void ** ctx_data);
1341   void *  par_bst;
1342   int     nb_data;
1343   void ** data;
1344 };
1345 
1346 /* https://textik.com/#488ce3649b6c60f5                          */
1347 /*                                                               */
1348 /*       +--------------+                                        */
1349 /*       |first_ctx_inst|                                        */
1350 /*       +---+----------+                                        */
1351 /*           |                                                   */
1352 /*        +--v-----+      +--------+     +--------+      +-----+ */
1353 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1354 /* |   |  +-+------+      +----+---+     +----+---+      +-----+ */
1355 /* |   |    |                  |              |                  */
1356 /* |   |  +-v------+           |              |                  */
1357 /* |   +--+ctx_inst<-----------+              |                  */
1358 /* |      +-+------+                          |                  */
1359 /* |        |                                 |                  */
1360 /* |      +-v------+                          |                  */
1361 /* +------+ctx_inst<--------------------------+                  */
1362 /*        +-+------+                                             */
1363 /*          |                                                    */
1364 /*        +-v---+                                                */
1365 /*        | ... |                                                */
1366 /*        +-----+                                                */
1367 
1368 /* Option structure. */
1369 /* """"""""""""""""" */
1370 struct opt_s
1371 {
1372   char * name;     /* option name.                            */
1373   char * next_ctx; /* new context this option may lead to     */
1374   ll_t * ctx_list; /* list of contexts allowing this option.  */
1375   char * params;   /* string containing all the parameters of *
1376                     | the option.                             */
1377 
1378   void (*action)(                     /* The option associated action.     */
1379                  char *  ctx_name,    /* context name.                     */
1380                  char *  opt_name,    /* option name.                      */
1381                  char *  par,         /* option parameter.                 */
1382                  int     nb_args,     /* number of arguments.              */
1383                  char ** args,        /* option arguments.                 */
1384                  int     nb_opt_data, /* number of option data pointers.   */
1385                  void ** opt_data,    /* option data pointers.             */
1386                  int     nb_ctx_data, /* nb of current  context data ptrs. */
1387                  void ** ctx_data     /* current context data pointers.    */
1388   );
1389 
1390   int nb_data;  /* number of the data pointers passed as argument to action. */
1391   void ** data; /* array of data pointers passed as argument to action.      */
1392 
1393   int args;     /* 1 if this option takes arguments else 0.                  */
1394   int optional; /* 1 if the option is optional, else 0.                      */
1395   int multiple; /* 1 if the option can appear more than one time in a        *
1396                  | context, else 0.                                          */
1397 
1398   int  opt_count_matter; /* 1 if we must restrict the count, else 0.         */
1399   int  occurrences;      /* Number of option occurrences in a context.       */
1400   char opt_count_oper;   /* <, = or >                                        */
1401   int  opt_count_mark;   /* Value to be compared to with opt_count_oper.     */
1402 
1403   char * arg; /* symbolic text after # describing the option argument.       */
1404 
1405   int optional_args; /* 1 of option is optional else 0.                      */
1406   int multiple_args; /* 1 is option can appear more than once in a context   *
1407                       | instance.                                            */
1408 
1409   int  opt_args_count_matter; /* 1 if count is rescticted, else 0.           */
1410   char opt_args_count_oper;   /* <, = or >                                   */
1411   int  opt_args_count_mark;   /* Value to be compared to with                *
1412                                | opt_count_oper.                             */
1413 
1414   int eval_first; /* 1 if this option must be evaluated before the options   *
1415                    | without this mark.                                      */
1416 
1417   ll_t * eval_before_list; /* List of pointers on options which must be      *
1418                             | evaluated before this option.                  */
1419 
1420   ll_t * constraints_list; /* List of constraint check functions pointers.   */
1421 };
1422 
1423 /* Context instance structure. */
1424 /* """"""""""""""""""""""""""" */
1425 struct ctx_inst_s
1426 {
1427   ctx_t *      ctx;             /* the context whose this is an instance of  */
1428   ctx_inst_t * prev_ctx_inst;   /* ctx_inst of the opt_inst which led to the *
1429                                  | creation of this ctx_inst structure.      */
1430   opt_inst_t * gen_opt_inst;    /* opt_inst which led to the creation of a   *
1431                                  | instance of this structure.               */
1432   ll_t *       incomp_bst_list; /* list of seen_opt_t BST.                   */
1433   void *       seen_opt_bst;    /* tree of seen_opt_t.                       */
1434   ll_t *       opt_req_list;    /* list of req_t.                            */
1435   ll_t *       opt_inst_list;   /* The list of option instances in this      *
1436                                  | context instance.                         */
1437   char *       par_name;        /* parameter which created this instance.    */
1438 };
1439 
1440 /* Option instance structure. */
1441 /* """""""""""""""""""""""""" */
1442 struct opt_inst_s
1443 {
1444   opt_t *      opt;           /* The option this is an instance of.        */
1445   char *       opt_name;      /* The option which led to this creation.    */
1446   char *       par;           /* The parameter which led to this creation. */
1447   ll_t *       values_list;   /* The list of arguments of this option.     */
1448   ctx_inst_t * next_ctx_inst; /* The new context instance this option.     *
1449                                | instance may create.                      */
1450 };
1451 
1452 /* Structure used to check if an option has bee seen or not */
1453 /* in a context instance.                                   */
1454 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1455 struct seen_opt_s
1456 {
1457   opt_t * opt;  /* The concerned option.                                */
1458   char *  par;  /* Parameter which led to the making of this structure. */
1459   int     seen; /* 1 if seen in the context instances, else 0.          */
1460 };
1461 
1462 /* Structure used to check if at least one instance of the options whose */
1463 /* pointers are in or_opt_list has been seen in the ctx_inst where an    */
1464 /* instance or opt is also present.                                      */
1465 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1466 struct req_s
1467 {
1468   opt_t * opt;         /* Option that asks for other options.    */
1469   ll_t *  or_opt_list; /* Required options, at least one of them *
1470                        |  must be present.                       */
1471 };
1472 
1473 /* Parameter structure which links a parameter to the option it belongs to. */
1474 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1475 struct par_s
1476 {
1477   char *  name; /* Parameter name (with the leading -). */
1478   opt_t * opt;  /* Attached option.                     */
1479 };
1480 
1481 /* Constraint structure. */
1482 /* """"""""""""""""""""" */
1483 struct constraint_s
1484 {
1485   int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1486   int     nb_args;
1487   char ** args;
1488   char *  to_free; /* pointer to the original string in which the array in *
1489                     | args points to. This poinnter is kept there to allow *
1490                     | it to be freed.                                      */
1491 };
1492 
1493 state_t *           cur_state      = NULL; /* Current analysis state.        */
1494 static ll_t *       cmdline_list   = NULL; /* List of interpreted CLI words  *
1495                                             | serves as the basis for the    *
1496                                             | analysis of the parameters.    */
1497 static ctx_t *      main_ctx       = NULL; /* initial context.               */
1498 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context    *
1499                                             | instance which holds the       *
1500                                             | options instances.             */
1501 static ll_t *       ctx_inst_list  = NULL; /* List of the context instances. */
1502 
1503 /* ======================================================= */
1504 /* Parse a string for the next matching token.             */
1505 /*                                                         */
1506 /* s:       string to parse.                               */
1507 /* token:   pre_allocated array of max tok_len characters. */
1508 /* pattern: scanf type pattern token must match.           */
1509 /* pos:     number of characters successfully parsed in s. */
1510 /*                                                         */
1511 /* Returns: a pointer to the first unread character or     */
1512 /*          to he terminating \0.                          */
1513 /* ======================================================= */
1514 static char *
strtoken(char * s,char * token,size_t tok_len,char * pattern,int * pos)1515 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1516 {
1517   char * full_pattern;
1518   char   len[3];
1519   int    n;
1520 
1521   *pos = 0;
1522 
1523   n = snprintf(len, 3, "%zu", tok_len);
1524   if (n < 0)
1525     return NULL;
1526 
1527   full_pattern = xmalloc(strlen(pattern) + n + 4);
1528 
1529   strcpy(full_pattern, "%");
1530   strcat(full_pattern, len);
1531   strcat(full_pattern, pattern);
1532   strcat(full_pattern, "%n");
1533 
1534   n = sscanf(s, full_pattern, token, pos);
1535 
1536   free(full_pattern);
1537 
1538   if (n != 1)
1539     return NULL;
1540 
1541   return s + *pos;
1542 }
1543 
1544 /* ****************************************** */
1545 /* Various comparison and deletion functions. */
1546 /* ****************************************** */
1547 
1548 static int
ctx_compare(const void * c1,const void * c2)1549 ctx_compare(const void * c1, const void * c2)
1550 {
1551   return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1552 }
1553 
1554 /* =========================== */
1555 /* Free a context_bst element. */
1556 /* =========================== */
1557 static void
ctx_free(void * c)1558 ctx_free(void * c)
1559 {
1560   ctx_t * ctx = c;
1561 
1562   free(ctx->name);
1563   free(ctx->data);
1564 
1565   ll_destroy(ctx->opt_list, NULL);
1566   ll_destroy(ctx->incomp_list, free);
1567   ll_destroy(ctx->req_list, free);
1568   bst_destroy(ctx->par_bst, par_free);
1569 
1570   free(c);
1571 }
1572 
1573 /* ============================= */
1574 /* Free a ctx_inst_list element. */
1575 /* ============================= */
1576 static void
ctx_inst_free(void * ci)1577 ctx_inst_free(void * ci)
1578 {
1579   ctx_inst_t * ctx_inst = ci;
1580 
1581   free(ctx_inst->par_name);
1582   ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1583   bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1584   ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1585   ll_destroy(ctx_inst->opt_req_list, req_free);
1586 
1587   free(ci);
1588 }
1589 
1590 /* ============================== */
1591 /* Free an opt_inst_list element. */
1592 /* ============================== */
1593 static void
opt_inst_free(void * oi)1594 opt_inst_free(void * oi)
1595 {
1596   opt_inst_t * opt_inst = oi;
1597 
1598   ll_destroy(opt_inst->values_list, NULL);
1599 
1600   free(oi);
1601 }
1602 
1603 /* ================================== */
1604 /* Compare two seen_opt_bst elements. */
1605 /* ================================== */
1606 static int
seen_opt_compare(const void * so1,const void * so2)1607 seen_opt_compare(const void * so1, const void * so2)
1608 {
1609   opt_t *o1, *o2;
1610 
1611   o1 = ((seen_opt_t *)so1)->opt;
1612   o2 = ((seen_opt_t *)so2)->opt;
1613 
1614   return strcmp(o1->name, o2->name);
1615 }
1616 
1617 /* ============================ */
1618 /* Free a seen_opt_bst element. */
1619 /* ============================ */
1620 void
seen_opt_free(void * so)1621 seen_opt_free(void * so)
1622 {
1623   seen_opt_t * seen_opt = so;
1624 
1625   free(seen_opt->par);
1626 
1627   free(so);
1628 }
1629 
1630 /* =========================== */
1631 /* Free an incomp_bst element. */
1632 /* =========================== */
1633 static void
incomp_bst_free(void * b)1634 incomp_bst_free(void * b)
1635 {
1636   bst_t * bst = b;
1637 
1638   bst_destroy(bst, NULL);
1639 }
1640 
1641 /* ============================= */
1642 /* Free an opt_req_list element. */
1643 /* ============================= */
1644 static void
req_free(void * r)1645 req_free(void * r)
1646 {
1647   req_t * req = r;
1648 
1649   ll_destroy(req->or_opt_list, NULL);
1650   free(req);
1651 }
1652 
1653 /* ================================= */
1654 /* Compare two options_bst elements. */
1655 /* ================================= */
1656 static int
opt_compare(const void * o1,const void * o2)1657 opt_compare(const void * o1, const void * o2)
1658 {
1659   return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1660 }
1661 
1662 /* ============================= */
1663 /* Free an options_bst elements. */
1664 /* ============================= */
1665 void
opt_free(void * o)1666 opt_free(void * o)
1667 {
1668   opt_t * opt = o;
1669 
1670   free(opt->name);
1671   free(opt->next_ctx);
1672   free(opt->params);
1673   free(opt->arg);
1674   free(opt->data);
1675 
1676   ll_destroy(opt->ctx_list, NULL);
1677   ll_destroy(opt->constraints_list, constraint_free);
1678   ll_destroy(opt->eval_before_list, NULL);
1679 
1680   free(o);
1681 }
1682 
1683 /* ============================= */
1684 /* Compare two par_bst elements. */
1685 /* ============================= */
1686 static int
par_compare(const void * a1,const void * a2)1687 par_compare(const void * a1, const void * a2)
1688 {
1689   return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1690 }
1691 
1692 /* ======================= */
1693 /* Free a par_bst element. */
1694 /* ======================= */
1695 static void
par_free(void * p)1696 par_free(void * p)
1697 {
1698   par_t * par = p;
1699 
1700   free(par->name);
1701 
1702   free(p);
1703 }
1704 
1705 /* ================================ */
1706 /* Free a constraints_list element. */
1707 /* ================================ */
1708 static void
constraint_free(void * c)1709 constraint_free(void * c)
1710 {
1711   constraint_t * cstr = c;
1712 
1713   free(cstr->args);
1714   free(cstr->to_free);
1715 
1716   free(c);
1717 }
1718 
1719 /* ******************************************************************** */
1720 /* Helper functions to locate contexts, options and parameters in a BST */
1721 /* by their names.                                                      */
1722 /* ******************************************************************** */
1723 
1724 static ctx_t *
locate_ctx(char * name)1725 locate_ctx(char * name)
1726 {
1727   bst_t * node;
1728   ctx_t   ctx = { 0 };
1729 
1730   ctx.name = name;
1731 
1732   if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1733     return NULL;
1734   else
1735     return node->key;
1736 }
1737 
1738 static opt_t *
locate_opt(char * name)1739 locate_opt(char * name)
1740 {
1741   bst_t * node;
1742   opt_t   opt = { 0 };
1743 
1744   opt.name = name;
1745 
1746   if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1747     return NULL;
1748   else
1749     return node->key;
1750 }
1751 
1752 static par_t *
locate_par(char * name,ctx_t * ctx)1753 locate_par(char * name, ctx_t * ctx)
1754 {
1755   bst_t * node;
1756   par_t   par = { 0 };
1757   void *  bst = ctx->par_bst;
1758 
1759   par.name = name;
1760 
1761   if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1762     return NULL;
1763   else
1764     return node->key;
1765 }
1766 
1767 /* ====================================================================== */
1768 /* Helper function to display the dependency constraints between options. */
1769 /* These constraints are set with the ctxopt_add_opt_settings function    */
1770 /* using the 'before' and 'after' arguments.                              */
1771 /* IN  list  : a list of options.                                         */
1772 /* ====================================================================== */
1773 static void
print_before_constraints(ll_t * list)1774 print_before_constraints(ll_t * list)
1775 {
1776   ll_node_t * node = list->head;
1777   ll_node_t * before_node;
1778   opt_t *     opt, *before_opt;
1779   int         msg = 0;
1780 
1781   while (node != NULL)
1782   {
1783     opt = node->data;
1784 
1785     if (opt->eval_before_list->len > 0)
1786     {
1787       if (!msg)
1788       {
1789         printf("\n  If present in the command line,");
1790         msg = 1; /* Display this message only once. */
1791       }
1792 
1793       before_node = opt->eval_before_list->head;
1794 
1795       printf("\n  ");
1796       while (before_node != NULL)
1797       {
1798         before_opt = before_node->data;
1799         printf("%s", before_opt->params);
1800 
1801         before_node = before_node->next;
1802 
1803         if (before_node != NULL)
1804           printf(" and\n  ");
1805       }
1806       printf(" will be evaluated after %s\n", opt->params);
1807     }
1808     node = node->next;
1809   }
1810 }
1811 
1812 /* =================================================================== */
1813 /* Utility function to format and print the options present in a list. */
1814 /*                                                                     */
1815 /* IN  list  : a list of options.                                      */
1816 /* OUT has_* : a set of flags which will determine the content of the  */
1817 /*             explanation given after the formatted printing of the   */
1818 /*             options.                                                */
1819 /* =================================================================== */
1820 static void
print_options(ll_t * list,int * has_optional,int * has_ellipsis,int * has_rule,int * has_generic_arg,int * has_ctx_change,int * has_early_eval)1821 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1822               int * has_rule, int * has_generic_arg, int * has_ctx_change,
1823               int * has_early_eval)
1824 {
1825   ll_node_t * node = list->head;
1826   opt_t *     opt;
1827   char *      line;
1828   char *      option;
1829 
1830   line = xstrdup("  ");
1831 
1832   while (node != NULL)
1833   {
1834     option = xstrdup("");
1835     opt    = node->data;
1836 
1837     if (opt->optional)
1838     {
1839       option        = strappend(option, "[", (char *)0);
1840       *has_optional = 1;
1841     }
1842 
1843     if (opt->eval_first)
1844     {
1845       option          = strappend(option, "*", (char *)0);
1846       *has_early_eval = 1;
1847     }
1848 
1849     option = strappend(option, opt->params, (char *)0);
1850 
1851     if (opt->next_ctx != NULL)
1852     {
1853       option          = strappend(option, ">", opt->next_ctx, (char *)0);
1854       *has_ctx_change = 1;
1855     }
1856 
1857     if (opt->multiple)
1858     {
1859       if (opt->opt_count_oper != '\0')
1860       {
1861         char m[4];
1862         char o[2];
1863         o[0] = opt->opt_count_oper;
1864         o[1] = '\0';
1865         snprintf(m, 3, "%u", opt->opt_count_mark);
1866         option    = strappend(option, "...", o, m, (char *)0);
1867         *has_rule = 1;
1868       }
1869       else
1870         option = strappend(option, "...", (char *)0);
1871 
1872       *has_ellipsis = 1;
1873     }
1874 
1875     if (opt->args)
1876     {
1877       if (*(opt->arg) == '#')
1878         *has_generic_arg = 1;
1879 
1880       option = strappend(option, " ", (char *)0);
1881 
1882       if (opt->optional_args)
1883       {
1884         option        = strappend(option, "[", opt->arg, (char *)0);
1885         *has_optional = 1;
1886       }
1887       else
1888         option = strappend(option, opt->arg, (char *)0);
1889 
1890       if (opt->multiple_args)
1891       {
1892         if (opt->opt_args_count_oper != '\0')
1893         {
1894           char m[4];
1895           char o[2];
1896           o[0] = opt->opt_args_count_oper;
1897           o[1] = '\0';
1898           snprintf(m, 3, "%u", opt->opt_args_count_mark);
1899           option    = strappend(option, "...", o, m, (char *)0);
1900           *has_rule = 1;
1901         }
1902         else
1903           option = strappend(option, "...", (char *)0);
1904 
1905         *has_ellipsis = 1;
1906       }
1907       if (opt->optional_args)
1908         option = strappend(option, "]", (char *)0);
1909     }
1910     if (opt->optional)
1911       option = strappend(option, "]", (char *)0);
1912 
1913     if (strlen(line) + 1 + strlen(option) < 80)
1914       line = strappend(line, option, " ", (char *)0);
1915     else
1916     {
1917       printf("%s\n", line);
1918       line[2] = '\0';
1919       line    = strappend(line, option, " ", (char *)0);
1920     }
1921 
1922     free(option);
1923 
1924     node = node->next;
1925   }
1926 
1927   printf("%s\n", line);
1928 
1929   free(line);
1930 }
1931 
1932 /* ==================================================== */
1933 /* Explain the special syntactic symbols present in the */
1934 /* generated usage messages.                            */
1935 /* ==================================================== */
1936 static void
print_explanations(int has_early_eval,int has_ctx_change,int has_generic_arg,int has_optional,int has_ellipsis,int has_rule)1937 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1938                    int has_optional, int has_ellipsis, int has_rule)
1939 {
1940   if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1941       || has_ellipsis || has_rule)
1942   {
1943     printf("\nExplanation of the syntax used above:\n");
1944     printf("Only the parameters (prefixed by -) and the arguments, if any, "
1945            "must be entered.\n");
1946     printf("The following is just there to explain the other symbols "
1947            "displayed.\n\n");
1948 
1949     if (has_early_eval)
1950       printf("*            : the parameters defined for this option will "
1951              "be evaluated first.\n");
1952     if (has_ctx_change)
1953       printf(">            : the context after this symbol will be the new "
1954              "default context.\n");
1955     if (has_generic_arg)
1956       printf("#tag         : argument with a hint about its meaning.\n");
1957     if (has_optional)
1958       printf("[...]        : the object between square brackets is "
1959              "optional.\n");
1960     if (has_ellipsis)
1961       printf("...          : several occurrences of the previous object "
1962              "are possible.\n");
1963     if (has_rule)
1964       printf("[<|=|>]number: rules constraining the number of "
1965              "parameters/arguments.\n");
1966   }
1967 }
1968 
1969 /* ************************************************************ */
1970 /* Various utilities and callback functions called when walking */
1971 /* through a BST.                                               */
1972 /* ************************************************************ */
1973 
1974 static void
bst_seen_opt_cb(const void * node,walk_order_e kind,int level)1975 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1976 {
1977   seen_opt_t * seen_opt = ((bst_t *)node)->key;
1978 
1979   if (kind == postorder || kind == leaf)
1980   {
1981     if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1982     {
1983       user_rc     = 1;
1984       user_string = strappend(user_string, seen_opt->opt->params, " ",
1985                               (char *)0);
1986     }
1987   }
1988 }
1989 
1990 static void
bst_seen_opt_seen_cb(const void * node,walk_order_e kind,int level)1991 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1992 {
1993   seen_opt_t * seen_opt = ((bst_t *)node)->key;
1994 
1995   if (kind == postorder || kind == leaf)
1996     if (seen_opt->seen == 1)
1997     {
1998       user_rc     = 1;
1999       user_object = seen_opt->par;
2000     }
2001 }
2002 
2003 static void
bst_print_ctx_cb(const void * node,walk_order_e kind,int level)2004 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
2005 {
2006   ctx_t * ctx     = main_ctx;
2007   ctx_t * cur_ctx = ((bst_t *)node)->key;
2008 
2009   ll_t * list;
2010 
2011   int has_optional    = 0;
2012   int has_ellipsis    = 0;
2013   int has_rule        = 0;
2014   int has_generic_arg = 0;
2015   int has_ctx_change  = 0;
2016   int has_early_eval  = 0;
2017 
2018   if (kind == postorder || kind == leaf)
2019     if (strcmp(ctx->name, cur_ctx->name) != 0)
2020     {
2021       list = cur_ctx->opt_list;
2022 
2023       printf("\nAllowed options in the context %s:\n", cur_ctx->name);
2024       print_options(list, &has_optional, &has_ellipsis, &has_rule,
2025                     &has_generic_arg, &has_ctx_change, &has_early_eval);
2026       print_before_constraints(list);
2027     }
2028 }
2029 
2030 static void
bst_check_opt_cb(const void * node,walk_order_e kind,int level)2031 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
2032 {
2033   opt_t * opt = ((bst_t *)node)->key;
2034 
2035   if (kind == postorder || kind == leaf)
2036   {
2037     if (opt->params == NULL) /* opt must have associated parameters. */
2038       fatal_internal("Option %s has no registered parameter.\n", opt->name);
2039 
2040     if (opt->action == NULL) /* opt must have an action. */
2041       fatal_internal("Option %s has no registered action.\n", opt->name);
2042   }
2043 }
2044 
2045 static void
bst_match_par_cb(const void * node,walk_order_e kind,int level)2046 bst_match_par_cb(const void * node, walk_order_e kind, int level)
2047 {
2048   ctx_t * ctx = ((bst_t *)node)->key;
2049 
2050   if (kind == postorder || kind == leaf)
2051   {
2052     char * str = xstrdup(user_string);
2053 
2054     while (*str != '\0')
2055     {
2056       if (locate_par(str, ctx) != NULL)
2057       {
2058         if (*user_string2 == '\0')
2059           user_string2 = strappend(user_string2, "- ", ctx->name, (char *)0);
2060         else
2061           user_string2 = strappend(user_string2, "\n- ", ctx->name, (char *)0);
2062         break;
2063       }
2064       str[strlen(str) - 1] = '\0';
2065     }
2066     free(str);
2067   }
2068 }
2069 
2070 static void
match_prefix_cb(const void * node,walk_order_e kind,int level)2071 match_prefix_cb(const void * node, walk_order_e kind, int level)
2072 {
2073   par_t * par = ((bst_t *)node)->key;
2074 
2075   if (kind == postorder || kind == leaf)
2076     if (strpref(par->name, (char *)user_object))
2077     {
2078       user_rc++;
2079       user_string = strappend(user_string, par->name, " ", (char *)0);
2080     }
2081 }
2082 
2083 /* ====================================================================== */
2084 /* A parameter may not be separated from its first option by spaces, in   */
2085 /* this case this function looks for a valid flag as a prefix and splits  */
2086 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid    */
2087 /* option).                                                               */
2088 /*                                                                        */
2089 /* IN  word : the word to be checked.                                     */
2090 /* IN  ctx  : the context in which the flag indexed by the word is to be  */
2091 /*            checked.                                                    */
2092 /* OUT pos  : the offset in word pointing just after the matching prefix. */
2093 /* OUT opt  : a pointer to the option associated with the new parameter   */
2094 /*            or NULL if none is found.                                   */
2095 /*                                                                        */
2096 /* The returned pointer must be freed by the caller.                      */
2097 /* ====================================================================== */
2098 static char *
look_for_valid_prefix_in_word(char * word,ctx_t * ctx,int * pos,opt_t ** opt)2099 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
2100 {
2101   char * new = NULL;
2102   int     len;
2103   par_t * par;
2104   par_t   tmp_par = { 0 };
2105 
2106   len = strlen(word);
2107 
2108   if (len > 2)
2109   {
2110     new = xstrdup(word);
2111 
2112     do
2113     {
2114       new[--len]   = '\0';
2115       tmp_par.name = new;
2116     } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2117 
2118     if (par != NULL)
2119     {
2120       *pos = len;
2121       *opt = par->opt;
2122     }
2123     else
2124     {
2125       free(new);
2126       new = NULL;
2127     }
2128   }
2129   else
2130     *pos = 0;
2131 
2132   return new;
2133 }
2134 
2135 /* ============================================================= */
2136 /* If par_name is an unique abbreviation of an exiting parameter */
2137 /* in the context ctx, then return this parameter.               */
2138 /* ============================================================= */
2139 static char *
abbrev_expand(char * par_name,ctx_t * ctx)2140 abbrev_expand(char * par_name, ctx_t * ctx)
2141 {
2142   user_object = par_name;
2143   user_rc     = 0;
2144 
2145   *user_string = '\0';
2146   bst_walk(ctx->par_bst, match_prefix_cb);
2147   rtrim(user_string, " ", 0);
2148 
2149   /* The previous bst_walk has built a string of blank separated parameters */
2150   /* all having par_name as prefix. This string is put in the user_string   */
2151   /* exchange zone. The number of these words in put in user_rc.            */
2152   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2153   if (user_rc == 1) /* The number of matching abbreviations. */
2154     return xstrdup(user_string);
2155   else /* There is at least tho defined parameters starting with par_name. */
2156   {
2157     char *  s, *first_s;
2158     par_t * par;
2159     opt_t * opt;
2160     int     opt_count   = 0;
2161     void *  tmp_opt_bst = NULL;
2162 
2163     /* Find all the options corresponding to these words and store them   */
2164     /* without duplication in a temporary BST. Only their resulting count */
2165     /* matters.                                                           */
2166     /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2167     s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2168                                              | the first word.         */
2169     while (s != NULL)
2170     {
2171       par = locate_par(s, ctx);
2172       opt = par->opt;
2173 
2174       if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2175       {
2176         /* This option as not already been seen    */
2177         /* store it and increase the seen counter. */
2178         /* """"""""""""""""""""""""""""""""""""""" */
2179         bst_search(opt, &tmp_opt_bst, opt_compare);
2180         opt_count++;
2181       }
2182       s = strtok(NULL, " ");
2183     }
2184 
2185     /* Clean the temporary BST without removing the pointer */
2186     /* to the real options.                                 */
2187     /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2188     if (tmp_opt_bst != NULL)
2189       bst_destroy(tmp_opt_bst, NULL);
2190 
2191     if (opt_count == 1)
2192       /* All the abbreviation are leading to only one option */
2193       /* We can just continue as in the previous case.       */
2194       /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2195       return xstrdup(first_s);
2196     else
2197       return NULL;
2198   }
2199 }
2200 
2201 /* ================================================================ */
2202 /* Terminate the program if mandatory options required by a context */
2203 /* are not present.                                                 */
2204 /* ================================================================ */
2205 static void
check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst,char * opt_par)2206 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2207 {
2208   char * missing;
2209 
2210   if (has_unseen_mandatory_opt(ctx_inst, &missing))
2211     fatal(CTXOPTMISPAR, missing);
2212 }
2213 
2214 /* ====================================================== */
2215 /* Return 1 if at least one mandatory option was not seen */
2216 /* when quitting a context, else 0.                       */
2217 /* ====================================================== */
2218 static int
has_unseen_mandatory_opt(ctx_inst_t * ctx_inst,char ** missing)2219 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2220 {
2221   user_rc      = 0;
2222   *user_string = '\0';
2223 
2224   bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2225   rtrim(user_string, " ", 0);
2226 
2227   *missing = user_string;
2228 
2229   return user_rc ? 1 : 0;
2230 }
2231 
2232 /* ========================================================================= */
2233 /* This function terminates the program if an option or its arguments do not */
2234 /* conform to its occurrences constraint.                                    */
2235 /* There constraints can appear by trailing >, < or = in their definition    */
2236 /* given in ctxopt_new_ctx.                                                  */
2237 /* ========================================================================= */
2238 static void
check_for_occurrence_issues(ctx_inst_t * ctx_inst)2239 check_for_occurrence_issues(ctx_inst_t * ctx_inst)
2240 {
2241   ctx_t *      ctx = ctx_inst->ctx;
2242   opt_t *      opt;
2243   ll_node_t *  node;
2244   opt_inst_t * opt_inst;
2245   char *       cur_opt_params   = cur_state->cur_opt_params;
2246   char *       cur_opt_par_name = cur_state->cur_opt_par_name;
2247 
2248   /* Checks options. */
2249   /* """"""""""""""" */
2250   node = ctx->opt_list->head;
2251 
2252   while (node != NULL)
2253   {
2254     opt = node->data;
2255 
2256     /* Update current_state. */
2257     /* """"""""""""""""""""" */
2258     cur_state->cur_opt_params = opt->params;
2259     cur_state->opts_count     = opt->opt_count_mark;
2260     cur_state->opt_args_count = opt->opt_args_count_mark;
2261 
2262     if (opt->opt_count_matter)
2263       switch (opt->opt_count_oper)
2264       {
2265         case '=':
2266           if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2267             fatal(CTXOPTCTEOPT, "");
2268           break;
2269 
2270         case '<':
2271           if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2272             fatal(CTXOPTCTLOPT, "");
2273           break;
2274 
2275         case '>':
2276           if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2277             fatal(CTXOPTCTGOPT, "");
2278           break;
2279       }
2280 
2281     node = node->next;
2282   }
2283 
2284   /* Checks arguments. */
2285   /* """"""""""""""""" */
2286   node = ctx_inst->opt_inst_list->head;
2287   while (node != NULL)
2288   {
2289     opt_inst = node->data;
2290     opt      = opt_inst->opt;
2291 
2292     /* Update current_state. */
2293     /* """"""""""""""""""""" */
2294     cur_state->cur_opt_par_name = opt_inst->par;
2295     cur_state->opts_count       = opt->opt_count_mark;
2296     cur_state->opt_args_count   = opt->opt_args_count_mark;
2297 
2298     int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2299 
2300     if (opt->opt_args_count_matter)
2301       switch (opt->opt_args_count_oper)
2302       {
2303         case '=':
2304           if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2305             fatal(CTXOPTCTEARG, "");
2306           break;
2307 
2308         case '<':
2309           if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2310             fatal(CTXOPTCTLARG, "");
2311           break;
2312 
2313         case '>':
2314           if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2315             fatal(CTXOPTCTGARG, "");
2316           break;
2317       }
2318 
2319     node = node->next;
2320   }
2321   cur_state->cur_opt_params   = cur_opt_params;
2322   cur_state->cur_opt_par_name = cur_opt_par_name;
2323 }
2324 
2325 /* ====================================================================== */
2326 /* This function terminates the program if all the options which are part */
2327 /* of a group of required options by some other option are missing.       */
2328 /* ====================================================================== */
2329 static void
check_for_requirement_issues(ctx_inst_t * ctx_inst)2330 check_for_requirement_issues(ctx_inst_t * ctx_inst)
2331 {
2332   ll_node_t * node;
2333   ll_node_t * req_node;
2334   req_t *     req;
2335   opt_t *     opt;
2336   opt_t *     req_opt;
2337   bst_t *     bst_node;
2338   seen_opt_t  tmp_seen_opt;
2339   int         found;
2340   char *      needed_params = NULL;
2341 
2342   node = ctx_inst->opt_req_list->head;
2343 
2344   while (node != NULL)
2345   {
2346     req = node->data;
2347 
2348     opt              = req->opt;
2349     tmp_seen_opt.opt = opt;
2350 
2351     bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2352                         seen_opt_compare);
2353 
2354     if (((seen_opt_t *)(bst_node->key))->seen != 0)
2355     {
2356       found    = 0;
2357       req_node = req->or_opt_list->head;
2358 
2359       /* needed_params accumulates the params of the options in the group. */
2360       /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2361       free(needed_params); /* free can applied to the NULL pointer. */
2362       needed_params = xstrdup("");
2363 
2364       /* Go through the list of the required group of options and */
2365       /* succeed when one of them has been seen in the context.   */
2366       /* otherwise a fatal error is triggered and the program is  */
2367       /* terminated.                                              */
2368       /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2369       while (req_node != NULL)
2370       {
2371         req_opt          = req_node->data;
2372         tmp_seen_opt.opt = req_opt;
2373         needed_params    = strappend(needed_params, req_opt->params, "\n  ",
2374                                   (char *)0);
2375 
2376         bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2377                             seen_opt_compare);
2378 
2379         if (((seen_opt_t *)(bst_node->key))->seen != 0)
2380         {
2381           found = 1; /* A required option has been seen, */
2382           break;     /* accept the group.                */
2383         }
2384         req_node = req_node->next;
2385       }
2386 
2387       rtrim(needed_params, "\n ", 0);
2388 
2389       /* This is a fatal error if none of the options in the required */
2390       /* options group has been seen in the context.                  */
2391       /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2392       if (!found)
2393       {
2394         char * errmsg;
2395 
2396         if (req->or_opt_list->len > 1)
2397           errmsg = xstrdup("At least one of the parameters among:\n  %s\n"
2398                            "requested by %s must be present.\n");
2399         else
2400           errmsg = xstrdup("The parameter %s "
2401                            "requested by %s must be present.\n");
2402 
2403         cur_state->req_opt_par_needed = needed_params;
2404         cur_state->req_opt_par        = opt->params;
2405 
2406         fatal(CTXOPTREQPAR, errmsg);
2407       }
2408     }
2409 
2410     node = node->next;
2411   }
2412 }
2413 
2414 /* ======================================================================== */
2415 /* Parse a strings describing options and some of their characteristics     */
2416 /* The input string must have follow some rules like in the examples below: */
2417 /*                                                                          */
2418 /* "opt_name1 opt_name2"                                                    */
2419 /* "[opt_name1] opt_name2"                                                  */
2420 /* "[opt_name1] opt_name2..."                                               */
2421 /* "[opt_name1 #...] opt_name2... [#]"                                      */
2422 /* "[opt_name1 [#...]] opt_name2... [#...]"                                 */
2423 /*                                                                          */
2424 /* Where [ ] encloses an optional part, # means: has parameters and ...     */
2425 /* means that  there can be more than one occurrence of the previous thing. */
2426 /*                                                                          */
2427 /* opt_name can be followed by a 'new context' change prefixed with the     */
2428 /* symbol >, as in opt1>c2 by eg.                                           */
2429 /*                                                                          */
2430 /* This function returns as soon as one (or no) option has been parsed and  */
2431 /* return the offset to the next option to parse.                           */
2432 /*                                                                          */
2433 /* In case of successful parsing, an new option is allocated and its        */
2434 /* pointer returned.                                                        */
2435 /* ======================================================================== */
2436 static int
opt_parse(char * s,opt_t ** opt)2437 opt_parse(char * s, opt_t ** opt)
2438 {
2439   int      opt_optional          = 0;
2440   int      opt_multiple          = 0;
2441   int      opt_count_matter      = 0;
2442   char     opt_count_oper        = '\0';
2443   unsigned opt_count_mark        = 0;
2444   int      opt_args              = 0;
2445   char     opt_arg[33]           = { 0 };
2446   int      opt_multiple_args     = 0;
2447   int      opt_args_count_matter = 0;
2448   char     opt_args_count_oper   = '\0';
2449   unsigned opt_args_count_mark   = 0;
2450   int      opt_optional_args     = 0;
2451   int      opt_eval_first        = 0;
2452 
2453   int n;
2454   int pos;
2455   int count = 0;
2456 
2457   char * s_orig = s;
2458 
2459   char * p;
2460   char * opt_name = NULL;
2461   char * next_ctx;
2462   char   token[65];
2463 
2464   *opt = NULL;
2465   memset(opt_arg, '\0', 33);
2466 
2467   /* Strip the leading blanks. */
2468   /* """"""""""""""""""""""""" */
2469   while (isblank(*s))
2470     s++;
2471 
2472   if (*s == '[') /* Start of an optional option. */
2473   {
2474     opt_optional = 1;
2475     s++;
2476   }
2477   s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2478   if (s == NULL)
2479     return -1; /* Empty string. */
2480 
2481   /* Early EOS, only return success if the option is mandatory. */
2482   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2483   if (!*s)
2484     if (opt_optional == 1)
2485       return -(s - s_orig - 1);
2486 
2487   /* Validate the option name */
2488   /* ALPHA+(ALPHANUM|_)*      */
2489   /* """""""""""""""""""""""" */
2490   p = token;
2491   if (!isalpha(*p) && *p != '*')
2492     return -(s - s_orig - 1); /* opt_name must start with a letter. */
2493 
2494   if (*p == '*')
2495     opt_eval_first = 1;
2496 
2497   p++;
2498   while (*p)
2499   {
2500     if (!isalnum(*p) && *p != '_' && *p != '>')
2501       return -(s - s_orig - 1); /* opt_name must contain a letter, *
2502                                  * a number or a _                 */
2503     p++;
2504   }
2505 
2506   if (opt_eval_first)
2507     opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2508   else
2509     opt_name = xstrdup(token);
2510 
2511   if (*s == ']')
2512   {
2513     s++;
2514     while (isblank(*s))
2515       s++;
2516 
2517     goto success;
2518   }
2519 
2520   /* Check if it can appear multiple times by looking for the dots. */
2521   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2522   p = strtoken(s, token, 3, "[.]", &pos);
2523   if (p)
2524   {
2525     if (strcmp(token, "...") == 0)
2526     {
2527       opt_multiple = 1;
2528       s            = p;
2529       if (*s == '<' || *s == '=' || *s == '>')
2530       {
2531         unsigned value;
2532         int      offset;
2533 
2534         n = sscanf(s + 1, "%u%n", &value, &offset);
2535         if (n == 1)
2536         {
2537           opt_count_matter = 1;
2538           opt_count_oper   = *s;
2539           opt_count_mark   = value;
2540         }
2541         s += offset + 1;
2542       }
2543     }
2544     else
2545     {
2546       free(opt_name);
2547       return -(s - s_orig - 1);
2548     }
2549   }
2550 
2551   if (*s == ']')
2552   {
2553     /* Abort on extraneous ] if the option is mandatory. */
2554     /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2555     if (!opt_optional)
2556     {
2557       free(opt_name);
2558       return -(s - s_orig - 1);
2559     }
2560 
2561     s++; /* skip the ] */
2562 
2563     if (!*s || isblank(*s))
2564       goto success;
2565     else
2566     {
2567       free(opt_name);
2568       return -(s - s_orig - 1);
2569     }
2570   }
2571 
2572   /* A blank separates the option name and the argument tag. */
2573   /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2574   if (isblank(*s))
2575   {
2576     char dots[4];
2577 
2578     while (isblank(*s))
2579       s++;
2580 
2581     if (!*s)
2582       goto success;
2583 
2584     pos = 0;
2585     n   = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2586     if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2587     {
2588       opt_args          = 1;
2589       opt_optional_args = 1;
2590       if (n == 2)
2591         opt_multiple_args = 1; /* There were dots. */
2592 
2593       s += pos + !!(n == 2) * 3; /* Skips the dots. */
2594 
2595       if (*s == '<' || *s == '=' || *s == '>')
2596       {
2597         unsigned value;
2598         int      offset;
2599 
2600         n = sscanf(s + 1, "%u%n", &value, &offset);
2601         if (n == 1)
2602         {
2603           opt_args_count_matter = 1;
2604           opt_args_count_oper   = *s;
2605           opt_args_count_mark   = value;
2606         }
2607         s += offset + 1;
2608       }
2609 
2610       /* Optional arg tag must end with a ] */
2611       /* """""""""""""""""""""""""""""""""" */
2612       if (*s != ']')
2613       {
2614         free(opt_name);
2615         return -(s - s_orig - 1);
2616       }
2617 
2618       s++; /* Skip the ] */
2619     }
2620     else
2621     {
2622       n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2623       if (pos > 0 && *opt_arg == '#') /* # has been read. */
2624       {
2625         opt_args = 1;
2626         if (n == 2) /* There were dots. */
2627           opt_multiple_args = 1;
2628 
2629         s += pos + !!(n == 2) * 3; /* Skip the dots. */
2630 
2631         if (*s == '<' || *s == '=' || *s == '>')
2632         {
2633           unsigned value;
2634           int      offset;
2635 
2636           n = sscanf(s + 1, "%u%n", &value, &offset);
2637           if (n == 1)
2638           {
2639             opt_args_count_matter = 1;
2640             opt_args_count_oper   = *s;
2641             opt_args_count_mark   = value;
2642           }
2643           s += offset + 1;
2644         }
2645       }
2646     }
2647     if (*s == ']')
2648     {
2649       /* Abort on extraneous ] if the option is mandatory. */
2650       /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2651       if (!opt_optional)
2652       {
2653         free(opt_name);
2654         return -(s - s_orig - 1);
2655       }
2656 
2657       s++; /* skip the ] */
2658 
2659       /* Strip the following blanks. */
2660       /* """"""""""""""""""""""""""" */
2661       while (isblank(*s))
2662         s++;
2663 
2664       goto success;
2665     }
2666     else if (opt_optional == 0 && (!*s || isblank(*s)))
2667     {
2668       /* Strip the following blanks. */
2669       /* """"""""""""""""""""""""""" */
2670       while (isblank(*s))
2671         s++;
2672 
2673       goto success;
2674     }
2675     else if (opt_args == 0) /* # was not read it is possibly the start *
2676                              * of another option.                      */
2677       goto success;
2678     else
2679     {
2680       free(opt_name);
2681       return -(s - s_orig - 1);
2682     }
2683   }
2684 
2685 success:
2686 
2687   /* Strip the following blanks. */
2688   /* """"""""""""""""""""""""""" */
2689   while (isblank(*s))
2690     s++;
2691 
2692   next_ctx = NULL;
2693 
2694   if (*opt_name == '>')
2695     fatal_internal("The option name is missing in %s.", opt_name);
2696 
2697   count = strchrcount(opt_name, '>');
2698   if (count == 1)
2699   {
2700     char * tmp = strchr(opt_name, '>');
2701     next_ctx   = xstrdup(tmp + 1);
2702     *tmp       = '\0';
2703   }
2704   else if (count > 1)
2705     fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2706 
2707   *opt = xmalloc(sizeof(opt_t));
2708 
2709   (*opt)->name                  = opt_name;
2710   (*opt)->optional              = opt_optional;
2711   (*opt)->multiple              = opt_multiple;
2712   (*opt)->opt_count_matter      = opt_count_matter;
2713   (*opt)->opt_count_oper        = opt_count_oper;
2714   (*opt)->opt_count_mark        = opt_count_mark;
2715   (*opt)->args                  = opt_args;
2716   (*opt)->arg                   = xstrdup(opt_arg);
2717   (*opt)->optional_args         = opt_optional_args;
2718   (*opt)->multiple_args         = opt_multiple_args;
2719   (*opt)->opt_args_count_matter = opt_args_count_matter;
2720   (*opt)->opt_args_count_oper   = opt_args_count_oper;
2721   (*opt)->opt_args_count_mark   = opt_args_count_mark;
2722   (*opt)->eval_first            = opt_eval_first;
2723   (*opt)->next_ctx              = next_ctx;
2724   (*opt)->ctx_list              = ll_new();
2725   (*opt)->constraints_list      = ll_new();
2726   (*opt)->eval_before_list      = ll_new();
2727   (*opt)->action                = NULL;
2728   (*opt)->params                = NULL;
2729   (*opt)->data                  = NULL;
2730 
2731   return s - s_orig;
2732 }
2733 
2734 /* ==================================================================== */
2735 /* Try to initialize all the option in a given string.                  */
2736 /* Each parsed option are put in a BST tree with its name as index.     */
2737 /*                                                                      */
2738 /* On collision, the arguments only the signature are required to be    */
2739 /* the same else this is considered as an error. Options can be used in */
2740 /* more than one context and can be optional in one and mandatory in    */
2741 /* another.                                                             */
2742 /* ==================================================================== */
2743 static int
init_opts(char * spec,ctx_t * ctx)2744 init_opts(char * spec, ctx_t * ctx)
2745 {
2746   opt_t * opt, *bst_opt;
2747   bst_t * node;
2748   int     offset;
2749 
2750   while (*spec)
2751   {
2752     if ((offset = opt_parse(spec, &opt)) > 0)
2753     {
2754       spec += offset;
2755 
2756       if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2757       {
2758         int same_next_ctx = 0;
2759 
2760         bst_opt = node->key; /* Node extracted from the BST. */
2761 
2762         if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2763           same_next_ctx = 1;
2764         else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2765           same_next_ctx = 0;
2766         else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2767           same_next_ctx = 0;
2768         else
2769           same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2770 
2771         if (bst_opt->optional_args != opt->optional_args
2772             || bst_opt->multiple_args != opt->multiple_args
2773             || bst_opt->args != opt->args || !same_next_ctx)
2774         {
2775           fatal_internal("The option %s already exists with "
2776                          "a different arguments signature.\n",
2777                          opt->name);
2778         }
2779 
2780         /* The newly created opt is already present in options_bst. */
2781         /* We can remove it.                                        */
2782         /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2783         opt_free(opt);
2784 
2785         /* The new occurrence of the option option is legal */
2786         /* append the current context ptr in the list.      */
2787         /* """""""""""""""""""""""""""""""""""""""""""""""" */
2788         ll_append(bst_opt->ctx_list, ctx);
2789 
2790         /* Append the new option to the context's options list. */
2791         /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2792         ll_append(ctx->opt_list, bst_opt);
2793       }
2794       else
2795       {
2796         /* Initialize the option's context list with the current context. */
2797         /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2798         ll_append(opt->ctx_list, ctx);
2799 
2800         /* Append the new option to the context's options list. */
2801         /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2802         ll_append(ctx->opt_list, opt);
2803 
2804         /* Insert the new option in the BST. */
2805         /* """"""""""""""""""""""""""""""""" */
2806         bst_search(opt, &options_bst, opt_compare);
2807       }
2808     }
2809     else
2810     {
2811       char * s = xstrndup(spec, -offset);
2812       printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2813       free(s);
2814 
2815       exit(EXIT_FAILURE);
2816     }
2817   }
2818 
2819   return 1;
2820 }
2821 
2822 /* ===================================================== */
2823 /* ctxopt initialization function, must be called first. */
2824 /* ===================================================== */
2825 void
ctxopt_init(char * prog_name,char * init_flags)2826 ctxopt_init(char * prog_name, char * init_flags)
2827 {
2828   int n;
2829 
2830   contexts_bst = NULL;
2831   options_bst  = NULL;
2832   char * ptr;
2833 
2834   user_rc      = 0;
2835   user_value   = 0;
2836   user_string  = xmalloc(8);
2837   user_string2 = xmalloc(8);
2838   user_object  = NULL;
2839   char flag[33], fname[31], vname[31];
2840   int  invalid;
2841 
2842   ctxopt_initialized = 1;
2843 
2844   /* Initialize current_state.*/
2845   /* """""""""""""""""""""""" */
2846   cur_state = xcalloc(sizeof(state_t), 0);
2847 
2848   /* Initialize custom error function pointers to NULL. */
2849   /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2850   err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2851   for (n = 0; n < CTXOPTERRSIZ; n++)
2852     err_functions[n] = NULL;
2853 
2854   /* Parse init_flags if any. */
2855   /* """""""""""""""""""""""" */
2856   while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2857   {
2858     if (*flag)
2859     {
2860       if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2861         fatal_internal("Invalid flag assignment: %s.", flag);
2862 
2863       if (strcmp(fname, "stop_if_non_option") == 0)
2864       {
2865         if (eval_yes(vname, &invalid))
2866           flags.stop_if_non_option = 1;
2867         else if (!invalid)
2868           flags.stop_if_non_option = 0;
2869         else
2870           fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2871       }
2872       else if (strcmp(fname, "allow_abbreviations") == 0)
2873       {
2874         if (eval_yes(vname, &invalid))
2875           flags.allow_abbreviations = 1;
2876         else if (!invalid)
2877           flags.allow_abbreviations = 0;
2878         else
2879           fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2880       }
2881       else if (strcmp(fname, "display_usage_on_error") == 0)
2882       {
2883         if (eval_yes(vname, &invalid))
2884           flags.display_usage_on_error = 1;
2885         else if (!invalid)
2886           flags.display_usage_on_error = 0;
2887         else
2888           fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2889       }
2890       else
2891         fatal_internal("Invalid flag name: %s.", fname);
2892     }
2893   }
2894 
2895   /* Update current_state. */
2896   /* """"""""""""""""""""" */
2897   if (prog_name)
2898   {
2899     if (*prog_name == '\0')
2900       cur_state->prog_name = xstrdup("program_name");
2901     else if ((ptr = strrchr(prog_name, '/')))
2902       cur_state->prog_name = xstrdup(ptr + 1);
2903     else
2904       cur_state->prog_name = xstrdup(prog_name);
2905   }
2906   else
2907     cur_state->prog_name = xstrdup("program_name");
2908 }
2909 
2910 /* ========================================================================= */
2911 /* Utility function which create and register a par_t object in a BST        */
2912 /* embedded in a context.                                                    */
2913 /* This object will have a name and a pointer to the option it refers to.    */
2914 /* These object will be used to quickly find an option from a command        */
2915 /* line parameter during the analysis phase.                                 */
2916 /*                                                                           */
2917 /* IN      : an option name.                                                 */
2918 /* IN      : a string of command line parameters to associate to the option. */
2919 /* Returns : 1 is all was fine else 0.                                       */
2920 /* ========================================================================= */
2921 static int
opt_set_parms(char * opt_name,char * par_str)2922 opt_set_parms(char * opt_name, char * par_str)
2923 {
2924   char *  par_name, *ctx_name;
2925   char *  tmp_par_str, *end_tmp_par_str;
2926   ctx_t * ctx;
2927   opt_t * opt;
2928   bst_t * node;
2929   par_t * par, tmp_par;
2930   int     rc = 1; /* Return code. */
2931 
2932   ll_t *      list;
2933   ll_node_t * lnode;
2934 
2935   /* Look if the given option is defined. */
2936   /* """""""""""""""""""""""""""""""""""" */
2937   opt = locate_opt(opt_name);
2938   if (opt == NULL)
2939     fatal_internal("Unknown option %s.", opt_name);
2940 
2941   /* For each context using this option. */
2942   /* """"""""""""""""""""""""""""""""""" */
2943   list = opt->ctx_list;
2944 
2945   lnode = list->head;
2946   while (lnode != NULL)
2947   {
2948     /* Locate the context in the contexts tree. */
2949     /* """""""""""""""""""""""""""""""""""""""" */
2950     ctx_name = ((ctx_t *)(lnode->data))->name;
2951 
2952     ctx = locate_ctx(ctx_name);
2953     if (ctx == NULL)
2954       fatal_internal("Unknown context %s.", ctx_name);
2955     else
2956     {
2957       void * par_bst = ctx->par_bst;
2958 
2959       tmp_par_str = xstrdup(par_str);
2960       ltrim(tmp_par_str, " \t");
2961       rtrim(tmp_par_str, " \t", 0);
2962       par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2963       if (par_name == NULL)
2964         fatal_internal("Parameters are missing for option %s.", opt_name);
2965 
2966       /* For each parameter given in par_str, creates a par_t object and */
2967       /* insert it the in the parameters BST of the context.             */
2968       /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2969       while (par_name != NULL)
2970       {
2971         tmp_par.name = par_name;
2972 
2973         node = bst_find(&tmp_par, &par_bst, par_compare);
2974         if (node != NULL)
2975         {
2976           fatal_internal("The parameter %s is already defined in context %s.",
2977                          par_name, ctx->name);
2978           rc = 0;
2979         }
2980         else
2981         {
2982           par       = xmalloc(sizeof(par_t));
2983           par->name = xstrdup(par_name);
2984           par->opt  = opt; /* Link the option to this parameter. */
2985 
2986           bst_search(par, &par_bst, par_compare);
2987         }
2988         par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2989       }
2990 
2991       /* Update the value of the root of ctx->par_bst as it may have */
2992       /* been modified.                                              */
2993       /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2994       ctx->par_bst = par_bst;
2995 
2996       free(tmp_par_str);
2997     }
2998     lnode = lnode->next;
2999   }
3000 
3001   return rc;
3002 }
3003 
3004 /* ==================================================================== */
3005 /* Create a new context instance.                                       */
3006 /* IN ctx            : a context pointer to allow this instance to      */
3007 /*                     access the context fields                        */
3008 /* IN prev_ctx_inst  : the context instance whose option leading to the */
3009 /*                     creation of this new context instance is part of */
3010 /* Returns           : the new context.                                 */
3011 /* ==================================================================== */
3012 static ctx_inst_t *
new_ctx_inst(ctx_t * ctx,ctx_inst_t * prev_ctx_inst)3013 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
3014 {
3015   opt_t *      opt;
3016   opt_inst_t * gen_opt_inst;
3017   ctx_inst_t * ctx_inst;
3018   seen_opt_t * seen_opt;
3019   char *       str, *opt_name;
3020   void *       bst;
3021   bst_t *      bst_node;
3022 
3023   /* Keep a trace of the opt_inst which was at the origin of the creation */
3024   /* of this context instance.                                            */
3025   /* This will serve during the evaluation of the option callbacks.       */
3026   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3027   if (prev_ctx_inst != NULL)
3028   {
3029     gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
3030 
3031     /* Update current_state. */
3032     /* """"""""""""""""""""" */
3033     cur_state->opt_name = gen_opt_inst->opt->name;
3034   }
3035   else
3036     gen_opt_inst = NULL;
3037 
3038   /* Create and initialize the new context instance. */
3039   /* """"""""""""""""""""""""""""""""""""""""""""""" */
3040   ctx_inst                  = xmalloc(sizeof(ctx_inst_t));
3041   ctx_inst->ctx             = ctx;
3042   ctx_inst->prev_ctx_inst   = prev_ctx_inst;
3043   ctx_inst->gen_opt_inst    = gen_opt_inst;
3044   ctx_inst->incomp_bst_list = ll_new();
3045   ctx_inst->opt_inst_list   = ll_new();
3046   ctx_inst->opt_req_list    = ll_new();
3047   ctx_inst->seen_opt_bst    = NULL;
3048 
3049   ll_node_t * node;
3050 
3051   if (prev_ctx_inst == NULL)
3052     first_ctx_inst = ctx_inst;
3053 
3054   /* Initialize the occurrence counters of each opt allowed in the context. */
3055   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3056   node = ctx->opt_list->head;
3057   while (node != NULL)
3058   {
3059     opt              = node->data;
3060     opt->occurrences = 0;
3061 
3062     node = node->next;
3063   }
3064 
3065   /* Initialize the BST containing the seen indicator for all the options */
3066   /* allowed in this context instance.                                    */
3067   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3068   node = ctx->opt_list->head;
3069   while (node != NULL)
3070   {
3071     opt            = node->data;
3072     seen_opt       = xmalloc(sizeof(seen_opt_t));
3073     seen_opt->opt  = opt;
3074     seen_opt->par  = NULL;
3075     seen_opt->seen = 0;
3076 
3077     bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
3078 
3079     node = node->next;
3080   }
3081 
3082   /* Initialize the BST containing the incompatibles options.              */
3083   /* Incompatibles option names are read from strings found in the list    */
3084   /* incomp_list present in each instance of ctx_t.                        */
3085   /* These names are then used to search for the object of type seen_opt_t */
3086   /* which is already present in the seen_opt_bst of the context instance. */
3087   /* in the BST.                                                           */
3088   /* Once found the seen_opt_t object in inserted in the new BST.          */
3089   /* At the end the new BST in added to the list incomp_bst_list.          */
3090   /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3091   node = ctx->incomp_list->head;
3092   while (node != NULL)
3093   {
3094     bst = NULL;
3095     seen_opt_t tmp_seen_opt;
3096 
3097     str = xstrdup(node->data);
3098     ltrim(str, " \t");
3099     rtrim(str, " \t", 0);
3100     opt_name = strtok(str, " \t"); /* Extract the first option name. */
3101 
3102     while (opt_name != NULL) /* For each option name. */
3103     {
3104       if ((opt = locate_opt(opt_name)) != NULL)
3105       {
3106         /* The option found is searched in the tree of potential */
3107         /* seen options.                                         */
3108         /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3109         tmp_seen_opt.opt = opt;
3110 
3111         bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
3112                             seen_opt_compare);
3113 
3114         if (bst_node != NULL)
3115         {
3116           /* If found then it is added into the new BST tree. */
3117           /* """""""""""""""""""""""""""""""""""""""""""""""" */
3118           seen_opt = bst_node->key;
3119           bst_search(seen_opt, &bst, seen_opt_compare);
3120         }
3121         else
3122           /* Not found! That means that the option is unknown in this */
3123           /* context as all options has have a seen_opt structure in  */
3124           /* seen_opt_bst.                                            */
3125           /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3126           fatal_internal("%s is not known in the context %s.", opt->name,
3127                          ctx->name);
3128       }
3129       else
3130         fatal_internal("Unknown option %s.", opt_name);
3131 
3132       opt_name = strtok(NULL, " \t");
3133     }
3134 
3135     free(str);
3136     ll_append(ctx_inst->incomp_bst_list, bst);
3137 
3138     node = node->next;
3139   }
3140 
3141   /* Initialize the list of res_t structures according to the     */
3142   /* list set in the context by ctxopt_add_ctx_settings/required. */
3143   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3144   node = ctx->req_list->head;
3145   while (node != NULL)
3146   {
3147     req_t * req = xmalloc(sizeof(req_t));
3148 
3149     str = xstrdup(node->data);
3150     ltrim(str, " \t");
3151     rtrim(str, " \t", 0);
3152     opt_name = strtok(str, " \t"); /* Extract the first option name. */
3153 
3154     if ((opt = locate_opt(opt_name)) != NULL)
3155     {
3156       req->opt         = opt;
3157       req->or_opt_list = ll_new();
3158       while ((opt_name = strtok(NULL, " \t")) != NULL)
3159       {
3160         if ((opt = locate_opt(opt_name)) != NULL)
3161           ll_append(req->or_opt_list, opt);
3162         else
3163           fatal_internal("Unknown option %s.", opt_name);
3164       }
3165       ll_append(ctx_inst->opt_req_list, req);
3166     }
3167     else
3168       fatal_internal("Unknown option %s.", opt_name);
3169 
3170     free(str);
3171 
3172     node = node->next;
3173   }
3174   return ctx_inst;
3175 }
3176 
3177 /* ====================================================================== */
3178 /* Create a list formed by all the significant command line words         */
3179 /* Words beginning or ending with { or } are split. Each of these         */
3180 /* symbols will get their own place in the list.                          */
3181 /*                                                                        */
3182 /* the {...} part delimits a context, the { will not appear in the list   */
3183 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3184 /* to facilitate the parsing phase. | must not be used by the end user.   */
3185 /*                                                                        */
3186 /* IN nb_word : number of word to parse, this is typically argc-1 as the  */
3187 /*              program name is not considered.                           */
3188 /* IN words   : is the array of strings constituting the command line to  */
3189 /*              parse.                                                    */
3190 /* Returns    : 1 on success, 0 if a { or } is missing.                   */
3191 /* ====================================================================== */
3192 static int
ctxopt_build_cmdline_list(int nb_words,char ** words)3193 ctxopt_build_cmdline_list(int nb_words, char ** words)
3194 {
3195   int        i;
3196   char *     prev_word = NULL;
3197   char *     word;
3198   char *     ptr;
3199   int        level = 0;
3200   ll_node_t *node, *start_node;
3201 
3202   /* The analysis is divided into three passes, this is not optimal but  */
3203   /* must be done only one time. Doing that we privilege readability.    */
3204   /*                                                                     */
3205   /* In the following, SG is the ascii character 1d (dec 29)             */
3206   /*                                                                     */
3207   /* The first pass creates the list, extract the leading an trailing    */
3208   /*  SG '{' and '}' of each word and give them their own place in the   */
3209   /* list                                                                */
3210   /*                                                                     */
3211   /* The second pass transform the '{...}' blocks by a trailing SG       */
3212   /* ({...} -> ...|)                                                     */
3213   /*                                                                     */
3214   /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3215   /* the middle in the remaining list elements and recreate the pseudo   */
3216   /* argument: {}                                                        */
3217   /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3218 
3219   /* If the option list is not empty, clear it before going further. */
3220   /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3221   if (cmdline_list != NULL)
3222     ll_destroy(cmdline_list, free);
3223 
3224   cmdline_list = ll_new();
3225 
3226   start_node = cmdline_list->head; /* In the following loop start_node will *
3227                                     * contain a pointer to the current      *
3228                                     * word stripped from its leading        *
3229                                     * sequence of {, }.                     */
3230   for (i = 0; i < nb_words; i++)
3231   {
3232     size_t len = strlen(words[i]);
3233     size_t start, end;
3234     char * str;
3235 
3236     str = words[i];
3237 
3238     /* Replace each occurrence of the legal word {} by the characters */
3239     /* 0x02 and 0x03 to hide them from the following process.         */
3240     /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3241     while ((ptr = strstr(str, "{}")) != NULL)
3242     {
3243       *ptr       = 0x02; /* Arbitrary values unlikely */
3244       *(ptr + 1) = 0x03; /* present in a word.        */
3245     }
3246 
3247     if (len > 1) /* The word contains at least 2 characters. */
3248     {
3249       start = 0;
3250 
3251       /* Interpret its beginning and look for the start of the real word. */
3252       /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3253       while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
3254       {
3255         ll_append(cmdline_list, xstrndup(str + start, 1));
3256         start++;
3257         start_node = cmdline_list->tail;
3258       }
3259 
3260       end = len - 1;
3261       if (str[end] == '{' || str[end] == '}')
3262       {
3263         if (end > 0 && str[end - 1] != '\\')
3264         {
3265           ll_append(cmdline_list, xstrndup(str + end, 1));
3266           end--;
3267           node = cmdline_list->tail;
3268 
3269           while (str[end] == '{' || str[end] == '}')
3270           {
3271             if (end > start && str[end - 1] == '\\')
3272               break;
3273 
3274             ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3275             end--;
3276             node = node->prev;
3277           }
3278         }
3279       }
3280 
3281       if (start <= end)
3282       {
3283         if (start_node != NULL)
3284           ll_insert_after(cmdline_list, start_node,
3285                           xstrndup(str + start, end - start + 1));
3286         else
3287           ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3288         start_node = cmdline_list->tail;
3289       }
3290     }
3291     else if (len == 1)
3292     {
3293       ll_append(cmdline_list, xstrdup(str));
3294       start_node = cmdline_list->tail;
3295     }
3296   }
3297 
3298   /* 2nd pass. */
3299   /* """"""""" */
3300   node = cmdline_list->head;
3301 
3302   level = 0;
3303   while (node != NULL)
3304   {
3305     word = node->data;
3306 
3307     if (strcmp(word, "{") == 0)
3308     {
3309       ll_node_t * old_node = node;
3310       level++;
3311       node = node->next;
3312       free(word);
3313       ll_delete(cmdline_list, old_node);
3314     }
3315     else if (strcmp(word, "}") == 0)
3316     {
3317       level--;
3318 
3319       if (level < 0)
3320         return 0;
3321       else
3322         *word = 0x1d;
3323     }
3324     else
3325       node = node->next;
3326   }
3327 
3328   if (level != 0)
3329     return 0;
3330 
3331   /* 3rd pass. */
3332   /* """"""""" */
3333   node = cmdline_list->head;
3334 
3335   while (node != NULL)
3336   {
3337     word = node->data;
3338 
3339     /* Restore the original { and } characters forming the legal word {}. */
3340     /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3341     while ((ptr = strchr(word, 0x02)) != NULL)
3342       *ptr = '{';
3343     while ((ptr = strchr(word, 0x03)) != NULL)
3344       *ptr = '}';
3345 
3346     /* Remove a SG if the previous element is SG. */
3347     /* """""""""""""""""""""""""""""""""""""""""" */
3348     if (strcmp(word, "\x1d") == 0)
3349     {
3350       if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3351       {
3352         ll_node_t * old_node = node;
3353         node                 = node->prev;
3354         free(old_node->data);
3355         ll_delete(cmdline_list, old_node);
3356       }
3357     }
3358     else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3359                                       * a parameter. Protect it.            */
3360     {
3361       free(node->data);
3362       node->data = xstrdup("\\-");
3363     }
3364 
3365     prev_word = node->data;
3366     node      = node->next;
3367   }
3368 
3369   /* Clean useless and SG at the beginning and end of list. */
3370   /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3371   node = cmdline_list->head;
3372 
3373   if (node == NULL)
3374     return 1;
3375 
3376   word = node->data;
3377 
3378   if (strcmp(word, "\x1d") == 0)
3379   {
3380     free(word);
3381     ll_delete(cmdline_list, node);
3382   }
3383 
3384   node = cmdline_list->tail;
3385   if (node == NULL)
3386     return 1;
3387 
3388   word = node->data;
3389 
3390   if (strcmp(word, "\x1d") == 0)
3391   {
3392     free(word);
3393     ll_delete(cmdline_list, node);
3394   }
3395 
3396   return 1;
3397 }
3398 
3399 /* ===================================================================== */
3400 /* Build and analyze the command line list and create the linked data    */
3401 /* structures whose data will be evaluated later by ctxopt_evaluate.     */
3402 /* This function identifies the following errors and creates an array of */
3403 /* The remaining unanalyzed arguments.                                   */
3404 /* - detect missing arguments                                            */
3405 /* - detect too many arguments                                           */
3406 /* - detect unknown parameters in a context                              */
3407 /* - detect too many occurrences of a parameters in a context            */
3408 /* - detect missing required arguments in a context                      */
3409 /*                                                                       */
3410 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3411 /*              program name is not considered                           */
3412 /* IN words   : is the array of strings constituting the command line to */
3413 /*              parse.                                                   */
3414 /* OUT nb_rem_args : nb of remaining command line arguments if a --      */
3415 /*                   is present in the list.                             */
3416 /* OUT rem_args    : array of remaining command line arguments if a --   */
3417 /*                   is present in the list. This array must be free by  */
3418 /*                   The caller as it is allocated here.                 */
3419 /* ===================================================================== */
3420 void
ctxopt_analyze(int nb_words,char ** words,int * nb_rem_args,char *** rem_args)3421 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3422                char *** rem_args)
3423 {
3424   char * ctxopt_debug_env; /* Environment variable CTXOPT_DEBUG content.  */
3425   int    ctxopt_debug;     /* 1 if ctxopt_debug_env is set and not empty. *
3426                             | 0 if ctxopt_debug_env is unset or empty.    */
3427 
3428   ctx_t *      ctx;
3429   opt_t *      opt;
3430   par_t *      par;
3431   ctx_inst_t * ctx_inst;
3432   opt_inst_t * opt_inst;
3433   int          expect_par        = 0;
3434   int          expect_arg        = 0;
3435   int          expect_par_or_arg = 0;
3436 
3437   ll_node_t *  cli_node;
3438   bst_t *      bst_node;
3439   seen_opt_t * bst_seen_opt;
3440   char *       par_name;
3441   void *       bst;
3442 
3443   ll_node_t * node;
3444 
3445   if (!ctxopt_build_cmdline_list(nb_words, words))
3446     fatal_internal("The command line could not be parsed: "
3447                    "missing '{' or '}' detected.");
3448 
3449   if (main_ctx == NULL)
3450     fatal_internal("At least one context must have been created.");
3451 
3452   /* Check that all options has an action and at least one parameter. */
3453   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3454   bst_walk(options_bst, bst_check_opt_cb);
3455 
3456   /* CTXOPT debug setting. */
3457   /* """"""""""""""""""""" */
3458   ctxopt_debug_env = getenv("CTXOPT_DEBUG");
3459   if (ctxopt_debug_env != NULL && *ctxopt_debug_env != '\0')
3460     ctxopt_debug = 1;
3461   else
3462     ctxopt_debug = 0;
3463 
3464   /* Create the first ctx_inst record. */
3465   /* """"""""""""""""""""""""""""""""" */
3466   ctx = main_ctx;
3467 
3468   ctx_inst_list      = ll_new();
3469   ctx_inst           = new_ctx_inst(ctx, NULL);
3470   ctx_inst->par_name = NULL;
3471 
3472   /* Update current_state. */
3473   /* """"""""""""""""""""" */
3474   cur_state->ctx_name = ctx->name;
3475 
3476   ll_append(ctx_inst_list, ctx_inst);
3477 
3478   /* For each node in the command line. */
3479   /* """""""""""""""""""""""""""""""""" */
3480   cli_node   = cmdline_list->head;
3481   expect_par = 1;
3482   par_name   = NULL;
3483 
3484   while (cli_node != NULL)
3485   {
3486     if (strcmp(cli_node->data, "--") == 0)
3487       break; /* No new parameter will be analyzed after this point. */
3488 
3489     par_name = cli_node->data;
3490 
3491     /* Replace a leading -- by a single - */
3492     /* """""""""""""""""""""""""""""""""" */
3493     if (strncmp(par_name, "--", 2) == 0)
3494       par_name += 1; /* Ignore the first dash. */
3495 
3496     if (strcmp(par_name, "\x1d") == 0)
3497     {
3498       check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3499       check_for_occurrence_issues(ctx_inst);
3500       check_for_requirement_issues(ctx_inst);
3501 
3502       /* Forced backtracking to the previous context instance. */
3503       /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3504       if (ctx_inst->prev_ctx_inst != NULL)
3505       {
3506         ctx_inst = ctx_inst->prev_ctx_inst;
3507         ctx      = ctx_inst->ctx;
3508 
3509         /* Update current_states. */
3510         /* """""""""""""""""""""  */
3511         cur_state->ctx_name     = ctx->name;
3512         cur_state->ctx_par_name = ctx_inst->par_name;
3513 
3514         if (ctxopt_debug)
3515           fprintf(stderr,
3516                   "CTXOPT_DEBUG: Context forced backtrack, "
3517                   "new current context: %s.\n",
3518                   ctx->name);
3519       }
3520       else
3521       {
3522         /* Update current_state. */
3523         /* """"""""""""""""""""" */
3524         cur_state->ctx_par_name = NULL;
3525       }
3526     }
3527     else if (expect_par && *par_name == '-')
3528     {
3529       int    pos = 0;
3530       char * prefix;
3531 
3532       /* Update current_state. */
3533       /* """"""""""""""""""""" */
3534       cur_state->cur_opt_par_name = par_name;
3535       cur_state->ctx_name         = ctx->name;
3536       cur_state->ctx_par_name     = ctx_inst->par_name;
3537 
3538       if (ctxopt_debug)
3539         fprintf(stderr, "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3540                 par_name, cur_state->ctx_name);
3541 
3542       /* An expected parameter has been seen. */
3543       /* """""""""""""""""""""""""""""""""""" */
3544       if ((par = locate_par(par_name, ctx)) == NULL)
3545       {
3546         opt_t * popt;
3547         char *  word;
3548 
3549         /* Look if this parameter is an unique abbreviation of a longer */
3550         /* parameter. If this is the case then just replace it with its */
3551         /* full length version and try again.                           */
3552         /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3553         if (flags.allow_abbreviations)
3554           if ((word = abbrev_expand(par_name, ctx)) != NULL)
3555           {
3556             cli_node->data = word;
3557             continue;
3558           }
3559 
3560         /* Try to find a prefix which is a valid parameter in this context */
3561         /* If found, split the cli_node in two to build a new parameter    */
3562         /* node and followed by a node containing the remaining string     */
3563         /* If the new parameter corresponds to an option not taking        */
3564         /* argument then prefix the remaining string whit a dash as it may */
3565         /* contain a new parameter.                                        */
3566         /* The new parameter will be re-evaluated in the next iteration of */
3567         /* the loop.                                                       */
3568         /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3569         prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3570         if (prefix != NULL && pos != 0)
3571         {
3572           if (ctxopt_debug)
3573             fprintf(stderr,
3574                     "CTXOPT_DEBUG: Found a valid parameter "
3575                     "as a prefix of %s: %s.\n",
3576                     par_name, prefix);
3577 
3578           cli_node->data = prefix; /* prefix contains le name of a valid *
3579                                     | parameter in this context.         */
3580 
3581           if (popt->args)
3582           {
3583             /* The parameter may be followed by arguments. */
3584             /* ''''''''''''''''''''''''''''''''''''''''''' */
3585             if (*(par_name + pos) == '-')
3586             {
3587               word = xstrdup("\\"); /* Protect the '-' */
3588               word = strappend(word, par_name + pos, (char *)0);
3589             }
3590             else
3591               word = xstrdup(par_name + pos);
3592           }
3593           else
3594           {
3595             /* The parameter does not take arguments, the    */
3596             /* following word must be a parameter or nothing */
3597             /* hence prefix it with a dash.                  */
3598             /* ''''''''''''''''''''''''''''''''''''''''''''' */
3599             word = xstrdup("-");
3600             word = strappend(word, par_name + pos, (char *)0);
3601           }
3602 
3603           /* Insert it after the current node in the list. */
3604           /* """"""""""""""""""""""""""""""""""""""""""""" */
3605           ll_insert_after(cmdline_list, cli_node, word);
3606 
3607           continue; /* loop. */
3608         }
3609         else
3610         {
3611           check_for_missing_mandatory_opt(ctx_inst, par_name);
3612           check_for_occurrence_issues(ctx_inst);
3613           check_for_requirement_issues(ctx_inst);
3614 
3615           if (ctx_inst->prev_ctx_inst == NULL)
3616           {
3617             char * errmsg = xstrdup("");
3618 
3619             /* Update current_state. */
3620             /* """"""""""""""""""""" */
3621             cur_state->ctx_par_name = NULL;
3622 
3623             *user_string  = '\0';
3624             *user_string2 = '\0';
3625 
3626             user_string = strappend(user_string, par_name, (char *)0);
3627 
3628             bst_walk(contexts_bst, bst_match_par_cb);
3629 
3630             if (*user_string2 != '\0')
3631             {
3632               char * help_msg;
3633               int    count = 0;
3634 
3635               count = strchrcount(user_string2, '\n');
3636 
3637               if (flags.display_usage_on_error)
3638                 help_msg = ", see below";
3639               else
3640                 help_msg = "";
3641 
3642               if (count == 0) /* Only one context involved. */
3643                 errmsg = strappend(
3644                   errmsg,
3645                   "\nThis parameter is only valid in the following "
3646                   "context:\n",
3647                   user_string2,
3648                   "\n\nFirst switch to this context using the appropriate "
3649                   "parameter",
3650                   help_msg, ".\n", (char *)0);
3651               else
3652                 errmsg = strappend(
3653                   errmsg,
3654                   "\nThis parameter is only valid in one of the following "
3655                   "contexts:\n",
3656                   user_string2,
3657                   "\n\nFirst switch to one of them using the appropriate "
3658                   "parameter",
3659                   help_msg, ".\n", (char *)0);
3660             }
3661 
3662             fatal(CTXOPTUNKPAR, errmsg);
3663           }
3664           else
3665           {
3666             /* Tries to backtrack and analyse the same parameter in the */
3667             /* previous context.                                        */
3668             /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3669             ctx_inst = ctx_inst->prev_ctx_inst;
3670             ctx      = ctx_inst->ctx;
3671 
3672             if (ctxopt_debug)
3673               fprintf(stderr,
3674                       "CTXOPT_DEBUG: Context backtrack, "
3675                       "new current context: %s.\n",
3676                       ctx->name);
3677 
3678             /* Update current_state. */
3679             /* """"""""""""""""""""" */
3680             cur_state->ctx_name     = ctx->name;
3681             cur_state->ctx_par_name = ctx_inst->par_name;
3682 
3683             cli_node = cli_node->prev;
3684           }
3685         }
3686       }
3687       else
3688       {
3689         seen_opt_t seen_opt;
3690 
3691         /* The parameter is valid in the context, create a opt_inst and */
3692         /* append it to the ctx_inst list options list.                 */
3693         /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3694         opt = par->opt;
3695 
3696         opt->occurrences++;
3697 
3698         opt_inst                = xmalloc(sizeof(opt_inst_t));
3699         opt_inst->opt           = opt;
3700         opt_inst->par           = par_name;
3701         opt_inst->values_list   = ll_new();
3702         opt_inst->next_ctx_inst = NULL;
3703 
3704         /* Update current_state. */
3705         /* """"""""""""""""""""" */
3706         cur_state->cur_opt_params = opt->params;
3707 
3708         /* Priority option are inserted at the start of the opt_inst list */
3709         /* but their order of appearance in the context definition must   */
3710         /* be preserver so each new priority option will be placed after  */
3711         /* the previous ones at the start of the opt_inst list.           */
3712         /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3713         if (!opt->eval_first)
3714         {
3715           /* Look if we have a registered dependency in the order of the */
3716           /* evaluation of two options.                                  */
3717           /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3718           if (opt->eval_before_list->len > 0)
3719           {
3720             ll_t *      list = ctx_inst->opt_inst_list;
3721             ll_node_t * opt_inst_node;
3722 
3723             ll_t *      before_list = opt->eval_before_list;
3724             ll_node_t * before_node = before_list->head;
3725 
3726             ll_node_t * target_node = NULL; /* If not NULL, the new node   *
3727                                             |  will be inserted before it. */
3728 
3729             /* For each entry in eval_before_list, try to find if it       */
3730             /* refers to an option already entered in the context. If this */
3731             /* is the case, insert it just before it instead of putting it */
3732             /* at the end.                                                 */
3733             /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3734             while (before_node != NULL)
3735             {
3736               opt_inst_node = list->head;
3737 
3738               while (opt_inst_node != target_node)
3739               {
3740                 opt_t * tmp_opt = (((opt_inst_t *)opt_inst_node->data))->opt;
3741 
3742                 /* We have found an option mentioned if the before_list  */
3743                 /* of the option we want to add. We can stop searching.  */
3744                 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3745                 if (strcmp(tmp_opt->name, ((opt_t *)before_node->data)->name))
3746                   opt_inst_node = opt_inst_node->next;
3747                 else
3748                   target_node = opt_inst_node; /* Set the target node. */
3749               }
3750 
3751               before_node = before_node->next;
3752             }
3753 
3754             /* Insert or append ? */
3755             /* """""""""""""""""" */
3756             if (target_node != NULL)
3757               ll_insert_before(ctx_inst->opt_inst_list, target_node, opt_inst);
3758             else
3759               ll_append(ctx_inst->opt_inst_list, opt_inst);
3760           }
3761           else
3762             ll_append(ctx_inst->opt_inst_list, opt_inst);
3763         }
3764         else
3765         {
3766           ll_node_t *  opt_inst_node = ctx_inst->opt_inst_list->head;
3767           opt_inst_t * tmp_opt_inst;
3768 
3769           while (opt_inst_node != NULL)
3770           {
3771             tmp_opt_inst = opt_inst_node->data;
3772             if (!tmp_opt_inst->opt->eval_first)
3773             {
3774               ll_insert_before(ctx_inst->opt_inst_list, opt_inst_node,
3775                                opt_inst);
3776               break;
3777             }
3778             else
3779               opt_inst_node = opt_inst_node->next;
3780           }
3781           if (opt_inst_node == NULL)
3782             ll_append(ctx_inst->opt_inst_list, opt_inst);
3783         }
3784 
3785         /* Check if an option was already seen in the */
3786         /* current context instance.                  */
3787         /* """""""""""""""""""""""""""""""""""""""""" */
3788         seen_opt.opt = opt;
3789 
3790         bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3791                             seen_opt_compare);
3792 
3793         /* bst_node cannot be NULL here. */
3794 
3795         bst_seen_opt = (seen_opt_t *)(bst_node->key);
3796 
3797         if (!opt->multiple && bst_seen_opt->seen == 1)
3798           fatal(CTXOPTDUPOPT, "");
3799 
3800         /* Check if this option is compatible with the options already */
3801         /* seen in this context instance.                              */
3802         /* Look if the option is present in one on the BST present in  */
3803         /* the incomp_bst_list of the context instance.                */
3804         /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3805         node = ctx_inst->incomp_bst_list->head;
3806         while (node != NULL)
3807         {
3808           bst         = node->data;
3809           user_object = NULL;
3810 
3811           /* There can only have one seen_opt object in the BST tree was */
3812           /* already seen, try to locate it, the result will be put in   */
3813           /* user_object by the bst_seen_opt_seen_cb function.           */
3814           /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3815           bst_walk(bst, bst_seen_opt_seen_cb);
3816 
3817           /* If it is the case, look if the current option is also */
3818           /* in this BST.                                          */
3819           /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3820           if (user_object != NULL)
3821           {
3822             bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3823 
3824             if (bst_node != NULL)
3825             {
3826               bst_seen_opt = (seen_opt_t *)(bst_node->key);
3827               if (bst_seen_opt->seen == 0)
3828                 fatal(CTXOPTINCOPT, (char *)user_object);
3829             }
3830           }
3831 
3832           node = node->next;
3833         }
3834 
3835         /* Mark this option as seen in the current context instance. */
3836         /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3837         bst_seen_opt->seen = 1;
3838         free(bst_seen_opt->par);
3839         bst_seen_opt->par = xstrdup(par_name);
3840 
3841         /* If this option leads to a next context, create a new ctx_inst */
3842         /* and switch to it for the analyse of the future parameter.     */
3843         /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3844         if (opt->next_ctx != NULL)
3845         {
3846           ctx = locate_ctx(opt->next_ctx);
3847 
3848           if (ctx == NULL)
3849             fatal_internal("Unknown context %s.", opt->next_ctx);
3850 
3851           opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3852           ctx_inst->par_name                 = xstrdup(par_name);
3853 
3854           ll_append(ctx_inst_list, ctx_inst);
3855 
3856           if (ctxopt_debug)
3857             fprintf(stderr,
3858                     "CTXOPT_DEBUG: Context change, "
3859                     "new current context: %s.\n",
3860                     ctx->name);
3861         }
3862 
3863         /* Look is we must expect some arguments. */
3864         /* """""""""""""""""""""""""""""""""""""" */
3865         expect_par_or_arg = 0;
3866         expect_par        = 0;
3867         expect_arg        = 0;
3868 
3869         if (!opt->args)
3870           expect_par = 1; /* Parameter doesn't accept any argument. */
3871         else
3872         {
3873           if (!opt->optional_args)
3874             expect_arg = 1; /* Parameter has mandatory arguments. */
3875           else
3876             expect_par_or_arg = 1; /* Parameter has optional arguments. */
3877         }
3878       }
3879     }
3880     else if (expect_par && *par_name != '-')
3881     {
3882       ll_node_t * n = cli_node->next;
3883 
3884       if (!flags.stop_if_non_option)
3885         /* Look if potential arguments must still be analyzed until the  */
3886         /* end of the context/command line part to analyze/command line. */
3887         /* If this is the case we have met an extra argument.            */
3888         /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3889         while (n != NULL)
3890         {
3891           if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3892             fatal(CTXOPTUNXARG, "");
3893 
3894           if (*(char *)(n->data) == '-')
3895             fatal(CTXOPTUNXARG, "");
3896 
3897           n = n->next;
3898         }
3899 
3900       break; /* An unexpected non parameter was seen, if no Potential *
3901               | arguments remain in the command line or               *
3902               | flags.stop_if_non_option is set, assume that it is is *
3903               | the first of the non arguments and stop the command   *
3904               | line analysis.                                        */
3905     }
3906     else if (expect_arg && *par_name != '-')
3907     {
3908       ll_node_t *    cstr_node;
3909       constraint_t * cstr;
3910 
3911       if (ctxopt_debug)
3912         fprintf(stderr, "CTXOPT_DEBUG: Argument: %s.\n", par_name);
3913 
3914       /* Check if the arguments of the option respects */
3915       /* the attached constraints if any.              */
3916       /* """"""""""""""""""""""""""""""""""""""""""""" */
3917       cstr_node = opt->constraints_list->head;
3918       while (cstr_node != NULL)
3919       {
3920         cstr = cstr_node->data;
3921         if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3922                               cur_state->cur_opt_par_name))
3923         {
3924           fputs("\n", stderr);
3925           ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3926         }
3927 
3928         cstr_node = cstr_node->next;
3929       }
3930 
3931       /* If the argument is valid, store it. */
3932       /* """"""""""""""""""""""""""""""""""" */
3933       if (*par_name == '\\' && *(par_name + 1) == '-')
3934         ll_append(opt_inst->values_list, par_name + 1);
3935       else
3936         ll_append(opt_inst->values_list, par_name);
3937 
3938       expect_arg        = 0;
3939       expect_par        = 0;
3940       expect_par_or_arg = 0;
3941 
3942       if (opt->multiple_args)
3943         expect_par_or_arg = 1;
3944       else
3945         expect_par = 1; /* Parameter takes only one argument. */
3946     }
3947     else if (expect_arg && *par_name == '-')
3948       fatal(CTXOPTMISARG, "");
3949     else if (expect_par_or_arg)
3950     {
3951       expect_arg        = 0;
3952       expect_par        = 0;
3953       expect_par_or_arg = 0;
3954 
3955       if (*par_name != '-')
3956         expect_arg = 1; /* Consider this word as an argument and retry. */
3957       else
3958         expect_par = 1; /* Consider this word as a parameter and retry. */
3959 
3960       cli_node = cli_node->prev;
3961     }
3962 
3963     cli_node = cli_node->next;
3964   }
3965 
3966   if (cmdline_list->len > 0 && par_name && *par_name == '-')
3967   {
3968     if (expect_arg && opt && !opt->optional_args)
3969       fatal(CTXOPTMISARG, "");
3970   }
3971 
3972   /* Look if a context_instance has unseen mandatory options. */
3973   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3974   node = ctx_inst_list->head;
3975   while (node != NULL)
3976   {
3977     ctx_inst = node->data;
3978 
3979     /* Update current_state. */
3980     /* """"""""""""""""""""" */
3981     cur_state->ctx_name     = ctx_inst->ctx->name;
3982     cur_state->ctx_par_name = ctx_inst->par_name;
3983 
3984     check_for_missing_mandatory_opt(ctx_inst, par_name);
3985     check_for_occurrence_issues(ctx_inst);
3986     check_for_requirement_issues(ctx_inst);
3987 
3988     node = node->next;
3989   }
3990 
3991   /* Allocate the array containing the remaining not analyzed */
3992   /* command line arguments.                                  */
3993   /* NOTE: The strings in the array are just pointer to the   */
3994   /*       data of the generating list and must not be freed. */
3995   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3996   if (cli_node != NULL)
3997   {
3998     if (strcmp((char *)cli_node->data, "--") == 0)
3999       /* The special parameter -- was encountered, the -- argument is not */
4000       /* put in the remaining arguments.                                  */
4001       /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4002       ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
4003     else
4004       /* A non parameter was encountered when a parameter was expected. We  */
4005       /* assume that the evaluation of the remaining command line  argument */
4006       /* are not the responsibility of the users code.                      */
4007       /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4008       ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
4009   }
4010   else
4011   {
4012     *nb_rem_args   = 0;
4013     *rem_args      = xmalloc(sizeof(char *));
4014     (*rem_args)[0] = NULL;
4015   }
4016 }
4017 
4018 /* ==================================================== */
4019 /* Free ctxopt memory used for its internal structures. */
4020 /* ==================================================== */
4021 void
ctxopt_free_memory(void)4022 ctxopt_free_memory(void)
4023 {
4024   ll_destroy(cmdline_list, free);
4025   ll_destroy(ctx_inst_list, ctx_inst_free);
4026   bst_destroy(options_bst, opt_free);
4027   bst_destroy(contexts_bst, ctx_free);
4028 }
4029 
4030 /* ==================================================================== */
4031 /* Parse the options data structures and launches the callback function */
4032 /* attached to each options instances.                                  */
4033 /* This calls a recursive function which proceeds context per context.  */
4034 /* ==================================================================== */
4035 void
ctxopt_evaluate(void)4036 ctxopt_evaluate(void)
4037 {
4038   evaluate_ctx_inst(first_ctx_inst);
4039 }
4040 
4041 /* =================================================================== */
4042 /* Recursive function called by ctxopt_evaluate to process the list of */
4043 /* the opt_inst present in a ctx_inst and attempt  to evaluate the     */
4044 /* action attached to the context and its option instances.            */
4045 /* =================================================================== */
4046 static void
evaluate_ctx_inst(ctx_inst_t * ctx_inst)4047 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
4048 {
4049   opt_inst_t * opt_inst;
4050   ctx_t *      ctx;
4051   opt_t *      opt;
4052   ll_node_t *  opt_inst_node;
4053   char **      args;
4054   int          nb_args;
4055 
4056   if (ctx_inst == NULL)
4057     return;
4058 
4059   ctx = ctx_inst->ctx;
4060 
4061   /* Do not evaluate the action attached to this context is there is no */
4062   /* option to evaluate.                                                */
4063   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4064   opt_inst_node = ctx_inst->opt_inst_list->head;
4065   if (opt_inst_node == NULL)
4066     return;
4067 
4068   /* Call the entering action attached to this context if any. */
4069   /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4070   if (ctx->action != NULL)
4071   {
4072     if (ctx_inst->prev_ctx_inst != NULL)
4073       ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
4074                   ctx->nb_data, ctx->data);
4075     else
4076       ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
4077   }
4078 
4079   /* For each instance of options. */
4080   /* """"""""""""""""""""""""""""" */
4081   while (opt_inst_node != NULL)
4082   {
4083     opt_inst = (opt_inst_t *)(opt_inst_node->data);
4084     ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
4085                 &args);
4086     opt = opt_inst->opt;
4087 
4088     /* Launch the attached action if any. */
4089     /* """""""""""""""""""""""""""""""""" */
4090     if (opt->action != NULL)
4091       opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
4092                   opt->nb_data, opt->data, ctx->nb_data, ctx->data);
4093 
4094     if (opt_inst->next_ctx_inst != NULL)
4095       evaluate_ctx_inst(opt_inst->next_ctx_inst);
4096 
4097     if (args != NULL)
4098       free(args);
4099 
4100     opt_inst_node = opt_inst_node->next;
4101   }
4102 
4103   /* Call the exiting action attached to this context if any. */
4104   /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4105   if (ctx->action != NULL)
4106   {
4107     if (ctx_inst->prev_ctx_inst != NULL)
4108       ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
4109                   ctx->nb_data, ctx->data);
4110     else
4111       ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
4112   }
4113 }
4114 
4115 /* ============================================================ */
4116 /* Create and initializes a new context.                        */
4117 /* - allocate space.                                            */
4118 /* - name it.                                                   */
4119 /* - initialize its option with a few of their characteristics. */
4120 /* ============================================================ */
4121 void
ctxopt_new_ctx(char * name,char * opts_specs)4122 ctxopt_new_ctx(char * name, char * opts_specs)
4123 {
4124   ctx_t * ctx;
4125   char *  p;
4126 
4127   if (!ctxopt_initialized)
4128     fatal_internal("Please call ctxopt_init first.");
4129 
4130   ctx = xmalloc(sizeof(ctx_t));
4131 
4132   /* Validates the context name: */
4133   /* ALPHA+(ALPHANUM|_)*         */
4134   /* """"""""""""""""""""""""""" */
4135   p = name;
4136   if (!isalpha(*p))
4137     fatal_internal("A context name must start with a letter: %s.", name);
4138 
4139   p++;
4140   while (*p)
4141   {
4142     if (!isalnum(*p) && *p != '_')
4143       fatal_internal("A context name must only contain letters, "
4144                      "numbers or '_': %s.",
4145                      name);
4146     p++;
4147   }
4148 
4149   ctx->name        = xstrdup(name);
4150   ctx->opt_list    = ll_new(); /* List of options legit in this context.   */
4151   ctx->incomp_list = ll_new(); /* List of incompatible options strings.    */
4152   ctx->req_list    = ll_new(); /* List of opts/required opts tuples (str). */
4153   ctx->par_bst     = NULL;
4154   ctx->data        = NULL;
4155   ctx->action      = NULL;
4156 
4157   /* The first created context is the main one. */
4158   /* """""""""""""""""""""""""""""""""""""""""" */
4159   if (contexts_bst == NULL)
4160   {
4161     main_ctx = ctx;
4162 
4163     cur_state->ctx_name = ctx->name;
4164   }
4165 
4166   if (init_opts(opts_specs, ctx) == 0)
4167     exit(EXIT_FAILURE);
4168   if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
4169     fatal_internal("The context %s already exists.", name);
4170   else
4171     bst_search(ctx, &contexts_bst, ctx_compare);
4172 }
4173 
4174 /* ==================================================== */
4175 /* Display a usage screen limited to a specific context */
4176 /* IN: the context name.                                */
4177 /* IN: what to do after (continue or exit the program)  */
4178 /*     possible values: continue_after, exit_after.     */
4179 /* ==================================================== */
4180 void
ctxopt_ctx_disp_usage(char * ctx_name,usage_behaviour action)4181 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
4182 {
4183   ctx_t * ctx;
4184   ll_t *  list;
4185 
4186   int has_optional    = 0;
4187   int has_ellipsis    = 0;
4188   int has_rule        = 0;
4189   int has_generic_arg = 0;
4190   int has_ctx_change  = 0;
4191   int has_early_eval  = 0;
4192 
4193   if (!flags.display_usage_on_error)
4194     return;
4195 
4196   ctx = locate_ctx(ctx_name);
4197   if (ctx == NULL)
4198     fatal_internal("Unknown context %s.", ctx_name);
4199 
4200   if (cur_state->ctx_par_name == NULL)
4201     printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
4202   else
4203     printf("\nSynopsis for the context introduced by %s:\n",
4204            cur_state->ctx_par_name);
4205 
4206   list = ctx->opt_list;
4207   print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4208                 &has_ctx_change, &has_early_eval);
4209 
4210   print_before_constraints(list);
4211 
4212   print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4213                      has_optional, has_ellipsis, has_rule);
4214 
4215   if (action == exit_after)
4216     exit(EXIT_FAILURE);
4217 }
4218 
4219 /* =================================================== */
4220 /* Display a full usage screen about all contexts.     */
4221 /* IN: what to do after (continue or exit the program) */
4222 /*     possible values: continue_after, exit_after.    */
4223 /* =================================================== */
4224 void
ctxopt_disp_usage(usage_behaviour action)4225 ctxopt_disp_usage(usage_behaviour action)
4226 {
4227   ll_t * list;
4228   int    has_optional    = 0;
4229   int    has_ellipsis    = 0;
4230   int    has_rule        = 0;
4231   int    has_generic_arg = 0;
4232   int    has_ctx_change  = 0;
4233   int    has_early_eval  = 0;
4234 
4235   if (!flags.display_usage_on_error)
4236     return;
4237 
4238   if (main_ctx == NULL)
4239     fatal_internal("At least one context must have been created.");
4240 
4241   /* Usage for the first context. */
4242   /* """""""""""""""""""""""""""" */
4243   printf("\nAllowed options in the base context:\n");
4244   list = main_ctx->opt_list;
4245   print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4246                 &has_ctx_change, &has_early_eval);
4247 
4248   /* Dependency constraints between options. */
4249   /* """"""""""""""""""""""""""""""""""""""" */
4250   print_before_constraints(list);
4251 
4252   /* Usage for the other contexts. */
4253   /* """"""""""""""""""""""""""""" */
4254   bst_walk(contexts_bst, bst_print_ctx_cb);
4255 
4256   /* Contextual syntactic explanations. */
4257   /* """""""""""""""""""""""""""""""""" */
4258   print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4259                      has_optional, has_ellipsis, has_rule);
4260 
4261   if (action == exit_after)
4262     exit(EXIT_FAILURE);
4263 }
4264 
4265 /* ************************************ */
4266 /* Built-in constraint check functions. */
4267 /* ************************************ */
4268 
4269 /* ============================================================= */
4270 /* This constraint checks if each arguments respects a format as */
4271 /* defined for the scanf function.                               */
4272 /* return 1 if yes and 0 if no.                                  */
4273 /* ============================================================= */
4274 int
ctxopt_format_constraint(int nb_args,char ** args,char * value,char * par)4275 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
4276 {
4277   int rc = 0;
4278 
4279   char   x[256];
4280   char   y;
4281   char * format;
4282 
4283   if (nb_args != 1)
4284     fatal_internal("Format constraint, invalid number of parameters.");
4285 
4286   if (strlen(value) > 255)
4287     value[255] = '\0';
4288 
4289   format = xstrdup(args[0]);
4290   format = strappend(format, "%c", (char *)0);
4291 
4292   rc = sscanf(value, format, x, &y);
4293   if (rc != 1)
4294     fprintf(stderr,
4295             "The argument %s of %s does not respect the imposed format %s.",
4296             value, par, args[0]);
4297 
4298   free(format);
4299 
4300   return rc == 1;
4301 }
4302 
4303 /* ================================================================== */
4304 /* This constraint checks if each arguments of the option instance is */
4305 /* between a minimum and a maximum (inclusive).                       */
4306 /* return 1 if yes and 0 if no.                                       */
4307 /* ================================================================== */
4308 int
ctxopt_re_constraint(int nb_args,char ** args,char * value,char * par)4309 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
4310 {
4311   regex_t re;
4312 
4313   if (nb_args != 1)
4314     fatal_internal(
4315       "Regular expression constraint, invalid number of parameters.");
4316 
4317   if (regcomp(&re, args[0], REG_EXTENDED) != 0)
4318     fatal_internal("Invalid regular expression %s.", args[0]);
4319 
4320   if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
4321   {
4322     fprintf(stderr,
4323             "The argument %s of %s doesn't match the constraining "
4324             "regular expression %s.",
4325             value, par, args[0]);
4326     return 0;
4327   }
4328 
4329   regfree(&re);
4330 
4331   return 1;
4332 }
4333 
4334 /* ================================================================== */
4335 /* This constraint checks if each arguments of the option instance is */
4336 /* between a minimum and a maximum (inclusive).                       */
4337 /* return 1 if yes and 0 if no.                                       */
4338 /* ================================================================== */
4339 int
ctxopt_range_constraint(int nb_args,char ** args,char * value,char * par)4340 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
4341 {
4342   long   min, max;
4343   char   c;
4344   char * ptr;
4345   int    n;
4346   long   v;
4347   int    min_only = 0;
4348   int    max_only = 0;
4349 
4350   if (nb_args != 2)
4351     fatal_internal("Range constraint, invalid number of parameters.");
4352 
4353   if (strcmp(args[0], ".") == 0)
4354     max_only = 1;
4355   else
4356     n = sscanf(args[0], "%ld%c", &min, &c);
4357 
4358   if (!max_only && n != 1)
4359     fatal_internal("Range constraint, min: invalid parameters.");
4360 
4361   if (strcmp(args[1], ".") == 0)
4362     min_only = 1;
4363   else
4364     n = sscanf(args[1], "%ld%c", &max, &c);
4365 
4366   if (!min_only && n != 1)
4367     fatal_internal("Range constraint, max: invalid parameters.");
4368 
4369   if (min_only && max_only)
4370     fatal_internal("Range constraint, invalid parameters.");
4371 
4372   errno = 0;
4373   v     = strtol(value, &ptr, 10);
4374   if (errno || ptr == value)
4375     return 0;
4376 
4377   if (min_only)
4378   {
4379     if (v < min)
4380     {
4381       fprintf(stderr,
4382               "The argument %ld of %s is not greater than or equal to %ld.", v,
4383               par, min);
4384       return 0;
4385     }
4386     else
4387       return 1;
4388   }
4389   else if (max_only)
4390   {
4391     if (v > max)
4392     {
4393       fprintf(stderr,
4394               "The argument %ld of %s is not less than or equal to %ld.", v,
4395               par, max);
4396       return 0;
4397     }
4398     else
4399       return 1;
4400   }
4401   else if (v < min || v > max)
4402   {
4403     fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
4404             par, min, max);
4405     return 0;
4406   }
4407 
4408   return 1; /* Check passed. */
4409 }
4410 
4411 /* =============================================================== */
4412 /* This function provides a way to set the behaviour of a context. */
4413 /* =============================================================== */
4414 void
ctxopt_add_global_settings(settings s,...)4415 ctxopt_add_global_settings(settings s, ...)
4416 {
4417   va_list(args);
4418   va_start(args, s);
4419 
4420   switch (s)
4421   {
4422     case error_functions:
4423     {
4424       typedef void fn(errors e, state_t * state);
4425 
4426       void (*function)(errors e, state_t * state);
4427 
4428       errors e;
4429       e                = va_arg(args, errors);
4430       function         = va_arg(args, fn *);
4431       err_functions[e] = function;
4432       break;
4433     }
4434 
4435     default:
4436       break;
4437   }
4438   va_end(args);
4439 }
4440 
4441 /* ================================================================ */
4442 /* This function provides a way to set the behaviour of an option.  */
4443 /* It can take a variable number of arguments according to its      */
4444 /* first  argument:                                                 */
4445 /* - parameter:                                                     */
4446 /*   o a string containing an option name and all its possible      */
4447 /*     parameters separates by spaces, tabs or commas (char *)      */
4448 /*     (e.g: "help -h -help").                                      */
4449 /* - actions:                                                       */
4450 /*   o a string containing an option name.                          */
4451 /*   o a pointer to a function which will be called at evaluation   */
4452 /*     time.                                                        */
4453 /* - constraints:                                                   */
4454 /*   o a string containing an option name.                          */
4455 /*   o a pointer to a function to check if an argument is valid.    */
4456 /*   o a strings containing the arguments to this function.         */
4457 /* ================================================================ */
4458 void
ctxopt_add_opt_settings(settings s,...)4459 ctxopt_add_opt_settings(settings s, ...)
4460 {
4461   opt_t * opt = NULL;
4462   void *  ptr = NULL;
4463 
4464   va_list(args);
4465   va_start(args, s);
4466 
4467   switch (s)
4468   {
4469     /* This part associates some command line parameters to an option. */
4470     /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4471     case parameters:
4472     {
4473       char * opt_name;
4474       char * params;
4475 
4476       /* The second argument must be a string containing: */
4477       /* - The name of an existing option.                */
4478       /* - a list of parameters with a leading dash (-).  */
4479       /* """""""""""""""""""""""""""""""""""""""""""""""" */
4480       ptr      = va_arg(args, char *);
4481       opt_name = ptr;
4482 
4483       if (opt_name != NULL)
4484       {
4485         if ((opt = locate_opt(opt_name)) != NULL)
4486         {
4487           ptr    = va_arg(args, char *);
4488           params = ptr;
4489 
4490           if (!opt_set_parms(opt_name, params))
4491             fatal_internal(
4492               "Duplicated parameters or bad settings for the option %s.",
4493               params);
4494         }
4495         else
4496           fatal_internal("Unknown option %s.", opt_name);
4497       }
4498       else
4499         fatal_internal(
4500           "ctxopt_opt_add_settings: parameters: not enough arguments.");
4501 
4502       /* Here opt is a known option. */
4503       /* """"""""""""""""""""""""""" */
4504       if (opt->params != NULL)
4505         fatal_internal("Parameters are already set for %s.", opt_name);
4506       else
4507       {
4508         size_t n;
4509         size_t l = strlen(params);
4510 
4511         opt->params = xstrdup(params);
4512         while ((n = strcspn(opt->params, " \t")) < l)
4513           opt->params[n] = '|';
4514       }
4515 
4516       break;
4517     }
4518 
4519     /* This part associates a callback function to an option.     */
4520     /* This function will be called when an instance of an option */
4521     /* is evaluated.                                              */
4522     /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4523     case actions:
4524     {
4525       void * data;
4526       void (*function)();
4527       int nb_data = 0;
4528 
4529       /* The second argument must be the name of an existing option. */
4530       /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4531       ptr = va_arg(args, char *);
4532 
4533       if ((opt = locate_opt(ptr)) != NULL)
4534       {
4535         typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4536                         void **);
4537 
4538         /* The third argument must be the callback function. */
4539         /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4540         function    = va_arg(args, fn *);
4541         opt->action = function;
4542 
4543         /* The fourth argument must be a pointer to an user's defined   */
4544         /* variable or structure that the previous function can manage. */
4545         /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4546         while ((data = va_arg(args, void *)) != NULL)
4547         {
4548           nb_data++;
4549           opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4550           opt->data[nb_data - 1] = data;
4551         }
4552         opt->nb_data = nb_data;
4553       }
4554       else
4555         fatal_internal("Unknown option %s.", ptr);
4556       break;
4557     }
4558 
4559     /* This part associates a list of functions to control some */
4560     /* characteristics of the arguments of an option.           */
4561     /* Each function will be called in order and must return 1  */
4562     /* to validate the arguments.                               */
4563     /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4564     case constraints:
4565     {
4566       char *         value;
4567       constraint_t * cstr;
4568       int (*function)();
4569 
4570       /* The second argument must be a string. */
4571       /* """"""""""""""""""""""""""""""""""""" */
4572       ptr = va_arg(args, char *);
4573 
4574       if ((opt = locate_opt(ptr)) != NULL)
4575       {
4576         typedef int fn(int, char **, char *);
4577 
4578         /* The third argument must be a function. */
4579         /* """""""""""""""""""""""""""""""""""""" */
4580         function = va_arg(args, fn *);
4581 
4582         cstr             = xmalloc(sizeof(constraint_t));
4583         cstr->constraint = function;
4584 
4585         /* The fourth argument must be a string containing the argument of */
4586         /* The previous function separated by spaces or tabs.              */
4587         /* Theses arguments will be passed to the previous function        */
4588         /* max: 32 argument!                                               */
4589         /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4590         value = xstrdup(va_arg(args, char *));
4591 
4592         cstr->to_free = value;
4593         cstr->args    = xcalloc(sizeof(char *), 32);
4594         cstr->nb_args = str2argv(value, cstr->args, 32);
4595         ll_append(opt->constraints_list, cstr);
4596       }
4597       else
4598         fatal_internal("Unknown option %s.", ptr);
4599       break;
4600     }
4601 
4602     /* This part allows to indicate that an option must be evaluated */
4603     /* after a list of other options.                                */
4604     /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4605     case after:
4606     {
4607       char * str;
4608 
4609       /* The second argument must be a string. */
4610       /* """"""""""""""""""""""""""""""""""""" */
4611       ptr = va_arg(args, char *);
4612 
4613       if ((opt = locate_opt(ptr)) != NULL)
4614       {
4615         char *  end_str;
4616         char *  opt_name;
4617         opt_t * opt_before;
4618 
4619         ptr = va_arg(args, char *);
4620 
4621         str = xstrdup(ptr);
4622         ltrim(str, " \t");
4623         rtrim(str, " \t", 0);
4624 
4625         /* Feed the list of options to be evaluated after the given option. */
4626         /* This list will contain pointers to options.                      */
4627         /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4628         opt_name = xstrtok_r(str, " \t,", &end_str);
4629         if (opt_name != NULL)
4630         {
4631           if ((opt_before = locate_opt(opt_name)) != NULL)
4632           {
4633             ll_append(opt->eval_before_list, opt_before);
4634             while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4635             {
4636               if ((opt_before = locate_opt(opt_name)) != NULL)
4637                 ll_append(opt->eval_before_list, opt_before);
4638               else
4639                 fatal_internal("Unknown option %s.", opt_name);
4640             }
4641           }
4642           else
4643             fatal_internal("Unknown option %s.", opt_name);
4644         }
4645         else
4646           fatal_internal("Not enough options to be evaluated after %s.",
4647                          opt->name);
4648 
4649         free(str);
4650       }
4651       else
4652         fatal_internal("Unknown option %s.", ptr);
4653 
4654       break;
4655     }
4656 
4657     /* This part allows to indicate that an option must be evaluated */
4658     /* before a list of other options.                               */
4659     /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4660     case before:
4661     {
4662       char * str;
4663 
4664       /* The second argument must be a string. */
4665       /* """"""""""""""""""""""""""""""""""""" */
4666       ptr = va_arg(args, char *);
4667 
4668       if ((opt = locate_opt(ptr)) != NULL)
4669       {
4670         char *  end_str;
4671         char *  opt_name;
4672         opt_t * opt_before;
4673 
4674         ptr = va_arg(args, char *);
4675 
4676         str = xstrdup(ptr);
4677         ltrim(str, " \t");
4678         rtrim(str, " \t", 0);
4679 
4680         /* Feed the list of options to be evaluated before the given option. */
4681         /* This list will contain pointers to options.                       */
4682         /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4683         opt_name = xstrtok_r(str, " \t,", &end_str);
4684         if (opt_name != NULL)
4685         {
4686           if ((opt_before = locate_opt(opt_name)) != NULL)
4687           {
4688             ll_append(opt_before->eval_before_list, opt);
4689             while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4690             {
4691               if ((opt_before = locate_opt(opt_name)) != NULL)
4692                 ll_append(opt_before->eval_before_list, opt);
4693               else
4694                 fatal_internal("Unknown option %s.", opt_name);
4695             }
4696           }
4697           else
4698             fatal_internal("Unknown option %s.", opt_name);
4699         }
4700         else
4701           fatal_internal("Not enough options to be evaluated before %s.",
4702                          opt->name);
4703 
4704         free(str);
4705       }
4706       else
4707         fatal_internal("Unknown option %s.", ptr);
4708 
4709       break;
4710     }
4711 
4712     default:
4713       break;
4714   }
4715   va_end(args);
4716 }
4717 
4718 /* =============================================================== */
4719 /* This function provides a way to set the behaviour of a context. */
4720 /* =============================================================== */
4721 void
ctxopt_add_ctx_settings(settings s,...)4722 ctxopt_add_ctx_settings(settings s, ...)
4723 {
4724   ctx_t * ctx;
4725 
4726   va_list(args);
4727   va_start(args, s);
4728 
4729   switch (s)
4730   {
4731     /* Add a set of mutually incompatible options in a context. */
4732     /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4733     case incompatibilities:
4734     {
4735       void * ptr;
4736       ll_t * list;
4737       size_t n;
4738       char * str;
4739 
4740       ptr = va_arg(args, char *);
4741       if ((ctx = locate_ctx(ptr)) != NULL)
4742       {
4743         ptr  = va_arg(args, char *);
4744         list = ctx->incomp_list;
4745 
4746         str = xstrdup(ptr);
4747         ltrim(str, " \t");
4748         rtrim(str, " \t", 0);
4749 
4750         n = strcspn(str, " \t");
4751         if (n > 0 && n < strlen(str))
4752           ll_append(list, str);
4753         else
4754           fatal_internal(
4755             "Not enough incompatible options in the string: \"%s\".", str);
4756       }
4757       else
4758         fatal_internal("Unknown context %s.", ptr);
4759       break;
4760     }
4761 
4762     case requirements:
4763     {
4764       void * ptr;
4765       ll_t * list;
4766       size_t n;
4767       char * str;
4768 
4769       ptr = va_arg(args, char *);
4770       if ((ctx = locate_ctx(ptr)) != NULL)
4771       {
4772         ptr  = va_arg(args, char *);
4773         list = ctx->req_list;
4774 
4775         str = xstrdup(ptr);
4776         ltrim(str, " \t");
4777         rtrim(str, " \t", 0);
4778 
4779         n = strcspn(str, " \t");
4780         if (n > 0 && n < strlen(str))
4781           ll_append(list, str);
4782         else
4783           fatal_internal("Not enough required options in the string: \"%s\".",
4784                          str);
4785       }
4786       else
4787         fatal_internal("Unknown context %s.", ptr);
4788       break;
4789     }
4790 
4791     /* Add functions which will be called when */
4792     /* entering and exiting a context.         */
4793     /* """"""""""""""""""""""""""""""""""""""" */
4794     case actions:
4795     {
4796       void * ptr;
4797       void * data;
4798       int (*function)();
4799       int nb_data = 0;
4800 
4801       ptr = va_arg(args, char *);
4802       if ((ctx = locate_ctx(ptr)) != NULL)
4803       {
4804         typedef int fn(char *, direction, char *, int, void **);
4805 
4806         function    = va_arg(args, fn *);
4807         ctx->action = function;
4808 
4809         while ((data = va_arg(args, void *)) != NULL)
4810         {
4811           nb_data++;
4812           ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4813           ctx->data[nb_data - 1] = data;
4814         }
4815         ctx->nb_data = nb_data;
4816       }
4817       else
4818         fatal_internal("Unknown context %s.", ptr);
4819       break;
4820     }
4821 
4822     default:
4823       break;
4824   }
4825   va_end(args);
4826 }
4827