Fix a potential dereference of NULL pointer
[ctxopt.git] / ctxopt.c
blobcc25f81bd20be1c359a9b737ddab3f5669c79d29
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 /* ################################################################### */
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"
19 /* ************************ */
20 /* Static global variables. */
21 /* ************************ */
23 static void * contexts_bst;
24 static void * options_bst;
26 state_t * cur_state;
28 /* Prototypes */
30 /* ************************** */
31 /* Fatal messages prototypes. */
32 /* ************************** */
34 static void (**err_functions)(errors e, state_t * state);
36 static void
37 fatal_internal(const char * format, ...);
39 static void
40 fatal(errors e, char * errmsg);
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 */
48 /* ************************************ */
49 /* Memory management static prototypes. */
50 /* ************************************ */
52 static void *
53 xmalloc(size_t size);
55 static void *
56 xcalloc(size_t num, size_t size);
58 static void *
59 xrealloc(void * ptr, size_t size);
61 static char *
62 xstrdup(const char * p);
64 static char *
65 xstrndup(const char * str, size_t len);
67 /* ********************** */
68 /* BST static prototypes. */
69 /* ********************** */
71 typedef struct bst_s bst_t;
73 typedef enum
75 preorder,
76 postorder,
77 endorder,
78 leaf
79 } walk_order_e;
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
87 static void
88 bst_destroy(void * vrootp, void (*clean)(void *));
90 static void *
91 bst_find(const void * vkey, void * const * vrootp,
92 int (*compar)(const void *, const void *));
94 static void *
95 bst_search(void * vkey, void ** vrootp,
96 int (*compar)(const void *, const void *));
98 static void
99 bst_walk_recurse(const bst_t * root,
100 void (*action)(const void *, walk_order_e, int), int level);
102 static void
103 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int));
105 /* ****************************** */
106 /* Linked list static prototypes. */
107 /* ****************************** */
109 typedef struct ll_node_s ll_node_t;
110 typedef struct ll_s ll_t;
112 static void
113 ll_append(ll_t * const list, void * const data);
115 static void
116 ll_prepend(ll_t * const list, void * const data);
118 static void
119 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data);
121 static void
122 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data);
124 static int
125 ll_delete(ll_t * const list, ll_node_t * node);
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
132 static void
133 ll_init(ll_t * list);
135 static ll_node_t *
136 ll_new_node(void);
138 static ll_t *
139 ll_new(void);
141 static void
142 ll_free(ll_t * const list, void (*)(void *));
144 static void
145 ll_destroy(ll_t * const list, void (*)(void *));
147 static int
148 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array);
150 /* ************************** */
151 /* Various static prototypes. */
152 /* ************************** */
154 static void
155 ltrim(char * str, const char * trim_str);
157 static void
158 rtrim(char * str, const char * trim_str, size_t min);
160 static int
161 strchrcount(char * str, char c);
163 static int
164 strpref(char * s1, char * s2);
166 static int
167 stricmp(const char * s1, const char * s2);
169 static char *
170 xstrtok_r(char * str, const char * delim, char ** end);
172 static int
173 eval_yes(char * value, int * invalid);
175 static char *
176 get_word(char * str, char * buf, size_t len);
178 /* ************************* */
179 /* ctxopt static prototypes. */
180 /* ************************* */
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;
192 static char *
193 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
195 static int
196 ctx_compare(const void * c1, const void * c2);
198 static void
199 ctx_free(void * o);
201 static void
202 ctx_inst_free(void * ci);
204 static void
205 opt_inst_free(void * oi);
207 static int
208 seen_opt_compare(const void * so1, const void * so2);
210 static void
211 incomp_bst_free(void * b);
213 static void
214 seen_opt_free(void * seen_opt);
216 static int
217 opt_compare(const void * o1, const void * o2);
219 static void
220 opt_free(void * o);
222 static int
223 par_compare(const void * a1, const void * a2);
225 static void
226 par_free(void * p);
228 static void
229 constraint_free(void * cstr);
231 static ctx_t *
232 locate_ctx(char * name);
234 static opt_t *
235 locate_opt(char * name);
237 static par_t *
238 locate_par(char * name, ctx_t * ctx);
240 static void
241 print_before_constraints(ll_t * list);
243 static void
244 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
245 int * has_rule, int * has_generic_arg, int * has_ctx_change,
246 int * has_early_eval);
247 static void
248 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
249 int has_optional, int has_ellipsis, int has_rule);
250 static void
251 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
253 static void
254 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
256 static void
257 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
259 static void
260 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
262 static void
263 bst_match_par_cb(const void * node, walk_order_e kind, int level);
265 static void
266 match_prefix_cb(const void * node, walk_order_e kind, int level);
268 static int
269 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
271 static int
272 opt_parse(char * s, opt_t ** opt);
274 static int
275 init_opts(char * spec, ctx_t * ctx);
277 static int
278 ctxopt_build_cmdline_list(int nb_words, char ** words);
280 static int
281 opt_set_parms(char * opt_name, char * par_str);
283 static ctx_inst_t *
284 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
286 static void
287 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
289 /* ****************************** */
290 /* Fatal messages implementation. */
291 /* ****************************** */
293 /* ================================================================== */
294 /* Fatal error function used when a fatal condition is encountered. */
295 /* This function is reserved for the ctxopt internal usage. */
296 /* */
297 /* format : printf like format */
298 /* ... : remaining arguments interpreted using the format argument */
299 /* ================================================================== */
300 static void
301 fatal_internal(const char * format, ...)
303 va_list args;
305 fprintf(stderr, "CTXOPT: ");
307 va_start(args, format);
308 vfprintf(stderr, format, args);
309 fprintf(stderr, "\n");
310 va_end(args);
312 exit(EXIT_FAILURE);
315 /* ====================================================================== */
316 /* Generic fatal error function. This one uses the global status ctxopt */
317 /* stored in the cur_state structure and can call custom error functions. */
318 /* registered by the users for a given error identifier if any. */
319 /* */
320 /* e : Error identifier responsible of the fatal error */
321 /* errmsg : Users's provided string specific to the error e */
322 /* Note that errmsg is not used in all cases */
323 /* */
324 /* CTXOPTMISPAR Missing parameter */
325 /* CTXOPTREQPAR Option: all parameters in a required group are */
326 /* missing. */
327 /* CTXOPTMISARG Missing argument */
328 /* CTXOPTUXPARG Unexpected argument */
329 /* CTXOPTDUPOPT Duplicated option */
330 /* CTXOPTUNKPAR Unknown parameter */
331 /* CTXOPTINCOPT Incompatible option */
332 /* CTXOPTCTEOPT Option: bad number of occurrences */
333 /* CTXOPTCTLOPT Option: not enough occurrences */
334 /* CTXOPTCTGOPT Option: too many occurrence of */
335 /* CTXOPTCTEARG Arguments: bad number of occurrences */
336 /* CTXOPTCTLARG Arguments: not enough occurrences */
337 /* CTXOPTCTGARG Arguments: too many occurrences */
338 /* ====================================================================== */
339 static void
340 fatal(errors e, char * errmsg)
342 if (err_functions[e] != NULL)
343 err_functions[e](e, cur_state);
344 else
346 switch (e)
348 case CTXOPTNOERR:
349 break;
351 case CTXOPTMISPAR:
352 if (cur_state->ctx_par_name != NULL)
353 fprintf(stderr,
354 "the mandatory parameter(s) %s are missing in the context "
355 "introduced by %s.\n",
356 errmsg, cur_state->ctx_par_name);
357 else
358 fprintf(stderr,
359 "The mandatory parameter(s) %s are missing "
360 "in the main context.\n",
361 errmsg);
363 free(errmsg);
364 break;
366 case CTXOPTREQPAR:
367 fprintf(stderr, errmsg, cur_state->req_opt_par_needed,
368 cur_state->req_opt_par);
369 break;
371 case CTXOPTUNXARG:
372 if (cur_state->cur_opt_par_name != NULL)
373 fprintf(stderr,
374 "The parameter %s takes no arguments "
375 "or has too many arguments.\n",
376 cur_state->cur_opt_par_name);
377 break;
379 case CTXOPTMISARG:
380 if (cur_state->pre_opt_par_name != NULL)
381 fprintf(stderr, "%s requires argument(s).\n",
382 cur_state->pre_opt_par_name);
383 else
384 fprintf(stderr, "%s requires argument(s).\n",
385 cur_state->cur_opt_par_name);
386 break;
388 case CTXOPTDUPOPT:
389 if (cur_state->pre_opt_par_name != NULL)
390 fprintf(stderr,
391 "The parameter %s can only appear once in the context "
392 "introduced by %s.\n",
393 cur_state->cur_opt_params, cur_state->ctx_par_name);
394 else
395 fprintf(stderr,
396 "The parameter %s can only appear once "
397 "in the main context.\n",
398 cur_state->cur_opt_params);
399 break;
401 case CTXOPTUNKPAR:
402 fprintf(stderr, "Unknown parameter %s.\n%s",
403 cur_state->cur_opt_par_name, errmsg);
404 break;
406 case CTXOPTINCOPT:
407 fprintf(stderr, "The parameter %s is incompatible with %s.\n",
408 cur_state->cur_opt_par_name, errmsg);
409 break;
411 case CTXOPTCTEOPT:
412 if (cur_state->ctx_par_name)
413 fprintf(stderr,
414 "The parameter %s must appear exactly %d times "
415 "in the context introduced by %s.\n",
416 cur_state->cur_opt_params, cur_state->opts_count,
417 cur_state->ctx_par_name);
418 else
419 fprintf(stderr,
420 "The parameter %s must appear exactly %d times "
421 "in the main context.\n",
422 cur_state->cur_opt_params, cur_state->opts_count);
423 break;
425 case CTXOPTCTLOPT:
426 if (cur_state->ctx_par_name)
427 fprintf(stderr,
428 "The parameter %s must appear less than %d times "
429 "in the context introduced by %s.\n",
430 cur_state->cur_opt_params, cur_state->opts_count,
431 cur_state->ctx_par_name);
432 else
433 fprintf(stderr,
434 "The parameter %s must appear less than %d times "
435 "in the main context.\n",
436 cur_state->cur_opt_params, cur_state->opts_count);
437 break;
439 case CTXOPTCTGOPT:
440 if (cur_state->ctx_par_name)
441 fprintf(stderr,
442 "The parameter %s must appear more than %d times "
443 "in the context introduced by %s.\n",
444 cur_state->cur_opt_params, cur_state->opts_count,
445 cur_state->ctx_par_name);
446 else
447 fprintf(stderr,
448 "The parameter %s must appear more than %d times "
449 "in the main context.\n",
450 cur_state->cur_opt_params, cur_state->opts_count);
451 break;
453 case CTXOPTCTEARG:
454 fprintf(stderr, "The parameter %s must have exactly %d arguments.\n",
455 cur_state->cur_opt_par_name, cur_state->opt_args_count);
456 break;
458 case CTXOPTCTLARG:
459 fprintf(stderr, "The parameter %s must have less than %d arguments.\n",
460 cur_state->cur_opt_par_name, cur_state->opt_args_count);
461 break;
463 case CTXOPTCTGARG:
464 fprintf(stderr, "The parameter %s must have more than %d arguments.\n",
465 cur_state->cur_opt_par_name, cur_state->opt_args_count);
466 break;
468 case CTXOPTERRSIZ:
469 break;
473 /* CTXOPTUNKPAR should display the full usage to help the user follow */
474 /* the chaining of contexts when several possible contexts have been */
475 /* identified. Otherwise, errmsg is the empty string and the display of */
476 /* the current usage is enough. */
477 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
478 if (e == CTXOPTUNKPAR && *errmsg != '\0')
479 ctxopt_disp_usage(continue_after);
480 else
481 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
483 exit(e); /* Exit with the error id e as return code. */
486 /* ********************************* */
487 /* Memory management implementation. */
488 /* ********************************* */
490 /* ================== */
491 /* Customized malloc. */
492 /* ================== */
493 static void *
494 xmalloc(size_t size)
496 void * allocated;
497 size_t real_size;
499 real_size = (size > 0) ? size : 1;
500 allocated = malloc(real_size);
501 if (allocated == NULL)
502 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
503 (unsigned long int)size);
505 return allocated;
508 /* ================== */
509 /* Customized calloc. */
510 /* ================== */
511 static void *
512 xcalloc(size_t n, size_t size)
514 void * allocated;
516 n = (n > 0) ? n : 1;
517 size = (size > 0) ? size : 1;
518 allocated = calloc(n, size);
519 if (allocated == NULL)
520 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
521 (unsigned long int)size);
523 return allocated;
526 /* =================== */
527 /* Customized realloc. */
528 /* =================== */
529 static void *
530 xrealloc(void * p, size_t size)
532 void * allocated;
534 allocated = realloc(p, size);
535 if (allocated == NULL && size > 0)
536 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
537 (unsigned long int)size);
539 return allocated;
542 /* ==================================== */
543 /* strdup implementation using xmalloc. */
544 /* ==================================== */
545 static char *
546 xstrdup(const char * p)
548 char * allocated;
550 allocated = xmalloc(strlen(p) + 1);
551 strcpy(allocated, p);
553 return allocated;
556 /* =================================================== */
557 /* strndup implementation using xmalloc. */
558 /* This version guarantees that there is a final '\0'. */
559 /* =================================================== */
560 static char *
561 xstrndup(const char * str, size_t len)
563 char * p;
565 p = memchr(str, '\0', len);
567 if (p)
568 len = p - str;
570 p = xmalloc(len + 1);
571 memcpy(p, str, len);
572 p[len] = '\0';
574 return p;
577 /* *************************** */
578 /* Linked list implementation. */
579 /* *************************** */
581 /* Linked list node structure. */
582 /* """"""""""""""""""""""""""" */
583 struct ll_node_s
585 void * data;
586 struct ll_node_s * next;
587 struct ll_node_s * prev;
590 /* Linked List structure. */
591 /* """""""""""""""""""""" */
592 struct ll_s
594 ll_node_t * head;
595 ll_node_t * tail;
596 long len;
599 /* ========================= */
600 /* Create a new linked list. */
601 /* ========================= */
602 static ll_t *
603 ll_new(void)
605 ll_t * ret = xmalloc(sizeof(ll_t));
606 ll_init(ret);
608 return ret;
611 /* =============================================== */
612 /* Free all the elements of a list (make it empty) */
613 /* NULL or a custom function may be used to free */
614 /* the sub components of the elements. */
615 /* =============================================== */
616 static void
617 ll_free(ll_t * const list, void (*clean)(void *))
619 if (list)
620 while (list->head)
622 /* Apply a custom cleaner if not NULL. */
623 /* """"""""""""""""""""""""""""""""""" */
624 if (clean)
625 clean(list->head->data);
627 ll_delete(list, list->head);
631 /* ==================================== */
632 /* Destroy a list and all its elements. */
633 /* ==================================== */
634 static void
635 ll_destroy(ll_t * list, void (*clean)(void *))
637 if (list)
639 ll_free(list, clean);
640 free(list);
644 /* ========================= */
645 /* Initialize a linked list. */
646 /* ========================= */
647 static void
648 ll_init(ll_t * list)
650 list->head = NULL;
651 list->tail = NULL;
652 list->len = 0;
655 /* ===================================================== */
656 /* Allocate the space for a new node in the linked list. */
657 /* ===================================================== */
658 static ll_node_t *
659 ll_new_node(void)
661 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
663 return ret;
666 /* ==================================================================== */
667 /* Append a new node filled with its data at the end of the linked list */
668 /* The user is responsible for the memory management of the data. */
669 /* ==================================================================== */
670 static void
671 ll_append(ll_t * const list, void * const data)
673 ll_node_t * node;
675 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
676 | uses xmalloc which does not return if there *
677 | is an allocation error. */
679 node->data = data;
680 node->next = NULL;
682 node->prev = list->tail;
683 if (list->tail)
684 list->tail->next = node;
685 else
686 list->head = node;
688 list->tail = node;
690 ++list->len;
693 /* ================================================================== */
694 /* Put a new node filled with its data at the beginning of the linked */
695 /* list. */
696 /* The user is responsible for the memory management of the data. */
697 /* ================================================================== */
698 static void
699 ll_prepend(ll_t * const list, void * const data)
701 ll_node_t * node;
703 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
704 | uses xmalloc which does not return if there *
705 | is an allocation error. */
707 node->data = data;
708 node->prev = NULL;
710 node->next = list->head;
711 if (list->head)
712 list->head->prev = node;
713 else
714 list->tail = node;
716 list->head = node;
718 ++list->len;
721 /* ======================================================== */
722 /* Insert a new node before the specified node in the list. */
723 /* ======================================================== */
724 static void
725 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
727 ll_node_t * new_node;
729 if (node->prev == NULL)
730 ll_prepend(list, data);
731 else
733 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
734 | uses xmalloc which does not return if there *
735 | is an allocation error. */
737 new_node->data = data;
738 new_node->next = node;
739 new_node->prev = node->prev;
740 node->prev->next = new_node;
741 node->prev = new_node;
743 ++list->len;
747 /* ======================================================= */
748 /* Insert a new node after the specified node in the list. */
749 /* ======================================================= */
750 static void
751 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
753 ll_node_t * new_node;
755 if (node->next == NULL)
756 ll_append(list, data);
757 else
759 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
760 | uses xmalloc which does not return if there *
761 | is an allocation error. */
763 new_node->data = data;
764 new_node->prev = node;
765 new_node->next = node->next;
766 node->next->prev = new_node;
767 node->next = new_node;
769 ++list->len;
773 /* ================================================================= */
774 /* Remove a node from a linked list */
775 /* The memory taken by the deleted node must be freed by the caller. */
776 /* ================================================================= */
777 static int
778 ll_delete(ll_t * const list, ll_node_t * node)
780 if (list->head == list->tail)
782 if (list->head != NULL)
783 list->head = list->tail = NULL;
784 else
785 return 0;
787 else if (node->prev == NULL)
789 list->head = node->next;
790 list->head->prev = NULL;
792 else if (node->next == NULL)
794 list->tail = node->prev;
795 list->tail->next = NULL;
797 else
799 node->next->prev = node->prev;
800 node->prev->next = node->next;
803 --list->len;
805 free(node);
807 return 1;
810 #if 0 /* Unused yet */
811 /* ======================================================================== */
812 /* Find a node in the list containing data. Return the node pointer or NULL */
813 /* if not found. */
814 /* A comparison function must be provided to compare a and b (strcmp like). */
815 /* ======================================================================== */
816 static ll_node_t *
817 ll_find(ll_t * const list, void * const data,
818 int (*cmpfunc)(const void * a, const void * b))
820 ll_node_t * node;
822 if (NULL == (node = list->head))
823 return NULL;
827 if (0 == cmpfunc(node->data, data))
828 return node;
829 } while (NULL != (node = node->next));
831 return NULL;
833 #endif
835 /* ==================================================================== */
836 /* Allocate and fill an array of strings from a list. */
837 /* WARNINGS: */
838 /* 1) The list node must contain strings (char *) */
839 /* 2) The strings in the resulting array MUST NOT be freed as the are */
840 /* NOT copied from the strings of the list. */
841 /* */
842 /* IN list : The list from which the array is generated */
843 /* IN start_node : The node of the list which will be the first node to */
844 /* consider to create the array */
845 /* OUT: count : The number of elements of the resulting array. */
846 /* OUT: array : The resulting array or NULL if the list is empty. */
847 /* RC : : The number of elements of the resulting array. */
848 /* ==================================================================== */
849 static int
850 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
852 int n = 0;
853 ll_node_t * node;
855 *count = 0;
857 node = start_node;
859 if (list == NULL || node == NULL)
861 *array = NULL;
863 return 0;
866 *array = xmalloc((list->len + 1) * sizeof(char *));
867 while (node != NULL)
869 (*array)[n++] = (char *)(node->data);
870 (*count)++;
872 node = node->next;
875 (*array)[*count] = NULL;
877 return *count;
880 /* ******************************************************************* */
881 /* BST (search.h compatible) implementation. */
882 /* */
883 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
884 /* the AT&T man page says. */
885 /* */
886 /* Written by reading the System V Interface Definition, not the code. */
887 /* */
888 /* Totally public domain. */
889 /* ******************************************************************* */
891 struct bst_s
893 void * key;
894 struct bst_s * llink;
895 struct bst_s * rlink;
898 #if 0 /* Unused yet */
899 /* =========================== */
900 /* Delete node with given key. */
901 /* =========================== */
902 static void *
903 bst_delete(const void * vkey, void ** vrootp,
904 int (*compar)(const void *, const void *))
906 bst_t ** rootp = (bst_t **)vrootp;
907 bst_t * p, *q, *r;
908 int cmp;
910 if (rootp == NULL || (p = *rootp) == NULL)
911 return NULL;
913 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
915 p = *rootp;
916 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
917 : &(*rootp)->rlink; /* follow rlink branch */
918 if (*rootp == NULL)
919 return NULL; /* key not found */
921 r = (*rootp)->rlink; /* D1: */
922 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
923 q = r;
924 else if (r != NULL)
925 { /* Right link is NULL? */
926 if (r->llink == NULL)
927 { /* D2: Find successor */
928 r->llink = q;
929 q = r;
931 else
932 { /* D3: Find NULL link */
933 for (q = r->llink; q->llink != NULL; q = r->llink)
934 r = q;
935 r->llink = q->rlink;
936 q->llink = (*rootp)->llink;
937 q->rlink = (*rootp)->rlink;
940 if (p != *rootp)
941 free(*rootp); /* D4: Free node */
942 *rootp = q; /* link parent to new node */
943 return p;
945 #endif
947 /* ===================================================================== */
948 /* Destroy a tree. */
949 /* The clean function pointer can be NULL, in this case the node content */
950 /* is not freed. */
951 /* ===================================================================== */
952 static void
953 bst_destroy(void * vrootp, void (*clean)(void *))
955 bst_t * root = (bst_t *)vrootp;
957 if (root == NULL)
958 return;
960 bst_destroy(root->llink, clean);
961 bst_destroy(root->rlink, clean);
963 if (clean)
964 clean((void *)root->key);
966 free(root);
969 /* ========================= */
970 /* Find a node, or return 0. */
971 /* ========================= */
972 static void *
973 bst_find(const void * vkey, void * const * vrootp,
974 int (*compar)(const void *, const void *))
976 bst_t * const * rootp = (bst_t * const *)vrootp;
978 if (rootp == NULL)
979 return NULL;
981 while (*rootp != NULL)
982 { /* T1: */
983 int r;
985 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
986 return *rootp; /* key found */
987 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
988 : &(*rootp)->rlink; /* T4: follow right branch */
990 return NULL;
993 /* ======================================= */
994 /* Find or inserts datum into search tree. */
995 /* ======================================= */
996 static void *
997 bst_search(void * vkey, void ** vrootp,
998 int (*compar)(const void *, const void *))
1000 bst_t * q;
1001 bst_t ** rootp = (bst_t **)vrootp;
1003 if (rootp == NULL)
1004 return NULL;
1006 while (*rootp != NULL)
1007 { /* Knuth's T1: */
1008 int r;
1010 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
1011 return *rootp; /* we found it! */
1013 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
1014 : &(*rootp)->rlink; /* T4: follow right branch */
1017 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
1018 if (q != 0)
1019 { /* make new node */
1020 *rootp = q; /* link new node to old */
1021 q->key = vkey; /* initialize new node */
1022 q->llink = q->rlink = NULL;
1024 return q;
1027 /* ========================= */
1028 /* Walk the nodes of a tree. */
1029 /* ========================= */
1030 static void
1031 bst_walk_recurse(const bst_t * root,
1032 void (*action)(const void *, walk_order_e, int), int level)
1034 if (root->llink == NULL && root->rlink == NULL)
1035 (*action)(root, leaf, level);
1036 else
1038 (*action)(root, preorder, level);
1039 if (root->llink != NULL)
1040 bst_walk_recurse(root->llink, action, level + 1);
1041 (*action)(root, postorder, level);
1042 if (root->rlink != NULL)
1043 bst_walk_recurse(root->rlink, action, level + 1);
1044 (*action)(root, endorder, level);
1048 static void
1049 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
1051 if (vroot != NULL && action != NULL)
1052 bst_walk_recurse(vroot, action, 0);
1055 /* ************************ */
1056 /* Various implementations. */
1057 /* ************************ */
1059 /* ======================== */
1060 /* Trim leading characters. */
1061 /* ======================== */
1062 static void
1063 ltrim(char * str, const char * trim_str)
1065 size_t len = strlen(str);
1066 size_t begin = strspn(str, trim_str);
1067 size_t i;
1069 if (begin > 0)
1070 for (i = begin; i <= len; ++i)
1071 str[i - begin] = str[i];
1074 /* ================================================= */
1075 /* Trim trailing characters. */
1076 /* The resulting string will have at least min bytes */
1077 /* even if trailing spaces remain. */
1078 /* ================================================= */
1079 static void
1080 rtrim(char * str, const char * trim_str, size_t min)
1082 size_t len = strlen(str);
1083 while (len > min && strchr(trim_str, str[len - 1]))
1084 str[--len] = '\0';
1087 /* ================================================== */
1088 /* Count the number of occurrences of the character c */
1089 /* in the string str. */
1090 /* The str pointer is assumed to be not NULL. */
1091 /* ================================================== */
1092 static int
1093 strchrcount(char * str, char c)
1095 int count = 0;
1097 while (*str)
1098 if (*str++ == c)
1099 count++;
1101 return count;
1104 /* =============================================== */
1105 /* Is the string str2 a prefix of the string str1? */
1106 /* =============================================== */
1107 static int
1108 strpref(char * str1, char * str2)
1110 while (*str1 != '\0' && *str1 == *str2)
1112 str1++;
1113 str2++;
1116 return *str2 == '\0';
1119 /* ========================== */
1120 /* Like strcmp ignoring case. */
1121 /* ========================== */
1122 static int
1123 stricmp(const char * s1, const char * s2)
1125 while (tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1127 if (*s1 == '\0')
1128 return 0;
1130 s1++;
1131 s2++;
1134 return (int)tolower((unsigned char)*s1) - (int)tolower((unsigned char)*s2);
1137 /* ======================================================================== */
1138 /* Strings concatenation with dynamic memory allocation. */
1139 /* IN : a variable number of char * arguments with NULL terminating */
1140 /* the sequence. */
1141 /* The first one must have been dynamically allocated and is mandatory */
1142 /* */
1143 /* Returns a new allocated string containing the concatenation of all */
1144 /* the arguments. It is the caller's responsibility to free the resulting */
1145 /* string. */
1146 /* ======================================================================== */
1147 static char *
1148 strappend(char * str, ...)
1150 size_t l;
1151 va_list args;
1152 char * s;
1154 l = 1 + strlen(str);
1155 va_start(args, str);
1157 s = va_arg(args, char *);
1159 while (s)
1161 l += strlen(s);
1162 s = va_arg(args, char *);
1165 va_end(args);
1167 str = xrealloc(str, l);
1169 va_start(args, str);
1170 s = va_arg(args, char *);
1172 while (s)
1174 strcat(str, s);
1175 s = va_arg(args, char *);
1177 va_end(args);
1179 return str;
1182 /* ====================================================================== */
1183 /* Public domain strtok_r() by Charlie Gordon. */
1184 /* from comp.lang.c 9/14/2007 */
1185 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1186 /* */
1187 /* (Declaration that it's public domain): */
1188 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1189 /* */
1190 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1191 /* *end == NULL */
1192 /* ====================================================================== */
1193 static char *
1194 xstrtok_r(char * str, const char * delim, char ** end)
1196 char * ret;
1198 if (str == NULL)
1199 str = *end;
1201 if (str == NULL)
1202 return NULL;
1204 str += strspn(str, delim);
1206 if (*str == '\0')
1207 return NULL;
1209 ret = str;
1211 str += strcspn(str, delim);
1213 if (*str)
1214 *str++ = '\0';
1216 *end = str;
1218 return ret;
1221 /* ===================================================================== */
1222 /* Put the first word of str, truncated to len characters, in buf. */
1223 /* Return a pointer in str pointing just after the word. */
1224 /* buf must have been pre-allocated to accept at least len+1 characters. */
1225 /* Note that buf can contains a sting full of spaces is str was not */
1226 /* trimmed before the call. */
1227 /* ===================================================================== */
1228 char *
1229 get_word(char * str, char * buf, size_t len)
1231 char * s = str;
1233 /* Skip spaces. */
1234 /* """""""""""" */
1235 while (*s && isspace(*s))
1236 s++;
1238 /* Set the new string start. */
1239 /* """"""""""""""""""""""""" */
1240 str = s;
1242 /* Get the word. */
1243 /*"""""""""""""" */
1244 while (*s && !isspace(*s) && s - str < len)
1245 s++;
1247 strncpy(buf, str, s - str);
1248 buf[s - str] = 0;
1250 return s;
1253 /* ==================================================================== */
1254 /* Return 1 is value is "1" or "yes" (ignoring case). */
1255 /* Return 0 is value is "0" or "no" (ignoring case). */
1256 /* If value has another value, then set invalid to 1 and also return 0 */
1257 /* invalid is set to 0i in all the other cases. */
1258 /* ==================================================================== */
1259 static int
1260 eval_yes(char * value, int * invalid)
1262 *invalid = 0;
1264 if (strcmp(value, "1") == 0 || stricmp(value, "yes") == 0)
1265 return 1;
1266 else if (strcmp(value, "0") != 0 && stricmp(value, "no") != 0)
1267 *invalid = 1;
1269 return 0;
1272 /* =========================================================== */
1273 /* Fill an array of strings from the words composing a string. */
1274 /* */
1275 /* str: initial string which will be altered. */
1276 /* args: array of pointers to the start of the words in str. */
1277 /* max: maximum number of words used before giving up. */
1278 /* return: the number of words (<=max). */
1279 /* =========================================================== */
1280 static int
1281 str2argv(char * str, char ** args, int max)
1283 int nb_args = 0;
1285 while (*str)
1287 if (nb_args >= max)
1288 return nb_args;
1290 while (*str == ' ' || *str == '\t')
1291 *(str++) = '\0';
1293 if (!*str)
1294 return nb_args;
1296 args[nb_args] = str;
1297 nb_args++;
1299 while (*str && (*str != ' ') && (*str != '\t'))
1300 str++;
1303 return nb_args;
1306 /* ********************** */
1307 /* ctxopt implementation. */
1308 /* ********************** */
1310 static int ctxopt_initialized = 0; /* cap_init has not yet been called */
1312 /* Flags structure initialized by ctxopt_init. */
1313 /* """"""""""""""""""""""""""""""""""""""""""" */
1314 struct flags_s
1316 int stop_if_non_option;
1317 int allow_abbreviations;
1320 /* Context structure. */
1321 /* """""""""""""""""" */
1322 struct ctx_s
1324 char * name;
1325 ll_t * opt_list; /* list of options allowed in this context. */
1326 ll_t * incomp_list; /* list of strings containing incompatible names *
1327 | of options separated by spaces or tabs. */
1328 ll_t * req_list; /* list of strings containing an option name and *
1329 | all the option names where at least one of *
1330 | them is required to be also present. */
1332 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1333 void ** ctx_data);
1334 void * par_bst;
1335 int nb_data;
1336 void ** data;
1339 /* https://textik.com/#488ce3649b6c60f5 */
1340 /* */
1341 /* +--------------+ */
1342 /* |first_ctx_inst| */
1343 /* +---+----------+ */
1344 /* | */
1345 /* +--v-----+ +--------+ +--------+ +-----+ */
1346 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1347 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1348 /* | | | | | */
1349 /* | | +-v------+ | | */
1350 /* | +--+ctx_inst<-----------+ | */
1351 /* | +-+------+ | */
1352 /* | | | */
1353 /* | +-v------+ | */
1354 /* +------+ctx_inst<--------------------------+ */
1355 /* +-+------+ */
1356 /* | */
1357 /* +-v---+ */
1358 /* | ... | */
1359 /* +-----+ */
1361 /* Option structure. */
1362 /* """"""""""""""""" */
1363 struct opt_s
1365 char * name; /* option name. */
1366 char * next_ctx; /* new context this option may lead to */
1367 ll_t * ctx_list; /* list of contexts allowing this option. */
1368 char * params; /* string containing all the parameters of *
1369 | the option. */
1371 void (*action)( /* The option associated action. */
1372 char * ctx_name, /* context name. */
1373 char * opt_name, /* option name. */
1374 char * par, /* option parameter. */
1375 int nb_args, /* number of arguments. */
1376 char ** args, /* option arguments. */
1377 int nb_opt_data, /* number of option data pointers. */
1378 void ** opt_data, /* option data pointers. */
1379 int nb_ctx_data, /* nb of current context data ptrs. */
1380 void ** ctx_data /* current context data pointers. */
1383 int nb_data; /* number of the data pointers passed as argument to action. */
1384 void ** data; /* array of data pointers passed as argument to action. */
1386 int args; /* 1 if this option takes arguments else 0. */
1387 int optional; /* 1 if the option is optional, else 0. */
1388 int multiple; /* 1 if the option can appear more than one time in a *
1389 | context, else 0. */
1391 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1392 int occurrences; /* Number of option occurrences in a context. */
1393 char opt_count_oper; /* <, = or > */
1394 unsigned opt_count_mark; /* Value to be compared to with opt_count_oper. */
1396 char * arg; /* symbolic text after # describing the option argument. */
1398 int optional_args; /* 1 of option is optional else 0. */
1399 int multiple_args; /* 1 is option can appear more than once in a context *
1400 | instance. */
1402 int opt_args_count_matter; /* 1 if count is rescticted, else 0. */
1403 char opt_args_count_oper; /* <, = or > */
1404 unsigned opt_args_count_mark; /* Value to be compared to with *
1405 | opt_count_oper. */
1407 int eval_first; /* 1 if this option must be evaluated before the options *
1408 | without this mark. */
1410 ll_t * eval_before_list; /* List of pointers on options which must be *
1411 | evaluated before this option. */
1413 ll_t * constraints_list; /* List of constraint check functions pointers. */
1416 /* Context instance structure. */
1417 /* """"""""""""""""""""""""""" */
1418 struct ctx_inst_s
1420 ctx_t * ctx; /* the context whose this is an instance of */
1421 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1422 | creation of this ctx_inst structure. */
1423 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1424 | instance of this structure. */
1425 ll_t * incomp_bst_list; /* list of seen_opt_t BST. */
1426 void * seen_opt_bst; /* tree of seen_opt_t. */
1427 ll_t * opt_req_list; /* list of req_t. */
1428 ll_t * opt_inst_list; /* The list of option instances in this *
1429 | context instance. */
1430 char * par_name; /* parameter which created this instance. */
1433 /* Option instance structure. */
1434 /* """""""""""""""""""""""""" */
1435 struct opt_inst_s
1437 opt_t * opt; /* The option this is an instance of. */
1438 char * opt_name; /* The option which led to this creation. */
1439 char * par; /* The parameter which led to this creation. */
1440 ll_t * values_list; /* The list of arguments of this option. */
1441 ctx_inst_t * next_ctx_inst; /* The new context instance this option. *
1442 | instance may create. */
1445 /* Structure used to check if an option has bee seen or not */
1446 /* in a context instance. */
1447 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1448 struct seen_opt_s
1450 opt_t * opt; /* The concerned option. */
1451 char * par; /* Parameter which led to the making of this structure. */
1452 int seen; /* 1 if seen in the context instances, else 0. */
1455 /* Structure used to check if at least one instance of the options whose */
1456 /* pointers are in or_opt_list has been seen in the ctx_inst where an */
1457 /* instance or opt is also present. */
1458 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1459 struct req_s
1461 opt_t * opt; /* Option that asks for other options. */
1462 ll_t * or_opt_list; /* Required options, at least one of them *
1463 | must be present. */
1466 /* Parameter structure which links a parameter to the option it belongs to. */
1467 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1468 struct par_s
1470 char * name; /* Parameter name (with the leading -). */
1471 opt_t * opt; /* Attached option. */
1474 /* Constraint structure. */
1475 /* """"""""""""""""""""" */
1476 struct constraint_s
1478 int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1479 int nb_args;
1480 char ** args;
1481 char * to_free; /* pointer to the original string in which the array in *
1482 | args points to. This poinnter is kept there to allow *
1483 | it to be freed. */
1486 state_t * cur_state = NULL; /* Current analysis state. */
1487 static ll_t * cmdline_list; /* List of interpreted CLI words *
1488 | serves as the basis for the *
1489 | analysis of the parameters. */
1490 static ctx_t * main_ctx = NULL; /* initial context. */
1491 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1492 | instance which holds the *
1493 | options instances. */
1494 static ll_t * ctx_inst_list = NULL; /* List of the context instances. */
1496 static flags_t flags = { 0, 1 };
1498 /* ======================================================= */
1499 /* Parse a string for the next matching token. */
1500 /* */
1501 /* s: string to parse. */
1502 /* token: pre_allocated array of max tok_len characters. */
1503 /* pattern: scanf type pattern token must match. */
1504 /* pos: number of characters successfully parsed in s. */
1505 /* */
1506 /* Returns: a pointer to the first unread character or */
1507 /* to he terminating \0. */
1508 /* ======================================================= */
1509 static char *
1510 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1512 char * full_pattern;
1513 char len[3];
1514 int n;
1516 *pos = 0;
1518 n = snprintf(len, 3, "%zu", tok_len);
1519 if (n < 0)
1520 return NULL;
1522 full_pattern = xmalloc(strlen(pattern) + n + 4);
1524 strcpy(full_pattern, "%");
1525 strcat(full_pattern, len);
1526 strcat(full_pattern, pattern);
1527 strcat(full_pattern, "%n");
1529 n = sscanf(s, full_pattern, token, pos);
1531 free(full_pattern);
1533 if (n != 1)
1534 return NULL;
1536 return s + *pos;
1539 /* ****************************************** */
1540 /* Various comparison and deletion functions. */
1541 /* ****************************************** */
1543 static int
1544 ctx_compare(const void * c1, const void * c2)
1546 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1549 /* =========================== */
1550 /* Free a context_bst element. */
1551 /* =========================== */
1552 static void
1553 ctx_free(void * c)
1555 ctx_t * ctx = c;
1557 free(ctx->name);
1558 free(ctx->data);
1560 ll_destroy(ctx->opt_list, NULL);
1561 ll_destroy(ctx->incomp_list, free);
1562 bst_destroy(ctx->par_bst, par_free);
1564 free(c);
1567 /* ============================= */
1568 /* Free a ctx_inst_list element. */
1569 /* ============================= */
1570 static void
1571 ctx_inst_free(void * ci)
1573 ctx_inst_t * ctx_inst = ci;
1575 free(ctx_inst->par_name);
1576 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1577 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1578 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1580 free(ci);
1583 /* ============================= */
1584 /* Free a opt_inst_list element. */
1585 /* ============================= */
1586 static void
1587 opt_inst_free(void * oi)
1589 opt_inst_t * opt_inst = oi;
1591 ll_destroy(opt_inst->values_list, NULL);
1593 free(oi);
1596 /* ================================== */
1597 /* Compare two seen_opt_bst elements. */
1598 /* ================================== */
1599 static int
1600 seen_opt_compare(const void * so1, const void * so2)
1602 opt_t *o1, *o2;
1604 o1 = ((seen_opt_t *)so1)->opt;
1605 o2 = ((seen_opt_t *)so2)->opt;
1607 return strcmp(o1->name, o2->name);
1610 /* ============================ */
1611 /* Free a seen_opt_bst element. */
1612 /* ============================ */
1613 void
1614 seen_opt_free(void * so)
1616 seen_opt_t * seen_opt = so;
1618 free(seen_opt->par);
1620 free(so);
1623 /* =========================== */
1624 /* Free an incomp_bst element. */
1625 /* =========================== */
1626 static void
1627 incomp_bst_free(void * b)
1629 bst_t * bst = b;
1631 bst_destroy(bst, NULL);
1634 /* ================================= */
1635 /* Compare two options_bst elements. */
1636 /* ================================= */
1637 static int
1638 opt_compare(const void * o1, const void * o2)
1640 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1643 /* ============================= */
1644 /* Free an options_bst elements. */
1645 /* ============================= */
1646 void
1647 opt_free(void * o)
1649 opt_t * opt = o;
1651 free(opt->name);
1652 free(opt->next_ctx);
1653 free(opt->params);
1654 free(opt->arg);
1655 free(opt->data);
1657 ll_destroy(opt->ctx_list, NULL);
1658 ll_destroy(opt->constraints_list, constraint_free);
1660 free(o);
1663 /* ============================= */
1664 /* Compare two par_bst elements. */
1665 /* ============================= */
1666 static int
1667 par_compare(const void * a1, const void * a2)
1669 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1672 /* ======================= */
1673 /* Free a par_bst element. */
1674 /* ======================= */
1675 static void
1676 par_free(void * p)
1678 par_t * par = p;
1680 free(par->name);
1682 free(p);
1685 /* ================================ */
1686 /* Free a constraints_list element. */
1687 /* ================================ */
1688 static void
1689 constraint_free(void * c)
1691 constraint_t * cstr = c;
1693 free(cstr->args);
1694 free(cstr->to_free);
1696 free(c);
1699 /* ******************************************************************** */
1700 /* Helper functions to locate contexts, options and parameters in a BST */
1701 /* by their names. */
1702 /* ******************************************************************** */
1704 static ctx_t *
1705 locate_ctx(char * name)
1707 bst_t * node;
1708 ctx_t ctx = { 0 };
1710 ctx.name = name;
1712 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1713 return NULL;
1714 else
1715 return node->key;
1718 static opt_t *
1719 locate_opt(char * name)
1721 bst_t * node;
1722 opt_t opt = { 0 };
1724 opt.name = name;
1726 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1727 return NULL;
1728 else
1729 return node->key;
1732 static par_t *
1733 locate_par(char * name, ctx_t * ctx)
1735 bst_t * node;
1736 par_t par = { 0 };
1737 void * bst = ctx->par_bst;
1739 par.name = name;
1741 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1742 return NULL;
1743 else
1744 return node->key;
1747 /* ====================================================================== */
1748 /* Helper function to display the dependency constraints between options. */
1749 /* These constraints are set with the ctxopt_add_opt_settings function */
1750 /* using the 'before' and 'after' arguments. */
1751 /* IN list : a list of options. */
1752 /* ====================================================================== */
1753 static void
1754 print_before_constraints(ll_t * list)
1756 ll_node_t * node = list->head;
1757 ll_node_t * before_node;
1758 opt_t * opt, *before_opt;
1759 int msg = 0;
1761 while (node != NULL)
1763 opt = node->data;
1765 if (opt->eval_before_list->len > 0)
1767 if (!msg)
1769 printf("\n If present in the command line,");
1770 msg = 1; /* Display this message only once. */
1773 before_node = opt->eval_before_list->head;
1775 printf("\n ");
1776 while (before_node != NULL)
1778 before_opt = before_node->data;
1779 printf("%s", before_opt->params);
1781 before_node = before_node->next;
1783 if (before_node != NULL)
1784 printf(" and\n ");
1786 printf(" will be evaluated after %s\n", opt->params);
1788 node = node->next;
1792 /* =================================================================== */
1793 /* Utility function to format and print the options present in a list. */
1794 /* */
1795 /* IN list : a list of options. */
1796 /* OUT has_* : a set of flags which will determine the content of the */
1797 /* explanation given after the formatted printing of the */
1798 /* options. */
1799 /* =================================================================== */
1800 static void
1801 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1802 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1803 int * has_early_eval)
1805 ll_node_t * node = list->head;
1806 opt_t * opt;
1807 char * line;
1808 char * option;
1810 line = xstrdup(" ");
1812 while (node != NULL)
1814 option = xstrdup("");
1815 opt = node->data;
1817 if (opt->optional)
1819 option = strappend(option, "[", (char *)0);
1820 *has_optional = 1;
1823 if (opt->eval_first)
1825 option = strappend(option, "*", (char *)0);
1826 *has_early_eval = 1;
1829 option = strappend(option, opt->params, (char *)0);
1831 if (opt->next_ctx != NULL)
1833 option = strappend(option, ">", opt->next_ctx, (char *)0);
1834 *has_ctx_change = 1;
1837 if (opt->multiple)
1839 if (opt->opt_count_oper != '\0')
1841 char m[4];
1842 char o[2];
1843 o[0] = opt->opt_count_oper;
1844 o[1] = '\0';
1845 snprintf(m, 3, "%u", opt->opt_count_mark);
1846 option = strappend(option, "...", o, m, (char *)0);
1847 *has_rule = 1;
1849 else
1850 option = strappend(option, "...", (char *)0);
1852 *has_ellipsis = 1;
1855 if (opt->args)
1857 if (*(opt->arg) == '#')
1858 *has_generic_arg = 1;
1860 option = strappend(option, " ", (char *)0);
1862 if (opt->optional_args)
1864 option = strappend(option, "[", opt->arg, (char *)0);
1865 *has_optional = 1;
1867 else
1868 option = strappend(option, opt->arg, (char *)0);
1870 if (opt->multiple_args)
1872 if (opt->opt_args_count_oper != '\0')
1874 char m[4];
1875 char o[2];
1876 o[0] = opt->opt_args_count_oper;
1877 o[1] = '\0';
1878 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1879 option = strappend(option, "...", o, m, (char *)0);
1880 *has_rule = 1;
1882 else
1883 option = strappend(option, "...", (char *)0);
1885 *has_ellipsis = 1;
1887 if (opt->optional_args)
1888 option = strappend(option, "]", (char *)0);
1890 if (opt->optional)
1891 option = strappend(option, "]", (char *)0);
1893 if (strlen(line) + 1 + strlen(option) < 80)
1894 line = strappend(line, option, " ", (char *)0);
1895 else
1897 printf("%s\n", line);
1898 line[2] = '\0';
1899 line = strappend(line, option, " ", (char *)0);
1902 free(option);
1904 node = node->next;
1907 printf("%s\n", line);
1909 free(line);
1912 /* ==================================================== */
1913 /* Explain the special syntactic symbols present in the */
1914 /* generated usage messages. */
1915 /* ==================================================== */
1916 static void
1917 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1918 int has_optional, int has_ellipsis, int has_rule)
1920 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1921 || has_ellipsis || has_rule)
1923 printf("\nExplanation of the syntax used above:\n");
1924 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1925 "must be entered.\n");
1926 printf("The following is just there to explain the other symbols "
1927 "displayed.\n\n");
1929 if (has_early_eval)
1930 printf("* : the parameters defined for this option will "
1931 "be evaluated first.\n");
1932 if (has_ctx_change)
1933 printf("> : the context after this symbol will be the new "
1934 "default context.\n");
1935 if (has_generic_arg)
1936 printf("#tag : argument with a hint about its meaning.\n");
1937 if (has_optional)
1938 printf("[...] : the object between square brackets is "
1939 "optional.\n");
1940 if (has_ellipsis)
1941 printf("... : several occurrences of the previous object "
1942 "are possible.\n");
1943 if (has_rule)
1944 printf("[<|=|>]number: rules constraining the number of "
1945 "parameters/arguments.\n");
1949 /* ************************************************************ */
1950 /* Various utilities and callback functions called when walking */
1951 /* through a BST. */
1952 /* ************************************************************ */
1954 static void
1955 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1957 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1959 if (kind == postorder || kind == leaf)
1961 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1963 user_rc = 1;
1964 user_string = strappend(user_string, seen_opt->opt->params, " ",
1965 (char *)0);
1970 static void
1971 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1973 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1975 if (kind == postorder || kind == leaf)
1976 if (seen_opt->seen == 1)
1978 user_rc = 1;
1979 user_object = seen_opt->par;
1983 static void
1984 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
1986 ctx_t * ctx = main_ctx;
1987 ctx_t * cur_ctx = ((bst_t *)node)->key;
1989 ll_t * list;
1991 int has_optional = 0;
1992 int has_ellipsis = 0;
1993 int has_rule = 0;
1994 int has_generic_arg = 0;
1995 int has_ctx_change = 0;
1996 int has_early_eval = 0;
1998 if (kind == postorder || kind == leaf)
1999 if (strcmp(ctx->name, cur_ctx->name) != 0)
2001 list = cur_ctx->opt_list;
2003 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
2004 print_options(list, &has_optional, &has_ellipsis, &has_rule,
2005 &has_generic_arg, &has_ctx_change, &has_early_eval);
2006 print_before_constraints(list);
2010 static void
2011 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
2013 opt_t * opt = ((bst_t *)node)->key;
2015 if (kind == postorder || kind == leaf)
2017 if (opt->params == NULL) /* opt must have associated parameters. */
2018 fatal_internal("Option %s has no registered parameter.\n", opt->name);
2020 if (opt->action == NULL) /* opt must have an action. */
2021 fatal_internal("Option %s has no registered action.\n", opt->name);
2025 static void
2026 bst_match_par_cb(const void * node, walk_order_e kind, int level)
2028 ctx_t * ctx = ((bst_t *)node)->key;
2030 if (kind == postorder || kind == leaf)
2032 char * str = xstrdup(user_string);
2034 while (*str != '\0')
2036 if (locate_par(str, ctx) != NULL)
2038 if (*user_string2 == '\0')
2039 user_string2 = strappend(user_string2, "- ", ctx->name, (char *)0);
2040 else
2041 user_string2 = strappend(user_string2, "\n- ", ctx->name, (char *)0);
2042 break;
2044 str[strlen(str) - 1] = '\0';
2046 free(str);
2050 static void
2051 match_prefix_cb(const void * node, walk_order_e kind, int level)
2053 par_t * par = ((bst_t *)node)->key;
2055 if (kind == postorder || kind == leaf)
2056 if (strpref(par->name, (char *)user_object))
2058 user_rc++;
2059 user_string = strappend(user_string, par->name, " ", (char *)0);
2063 /* ====================================================================== */
2064 /* A parameter may not be separated from its first option by spaces, in */
2065 /* this case this function looks for a valid flag as a prefix and splits */
2066 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
2067 /* option). */
2068 /* */
2069 /* IN word : the word to be checked. */
2070 /* IN ctx : the context in which the flag indexed by the word is to be */
2071 /* checked. */
2072 /* OUT pos : the offset in word pointing just after the matching prefix. */
2073 /* OUT opt : a pointer to the option associated with the new parameter */
2074 /* or NULL if none is found. */
2075 /* */
2076 /* The returned pointer must be freed by the caller. */
2077 /* ====================================================================== */
2078 static char *
2079 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
2081 char * new = NULL;
2082 int len;
2083 par_t * par;
2084 par_t tmp_par = { 0 };
2086 len = strlen(word);
2088 if (len > 2)
2090 new = xstrdup(word);
2094 new[--len] = '\0';
2095 tmp_par.name = new;
2096 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2098 if (par != NULL)
2100 *pos = len;
2101 *opt = par->opt;
2103 else
2105 free(new);
2106 new = NULL;
2109 else
2110 *pos = 0;
2112 return new;
2115 /* ============================================================= */
2116 /* If par_name is an unique abbreviation of an exiting parameter */
2117 /* in the context ctx, then return this parameter. */
2118 /* ============================================================= */
2119 static char *
2120 abbrev_expand(char * par_name, ctx_t * ctx)
2122 user_object = par_name;
2123 user_rc = 0;
2125 *user_string = '\0';
2126 bst_walk(ctx->par_bst, match_prefix_cb);
2127 rtrim(user_string, " ", 0);
2129 /* The previous bst_walk has built a string of blank separated parameters */
2130 /* all having par_name as prefix. This string is put in the user_string */
2131 /* exchange zone. The number of these words in put in user_rc. */
2132 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2133 if (user_rc == 1) /* The number of matching abbreviations. */
2134 return xstrdup(user_string);
2135 else /* There is at least tho defined parameters starting with par_name. */
2137 char * s, *first_s;
2138 par_t * par;
2139 opt_t * opt;
2140 int opt_count = 0;
2141 void * tmp_opt_bst = NULL;
2143 /* Find all the options corresponding to these words and store them */
2144 /* without duplication in a temporary BST. Only their resulting count */
2145 /* matters. */
2146 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2147 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2148 | the first word. */
2149 while (s != NULL)
2151 par = locate_par(s, ctx);
2152 opt = par->opt;
2154 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2156 /* This option as not already been seen */
2157 /* store it and increase the seen counter. */
2158 /* """"""""""""""""""""""""""""""""""""""" */
2159 bst_search(opt, &tmp_opt_bst, opt_compare);
2160 opt_count++;
2162 s = strtok(NULL, " ");
2165 /* Clean the temporary BST without removing the pointer */
2166 /* to the real options. */
2167 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2168 if (tmp_opt_bst != NULL)
2169 bst_destroy(tmp_opt_bst, NULL);
2171 if (opt_count == 1)
2172 /* All the abbreviation are leading to only one option */
2173 /* We can just continue as in the previous case. */
2174 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2175 return xstrdup(first_s);
2176 else
2177 return NULL;
2181 /* ================================================================ */
2182 /* Terminate the program if mandatory options required by a context */
2183 /* are not present. */
2184 /* ================================================================ */
2185 static void
2186 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2188 char * missing;
2190 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2191 fatal(CTXOPTMISPAR, missing);
2194 /* ====================================================== */
2195 /* Return 1 if at least one mandatory option was not seen */
2196 /* when quitting a context, else 0. */
2197 /* ====================================================== */
2198 static int
2199 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2201 user_rc = 0;
2202 *user_string = '\0';
2204 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2205 rtrim(user_string, " ", 0);
2207 *missing = user_string;
2209 return user_rc ? 1 : 0;
2212 /* ========================================================================= */
2213 /* This function terminates the program if an option or its arguments do not */
2214 /* conform to its occurrences constraint. */
2215 /* There constraints can appear by trailing >, < or = in their definition */
2216 /* given in ctxopt_new_ctx. */
2217 /* ========================================================================= */
2218 static void
2219 check_for_occurrence_issues(ctx_inst_t * ctx_inst)
2221 ctx_t * ctx = ctx_inst->ctx;
2222 opt_t * opt;
2223 ll_node_t * node;
2224 opt_inst_t * opt_inst;
2225 char * cur_opt_params = cur_state->cur_opt_params;
2226 char * cur_opt_par_name = cur_state->cur_opt_par_name;
2228 /* Checks options. */
2229 /* """"""""""""""" */
2230 node = ctx->opt_list->head;
2232 while (node != NULL)
2234 opt = node->data;
2236 /* Update current_state. */
2237 /* """"""""""""""""""""" */
2238 cur_state->cur_opt_params = opt->params;
2239 cur_state->opts_count = opt->opt_count_mark;
2240 cur_state->opt_args_count = opt->opt_args_count_mark;
2242 if (opt->opt_count_matter)
2243 switch (opt->opt_count_oper)
2245 case '=':
2246 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2247 fatal(CTXOPTCTEOPT, NULL);
2248 break;
2250 case '<':
2251 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2252 fatal(CTXOPTCTLOPT, NULL);
2253 break;
2255 case '>':
2256 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2257 fatal(CTXOPTCTGOPT, NULL);
2258 break;
2261 node = node->next;
2264 /* Checks arguments. */
2265 /* """"""""""""""""" */
2266 node = ctx_inst->opt_inst_list->head;
2267 while (node != NULL)
2269 opt_inst = node->data;
2270 opt = opt_inst->opt;
2272 /* Update current_state. */
2273 /* """"""""""""""""""""" */
2274 cur_state->cur_opt_par_name = opt_inst->par;
2275 cur_state->opts_count = opt->opt_count_mark;
2276 cur_state->opt_args_count = opt->opt_args_count_mark;
2278 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2280 if (opt->opt_args_count_matter)
2281 switch (opt->opt_args_count_oper)
2283 case '=':
2284 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2285 fatal(CTXOPTCTEARG, NULL);
2286 break;
2288 case '<':
2289 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2290 fatal(CTXOPTCTLARG, NULL);
2291 break;
2293 case '>':
2294 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2295 fatal(CTXOPTCTGARG, NULL);
2296 break;
2299 node = node->next;
2301 cur_state->cur_opt_params = cur_opt_params;
2302 cur_state->cur_opt_par_name = cur_opt_par_name;
2305 /* ====================================================================== */
2306 /* This function terminates the program if all the options which are part */
2307 /* of a group of required options by some other option are missing. */
2308 /* ====================================================================== */
2309 static void
2310 check_for_requirement_issues(ctx_inst_t * ctx_inst)
2312 ll_node_t * node;
2313 ll_node_t * req_node;
2314 req_t * req;
2315 opt_t * opt;
2316 opt_t * req_opt;
2317 bst_t * bst_node;
2318 seen_opt_t tmp_seen_opt;
2319 int found;
2320 char * needed_params = NULL;
2322 node = ctx_inst->opt_req_list->head;
2324 while (node != NULL)
2326 req = node->data;
2328 opt = req->opt;
2329 tmp_seen_opt.opt = opt;
2331 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2332 seen_opt_compare);
2334 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2336 found = 0;
2337 req_node = req->or_opt_list->head;
2339 /* needed_params accumulates the params of the options in the group. */
2340 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2341 free(needed_params); /* free can applied to the NULL pointer. */
2342 needed_params = xstrdup("");
2344 /* Go through the list of the required group of options and */
2345 /* succeed when one of them has been seen in the context. */
2346 /* otherwise a fatal error is triggered and the program is */
2347 /* terminated. */
2348 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2349 while (req_node != NULL)
2351 req_opt = req_node->data;
2352 tmp_seen_opt.opt = req_opt;
2353 needed_params = strappend(needed_params, req_opt->params, "\n ",
2354 (char *)0);
2356 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2357 seen_opt_compare);
2359 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2361 found = 1; /* A required option has been seen, */
2362 break; /* accept the group. */
2364 req_node = req_node->next;
2367 rtrim(needed_params, "\n ", 0);
2369 /* This is a fatal error if none of the options in the required */
2370 /* options group has been seen in the context. */
2371 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2372 if (!found)
2374 char * errmsg;
2376 if (req->or_opt_list->len > 1)
2377 errmsg = xstrdup("At least one of the parameters among:\n %s\n"
2378 "requested by %s must be present.\n");
2379 else
2380 errmsg = xstrdup("The parameter %s "
2381 "requested by %s must be present.\n");
2383 cur_state->req_opt_par_needed = needed_params;
2384 cur_state->req_opt_par = opt->params;
2386 fatal(CTXOPTREQPAR, errmsg);
2390 node = node->next;
2394 /* ======================================================================== */
2395 /* Parse a strings describing options and some of their characteristics */
2396 /* The input string must have follow some rules like in the examples below: */
2397 /* */
2398 /* "opt_name1 opt_name2" */
2399 /* "[opt_name1] opt_name2" */
2400 /* "[opt_name1] opt_name2..." */
2401 /* "[opt_name1 #...] opt_name2... [#]" */
2402 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2403 /* */
2404 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2405 /* means that there can be more than one occurrence of the previous thing. */
2406 /* */
2407 /* opt_name can be followed by a 'new context' change prefixed with the */
2408 /* symbol >, as in opt1>c2 by eg. */
2409 /* */
2410 /* This function returns as soon as one (or no) option has been parsed and */
2411 /* return the offset to the next option to parse. */
2412 /* */
2413 /* In case of successful parsing, an new option is allocated and its */
2414 /* pointer returned. */
2415 /* ======================================================================== */
2416 static int
2417 opt_parse(char * s, opt_t ** opt)
2419 int opt_optional = 0;
2420 int opt_multiple = 0;
2421 int opt_count_matter = 0;
2422 char opt_count_oper = '\0';
2423 unsigned opt_count_mark = 0;
2424 int opt_args = 0;
2425 char opt_arg[33] = { 0 };
2426 int opt_multiple_args = 0;
2427 int opt_args_count_matter = 0;
2428 char opt_args_count_oper = '\0';
2429 unsigned opt_args_count_mark = 0;
2430 int opt_optional_args = 0;
2431 int opt_eval_first = 0;
2433 int n;
2434 int pos;
2435 int count = 0;
2437 char * s_orig = s;
2439 char * p;
2440 char * opt_name;
2441 char * next_ctx;
2442 char token[65];
2444 *opt = NULL;
2445 memset(opt_arg, '\0', 33);
2447 /* Strip the leading blanks. */
2448 /* """"""""""""""""""""""""" */
2449 while (isblank(*s))
2450 s++;
2452 if (*s == '[') /* Start of an optional option. */
2454 opt_optional = 1;
2455 s++;
2457 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2458 if (s == NULL)
2459 return -1; /* Empty string. */
2461 /* Early EOS, only return success if the option is mandatory. */
2462 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2463 if (!*s)
2464 if (opt_optional == 1)
2465 return -(s - s_orig - 1);
2467 /* Validate the option name */
2468 /* ALPHA+(ALPHANUM|_)* */
2469 /* """""""""""""""""""""""" */
2470 p = token;
2471 if (!isalpha(*p) && *p != '*')
2472 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2474 if (*p == '*')
2475 opt_eval_first = 1;
2477 p++;
2478 while (*p)
2480 if (!isalnum(*p) && *p != '_' && *p != '>')
2481 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2482 * a number or a _ */
2483 p++;
2486 if (opt_eval_first)
2487 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2488 else
2489 opt_name = xstrdup(token);
2491 if (*s == ']')
2493 s++;
2494 while (isblank(*s))
2495 s++;
2497 goto success;
2500 /* Check if it can appear multiple times by looking for the dots. */
2501 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2502 p = strtoken(s, token, 3, "[.]", &pos);
2503 if (p)
2505 if (strcmp(token, "...") == 0)
2507 opt_multiple = 1;
2508 s = p;
2509 if (*s == '<' || *s == '=' || *s == '>')
2511 unsigned value;
2512 int offset;
2514 n = sscanf(s + 1, "%u%n", &value, &offset);
2515 if (n == 1)
2517 opt_count_matter = 1;
2518 opt_count_oper = *s;
2519 opt_count_mark = value;
2521 s += offset + 1;
2524 else
2526 free(opt_name);
2527 return -(s - s_orig - 1);
2531 if (*s == ']')
2533 /* Abort on extraneous ] if the option is mandatory. */
2534 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2535 if (!opt_optional)
2536 return -(s - s_orig - 1);
2538 s++; /* skip the ] */
2540 if (!*s || isblank(*s))
2541 goto success;
2542 else
2543 return -(s - s_orig - 1);
2546 /* A blank separates the option name and the argument tag. */
2547 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2548 if (isblank(*s))
2550 char dots[4];
2552 while (isblank(*s))
2553 s++;
2555 if (!*s)
2556 goto success;
2558 pos = 0;
2559 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2560 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2562 opt_args = 1;
2563 opt_optional_args = 1;
2564 if (n == 2)
2565 opt_multiple_args = 1; /* There were dots. */
2567 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2569 if (*s == '<' || *s == '=' || *s == '>')
2571 unsigned value;
2572 int offset;
2574 n = sscanf(s + 1, "%u%n", &value, &offset);
2575 if (n == 1)
2577 opt_args_count_matter = 1;
2578 opt_args_count_oper = *s;
2579 opt_args_count_mark = value;
2581 s += offset + 1;
2584 /* Optional arg tag must end with a ] */
2585 /* """""""""""""""""""""""""""""""""" */
2586 if (*s != ']')
2588 free(opt_name);
2589 return -(s - s_orig - 1);
2592 s++; /* Skip the ] */
2594 else
2596 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2597 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2599 opt_args = 1;
2600 if (n == 2) /* There were dots. */
2601 opt_multiple_args = 1;
2603 s += pos + !!(n == 2) * 3; /* Skip the dots */
2605 if (*s == '<' || *s == '=' || *s == '>')
2607 unsigned value;
2608 int offset;
2610 n = sscanf(s + 1, "%u%n", &value, &offset);
2611 if (n == 1)
2613 opt_args_count_matter = 1;
2614 opt_args_count_oper = *s;
2615 opt_args_count_mark = value;
2617 s += offset + 1;
2621 if (*s == ']')
2623 /* Abort on extraneous ] if the option is mandatory. */
2624 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2625 if (!opt_optional)
2626 return -(s - s_orig - 1);
2628 s++; /* skip the ] */
2630 /* Strip the following blanks. */
2631 /* """"""""""""""""""""""""""" */
2632 while (isblank(*s))
2633 s++;
2635 goto success;
2637 else if (opt_optional == 0 && (!*s || isblank(*s)))
2639 /* Strip the following blanks. */
2640 /* """"""""""""""""""""""""""" */
2641 while (isblank(*s))
2642 s++;
2644 goto success;
2646 else if (opt_args == 0) /* # was not read it is possibly the start *
2647 * of another option. */
2648 goto success;
2649 else
2650 return -(s - s_orig - 1);
2653 success:
2655 /* Strip the following blanks. */
2656 /* """"""""""""""""""""""""""" */
2657 while (isblank(*s))
2658 s++;
2660 next_ctx = NULL;
2662 if (*opt_name == '>')
2663 fatal_internal("The option name is missing in %s.", opt_name);
2665 count = strchrcount(opt_name, '>');
2666 if (count == 1)
2668 char * tmp = strchr(opt_name, '>');
2669 next_ctx = xstrdup(tmp + 1);
2670 *tmp = '\0';
2672 else if (count > 1)
2673 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2675 *opt = xmalloc(sizeof(opt_t));
2677 (*opt)->name = opt_name;
2678 (*opt)->optional = opt_optional;
2679 (*opt)->multiple = opt_multiple;
2680 (*opt)->opt_count_matter = opt_count_matter;
2681 (*opt)->opt_count_oper = opt_count_oper;
2682 (*opt)->opt_count_mark = opt_count_mark;
2683 (*opt)->args = opt_args;
2684 (*opt)->arg = xstrdup(opt_arg);
2685 (*opt)->optional_args = opt_optional_args;
2686 (*opt)->multiple_args = opt_multiple_args;
2687 (*opt)->opt_args_count_matter = opt_args_count_matter;
2688 (*opt)->opt_args_count_oper = opt_args_count_oper;
2689 (*opt)->opt_args_count_mark = opt_args_count_mark;
2690 (*opt)->eval_first = opt_eval_first;
2691 (*opt)->next_ctx = next_ctx;
2692 (*opt)->ctx_list = ll_new();
2693 (*opt)->constraints_list = ll_new();
2694 (*opt)->eval_before_list = ll_new();
2695 (*opt)->action = NULL;
2696 (*opt)->params = NULL;
2697 (*opt)->data = NULL;
2699 return s - s_orig;
2702 /* ==================================================================== */
2703 /* Try to initialize all the option in a given string */
2704 /* Each parsed option are put in a BST tree with its name as index. */
2705 /* */
2706 /* On collision, the arguments only the signature are required to be */
2707 /* the same else this is considered as an error. Options can be used in */
2708 /* more than one context and can be optional in one and mandatory in */
2709 /* another. */
2710 /* ==================================================================== */
2711 static int
2712 init_opts(char * spec, ctx_t * ctx)
2714 opt_t * opt, *bst_opt;
2715 bst_t * node;
2716 int offset;
2718 while (*spec)
2720 if ((offset = opt_parse(spec, &opt)) > 0)
2722 spec += offset;
2724 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2726 int same_next_ctx = 0;
2728 bst_opt = node->key; /* Node extracted from the BST. */
2730 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2731 same_next_ctx = 1;
2732 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2733 same_next_ctx = 0;
2734 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2735 same_next_ctx = 0;
2736 else
2737 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2739 if (bst_opt->optional_args != opt->optional_args
2740 || bst_opt->multiple_args != opt->multiple_args
2741 || bst_opt->args != opt->args || !same_next_ctx)
2743 fatal_internal("The option %s already exists with "
2744 "a different arguments signature.\n",
2745 opt->name);
2748 /* The newly created opt is already present in options_bst. */
2749 /* We can remove it. */
2750 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2751 opt_free(opt);
2753 /* The new occurrence of the option option is legal */
2754 /* append the current context ptr in the list. */
2755 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2756 ll_append(bst_opt->ctx_list, ctx);
2758 /* Append the new option to the context's options list. */
2759 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2760 ll_append(ctx->opt_list, bst_opt);
2762 else
2764 /* Initialize the option's context list with the current context. */
2765 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2766 ll_append(opt->ctx_list, ctx);
2768 /* Append the new option to the context's options list. */
2769 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2770 ll_append(ctx->opt_list, opt);
2772 /* Insert the new option in the BST. */
2773 /* """"""""""""""""""""""""""""""""" */
2774 bst_search(opt, &options_bst, opt_compare);
2777 else
2779 char * s = xstrndup(spec, -offset);
2780 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2781 free(s);
2783 exit(EXIT_FAILURE);
2787 return 1;
2790 /* ===================================================== */
2791 /* ctxopt initialization function, must be called first. */
2792 /* ===================================================== */
2793 void
2794 ctxopt_init(char * prog_name, char * init_flags)
2796 int n;
2798 contexts_bst = NULL;
2799 options_bst = NULL;
2800 char * ptr;
2802 user_rc = 0;
2803 user_value = 0;
2804 user_string = xmalloc(8);
2805 user_string2 = xmalloc(8);
2806 user_object = NULL;
2807 char flag[33], fname[31], vname[31];
2808 int invalid;
2810 ctxopt_initialized = 1;
2812 /* Initialize current_state.*/
2813 /* """""""""""""""""""""""" */
2814 cur_state = xcalloc(sizeof(state_t), 0);
2816 /* Initialize custom error function pointers to NULL. */
2817 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2818 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2819 for (n = 0; n < CTXOPTERRSIZ; n++)
2820 err_functions[n] = NULL;
2822 /* Parse init_flags if any. */
2823 /* """""""""""""""""""""""" */
2824 while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2826 if (*flag)
2828 if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2829 fatal_internal("Invalid flag assignment: %s.", flag);
2831 if (strcmp(fname, "stop_if_non_option") == 0)
2833 if (eval_yes(vname, &invalid))
2834 flags.stop_if_non_option = 1;
2835 else if (!invalid)
2836 flags.stop_if_non_option = 0;
2837 else
2838 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2840 else if (strcmp(fname, "allow_abbreviations") == 0)
2842 if (eval_yes(vname, &invalid))
2843 flags.allow_abbreviations = 1;
2844 else if (!invalid)
2845 flags.allow_abbreviations = 0;
2846 else
2847 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2849 else
2850 fatal_internal("Invalid flag name: %s.", fname);
2854 /* Update current_state. */
2855 /* """"""""""""""""""""" */
2856 if (prog_name)
2858 if (*prog_name == '\0')
2859 cur_state->prog_name = xstrdup("program_name");
2860 else if ((ptr = strrchr(prog_name, '/')))
2861 cur_state->prog_name = xstrdup(ptr + 1);
2862 else
2863 cur_state->prog_name = xstrdup(prog_name);
2865 else
2866 cur_state->prog_name = xstrdup("program_name");
2869 /* ========================================================================= */
2870 /* Utility function which create and register a par_t object in a BST */
2871 /* embedded in a context. */
2872 /* This object will have a name and a pointer to the option it refers to. */
2873 /* These object will be used to quickly find an option from a command */
2874 /* line parameter during the analysis phase. */
2875 /* */
2876 /* IN : an option name. */
2877 /* IN : a string of command line parameters to associate to the option. */
2878 /* Returns : 1 is all was fine else 0. */
2879 /* ========================================================================= */
2880 static int
2881 opt_set_parms(char * opt_name, char * par_str)
2883 char * par_name, *ctx_name;
2884 char * tmp_par_str, *end_tmp_par_str;
2885 ctx_t * ctx;
2886 opt_t * opt;
2887 bst_t * node;
2888 par_t * par, tmp_par;
2889 int rc = 1; /* return code */
2891 ll_t * list;
2892 ll_node_t * lnode;
2894 /* Look if the given option is defined. */
2895 /* """""""""""""""""""""""""""""""""""" */
2896 opt = locate_opt(opt_name);
2897 if (opt == NULL)
2898 fatal_internal("Unknown option %s.", opt_name);
2900 /* For each context using this option. */
2901 /* """"""""""""""""""""""""""""""""""" */
2902 list = opt->ctx_list;
2904 lnode = list->head;
2905 while (lnode != NULL)
2907 /* Locate the context in the contexts tree. */
2908 /* """""""""""""""""""""""""""""""""""""""" */
2909 ctx_name = ((ctx_t *)(lnode->data))->name;
2911 ctx = locate_ctx(ctx_name);
2912 if (ctx == NULL)
2913 fatal_internal("Unknown context %s.", ctx_name);
2914 else
2916 void * par_bst = ctx->par_bst;
2918 tmp_par_str = xstrdup(par_str);
2919 ltrim(tmp_par_str, " \t");
2920 rtrim(tmp_par_str, " \t", 0);
2921 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2922 if (par_name == NULL)
2923 fatal_internal("Parameters are missing for option %s.", opt_name);
2925 /* For each parameter given in par_str, creates a par_t object and */
2926 /* insert it the in the parameters BST of the context. */
2927 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2928 while (par_name != NULL)
2930 tmp_par.name = par_name;
2932 node = bst_find(&tmp_par, &par_bst, par_compare);
2933 if (node != NULL)
2935 fatal_internal("The parameter %s is already defined in context %s.",
2936 par_name, ctx->name);
2937 rc = 0;
2939 else
2941 par = xmalloc(sizeof(par_t));
2942 par->name = xstrdup(par_name);
2943 par->opt = opt; /* Link the option to this parameter */
2945 bst_search(par, &par_bst, par_compare);
2947 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2950 /* Update the value of the root of ctx->par_bst as it may have */
2951 /* been modified. */
2952 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2953 ctx->par_bst = par_bst;
2955 free(tmp_par_str);
2957 lnode = lnode->next;
2960 return rc;
2963 /* ==================================================================== */
2964 /* Create a new context instance. */
2965 /* IN ctx : a context pointer to allow this instance to */
2966 /* access the context fields */
2967 /* IN prev_ctx_inst : the context instance whose option leading to the */
2968 /* creation of this new context instance is part of */
2969 /* Returns : the new context. */
2970 /* ==================================================================== */
2971 static ctx_inst_t *
2972 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
2974 opt_t * opt;
2975 opt_inst_t * gen_opt_inst;
2976 ctx_inst_t * ctx_inst;
2977 seen_opt_t * seen_opt;
2978 char * str, *opt_name;
2979 void * bst;
2980 bst_t * bst_node;
2982 /* Keep a trace of the opt_inst which was at the origin of the creation */
2983 /* of this context instance. */
2984 /* This will serve during the evaluation of the option callbacks. */
2985 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2986 if (prev_ctx_inst != NULL)
2988 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
2990 /* Update current_state. */
2991 /* """"""""""""""""""""" */
2992 cur_state->opt_name = gen_opt_inst->opt->name;
2994 else
2995 gen_opt_inst = NULL;
2997 /* Create and initialize the new context instance. */
2998 /* """"""""""""""""""""""""""""""""""""""""""""""" */
2999 ctx_inst = xmalloc(sizeof(ctx_inst_t));
3000 ctx_inst->ctx = ctx;
3001 ctx_inst->prev_ctx_inst = prev_ctx_inst;
3002 ctx_inst->gen_opt_inst = gen_opt_inst;
3003 ctx_inst->incomp_bst_list = ll_new();
3004 ctx_inst->opt_inst_list = ll_new();
3005 ctx_inst->opt_req_list = ll_new();
3006 ctx_inst->seen_opt_bst = NULL;
3008 ll_node_t * node;
3010 if (prev_ctx_inst == NULL)
3011 first_ctx_inst = ctx_inst;
3013 /* Initialize the occurrence counters of each opt allowed in the context. */
3014 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3015 node = ctx->opt_list->head;
3016 while (node != NULL)
3018 opt = node->data;
3019 opt->occurrences = 0;
3021 node = node->next;
3024 /* Initialize the BST containing the seen indicator for all the options */
3025 /* allowed in this context instance. */
3026 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3027 node = ctx->opt_list->head;
3028 while (node != NULL)
3030 opt = node->data;
3031 seen_opt = xmalloc(sizeof(seen_opt_t));
3032 seen_opt->opt = opt;
3033 seen_opt->par = NULL;
3034 seen_opt->seen = 0;
3036 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
3038 node = node->next;
3041 /* Initialize the BST containing the incompatibles options. */
3042 /* Incompatibles option names are read from strings found in the list */
3043 /* incomp_list present in each instance of ctx_t. */
3044 /* These names are then used to search for the object of type seen_opt_t */
3045 /* which is already present in the seen_opt_bst of the context instance. */
3046 /* in the BST. */
3047 /* Once found the seen_opt_t object in inserted in the new BST */
3048 /* At the end the new BST in added to the list incomp_bst_list. */
3049 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3050 node = ctx->incomp_list->head;
3051 while (node != NULL)
3053 bst = NULL;
3054 seen_opt_t tmp_seen_opt;
3056 str = xstrdup(node->data);
3057 ltrim(str, " \t");
3058 rtrim(str, " \t", 0);
3059 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3061 while (opt_name != NULL) /* For each option name. */
3063 if ((opt = locate_opt(opt_name)) != NULL)
3065 /* The option found is searched in the tree of potential */
3066 /* seen options. */
3067 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3068 tmp_seen_opt.opt = opt;
3070 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
3071 seen_opt_compare);
3073 if (bst_node != NULL)
3075 /* If found then it is added into the new BST tree. */
3076 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3077 seen_opt = bst_node->key;
3078 bst_search(seen_opt, &bst, seen_opt_compare);
3080 else
3081 /* Not found! That means that the option is unknown in this */
3082 /* context as all options has have a seen_opt structure in */
3083 /* seen_opt_bst. */
3084 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3085 fatal_internal("%s is not known in the context %s.", opt->name,
3086 ctx->name);
3088 else
3089 fatal_internal("Unknown option %s.", opt_name);
3091 opt_name = strtok(NULL, " \t");
3094 free(str);
3095 ll_append(ctx_inst->incomp_bst_list, bst);
3097 node = node->next;
3100 /* Initialize the list of res_t structures according to the */
3101 /* list set in the context by ctxopt_add_ctx_settings/required. */
3102 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3103 node = ctx->req_list->head;
3104 while (node != NULL)
3106 req_t * req = xmalloc(sizeof(req_t));
3108 str = xstrdup(node->data);
3109 ltrim(str, " \t");
3110 rtrim(str, " \t", 0);
3111 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3113 if ((opt = locate_opt(opt_name)) != NULL)
3115 req->opt = opt;
3116 req->or_opt_list = ll_new();
3117 while ((opt_name = strtok(NULL, " \t")) != NULL)
3119 if ((opt = locate_opt(opt_name)) != NULL)
3120 ll_append(req->or_opt_list, opt);
3121 else
3122 fatal_internal("Unknown option %s.", opt_name);
3124 ll_append(ctx_inst->opt_req_list, req);
3126 else
3127 fatal_internal("Unknown option %s.", opt_name);
3129 node = node->next;
3131 return ctx_inst;
3134 /* ====================================================================== */
3135 /* Create a list formed by all the significant command line words */
3136 /* Words beginning or ending with { or } are split. Each of these */
3137 /* symbols will get their own place in the list. */
3138 /* */
3139 /* the {...} part delimits a context, the { will not appear in the list */
3140 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3141 /* to facilitate the parsing phase. | must not be used by the end user. */
3142 /* */
3143 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3144 /* program name is not considered. */
3145 /* IN words : is the array of strings constituting the command line to */
3146 /* parse. */
3147 /* Returns : 1 on success, 0 if a { or } is missing. */
3148 /* ====================================================================== */
3149 static int
3150 ctxopt_build_cmdline_list(int nb_words, char ** words)
3152 int i;
3153 char * prev_word = NULL;
3154 char * word;
3155 char * ptr;
3156 int level = 0;
3157 ll_node_t *node, *start_node;
3159 /* The analysis is divided into three passes, this is not optimal but */
3160 /* must be done only one time. Doing that we privilege readability. */
3161 /* */
3162 /* In the following, SG is the ascii character 1d (dec 29) */
3163 /* */
3164 /* The first pass creates the list, extract the leading an trailing */
3165 /* SG '{' and '}' of each word and give them their own place in the */
3166 /* list */
3167 /* */
3168 /* The second pass transform the '{...}' blocks by a trailing SG */
3169 /* ({...} -> ...|) */
3170 /* */
3171 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3172 /* the middle in the remaining list elements and recreate the pseudo */
3173 /* argument: {} */
3174 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3176 /* If the option list is not empty, clear it before going further. */
3177 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3178 if (cmdline_list != NULL)
3180 node = cmdline_list->head;
3181 while (node != NULL)
3183 free(node->data);
3184 ll_delete(cmdline_list, node);
3185 node = cmdline_list->head;
3188 else
3189 cmdline_list = ll_new();
3191 start_node = cmdline_list->head; /* In the following loop start_node will *
3192 * contain a pointer to the current *
3193 * word stripped from its leading *
3194 * sequence of {, }. */
3195 for (i = 0; i < nb_words; i++)
3197 size_t len = strlen(words[i]);
3198 size_t start, end;
3199 char * str;
3201 str = words[i];
3203 /* Replace each occurrence of the legal word {} by the characters */
3204 /* 0x02 and 0x03 to hide them from the following process. */
3205 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3206 while ((ptr = strstr(str, "{}")) != NULL)
3208 *ptr = 0x02; /* Arbitrary values unlikely. */
3209 *(ptr + 1) = 0x03; /* present in a word */
3212 if (len > 1) /* The word contains at least 2 characters. */
3214 start = 0;
3216 /* Interpret its beginning and look for the start of the real word. */
3217 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3218 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
3220 ll_append(cmdline_list, xstrndup(str + start, 1));
3221 start++;
3222 start_node = cmdline_list->tail;
3225 end = len - 1;
3226 if (str[end] == '{' || str[end] == '}')
3228 if (end > 0 && str[end - 1] != '\\')
3230 ll_append(cmdline_list, xstrndup(str + end, 1));
3231 end--;
3232 node = cmdline_list->tail;
3234 while (str[end] == '{' || str[end] == '}')
3236 if (end > start && str[end - 1] == '\\')
3237 break;
3239 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3240 end--;
3241 node = node->prev;
3246 if (start <= end)
3248 if (start_node != NULL)
3249 ll_insert_after(cmdline_list, start_node,
3250 xstrndup(str + start, end - start + 1));
3251 else
3252 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3253 start_node = cmdline_list->tail;
3256 else if (len == 1)
3258 ll_append(cmdline_list, xstrdup(str));
3259 start_node = cmdline_list->tail;
3263 /* 2nd pass. */
3264 /* """"""""" */
3265 node = cmdline_list->head;
3267 level = 0;
3268 while (node != NULL)
3270 word = node->data;
3272 if (strcmp(word, "{") == 0)
3274 ll_node_t * old_node = node;
3275 level++;
3276 node = node->next;
3277 free(word);
3278 ll_delete(cmdline_list, old_node);
3280 else if (strcmp(word, "}") == 0)
3282 level--;
3284 if (level < 0)
3285 return 0;
3286 else
3287 *word = 0x1d;
3289 else
3290 node = node->next;
3293 if (level != 0)
3294 return 0;
3296 /* 3rd pass. */
3297 /* """"""""" */
3298 node = cmdline_list->head;
3300 while (node != NULL)
3302 word = node->data;
3304 /* Restore the original { and } characters forming the legal word {}. */
3305 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3306 while ((ptr = strchr(word, 0x02)) != NULL)
3307 *ptr = '{';
3308 while ((ptr = strchr(word, 0x03)) != NULL)
3309 *ptr = '}';
3311 /* Remove a SG if the previous element is SG. */
3312 /* """""""""""""""""""""""""""""""""""""""""" */
3313 if (strcmp(word, "\x1d") == 0)
3315 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3317 ll_node_t * old_node = node;
3318 node = node->prev;
3319 free(old_node->data);
3320 ll_delete(cmdline_list, old_node);
3323 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3324 * a parameter. Protect it. */
3326 free(node->data);
3327 node->data = xstrdup("\\-");
3330 prev_word = node->data;
3331 node = node->next;
3334 /* Clean useless and SG at the beginning and end of list. */
3335 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3336 node = cmdline_list->head;
3338 if (node == NULL)
3339 return 1;
3341 word = node->data;
3343 if (strcmp(word, "\x1d") == 0)
3345 free(word);
3346 ll_delete(cmdline_list, node);
3349 node = cmdline_list->tail;
3350 if (node == NULL)
3351 return 1;
3353 word = node->data;
3355 if (strcmp(word, "\x1d") == 0)
3357 free(word);
3358 ll_delete(cmdline_list, node);
3361 return 1;
3364 /* ===================================================================== */
3365 /* Build and analyze the command line list and create the linked data */
3366 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3367 /* This function identifies the following errors and creates an array of */
3368 /* The remaining unanalyzed arguments. */
3369 /* - detect missing arguments */
3370 /* - detect too many arguments */
3371 /* - detect unknown parameters in a context */
3372 /* - detect too many occurrences of a parameters in a context */
3373 /* - detect missing required arguments in a context */
3374 /* */
3375 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3376 /* program name is not considered */
3377 /* IN words : is the array of strings constituting the command line to */
3378 /* parse. */
3379 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3380 /* is present in the list. */
3381 /* OUT rem_args : array of remaining command line arguments if a -- */
3382 /* is present in the list. This array must be free by */
3383 /* The caller as it is allocated here. */
3384 /* ===================================================================== */
3385 void
3386 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3387 char *** rem_args)
3389 char * ctxopt_debug_env; /* Environment variable CTXOPT_DEBUG content. */
3390 int ctxopt_debug; /* 1 if ctxopt_debug_env is set and not empty. *
3391 | 0 if ctxopt_debug_env is unset or empty. */
3393 ctx_t * ctx;
3394 opt_t * opt;
3395 par_t * par;
3396 ctx_inst_t * ctx_inst;
3397 opt_inst_t * opt_inst;
3398 int expect_par = 0;
3399 int expect_arg = 0;
3400 int expect_par_or_arg = 0;
3402 ll_node_t * cli_node;
3403 bst_t * bst_node;
3404 seen_opt_t * bst_seen_opt;
3405 char * par_name;
3406 void * bst;
3408 ll_node_t * node;
3410 if (!ctxopt_build_cmdline_list(nb_words, words))
3411 fatal_internal("The command line could not be parsed: "
3412 "missing '{' or '}' detected.");
3414 if (main_ctx == NULL)
3415 fatal_internal("At least one context must have been created.");
3417 /* Check that all options has an action and at least one parameter. */
3418 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3419 bst_walk(options_bst, bst_check_opt_cb);
3421 /* CTXOPT debug setting */
3422 /* """""""""""""""""""" */
3423 ctxopt_debug_env = getenv("CTXOPT_DEBUG");
3424 if (ctxopt_debug_env != NULL && *ctxopt_debug_env != '\0')
3425 ctxopt_debug = 1;
3426 else
3427 ctxopt_debug = 0;
3429 /* Create the first ctx_inst record. */
3430 /* """"""""""""""""""""""""""""""""" */
3431 ctx = main_ctx;
3433 ctx_inst_list = ll_new();
3434 ctx_inst = new_ctx_inst(ctx, NULL);
3435 ctx_inst->par_name = NULL;
3437 /* Update current_state. */
3438 /* """"""""""""""""""""" */
3439 cur_state->ctx_name = ctx->name;
3441 ll_append(ctx_inst_list, ctx_inst);
3443 /* For each node in the command line. */
3444 /* """""""""""""""""""""""""""""""""" */
3445 cli_node = cmdline_list->head;
3446 expect_par = 1;
3447 par_name = NULL;
3449 while (cli_node != NULL)
3452 if (strcmp(cli_node->data, "--") == 0)
3453 break; /* No new parameter will be analyzed after this point. */
3455 par_name = cli_node->data;
3457 /* Replace a leading -- by a single - */
3458 /* """""""""""""""""""""""""""""""""" */
3459 if (strncmp(par_name, "--", 2) == 0)
3460 par_name += 1; /* Ignore the first dash */
3462 if (strcmp(par_name, "\x1d") == 0)
3464 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3465 check_for_occurrence_issues(ctx_inst);
3466 check_for_requirement_issues(ctx_inst);
3468 /* Forced backtracking to the previous context instance. */
3469 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3470 if (ctx_inst->prev_ctx_inst != NULL)
3472 ctx_inst = ctx_inst->prev_ctx_inst;
3473 ctx = ctx_inst->ctx;
3475 /* Update current_states. */
3476 /* """"""""""""""""""""" */
3477 cur_state->ctx_name = ctx->name;
3478 cur_state->ctx_par_name = ctx_inst->par_name;
3480 if (ctxopt_debug)
3481 fprintf(stderr,
3482 "CTXOPT_DEBUG: Context forced backtrack, "
3483 "new current context: %s.\n",
3484 ctx->name);
3486 else
3488 /* Update current_state. */
3489 /* """"""""""""""""""""" */
3490 cur_state->ctx_par_name = NULL;
3493 else if (expect_par && *par_name == '-')
3495 int pos = 0;
3496 char * prefix;
3498 /* Update current_state. */
3499 /* """"""""""""""""""""" */
3500 cur_state->cur_opt_par_name = par_name;
3501 cur_state->ctx_name = ctx->name;
3502 cur_state->ctx_par_name = ctx_inst->par_name;
3504 if (ctxopt_debug)
3505 fprintf(stderr, "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3506 par_name, cur_state->ctx_name);
3508 /* An expected parameter has been seen. */
3509 /* """""""""""""""""""""""""""""""""""" */
3510 if ((par = locate_par(par_name, ctx)) == NULL)
3512 opt_t * popt;
3513 char * word;
3515 /* Look if this parameter is an unique abbreviation of a longer */
3516 /* parameter. If this is the case then just replace it with its */
3517 /* full length version and try again. */
3518 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3519 if (flags.allow_abbreviations)
3520 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3522 cli_node->data = word;
3523 continue;
3526 /* Try to find a prefix which is a valid parameter in this context */
3527 /* If found, split the cli_node in two to build a new parameter */
3528 /* node and followed by a node containing the remaining string */
3529 /* If the new parameter corresponds to an option not taking */
3530 /* argument then prefix the remaining string whit a dash as it may */
3531 /* contain a new parameter. */
3532 /* The new parameter will be re-evaluated in the next iteration of */
3533 /* the loop. */
3534 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3535 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3536 if (prefix != NULL && pos != 0)
3538 if (ctxopt_debug)
3539 fprintf(stderr,
3540 "CTXOPT_DEBUG: Found a valid parameter "
3541 "as a prefix of %s: %s.\n",
3542 par_name, prefix);
3544 cli_node->data = prefix; /* prefix contains le name of a valid *
3545 | parameter in this context. */
3547 if (popt->args)
3549 /* The parameter may be followed by arguments. */
3550 /* ''''''''''''''''''''''''''''''''''''''''''' */
3551 if (*(par_name + pos) == '-')
3553 word = xstrdup("\\"); /* Protect the '-' */
3554 word = strappend(word, par_name + pos, (char *)0);
3556 else
3557 word = xstrdup(par_name + pos);
3559 else
3561 /* The parameter does not take arguments, the */
3562 /* following word must be a parameter or nothing */
3563 /* hence prefix it with a dash. */
3564 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3565 word = xstrdup("-");
3566 word = strappend(word, par_name + pos, (char *)0);
3569 /* Insert it after the current node in the list. */
3570 /* """"""""""""""""""""""""""""""""""""""""""""" */
3571 ll_insert_after(cmdline_list, cli_node, word);
3573 continue; /* loop */
3575 else
3577 check_for_missing_mandatory_opt(ctx_inst, par_name);
3578 check_for_occurrence_issues(ctx_inst);
3579 check_for_requirement_issues(ctx_inst);
3581 if (ctx_inst->prev_ctx_inst == NULL)
3583 char * errmsg = xstrdup("");
3585 /* Update current_state. */
3586 /* """"""""""""""""""""" */
3587 cur_state->ctx_par_name = NULL;
3589 *user_string = '\0';
3590 *user_string2 = '\0';
3592 user_string = strappend(user_string, par_name, (char *)0);
3594 bst_walk(contexts_bst, bst_match_par_cb);
3596 if (*user_string2 != '\0')
3598 errmsg = strappend(
3599 errmsg,
3600 "\nThis parameter is only valid in one of the following "
3601 "contexts:\n",
3602 user_string2,
3603 "\n\nSwitch to one of them first using the appropriate "
3604 "parameter, see below.\n",
3605 (char *)0);
3608 fatal(CTXOPTUNKPAR, errmsg);
3610 else
3612 /* Tries to backtrack and analyse the same parameter in the */
3613 /* previous context. */
3614 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3615 ctx_inst = ctx_inst->prev_ctx_inst;
3616 ctx = ctx_inst->ctx;
3618 if (ctxopt_debug)
3619 fprintf(stderr,
3620 "CTXOPT_DEBUG: Context backtrack, "
3621 "new current context: %s.\n",
3622 ctx->name);
3624 /* Update current_state. */
3625 /* """"""""""""""""""""" */
3626 cur_state->ctx_name = ctx->name;
3627 cur_state->ctx_par_name = ctx_inst->par_name;
3629 cli_node = cli_node->prev;
3633 else
3635 seen_opt_t seen_opt;
3637 /* The parameter is valid in the context, create a opt_inst and */
3638 /* append it to the ctx_inst list options list. */
3639 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3640 opt = par->opt;
3642 opt->occurrences++;
3644 opt_inst = xmalloc(sizeof(opt_inst_t));
3645 opt_inst->opt = opt;
3646 opt_inst->par = par_name;
3647 opt_inst->values_list = ll_new();
3648 opt_inst->next_ctx_inst = NULL;
3650 /* Update current_state. */
3651 /* """"""""""""""""""""" */
3652 cur_state->cur_opt_params = opt->params;
3654 /* Priority option are inserted at the start of the opt_inst list */
3655 /* but their order of appearance in the context definition must */
3656 /* be preserver so each new priority option will be placed after */
3657 /* the previous ones at the start of the opt_inst list. */
3658 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3659 if (!opt->eval_first)
3661 /* Look if we have a registered dependency in the order of the */
3662 /* evaluation of two options. */
3663 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3664 if (opt->eval_before_list->len > 0)
3666 ll_t * list = ctx_inst->opt_inst_list;
3667 ll_node_t * opt_inst_node;
3669 ll_t * before_list = opt->eval_before_list;
3670 ll_node_t * before_node = before_list->head;
3672 ll_node_t * target_node = NULL; /* If not NULL, the new node *
3673 | will be inserted before it. */
3675 /* For each entry in eval_before_list, try to find if it */
3676 /* refers to an option already entered in the context. If this */
3677 /* is the case, insert it just before it instead of putting it */
3678 /* at the end. */
3679 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3680 while (before_node != NULL)
3682 opt_inst_node = list->head;
3684 while (opt_inst_node != target_node)
3686 opt_t * tmp_opt = (((opt_inst_t *)opt_inst_node->data))->opt;
3688 /* We have found an option mentioned if the before_list */
3689 /* of the option we want to add. We can stop searching. */
3690 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3691 if (strcmp(tmp_opt->name, ((opt_t *)before_node->data)->name))
3692 opt_inst_node = opt_inst_node->next;
3693 else
3694 target_node = opt_inst_node; /* Set the target node. */
3697 before_node = before_node->next;
3700 /* Insert or append ? */
3701 /* """""""""""""""""" */
3702 if (target_node != NULL)
3703 ll_insert_before(ctx_inst->opt_inst_list, target_node, opt_inst);
3704 else
3705 ll_append(ctx_inst->opt_inst_list, opt_inst);
3707 else
3708 ll_append(ctx_inst->opt_inst_list, opt_inst);
3710 else
3712 ll_node_t * opt_inst_node = ctx_inst->opt_inst_list->head;
3713 opt_inst_t * tmp_opt_inst;
3715 while (opt_inst_node != NULL)
3717 tmp_opt_inst = opt_inst_node->data;
3718 if (!tmp_opt_inst->opt->eval_first)
3720 ll_insert_before(ctx_inst->opt_inst_list, opt_inst_node,
3721 opt_inst);
3722 break;
3724 else
3725 opt_inst_node = opt_inst_node->next;
3727 if (opt_inst_node == NULL)
3728 ll_append(ctx_inst->opt_inst_list, opt_inst);
3731 /* Check if an option was already seen in the */
3732 /* current context instance. */
3733 /* """""""""""""""""""""""""""""""""""""""""" */
3734 seen_opt.opt = opt;
3736 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3737 seen_opt_compare);
3739 /* bst_node cannot be NULL here. */
3741 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3743 if (!opt->multiple && bst_seen_opt->seen == 1)
3744 fatal(CTXOPTDUPOPT, NULL);
3746 /* Check if this option is compatible with the options already */
3747 /* seen in this context instance. */
3748 /* Look if the option is present in one on the BST present in */
3749 /* the incomp_bst_list of the context instance. */
3750 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3751 node = ctx_inst->incomp_bst_list->head;
3752 while (node != NULL)
3754 bst = node->data;
3755 user_object = NULL;
3757 /* There can only have one seen_opt object in the BST tree was */
3758 /* already seen, try to locate it, the result will be put in */
3759 /* user_object by the bst_seen_opt_seen_cb function. */
3760 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3761 bst_walk(bst, bst_seen_opt_seen_cb);
3763 /* If it is the case, look if the current option is also */
3764 /* in this BST. */
3765 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3766 if (user_object != NULL)
3768 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3770 if (bst_node != NULL)
3772 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3773 if (bst_seen_opt->seen == 0)
3774 fatal(CTXOPTINCOPT, (char *)user_object);
3778 node = node->next;
3781 /* Mark this option as seen in the current context instance. */
3782 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3783 bst_seen_opt->seen = 1;
3784 free(bst_seen_opt->par);
3785 bst_seen_opt->par = xstrdup(par_name);
3787 /* If this option leads to a next context, create a new ctx_inst */
3788 /* and switch to it for the analyse of the future parameter. */
3789 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3790 if (opt->next_ctx != NULL)
3792 ctx = locate_ctx(opt->next_ctx);
3794 if (ctx == NULL)
3795 fatal_internal("Unknown context %s.", opt->next_ctx);
3797 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3798 ctx_inst->par_name = xstrdup(par_name);
3800 ll_append(ctx_inst_list, ctx_inst);
3802 if (ctxopt_debug)
3803 fprintf(stderr,
3804 "CTXOPT_DEBUG: Context change, "
3805 "new current context: %s.\n",
3806 ctx->name);
3809 /* Look is we must expect some arguments. */
3810 /* """""""""""""""""""""""""""""""""""""" */
3811 expect_par_or_arg = 0;
3812 expect_par = 0;
3813 expect_arg = 0;
3815 if (!opt->args)
3816 expect_par = 1; /* Parameter doesn't accept any argument. */
3817 else
3819 if (!opt->optional_args)
3820 expect_arg = 1; /* Parameter has mandatory arguments. */
3821 else
3822 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3826 else if (expect_par && *par_name != '-')
3828 ll_node_t * n = cli_node->next;
3830 if (!flags.stop_if_non_option)
3831 /* Look if potential arguments must still be analyzed until the */
3832 /* end of the context/command line part to analyze/command line. */
3833 /* If this is the case we have met an extra argument. */
3834 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3835 while (n != NULL)
3837 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3838 fatal(CTXOPTUNXARG, NULL);
3840 if (*(char *)(n->data) == '-')
3841 fatal(CTXOPTUNXARG, NULL);
3843 n = n->next;
3846 break; /* An unexpected non parameter was seen, if no Potential *
3847 | arguments remain in the command line or *
3848 | flags.stop_if_non_option is set, assume that it is is *
3849 | the first of the non arguments and stop the command *
3850 | line analysis. */
3852 else if (expect_arg && *par_name != '-')
3854 ll_node_t * cstr_node;
3855 constraint_t * cstr;
3857 if (ctxopt_debug)
3858 fprintf(stderr, "CTXOPT_DEBUG: Argument: %s.\n", par_name);
3860 /* Check if the arguments of the option respects */
3861 /* the attached constraints if any. */
3862 /* """"""""""""""""""""""""""""""""""""""""""""" */
3863 cstr_node = opt->constraints_list->head;
3864 while (cstr_node != NULL)
3866 cstr = cstr_node->data;
3867 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3868 cur_state->cur_opt_par_name))
3870 fputs("\n", stderr);
3871 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3874 cstr_node = cstr_node->next;
3877 /* If the argument is valid, store it. */
3878 /* """"""""""""""""""""""""""""""""""" */
3879 if (*par_name == '\\' && *(par_name + 1) == '-')
3880 ll_append(opt_inst->values_list, par_name + 1);
3881 else
3882 ll_append(opt_inst->values_list, par_name);
3884 expect_arg = 0;
3885 expect_par = 0;
3886 expect_par_or_arg = 0;
3888 if (opt->multiple_args)
3889 expect_par_or_arg = 1;
3890 else
3891 expect_par = 1; /* Parameter takes only one argument. */
3893 else if (expect_arg && *par_name == '-')
3894 fatal(CTXOPTMISARG, NULL);
3895 else if (expect_par_or_arg)
3897 expect_arg = 0;
3898 expect_par = 0;
3899 expect_par_or_arg = 0;
3901 if (*par_name != '-')
3902 expect_arg = 1; /* Consider this word as an argument and retry. */
3903 else
3904 expect_par = 1; /* Consider this word as a parameter and retry. */
3906 cli_node = cli_node->prev;
3909 cli_node = cli_node->next;
3912 if (cmdline_list->len > 0 && par_name && *par_name == '-')
3914 if (expect_arg && !opt->optional_args)
3915 fatal(CTXOPTMISARG, NULL);
3918 /* Look if a context_instance has unseen mandatory options. */
3919 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3920 node = ctx_inst_list->head;
3921 while (node != NULL)
3923 ctx_inst = node->data;
3925 /* Update current_state. */
3926 /* """"""""""""""""""""" */
3927 cur_state->ctx_name = ctx_inst->ctx->name;
3928 cur_state->ctx_par_name = ctx_inst->par_name;
3930 check_for_missing_mandatory_opt(ctx_inst, par_name);
3931 check_for_occurrence_issues(ctx_inst);
3932 check_for_requirement_issues(ctx_inst);
3934 node = node->next;
3937 /* Allocate the array containing the remaining not analyzed */
3938 /* command line arguments. */
3939 /* NOTE: The strings in the array are just pointer to the */
3940 /* data of the generating list and must not be freed. */
3941 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3942 if (cli_node != NULL)
3944 if (strcmp((char *)cli_node->data, "--") == 0)
3945 /* The special parameter -- was encountered, the -- argument is not */
3946 /* put in the remaining arguments. */
3947 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3948 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3949 else
3950 /* A non parameter was encountered when a parameter was expected. We */
3951 /* assume that the evaluation of the remaining command line argument */
3952 /* are not the responsibility of the users code. */
3953 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3954 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3956 else
3958 *nb_rem_args = 0;
3959 *rem_args = xmalloc(sizeof(char *));
3960 (*rem_args)[0] = NULL;
3964 /* ==================================================== */
3965 /* Free ctxopt memory used for its internal structures. */
3966 /* ==================================================== */
3967 void
3968 ctxopt_free_memory(void)
3970 ll_destroy(cmdline_list, free);
3971 ll_destroy(ctx_inst_list, ctx_inst_free);
3972 bst_destroy(options_bst, opt_free);
3973 bst_destroy(contexts_bst, ctx_free);
3976 /* ==================================================================== */
3977 /* Parse the options data structures and launches the callback function */
3978 /* attached to each options instances. */
3979 /* This calls a recursive function which proceeds context per context. */
3980 /* ==================================================================== */
3981 void
3982 ctxopt_evaluate(void)
3984 evaluate_ctx_inst(first_ctx_inst);
3987 /* =================================================================== */
3988 /* Recursive function called by ctxopt_evaluate to process the list of */
3989 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3990 /* action attached to the context and its option instances. */
3991 /* =================================================================== */
3992 static void
3993 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
3995 opt_inst_t * opt_inst;
3996 ctx_t * ctx;
3997 opt_t * opt;
3998 ll_node_t * opt_inst_node;
3999 char ** args;
4000 int nb_args;
4002 if (ctx_inst == NULL)
4003 return;
4005 ctx = ctx_inst->ctx;
4007 /* Do not evaluate the action attached to this context is there is no */
4008 /* option to evaluate. */
4009 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4010 opt_inst_node = ctx_inst->opt_inst_list->head;
4011 if (opt_inst_node == NULL)
4012 return;
4014 /* Call the entering action attached to this context if any. */
4015 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4016 if (ctx->action != NULL)
4018 if (ctx_inst->prev_ctx_inst != NULL)
4019 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
4020 ctx->nb_data, ctx->data);
4021 else
4022 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
4025 /* For each instance of options. */
4026 /* """"""""""""""""""""""""""""" */
4027 while (opt_inst_node != NULL)
4029 opt_inst = (opt_inst_t *)(opt_inst_node->data);
4030 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
4031 &args);
4032 opt = opt_inst->opt;
4034 /* Launch the attached action if any. */
4035 /* """""""""""""""""""""""""""""""""" */
4036 if (opt->action != NULL)
4037 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
4038 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
4040 if (opt_inst->next_ctx_inst != NULL)
4041 evaluate_ctx_inst(opt_inst->next_ctx_inst);
4043 if (args != NULL)
4044 free(args);
4046 opt_inst_node = opt_inst_node->next;
4049 /* Call the exiting action attached to this context if any. */
4050 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4051 if (ctx->action != NULL)
4053 if (ctx_inst->prev_ctx_inst != NULL)
4054 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
4055 ctx->nb_data, ctx->data);
4056 else
4057 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
4061 /* ============================================================ */
4062 /* Create and initializes a new context. */
4063 /* - allocate space. */
4064 /* - name it. */
4065 /* - initialize its option with a few of their characteristics. */
4066 /* ============================================================ */
4067 void
4068 ctxopt_new_ctx(char * name, char * opts_specs)
4070 ctx_t * ctx;
4071 char * p;
4073 if (!ctxopt_initialized)
4074 fatal_internal("Please call ctxopt_init first.");
4076 ctx = xmalloc(sizeof(ctx_t));
4078 /* Validates the context name: */
4079 /* ALPHA+(ALPHANUM|_)* */
4080 /* """"""""""""""""""""""""""" */
4081 p = name;
4082 if (!isalpha(*p))
4083 fatal_internal("A context name must start with a letter: %s.", name);
4085 p++;
4086 while (*p)
4088 if (!isalnum(*p) && *p != '_')
4089 fatal_internal("A context name must only contain letters, "
4090 "numbers or '_': %s.",
4091 name);
4092 p++;
4095 ctx->name = xstrdup(name);
4096 ctx->opt_list = ll_new(); /* List of options legit in this context. */
4097 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
4098 ctx->req_list = ll_new(); /* List of opts/required opts tuples (str). */
4099 ctx->par_bst = NULL;
4100 ctx->data = NULL;
4101 ctx->action = NULL;
4103 /* The first created context is the main one. */
4104 /* """""""""""""""""""""""""""""""""""""""""" */
4105 if (contexts_bst == NULL)
4107 main_ctx = ctx;
4109 cur_state->ctx_name = ctx->name;
4112 if (init_opts(opts_specs, ctx) == 0)
4113 exit(EXIT_FAILURE);
4114 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
4115 fatal_internal("The context %s already exists.", name);
4116 else
4117 bst_search(ctx, &contexts_bst, ctx_compare);
4120 /* ==================================================== */
4121 /* Display a usage screen limited to a specific context */
4122 /* IN: the context name. */
4123 /* IN: what to do after (continue or exit the program) */
4124 /* possible values: continue_after, exit_after. */
4125 /* ==================================================== */
4126 void
4127 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
4129 ctx_t * ctx;
4130 ll_t * list;
4132 int has_optional = 0;
4133 int has_ellipsis = 0;
4134 int has_rule = 0;
4135 int has_generic_arg = 0;
4136 int has_ctx_change = 0;
4137 int has_early_eval = 0;
4139 ctx = locate_ctx(ctx_name);
4140 if (ctx == NULL)
4141 fatal_internal("Unknown context %s.", ctx_name);
4143 if (cur_state->ctx_par_name == NULL)
4144 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
4145 else
4146 printf("\nSynopsis for the context introduced by %s:\n",
4147 cur_state->ctx_par_name);
4149 list = ctx->opt_list;
4150 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4151 &has_ctx_change, &has_early_eval);
4153 print_before_constraints(list);
4155 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4156 has_optional, has_ellipsis, has_rule);
4158 if (action == exit_after)
4159 exit(EXIT_FAILURE);
4162 /* =================================================== */
4163 /* Display a full usage screen about all contexts. */
4164 /* IN: what to do after (continue or exit the program) */
4165 /* possible values: continue_after, exit_after. */
4166 /* =================================================== */
4167 void
4168 ctxopt_disp_usage(usage_behaviour action)
4170 ll_t * list;
4171 int has_optional = 0;
4172 int has_ellipsis = 0;
4173 int has_rule = 0;
4174 int has_generic_arg = 0;
4175 int has_ctx_change = 0;
4176 int has_early_eval = 0;
4178 if (main_ctx == NULL)
4179 fatal_internal("At least one context must have been created.");
4181 /* Usage for the first context. */
4182 /* """""""""""""""""""""""""""" */
4183 printf("\nAllowed options in the base context:\n");
4184 list = main_ctx->opt_list;
4185 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4186 &has_ctx_change, &has_early_eval);
4188 /* Dependency constraints between options. */
4189 /* """"""""""""""""""""""""""""""""""""""" */
4190 print_before_constraints(list);
4192 /* Usage for the other contexts. */
4193 /* """"""""""""""""""""""""""""" */
4194 bst_walk(contexts_bst, bst_print_ctx_cb);
4196 /* Contextual syntactic explanations. */
4197 /* """""""""""""""""""""""""""""""""" */
4198 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4199 has_optional, has_ellipsis, has_rule);
4201 if (action == exit_after)
4202 exit(EXIT_FAILURE);
4205 /* *********************************** */
4206 /* Built-in constraint check functions */
4207 /* *********************************** */
4209 /* ============================================================= */
4210 /* This constraint checks if each arguments respects a format as */
4211 /* defined for the scanf function. */
4212 /* return 1 if yes and 0 if no. */
4213 /* ============================================================= */
4215 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
4217 int rc = 0;
4219 char x[256];
4220 char y;
4221 char * format;
4223 if (nb_args != 1)
4224 fatal_internal("Format constraint, invalid number of parameters.");
4226 if (strlen(value) > 255)
4227 value[255] = '\0';
4229 format = xstrdup(args[0]);
4230 format = strappend(format, "%c", (char *)0);
4232 rc = sscanf(value, format, x, &y);
4233 if (rc != 1)
4234 fprintf(stderr,
4235 "The argument %s of %s does not respect the imposed format %s.",
4236 value, par, args[0]);
4238 free(format);
4240 return rc == 1;
4243 /* ================================================================== */
4244 /* This constraint checks if each arguments of the option instance is */
4245 /* between a minimum and a maximum (inclusive). */
4246 /* return 1 if yes and 0 if no. */
4247 /* ================================================================== */
4249 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
4251 regex_t re;
4253 if (nb_args != 1)
4254 fatal_internal(
4255 "Regular expression constraint, invalid number of parameters.");
4257 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
4258 fatal_internal("Invalid regular expression %s.", args[0]);
4260 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
4262 fprintf(stderr,
4263 "The argument %s of %s doesn't match the constraining "
4264 "regular expression %s.",
4265 value, par, args[0]);
4266 return 0;
4269 regfree(&re);
4271 return 1;
4274 /* ================================================================== */
4275 /* This constraint checks if each arguments of the option instance is */
4276 /* between a minimum and a maximum (inclusive). */
4277 /* return 1 if yes and 0 if no. */
4278 /* ================================================================== */
4280 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
4282 long min, max;
4283 char c;
4284 char * ptr;
4285 int n;
4286 long v;
4287 int min_only = 0;
4288 int max_only = 0;
4290 if (nb_args != 2)
4291 fatal_internal("Range constraint, invalid number of parameters.");
4293 if (strcmp(args[0], ".") == 0)
4294 max_only = 1;
4295 else
4296 n = sscanf(args[0], "%ld%c", &min, &c);
4298 if (!max_only && n != 1)
4299 fatal_internal("Range constraint, min: invalid parameters.");
4301 if (strcmp(args[1], ".") == 0)
4302 min_only = 1;
4303 else
4304 n = sscanf(args[1], "%ld%c", &max, &c);
4306 if (!min_only && n != 1)
4307 fatal_internal("Range constraint, max: invalid parameters.");
4309 if (min_only && max_only)
4310 fatal_internal("Range constraint, invalid parameters.");
4312 errno = 0;
4313 v = strtol(value, &ptr, 10);
4314 if (errno || ptr == value)
4315 return 0;
4317 if (min_only)
4319 if (v < min)
4321 fprintf(stderr,
4322 "The argument %ld of %s is not greater than or equal to %ld.", v,
4323 par, min);
4324 return 0;
4326 else
4327 return 1;
4329 else if (max_only)
4331 if (v > max)
4333 fprintf(stderr,
4334 "The argument %ld of %s is not less than or equal to %ld.", v,
4335 par, max);
4336 return 0;
4338 else
4339 return 1;
4341 else if (v < min || v > max)
4343 fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
4344 par, min, max);
4345 return 0;
4348 return 1; /* check passed */
4351 /* =============================================================== */
4352 /* This function provides a way to set the behaviour of a context. */
4353 /* =============================================================== */
4354 void
4355 ctxopt_add_global_settings(settings s, ...)
4357 va_list(args);
4358 va_start(args, s);
4360 switch (s)
4362 case error_functions:
4364 typedef void fn(errors e, state_t * state);
4366 void (*function)(errors e, state_t * state);
4368 errors e;
4369 e = va_arg(args, errors);
4370 function = va_arg(args, fn *);
4371 err_functions[e] = function;
4372 break;
4375 default:
4376 break;
4378 va_end(args);
4381 /* ================================================================ */
4382 /* This function provides a way to set the behaviour of an option. */
4383 /* It can take a variable number of arguments according to its */
4384 /* first argument: */
4385 /* - parameter: */
4386 /* o a string containing an option name and all its possible */
4387 /* parameters separates by spaces, tabs or commas (char *) */
4388 /* (e.g: "help -h -help"). */
4389 /* - actions: */
4390 /* o a string containing an option name. */
4391 /* o a pointer to a function which will be called at evaluation */
4392 /* time. */
4393 /* - constraints: */
4394 /* o a string containing an option name. */
4395 /* o a pointer to a function to check if an argument is valid. */
4396 /* o a strings containing the arguments to this function. */
4397 /* ================================================================ */
4398 void
4399 ctxopt_add_opt_settings(settings s, ...)
4401 opt_t * opt;
4402 void * ptr = NULL;
4404 va_list(args);
4405 va_start(args, s);
4407 switch (s)
4409 /* This part associates some command line parameters to an option. */
4410 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4411 case parameters:
4413 char * opt_name;
4414 char * params;
4416 /* The second argument must be a string containing: */
4417 /* - The name of an existing option. */
4418 /* - a list of parameters with a leading dash (-). */
4419 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4420 ptr = va_arg(args, char *);
4421 opt_name = ptr;
4423 if (opt_name != NULL)
4425 if ((opt = locate_opt(opt_name)) != NULL)
4427 ptr = va_arg(args, char *);
4428 params = ptr;
4430 if (!opt_set_parms(opt_name, params))
4431 fatal_internal(
4432 "Duplicated parameters or bad settings for the option %s.",
4433 params);
4435 else
4436 fatal_internal("Unknown option %s.", opt_name);
4438 else
4439 fatal_internal(
4440 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4442 /* Here opt is a known option. */
4443 /* """"""""""""""""""""""""""" */
4444 if (opt->params != NULL)
4445 fatal_internal("Parameters are already set for %s.", opt_name);
4446 else
4448 size_t n;
4449 size_t l = strlen(params);
4451 opt->params = xstrdup(params);
4452 while ((n = strcspn(opt->params, " \t")) < l)
4453 opt->params[n] = '|';
4456 break;
4459 /* This part associates a callback function to an option. */
4460 /* This function will be called when an instance of an option */
4461 /* is evaluated. */
4462 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4463 case actions:
4465 void * data;
4466 void (*function)();
4467 int nb_data = 0;
4469 /* The second argument must be the name of an existing option. */
4470 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4471 ptr = va_arg(args, char *);
4473 if ((opt = locate_opt(ptr)) != NULL)
4475 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4476 void **);
4478 /* The third argument must be the callback function. */
4479 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4480 function = va_arg(args, fn *);
4481 opt->action = function;
4483 /* The fourth argument must be a pointer to an user's defined */
4484 /* variable or structure that the previous function can manage. */
4485 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4486 while ((data = va_arg(args, void *)) != NULL)
4488 nb_data++;
4489 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4490 opt->data[nb_data - 1] = data;
4492 opt->nb_data = nb_data;
4494 else
4495 fatal_internal("Unknown option %s.", ptr);
4496 break;
4499 /* This part associates a list of functions to control some */
4500 /* characteristics of the arguments of an option. */
4501 /* Each function will be called in order and must return 1 */
4502 /* to validate the arguments. */
4503 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4504 case constraints:
4506 char * value;
4507 constraint_t * cstr;
4508 int (*function)();
4510 /* The second argument must be a string. */
4511 /* """"""""""""""""""""""""""""""""""""" */
4512 ptr = va_arg(args, char *);
4514 if ((opt = locate_opt(ptr)) != NULL)
4516 typedef int fn(int, char **, char *);
4518 /* The third argument must be a function. */
4519 /* """""""""""""""""""""""""""""""""""""" */
4520 function = va_arg(args, fn *);
4522 cstr = xmalloc(sizeof(constraint_t));
4523 cstr->constraint = function;
4525 /* The fourth argument must be a string containing the argument of */
4526 /* The previous function separated by spaces or tabs. */
4527 /* Theses arguments will be passed to the previous function */
4528 /* max: 32 argument! */
4529 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4530 value = xstrdup(va_arg(args, char *));
4532 cstr->to_free = value;
4533 cstr->args = xcalloc(sizeof(char *), 32);
4534 cstr->nb_args = str2argv(value, cstr->args, 32);
4535 ll_append(opt->constraints_list, cstr);
4537 else
4538 fatal_internal("Unknown option %s.", ptr);
4539 break;
4542 /* This part allows to indicate that an option must be evaluated */
4543 /* after a list of other options. */
4544 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4545 case after:
4547 char * str;
4549 /* The second argument must be a string. */
4550 /* """"""""""""""""""""""""""""""""""""" */
4551 ptr = va_arg(args, char *);
4553 if ((opt = locate_opt(ptr)) != NULL)
4555 char * end_str;
4556 char * opt_name;
4557 opt_t * opt_before;
4559 ptr = va_arg(args, char *);
4561 str = xstrdup(ptr);
4562 ltrim(str, " \t");
4563 rtrim(str, " \t", 0);
4565 /* Feed the list of options to be evaluated after the given option. */
4566 /* This list will contain pointers to options. */
4567 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4568 opt_name = xstrtok_r(str, " \t,", &end_str);
4569 if (opt_name != NULL)
4571 if ((opt_before = locate_opt(opt_name)) != NULL)
4573 ll_append(opt->eval_before_list, opt_before);
4574 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4576 if ((opt_before = locate_opt(opt_name)) != NULL)
4577 ll_append(opt->eval_before_list, opt_before);
4578 else
4579 fatal_internal("Unknown option %s.", opt_name);
4582 else
4583 fatal_internal("Unknown option %s.", opt_name);
4585 else
4586 fatal_internal("Not enough options to be evaluated after %s.",
4587 opt->name);
4589 free(str);
4591 else
4592 fatal_internal("Unknown option %s.", ptr);
4594 break;
4597 /* This part allows to indicate that an option must be evaluated */
4598 /* before a list of other options. */
4599 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4600 case before:
4602 char * str;
4604 /* The second argument must be a string. */
4605 /* """"""""""""""""""""""""""""""""""""" */
4606 ptr = va_arg(args, char *);
4608 if ((opt = locate_opt(ptr)) != NULL)
4610 char * end_str;
4611 char * opt_name;
4612 opt_t * opt_before;
4614 ptr = va_arg(args, char *);
4616 str = xstrdup(ptr);
4617 ltrim(str, " \t");
4618 rtrim(str, " \t", 0);
4620 /* Feed the list of options to be evaluated before the given option. */
4621 /* This list will contain pointers to options. */
4622 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4623 opt_name = xstrtok_r(str, " \t,", &end_str);
4624 if (opt_name != NULL)
4626 if ((opt_before = locate_opt(opt_name)) != NULL)
4628 ll_append(opt_before->eval_before_list, opt);
4629 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4631 if ((opt_before = locate_opt(opt_name)) != NULL)
4632 ll_append(opt_before->eval_before_list, opt);
4633 else
4634 fatal_internal("Unknown option %s.", opt_name);
4637 else
4638 fatal_internal("Unknown option %s.", opt_name);
4640 else
4641 fatal_internal("Not enough options to be evaluated before %s.",
4642 opt->name);
4644 free(str);
4646 else
4647 fatal_internal("Unknown option %s.", ptr);
4649 break;
4652 default:
4653 break;
4655 va_end(args);
4658 /* =============================================================== */
4659 /* This function provides a way to set the behaviour of a context. */
4660 /* =============================================================== */
4661 void
4662 ctxopt_add_ctx_settings(settings s, ...)
4664 ctx_t * ctx;
4666 va_list(args);
4667 va_start(args, s);
4669 switch (s)
4671 /* Add a set of mutually incompatible options in a context. */
4672 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4673 case incompatibilities:
4675 void * ptr;
4676 ll_t * list;
4677 size_t n;
4678 char * str;
4680 ptr = va_arg(args, char *);
4681 if ((ctx = locate_ctx(ptr)) != NULL)
4683 ptr = va_arg(args, char *);
4684 list = ctx->incomp_list;
4686 str = xstrdup(ptr);
4687 ltrim(str, " \t");
4688 rtrim(str, " \t", 0);
4690 n = strcspn(str, " \t");
4691 if (n > 0 && n < strlen(str))
4692 ll_append(list, str);
4693 else
4694 fatal_internal(
4695 "Not enough incompatible options in the string: \"%s\".", str);
4697 else
4698 fatal_internal("Unknown context %s.", ptr);
4699 break;
4702 case requirements:
4704 void * ptr;
4705 ll_t * list;
4706 size_t n;
4707 char * str;
4709 ptr = va_arg(args, char *);
4710 if ((ctx = locate_ctx(ptr)) != NULL)
4712 ptr = va_arg(args, char *);
4713 list = ctx->req_list;
4715 str = xstrdup(ptr);
4716 ltrim(str, " \t");
4717 rtrim(str, " \t", 0);
4719 n = strcspn(str, " \t");
4720 if (n > 0 && n < strlen(str))
4721 ll_append(list, str);
4722 else
4723 fatal_internal("Not enough required options in the string: \"%s\".",
4724 str);
4726 else
4727 fatal_internal("Unknown context %s.", ptr);
4728 break;
4731 /* Add functions which will be called when */
4732 /* entering and exiting a context. */
4733 /* """"""""""""""""""""""""""""""""""""""" */
4734 case actions:
4736 void * ptr;
4737 void * data;
4738 int (*function)();
4739 int nb_data = 0;
4741 ptr = va_arg(args, char *);
4742 if ((ctx = locate_ctx(ptr)) != NULL)
4744 typedef int fn(char *, direction, char *, int, void **);
4746 function = va_arg(args, fn *);
4747 ctx->action = function;
4749 while ((data = va_arg(args, void *)) != NULL)
4751 nb_data++;
4752 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4753 ctx->data[nb_data - 1] = data;
4755 ctx->nb_data = nb_data;
4757 else
4758 fatal_internal("Unknown context %s.", ptr);
4759 break;
4762 default:
4763 break;
4765 va_end(args);