1 /* ################################################################### */
2 /* Copyright 2015, Pierre Gentile (p.gen.progs@gmail.com) */
4 /* This Source Code Form is subject to the terms of the Mozilla Public */
5 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
6 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 /* ################################################################### */
16 #include <sys/types.h>
22 /* ************************ */
23 /* Static global variables. */
24 /* ************************ */
26 static void *contexts_bst
;
27 static void *options_bst
;
33 /* ************************** */
34 /* Fatal messages prototypes. */
35 /* ************************** */
37 static void (**err_functions
)(errors e
, state_t
*state
);
40 fatal_internal(const char *format
, ...);
43 fatal(errors e
, char *errmsg
);
45 static int user_rc
; /* Used by various callback functions. */
46 static int user_value
; /* Used by various callback functions. */
47 static char *user_string
; /* Used by various callback functions. */
48 static char *user_string2
; /* Used by various callback functions. */
49 static void *user_object
; /* Used by various callback functions. */
51 /* ************************************ */
52 /* Memory management static prototypes. */
53 /* ************************************ */
59 xcalloc(size_t num
, size_t size
);
62 xrealloc(void *ptr
, size_t size
);
65 xstrdup(const char *p
);
68 xstrndup(const char *str
, size_t len
);
70 /* ********************** */
71 /* BST static prototypes. */
72 /* ********************** */
74 typedef struct bst_s bst_t
;
84 #if 0 /* Unused yet. */
86 bst_delete(const void * vkey
, void ** vrootp
,
87 int (*compar
)(const void *, const void *));
91 bst_destroy(void *vrootp
, void (*clean
)(void *));
94 bst_find(const void *vkey
,
96 int (*compar
)(const void *, const void *));
99 bst_search(void *vkey
,
101 int (*compar
)(const void *, const void *));
104 bst_walk_recurse(const bst_t
*root
,
105 void (*action
)(const void *, walk_order_e
, int),
109 bst_walk(const void *vroot
, void (*action
)(const void *, walk_order_e
, int));
111 /* ****************************** */
112 /* Linked list static prototypes. */
113 /* ****************************** */
115 typedef struct ll_node_s ll_node_t
;
116 typedef struct ll_s ll_t
;
119 ll_append(ll_t
* const list
, void * const data
);
122 ll_prepend(ll_t
* const list
, void * const data
);
125 ll_insert_after(ll_t
* const list
, ll_node_t
*node
, void * const data
);
128 ll_insert_before(ll_t
* const list
, ll_node_t
*node
, void * const data
);
131 ll_delete(ll_t
* const list
, ll_node_t
*node
);
143 ll_free(ll_t
* const list
, void (*)(void *));
146 ll_destroy(ll_t
* const list
, void (*)(void *));
149 ll_strarray(ll_t
*list
, ll_node_t
*start_node
, int *count
, char ***array
);
151 /* ************************** */
152 /* Various static prototypes. */
153 /* ************************** */
156 ltrim(char *str
, const char *trim_str
);
159 rtrim(char *str
, const char *trim_str
, size_t min
);
162 strchrcount(char *str
, char c
);
165 strpref(char *s1
, char *s2
);
168 stricmp(const char *s1
, const char *s2
);
171 xstrtok_r(char *str
, const char *delim
, char **end
);
174 eval_yes(char *value
, int *invalid
);
177 get_word(char *str
, char *buf
, size_t len
);
179 /* ************************* */
180 /* ctxopt static prototypes. */
181 /* ************************* */
183 typedef struct flags_s flags_t
;
184 typedef struct opt_s opt_t
;
185 typedef struct par_s par_t
;
186 typedef struct ctx_s ctx_t
;
187 typedef struct constraint_s constraint_t
;
188 typedef struct ctx_inst_s ctx_inst_t
;
189 typedef struct opt_inst_s opt_inst_t
;
190 typedef struct seen_opt_s seen_opt_t
;
191 typedef struct req_s req_t
;
194 strtoken(char *s
, char *token
, size_t tok_len
, char *pattern
, int *pos
);
197 ctx_compare(const void *c1
, const void *c2
);
203 ctx_inst_free(void *ci
);
206 opt_inst_free(void *oi
);
209 seen_opt_compare(const void *so1
, const void *so2
);
212 incomp_bst_free(void *b
);
218 seen_opt_free(void *seen_opt
);
221 opt_compare(const void *o1
, const void *o2
);
227 par_compare(const void *a1
, const void *a2
);
233 constraint_free(void *cstr
);
236 locate_ctx(char *name
);
239 locate_opt(char *name
);
242 locate_par(char *name
, ctx_t
*ctx
);
245 print_before_constraints(ll_t
*list
);
248 print_options(ll_t
*list
,
252 int *has_generic_arg
,
254 int *has_early_eval
);
256 print_explanations(int has_early_eval
,
263 bst_seen_opt_cb(const void *node
, walk_order_e kind
, int level
);
266 bst_seen_opt_seen_cb(const void *node
, walk_order_e kind
, int level
);
269 bst_print_ctx_cb(const void *node
, walk_order_e kind
, int level
);
272 bst_check_opt_cb(const void *node
, walk_order_e kind
, int level
);
275 bst_match_par_cb(const void *node
, walk_order_e kind
, int level
);
278 match_prefix_cb(const void *node
, walk_order_e kind
, int level
);
281 has_unseen_mandatory_opt(ctx_inst_t
*ctx_inst
, char **missing
);
284 opt_parse(char *s
, opt_t
**opt
);
287 init_opts(char *spec
, ctx_t
*ctx
);
290 ctxopt_build_cmdline_list(int nb_words
, char **words
);
293 opt_set_parms(char *opt_name
, char *par_str
);
296 new_ctx_inst(ctx_t
*ctx
, ctx_inst_t
*prev_ctx_inst
);
299 evaluate_ctx_inst(ctx_inst_t
*ctx_inst
);
301 /* ****************************** */
302 /* Fatal messages implementation. */
303 /* ****************************** */
305 /* =================================================================== */
306 /* Fatal error function used when a fatal condition is encountered. */
307 /* This function is reserved for the ctxopt internal usage. */
309 /* format : printf like format. */
310 /* ... : remaining arguments interpreted using the format argument. */
311 /* =================================================================== */
313 fatal_internal(const char *format
, ...)
317 fprintf(stderr
, "CTXOPT: ");
319 va_start(args
, format
);
320 vfprintf(stderr
, format
, args
);
321 fprintf(stderr
, "\n");
327 /* ====================================================================== */
328 /* Generic fatal error function. This one uses the global status ctxopt */
329 /* stored in the cur_state structure and can call custom error functions. */
330 /* registered by the users for a given error identifier if any. */
332 /* e : Error identifier responsible of the fatal error. */
333 /* errmsg : User's provided string specific to the error e. */
334 /* Note that errmsg is not used in all cases. */
336 /* CTXOPTMISPAR Missing parameter. */
337 /* CTXOPTREQPAR Option: all parameters in a required group are */
339 /* CTXOPTMISARG Missing argument. */
340 /* CTXOPTUXPARG Unexpected argument. */
341 /* CTXOPTDUPOPT Duplicated option. */
342 /* CTXOPTUNKPAR Unknown parameter. */
343 /* CTXOPTINCOPT Incompatible option. */
344 /* CTXOPTCTEOPT Option: bad number of occurrences. */
345 /* CTXOPTCTLOPT Option: not enough occurrences. */
346 /* CTXOPTCTGOPT Option: too many occurrence of. */
347 /* CTXOPTCTEARG Arguments: bad number of occurrences. */
348 /* CTXOPTCTLARG Arguments: not enough occurrences. */
349 /* CTXOPTCTGARG Arguments: too many occurrences. */
350 /* ====================================================================== */
352 fatal(errors e
, char *errmsg
)
354 if (err_functions
[e
] != NULL
)
355 err_functions
[e
](e
, cur_state
);
364 if (cur_state
->ctx_par_name
!= NULL
)
366 "the mandatory parameter(s) %s are missing in the context "
367 "introduced by %s.\n",
369 cur_state
->ctx_par_name
);
372 "The mandatory parameter(s) %s are missing "
373 "in the main context.\n",
382 cur_state
->req_opt_par_needed
,
383 cur_state
->req_opt_par
);
387 if (cur_state
->cur_opt_par_name
!= NULL
)
389 "The parameter %s takes no arguments "
390 "or has too many arguments.\n",
391 cur_state
->cur_opt_par_name
);
395 if (cur_state
->pre_opt_par_name
!= NULL
)
397 "%s requires argument(s).\n",
398 cur_state
->pre_opt_par_name
);
401 "%s requires argument(s).\n",
402 cur_state
->cur_opt_par_name
);
406 if (cur_state
->pre_opt_par_name
!= NULL
)
408 "The parameter %s can only appear once in the context "
409 "introduced by %s.\n",
410 cur_state
->cur_opt_params
,
411 cur_state
->ctx_par_name
);
414 "The parameter %s can only appear once "
415 "in the main context.\n",
416 cur_state
->cur_opt_params
);
421 "Unknown parameter %s.\n%s",
422 cur_state
->cur_opt_par_name
,
428 "The parameter %s is incompatible with %s.\n",
429 cur_state
->cur_opt_par_name
,
434 if (cur_state
->ctx_par_name
)
436 "The parameter %s must appear exactly %d times "
437 "in the context introduced by %s.\n",
438 cur_state
->cur_opt_params
,
439 cur_state
->opts_count
,
440 cur_state
->ctx_par_name
);
443 "The parameter %s must appear exactly %d times "
444 "in the main context.\n",
445 cur_state
->cur_opt_params
,
446 cur_state
->opts_count
);
450 if (cur_state
->ctx_par_name
)
452 "The parameter %s must appear less than %d times "
453 "in the context introduced by %s.\n",
454 cur_state
->cur_opt_params
,
455 cur_state
->opts_count
,
456 cur_state
->ctx_par_name
);
459 "The parameter %s must appear less than %d times "
460 "in the main context.\n",
461 cur_state
->cur_opt_params
,
462 cur_state
->opts_count
);
466 if (cur_state
->ctx_par_name
)
468 "The parameter %s must appear more than %d times "
469 "in the context introduced by %s.\n",
470 cur_state
->cur_opt_params
,
471 cur_state
->opts_count
,
472 cur_state
->ctx_par_name
);
475 "The parameter %s must appear more than %d times "
476 "in the main context.\n",
477 cur_state
->cur_opt_params
,
478 cur_state
->opts_count
);
483 "The parameter %s must have exactly %d arguments.\n",
484 cur_state
->cur_opt_par_name
,
485 cur_state
->opt_args_count
);
490 "The parameter %s must have less than %d arguments.\n",
491 cur_state
->cur_opt_par_name
,
492 cur_state
->opt_args_count
);
497 "The parameter %s must have more than %d arguments.\n",
498 cur_state
->cur_opt_par_name
,
499 cur_state
->opt_args_count
);
507 /* CTXOPTUNKPAR should display the full usage to help the user follow */
508 /* the chaining of contexts when several possible contexts have been */
509 /* identified. Otherwise, errmsg is the empty string and the display of */
510 /* the current usage is enough. */
511 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
512 if (e
== CTXOPTUNKPAR
&& *errmsg
!= '\0')
513 ctxopt_disp_usage(continue_after
);
515 ctxopt_ctx_disp_usage(NULL
, continue_after
);
517 exit(e
); /* Exit with the error id e as return code. */
520 /* ********************************* */
521 /* Memory management implementation. */
522 /* ********************************* */
524 /* ================== */
525 /* Customized malloc. */
526 /* ================== */
533 real_size
= (size
> 0) ? size
: 1;
534 allocated
= malloc(real_size
);
535 if (allocated
== NULL
)
536 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
537 (unsigned long int)size
);
542 /* ================== */
543 /* Customized calloc. */
544 /* ================== */
546 xcalloc(size_t n
, size_t size
)
551 size
= (size
> 0) ? size
: 1;
552 allocated
= calloc(n
, size
);
553 if (allocated
== NULL
)
554 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
555 (unsigned long int)size
);
560 /* =================== */
561 /* Customized realloc. */
562 /* =================== */
564 xrealloc(void *p
, size_t size
)
568 allocated
= realloc(p
, size
);
569 if (allocated
== NULL
&& size
> 0)
570 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
571 (unsigned long int)size
);
576 /* ==================================== */
577 /* strdup implementation using xmalloc. */
578 /* ==================================== */
580 xstrdup(const char *p
)
584 allocated
= xmalloc(strlen(p
) + 1);
585 strcpy(allocated
, p
);
590 /* =================================================== */
591 /* strndup implementation using xmalloc. */
592 /* This version guarantees that there is a final '\0'. */
593 /* =================================================== */
595 xstrndup(const char *str
, size_t len
)
599 p
= memchr(str
, '\0', len
);
604 p
= xmalloc(len
+ 1);
611 /* *************************** */
612 /* Linked list implementation. */
613 /* *************************** */
615 /* Linked list node structure. */
616 /* """"""""""""""""""""""""""" */
620 struct ll_node_s
*next
;
621 struct ll_node_s
*prev
;
624 /* Linked List structure. */
625 /* """""""""""""""""""""" */
633 /* ========================= */
634 /* Create a new linked list. */
635 /* ========================= */
639 ll_t
*ret
= xmalloc(sizeof(ll_t
));
645 /* =============================================== */
646 /* Free all the elements of a list (make it empty) */
647 /* NULL or a custom function may be used to free */
648 /* the sub components of the elements. */
649 /* =============================================== */
651 ll_free(ll_t
* const list
, void (*clean
)(void *))
661 /* Apply a custom cleaner if not NULL. */
662 /* """"""""""""""""""""""""""""""""""" */
666 ll_delete(list
, node
);
673 /* ==================================== */
674 /* Destroy a list and all its elements. */
675 /* ==================================== */
677 ll_destroy(ll_t
*list
, void (*clean
)(void *))
681 ll_free(list
, clean
);
686 /* ========================= */
687 /* Initialize a linked list. */
688 /* ========================= */
697 /* ===================================================== */
698 /* Allocate the space for a new node in the linked list. */
699 /* ===================================================== */
703 ll_node_t
*ret
= xmalloc(sizeof(ll_node_t
));
708 /* ==================================================================== */
709 /* Append a new node filled with its data at the end of the linked list */
710 /* The user is responsible for the memory management of the data. */
711 /* ==================================================================== */
713 ll_append(ll_t
* const list
, void * const data
)
717 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
718 | uses xmalloc which does not return if there *
719 | is an allocation error. */
722 node
->next
= NULL
; /* This node will be the last. */
723 node
->prev
= list
->tail
; /* NULL if it is a new list. */
726 list
->tail
->next
= node
;
732 ++list
->len
; /* One more node in the list. */
735 /* ================================================================== */
736 /* Put a new node filled with its data at the beginning of the linked */
738 /* The user is responsible for the memory management of the data. */
739 /* ================================================================== */
741 ll_prepend(ll_t
* const list
, void * const data
)
745 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
746 | uses xmalloc which does not return if there *
747 | is an allocation error. */
750 node
->prev
= NULL
; /* This node will be the first. */
751 node
->next
= list
->head
; /* NULL if it is a new list. */
754 list
->head
->prev
= node
;
760 ++list
->len
; /* One more node in the list. */
763 /* ======================================================== */
764 /* Insert a new node before the specified node in the list. */
765 /* ======================================================== */
767 ll_insert_before(ll_t
* const list
, ll_node_t
*node
, void * const data
)
771 if (node
->prev
== NULL
)
772 ll_prepend(list
, data
);
775 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
776 | uses xmalloc which does not return if there *
777 | is an allocation error. */
779 new_node
->data
= data
;
780 new_node
->next
= node
;
781 new_node
->prev
= node
->prev
;
783 node
->prev
->next
= new_node
;
784 node
->prev
= new_node
;
786 ++list
->len
; /* One more node in the list. */
790 /* ======================================================= */
791 /* Insert a new node after the specified node in the list. */
792 /* ======================================================= */
794 ll_insert_after(ll_t
* const list
, ll_node_t
*node
, void * const data
)
798 if (node
->next
== NULL
)
799 ll_append(list
, data
);
802 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
803 | uses xmalloc which does not return if there *
804 | is an allocation error. */
806 new_node
->data
= data
;
807 new_node
->prev
= node
;
808 new_node
->next
= node
->next
;
810 node
->next
->prev
= new_node
;
811 node
->next
= new_node
;
813 ++list
->len
; /* One more node in the list. */
817 /* ================================================================= */
818 /* Remove a node from a linked list. */
819 /* The memory taken by the deleted node must be freed by the caller. */
820 /* ================================================================= */
822 ll_delete(ll_t
* const list
, ll_node_t
*node
)
824 if (list
->head
== list
->tail
)
826 /* We delete the last remaining element from the list. */
827 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
828 if (list
->head
== NULL
)
831 list
->head
= list
->tail
= NULL
;
833 else if (node
->prev
== NULL
)
835 /* We delete the first element from the list. */
836 /* """""""""""""""""""""""""""""""""""""""""" */
837 list
->head
= node
->next
;
838 list
->head
->prev
= NULL
;
840 else if (node
->next
== NULL
)
842 /* We delete the last element from the list. */
843 /* """"""""""""""""""""""""""""""""""""""""" */
844 list
->tail
= node
->prev
;
845 list
->tail
->next
= NULL
;
849 /* We delete an element from the list. */
850 /* """"""""""""""""""""""""""""""""""" */
851 node
->next
->prev
= node
->prev
;
852 node
->prev
->next
= node
->next
;
857 --list
->len
; /* One less node in the list. */
862 /* ==================================================================== */
863 /* Allocate and fill an array of strings from a list. */
865 /* 1) The list node must contain strings (char *). */
866 /* 2) The strings in the resulting array MUST NOT be freed as the are */
867 /* NOT copied from the strings of the list. */
869 /* IN list : The list from which the array is generated. */
870 /* IN start_node : The node of the list which will be the first node to */
871 /* consider to create the array. */
872 /* OUT: count : The number of elements of the resulting array. */
873 /* OUT: array : The resulting array or NULL if the list is empty. */
874 /* RC : : The number of elements of the resulting array. */
875 /* ==================================================================== */
877 ll_strarray(ll_t
*list
, ll_node_t
*start_node
, int *count
, char ***array
)
886 if (list
== NULL
|| node
== NULL
)
893 *array
= xmalloc((list
->len
+ 1) * sizeof(char *));
896 (*array
)[n
++] = (char *)(node
->data
);
902 (*array
)[*count
] = NULL
;
907 /* ******************************************************************* */
908 /* BST (search.h compatible) implementation. */
910 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
911 /* the AT&T man page says. */
913 /* Written by reading the System V Interface Definition, not the code. */
915 /* Totally public domain. */
916 /* ******************************************************************* */
925 #if 0 /* Unused yet. */
926 /* =========================== */
927 /* Delete node with given key. */
928 /* =========================== */
930 bst_delete(const void * vkey
, void ** vrootp
,
931 int (*compar
)(const void *, const void *))
933 bst_t
** rootp
= (bst_t
**)vrootp
;
937 if (rootp
== NULL
|| (p
= *rootp
) == NULL
)
940 while ((cmp
= (*compar
)(vkey
, (*rootp
)->key
)) != 0)
943 rootp
= (cmp
< 0) ? &(*rootp
)->llink
/* follow llink branch */
944 : &(*rootp
)->rlink
; /* follow rlink branch */
946 return NULL
; /* key not found */
948 r
= (*rootp
)->rlink
; /* D1: */
949 if ((q
= (*rootp
)->llink
) == NULL
) /* Left NULL? */
952 { /* Right link is NULL? */
953 if (r
->llink
== NULL
)
954 { /* D2: Find successor */
959 { /* D3: Find NULL link */
960 for (q
= r
->llink
; q
->llink
!= NULL
; q
= r
->llink
)
963 q
->llink
= (*rootp
)->llink
;
964 q
->rlink
= (*rootp
)->rlink
;
968 free(*rootp
); /* D4: Free node */
969 *rootp
= q
; /* link parent to new node */
974 /* ===================================================================== */
975 /* Destroy a tree. */
976 /* The clean function pointer can be NULL, in this case the node content */
978 /* ===================================================================== */
980 bst_destroy(void *vrootp
, void (*clean
)(void *))
982 bst_t
*root
= (bst_t
*)vrootp
;
987 bst_destroy(root
->llink
, clean
);
988 bst_destroy(root
->rlink
, clean
);
991 clean((void *)root
->key
);
996 /* ========================= */
997 /* Find a node, or return 0. */
998 /* ========================= */
1000 bst_find(const void *vkey
,
1001 void * const *vrootp
,
1002 int (*compar
)(const void *, const void *))
1004 bst_t
* const *rootp
= (bst_t
* const *)vrootp
;
1009 while (*rootp
!= NULL
)
1013 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
1014 return *rootp
; /* key found */
1015 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
1016 : &(*rootp
)->rlink
; /* T4: follow right branch */
1021 /* ======================================= */
1022 /* Find or inserts datum into search tree. */
1023 /* ======================================= */
1025 bst_search(void *vkey
, void **vrootp
, int (*compar
)(const void *, const void *))
1028 bst_t
**rootp
= (bst_t
**)vrootp
;
1033 while (*rootp
!= NULL
)
1037 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
1038 return *rootp
; /* we found it! */
1040 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
1041 : &(*rootp
)->rlink
; /* T4: follow right branch */
1044 q
= xmalloc(sizeof(bst_t
)); /* T5: key not found */
1046 { /* make new node */
1047 *rootp
= q
; /* link new node to old */
1048 q
->key
= vkey
; /* initialize new node */
1049 q
->llink
= q
->rlink
= NULL
;
1054 /* ========================= */
1055 /* Walk the nodes of a tree. */
1056 /* ========================= */
1058 bst_walk_recurse(const bst_t
*root
,
1059 void (*action
)(const void *, walk_order_e
, int),
1062 if (root
->llink
== NULL
&& root
->rlink
== NULL
)
1063 (*action
)(root
, leaf
, level
);
1066 (*action
)(root
, preorder
, level
);
1067 if (root
->llink
!= NULL
)
1068 bst_walk_recurse(root
->llink
, action
, level
+ 1);
1069 (*action
)(root
, postorder
, level
);
1070 if (root
->rlink
!= NULL
)
1071 bst_walk_recurse(root
->rlink
, action
, level
+ 1);
1072 (*action
)(root
, endorder
, level
);
1077 bst_walk(const void *vroot
, void (*action
)(const void *, walk_order_e
, int))
1079 if (vroot
!= NULL
&& action
!= NULL
)
1080 bst_walk_recurse(vroot
, action
, 0);
1083 /* ************************ */
1084 /* Various implementations. */
1085 /* ************************ */
1087 /* ======================== */
1088 /* Trim leading characters. */
1089 /* ======================== */
1091 ltrim(char *str
, const char *trim_str
)
1093 size_t len
= strlen(str
);
1094 size_t begin
= strspn(str
, trim_str
);
1098 for (i
= begin
; i
<= len
; ++i
)
1099 str
[i
- begin
] = str
[i
];
1102 /* ================================================= */
1103 /* Trim trailing characters. */
1104 /* The resulting string will have at least min bytes */
1105 /* even if trailing spaces remain. */
1106 /* ================================================= */
1108 rtrim(char *str
, const char *trim_str
, size_t min
)
1110 size_t len
= strlen(str
);
1111 while (len
> min
&& strchr(trim_str
, str
[len
- 1]))
1115 /* ================================================== */
1116 /* Count the number of occurrences of the character c */
1117 /* in the string str. */
1118 /* The str pointer is assumed to be not NULL. */
1119 /* ================================================== */
1121 strchrcount(char *str
, char c
)
1132 /* =============================================== */
1133 /* Is the string str2 a prefix of the string str1? */
1134 /* =============================================== */
1136 strpref(char *str1
, char *str2
)
1138 while (*str1
!= '\0' && *str1
== *str2
)
1144 return *str2
== '\0';
1147 /* ========================== */
1148 /* Like strcmp ignoring case. */
1149 /* ========================== */
1151 stricmp(const char *s1
, const char *s2
)
1153 while (tolower((unsigned char)*s1
) == tolower((unsigned char)*s2
))
1162 return (int)tolower((unsigned char)*s1
) - (int)tolower((unsigned char)*s2
);
1165 /* ====================================================================== */
1166 /* Strings concatenation with dynamic memory allocation. */
1167 /* IN : a variable number of char * arguments with NULL terminating */
1169 /* The first one must have been dynamically allocated and is */
1172 /* Returns a new allocated string containing the concatenation of all */
1173 /* the arguments. It is the caller's responsibility to free the resulting */
1175 /* ====================================================================== */
1177 strappend(char *str
, ...)
1183 l
= 1 + strlen(str
);
1184 va_start(args
, str
);
1186 s
= va_arg(args
, char *);
1191 s
= va_arg(args
, char *);
1197 str
= xrealloc(str
, l
);
1199 va_start(args
, str
);
1200 s
= va_arg(args
, char *);
1205 s
= va_arg(args
, char *);
1212 /* ====================================================================== */
1213 /* Public domain strtok_r() by Charlie Gordon. */
1214 /* from comp.lang.c 9/14/2007 */
1215 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1217 /* (Declaration that it's public domain): */
1218 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1220 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1222 /* ====================================================================== */
1224 xstrtok_r(char *str
, const char *delim
, char **end
)
1234 str
+= strspn(str
, delim
);
1241 str
+= strcspn(str
, delim
);
1251 /* ===================================================================== */
1252 /* Put the first word of str, truncated to len characters, in buf. */
1253 /* Return a pointer in str pointing just after the word. */
1254 /* buf must have been pre-allocated to accept at least len+1 characters. */
1255 /* Note that buf can contains a sting full of spaces is str was not */
1256 /* trimmed before the call. */
1257 /* ===================================================================== */
1259 get_word(char *str
, char *buf
, size_t len
)
1265 while (*s
&& isspace(*s
))
1268 /* Set the new string start. */
1269 /* """"""""""""""""""""""""" */
1274 while (*s
&& !isspace(*s
) && s
- str
< (ptrdiff_t)len
)
1277 strncpy(buf
, str
, s
- str
);
1283 /* ==================================================================== */
1284 /* Return 1 is value is "1" or "yes" (ignoring case). */
1285 /* Return 0 is value is "0" or "no" (ignoring case). */
1286 /* If value has another value, then set invalid to 1 and also return 0 */
1287 /* invalid is set to 0 in all the other cases. */
1288 /* ==================================================================== */
1290 eval_yes(char *value
, int *invalid
)
1294 if (strcmp(value
, "1") == 0 || stricmp(value
, "yes") == 0)
1296 else if (strcmp(value
, "0") != 0 && stricmp(value
, "no") != 0)
1302 /* =========================================================== */
1303 /* Fill an array of strings from the words composing a string. */
1305 /* str: initial string which will be altered. */
1306 /* args: array of pointers to the start of the words in str. */
1307 /* max: maximum number of words used before giving up. */
1308 /* return: the number of words (<=max). */
1309 /* =========================================================== */
1311 str2argv(char *str
, char **args
, int max
)
1320 while (*str
== ' ' || *str
== '\t')
1326 args
[nb_args
] = str
;
1329 while (*str
&& (*str
!= ' ') && (*str
!= '\t'))
1336 /* ********************** */
1337 /* ctxopt implementation. */
1338 /* ********************** */
1340 static int ctxopt_initialized
= 0; /* cap_init has not yet been called. */
1342 /* Flags structure initialized by ctxopt_init. */
1343 /* """"""""""""""""""""""""""""""""""""""""""" */
1346 int stop_if_non_option
;
1347 int allow_abbreviations
;
1348 int display_usage_on_error
;
1351 static flags_t flags
= { 0, 1, 1 };
1353 /* Context structure. */
1354 /* """""""""""""""""" */
1358 ll_t
*opt_list
; /* list of options allowed in this context. */
1359 ll_t
*incomp_list
; /* list of strings containing incompatible names *
1360 | of options separated by spaces or tabs. */
1361 ll_t
*req_list
; /* list of strings containing an option name and *
1362 | all the option names where at least one of *
1363 | them is required to be also present. */
1365 int (*action
)(char *name
,
1375 /* https://textik.com/#488ce3649b6c60f5 */
1377 /* +--------------+ */
1378 /* |first_ctx_inst| */
1379 /* +---+----------+ */
1381 /* +--v-----+ +--------+ +--------+ +-----+ */
1382 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1383 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1385 /* | | +-v------+ | | */
1386 /* | +--+ctx_inst<-----------+ | */
1387 /* | +-+------+ | */
1389 /* | +-v------+ | */
1390 /* +------+ctx_inst<--------------------------+ */
1397 /* Option structure. */
1398 /* """"""""""""""""" */
1401 char *name
; /* option name. */
1402 char *next_ctx
; /* new context this option may lead to */
1403 ll_t
*ctx_list
; /* list of contexts allowing this option. */
1404 char *params
; /* string containing all the parameters of *
1407 void (*action
)( /* The option associated action. */
1408 char *ctx_name
, /* context name. */
1409 char *opt_name
, /* option name. */
1410 char *par
, /* option parameter. */
1411 int nb_args
, /* number of arguments. */
1412 char **args
, /* option arguments. */
1413 int nb_opt_data
, /* number of option data pointers. */
1414 void **opt_data
, /* option data pointers. */
1415 int nb_ctx_data
, /* nb of current context data ptrs. */
1416 void **ctx_data
/* current context data pointers. */
1419 int visible_in_help
; /* visibility in help. */
1420 int nb_data
; /* number of the data pointers passed as argument to action. */
1421 void **data
; /* array of data pointers passed as argument to action. */
1423 int args
; /* 1 if this option takes arguments else 0. */
1424 int optional
; /* 1 if the option is optional, else 0. */
1425 int multiple
; /* 1 if the option can appear more than one time in a *
1426 | context, else 0. */
1428 int opt_count_matter
; /* 1 if we must restrict the count, else 0. */
1429 int occurrences
; /* Number of option occurrences in a context. */
1430 char opt_count_oper
; /* <, = or > */
1431 int opt_count_mark
; /* Value to be compared to with opt_count_oper. */
1433 char *arg
; /* symbolic text after # describing the option argument. */
1435 int optional_args
; /* 1 of option is optional else 0. */
1436 int multiple_args
; /* 1 is option can appear more than once in a context *
1439 int opt_args_count_matter
; /* 1 if count is restricted, else 0. */
1440 char opt_args_count_oper
; /* <, = or > */
1441 int opt_args_count_mark
; /* Value to be compared to with *
1442 | opt_count_oper. */
1444 int eval_first
; /* 1 if this option must be evaluated before the options *
1445 | without this mark. */
1447 ll_t
*eval_before_list
; /* List of pointers on options which must be *
1448 | evaluated before this option. */
1450 ll_t
*constraints_list
; /* List of constraint check functions pointers. */
1453 /* Context instance structure. */
1454 /* """"""""""""""""""""""""""" */
1457 ctx_t
*ctx
; /* the context whose this is an instance of */
1458 ctx_inst_t
*prev_ctx_inst
; /* ctx_inst of the opt_inst which led to the *
1459 | creation of this ctx_inst structure. */
1460 opt_inst_t
*gen_opt_inst
; /* opt_inst which led to the creation of a *
1461 | instance of this structure. */
1462 ll_t
*incomp_bst_list
; /* list of seen_opt_t BST. */
1463 void *seen_opt_bst
; /* tree of seen_opt_t. */
1464 ll_t
*opt_req_list
; /* list of req_t. */
1465 ll_t
*opt_inst_list
; /* The list of option instances in this *
1466 | context instance. */
1467 char *par_name
; /* parameter which created this instance. */
1470 /* Option instance structure. */
1471 /* """""""""""""""""""""""""" */
1474 opt_t
*opt
; /* The option this is an instance of. */
1475 char *opt_name
; /* The option which led to this creation. */
1476 char *par
; /* The parameter which led to this creation. */
1477 ll_t
*values_list
; /* The list of arguments of this option. */
1478 ctx_inst_t
*next_ctx_inst
; /* The new context instance this option. *
1479 | instance may create. */
1482 /* Structure used to check if an option has bee seen or not */
1483 /* in a context instance. */
1484 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1487 opt_t
*opt
; /* The concerned option. */
1488 char *par
; /* Parameter which led to the making of this structure. */
1489 int seen
; /* 1 if seen in the context instances, else 0. */
1492 /* Structure used to check if at least one instance of the options whose */
1493 /* pointers are in or_opt_list has been seen in the ctx_inst where an */
1494 /* instance or opt is also present. */
1495 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1498 opt_t
*opt
; /* Option that asks for other options. */
1499 ll_t
*or_opt_list
; /* Required options, at least one of them *
1500 | must be present. */
1503 /* Parameter structure which links a parameter to the option it belongs to. */
1504 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1507 char *name
; /* Parameter name (with the leading -). */
1508 opt_t
*opt
; /* Attached option. */
1511 /* Constraint structure. */
1512 /* """"""""""""""""""""" */
1515 int (*constraint
)(int nb_args
, char **args
, char *value
, char *parameter
);
1518 char *to_free
; /* pointer to the original string in which the array in *
1519 | args points to. This pointer is kept there to allow *
1520 | it to be freed. */
1523 state_t
*cur_state
= NULL
; /* Current analysis state. */
1524 static ll_t
*cmdline_list
= NULL
; /* List of interpreted CLI words *
1525 | serves as the basis for the *
1526 | analysis of the parameters. */
1527 static ctx_t
*main_ctx
= NULL
; /* initial context. */
1528 static ctx_inst_t
*first_ctx_inst
= NULL
; /* Pointer to the fist context *
1529 | instance which holds the *
1530 | options instances. */
1531 static ll_t
*ctx_inst_list
= NULL
; /* List of the context instances. */
1533 /* ======================================================= */
1534 /* Parse a string for the next matching token. */
1536 /* s: string to parse. */
1537 /* token: pre_allocated array of max tok_len characters. */
1538 /* pattern: scanf type pattern token must match. */
1539 /* pos: number of characters successfully parsed in s. */
1541 /* Returns: a pointer to the first unread character or */
1542 /* to he terminating \0. */
1543 /* ======================================================= */
1545 strtoken(char *s
, char *token
, size_t tok_len
, char *pattern
, int *pos
)
1553 n
= snprintf(len
, 3, "%zu", tok_len
);
1557 full_pattern
= xmalloc(strlen(pattern
) + n
+ 4);
1559 strcpy(full_pattern
, "%");
1560 strcat(full_pattern
, len
);
1561 strcat(full_pattern
, pattern
);
1562 strcat(full_pattern
, "%n");
1564 n
= sscanf(s
, full_pattern
, token
, pos
);
1574 /* ****************************************** */
1575 /* Various comparison and deletion functions. */
1576 /* ****************************************** */
1579 ctx_compare(const void *c1
, const void *c2
)
1581 return strcmp(((ctx_t
*)c1
)->name
, ((ctx_t
*)c2
)->name
);
1584 /* =========================== */
1585 /* Free a context_bst element. */
1586 /* =========================== */
1595 ll_destroy(ctx
->opt_list
, NULL
);
1596 ll_destroy(ctx
->incomp_list
, free
);
1597 ll_destroy(ctx
->req_list
, free
);
1598 bst_destroy(ctx
->par_bst
, par_free
);
1603 /* ============================= */
1604 /* Free a ctx_inst_list element. */
1605 /* ============================= */
1607 ctx_inst_free(void *ci
)
1609 ctx_inst_t
*ctx_inst
= ci
;
1611 free(ctx_inst
->par_name
);
1612 ll_destroy(ctx_inst
->incomp_bst_list
, incomp_bst_free
);
1613 bst_destroy(ctx_inst
->seen_opt_bst
, seen_opt_free
);
1614 ll_destroy(ctx_inst
->opt_inst_list
, opt_inst_free
);
1615 ll_destroy(ctx_inst
->opt_req_list
, req_free
);
1620 /* ============================== */
1621 /* Free an opt_inst_list element. */
1622 /* ============================== */
1624 opt_inst_free(void *oi
)
1626 opt_inst_t
*opt_inst
= oi
;
1628 ll_destroy(opt_inst
->values_list
, NULL
);
1633 /* ================================== */
1634 /* Compare two seen_opt_bst elements. */
1635 /* ================================== */
1637 seen_opt_compare(const void *so1
, const void *so2
)
1641 o1
= ((seen_opt_t
*)so1
)->opt
;
1642 o2
= ((seen_opt_t
*)so2
)->opt
;
1644 return strcmp(o1
->name
, o2
->name
);
1647 /* ============================ */
1648 /* Free a seen_opt_bst element. */
1649 /* ============================ */
1651 seen_opt_free(void *so
)
1653 seen_opt_t
*seen_opt
= so
;
1655 free(seen_opt
->par
);
1660 /* =========================== */
1661 /* Free an incomp_bst element. */
1662 /* =========================== */
1664 incomp_bst_free(void *b
)
1668 bst_destroy(bst
, NULL
);
1671 /* ============================= */
1672 /* Free an opt_req_list element. */
1673 /* ============================= */
1679 ll_destroy(req
->or_opt_list
, NULL
);
1683 /* ================================= */
1684 /* Compare two options_bst elements. */
1685 /* ================================= */
1687 opt_compare(const void *o1
, const void *o2
)
1689 return strcmp(((opt_t
*)o1
)->name
, ((opt_t
*)o2
)->name
);
1692 /* ============================= */
1693 /* Free an options_bst elements. */
1694 /* ============================= */
1701 free(opt
->next_ctx
);
1706 ll_destroy(opt
->ctx_list
, NULL
);
1707 ll_destroy(opt
->constraints_list
, constraint_free
);
1708 ll_destroy(opt
->eval_before_list
, NULL
);
1713 /* ============================= */
1714 /* Compare two par_bst elements. */
1715 /* ============================= */
1717 par_compare(const void *a1
, const void *a2
)
1719 return strcmp(((par_t
*)a1
)->name
, ((par_t
*)a2
)->name
);
1722 /* ======================= */
1723 /* Free a par_bst element. */
1724 /* ======================= */
1735 /* ================================ */
1736 /* Free a constraints_list element. */
1737 /* ================================ */
1739 constraint_free(void *c
)
1741 constraint_t
*cstr
= c
;
1744 free(cstr
->to_free
);
1749 /* ******************************************************************** */
1750 /* Helper functions to locate contexts, options and parameters in a BST */
1751 /* by their names. */
1752 /* ******************************************************************** */
1755 locate_ctx(char *name
)
1762 if ((node
= bst_find(&ctx
, &contexts_bst
, ctx_compare
)) == NULL
)
1769 locate_opt(char *name
)
1776 if ((node
= bst_find(&opt
, &options_bst
, opt_compare
)) == NULL
)
1783 locate_par(char *name
, ctx_t
*ctx
)
1787 void *bst
= ctx
->par_bst
;
1791 if ((node
= bst_find(&par
, &bst
, par_compare
)) == NULL
)
1797 /* ====================================================================== */
1798 /* Helper function to display the dependency constraints between options. */
1799 /* These constraints are set with the ctxopt_add_opt_settings function */
1800 /* using the 'before' and 'after' arguments. */
1801 /* IN list : a list of options. */
1802 /* ====================================================================== */
1804 print_before_constraints(ll_t
*list
)
1806 ll_node_t
*node
= list
->head
;
1807 ll_node_t
*before_node
;
1808 opt_t
*opt
, *before_opt
;
1811 while (node
!= NULL
)
1815 if (opt
->eval_before_list
->len
> 0)
1819 printf("\n If present in the command line,");
1820 msg
= 1; /* Display this message only once. */
1823 before_node
= opt
->eval_before_list
->head
;
1826 while (before_node
!= NULL
)
1828 before_opt
= before_node
->data
;
1829 printf("%s", before_opt
->params
);
1831 before_node
= before_node
->next
;
1833 if (before_node
!= NULL
)
1836 printf(" will be evaluated after %s\n", opt
->params
);
1842 /* =================================================================== */
1843 /* Utility function to format and print the options present in a list. */
1845 /* IN list : a list of options. */
1846 /* OUT has_* : a set of flags which will determine the content of the */
1847 /* explanation given after the formatted printing of the */
1849 /* =================================================================== */
1851 print_options(ll_t
*list
,
1855 int *has_generic_arg
,
1856 int *has_ctx_change
,
1857 int *has_early_eval
)
1859 ll_node_t
*node
= list
->head
;
1864 line
= xstrdup(" ");
1866 while (node
!= NULL
)
1868 option
= xstrdup("");
1871 /* Skip option set as not visible in help. */
1872 /* """"""""""""""""""""""""""""""""""""""" */
1873 if (!opt
->visible_in_help
)
1881 option
= strappend(option
, "[", (char *)0);
1885 if (opt
->eval_first
)
1887 option
= strappend(option
, "*", (char *)0);
1888 *has_early_eval
= 1;
1891 option
= strappend(option
, opt
->params
, (char *)0);
1893 if (opt
->next_ctx
!= NULL
)
1895 option
= strappend(option
, ">", opt
->next_ctx
, (char *)0);
1896 *has_ctx_change
= 1;
1901 if (opt
->opt_count_oper
!= '\0')
1905 o
[0] = opt
->opt_count_oper
;
1907 snprintf(m
, 3, "%u", opt
->opt_count_mark
);
1908 option
= strappend(option
, "...", o
, m
, (char *)0);
1912 option
= strappend(option
, "...", (char *)0);
1919 if (*(opt
->arg
) == '#')
1920 *has_generic_arg
= 1;
1922 option
= strappend(option
, " ", (char *)0);
1924 if (opt
->optional_args
)
1926 option
= strappend(option
, "[", opt
->arg
, (char *)0);
1930 option
= strappend(option
, opt
->arg
, (char *)0);
1932 if (opt
->multiple_args
)
1934 if (opt
->opt_args_count_oper
!= '\0')
1938 o
[0] = opt
->opt_args_count_oper
;
1940 snprintf(m
, 3, "%u", opt
->opt_args_count_mark
);
1941 option
= strappend(option
, "...", o
, m
, (char *)0);
1945 option
= strappend(option
, "...", (char *)0);
1949 if (opt
->optional_args
)
1950 option
= strappend(option
, "]", (char *)0);
1953 option
= strappend(option
, "]", (char *)0);
1955 if (strlen(line
) + 1 + strlen(option
) < 80)
1956 line
= strappend(line
, option
, " ", (char *)0);
1959 printf("%s\n", line
);
1961 line
= strappend(line
, option
, " ", (char *)0);
1969 printf("%s\n", line
);
1974 /* ==================================================== */
1975 /* Explain the special syntactic symbols present in the */
1976 /* generated usage messages. */
1977 /* ==================================================== */
1979 print_explanations(int has_early_eval
,
1981 int has_generic_arg
,
1986 if (has_early_eval
|| has_ctx_change
|| has_generic_arg
|| has_optional
1987 || has_ellipsis
|| has_rule
)
1989 printf("\nExplanation of the syntax used above:\n");
1990 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1991 "must be entered.\n");
1992 printf("The following is just there to explain the other symbols "
1996 printf("* : the parameters defined for this option will "
1997 "be evaluated first.\n");
1999 printf("> : the context after this symbol will be the new "
2000 "default context.\n");
2001 if (has_generic_arg
)
2002 printf("#tag : argument with a hint about its meaning.\n");
2004 printf("[...] : the object between square brackets is "
2007 printf("... : several occurrences of the previous object "
2010 printf("[<|=|>]number: rules constraining the number of "
2011 "parameters/arguments.\n");
2015 /* ************************************************************ */
2016 /* Various utilities and callback functions called when walking */
2017 /* through a BST. */
2018 /* ************************************************************ */
2021 bst_seen_opt_cb(const void *node
, walk_order_e kind
, int level
)
2023 seen_opt_t
*seen_opt
= ((bst_t
*)node
)->key
;
2025 if (kind
== postorder
|| kind
== leaf
)
2027 if ((!seen_opt
->opt
->optional
) && seen_opt
->seen
== 0)
2030 user_string
= strappend(user_string
,
2031 seen_opt
->opt
->params
,
2039 bst_seen_opt_seen_cb(const void *node
, walk_order_e kind
, int level
)
2041 seen_opt_t
*seen_opt
= ((bst_t
*)node
)->key
;
2043 if (kind
== postorder
|| kind
== leaf
)
2044 if (seen_opt
->seen
== 1)
2047 user_object
= seen_opt
->par
;
2052 bst_print_ctx_cb(const void *node
, walk_order_e kind
, int level
)
2054 ctx_t
*ctx
= main_ctx
;
2055 ctx_t
*cur_ctx
= ((bst_t
*)node
)->key
;
2059 int has_optional
= 0;
2060 int has_ellipsis
= 0;
2062 int has_generic_arg
= 0;
2063 int has_ctx_change
= 0;
2064 int has_early_eval
= 0;
2066 if (kind
== postorder
|| kind
== leaf
)
2067 if (strcmp(ctx
->name
, cur_ctx
->name
) != 0)
2069 list
= cur_ctx
->opt_list
;
2071 printf("\nAllowed options in the context %s:\n", cur_ctx
->name
);
2079 print_before_constraints(list
);
2084 bst_check_opt_cb(const void *node
, walk_order_e kind
, int level
)
2086 opt_t
*opt
= ((bst_t
*)node
)->key
;
2088 if (kind
== postorder
|| kind
== leaf
)
2090 if (opt
->params
== NULL
) /* opt must have associated parameters. */
2091 fatal_internal("Option %s has no registered parameter.\n", opt
->name
);
2093 if (opt
->action
== NULL
) /* opt must have an action. */
2094 fatal_internal("Option %s has no registered action.\n", opt
->name
);
2099 bst_match_par_cb(const void *node
, walk_order_e kind
, int level
)
2101 ctx_t
*ctx
= ((bst_t
*)node
)->key
;
2103 if (kind
== postorder
|| kind
== leaf
)
2105 char *str
= xstrdup(user_string
);
2107 while (*str
!= '\0')
2109 if (locate_par(str
, ctx
) != NULL
)
2111 if (*user_string2
== '\0')
2112 user_string2
= strappend(user_string2
, "- ", ctx
->name
, (char *)0);
2114 user_string2
= strappend(user_string2
, "\n- ", ctx
->name
, (char *)0);
2117 str
[strlen(str
) - 1] = '\0';
2124 match_prefix_cb(const void *node
, walk_order_e kind
, int level
)
2126 par_t
*par
= ((bst_t
*)node
)->key
;
2128 if (kind
== postorder
|| kind
== leaf
)
2129 if (strpref(par
->name
, (char *)user_object
))
2132 user_string
= strappend(user_string
, par
->name
, " ", (char *)0);
2136 /* ====================================================================== */
2137 /* A parameter may not be separated from its first option by spaces, in */
2138 /* this case this function looks for a valid flag as a prefix and splits */
2139 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
2142 /* IN word : the word to be checked. */
2143 /* IN ctx : the context in which the flag indexed by the word is to be */
2145 /* OUT pos : the offset in word pointing just after the matching prefix. */
2146 /* OUT opt : a pointer to the option associated with the new parameter */
2147 /* or NULL if none is found. */
2149 /* The returned pointer must be freed by the caller. */
2150 /* ====================================================================== */
2152 look_for_valid_prefix_in_word(char *word
, ctx_t
*ctx
, int *pos
, opt_t
**opt
)
2157 par_t tmp_par
= { 0 };
2163 new = xstrdup(word
);
2169 } while ((par
= locate_par(tmp_par
.name
, ctx
)) == NULL
&& len
> 2);
2188 /* ============================================================= */
2189 /* If par_name is an unique abbreviation of an exiting parameter */
2190 /* in the context ctx, then return this parameter. */
2191 /* ============================================================= */
2193 abbrev_expand(char *par_name
, ctx_t
*ctx
)
2195 user_object
= par_name
;
2198 *user_string
= '\0';
2199 bst_walk(ctx
->par_bst
, match_prefix_cb
);
2200 rtrim(user_string
, " ", 0);
2202 /* The previous bst_walk has built a string of blank separated parameters */
2203 /* all having par_name as prefix. This string is put in the user_string */
2204 /* exchange zone. The number of these words in put in user_rc. */
2205 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2206 if (user_rc
== 1) /* The number of matching abbreviations. */
2207 return xstrdup(user_string
);
2208 else /* There is at least two defined parameters starting with par_name. */
2214 void *tmp_opt_bst
= NULL
;
2216 /* Find all the options corresponding to these words and store them */
2217 /* without duplication in a temporary BST. Only their resulting count */
2219 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2220 s
= first_s
= strtok(user_string
, " "); /* first_s holds a copy of *
2221 | the first word. */
2224 par
= locate_par(s
, ctx
); /* par cannot be NULL here. */
2227 if (bst_find(opt
, &tmp_opt_bst
, opt_compare
) == NULL
)
2229 /* This option as not already been seen */
2230 /* store it and increase the seen counter. */
2231 /* """"""""""""""""""""""""""""""""""""""" */
2232 bst_search(opt
, &tmp_opt_bst
, opt_compare
);
2235 s
= strtok(NULL
, " ");
2238 /* Clean the temporary BST without removing the pointer */
2239 /* to the real options. */
2240 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2241 if (tmp_opt_bst
!= NULL
)
2242 bst_destroy(tmp_opt_bst
, NULL
);
2245 /* All the abbreviation are leading to only one option */
2246 /* We can just continue as in the previous case. */
2247 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2248 return xstrdup(first_s
);
2254 /* ================================================================ */
2255 /* Terminate the program if mandatory options required by a context */
2256 /* are not present. */
2257 /* ================================================================ */
2259 check_for_missing_mandatory_opt(ctx_inst_t
*ctx_inst
, char *opt_par
)
2263 if (has_unseen_mandatory_opt(ctx_inst
, &missing
))
2264 fatal(CTXOPTMISPAR
, missing
);
2267 /* ====================================================== */
2268 /* Return 1 if at least one mandatory option was not seen */
2269 /* when quitting a context, else 0. */
2270 /* ====================================================== */
2272 has_unseen_mandatory_opt(ctx_inst_t
*ctx_inst
, char **missing
)
2275 *user_string
= '\0';
2277 bst_walk(ctx_inst
->seen_opt_bst
, bst_seen_opt_cb
);
2278 rtrim(user_string
, " ", 0);
2280 *missing
= user_string
;
2282 return user_rc
? 1 : 0;
2285 /* ========================================================================= */
2286 /* This function terminates the program if an option or its arguments do not */
2287 /* conform to its occurrences constraint. */
2288 /* There constraints can appear by trailing >, < or = in their definition */
2289 /* given in ctxopt_new_ctx. */
2290 /* ========================================================================= */
2292 check_for_occurrence_issues(ctx_inst_t
*ctx_inst
)
2294 ctx_t
*ctx
= ctx_inst
->ctx
;
2297 opt_inst_t
*opt_inst
;
2298 char *cur_opt_params
= cur_state
->cur_opt_params
;
2299 char *cur_opt_par_name
= cur_state
->cur_opt_par_name
;
2301 /* Checks options. */
2302 /* """"""""""""""" */
2303 node
= ctx
->opt_list
->head
;
2305 while (node
!= NULL
)
2309 /* Update current_state. */
2310 /* """"""""""""""""""""" */
2311 cur_state
->cur_opt_params
= opt
->params
;
2312 cur_state
->opts_count
= opt
->opt_count_mark
;
2313 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2315 if (opt
->opt_count_matter
)
2316 switch (opt
->opt_count_oper
)
2319 if (opt
->occurrences
> 0 && opt
->opt_count_mark
!= opt
->occurrences
)
2320 fatal(CTXOPTCTEOPT
, "");
2324 if (opt
->occurrences
> 0 && opt
->opt_count_mark
<= opt
->occurrences
)
2325 fatal(CTXOPTCTLOPT
, "");
2329 if (opt
->occurrences
> 0 && opt
->opt_count_mark
>= opt
->occurrences
)
2330 fatal(CTXOPTCTGOPT
, "");
2337 /* Checks arguments. */
2338 /* """"""""""""""""" */
2339 node
= ctx_inst
->opt_inst_list
->head
;
2340 while (node
!= NULL
)
2342 opt_inst
= node
->data
;
2343 opt
= opt_inst
->opt
;
2345 /* Update current_state. */
2346 /* """"""""""""""""""""" */
2347 cur_state
->cur_opt_par_name
= opt_inst
->par
;
2348 cur_state
->opts_count
= opt
->opt_count_mark
;
2349 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2351 int nb_values
= opt_inst
->values_list
->len
; /* Number of arguments of opt */
2353 if (opt
->opt_args_count_matter
)
2354 switch (opt
->opt_args_count_oper
)
2357 if (nb_values
> 0 && opt
->opt_args_count_mark
!= nb_values
)
2358 fatal(CTXOPTCTEARG
, "");
2362 if (nb_values
> 0 && opt
->opt_args_count_mark
<= nb_values
)
2363 fatal(CTXOPTCTLARG
, "");
2367 if (nb_values
> 0 && opt
->opt_args_count_mark
>= nb_values
)
2368 fatal(CTXOPTCTGARG
, "");
2374 cur_state
->cur_opt_params
= cur_opt_params
;
2375 cur_state
->cur_opt_par_name
= cur_opt_par_name
;
2378 /* ====================================================================== */
2379 /* This function terminates the program if all the options which are part */
2380 /* of a group of required options by some other option are missing. */
2381 /* ====================================================================== */
2383 check_for_requirement_issues(ctx_inst_t
*ctx_inst
)
2386 ll_node_t
*req_node
;
2391 seen_opt_t tmp_seen_opt
;
2393 char *needed_params
= NULL
;
2395 node
= ctx_inst
->opt_req_list
->head
;
2397 while (node
!= NULL
)
2402 tmp_seen_opt
.opt
= opt
;
2404 bst_node
= bst_find(&tmp_seen_opt
,
2405 &(ctx_inst
->seen_opt_bst
),
2408 /* TODO: make sure bst_node cannot be NULL here. */
2410 if (bst_node
&& ((seen_opt_t
*)(bst_node
->key
))->seen
!= 0)
2413 req_node
= req
->or_opt_list
->head
;
2415 /* needed_params accumulates the params of the options in the group. */
2416 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2417 free(needed_params
); /* free can applied to the NULL pointer. */
2418 needed_params
= xstrdup("");
2420 /* Go through the list of the required group of options and */
2421 /* succeed when one of them has been seen in the context. */
2422 /* otherwise a fatal error is triggered and the program is */
2424 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2425 while (req_node
!= NULL
)
2427 req_opt
= req_node
->data
;
2428 tmp_seen_opt
.opt
= req_opt
;
2429 needed_params
= strappend(needed_params
,
2434 bst_node
= bst_find(&tmp_seen_opt
,
2435 &(ctx_inst
->seen_opt_bst
),
2438 if (((seen_opt_t
*)(bst_node
->key
))->seen
!= 0)
2440 found
= 1; /* A required option has been seen, */
2441 break; /* accept the group. */
2443 req_node
= req_node
->next
;
2446 rtrim(needed_params
, "\n ", 0);
2448 /* This is a fatal error if none of the options in the required */
2449 /* options group has been seen in the context. */
2450 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2455 if (req
->or_opt_list
->len
> 1)
2456 errmsg
= xstrdup("At least one of the parameters among:\n %s\n"
2457 "requested by %s must be present.\n");
2459 errmsg
= xstrdup("The parameter %s "
2460 "requested by %s must be present.\n");
2462 cur_state
->req_opt_par_needed
= needed_params
;
2463 cur_state
->req_opt_par
= opt
->params
;
2465 fatal(CTXOPTREQPAR
, errmsg
);
2473 /* ======================================================================== */
2474 /* Parse a strings describing options and some of their characteristics */
2475 /* The input string must have follow some rules like in the examples below: */
2477 /* "opt_name1 opt_name2" */
2478 /* "[opt_name1] opt_name2" */
2479 /* "[opt_name1] opt_name2..." */
2480 /* "[opt_name1 #...] opt_name2... [#]" */
2481 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2483 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2484 /* means that there can be more than one occurrence of the previous thing. */
2486 /* opt_name can be followed by a 'new context' change prefixed with the */
2487 /* symbol >, as in opt1>c2 by eg. */
2489 /* This function returns as soon as one (or no) option has been parsed and */
2490 /* return the offset to the next option to parse. */
2492 /* In case of successful parsing, an new option is allocated and its */
2493 /* pointer returned. */
2494 /* ======================================================================== */
2496 opt_parse(char *s
, opt_t
**opt
)
2498 int opt_optional
= 0;
2499 int opt_multiple
= 0;
2500 int opt_count_matter
= 0;
2501 char opt_count_oper
= '\0';
2502 unsigned opt_count_mark
= 0;
2504 char opt_arg
[33] = { 0 };
2505 int opt_multiple_args
= 0;
2506 int opt_args_count_matter
= 0;
2507 char opt_args_count_oper
= '\0';
2508 unsigned opt_args_count_mark
= 0;
2509 int opt_optional_args
= 0;
2510 int opt_eval_first
= 0;
2519 char *opt_name
= NULL
;
2524 memset(opt_arg
, '\0', 33);
2526 /* Strip the leading blanks. */
2527 /* """"""""""""""""""""""""" */
2531 if (*s
== '[') /* Start of an optional option. */
2536 s
= strtoken(s
, token
, sizeof(token
) - 1, "[^] \n\t.]", &pos
);
2538 return -1; /* Empty string. */
2540 /* Early EOS, only return success if the option is mandatory. */
2541 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2543 if (opt_optional
== 1)
2544 return -(s
- s_orig
- 1);
2546 /* Validate the option name */
2547 /* ALPHA+(ALPHANUM|_)* */
2548 /* """""""""""""""""""""""" */
2550 if (!isalpha(*p
) && *p
!= '*')
2551 return -(s
- s_orig
- 1); /* opt_name must start with a letter. */
2559 if (!isalnum(*p
) && *p
!= '_' && *p
!= '>')
2560 return -(s
- s_orig
- 1); /* opt_name must contain a letter, *
2561 * a number or a _ */
2566 opt_name
= xstrdup(token
+ 1); /* Ignore the first '*' in token. */
2568 opt_name
= xstrdup(token
);
2579 /* Check if it can appear multiple times by looking for the dots. */
2580 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2581 p
= strtoken(s
, token
, 3, "[.]", &pos
);
2584 if (strcmp(token
, "...") == 0)
2588 if (*s
== '<' || *s
== '=' || *s
== '>')
2593 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2596 opt_count_matter
= 1;
2597 opt_count_oper
= *s
;
2598 opt_count_mark
= value
;
2606 return -(s
- s_orig
- 1);
2612 /* Abort on extraneous ] if the option is mandatory. */
2613 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2617 return -(s
- s_orig
- 1);
2620 s
++; /* skip the ] */
2622 if (!*s
|| isblank(*s
))
2627 return -(s
- s_orig
- 1);
2631 /* A blank separates the option name and the argument tag. */
2632 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2644 n
= sscanf(s
, "[%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2645 if (pos
> 1 && *opt_arg
== '#') /* [# has been read. */
2648 opt_optional_args
= 1;
2650 opt_multiple_args
= 1; /* There were dots. */
2652 s
+= pos
+ !!(n
== 2) * 3; /* Skips the dots. */
2654 if (*s
== '<' || *s
== '=' || *s
== '>')
2659 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2662 opt_args_count_matter
= 1;
2663 opt_args_count_oper
= *s
;
2664 opt_args_count_mark
= value
;
2669 /* Optional arg tag must end with a ] */
2670 /* """""""""""""""""""""""""""""""""" */
2674 return -(s
- s_orig
- 1);
2677 s
++; /* Skip the ] */
2681 n
= sscanf(s
, "%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2682 if (pos
> 0 && *opt_arg
== '#') /* # has been read. */
2685 if (n
== 2) /* There were dots. */
2686 opt_multiple_args
= 1;
2688 s
+= pos
+ !!(n
== 2) * 3; /* Skip the dots. */
2690 if (*s
== '<' || *s
== '=' || *s
== '>')
2695 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2698 opt_args_count_matter
= 1;
2699 opt_args_count_oper
= *s
;
2700 opt_args_count_mark
= value
;
2708 /* Abort on extraneous ] if the option is mandatory. */
2709 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2713 return -(s
- s_orig
- 1);
2716 s
++; /* skip the ] */
2718 /* Strip the following blanks. */
2719 /* """"""""""""""""""""""""""" */
2725 else if (opt_optional
== 0 && (!*s
|| isblank(*s
)))
2727 /* Strip the following blanks. */
2728 /* """"""""""""""""""""""""""" */
2734 else if (opt_args
== 0) /* # was not read it is possibly the start *
2735 | of another option. */
2740 return -(s
- s_orig
- 1);
2746 /* Strip the following blanks. */
2747 /* """"""""""""""""""""""""""" */
2753 if (*opt_name
== '>')
2754 fatal_internal("The option name is missing in %s.", opt_name
);
2756 count
= strchrcount(opt_name
, '>');
2759 char *tmp
= strchr(opt_name
, '>');
2760 next_ctx
= xstrdup(tmp
+ 1);
2764 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name
);
2766 *opt
= xmalloc(sizeof(opt_t
));
2768 (*opt
)->name
= opt_name
;
2769 (*opt
)->optional
= opt_optional
;
2770 (*opt
)->multiple
= opt_multiple
;
2771 (*opt
)->opt_count_matter
= opt_count_matter
;
2772 (*opt
)->opt_count_oper
= opt_count_oper
;
2773 (*opt
)->opt_count_mark
= opt_count_mark
;
2774 (*opt
)->args
= opt_args
;
2775 (*opt
)->arg
= xstrdup(opt_arg
);
2776 (*opt
)->optional_args
= opt_optional_args
;
2777 (*opt
)->multiple_args
= opt_multiple_args
;
2778 (*opt
)->opt_args_count_matter
= opt_args_count_matter
;
2779 (*opt
)->opt_args_count_oper
= opt_args_count_oper
;
2780 (*opt
)->opt_args_count_mark
= opt_args_count_mark
;
2781 (*opt
)->eval_first
= opt_eval_first
;
2782 (*opt
)->next_ctx
= next_ctx
;
2783 (*opt
)->ctx_list
= ll_new();
2784 (*opt
)->constraints_list
= ll_new();
2785 (*opt
)->eval_before_list
= ll_new();
2786 (*opt
)->action
= NULL
;
2787 (*opt
)->params
= NULL
;
2788 (*opt
)->data
= NULL
;
2789 (*opt
)->visible_in_help
= 1;
2794 /* ==================================================================== */
2795 /* Try to initialize all the option in a given string. */
2796 /* Each parsed option are put in a BST tree with its name as index. */
2798 /* On collision, the arguments only the signature are required to be */
2799 /* the same else this is considered as an error. Options can be used in */
2800 /* more than one context and can be optional in one and mandatory in */
2802 /* ==================================================================== */
2804 init_opts(char *spec
, ctx_t
*ctx
)
2806 opt_t
*opt
, *bst_opt
;
2812 if ((offset
= opt_parse(spec
, &opt
)) > 0)
2816 if ((node
= bst_find(opt
, &options_bst
, opt_compare
)) != NULL
)
2818 int same_next_ctx
= 0;
2820 bst_opt
= node
->key
; /* Node extracted from the BST. */
2822 if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
== NULL
)
2824 else if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
!= NULL
)
2826 else if (bst_opt
->next_ctx
!= NULL
&& opt
->next_ctx
== NULL
)
2829 same_next_ctx
= strcmp(bst_opt
->next_ctx
, opt
->next_ctx
) == 0;
2831 if (bst_opt
->optional_args
!= opt
->optional_args
2832 || bst_opt
->multiple_args
!= opt
->multiple_args
2833 || bst_opt
->args
!= opt
->args
|| !same_next_ctx
)
2835 fatal_internal("The option %s already exists with "
2836 "a different arguments signature.\n",
2840 /* The newly created opt is already present in options_bst. */
2841 /* We can remove it. */
2842 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2845 /* The new occurrence of the option option is legal */
2846 /* append the current context ptr in the list. */
2847 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2848 ll_append(bst_opt
->ctx_list
, ctx
);
2850 /* Append the new option to the context's options list. */
2851 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2852 ll_append(ctx
->opt_list
, bst_opt
);
2856 /* Initialize the option's context list with the current context. */
2857 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2858 ll_append(opt
->ctx_list
, ctx
);
2860 /* Append the new option to the context's options list. */
2861 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2862 ll_append(ctx
->opt_list
, opt
);
2864 /* Insert the new option in the BST. */
2865 /* """"""""""""""""""""""""""""""""" */
2866 bst_search(opt
, &options_bst
, opt_compare
);
2871 char *s
= xstrndup(spec
, -offset
);
2872 printf("%s <---\nSyntax error at or before offset %d\n", s
, -offset
);
2882 /* ===================================================== */
2883 /* ctxopt initialization function, must be called first. */
2884 /* ===================================================== */
2886 ctxopt_init(char *prog_name
, char *init_flags
)
2890 contexts_bst
= NULL
;
2896 user_string
= xmalloc(8);
2897 user_string2
= xmalloc(8);
2899 char flag
[33], fname
[31], vname
[31];
2902 ctxopt_initialized
= 1;
2904 /* Initialize current_state.*/
2905 /* """""""""""""""""""""""" */
2906 cur_state
= xcalloc(sizeof(state_t
), 0);
2908 /* Initialize custom error function pointers to NULL. */
2909 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2910 err_functions
= xmalloc(CTXOPTERRSIZ
* sizeof(void *));
2911 for (n
= 0; n
< CTXOPTERRSIZ
; n
++)
2912 err_functions
[n
] = NULL
;
2914 /* Parse init_flags if any. */
2915 /* """""""""""""""""""""""" */
2916 while (*init_flags
&& (init_flags
= get_word(init_flags
, flag
, 32)))
2920 if (sscanf(flag
, "%30[^=]=%30[^=]", fname
, vname
) != 2)
2921 fatal_internal("Invalid flag assignment: %s.", flag
);
2923 if (strcmp(fname
, "stop_if_non_option") == 0)
2925 if (eval_yes(vname
, &invalid
))
2926 flags
.stop_if_non_option
= 1;
2928 flags
.stop_if_non_option
= 0;
2930 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2932 else if (strcmp(fname
, "allow_abbreviations") == 0)
2934 if (eval_yes(vname
, &invalid
))
2935 flags
.allow_abbreviations
= 1;
2937 flags
.allow_abbreviations
= 0;
2939 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2941 else if (strcmp(fname
, "display_usage_on_error") == 0)
2943 if (eval_yes(vname
, &invalid
))
2944 flags
.display_usage_on_error
= 1;
2946 flags
.display_usage_on_error
= 0;
2948 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2951 fatal_internal("Invalid flag name: %s.", fname
);
2955 /* Update current_state. */
2956 /* """"""""""""""""""""" */
2959 if (*prog_name
== '\0')
2960 cur_state
->prog_name
= xstrdup("program_name");
2961 else if ((ptr
= strrchr(prog_name
, '/')))
2962 cur_state
->prog_name
= xstrdup(ptr
+ 1);
2964 cur_state
->prog_name
= xstrdup(prog_name
);
2967 cur_state
->prog_name
= xstrdup("program_name");
2970 /* ========================================================================= */
2971 /* Utility function which create and register a par_t object in a BST */
2972 /* embedded in a context. */
2973 /* This object will have a name and a pointer to the option it refers to. */
2974 /* These object will be used to quickly find an option from a command */
2975 /* line parameter during the analysis phase. */
2977 /* IN : an option name. */
2978 /* IN : a string of command line parameters to associate to the option. */
2979 /* Returns : 1 is all was fine else 0. */
2980 /* ========================================================================= */
2982 opt_set_parms(char *opt_name
, char *par_str
)
2984 char *par_name
, *ctx_name
;
2985 char *tmp_par_str
, *end_tmp_par_str
;
2989 par_t
*par
, tmp_par
;
2990 int rc
= 1; /* Return code. */
2995 /* Look if the given option is defined. */
2996 /* """""""""""""""""""""""""""""""""""" */
2997 opt
= locate_opt(opt_name
);
2999 fatal_internal("Unknown option %s.", opt_name
);
3001 /* For each context using this option. */
3002 /* """"""""""""""""""""""""""""""""""" */
3003 list
= opt
->ctx_list
;
3006 while (lnode
!= NULL
)
3008 /* Locate the context in the contexts tree. */
3009 /* """""""""""""""""""""""""""""""""""""""" */
3010 ctx_name
= ((ctx_t
*)(lnode
->data
))->name
;
3012 ctx
= locate_ctx(ctx_name
);
3014 fatal_internal("Unknown context %s.", ctx_name
);
3017 void *par_bst
= ctx
->par_bst
;
3019 tmp_par_str
= xstrdup(par_str
);
3020 ltrim(tmp_par_str
, " \t");
3021 rtrim(tmp_par_str
, " \t", 0);
3022 par_name
= xstrtok_r(tmp_par_str
, " \t,", &end_tmp_par_str
);
3023 if (par_name
== NULL
)
3024 fatal_internal("Parameters are missing for option %s.", opt_name
);
3026 /* For each parameter given in par_str, creates a par_t object and */
3027 /* insert it the in the parameters BST of the context. */
3028 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3029 while (par_name
!= NULL
)
3031 tmp_par
.name
= par_name
;
3033 node
= bst_find(&tmp_par
, &par_bst
, par_compare
);
3036 fatal_internal("The parameter %s is already defined in context %s.",
3043 par
= xmalloc(sizeof(par_t
));
3044 par
->name
= xstrdup(par_name
);
3045 par
->opt
= opt
; /* Link the option to this parameter. */
3047 bst_search(par
, &par_bst
, par_compare
);
3049 par_name
= xstrtok_r(NULL
, " \t,", &end_tmp_par_str
);
3052 /* Update the value of the root of ctx->par_bst as it may have */
3053 /* been modified. */
3054 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3055 ctx
->par_bst
= par_bst
;
3059 lnode
= lnode
->next
;
3065 /* ==================================================================== */
3066 /* Create a new context instance. */
3067 /* IN ctx : a context pointer to allow this instance to */
3068 /* access the context fields */
3069 /* IN prev_ctx_inst : the context instance whose option leading to the */
3070 /* creation of this new context instance is part of */
3071 /* Returns : the new context. */
3072 /* ==================================================================== */
3074 new_ctx_inst(ctx_t
*ctx
, ctx_inst_t
*prev_ctx_inst
)
3077 opt_inst_t
*gen_opt_inst
;
3078 ctx_inst_t
*ctx_inst
;
3079 seen_opt_t
*seen_opt
;
3080 char *str
, *opt_name
;
3084 /* Keep a trace of the opt_inst which was at the origin of the creation */
3085 /* of this context instance. */
3086 /* This will serve during the evaluation of the option callbacks. */
3087 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3088 if (prev_ctx_inst
!= NULL
)
3090 gen_opt_inst
= (opt_inst_t
*)(prev_ctx_inst
->opt_inst_list
->tail
->data
);
3092 /* Update current_state. */
3093 /* """"""""""""""""""""" */
3094 cur_state
->opt_name
= gen_opt_inst
->opt
->name
;
3097 gen_opt_inst
= NULL
;
3099 /* Create and initialize the new context instance. */
3100 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3101 ctx_inst
= xmalloc(sizeof(ctx_inst_t
));
3102 ctx_inst
->ctx
= ctx
;
3103 ctx_inst
->prev_ctx_inst
= prev_ctx_inst
;
3104 ctx_inst
->gen_opt_inst
= gen_opt_inst
;
3105 ctx_inst
->incomp_bst_list
= ll_new();
3106 ctx_inst
->opt_inst_list
= ll_new();
3107 ctx_inst
->opt_req_list
= ll_new();
3108 ctx_inst
->seen_opt_bst
= NULL
;
3112 if (prev_ctx_inst
== NULL
)
3113 first_ctx_inst
= ctx_inst
;
3115 /* Initialize the occurrence counters of each opt allowed in the context. */
3116 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3117 node
= ctx
->opt_list
->head
;
3118 while (node
!= NULL
)
3121 opt
->occurrences
= 0;
3126 /* Initialize the BST containing the seen indicator for all the options */
3127 /* allowed in this context instance. */
3128 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3129 node
= ctx
->opt_list
->head
;
3130 while (node
!= NULL
)
3133 seen_opt
= xmalloc(sizeof(seen_opt_t
));
3134 seen_opt
->opt
= opt
;
3135 seen_opt
->par
= NULL
;
3138 bst_search(seen_opt
, &(ctx_inst
->seen_opt_bst
), seen_opt_compare
);
3143 /* Initialize the BST containing the incompatibles options. */
3144 /* Incompatibles option names are read from strings found in the list */
3145 /* incomp_list present in each instance of ctx_t. */
3146 /* These names are then used to search for the object of type seen_opt_t */
3147 /* which is already present in the seen_opt_bst of the context instance. */
3149 /* Once found the seen_opt_t object in inserted in the new BST. */
3150 /* At the end the new BST in added to the list incomp_bst_list. */
3151 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3152 node
= ctx
->incomp_list
->head
;
3153 while (node
!= NULL
)
3156 seen_opt_t tmp_seen_opt
;
3158 str
= xstrdup(node
->data
);
3160 rtrim(str
, " \t", 0);
3161 opt_name
= strtok(str
, " \t"); /* Extract the first option name. */
3163 while (opt_name
!= NULL
) /* For each option name. */
3165 if ((opt
= locate_opt(opt_name
)) != NULL
)
3167 /* The option found is searched in the tree of potential */
3169 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3170 tmp_seen_opt
.opt
= opt
;
3172 bst_node
= bst_find(&tmp_seen_opt
,
3173 &(ctx_inst
->seen_opt_bst
),
3176 if (bst_node
!= NULL
)
3178 /* If found then it is added into the new BST tree. */
3179 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3180 seen_opt
= bst_node
->key
;
3181 bst_search(seen_opt
, &bst
, seen_opt_compare
);
3184 /* Not found! That means that the option is unknown in this */
3185 /* context as all options has have a seen_opt structure in */
3187 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3188 fatal_internal("%s is not known in the context %s.",
3193 fatal_internal("Unknown option %s.", opt_name
);
3195 opt_name
= strtok(NULL
, " \t");
3199 ll_append(ctx_inst
->incomp_bst_list
, bst
);
3204 /* Initialize the list of res_t structures according to the */
3205 /* list set in the context by ctxopt_add_ctx_settings/required. */
3206 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3207 node
= ctx
->req_list
->head
;
3208 while (node
!= NULL
)
3210 req_t
*req
= xmalloc(sizeof(req_t
));
3212 str
= xstrdup(node
->data
);
3214 rtrim(str
, " \t", 0);
3215 opt_name
= strtok(str
, " \t"); /* Extract the first option name. */
3217 if ((opt
= locate_opt(opt_name
)) != NULL
)
3220 req
->or_opt_list
= ll_new();
3221 while ((opt_name
= strtok(NULL
, " \t")) != NULL
)
3223 if ((opt
= locate_opt(opt_name
)) != NULL
)
3224 ll_append(req
->or_opt_list
, opt
);
3226 fatal_internal("Unknown option %s.", opt_name
);
3228 ll_append(ctx_inst
->opt_req_list
, req
);
3231 fatal_internal("Unknown option %s.", opt_name
);
3240 /* ====================================================================== */
3241 /* Create a list formed by all the significant command line words */
3242 /* Words beginning or ending with { or } are split. Each of these */
3243 /* symbols will get their own place in the list. */
3245 /* the {...} part delimits a context, the { will not appear in the list */
3246 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3247 /* to facilitate the parsing phase. | must not be used by the end user. */
3249 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3250 /* program name is not considered. */
3251 /* IN words : is the array of strings constituting the command line to */
3253 /* Returns : 1 on success, 0 if a { or } is missing. */
3254 /* ====================================================================== */
3256 ctxopt_build_cmdline_list(int nb_words
, char **words
)
3259 char *prev_word
= NULL
;
3263 ll_node_t
*node
, *start_node
;
3265 /* The analysis is divided into three passes, this is not optimal but */
3266 /* must be done only one time. Doing that we privilege readability. */
3268 /* In the following, SG is the ASCII character 1d (dec 29) */
3270 /* The first pass creates the list, extract the leading an trailing */
3271 /* SG '{' and '}' of each word and give them their own place in the */
3274 /* The second pass transform the '{...}' blocks by a trailing SG */
3275 /* ({...} -> ...|) */
3277 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3278 /* the middle in the remaining list elements and recreate the pseudo */
3280 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3282 /* If the option list is not empty, clear it before going further. */
3283 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3284 if (cmdline_list
!= NULL
)
3285 ll_destroy(cmdline_list
, free
);
3287 cmdline_list
= ll_new();
3289 start_node
= cmdline_list
->head
; /* In the following loop start_node will *
3290 | contain a pointer to the current *
3291 | word stripped from its leading *
3292 | sequence of {, }. */
3293 for (i
= 0; i
< nb_words
; i
++)
3295 size_t len
= strlen(words
[i
]);
3301 /* Replace each occurrence of the legal word {} by the characters */
3302 /* 0x02 and 0x03 to hide them from the following process. */
3303 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3304 while ((ptr
= strstr(str
, "{}")) != NULL
)
3306 *ptr
= 0x02; /* Arbitrary values unlikely */
3307 *(ptr
+ 1) = 0x03; /* present in a word. */
3310 if (len
> 1) /* The word contains at least 2 characters. */
3314 /* Interpret its beginning and look for the start of the real word. */
3315 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3316 while (start
<= len
- 1 && (str
[start
] == '{' || str
[start
] == '}'))
3318 ll_append(cmdline_list
, xstrndup(str
+ start
, 1));
3320 start_node
= cmdline_list
->tail
;
3324 if (str
[end
] == '{' || str
[end
] == '}')
3326 if (end
> 0 && str
[end
- 1] != '\\')
3328 ll_append(cmdline_list
, xstrndup(str
+ end
, 1));
3330 node
= cmdline_list
->tail
;
3332 while (str
[end
] == '{' || str
[end
] == '}')
3334 if (end
> start
&& str
[end
- 1] == '\\')
3337 ll_insert_before(cmdline_list
, node
, xstrndup(str
+ end
, 1));
3346 if (start_node
!= NULL
)
3347 ll_insert_after(cmdline_list
,
3349 xstrndup(str
+ start
, end
- start
+ 1));
3351 ll_append(cmdline_list
, xstrndup(str
+ start
, end
- start
+ 1));
3352 start_node
= cmdline_list
->tail
;
3357 ll_append(cmdline_list
, xstrdup(str
));
3358 start_node
= cmdline_list
->tail
;
3364 node
= cmdline_list
->head
;
3367 while (node
!= NULL
)
3371 if (strcmp(word
, "{") == 0)
3373 ll_node_t
*old_node
= node
;
3377 ll_delete(cmdline_list
, old_node
);
3379 else if (strcmp(word
, "}") == 0)
3397 node
= cmdline_list
->head
;
3399 while (node
!= NULL
)
3403 /* Restore the original { and } characters forming the legal word {}. */
3404 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3405 while ((ptr
= strchr(word
, 0x02)) != NULL
)
3407 while ((ptr
= strchr(word
, 0x03)) != NULL
)
3410 /* Remove a SG if the previous element is SG. */
3411 /* """""""""""""""""""""""""""""""""""""""""" */
3412 if (strcmp(word
, "\x1d") == 0)
3414 if (prev_word
!= NULL
&& (strcmp(prev_word
, "\x1d") == 0))
3416 ll_node_t
*old_node
= node
;
3418 free(old_node
->data
);
3419 ll_delete(cmdline_list
, old_node
);
3422 else if (strcmp(word
, "-") == 0) /* A single - is a legal argument, not *
3423 | a parameter. Protect it. */
3426 node
->data
= xstrdup("\\-");
3429 prev_word
= node
->data
;
3433 /* Clean useless and SG at the beginning and end of list. */
3434 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3435 node
= cmdline_list
->head
;
3442 if (strcmp(word
, "\x1d") == 0)
3445 ll_delete(cmdline_list
, node
);
3448 node
= cmdline_list
->tail
;
3454 if (strcmp(word
, "\x1d") == 0)
3457 ll_delete(cmdline_list
, node
);
3463 /* ===================================================================== */
3464 /* Build and analyze the command line list and create the linked data */
3465 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3466 /* This function identifies the following errors and creates an array of */
3467 /* The remaining not yet analyzed arguments. */
3468 /* - detect missing arguments */
3469 /* - detect too many arguments */
3470 /* - detect unknown parameters in a context */
3471 /* - detect too many occurrences of a parameters in a context */
3472 /* - detect missing required arguments in a context */
3474 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3475 /* program name is not considered */
3476 /* IN words : is the array of strings constituting the command line to */
3478 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3479 /* is present in the list. */
3480 /* OUT rem_args : array of remaining command line arguments if a -- */
3481 /* is present in the list. This array must be free by */
3482 /* The caller as it is allocated here. */
3483 /* ===================================================================== */
3485 ctxopt_analyze(int nb_words
, char **words
, int *nb_rem_args
, char ***rem_args
)
3487 char *ctxopt_debug_env
; /* Environment variable CTXOPT_DEBUG content. */
3488 int ctxopt_debug
; /* 1 if ctxopt_debug_env is set and not empty. *
3489 | 0 if ctxopt_debug_env is unset or empty. */
3494 ctx_inst_t
*ctx_inst
;
3495 opt_inst_t
*opt_inst
;
3498 int expect_par_or_arg
= 0;
3500 ll_node_t
*cli_node
;
3502 seen_opt_t
*bst_seen_opt
;
3508 if (!ctxopt_build_cmdline_list(nb_words
, words
))
3509 fatal_internal("The command line could not be parsed: "
3510 "missing '{' or '}' detected.");
3512 if (main_ctx
== NULL
)
3513 fatal_internal("At least one context must have been created.");
3515 /* Check that all options has an action and at least one parameter. */
3516 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3517 bst_walk(options_bst
, bst_check_opt_cb
);
3519 /* CTXOPT debug setting. */
3520 /* """"""""""""""""""""" */
3521 ctxopt_debug_env
= getenv("CTXOPT_DEBUG");
3522 if (ctxopt_debug_env
!= NULL
&& *ctxopt_debug_env
!= '\0')
3527 /* Create the first ctx_inst record. */
3528 /* """"""""""""""""""""""""""""""""" */
3531 ctx_inst_list
= ll_new();
3532 ctx_inst
= new_ctx_inst(ctx
, NULL
);
3533 ctx_inst
->par_name
= NULL
;
3535 /* Update current_state. */
3536 /* """"""""""""""""""""" */
3537 cur_state
->ctx_name
= ctx
->name
;
3539 ll_append(ctx_inst_list
, ctx_inst
);
3541 /* For each node in the command line. */
3542 /* """""""""""""""""""""""""""""""""" */
3543 cli_node
= cmdline_list
->head
;
3547 while (cli_node
!= NULL
)
3549 if (strcmp(cli_node
->data
, "--") == 0)
3550 break; /* No new parameter will be analyzed after this point. */
3552 par_name
= cli_node
->data
;
3554 /* Replace a leading -- by a single - */
3555 /* """""""""""""""""""""""""""""""""" */
3556 if (strncmp(par_name
, "--", 2) == 0)
3557 par_name
+= 1; /* Ignore the first dash. */
3559 if (strcmp(par_name
, "\x1d") == 0)
3561 check_for_missing_mandatory_opt(ctx_inst
, (char *)(cli_node
->prev
->data
));
3562 check_for_occurrence_issues(ctx_inst
);
3563 check_for_requirement_issues(ctx_inst
);
3565 /* Forced backtracking to the previous context instance. */
3566 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3567 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3569 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3570 ctx
= ctx_inst
->ctx
;
3572 /* Update current_states. */
3573 /* """"""""""""""""""""" */
3574 cur_state
->ctx_name
= ctx
->name
;
3575 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3579 "CTXOPT_DEBUG: Context forced backtrack, "
3580 "new current context: %s.\n",
3585 /* Update current_state. */
3586 /* """"""""""""""""""""" */
3587 cur_state
->ctx_par_name
= NULL
;
3590 else if (expect_par
&& *par_name
== '-')
3595 /* Update current_state. */
3596 /* """"""""""""""""""""" */
3597 cur_state
->cur_opt_par_name
= par_name
;
3598 cur_state
->ctx_name
= ctx
->name
;
3599 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3603 "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3605 cur_state
->ctx_name
);
3607 /* An expected parameter has been seen. */
3608 /* """""""""""""""""""""""""""""""""""" */
3609 if ((par
= locate_par(par_name
, ctx
)) == NULL
)
3614 /* Look if this parameter is an unique abbreviation of a longer */
3615 /* parameter. If this is the case then just replace it with its */
3616 /* full length version and try again. */
3617 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3618 if (flags
.allow_abbreviations
)
3619 if ((word
= abbrev_expand(par_name
, ctx
)) != NULL
)
3621 cli_node
->data
= word
;
3625 /* Try to find a prefix which is a valid parameter in this context */
3626 /* If found, split the cli_node in two to build a new parameter */
3627 /* node and followed by a node containing the remaining string */
3628 /* If the new parameter corresponds to an option not taking */
3629 /* argument then prefix the remaining string whit a dash as it may */
3630 /* contain a new parameter. */
3631 /* The new parameter will be re-evaluated in the next iteration of */
3633 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3634 prefix
= look_for_valid_prefix_in_word(par_name
, ctx
, &pos
, &popt
);
3635 if (prefix
!= NULL
&& pos
!= 0)
3639 "CTXOPT_DEBUG: Found a valid parameter "
3640 "as a prefix of %s: %s.\n",
3644 cli_node
->data
= prefix
; /* prefix contains le name of a valid *
3645 | parameter in this context. */
3649 /* The parameter may be followed by arguments. */
3650 /* ''''''''''''''''''''''''''''''''''''''''''' */
3651 if (*(par_name
+ pos
) == '-')
3653 word
= xstrdup("\\"); /* Protect the '-' */
3654 word
= strappend(word
, par_name
+ pos
, (char *)0);
3657 word
= xstrdup(par_name
+ pos
);
3661 /* The parameter does not take arguments, the */
3662 /* following word must be a parameter or nothing */
3663 /* hence prefix it with a dash. */
3664 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3665 word
= xstrdup("-");
3666 word
= strappend(word
, par_name
+ pos
, (char *)0);
3669 /* Insert it after the current node in the list. */
3670 /* """"""""""""""""""""""""""""""""""""""""""""" */
3671 ll_insert_after(cmdline_list
, cli_node
, word
);
3673 continue; /* loop. */
3677 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3678 check_for_occurrence_issues(ctx_inst
);
3679 check_for_requirement_issues(ctx_inst
);
3681 if (ctx_inst
->prev_ctx_inst
== NULL
)
3683 char *errmsg
= xstrdup("");
3685 /* Update current_state. */
3686 /* """"""""""""""""""""" */
3687 cur_state
->ctx_par_name
= NULL
;
3689 *user_string
= '\0';
3690 *user_string2
= '\0';
3692 user_string
= strappend(user_string
, par_name
, (char *)0);
3694 bst_walk(contexts_bst
, bst_match_par_cb
);
3696 if (*user_string2
!= '\0')
3701 count
= strchrcount(user_string2
, '\n');
3703 if (flags
.display_usage_on_error
)
3704 help_msg
= ", see below";
3708 if (count
== 0) /* Only one context involved. */
3711 "\nThis parameter is only valid in the following "
3714 "\n\nFirst switch to this context using the appropriate "
3722 "\nThis parameter is only valid in one of the following "
3725 "\n\nFirst switch to one of them using the appropriate "
3732 fatal(CTXOPTUNKPAR
, errmsg
);
3736 /* Tries to backtrack and analyse the same parameter in the */
3737 /* previous context. */
3738 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3739 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3740 ctx
= ctx_inst
->ctx
;
3744 "CTXOPT_DEBUG: Context backtrack, "
3745 "new current context: %s.\n",
3748 /* Update current_state. */
3749 /* """"""""""""""""""""" */
3750 cur_state
->ctx_name
= ctx
->name
;
3751 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3753 cli_node
= cli_node
->prev
;
3759 seen_opt_t seen_opt
;
3761 /* The parameter is valid in the context, create a opt_inst and */
3762 /* append it to the ctx_inst list options list. */
3763 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3768 opt_inst
= xmalloc(sizeof(opt_inst_t
));
3769 opt_inst
->opt
= opt
;
3770 opt_inst
->par
= par_name
;
3771 opt_inst
->values_list
= ll_new();
3772 opt_inst
->next_ctx_inst
= NULL
;
3774 /* Update current_state. */
3775 /* """"""""""""""""""""" */
3776 cur_state
->cur_opt_params
= opt
->params
;
3778 /* Priority option are inserted at the start of the opt_inst list */
3779 /* but their order of appearance in the context definition must */
3780 /* be preserver so each new priority option will be placed after */
3781 /* the previous ones at the start of the opt_inst list. */
3782 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3783 if (!opt
->eval_first
)
3785 /* Look if we have a registered dependency in the order of the */
3786 /* evaluation of two options. */
3787 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3788 if (opt
->eval_before_list
->len
> 0)
3790 ll_t
*list
= ctx_inst
->opt_inst_list
;
3791 ll_node_t
*opt_inst_node
;
3793 ll_t
*before_list
= opt
->eval_before_list
;
3794 ll_node_t
*before_node
= before_list
->head
;
3796 ll_node_t
*target_node
= NULL
; /* If not NULL, the new node *
3797 | will be inserted before it. */
3799 /* For each entry in eval_before_list, try to find if it */
3800 /* refers to an option already entered in the context. If this */
3801 /* is the case, insert it just before it instead of putting it */
3803 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3804 while (before_node
!= NULL
)
3806 opt_inst_node
= list
->head
;
3808 while (opt_inst_node
!= target_node
)
3810 opt_t
*tmp_opt
= (((opt_inst_t
*)opt_inst_node
->data
))->opt
;
3812 /* We have found an option mentioned if the before_list */
3813 /* of the option we want to add. We can stop searching. */
3814 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3815 if (strcmp(tmp_opt
->name
, ((opt_t
*)before_node
->data
)->name
))
3816 opt_inst_node
= opt_inst_node
->next
;
3818 target_node
= opt_inst_node
; /* Set the target node. */
3821 before_node
= before_node
->next
;
3824 /* Insert or append ? */
3825 /* """""""""""""""""" */
3826 if (target_node
!= NULL
)
3827 ll_insert_before(ctx_inst
->opt_inst_list
, target_node
, opt_inst
);
3829 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3832 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3836 ll_node_t
*opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
3837 opt_inst_t
*tmp_opt_inst
;
3839 while (opt_inst_node
!= NULL
)
3841 tmp_opt_inst
= opt_inst_node
->data
;
3842 if (!tmp_opt_inst
->opt
->eval_first
)
3844 ll_insert_before(ctx_inst
->opt_inst_list
,
3850 opt_inst_node
= opt_inst_node
->next
;
3852 if (opt_inst_node
== NULL
)
3853 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3856 /* Check if an option was already seen in the */
3857 /* current context instance. */
3858 /* """""""""""""""""""""""""""""""""""""""""" */
3861 bst_node
= bst_find(&seen_opt
,
3862 &(ctx_inst
->seen_opt_bst
),
3865 /* bst_node cannot be NULL here. */
3867 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3869 if (!opt
->multiple
&& bst_seen_opt
->seen
== 1)
3870 fatal(CTXOPTDUPOPT
, "");
3872 /* Check if this option is compatible with the options already */
3873 /* seen in this context instance. */
3874 /* Look if the option is present in one on the BST present in */
3875 /* the incomp_bst_list of the context instance. */
3876 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3877 node
= ctx_inst
->incomp_bst_list
->head
;
3878 while (node
!= NULL
)
3883 /* There can only have one seen_opt object in the BST tree was */
3884 /* already seen, try to locate it, the result will be put in */
3885 /* user_object by the bst_seen_opt_seen_cb function. */
3886 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3887 bst_walk(bst
, bst_seen_opt_seen_cb
);
3889 /* If it is the case, look if the current option is also */
3891 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3892 if (user_object
!= NULL
)
3894 bst_node
= bst_find(bst_seen_opt
, &bst
, seen_opt_compare
);
3896 if (bst_node
!= NULL
)
3898 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3899 if (bst_seen_opt
->seen
== 0)
3900 fatal(CTXOPTINCOPT
, (char *)user_object
);
3907 /* Mark this option as seen in the current context instance. */
3908 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3909 bst_seen_opt
->seen
= 1;
3910 free(bst_seen_opt
->par
);
3911 bst_seen_opt
->par
= xstrdup(par_name
);
3913 /* If this option leads to a next context, create a new ctx_inst */
3914 /* and switch to it for the analyse of the future parameter. */
3915 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3916 if (opt
->next_ctx
!= NULL
)
3918 ctx
= locate_ctx(opt
->next_ctx
);
3921 fatal_internal("Unknown context %s.", opt
->next_ctx
);
3923 opt_inst
->next_ctx_inst
= ctx_inst
= new_ctx_inst(ctx
, ctx_inst
);
3924 ctx_inst
->par_name
= xstrdup(par_name
);
3926 ll_append(ctx_inst_list
, ctx_inst
);
3930 "CTXOPT_DEBUG: Context change, "
3931 "new current context: %s.\n",
3935 /* Look is we must expect some arguments. */
3936 /* """""""""""""""""""""""""""""""""""""" */
3937 expect_par_or_arg
= 0;
3942 expect_par
= 1; /* Parameter doesn't accept any argument. */
3945 if (!opt
->optional_args
)
3946 expect_arg
= 1; /* Parameter has mandatory arguments. */
3948 expect_par_or_arg
= 1; /* Parameter has optional arguments. */
3952 else if (expect_par
&& *par_name
!= '-')
3954 ll_node_t
*n
= cli_node
->next
;
3956 if (!flags
.stop_if_non_option
)
3957 /* Look if potential arguments must still be analyzed until the */
3958 /* end of the context/command line part to analyze/command line. */
3959 /* If this is the case we have met an extra argument. */
3960 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3963 if (strcmp(n
->data
, "--") == 0 || strcmp(n
->data
, "\x1d") == 0)
3964 fatal(CTXOPTUNXARG
, "");
3966 if (*(char *)(n
->data
) == '-')
3967 fatal(CTXOPTUNXARG
, "");
3972 break; /* An unexpected non parameter was seen, if no Potential *
3973 | arguments remain in the command line or *
3974 | flags.stop_if_non_option is set, assume that it is is *
3975 | the first of the non arguments and stop the command *
3978 else if (expect_arg
&& *par_name
!= '-')
3980 ll_node_t
*cstr_node
;
3984 fprintf(stderr
, "CTXOPT_DEBUG: Argument: %s.\n", par_name
);
3986 /* Check if the arguments of the option respects */
3987 /* the attached constraints if any. */
3988 /* """"""""""""""""""""""""""""""""""""""""""""" */
3989 cstr_node
= opt
->constraints_list
->head
;
3990 while (cstr_node
!= NULL
)
3992 cstr
= cstr_node
->data
;
3993 if (!cstr
->constraint(cstr
->nb_args
,
3996 cur_state
->cur_opt_par_name
))
3998 fputs("\n", stderr
);
3999 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, exit_after
);
4002 cstr_node
= cstr_node
->next
;
4005 /* If the argument is valid, store it. */
4006 /* """"""""""""""""""""""""""""""""""" */
4007 if (*par_name
== '\\' && *(par_name
+ 1) == '-')
4008 ll_append(opt_inst
->values_list
, par_name
+ 1);
4010 ll_append(opt_inst
->values_list
, par_name
);
4014 expect_par_or_arg
= 0;
4016 if (opt
->multiple_args
)
4017 expect_par_or_arg
= 1;
4019 expect_par
= 1; /* Parameter takes only one argument. */
4021 else if (expect_arg
&& *par_name
== '-')
4022 fatal(CTXOPTMISARG
, "");
4023 else if (expect_par_or_arg
)
4027 expect_par_or_arg
= 0;
4029 if (*par_name
!= '-')
4030 expect_arg
= 1; /* Consider this word as an argument and retry. */
4032 expect_par
= 1; /* Consider this word as a parameter and retry. */
4034 cli_node
= cli_node
->prev
;
4037 cli_node
= cli_node
->next
;
4040 if (cmdline_list
->len
> 0 && par_name
&& *par_name
== '-')
4042 if (expect_arg
&& opt
&& !opt
->optional_args
)
4043 fatal(CTXOPTMISARG
, "");
4046 /* Look if a context_instance has unseen mandatory options. */
4047 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4048 node
= ctx_inst_list
->head
;
4049 while (node
!= NULL
)
4051 ctx_inst
= node
->data
;
4053 /* Update current_state. */
4054 /* """"""""""""""""""""" */
4055 cur_state
->ctx_name
= ctx_inst
->ctx
->name
;
4056 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
4058 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
4059 check_for_occurrence_issues(ctx_inst
);
4060 check_for_requirement_issues(ctx_inst
);
4065 /* Allocate the array containing the remaining not analyzed */
4066 /* command line arguments. */
4067 /* NOTE: The strings in the array are just pointer to the */
4068 /* data of the generating list and must not be freed. */
4069 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4070 if (cli_node
!= NULL
)
4072 if (strcmp((char *)cli_node
->data
, "--") == 0)
4073 /* The special parameter -- was encountered, the -- argument is not */
4074 /* put in the remaining arguments. */
4075 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4076 ll_strarray(cmdline_list
, cli_node
->next
, nb_rem_args
, rem_args
);
4078 /* A non parameter was encountered when a parameter was expected. We */
4079 /* assume that the evaluation of the remaining command line argument */
4080 /* are not the responsibility of the users code. */
4081 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4082 ll_strarray(cmdline_list
, cli_node
, nb_rem_args
, rem_args
);
4087 *rem_args
= xmalloc(sizeof(char *));
4088 (*rem_args
)[0] = NULL
;
4092 /* ==================================================== */
4093 /* Free ctxopt memory used for its internal structures. */
4094 /* ==================================================== */
4096 ctxopt_free_memory(void)
4098 ll_destroy(cmdline_list
, free
);
4099 ll_destroy(ctx_inst_list
, ctx_inst_free
);
4100 bst_destroy(options_bst
, opt_free
);
4101 bst_destroy(contexts_bst
, ctx_free
);
4104 /* ==================================================================== */
4105 /* Parse the options data structures and launches the callback function */
4106 /* attached to each options instances. */
4107 /* This calls a recursive function which proceeds context per context. */
4108 /* ==================================================================== */
4110 ctxopt_evaluate(void)
4112 evaluate_ctx_inst(first_ctx_inst
);
4115 /* =================================================================== */
4116 /* Recursive function called by ctxopt_evaluate to process the list of */
4117 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
4118 /* action attached to the context and its option instances. */
4119 /* =================================================================== */
4121 evaluate_ctx_inst(ctx_inst_t
*ctx_inst
)
4123 opt_inst_t
*opt_inst
;
4126 ll_node_t
*opt_inst_node
;
4130 if (ctx_inst
== NULL
)
4133 ctx
= ctx_inst
->ctx
;
4135 /* Do not evaluate the action attached to this context is there is no */
4136 /* option to evaluate. */
4137 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4138 opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
4139 if (opt_inst_node
== NULL
)
4142 /* Call the entering action attached to this context if any. */
4143 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4144 if (ctx
->action
!= NULL
)
4146 if (ctx_inst
->prev_ctx_inst
!= NULL
)
4147 ctx
->action(ctx
->name
,
4149 ctx_inst
->prev_ctx_inst
->ctx
->name
,
4153 ctx
->action(ctx
->name
, entering
, NULL
, ctx
->nb_data
, ctx
->data
);
4156 /* For each instance of options. */
4157 /* """"""""""""""""""""""""""""" */
4158 while (opt_inst_node
!= NULL
)
4160 opt_inst
= (opt_inst_t
*)(opt_inst_node
->data
);
4161 ll_strarray(opt_inst
->values_list
,
4162 opt_inst
->values_list
->head
,
4165 opt
= opt_inst
->opt
;
4167 /* Launch the attached action if any. */
4168 /* """""""""""""""""""""""""""""""""" */
4169 if (opt
->action
!= NULL
)
4170 opt
->action(ctx
->name
,
4180 if (opt_inst
->next_ctx_inst
!= NULL
)
4181 evaluate_ctx_inst(opt_inst
->next_ctx_inst
);
4186 opt_inst_node
= opt_inst_node
->next
;
4189 /* Call the exiting action attached to this context if any. */
4190 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4191 if (ctx
->action
!= NULL
)
4193 if (ctx_inst
->prev_ctx_inst
!= NULL
)
4194 ctx
->action(ctx
->name
,
4196 ctx_inst
->prev_ctx_inst
->ctx
->name
,
4200 ctx
->action(ctx
->name
, exiting
, NULL
, ctx
->nb_data
, ctx
->data
);
4204 /* ============================================================ */
4205 /* Create and initializes a new context. */
4206 /* - allocate space. */
4208 /* - initialize its option with a few of their characteristics. */
4209 /* ============================================================ */
4211 ctxopt_new_ctx(char *name
, char *opts_specs
)
4216 if (!ctxopt_initialized
)
4217 fatal_internal("Please call ctxopt_init first.");
4219 ctx
= xmalloc(sizeof(ctx_t
));
4221 /* Validates the context name: */
4222 /* ALPHA+(ALPHANUM|_)* */
4223 /* """"""""""""""""""""""""""" */
4225 if (*p
== '\0' || !isalpha(*p
))
4226 fatal_internal("A context name must start with a letter: %s.", name
);
4231 if (!isalnum(*p
) && *p
!= '_')
4232 fatal_internal("A context name must only contain letters, "
4233 "numbers or '_': %s.",
4238 ctx
->name
= xstrdup(name
);
4239 ctx
->opt_list
= ll_new(); /* List of options legit in this context. */
4240 ctx
->incomp_list
= ll_new(); /* List of incompatible options strings. */
4241 ctx
->req_list
= ll_new(); /* List of opts/required opts tuples (str). */
4242 ctx
->par_bst
= NULL
;
4246 /* The first created context is the main one. */
4247 /* """""""""""""""""""""""""""""""""""""""""" */
4248 if (contexts_bst
== NULL
)
4252 cur_state
->ctx_name
= ctx
->name
;
4255 if (init_opts(opts_specs
, ctx
) == 0)
4257 if (bst_find(ctx
, &contexts_bst
, ctx_compare
) != NULL
)
4258 fatal_internal("The context %s already exists.", name
);
4260 bst_search(ctx
, &contexts_bst
, ctx_compare
);
4263 /* ==================================================== */
4264 /* Display a usage screen limited to a specific context */
4265 /* IN: the context name. */
4266 /* IN: what to do after (continue or exit the program) */
4267 /* possible values: continue_after, exit_after. */
4268 /* ==================================================== */
4270 ctxopt_ctx_disp_usage(char *ctx_name
, usage_behaviour action
)
4275 int has_optional
= 0;
4276 int has_ellipsis
= 0;
4278 int has_generic_arg
= 0;
4279 int has_ctx_change
= 0;
4280 int has_early_eval
= 0;
4282 if (!flags
.display_usage_on_error
)
4287 if (ctx_name
!= NULL
)
4288 ctx
= locate_ctx(ctx_name
);
4290 ctx
= locate_ctx(cur_state
->ctx_name
);
4293 fatal_internal("Unknown context %s.", ctx_name
);
4295 if (cur_state
->ctx_par_name
== NULL
)
4296 printf("\nSynopsis:\n%s \\\n", cur_state
->prog_name
);
4298 printf("\nSynopsis for the context introduced by %s:\n",
4299 cur_state
->ctx_par_name
);
4301 list
= ctx
->opt_list
;
4310 print_before_constraints(list
);
4312 print_explanations(has_early_eval
,
4319 if (action
== exit_after
)
4323 /* =================================================== */
4324 /* Display a full usage screen about all contexts. */
4325 /* IN: what to do after (continue or exit the program) */
4326 /* possible values: continue_after, exit_after. */
4327 /* =================================================== */
4329 ctxopt_disp_usage(usage_behaviour action
)
4332 int has_optional
= 0;
4333 int has_ellipsis
= 0;
4335 int has_generic_arg
= 0;
4336 int has_ctx_change
= 0;
4337 int has_early_eval
= 0;
4339 if (!flags
.display_usage_on_error
)
4342 if (main_ctx
== NULL
)
4343 fatal_internal("At least one context must have been created.");
4345 /* Usage for the first context. */
4346 /* """""""""""""""""""""""""""" */
4347 printf("\nAllowed options in the base context:\n");
4348 list
= main_ctx
->opt_list
;
4357 /* Dependency constraints between options. */
4358 /* """"""""""""""""""""""""""""""""""""""" */
4359 print_before_constraints(list
);
4361 /* Usage for the other contexts. */
4362 /* """"""""""""""""""""""""""""" */
4363 bst_walk(contexts_bst
, bst_print_ctx_cb
);
4365 /* Contextual syntactic explanations. */
4366 /* """""""""""""""""""""""""""""""""" */
4367 print_explanations(has_early_eval
,
4374 if (action
== exit_after
)
4378 /* ************************************ */
4379 /* Built-in constraint check functions. */
4380 /* ************************************ */
4382 /* ============================================================= */
4383 /* This constraint checks if each arguments respects a format as */
4384 /* defined for the scanf function. */
4385 /* return 1 if yes and 0 if no. */
4386 /* ============================================================= */
4388 ctxopt_format_constraint(int nb_args
, char **args
, char *value
, char *par
)
4397 fatal_internal("Format constraint, invalid number of parameters.");
4399 if (strlen(value
) > 255)
4402 format
= xstrdup(args
[0]);
4403 format
= strappend(format
, "%c", (char *)0);
4405 rc
= sscanf(value
, format
, x
, &y
);
4408 "The argument %s of %s does not respect the imposed format %s.",
4418 /* ================================================================== */
4419 /* This constraint checks if each arguments of the option instance is */
4420 /* between a minimum and a maximum (inclusive). */
4421 /* return 1 if yes and 0 if no. */
4422 /* ================================================================== */
4424 ctxopt_re_constraint(int nb_args
, char **args
, char *value
, char *par
)
4430 "Regular expression constraint, invalid number of parameters.");
4432 if (regcomp(&re
, args
[0], REG_EXTENDED
) != 0)
4433 fatal_internal("Invalid regular expression %s.", args
[0]);
4435 if (regexec(&re
, value
, (size_t)0, NULL
, 0) != 0)
4438 "The argument %s of %s doesn't match the constraining "
4439 "regular expression %s.",
4451 /* ================================================================== */
4452 /* This constraint checks if each arguments of the option instance is */
4453 /* between a minimum and a maximum (inclusive). */
4454 /* return 1 if yes and 0 if no. */
4455 /* ================================================================== */
4457 ctxopt_range_constraint(int nb_args
, char **args
, char *value
, char *par
)
4459 long min
= LONG_MIN
, max
= LONG_MAX
;
4468 fatal_internal("Range constraint, invalid number of parameters.");
4471 if (strcmp(args
[0], ".") == 0)
4474 n
= sscanf(args
[0], "%ld%c", &min
, &c
);
4476 if (!max_only
&& n
!= 1)
4477 fatal_internal("Range constraint, min: invalid parameters.");
4480 if (strcmp(args
[1], ".") == 0)
4483 n
= sscanf(args
[1], "%ld%c", &max
, &c
);
4485 if (!min_only
&& n
!= 1)
4486 fatal_internal("Range constraint, max: invalid parameters.");
4488 if (min_only
&& max_only
)
4489 fatal_internal("Range constraint, invalid parameters.");
4492 v
= strtol(value
, &ptr
, 10);
4493 if (errno
|| ptr
== value
)
4501 "The argument %ld of %s is not greater than or equal to %ld.",
4515 "The argument %ld of %s is not less than or equal to %ld.",
4524 else if (v
< min
|| v
> max
)
4527 "The argument %ld of %s is not between %ld and %ld.",
4535 return 1; /* Check passed. */
4538 /* =============================================================== */
4539 /* This function provides a way to set the behaviour of a context. */
4540 /* =============================================================== */
4542 ctxopt_add_global_settings(settings s
, ...)
4549 case error_functions
:
4551 typedef void fn(errors e
, state_t
* state
);
4553 void (*function
)(errors e
, state_t
*state
);
4556 e
= va_arg(args
, errors
);
4557 function
= va_arg(args
, fn
*);
4558 err_functions
[e
] = function
;
4568 /* ================================================================ */
4569 /* This function provides a way to set the behaviour of an option. */
4570 /* It can take a variable number of arguments according to its */
4571 /* first argument: */
4573 /* o a string containing an option name and all its possible */
4574 /* parameters separates by spaces, tabs or commas (char *) */
4575 /* (e.g: "help -h -help"). */
4577 /* o a string containing an option name. */
4578 /* o a pointer to a function which will be called at evaluation */
4580 /* - constraints: */
4581 /* o a string containing an option name. */
4582 /* o a pointer to a function to check if an argument is valid. */
4583 /* o a strings containing the arguments to this function. */
4584 /* - visible_in_help: */
4585 /* o a string containing an option name. */
4586 /* o the string "yes" or "no" (case insensitive) which will */
4587 /* determine if the option must be seen in help/usage. */
4588 /* ================================================================ */
4590 ctxopt_add_opt_settings(settings s
, ...)
4600 /* This part associates some command line parameters to an option. */
4601 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4604 char *opt_name
= NULL
;
4607 /* The second argument must be a string containing: */
4608 /* - The name of an existing option. */
4609 /* - a list of parameters with a leading dash (-). */
4610 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4611 ptr
= va_arg(args
, char *);
4614 if (opt_name
!= NULL
)
4616 if ((opt
= locate_opt(opt_name
)) != NULL
)
4618 ptr
= va_arg(args
, char *);
4621 if (!opt_set_parms(opt_name
, params
))
4623 "Duplicated parameters or bad settings for the option %s.",
4627 fatal_internal("Unknown option %s.", opt_name
);
4631 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4633 /* Here opt is a known option. */
4634 /* """"""""""""""""""""""""""" */
4635 if (opt
->params
!= NULL
)
4636 fatal_internal("Parameters are already set for %s.", opt_name
);
4640 size_t l
= strlen(params
);
4642 opt
->params
= xstrdup(params
);
4643 while ((n
= strcspn(opt
->params
, " \t")) < l
)
4644 opt
->params
[n
] = '|';
4650 /* This part associates a callback function to an option. */
4651 /* This function will be called when an instance of an option */
4653 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4660 /* The second argument must be the name of an existing option. */
4661 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4662 ptr
= va_arg(args
, char *);
4664 if ((opt
= locate_opt(ptr
)) != NULL
)
4667 fn(char *, char *, char *, int, char **, int, void *, int, void **);
4669 /* The third argument must be the callback function. */
4670 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4671 function
= va_arg(args
, fn
*);
4672 opt
->action
= function
;
4674 /* The fourth argument must be a pointer to an user's defined */
4675 /* variable or structure that the previous function can manage. */
4676 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4677 while ((data
= va_arg(args
, void *)) != NULL
)
4680 opt
->data
= xrealloc(opt
->data
, nb_data
* sizeof(void *));
4681 opt
->data
[nb_data
- 1] = data
;
4683 opt
->nb_data
= nb_data
;
4686 fatal_internal("Unknown option %s.", ptr
);
4690 /* This part associates a list of functions to control some */
4691 /* characteristics of the arguments of an option. */
4692 /* Each function will be called in order and must return 1 */
4693 /* to validate the arguments. */
4694 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4701 /* The second argument must be a string. */
4702 /* """"""""""""""""""""""""""""""""""""" */
4703 ptr
= va_arg(args
, char *);
4705 if ((opt
= locate_opt(ptr
)) != NULL
)
4707 typedef int fn(int, char **, char *);
4709 /* The third argument must be a function. */
4710 /* """""""""""""""""""""""""""""""""""""" */
4711 function
= va_arg(args
, fn
*);
4713 cstr
= xmalloc(sizeof(constraint_t
));
4714 cstr
->constraint
= function
;
4716 /* The fourth argument must be a string containing the argument of */
4717 /* The previous function separated by spaces or tabs. */
4718 /* Theses arguments will be passed to the previous function */
4719 /* max: 32 argument! */
4720 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4721 value
= xstrdup(va_arg(args
, char *));
4723 cstr
->to_free
= value
;
4724 cstr
->args
= xcalloc(sizeof(char *), 32);
4725 cstr
->nb_args
= str2argv(value
, cstr
->args
, 32);
4726 ll_append(opt
->constraints_list
, cstr
);
4729 fatal_internal("Unknown option %s.", ptr
);
4733 /* This part allows to indicate that an option must be evaluated */
4734 /* after a list of other options. */
4735 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4740 /* The second argument must be a string. */
4741 /* """"""""""""""""""""""""""""""""""""" */
4742 ptr
= va_arg(args
, char *);
4744 if ((opt
= locate_opt(ptr
)) != NULL
)
4750 ptr
= va_arg(args
, char *);
4754 rtrim(str
, " \t", 0);
4756 /* Feed the list of options to be evaluated after the given option. */
4757 /* This list will contain pointers to options. */
4758 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4759 opt_name
= xstrtok_r(str
, " \t,", &end_str
);
4760 if (opt_name
!= NULL
)
4762 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4764 ll_append(opt
->eval_before_list
, opt_before
);
4765 while ((opt_name
= xstrtok_r(NULL
, " \t,", &end_str
)) != NULL
)
4767 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4768 ll_append(opt
->eval_before_list
, opt_before
);
4770 fatal_internal("Unknown option %s.", opt_name
);
4774 fatal_internal("Unknown option %s.", opt_name
);
4777 fatal_internal("Not enough options to be evaluated after %s.",
4783 fatal_internal("Unknown option %s.", ptr
);
4788 /* This part allows to indicate that an option must be evaluated */
4789 /* before a list of other options. */
4790 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4795 /* The second argument must be a string. */
4796 /* """"""""""""""""""""""""""""""""""""" */
4797 ptr
= va_arg(args
, char *);
4799 if ((opt
= locate_opt(ptr
)) != NULL
)
4805 ptr
= va_arg(args
, char *);
4809 rtrim(str
, " \t", 0);
4811 /* Feed the list of options to be evaluated before the given option. */
4812 /* This list will contain pointers to options. */
4813 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4814 opt_name
= xstrtok_r(str
, " \t,", &end_str
);
4815 if (opt_name
!= NULL
)
4817 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4819 ll_append(opt_before
->eval_before_list
, opt
);
4820 while ((opt_name
= xstrtok_r(NULL
, " \t,", &end_str
)) != NULL
)
4822 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4823 ll_append(opt_before
->eval_before_list
, opt
);
4825 fatal_internal("Unknown option %s.", opt_name
);
4829 fatal_internal("Unknown option %s.", opt_name
);
4832 fatal_internal("Not enough options to be evaluated before %s.",
4838 fatal_internal("Unknown option %s.", ptr
);
4843 case visible_in_help
:
4848 /* The second argument must be a string containing */
4849 /* The name of an existing option. */
4850 /* """"""""""""""""""""""""""""""""""""""""""""""" */
4851 ptr
= va_arg(args
, char *);
4854 if (opt_name
!= NULL
)
4856 if ((opt
= locate_opt(opt_name
)) != NULL
)
4858 ptr
= va_arg(args
, char *);
4861 if (stricmp(toggle
, "yes") == 0)
4862 opt
->visible_in_help
= 1;
4863 else if (stricmp(toggle
, "no") == 0)
4864 opt
->visible_in_help
= 0;
4866 fatal_internal("The value for the visible_in_help setting must be "
4867 "\"yes\" or \"no\" (case insensitive).",
4871 fatal_internal("Unknown option %s.", opt_name
);
4875 "ctxopt_opt_add_settings: visible_in_help: not enough arguments.");
4886 /* =============================================================== */
4887 /* This function provides a way to set the behaviour of a context. */
4888 /* =============================================================== */
4890 ctxopt_add_ctx_settings(settings s
, ...)
4899 /* Add a set of mutually incompatible options in a context. */
4900 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4901 case incompatibilities
:
4908 ptr
= va_arg(args
, char *);
4909 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4911 ptr
= va_arg(args
, char *);
4912 list
= ctx
->incomp_list
;
4916 rtrim(str
, " \t", 0);
4918 n
= strcspn(str
, " \t");
4919 if (n
> 0 && n
< strlen(str
))
4920 ll_append(list
, str
);
4923 "Not enough incompatible options in the string: \"%s\".",
4927 fatal_internal("Unknown context %s.", ptr
);
4938 ptr
= va_arg(args
, char *);
4939 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4941 ptr
= va_arg(args
, char *);
4942 list
= ctx
->req_list
;
4946 rtrim(str
, " \t", 0);
4948 n
= strcspn(str
, " \t");
4949 if (n
> 0 && n
< strlen(str
))
4950 ll_append(list
, str
);
4952 fatal_internal("Not enough required options in the string: \"%s\".",
4956 fatal_internal("Unknown context %s.", ptr
);
4960 /* Add functions which will be called when */
4961 /* entering and exiting a context. */
4962 /* """"""""""""""""""""""""""""""""""""""" */
4970 ptr
= va_arg(args
, char *);
4971 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4973 typedef int fn(char *, direction
, char *, int, void **);
4975 function
= va_arg(args
, fn
*);
4976 ctx
->action
= function
;
4978 while ((data
= va_arg(args
, void *)) != NULL
)
4981 ctx
->data
= xrealloc(ctx
->data
, nb_data
* sizeof(void *));
4982 ctx
->data
[nb_data
- 1] = data
;
4984 ctx
->nb_data
= nb_data
;
4987 fatal_internal("Unknown context %s.", ptr
);