13 /* *********************** */
14 /* Static global variables */
15 /* *********************** */
16 static void * contexts_bst
;
17 static void * options_bst
;
23 /* ****************** */
24 /* Messages interface */
25 /* ****************** */
27 static void (**err_functions
)(errors e
, state_t
* state
);
30 fatal_internal(const char * format
, ...);
33 fatal(errors e
, char * errmsg
);
35 static int user_rc
; /* Used by various callback functions */
36 static int user_value
; /* Used by various callback functions */
37 static char * user_string
; /* Used by various callback functions */
38 static char * user_string2
; /* Used by various callback functions */
39 static void * user_object
; /* Used by various callback functions */
41 /* *************************** */
42 /* Memory management interface */
43 /* *************************** */
49 xcalloc(size_t num
, size_t size
);
52 xrealloc(void * ptr
, size_t size
);
55 xstrdup(const char * p
);
58 xstrndup(const char * str
, size_t len
);
63 typedef struct bst_s bst_t
;
73 #if 0 /* Unused yet */
75 bst_delete(const void * vkey
, void ** vrootp
,
76 int (*compar
)(const void *, const void *));
80 bst_destroy_recurse(bst_t
* root
, void (*free_action
)(void *));
83 bst_destroy(void * vrootp
, void (*freefct
)(void *));
86 bst_find(const void * vkey
, void * const * vrootp
,
87 int (*compar
)(const void *, const void *));
90 bst_search(const void * vkey
, void ** vrootp
,
91 int (*compar
)(const void *, const void *));
94 bst_walk_recurse(const bst_t
* root
,
95 void (*action
)(const void *, walk_order_e
, int), int level
);
98 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int));
100 /* ********************* */
101 /* Linked list Interface */
102 /* ********************* */
104 typedef struct ll_node_s ll_node_t
;
105 typedef struct ll_s ll_t
;
108 ll_append(ll_t
* const list
, void * const data
);
111 ll_prepend(ll_t
* const list
, void * const data
);
114 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
);
117 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
);
120 ll_delete(ll_t
* const list
, ll_node_t
* node
);
122 #if 0 /* Unused yet */
124 ll_find(ll_t
* const, void * const, int (*)(const void *, const void *));
128 ll_init(ll_t
* list
);
137 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
);
139 /* ****************** */
140 /* various interfaces */
141 /* ****************** */
144 ltrim(char * str
, const char * trim_str
);
147 rtrim(char * str
, const char * trim_str
, size_t min
);
150 strchrcount(char * str
, char c
);
153 strpref(char * s1
, char * s2
);
156 xstrtok_r(char * str
, const char * delim
, char ** end
);
158 /* **************** */
159 /* ctxopt interface */
160 /* **************** */
162 typedef struct opt_s opt_t
;
163 typedef struct par_s par_t
;
164 typedef struct ctx_s ctx_t
;
165 typedef struct constraint_s constraint_t
;
166 typedef struct ctx_inst_s ctx_inst_t
;
167 typedef struct opt_inst_s opt_inst_t
;
168 typedef struct seen_opt_s seen_opt_t
;
171 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
);
174 ctx_compare(const void * c1
, const void * c2
);
177 opt_compare(const void * o1
, const void * o2
);
180 par_compare(const void * a1
, const void * a2
);
183 seen_opt_compare(const void * so1
, const void * so2
);
186 locate_ctx(char * name
);
189 locate_opt(char * name
);
192 locate_par(char * name
, ctx_t
* ctx
);
195 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
196 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
197 int * has_early_eval
);
199 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
200 int has_optional
, int has_ellipsis
, int has_rule
);
202 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
);
205 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
);
208 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
);
211 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
);
214 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
);
217 match_prefix_cb(const void * node
, walk_order_e kind
, int level
);
220 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
);
223 opt_parse(char * s
, opt_t
** opt
);
226 init_opts(char * spec
, ctx_t
* ctx
);
229 ctxopt_build_cmdline_list(int nb_words
, char ** words
);
232 opt_set_parms(char * opt_name
, char * par_str
);
235 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
);
238 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
);
240 /* ********************** */
241 /* Message implementation */
242 /* ********************** */
244 /* ================================================================== */
245 /* Fatal error function used when an fatal condition was encountered. */
246 /* This function is reserved for the ctxopt internal usage. */
248 /* format : printf like format */
249 /* ... : remaining arguments interpreted using the format argument */
250 /* ================================================================== */
252 fatal_internal(const char * format
, ...)
256 fprintf(stderr
, "CTXOPT: ");
258 va_start(args
, format
);
259 vfprintf(stderr
, format
, args
);
260 fprintf(stderr
, "\n");
266 /* ====================================================================== */
267 /* Generic fatal error function. This one uses the global status ctxopt */
268 /* stored in the cur_state structure and can call custom error functions. */
269 /* registered by the users for a given error identifier. */
271 /* e : error identifier responsible of the fatal error */
272 /* errmsg : users's provided string specific to the error e */
273 /* Note that errmsg is not used in all cases */
275 /* CTXOPTMISPAR Missing parameter */
276 /* CTXOPTMISARG Missing argument */
277 /* CTXOPTDUPOPT Duplicated option */
278 /* CTXOPTUNKPAR Unknown parameter */
279 /* CTXOPTINCOPT Incompatible option */
280 /* CTXOPTCTEOPT Option: bad number of occurrences */
281 /* CTXOPTCTLOPT Option: not enough occurrences */
282 /* CTXOPTCTGOPT Option: too many occurrence of */
283 /* CTXOPTCTEARG Arguments: bad number of occurrences */
284 /* CTXOPTCTLARG Arguments: not enough occurrences */
285 /* CTXOPTCTGARG Arguments: too many occurrences */
286 /* ====================================================================== */
288 fatal(errors e
, char * errmsg
)
290 if (err_functions
[e
] != NULL
)
291 err_functions
[e
](e
, cur_state
);
300 if (cur_state
->ctx_par_name
!= NULL
)
302 "Mandatory parameter(s): %s are missing in the context "
303 "introduced by %s.\n",
304 errmsg
, cur_state
->ctx_par_name
);
307 "Mandatory parameter(s): %s are missing "
308 "in the main context.\n",
315 if (cur_state
->pre_opt_par_name
!= NULL
)
316 fprintf(stderr
, "%s requires argument(s).\n",
317 cur_state
->pre_opt_par_name
);
319 fprintf(stderr
, "%s requires argument(s).\n",
320 cur_state
->cur_opt_par_name
);
324 if (cur_state
->pre_opt_par_name
!= NULL
)
326 "The parameter(s) %s can only appear once in the context "
327 "introduced by %s.\n",
328 cur_state
->cur_opt_par_name
, cur_state
->ctx_par_name
);
331 "The parameter(s) %s can only appear once "
332 "in the main context.\n",
333 cur_state
->cur_opt_par_name
);
337 fprintf(stderr
, "Unknown parameter: %s.\n",
338 cur_state
->cur_opt_par_name
);
339 fprintf(stderr
, errmsg
);
343 fprintf(stderr
, "%s is incompatible with %s.\n",
344 cur_state
->cur_opt_par_name
, errmsg
);
348 if (cur_state
->ctx_par_name
)
350 "%s must appear exactly %u times in the context "
351 "introduced by %s.\n",
352 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
353 cur_state
->ctx_par_name
);
356 "%s must appear exactly %u times in "
357 "the main context.\n",
358 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
362 if (cur_state
->ctx_par_name
)
364 "%s must appear less than %u times in the context "
365 "introduced by %s.\n",
366 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
367 cur_state
->ctx_par_name
);
370 "%s must appear less than %u times in the main context.\n",
371 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
375 if (cur_state
->ctx_par_name
)
377 "%s must appear more than %u times in the context "
378 "introduced by %s.\n",
379 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
380 cur_state
->ctx_par_name
);
383 "%s must appear more than %u times in the main context.\n",
384 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
388 fprintf(stderr
, "%s must have exactly %u arguments.\n",
389 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
393 fprintf(stderr
, "%s must have less than %u arguments.\n",
394 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
398 fprintf(stderr
, "%s must have more than %u arguments.\n",
399 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
407 if (cur_state
->ctx_name
!= NULL
)
408 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, continue_after
);
410 exit(e
); /* Program exist with the error id e as return code */
413 /* ******************************** */
414 /* Memory management implementation */
415 /* ******************************** */
417 /* ================= */
418 /* Customized malloc */
419 /* ================= */
426 real_size
= (size
> 0) ? size
: 1;
427 allocated
= malloc(real_size
);
428 if (allocated
== NULL
)
429 fatal_internal("Insufficient memory (attempt to malloc %lu bytes)\n",
430 (unsigned long int)size
);
435 /* ================= */
436 /* Customized calloc */
437 /* ================= */
439 xcalloc(size_t n
, size_t size
)
444 size
= (size
> 0) ? size
: 1;
445 allocated
= calloc(n
, size
);
446 if (allocated
== NULL
)
447 fatal_internal("Insufficient memory (attempt to calloc %lu bytes)\n",
448 (unsigned long int)size
);
453 /* ================== */
454 /* Customized realloc */
455 /* ================== */
457 xrealloc(void * p
, size_t size
)
461 allocated
= realloc(p
, size
);
462 if (allocated
== NULL
&& size
> 0)
463 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes)\n",
464 (unsigned long int)size
);
469 /* =================================== */
470 /* strdup implementation using xmalloc */
471 /* =================================== */
473 xstrdup(const char * p
)
477 allocated
= xmalloc(strlen(p
) + 1);
478 strcpy(allocated
, p
);
483 /* ================================================== */
484 /* strndup implementation using xmalloc */
485 /* This version guarantees that there is a final '\0' */
486 /* ================================================== */
488 xstrndup(const char * str
, size_t len
)
492 p
= memchr(str
, '\0', len
);
497 p
= xmalloc(len
+ 1);
504 /* ************************** */
505 /* Linked list implementation */
506 /* ************************** */
508 /* Linked list node structure */
509 /* """""""""""""""""""""""""" */
513 struct ll_node_s
* next
;
514 struct ll_node_s
* prev
;
517 /* Linked List structure */
518 /* """"""""""""""""""""" */
526 /* ======================== */
527 /* Create a new linked list */
528 /* ======================== */
532 ll_t
* ret
= xmalloc(sizeof(ll_t
));
538 /* ======================== */
539 /* Initialize a linked list */
540 /* ======================== */
549 /* ==================================================== */
550 /* Allocate the space for a new node in the linked list */
551 /* ==================================================== */
555 ll_node_t
* ret
= xmalloc(sizeof(ll_node_t
));
560 /* ==================================================================== */
561 /* Append a new node filled with its data at the end of the linked list */
562 /* The user is responsible for the memory management of the data */
563 /* ==================================================================== */
565 ll_append(ll_t
* const list
, void * const data
)
569 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
570 | uses xmalloc which does not return if there *
571 | is an allocation error. */
576 node
->prev
= list
->tail
;
578 list
->tail
->next
= node
;
587 /* =================================================================== */
588 /* Put a new node filled with its data at the beginning of the linked */
589 /* list. The user is responsible for the memory management of the data */
590 /* =================================================================== */
592 ll_prepend(ll_t
* const list
, void * const data
)
596 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
597 | uses xmalloc which does not return if there *
598 | is an allocation error. */
603 node
->next
= list
->head
;
605 list
->head
->prev
= node
;
614 /* ======================================================= */
615 /* Insert a new node before the specified node in the list */
616 /* ======================================================= */
618 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
)
620 ll_node_t
* new_node
;
622 if (node
->prev
== NULL
)
623 ll_prepend(list
, data
);
626 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
627 | uses xmalloc which does not return if there *
628 | is an allocation error. */
630 new_node
->data
= data
;
631 new_node
->next
= node
;
632 new_node
->prev
= node
->prev
;
633 node
->prev
->next
= new_node
;
634 node
->prev
= new_node
;
640 /* ====================================================== */
641 /* Insert a new node after the specified node in the list */
642 /* ====================================================== */
644 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
)
646 ll_node_t
* new_node
;
648 if (node
->next
== NULL
)
649 ll_append(list
, data
);
652 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
653 | uses xmalloc which does not return if there *
654 | is an allocation error. */
656 new_node
->data
= data
;
657 new_node
->prev
= node
;
658 new_node
->next
= node
->next
;
659 node
->next
->prev
= new_node
;
660 node
->next
= new_node
;
666 /* ================================================================ */
667 /* Remove a node from a linked list */
668 /* The memory taken by the deleted node must be freed by the caller */
669 /* ================================================================ */
671 ll_delete(ll_t
* const list
, ll_node_t
* node
)
673 if (list
->head
== list
->tail
)
675 if (list
->head
!= NULL
)
676 list
->head
= list
->tail
= NULL
;
680 else if (node
->prev
== NULL
)
682 list
->head
= node
->next
;
683 list
->head
->prev
= NULL
;
685 else if (node
->next
== NULL
)
687 list
->tail
= node
->prev
;
688 list
->tail
->next
= NULL
;
692 node
->next
->prev
= node
->prev
;
693 node
->prev
->next
= node
->next
;
701 #if 0 /* Unused yet */
702 /* =========================================================================*/
703 /* Find a node in the list containing data. Return the node pointer or NULL */
705 /* A comparison function must be provided to compare a and b (strcmp like). */
706 /* =========================================================================*/
708 ll_find(ll_t
* const list
, void * const data
,
709 int (*cmpfunc
)(const void * a
, const void * b
))
713 if (NULL
== (node
= list
->head
))
718 if (0 == cmpfunc(node
->data
, data
))
720 } while (NULL
!= (node
= node
->next
));
726 /* ==================================================================== */
727 /* Allocates and fills an array of strings from a list */
729 /* 1) The list node must contain strings (char *) */
730 /* 2) The strings in the resulting array MUST NOT be freed as the are */
731 /* NOT copied from the strings of the list. */
733 /* IN list : The list from which the array is generated */
734 /* IN start_node : The node of the list which will be the first node to */
735 /* consider to create the array */
736 /* OUT: count : The number of elements of the resulting array. */
737 /* OUT: array : The resulting array or NULL if the list is empty. */
738 /* RC : : The number of elements of the resulting array. */
739 /* ==================================================================== */
741 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
)
750 if (list
== NULL
|| node
== NULL
)
757 *array
= xmalloc((list
->len
+ 1) * sizeof(char *));
760 (*array
)[n
++] = (char *)(node
->data
);
766 (*array
)[*count
] = NULL
;
771 /* ******************************************************************* */
772 /* BST (search.h compatible) implementation */
774 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
775 /* the AT&T man page says. */
777 /* Written by reading the System V Interface Definition, not the code. */
779 /* Totally public domain. */
780 /* ******************************************************************* */
785 struct bst_s
* llink
;
786 struct bst_s
* rlink
;
789 #if 0 /* Unused yet */
790 /* ========================== */
791 /* delete node with given key */
792 /* ========================== */
794 bst_delete(const void * vkey
, void ** vrootp
,
795 int (*compar
)(const void *, const void *))
797 bst_t
** rootp
= (bst_t
**)vrootp
;
801 if (rootp
== NULL
|| (p
= *rootp
) == NULL
)
804 while ((cmp
= (*compar
)(vkey
, (*rootp
)->key
)) != 0)
807 rootp
= (cmp
< 0) ? &(*rootp
)->llink
/* follow llink branch */
808 : &(*rootp
)->rlink
; /* follow rlink branch */
810 return NULL
; /* key not found */
812 r
= (*rootp
)->rlink
; /* D1: */
813 if ((q
= (*rootp
)->llink
) == NULL
) /* Left NULL? */
816 { /* Right link is NULL? */
817 if (r
->llink
== NULL
)
818 { /* D2: Find successor */
823 { /* D3: Find NULL link */
824 for (q
= r
->llink
; q
->llink
!= NULL
; q
= r
->llink
)
827 q
->llink
= (*rootp
)->llink
;
828 q
->rlink
= (*rootp
)->rlink
;
832 free(*rootp
); /* D4: Free node */
833 *rootp
= q
; /* link parent to new node */
842 bst_destroy_recurse(bst_t
* root
, void (*free_action
)(void *))
844 if (root
->llink
!= NULL
)
845 bst_destroy_recurse(root
->llink
, free_action
);
846 if (root
->rlink
!= NULL
)
847 bst_destroy_recurse(root
->rlink
, free_action
);
849 (*free_action
)((void *)root
->key
);
854 bst_destroy(void * vrootp
, void (*freefct
)(void *))
856 bst_t
* root
= (bst_t
*)vrootp
;
859 bst_destroy_recurse(root
, freefct
);
862 /* ======================== */
863 /* find a node, or return 0 */
864 /* ======================== */
866 bst_find(const void * vkey
, void * const * vrootp
,
867 int (*compar
)(const void *, const void *))
869 bst_t
* const * rootp
= (bst_t
* const *)vrootp
;
874 while (*rootp
!= NULL
)
878 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
879 return *rootp
; /* key found */
880 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
881 : &(*rootp
)->rlink
; /* T4: follow right branch */
886 /* ===================================== */
887 /* find or insert datum into search tree */
888 /* ===================================== */
890 bst_search(const void * vkey
, void ** vrootp
,
891 int (*compar
)(const void *, const void *))
894 bst_t
** rootp
= (bst_t
**)vrootp
;
899 while (*rootp
!= NULL
)
903 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
904 return *rootp
; /* we found it! */
906 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
907 : &(*rootp
)->rlink
; /* T4: follow right branch */
910 q
= xmalloc(sizeof(bst_t
)); /* T5: key not found */
912 { /* make new node */
913 *rootp
= q
; /* link new node to old */
914 q
->key
= (void *)vkey
; /* initialize new node */
915 q
->llink
= q
->rlink
= NULL
;
920 /* ======================== */
921 /* Walk the nodes of a tree */
922 /* ======================== */
924 bst_walk_recurse(const bst_t
* root
,
925 void (*action
)(const void *, walk_order_e
, int), int level
)
927 if (root
->llink
== NULL
&& root
->rlink
== NULL
)
928 (*action
)(root
, leaf
, level
);
931 (*action
)(root
, preorder
, level
);
932 if (root
->llink
!= NULL
)
933 bst_walk_recurse(root
->llink
, action
, level
+ 1);
934 (*action
)(root
, postorder
, level
);
935 if (root
->rlink
!= NULL
)
936 bst_walk_recurse(root
->rlink
, action
, level
+ 1);
937 (*action
)(root
, endorder
, level
);
942 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int))
944 if (vroot
!= NULL
&& action
!= NULL
)
945 bst_walk_recurse(vroot
, action
, 0);
948 /* *********************** */
949 /* various implementations */
950 /* *********************** */
952 /* ======================= */
953 /* Trim leading characters */
954 /* ======================= */
956 ltrim(char * str
, const char * trim_str
)
958 size_t len
= strlen(str
);
959 size_t begin
= strspn(str
, trim_str
);
963 for (i
= begin
; i
<= len
; ++i
)
964 str
[i
- begin
] = str
[i
];
967 /* ================================================= */
968 /* Trim trailing characters */
969 /* The resulting string will have at least min bytes */
970 /* even if trailing spaces remain. */
971 /* ================================================= */
973 rtrim(char * str
, const char * trim_str
, size_t min
)
975 size_t len
= strlen(str
);
976 while (len
> min
&& strchr(trim_str
, str
[len
- 1]))
980 /* ================================================== */
981 /* Count the number of occurrences of the character c */
982 /* in the string str. */
983 /* The str pointer is assumed to be not NULL */
984 /* ================================================== */
986 strchrcount(char * str
, char c
)
997 /* =============================================== */
998 /* Is the string str2 a prefix of the string str1? */
999 /* =============================================== */
1001 strpref(char * str1
, char * str2
)
1003 while (*str1
!= '\0' && *str1
== *str2
)
1009 return *str2
== '\0';
1012 /* ======================================================================== */
1013 /* Strings concatenation with dynamic memory allocation */
1014 /* IN : a variable number of char * arguments with NULL terminating */
1016 /* The first one must have been dynamically allocated and is mandatory */
1018 /* Returns a new allocated string containing the concatenation of all */
1019 /* the arguments. It is the caller's responsibility to free the resulting */
1021 /* ======================================================================== */
1023 strappend(char * str
, ...)
1029 l
= 1 + strlen(str
);
1030 va_start(args
, str
);
1032 s
= va_arg(args
, char *);
1037 s
= va_arg(args
, char *);
1042 str
= xrealloc(str
, l
);
1044 va_start(args
, str
);
1045 s
= va_arg(args
, char *);
1050 s
= va_arg(args
, char *);
1057 /* ====================================================================== */
1058 /* public domain strtok_r() by Charlie Gordon */
1059 /* from comp.lang.c 9/14/2007 */
1060 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1062 /* (Declaration that it's public domain): */
1063 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1065 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1067 /* ====================================================================== */
1069 xstrtok_r(char * str
, const char * delim
, char ** end
)
1079 str
+= strspn(str
, delim
);
1086 str
+= strcspn(str
, delim
);
1096 /* =========================================================== */
1097 /* Fills an array of strings from the words composing a string */
1099 /* str: initial string which will be altered */
1100 /* args: array of pointers to the start of the words in str */
1101 /* max: maximum number of words used before giving up */
1102 /* return: the number of words (<=max) */
1103 /* =========================================================== */
1105 str2argv(char * str
, char ** args
, int max
)
1114 while (*str
== ' ' || *str
== '\t')
1120 args
[nb_args
] = str
;
1123 while (*str
&& (*str
!= ' ') && (*str
!= '\t'))
1130 /* ********************* */
1131 /* ctxopt implementation */
1132 /* ********************* */
1134 static int ctxopt_initialized
= 0; /* cap_init has not yet been called */
1136 /* context structure */
1137 /* """"""""""""""""" */
1141 ll_t
* opt_list
; /* list of options allowed in this context */
1142 ll_t
* incomp_list
; /* list of strings containing incompatible names *
1143 | of options separated by spaces or tabs */
1144 int (*action
)(char * name
, int type
, char * new_ctx
, int ctx_nb_data
,
1151 /* https://textik.com/#488ce3649b6c60f5 */
1153 /* +--------------+ */
1154 /* |first_ctx_inst| */
1155 /* +---+----------+ */
1157 /* +--v-----+ +--------+ +--------+ +-----+ */
1158 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1159 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1161 /* | | +-v------+ | | */
1162 /* | +--+ctx_inst<-----------+ | */
1163 /* | +-+------+ | */
1165 /* | +-v------+ | */
1166 /* +------+ctx_inst<--------------------------+ */
1173 /* option structure */
1174 /* """""""""""""""" */
1177 char * name
; /* option name */
1178 char * next_ctx
; /* new context this option may lead to */
1179 ll_t
* ctx_list
; /* list of contexts allowing this option */
1180 char * params
; /* string containing all the parameters of *
1183 void (*action
)( /* The option associated action */
1184 char * ctx_name
, /* context name */
1185 char * opt_name
, /* option name */
1186 char * par
, /* option parameter */
1187 int nb_args
, /* number of arguments */
1188 char ** args
, /* option arguments */
1189 int nb_opt_data
, /* number of option data pointers */
1190 void ** opt_data
, /* option data pointers */
1191 int nb_ctx_data
, /* nb of current context data ptrs */
1192 void ** ctx_data
/* current context data pointers */
1195 int nb_data
; /* number of the data pointers passed as argument to *
1197 void ** data
; /* array of data pointers passed as argument to action */
1199 int args
; /* 1 if this option takes arguments else 0 */
1200 int optional
; /* 1 if the option is optional, else 0 */
1201 int multiple
; /* 1 if the option can appear more than one time in a *
1202 | context, else 0 */
1204 int opt_count_matter
; /* 1 if we must restrict the count, else 0 */
1205 int occurrences
; /* Number of option occurrences in a context */
1206 char opt_count_oper
; /* <, = or > */
1207 unsigned opt_count_mark
; /* Value to be compared to with opt_count_oper */
1209 char * arg
; /* symbolic text after # describing the option argument */
1211 int optional_args
; /* 1 of option is optional else 0 */
1212 int multiple_args
; /* 1 is option can appear more than once in a context *
1215 int opt_args_count_matter
; /* 1 if we must restrict the count, else 0 */
1216 char opt_args_count_oper
; /* <, = or > */
1217 unsigned opt_args_count_mark
; /* Value to be compared to with *
1220 int eval_first
; /* 1 if this option must be evaluated before *
1221 | the options without this mark */
1223 ll_t
* constraints_list
; /* List of constraint checking functions pointers. */
1226 /* context instance structure */
1227 /* """""""""""""""""""""""""" */
1230 ctx_t
* ctx
; /* the context whose this is an instance of */
1231 ctx_inst_t
* prev_ctx_inst
; /* ctx_inst of the opt_inst which led to the *
1232 | creation of this ctx_inst structure. */
1233 opt_inst_t
* gen_opt_inst
; /* opt_inst which led to the creation of a *
1234 | instance of this structure */
1235 ll_t
* incomp_bst_list
; /* list of seen_opt_t bst */
1236 void * seen_opt_bst
; /* tree of seen_opt_t */
1237 ll_t
* opt_inst_list
; /* The list of option instances in this *
1238 | context instance */
1239 char * par_name
; /* parameter which created this instance */
1242 /* Option instance structure */
1243 /* """"""""""""""""""""""""" */
1246 opt_t
* opt
; /* The option this is an instance of */
1247 char * opt_name
; /* The option which led to this creation */
1248 char * par
; /* The parameter which led to this creation */
1249 ll_t
* values_list
; /* The list of arguments of this option */
1250 ctx_inst_t
* next_ctx_inst
; /* The new context instance this option *
1251 | instance may create */
1254 /* Structure used to check if an option has bee seen or not */
1255 /* in a context instance */
1256 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1259 opt_t
* opt
; /* The concerned option */
1260 char * par
; /* Parameter which led to the making of this structure */
1261 char * count
; /* Number of seen occurrences of this parameter */
1262 int seen
; /* 1 if seen in the context instances, else 0 */
1265 /* parameter structure which links a parameter to the option it belongs to */
1266 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1269 char * name
; /* Parameter name (with the leading - */
1270 opt_t
* opt
; /* Attached option */
1273 /* Constraint structure */
1274 /* """""""""""""""""""" */
1277 int (*constraint
)(int nb_args
, char ** args
, char * value
);
1282 state_t
* cur_state
= NULL
; /* Current analysis state */
1283 static ll_t
* cmdline_list
; /* List of interpreted CLI words *
1284 | serves as the basis for the *
1285 | analysis of the parameters */
1286 static ctx_t
* main_ctx
= NULL
; /* initial context instance */
1287 static ctx_inst_t
* first_ctx_inst
= NULL
; /* Pointer to the fist context *
1288 | instance which holds the *
1289 | options instances */
1290 static ll_t
* ctx_inst_list
; /* List of the context instances */
1292 /* ====================================================== */
1293 /* Parse a string for the next matching token. */
1295 /* s: string to parse. */
1296 /* token: pre_allocated array of max tok_len characters */
1297 /* pattern: scanf type pattern token must match */
1298 /* pos: number of characters successfully parsed in s */
1300 /* Returns: a pointer to the first unread character or */
1301 /* to he terminating \0. */
1302 /* ====================================================== */
1304 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
)
1306 char * full_pattern
;
1312 n
= snprintf(len
, 3, "%zu", tok_len
);
1316 full_pattern
= xmalloc(strlen(pattern
) + n
+ 4);
1318 strcpy(full_pattern
, "%");
1319 strcat(full_pattern
, len
);
1320 strcat(full_pattern
, pattern
);
1321 strcat(full_pattern
, "%n");
1323 n
= sscanf(s
, full_pattern
, token
, pos
);
1333 /* **************************** */
1334 /* Various comparison functions */
1335 /* **************************** */
1338 ctx_compare(const void * c1
, const void * c2
)
1340 return strcmp(((ctx_t
*)c1
)->name
, ((ctx_t
*)c2
)->name
);
1344 opt_compare(const void * o1
, const void * o2
)
1346 return strcmp(((opt_t
*)o1
)->name
, ((opt_t
*)o2
)->name
);
1350 par_compare(const void * a1
, const void * a2
)
1352 return strcmp(((par_t
*)a1
)->name
, ((par_t
*)a2
)->name
);
1356 seen_opt_compare(const void * so1
, const void * so2
)
1360 o1
= ((seen_opt_t
*)so1
)->opt
;
1361 o2
= ((seen_opt_t
*)so2
)->opt
;
1363 return strcmp(o1
->name
, o2
->name
);
1366 /* ******************************************************************** */
1367 /* Helper functions to locate contexts, options and parameters in a bst */
1368 /* by their names. */
1369 /* ******************************************************************** */
1372 locate_ctx(char * name
)
1379 if ((node
= bst_find(&ctx
, &contexts_bst
, ctx_compare
)) == NULL
)
1386 locate_opt(char * name
)
1393 if ((node
= bst_find(&opt
, &options_bst
, opt_compare
)) == NULL
)
1400 locate_par(char * name
, ctx_t
* ctx
)
1404 void * bst
= ctx
->par_bst
;
1408 if ((node
= bst_find(&par
, &bst
, par_compare
)) == NULL
)
1414 /* =================================================================== */
1415 /* Utility function to format and print the options present in a list. */
1417 /* IN list : a list of options */
1418 /* OUT has_* : a set of flags which will determine the content of the */
1419 /* explanation given after the formatted printing of the */
1421 /* =================================================================== */
1423 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
1424 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
1425 int * has_early_eval
)
1427 ll_node_t
* node
= list
->head
;
1432 line
= xstrdup(" ");
1434 while (node
!= NULL
)
1436 option
= xstrdup("");
1441 option
= strappend(option
, "[", NULL
);
1445 if (opt
->eval_first
)
1447 option
= strappend(option
, "*", NULL
);
1448 *has_early_eval
= 1;
1451 option
= strappend(option
, opt
->params
, NULL
);
1453 if (opt
->next_ctx
!= NULL
)
1455 option
= strappend(option
, ">", opt
->next_ctx
, NULL
);
1456 *has_ctx_change
= 1;
1461 if (opt
->opt_count_oper
!= '\0')
1465 o
[0] = opt
->opt_count_oper
;
1467 snprintf(m
, 3, "%u", opt
->opt_count_mark
);
1468 option
= strappend(option
, "...", o
, m
, NULL
);
1472 option
= strappend(option
, "...", NULL
);
1479 if (*(opt
->arg
) == '#')
1480 *has_generic_arg
= 1;
1482 option
= strappend(option
, " ", NULL
);
1484 if (opt
->optional_args
)
1486 option
= strappend(option
, "[", opt
->arg
, NULL
);
1490 option
= strappend(option
, opt
->arg
, NULL
);
1492 if (opt
->multiple_args
)
1494 if (opt
->opt_args_count_oper
!= '\0')
1498 o
[0] = opt
->opt_args_count_oper
;
1500 snprintf(m
, 3, "%u", opt
->opt_args_count_mark
);
1501 option
= strappend(option
, "...", o
, m
, NULL
);
1505 option
= strappend(option
, "...", NULL
);
1509 if (opt
->optional_args
)
1510 option
= strappend(option
, "]", NULL
);
1513 option
= strappend(option
, "]", NULL
);
1515 if (strlen(line
) + 1 + strlen(option
) < 80)
1516 line
= strappend(line
, option
, " ", NULL
);
1519 printf("%s\n", line
);
1521 line
= strappend(line
, option
, " ", NULL
);
1529 printf("%s\n", line
);
1534 /* ==================================================== */
1535 /* Explain the special syntactic symbols present in the */
1536 /* generated usage messages. */
1537 /* ==================================================== */
1539 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
1540 int has_optional
, int has_ellipsis
, int has_rule
)
1542 if (has_early_eval
|| has_ctx_change
|| has_generic_arg
|| has_optional
1543 || has_ellipsis
|| has_rule
)
1545 printf("\nSyntactic explanations:\n");
1546 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1547 "must be entered.\n");
1548 printf("The following is just there to explain the other symbols "
1552 printf("* : the parameters for this option will be "
1553 "evaluated first.\n");
1556 "> : The context after this symbol will become the next "
1558 if (has_generic_arg
)
1559 printf("#tag : argument tag giving a clue to its meaning.\n");
1562 "[...] : the object between square brackets is optional.\n");
1564 printf("... : the previous object can be repeated more "
1565 "than one time.\n");
1567 printf("[<|=|>]number: rules constraining the number of "
1568 "parameters/arguments.\n");
1572 /* ******************************************************* */
1573 /* Various utility and callback function call when walking */
1574 /* through a bst. */
1575 /* ******************************************************* */
1578 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
)
1580 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1582 if (kind
== postorder
|| kind
== leaf
)
1584 if ((!seen_opt
->opt
->optional
) && seen_opt
->seen
== 0)
1587 user_string
= strappend(user_string
, seen_opt
->opt
->params
, " ", NULL
);
1593 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
)
1595 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1597 if (kind
== postorder
|| kind
== leaf
)
1598 if (seen_opt
->seen
== 1)
1601 user_object
= seen_opt
->par
;
1606 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
)
1608 ctx_t
* ctx
= main_ctx
;
1609 ctx_t
* cur_ctx
= ((bst_t
*)node
)->key
;
1613 int has_optional
= 0;
1614 int has_ellipsis
= 0;
1616 int has_generic_arg
= 0;
1617 int has_ctx_change
= 0;
1618 int has_early_eval
= 0;
1620 if (kind
== postorder
|| kind
== leaf
)
1621 if (strcmp(ctx
->name
, cur_ctx
->name
) != 0)
1623 list
= cur_ctx
->opt_list
;
1625 printf("\nAllowed options in the context %s:\n", cur_ctx
->name
);
1626 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
,
1627 &has_generic_arg
, &has_ctx_change
, &has_early_eval
);
1632 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
)
1634 opt_t
* opt
= ((bst_t
*)node
)->key
;
1636 if (kind
== postorder
|| kind
== leaf
)
1638 if (opt
->params
== NULL
) /* opt must have associated parameters */
1639 fatal_internal("Option %s has no registered parameter.\n", opt
->name
);
1641 if (opt
->action
== NULL
) /* opt must have an action */
1642 fatal_internal("Option %s has no registered action.\n", opt
->name
);
1647 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
)
1649 ctx_t
* ctx
= ((bst_t
*)node
)->key
;
1651 if (kind
== postorder
|| kind
== leaf
)
1653 char * str
= xstrdup(user_string
);
1655 while (*str
!= '\0')
1657 if (locate_par(str
, ctx
) != NULL
)
1659 user_string2
= strappend(user_string2
, " ", ctx
->name
, NULL
);
1662 str
[strlen(str
) - 1] = '\0';
1669 match_prefix_cb(const void * node
, walk_order_e kind
, int level
)
1671 par_t
* par
= ((bst_t
*)node
)->key
;
1673 if (kind
== postorder
|| kind
== leaf
)
1674 if (strpref(par
->name
, (char *)user_object
))
1677 user_string
= strappend(user_string
, par
->name
, " ", NULL
);
1682 bst_null_action(void * data
)
1684 ; /* nothing to do */
1687 /* ====================================================================== */
1688 /* A parameter may not be separated from its first option by spaces, in */
1689 /* this case this function looks for a valid flag as a prefix and splits */
1690 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1693 /* IN word : the word to be checked. */
1694 /* IN ctx : the context in which the flag indexed by the word is to be */
1696 /* OUT pos : the offset in word pointing just after the matching prefix. */
1697 /* OUT opt : a pointer to the option associated with the new parameter */
1698 /* or NULL if none is found. */
1700 /* The returned pointer must be freed by the caller */
1701 /* ====================================================================== */
1703 look_for_valid_prefix_in_word(char * word
, ctx_t
* ctx
, int * pos
, opt_t
** opt
)
1708 par_t tmp_par
= { 0 };
1714 new = xstrdup(word
);
1720 } while ((par
= locate_par(tmp_par
.name
, ctx
)) == NULL
&& len
> 2);
1739 /* ============================================================= */
1740 /* If par_name is an unique abbreviation of an exiting parameter */
1741 /* in the context ctx, then return this parameter. */
1742 /* ============================================================= */
1744 abbrev_expand(char * par_name
, ctx_t
* ctx
)
1746 user_object
= par_name
;
1749 *user_string
= '\0';
1750 bst_walk(ctx
->par_bst
, match_prefix_cb
);
1751 rtrim(user_string
, " ", 0);
1753 if (user_rc
== 1) /* The number of matching abbreviations */
1754 return xstrdup(user_string
);
1761 void * tmp_opt_bst
= NULL
;
1763 /* for each word in the matching parameters return by walking the */
1764 /* parameter's tree of this context, do: */
1765 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1766 s
= first_s
= strtok(user_string
, " "); /* first_s holds a copy of *
1767 | the first word. */
1770 par
= locate_par(s
, ctx
);
1773 if (bst_find(opt
, &tmp_opt_bst
, opt_compare
) == NULL
)
1775 /* This option as not already been seen */
1776 /* store it and increase the seen counter */
1777 /* """""""""""""""""""""""""""""""""""""" */
1778 bst_search(opt
, &tmp_opt_bst
, opt_compare
);
1781 s
= strtok(NULL
, " ");
1784 if (tmp_opt_bst
!= NULL
)
1785 bst_destroy(tmp_opt_bst
, bst_null_action
);
1788 /* All the abbreviation lead to only one option */
1789 /* We can just continue as in the previous case. */
1790 /* """""""""""""""""""""""""""""""""""""""""""""" */
1791 return xstrdup(first_s
);
1797 /* ================================================================= */
1798 /* Terminates the program if mandatory options required by a context */
1799 /* are not present. */
1800 /* ================================================================= */
1802 check_for_missing_mandatory_opt(ctx_inst_t
* ctx_inst
, char * opt_par
)
1806 if (has_unseen_mandatory_opt(ctx_inst
, &missing
))
1807 fatal(CTXOPTMISPAR
, missing
);
1810 /* ====================================================== */
1811 /* Return 1 if at least one mandatory option was not seen */
1812 /* when quitting a context, else 0 */
1813 /* ====================================================== */
1815 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
)
1818 *user_string
= '\0';
1820 bst_walk(ctx_inst
->seen_opt_bst
, bst_seen_opt_cb
);
1821 rtrim(user_string
, " ", 0);
1823 *missing
= user_string
;
1825 return user_rc
? 1 : 0;
1828 /* ========================================================================= */
1829 /* This function terminates the program if an option or its arguments do not */
1830 /* conform to its occurrences constraint. */
1831 /* There constraints can appear by trailing >, < or = in their definition */
1832 /* given in ctxopt_new_ctx. */
1833 /* ========================================================================= */
1835 check_for_occurrences_issues(ctx_inst_t
* ctx_inst
)
1837 ctx_t
* ctx
= ctx_inst
->ctx
;
1840 opt_inst_t
* opt_inst
;
1844 node
= ctx
->opt_list
->head
;
1846 while (node
!= NULL
)
1850 /* Update current_state */
1851 /* -------------------- */
1852 cur_state
->opts_count
= opt
->opt_count_mark
;
1853 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
1855 if (opt
->opt_count_matter
)
1856 switch (opt
->opt_count_oper
)
1859 if (opt
->occurrences
> 0 && opt
->opt_count_mark
!= opt
->occurrences
)
1860 fatal(CTXOPTCTEOPT
, "");
1864 if (opt
->occurrences
> 0 && opt
->opt_count_mark
<= opt
->occurrences
)
1865 fatal(CTXOPTCTLOPT
, "");
1869 if (opt
->occurrences
> 0 && opt
->opt_count_mark
>= opt
->occurrences
)
1870 fatal(CTXOPTCTGOPT
, "");
1877 /* Check arguments */
1878 /* """"""""""""""" */
1879 node
= ctx_inst
->opt_inst_list
->head
;
1880 while (node
!= NULL
)
1882 opt_inst
= node
->data
;
1883 opt
= opt_inst
->opt
;
1885 /* Update current_state */
1886 /* -------------------- */
1887 cur_state
->opts_count
= opt
->opt_count_mark
;
1888 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
1890 int nb_values
= opt_inst
->values_list
->len
; /* Number of arguments of opt */
1892 if (opt
->opt_args_count_matter
)
1893 switch (opt
->opt_args_count_oper
)
1896 if (nb_values
> 0 && opt
->opt_args_count_mark
!= nb_values
)
1897 fatal(CTXOPTCTEARG
, "");
1901 if (nb_values
> 0 && opt
->opt_args_count_mark
<= nb_values
)
1902 fatal(CTXOPTCTLARG
, "");
1906 if (nb_values
> 0 && opt
->opt_args_count_mark
>= nb_values
)
1907 fatal(CTXOPTCTGARG
, "");
1915 /* ======================================================================== */
1916 /* Parse a strings describing options and some of their characteristics */
1917 /* The input string must have follow some rules like in the examples below: */
1919 /* "opt_name1 opt_name2" */
1920 /* "[opt_name1] opt_name2" */
1921 /* "[opt_name1] opt_name2..." */
1922 /* "[opt_name1 #...] opt_name2... [#]" */
1923 /* "[opt_name1 [#...]] opt_name2... [#...]" */
1925 /* Where [ ] encloses an optional part, # means: has parameters and ... */
1926 /* means that there can be more than one occurrence of the previous thing. */
1928 /* opt_name can be followed by a 'new context' change prefixed with the */
1929 /* symbol >, as in opt1>c2 by eg */
1931 /* This function returns as soon as one (or no) option has been parsed and */
1932 /* return the offset to the next option to parse. */
1934 /* In case of successful parsing, an new option is allocated and its */
1935 /* pointer returned. */
1936 /* ======================================================================== */
1938 opt_parse(char * s
, opt_t
** opt
)
1940 int opt_optional
= 0;
1941 int opt_multiple
= 0;
1942 int opt_count_matter
= 0;
1943 char opt_count_oper
= '\0';
1944 unsigned opt_count_mark
= 0;
1947 int opt_multiple_args
= 0;
1948 int opt_args_count_matter
= 0;
1949 char opt_args_count_oper
= '\0';
1950 unsigned opt_args_count_mark
= 0;
1951 int opt_optional_args
= 0;
1952 int opt_eval_first
= 0;
1966 memset(opt_arg
, '\0', 33);
1968 /* strip the leading blanks */
1972 if (*s
== '[') /* Start of an optional option */
1977 s
= strtoken(s
, token
, sizeof(token
), "[^] \n\t.]", &pos
);
1979 return -1; /* empty string */
1981 /* Early EOS, only return success if the option is mandatory */
1982 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1984 if (opt_optional
== 1)
1985 return -(s
- s_orig
- 1);
1987 /* validate the option name */
1988 /* ALPHA+(ALPHANUM|_)* */
1989 /* """""""""""""""""""""""" */
1991 if (!isalpha(*p
) && *p
!= '*')
1992 return -(s
- s_orig
- 1); /* opt_name must start with a letter */
2000 if (!isalnum(*p
) && *p
!= '_' && *p
!= '>')
2001 return -(s
- s_orig
- 1); /* opt_name must contain a letter, *
2002 * a number or a _ */
2007 opt_name
= xstrdup(token
+ 1); /* Ignore the first '*' in token */
2009 opt_name
= xstrdup(token
);
2020 /* Check if it can appear multiple times by looking for the dots */
2021 p
= strtoken(s
, token
, 3, "[.]", &pos
);
2024 if (strcmp(token
, "...") == 0)
2028 if (*s
== '<' || *s
== '=' || *s
== '>')
2033 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2036 opt_count_matter
= 1;
2037 opt_count_oper
= *s
;
2038 opt_count_mark
= value
;
2046 return -(s
- s_orig
- 1);
2050 /* A blank separates the option name and the argument tag */
2062 n
= sscanf(s
, "[%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2063 if (pos
> 1 && *opt_arg
== '#') /* [# has been read */
2066 opt_optional_args
= 1;
2068 opt_multiple_args
= 1; /* there were dots */
2070 s
+= pos
+ !!(n
== 2) * 3; /* skip the dots */
2072 if (*s
== '<' || *s
== '=' || *s
== '>')
2077 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2080 opt_args_count_matter
= 1;
2081 opt_args_count_oper
= *s
;
2082 opt_args_count_mark
= value
;
2087 /* Optional arg tag must end with a ] */
2091 return -(s
- s_orig
- 1);
2094 s
++; /* skip the ] */
2098 n
= sscanf(s
, "%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2099 if (pos
> 0 && *opt_arg
== '#') /* # has been read */
2102 if (n
== 2) /* there were dots */
2103 opt_multiple_args
= 1;
2105 s
+= pos
+ !!(n
== 2) * 3; /* skip the dots */
2107 if (*s
== '<' || *s
== '=' || *s
== '>')
2112 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2115 opt_args_count_matter
= 1;
2116 opt_args_count_oper
= *s
;
2117 opt_args_count_mark
= value
;
2125 /* Abort on extraneous ] if the option is mandatory */
2127 return -(s
- s_orig
- 1);
2129 s
++; /* skip the ] */
2131 /* Strip the following blanks */
2137 else if (opt_optional
== 0 && (!*s
|| isblank(*s
)))
2139 /* Strip the following blanks */
2145 else if (opt_args
== 0) /* # was not read it is possibly the start *
2146 * of another option */
2149 return -(s
- s_orig
- 1);
2154 /* strip the following blanks */
2160 if (*opt_name
== '>')
2161 fatal_internal("%s: option name is missing.", opt_name
);
2163 count
= strchrcount(opt_name
, '>');
2166 char * tmp
= strchr(opt_name
, '>');
2167 next_ctx
= xstrdup(tmp
+ 1);
2171 fatal_internal("%s: only one occurrence of '>' is allowed.", opt_name
);
2173 *opt
= xmalloc(sizeof(opt_t
));
2175 (*opt
)->name
= opt_name
;
2176 (*opt
)->optional
= opt_optional
;
2177 (*opt
)->multiple
= opt_multiple
;
2178 (*opt
)->opt_count_matter
= opt_count_matter
;
2179 (*opt
)->opt_count_oper
= opt_count_oper
;
2180 (*opt
)->opt_count_mark
= opt_count_mark
;
2181 (*opt
)->args
= opt_args
;
2182 (*opt
)->arg
= xstrdup(opt_arg
);
2183 (*opt
)->optional_args
= opt_optional_args
;
2184 (*opt
)->multiple_args
= opt_multiple_args
;
2185 (*opt
)->opt_args_count_matter
= opt_args_count_matter
;
2186 (*opt
)->opt_args_count_oper
= opt_args_count_oper
;
2187 (*opt
)->opt_args_count_mark
= opt_args_count_mark
;
2188 (*opt
)->eval_first
= opt_eval_first
;
2189 (*opt
)->next_ctx
= next_ctx
;
2190 (*opt
)->ctx_list
= ll_new();
2191 (*opt
)->constraints_list
= ll_new();
2192 (*opt
)->action
= NULL
;
2193 (*opt
)->data
= NULL
;
2198 /* ===================================================================== */
2199 /* Try to initialize all the option in a given string */
2200 /* Each parsed option are put in a bst tree with its name as index */
2202 /* On collision, the arguments only the signature are required to be */
2203 /* the same else this is considered as an error. Options can be used in */
2204 /* more than one context and can be optional in one and mandatory in */
2206 /* ===================================================================== */
2208 init_opts(char * spec
, ctx_t
* ctx
)
2210 opt_t
* opt
, *bst_opt
;
2216 if ((offset
= opt_parse(spec
, &opt
)) > 0)
2220 if ((node
= bst_find(opt
, &options_bst
, opt_compare
)) != NULL
)
2222 int same_next_ctx
= 0;
2224 bst_opt
= node
->key
; /* node extracted from the BST */
2226 if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
== NULL
)
2228 else if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
!= NULL
)
2230 else if (bst_opt
->next_ctx
!= NULL
&& opt
->next_ctx
== NULL
)
2233 same_next_ctx
= strcmp(bst_opt
->next_ctx
, opt
->next_ctx
) == 0;
2235 if (bst_opt
->optional_args
!= opt
->optional_args
2236 || bst_opt
->multiple_args
!= opt
->multiple_args
2237 || bst_opt
->args
!= opt
->args
|| !same_next_ctx
)
2239 fatal_internal("option %s already exists with "
2240 "a different arguments signature.\n",
2248 /* The new occurrence of the option option is legal */
2249 /* append the current context ptr in the list */
2250 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2251 ll_append(bst_opt
->ctx_list
, ctx
);
2253 /* Append the new option to the context's options list */
2254 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2255 ll_append(ctx
->opt_list
, bst_opt
);
2261 /* Initialize the option's context list with the current context */
2262 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2263 ll_append(opt
->ctx_list
, ctx
);
2265 /* Append the new option to the context's options list */
2266 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2267 ll_append(ctx
->opt_list
, opt
);
2269 /* Insert the new option in the BST */
2270 /* """""""""""""""""""""""""""""""" */
2271 bst_search(opt
, &options_bst
, opt_compare
);
2276 char * s
= xstrndup(spec
, -offset
);
2277 printf("%s <---\nSyntax error at or before offset %d\n", s
, -offset
);
2287 /* ==================================================== */
2288 /* ctxopt initialization function, must be called first */
2289 /* ==================================================== */
2291 ctxopt_init(char * prog_name
)
2295 contexts_bst
= NULL
;
2300 user_string
= xmalloc(8);
2301 user_string2
= xmalloc(8);
2304 ctxopt_initialized
= 1;
2306 /* Update current_state */
2307 /* -------------------- */
2308 cur_state
= xcalloc(sizeof(state_t
), 0);
2310 /* Initialize custom error function pointers to NULL */
2311 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2312 err_functions
= xmalloc(CTXOPTERRSIZ
* sizeof(void *));
2313 for (n
= 0; n
< CTXOPTERRSIZ
; n
++)
2314 err_functions
[n
] = NULL
;
2316 /* Update current_state */
2317 /* -------------------- */
2320 if (*prog_name
== '\0')
2321 cur_state
->prog_name
= xstrdup("program_name");
2322 else if ((ptr
= strrchr(prog_name
, '/')))
2323 cur_state
->prog_name
= xstrdup(ptr
+ 1);
2325 cur_state
->prog_name
= xstrdup(prog_name
);
2328 cur_state
->prog_name
= xstrdup("program_name");
2331 /* ========================================================================= */
2332 /* Utility function which create and register a par_t object in a bst */
2333 /* embedded in a context. */
2334 /* This object will have a name and a pointer to the option it refers to. */
2335 /* These object will be used to quickly find an option from a command */
2336 /* line parameter during the analysis phase. */
2338 /* IN : an option name. */
2339 /* IN : a string of command line parameters to associate to the option. */
2340 /* Returns : 1 is all was fine else 0. */
2341 /* ========================================================================= */
2343 opt_set_parms(char * opt_name
, char * par_str
)
2345 char * par_name
, *ctx_name
;
2346 char * tmp_par_str
, *end_tmp_par_str
;
2350 par_t
* par
, tmp_par
;
2351 int rc
= 1; /* return code */
2356 /* Look is the given option is defined */
2357 /* """"""""""""""""""""""""""""""""""" */
2358 opt
= locate_opt(opt_name
);
2360 fatal_internal("Unknown option %s", opt_name
);
2362 /* For each context using this option */
2363 /* """""""""""""""""""""""""""""""""" */
2364 list
= opt
->ctx_list
;
2367 while (lnode
!= NULL
)
2369 /* Locate the context in the contexts tree */
2370 /* """"""""""""""""""""""""""""""""""""""" */
2371 ctx_name
= ((ctx_t
*)(lnode
->data
))->name
;
2373 ctx
= locate_ctx(ctx_name
);
2375 fatal_internal("Unknown context %s", ctx_name
);
2378 void * par_bst
= ctx
->par_bst
;
2380 tmp_par_str
= xstrdup(par_str
);
2381 ltrim(tmp_par_str
, " \t");
2382 rtrim(tmp_par_str
, " \t", 0);
2383 par_name
= xstrtok_r(tmp_par_str
, " \t,", &end_tmp_par_str
);
2384 if (par_name
== NULL
)
2385 fatal_internal("Parameters are missing for option %s", opt_name
);
2387 /* For each parameter given in par_str, create an par_t object and */
2388 /* insert it the in the parameters bst of the context. */
2389 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2390 while (par_name
!= NULL
)
2392 tmp_par
.name
= par_name
;
2394 node
= bst_find(&tmp_par
, &par_bst
, par_compare
);
2397 fatal_internal("The parameter %s is already defined in context %s",
2398 par_name
, ctx
->name
);
2403 par
= xmalloc(sizeof(par_t
));
2404 par
->name
= xstrdup(par_name
);
2405 par
->opt
= opt
; /* Link the option to this parameter */
2407 bst_search(par
, &par_bst
, par_compare
);
2409 par_name
= xstrtok_r(NULL
, " \t,", &end_tmp_par_str
);
2412 /* Update the value of the root of ctx->par_bst as it may have */
2414 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2415 ctx
->par_bst
= par_bst
;
2419 lnode
= lnode
->next
;
2425 /* ==================================================================== */
2426 /* Creation of a new context instance */
2427 /* IN ctx : a context pointer to allow this instance to */
2428 /* access the context fields */
2429 /* IN prev_ctx_inst : the context instance whose option leading to the */
2430 /* creation of this new context instance is part of */
2431 /* Returns : the new context. */
2432 /* ==================================================================== */
2434 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
)
2437 opt_inst_t
* gen_opt_inst
;
2438 ctx_inst_t
* ctx_inst
;
2439 seen_opt_t
* seen_opt
;
2440 char * str
, *opt_name
;
2444 /* Keep a trace of the opt_inst which was at the origin of the creation */
2445 /* of this context instance. */
2446 /* This will serve during the evaluation of the option callbacks */
2447 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2448 if (prev_ctx_inst
!= NULL
)
2450 gen_opt_inst
= (opt_inst_t
*)(prev_ctx_inst
->opt_inst_list
->tail
->data
);
2451 /* Update current_state */
2452 /* -------------------- */
2453 cur_state
->opt_name
= gen_opt_inst
->opt
->name
;
2456 gen_opt_inst
= NULL
;
2458 /* Create and initialize the new context instance */
2459 /* """""""""""""""""""""""""""""""""""""""""""""" */
2460 ctx_inst
= xmalloc(sizeof(ctx_inst_t
));
2461 ctx_inst
->ctx
= ctx
;
2462 ctx_inst
->prev_ctx_inst
= prev_ctx_inst
;
2463 ctx_inst
->gen_opt_inst
= gen_opt_inst
;
2464 ctx_inst
->incomp_bst_list
= ll_new();
2465 ctx_inst
->opt_inst_list
= ll_new();
2466 ctx_inst
->seen_opt_bst
= NULL
;
2468 /* Update current_state */
2469 /* -------------------- */
2470 cur_state
->ctx_name
= ctx
->name
;
2474 if (prev_ctx_inst
== NULL
)
2475 first_ctx_inst
= ctx_inst
;
2477 /* Initialize the occurrence counters of each opt allowed in the context */
2478 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2479 node
= ctx
->opt_list
->head
;
2480 while (node
!= NULL
)
2483 opt
->occurrences
= 0;
2488 /* Initialize the bst containing the seen indicator for all the options */
2489 /* allowed in this context instance. */
2490 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2491 node
= ctx
->opt_list
->head
;
2492 while (node
!= NULL
)
2495 seen_opt
= xmalloc(sizeof(seen_opt_t
));
2496 seen_opt
->opt
= opt
;
2499 bst_search(seen_opt
, &(ctx_inst
->seen_opt_bst
), seen_opt_compare
);
2504 /* Initialize the bst containing the incompatibles options */
2505 /* Incompatibles option names are read from strings found in the list */
2506 /* incomp_list present in each instance of ctx_t. */
2507 /* These names are then used to search for the object of type seen_opt_t */
2508 /* which is already present in the seen_opt_bst of the context instance. */
2510 /* Once found the seen_opt_t object in inserted in the new bst */
2511 /* At the end the new bst in addes to the list incomp_bst_list. */
2512 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2513 node
= ctx
->incomp_list
->head
;
2514 while (node
!= NULL
)
2517 seen_opt_t tmp_seen_opt
;
2519 str
= xstrdup(node
->data
);
2521 rtrim(str
, " \t", 0);
2522 opt_name
= strtok(str
, " \t"); /* extract the first option name */
2524 while (opt_name
!= NULL
) /* for each option name */
2526 if ((opt
= locate_opt(opt_name
)) != NULL
)
2528 /* The option found is searched in the tree of potential */
2530 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2531 tmp_seen_opt
.opt
= opt
;
2533 bst_node
= bst_find(&tmp_seen_opt
, &(ctx_inst
->seen_opt_bst
),
2536 if (bst_node
!= NULL
)
2538 /* if found it, is added into the new bst tree */
2539 /* """"""""""""""""""""""""""""""""""""""""""" */
2540 seen_opt
= bst_node
->key
;
2541 bst_search(seen_opt
, &bst
, seen_opt_compare
);
2544 /* Not found! That means that the option is unknown in this */
2545 /* context as all options has have a seen_opt structure in */
2547 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2548 fatal_internal("%s is not known in the context %s", opt
->name
,
2552 fatal_internal("%s: unknown option.", opt_name
);
2554 opt_name
= strtok(NULL
, " \t");
2558 ll_append(ctx_inst
->incomp_bst_list
, bst
);
2566 /* ======================================================================== */
2567 /* Create a list formed by all the significant command line words */
2568 /* Words beginning or ending with ^,{ or } are split. Each of these */
2569 /* symbols will get their own place in the list */
2571 /* - ^ forces the next option to be evaluated in the first context */
2572 /* - the {...} part delimits a context, the { will not appear in the list */
2573 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2574 /* to facilitate the parsing phase. | must not be used by the end user */
2576 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2577 /* program name is not considered */
2578 /* IN words : is the array of strings constituting the command line to */
2580 /* Returns : 1 on success, 0 if a { or } is missing. */
2581 /* ======================================================================== */
2583 ctxopt_build_cmdline_list(int nb_words
, char ** words
)
2586 char * prev_word
= NULL
;
2590 ll_node_t
*node
, *start_node
;
2592 /* The analysis is divided into three passes, this is not optimal but */
2593 /* must be done only one time. Doing that we privilege readability. */
2595 /* In the following, SG is the ascii character 1d (dec 29) */
2597 /* The first pass creates the list, extract the leading an trailing */
2598 /* SG '{' and '}' of each word and give them their own place in the */
2601 /* The second pass transform the '{...}' blocks by a trailing SG */
2602 /* ({...} -> ...|) */
2604 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2605 /* the middle in the remaining list elements and recreate the pseudo */
2607 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2609 /* If the option list is not empty, clear it before going further */
2610 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2612 if (cmdline_list
!= NULL
)
2614 node
= cmdline_list
->head
;
2615 while (node
!= NULL
)
2618 ll_delete(cmdline_list
, node
);
2621 node
= cmdline_list
->head
;
2625 cmdline_list
= ll_new();
2627 start_node
= cmdline_list
->head
; /* in the following loop start_node will *
2628 * contain a pointer to the current *
2629 * word stripped from its leading *
2630 * sequence of {, } or ^ */
2631 for (i
= 0; i
< nb_words
; i
++)
2633 size_t len
= strlen(words
[i
]);
2639 /* Replace each occurrence of the legal word {} by the characters */
2640 /* 0x02 and 0x03 to hide them from the following process. */
2641 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2642 while ((ptr
= strstr(str
, "{}")) != NULL
)
2644 *ptr
= 0x02; /* arbitrary values unlikely */
2645 *(ptr
+ 1) = 0x03; /* present in a word */
2648 if (len
> 1) /* The word contains at least 2 characters */
2652 /* Interpret its beginning and look for the start of the real word */
2653 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2654 while (start
<= len
- 1 && (str
[start
] == '{' || str
[start
] == '}'))
2656 ll_append(cmdline_list
, xstrndup(str
+ start
, 1));
2658 start_node
= cmdline_list
->tail
;
2662 if (str
[end
] == '{' || str
[end
] == '}')
2664 if (end
> 0 && str
[end
- 1] != '\\')
2666 ll_append(cmdline_list
, xstrndup(str
+ end
, 1));
2668 node
= cmdline_list
->tail
;
2670 while (str
[end
] == '{' || str
[end
] == '}')
2672 if (end
> start
&& str
[end
- 1] == '\\')
2675 ll_insert_before(cmdline_list
, node
, xstrndup(str
+ end
, 1));
2684 if (start_node
!= NULL
)
2685 ll_insert_after(cmdline_list
, start_node
,
2686 xstrndup(str
+ start
, end
- start
+ 1));
2688 ll_append(cmdline_list
, xstrndup(str
+ start
, end
- start
+ 1));
2689 start_node
= cmdline_list
->tail
;
2694 ll_append(cmdline_list
, xstrdup(str
));
2695 start_node
= cmdline_list
->tail
;
2701 node
= cmdline_list
->head
;
2704 while (node
!= NULL
)
2708 if (strcmp(word
, "{") == 0)
2710 ll_node_t
* old_node
= node
;
2713 ll_delete(cmdline_list
, old_node
);
2717 else if (strcmp(word
, "}") == 0)
2735 node
= cmdline_list
->head
;
2737 while (node
!= NULL
)
2741 /* Restore the original { and } characters forming the legal word {} */
2742 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2743 while ((ptr
= strchr(word
, 0x02)) != NULL
)
2745 while ((ptr
= strchr(word
, 0x03)) != NULL
)
2748 /* Remove a SG if the previous element is SG */
2749 /* """"""""""""""""""""""""""""""""""""""""" */
2750 if (strcmp(word
, "\x1d") == 0)
2752 if (prev_word
!= NULL
&& (strcmp(prev_word
, "\x1d") == 0))
2754 ll_node_t
* old_node
= node
;
2756 ll_delete(cmdline_list
, old_node
);
2757 free(old_node
->data
);
2761 else if (strcmp(word
, "-") == 0) /* a single - is a legal argument, not *
2762 * a parameter. Protect it */
2765 node
->data
= xstrdup("\\-");
2768 prev_word
= node
->data
;
2772 /* Clean useless and SG at the beginning and end of list */
2773 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2774 node
= cmdline_list
->head
;
2781 if (strcmp(word
, "\x1d") == 0)
2783 ll_delete(cmdline_list
, node
);
2788 node
= cmdline_list
->tail
;
2794 if (strcmp(word
, "\x1d") == 0)
2796 ll_delete(cmdline_list
, node
);
2804 /* ===================================================================== */
2805 /* Build and analyze the command line list */
2806 /* function and create the linked data structures whose data will be */
2807 /* evaluated later by ctxopt_evaluate. */
2808 /* This function identifies the following errors and creates an array of */
2809 /* The remaining unanalyzed arguments. */
2810 /* - detect missing arguments */
2811 /* - detect too many arguments */
2812 /* - detect unknown parameters in a context */
2813 /* - detect too many occurrences of a parameters in a context */
2814 /* - detect missing required arguments in a context */
2816 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2817 /* program name is not considered */
2818 /* IN words : is the array of strings constituting the command line to */
2820 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
2821 /* is present in the list. */
2822 /* OUT rem_args : array of remaining command line arguments if a -- */
2823 /* is present in the list. This array must be free by */
2824 /* The caller as it is allocated here. */
2825 /* ===================================================================== */
2827 ctxopt_analyze(int nb_words
, char ** words
, int * nb_rem_args
,
2833 ctx_inst_t
* ctx_inst
;
2834 opt_inst_t
* opt_inst
;
2837 int expect_par_or_arg
= 0;
2839 ll_node_t
* cli_node
;
2841 seen_opt_t
* bst_seen_opt
;
2847 if (!ctxopt_build_cmdline_list(nb_words
, words
))
2849 "The command line could not be parsed: missing { or } detected.");
2851 if (main_ctx
== NULL
)
2852 fatal_internal("At least one context must have been created.");
2854 /* Check that all options has an action and at least one parameter */
2855 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2856 bst_walk(options_bst
, bst_check_opt_cb
);
2858 /* Create the first ctx_inst record */
2859 /* """""""""""""""""""""""""""""""" */
2862 ctx_inst_list
= ll_new();
2863 ctx_inst
= new_ctx_inst(ctx
, NULL
);
2864 ctx_inst
->par_name
= NULL
;
2866 ll_append(ctx_inst_list
, ctx_inst
);
2868 /* For each node in the command line */
2869 /* """"""""""""""""""""""""""""""""" */
2870 cli_node
= cmdline_list
->head
;
2872 while (cli_node
!= NULL
)
2874 if (strcmp(cli_node
->data
, "--") == 0)
2875 break; /* No new parameter will be analyze after this point */
2877 par_name
= cli_node
->data
;
2879 if (strcmp(par_name
, "\x1d") == 0)
2881 check_for_missing_mandatory_opt(ctx_inst
, (char *)(cli_node
->prev
->data
));
2882 check_for_occurrences_issues(ctx_inst
);
2884 /* Forced backtracking to the previous context instance */
2885 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2886 if (ctx_inst
->prev_ctx_inst
!= NULL
)
2888 ctx_inst
= ctx_inst
->prev_ctx_inst
;
2889 ctx
= ctx_inst
->ctx
;
2891 /* Update current_state */
2892 /* -------------------- */
2893 cur_state
->ctx_name
= ctx
->name
;
2894 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
2897 else if (expect_par
&& *par_name
== '-')
2902 /* Update current_state */
2903 /* -------------------- */
2904 cur_state
->cur_opt_par_name
= par_name
;
2906 /* An expected parameter has been seen */
2907 /* """"""""""""""""""""""""""""""""""" */
2908 if ((par
= locate_par(par_name
, ctx
)) == NULL
)
2913 /* See if this parameter is an unique abbreviation of a longer */
2914 /* parameter. If this is the case then just replace it with its */
2915 /* full length version and try again. */
2916 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2917 if ((word
= abbrev_expand(par_name
, ctx
)) != NULL
)
2919 cli_node
->data
= word
;
2923 /* Try to find a prefix which is a valid parameter in this context */
2924 /* If found, split the cli_node in two to build a new parameter */
2925 /* node and followed by a node containing the remaining string */
2926 /* If the new parameter corresponds to an option not taking */
2927 /* argument then prefix the remaining string whit a dash as it may */
2928 /* contain a new parameter. */
2929 /* The new parameter will be re-evaluated in the next iteration of */
2931 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2932 prefix
= look_for_valid_prefix_in_word(par_name
, ctx
, &pos
, &popt
);
2933 if (prefix
!= NULL
&& pos
!= 0)
2935 cli_node
->data
= prefix
; /* prefix contains le name of a valid *
2936 | parameter in this context. */
2940 /* The parameter may be followed by arguments */
2941 /* '''''''''''''''''''''''''''''''''''''''''' */
2942 if (*(par_name
+ pos
) == '-')
2944 word
= xstrdup("\\"); /* Protect the '-' */
2945 word
= strappend(word
, par_name
+ pos
, NULL
);
2948 word
= xstrdup(par_name
+ pos
);
2952 /* The parameter does not take arguments, the */
2953 /* following word must be a parameter or nothing */
2954 /* hence prefix it with a dash. */
2955 /* ''''''''''''''''''''''''''''''''''''''''''''' */
2956 word
= xstrdup("-");
2957 word
= strappend(word
, par_name
+ pos
, NULL
);
2960 /* Insert it after the current node in the list */
2961 /* """""""""""""""""""""""""""""""""""""""""""" */
2962 ll_insert_after(cmdline_list
, cli_node
, word
);
2964 continue; /* loop */
2968 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
2969 check_for_occurrences_issues(ctx_inst
);
2971 if (ctx_inst
->prev_ctx_inst
== NULL
)
2973 char * errmsg
= xstrdup("");
2975 *user_string
= '\0';
2976 *user_string2
= '\0';
2978 user_string
= strappend(user_string
, par_name
, NULL
);
2980 bst_walk(contexts_bst
, bst_match_par_cb
);
2982 if (*user_string2
!= '\0')
2986 "\nIt appears to be defined in the context(s):", user_string2
,
2987 "\nAdd -h or -H for more help.", NULL
);
2990 fatal(CTXOPTUNKPAR
, errmsg
);
2994 /* Try to backtrack and analyse the same parameter in the */
2995 /* previous context. */
2996 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
2997 ctx_inst
= ctx_inst
->prev_ctx_inst
;
2998 ctx
= ctx_inst
->ctx
;
3000 /* Update current_state */
3001 /* -------------------- */
3002 cur_state
->ctx_name
= ctx
->name
;
3003 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3005 cli_node
= cli_node
->prev
;
3011 seen_opt_t seen_opt
;
3013 /* The parameter is legal in the context, create a opt_inst and */
3014 /* append it to the ctx_inst list options list */
3015 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3020 opt_inst
= xmalloc(sizeof(opt_inst_t
));
3021 opt_inst
->opt
= opt
;
3022 opt_inst
->par
= par_name
;
3023 opt_inst
->values_list
= ll_new();
3024 opt_inst
->next_ctx_inst
= NULL
;
3026 /* Priority option inserted at the start of the opt_inst list */
3027 /* but their order of appearance in the context definition must */
3028 /* be preserver so each new priority option will be placed after */
3029 /* the previous ones at the start of the opt_inst list. */
3030 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3031 if (!opt
->eval_first
)
3032 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3035 ll_node_t
* node
= ctx_inst
->opt_inst_list
->head
;
3036 opt_inst_t
* tmp_opt_inst
;
3037 while (node
!= NULL
)
3039 tmp_opt_inst
= node
->data
;
3040 if (!tmp_opt_inst
->opt
->eval_first
)
3042 ll_insert_before(ctx_inst
->opt_inst_list
, node
, opt_inst
);
3049 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3052 /* Check if an option was already seen in the */
3053 /* current context instance */
3054 /* """""""""""""""""""""""""""""""""""""""""" */
3057 bst_node
= bst_find(&seen_opt
, &(ctx_inst
->seen_opt_bst
),
3060 /* bst_node cannot be NULL here */
3062 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3064 if (!opt
->multiple
&& bst_seen_opt
->seen
== 1)
3065 fatal(CTXOPTDUPOPT
, NULL
);
3067 /* Check if this option is compatible with the options already */
3068 /* seen in this context instance. */
3069 /* Look if the option is present in one on the bst present in */
3070 /* the incomp_bst_list of the context instance */
3072 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3073 node
= ctx_inst
->incomp_bst_list
->head
;
3074 while (node
!= NULL
)
3079 /* They can only have one seen_opt object in the bst tree was */
3080 /* already seen, try to locate it, the result will be put in */
3081 /* user_object by the bst_seen_opt_seen_cb function */
3082 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3083 bst_walk(bst
, bst_seen_opt_seen_cb
);
3085 /* It it is the case, look if the current option is also */
3087 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3088 if (user_object
!= NULL
)
3090 bst_node
= bst_find(bst_seen_opt
, &bst
, seen_opt_compare
);
3092 if (bst_node
!= NULL
)
3094 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3095 if (bst_seen_opt
->seen
== 0)
3096 fatal(CTXOPTINCOPT
, (char *)user_object
);
3103 /* Mark this option seen in the current context instance */
3104 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3105 bst_seen_opt
->seen
= 1;
3106 bst_seen_opt
->par
= xstrdup(par_name
);
3108 /* If this option leads to a next context, create a new ctx_inst */
3109 /* and switch to it for the analyse of the future parameter */
3110 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3111 if (opt
->next_ctx
!= NULL
)
3113 ctx
= locate_ctx(opt
->next_ctx
);
3116 fatal_internal("%s: unknown context.", opt
->next_ctx
);
3118 opt_inst
->next_ctx_inst
= ctx_inst
= new_ctx_inst(ctx
, ctx_inst
);
3119 ctx_inst
->par_name
= xstrdup(par_name
);
3121 /* Update current_state */
3122 /* -------------------- */
3123 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3125 ll_append(ctx_inst_list
, ctx_inst
);
3128 /* See is we must expect some arguments */
3129 /* """""""""""""""""""""""""""""""""""" */
3130 expect_par_or_arg
= 0;
3135 expect_par
= 1; /* Parameter doesn't accept any argument */
3138 if (!opt
->optional_args
)
3139 expect_arg
= 1; /* Parameter has mandatory arguments */
3141 expect_par_or_arg
= 1; /* Parameter has optional arguments */
3145 else if (expect_par
&& *par_name
!= '-')
3146 break; /* a unexpected non parameter was seen, assume it is the end *
3147 | of parameters and the start of non ctxopt managed command *
3148 | line arguments. */
3149 else if (expect_arg
&& *par_name
!= '-')
3151 ll_node_t
* cstr_node
;
3152 constraint_t
* cstr
;
3154 /* Check if the arguments of the option respects */
3155 /* the attached constraints if any */
3156 /* """"""""""""""""""""""""""""""""""""""""""""" */
3157 cstr_node
= opt
->constraints_list
->head
;
3158 while (cstr_node
!= NULL
)
3160 cstr
= cstr_node
->data
;
3161 if (!cstr
->constraint(cstr
->nb_args
, cstr
->args
, par_name
))
3164 cstr_node
= cstr_node
->next
;
3167 /* If the argument is valid, store it */
3168 /* """""""""""""""""""""""""""""""""" */
3169 if (*par_name
== '\\' && *(par_name
+ 1) != '\0'
3170 && *(par_name
+ 1) == '-')
3171 ll_append(opt_inst
->values_list
, par_name
+ 1);
3173 ll_append(opt_inst
->values_list
, par_name
);
3177 expect_par_or_arg
= 0;
3179 if (opt
->multiple_args
)
3180 expect_par_or_arg
= 1;
3182 expect_par
= 1; /* Parameter takes only one argument */
3184 else if (expect_arg
&& *par_name
== '-')
3185 fatal(CTXOPTMISARG
, NULL
);
3186 else if (expect_par_or_arg
)
3190 expect_par_or_arg
= 0;
3192 if (*par_name
!= '-')
3193 expect_arg
= 1; /* Consider this word as an argument and retry */
3195 expect_par
= 1; /* Consider this word as a parameter and retry */
3197 cli_node
= cli_node
->prev
;
3200 cli_node
= cli_node
->next
;
3203 if (cmdline_list
->len
> 0 && *par_name
== '-')
3205 if (expect_arg
&& !opt
->optional_args
)
3206 fatal(CTXOPTMISARG
, NULL
);
3209 /* Look if a context_instance has unseen mandatory options */
3210 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3211 node
= ctx_inst_list
->head
;
3212 while (node
!= NULL
)
3214 ctx_inst
= (ctx_inst_t
*)(node
->data
);
3216 /* Update current_state */
3217 /* -------------------- */
3218 cur_state
->ctx_name
= ctx_inst
->ctx
->name
;
3219 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3221 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3222 check_for_occurrences_issues(ctx_inst
);
3227 /* Allocate the array containing the remaining not analyzed */
3228 /* command line arguments. */
3229 /* NOTE: The strings in the array are just pointer to the */
3230 /* data of the generating list and must not be freed. */
3231 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3232 if (cli_node
!= NULL
)
3234 if (strcmp((char *)cli_node
->data
, "--") == 0)
3235 /* The special parameter -- was encountered, the -- argument is not */
3236 /* put in the remaining arguments. */
3237 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3238 ll_strarray(cmdline_list
, cli_node
->next
, nb_rem_args
, rem_args
);
3240 /* A non parameter was encountered when a parameter was expected. We */
3241 /* assume that the evaluation of the remaining command line argument */
3242 /* are not the responsibility of the users code. */
3243 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3244 ll_strarray(cmdline_list
, cli_node
, nb_rem_args
, rem_args
);
3249 *rem_args
= xmalloc(sizeof(char *));
3250 (*rem_args
)[0] = NULL
;
3254 /* ===================================================================== */
3255 /* Parses the options data structures and launches the callback function */
3256 /* attached to each options instances. */
3257 /* This calls a recursive function which proceeds context per context. */
3258 /* ===================================================================== */
3260 ctxopt_evaluate(void)
3262 evaluate_ctx_inst(first_ctx_inst
);
3265 /* =================================================================== */
3266 /* Recursive function called by ctxopt_evaluate to process the list of */
3267 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3268 /* action attached to the context and its option instances */
3269 /* =================================================================== */
3271 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
)
3273 opt_inst_t
* opt_inst
;
3276 ll_node_t
* opt_inst_node
;
3280 if (ctx_inst
== NULL
)
3283 ctx
= ctx_inst
->ctx
;
3285 /* Call the entering action attached to this context if any */
3286 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3287 if (ctx
->action
!= NULL
)
3289 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3290 ctx
->action(ctx
->name
, entering
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3291 ctx
->nb_data
, ctx
->data
);
3293 ctx
->action(ctx
->name
, entering
, NULL
, ctx
->nb_data
, ctx
->data
);
3296 /* For each instance of options */
3297 /* """""""""""""""""""""""""""" */
3298 opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
3299 while (opt_inst_node
!= NULL
)
3301 opt_inst
= (opt_inst_t
*)(opt_inst_node
->data
);
3302 ll_strarray(opt_inst
->values_list
, opt_inst
->values_list
->head
, &nb_args
,
3304 opt
= opt_inst
->opt
;
3306 /* Launch the attached action if any */
3307 /* """"""""""""""""""""""""""""""""" */
3308 if (opt
->action
!= NULL
)
3309 opt
->action(ctx
->name
, opt
->name
, opt_inst
->par
, nb_args
, args
,
3310 opt
->nb_data
, opt
->data
, ctx
->nb_data
, ctx
->data
);
3312 if (opt_inst
->next_ctx_inst
!= NULL
)
3313 evaluate_ctx_inst(opt_inst
->next_ctx_inst
);
3318 opt_inst_node
= opt_inst_node
->next
;
3321 /* Call the exiting action attached to this context if any */
3322 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3323 if (ctx
->action
!= NULL
)
3325 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3326 ctx
->action(ctx
->name
, exiting
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3327 ctx
->nb_data
, ctx
->data
);
3329 ctx
->action(ctx
->name
, exiting
, NULL
, ctx
->nb_data
, ctx
->data
);
3333 /* =========================================================== */
3334 /* Create and initialize a new context */
3335 /* - allocate space */
3337 /* - initialize its option with a few of their characteristics */
3338 /* =========================================================== */
3340 ctxopt_new_ctx(char * name
, char * opts_specs
)
3346 if (!ctxopt_initialized
)
3347 fatal_internal("Please call ctxopt_init first.");
3349 ctx
= xmalloc(sizeof(ctx_t
));
3351 /* The first created context is the main one */
3352 /* """"""""""""""""""""""""""""""""""""""""" */
3353 if (contexts_bst
== NULL
)
3356 /* validate the context name */
3357 /* ALPHA+(ALPHANUM|_)* */
3358 /* """"""""""""""""""""""""" */
3361 fatal_internal("%s: a context name must start with a letter.", name
);
3366 if (!isalnum(*p
) && *p
!= '_')
3367 fatal_internal("%s: a context name must only contain letters, "
3373 ctx
->name
= xstrdup(name
);
3374 ctx
->opt_list
= ll_new(); /* list of options legit in this context */
3375 ctx
->incomp_list
= ll_new(); /* list of incompatible options strings */
3376 ctx
->par_bst
= NULL
;
3380 if (init_opts(opts_specs
, ctx
) == 0)
3382 if ((node
= bst_find(ctx
, &contexts_bst
, ctx_compare
)) != NULL
)
3383 fatal_internal("The context %s already exists", name
);
3385 bst_search(ctx
, &contexts_bst
, ctx_compare
);
3388 /* ==================================================== */
3389 /* Display a usage screen limited to a specific context */
3390 /* IN: the context name. */
3391 /* IN: what to do after (continue or exit the program) */
3392 /* possible values: continue_after, exit_after. */
3393 /* ==================================================== */
3395 ctxopt_ctx_disp_usage(char * ctx_name
, usage_behaviour action
)
3400 int has_optional
= 0;
3401 int has_ellipsis
= 0;
3403 int has_generic_arg
= 0;
3404 int has_ctx_change
= 0;
3405 int has_early_eval
= 0;
3407 ctx
= locate_ctx(ctx_name
);
3409 fatal_internal("%s: unknown context.", ctx_name
);
3411 if (cur_state
->ctx_par_name
== NULL
)
3412 printf("\nSynopsis:\n%s \\\n", cur_state
->prog_name
);
3414 printf("\nSynopsis for the context introduced by %s:\n",
3415 cur_state
->ctx_par_name
);
3417 list
= ctx
->opt_list
;
3418 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3419 &has_ctx_change
, &has_early_eval
);
3421 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3422 has_optional
, has_ellipsis
, has_rule
);
3424 if (action
== exit_after
)
3428 /* ==================================================== */
3429 /* Display a full usage screen about all contexts. */
3430 /* IN: what to do after (continue or exit the program) */
3431 /* possible values: continue_after, exit_after. */
3432 /* ==================================================== */
3434 ctxopt_disp_usage(usage_behaviour action
)
3437 int has_optional
= 0;
3438 int has_ellipsis
= 0;
3440 int has_generic_arg
= 0;
3441 int has_ctx_change
= 0;
3442 int has_early_eval
= 0;
3444 if (main_ctx
== NULL
)
3445 fatal_internal("At least one context must have been created.");
3447 /* Usage for the first context */
3448 /* """"""""""""""""""""""""""" */
3449 printf("\nAllowed options in the default context:\n");
3450 list
= main_ctx
->opt_list
;
3451 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3452 &has_ctx_change
, &has_early_eval
);
3454 /* Usage for the other contexts */
3455 /* """""""""""""""""""""""""""" */
3456 bst_walk(contexts_bst
, bst_print_ctx_cb
);
3458 /* Contextual syntactic explanations */
3459 /* """"""""""""""""""""""""""""""""" */
3460 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3461 has_optional
, has_ellipsis
, has_rule
);
3463 if (action
== exit_after
)
3467 /* ************************************** */
3468 /* Built-in constraint checking functions */
3469 /* ************************************** */
3471 /* ================================================================ */
3472 /* This constraint checks if each arguments respects a format as */
3473 /* defined for the scanf function. */
3474 /* return 1 if yes and 0 if no */
3475 /* ================================================================ */
3477 ctxopt_format_constraint(int nb_args
, char ** args
, char * value
)
3486 fatal_internal("Format constraint: invalid number of parameters.");
3488 if (strlen(value
) > 255)
3491 format
= xstrdup(args
[0]);
3492 format
= strappend(format
, "%c", NULL
);
3494 rc
= sscanf(value
, format
, x
, &y
);
3496 fatal_internal("The argument %s does not respect the imposed format: %s",
3504 /* ================================================================== */
3505 /* This constraint checks if each arguments of the option instance is */
3506 /* between a minimum and a maximum (inclusive). */
3507 /* return 1 if yes and 0 if no */
3508 /* ================================================================== */
3510 ctxopt_re_constraint(int nb_args
, char ** args
, char * value
)
3516 "Regular expression constraint: invalid number of parameters.");
3518 if (regcomp(&re
, args
[0], REG_EXTENDED
) != 0)
3519 fatal_internal("Invalid regular expression %s", args
[0]);
3521 if (regexec(&re
, value
, (size_t)0, NULL
, 0) != 0)
3523 fatal_internal("The argument %s doesn't match the constraining "
3524 "regular expression %s",
3534 /* ================================================================== */
3535 /* This constraint checks if each arguments of the option instance is */
3536 /* between a minimum and a maximum (inclusive). */
3537 /* return 1 if yes and 0 if no */
3538 /* ================================================================== */
3540 ctxopt_range_constraint(int nb_args
, char ** args
, char * value
)
3551 fatal_internal("Range constraint: invalid number of parameters.");
3553 if (strcmp(args
[0], ".") == 0)
3556 n
= sscanf(args
[0], "%ld%c", &min
, &c
);
3558 if (!max_only
&& n
!= 1)
3559 fatal_internal("Range constraint: min: invalid parameters.");
3561 if (strcmp(args
[1], ".") == 0)
3564 n
= sscanf(args
[1], "%ld%c", &max
, &c
);
3566 if (!min_only
&& n
!= 1)
3567 fatal_internal("Range constraint: max: invalid parameters.");
3569 if (min_only
&& max_only
)
3570 fatal_internal("Range constraint: invalid parameters.");
3573 v
= strtol(value
, &ptr
, 10);
3574 if (errno
|| ptr
== value
)
3581 fatal_internal("%ld is not greater or equal to %ld as requested.", v
,
3592 fatal_internal("%ld is not lower or equal to %ld as requested.", v
, max
);
3598 else if (v
< min
|| v
> max
)
3600 fatal_internal("%ld is not between %ld and %ld as requested.", v
, min
, max
);
3604 return 1; /* check passed */
3607 /* ============================================================== */
3608 /* This function provides a way to set the behaviour of a context */
3609 /* ============================================================== */
3611 ctxopt_add_global_settings(settings s
, ...)
3618 case error_functions
:
3620 void (*function
)(errors e
, state_t
* state
);
3623 e
= va_arg(args
, errors
);
3624 function
= va_arg(args
, void (*)(errors e
, state_t
* state
));
3625 err_functions
[e
] = function
;
3635 /* ================================================================ */
3636 /* This function provides a way to set the behaviour of an option */
3637 /* It can take a variable number of arguments according to its */
3638 /* first argument: */
3640 /* o a string containing an option name and all its possible */
3641 /* parameters separates by spaces, tabs or commas (char *) */
3642 /* (e.g: "help -h -help") */
3644 /* o a string containing an option name */
3645 /* o a pointer to a function which will be called at evaluation */
3647 /* - constraints: */
3648 /* o a string containing an option name */
3649 /* o a pointer to a function to check if an argument is valid. */
3650 /* o a strings containing the arguments to this function. */
3651 /* ================================================================ */
3653 ctxopt_add_opt_settings(settings s
, ...)
3663 /* This part associates some command line parameters to an option */
3664 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3670 /* The second argument must be a string containing: */
3671 /* - The name of an existing option */
3672 /* - a list of parameters with a leading dash (-) */
3673 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3674 ptr
= va_arg(args
, char *);
3677 if (opt_name
!= NULL
)
3679 if ((opt
= locate_opt(opt_name
)) != NULL
)
3681 ptr
= va_arg(args
, char *);
3684 if (!opt_set_parms(opt_name
, params
))
3686 "duplicates parameters or bad settings for the option (%s).",
3690 fatal_internal("%s: unknown option.", opt_name
);
3694 "ctxopt_opt_add_settings: parameters: not enough arguments.");
3696 /* Here opt is a known option */
3697 /* """""""""""""""""""""""""" */
3698 if (opt
->params
!= NULL
)
3699 fatal_internal("Parameters are already set for %s", opt_name
);
3703 size_t l
= strlen(params
);
3705 opt
->params
= xstrdup(params
);
3706 while ((n
= strcspn(opt
->params
, " \t")) < l
)
3707 opt
->params
[n
] = '|';
3709 /* Update current_state */
3710 /* -------------------- */
3711 cur_state
->opt_params
= xstrdup(opt
->params
);
3717 /* This part associates a callback function to an option. */
3718 /* This function will be called as when an instance of an option */
3720 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3727 /* The second argument must be the name of an existing option */
3728 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3729 ptr
= va_arg(args
, char *);
3731 if ((opt
= locate_opt(ptr
)) != NULL
)
3733 /* The third argument must be the callback function */
3734 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3735 function
= va_arg(args
, void (*)(char *, char *, char *, int, char **,
3736 int, void *, int, void **));
3737 opt
->action
= function
;
3739 /* The fourth argument must be a pointer to an user's defined */
3740 /* variable or structure that the previous function can manage */
3741 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3742 while ((data
= va_arg(args
, void *)) != NULL
)
3745 opt
->data
= xrealloc(opt
->data
, nb_data
* sizeof(void *));
3746 opt
->data
[nb_data
- 1] = data
;
3748 opt
->nb_data
= nb_data
;
3751 fatal_internal("%s: unknown option.", ptr
);
3755 /* This part associates a list of functions to control some */
3756 /* characteristics of the arguments of an option. */
3757 /* Each function will be called in order and must return 1 */
3758 /* to validate the arguments. */
3759 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3763 constraint_t
* cstr
;
3766 /* The second argument must be a string */
3767 /* """""""""""""""""""""""""""""""""""" */
3768 ptr
= va_arg(args
, char *);
3770 if ((opt
= locate_opt(ptr
)) != NULL
)
3772 /* The third argument must be a function */
3773 /* """"""""""""""""""""""""""""""""""""" */
3774 function
= va_arg(args
, int (*)(int, char **, char *));
3776 cstr
= xmalloc(sizeof(constraint_t
));
3777 cstr
->constraint
= function
;
3779 /* The fourth argument must be a string containing the argument of */
3780 /* The previous function separated by spaces or tabs */
3781 /* Theses arguments will be passed to the previous function */
3782 /* max: 32 argument! */
3783 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3784 value
= xstrdup(va_arg(args
, char *));
3786 cstr
->args
= xcalloc(sizeof(char *), 32);
3787 cstr
->nb_args
= str2argv(value
, cstr
->args
, 32);
3788 ll_append(opt
->constraints_list
, cstr
);
3791 fatal_internal("%s: unknown option.", ptr
);
3801 /* ============================================================== */
3802 /* This function provides a way to set the behaviour of a context */
3803 /* ============================================================== */
3805 ctxopt_add_ctx_settings(settings s
, ...)
3814 /* Add a set of mutually incompatibles option in a context. */
3815 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3816 case incompatibilities
:
3823 ptr
= va_arg(args
, char *);
3824 if ((ctx
= locate_ctx(ptr
)) != NULL
)
3826 ptr
= va_arg(args
, char *);
3827 list
= ctx
->incomp_list
;
3831 rtrim(str
, " \t", 0);
3833 n
= strcspn(str
, " \t");
3834 if (n
> 0 && n
< strlen(str
))
3835 ll_append(list
, str
);
3838 "Not enough incompatible options in the string: \"%s\"", str
);
3841 fatal_internal("%s: unknown context.", ptr
);
3845 /* Add functions which be call when entering and exiting a context. */
3846 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3854 ptr
= va_arg(args
, char *);
3855 if ((ctx
= locate_ctx(ptr
)) != NULL
)
3857 function
= va_arg(args
,
3858 int (*)(char *, direction
, char *, int, void **));
3859 ctx
->action
= function
;
3861 while ((data
= va_arg(args
, void *)) != NULL
)
3864 ctx
->data
= xrealloc(ctx
->data
, nb_data
* sizeof(void *));
3865 ctx
->data
[nb_data
- 1] = data
;
3867 ctx
->nb_data
= nb_data
;
3870 fatal_internal("%s: unknown context.", ptr
);