1 /* ################################################################### */
2 /* This Source Code Form is subject to the terms of the Mozilla Public */
3 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
4 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 /* ################################################################### */
13 #include <sys/types.h>
19 /* ************************ */
20 /* Static global variables. */
21 /* ************************ */
23 static void * contexts_bst
;
24 static void * options_bst
;
30 /* ************************** */
31 /* Fatal messages prototypes. */
32 /* ************************** */
34 static void (**err_functions
)(errors e
, state_t
* state
);
37 fatal_internal(const char * format
, ...);
40 fatal(errors e
, char * errmsg
);
42 static int user_rc
; /* Used by various callback functions */
43 static int user_value
; /* Used by various callback functions */
44 static char * user_string
; /* Used by various callback functions */
45 static char * user_string2
; /* Used by various callback functions */
46 static void * user_object
; /* Used by various callback functions */
48 /* ************************************ */
49 /* Memory management static prototypes. */
50 /* ************************************ */
56 xcalloc(size_t num
, size_t size
);
59 xrealloc(void * ptr
, size_t size
);
62 xstrdup(const char * p
);
65 xstrndup(const char * str
, size_t len
);
67 /* ********************** */
68 /* BST static prototypes. */
69 /* ********************** */
71 typedef struct bst_s bst_t
;
81 #if 0 /* Unused yet */
83 bst_delete(const void * vkey
, void ** vrootp
,
84 int (*compar
)(const void *, const void *));
88 bst_destroy(void * vrootp
, void (*clean
)(void *));
91 bst_find(const void * vkey
, void * const * vrootp
,
92 int (*compar
)(const void *, const void *));
95 bst_search(void * vkey
, void ** vrootp
,
96 int (*compar
)(const void *, const void *));
99 bst_walk_recurse(const bst_t
* root
,
100 void (*action
)(const void *, walk_order_e
, int), int level
);
103 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int));
105 /* ****************************** */
106 /* Linked list static prototypes. */
107 /* ****************************** */
109 typedef struct ll_node_s ll_node_t
;
110 typedef struct ll_s ll_t
;
113 ll_append(ll_t
* const list
, void * const data
);
116 ll_prepend(ll_t
* const list
, void * const data
);
119 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
);
122 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
);
125 ll_delete(ll_t
* const list
, ll_node_t
* node
);
127 #if 0 /* Unused yet */
129 ll_find(ll_t
* const, void * const, int (*)(const void *, const void *));
133 ll_init(ll_t
* list
);
142 ll_free(ll_t
* const list
, void (*)(void *));
145 ll_destroy(ll_t
* const list
, void (*)(void *));
148 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
);
150 /* ************************** */
151 /* Various static prototypes. */
152 /* ************************** */
155 ltrim(char * str
, const char * trim_str
);
158 rtrim(char * str
, const char * trim_str
, size_t min
);
161 strchrcount(char * str
, char c
);
164 strpref(char * s1
, char * s2
);
167 stricmp(const char * s1
, const char * s2
);
170 xstrtok_r(char * str
, const char * delim
, char ** end
);
173 eval_yes(char * value
, int * invalid
);
176 get_word(char * str
, char * buf
, size_t len
);
178 /* ************************* */
179 /* ctxopt static prototypes. */
180 /* ************************* */
182 typedef struct flags_s flags_t
;
183 typedef struct opt_s opt_t
;
184 typedef struct par_s par_t
;
185 typedef struct ctx_s ctx_t
;
186 typedef struct constraint_s constraint_t
;
187 typedef struct ctx_inst_s ctx_inst_t
;
188 typedef struct opt_inst_s opt_inst_t
;
189 typedef struct seen_opt_s seen_opt_t
;
190 typedef struct req_s req_t
;
193 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
);
196 ctx_compare(const void * c1
, const void * c2
);
202 ctx_inst_free(void * ci
);
205 opt_inst_free(void * oi
);
208 seen_opt_compare(const void * so1
, const void * so2
);
211 incomp_bst_free(void * b
);
217 seen_opt_free(void * seen_opt
);
220 opt_compare(const void * o1
, const void * o2
);
226 par_compare(const void * a1
, const void * a2
);
232 constraint_free(void * cstr
);
235 locate_ctx(char * name
);
238 locate_opt(char * name
);
241 locate_par(char * name
, ctx_t
* ctx
);
244 print_before_constraints(ll_t
* list
);
247 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
248 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
249 int * has_early_eval
);
251 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
252 int has_optional
, int has_ellipsis
, int has_rule
);
254 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
);
257 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
);
260 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
);
263 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
);
266 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
);
269 match_prefix_cb(const void * node
, walk_order_e kind
, int level
);
272 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
);
275 opt_parse(char * s
, opt_t
** opt
);
278 init_opts(char * spec
, ctx_t
* ctx
);
281 ctxopt_build_cmdline_list(int nb_words
, char ** words
);
284 opt_set_parms(char * opt_name
, char * par_str
);
287 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
);
290 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
);
292 /* ****************************** */
293 /* Fatal messages implementation. */
294 /* ****************************** */
296 /* ================================================================== */
297 /* Fatal error function used when a fatal condition is encountered. */
298 /* This function is reserved for the ctxopt internal usage. */
300 /* format : printf like format */
301 /* ... : remaining arguments interpreted using the format argument */
302 /* ================================================================== */
304 fatal_internal(const char * format
, ...)
308 fprintf(stderr
, "CTXOPT: ");
310 va_start(args
, format
);
311 vfprintf(stderr
, format
, args
);
312 fprintf(stderr
, "\n");
318 /* ====================================================================== */
319 /* Generic fatal error function. This one uses the global status ctxopt */
320 /* stored in the cur_state structure and can call custom error functions. */
321 /* registered by the users for a given error identifier if any. */
323 /* e : Error identifier responsible of the fatal error */
324 /* errmsg : Users's provided string specific to the error e */
325 /* Note that errmsg is not used in all cases */
327 /* CTXOPTMISPAR Missing parameter */
328 /* CTXOPTREQPAR Option: all parameters in a required group are */
330 /* CTXOPTMISARG Missing argument */
331 /* CTXOPTUXPARG Unexpected argument */
332 /* CTXOPTDUPOPT Duplicated option */
333 /* CTXOPTUNKPAR Unknown parameter */
334 /* CTXOPTINCOPT Incompatible option */
335 /* CTXOPTCTEOPT Option: bad number of occurrences */
336 /* CTXOPTCTLOPT Option: not enough occurrences */
337 /* CTXOPTCTGOPT Option: too many occurrence of */
338 /* CTXOPTCTEARG Arguments: bad number of occurrences */
339 /* CTXOPTCTLARG Arguments: not enough occurrences */
340 /* CTXOPTCTGARG Arguments: too many occurrences */
341 /* ====================================================================== */
343 fatal(errors e
, char * errmsg
)
345 if (err_functions
[e
] != NULL
)
346 err_functions
[e
](e
, cur_state
);
355 if (cur_state
->ctx_par_name
!= NULL
)
357 "the mandatory parameter(s) %s are missing in the context "
358 "introduced by %s.\n",
359 errmsg
, cur_state
->ctx_par_name
);
362 "The mandatory parameter(s) %s are missing "
363 "in the main context.\n",
370 fprintf(stderr
, errmsg
, cur_state
->req_opt_par_needed
,
371 cur_state
->req_opt_par
);
375 if (cur_state
->cur_opt_par_name
!= NULL
)
377 "The parameter %s takes no arguments "
378 "or has too many arguments.\n",
379 cur_state
->cur_opt_par_name
);
383 if (cur_state
->pre_opt_par_name
!= NULL
)
384 fprintf(stderr
, "%s requires argument(s).\n",
385 cur_state
->pre_opt_par_name
);
387 fprintf(stderr
, "%s requires argument(s).\n",
388 cur_state
->cur_opt_par_name
);
392 if (cur_state
->pre_opt_par_name
!= NULL
)
394 "The parameter %s can only appear once in the context "
395 "introduced by %s.\n",
396 cur_state
->cur_opt_params
, cur_state
->ctx_par_name
);
399 "The parameter %s can only appear once "
400 "in the main context.\n",
401 cur_state
->cur_opt_params
);
405 fprintf(stderr
, "Unknown parameter %s.\n%s",
406 cur_state
->cur_opt_par_name
, errmsg
);
410 fprintf(stderr
, "The parameter %s is incompatible with %s.\n",
411 cur_state
->cur_opt_par_name
, errmsg
);
415 if (cur_state
->ctx_par_name
)
417 "The parameter %s must appear exactly %d times "
418 "in the context introduced by %s.\n",
419 cur_state
->cur_opt_params
, cur_state
->opts_count
,
420 cur_state
->ctx_par_name
);
423 "The parameter %s must appear exactly %d times "
424 "in the main context.\n",
425 cur_state
->cur_opt_params
, cur_state
->opts_count
);
429 if (cur_state
->ctx_par_name
)
431 "The parameter %s must appear less than %d times "
432 "in the context introduced by %s.\n",
433 cur_state
->cur_opt_params
, cur_state
->opts_count
,
434 cur_state
->ctx_par_name
);
437 "The parameter %s must appear less than %d times "
438 "in the main context.\n",
439 cur_state
->cur_opt_params
, cur_state
->opts_count
);
443 if (cur_state
->ctx_par_name
)
445 "The parameter %s must appear more than %d times "
446 "in the context introduced by %s.\n",
447 cur_state
->cur_opt_params
, cur_state
->opts_count
,
448 cur_state
->ctx_par_name
);
451 "The parameter %s must appear more than %d times "
452 "in the main context.\n",
453 cur_state
->cur_opt_params
, cur_state
->opts_count
);
457 fprintf(stderr
, "The parameter %s must have exactly %d arguments.\n",
458 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
462 fprintf(stderr
, "The parameter %s must have less than %d arguments.\n",
463 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
467 fprintf(stderr
, "The parameter %s must have more than %d arguments.\n",
468 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
476 /* CTXOPTUNKPAR should display the full usage to help the user follow */
477 /* the chaining of contexts when several possible contexts have been */
478 /* identified. Otherwise, errmsg is the empty string and the display of */
479 /* the current usage is enough. */
480 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
481 if (e
== CTXOPTUNKPAR
&& *errmsg
!= '\0')
482 ctxopt_disp_usage(continue_after
);
484 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, continue_after
);
486 exit(e
); /* Exit with the error id e as return code. */
489 /* ********************************* */
490 /* Memory management implementation. */
491 /* ********************************* */
493 /* ================== */
494 /* Customized malloc. */
495 /* ================== */
502 real_size
= (size
> 0) ? size
: 1;
503 allocated
= malloc(real_size
);
504 if (allocated
== NULL
)
505 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
506 (unsigned long int)size
);
511 /* ================== */
512 /* Customized calloc. */
513 /* ================== */
515 xcalloc(size_t n
, size_t size
)
520 size
= (size
> 0) ? size
: 1;
521 allocated
= calloc(n
, size
);
522 if (allocated
== NULL
)
523 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
524 (unsigned long int)size
);
529 /* =================== */
530 /* Customized realloc. */
531 /* =================== */
533 xrealloc(void * p
, size_t size
)
537 allocated
= realloc(p
, size
);
538 if (allocated
== NULL
&& size
> 0)
539 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
540 (unsigned long int)size
);
545 /* ==================================== */
546 /* strdup implementation using xmalloc. */
547 /* ==================================== */
549 xstrdup(const char * p
)
553 allocated
= xmalloc(strlen(p
) + 1);
554 strcpy(allocated
, p
);
559 /* =================================================== */
560 /* strndup implementation using xmalloc. */
561 /* This version guarantees that there is a final '\0'. */
562 /* =================================================== */
564 xstrndup(const char * str
, size_t len
)
568 p
= memchr(str
, '\0', len
);
573 p
= xmalloc(len
+ 1);
580 /* *************************** */
581 /* Linked list implementation. */
582 /* *************************** */
584 /* Linked list node structure. */
585 /* """"""""""""""""""""""""""" */
589 struct ll_node_s
* next
;
590 struct ll_node_s
* prev
;
593 /* Linked List structure. */
594 /* """""""""""""""""""""" */
602 /* ========================= */
603 /* Create a new linked list. */
604 /* ========================= */
608 ll_t
* ret
= xmalloc(sizeof(ll_t
));
614 /* =============================================== */
615 /* Free all the elements of a list (make it empty) */
616 /* NULL or a custom function may be used to free */
617 /* the sub components of the elements. */
618 /* =============================================== */
620 ll_free(ll_t
* const list
, void (*clean
)(void *))
625 /* Apply a custom cleaner if not NULL. */
626 /* """"""""""""""""""""""""""""""""""" */
628 clean(list
->head
->data
);
630 ll_delete(list
, list
->head
);
634 /* ==================================== */
635 /* Destroy a list and all its elements. */
636 /* ==================================== */
638 ll_destroy(ll_t
* list
, void (*clean
)(void *))
642 ll_free(list
, clean
);
647 /* ========================= */
648 /* Initialize a linked list. */
649 /* ========================= */
658 /* ===================================================== */
659 /* Allocate the space for a new node in the linked list. */
660 /* ===================================================== */
664 ll_node_t
* ret
= xmalloc(sizeof(ll_node_t
));
669 /* ==================================================================== */
670 /* Append a new node filled with its data at the end of the linked list */
671 /* The user is responsible for the memory management of the data. */
672 /* ==================================================================== */
674 ll_append(ll_t
* const list
, void * const data
)
678 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
679 | uses xmalloc which does not return if there *
680 | is an allocation error. */
685 node
->prev
= list
->tail
;
687 list
->tail
->next
= node
;
696 /* ================================================================== */
697 /* Put a new node filled with its data at the beginning of the linked */
699 /* The user is responsible for the memory management of the data. */
700 /* ================================================================== */
702 ll_prepend(ll_t
* const list
, void * const data
)
706 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
707 | uses xmalloc which does not return if there *
708 | is an allocation error. */
713 node
->next
= list
->head
;
715 list
->head
->prev
= node
;
724 /* ======================================================== */
725 /* Insert a new node before the specified node in the list. */
726 /* ======================================================== */
728 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
)
730 ll_node_t
* new_node
;
732 if (node
->prev
== NULL
)
733 ll_prepend(list
, data
);
736 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
737 | uses xmalloc which does not return if there *
738 | is an allocation error. */
740 new_node
->data
= data
;
741 new_node
->next
= node
;
742 new_node
->prev
= node
->prev
;
743 node
->prev
->next
= new_node
;
744 node
->prev
= new_node
;
750 /* ======================================================= */
751 /* Insert a new node after the specified node in the list. */
752 /* ======================================================= */
754 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
)
756 ll_node_t
* new_node
;
758 if (node
->next
== NULL
)
759 ll_append(list
, data
);
762 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
763 | uses xmalloc which does not return if there *
764 | is an allocation error. */
766 new_node
->data
= data
;
767 new_node
->prev
= node
;
768 new_node
->next
= node
->next
;
769 node
->next
->prev
= new_node
;
770 node
->next
= new_node
;
776 /* ================================================================= */
777 /* Remove a node from a linked list */
778 /* The memory taken by the deleted node must be freed by the caller. */
779 /* ================================================================= */
781 ll_delete(ll_t
* const list
, ll_node_t
* node
)
783 if (list
->head
== list
->tail
)
785 if (list
->head
!= NULL
)
786 list
->head
= list
->tail
= NULL
;
790 else if (node
->prev
== NULL
)
792 list
->head
= node
->next
;
793 list
->head
->prev
= NULL
;
795 else if (node
->next
== NULL
)
797 list
->tail
= node
->prev
;
798 list
->tail
->next
= NULL
;
802 node
->next
->prev
= node
->prev
;
803 node
->prev
->next
= node
->next
;
813 #if 0 /* Unused yet */
814 /* ======================================================================== */
815 /* Find a node in the list containing data. Return the node pointer or NULL */
817 /* A comparison function must be provided to compare a and b (strcmp like). */
818 /* ======================================================================== */
820 ll_find(ll_t
* const list
, void * const data
,
821 int (*cmpfunc
)(const void * a
, const void * b
))
825 if (NULL
== (node
= list
->head
))
830 if (0 == cmpfunc(node
->data
, data
))
832 } while (NULL
!= (node
= node
->next
));
838 /* ==================================================================== */
839 /* Allocate and fill an array of strings from a list. */
841 /* 1) The list node must contain strings (char *) */
842 /* 2) The strings in the resulting array MUST NOT be freed as the are */
843 /* NOT copied from the strings of the list. */
845 /* IN list : The list from which the array is generated */
846 /* IN start_node : The node of the list which will be the first node to */
847 /* consider to create the array */
848 /* OUT: count : The number of elements of the resulting array. */
849 /* OUT: array : The resulting array or NULL if the list is empty. */
850 /* RC : : The number of elements of the resulting array. */
851 /* ==================================================================== */
853 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
)
862 if (list
== NULL
|| node
== NULL
)
869 *array
= xmalloc((list
->len
+ 1) * sizeof(char *));
872 (*array
)[n
++] = (char *)(node
->data
);
878 (*array
)[*count
] = NULL
;
883 /* ******************************************************************* */
884 /* BST (search.h compatible) implementation. */
886 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
887 /* the AT&T man page says. */
889 /* Written by reading the System V Interface Definition, not the code. */
891 /* Totally public domain. */
892 /* ******************************************************************* */
897 struct bst_s
* llink
;
898 struct bst_s
* rlink
;
901 #if 0 /* Unused yet */
902 /* =========================== */
903 /* Delete node with given key. */
904 /* =========================== */
906 bst_delete(const void * vkey
, void ** vrootp
,
907 int (*compar
)(const void *, const void *))
909 bst_t
** rootp
= (bst_t
**)vrootp
;
913 if (rootp
== NULL
|| (p
= *rootp
) == NULL
)
916 while ((cmp
= (*compar
)(vkey
, (*rootp
)->key
)) != 0)
919 rootp
= (cmp
< 0) ? &(*rootp
)->llink
/* follow llink branch */
920 : &(*rootp
)->rlink
; /* follow rlink branch */
922 return NULL
; /* key not found */
924 r
= (*rootp
)->rlink
; /* D1: */
925 if ((q
= (*rootp
)->llink
) == NULL
) /* Left NULL? */
928 { /* Right link is NULL? */
929 if (r
->llink
== NULL
)
930 { /* D2: Find successor */
935 { /* D3: Find NULL link */
936 for (q
= r
->llink
; q
->llink
!= NULL
; q
= r
->llink
)
939 q
->llink
= (*rootp
)->llink
;
940 q
->rlink
= (*rootp
)->rlink
;
944 free(*rootp
); /* D4: Free node */
945 *rootp
= q
; /* link parent to new node */
950 /* ===================================================================== */
951 /* Destroy a tree. */
952 /* The clean function pointer can be NULL, in this case the node content */
954 /* ===================================================================== */
956 bst_destroy(void * vrootp
, void (*clean
)(void *))
958 bst_t
* root
= (bst_t
*)vrootp
;
963 bst_destroy(root
->llink
, clean
);
964 bst_destroy(root
->rlink
, clean
);
967 clean((void *)root
->key
);
972 /* ========================= */
973 /* Find a node, or return 0. */
974 /* ========================= */
976 bst_find(const void * vkey
, void * const * vrootp
,
977 int (*compar
)(const void *, const void *))
979 bst_t
* const * rootp
= (bst_t
* const *)vrootp
;
984 while (*rootp
!= NULL
)
988 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
989 return *rootp
; /* key found */
990 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
991 : &(*rootp
)->rlink
; /* T4: follow right branch */
996 /* ======================================= */
997 /* Find or inserts datum into search tree. */
998 /* ======================================= */
1000 bst_search(void * vkey
, void ** vrootp
,
1001 int (*compar
)(const void *, const void *))
1004 bst_t
** rootp
= (bst_t
**)vrootp
;
1009 while (*rootp
!= NULL
)
1013 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
1014 return *rootp
; /* we found it! */
1016 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
1017 : &(*rootp
)->rlink
; /* T4: follow right branch */
1020 q
= xmalloc(sizeof(bst_t
)); /* T5: key not found */
1022 { /* make new node */
1023 *rootp
= q
; /* link new node to old */
1024 q
->key
= vkey
; /* initialize new node */
1025 q
->llink
= q
->rlink
= NULL
;
1030 /* ========================= */
1031 /* Walk the nodes of a tree. */
1032 /* ========================= */
1034 bst_walk_recurse(const bst_t
* root
,
1035 void (*action
)(const void *, walk_order_e
, int), int level
)
1037 if (root
->llink
== NULL
&& root
->rlink
== NULL
)
1038 (*action
)(root
, leaf
, level
);
1041 (*action
)(root
, preorder
, level
);
1042 if (root
->llink
!= NULL
)
1043 bst_walk_recurse(root
->llink
, action
, level
+ 1);
1044 (*action
)(root
, postorder
, level
);
1045 if (root
->rlink
!= NULL
)
1046 bst_walk_recurse(root
->rlink
, action
, level
+ 1);
1047 (*action
)(root
, endorder
, level
);
1052 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int))
1054 if (vroot
!= NULL
&& action
!= NULL
)
1055 bst_walk_recurse(vroot
, action
, 0);
1058 /* ************************ */
1059 /* Various implementations. */
1060 /* ************************ */
1062 /* ======================== */
1063 /* Trim leading characters. */
1064 /* ======================== */
1066 ltrim(char * str
, const char * trim_str
)
1068 size_t len
= strlen(str
);
1069 size_t begin
= strspn(str
, trim_str
);
1073 for (i
= begin
; i
<= len
; ++i
)
1074 str
[i
- begin
] = str
[i
];
1077 /* ================================================= */
1078 /* Trim trailing characters. */
1079 /* The resulting string will have at least min bytes */
1080 /* even if trailing spaces remain. */
1081 /* ================================================= */
1083 rtrim(char * str
, const char * trim_str
, size_t min
)
1085 size_t len
= strlen(str
);
1086 while (len
> min
&& strchr(trim_str
, str
[len
- 1]))
1090 /* ================================================== */
1091 /* Count the number of occurrences of the character c */
1092 /* in the string str. */
1093 /* The str pointer is assumed to be not NULL. */
1094 /* ================================================== */
1096 strchrcount(char * str
, char c
)
1107 /* =============================================== */
1108 /* Is the string str2 a prefix of the string str1? */
1109 /* =============================================== */
1111 strpref(char * str1
, char * str2
)
1113 while (*str1
!= '\0' && *str1
== *str2
)
1119 return *str2
== '\0';
1122 /* ========================== */
1123 /* Like strcmp ignoring case. */
1124 /* ========================== */
1126 stricmp(const char * s1
, const char * s2
)
1128 while (tolower((unsigned char)*s1
) == tolower((unsigned char)*s2
))
1137 return (int)tolower((unsigned char)*s1
) - (int)tolower((unsigned char)*s2
);
1140 /* ======================================================================== */
1141 /* Strings concatenation with dynamic memory allocation. */
1142 /* IN : a variable number of char * arguments with NULL terminating */
1144 /* The first one must have been dynamically allocated and is mandatory */
1146 /* Returns a new allocated string containing the concatenation of all */
1147 /* the arguments. It is the caller's responsibility to free the resulting */
1149 /* ======================================================================== */
1151 strappend(char * str
, ...)
1157 l
= 1 + strlen(str
);
1158 va_start(args
, str
);
1160 s
= va_arg(args
, char *);
1165 s
= va_arg(args
, char *);
1170 str
= xrealloc(str
, l
);
1172 va_start(args
, str
);
1173 s
= va_arg(args
, char *);
1178 s
= va_arg(args
, char *);
1185 /* ====================================================================== */
1186 /* Public domain strtok_r() by Charlie Gordon. */
1187 /* from comp.lang.c 9/14/2007 */
1188 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1190 /* (Declaration that it's public domain): */
1191 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1193 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1195 /* ====================================================================== */
1197 xstrtok_r(char * str
, const char * delim
, char ** end
)
1207 str
+= strspn(str
, delim
);
1214 str
+= strcspn(str
, delim
);
1224 /* ===================================================================== */
1225 /* Put the first word of str, truncated to len characters, in buf. */
1226 /* Return a pointer in str pointing just after the word. */
1227 /* buf must have been pre-allocated to accept at least len+1 characters. */
1228 /* Note that buf can contains a sting full of spaces is str was not */
1229 /* trimmed before the call. */
1230 /* ===================================================================== */
1232 get_word(char * str
, char * buf
, size_t len
)
1238 while (*s
&& isspace(*s
))
1241 /* Set the new string start. */
1242 /* """"""""""""""""""""""""" */
1247 while (*s
&& !isspace(*s
) && s
- str
< len
)
1250 strncpy(buf
, str
, s
- str
);
1256 /* ==================================================================== */
1257 /* Return 1 is value is "1" or "yes" (ignoring case). */
1258 /* Return 0 is value is "0" or "no" (ignoring case). */
1259 /* If value has another value, then set invalid to 1 and also return 0 */
1260 /* invalid is set to 0i in all the other cases. */
1261 /* ==================================================================== */
1263 eval_yes(char * value
, int * invalid
)
1267 if (strcmp(value
, "1") == 0 || stricmp(value
, "yes") == 0)
1269 else if (strcmp(value
, "0") != 0 && stricmp(value
, "no") != 0)
1275 /* =========================================================== */
1276 /* Fill an array of strings from the words composing a string. */
1278 /* str: initial string which will be altered. */
1279 /* args: array of pointers to the start of the words in str. */
1280 /* max: maximum number of words used before giving up. */
1281 /* return: the number of words (<=max). */
1282 /* =========================================================== */
1284 str2argv(char * str
, char ** args
, int max
)
1293 while (*str
== ' ' || *str
== '\t')
1299 args
[nb_args
] = str
;
1302 while (*str
&& (*str
!= ' ') && (*str
!= '\t'))
1309 /* ********************** */
1310 /* ctxopt implementation. */
1311 /* ********************** */
1313 static int ctxopt_initialized
= 0; /* cap_init has not yet been called */
1315 /* Flags structure initialized by ctxopt_init. */
1316 /* """"""""""""""""""""""""""""""""""""""""""" */
1319 int stop_if_non_option
;
1320 int allow_abbreviations
;
1321 int display_usage_on_error
;
1324 static flags_t flags
= { 0, 1, 1 };
1326 /* Context structure. */
1327 /* """""""""""""""""" */
1331 ll_t
* opt_list
; /* list of options allowed in this context. */
1332 ll_t
* incomp_list
; /* list of strings containing incompatible names *
1333 | of options separated by spaces or tabs. */
1334 ll_t
* req_list
; /* list of strings containing an option name and *
1335 | all the option names where at least one of *
1336 | them is required to be also present. */
1338 int (*action
)(char * name
, int type
, char * new_ctx
, int ctx_nb_data
,
1345 /* https://textik.com/#488ce3649b6c60f5 */
1347 /* +--------------+ */
1348 /* |first_ctx_inst| */
1349 /* +---+----------+ */
1351 /* +--v-----+ +--------+ +--------+ +-----+ */
1352 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1353 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1355 /* | | +-v------+ | | */
1356 /* | +--+ctx_inst<-----------+ | */
1357 /* | +-+------+ | */
1359 /* | +-v------+ | */
1360 /* +------+ctx_inst<--------------------------+ */
1367 /* Option structure. */
1368 /* """"""""""""""""" */
1371 char * name
; /* option name. */
1372 char * next_ctx
; /* new context this option may lead to */
1373 ll_t
* ctx_list
; /* list of contexts allowing this option. */
1374 char * params
; /* string containing all the parameters of *
1377 void (*action
)( /* The option associated action. */
1378 char * ctx_name
, /* context name. */
1379 char * opt_name
, /* option name. */
1380 char * par
, /* option parameter. */
1381 int nb_args
, /* number of arguments. */
1382 char ** args
, /* option arguments. */
1383 int nb_opt_data
, /* number of option data pointers. */
1384 void ** opt_data
, /* option data pointers. */
1385 int nb_ctx_data
, /* nb of current context data ptrs. */
1386 void ** ctx_data
/* current context data pointers. */
1389 int nb_data
; /* number of the data pointers passed as argument to action. */
1390 void ** data
; /* array of data pointers passed as argument to action. */
1392 int args
; /* 1 if this option takes arguments else 0. */
1393 int optional
; /* 1 if the option is optional, else 0. */
1394 int multiple
; /* 1 if the option can appear more than one time in a *
1395 | context, else 0. */
1397 int opt_count_matter
; /* 1 if we must restrict the count, else 0. */
1398 int occurrences
; /* Number of option occurrences in a context. */
1399 char opt_count_oper
; /* <, = or > */
1400 int opt_count_mark
; /* Value to be compared to with opt_count_oper. */
1402 char * arg
; /* symbolic text after # describing the option argument. */
1404 int optional_args
; /* 1 of option is optional else 0. */
1405 int multiple_args
; /* 1 is option can appear more than once in a context *
1408 int opt_args_count_matter
; /* 1 if count is rescticted, else 0. */
1409 char opt_args_count_oper
; /* <, = or > */
1410 int opt_args_count_mark
; /* Value to be compared to with *
1411 | opt_count_oper. */
1413 int eval_first
; /* 1 if this option must be evaluated before the options *
1414 | without this mark. */
1416 ll_t
* eval_before_list
; /* List of pointers on options which must be *
1417 | evaluated before this option. */
1419 ll_t
* constraints_list
; /* List of constraint check functions pointers. */
1422 /* Context instance structure. */
1423 /* """"""""""""""""""""""""""" */
1426 ctx_t
* ctx
; /* the context whose this is an instance of */
1427 ctx_inst_t
* prev_ctx_inst
; /* ctx_inst of the opt_inst which led to the *
1428 | creation of this ctx_inst structure. */
1429 opt_inst_t
* gen_opt_inst
; /* opt_inst which led to the creation of a *
1430 | instance of this structure. */
1431 ll_t
* incomp_bst_list
; /* list of seen_opt_t BST. */
1432 void * seen_opt_bst
; /* tree of seen_opt_t. */
1433 ll_t
* opt_req_list
; /* list of req_t. */
1434 ll_t
* opt_inst_list
; /* The list of option instances in this *
1435 | context instance. */
1436 char * par_name
; /* parameter which created this instance. */
1439 /* Option instance structure. */
1440 /* """""""""""""""""""""""""" */
1443 opt_t
* opt
; /* The option this is an instance of. */
1444 char * opt_name
; /* The option which led to this creation. */
1445 char * par
; /* The parameter which led to this creation. */
1446 ll_t
* values_list
; /* The list of arguments of this option. */
1447 ctx_inst_t
* next_ctx_inst
; /* The new context instance this option. *
1448 | instance may create. */
1451 /* Structure used to check if an option has bee seen or not */
1452 /* in a context instance. */
1453 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1456 opt_t
* opt
; /* The concerned option. */
1457 char * par
; /* Parameter which led to the making of this structure. */
1458 int seen
; /* 1 if seen in the context instances, else 0. */
1461 /* Structure used to check if at least one instance of the options whose */
1462 /* pointers are in or_opt_list has been seen in the ctx_inst where an */
1463 /* instance or opt is also present. */
1464 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1467 opt_t
* opt
; /* Option that asks for other options. */
1468 ll_t
* or_opt_list
; /* Required options, at least one of them *
1469 | must be present. */
1472 /* Parameter structure which links a parameter to the option it belongs to. */
1473 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1476 char * name
; /* Parameter name (with the leading -). */
1477 opt_t
* opt
; /* Attached option. */
1480 /* Constraint structure. */
1481 /* """"""""""""""""""""" */
1484 int (*constraint
)(int nb_args
, char ** args
, char * value
, char * parameter
);
1487 char * to_free
; /* pointer to the original string in which the array in *
1488 | args points to. This poinnter is kept there to allow *
1489 | it to be freed. */
1492 state_t
* cur_state
= NULL
; /* Current analysis state. */
1493 static ll_t
* cmdline_list
= NULL
; /* List of interpreted CLI words *
1494 | serves as the basis for the *
1495 | analysis of the parameters. */
1496 static ctx_t
* main_ctx
= NULL
; /* initial context. */
1497 static ctx_inst_t
* first_ctx_inst
= NULL
; /* Pointer to the fist context *
1498 | instance which holds the *
1499 | options instances. */
1500 static ll_t
* ctx_inst_list
= NULL
; /* List of the context instances. */
1502 /* ======================================================= */
1503 /* Parse a string for the next matching token. */
1505 /* s: string to parse. */
1506 /* token: pre_allocated array of max tok_len characters. */
1507 /* pattern: scanf type pattern token must match. */
1508 /* pos: number of characters successfully parsed in s. */
1510 /* Returns: a pointer to the first unread character or */
1511 /* to he terminating \0. */
1512 /* ======================================================= */
1514 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
)
1516 char * full_pattern
;
1522 n
= snprintf(len
, 3, "%zu", tok_len
);
1526 full_pattern
= xmalloc(strlen(pattern
) + n
+ 4);
1528 strcpy(full_pattern
, "%");
1529 strcat(full_pattern
, len
);
1530 strcat(full_pattern
, pattern
);
1531 strcat(full_pattern
, "%n");
1533 n
= sscanf(s
, full_pattern
, token
, pos
);
1543 /* ****************************************** */
1544 /* Various comparison and deletion functions. */
1545 /* ****************************************** */
1548 ctx_compare(const void * c1
, const void * c2
)
1550 return strcmp(((ctx_t
*)c1
)->name
, ((ctx_t
*)c2
)->name
);
1553 /* =========================== */
1554 /* Free a context_bst element. */
1555 /* =========================== */
1564 ll_destroy(ctx
->opt_list
, NULL
);
1565 ll_destroy(ctx
->incomp_list
, free
);
1566 ll_destroy(ctx
->req_list
, free
);
1567 bst_destroy(ctx
->par_bst
, par_free
);
1572 /* ============================= */
1573 /* Free a ctx_inst_list element. */
1574 /* ============================= */
1576 ctx_inst_free(void * ci
)
1578 ctx_inst_t
* ctx_inst
= ci
;
1580 free(ctx_inst
->par_name
);
1581 ll_destroy(ctx_inst
->incomp_bst_list
, incomp_bst_free
);
1582 bst_destroy(ctx_inst
->seen_opt_bst
, seen_opt_free
);
1583 ll_destroy(ctx_inst
->opt_inst_list
, opt_inst_free
);
1584 ll_destroy(ctx_inst
->opt_req_list
, req_free
);
1589 /* ============================== */
1590 /* Free an opt_inst_list element. */
1591 /* ============================== */
1593 opt_inst_free(void * oi
)
1595 opt_inst_t
* opt_inst
= oi
;
1597 ll_destroy(opt_inst
->values_list
, NULL
);
1602 /* ================================== */
1603 /* Compare two seen_opt_bst elements. */
1604 /* ================================== */
1606 seen_opt_compare(const void * so1
, const void * so2
)
1610 o1
= ((seen_opt_t
*)so1
)->opt
;
1611 o2
= ((seen_opt_t
*)so2
)->opt
;
1613 return strcmp(o1
->name
, o2
->name
);
1616 /* ============================ */
1617 /* Free a seen_opt_bst element. */
1618 /* ============================ */
1620 seen_opt_free(void * so
)
1622 seen_opt_t
* seen_opt
= so
;
1624 free(seen_opt
->par
);
1629 /* =========================== */
1630 /* Free an incomp_bst element. */
1631 /* =========================== */
1633 incomp_bst_free(void * b
)
1637 bst_destroy(bst
, NULL
);
1640 /* ============================= */
1641 /* Free an opt_req_list element. */
1642 /* ============================= */
1648 ll_destroy(req
->or_opt_list
, NULL
);
1652 /* ================================= */
1653 /* Compare two options_bst elements. */
1654 /* ================================= */
1656 opt_compare(const void * o1
, const void * o2
)
1658 return strcmp(((opt_t
*)o1
)->name
, ((opt_t
*)o2
)->name
);
1661 /* ============================= */
1662 /* Free an options_bst elements. */
1663 /* ============================= */
1670 free(opt
->next_ctx
);
1675 ll_destroy(opt
->ctx_list
, NULL
);
1676 ll_destroy(opt
->constraints_list
, constraint_free
);
1677 ll_destroy(opt
->eval_before_list
, NULL
);
1682 /* ============================= */
1683 /* Compare two par_bst elements. */
1684 /* ============================= */
1686 par_compare(const void * a1
, const void * a2
)
1688 return strcmp(((par_t
*)a1
)->name
, ((par_t
*)a2
)->name
);
1691 /* ======================= */
1692 /* Free a par_bst element. */
1693 /* ======================= */
1704 /* ================================ */
1705 /* Free a constraints_list element. */
1706 /* ================================ */
1708 constraint_free(void * c
)
1710 constraint_t
* cstr
= c
;
1713 free(cstr
->to_free
);
1718 /* ******************************************************************** */
1719 /* Helper functions to locate contexts, options and parameters in a BST */
1720 /* by their names. */
1721 /* ******************************************************************** */
1724 locate_ctx(char * name
)
1731 if ((node
= bst_find(&ctx
, &contexts_bst
, ctx_compare
)) == NULL
)
1738 locate_opt(char * name
)
1745 if ((node
= bst_find(&opt
, &options_bst
, opt_compare
)) == NULL
)
1752 locate_par(char * name
, ctx_t
* ctx
)
1756 void * bst
= ctx
->par_bst
;
1760 if ((node
= bst_find(&par
, &bst
, par_compare
)) == NULL
)
1766 /* ====================================================================== */
1767 /* Helper function to display the dependency constraints between options. */
1768 /* These constraints are set with the ctxopt_add_opt_settings function */
1769 /* using the 'before' and 'after' arguments. */
1770 /* IN list : a list of options. */
1771 /* ====================================================================== */
1773 print_before_constraints(ll_t
* list
)
1775 ll_node_t
* node
= list
->head
;
1776 ll_node_t
* before_node
;
1777 opt_t
* opt
, *before_opt
;
1780 while (node
!= NULL
)
1784 if (opt
->eval_before_list
->len
> 0)
1788 printf("\n If present in the command line,");
1789 msg
= 1; /* Display this message only once. */
1792 before_node
= opt
->eval_before_list
->head
;
1795 while (before_node
!= NULL
)
1797 before_opt
= before_node
->data
;
1798 printf("%s", before_opt
->params
);
1800 before_node
= before_node
->next
;
1802 if (before_node
!= NULL
)
1805 printf(" will be evaluated after %s\n", opt
->params
);
1811 /* =================================================================== */
1812 /* Utility function to format and print the options present in a list. */
1814 /* IN list : a list of options. */
1815 /* OUT has_* : a set of flags which will determine the content of the */
1816 /* explanation given after the formatted printing of the */
1818 /* =================================================================== */
1820 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
1821 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
1822 int * has_early_eval
)
1824 ll_node_t
* node
= list
->head
;
1829 line
= xstrdup(" ");
1831 while (node
!= NULL
)
1833 option
= xstrdup("");
1838 option
= strappend(option
, "[", (char *)0);
1842 if (opt
->eval_first
)
1844 option
= strappend(option
, "*", (char *)0);
1845 *has_early_eval
= 1;
1848 option
= strappend(option
, opt
->params
, (char *)0);
1850 if (opt
->next_ctx
!= NULL
)
1852 option
= strappend(option
, ">", opt
->next_ctx
, (char *)0);
1853 *has_ctx_change
= 1;
1858 if (opt
->opt_count_oper
!= '\0')
1862 o
[0] = opt
->opt_count_oper
;
1864 snprintf(m
, 3, "%u", opt
->opt_count_mark
);
1865 option
= strappend(option
, "...", o
, m
, (char *)0);
1869 option
= strappend(option
, "...", (char *)0);
1876 if (*(opt
->arg
) == '#')
1877 *has_generic_arg
= 1;
1879 option
= strappend(option
, " ", (char *)0);
1881 if (opt
->optional_args
)
1883 option
= strappend(option
, "[", opt
->arg
, (char *)0);
1887 option
= strappend(option
, opt
->arg
, (char *)0);
1889 if (opt
->multiple_args
)
1891 if (opt
->opt_args_count_oper
!= '\0')
1895 o
[0] = opt
->opt_args_count_oper
;
1897 snprintf(m
, 3, "%u", opt
->opt_args_count_mark
);
1898 option
= strappend(option
, "...", o
, m
, (char *)0);
1902 option
= strappend(option
, "...", (char *)0);
1906 if (opt
->optional_args
)
1907 option
= strappend(option
, "]", (char *)0);
1910 option
= strappend(option
, "]", (char *)0);
1912 if (strlen(line
) + 1 + strlen(option
) < 80)
1913 line
= strappend(line
, option
, " ", (char *)0);
1916 printf("%s\n", line
);
1918 line
= strappend(line
, option
, " ", (char *)0);
1926 printf("%s\n", line
);
1931 /* ==================================================== */
1932 /* Explain the special syntactic symbols present in the */
1933 /* generated usage messages. */
1934 /* ==================================================== */
1936 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
1937 int has_optional
, int has_ellipsis
, int has_rule
)
1939 if (has_early_eval
|| has_ctx_change
|| has_generic_arg
|| has_optional
1940 || has_ellipsis
|| has_rule
)
1942 printf("\nExplanation of the syntax used above:\n");
1943 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1944 "must be entered.\n");
1945 printf("The following is just there to explain the other symbols "
1949 printf("* : the parameters defined for this option will "
1950 "be evaluated first.\n");
1952 printf("> : the context after this symbol will be the new "
1953 "default context.\n");
1954 if (has_generic_arg
)
1955 printf("#tag : argument with a hint about its meaning.\n");
1957 printf("[...] : the object between square brackets is "
1960 printf("... : several occurrences of the previous object "
1963 printf("[<|=|>]number: rules constraining the number of "
1964 "parameters/arguments.\n");
1968 /* ************************************************************ */
1969 /* Various utilities and callback functions called when walking */
1970 /* through a BST. */
1971 /* ************************************************************ */
1974 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
)
1976 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1978 if (kind
== postorder
|| kind
== leaf
)
1980 if ((!seen_opt
->opt
->optional
) && seen_opt
->seen
== 0)
1983 user_string
= strappend(user_string
, seen_opt
->opt
->params
, " ",
1990 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
)
1992 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1994 if (kind
== postorder
|| kind
== leaf
)
1995 if (seen_opt
->seen
== 1)
1998 user_object
= seen_opt
->par
;
2003 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
)
2005 ctx_t
* ctx
= main_ctx
;
2006 ctx_t
* cur_ctx
= ((bst_t
*)node
)->key
;
2010 int has_optional
= 0;
2011 int has_ellipsis
= 0;
2013 int has_generic_arg
= 0;
2014 int has_ctx_change
= 0;
2015 int has_early_eval
= 0;
2017 if (kind
== postorder
|| kind
== leaf
)
2018 if (strcmp(ctx
->name
, cur_ctx
->name
) != 0)
2020 list
= cur_ctx
->opt_list
;
2022 printf("\nAllowed options in the context %s:\n", cur_ctx
->name
);
2023 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
,
2024 &has_generic_arg
, &has_ctx_change
, &has_early_eval
);
2025 print_before_constraints(list
);
2030 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
)
2032 opt_t
* opt
= ((bst_t
*)node
)->key
;
2034 if (kind
== postorder
|| kind
== leaf
)
2036 if (opt
->params
== NULL
) /* opt must have associated parameters. */
2037 fatal_internal("Option %s has no registered parameter.\n", opt
->name
);
2039 if (opt
->action
== NULL
) /* opt must have an action. */
2040 fatal_internal("Option %s has no registered action.\n", opt
->name
);
2045 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
)
2047 ctx_t
* ctx
= ((bst_t
*)node
)->key
;
2049 if (kind
== postorder
|| kind
== leaf
)
2051 char * str
= xstrdup(user_string
);
2053 while (*str
!= '\0')
2055 if (locate_par(str
, ctx
) != NULL
)
2057 if (*user_string2
== '\0')
2058 user_string2
= strappend(user_string2
, "- ", ctx
->name
, (char *)0);
2060 user_string2
= strappend(user_string2
, "\n- ", ctx
->name
, (char *)0);
2063 str
[strlen(str
) - 1] = '\0';
2070 match_prefix_cb(const void * node
, walk_order_e kind
, int level
)
2072 par_t
* par
= ((bst_t
*)node
)->key
;
2074 if (kind
== postorder
|| kind
== leaf
)
2075 if (strpref(par
->name
, (char *)user_object
))
2078 user_string
= strappend(user_string
, par
->name
, " ", (char *)0);
2082 /* ====================================================================== */
2083 /* A parameter may not be separated from its first option by spaces, in */
2084 /* this case this function looks for a valid flag as a prefix and splits */
2085 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
2088 /* IN word : the word to be checked. */
2089 /* IN ctx : the context in which the flag indexed by the word is to be */
2091 /* OUT pos : the offset in word pointing just after the matching prefix. */
2092 /* OUT opt : a pointer to the option associated with the new parameter */
2093 /* or NULL if none is found. */
2095 /* The returned pointer must be freed by the caller. */
2096 /* ====================================================================== */
2098 look_for_valid_prefix_in_word(char * word
, ctx_t
* ctx
, int * pos
, opt_t
** opt
)
2103 par_t tmp_par
= { 0 };
2109 new = xstrdup(word
);
2115 } while ((par
= locate_par(tmp_par
.name
, ctx
)) == NULL
&& len
> 2);
2134 /* ============================================================= */
2135 /* If par_name is an unique abbreviation of an exiting parameter */
2136 /* in the context ctx, then return this parameter. */
2137 /* ============================================================= */
2139 abbrev_expand(char * par_name
, ctx_t
* ctx
)
2141 user_object
= par_name
;
2144 *user_string
= '\0';
2145 bst_walk(ctx
->par_bst
, match_prefix_cb
);
2146 rtrim(user_string
, " ", 0);
2148 /* The previous bst_walk has built a string of blank separated parameters */
2149 /* all having par_name as prefix. This string is put in the user_string */
2150 /* exchange zone. The number of these words in put in user_rc. */
2151 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2152 if (user_rc
== 1) /* The number of matching abbreviations. */
2153 return xstrdup(user_string
);
2154 else /* There is at least tho defined parameters starting with par_name. */
2160 void * tmp_opt_bst
= NULL
;
2162 /* Find all the options corresponding to these words and store them */
2163 /* without duplication in a temporary BST. Only their resulting count */
2165 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2166 s
= first_s
= strtok(user_string
, " "); /* first_s holds a copy of *
2167 | the first word. */
2170 par
= locate_par(s
, ctx
);
2173 if (bst_find(opt
, &tmp_opt_bst
, opt_compare
) == NULL
)
2175 /* This option as not already been seen */
2176 /* store it and increase the seen counter. */
2177 /* """"""""""""""""""""""""""""""""""""""" */
2178 bst_search(opt
, &tmp_opt_bst
, opt_compare
);
2181 s
= strtok(NULL
, " ");
2184 /* Clean the temporary BST without removing the pointer */
2185 /* to the real options. */
2186 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2187 if (tmp_opt_bst
!= NULL
)
2188 bst_destroy(tmp_opt_bst
, NULL
);
2191 /* All the abbreviation are leading to only one option */
2192 /* We can just continue as in the previous case. */
2193 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2194 return xstrdup(first_s
);
2200 /* ================================================================ */
2201 /* Terminate the program if mandatory options required by a context */
2202 /* are not present. */
2203 /* ================================================================ */
2205 check_for_missing_mandatory_opt(ctx_inst_t
* ctx_inst
, char * opt_par
)
2209 if (has_unseen_mandatory_opt(ctx_inst
, &missing
))
2210 fatal(CTXOPTMISPAR
, missing
);
2213 /* ====================================================== */
2214 /* Return 1 if at least one mandatory option was not seen */
2215 /* when quitting a context, else 0. */
2216 /* ====================================================== */
2218 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
)
2221 *user_string
= '\0';
2223 bst_walk(ctx_inst
->seen_opt_bst
, bst_seen_opt_cb
);
2224 rtrim(user_string
, " ", 0);
2226 *missing
= user_string
;
2228 return user_rc
? 1 : 0;
2231 /* ========================================================================= */
2232 /* This function terminates the program if an option or its arguments do not */
2233 /* conform to its occurrences constraint. */
2234 /* There constraints can appear by trailing >, < or = in their definition */
2235 /* given in ctxopt_new_ctx. */
2236 /* ========================================================================= */
2238 check_for_occurrence_issues(ctx_inst_t
* ctx_inst
)
2240 ctx_t
* ctx
= ctx_inst
->ctx
;
2243 opt_inst_t
* opt_inst
;
2244 char * cur_opt_params
= cur_state
->cur_opt_params
;
2245 char * cur_opt_par_name
= cur_state
->cur_opt_par_name
;
2247 /* Checks options. */
2248 /* """"""""""""""" */
2249 node
= ctx
->opt_list
->head
;
2251 while (node
!= NULL
)
2255 /* Update current_state. */
2256 /* """"""""""""""""""""" */
2257 cur_state
->cur_opt_params
= opt
->params
;
2258 cur_state
->opts_count
= opt
->opt_count_mark
;
2259 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2261 if (opt
->opt_count_matter
)
2262 switch (opt
->opt_count_oper
)
2265 if (opt
->occurrences
> 0 && opt
->opt_count_mark
!= opt
->occurrences
)
2266 fatal(CTXOPTCTEOPT
, "");
2270 if (opt
->occurrences
> 0 && opt
->opt_count_mark
<= opt
->occurrences
)
2271 fatal(CTXOPTCTLOPT
, "");
2275 if (opt
->occurrences
> 0 && opt
->opt_count_mark
>= opt
->occurrences
)
2276 fatal(CTXOPTCTGOPT
, "");
2283 /* Checks arguments. */
2284 /* """"""""""""""""" */
2285 node
= ctx_inst
->opt_inst_list
->head
;
2286 while (node
!= NULL
)
2288 opt_inst
= node
->data
;
2289 opt
= opt_inst
->opt
;
2291 /* Update current_state. */
2292 /* """"""""""""""""""""" */
2293 cur_state
->cur_opt_par_name
= opt_inst
->par
;
2294 cur_state
->opts_count
= opt
->opt_count_mark
;
2295 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2297 int nb_values
= opt_inst
->values_list
->len
; /* Number of arguments of opt */
2299 if (opt
->opt_args_count_matter
)
2300 switch (opt
->opt_args_count_oper
)
2303 if (nb_values
> 0 && opt
->opt_args_count_mark
!= nb_values
)
2304 fatal(CTXOPTCTEARG
, "");
2308 if (nb_values
> 0 && opt
->opt_args_count_mark
<= nb_values
)
2309 fatal(CTXOPTCTLARG
, "");
2313 if (nb_values
> 0 && opt
->opt_args_count_mark
>= nb_values
)
2314 fatal(CTXOPTCTGARG
, "");
2320 cur_state
->cur_opt_params
= cur_opt_params
;
2321 cur_state
->cur_opt_par_name
= cur_opt_par_name
;
2324 /* ====================================================================== */
2325 /* This function terminates the program if all the options which are part */
2326 /* of a group of required options by some other option are missing. */
2327 /* ====================================================================== */
2329 check_for_requirement_issues(ctx_inst_t
* ctx_inst
)
2332 ll_node_t
* req_node
;
2337 seen_opt_t tmp_seen_opt
;
2339 char * needed_params
= NULL
;
2341 node
= ctx_inst
->opt_req_list
->head
;
2343 while (node
!= NULL
)
2348 tmp_seen_opt
.opt
= opt
;
2350 bst_node
= bst_find(&tmp_seen_opt
, &(ctx_inst
->seen_opt_bst
),
2353 if (((seen_opt_t
*)(bst_node
->key
))->seen
!= 0)
2356 req_node
= req
->or_opt_list
->head
;
2358 /* needed_params accumulates the params of the options in the group. */
2359 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2360 free(needed_params
); /* free can applied to the NULL pointer. */
2361 needed_params
= xstrdup("");
2363 /* Go through the list of the required group of options and */
2364 /* succeed when one of them has been seen in the context. */
2365 /* otherwise a fatal error is triggered and the program is */
2367 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2368 while (req_node
!= NULL
)
2370 req_opt
= req_node
->data
;
2371 tmp_seen_opt
.opt
= req_opt
;
2372 needed_params
= strappend(needed_params
, req_opt
->params
, "\n ",
2375 bst_node
= bst_find(&tmp_seen_opt
, &(ctx_inst
->seen_opt_bst
),
2378 if (((seen_opt_t
*)(bst_node
->key
))->seen
!= 0)
2380 found
= 1; /* A required option has been seen, */
2381 break; /* accept the group. */
2383 req_node
= req_node
->next
;
2386 rtrim(needed_params
, "\n ", 0);
2388 /* This is a fatal error if none of the options in the required */
2389 /* options group has been seen in the context. */
2390 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2395 if (req
->or_opt_list
->len
> 1)
2396 errmsg
= xstrdup("At least one of the parameters among:\n %s\n"
2397 "requested by %s must be present.\n");
2399 errmsg
= xstrdup("The parameter %s "
2400 "requested by %s must be present.\n");
2402 cur_state
->req_opt_par_needed
= needed_params
;
2403 cur_state
->req_opt_par
= opt
->params
;
2405 fatal(CTXOPTREQPAR
, errmsg
);
2413 /* ======================================================================== */
2414 /* Parse a strings describing options and some of their characteristics */
2415 /* The input string must have follow some rules like in the examples below: */
2417 /* "opt_name1 opt_name2" */
2418 /* "[opt_name1] opt_name2" */
2419 /* "[opt_name1] opt_name2..." */
2420 /* "[opt_name1 #...] opt_name2... [#]" */
2421 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2423 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2424 /* means that there can be more than one occurrence of the previous thing. */
2426 /* opt_name can be followed by a 'new context' change prefixed with the */
2427 /* symbol >, as in opt1>c2 by eg. */
2429 /* This function returns as soon as one (or no) option has been parsed and */
2430 /* return the offset to the next option to parse. */
2432 /* In case of successful parsing, an new option is allocated and its */
2433 /* pointer returned. */
2434 /* ======================================================================== */
2436 opt_parse(char * s
, opt_t
** opt
)
2438 int opt_optional
= 0;
2439 int opt_multiple
= 0;
2440 int opt_count_matter
= 0;
2441 char opt_count_oper
= '\0';
2442 unsigned opt_count_mark
= 0;
2444 char opt_arg
[33] = { 0 };
2445 int opt_multiple_args
= 0;
2446 int opt_args_count_matter
= 0;
2447 char opt_args_count_oper
= '\0';
2448 unsigned opt_args_count_mark
= 0;
2449 int opt_optional_args
= 0;
2450 int opt_eval_first
= 0;
2459 char * opt_name
= NULL
;
2464 memset(opt_arg
, '\0', 33);
2466 /* Strip the leading blanks. */
2467 /* """"""""""""""""""""""""" */
2471 if (*s
== '[') /* Start of an optional option. */
2476 s
= strtoken(s
, token
, sizeof(token
) - 1, "[^] \n\t.]", &pos
);
2478 return -1; /* Empty string. */
2480 /* Early EOS, only return success if the option is mandatory. */
2481 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2483 if (opt_optional
== 1)
2484 return -(s
- s_orig
- 1);
2486 /* Validate the option name */
2487 /* ALPHA+(ALPHANUM|_)* */
2488 /* """""""""""""""""""""""" */
2490 if (!isalpha(*p
) && *p
!= '*')
2491 return -(s
- s_orig
- 1); /* opt_name must start with a letter. */
2499 if (!isalnum(*p
) && *p
!= '_' && *p
!= '>')
2500 return -(s
- s_orig
- 1); /* opt_name must contain a letter, *
2501 * a number or a _ */
2506 opt_name
= xstrdup(token
+ 1); /* Ignore the first '*' in token. */
2508 opt_name
= xstrdup(token
);
2519 /* Check if it can appear multiple times by looking for the dots. */
2520 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2521 p
= strtoken(s
, token
, 3, "[.]", &pos
);
2524 if (strcmp(token
, "...") == 0)
2528 if (*s
== '<' || *s
== '=' || *s
== '>')
2533 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2536 opt_count_matter
= 1;
2537 opt_count_oper
= *s
;
2538 opt_count_mark
= value
;
2546 return -(s
- s_orig
- 1);
2552 /* Abort on extraneous ] if the option is mandatory. */
2553 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2557 return -(s
- s_orig
- 1);
2560 s
++; /* skip the ] */
2562 if (!*s
|| isblank(*s
))
2567 return -(s
- s_orig
- 1);
2571 /* A blank separates the option name and the argument tag. */
2572 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2584 n
= sscanf(s
, "[%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2585 if (pos
> 1 && *opt_arg
== '#') /* [# has been read. */
2588 opt_optional_args
= 1;
2590 opt_multiple_args
= 1; /* There were dots. */
2592 s
+= pos
+ !!(n
== 2) * 3; /* Skips the dots. */
2594 if (*s
== '<' || *s
== '=' || *s
== '>')
2599 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2602 opt_args_count_matter
= 1;
2603 opt_args_count_oper
= *s
;
2604 opt_args_count_mark
= value
;
2609 /* Optional arg tag must end with a ] */
2610 /* """""""""""""""""""""""""""""""""" */
2614 return -(s
- s_orig
- 1);
2617 s
++; /* Skip the ] */
2621 n
= sscanf(s
, "%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2622 if (pos
> 0 && *opt_arg
== '#') /* # has been read. */
2625 if (n
== 2) /* There were dots. */
2626 opt_multiple_args
= 1;
2628 s
+= pos
+ !!(n
== 2) * 3; /* Skip the dots */
2630 if (*s
== '<' || *s
== '=' || *s
== '>')
2635 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2638 opt_args_count_matter
= 1;
2639 opt_args_count_oper
= *s
;
2640 opt_args_count_mark
= value
;
2648 /* Abort on extraneous ] if the option is mandatory. */
2649 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2653 return -(s
- s_orig
- 1);
2656 s
++; /* skip the ] */
2658 /* Strip the following blanks. */
2659 /* """"""""""""""""""""""""""" */
2665 else if (opt_optional
== 0 && (!*s
|| isblank(*s
)))
2667 /* Strip the following blanks. */
2668 /* """"""""""""""""""""""""""" */
2674 else if (opt_args
== 0) /* # was not read it is possibly the start *
2675 * of another option. */
2680 return -(s
- s_orig
- 1);
2686 /* Strip the following blanks. */
2687 /* """"""""""""""""""""""""""" */
2693 if (*opt_name
== '>')
2694 fatal_internal("The option name is missing in %s.", opt_name
);
2696 count
= strchrcount(opt_name
, '>');
2699 char * tmp
= strchr(opt_name
, '>');
2700 next_ctx
= xstrdup(tmp
+ 1);
2704 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name
);
2706 *opt
= xmalloc(sizeof(opt_t
));
2708 (*opt
)->name
= opt_name
;
2709 (*opt
)->optional
= opt_optional
;
2710 (*opt
)->multiple
= opt_multiple
;
2711 (*opt
)->opt_count_matter
= opt_count_matter
;
2712 (*opt
)->opt_count_oper
= opt_count_oper
;
2713 (*opt
)->opt_count_mark
= opt_count_mark
;
2714 (*opt
)->args
= opt_args
;
2715 (*opt
)->arg
= xstrdup(opt_arg
);
2716 (*opt
)->optional_args
= opt_optional_args
;
2717 (*opt
)->multiple_args
= opt_multiple_args
;
2718 (*opt
)->opt_args_count_matter
= opt_args_count_matter
;
2719 (*opt
)->opt_args_count_oper
= opt_args_count_oper
;
2720 (*opt
)->opt_args_count_mark
= opt_args_count_mark
;
2721 (*opt
)->eval_first
= opt_eval_first
;
2722 (*opt
)->next_ctx
= next_ctx
;
2723 (*opt
)->ctx_list
= ll_new();
2724 (*opt
)->constraints_list
= ll_new();
2725 (*opt
)->eval_before_list
= ll_new();
2726 (*opt
)->action
= NULL
;
2727 (*opt
)->params
= NULL
;
2728 (*opt
)->data
= NULL
;
2733 /* ==================================================================== */
2734 /* Try to initialize all the option in a given string */
2735 /* Each parsed option are put in a BST tree with its name as index. */
2737 /* On collision, the arguments only the signature are required to be */
2738 /* the same else this is considered as an error. Options can be used in */
2739 /* more than one context and can be optional in one and mandatory in */
2741 /* ==================================================================== */
2743 init_opts(char * spec
, ctx_t
* ctx
)
2745 opt_t
* opt
, *bst_opt
;
2751 if ((offset
= opt_parse(spec
, &opt
)) > 0)
2755 if ((node
= bst_find(opt
, &options_bst
, opt_compare
)) != NULL
)
2757 int same_next_ctx
= 0;
2759 bst_opt
= node
->key
; /* Node extracted from the BST. */
2761 if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
== NULL
)
2763 else if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
!= NULL
)
2765 else if (bst_opt
->next_ctx
!= NULL
&& opt
->next_ctx
== NULL
)
2768 same_next_ctx
= strcmp(bst_opt
->next_ctx
, opt
->next_ctx
) == 0;
2770 if (bst_opt
->optional_args
!= opt
->optional_args
2771 || bst_opt
->multiple_args
!= opt
->multiple_args
2772 || bst_opt
->args
!= opt
->args
|| !same_next_ctx
)
2774 fatal_internal("The option %s already exists with "
2775 "a different arguments signature.\n",
2779 /* The newly created opt is already present in options_bst. */
2780 /* We can remove it. */
2781 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2784 /* The new occurrence of the option option is legal */
2785 /* append the current context ptr in the list. */
2786 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2787 ll_append(bst_opt
->ctx_list
, ctx
);
2789 /* Append the new option to the context's options list. */
2790 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2791 ll_append(ctx
->opt_list
, bst_opt
);
2795 /* Initialize the option's context list with the current context. */
2796 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2797 ll_append(opt
->ctx_list
, ctx
);
2799 /* Append the new option to the context's options list. */
2800 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2801 ll_append(ctx
->opt_list
, opt
);
2803 /* Insert the new option in the BST. */
2804 /* """"""""""""""""""""""""""""""""" */
2805 bst_search(opt
, &options_bst
, opt_compare
);
2810 char * s
= xstrndup(spec
, -offset
);
2811 printf("%s <---\nSyntax error at or before offset %d\n", s
, -offset
);
2821 /* ===================================================== */
2822 /* ctxopt initialization function, must be called first. */
2823 /* ===================================================== */
2825 ctxopt_init(char * prog_name
, char * init_flags
)
2829 contexts_bst
= NULL
;
2835 user_string
= xmalloc(8);
2836 user_string2
= xmalloc(8);
2838 char flag
[33], fname
[31], vname
[31];
2841 ctxopt_initialized
= 1;
2843 /* Initialize current_state.*/
2844 /* """""""""""""""""""""""" */
2845 cur_state
= xcalloc(sizeof(state_t
), 0);
2847 /* Initialize custom error function pointers to NULL. */
2848 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2849 err_functions
= xmalloc(CTXOPTERRSIZ
* sizeof(void *));
2850 for (n
= 0; n
< CTXOPTERRSIZ
; n
++)
2851 err_functions
[n
] = NULL
;
2853 /* Parse init_flags if any. */
2854 /* """""""""""""""""""""""" */
2855 while (*init_flags
&& (init_flags
= get_word(init_flags
, flag
, 32)))
2859 if (sscanf(flag
, "%30[^=]=%30[^=]", fname
, vname
) != 2)
2860 fatal_internal("Invalid flag assignment: %s.", flag
);
2862 if (strcmp(fname
, "stop_if_non_option") == 0)
2864 if (eval_yes(vname
, &invalid
))
2865 flags
.stop_if_non_option
= 1;
2867 flags
.stop_if_non_option
= 0;
2869 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2871 else if (strcmp(fname
, "allow_abbreviations") == 0)
2873 if (eval_yes(vname
, &invalid
))
2874 flags
.allow_abbreviations
= 1;
2876 flags
.allow_abbreviations
= 0;
2878 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2880 else if (strcmp(fname
, "display_usage_on_error") == 0)
2882 if (eval_yes(vname
, &invalid
))
2883 flags
.display_usage_on_error
= 1;
2885 flags
.display_usage_on_error
= 0;
2887 fatal_internal("Invalid flag value for %s: %s.", fname
, vname
);
2890 fatal_internal("Invalid flag name: %s.", fname
);
2894 /* Update current_state. */
2895 /* """"""""""""""""""""" */
2898 if (*prog_name
== '\0')
2899 cur_state
->prog_name
= xstrdup("program_name");
2900 else if ((ptr
= strrchr(prog_name
, '/')))
2901 cur_state
->prog_name
= xstrdup(ptr
+ 1);
2903 cur_state
->prog_name
= xstrdup(prog_name
);
2906 cur_state
->prog_name
= xstrdup("program_name");
2909 /* ========================================================================= */
2910 /* Utility function which create and register a par_t object in a BST */
2911 /* embedded in a context. */
2912 /* This object will have a name and a pointer to the option it refers to. */
2913 /* These object will be used to quickly find an option from a command */
2914 /* line parameter during the analysis phase. */
2916 /* IN : an option name. */
2917 /* IN : a string of command line parameters to associate to the option. */
2918 /* Returns : 1 is all was fine else 0. */
2919 /* ========================================================================= */
2921 opt_set_parms(char * opt_name
, char * par_str
)
2923 char * par_name
, *ctx_name
;
2924 char * tmp_par_str
, *end_tmp_par_str
;
2928 par_t
* par
, tmp_par
;
2929 int rc
= 1; /* return code */
2934 /* Look if the given option is defined. */
2935 /* """""""""""""""""""""""""""""""""""" */
2936 opt
= locate_opt(opt_name
);
2938 fatal_internal("Unknown option %s.", opt_name
);
2940 /* For each context using this option. */
2941 /* """"""""""""""""""""""""""""""""""" */
2942 list
= opt
->ctx_list
;
2945 while (lnode
!= NULL
)
2947 /* Locate the context in the contexts tree. */
2948 /* """""""""""""""""""""""""""""""""""""""" */
2949 ctx_name
= ((ctx_t
*)(lnode
->data
))->name
;
2951 ctx
= locate_ctx(ctx_name
);
2953 fatal_internal("Unknown context %s.", ctx_name
);
2956 void * par_bst
= ctx
->par_bst
;
2958 tmp_par_str
= xstrdup(par_str
);
2959 ltrim(tmp_par_str
, " \t");
2960 rtrim(tmp_par_str
, " \t", 0);
2961 par_name
= xstrtok_r(tmp_par_str
, " \t,", &end_tmp_par_str
);
2962 if (par_name
== NULL
)
2963 fatal_internal("Parameters are missing for option %s.", opt_name
);
2965 /* For each parameter given in par_str, creates a par_t object and */
2966 /* insert it the in the parameters BST of the context. */
2967 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2968 while (par_name
!= NULL
)
2970 tmp_par
.name
= par_name
;
2972 node
= bst_find(&tmp_par
, &par_bst
, par_compare
);
2975 fatal_internal("The parameter %s is already defined in context %s.",
2976 par_name
, ctx
->name
);
2981 par
= xmalloc(sizeof(par_t
));
2982 par
->name
= xstrdup(par_name
);
2983 par
->opt
= opt
; /* Link the option to this parameter */
2985 bst_search(par
, &par_bst
, par_compare
);
2987 par_name
= xstrtok_r(NULL
, " \t,", &end_tmp_par_str
);
2990 /* Update the value of the root of ctx->par_bst as it may have */
2991 /* been modified. */
2992 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2993 ctx
->par_bst
= par_bst
;
2997 lnode
= lnode
->next
;
3003 /* ==================================================================== */
3004 /* Create a new context instance. */
3005 /* IN ctx : a context pointer to allow this instance to */
3006 /* access the context fields */
3007 /* IN prev_ctx_inst : the context instance whose option leading to the */
3008 /* creation of this new context instance is part of */
3009 /* Returns : the new context. */
3010 /* ==================================================================== */
3012 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
)
3015 opt_inst_t
* gen_opt_inst
;
3016 ctx_inst_t
* ctx_inst
;
3017 seen_opt_t
* seen_opt
;
3018 char * str
, *opt_name
;
3022 /* Keep a trace of the opt_inst which was at the origin of the creation */
3023 /* of this context instance. */
3024 /* This will serve during the evaluation of the option callbacks. */
3025 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3026 if (prev_ctx_inst
!= NULL
)
3028 gen_opt_inst
= (opt_inst_t
*)(prev_ctx_inst
->opt_inst_list
->tail
->data
);
3030 /* Update current_state. */
3031 /* """"""""""""""""""""" */
3032 cur_state
->opt_name
= gen_opt_inst
->opt
->name
;
3035 gen_opt_inst
= NULL
;
3037 /* Create and initialize the new context instance. */
3038 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3039 ctx_inst
= xmalloc(sizeof(ctx_inst_t
));
3040 ctx_inst
->ctx
= ctx
;
3041 ctx_inst
->prev_ctx_inst
= prev_ctx_inst
;
3042 ctx_inst
->gen_opt_inst
= gen_opt_inst
;
3043 ctx_inst
->incomp_bst_list
= ll_new();
3044 ctx_inst
->opt_inst_list
= ll_new();
3045 ctx_inst
->opt_req_list
= ll_new();
3046 ctx_inst
->seen_opt_bst
= NULL
;
3050 if (prev_ctx_inst
== NULL
)
3051 first_ctx_inst
= ctx_inst
;
3053 /* Initialize the occurrence counters of each opt allowed in the context. */
3054 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3055 node
= ctx
->opt_list
->head
;
3056 while (node
!= NULL
)
3059 opt
->occurrences
= 0;
3064 /* Initialize the BST containing the seen indicator for all the options */
3065 /* allowed in this context instance. */
3066 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3067 node
= ctx
->opt_list
->head
;
3068 while (node
!= NULL
)
3071 seen_opt
= xmalloc(sizeof(seen_opt_t
));
3072 seen_opt
->opt
= opt
;
3073 seen_opt
->par
= NULL
;
3076 bst_search(seen_opt
, &(ctx_inst
->seen_opt_bst
), seen_opt_compare
);
3081 /* Initialize the BST containing the incompatibles options. */
3082 /* Incompatibles option names are read from strings found in the list */
3083 /* incomp_list present in each instance of ctx_t. */
3084 /* These names are then used to search for the object of type seen_opt_t */
3085 /* which is already present in the seen_opt_bst of the context instance. */
3087 /* Once found the seen_opt_t object in inserted in the new BST */
3088 /* At the end the new BST in added to the list incomp_bst_list. */
3089 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3090 node
= ctx
->incomp_list
->head
;
3091 while (node
!= NULL
)
3094 seen_opt_t tmp_seen_opt
;
3096 str
= xstrdup(node
->data
);
3098 rtrim(str
, " \t", 0);
3099 opt_name
= strtok(str
, " \t"); /* Extract the first option name. */
3101 while (opt_name
!= NULL
) /* For each option name. */
3103 if ((opt
= locate_opt(opt_name
)) != NULL
)
3105 /* The option found is searched in the tree of potential */
3107 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3108 tmp_seen_opt
.opt
= opt
;
3110 bst_node
= bst_find(&tmp_seen_opt
, &(ctx_inst
->seen_opt_bst
),
3113 if (bst_node
!= NULL
)
3115 /* If found then it is added into the new BST tree. */
3116 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3117 seen_opt
= bst_node
->key
;
3118 bst_search(seen_opt
, &bst
, seen_opt_compare
);
3121 /* Not found! That means that the option is unknown in this */
3122 /* context as all options has have a seen_opt structure in */
3124 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3125 fatal_internal("%s is not known in the context %s.", opt
->name
,
3129 fatal_internal("Unknown option %s.", opt_name
);
3131 opt_name
= strtok(NULL
, " \t");
3135 ll_append(ctx_inst
->incomp_bst_list
, bst
);
3140 /* Initialize the list of res_t structures according to the */
3141 /* list set in the context by ctxopt_add_ctx_settings/required. */
3142 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3143 node
= ctx
->req_list
->head
;
3144 while (node
!= NULL
)
3146 req_t
* req
= xmalloc(sizeof(req_t
));
3148 str
= xstrdup(node
->data
);
3150 rtrim(str
, " \t", 0);
3151 opt_name
= strtok(str
, " \t"); /* Extract the first option name. */
3153 if ((opt
= locate_opt(opt_name
)) != NULL
)
3156 req
->or_opt_list
= ll_new();
3157 while ((opt_name
= strtok(NULL
, " \t")) != NULL
)
3159 if ((opt
= locate_opt(opt_name
)) != NULL
)
3160 ll_append(req
->or_opt_list
, opt
);
3162 fatal_internal("Unknown option %s.", opt_name
);
3164 ll_append(ctx_inst
->opt_req_list
, req
);
3167 fatal_internal("Unknown option %s.", opt_name
);
3176 /* ====================================================================== */
3177 /* Create a list formed by all the significant command line words */
3178 /* Words beginning or ending with { or } are split. Each of these */
3179 /* symbols will get their own place in the list. */
3181 /* the {...} part delimits a context, the { will not appear in the list */
3182 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3183 /* to facilitate the parsing phase. | must not be used by the end user. */
3185 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3186 /* program name is not considered. */
3187 /* IN words : is the array of strings constituting the command line to */
3189 /* Returns : 1 on success, 0 if a { or } is missing. */
3190 /* ====================================================================== */
3192 ctxopt_build_cmdline_list(int nb_words
, char ** words
)
3195 char * prev_word
= NULL
;
3199 ll_node_t
*node
, *start_node
;
3201 /* The analysis is divided into three passes, this is not optimal but */
3202 /* must be done only one time. Doing that we privilege readability. */
3204 /* In the following, SG is the ascii character 1d (dec 29) */
3206 /* The first pass creates the list, extract the leading an trailing */
3207 /* SG '{' and '}' of each word and give them their own place in the */
3210 /* The second pass transform the '{...}' blocks by a trailing SG */
3211 /* ({...} -> ...|) */
3213 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3214 /* the middle in the remaining list elements and recreate the pseudo */
3216 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3218 /* If the option list is not empty, clear it before going further. */
3219 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3220 if (cmdline_list
!= NULL
)
3221 ll_destroy(cmdline_list
, free
);
3223 cmdline_list
= ll_new();
3225 start_node
= cmdline_list
->head
; /* In the following loop start_node will *
3226 * contain a pointer to the current *
3227 * word stripped from its leading *
3228 * sequence of {, }. */
3229 for (i
= 0; i
< nb_words
; i
++)
3231 size_t len
= strlen(words
[i
]);
3237 /* Replace each occurrence of the legal word {} by the characters */
3238 /* 0x02 and 0x03 to hide them from the following process. */
3239 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3240 while ((ptr
= strstr(str
, "{}")) != NULL
)
3242 *ptr
= 0x02; /* Arbitrary values unlikely. */
3243 *(ptr
+ 1) = 0x03; /* present in a word */
3246 if (len
> 1) /* The word contains at least 2 characters. */
3250 /* Interpret its beginning and look for the start of the real word. */
3251 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3252 while (start
<= len
- 1 && (str
[start
] == '{' || str
[start
] == '}'))
3254 ll_append(cmdline_list
, xstrndup(str
+ start
, 1));
3256 start_node
= cmdline_list
->tail
;
3260 if (str
[end
] == '{' || str
[end
] == '}')
3262 if (end
> 0 && str
[end
- 1] != '\\')
3264 ll_append(cmdline_list
, xstrndup(str
+ end
, 1));
3266 node
= cmdline_list
->tail
;
3268 while (str
[end
] == '{' || str
[end
] == '}')
3270 if (end
> start
&& str
[end
- 1] == '\\')
3273 ll_insert_before(cmdline_list
, node
, xstrndup(str
+ end
, 1));
3282 if (start_node
!= NULL
)
3283 ll_insert_after(cmdline_list
, start_node
,
3284 xstrndup(str
+ start
, end
- start
+ 1));
3286 ll_append(cmdline_list
, xstrndup(str
+ start
, end
- start
+ 1));
3287 start_node
= cmdline_list
->tail
;
3292 ll_append(cmdline_list
, xstrdup(str
));
3293 start_node
= cmdline_list
->tail
;
3299 node
= cmdline_list
->head
;
3302 while (node
!= NULL
)
3306 if (strcmp(word
, "{") == 0)
3308 ll_node_t
* old_node
= node
;
3312 ll_delete(cmdline_list
, old_node
);
3314 else if (strcmp(word
, "}") == 0)
3332 node
= cmdline_list
->head
;
3334 while (node
!= NULL
)
3338 /* Restore the original { and } characters forming the legal word {}. */
3339 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3340 while ((ptr
= strchr(word
, 0x02)) != NULL
)
3342 while ((ptr
= strchr(word
, 0x03)) != NULL
)
3345 /* Remove a SG if the previous element is SG. */
3346 /* """""""""""""""""""""""""""""""""""""""""" */
3347 if (strcmp(word
, "\x1d") == 0)
3349 if (prev_word
!= NULL
&& (strcmp(prev_word
, "\x1d") == 0))
3351 ll_node_t
* old_node
= node
;
3353 free(old_node
->data
);
3354 ll_delete(cmdline_list
, old_node
);
3357 else if (strcmp(word
, "-") == 0) /* A single - is a legal argument, not *
3358 * a parameter. Protect it. */
3361 node
->data
= xstrdup("\\-");
3364 prev_word
= node
->data
;
3368 /* Clean useless and SG at the beginning and end of list. */
3369 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3370 node
= cmdline_list
->head
;
3377 if (strcmp(word
, "\x1d") == 0)
3380 ll_delete(cmdline_list
, node
);
3383 node
= cmdline_list
->tail
;
3389 if (strcmp(word
, "\x1d") == 0)
3392 ll_delete(cmdline_list
, node
);
3398 /* ===================================================================== */
3399 /* Build and analyze the command line list and create the linked data */
3400 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3401 /* This function identifies the following errors and creates an array of */
3402 /* The remaining unanalyzed arguments. */
3403 /* - detect missing arguments */
3404 /* - detect too many arguments */
3405 /* - detect unknown parameters in a context */
3406 /* - detect too many occurrences of a parameters in a context */
3407 /* - detect missing required arguments in a context */
3409 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3410 /* program name is not considered */
3411 /* IN words : is the array of strings constituting the command line to */
3413 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3414 /* is present in the list. */
3415 /* OUT rem_args : array of remaining command line arguments if a -- */
3416 /* is present in the list. This array must be free by */
3417 /* The caller as it is allocated here. */
3418 /* ===================================================================== */
3420 ctxopt_analyze(int nb_words
, char ** words
, int * nb_rem_args
,
3423 char * ctxopt_debug_env
; /* Environment variable CTXOPT_DEBUG content. */
3424 int ctxopt_debug
; /* 1 if ctxopt_debug_env is set and not empty. *
3425 | 0 if ctxopt_debug_env is unset or empty. */
3430 ctx_inst_t
* ctx_inst
;
3431 opt_inst_t
* opt_inst
;
3434 int expect_par_or_arg
= 0;
3436 ll_node_t
* cli_node
;
3438 seen_opt_t
* bst_seen_opt
;
3444 if (!ctxopt_build_cmdline_list(nb_words
, words
))
3445 fatal_internal("The command line could not be parsed: "
3446 "missing '{' or '}' detected.");
3448 if (main_ctx
== NULL
)
3449 fatal_internal("At least one context must have been created.");
3451 /* Check that all options has an action and at least one parameter. */
3452 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3453 bst_walk(options_bst
, bst_check_opt_cb
);
3455 /* CTXOPT debug setting */
3456 /* """""""""""""""""""" */
3457 ctxopt_debug_env
= getenv("CTXOPT_DEBUG");
3458 if (ctxopt_debug_env
!= NULL
&& *ctxopt_debug_env
!= '\0')
3463 /* Create the first ctx_inst record. */
3464 /* """"""""""""""""""""""""""""""""" */
3467 ctx_inst_list
= ll_new();
3468 ctx_inst
= new_ctx_inst(ctx
, NULL
);
3469 ctx_inst
->par_name
= NULL
;
3471 /* Update current_state. */
3472 /* """"""""""""""""""""" */
3473 cur_state
->ctx_name
= ctx
->name
;
3475 ll_append(ctx_inst_list
, ctx_inst
);
3477 /* For each node in the command line. */
3478 /* """""""""""""""""""""""""""""""""" */
3479 cli_node
= cmdline_list
->head
;
3483 while (cli_node
!= NULL
)
3485 if (strcmp(cli_node
->data
, "--") == 0)
3486 break; /* No new parameter will be analyzed after this point. */
3488 par_name
= cli_node
->data
;
3490 /* Replace a leading -- by a single - */
3491 /* """""""""""""""""""""""""""""""""" */
3492 if (strncmp(par_name
, "--", 2) == 0)
3493 par_name
+= 1; /* Ignore the first dash */
3495 if (strcmp(par_name
, "\x1d") == 0)
3497 check_for_missing_mandatory_opt(ctx_inst
, (char *)(cli_node
->prev
->data
));
3498 check_for_occurrence_issues(ctx_inst
);
3499 check_for_requirement_issues(ctx_inst
);
3501 /* Forced backtracking to the previous context instance. */
3502 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3503 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3505 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3506 ctx
= ctx_inst
->ctx
;
3508 /* Update current_states. */
3509 /* """"""""""""""""""""" */
3510 cur_state
->ctx_name
= ctx
->name
;
3511 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3515 "CTXOPT_DEBUG: Context forced backtrack, "
3516 "new current context: %s.\n",
3521 /* Update current_state. */
3522 /* """"""""""""""""""""" */
3523 cur_state
->ctx_par_name
= NULL
;
3526 else if (expect_par
&& *par_name
== '-')
3531 /* Update current_state. */
3532 /* """"""""""""""""""""" */
3533 cur_state
->cur_opt_par_name
= par_name
;
3534 cur_state
->ctx_name
= ctx
->name
;
3535 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3538 fprintf(stderr
, "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3539 par_name
, cur_state
->ctx_name
);
3541 /* An expected parameter has been seen. */
3542 /* """""""""""""""""""""""""""""""""""" */
3543 if ((par
= locate_par(par_name
, ctx
)) == NULL
)
3548 /* Look if this parameter is an unique abbreviation of a longer */
3549 /* parameter. If this is the case then just replace it with its */
3550 /* full length version and try again. */
3551 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3552 if (flags
.allow_abbreviations
)
3553 if ((word
= abbrev_expand(par_name
, ctx
)) != NULL
)
3555 cli_node
->data
= word
;
3559 /* Try to find a prefix which is a valid parameter in this context */
3560 /* If found, split the cli_node in two to build a new parameter */
3561 /* node and followed by a node containing the remaining string */
3562 /* If the new parameter corresponds to an option not taking */
3563 /* argument then prefix the remaining string whit a dash as it may */
3564 /* contain a new parameter. */
3565 /* The new parameter will be re-evaluated in the next iteration of */
3567 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3568 prefix
= look_for_valid_prefix_in_word(par_name
, ctx
, &pos
, &popt
);
3569 if (prefix
!= NULL
&& pos
!= 0)
3573 "CTXOPT_DEBUG: Found a valid parameter "
3574 "as a prefix of %s: %s.\n",
3577 cli_node
->data
= prefix
; /* prefix contains le name of a valid *
3578 | parameter in this context. */
3582 /* The parameter may be followed by arguments. */
3583 /* ''''''''''''''''''''''''''''''''''''''''''' */
3584 if (*(par_name
+ pos
) == '-')
3586 word
= xstrdup("\\"); /* Protect the '-' */
3587 word
= strappend(word
, par_name
+ pos
, (char *)0);
3590 word
= xstrdup(par_name
+ pos
);
3594 /* The parameter does not take arguments, the */
3595 /* following word must be a parameter or nothing */
3596 /* hence prefix it with a dash. */
3597 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3598 word
= xstrdup("-");
3599 word
= strappend(word
, par_name
+ pos
, (char *)0);
3602 /* Insert it after the current node in the list. */
3603 /* """"""""""""""""""""""""""""""""""""""""""""" */
3604 ll_insert_after(cmdline_list
, cli_node
, word
);
3606 continue; /* loop */
3610 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3611 check_for_occurrence_issues(ctx_inst
);
3612 check_for_requirement_issues(ctx_inst
);
3614 if (ctx_inst
->prev_ctx_inst
== NULL
)
3616 char * errmsg
= xstrdup("");
3618 /* Update current_state. */
3619 /* """"""""""""""""""""" */
3620 cur_state
->ctx_par_name
= NULL
;
3622 *user_string
= '\0';
3623 *user_string2
= '\0';
3625 user_string
= strappend(user_string
, par_name
, (char *)0);
3627 bst_walk(contexts_bst
, bst_match_par_cb
);
3629 if (*user_string2
!= '\0')
3633 if (flags
.display_usage_on_error
)
3634 help_msg
= "see below.\n";
3636 help_msg
= "\nrefer to the manual for more information.\n";
3640 "\nThis parameter is only valid in one of the following "
3643 "\n\nSwitch to one of them first using the appropriate "
3645 help_msg
, (char *)0);
3648 fatal(CTXOPTUNKPAR
, errmsg
);
3652 /* Tries to backtrack and analyse the same parameter in the */
3653 /* previous context. */
3654 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3655 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3656 ctx
= ctx_inst
->ctx
;
3660 "CTXOPT_DEBUG: Context backtrack, "
3661 "new current context: %s.\n",
3664 /* Update current_state. */
3665 /* """"""""""""""""""""" */
3666 cur_state
->ctx_name
= ctx
->name
;
3667 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3669 cli_node
= cli_node
->prev
;
3675 seen_opt_t seen_opt
;
3677 /* The parameter is valid in the context, create a opt_inst and */
3678 /* append it to the ctx_inst list options list. */
3679 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3684 opt_inst
= xmalloc(sizeof(opt_inst_t
));
3685 opt_inst
->opt
= opt
;
3686 opt_inst
->par
= par_name
;
3687 opt_inst
->values_list
= ll_new();
3688 opt_inst
->next_ctx_inst
= NULL
;
3690 /* Update current_state. */
3691 /* """"""""""""""""""""" */
3692 cur_state
->cur_opt_params
= opt
->params
;
3694 /* Priority option are inserted at the start of the opt_inst list */
3695 /* but their order of appearance in the context definition must */
3696 /* be preserver so each new priority option will be placed after */
3697 /* the previous ones at the start of the opt_inst list. */
3698 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3699 if (!opt
->eval_first
)
3701 /* Look if we have a registered dependency in the order of the */
3702 /* evaluation of two options. */
3703 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3704 if (opt
->eval_before_list
->len
> 0)
3706 ll_t
* list
= ctx_inst
->opt_inst_list
;
3707 ll_node_t
* opt_inst_node
;
3709 ll_t
* before_list
= opt
->eval_before_list
;
3710 ll_node_t
* before_node
= before_list
->head
;
3712 ll_node_t
* target_node
= NULL
; /* If not NULL, the new node *
3713 | will be inserted before it. */
3715 /* For each entry in eval_before_list, try to find if it */
3716 /* refers to an option already entered in the context. If this */
3717 /* is the case, insert it just before it instead of putting it */
3719 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3720 while (before_node
!= NULL
)
3722 opt_inst_node
= list
->head
;
3724 while (opt_inst_node
!= target_node
)
3726 opt_t
* tmp_opt
= (((opt_inst_t
*)opt_inst_node
->data
))->opt
;
3728 /* We have found an option mentioned if the before_list */
3729 /* of the option we want to add. We can stop searching. */
3730 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3731 if (strcmp(tmp_opt
->name
, ((opt_t
*)before_node
->data
)->name
))
3732 opt_inst_node
= opt_inst_node
->next
;
3734 target_node
= opt_inst_node
; /* Set the target node. */
3737 before_node
= before_node
->next
;
3740 /* Insert or append ? */
3741 /* """""""""""""""""" */
3742 if (target_node
!= NULL
)
3743 ll_insert_before(ctx_inst
->opt_inst_list
, target_node
, opt_inst
);
3745 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3748 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3752 ll_node_t
* opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
3753 opt_inst_t
* tmp_opt_inst
;
3755 while (opt_inst_node
!= NULL
)
3757 tmp_opt_inst
= opt_inst_node
->data
;
3758 if (!tmp_opt_inst
->opt
->eval_first
)
3760 ll_insert_before(ctx_inst
->opt_inst_list
, opt_inst_node
,
3765 opt_inst_node
= opt_inst_node
->next
;
3767 if (opt_inst_node
== NULL
)
3768 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3771 /* Check if an option was already seen in the */
3772 /* current context instance. */
3773 /* """""""""""""""""""""""""""""""""""""""""" */
3776 bst_node
= bst_find(&seen_opt
, &(ctx_inst
->seen_opt_bst
),
3779 /* bst_node cannot be NULL here. */
3781 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3783 if (!opt
->multiple
&& bst_seen_opt
->seen
== 1)
3784 fatal(CTXOPTDUPOPT
, "");
3786 /* Check if this option is compatible with the options already */
3787 /* seen in this context instance. */
3788 /* Look if the option is present in one on the BST present in */
3789 /* the incomp_bst_list of the context instance. */
3790 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3791 node
= ctx_inst
->incomp_bst_list
->head
;
3792 while (node
!= NULL
)
3797 /* There can only have one seen_opt object in the BST tree was */
3798 /* already seen, try to locate it, the result will be put in */
3799 /* user_object by the bst_seen_opt_seen_cb function. */
3800 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3801 bst_walk(bst
, bst_seen_opt_seen_cb
);
3803 /* If it is the case, look if the current option is also */
3805 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3806 if (user_object
!= NULL
)
3808 bst_node
= bst_find(bst_seen_opt
, &bst
, seen_opt_compare
);
3810 if (bst_node
!= NULL
)
3812 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3813 if (bst_seen_opt
->seen
== 0)
3814 fatal(CTXOPTINCOPT
, (char *)user_object
);
3821 /* Mark this option as seen in the current context instance. */
3822 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3823 bst_seen_opt
->seen
= 1;
3824 free(bst_seen_opt
->par
);
3825 bst_seen_opt
->par
= xstrdup(par_name
);
3827 /* If this option leads to a next context, create a new ctx_inst */
3828 /* and switch to it for the analyse of the future parameter. */
3829 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3830 if (opt
->next_ctx
!= NULL
)
3832 ctx
= locate_ctx(opt
->next_ctx
);
3835 fatal_internal("Unknown context %s.", opt
->next_ctx
);
3837 opt_inst
->next_ctx_inst
= ctx_inst
= new_ctx_inst(ctx
, ctx_inst
);
3838 ctx_inst
->par_name
= xstrdup(par_name
);
3840 ll_append(ctx_inst_list
, ctx_inst
);
3844 "CTXOPT_DEBUG: Context change, "
3845 "new current context: %s.\n",
3849 /* Look is we must expect some arguments. */
3850 /* """""""""""""""""""""""""""""""""""""" */
3851 expect_par_or_arg
= 0;
3856 expect_par
= 1; /* Parameter doesn't accept any argument. */
3859 if (!opt
->optional_args
)
3860 expect_arg
= 1; /* Parameter has mandatory arguments. */
3862 expect_par_or_arg
= 1; /* Parameter has optional arguments. */
3866 else if (expect_par
&& *par_name
!= '-')
3868 ll_node_t
* n
= cli_node
->next
;
3870 if (!flags
.stop_if_non_option
)
3871 /* Look if potential arguments must still be analyzed until the */
3872 /* end of the context/command line part to analyze/command line. */
3873 /* If this is the case we have met an extra argument. */
3874 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3877 if (strcmp(n
->data
, "--") == 0 || strcmp(n
->data
, "\x1d") == 0)
3878 fatal(CTXOPTUNXARG
, "");
3880 if (*(char *)(n
->data
) == '-')
3881 fatal(CTXOPTUNXARG
, "");
3886 break; /* An unexpected non parameter was seen, if no Potential *
3887 | arguments remain in the command line or *
3888 | flags.stop_if_non_option is set, assume that it is is *
3889 | the first of the non arguments and stop the command *
3892 else if (expect_arg
&& *par_name
!= '-')
3894 ll_node_t
* cstr_node
;
3895 constraint_t
* cstr
;
3898 fprintf(stderr
, "CTXOPT_DEBUG: Argument: %s.\n", par_name
);
3900 /* Check if the arguments of the option respects */
3901 /* the attached constraints if any. */
3902 /* """"""""""""""""""""""""""""""""""""""""""""" */
3903 cstr_node
= opt
->constraints_list
->head
;
3904 while (cstr_node
!= NULL
)
3906 cstr
= cstr_node
->data
;
3907 if (!cstr
->constraint(cstr
->nb_args
, cstr
->args
, par_name
,
3908 cur_state
->cur_opt_par_name
))
3910 fputs("\n", stderr
);
3911 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, exit_after
);
3914 cstr_node
= cstr_node
->next
;
3917 /* If the argument is valid, store it. */
3918 /* """"""""""""""""""""""""""""""""""" */
3919 if (*par_name
== '\\' && *(par_name
+ 1) == '-')
3920 ll_append(opt_inst
->values_list
, par_name
+ 1);
3922 ll_append(opt_inst
->values_list
, par_name
);
3926 expect_par_or_arg
= 0;
3928 if (opt
->multiple_args
)
3929 expect_par_or_arg
= 1;
3931 expect_par
= 1; /* Parameter takes only one argument. */
3933 else if (expect_arg
&& *par_name
== '-')
3934 fatal(CTXOPTMISARG
, "");
3935 else if (expect_par_or_arg
)
3939 expect_par_or_arg
= 0;
3941 if (*par_name
!= '-')
3942 expect_arg
= 1; /* Consider this word as an argument and retry. */
3944 expect_par
= 1; /* Consider this word as a parameter and retry. */
3946 cli_node
= cli_node
->prev
;
3949 cli_node
= cli_node
->next
;
3952 if (cmdline_list
->len
> 0 && par_name
&& *par_name
== '-')
3954 if (expect_arg
&& opt
&& !opt
->optional_args
)
3955 fatal(CTXOPTMISARG
, "");
3958 /* Look if a context_instance has unseen mandatory options. */
3959 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3960 node
= ctx_inst_list
->head
;
3961 while (node
!= NULL
)
3963 ctx_inst
= node
->data
;
3965 /* Update current_state. */
3966 /* """"""""""""""""""""" */
3967 cur_state
->ctx_name
= ctx_inst
->ctx
->name
;
3968 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3970 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3971 check_for_occurrence_issues(ctx_inst
);
3972 check_for_requirement_issues(ctx_inst
);
3977 /* Allocate the array containing the remaining not analyzed */
3978 /* command line arguments. */
3979 /* NOTE: The strings in the array are just pointer to the */
3980 /* data of the generating list and must not be freed. */
3981 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3982 if (cli_node
!= NULL
)
3984 if (strcmp((char *)cli_node
->data
, "--") == 0)
3985 /* The special parameter -- was encountered, the -- argument is not */
3986 /* put in the remaining arguments. */
3987 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3988 ll_strarray(cmdline_list
, cli_node
->next
, nb_rem_args
, rem_args
);
3990 /* A non parameter was encountered when a parameter was expected. We */
3991 /* assume that the evaluation of the remaining command line argument */
3992 /* are not the responsibility of the users code. */
3993 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3994 ll_strarray(cmdline_list
, cli_node
, nb_rem_args
, rem_args
);
3999 *rem_args
= xmalloc(sizeof(char *));
4000 (*rem_args
)[0] = NULL
;
4004 /* ==================================================== */
4005 /* Free ctxopt memory used for its internal structures. */
4006 /* ==================================================== */
4008 ctxopt_free_memory(void)
4010 ll_destroy(cmdline_list
, free
);
4011 ll_destroy(ctx_inst_list
, ctx_inst_free
);
4012 bst_destroy(options_bst
, opt_free
);
4013 bst_destroy(contexts_bst
, ctx_free
);
4016 /* ==================================================================== */
4017 /* Parse the options data structures and launches the callback function */
4018 /* attached to each options instances. */
4019 /* This calls a recursive function which proceeds context per context. */
4020 /* ==================================================================== */
4022 ctxopt_evaluate(void)
4024 evaluate_ctx_inst(first_ctx_inst
);
4027 /* =================================================================== */
4028 /* Recursive function called by ctxopt_evaluate to process the list of */
4029 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
4030 /* action attached to the context and its option instances. */
4031 /* =================================================================== */
4033 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
)
4035 opt_inst_t
* opt_inst
;
4038 ll_node_t
* opt_inst_node
;
4042 if (ctx_inst
== NULL
)
4045 ctx
= ctx_inst
->ctx
;
4047 /* Do not evaluate the action attached to this context is there is no */
4048 /* option to evaluate. */
4049 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4050 opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
4051 if (opt_inst_node
== NULL
)
4054 /* Call the entering action attached to this context if any. */
4055 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4056 if (ctx
->action
!= NULL
)
4058 if (ctx_inst
->prev_ctx_inst
!= NULL
)
4059 ctx
->action(ctx
->name
, entering
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
4060 ctx
->nb_data
, ctx
->data
);
4062 ctx
->action(ctx
->name
, entering
, NULL
, ctx
->nb_data
, ctx
->data
);
4065 /* For each instance of options. */
4066 /* """"""""""""""""""""""""""""" */
4067 while (opt_inst_node
!= NULL
)
4069 opt_inst
= (opt_inst_t
*)(opt_inst_node
->data
);
4070 ll_strarray(opt_inst
->values_list
, opt_inst
->values_list
->head
, &nb_args
,
4072 opt
= opt_inst
->opt
;
4074 /* Launch the attached action if any. */
4075 /* """""""""""""""""""""""""""""""""" */
4076 if (opt
->action
!= NULL
)
4077 opt
->action(ctx
->name
, opt
->name
, opt_inst
->par
, nb_args
, args
,
4078 opt
->nb_data
, opt
->data
, ctx
->nb_data
, ctx
->data
);
4080 if (opt_inst
->next_ctx_inst
!= NULL
)
4081 evaluate_ctx_inst(opt_inst
->next_ctx_inst
);
4086 opt_inst_node
= opt_inst_node
->next
;
4089 /* Call the exiting action attached to this context if any. */
4090 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4091 if (ctx
->action
!= NULL
)
4093 if (ctx_inst
->prev_ctx_inst
!= NULL
)
4094 ctx
->action(ctx
->name
, exiting
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
4095 ctx
->nb_data
, ctx
->data
);
4097 ctx
->action(ctx
->name
, exiting
, NULL
, ctx
->nb_data
, ctx
->data
);
4101 /* ============================================================ */
4102 /* Create and initializes a new context. */
4103 /* - allocate space. */
4105 /* - initialize its option with a few of their characteristics. */
4106 /* ============================================================ */
4108 ctxopt_new_ctx(char * name
, char * opts_specs
)
4113 if (!ctxopt_initialized
)
4114 fatal_internal("Please call ctxopt_init first.");
4116 ctx
= xmalloc(sizeof(ctx_t
));
4118 /* Validates the context name: */
4119 /* ALPHA+(ALPHANUM|_)* */
4120 /* """"""""""""""""""""""""""" */
4123 fatal_internal("A context name must start with a letter: %s.", name
);
4128 if (!isalnum(*p
) && *p
!= '_')
4129 fatal_internal("A context name must only contain letters, "
4130 "numbers or '_': %s.",
4135 ctx
->name
= xstrdup(name
);
4136 ctx
->opt_list
= ll_new(); /* List of options legit in this context. */
4137 ctx
->incomp_list
= ll_new(); /* List of incompatible options strings. */
4138 ctx
->req_list
= ll_new(); /* List of opts/required opts tuples (str). */
4139 ctx
->par_bst
= NULL
;
4143 /* The first created context is the main one. */
4144 /* """""""""""""""""""""""""""""""""""""""""" */
4145 if (contexts_bst
== NULL
)
4149 cur_state
->ctx_name
= ctx
->name
;
4152 if (init_opts(opts_specs
, ctx
) == 0)
4154 if (bst_find(ctx
, &contexts_bst
, ctx_compare
) != NULL
)
4155 fatal_internal("The context %s already exists.", name
);
4157 bst_search(ctx
, &contexts_bst
, ctx_compare
);
4160 /* ==================================================== */
4161 /* Display a usage screen limited to a specific context */
4162 /* IN: the context name. */
4163 /* IN: what to do after (continue or exit the program) */
4164 /* possible values: continue_after, exit_after. */
4165 /* ==================================================== */
4167 ctxopt_ctx_disp_usage(char * ctx_name
, usage_behaviour action
)
4172 int has_optional
= 0;
4173 int has_ellipsis
= 0;
4175 int has_generic_arg
= 0;
4176 int has_ctx_change
= 0;
4177 int has_early_eval
= 0;
4179 if (!flags
.display_usage_on_error
)
4182 ctx
= locate_ctx(ctx_name
);
4184 fatal_internal("Unknown context %s.", ctx_name
);
4186 if (cur_state
->ctx_par_name
== NULL
)
4187 printf("\nSynopsis:\n%s \\\n", cur_state
->prog_name
);
4189 printf("\nSynopsis for the context introduced by %s:\n",
4190 cur_state
->ctx_par_name
);
4192 list
= ctx
->opt_list
;
4193 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
4194 &has_ctx_change
, &has_early_eval
);
4196 print_before_constraints(list
);
4198 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
4199 has_optional
, has_ellipsis
, has_rule
);
4201 if (action
== exit_after
)
4205 /* =================================================== */
4206 /* Display a full usage screen about all contexts. */
4207 /* IN: what to do after (continue or exit the program) */
4208 /* possible values: continue_after, exit_after. */
4209 /* =================================================== */
4211 ctxopt_disp_usage(usage_behaviour action
)
4214 int has_optional
= 0;
4215 int has_ellipsis
= 0;
4217 int has_generic_arg
= 0;
4218 int has_ctx_change
= 0;
4219 int has_early_eval
= 0;
4221 if (!flags
.display_usage_on_error
)
4224 if (main_ctx
== NULL
)
4225 fatal_internal("At least one context must have been created.");
4227 /* Usage for the first context. */
4228 /* """""""""""""""""""""""""""" */
4229 printf("\nAllowed options in the base context:\n");
4230 list
= main_ctx
->opt_list
;
4231 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
4232 &has_ctx_change
, &has_early_eval
);
4234 /* Dependency constraints between options. */
4235 /* """"""""""""""""""""""""""""""""""""""" */
4236 print_before_constraints(list
);
4238 /* Usage for the other contexts. */
4239 /* """"""""""""""""""""""""""""" */
4240 bst_walk(contexts_bst
, bst_print_ctx_cb
);
4242 /* Contextual syntactic explanations. */
4243 /* """""""""""""""""""""""""""""""""" */
4244 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
4245 has_optional
, has_ellipsis
, has_rule
);
4247 if (action
== exit_after
)
4251 /* *********************************** */
4252 /* Built-in constraint check functions */
4253 /* *********************************** */
4255 /* ============================================================= */
4256 /* This constraint checks if each arguments respects a format as */
4257 /* defined for the scanf function. */
4258 /* return 1 if yes and 0 if no. */
4259 /* ============================================================= */
4261 ctxopt_format_constraint(int nb_args
, char ** args
, char * value
, char * par
)
4270 fatal_internal("Format constraint, invalid number of parameters.");
4272 if (strlen(value
) > 255)
4275 format
= xstrdup(args
[0]);
4276 format
= strappend(format
, "%c", (char *)0);
4278 rc
= sscanf(value
, format
, x
, &y
);
4281 "The argument %s of %s does not respect the imposed format %s.",
4282 value
, par
, args
[0]);
4289 /* ================================================================== */
4290 /* This constraint checks if each arguments of the option instance is */
4291 /* between a minimum and a maximum (inclusive). */
4292 /* return 1 if yes and 0 if no. */
4293 /* ================================================================== */
4295 ctxopt_re_constraint(int nb_args
, char ** args
, char * value
, char * par
)
4301 "Regular expression constraint, invalid number of parameters.");
4303 if (regcomp(&re
, args
[0], REG_EXTENDED
) != 0)
4304 fatal_internal("Invalid regular expression %s.", args
[0]);
4306 if (regexec(&re
, value
, (size_t)0, NULL
, 0) != 0)
4309 "The argument %s of %s doesn't match the constraining "
4310 "regular expression %s.",
4311 value
, par
, args
[0]);
4320 /* ================================================================== */
4321 /* This constraint checks if each arguments of the option instance is */
4322 /* between a minimum and a maximum (inclusive). */
4323 /* return 1 if yes and 0 if no. */
4324 /* ================================================================== */
4326 ctxopt_range_constraint(int nb_args
, char ** args
, char * value
, char * par
)
4337 fatal_internal("Range constraint, invalid number of parameters.");
4339 if (strcmp(args
[0], ".") == 0)
4342 n
= sscanf(args
[0], "%ld%c", &min
, &c
);
4344 if (!max_only
&& n
!= 1)
4345 fatal_internal("Range constraint, min: invalid parameters.");
4347 if (strcmp(args
[1], ".") == 0)
4350 n
= sscanf(args
[1], "%ld%c", &max
, &c
);
4352 if (!min_only
&& n
!= 1)
4353 fatal_internal("Range constraint, max: invalid parameters.");
4355 if (min_only
&& max_only
)
4356 fatal_internal("Range constraint, invalid parameters.");
4359 v
= strtol(value
, &ptr
, 10);
4360 if (errno
|| ptr
== value
)
4368 "The argument %ld of %s is not greater than or equal to %ld.", v
,
4380 "The argument %ld of %s is not less than or equal to %ld.", v
,
4387 else if (v
< min
|| v
> max
)
4389 fprintf(stderr
, "The argument %ld of %s is not between %ld and %ld.", v
,
4394 return 1; /* check passed */
4397 /* =============================================================== */
4398 /* This function provides a way to set the behaviour of a context. */
4399 /* =============================================================== */
4401 ctxopt_add_global_settings(settings s
, ...)
4408 case error_functions
:
4410 typedef void fn(errors e
, state_t
* state
);
4412 void (*function
)(errors e
, state_t
* state
);
4415 e
= va_arg(args
, errors
);
4416 function
= va_arg(args
, fn
*);
4417 err_functions
[e
] = function
;
4427 /* ================================================================ */
4428 /* This function provides a way to set the behaviour of an option. */
4429 /* It can take a variable number of arguments according to its */
4430 /* first argument: */
4432 /* o a string containing an option name and all its possible */
4433 /* parameters separates by spaces, tabs or commas (char *) */
4434 /* (e.g: "help -h -help"). */
4436 /* o a string containing an option name. */
4437 /* o a pointer to a function which will be called at evaluation */
4439 /* - constraints: */
4440 /* o a string containing an option name. */
4441 /* o a pointer to a function to check if an argument is valid. */
4442 /* o a strings containing the arguments to this function. */
4443 /* ================================================================ */
4445 ctxopt_add_opt_settings(settings s
, ...)
4455 /* This part associates some command line parameters to an option. */
4456 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4462 /* The second argument must be a string containing: */
4463 /* - The name of an existing option. */
4464 /* - a list of parameters with a leading dash (-). */
4465 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4466 ptr
= va_arg(args
, char *);
4469 if (opt_name
!= NULL
)
4471 if ((opt
= locate_opt(opt_name
)) != NULL
)
4473 ptr
= va_arg(args
, char *);
4476 if (!opt_set_parms(opt_name
, params
))
4478 "Duplicated parameters or bad settings for the option %s.",
4482 fatal_internal("Unknown option %s.", opt_name
);
4486 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4488 /* Here opt is a known option. */
4489 /* """"""""""""""""""""""""""" */
4490 if (opt
->params
!= NULL
)
4491 fatal_internal("Parameters are already set for %s.", opt_name
);
4495 size_t l
= strlen(params
);
4497 opt
->params
= xstrdup(params
);
4498 while ((n
= strcspn(opt
->params
, " \t")) < l
)
4499 opt
->params
[n
] = '|';
4505 /* This part associates a callback function to an option. */
4506 /* This function will be called when an instance of an option */
4508 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4515 /* The second argument must be the name of an existing option. */
4516 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4517 ptr
= va_arg(args
, char *);
4519 if ((opt
= locate_opt(ptr
)) != NULL
)
4521 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4524 /* The third argument must be the callback function. */
4525 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4526 function
= va_arg(args
, fn
*);
4527 opt
->action
= function
;
4529 /* The fourth argument must be a pointer to an user's defined */
4530 /* variable or structure that the previous function can manage. */
4531 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4532 while ((data
= va_arg(args
, void *)) != NULL
)
4535 opt
->data
= xrealloc(opt
->data
, nb_data
* sizeof(void *));
4536 opt
->data
[nb_data
- 1] = data
;
4538 opt
->nb_data
= nb_data
;
4541 fatal_internal("Unknown option %s.", ptr
);
4545 /* This part associates a list of functions to control some */
4546 /* characteristics of the arguments of an option. */
4547 /* Each function will be called in order and must return 1 */
4548 /* to validate the arguments. */
4549 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4553 constraint_t
* cstr
;
4556 /* The second argument must be a string. */
4557 /* """"""""""""""""""""""""""""""""""""" */
4558 ptr
= va_arg(args
, char *);
4560 if ((opt
= locate_opt(ptr
)) != NULL
)
4562 typedef int fn(int, char **, char *);
4564 /* The third argument must be a function. */
4565 /* """""""""""""""""""""""""""""""""""""" */
4566 function
= va_arg(args
, fn
*);
4568 cstr
= xmalloc(sizeof(constraint_t
));
4569 cstr
->constraint
= function
;
4571 /* The fourth argument must be a string containing the argument of */
4572 /* The previous function separated by spaces or tabs. */
4573 /* Theses arguments will be passed to the previous function */
4574 /* max: 32 argument! */
4575 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4576 value
= xstrdup(va_arg(args
, char *));
4578 cstr
->to_free
= value
;
4579 cstr
->args
= xcalloc(sizeof(char *), 32);
4580 cstr
->nb_args
= str2argv(value
, cstr
->args
, 32);
4581 ll_append(opt
->constraints_list
, cstr
);
4584 fatal_internal("Unknown option %s.", ptr
);
4588 /* This part allows to indicate that an option must be evaluated */
4589 /* after a list of other options. */
4590 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4595 /* The second argument must be a string. */
4596 /* """"""""""""""""""""""""""""""""""""" */
4597 ptr
= va_arg(args
, char *);
4599 if ((opt
= locate_opt(ptr
)) != NULL
)
4605 ptr
= va_arg(args
, char *);
4609 rtrim(str
, " \t", 0);
4611 /* Feed the list of options to be evaluated after the given option. */
4612 /* This list will contain pointers to options. */
4613 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4614 opt_name
= xstrtok_r(str
, " \t,", &end_str
);
4615 if (opt_name
!= NULL
)
4617 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4619 ll_append(opt
->eval_before_list
, opt_before
);
4620 while ((opt_name
= xstrtok_r(NULL
, " \t,", &end_str
)) != NULL
)
4622 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4623 ll_append(opt
->eval_before_list
, opt_before
);
4625 fatal_internal("Unknown option %s.", opt_name
);
4629 fatal_internal("Unknown option %s.", opt_name
);
4632 fatal_internal("Not enough options to be evaluated after %s.",
4638 fatal_internal("Unknown option %s.", ptr
);
4643 /* This part allows to indicate that an option must be evaluated */
4644 /* before a list of other options. */
4645 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4650 /* The second argument must be a string. */
4651 /* """"""""""""""""""""""""""""""""""""" */
4652 ptr
= va_arg(args
, char *);
4654 if ((opt
= locate_opt(ptr
)) != NULL
)
4660 ptr
= va_arg(args
, char *);
4664 rtrim(str
, " \t", 0);
4666 /* Feed the list of options to be evaluated before the given option. */
4667 /* This list will contain pointers to options. */
4668 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4669 opt_name
= xstrtok_r(str
, " \t,", &end_str
);
4670 if (opt_name
!= NULL
)
4672 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4674 ll_append(opt_before
->eval_before_list
, opt
);
4675 while ((opt_name
= xstrtok_r(NULL
, " \t,", &end_str
)) != NULL
)
4677 if ((opt_before
= locate_opt(opt_name
)) != NULL
)
4678 ll_append(opt_before
->eval_before_list
, opt
);
4680 fatal_internal("Unknown option %s.", opt_name
);
4684 fatal_internal("Unknown option %s.", opt_name
);
4687 fatal_internal("Not enough options to be evaluated before %s.",
4693 fatal_internal("Unknown option %s.", ptr
);
4704 /* =============================================================== */
4705 /* This function provides a way to set the behaviour of a context. */
4706 /* =============================================================== */
4708 ctxopt_add_ctx_settings(settings s
, ...)
4717 /* Add a set of mutually incompatible options in a context. */
4718 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4719 case incompatibilities
:
4726 ptr
= va_arg(args
, char *);
4727 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4729 ptr
= va_arg(args
, char *);
4730 list
= ctx
->incomp_list
;
4734 rtrim(str
, " \t", 0);
4736 n
= strcspn(str
, " \t");
4737 if (n
> 0 && n
< strlen(str
))
4738 ll_append(list
, str
);
4741 "Not enough incompatible options in the string: \"%s\".", str
);
4744 fatal_internal("Unknown context %s.", ptr
);
4755 ptr
= va_arg(args
, char *);
4756 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4758 ptr
= va_arg(args
, char *);
4759 list
= ctx
->req_list
;
4763 rtrim(str
, " \t", 0);
4765 n
= strcspn(str
, " \t");
4766 if (n
> 0 && n
< strlen(str
))
4767 ll_append(list
, str
);
4769 fatal_internal("Not enough required options in the string: \"%s\".",
4773 fatal_internal("Unknown context %s.", ptr
);
4777 /* Add functions which will be called when */
4778 /* entering and exiting a context. */
4779 /* """"""""""""""""""""""""""""""""""""""" */
4787 ptr
= va_arg(args
, char *);
4788 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4790 typedef int fn(char *, direction
, char *, int, void **);
4792 function
= va_arg(args
, fn
*);
4793 ctx
->action
= function
;
4795 while ((data
= va_arg(args
, void *)) != NULL
)
4798 ctx
->data
= xrealloc(ctx
->data
, nb_data
* sizeof(void *));
4799 ctx
->data
[nb_data
- 1] = data
;
4801 ctx
->nb_data
= nb_data
;
4804 fatal_internal("Unknown context %s.", ptr
);