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 /* ################################################################### */
13 #include <sys/types.h>
19 /* ************************ */
20 /* Static global variables. */
21 /* ************************ */
23 static void * contexts_bst
;
24 static void * options_bst
;
30 /* ************************** */
31 /* Fatal messages prototypes. */
32 /* ************************** */
34 static void (**err_functions
)(errors e
, state_t
* state
);
37 fatal_internal(const char * format
, ...);
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 /* ************************************ */
56 xcalloc(size_t num
, size_t size
);
59 xrealloc(void * ptr
, size_t size
);
62 xstrdup(const char * p
);
65 xstrndup(const char * str
, size_t len
);
67 /* ********************** */
68 /* BST static prototypes. */
69 /* ********************** */
71 typedef struct bst_s bst_t
;
81 #if 0 /* Unused yet */
83 bst_delete(const void * vkey
, void ** vrootp
,
84 int (*compar
)(const void *, const void *));
88 bst_destroy(void * vrootp
, void (*clean
)(void *));
91 bst_find(const void * vkey
, void * const * vrootp
,
92 int (*compar
)(const void *, const void *));
95 bst_search(void * vkey
, void ** vrootp
,
96 int (*compar
)(const void *, const void *));
99 bst_walk_recurse(const bst_t
* root
,
100 void (*action
)(const void *, walk_order_e
, int), int level
);
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
;
113 ll_append(ll_t
* const list
, void * const data
);
116 ll_prepend(ll_t
* const list
, void * const data
);
119 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
);
122 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
);
125 ll_delete(ll_t
* const list
, ll_node_t
* node
);
127 #if 0 /* Unused yet */
129 ll_find(ll_t
* const, void * const, int (*)(const void *, const void *));
133 ll_init(ll_t
* list
);
142 ll_free(ll_t
* const list
, void (*)(void *));
145 ll_destroy(ll_t
* const list
, void (*)(void *));
148 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
);
150 /* ************************** */
151 /* Various static prototypes. */
152 /* ************************** */
155 ltrim(char * str
, const char * trim_str
);
158 rtrim(char * str
, const char * trim_str
, size_t min
);
161 strchrcount(char * str
, char c
);
164 strpref(char * s1
, char * s2
);
167 stricmp(const char * s1
, const char * s2
);
170 xstrtok_r(char * str
, const char * delim
, char ** end
);
173 eval_yes(char * value
, int * invalid
);
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
;
192 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
);
195 ctx_compare(const void * c1
, const void * c2
);
201 ctx_inst_free(void * ci
);
204 opt_inst_free(void * oi
);
207 seen_opt_compare(const void * so1
, const void * so2
);
210 incomp_bst_free(void * b
);
213 seen_opt_free(void * seen_opt
);
216 opt_compare(const void * o1
, const void * o2
);
222 par_compare(const void * a1
, const void * a2
);
228 constraint_free(void * cstr
);
231 locate_ctx(char * name
);
234 locate_opt(char * name
);
237 locate_par(char * name
, ctx_t
* ctx
);
240 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
241 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
242 int * has_early_eval
);
244 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
245 int has_optional
, int has_ellipsis
, int has_rule
);
247 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
);
250 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
);
253 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
);
256 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
);
259 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
);
262 match_prefix_cb(const void * node
, walk_order_e kind
, int level
);
265 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
);
268 opt_parse(char * s
, opt_t
** opt
);
271 init_opts(char * spec
, ctx_t
* ctx
);
274 ctxopt_build_cmdline_list(int nb_words
, char ** words
);
277 opt_set_parms(char * opt_name
, char * par_str
);
280 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
);
283 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
);
285 /* ****************************** */
286 /* Fatal messages implementation. */
287 /* ****************************** */
289 /* ================================================================== */
290 /* Fatal error function used when a fatal condition is encountered. */
291 /* This function is reserved for the ctxopt internal usage. */
293 /* format : printf like format */
294 /* ... : remaining arguments interpreted using the format argument */
295 /* ================================================================== */
297 fatal_internal(const char * format
, ...)
301 fprintf(stderr
, "CTXOPT: ");
303 va_start(args
, format
);
304 vfprintf(stderr
, format
, args
);
305 fprintf(stderr
, "\n");
311 /* ====================================================================== */
312 /* Generic fatal error function. This one uses the global status ctxopt */
313 /* stored in the cur_state structure and can call custom error functions. */
314 /* registered by the users for a given error identifier if any. */
316 /* e : Error identifier responsible of the fatal error */
317 /* errmsg : Users's provided string specific to the error e */
318 /* Note that errmsg is not used in all cases */
320 /* CTXOPTMISPAR Missing parameter */
321 /* CTXOPTMISARG Missing argument */
322 /* CTXOPTUXPARG Unexpected argument */
323 /* CTXOPTDUPOPT Duplicated option */
324 /* CTXOPTUNKPAR Unknown parameter */
325 /* CTXOPTINCOPT Incompatible option */
326 /* CTXOPTCTEOPT Option: bad number of occurrences */
327 /* CTXOPTCTLOPT Option: not enough occurrences */
328 /* CTXOPTCTGOPT Option: too many occurrence of */
329 /* CTXOPTCTEARG Arguments: bad number of occurrences */
330 /* CTXOPTCTLARG Arguments: not enough occurrences */
331 /* CTXOPTCTGARG Arguments: too many occurrences */
332 /* ====================================================================== */
334 fatal(errors e
, char * errmsg
)
336 if (err_functions
[e
] != NULL
)
337 err_functions
[e
](e
, cur_state
);
346 if (cur_state
->ctx_par_name
!= NULL
)
348 "the mandatory parameter(s) %s are missing in the context "
349 "introduced by %s.\n",
350 errmsg
, cur_state
->ctx_par_name
);
353 "The mandatory parameter(s) %s are missing "
354 "in the main context.\n",
362 "The parameter %s takes no arguments "
363 "or has too many arguments.\n",
364 cur_state
->cur_opt_par_name
);
368 if (cur_state
->pre_opt_par_name
!= NULL
)
369 fprintf(stderr
, "%s requires argument(s).\n",
370 cur_state
->pre_opt_par_name
);
372 fprintf(stderr
, "%s requires argument(s).\n",
373 cur_state
->cur_opt_par_name
);
377 if (cur_state
->pre_opt_par_name
!= NULL
)
379 "The parameter %s can only appear once in the context "
380 "introduced by %s.\n",
381 cur_state
->cur_opt_params
, cur_state
->ctx_par_name
);
384 "The parameter %s can only appear once "
385 "in the main context.\n",
386 cur_state
->cur_opt_params
);
390 fprintf(stderr
, "Unknown parameter %s.\n", cur_state
->cur_opt_par_name
);
394 fprintf(stderr
, "The parameter %s is incompatible with %s.\n",
395 cur_state
->cur_opt_par_name
, errmsg
);
399 if (cur_state
->ctx_par_name
)
401 "The parameter %s must appear exactly %d times "
402 "in the context introduced by %s.\n",
403 cur_state
->cur_opt_params
, cur_state
->opts_count
,
404 cur_state
->ctx_par_name
);
407 "The parameter %s must appear exactly %d times "
408 "in the main context.\n",
409 cur_state
->cur_opt_params
, cur_state
->opts_count
);
413 if (cur_state
->ctx_par_name
)
415 "The parameter %s must appear less than %d times "
416 "in the context introduced by %s.\n",
417 cur_state
->cur_opt_params
, cur_state
->opts_count
,
418 cur_state
->ctx_par_name
);
421 "The parameter %s must appear less than %d times "
422 "in the main context.\n",
423 cur_state
->cur_opt_params
, cur_state
->opts_count
);
427 if (cur_state
->ctx_par_name
)
429 "The parameter %s must appear more than %d times "
430 "in the context introduced by %s.\n",
431 cur_state
->cur_opt_params
, cur_state
->opts_count
,
432 cur_state
->ctx_par_name
);
435 "The parameter %s must appear more than %d times "
436 "in the main context.\n",
437 cur_state
->cur_opt_params
, cur_state
->opts_count
);
441 fprintf(stderr
, "The parameter %s must have exactly %d arguments.\n",
442 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
446 fprintf(stderr
, "The parameter %s must have less than %d arguments.\n",
447 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
451 fprintf(stderr
, "The parameter %s must have more than %d arguments.\n",
452 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
460 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, continue_after
);
462 exit(e
); /* Exit with the error id e as return code. */
465 /* ********************************* */
466 /* Memory management implementation. */
467 /* ********************************* */
469 /* ================== */
470 /* Customized malloc. */
471 /* ================== */
478 real_size
= (size
> 0) ? size
: 1;
479 allocated
= malloc(real_size
);
480 if (allocated
== NULL
)
481 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
482 (unsigned long int)size
);
487 /* ================== */
488 /* Customized calloc. */
489 /* ================== */
491 xcalloc(size_t n
, size_t size
)
496 size
= (size
> 0) ? size
: 1;
497 allocated
= calloc(n
, size
);
498 if (allocated
== NULL
)
499 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
500 (unsigned long int)size
);
505 /* =================== */
506 /* Customized realloc. */
507 /* =================== */
509 xrealloc(void * p
, size_t size
)
513 allocated
= realloc(p
, size
);
514 if (allocated
== NULL
&& size
> 0)
515 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
516 (unsigned long int)size
);
521 /* ==================================== */
522 /* strdup implementation using xmalloc. */
523 /* ==================================== */
525 xstrdup(const char * p
)
529 allocated
= xmalloc(strlen(p
) + 1);
530 strcpy(allocated
, p
);
535 /* =================================================== */
536 /* strndup implementation using xmalloc. */
537 /* This version guarantees that there is a final '\0'. */
538 /* =================================================== */
540 xstrndup(const char * str
, size_t len
)
544 p
= memchr(str
, '\0', len
);
549 p
= xmalloc(len
+ 1);
556 /* *************************** */
557 /* Linked list implementation. */
558 /* *************************** */
560 /* Linked list node structure. */
561 /* """"""""""""""""""""""""""" */
565 struct ll_node_s
* next
;
566 struct ll_node_s
* prev
;
569 /* Linked List structure. */
570 /* """""""""""""""""""""" */
578 /* ========================= */
579 /* Create a new linked list. */
580 /* ========================= */
584 ll_t
* ret
= xmalloc(sizeof(ll_t
));
590 /* =============================================== */
591 /* Free all the elements of a list (make it empty) */
592 /* NULL or a custom function may be used to free */
593 /* the sub components of the elements. */
594 /* =============================================== */
596 ll_free(ll_t
* const list
, void (*clean
)(void *))
601 /* Apply a custom cleaner if not NULL. */
602 /* """"""""""""""""""""""""""""""""""" */
604 clean(list
->head
->data
);
606 ll_delete(list
, list
->head
);
610 /* ==================================== */
611 /* Destroy a list and all its elements. */
612 /* ==================================== */
614 ll_destroy(ll_t
* list
, void (*clean
)(void *))
618 ll_free(list
, clean
);
623 /* ========================= */
624 /* Initialize a linked list. */
625 /* ========================= */
634 /* ===================================================== */
635 /* Allocate the space for a new node in the linked list. */
636 /* ===================================================== */
640 ll_node_t
* ret
= xmalloc(sizeof(ll_node_t
));
645 /* ==================================================================== */
646 /* Append a new node filled with its data at the end of the linked list */
647 /* The user is responsible for the memory management of the data. */
648 /* ==================================================================== */
650 ll_append(ll_t
* const list
, void * const data
)
654 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
655 | uses xmalloc which does not return if there *
656 | is an allocation error. */
661 node
->prev
= list
->tail
;
663 list
->tail
->next
= node
;
672 /* ================================================================== */
673 /* Put a new node filled with its data at the beginning of the linked */
675 /* The user is responsible for the memory management of the data. */
676 /* ================================================================== */
678 ll_prepend(ll_t
* const list
, void * const data
)
682 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
683 | uses xmalloc which does not return if there *
684 | is an allocation error. */
689 node
->next
= list
->head
;
691 list
->head
->prev
= node
;
700 /* ======================================================== */
701 /* Insert a new node before the specified node in the list. */
702 /* ======================================================== */
704 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
)
706 ll_node_t
* new_node
;
708 if (node
->prev
== NULL
)
709 ll_prepend(list
, data
);
712 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
713 | uses xmalloc which does not return if there *
714 | is an allocation error. */
716 new_node
->data
= data
;
717 new_node
->next
= node
;
718 new_node
->prev
= node
->prev
;
719 node
->prev
->next
= new_node
;
720 node
->prev
= new_node
;
726 /* ======================================================= */
727 /* Insert a new node after the specified node in the list. */
728 /* ======================================================= */
730 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
)
732 ll_node_t
* new_node
;
734 if (node
->next
== NULL
)
735 ll_append(list
, data
);
738 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
739 | uses xmalloc which does not return if there *
740 | is an allocation error. */
742 new_node
->data
= data
;
743 new_node
->prev
= node
;
744 new_node
->next
= node
->next
;
745 node
->next
->prev
= new_node
;
746 node
->next
= new_node
;
752 /* ================================================================= */
753 /* Remove a node from a linked list */
754 /* The memory taken by the deleted node must be freed by the caller. */
755 /* ================================================================= */
757 ll_delete(ll_t
* const list
, ll_node_t
* node
)
759 if (list
->head
== list
->tail
)
761 if (list
->head
!= NULL
)
762 list
->head
= list
->tail
= NULL
;
766 else if (node
->prev
== NULL
)
768 list
->head
= node
->next
;
769 list
->head
->prev
= NULL
;
771 else if (node
->next
== NULL
)
773 list
->tail
= node
->prev
;
774 list
->tail
->next
= NULL
;
778 node
->next
->prev
= node
->prev
;
779 node
->prev
->next
= node
->next
;
789 #if 0 /* Unused yet */
790 /* ======================================================================== */
791 /* Find a node in the list containing data. Return the node pointer or NULL */
793 /* A comparison function must be provided to compare a and b (strcmp like). */
794 /* ======================================================================== */
796 ll_find(ll_t
* const list
, void * const data
,
797 int (*cmpfunc
)(const void * a
, const void * b
))
801 if (NULL
== (node
= list
->head
))
806 if (0 == cmpfunc(node
->data
, data
))
808 } while (NULL
!= (node
= node
->next
));
814 /* ==================================================================== */
815 /* Allocate and fill an array of strings from a list. */
817 /* 1) The list node must contain strings (char *) */
818 /* 2) The strings in the resulting array MUST NOT be freed as the are */
819 /* NOT copied from the strings of the list. */
821 /* IN list : The list from which the array is generated */
822 /* IN start_node : The node of the list which will be the first node to */
823 /* consider to create the array */
824 /* OUT: count : The number of elements of the resulting array. */
825 /* OUT: array : The resulting array or NULL if the list is empty. */
826 /* RC : : The number of elements of the resulting array. */
827 /* ==================================================================== */
829 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
)
838 if (list
== NULL
|| node
== NULL
)
845 *array
= xmalloc((list
->len
+ 1) * sizeof(char *));
848 (*array
)[n
++] = (char *)(node
->data
);
854 (*array
)[*count
] = NULL
;
859 /* ******************************************************************* */
860 /* BST (search.h compatible) implementation. */
862 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
863 /* the AT&T man page says. */
865 /* Written by reading the System V Interface Definition, not the code. */
867 /* Totally public domain. */
868 /* ******************************************************************* */
873 struct bst_s
* llink
;
874 struct bst_s
* rlink
;
877 #if 0 /* Unused yet */
878 /* =========================== */
879 /* Delete node with given key. */
880 /* =========================== */
882 bst_delete(const void * vkey
, void ** vrootp
,
883 int (*compar
)(const void *, const void *))
885 bst_t
** rootp
= (bst_t
**)vrootp
;
889 if (rootp
== NULL
|| (p
= *rootp
) == NULL
)
892 while ((cmp
= (*compar
)(vkey
, (*rootp
)->key
)) != 0)
895 rootp
= (cmp
< 0) ? &(*rootp
)->llink
/* follow llink branch */
896 : &(*rootp
)->rlink
; /* follow rlink branch */
898 return NULL
; /* key not found */
900 r
= (*rootp
)->rlink
; /* D1: */
901 if ((q
= (*rootp
)->llink
) == NULL
) /* Left NULL? */
904 { /* Right link is NULL? */
905 if (r
->llink
== NULL
)
906 { /* D2: Find successor */
911 { /* D3: Find NULL link */
912 for (q
= r
->llink
; q
->llink
!= NULL
; q
= r
->llink
)
915 q
->llink
= (*rootp
)->llink
;
916 q
->rlink
= (*rootp
)->rlink
;
920 free(*rootp
); /* D4: Free node */
921 *rootp
= q
; /* link parent to new node */
926 /* ===================================================================== */
927 /* Destroy a tree. */
928 /* The clean function pointer can be NULL, in this case the node content */
930 /* ===================================================================== */
932 bst_destroy(void * vrootp
, void (*clean
)(void *))
934 bst_t
* root
= (bst_t
*)vrootp
;
939 bst_destroy(root
->llink
, clean
);
940 bst_destroy(root
->rlink
, clean
);
943 clean((void *)root
->key
);
948 /* ========================= */
949 /* Find a node, or return 0. */
950 /* ========================= */
952 bst_find(const void * vkey
, void * const * vrootp
,
953 int (*compar
)(const void *, const void *))
955 bst_t
* const * rootp
= (bst_t
* const *)vrootp
;
960 while (*rootp
!= NULL
)
964 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
965 return *rootp
; /* key found */
966 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
967 : &(*rootp
)->rlink
; /* T4: follow right branch */
972 /* ======================================= */
973 /* Find or inserts datum into search tree. */
974 /* ======================================= */
976 bst_search(void * vkey
, void ** vrootp
,
977 int (*compar
)(const void *, const void *))
980 bst_t
** rootp
= (bst_t
**)vrootp
;
985 while (*rootp
!= NULL
)
989 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
990 return *rootp
; /* we found it! */
992 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
993 : &(*rootp
)->rlink
; /* T4: follow right branch */
996 q
= xmalloc(sizeof(bst_t
)); /* T5: key not found */
998 { /* make new node */
999 *rootp
= q
; /* link new node to old */
1000 q
->key
= vkey
; /* initialize new node */
1001 q
->llink
= q
->rlink
= NULL
;
1006 /* ========================= */
1007 /* Walk the nodes of a tree. */
1008 /* ========================= */
1010 bst_walk_recurse(const bst_t
* root
,
1011 void (*action
)(const void *, walk_order_e
, int), int level
)
1013 if (root
->llink
== NULL
&& root
->rlink
== NULL
)
1014 (*action
)(root
, leaf
, level
);
1017 (*action
)(root
, preorder
, level
);
1018 if (root
->llink
!= NULL
)
1019 bst_walk_recurse(root
->llink
, action
, level
+ 1);
1020 (*action
)(root
, postorder
, level
);
1021 if (root
->rlink
!= NULL
)
1022 bst_walk_recurse(root
->rlink
, action
, level
+ 1);
1023 (*action
)(root
, endorder
, level
);
1028 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int))
1030 if (vroot
!= NULL
&& action
!= NULL
)
1031 bst_walk_recurse(vroot
, action
, 0);
1034 /* ************************ */
1035 /* Various implementations. */
1036 /* ************************ */
1038 /* ======================== */
1039 /* Trim leading characters. */
1040 /* ======================== */
1042 ltrim(char * str
, const char * trim_str
)
1044 size_t len
= strlen(str
);
1045 size_t begin
= strspn(str
, trim_str
);
1049 for (i
= begin
; i
<= len
; ++i
)
1050 str
[i
- begin
] = str
[i
];
1053 /* ================================================= */
1054 /* Trim trailing characters. */
1055 /* The resulting string will have at least min bytes */
1056 /* even if trailing spaces remain. */
1057 /* ================================================= */
1059 rtrim(char * str
, const char * trim_str
, size_t min
)
1061 size_t len
= strlen(str
);
1062 while (len
> min
&& strchr(trim_str
, str
[len
- 1]))
1066 /* ================================================== */
1067 /* Count the number of occurrences of the character c */
1068 /* in the string str. */
1069 /* The str pointer is assumed to be not NULL. */
1070 /* ================================================== */
1072 strchrcount(char * str
, char c
)
1083 /* =============================================== */
1084 /* Is the string str2 a prefix of the string str1? */
1085 /* =============================================== */
1087 strpref(char * str1
, char * str2
)
1089 while (*str1
!= '\0' && *str1
== *str2
)
1095 return *str2
== '\0';
1098 /* ========================== */
1099 /* Like strcmp ignoring case. */
1100 /* ========================== */
1102 stricmp(const char * s1
, const char * s2
)
1104 while (tolower((unsigned char)*s1
) == tolower((unsigned char)*s2
))
1113 return (int)tolower((unsigned char)*s1
) - (int)tolower((unsigned char)*s2
);
1116 /* ======================================================================== */
1117 /* Strings concatenation with dynamic memory allocation. */
1118 /* IN : a variable number of char * arguments with NULL terminating */
1120 /* The first one must have been dynamically allocated and is mandatory */
1122 /* Returns a new allocated string containing the concatenation of all */
1123 /* the arguments. It is the caller's responsibility to free the resulting */
1125 /* ======================================================================== */
1127 strappend(char * str
, ...)
1133 l
= 1 + strlen(str
);
1134 va_start(args
, str
);
1136 s
= va_arg(args
, char *);
1141 s
= va_arg(args
, char *);
1146 str
= xrealloc(str
, l
);
1148 va_start(args
, str
);
1149 s
= va_arg(args
, char *);
1154 s
= va_arg(args
, char *);
1161 /* ====================================================================== */
1162 /* Public domain strtok_r() by Charlie Gordon. */
1163 /* from comp.lang.c 9/14/2007 */
1164 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1166 /* (Declaration that it's public domain): */
1167 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1169 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1171 /* ====================================================================== */
1173 xstrtok_r(char * str
, const char * delim
, char ** end
)
1183 str
+= strspn(str
, delim
);
1190 str
+= strcspn(str
, delim
);
1200 /* ===================================================================== */
1201 /* Put the first word of str, truncated to len characters, in buf. */
1202 /* Return a pointer in str pointing just after the word. */
1203 /* buf must have been pre-allocated to accept at least len+1 characters. */
1204 /* Note that buf can contains a sting full of spaces is str was not */
1205 /* trimmed before the call. */
1206 /* ===================================================================== */
1208 get_word(char * str
, char * buf
, size_t len
)
1214 while (*s
&& isspace(*s
))
1217 /* Set the new string start. */
1218 /* """"""""""""""""""""""""" */
1223 while (*s
&& !isspace(*s
) && s
- str
< len
)
1226 strncpy(buf
, str
, s
- str
);
1232 /* ==================================================================== */
1233 /* Return 1 is value is "1" or "yes" (ignoring case). */
1234 /* Return 0 is value is "0" or "no" (ignoring case). */
1235 /* If value has another value, then set invalid to 1 and also return 0 */
1236 /* invalid is set to 0i in all the other cases. */
1237 /* ==================================================================== */
1239 eval_yes(char * value
, int * invalid
)
1243 if (strcmp(value
, "1") == 0 || stricmp(value
, "yes") == 0)
1245 else if (strcmp(value
, "0") != 0 && stricmp(value
, "no") != 0)
1251 /* =========================================================== */
1252 /* Fill an array of strings from the words composing a string. */
1254 /* str: initial string which will be altered. */
1255 /* args: array of pointers to the start of the words in str. */
1256 /* max: maximum number of words used before giving up. */
1257 /* return: the number of words (<=max). */
1258 /* =========================================================== */
1260 str2argv(char * str
, char ** args
, int max
)
1269 while (*str
== ' ' || *str
== '\t')
1275 args
[nb_args
] = str
;
1278 while (*str
&& (*str
!= ' ') && (*str
!= '\t'))
1285 /* ********************** */
1286 /* ctxopt implementation. */
1287 /* ********************** */
1289 static int ctxopt_initialized
= 0; /* cap_init has not yet been called */
1291 /* Flags structure initialized by ctxopt_init. */
1292 /* """"""""""""""""""""""""""""""""""""""""""" */
1295 int stop_if_non_option
;
1296 int allow_abbreviations
;
1299 /* Context structure. */
1300 /* """""""""""""""""" */
1304 ll_t
* opt_list
; /* list of options allowed in this context. */
1305 ll_t
* incomp_list
; /* list of strings containing incompatible names *
1306 | of options separated by spaces or tabs. */
1307 int (*action
)(char * name
, int type
, char * new_ctx
, int ctx_nb_data
,
1314 /* https://textik.com/#488ce3649b6c60f5 */
1316 /* +--------------+ */
1317 /* |first_ctx_inst| */
1318 /* +---+----------+ */
1320 /* +--v-----+ +--------+ +--------+ +-----+ */
1321 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1322 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1324 /* | | +-v------+ | | */
1325 /* | +--+ctx_inst<-----------+ | */
1326 /* | +-+------+ | */
1328 /* | +-v------+ | */
1329 /* +------+ctx_inst<--------------------------+ */
1336 /* Option structure. */
1337 /* """"""""""""""""" */
1340 char * name
; /* option name. */
1341 char * next_ctx
; /* new context this option may lead to */
1342 ll_t
* ctx_list
; /* list of contexts allowing this option. */
1343 char * params
; /* string containing all the parameters of *
1346 void (*action
)( /* The option associated action. */
1347 char * ctx_name
, /* context name. */
1348 char * opt_name
, /* option name. */
1349 char * par
, /* option parameter. */
1350 int nb_args
, /* number of arguments. */
1351 char ** args
, /* option arguments. */
1352 int nb_opt_data
, /* number of option data pointers. */
1353 void ** opt_data
, /* option data pointers. */
1354 int nb_ctx_data
, /* nb of current context data ptrs. */
1355 void ** ctx_data
/* current context data pointers. */
1358 int nb_data
; /* number of the data pointers passed as argument to action. */
1359 void ** data
; /* array of data pointers passed as argument to action. */
1361 int args
; /* 1 if this option takes arguments else 0. */
1362 int optional
; /* 1 if the option is optional, else 0. */
1363 int multiple
; /* 1 if the option can appear more than one time in a *
1364 | context, else 0. */
1366 int opt_count_matter
; /* 1 if we must restrict the count, else 0. */
1367 int occurrences
; /* Number of option occurrences in a context. */
1368 char opt_count_oper
; /* <, = or > */
1369 unsigned opt_count_mark
; /* Value to be compared to with opt_count_oper. */
1371 char * arg
; /* symbolic text after # describing the option argument. */
1373 int optional_args
; /* 1 of option is optional else 0. */
1374 int multiple_args
; /* 1 is option can appear more than once in a context *
1377 int opt_args_count_matter
; /* 1 if count is rescticted, else 0. */
1378 char opt_args_count_oper
; /* <, = or > */
1379 unsigned opt_args_count_mark
; /* Value to be compared to with *
1380 | opt_count_oper. */
1382 int eval_first
; /* 1 if this option must be evaluated before the options *
1383 | without this mark. */
1385 ll_t
* eval_before_list
; /* List of pointers on options which must be *
1386 | evaluated before this option. */
1388 ll_t
* constraints_list
; /* List of constraint check functions pointers. */
1391 /* Context instance structure. */
1392 /* """"""""""""""""""""""""""" */
1395 ctx_t
* ctx
; /* the context whose this is an instance of */
1396 ctx_inst_t
* prev_ctx_inst
; /* ctx_inst of the opt_inst which led to the *
1397 | creation of this ctx_inst structure. */
1398 opt_inst_t
* gen_opt_inst
; /* opt_inst which led to the creation of a *
1399 | instance of this structure. */
1400 ll_t
* incomp_bst_list
; /* list of seen_opt_t BST. */
1401 void * seen_opt_bst
; /* tree of seen_opt_t. */
1402 ll_t
* opt_inst_list
; /* The list of option instances in this *
1403 | context instance. */
1404 char * par_name
; /* parameter which created this instance. */
1407 /* Option instance structure. */
1408 /* """""""""""""""""""""""""" */
1411 opt_t
* opt
; /* The option this is an instance of. */
1412 char * opt_name
; /* The option which led to this creation. */
1413 char * par
; /* The parameter which led to this creation. */
1414 ll_t
* values_list
; /* The list of arguments of this option. */
1415 ctx_inst_t
* next_ctx_inst
; /* The new context instance this option. *
1416 | instance may create. */
1419 /* Structure used to check if an option has bee seen or not */
1420 /* in a context instance. */
1421 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1424 opt_t
* opt
; /* The concerned option. */
1425 char * par
; /* Parameter which led to the making of this structure. */
1426 int seen
; /* 1 if seen in the context instances, else 0. */
1429 /* Parameter structure which links a parameter to the option it belongs to. */
1430 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1433 char * name
; /* Parameter name (with the leading -). */
1434 opt_t
* opt
; /* Attached option. */
1437 /* Constraint structure. */
1438 /* """"""""""""""""""""" */
1441 int (*constraint
)(int nb_args
, char ** args
, char * value
, char * parameter
);
1444 char * to_free
; /* pointer to the original string in which the array in *
1445 | args points to. This poinnter is kept there to allow *
1446 | it to be freed. */
1449 state_t
* cur_state
= NULL
; /* Current analysis state. */
1450 static ll_t
* cmdline_list
; /* List of interpreted CLI words *
1451 | serves as the basis for the *
1452 | analysis of the parameters. */
1453 static ctx_t
* main_ctx
= NULL
; /* initial context. */
1454 static ctx_inst_t
* first_ctx_inst
= NULL
; /* Pointer to the fist context *
1455 | instance which holds the *
1456 | options instances. */
1457 static ll_t
* ctx_inst_list
= NULL
; /* List of the context instances. */
1459 static flags_t flags
= { 0, 1 };
1461 /* ======================================================= */
1462 /* Parse a string for the next matching token. */
1464 /* s: string to parse. */
1465 /* token: pre_allocated array of max tok_len characters. */
1466 /* pattern: scanf type pattern token must match. */
1467 /* pos: number of characters successfully parsed in s. */
1469 /* Returns: a pointer to the first unread character or */
1470 /* to he terminating \0. */
1471 /* ======================================================= */
1473 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
)
1475 char * full_pattern
;
1481 n
= snprintf(len
, 3, "%zu", tok_len
);
1485 full_pattern
= xmalloc(strlen(pattern
) + n
+ 4);
1487 strcpy(full_pattern
, "%");
1488 strcat(full_pattern
, len
);
1489 strcat(full_pattern
, pattern
);
1490 strcat(full_pattern
, "%n");
1492 n
= sscanf(s
, full_pattern
, token
, pos
);
1502 /* ****************************************** */
1503 /* Various comparison and deletion functions. */
1504 /* ****************************************** */
1507 ctx_compare(const void * c1
, const void * c2
)
1509 return strcmp(((ctx_t
*)c1
)->name
, ((ctx_t
*)c2
)->name
);
1512 /* =========================== */
1513 /* Free a context_bst element. */
1514 /* =========================== */
1523 ll_destroy(ctx
->opt_list
, NULL
);
1524 ll_destroy(ctx
->incomp_list
, free
);
1525 bst_destroy(ctx
->par_bst
, par_free
);
1530 /* ============================= */
1531 /* Free a ctx_inst_list element. */
1532 /* ============================= */
1534 ctx_inst_free(void * ci
)
1536 ctx_inst_t
* ctx_inst
= ci
;
1538 free(ctx_inst
->par_name
);
1539 ll_destroy(ctx_inst
->incomp_bst_list
, incomp_bst_free
);
1540 bst_destroy(ctx_inst
->seen_opt_bst
, seen_opt_free
);
1541 ll_destroy(ctx_inst
->opt_inst_list
, opt_inst_free
);
1546 /* ============================= */
1547 /* Free a opt_inst_list element. */
1548 /* ============================= */
1550 opt_inst_free(void * oi
)
1552 opt_inst_t
* opt_inst
= oi
;
1554 ll_destroy(opt_inst
->values_list
, NULL
);
1559 /* ================================== */
1560 /* Compare two seen_opt_bst elements. */
1561 /* ================================== */
1563 seen_opt_compare(const void * so1
, const void * so2
)
1567 o1
= ((seen_opt_t
*)so1
)->opt
;
1568 o2
= ((seen_opt_t
*)so2
)->opt
;
1570 return strcmp(o1
->name
, o2
->name
);
1573 /* ============================ */
1574 /* Free a seen_opt_bst element. */
1575 /* ============================ */
1577 seen_opt_free(void * so
)
1579 seen_opt_t
* seen_opt
= so
;
1581 free(seen_opt
->par
);
1586 /* =========================== */
1587 /* Free an incomp_bst element. */
1588 /* =========================== */
1590 incomp_bst_free(void * b
)
1594 bst_destroy(bst
, NULL
);
1597 /* ================================= */
1598 /* Compare two options_bst elements. */
1599 /* ================================= */
1601 opt_compare(const void * o1
, const void * o2
)
1603 return strcmp(((opt_t
*)o1
)->name
, ((opt_t
*)o2
)->name
);
1606 /* ============================= */
1607 /* Free an options_bst elements. */
1608 /* ============================= */
1615 free(opt
->next_ctx
);
1620 ll_destroy(opt
->ctx_list
, NULL
);
1621 ll_destroy(opt
->constraints_list
, constraint_free
);
1626 /* ============================= */
1627 /* Compare two par_bst elements. */
1628 /* ============================= */
1630 par_compare(const void * a1
, const void * a2
)
1632 return strcmp(((par_t
*)a1
)->name
, ((par_t
*)a2
)->name
);
1635 /* ======================= */
1636 /* Free a par_bst element. */
1637 /* ======================= */
1648 /* ================================ */
1649 /* Free a constraints_list element. */
1650 /* ================================ */
1652 constraint_free(void * c
)
1654 constraint_t
* cstr
= c
;
1657 free(cstr
->to_free
);
1662 /* ******************************************************************** */
1663 /* Helper functions to locate contexts, options and parameters in a BST */
1664 /* by their names. */
1665 /* ******************************************************************** */
1668 locate_ctx(char * name
)
1675 if ((node
= bst_find(&ctx
, &contexts_bst
, ctx_compare
)) == NULL
)
1682 locate_opt(char * name
)
1689 if ((node
= bst_find(&opt
, &options_bst
, opt_compare
)) == NULL
)
1696 locate_par(char * name
, ctx_t
* ctx
)
1700 void * bst
= ctx
->par_bst
;
1704 if ((node
= bst_find(&par
, &bst
, par_compare
)) == NULL
)
1710 /* =================================================================== */
1711 /* Utility function to format and print the options present in a list. */
1713 /* IN list : a list of options. */
1714 /* OUT has_* : a set of flags which will determine the content of the */
1715 /* explanation given after the formatted printing of the */
1717 /* =================================================================== */
1719 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
1720 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
1721 int * has_early_eval
)
1723 ll_node_t
* node
= list
->head
;
1728 line
= xstrdup(" ");
1730 while (node
!= NULL
)
1732 option
= xstrdup("");
1737 option
= strappend(option
, "[", NULL
);
1741 if (opt
->eval_first
)
1743 option
= strappend(option
, "*", NULL
);
1744 *has_early_eval
= 1;
1747 option
= strappend(option
, opt
->params
, NULL
);
1749 if (opt
->next_ctx
!= NULL
)
1751 option
= strappend(option
, ">", opt
->next_ctx
, NULL
);
1752 *has_ctx_change
= 1;
1757 if (opt
->opt_count_oper
!= '\0')
1761 o
[0] = opt
->opt_count_oper
;
1763 snprintf(m
, 3, "%u", opt
->opt_count_mark
);
1764 option
= strappend(option
, "...", o
, m
, NULL
);
1768 option
= strappend(option
, "...", NULL
);
1775 if (*(opt
->arg
) == '#')
1776 *has_generic_arg
= 1;
1778 option
= strappend(option
, " ", NULL
);
1780 if (opt
->optional_args
)
1782 option
= strappend(option
, "[", opt
->arg
, NULL
);
1786 option
= strappend(option
, opt
->arg
, NULL
);
1788 if (opt
->multiple_args
)
1790 if (opt
->opt_args_count_oper
!= '\0')
1794 o
[0] = opt
->opt_args_count_oper
;
1796 snprintf(m
, 3, "%u", opt
->opt_args_count_mark
);
1797 option
= strappend(option
, "...", o
, m
, NULL
);
1801 option
= strappend(option
, "...", NULL
);
1805 if (opt
->optional_args
)
1806 option
= strappend(option
, "]", NULL
);
1809 option
= strappend(option
, "]", NULL
);
1811 if (strlen(line
) + 1 + strlen(option
) < 80)
1812 line
= strappend(line
, option
, " ", NULL
);
1815 printf("%s\n", line
);
1817 line
= strappend(line
, option
, " ", NULL
);
1825 printf("%s\n", line
);
1830 /* ==================================================== */
1831 /* Explain the special syntactic symbols present in the */
1832 /* generated usage messages. */
1833 /* ==================================================== */
1835 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
1836 int has_optional
, int has_ellipsis
, int has_rule
)
1838 if (has_early_eval
|| has_ctx_change
|| has_generic_arg
|| has_optional
1839 || has_ellipsis
|| has_rule
)
1841 printf("\nExplanation of the syntax used above:\n");
1842 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1843 "must be entered.\n");
1844 printf("The following is just there to explain the other symbols "
1848 printf("* : the parameters for this option will be "
1849 "evaluated first.\n");
1852 "> : The context after this symbol will become the next "
1854 if (has_generic_arg
)
1855 printf("#tag : argument tag giving a clue to its meaning.\n");
1858 "[...] : the object between square brackets is optional.\n");
1860 printf("... : several occurrences of the previous object "
1863 printf("[<|=|>]number: rules constraining the number of "
1864 "parameters/arguments.\n");
1868 /* ************************************************************ */
1869 /* Various utilities and callback functions called when walking */
1870 /* through a BST. */
1871 /* ************************************************************ */
1874 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
)
1876 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1878 if (kind
== postorder
|| kind
== leaf
)
1880 if ((!seen_opt
->opt
->optional
) && seen_opt
->seen
== 0)
1883 user_string
= strappend(user_string
, seen_opt
->opt
->params
, " ", NULL
);
1889 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
)
1891 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1893 if (kind
== postorder
|| kind
== leaf
)
1894 if (seen_opt
->seen
== 1)
1897 user_object
= seen_opt
->par
;
1902 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
)
1904 ctx_t
* ctx
= main_ctx
;
1905 ctx_t
* cur_ctx
= ((bst_t
*)node
)->key
;
1909 int has_optional
= 0;
1910 int has_ellipsis
= 0;
1912 int has_generic_arg
= 0;
1913 int has_ctx_change
= 0;
1914 int has_early_eval
= 0;
1916 if (kind
== postorder
|| kind
== leaf
)
1917 if (strcmp(ctx
->name
, cur_ctx
->name
) != 0)
1919 list
= cur_ctx
->opt_list
;
1921 printf("\nAllowed options in the context %s:\n", cur_ctx
->name
);
1922 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
,
1923 &has_generic_arg
, &has_ctx_change
, &has_early_eval
);
1928 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
)
1930 opt_t
* opt
= ((bst_t
*)node
)->key
;
1932 if (kind
== postorder
|| kind
== leaf
)
1934 if (opt
->params
== NULL
) /* opt must have associated parameters. */
1935 fatal_internal("Option %s has no registered parameter.\n", opt
->name
);
1937 if (opt
->action
== NULL
) /* opt must have an action. */
1938 fatal_internal("Option %s has no registered action.\n", opt
->name
);
1943 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
)
1945 ctx_t
* ctx
= ((bst_t
*)node
)->key
;
1947 if (kind
== postorder
|| kind
== leaf
)
1949 char * str
= xstrdup(user_string
);
1951 while (*str
!= '\0')
1953 if (locate_par(str
, ctx
) != NULL
)
1955 user_string2
= strappend(user_string2
, " ", ctx
->name
, NULL
);
1958 str
[strlen(str
) - 1] = '\0';
1965 match_prefix_cb(const void * node
, walk_order_e kind
, int level
)
1967 par_t
* par
= ((bst_t
*)node
)->key
;
1969 if (kind
== postorder
|| kind
== leaf
)
1970 if (strpref(par
->name
, (char *)user_object
))
1973 user_string
= strappend(user_string
, par
->name
, " ", NULL
);
1977 /* ====================================================================== */
1978 /* A parameter may not be separated from its first option by spaces, in */
1979 /* this case this function looks for a valid flag as a prefix and splits */
1980 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1983 /* IN word : the word to be checked. */
1984 /* IN ctx : the context in which the flag indexed by the word is to be */
1986 /* OUT pos : the offset in word pointing just after the matching prefix. */
1987 /* OUT opt : a pointer to the option associated with the new parameter */
1988 /* or NULL if none is found. */
1990 /* The returned pointer must be freed by the caller. */
1991 /* ====================================================================== */
1993 look_for_valid_prefix_in_word(char * word
, ctx_t
* ctx
, int * pos
, opt_t
** opt
)
1998 par_t tmp_par
= { 0 };
2004 new = xstrdup(word
);
2010 } while ((par
= locate_par(tmp_par
.name
, ctx
)) == NULL
&& len
> 2);
2029 /* ============================================================= */
2030 /* If par_name is an unique abbreviation of an exiting parameter */
2031 /* in the context ctx, then return this parameter. */
2032 /* ============================================================= */
2034 abbrev_expand(char * par_name
, ctx_t
* ctx
)
2036 user_object
= par_name
;
2039 *user_string
= '\0';
2040 bst_walk(ctx
->par_bst
, match_prefix_cb
);
2041 rtrim(user_string
, " ", 0);
2043 /* The previous bst_walk has built a string of blank separated parameters */
2044 /* all having par_name as prefix. This string is put in the user_string */
2045 /* exchange zone. The number of these words in put in user_rc. */
2046 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2047 if (user_rc
== 1) /* The number of matching abbreviations. */
2048 return xstrdup(user_string
);
2049 else /* There is at least tho defined parameters starting with par_name. */
2055 void * tmp_opt_bst
= NULL
;
2057 /* Find all the options corresponding to these words and store them */
2058 /* without duplication in a temporary BST. Only their resulting count */
2060 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2061 s
= first_s
= strtok(user_string
, " "); /* first_s holds a copy of *
2062 | the first word. */
2065 par
= locate_par(s
, ctx
);
2068 if (bst_find(opt
, &tmp_opt_bst
, opt_compare
) == NULL
)
2070 /* This option as not already been seen */
2071 /* store it and increase the seen counter. */
2072 /* """"""""""""""""""""""""""""""""""""""" */
2073 bst_search(opt
, &tmp_opt_bst
, opt_compare
);
2076 s
= strtok(NULL
, " ");
2079 /* Clean the temporary BST without removing the pointer */
2080 /* to the real options. */
2081 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2082 if (tmp_opt_bst
!= NULL
)
2083 bst_destroy(tmp_opt_bst
, NULL
);
2086 /* All the abbreviation are leading to only one option */
2087 /* We can just continue as in the previous case. */
2088 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2089 return xstrdup(first_s
);
2095 /* ================================================================ */
2096 /* Terminate the program if mandatory options required by a context */
2097 /* are not present. */
2098 /* ================================================================ */
2100 check_for_missing_mandatory_opt(ctx_inst_t
* ctx_inst
, char * opt_par
)
2104 if (has_unseen_mandatory_opt(ctx_inst
, &missing
))
2105 fatal(CTXOPTMISPAR
, missing
);
2108 /* ====================================================== */
2109 /* Return 1 if at least one mandatory option was not seen */
2110 /* when quitting a context, else 0. */
2111 /* ====================================================== */
2113 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
)
2116 *user_string
= '\0';
2118 bst_walk(ctx_inst
->seen_opt_bst
, bst_seen_opt_cb
);
2119 rtrim(user_string
, " ", 0);
2121 *missing
= user_string
;
2123 return user_rc
? 1 : 0;
2126 /* ========================================================================= */
2127 /* This function terminates the program if an option or its arguments do not */
2128 /* conform to its occurrences constraint. */
2129 /* There constraints can appear by trailing >, < or = in their definition */
2130 /* given in ctxopt_new_ctx. */
2131 /* ========================================================================= */
2133 check_for_occurrences_issues(ctx_inst_t
* ctx_inst
)
2135 ctx_t
* ctx
= ctx_inst
->ctx
;
2138 opt_inst_t
* opt_inst
;
2139 char * cur_opt_params
= cur_state
->cur_opt_params
;
2140 char * cur_opt_par_name
= cur_state
->cur_opt_par_name
;
2142 /* Checks options. */
2143 /* """"""""""""""" */
2144 node
= ctx
->opt_list
->head
;
2146 while (node
!= NULL
)
2150 /* Update current_state. */
2151 /* """"""""""""""""""""" */
2152 cur_state
->cur_opt_params
= opt
->params
;
2153 cur_state
->opts_count
= opt
->opt_count_mark
;
2154 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2156 if (opt
->opt_count_matter
)
2157 switch (opt
->opt_count_oper
)
2160 if (opt
->occurrences
> 0 && opt
->opt_count_mark
!= opt
->occurrences
)
2161 fatal(CTXOPTCTEOPT
, NULL
);
2165 if (opt
->occurrences
> 0 && opt
->opt_count_mark
<= opt
->occurrences
)
2166 fatal(CTXOPTCTLOPT
, NULL
);
2170 if (opt
->occurrences
> 0 && opt
->opt_count_mark
>= opt
->occurrences
)
2171 fatal(CTXOPTCTGOPT
, NULL
);
2178 /* Checks arguments. */
2179 /* """"""""""""""""" */
2180 node
= ctx_inst
->opt_inst_list
->head
;
2181 while (node
!= NULL
)
2183 opt_inst
= node
->data
;
2184 opt
= opt_inst
->opt
;
2186 /* Update current_state. */
2187 /* """"""""""""""""""""" */
2188 cur_state
->cur_opt_par_name
= opt_inst
->par
;
2189 cur_state
->opts_count
= opt
->opt_count_mark
;
2190 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2192 int nb_values
= opt_inst
->values_list
->len
; /* Number of arguments of opt */
2194 if (opt
->opt_args_count_matter
)
2195 switch (opt
->opt_args_count_oper
)
2198 if (nb_values
> 0 && opt
->opt_args_count_mark
!= nb_values
)
2199 fatal(CTXOPTCTEARG
, NULL
);
2203 if (nb_values
> 0 && opt
->opt_args_count_mark
<= nb_values
)
2204 fatal(CTXOPTCTLARG
, NULL
);
2208 if (nb_values
> 0 && opt
->opt_args_count_mark
>= nb_values
)
2209 fatal(CTXOPTCTGARG
, NULL
);
2215 cur_state
->cur_opt_params
= cur_opt_params
;
2216 cur_state
->cur_opt_par_name
= cur_opt_par_name
;
2219 /* ======================================================================== */
2220 /* Parse a strings describing options and some of their characteristics */
2221 /* The input string must have follow some rules like in the examples below: */
2223 /* "opt_name1 opt_name2" */
2224 /* "[opt_name1] opt_name2" */
2225 /* "[opt_name1] opt_name2..." */
2226 /* "[opt_name1 #...] opt_name2... [#]" */
2227 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2229 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2230 /* means that there can be more than one occurrence of the previous thing. */
2232 /* opt_name can be followed by a 'new context' change prefixed with the */
2233 /* symbol >, as in opt1>c2 by eg. */
2235 /* This function returns as soon as one (or no) option has been parsed and */
2236 /* return the offset to the next option to parse. */
2238 /* In case of successful parsing, an new option is allocated and its */
2239 /* pointer returned. */
2240 /* ======================================================================== */
2242 opt_parse(char * s
, opt_t
** opt
)
2244 int opt_optional
= 0;
2245 int opt_multiple
= 0;
2246 int opt_count_matter
= 0;
2247 char opt_count_oper
= '\0';
2248 unsigned opt_count_mark
= 0;
2250 char opt_arg
[33] = { 0 };
2251 int opt_multiple_args
= 0;
2252 int opt_args_count_matter
= 0;
2253 char opt_args_count_oper
= '\0';
2254 unsigned opt_args_count_mark
= 0;
2255 int opt_optional_args
= 0;
2256 int opt_eval_first
= 0;
2270 memset(opt_arg
, '\0', 33);
2272 /* Strip the leading blanks. */
2273 /* """"""""""""""""""""""""" */
2277 if (*s
== '[') /* Start of an optional option. */
2282 s
= strtoken(s
, token
, sizeof(token
) - 1, "[^] \n\t.]", &pos
);
2284 return -1; /* Empty string. */
2286 /* Early EOS, only return success if the option is mandatory. */
2287 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2289 if (opt_optional
== 1)
2290 return -(s
- s_orig
- 1);
2292 /* Validate the option name */
2293 /* ALPHA+(ALPHANUM|_)* */
2294 /* """""""""""""""""""""""" */
2296 if (!isalpha(*p
) && *p
!= '*')
2297 return -(s
- s_orig
- 1); /* opt_name must start with a letter. */
2305 if (!isalnum(*p
) && *p
!= '_' && *p
!= '>')
2306 return -(s
- s_orig
- 1); /* opt_name must contain a letter, *
2307 * a number or a _ */
2312 opt_name
= xstrdup(token
+ 1); /* Ignore the first '*' in token. */
2314 opt_name
= xstrdup(token
);
2325 /* Check if it can appear multiple times by looking for the dots. */
2326 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2327 p
= strtoken(s
, token
, 3, "[.]", &pos
);
2330 if (strcmp(token
, "...") == 0)
2334 if (*s
== '<' || *s
== '=' || *s
== '>')
2339 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2342 opt_count_matter
= 1;
2343 opt_count_oper
= *s
;
2344 opt_count_mark
= value
;
2352 return -(s
- s_orig
- 1);
2358 /* Abort on extraneous ] if the option is mandatory. */
2359 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2361 return -(s
- s_orig
- 1);
2363 s
++; /* skip the ] */
2365 if (!*s
|| isblank(*s
))
2368 return -(s
- s_orig
- 1);
2371 /* A blank separates the option name and the argument tag. */
2372 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2384 n
= sscanf(s
, "[%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2385 if (pos
> 1 && *opt_arg
== '#') /* [# has been read. */
2388 opt_optional_args
= 1;
2390 opt_multiple_args
= 1; /* There were dots. */
2392 s
+= pos
+ !!(n
== 2) * 3; /* Skips the dots. */
2394 if (*s
== '<' || *s
== '=' || *s
== '>')
2399 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2402 opt_args_count_matter
= 1;
2403 opt_args_count_oper
= *s
;
2404 opt_args_count_mark
= value
;
2409 /* Optional arg tag must end with a ] */
2410 /* """""""""""""""""""""""""""""""""" */
2414 return -(s
- s_orig
- 1);
2417 s
++; /* Skip the ] */
2421 n
= sscanf(s
, "%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2422 if (pos
> 0 && *opt_arg
== '#') /* # has been read. */
2425 if (n
== 2) /* There were dots. */
2426 opt_multiple_args
= 1;
2428 s
+= pos
+ !!(n
== 2) * 3; /* Skip the dots */
2430 if (*s
== '<' || *s
== '=' || *s
== '>')
2435 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2438 opt_args_count_matter
= 1;
2439 opt_args_count_oper
= *s
;
2440 opt_args_count_mark
= value
;
2448 /* Abort on extraneous ] if the option is mandatory. */
2449 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2451 return -(s
- s_orig
- 1);
2453 s
++; /* skip the ] */
2455 /* Strip the following blanks. */
2456 /* """"""""""""""""""""""""""" */
2462 else if (opt_optional
== 0 && (!*s
|| isblank(*s
)))
2464 /* Strip the following blanks. */
2465 /* """"""""""""""""""""""""""" */
2471 else if (opt_args
== 0) /* # was not read it is possibly the start *
2472 * of another option. */
2475 return -(s
- s_orig
- 1);
2480 /* Strip the following blanks. */
2481 /* """"""""""""""""""""""""""" */
2487 if (*opt_name
== '>')
2488 fatal_internal("The option name is missing in %s.", opt_name
);
2490 count
= strchrcount(opt_name
, '>');
2493 char * tmp
= strchr(opt_name
, '>');
2494 next_ctx
= xstrdup(tmp
+ 1);
2498 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name
);
2500 *opt
= xmalloc(sizeof(opt_t
));
2502 (*opt
)->name
= opt_name
;
2503 (*opt
)->optional
= opt_optional
;
2504 (*opt
)->multiple
= opt_multiple
;
2505 (*opt
)->opt_count_matter
= opt_count_matter
;
2506 (*opt
)->opt_count_oper
= opt_count_oper
;
2507 (*opt
)->opt_count_mark
= opt_count_mark
;
2508 (*opt
)->args
= opt_args
;
2509 (*opt
)->arg
= xstrdup(opt_arg
);
2510 (*opt
)->optional_args
= opt_optional_args
;
2511 (*opt
)->multiple_args
= opt_multiple_args
;
2512 (*opt
)->opt_args_count_matter
= opt_args_count_matter
;
2513 (*opt
)->opt_args_count_oper
= opt_args_count_oper
;
2514 (*opt
)->opt_args_count_mark
= opt_args_count_mark
;
2515 (*opt
)->eval_first
= opt_eval_first
;
2516 (*opt
)->next_ctx
= next_ctx
;
2517 (*opt
)->ctx_list
= ll_new();
2518 (*opt
)->constraints_list
= ll_new();
2519 (*opt
)->eval_before_list
= ll_new();
2520 (*opt
)->action
= NULL
;
2521 (*opt
)->params
= NULL
;
2522 (*opt
)->data
= NULL
;
2527 /* ==================================================================== */
2528 /* Try to initialize all the option in a given string */
2529 /* Each parsed option are put in a BST tree with its name as index. */
2531 /* On collision, the arguments only the signature are required to be */
2532 /* the same else this is considered as an error. Options can be used in */
2533 /* more than one context and can be optional in one and mandatory in */
2535 /* ==================================================================== */
2537 init_opts(char * spec
, ctx_t
* ctx
)
2539 opt_t
* opt
, *bst_opt
;
2545 if ((offset
= opt_parse(spec
, &opt
)) > 0)
2549 if ((node
= bst_find(opt
, &options_bst
, opt_compare
)) != NULL
)
2551 int same_next_ctx
= 0;
2553 bst_opt
= node
->key
; /* Node extracted from the BST. */
2555 if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
== NULL
)
2557 else if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
!= NULL
)
2559 else if (bst_opt
->next_ctx
!= NULL
&& opt
->next_ctx
== NULL
)
2562 same_next_ctx
= strcmp(bst_opt
->next_ctx
, opt
->next_ctx
) == 0;
2564 if (bst_opt
->optional_args
!= opt
->optional_args
2565 || bst_opt
->multiple_args
!= opt
->multiple_args
2566 || bst_opt
->args
!= opt
->args
|| !same_next_ctx
)
2568 fatal_internal("The option %s already exists with "
2569 "a different arguments signature.\n",
2573 /* The newly created opt is already present in options_bst. */
2574 /* We can remove it. */
2575 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2578 /* The new occurrence of the option option is legal */
2579 /* append the current context ptr in the list. */
2580 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2581 ll_append(bst_opt
->ctx_list
, ctx
);
2583 /* Append the new option to the context's options list. */
2584 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2585 ll_append(ctx
->opt_list
, bst_opt
);
2589 /* Initialize the option's context list with the current context. */
2590 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2591 ll_append(opt
->ctx_list
, ctx
);
2593 /* Append the new option to the context's options list. */
2594 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2595 ll_append(ctx
->opt_list
, opt
);
2597 /* Insert the new option in the BST. */
2598 /* """"""""""""""""""""""""""""""""" */
2599 bst_search(opt
, &options_bst
, opt_compare
);
2604 char * s
= xstrndup(spec
, -offset
);
2605 printf("%s <---\nSyntax error at or before offset %d\n", s
, -offset
);
2615 /* ===================================================== */
2616 /* ctxopt initialization function, must be called first. */
2617 /* ===================================================== */
2619 ctxopt_init(char * prog_name
, char * init_flags
)
2623 contexts_bst
= NULL
;
2629 user_string
= xmalloc(8);
2630 user_string2
= xmalloc(8);
2632 char flag
[33], fname
[31], vname
[31];
2635 ctxopt_initialized
= 1;
2637 /* Initialize current_state.*/
2638 /* """""""""""""""""""""""" */
2639 cur_state
= xcalloc(sizeof(state_t
), 0);
2641 /* Initialize custom error function pointers to NULL. */
2642 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2643 err_functions
= xmalloc(CTXOPTERRSIZ
* sizeof(void *));
2644 for (n
= 0; n
< CTXOPTERRSIZ
; n
++)
2645 err_functions
[n
] = NULL
;
2647 /* Parse init_flags if any. */
2648 /* """""""""""""""""""""""" */
2649 while (*init_flags
&& (init_flags
= get_word(init_flags
, flag
, 32)))
2653 if (sscanf(flag
, "%30[^=]=%30[^=]", fname
, vname
) != 2)
2654 fatal_internal("Invalid flag assignment: %s.", flag
);
2656 if (strcmp(fname
, "stop_if_non_option") == 0)
2658 if (eval_yes(vname
, &invalid
))
2659 flags
.stop_if_non_option
= 1;
2661 flags
.stop_if_non_option
= 0;
2663 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2665 else if (strcmp(fname
, "allow_abbreviations") == 0)
2667 if (eval_yes(vname
, &invalid
))
2668 flags
.allow_abbreviations
= 1;
2670 flags
.allow_abbreviations
= 0;
2672 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2675 fatal_internal("Invalid flag name: %s.", fname
);
2679 /* Update current_state. */
2680 /* """"""""""""""""""""" */
2683 if (*prog_name
== '\0')
2684 cur_state
->prog_name
= xstrdup("program_name");
2685 else if ((ptr
= strrchr(prog_name
, '/')))
2686 cur_state
->prog_name
= xstrdup(ptr
+ 1);
2688 cur_state
->prog_name
= xstrdup(prog_name
);
2691 cur_state
->prog_name
= xstrdup("program_name");
2694 /* ========================================================================= */
2695 /* Utility function which create and register a par_t object in a BST */
2696 /* embedded in a context. */
2697 /* This object will have a name and a pointer to the option it refers to. */
2698 /* These object will be used to quickly find an option from a command */
2699 /* line parameter during the analysis phase. */
2701 /* IN : an option name. */
2702 /* IN : a string of command line parameters to associate to the option. */
2703 /* Returns : 1 is all was fine else 0. */
2704 /* ========================================================================= */
2706 opt_set_parms(char * opt_name
, char * par_str
)
2708 char * par_name
, *ctx_name
;
2709 char * tmp_par_str
, *end_tmp_par_str
;
2713 par_t
* par
, tmp_par
;
2714 int rc
= 1; /* return code */
2719 /* Look if the given option is defined. */
2720 /* """""""""""""""""""""""""""""""""""" */
2721 opt
= locate_opt(opt_name
);
2723 fatal_internal("Unknown option %s.", opt_name
);
2725 /* For each context using this option. */
2726 /* """"""""""""""""""""""""""""""""""" */
2727 list
= opt
->ctx_list
;
2730 while (lnode
!= NULL
)
2732 /* Locate the context in the contexts tree. */
2733 /* """""""""""""""""""""""""""""""""""""""" */
2734 ctx_name
= ((ctx_t
*)(lnode
->data
))->name
;
2736 ctx
= locate_ctx(ctx_name
);
2738 fatal_internal("Unknown context %s.", ctx_name
);
2741 void * par_bst
= ctx
->par_bst
;
2743 tmp_par_str
= xstrdup(par_str
);
2744 ltrim(tmp_par_str
, " \t");
2745 rtrim(tmp_par_str
, " \t", 0);
2746 par_name
= xstrtok_r(tmp_par_str
, " \t,", &end_tmp_par_str
);
2747 if (par_name
== NULL
)
2748 fatal_internal("Parameters are missing for option %s.", opt_name
);
2750 /* For each parameter given in par_str, creates a par_t object and */
2751 /* insert it the in the parameters BST of the context. */
2752 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2753 while (par_name
!= NULL
)
2755 tmp_par
.name
= par_name
;
2757 node
= bst_find(&tmp_par
, &par_bst
, par_compare
);
2760 fatal_internal("The parameter %s is already defined in context %s.",
2761 par_name
, ctx
->name
);
2766 par
= xmalloc(sizeof(par_t
));
2767 par
->name
= xstrdup(par_name
);
2768 par
->opt
= opt
; /* Link the option to this parameter */
2770 bst_search(par
, &par_bst
, par_compare
);
2772 par_name
= xstrtok_r(NULL
, " \t,", &end_tmp_par_str
);
2775 /* Update the value of the root of ctx->par_bst as it may have */
2776 /* been modified. */
2777 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2778 ctx
->par_bst
= par_bst
;
2782 lnode
= lnode
->next
;
2788 /* ==================================================================== */
2789 /* Create a new context instance. */
2790 /* IN ctx : a context pointer to allow this instance to */
2791 /* access the context fields */
2792 /* IN prev_ctx_inst : the context instance whose option leading to the */
2793 /* creation of this new context instance is part of */
2794 /* Returns : the new context. */
2795 /* ==================================================================== */
2797 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
)
2800 opt_inst_t
* gen_opt_inst
;
2801 ctx_inst_t
* ctx_inst
;
2802 seen_opt_t
* seen_opt
;
2803 char * str
, *opt_name
;
2807 /* Keep a trace of the opt_inst which was at the origin of the creation */
2808 /* of this context instance. */
2809 /* This will serve during the evaluation of the option callbacks. */
2810 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2811 if (prev_ctx_inst
!= NULL
)
2813 gen_opt_inst
= (opt_inst_t
*)(prev_ctx_inst
->opt_inst_list
->tail
->data
);
2815 /* Update current_state. */
2816 /* """"""""""""""""""""" */
2817 cur_state
->opt_name
= gen_opt_inst
->opt
->name
;
2820 gen_opt_inst
= NULL
;
2822 /* Create and initialize the new context instance. */
2823 /* """"""""""""""""""""""""""""""""""""""""""""""" */
2824 ctx_inst
= xmalloc(sizeof(ctx_inst_t
));
2825 ctx_inst
->ctx
= ctx
;
2826 ctx_inst
->prev_ctx_inst
= prev_ctx_inst
;
2827 ctx_inst
->gen_opt_inst
= gen_opt_inst
;
2828 ctx_inst
->incomp_bst_list
= ll_new();
2829 ctx_inst
->opt_inst_list
= ll_new();
2830 ctx_inst
->seen_opt_bst
= NULL
;
2834 if (prev_ctx_inst
== NULL
)
2835 first_ctx_inst
= ctx_inst
;
2837 /* Initialize the occurrence counters of each opt allowed in the context. */
2838 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2839 node
= ctx
->opt_list
->head
;
2840 while (node
!= NULL
)
2843 opt
->occurrences
= 0;
2848 /* Initialize the BST containing the seen indicator for all the options */
2849 /* allowed in this context instance. */
2850 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2851 node
= ctx
->opt_list
->head
;
2852 while (node
!= NULL
)
2855 seen_opt
= xmalloc(sizeof(seen_opt_t
));
2856 seen_opt
->opt
= opt
;
2857 seen_opt
->par
= NULL
;
2860 bst_search(seen_opt
, &(ctx_inst
->seen_opt_bst
), seen_opt_compare
);
2865 /* Initialize the BST containing the incompatibles options. */
2866 /* Incompatibles option names are read from strings found in the list */
2867 /* incomp_list present in each instance of ctx_t. */
2868 /* These names are then used to search for the object of type seen_opt_t */
2869 /* which is already present in the seen_opt_bst of the context instance. */
2871 /* Once found the seen_opt_t object in inserted in the new BST */
2872 /* At the end the new BST in added to the list incomp_bst_list. */
2873 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2874 node
= ctx
->incomp_list
->head
;
2875 while (node
!= NULL
)
2878 seen_opt_t tmp_seen_opt
;
2880 str
= xstrdup(node
->data
);
2882 rtrim(str
, " \t", 0);
2883 opt_name
= strtok(str
, " \t"); /* Extract the first option name. */
2885 while (opt_name
!= NULL
) /* For each option name. */
2887 if ((opt
= locate_opt(opt_name
)) != NULL
)
2889 /* The option found is searched in the tree of potential */
2891 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2892 tmp_seen_opt
.opt
= opt
;
2894 bst_node
= bst_find(&tmp_seen_opt
, &(ctx_inst
->seen_opt_bst
),
2897 if (bst_node
!= NULL
)
2899 /* If found then it is added into the new BST tree. */
2900 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2901 seen_opt
= bst_node
->key
;
2902 bst_search(seen_opt
, &bst
, seen_opt_compare
);
2905 /* Not found! That means that the option is unknown in this */
2906 /* context as all options has have a seen_opt structure in */
2908 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2909 fatal_internal("%s is not known in the context %s.", opt
->name
,
2913 fatal_internal("Unknown option %s.", opt_name
);
2915 opt_name
= strtok(NULL
, " \t");
2919 ll_append(ctx_inst
->incomp_bst_list
, bst
);
2927 /* ====================================================================== */
2928 /* Create a list formed by all the significant command line words */
2929 /* Words beginning or ending with { or } are split. Each of these */
2930 /* symbols will get their own place in the list. */
2932 /* the {...} part delimits a context, the { will not appear in the list */
2933 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2934 /* to facilitate the parsing phase. | must not be used by the end user. */
2936 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2937 /* program name is not considered. */
2938 /* IN words : is the array of strings constituting the command line to */
2940 /* Returns : 1 on success, 0 if a { or } is missing. */
2941 /* ====================================================================== */
2943 ctxopt_build_cmdline_list(int nb_words
, char ** words
)
2946 char * prev_word
= NULL
;
2950 ll_node_t
*node
, *start_node
;
2952 /* The analysis is divided into three passes, this is not optimal but */
2953 /* must be done only one time. Doing that we privilege readability. */
2955 /* In the following, SG is the ascii character 1d (dec 29) */
2957 /* The first pass creates the list, extract the leading an trailing */
2958 /* SG '{' and '}' of each word and give them their own place in the */
2961 /* The second pass transform the '{...}' blocks by a trailing SG */
2962 /* ({...} -> ...|) */
2964 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2965 /* the middle in the remaining list elements and recreate the pseudo */
2967 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2969 /* If the option list is not empty, clear it before going further. */
2970 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2971 if (cmdline_list
!= NULL
)
2973 node
= cmdline_list
->head
;
2974 while (node
!= NULL
)
2977 ll_delete(cmdline_list
, node
);
2978 node
= cmdline_list
->head
;
2982 cmdline_list
= ll_new();
2984 start_node
= cmdline_list
->head
; /* In the following loop start_node will *
2985 * contain a pointer to the current *
2986 * word stripped from its leading *
2987 * sequence of {, }. */
2988 for (i
= 0; i
< nb_words
; i
++)
2990 size_t len
= strlen(words
[i
]);
2996 /* Replace each occurrence of the legal word {} by the characters */
2997 /* 0x02 and 0x03 to hide them from the following process. */
2998 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2999 while ((ptr
= strstr(str
, "{}")) != NULL
)
3001 *ptr
= 0x02; /* Arbitrary values unlikely. */
3002 *(ptr
+ 1) = 0x03; /* present in a word */
3005 if (len
> 1) /* The word contains at least 2 characters. */
3009 /* Interpret its beginning and look for the start of the real word. */
3010 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3011 while (start
<= len
- 1 && (str
[start
] == '{' || str
[start
] == '}'))
3013 ll_append(cmdline_list
, xstrndup(str
+ start
, 1));
3015 start_node
= cmdline_list
->tail
;
3019 if (str
[end
] == '{' || str
[end
] == '}')
3021 if (end
> 0 && str
[end
- 1] != '\\')
3023 ll_append(cmdline_list
, xstrndup(str
+ end
, 1));
3025 node
= cmdline_list
->tail
;
3027 while (str
[end
] == '{' || str
[end
] == '}')
3029 if (end
> start
&& str
[end
- 1] == '\\')
3032 ll_insert_before(cmdline_list
, node
, xstrndup(str
+ end
, 1));
3041 if (start_node
!= NULL
)
3042 ll_insert_after(cmdline_list
, start_node
,
3043 xstrndup(str
+ start
, end
- start
+ 1));
3045 ll_append(cmdline_list
, xstrndup(str
+ start
, end
- start
+ 1));
3046 start_node
= cmdline_list
->tail
;
3051 ll_append(cmdline_list
, xstrdup(str
));
3052 start_node
= cmdline_list
->tail
;
3058 node
= cmdline_list
->head
;
3061 while (node
!= NULL
)
3065 if (strcmp(word
, "{") == 0)
3067 ll_node_t
* old_node
= node
;
3071 ll_delete(cmdline_list
, old_node
);
3073 else if (strcmp(word
, "}") == 0)
3091 node
= cmdline_list
->head
;
3093 while (node
!= NULL
)
3097 /* Restore the original { and } characters forming the legal word {}. */
3098 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3099 while ((ptr
= strchr(word
, 0x02)) != NULL
)
3101 while ((ptr
= strchr(word
, 0x03)) != NULL
)
3104 /* Remove a SG if the previous element is SG. */
3105 /* """""""""""""""""""""""""""""""""""""""""" */
3106 if (strcmp(word
, "\x1d") == 0)
3108 if (prev_word
!= NULL
&& (strcmp(prev_word
, "\x1d") == 0))
3110 ll_node_t
* old_node
= node
;
3112 free(old_node
->data
);
3113 ll_delete(cmdline_list
, old_node
);
3116 else if (strcmp(word
, "-") == 0) /* A single - is a legal argument, not *
3117 * a parameter. Protect it. */
3120 node
->data
= xstrdup("\\-");
3123 prev_word
= node
->data
;
3127 /* Clean useless and SG at the beginning and end of list. */
3128 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3129 node
= cmdline_list
->head
;
3136 if (strcmp(word
, "\x1d") == 0)
3139 ll_delete(cmdline_list
, node
);
3142 node
= cmdline_list
->tail
;
3148 if (strcmp(word
, "\x1d") == 0)
3151 ll_delete(cmdline_list
, node
);
3157 /* ===================================================================== */
3158 /* Build and analyze the command line list and create the linked data */
3159 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3160 /* This function identifies the following errors and creates an array of */
3161 /* The remaining unanalyzed arguments. */
3162 /* - detect missing arguments */
3163 /* - detect too many arguments */
3164 /* - detect unknown parameters in a context */
3165 /* - detect too many occurrences of a parameters in a context */
3166 /* - detect missing required arguments in a context */
3168 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3169 /* program name is not considered */
3170 /* IN words : is the array of strings constituting the command line to */
3172 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3173 /* is present in the list. */
3174 /* OUT rem_args : array of remaining command line arguments if a -- */
3175 /* is present in the list. This array must be free by */
3176 /* The caller as it is allocated here. */
3177 /* ===================================================================== */
3179 ctxopt_analyze(int nb_words
, char ** words
, int * nb_rem_args
,
3185 ctx_inst_t
* ctx_inst
;
3186 opt_inst_t
* opt_inst
;
3189 int expect_par_or_arg
= 0;
3191 ll_node_t
* cli_node
;
3193 seen_opt_t
* bst_seen_opt
;
3199 if (!ctxopt_build_cmdline_list(nb_words
, words
))
3200 fatal_internal("The command line could not be parsed: "
3201 "missing '{' or '}' detected.");
3203 if (main_ctx
== NULL
)
3204 fatal_internal("At least one context must have been created.");
3206 /* Check that all options has an action and at least one parameter. */
3207 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3208 bst_walk(options_bst
, bst_check_opt_cb
);
3210 /* Create the first ctx_inst record. */
3211 /* """"""""""""""""""""""""""""""""" */
3214 ctx_inst_list
= ll_new();
3215 ctx_inst
= new_ctx_inst(ctx
, NULL
);
3216 ctx_inst
->par_name
= NULL
;
3218 /* Update current_state. */
3219 /* """"""""""""""""""""" */
3220 cur_state
->ctx_name
= ctx
->name
;
3222 ll_append(ctx_inst_list
, ctx_inst
);
3224 /* For each node in the command line. */
3225 /* """""""""""""""""""""""""""""""""" */
3226 cli_node
= cmdline_list
->head
;
3228 while (cli_node
!= NULL
)
3230 if (strcmp(cli_node
->data
, "--") == 0)
3231 break; /* No new parameter will be analyzed after this point. */
3233 par_name
= cli_node
->data
;
3235 /* Replace a leading -- by a single - */
3236 /* """""""""""""""""""""""""""""""""" */
3237 if (strncmp(cli_node
->data
, "--", 2) == 0)
3238 par_name
+= 1; /* Ignore the first dash */
3240 if (strcmp(par_name
, "\x1d") == 0)
3242 check_for_missing_mandatory_opt(ctx_inst
, (char *)(cli_node
->prev
->data
));
3243 check_for_occurrences_issues(ctx_inst
);
3245 /* Forced backtracking to the previous context instance. */
3246 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3247 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3249 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3250 ctx
= ctx_inst
->ctx
;
3252 /* Update current_states. */
3253 /* """"""""""""""""""""" */
3254 cur_state
->ctx_name
= ctx
->name
;
3255 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3259 /* Update current_state. */
3260 /* """"""""""""""""""""" */
3261 cur_state
->ctx_par_name
= NULL
;
3264 else if (expect_par
&& *par_name
== '-')
3269 /* Update current_state. */
3270 /* """"""""""""""""""""" */
3271 cur_state
->cur_opt_par_name
= par_name
;
3272 cur_state
->ctx_name
= ctx
->name
;
3273 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3275 /* An expected parameter has been seen. */
3276 /* """""""""""""""""""""""""""""""""""" */
3277 if ((par
= locate_par(par_name
, ctx
)) == NULL
)
3282 /* Look if this parameter is an unique abbreviation of a longer */
3283 /* parameter. If this is the case then just replace it with its */
3284 /* full length version and try again. */
3285 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3286 if (flags
.allow_abbreviations
)
3287 if ((word
= abbrev_expand(par_name
, ctx
)) != NULL
)
3289 cli_node
->data
= word
;
3293 /* Try to find a prefix which is a valid parameter in this context */
3294 /* If found, split the cli_node in two to build a new parameter */
3295 /* node and followed by a node containing the remaining string */
3296 /* If the new parameter corresponds to an option not taking */
3297 /* argument then prefix the remaining string whit a dash as it may */
3298 /* contain a new parameter. */
3299 /* The new parameter will be re-evaluated in the next iteration of */
3301 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3302 prefix
= look_for_valid_prefix_in_word(par_name
, ctx
, &pos
, &popt
);
3303 if (prefix
!= NULL
&& pos
!= 0)
3305 cli_node
->data
= prefix
; /* prefix contains le name of a valid *
3306 | parameter in this context. */
3310 /* The parameter may be followed by arguments. */
3311 /* ''''''''''''''''''''''''''''''''''''''''''' */
3312 if (*(par_name
+ pos
) == '-')
3314 word
= xstrdup("\\"); /* Protect the '-' */
3315 word
= strappend(word
, par_name
+ pos
, NULL
);
3318 word
= xstrdup(par_name
+ pos
);
3322 /* The parameter does not take arguments, the */
3323 /* following word must be a parameter or nothing */
3324 /* hence prefix it with a dash. */
3325 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3326 word
= xstrdup("-");
3327 word
= strappend(word
, par_name
+ pos
, NULL
);
3330 /* Insert it after the current node in the list. */
3331 /* """"""""""""""""""""""""""""""""""""""""""""" */
3332 ll_insert_after(cmdline_list
, cli_node
, word
);
3334 continue; /* loop */
3338 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3339 check_for_occurrences_issues(ctx_inst
);
3341 if (ctx_inst
->prev_ctx_inst
== NULL
)
3343 char * errmsg
= xstrdup("");
3345 /* Update current_state. */
3346 /* """"""""""""""""""""" */
3347 cur_state
->ctx_par_name
= NULL
;
3349 *user_string
= '\0';
3350 *user_string2
= '\0';
3352 user_string
= strappend(user_string
, par_name
, NULL
);
3354 bst_walk(contexts_bst
, bst_match_par_cb
);
3356 if (*user_string2
!= '\0')
3360 "\nIt appears to be defined in the context(s):", user_string2
,
3364 fatal(CTXOPTUNKPAR
, errmsg
);
3368 /* Tries to backtrack and analyse the same parameter in the */
3369 /* previous context. */
3370 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3371 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3372 ctx
= ctx_inst
->ctx
;
3374 /* Update current_state. */
3375 /* """"""""""""""""""""" */
3376 cur_state
->ctx_name
= ctx
->name
;
3377 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3379 cli_node
= cli_node
->prev
;
3385 seen_opt_t seen_opt
;
3387 /* The parameter is valid in the context, create a opt_inst and */
3388 /* append it to the ctx_inst list options list. */
3389 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3394 opt_inst
= xmalloc(sizeof(opt_inst_t
));
3395 opt_inst
->opt
= opt
;
3396 opt_inst
->par
= par_name
;
3397 opt_inst
->values_list
= ll_new();
3398 opt_inst
->next_ctx_inst
= NULL
;
3400 /* Update current_state. */
3401 /* """"""""""""""""""""" */
3402 cur_state
->cur_opt_params
= opt
->params
;
3404 /* Priority option are inserted at the start of the opt_inst list */
3405 /* but their order of appearance in the context definition must */
3406 /* be preserver so each new priority option will be placed after */
3407 /* the previous ones at the start of the opt_inst list. */
3408 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3409 if (!opt
->eval_first
)
3411 /* Look if we have a registered dependency in the order of the */
3412 /* evaluation of two options. */
3413 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3414 if (opt
->eval_before_list
->len
> 0)
3416 ll_t
* list
= ctx_inst
->opt_inst_list
;
3417 ll_node_t
* opt_inst_node
;
3419 ll_t
* before_list
= opt
->eval_before_list
;
3420 ll_node_t
* before_node
= before_list
->head
;
3422 ll_node_t
* target_node
= NULL
; /* If not NULL, the new node *
3423 | will be inserted before it. */
3425 /* For each entry in eval_before_list, try to find if it */
3426 /* refers to an option already entered in the context. If this */
3427 /* is the case, insert it just before it instead of putting it */
3429 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3430 while (before_node
!= NULL
)
3432 opt_inst_node
= list
->head
;
3434 while (opt_inst_node
!= target_node
)
3436 opt_t
* tmp_opt
= (((opt_inst_t
*)opt_inst_node
->data
))->opt
;
3438 /* We have found an option mentioned if the before_list */
3439 /* of the option we want to add. We can stop searching. */
3440 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3441 if (strcmp(tmp_opt
->name
, ((opt_t
*)before_node
->data
)->name
))
3442 opt_inst_node
= opt_inst_node
->next
;
3444 target_node
= opt_inst_node
; /* Set the target node. */
3447 before_node
= before_node
->next
;
3450 /* Insert or append ? */
3451 /* """""""""""""""""" */
3452 if (target_node
!= NULL
)
3453 ll_insert_before(ctx_inst
->opt_inst_list
, target_node
, opt_inst
);
3455 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3458 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3462 ll_node_t
* opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
3463 opt_inst_t
* tmp_opt_inst
;
3465 while (opt_inst_node
!= NULL
)
3467 tmp_opt_inst
= opt_inst_node
->data
;
3468 if (!tmp_opt_inst
->opt
->eval_first
)
3470 ll_insert_before(ctx_inst
->opt_inst_list
, opt_inst_node
,
3475 opt_inst_node
= opt_inst_node
->next
;
3477 if (opt_inst_node
== NULL
)
3478 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3481 /* Check if an option was already seen in the */
3482 /* current context instance. */
3483 /* """""""""""""""""""""""""""""""""""""""""" */
3486 bst_node
= bst_find(&seen_opt
, &(ctx_inst
->seen_opt_bst
),
3489 /* bst_node cannot be NULL here. */
3491 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3493 if (!opt
->multiple
&& bst_seen_opt
->seen
== 1)
3494 fatal(CTXOPTDUPOPT
, NULL
);
3496 /* Check if this option is compatible with the options already */
3497 /* seen in this context instance. */
3498 /* Look if the option is present in one on the BST present in */
3499 /* the incomp_bst_list of the context instance. */
3500 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3501 node
= ctx_inst
->incomp_bst_list
->head
;
3502 while (node
!= NULL
)
3507 /* There can only have one seen_opt object in the BST tree was */
3508 /* already seen, try to locate it, the result will be put in */
3509 /* user_object by the bst_seen_opt_seen_cb function. */
3510 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3511 bst_walk(bst
, bst_seen_opt_seen_cb
);
3513 /* If it is the case, look if the current option is also */
3515 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3516 if (user_object
!= NULL
)
3518 bst_node
= bst_find(bst_seen_opt
, &bst
, seen_opt_compare
);
3520 if (bst_node
!= NULL
)
3522 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3523 if (bst_seen_opt
->seen
== 0)
3524 fatal(CTXOPTINCOPT
, (char *)user_object
);
3531 /* Mark this option as seen in the current context instance. */
3532 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3533 bst_seen_opt
->seen
= 1;
3534 free(bst_seen_opt
->par
);
3535 bst_seen_opt
->par
= xstrdup(par_name
);
3537 /* If this option leads to a next context, create a new ctx_inst */
3538 /* and switch to it for the analyse of the future parameter. */
3539 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3540 if (opt
->next_ctx
!= NULL
)
3542 ctx
= locate_ctx(opt
->next_ctx
);
3545 fatal_internal("Unknown context %s.", opt
->next_ctx
);
3547 opt_inst
->next_ctx_inst
= ctx_inst
= new_ctx_inst(ctx
, ctx_inst
);
3548 ctx_inst
->par_name
= xstrdup(par_name
);
3550 ll_append(ctx_inst_list
, ctx_inst
);
3553 /* Look is we must expect some arguments. */
3554 /* """""""""""""""""""""""""""""""""""""" */
3555 expect_par_or_arg
= 0;
3560 expect_par
= 1; /* Parameter doesn't accept any argument. */
3563 if (!opt
->optional_args
)
3564 expect_arg
= 1; /* Parameter has mandatory arguments. */
3566 expect_par_or_arg
= 1; /* Parameter has optional arguments. */
3570 else if (expect_par
&& *par_name
!= '-')
3572 ll_node_t
* n
= cli_node
->next
;
3574 if (!flags
.stop_if_non_option
)
3575 /* Look if potential arguments must still be analyzed until the */
3576 /* end of the context/command line part to analyze/command line. */
3577 /* If this is the case we have met an extra argument. */
3578 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3581 if (strcmp(n
->data
, "--") == 0 || strcmp(n
->data
, "\x1d") == 0)
3582 fatal(CTXOPTUNXARG
, NULL
);
3584 if (*(char *)(n
->data
) == '-')
3585 fatal(CTXOPTUNXARG
, NULL
);
3590 break; /* An unexpected non parameter was seen, if no Potential *
3591 | arguments remain in the command line or *
3592 | flags.stop_if_non_option is set, assume that it is is *
3593 | the first of the non arguments and stop the command *
3596 else if (expect_arg
&& *par_name
!= '-')
3598 ll_node_t
* cstr_node
;
3599 constraint_t
* cstr
;
3601 /* Check if the arguments of the option respects */
3602 /* the attached constraints if any. */
3603 /* """"""""""""""""""""""""""""""""""""""""""""" */
3604 cstr_node
= opt
->constraints_list
->head
;
3605 while (cstr_node
!= NULL
)
3607 cstr
= cstr_node
->data
;
3608 if (!cstr
->constraint(cstr
->nb_args
, cstr
->args
, par_name
,
3609 cur_state
->cur_opt_par_name
))
3611 fputs("\n", stderr
);
3612 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, exit_after
);
3615 cstr_node
= cstr_node
->next
;
3618 /* If the argument is valid, store it. */
3619 /* """"""""""""""""""""""""""""""""""" */
3620 if (*par_name
== '\\' && *(par_name
+ 1) == '-')
3621 ll_append(opt_inst
->values_list
, par_name
+ 1);
3623 ll_append(opt_inst
->values_list
, par_name
);
3627 expect_par_or_arg
= 0;
3629 if (opt
->multiple_args
)
3630 expect_par_or_arg
= 1;
3632 expect_par
= 1; /* Parameter takes only one argument. */
3634 else if (expect_arg
&& *par_name
== '-')
3635 fatal(CTXOPTMISARG
, NULL
);
3636 else if (expect_par_or_arg
)
3640 expect_par_or_arg
= 0;
3642 if (*par_name
!= '-')
3643 expect_arg
= 1; /* Consider this word as an argument and retry. */
3645 expect_par
= 1; /* Consider this word as a parameter and retry. */
3647 cli_node
= cli_node
->prev
;
3650 cli_node
= cli_node
->next
;
3653 if (cmdline_list
->len
> 0 && *par_name
== '-')
3655 if (expect_arg
&& !opt
->optional_args
)
3656 fatal(CTXOPTMISARG
, NULL
);
3659 /* Look if a context_instance has unseen mandatory options. */
3660 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3661 node
= ctx_inst_list
->head
;
3662 while (node
!= NULL
)
3664 ctx_inst
= node
->data
;
3666 /* Update current_state. */
3667 /* """"""""""""""""""""" */
3668 cur_state
->ctx_name
= ctx_inst
->ctx
->name
;
3669 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3671 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3672 check_for_occurrences_issues(ctx_inst
);
3677 /* Allocate the array containing the remaining not analyzed */
3678 /* command line arguments. */
3679 /* NOTE: The strings in the array are just pointer to the */
3680 /* data of the generating list and must not be freed. */
3681 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3682 if (cli_node
!= NULL
)
3684 if (strcmp((char *)cli_node
->data
, "--") == 0)
3685 /* The special parameter -- was encountered, the -- argument is not */
3686 /* put in the remaining arguments. */
3687 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3688 ll_strarray(cmdline_list
, cli_node
->next
, nb_rem_args
, rem_args
);
3690 /* A non parameter was encountered when a parameter was expected. We */
3691 /* assume that the evaluation of the remaining command line argument */
3692 /* are not the responsibility of the users code. */
3693 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3694 ll_strarray(cmdline_list
, cli_node
, nb_rem_args
, rem_args
);
3699 *rem_args
= xmalloc(sizeof(char *));
3700 (*rem_args
)[0] = NULL
;
3704 /* ==================================================== */
3705 /* Free ctxopt memory used for its internal structures. */
3706 /* ==================================================== */
3708 ctxopt_free_memory(void)
3710 ll_destroy(cmdline_list
, free
);
3711 ll_destroy(ctx_inst_list
, ctx_inst_free
);
3712 bst_destroy(options_bst
, opt_free
);
3713 bst_destroy(contexts_bst
, ctx_free
);
3716 /* ==================================================================== */
3717 /* Parse the options data structures and launches the callback function */
3718 /* attached to each options instances. */
3719 /* This calls a recursive function which proceeds context per context. */
3720 /* ==================================================================== */
3722 ctxopt_evaluate(void)
3724 evaluate_ctx_inst(first_ctx_inst
);
3727 /* =================================================================== */
3728 /* Recursive function called by ctxopt_evaluate to process the list of */
3729 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3730 /* action attached to the context and its option instances. */
3731 /* =================================================================== */
3733 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
)
3735 opt_inst_t
* opt_inst
;
3738 ll_node_t
* opt_inst_node
;
3742 if (ctx_inst
== NULL
)
3745 ctx
= ctx_inst
->ctx
;
3747 /* Do not evaluate the action attached to this context is there is no */
3748 /* option to evaluate. */
3749 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3750 opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
3751 if (opt_inst_node
== NULL
)
3754 /* Call the entering action attached to this context if any. */
3755 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3756 if (ctx
->action
!= NULL
)
3758 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3759 ctx
->action(ctx
->name
, entering
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3760 ctx
->nb_data
, ctx
->data
);
3762 ctx
->action(ctx
->name
, entering
, NULL
, ctx
->nb_data
, ctx
->data
);
3765 /* For each instance of options. */
3766 /* """"""""""""""""""""""""""""" */
3767 while (opt_inst_node
!= NULL
)
3769 opt_inst
= (opt_inst_t
*)(opt_inst_node
->data
);
3770 ll_strarray(opt_inst
->values_list
, opt_inst
->values_list
->head
, &nb_args
,
3772 opt
= opt_inst
->opt
;
3774 /* Launch the attached action if any. */
3775 /* """""""""""""""""""""""""""""""""" */
3776 if (opt
->action
!= NULL
)
3777 opt
->action(ctx
->name
, opt
->name
, opt_inst
->par
, nb_args
, args
,
3778 opt
->nb_data
, opt
->data
, ctx
->nb_data
, ctx
->data
);
3780 if (opt_inst
->next_ctx_inst
!= NULL
)
3781 evaluate_ctx_inst(opt_inst
->next_ctx_inst
);
3786 opt_inst_node
= opt_inst_node
->next
;
3789 /* Call the exiting action attached to this context if any. */
3790 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3791 if (ctx
->action
!= NULL
)
3793 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3794 ctx
->action(ctx
->name
, exiting
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3795 ctx
->nb_data
, ctx
->data
);
3797 ctx
->action(ctx
->name
, exiting
, NULL
, ctx
->nb_data
, ctx
->data
);
3801 /* ============================================================ */
3802 /* Create and initializes a new context. */
3803 /* - allocate space. */
3805 /* - initialize its option with a few of their characteristics. */
3806 /* ============================================================ */
3808 ctxopt_new_ctx(char * name
, char * opts_specs
)
3813 if (!ctxopt_initialized
)
3814 fatal_internal("Please call ctxopt_init first.");
3816 ctx
= xmalloc(sizeof(ctx_t
));
3818 /* Validates the context name: */
3819 /* ALPHA+(ALPHANUM|_)* */
3820 /* """"""""""""""""""""""""""" */
3823 fatal_internal("A context name must start with a letter: %s.", name
);
3828 if (!isalnum(*p
) && *p
!= '_')
3829 fatal_internal("A context name must only contain letters, "
3830 "numbers or '_': %s.",
3835 ctx
->name
= xstrdup(name
);
3836 ctx
->opt_list
= ll_new(); /* List of options legit in this context. */
3837 ctx
->incomp_list
= ll_new(); /* List of incompatible options strings. */
3838 ctx
->par_bst
= NULL
;
3842 /* The first created context is the main one. */
3843 /* """""""""""""""""""""""""""""""""""""""""" */
3844 if (contexts_bst
== NULL
)
3848 cur_state
->ctx_name
= ctx
->name
;
3851 if (init_opts(opts_specs
, ctx
) == 0)
3853 if (bst_find(ctx
, &contexts_bst
, ctx_compare
) != NULL
)
3854 fatal_internal("The context %s already exists.", name
);
3856 bst_search(ctx
, &contexts_bst
, ctx_compare
);
3859 /* ==================================================== */
3860 /* Display a usage screen limited to a specific context */
3861 /* IN: the context name. */
3862 /* IN: what to do after (continue or exit the program) */
3863 /* possible values: continue_after, exit_after. */
3864 /* ==================================================== */
3866 ctxopt_ctx_disp_usage(char * ctx_name
, usage_behaviour action
)
3871 int has_optional
= 0;
3872 int has_ellipsis
= 0;
3874 int has_generic_arg
= 0;
3875 int has_ctx_change
= 0;
3876 int has_early_eval
= 0;
3878 ctx
= locate_ctx(ctx_name
);
3880 fatal_internal("Unknown context %s.", ctx_name
);
3882 if (cur_state
->ctx_par_name
== NULL
)
3883 printf("\nSynopsis:\n%s \\\n", cur_state
->prog_name
);
3885 printf("\nSynopsis for the context introduced by %s:\n",
3886 cur_state
->ctx_par_name
);
3888 list
= ctx
->opt_list
;
3889 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3890 &has_ctx_change
, &has_early_eval
);
3892 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3893 has_optional
, has_ellipsis
, has_rule
);
3895 if (action
== exit_after
)
3899 /* =================================================== */
3900 /* Display a full usage screen about all contexts. */
3901 /* IN: what to do after (continue or exit the program) */
3902 /* possible values: continue_after, exit_after. */
3903 /* =================================================== */
3905 ctxopt_disp_usage(usage_behaviour action
)
3908 int has_optional
= 0;
3909 int has_ellipsis
= 0;
3911 int has_generic_arg
= 0;
3912 int has_ctx_change
= 0;
3913 int has_early_eval
= 0;
3915 if (main_ctx
== NULL
)
3916 fatal_internal("At least one context must have been created.");
3918 /* Usage for the first context. */
3919 /* """""""""""""""""""""""""""" */
3920 printf("\nAllowed options in the default context:\n");
3921 list
= main_ctx
->opt_list
;
3922 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3923 &has_ctx_change
, &has_early_eval
);
3925 /* Usage for the other contexts. */
3926 /* """"""""""""""""""""""""""""" */
3927 bst_walk(contexts_bst
, bst_print_ctx_cb
);
3929 /* Contextual syntactic explanations. */
3930 /* """""""""""""""""""""""""""""""""" */
3931 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3932 has_optional
, has_ellipsis
, has_rule
);
3934 if (action
== exit_after
)
3938 /* *********************************** */
3939 /* Built-in constraint check functions */
3940 /* *********************************** */
3942 /* ============================================================= */
3943 /* This constraint checks if each arguments respects a format as */
3944 /* defined for the scanf function. */
3945 /* return 1 if yes and 0 if no. */
3946 /* ============================================================= */
3948 ctxopt_format_constraint(int nb_args
, char ** args
, char * value
, char * par
)
3957 fatal_internal("Format constraint, invalid number of parameters.");
3959 if (strlen(value
) > 255)
3962 format
= xstrdup(args
[0]);
3963 format
= strappend(format
, "%c", NULL
);
3965 rc
= sscanf(value
, format
, x
, &y
);
3968 "The argument %s of %s does not respect the imposed format %s.",
3969 value
, par
, args
[0]);
3976 /* ================================================================== */
3977 /* This constraint checks if each arguments of the option instance is */
3978 /* between a minimum and a maximum (inclusive). */
3979 /* return 1 if yes and 0 if no. */
3980 /* ================================================================== */
3982 ctxopt_re_constraint(int nb_args
, char ** args
, char * value
, char * par
)
3988 "Regular expression constraint, invalid number of parameters.");
3990 if (regcomp(&re
, args
[0], REG_EXTENDED
) != 0)
3991 fatal_internal("Invalid regular expression %s.", args
[0]);
3993 if (regexec(&re
, value
, (size_t)0, NULL
, 0) != 0)
3996 "The argument %s of %s doesn't match the constraining "
3997 "regular expression %s.",
3998 value
, par
, args
[0]);
4007 /* ================================================================== */
4008 /* This constraint checks if each arguments of the option instance is */
4009 /* between a minimum and a maximum (inclusive). */
4010 /* return 1 if yes and 0 if no. */
4011 /* ================================================================== */
4013 ctxopt_range_constraint(int nb_args
, char ** args
, char * value
, char * par
)
4024 fatal_internal("Range constraint, invalid number of parameters.");
4026 if (strcmp(args
[0], ".") == 0)
4029 n
= sscanf(args
[0], "%ld%c", &min
, &c
);
4031 if (!max_only
&& n
!= 1)
4032 fatal_internal("Range constraint, min: invalid parameters.");
4034 if (strcmp(args
[1], ".") == 0)
4037 n
= sscanf(args
[1], "%ld%c", &max
, &c
);
4039 if (!min_only
&& n
!= 1)
4040 fatal_internal("Range constraint, max: invalid parameters.");
4042 if (min_only
&& max_only
)
4043 fatal_internal("Range constraint, invalid parameters.");
4046 v
= strtol(value
, &ptr
, 10);
4047 if (errno
|| ptr
== value
)
4055 "The argument %ld of %s is not greater than or equal to %ld.", v
,
4067 "The argument %ld of %s is not less than or equal to %ld.", v
,
4074 else if (v
< min
|| v
> max
)
4076 fprintf(stderr
, "The argument %ld of %s is not between %ld and %ld.", v
,
4081 return 1; /* check passed */
4084 /* =============================================================== */
4085 /* This function provides a way to set the behaviour of a context. */
4086 /* =============================================================== */
4088 ctxopt_add_global_settings(settings s
, ...)
4095 case error_functions
:
4097 typedef void fn(errors e
, state_t
* state
);
4099 void (*function
)(errors e
, state_t
* state
);
4102 e
= va_arg(args
, errors
);
4103 function
= va_arg(args
, fn
*);
4104 err_functions
[e
] = function
;
4114 /* ================================================================ */
4115 /* This function provides a way to set the behaviour of an option. */
4116 /* It can take a variable number of arguments according to its */
4117 /* first argument: */
4119 /* o a string containing an option name and all its possible */
4120 /* parameters separates by spaces, tabs or commas (char *) */
4121 /* (e.g: "help -h -help"). */
4123 /* o a string containing an option name. */
4124 /* o a pointer to a function which will be called at evaluation */
4126 /* - constraints: */
4127 /* o a string containing an option name. */
4128 /* o a pointer to a function to check if an argument is valid. */
4129 /* o a strings containing the arguments to this function. */
4130 /* ================================================================ */
4132 ctxopt_add_opt_settings(settings s
, ...)
4142 /* This part associates some command line parameters to an option. */
4143 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4149 /* The second argument must be a string containing: */
4150 /* - The name of an existing option. */
4151 /* - a list of parameters with a leading dash (-). */
4152 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4153 ptr
= va_arg(args
, char *);
4156 if (opt_name
!= NULL
)
4158 if ((opt
= locate_opt(opt_name
)) != NULL
)
4160 ptr
= va_arg(args
, char *);
4163 if (!opt_set_parms(opt_name
, params
))
4165 "Duplicated parameters or bad settings for the option %s.",
4169 fatal_internal("Unknown option %s.", opt_name
);
4173 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4175 /* Here opt is a known option. */
4176 /* """"""""""""""""""""""""""" */
4177 if (opt
->params
!= NULL
)
4178 fatal_internal("Parameters are already set for %s.", opt_name
);
4182 size_t l
= strlen(params
);
4184 opt
->params
= xstrdup(params
);
4185 while ((n
= strcspn(opt
->params
, " \t")) < l
)
4186 opt
->params
[n
] = '|';
4192 /* This part associates a callback function to an option. */
4193 /* This function will be called when an instance of an option */
4195 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4202 /* The second argument must be the name of an existing option. */
4203 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4204 ptr
= va_arg(args
, char *);
4206 if ((opt
= locate_opt(ptr
)) != NULL
)
4208 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4211 /* The third argument must be the callback function. */
4212 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4213 function
= va_arg(args
, fn
*);
4214 opt
->action
= function
;
4216 /* The fourth argument must be a pointer to an user's defined */
4217 /* variable or structure that the previous function can manage. */
4218 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4219 while ((data
= va_arg(args
, void *)) != NULL
)
4222 opt
->data
= xrealloc(opt
->data
, nb_data
* sizeof(void *));
4223 opt
->data
[nb_data
- 1] = data
;
4225 opt
->nb_data
= nb_data
;
4228 fatal_internal("Unknown option %s.", ptr
);
4232 /* This part associates a list of functions to control some */
4233 /* characteristics of the arguments of an option. */
4234 /* Each function will be called in order and must return 1 */
4235 /* to validate the arguments. */
4236 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4240 constraint_t
* cstr
;
4243 /* The second argument must be a string. */
4244 /* """"""""""""""""""""""""""""""""""""" */
4245 ptr
= va_arg(args
, char *);
4247 if ((opt
= locate_opt(ptr
)) != NULL
)
4249 typedef int fn(int, char **, char *);
4251 /* The third argument must be a function. */
4252 /* """""""""""""""""""""""""""""""""""""" */
4253 function
= va_arg(args
, fn
*);
4255 cstr
= xmalloc(sizeof(constraint_t
));
4256 cstr
->constraint
= function
;
4258 /* The fourth argument must be a string containing the argument of */
4259 /* The previous function separated by spaces or tabs. */
4260 /* Theses arguments will be passed to the previous function */
4261 /* max: 32 argument! */
4262 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4263 value
= xstrdup(va_arg(args
, char *));
4265 cstr
->to_free
= value
;
4266 cstr
->args
= xcalloc(sizeof(char *), 32);
4267 cstr
->nb_args
= str2argv(value
, cstr
->args
, 32);
4268 ll_append(opt
->constraints_list
, cstr
);
4271 fatal_internal("Unknown option %s.", ptr
);
4275 /* This part allows to indicate that an option must be evaluated */
4276 /* after a list of other options. */
4277 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4283 /* The second argument must be a string. */
4284 /* """"""""""""""""""""""""""""""""""""" */
4285 ptr
= va_arg(args
, char *);
4287 if ((opt
= locate_opt(ptr
)) != NULL
)
4293 ptr
= va_arg(args
, char *);
4297 rtrim(str
, " \t", 0);
4299 /* Feed the list of options to be evaluated after the given option. */
4300 /* This list will contain pointers to options. */
4301 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4302 opt_name
= xstrtok_r(str
, " \t,", &end_str
);
4303 if (opt_name
!= NULL
)
4305 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4307 ll_append(opt
->eval_before_list
, opt_before
);
4308 while ((opt_name
= xstrtok_r(NULL
, " \t,", &end_str
)) != NULL
)
4310 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4311 ll_append(opt
->eval_before_list
, opt_before
);
4313 fatal_internal("Unknown option %s.", opt_name
);
4317 fatal_internal("Unknown option %s.", opt_name
);
4320 fatal_internal("Not enough options to be evaluated after %s.",
4326 fatal_internal("Unknown option %s.", ptr
);
4331 /* This part allows to indicate that an option must be evaluated */
4332 /* before a list of other options. */
4333 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4339 /* The second argument must be a string. */
4340 /* """"""""""""""""""""""""""""""""""""" */
4341 ptr
= va_arg(args
, char *);
4343 if ((opt
= locate_opt(ptr
)) != NULL
)
4349 ptr
= va_arg(args
, char *);
4353 rtrim(str
, " \t", 0);
4355 /* Feed the list of options to be evaluated before the given option. */
4356 /* This list will contain pointers to options. */
4357 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4358 opt_name
= xstrtok_r(str
, " \t,", &end_str
);
4359 if (opt_name
!= NULL
)
4361 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4363 ll_append(opt_before
->eval_before_list
, opt
);
4364 while ((opt_name
= xstrtok_r(NULL
, " \t,", &end_str
)) != NULL
)
4366 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4367 ll_append(opt_before
->eval_before_list
, opt
);
4369 fatal_internal("Unknown option %s.", opt_name
);
4373 fatal_internal("Unknown option %s.", opt_name
);
4376 fatal_internal("Not enough options to be evaluated before %s.",
4382 fatal_internal("Unknown option %s.", ptr
);
4393 /* =============================================================== */
4394 /* This function provides a way to set the behaviour of a context. */
4395 /* =============================================================== */
4397 ctxopt_add_ctx_settings(settings s
, ...)
4406 /* Add a set of mutually incompatible options in a context. */
4407 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4408 case incompatibilities
:
4415 ptr
= va_arg(args
, char *);
4416 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4418 ptr
= va_arg(args
, char *);
4419 list
= ctx
->incomp_list
;
4423 rtrim(str
, " \t", 0);
4425 n
= strcspn(str
, " \t");
4426 if (n
> 0 && n
< strlen(str
))
4427 ll_append(list
, str
);
4430 "Not enough incompatible options in the string: \"%s\".", str
);
4433 fatal_internal("Unknown context %s.", ptr
);
4437 /* Add functions which will be called when */
4438 /* entering and exiting a context. */
4439 /* """"""""""""""""""""""""""""""""""""""" */
4447 ptr
= va_arg(args
, char *);
4448 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4450 typedef int fn(char *, direction
, char *, int, void **);
4452 function
= va_arg(args
, fn
*);
4453 ctx
->action
= function
;
4455 while ((data
= va_arg(args
, void *)) != NULL
)
4458 ctx
->data
= xrealloc(ctx
->data
, nb_data
* sizeof(void *));
4459 ctx
->data
[nb_data
- 1] = data
;
4461 ctx
->nb_data
= nb_data
;
4464 fatal_internal("Unknown context %s.", ptr
);