1 /* ########################################################### */
2 /* This Software is licensed under the GPL licensed Version 2, */
3 /* please read http://www.gnu.org/copyleft/gpl.html */
4 /* ########################################################### */
12 #include <sys/types.h>
18 /* ************************ */
19 /* Static global variables. */
20 /* ************************ */
22 static void * contexts_bst
;
23 static void * options_bst
;
29 /* ************************** */
30 /* Fatal messages prototypes. */
31 /* ************************** */
33 static void (**err_functions
)(errors e
, state_t
* state
);
36 fatal_internal(const char * format
, ...);
39 fatal(errors e
, char * errmsg
);
41 static int user_rc
; /* Used by various callback functions */
42 static int user_value
; /* Used by various callback functions */
43 static char * user_string
; /* Used by various callback functions */
44 static char * user_string2
; /* Used by various callback functions */
45 static void * user_object
; /* Used by various callback functions */
47 /* ************************************ */
48 /* Memory management static prototypes. */
49 /* ************************************ */
55 xcalloc(size_t num
, size_t size
);
58 xrealloc(void * ptr
, size_t size
);
61 xstrdup(const char * p
);
64 xstrndup(const char * str
, size_t len
);
66 /* ********************** */
67 /* BST static prototypes. */
68 /* ********************** */
70 typedef struct bst_s bst_t
;
81 bst_delete(const void * vkey
, void ** vrootp
,
82 int (*compar
)(const void *, const void *));
85 bst_destroy(void * vrootp
, void (*clean
)(void *));
88 bst_find(const void * vkey
, void * const * vrootp
,
89 int (*compar
)(const void *, const void *));
92 bst_search(void * vkey
, void ** vrootp
,
93 int (*compar
)(const void *, const void *));
96 bst_walk_recurse(const bst_t
* root
,
97 void (*action
)(const void *, walk_order_e
, int), int level
);
100 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int));
102 /* ****************************** */
103 /* Linked list static prototypes. */
104 /* ****************************** */
106 typedef struct ll_node_s ll_node_t
;
107 typedef struct ll_s ll_t
;
110 ll_append(ll_t
* const list
, void * const data
);
113 ll_prepend(ll_t
* const list
, void * const data
);
116 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
);
119 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
);
122 ll_delete(ll_t
* const list
, ll_node_t
* node
);
124 #if 0 /* Unused yet */
126 ll_find(ll_t
* const, void * const, int (*)(const void *, const void *));
130 ll_init(ll_t
* list
);
139 ll_free(ll_t
* const list
, void (*)(void *));
142 ll_destroy(ll_t
* const list
, void (*)(void *));
145 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
);
147 /* ************************** */
148 /* Various static prototypes. */
149 /* ************************** */
152 ltrim(char * str
, const char * trim_str
);
155 rtrim(char * str
, const char * trim_str
, size_t min
);
158 strchrcount(char * str
, char c
);
161 strpref(char * s1
, char * s2
);
164 xstrtok_r(char * str
, const char * delim
, char ** end
);
166 /* ************************* */
167 /* ctxopt static prototypes. */
168 /* ************************* */
170 typedef struct opt_s opt_t
;
171 typedef struct par_s par_t
;
172 typedef struct ctx_s ctx_t
;
173 typedef struct constraint_s constraint_t
;
174 typedef struct ctx_inst_s ctx_inst_t
;
175 typedef struct opt_inst_s opt_inst_t
;
176 typedef struct seen_opt_s seen_opt_t
;
179 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
);
182 ctx_compare(const void * c1
, const void * c2
);
188 ctx_inst_free(void * ci
);
191 opt_inst_free(void * oi
);
194 seen_opt_compare(const void * so1
, const void * so2
);
197 incomp_bst_free(void * b
);
200 seen_opt_free(void * seen_opt
);
203 opt_compare(const void * o1
, const void * o2
);
209 par_compare(const void * a1
, const void * a2
);
215 constraint_free(void * cstr
);
218 locate_ctx(char * name
);
221 locate_opt(char * name
);
224 locate_par(char * name
, ctx_t
* ctx
);
227 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
228 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
229 int * has_early_eval
);
231 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
232 int has_optional
, int has_ellipsis
, int has_rule
);
234 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
);
237 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
);
240 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
);
243 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
);
246 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
);
249 match_prefix_cb(const void * node
, walk_order_e kind
, int level
);
252 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
);
255 opt_parse(char * s
, opt_t
** opt
);
258 init_opts(char * spec
, ctx_t
* ctx
);
261 ctxopt_build_cmdline_list(int nb_words
, char ** words
);
264 opt_set_parms(char * opt_name
, char * par_str
);
267 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
);
270 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
);
272 /* ****************************** */
273 /* Fatal messages implementation. */
274 /* ****************************** */
276 /* ================================================================== */
277 /* Fatal error function used when a fatal condition is encountered. */
278 /* This function is reserved for the ctxopt internal usage. */
280 /* format : printf like format */
281 /* ... : remaining arguments interpreted using the format argument */
282 /* ================================================================== */
284 fatal_internal(const char * format
, ...)
288 fprintf(stderr
, "CTXOPT: ");
290 va_start(args
, format
);
291 vfprintf(stderr
, format
, args
);
292 fprintf(stderr
, "\n");
298 /* ====================================================================== */
299 /* Generic fatal error function. This one uses the global status ctxopt */
300 /* stored in the cur_state structure and can call custom error functions. */
301 /* registered by the users for a given error identifier if any. */
303 /* e : Error identifier responsible of the fatal error */
304 /* errmsg : Users's provided string specific to the error e */
305 /* Note that errmsg is not used in all cases */
307 /* CTXOPTMISPAR Missing parameter */
308 /* CTXOPTMISARG Missing argument */
309 /* CTXOPTUXPARG Unexpected argument */
310 /* CTXOPTDUPOPT Duplicated option */
311 /* CTXOPTUNKPAR Unknown parameter */
312 /* CTXOPTINCOPT Incompatible option */
313 /* CTXOPTCTEOPT Option: bad number of occurrences */
314 /* CTXOPTCTLOPT Option: not enough occurrences */
315 /* CTXOPTCTGOPT Option: too many occurrence of */
316 /* CTXOPTCTEARG Arguments: bad number of occurrences */
317 /* CTXOPTCTLARG Arguments: not enough occurrences */
318 /* CTXOPTCTGARG Arguments: too many occurrences */
319 /* ====================================================================== */
321 fatal(errors e
, char * errmsg
)
323 if (err_functions
[e
] != NULL
)
324 err_functions
[e
](e
, cur_state
);
333 if (cur_state
->ctx_par_name
!= NULL
)
335 "Mandatory parameter(s): %s are missing in the context "
336 "introduced by %s.\n",
337 errmsg
, cur_state
->ctx_par_name
);
340 "Mandatory parameter(s): %s are missing "
341 "in the main context.\n",
348 fprintf(stderr
, "%s only takes one argument.\n",
349 cur_state
->cur_opt_par_name
);
353 if (cur_state
->pre_opt_par_name
!= NULL
)
354 fprintf(stderr
, "%s requires argument(s).\n",
355 cur_state
->pre_opt_par_name
);
357 fprintf(stderr
, "%s requires argument(s).\n",
358 cur_state
->cur_opt_par_name
);
362 if (cur_state
->pre_opt_par_name
!= NULL
)
364 "The parameter(s) %s can only appear once in the context "
365 "introduced by %s.\n",
366 cur_state
->cur_opt_par_name
, cur_state
->ctx_par_name
);
369 "The parameter(s) %s can only appear once "
370 "in the main context.\n",
371 cur_state
->cur_opt_par_name
);
375 fprintf(stderr
, "Unknown parameter: %s.\n",
376 cur_state
->cur_opt_par_name
);
380 fprintf(stderr
, "%s is incompatible with %s.\n",
381 cur_state
->cur_opt_par_name
, errmsg
);
385 if (cur_state
->ctx_par_name
)
387 "%s must appear exactly %u times in the context "
388 "introduced by %s.\n",
389 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
390 cur_state
->ctx_par_name
);
393 "%s must appear exactly %u times in "
394 "the main context.\n",
395 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
399 if (cur_state
->ctx_par_name
)
401 "%s must appear less than %u times in the context "
402 "introduced by %s.\n",
403 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
404 cur_state
->ctx_par_name
);
407 "%s must appear less than %u times in the main context.\n",
408 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
412 if (cur_state
->ctx_par_name
)
414 "%s must appear more than %u times in the context "
415 "introduced by %s.\n",
416 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
417 cur_state
->ctx_par_name
);
420 "%s must appear more than %u times in the main context.\n",
421 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
425 fprintf(stderr
, "%s must have exactly %u arguments.\n",
426 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
430 fprintf(stderr
, "%s must have less than %u arguments.\n",
431 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
435 fprintf(stderr
, "%s must have more than %u arguments.\n",
436 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
444 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, continue_after
);
446 exit(e
); /* Exit with the error id e as return code. */
449 /* ********************************* */
450 /* Memory management implementation. */
451 /* ********************************* */
453 /* ================== */
454 /* Customized malloc. */
455 /* ================== */
462 real_size
= (size
> 0) ? size
: 1;
463 allocated
= malloc(real_size
);
464 if (allocated
== NULL
)
465 fatal_internal("Insufficient memory (attempt to malloc %lu bytes)\n",
466 (unsigned long int)size
);
471 /* ================== */
472 /* Customized calloc. */
473 /* ================== */
475 xcalloc(size_t n
, size_t size
)
480 size
= (size
> 0) ? size
: 1;
481 allocated
= calloc(n
, size
);
482 if (allocated
== NULL
)
483 fatal_internal("Insufficient memory (attempt to calloc %lu bytes)\n",
484 (unsigned long int)size
);
489 /* =================== */
490 /* Customized realloc. */
491 /* =================== */
493 xrealloc(void * p
, size_t size
)
497 allocated
= realloc(p
, size
);
498 if (allocated
== NULL
&& size
> 0)
499 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes)\n",
500 (unsigned long int)size
);
505 /* ==================================== */
506 /* strdup implementation using xmalloc. */
507 /* ==================================== */
509 xstrdup(const char * p
)
513 allocated
= xmalloc(strlen(p
) + 1);
514 strcpy(allocated
, p
);
519 /* =================================================== */
520 /* strndup implementation using xmalloc. */
521 /* This version guarantees that there is a final '\0'. */
522 /* =================================================== */
524 xstrndup(const char * str
, size_t len
)
528 p
= memchr(str
, '\0', len
);
533 p
= xmalloc(len
+ 1);
540 /* *************************** */
541 /* Linked list implementation. */
542 /* *************************** */
544 /* Linked list node structure. */
545 /* """"""""""""""""""""""""""" */
549 struct ll_node_s
* next
;
550 struct ll_node_s
* prev
;
553 /* Linked List structure. */
554 /* """""""""""""""""""""" */
562 /* ========================= */
563 /* Create a new linked list. */
564 /* ========================= */
568 ll_t
* ret
= xmalloc(sizeof(ll_t
));
574 /* =============================================== */
575 /* Free all the elements of a list (make it empty) */
576 /* NULL or a custom function may be used to free */
577 /* the sub components of the elements. */
578 /* =============================================== */
580 ll_free(ll_t
* const list
, void (*clean
)(void *))
587 /* Apply a custom cleaner if not NULL. */
588 /* """"""""""""""""""""""""""""""""""" */
590 clean(list
->head
->data
);
592 ll_delete(list
, list
->head
);
596 /* ==================================== */
597 /* Destroy a list and all its elements. */
598 /* ==================================== */
600 ll_destroy(ll_t
* list
, void (*clean
)(void *))
604 ll_free(list
, clean
);
609 /* ========================= */
610 /* Initialize a linked list. */
611 /* ========================= */
620 /* ===================================================== */
621 /* Allocate the space for a new node in the linked list. */
622 /* ===================================================== */
626 ll_node_t
* ret
= xmalloc(sizeof(ll_node_t
));
631 /* ==================================================================== */
632 /* Append a new node filled with its data at the end of the linked list */
633 /* The user is responsible for the memory management of the data. */
634 /* ==================================================================== */
636 ll_append(ll_t
* const list
, void * const data
)
640 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
641 | uses xmalloc which does not return if there *
642 | is an allocation error. */
647 node
->prev
= list
->tail
;
649 list
->tail
->next
= node
;
658 /* ================================================================== */
659 /* Put a new node filled with its data at the beginning of the linked */
661 /* The user is responsible for the memory management of the data. */
662 /* ================================================================== */
664 ll_prepend(ll_t
* const list
, void * const data
)
668 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
669 | uses xmalloc which does not return if there *
670 | is an allocation error. */
675 node
->next
= list
->head
;
677 list
->head
->prev
= node
;
686 /* ======================================================== */
687 /* Insert a new node before the specified node in the list. */
688 /* ======================================================== */
690 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
)
692 ll_node_t
* new_node
;
694 if (node
->prev
== NULL
)
695 ll_prepend(list
, data
);
698 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
699 | uses xmalloc which does not return if there *
700 | is an allocation error. */
702 new_node
->data
= data
;
703 new_node
->next
= node
;
704 new_node
->prev
= node
->prev
;
705 node
->prev
->next
= new_node
;
706 node
->prev
= new_node
;
712 /* ======================================================= */
713 /* Insert a new node after the specified node in the list. */
714 /* ======================================================= */
716 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
)
718 ll_node_t
* new_node
;
720 if (node
->next
== NULL
)
721 ll_append(list
, data
);
724 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
725 | uses xmalloc which does not return if there *
726 | is an allocation error. */
728 new_node
->data
= data
;
729 new_node
->prev
= node
;
730 new_node
->next
= node
->next
;
731 node
->next
->prev
= new_node
;
732 node
->next
= new_node
;
738 /* ================================================================= */
739 /* Remove a node from a linked list */
740 /* The memory taken by the deleted node must be freed by the caller. */
741 /* ================================================================= */
743 ll_delete(ll_t
* const list
, ll_node_t
* node
)
745 if (list
->head
== list
->tail
)
747 if (list
->head
!= NULL
)
748 list
->head
= list
->tail
= NULL
;
752 else if (node
->prev
== NULL
)
754 list
->head
= node
->next
;
755 list
->head
->prev
= NULL
;
757 else if (node
->next
== NULL
)
759 list
->tail
= node
->prev
;
760 list
->tail
->next
= NULL
;
764 node
->next
->prev
= node
->prev
;
765 node
->prev
->next
= node
->next
;
775 #if 0 /* Unused yet */
776 /* ======================================================================== */
777 /* Find a node in the list containing data. Return the node pointer or NULL */
779 /* A comparison function must be provided to compare a and b (strcmp like). */
780 /* ======================================================================== */
782 ll_find(ll_t
* const list
, void * const data
,
783 int (*cmpfunc
)(const void * a
, const void * b
))
787 if (NULL
== (node
= list
->head
))
792 if (0 == cmpfunc(node
->data
, data
))
794 } while (NULL
!= (node
= node
->next
));
800 /* ==================================================================== */
801 /* Allocate and fill an array of strings from a list. */
803 /* 1) The list node must contain strings (char *) */
804 /* 2) The strings in the resulting array MUST NOT be freed as the are */
805 /* NOT copied from the strings of the list. */
807 /* IN list : The list from which the array is generated */
808 /* IN start_node : The node of the list which will be the first node to */
809 /* consider to create the array */
810 /* OUT: count : The number of elements of the resulting array. */
811 /* OUT: array : The resulting array or NULL if the list is empty. */
812 /* RC : : The number of elements of the resulting array. */
813 /* ==================================================================== */
815 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
)
824 if (list
== NULL
|| node
== NULL
)
831 *array
= xmalloc((list
->len
+ 1) * sizeof(char *));
834 (*array
)[n
++] = (char *)(node
->data
);
840 (*array
)[*count
] = NULL
;
845 /* ******************************************************************* */
846 /* BST (search.h compatible) implementation. */
848 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
849 /* the AT&T man page says. */
851 /* Written by reading the System V Interface Definition, not the code. */
853 /* Totally public domain. */
854 /* ******************************************************************* */
859 struct bst_s
* llink
;
860 struct bst_s
* rlink
;
863 #if 0 /* Unused yet */
864 /* =========================== */
865 /* Delete node with given key. */
866 /* =========================== */
868 bst_delete(const void * vkey
, void ** vrootp
,
869 int (*compar
)(const void *, const void *))
871 bst_t
** rootp
= (bst_t
**)vrootp
;
875 if (rootp
== NULL
|| (p
= *rootp
) == NULL
)
878 while ((cmp
= (*compar
)(vkey
, (*rootp
)->key
)) != 0)
881 rootp
= (cmp
< 0) ? &(*rootp
)->llink
/* follow llink branch */
882 : &(*rootp
)->rlink
; /* follow rlink branch */
884 return NULL
; /* key not found */
886 r
= (*rootp
)->rlink
; /* D1: */
887 if ((q
= (*rootp
)->llink
) == NULL
) /* Left NULL? */
890 { /* Right link is NULL? */
891 if (r
->llink
== NULL
)
892 { /* D2: Find successor */
897 { /* D3: Find NULL link */
898 for (q
= r
->llink
; q
->llink
!= NULL
; q
= r
->llink
)
901 q
->llink
= (*rootp
)->llink
;
902 q
->rlink
= (*rootp
)->rlink
;
906 free(*rootp
); /* D4: Free node */
907 *rootp
= q
; /* link parent to new node */
912 /* ===================================================================== */
913 /* Destroy a tree. */
914 /* The clean function pointer can be NULL, in this case the node content */
916 /* ===================================================================== */
918 bst_destroy(void * vrootp
, void (*clean
)(void *))
920 bst_t
* root
= (bst_t
*)vrootp
;
925 bst_destroy(root
->llink
, clean
);
926 bst_destroy(root
->rlink
, clean
);
929 clean((void *)root
->key
);
934 /* ========================= */
935 /* Find a node, or return 0. */
936 /* ========================= */
938 bst_find(const void * vkey
, void * const * vrootp
,
939 int (*compar
)(const void *, const void *))
941 bst_t
* const * rootp
= (bst_t
* const *)vrootp
;
946 while (*rootp
!= NULL
)
950 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
951 return *rootp
; /* key found */
952 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
953 : &(*rootp
)->rlink
; /* T4: follow right branch */
958 /* ======================================= */
959 /* Find or inserts datum into search tree. */
960 /* ======================================= */
962 bst_search(void * vkey
, void ** vrootp
,
963 int (*compar
)(const void *, const void *))
966 bst_t
** rootp
= (bst_t
**)vrootp
;
971 while (*rootp
!= NULL
)
975 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
976 return *rootp
; /* we found it! */
978 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
979 : &(*rootp
)->rlink
; /* T4: follow right branch */
982 q
= xmalloc(sizeof(bst_t
)); /* T5: key not found */
984 { /* make new node */
985 *rootp
= q
; /* link new node to old */
986 q
->key
= vkey
; /* initialize new node */
987 q
->llink
= q
->rlink
= NULL
;
992 /* ========================= */
993 /* Walk the nodes of a tree. */
994 /* ========================= */
996 bst_walk_recurse(const bst_t
* root
,
997 void (*action
)(const void *, walk_order_e
, int), int level
)
999 if (root
->llink
== NULL
&& root
->rlink
== NULL
)
1000 (*action
)(root
, leaf
, level
);
1003 (*action
)(root
, preorder
, level
);
1004 if (root
->llink
!= NULL
)
1005 bst_walk_recurse(root
->llink
, action
, level
+ 1);
1006 (*action
)(root
, postorder
, level
);
1007 if (root
->rlink
!= NULL
)
1008 bst_walk_recurse(root
->rlink
, action
, level
+ 1);
1009 (*action
)(root
, endorder
, level
);
1014 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int))
1016 if (vroot
!= NULL
&& action
!= NULL
)
1017 bst_walk_recurse(vroot
, action
, 0);
1020 /* ************************ */
1021 /* Various implementations. */
1022 /* ************************ */
1024 /* ======================== */
1025 /* Trim leading characters. */
1026 /* ======================== */
1028 ltrim(char * str
, const char * trim_str
)
1030 size_t len
= strlen(str
);
1031 size_t begin
= strspn(str
, trim_str
);
1035 for (i
= begin
; i
<= len
; ++i
)
1036 str
[i
- begin
] = str
[i
];
1039 /* ================================================= */
1040 /* Trim trailing characters. */
1041 /* The resulting string will have at least min bytes */
1042 /* even if trailing spaces remain. */
1043 /* ================================================= */
1045 rtrim(char * str
, const char * trim_str
, size_t min
)
1047 size_t len
= strlen(str
);
1048 while (len
> min
&& strchr(trim_str
, str
[len
- 1]))
1052 /* ================================================== */
1053 /* Count the number of occurrences of the character c */
1054 /* in the string str. */
1055 /* The str pointer is assumed to be not NULL. */
1056 /* ================================================== */
1058 strchrcount(char * str
, char c
)
1069 /* =============================================== */
1070 /* Is the string str2 a prefix of the string str1? */
1071 /* =============================================== */
1073 strpref(char * str1
, char * str2
)
1075 while (*str1
!= '\0' && *str1
== *str2
)
1081 return *str2
== '\0';
1084 /* ======================================================================== */
1085 /* Strings concatenation with dynamic memory allocation. */
1086 /* IN : a variable number of char * arguments with NULL terminating */
1088 /* The first one must have been dynamically allocated and is mandatory */
1090 /* Returns a new allocated string containing the concatenation of all */
1091 /* the arguments. It is the caller's responsibility to free the resulting */
1093 /* ======================================================================== */
1095 strappend(char * str
, ...)
1101 l
= 1 + strlen(str
);
1102 va_start(args
, str
);
1104 s
= va_arg(args
, char *);
1109 s
= va_arg(args
, char *);
1114 str
= xrealloc(str
, l
);
1116 va_start(args
, str
);
1117 s
= va_arg(args
, char *);
1122 s
= va_arg(args
, char *);
1129 /* ====================================================================== */
1130 /* Public domain strtok_r() by Charlie Gordon. */
1131 /* from comp.lang.c 9/14/2007 */
1132 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1134 /* (Declaration that it's public domain): */
1135 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1137 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1139 /* ====================================================================== */
1141 xstrtok_r(char * str
, const char * delim
, char ** end
)
1151 str
+= strspn(str
, delim
);
1158 str
+= strcspn(str
, delim
);
1168 /* =========================================================== */
1169 /* Fill an array of strings from the words composing a string. */
1171 /* str: initial string which will be altered. */
1172 /* args: array of pointers to the start of the words in str. */
1173 /* max: maximum number of words used before giving up. */
1174 /* return: the number of words (<=max). */
1175 /* =========================================================== */
1177 str2argv(char * str
, char ** args
, int max
)
1186 while (*str
== ' ' || *str
== '\t')
1192 args
[nb_args
] = str
;
1195 while (*str
&& (*str
!= ' ') && (*str
!= '\t'))
1202 /* ********************** */
1203 /* ctxopt implementation. */
1204 /* ********************** */
1206 static int ctxopt_initialized
= 0; /* cap_init has not yet been called */
1208 /* Context structure. */
1209 /* """""""""""""""""" */
1213 ll_t
* opt_list
; /* list of options allowed in this context. */
1214 ll_t
* incomp_list
; /* list of strings containing incompatible names *
1215 | of options separated by spaces or tabs. */
1216 int (*action
)(char * name
, int type
, char * new_ctx
, int ctx_nb_data
,
1223 /* https://textik.com/#488ce3649b6c60f5 */
1225 /* +--------------+ */
1226 /* |first_ctx_inst| */
1227 /* +---+----------+ */
1229 /* +--v-----+ +--------+ +--------+ +-----+ */
1230 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1231 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1233 /* | | +-v------+ | | */
1234 /* | +--+ctx_inst<-----------+ | */
1235 /* | +-+------+ | */
1237 /* | +-v------+ | */
1238 /* +------+ctx_inst<--------------------------+ */
1245 /* Option structure. */
1246 /* """"""""""""""""" */
1249 char * name
; /* option name. */
1250 char * next_ctx
; /* new context this option may lead to */
1251 ll_t
* ctx_list
; /* list of contexts allowing this option. */
1252 char * params
; /* string containing all the parameters of *
1255 void (*action
)( /* The option associated action. */
1256 char * ctx_name
, /* context name. */
1257 char * opt_name
, /* option name. */
1258 char * par
, /* option parameter. */
1259 int nb_args
, /* number of arguments. */
1260 char ** args
, /* option arguments. */
1261 int nb_opt_data
, /* number of option data pointers. */
1262 void ** opt_data
, /* option data pointers. */
1263 int nb_ctx_data
, /* nb of current context data ptrs. */
1264 void ** ctx_data
/* current context data pointers. */
1267 int nb_data
; /* number of the data pointers passed as argument to action. */
1268 void ** data
; /* array of data pointers passed as argument to action. */
1270 int args
; /* 1 if this option takes arguments else 0. */
1271 int optional
; /* 1 if the option is optional, else 0. */
1272 int multiple
; /* 1 if the option can appear more than one time in a *
1273 | context, else 0. */
1275 int opt_count_matter
; /* 1 if we must restrict the count, else 0. */
1276 int occurrences
; /* Number of option occurrences in a context. */
1277 char opt_count_oper
; /* <, = or > */
1278 unsigned opt_count_mark
; /* Value to be compared to with opt_count_oper. */
1280 char * arg
; /* symbolic text after # describing the option argument. */
1282 int optional_args
; /* 1 of option is optional else 0. */
1283 int multiple_args
; /* 1 is option can appear more than once in a context *
1286 int opt_args_count_matter
; /* 1 if count is rescticted, else 0. */
1287 char opt_args_count_oper
; /* <, = or > */
1288 unsigned opt_args_count_mark
; /* Value to be compared to with *
1289 | opt_count_oper. */
1291 int eval_first
; /* 1 if this option must be evaluated before the options *
1292 | without this mark. */
1294 ll_t
* constraints_list
; /* List of constraint check functions pointers. */
1297 /* Context instance structure. */
1298 /* """"""""""""""""""""""""""" */
1301 ctx_t
* ctx
; /* the context whose this is an instance of */
1302 ctx_inst_t
* prev_ctx_inst
; /* ctx_inst of the opt_inst which led to the *
1303 | creation of this ctx_inst structure. */
1304 opt_inst_t
* gen_opt_inst
; /* opt_inst which led to the creation of a *
1305 | instance of this structure. */
1306 ll_t
* incomp_bst_list
; /* list of seen_opt_t BST. */
1307 void * seen_opt_bst
; /* tree of seen_opt_t. */
1308 ll_t
* opt_inst_list
; /* The list of option instances in this *
1309 | context instance. */
1310 char * par_name
; /* parameter which created this instance. */
1313 /* Option instance structure. */
1314 /* """""""""""""""""""""""""" */
1317 opt_t
* opt
; /* The option this is an instance of. */
1318 char * opt_name
; /* The option which led to this creation. */
1319 char * par
; /* The parameter which led to this creation. */
1320 ll_t
* values_list
; /* The list of arguments of this option. */
1321 ctx_inst_t
* next_ctx_inst
; /* The new context instance this option. *
1322 | instance may create. */
1325 /* Structure used to check if an option has bee seen or not */
1326 /* in a context instance. */
1327 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1330 opt_t
* opt
; /* The concerned option. */
1331 char * par
; /* Parameter which led to the making of this structure. */
1332 int seen
; /* 1 if seen in the context instances, else 0. */
1335 /* Parameter structure which links a parameter to the option it belongs to. */
1336 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1339 char * name
; /* Parameter name (with the leading -). */
1340 opt_t
* opt
; /* Attached option. */
1343 /* Constraint structure. */
1344 /* """"""""""""""""""""" */
1347 int (*constraint
)(int nb_args
, char ** args
, char * value
, char * parameter
);
1350 char * to_free
; /* pointer to the original string in which the array in *
1351 | args points to. This poinnter is kept there to allow *
1352 | it to be freed. */
1355 state_t
* cur_state
= NULL
; /* Current analysis state. */
1356 static ll_t
* cmdline_list
; /* List of interpreted CLI words *
1357 | serves as the basis for the *
1358 | analysis of the parameters. */
1359 static ctx_t
* main_ctx
= NULL
; /* initial context. */
1360 static ctx_inst_t
* first_ctx_inst
= NULL
; /* Pointer to the fist context *
1361 | instance which holds the *
1362 | options instances. */
1363 static ll_t
* ctx_inst_list
= NULL
; /* List of the context instances. */
1365 /* ======================================================= */
1366 /* Parse a string for the next matching token. */
1368 /* s: string to parse. */
1369 /* token: pre_allocated array of max tok_len characters. */
1370 /* pattern: scanf type pattern token must match. */
1371 /* pos: number of characters successfully parsed in s. */
1373 /* Returns: a pointer to the first unread character or */
1374 /* to he terminating \0. */
1375 /* ======================================================= */
1377 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
)
1379 char * full_pattern
;
1385 n
= snprintf(len
, 3, "%zu", tok_len
);
1389 full_pattern
= xmalloc(strlen(pattern
) + n
+ 4);
1391 strcpy(full_pattern
, "%");
1392 strcat(full_pattern
, len
);
1393 strcat(full_pattern
, pattern
);
1394 strcat(full_pattern
, "%n");
1396 n
= sscanf(s
, full_pattern
, token
, pos
);
1406 /* ****************************************** */
1407 /* Various comparison and deletion functions. */
1408 /* ****************************************** */
1411 ctx_compare(const void * c1
, const void * c2
)
1413 return strcmp(((ctx_t
*)c1
)->name
, ((ctx_t
*)c2
)->name
);
1416 /* =========================== */
1417 /* Free a context_bst element. */
1418 /* =========================== */
1427 ll_destroy(ctx
->opt_list
, NULL
);
1428 ll_destroy(ctx
->incomp_list
, free
);
1429 bst_destroy(ctx
->par_bst
, par_free
);
1434 /* ============================= */
1435 /* Free a ctx_inst_list element. */
1436 /* ============================= */
1438 ctx_inst_free(void * ci
)
1440 ctx_inst_t
* ctx_inst
= ci
;
1442 free(ctx_inst
->par_name
);
1443 ll_destroy(ctx_inst
->incomp_bst_list
, incomp_bst_free
);
1444 bst_destroy(ctx_inst
->seen_opt_bst
, seen_opt_free
);
1445 ll_destroy(ctx_inst
->opt_inst_list
, opt_inst_free
);
1450 /* ============================= */
1451 /* Free a opt_inst_list element. */
1452 /* ============================= */
1454 opt_inst_free(void * oi
)
1456 opt_inst_t
* opt_inst
= oi
;
1458 ll_destroy(opt_inst
->values_list
, NULL
);
1463 /* ================================== */
1464 /* Compare two seen_opt_bst elements. */
1465 /* ================================== */
1467 seen_opt_compare(const void * so1
, const void * so2
)
1471 o1
= ((seen_opt_t
*)so1
)->opt
;
1472 o2
= ((seen_opt_t
*)so2
)->opt
;
1474 return strcmp(o1
->name
, o2
->name
);
1477 /* ============================ */
1478 /* Free a seen_opt_bst element. */
1479 /* ============================ */
1481 seen_opt_free(void * so
)
1483 seen_opt_t
* seen_opt
= so
;
1485 free(seen_opt
->par
);
1490 /* =========================== */
1491 /* Free an incomp_bst element. */
1492 /* =========================== */
1494 incomp_bst_free(void * b
)
1498 bst_destroy(bst
, NULL
);
1501 /* ================================= */
1502 /* Compare two options_bst elements. */
1503 /* ================================= */
1505 opt_compare(const void * o1
, const void * o2
)
1507 return strcmp(((opt_t
*)o1
)->name
, ((opt_t
*)o2
)->name
);
1510 /* ============================= */
1511 /* Free an options_bst elements. */
1512 /* ============================= */
1519 free(opt
->next_ctx
);
1524 ll_destroy(opt
->ctx_list
, NULL
);
1525 ll_destroy(opt
->constraints_list
, constraint_free
);
1530 /* ============================= */
1531 /* Compare two par_bst elements. */
1532 /* ============================= */
1534 par_compare(const void * a1
, const void * a2
)
1536 return strcmp(((par_t
*)a1
)->name
, ((par_t
*)a2
)->name
);
1539 /* ======================= */
1540 /* Free a par_bst element. */
1541 /* ======================= */
1552 /* ================================ */
1553 /* Free a constraints_list element. */
1554 /* ================================ */
1556 constraint_free(void * c
)
1558 constraint_t
* cstr
= c
;
1561 free(cstr
->to_free
);
1566 /* ******************************************************************** */
1567 /* Helper functions to locate contexts, options and parameters in a BST */
1568 /* by their names. */
1569 /* ******************************************************************** */
1572 locate_ctx(char * name
)
1579 if ((node
= bst_find(&ctx
, &contexts_bst
, ctx_compare
)) == NULL
)
1586 locate_opt(char * name
)
1593 if ((node
= bst_find(&opt
, &options_bst
, opt_compare
)) == NULL
)
1600 locate_par(char * name
, ctx_t
* ctx
)
1604 void * bst
= ctx
->par_bst
;
1608 if ((node
= bst_find(&par
, &bst
, par_compare
)) == NULL
)
1614 /* =================================================================== */
1615 /* Utility function to format and print the options present in a list. */
1617 /* IN list : a list of options. */
1618 /* OUT has_* : a set of flags which will determine the content of the */
1619 /* explanation given after the formatted printing of the */
1621 /* =================================================================== */
1623 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
1624 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
1625 int * has_early_eval
)
1627 ll_node_t
* node
= list
->head
;
1632 line
= xstrdup(" ");
1634 while (node
!= NULL
)
1636 option
= xstrdup("");
1641 option
= strappend(option
, "[", NULL
);
1645 if (opt
->eval_first
)
1647 option
= strappend(option
, "*", NULL
);
1648 *has_early_eval
= 1;
1651 option
= strappend(option
, opt
->params
, NULL
);
1653 if (opt
->next_ctx
!= NULL
)
1655 option
= strappend(option
, ">", opt
->next_ctx
, NULL
);
1656 *has_ctx_change
= 1;
1661 if (opt
->opt_count_oper
!= '\0')
1665 o
[0] = opt
->opt_count_oper
;
1667 snprintf(m
, 3, "%u", opt
->opt_count_mark
);
1668 option
= strappend(option
, "...", o
, m
, NULL
);
1672 option
= strappend(option
, "...", NULL
);
1679 if (*(opt
->arg
) == '#')
1680 *has_generic_arg
= 1;
1682 option
= strappend(option
, " ", NULL
);
1684 if (opt
->optional_args
)
1686 option
= strappend(option
, "[", opt
->arg
, NULL
);
1690 option
= strappend(option
, opt
->arg
, NULL
);
1692 if (opt
->multiple_args
)
1694 if (opt
->opt_args_count_oper
!= '\0')
1698 o
[0] = opt
->opt_args_count_oper
;
1700 snprintf(m
, 3, "%u", opt
->opt_args_count_mark
);
1701 option
= strappend(option
, "...", o
, m
, NULL
);
1705 option
= strappend(option
, "...", NULL
);
1709 if (opt
->optional_args
)
1710 option
= strappend(option
, "]", NULL
);
1713 option
= strappend(option
, "]", NULL
);
1715 if (strlen(line
) + 1 + strlen(option
) < 80)
1716 line
= strappend(line
, option
, " ", NULL
);
1719 printf("%s\n", line
);
1721 line
= strappend(line
, option
, " ", NULL
);
1729 printf("%s\n", line
);
1734 /* ==================================================== */
1735 /* Explain the special syntactic symbols present in the */
1736 /* generated usage messages. */
1737 /* ==================================================== */
1739 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
1740 int has_optional
, int has_ellipsis
, int has_rule
)
1742 if (has_early_eval
|| has_ctx_change
|| has_generic_arg
|| has_optional
1743 || has_ellipsis
|| has_rule
)
1745 printf("\nSyntactic explanations:\n");
1746 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1747 "must be entered.\n");
1748 printf("The following is just there to explain the other symbols "
1752 printf("* : the parameters for this option will be "
1753 "evaluated first.\n");
1756 "> : The context after this symbol will become the next "
1758 if (has_generic_arg
)
1759 printf("#tag : argument tag giving a clue to its meaning.\n");
1762 "[...] : the object between square brackets is optional.\n");
1764 printf("... : the previous object can be repeated more "
1765 "than one time.\n");
1767 printf("[<|=|>]number: rules constraining the number of "
1768 "parameters/arguments.\n");
1772 /* ************************************************************ */
1773 /* Various utilities and callback functions called when walking */
1774 /* through a BST. */
1775 /* ************************************************************ */
1778 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
)
1780 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1782 if (kind
== postorder
|| kind
== leaf
)
1784 if ((!seen_opt
->opt
->optional
) && seen_opt
->seen
== 0)
1787 user_string
= strappend(user_string
, seen_opt
->opt
->params
, " ", NULL
);
1793 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
)
1795 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1797 if (kind
== postorder
|| kind
== leaf
)
1798 if (seen_opt
->seen
== 1)
1801 user_object
= seen_opt
->par
;
1806 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
)
1808 ctx_t
* ctx
= main_ctx
;
1809 ctx_t
* cur_ctx
= ((bst_t
*)node
)->key
;
1813 int has_optional
= 0;
1814 int has_ellipsis
= 0;
1816 int has_generic_arg
= 0;
1817 int has_ctx_change
= 0;
1818 int has_early_eval
= 0;
1820 if (kind
== postorder
|| kind
== leaf
)
1821 if (strcmp(ctx
->name
, cur_ctx
->name
) != 0)
1823 list
= cur_ctx
->opt_list
;
1825 printf("\nAllowed options in the context %s:\n", cur_ctx
->name
);
1826 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
,
1827 &has_generic_arg
, &has_ctx_change
, &has_early_eval
);
1832 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
)
1834 opt_t
* opt
= ((bst_t
*)node
)->key
;
1836 if (kind
== postorder
|| kind
== leaf
)
1838 if (opt
->params
== NULL
) /* opt must have associated parameters. */
1839 fatal_internal("Option %s has no registered parameter.\n", opt
->name
);
1841 if (opt
->action
== NULL
) /* opt must have an action. */
1842 fatal_internal("Option %s has no registered action.\n", opt
->name
);
1847 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
)
1849 ctx_t
* ctx
= ((bst_t
*)node
)->key
;
1851 if (kind
== postorder
|| kind
== leaf
)
1853 char * str
= xstrdup(user_string
);
1855 while (*str
!= '\0')
1857 if (locate_par(str
, ctx
) != NULL
)
1859 user_string2
= strappend(user_string2
, " ", ctx
->name
, NULL
);
1862 str
[strlen(str
) - 1] = '\0';
1869 match_prefix_cb(const void * node
, walk_order_e kind
, int level
)
1871 par_t
* par
= ((bst_t
*)node
)->key
;
1873 if (kind
== postorder
|| kind
== leaf
)
1874 if (strpref(par
->name
, (char *)user_object
))
1877 user_string
= strappend(user_string
, par
->name
, " ", NULL
);
1881 /* ====================================================================== */
1882 /* A parameter may not be separated from its first option by spaces, in */
1883 /* this case this function looks for a valid flag as a prefix and splits */
1884 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1887 /* IN word : the word to be checked. */
1888 /* IN ctx : the context in which the flag indexed by the word is to be */
1890 /* OUT pos : the offset in word pointing just after the matching prefix. */
1891 /* OUT opt : a pointer to the option associated with the new parameter */
1892 /* or NULL if none is found. */
1894 /* The returned pointer must be freed by the caller. */
1895 /* ====================================================================== */
1897 look_for_valid_prefix_in_word(char * word
, ctx_t
* ctx
, int * pos
, opt_t
** opt
)
1902 par_t tmp_par
= { 0 };
1908 new = xstrdup(word
);
1914 } while ((par
= locate_par(tmp_par
.name
, ctx
)) == NULL
&& len
> 2);
1933 /* ============================================================= */
1934 /* If par_name is an unique abbreviation of an exiting parameter */
1935 /* in the context ctx, then return this parameter. */
1936 /* ============================================================= */
1938 abbrev_expand(char * par_name
, ctx_t
* ctx
)
1940 user_object
= par_name
;
1943 *user_string
= '\0';
1944 bst_walk(ctx
->par_bst
, match_prefix_cb
);
1945 rtrim(user_string
, " ", 0);
1947 /* The previous bst_walk has built a string of blank separated parameters */
1948 /* all having par_name as prefix. This string is put in the user_string */
1949 /* exchange zone. The number of these words in put in user_rc. */
1950 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1951 if (user_rc
== 1) /* The number of matching abbreviations. */
1952 return xstrdup(user_string
);
1953 else /* There is at least tho defined parameters starting with par_name. */
1959 void * tmp_opt_bst
= NULL
;
1961 /* Find all the options corresponding to these words and store them */
1962 /* without duplication in a temporary BST. Only their resulting count */
1964 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1965 s
= first_s
= strtok(user_string
, " "); /* first_s holds a copy of *
1966 | the first word. */
1969 par
= locate_par(s
, ctx
);
1972 if (bst_find(opt
, &tmp_opt_bst
, opt_compare
) == NULL
)
1974 /* This option as not already been seen */
1975 /* store it and increase the seen counter. */
1976 /* """"""""""""""""""""""""""""""""""""""" */
1977 bst_search(opt
, &tmp_opt_bst
, opt_compare
);
1980 s
= strtok(NULL
, " ");
1983 /* Clean the temporary BST without removing the pointer */
1984 /* to the real options. */
1985 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1986 if (tmp_opt_bst
!= NULL
)
1987 bst_destroy(tmp_opt_bst
, NULL
);
1990 /* All the abbreviation are leading to only one option */
1991 /* We can just continue as in the previous case. */
1992 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
1993 return xstrdup(first_s
);
1999 /* ================================================================ */
2000 /* Terminate the program if mandatory options required by a context */
2001 /* are not present. */
2002 /* ================================================================ */
2004 check_for_missing_mandatory_opt(ctx_inst_t
* ctx_inst
, char * opt_par
)
2008 if (has_unseen_mandatory_opt(ctx_inst
, &missing
))
2009 fatal(CTXOPTMISPAR
, missing
);
2012 /* ====================================================== */
2013 /* Return 1 if at least one mandatory option was not seen */
2014 /* when quitting a context, else 0. */
2015 /* ====================================================== */
2017 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
)
2020 *user_string
= '\0';
2022 bst_walk(ctx_inst
->seen_opt_bst
, bst_seen_opt_cb
);
2023 rtrim(user_string
, " ", 0);
2025 *missing
= user_string
;
2027 return user_rc
? 1 : 0;
2030 /* ========================================================================= */
2031 /* This function terminates the program if an option or its arguments do not */
2032 /* conform to its occurrences constraint. */
2033 /* There constraints can appear by trailing >, < or = in their definition */
2034 /* given in ctxopt_new_ctx. */
2035 /* ========================================================================= */
2037 check_for_occurrences_issues(ctx_inst_t
* ctx_inst
)
2039 ctx_t
* ctx
= ctx_inst
->ctx
;
2042 opt_inst_t
* opt_inst
;
2044 /* Checks options. */
2045 /* """"""""""""""" */
2046 node
= ctx
->opt_list
->head
;
2048 while (node
!= NULL
)
2052 /* Update current_state. */
2053 /* """"""""""""""""""""" */
2054 cur_state
->opts_count
= opt
->opt_count_mark
;
2055 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2057 if (opt
->opt_count_matter
)
2058 switch (opt
->opt_count_oper
)
2061 if (opt
->occurrences
> 0 && opt
->opt_count_mark
!= opt
->occurrences
)
2062 fatal(CTXOPTCTEOPT
, "");
2066 if (opt
->occurrences
> 0 && opt
->opt_count_mark
<= opt
->occurrences
)
2067 fatal(CTXOPTCTLOPT
, "");
2071 if (opt
->occurrences
> 0 && opt
->opt_count_mark
>= opt
->occurrences
)
2072 fatal(CTXOPTCTGOPT
, "");
2079 /* Checks arguments. */
2080 /* """"""""""""""""" */
2081 node
= ctx_inst
->opt_inst_list
->head
;
2082 while (node
!= NULL
)
2084 opt_inst
= node
->data
;
2085 opt
= opt_inst
->opt
;
2087 /* Update current_state. */
2088 /* """"""""""""""""""""" */
2089 cur_state
->opts_count
= opt
->opt_count_mark
;
2090 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
2092 int nb_values
= opt_inst
->values_list
->len
; /* Number of arguments of opt */
2094 if (opt
->opt_args_count_matter
)
2095 switch (opt
->opt_args_count_oper
)
2098 if (nb_values
> 0 && opt
->opt_args_count_mark
!= nb_values
)
2099 fatal(CTXOPTCTEARG
, "");
2103 if (nb_values
> 0 && opt
->opt_args_count_mark
<= nb_values
)
2104 fatal(CTXOPTCTLARG
, "");
2108 if (nb_values
> 0 && opt
->opt_args_count_mark
>= nb_values
)
2109 fatal(CTXOPTCTGARG
, "");
2117 /* ======================================================================== */
2118 /* Parse a strings describing options and some of their characteristics */
2119 /* The input string must have follow some rules like in the examples below: */
2121 /* "opt_name1 opt_name2" */
2122 /* "[opt_name1] opt_name2" */
2123 /* "[opt_name1] opt_name2..." */
2124 /* "[opt_name1 #...] opt_name2... [#]" */
2125 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2127 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2128 /* means that there can be more than one occurrence of the previous thing. */
2130 /* opt_name can be followed by a 'new context' change prefixed with the */
2131 /* symbol >, as in opt1>c2 by eg. */
2133 /* This function returns as soon as one (or no) option has been parsed and */
2134 /* return the offset to the next option to parse. */
2136 /* In case of successful parsing, an new option is allocated and its */
2137 /* pointer returned. */
2138 /* ======================================================================== */
2140 opt_parse(char * s
, opt_t
** opt
)
2142 int opt_optional
= 0;
2143 int opt_multiple
= 0;
2144 int opt_count_matter
= 0;
2145 char opt_count_oper
= '\0';
2146 unsigned opt_count_mark
= 0;
2148 char opt_arg
[33] = { 0 };
2149 int opt_multiple_args
= 0;
2150 int opt_args_count_matter
= 0;
2151 char opt_args_count_oper
= '\0';
2152 unsigned opt_args_count_mark
= 0;
2153 int opt_optional_args
= 0;
2154 int opt_eval_first
= 0;
2168 memset(opt_arg
, '\0', 33);
2170 /* Strip the leading blanks. */
2171 /* """"""""""""""""""""""""" */
2175 if (*s
== '[') /* Start of an optional option. */
2180 s
= strtoken(s
, token
, sizeof(token
) - 1, "[^] \n\t.]", &pos
);
2182 return -1; /* Empty string. */
2184 /* Early EOS, only return success if the option is mandatory. */
2185 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2187 if (opt_optional
== 1)
2188 return -(s
- s_orig
- 1);
2190 /* Validate the option name */
2191 /* ALPHA+(ALPHANUM|_)* */
2192 /* """""""""""""""""""""""" */
2194 if (!isalpha(*p
) && *p
!= '*')
2195 return -(s
- s_orig
- 1); /* opt_name must start with a letter. */
2203 if (!isalnum(*p
) && *p
!= '_' && *p
!= '>')
2204 return -(s
- s_orig
- 1); /* opt_name must contain a letter, *
2205 * a number or a _ */
2210 opt_name
= xstrdup(token
+ 1); /* Ignore the first '*' in token. */
2212 opt_name
= xstrdup(token
);
2223 /* Check if it can appear multiple times by looking for the dots. */
2224 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2225 p
= strtoken(s
, token
, 3, "[.]", &pos
);
2228 if (strcmp(token
, "...") == 0)
2232 if (*s
== '<' || *s
== '=' || *s
== '>')
2237 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2240 opt_count_matter
= 1;
2241 opt_count_oper
= *s
;
2242 opt_count_mark
= value
;
2250 return -(s
- s_orig
- 1);
2254 /* A blank separates the option name and the argument tag. */
2255 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2267 n
= sscanf(s
, "[%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2268 if (pos
> 1 && *opt_arg
== '#') /* [# has been read. */
2271 opt_optional_args
= 1;
2273 opt_multiple_args
= 1; /* There were dots. */
2275 s
+= pos
+ !!(n
== 2) * 3; /* Skips the dots. */
2277 if (*s
== '<' || *s
== '=' || *s
== '>')
2282 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2285 opt_args_count_matter
= 1;
2286 opt_args_count_oper
= *s
;
2287 opt_args_count_mark
= value
;
2292 /* Optional arg tag must end with a ] */
2293 /* """""""""""""""""""""""""""""""""" */
2297 return -(s
- s_orig
- 1);
2300 s
++; /* Skip the ] */
2304 n
= sscanf(s
, "%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2305 if (pos
> 0 && *opt_arg
== '#') /* # has been read. */
2308 if (n
== 2) /* There were dots. */
2309 opt_multiple_args
= 1;
2311 s
+= pos
+ !!(n
== 2) * 3; /* Skip the dots */
2313 if (*s
== '<' || *s
== '=' || *s
== '>')
2318 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2321 opt_args_count_matter
= 1;
2322 opt_args_count_oper
= *s
;
2323 opt_args_count_mark
= value
;
2331 /* Abort on extraneous ] if the option is mandatory. */
2332 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2334 return -(s
- s_orig
- 1);
2336 s
++; /* skip the ] */
2338 /* Strip the following blanks. */
2339 /* """"""""""""""""""""""""""" */
2345 else if (opt_optional
== 0 && (!*s
|| isblank(*s
)))
2347 /* Strip the following blanks. */
2348 /* """"""""""""""""""""""""""" */
2354 else if (opt_args
== 0) /* # was not read it is possibly the start *
2355 * of another option. */
2358 return -(s
- s_orig
- 1);
2363 /* Strip the following blanks. */
2364 /* """"""""""""""""""""""""""" */
2370 if (*opt_name
== '>')
2371 fatal_internal("%s: option name is missing.", opt_name
);
2373 count
= strchrcount(opt_name
, '>');
2376 char * tmp
= strchr(opt_name
, '>');
2377 next_ctx
= xstrdup(tmp
+ 1);
2381 fatal_internal("%s: only one occurrence of '>' is allowed.", opt_name
);
2383 *opt
= xmalloc(sizeof(opt_t
));
2385 (*opt
)->name
= opt_name
;
2386 (*opt
)->optional
= opt_optional
;
2387 (*opt
)->multiple
= opt_multiple
;
2388 (*opt
)->opt_count_matter
= opt_count_matter
;
2389 (*opt
)->opt_count_oper
= opt_count_oper
;
2390 (*opt
)->opt_count_mark
= opt_count_mark
;
2391 (*opt
)->args
= opt_args
;
2392 (*opt
)->arg
= xstrdup(opt_arg
);
2393 (*opt
)->optional_args
= opt_optional_args
;
2394 (*opt
)->multiple_args
= opt_multiple_args
;
2395 (*opt
)->opt_args_count_matter
= opt_args_count_matter
;
2396 (*opt
)->opt_args_count_oper
= opt_args_count_oper
;
2397 (*opt
)->opt_args_count_mark
= opt_args_count_mark
;
2398 (*opt
)->eval_first
= opt_eval_first
;
2399 (*opt
)->next_ctx
= next_ctx
;
2400 (*opt
)->ctx_list
= ll_new();
2401 (*opt
)->constraints_list
= ll_new();
2402 (*opt
)->action
= NULL
;
2403 (*opt
)->params
= NULL
;
2404 (*opt
)->data
= NULL
;
2409 /* ==================================================================== */
2410 /* Try to initialize all the option in a given string */
2411 /* Each parsed option are put in a BST tree with its name as index. */
2413 /* On collision, the arguments only the signature are required to be */
2414 /* the same else this is considered as an error. Options can be used in */
2415 /* more than one context and can be optional in one and mandatory in */
2417 /* ==================================================================== */
2419 init_opts(char * spec
, ctx_t
* ctx
)
2421 opt_t
* opt
, *bst_opt
;
2427 if ((offset
= opt_parse(spec
, &opt
)) > 0)
2431 if ((node
= bst_find(opt
, &options_bst
, opt_compare
)) != NULL
)
2433 int same_next_ctx
= 0;
2435 bst_opt
= node
->key
; /* Node extracted from the BST. */
2437 if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
== NULL
)
2439 else if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
!= NULL
)
2441 else if (bst_opt
->next_ctx
!= NULL
&& opt
->next_ctx
== NULL
)
2444 same_next_ctx
= strcmp(bst_opt
->next_ctx
, opt
->next_ctx
) == 0;
2446 if (bst_opt
->optional_args
!= opt
->optional_args
2447 || bst_opt
->multiple_args
!= opt
->multiple_args
2448 || bst_opt
->args
!= opt
->args
|| !same_next_ctx
)
2450 fatal_internal("option %s already exists with "
2451 "a different arguments signature.\n",
2455 /* The newly created opt is already present in options_bst. */
2456 /* We can remove it. */
2457 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2460 /* The new occurrence of the option option is legal */
2461 /* append the current context ptr in the list. */
2462 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2463 ll_append(bst_opt
->ctx_list
, ctx
);
2465 /* Append the new option to the context's options list. */
2466 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2467 ll_append(ctx
->opt_list
, bst_opt
);
2471 /* Initialize the option's context list with the current context. */
2472 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2473 ll_append(opt
->ctx_list
, ctx
);
2475 /* Append the new option to the context's options list. */
2476 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2477 ll_append(ctx
->opt_list
, opt
);
2479 /* Insert the new option in the BST. */
2480 /* """"""""""""""""""""""""""""""""" */
2481 bst_search(opt
, &options_bst
, opt_compare
);
2486 char * s
= xstrndup(spec
, -offset
);
2487 printf("%s <---\nSyntax error at or before offset %d\n", s
, -offset
);
2497 /* ===================================================== */
2498 /* ctxopt initialization function, must be called first. */
2499 /* ===================================================== */
2501 ctxopt_init(char * prog_name
)
2505 contexts_bst
= NULL
;
2511 user_string
= xmalloc(8);
2512 user_string2
= xmalloc(8);
2515 ctxopt_initialized
= 1;
2517 /* Update current_state.*/
2518 /* """"""""""""""""""""" */
2519 cur_state
= xcalloc(sizeof(state_t
), 0);
2521 /* Initialize custom error function pointers to NULL. */
2522 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2523 err_functions
= xmalloc(CTXOPTERRSIZ
* sizeof(void *));
2524 for (n
= 0; n
< CTXOPTERRSIZ
; n
++)
2525 err_functions
[n
] = NULL
;
2527 /* Update current_state. */
2528 /* """"""""""""""""""""" */
2531 if (*prog_name
== '\0')
2532 cur_state
->prog_name
= xstrdup("program_name");
2533 else if ((ptr
= strrchr(prog_name
, '/')))
2534 cur_state
->prog_name
= xstrdup(ptr
+ 1);
2536 cur_state
->prog_name
= xstrdup(prog_name
);
2539 cur_state
->prog_name
= xstrdup("program_name");
2542 /* ========================================================================= */
2543 /* Utility function which create and register a par_t object in a BST */
2544 /* embedded in a context. */
2545 /* This object will have a name and a pointer to the option it refers to. */
2546 /* These object will be used to quickly find an option from a command */
2547 /* line parameter during the analysis phase. */
2549 /* IN : an option name. */
2550 /* IN : a string of command line parameters to associate to the option. */
2551 /* Returns : 1 is all was fine else 0. */
2552 /* ========================================================================= */
2554 opt_set_parms(char * opt_name
, char * par_str
)
2556 char * par_name
, *ctx_name
;
2557 char * tmp_par_str
, *end_tmp_par_str
;
2561 par_t
* par
, tmp_par
;
2562 int rc
= 1; /* return code */
2567 /* Look if the given option is defined. */
2568 /* """""""""""""""""""""""""""""""""""" */
2569 opt
= locate_opt(opt_name
);
2571 fatal_internal("Unknown option %s", opt_name
);
2573 /* For each context using this option. */
2574 /* """"""""""""""""""""""""""""""""""" */
2575 list
= opt
->ctx_list
;
2578 while (lnode
!= NULL
)
2580 /* Locate the context in the contexts tree. */
2581 /* """""""""""""""""""""""""""""""""""""""" */
2582 ctx_name
= ((ctx_t
*)(lnode
->data
))->name
;
2584 ctx
= locate_ctx(ctx_name
);
2586 fatal_internal("Unknown context %s", ctx_name
);
2589 void * par_bst
= ctx
->par_bst
;
2591 tmp_par_str
= xstrdup(par_str
);
2592 ltrim(tmp_par_str
, " \t");
2593 rtrim(tmp_par_str
, " \t", 0);
2594 par_name
= xstrtok_r(tmp_par_str
, " \t,", &end_tmp_par_str
);
2595 if (par_name
== NULL
)
2596 fatal_internal("Parameters are missing for option %s", opt_name
);
2598 /* For each parameter given in par_str, creates a par_t object and */
2599 /* insert it the in the parameters BST of the context. */
2600 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2601 while (par_name
!= NULL
)
2603 tmp_par
.name
= par_name
;
2605 node
= bst_find(&tmp_par
, &par_bst
, par_compare
);
2608 fatal_internal("The parameter %s is already defined in context %s",
2609 par_name
, ctx
->name
);
2614 par
= xmalloc(sizeof(par_t
));
2615 par
->name
= xstrdup(par_name
);
2616 par
->opt
= opt
; /* Link the option to this parameter */
2618 bst_search(par
, &par_bst
, par_compare
);
2620 par_name
= xstrtok_r(NULL
, " \t,", &end_tmp_par_str
);
2623 /* Update the value of the root of ctx->par_bst as it may have */
2624 /* been modified. */
2625 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2626 ctx
->par_bst
= par_bst
;
2630 lnode
= lnode
->next
;
2636 /* ==================================================================== */
2637 /* Create a new context instance. */
2638 /* IN ctx : a context pointer to allow this instance to */
2639 /* access the context fields */
2640 /* IN prev_ctx_inst : the context instance whose option leading to the */
2641 /* creation of this new context instance is part of */
2642 /* Returns : the new context. */
2643 /* ==================================================================== */
2645 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
)
2648 opt_inst_t
* gen_opt_inst
;
2649 ctx_inst_t
* ctx_inst
;
2650 seen_opt_t
* seen_opt
;
2651 char * str
, *opt_name
;
2655 /* Keep a trace of the opt_inst which was at the origin of the creation */
2656 /* of this context instance. */
2657 /* This will serve during the evaluation of the option callbacks. */
2658 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2659 if (prev_ctx_inst
!= NULL
)
2661 gen_opt_inst
= (opt_inst_t
*)(prev_ctx_inst
->opt_inst_list
->tail
->data
);
2663 /* Update current_state. */
2664 /* """"""""""""""""""""" */
2665 cur_state
->opt_name
= gen_opt_inst
->opt
->name
;
2668 gen_opt_inst
= NULL
;
2670 /* Create and initialize the new context instance. */
2671 /* """"""""""""""""""""""""""""""""""""""""""""""" */
2672 ctx_inst
= xmalloc(sizeof(ctx_inst_t
));
2673 ctx_inst
->ctx
= ctx
;
2674 ctx_inst
->prev_ctx_inst
= prev_ctx_inst
;
2675 ctx_inst
->gen_opt_inst
= gen_opt_inst
;
2676 ctx_inst
->incomp_bst_list
= ll_new();
2677 ctx_inst
->opt_inst_list
= ll_new();
2678 ctx_inst
->seen_opt_bst
= NULL
;
2682 if (prev_ctx_inst
== NULL
)
2683 first_ctx_inst
= ctx_inst
;
2685 /* Initialize the occurrence counters of each opt allowed in the context. */
2686 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2687 node
= ctx
->opt_list
->head
;
2688 while (node
!= NULL
)
2691 opt
->occurrences
= 0;
2696 /* Initialize the BST containing the seen indicator for all the options */
2697 /* allowed in this context instance. */
2698 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2699 node
= ctx
->opt_list
->head
;
2700 while (node
!= NULL
)
2703 seen_opt
= xmalloc(sizeof(seen_opt_t
));
2704 seen_opt
->opt
= opt
;
2705 seen_opt
->par
= NULL
;
2708 bst_search(seen_opt
, &(ctx_inst
->seen_opt_bst
), seen_opt_compare
);
2713 /* Initialize the BST containing the incompatibles options. */
2714 /* Incompatibles option names are read from strings found in the list */
2715 /* incomp_list present in each instance of ctx_t. */
2716 /* These names are then used to search for the object of type seen_opt_t */
2717 /* which is already present in the seen_opt_bst of the context instance. */
2719 /* Once found the seen_opt_t object in inserted in the new BST */
2720 /* At the end the new BST in added to the list incomp_bst_list. */
2721 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2722 node
= ctx
->incomp_list
->head
;
2723 while (node
!= NULL
)
2726 seen_opt_t tmp_seen_opt
;
2728 str
= xstrdup(node
->data
);
2730 rtrim(str
, " \t", 0);
2731 opt_name
= strtok(str
, " \t"); /* Extract the first option name. */
2733 while (opt_name
!= NULL
) /* For each option name. */
2735 if ((opt
= locate_opt(opt_name
)) != NULL
)
2737 /* The option found is searched in the tree of potential */
2739 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2740 tmp_seen_opt
.opt
= opt
;
2742 bst_node
= bst_find(&tmp_seen_opt
, &(ctx_inst
->seen_opt_bst
),
2745 if (bst_node
!= NULL
)
2747 /* If found then it is added into the new BST tree. */
2748 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2749 seen_opt
= bst_node
->key
;
2750 bst_search(seen_opt
, &bst
, seen_opt_compare
);
2753 /* Not found! That means that the option is unknown in this */
2754 /* context as all options has have a seen_opt structure in */
2756 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2757 fatal_internal("%s is not known in the context %s", opt
->name
,
2761 fatal_internal("%s: unknown option.", opt_name
);
2763 opt_name
= strtok(NULL
, " \t");
2767 ll_append(ctx_inst
->incomp_bst_list
, bst
);
2775 /* ====================================================================== */
2776 /* Create a list formed by all the significant command line words */
2777 /* Words beginning or ending with { or } are split. Each of these */
2778 /* symbols will get their own place in the list. */
2780 /* the {...} part delimits a context, the { will not appear in the list */
2781 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2782 /* to facilitate the parsing phase. | must not be used by the end user. */
2784 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2785 /* program name is not considered. */
2786 /* IN words : is the array of strings constituting the command line to */
2788 /* Returns : 1 on success, 0 if a { or } is missing. */
2789 /* ====================================================================== */
2791 ctxopt_build_cmdline_list(int nb_words
, char ** words
)
2794 char * prev_word
= NULL
;
2798 ll_node_t
*node
, *start_node
;
2800 /* The analysis is divided into three passes, this is not optimal but */
2801 /* must be done only one time. Doing that we privilege readability. */
2803 /* In the following, SG is the ascii character 1d (dec 29) */
2805 /* The first pass creates the list, extract the leading an trailing */
2806 /* SG '{' and '}' of each word and give them their own place in the */
2809 /* The second pass transform the '{...}' blocks by a trailing SG */
2810 /* ({...} -> ...|) */
2812 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2813 /* the middle in the remaining list elements and recreate the pseudo */
2815 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2817 /* If the option list is not empty, clear it before going further. */
2818 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2819 if (cmdline_list
!= NULL
)
2821 node
= cmdline_list
->head
;
2822 while (node
!= NULL
)
2825 ll_delete(cmdline_list
, node
);
2826 node
= cmdline_list
->head
;
2830 cmdline_list
= ll_new();
2832 start_node
= cmdline_list
->head
; /* In the following loop start_node will *
2833 * contain a pointer to the current *
2834 * word stripped from its leading *
2835 * sequence of {, }. */
2836 for (i
= 0; i
< nb_words
; i
++)
2838 size_t len
= strlen(words
[i
]);
2844 /* Replace each occurrence of the legal word {} by the characters */
2845 /* 0x02 and 0x03 to hide them from the following process. */
2846 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2847 while ((ptr
= strstr(str
, "{}")) != NULL
)
2849 *ptr
= 0x02; /* Arbitrary values unlikely. */
2850 *(ptr
+ 1) = 0x03; /* present in a word */
2853 if (len
> 1) /* The word contains at least 2 characters. */
2857 /* Interpret its beginning and look for the start of the real word. */
2858 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2859 while (start
<= len
- 1 && (str
[start
] == '{' || str
[start
] == '}'))
2861 ll_append(cmdline_list
, xstrndup(str
+ start
, 1));
2863 start_node
= cmdline_list
->tail
;
2867 if (str
[end
] == '{' || str
[end
] == '}')
2869 if (end
> 0 && str
[end
- 1] != '\\')
2871 ll_append(cmdline_list
, xstrndup(str
+ end
, 1));
2873 node
= cmdline_list
->tail
;
2875 while (str
[end
] == '{' || str
[end
] == '}')
2877 if (end
> start
&& str
[end
- 1] == '\\')
2880 ll_insert_before(cmdline_list
, node
, xstrndup(str
+ end
, 1));
2889 if (start_node
!= NULL
)
2890 ll_insert_after(cmdline_list
, start_node
,
2891 xstrndup(str
+ start
, end
- start
+ 1));
2893 ll_append(cmdline_list
, xstrndup(str
+ start
, end
- start
+ 1));
2894 start_node
= cmdline_list
->tail
;
2899 ll_append(cmdline_list
, xstrdup(str
));
2900 start_node
= cmdline_list
->tail
;
2906 node
= cmdline_list
->head
;
2909 while (node
!= NULL
)
2913 if (strcmp(word
, "{") == 0)
2915 ll_node_t
* old_node
= node
;
2919 ll_delete(cmdline_list
, old_node
);
2921 else if (strcmp(word
, "}") == 0)
2939 node
= cmdline_list
->head
;
2941 while (node
!= NULL
)
2945 /* Restore the original { and } characters forming the legal word {}. */
2946 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2947 while ((ptr
= strchr(word
, 0x02)) != NULL
)
2949 while ((ptr
= strchr(word
, 0x03)) != NULL
)
2952 /* Remove a SG if the previous element is SG. */
2953 /* """""""""""""""""""""""""""""""""""""""""" */
2954 if (strcmp(word
, "\x1d") == 0)
2956 if (prev_word
!= NULL
&& (strcmp(prev_word
, "\x1d") == 0))
2958 ll_node_t
* old_node
= node
;
2960 free(old_node
->data
);
2961 ll_delete(cmdline_list
, old_node
);
2964 else if (strcmp(word
, "-") == 0) /* A single - is a legal argument, not *
2965 * a parameter. Protect it. */
2968 node
->data
= xstrdup("\\-");
2971 prev_word
= node
->data
;
2975 /* Clean useless and SG at the beginning and end of list. */
2976 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
2977 node
= cmdline_list
->head
;
2984 if (strcmp(word
, "\x1d") == 0)
2987 ll_delete(cmdline_list
, node
);
2990 node
= cmdline_list
->tail
;
2996 if (strcmp(word
, "\x1d") == 0)
2999 ll_delete(cmdline_list
, node
);
3005 /* ===================================================================== */
3006 /* Build and analyze the command line list and create the linked data */
3007 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3008 /* This function identifies the following errors and creates an array of */
3009 /* The remaining unanalyzed arguments. */
3010 /* - detect missing arguments */
3011 /* - detect too many arguments */
3012 /* - detect unknown parameters in a context */
3013 /* - detect too many occurrences of a parameters in a context */
3014 /* - detect missing required arguments in a context */
3016 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3017 /* program name is not considered */
3018 /* IN words : is the array of strings constituting the command line to */
3020 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3021 /* is present in the list. */
3022 /* OUT rem_args : array of remaining command line arguments if a -- */
3023 /* is present in the list. This array must be free by */
3024 /* The caller as it is allocated here. */
3025 /* ===================================================================== */
3027 ctxopt_analyze(int nb_words
, char ** words
, int * nb_rem_args
,
3033 ctx_inst_t
* ctx_inst
;
3034 opt_inst_t
* opt_inst
;
3037 int expect_par_or_arg
= 0;
3039 ll_node_t
* cli_node
;
3041 seen_opt_t
* bst_seen_opt
;
3047 if (!ctxopt_build_cmdline_list(nb_words
, words
))
3049 "The command line could not be parsed: missing { or } detected.");
3051 if (main_ctx
== NULL
)
3052 fatal_internal("At least one context must have been created.");
3054 /* Check that all options has an action and at least one parameter. */
3055 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3056 bst_walk(options_bst
, bst_check_opt_cb
);
3058 /* Create the first ctx_inst record. */
3059 /* """"""""""""""""""""""""""""""""" */
3062 ctx_inst_list
= ll_new();
3063 ctx_inst
= new_ctx_inst(ctx
, NULL
);
3064 ctx_inst
->par_name
= NULL
;
3066 /* Update current_state. */
3067 /* """"""""""""""""""""" */
3068 cur_state
->ctx_name
= ctx
->name
;
3070 ll_append(ctx_inst_list
, ctx_inst
);
3072 /* For each node in the command line. */
3073 /* """""""""""""""""""""""""""""""""" */
3074 cli_node
= cmdline_list
->head
;
3076 while (cli_node
!= NULL
)
3078 if (strcmp(cli_node
->data
, "--") == 0)
3079 break; /* No new parameter will be analyzed after this point. */
3081 par_name
= cli_node
->data
;
3083 if (strcmp(par_name
, "\x1d") == 0)
3085 check_for_missing_mandatory_opt(ctx_inst
, (char *)(cli_node
->prev
->data
));
3086 check_for_occurrences_issues(ctx_inst
);
3088 /* Forced backtracking to the previous context instance. */
3089 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3090 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3092 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3093 ctx
= ctx_inst
->ctx
;
3095 /* Update current_states. */
3096 /* """"""""""""""""""""" */
3097 cur_state
->ctx_name
= ctx
->name
;
3098 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3102 /* Update current_state. */
3103 /* """"""""""""""""""""" */
3104 cur_state
->ctx_par_name
= NULL
;
3107 else if (expect_par
&& *par_name
== '-')
3112 /* Update current_state. */
3113 /* """"""""""""""""""""" */
3114 cur_state
->cur_opt_par_name
= par_name
;
3115 cur_state
->ctx_name
= ctx
->name
;
3116 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3118 /* An expected parameter has been seen. */
3119 /* """""""""""""""""""""""""""""""""""" */
3120 if ((par
= locate_par(par_name
, ctx
)) == NULL
)
3125 /* Look if this parameter is an unique abbreviation of a longer */
3126 /* parameter. If this is the case then just replace it with its */
3127 /* full length version and try again. */
3128 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3129 if ((word
= abbrev_expand(par_name
, ctx
)) != NULL
)
3131 cli_node
->data
= word
;
3135 /* Try to find a prefix which is a valid parameter in this context */
3136 /* If found, split the cli_node in two to build a new parameter */
3137 /* node and followed by a node containing the remaining string */
3138 /* If the new parameter corresponds to an option not taking */
3139 /* argument then prefix the remaining string whit a dash as it may */
3140 /* contain a new parameter. */
3141 /* The new parameter will be re-evaluated in the next iteration of */
3143 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3144 prefix
= look_for_valid_prefix_in_word(par_name
, ctx
, &pos
, &popt
);
3145 if (prefix
!= NULL
&& pos
!= 0)
3147 cli_node
->data
= prefix
; /* prefix contains le name of a valid *
3148 | parameter in this context. */
3152 /* The parameter may be followed by arguments. */
3153 /* ''''''''''''''''''''''''''''''''''''''''''' */
3154 if (*(par_name
+ pos
) == '-')
3156 word
= xstrdup("\\"); /* Protect the '-' */
3157 word
= strappend(word
, par_name
+ pos
, NULL
);
3160 word
= xstrdup(par_name
+ pos
);
3164 /* The parameter does not take arguments, the */
3165 /* following word must be a parameter or nothing */
3166 /* hence prefix it with a dash. */
3167 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3168 word
= xstrdup("-");
3169 word
= strappend(word
, par_name
+ pos
, NULL
);
3172 /* Insert it after the current node in the list. */
3173 /* """"""""""""""""""""""""""""""""""""""""""""" */
3174 ll_insert_after(cmdline_list
, cli_node
, word
);
3176 continue; /* loop */
3180 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3181 check_for_occurrences_issues(ctx_inst
);
3183 if (ctx_inst
->prev_ctx_inst
== NULL
)
3185 char * errmsg
= xstrdup("");
3187 /* Update current_state. */
3188 /* """"""""""""""""""""" */
3189 cur_state
->ctx_par_name
= NULL
;
3191 *user_string
= '\0';
3192 *user_string2
= '\0';
3194 user_string
= strappend(user_string
, par_name
, NULL
);
3196 bst_walk(contexts_bst
, bst_match_par_cb
);
3198 if (*user_string2
!= '\0')
3202 "\nIt appears to be defined in the context(s):", user_string2
,
3206 fatal(CTXOPTUNKPAR
, errmsg
);
3210 /* Tries to backtrack and analyse the same parameter in the */
3211 /* previous context. */
3212 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3213 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3214 ctx
= ctx_inst
->ctx
;
3216 /* Update current_state. */
3217 /* """"""""""""""""""""" */
3218 cur_state
->ctx_name
= ctx
->name
;
3219 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3221 cli_node
= cli_node
->prev
;
3227 seen_opt_t seen_opt
;
3229 /* The parameter is valid in the context, create a opt_inst and */
3230 /* append it to the ctx_inst list options list. */
3231 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3236 opt_inst
= xmalloc(sizeof(opt_inst_t
));
3237 opt_inst
->opt
= opt
;
3238 opt_inst
->par
= par_name
;
3239 opt_inst
->values_list
= ll_new();
3240 opt_inst
->next_ctx_inst
= NULL
;
3242 /* Priority option are inserted at the start of the opt_inst list */
3243 /* but their order of appearance in the context definition must */
3244 /* be preserver so each new priority option will be placed after */
3245 /* the previous ones at the start of the opt_inst list. */
3246 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3247 if (!opt
->eval_first
)
3248 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3251 ll_node_t
* node
= ctx_inst
->opt_inst_list
->head
;
3252 opt_inst_t
* tmp_opt_inst
;
3253 while (node
!= NULL
)
3255 tmp_opt_inst
= node
->data
;
3256 if (!tmp_opt_inst
->opt
->eval_first
)
3258 ll_insert_before(ctx_inst
->opt_inst_list
, node
, opt_inst
);
3265 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3268 /* Check if an option was already seen in the */
3269 /* current context instance. */
3270 /* """""""""""""""""""""""""""""""""""""""""" */
3273 bst_node
= bst_find(&seen_opt
, &(ctx_inst
->seen_opt_bst
),
3276 /* bst_node cannot be NULL here. */
3278 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3280 if (!opt
->multiple
&& bst_seen_opt
->seen
== 1)
3281 fatal(CTXOPTDUPOPT
, NULL
);
3283 /* Check if this option is compatible with the options already */
3284 /* seen in this context instance. */
3285 /* Look if the option is present in one on the BST present in */
3286 /* the incomp_bst_list of the context instance. */
3287 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3288 node
= ctx_inst
->incomp_bst_list
->head
;
3289 while (node
!= NULL
)
3294 /* There can only have one seen_opt object in the BST tree was */
3295 /* already seen, try to locate it, the result will be put in */
3296 /* user_object by the bst_seen_opt_seen_cb function. */
3297 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3298 bst_walk(bst
, bst_seen_opt_seen_cb
);
3300 /* If it is the case, look if the current option is also */
3302 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3303 if (user_object
!= NULL
)
3305 bst_node
= bst_find(bst_seen_opt
, &bst
, seen_opt_compare
);
3307 if (bst_node
!= NULL
)
3309 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3310 if (bst_seen_opt
->seen
== 0)
3311 fatal(CTXOPTINCOPT
, (char *)user_object
);
3318 /* Mark this option as seen in the current context instance. */
3319 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3320 bst_seen_opt
->seen
= 1;
3321 free(bst_seen_opt
->par
);
3322 bst_seen_opt
->par
= xstrdup(par_name
);
3324 /* If this option leads to a next context, create a new ctx_inst */
3325 /* and switch to it for the analyse of the future parameter. */
3326 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3327 if (opt
->next_ctx
!= NULL
)
3329 ctx
= locate_ctx(opt
->next_ctx
);
3332 fatal_internal("%s: unknown context.", opt
->next_ctx
);
3334 opt_inst
->next_ctx_inst
= ctx_inst
= new_ctx_inst(ctx
, ctx_inst
);
3335 ctx_inst
->par_name
= xstrdup(par_name
);
3337 ll_append(ctx_inst_list
, ctx_inst
);
3340 /* Look is we must expect some arguments. */
3341 /* """""""""""""""""""""""""""""""""""""" */
3342 expect_par_or_arg
= 0;
3347 expect_par
= 1; /* Parameter doesn't accept any argument. */
3350 if (!opt
->optional_args
)
3351 expect_arg
= 1; /* Parameter has mandatory arguments. */
3353 expect_par_or_arg
= 1; /* Parameter has optional arguments. */
3357 else if (expect_par
&& *par_name
!= '-')
3359 ll_node_t
* n
= cli_node
->next
;
3361 /* Look if potential arguments must still be analyzed until the */
3362 /* end of the context/command line part to analyze/command line. */
3363 /* If this is the case we have met an extra argument. */
3364 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3367 if (strcmp(n
->data
, "--") == 0 || strcmp(n
->data
, "\x1d") == 0)
3368 fatal(CTXOPTUNXARG
, NULL
);
3370 if (*(char *)(n
->data
) == '-')
3371 fatal(CTXOPTUNXARG
, NULL
);
3376 break; /* An unexpected non parameter was seen, if no Potential *
3377 | arguments remain in the command line assume that it *
3378 | is is the first of the non arguments and stop the *
3379 | command line analysis. */
3381 else if (expect_arg
&& *par_name
!= '-')
3383 ll_node_t
* cstr_node
;
3384 constraint_t
* cstr
;
3386 /* Check if the arguments of the option respects */
3387 /* the attached constraints if any. */
3388 /* """"""""""""""""""""""""""""""""""""""""""""" */
3389 cstr_node
= opt
->constraints_list
->head
;
3390 while (cstr_node
!= NULL
)
3392 cstr
= cstr_node
->data
;
3393 if (!cstr
->constraint(cstr
->nb_args
, cstr
->args
, par_name
,
3394 cur_state
->cur_opt_par_name
))
3396 fputs("\n", stderr
);
3397 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, exit_after
);
3400 cstr_node
= cstr_node
->next
;
3403 /* If the argument is valid, store it. */
3404 /* """"""""""""""""""""""""""""""""""" */
3405 if (*par_name
== '\\' && *(par_name
+ 1) == '-')
3406 ll_append(opt_inst
->values_list
, par_name
+ 1);
3408 ll_append(opt_inst
->values_list
, par_name
);
3412 expect_par_or_arg
= 0;
3414 if (opt
->multiple_args
)
3415 expect_par_or_arg
= 1;
3417 expect_par
= 1; /* Parameter takes only one argument. */
3419 else if (expect_arg
&& *par_name
== '-')
3420 fatal(CTXOPTMISARG
, NULL
);
3421 else if (expect_par_or_arg
)
3425 expect_par_or_arg
= 0;
3427 if (*par_name
!= '-')
3428 expect_arg
= 1; /* Consider this word as an argument and retry. */
3430 expect_par
= 1; /* Consider this word as a parameter and retry. */
3432 cli_node
= cli_node
->prev
;
3435 cli_node
= cli_node
->next
;
3438 if (cmdline_list
->len
> 0 && *par_name
== '-')
3440 if (expect_arg
&& !opt
->optional_args
)
3441 fatal(CTXOPTMISARG
, NULL
);
3444 /* Look if a context_instance has unseen mandatory options. */
3445 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3446 node
= ctx_inst_list
->head
;
3447 while (node
!= NULL
)
3449 ctx_inst
= node
->data
;
3451 /* Update current_state. */
3452 /* """"""""""""""""""""" */
3453 cur_state
->ctx_name
= ctx_inst
->ctx
->name
;
3454 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3456 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3457 check_for_occurrences_issues(ctx_inst
);
3462 /* Allocate the array containing the remaining not analyzed */
3463 /* command line arguments. */
3464 /* NOTE: The strings in the array are just pointer to the */
3465 /* data of the generating list and must not be freed. */
3466 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3467 if (cli_node
!= NULL
)
3469 if (strcmp((char *)cli_node
->data
, "--") == 0)
3470 /* The special parameter -- was encountered, the -- argument is not */
3471 /* put in the remaining arguments. */
3472 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3473 ll_strarray(cmdline_list
, cli_node
->next
, nb_rem_args
, rem_args
);
3475 /* A non parameter was encountered when a parameter was expected. We */
3476 /* assume that the evaluation of the remaining command line argument */
3477 /* are not the responsibility of the users code. */
3478 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3479 ll_strarray(cmdline_list
, cli_node
, nb_rem_args
, rem_args
);
3484 *rem_args
= xmalloc(sizeof(char *));
3485 (*rem_args
)[0] = NULL
;
3489 /* ==================================================== */
3490 /* Free ctxopt memory used for its internal structures. */
3491 /* ==================================================== */
3493 ctxopt_free_memory(void)
3495 ll_destroy(cmdline_list
, NULL
);
3496 ll_destroy(ctx_inst_list
, ctx_inst_free
);
3497 bst_destroy(options_bst
, opt_free
);
3498 bst_destroy(contexts_bst
, ctx_free
);
3501 /* ==================================================================== */
3502 /* Parse the options data structures and launches the callback function */
3503 /* attached to each options instances. */
3504 /* This calls a recursive function which proceeds context per context. */
3505 /* ==================================================================== */
3507 ctxopt_evaluate(void)
3509 evaluate_ctx_inst(first_ctx_inst
);
3512 /* =================================================================== */
3513 /* Recursive function called by ctxopt_evaluate to process the list of */
3514 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3515 /* action attached to the context and its option instances. */
3516 /* =================================================================== */
3518 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
)
3520 opt_inst_t
* opt_inst
;
3523 ll_node_t
* opt_inst_node
;
3527 if (ctx_inst
== NULL
)
3530 ctx
= ctx_inst
->ctx
;
3532 /* Do not evaluate the action attached to this context is there is no */
3533 /* option to evaluate. */
3534 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3535 opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
3536 if (opt_inst_node
== NULL
)
3539 /* Call the entering action attached to this context if any. */
3540 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3541 if (ctx
->action
!= NULL
)
3543 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3544 ctx
->action(ctx
->name
, entering
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3545 ctx
->nb_data
, ctx
->data
);
3547 ctx
->action(ctx
->name
, entering
, NULL
, ctx
->nb_data
, ctx
->data
);
3550 /* For each instance of options. */
3551 /* """"""""""""""""""""""""""""" */
3552 while (opt_inst_node
!= NULL
)
3554 opt_inst
= (opt_inst_t
*)(opt_inst_node
->data
);
3555 ll_strarray(opt_inst
->values_list
, opt_inst
->values_list
->head
, &nb_args
,
3557 opt
= opt_inst
->opt
;
3559 /* Launch the attached action if any. */
3560 /* """""""""""""""""""""""""""""""""" */
3561 if (opt
->action
!= NULL
)
3562 opt
->action(ctx
->name
, opt
->name
, opt_inst
->par
, nb_args
, args
,
3563 opt
->nb_data
, opt
->data
, ctx
->nb_data
, ctx
->data
);
3565 if (opt_inst
->next_ctx_inst
!= NULL
)
3566 evaluate_ctx_inst(opt_inst
->next_ctx_inst
);
3571 opt_inst_node
= opt_inst_node
->next
;
3574 /* Call the exiting action attached to this context if any. */
3575 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3576 if (ctx
->action
!= NULL
)
3578 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3579 ctx
->action(ctx
->name
, exiting
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3580 ctx
->nb_data
, ctx
->data
);
3582 ctx
->action(ctx
->name
, exiting
, NULL
, ctx
->nb_data
, ctx
->data
);
3586 /* ============================================================ */
3587 /* Create and initializes a new context. */
3588 /* - allocate space. */
3590 /* - initialize its option with a few of their characteristics. */
3591 /* ============================================================ */
3593 ctxopt_new_ctx(char * name
, char * opts_specs
)
3599 if (!ctxopt_initialized
)
3600 fatal_internal("Please call ctxopt_init first.");
3602 ctx
= xmalloc(sizeof(ctx_t
));
3604 /* Validates the context name: */
3605 /* ALPHA+(ALPHANUM|_)* */
3606 /* """"""""""""""""""""""""""" */
3609 fatal_internal("%s: a context name must start with a letter.", name
);
3614 if (!isalnum(*p
) && *p
!= '_')
3615 fatal_internal("%s: a context name must only contain letters, "
3621 ctx
->name
= xstrdup(name
);
3622 ctx
->opt_list
= ll_new(); /* List of options legit in this context. */
3623 ctx
->incomp_list
= ll_new(); /* List of incompatible options strings. */
3624 ctx
->par_bst
= NULL
;
3628 /* The first created context is the main one. */
3629 /* """""""""""""""""""""""""""""""""""""""""" */
3630 if (contexts_bst
== NULL
)
3634 cur_state
->ctx_name
= ctx
->name
;
3637 if (init_opts(opts_specs
, ctx
) == 0)
3639 if (bst_find(ctx
, &contexts_bst
, ctx_compare
) != NULL
)
3640 fatal_internal("The context %s already exists", name
);
3642 bst_search(ctx
, &contexts_bst
, ctx_compare
);
3645 /* ==================================================== */
3646 /* Display a usage screen limited to a specific context */
3647 /* IN: the context name. */
3648 /* IN: what to do after (continue or exit the program) */
3649 /* possible values: continue_after, exit_after. */
3650 /* ==================================================== */
3652 ctxopt_ctx_disp_usage(char * ctx_name
, usage_behaviour action
)
3657 int has_optional
= 0;
3658 int has_ellipsis
= 0;
3660 int has_generic_arg
= 0;
3661 int has_ctx_change
= 0;
3662 int has_early_eval
= 0;
3664 ctx
= locate_ctx(ctx_name
);
3666 fatal_internal("%s: unknown context.", ctx_name
);
3668 if (cur_state
->ctx_par_name
== NULL
)
3669 printf("\nSynopsis:\n%s \\\n", cur_state
->prog_name
);
3671 printf("\nSynopsis for the context introduced by %s:\n",
3672 cur_state
->ctx_par_name
);
3674 list
= ctx
->opt_list
;
3675 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3676 &has_ctx_change
, &has_early_eval
);
3678 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3679 has_optional
, has_ellipsis
, has_rule
);
3681 if (action
== exit_after
)
3685 /* =================================================== */
3686 /* Display a full usage screen about all contexts. */
3687 /* IN: what to do after (continue or exit the program) */
3688 /* possible values: continue_after, exit_after. */
3689 /* =================================================== */
3691 ctxopt_disp_usage(usage_behaviour action
)
3694 int has_optional
= 0;
3695 int has_ellipsis
= 0;
3697 int has_generic_arg
= 0;
3698 int has_ctx_change
= 0;
3699 int has_early_eval
= 0;
3701 if (main_ctx
== NULL
)
3702 fatal_internal("At least one context must have been created.");
3704 /* Usage for the first context. */
3705 /* """""""""""""""""""""""""""" */
3706 printf("\nAllowed options in the default context:\n");
3707 list
= main_ctx
->opt_list
;
3708 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3709 &has_ctx_change
, &has_early_eval
);
3711 /* Usage for the other contexts. */
3712 /* """"""""""""""""""""""""""""" */
3713 bst_walk(contexts_bst
, bst_print_ctx_cb
);
3715 /* Contextual syntactic explanations. */
3716 /* """""""""""""""""""""""""""""""""" */
3717 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3718 has_optional
, has_ellipsis
, has_rule
);
3720 if (action
== exit_after
)
3724 /* *********************************** */
3725 /* Built-in constraint check functions */
3726 /* *********************************** */
3728 /* ============================================================= */
3729 /* This constraint checks if each arguments respects a format as */
3730 /* defined for the scanf function. */
3731 /* return 1 if yes and 0 if no. */
3732 /* ============================================================= */
3734 ctxopt_format_constraint(int nb_args
, char ** args
, char * value
, char * par
)
3743 fatal_internal("Format constraint: invalid number of parameters.");
3745 if (strlen(value
) > 255)
3748 format
= xstrdup(args
[0]);
3749 format
= strappend(format
, "%c", NULL
);
3751 rc
= sscanf(value
, format
, x
, &y
);
3754 "The argument %s of %s does not respect the imposed format: %s.",
3755 value
, par
, args
[0]);
3762 /* ================================================================== */
3763 /* This constraint checks if each arguments of the option instance is */
3764 /* between a minimum and a maximum (inclusive). */
3765 /* return 1 if yes and 0 if no. */
3766 /* ================================================================== */
3768 ctxopt_re_constraint(int nb_args
, char ** args
, char * value
, char * par
)
3774 "Regular expression constraint: invalid number of parameters.");
3776 if (regcomp(&re
, args
[0], REG_EXTENDED
) != 0)
3777 fatal_internal("Invalid regular expression %s.", args
[0]);
3779 if (regexec(&re
, value
, (size_t)0, NULL
, 0) != 0)
3782 "The argument %s of %s doesn't match the constraining "
3783 "regular expression %s.",
3784 value
, par
, args
[0]);
3793 /* ================================================================== */
3794 /* This constraint checks if each arguments of the option instance is */
3795 /* between a minimum and a maximum (inclusive). */
3796 /* return 1 if yes and 0 if no. */
3797 /* ================================================================== */
3799 ctxopt_range_constraint(int nb_args
, char ** args
, char * value
, char * par
)
3810 fatal_internal("Range constraint: invalid number of parameters.");
3812 if (strcmp(args
[0], ".") == 0)
3815 n
= sscanf(args
[0], "%ld%c", &min
, &c
);
3817 if (!max_only
&& n
!= 1)
3818 fatal_internal("Range constraint: min: invalid parameters.");
3820 if (strcmp(args
[1], ".") == 0)
3823 n
= sscanf(args
[1], "%ld%c", &max
, &c
);
3825 if (!min_only
&& n
!= 1)
3826 fatal_internal("Range constraint: max: invalid parameters.");
3828 if (min_only
&& max_only
)
3829 fatal_internal("Range constraint: invalid parameters.");
3832 v
= strtol(value
, &ptr
, 10);
3833 if (errno
|| ptr
== value
)
3841 "The argument %ld of %s is not greater than or equal to %ld.", v
,
3853 "The argument %ld of %s is not less than or equal to %ld.", v
,
3860 else if (v
< min
|| v
> max
)
3862 fprintf(stderr
, "The argument %ld of %s is not between %ld and %ld.", v
,
3867 return 1; /* check passed */
3870 /* =============================================================== */
3871 /* This function provides a way to set the behaviour of a context. */
3872 /* =============================================================== */
3874 ctxopt_add_global_settings(settings s
, ...)
3881 case error_functions
:
3883 typedef void fn(errors e
, state_t
* state
);
3885 void (*function
)(errors e
, state_t
* state
);
3888 e
= va_arg(args
, errors
);
3889 function
= va_arg(args
, fn
*);
3890 err_functions
[e
] = function
;
3900 /* ================================================================ */
3901 /* This function provides a way to set the behaviour of an option. */
3902 /* It can take a variable number of arguments according to its */
3903 /* first argument: */
3905 /* o a string containing an option name and all its possible */
3906 /* parameters separates by spaces, tabs or commas (char *) */
3907 /* (e.g: "help -h -help"). */
3909 /* o a string containing an option name. */
3910 /* o a pointer to a function which will be called at evaluation */
3912 /* - constraints: */
3913 /* o a string containing an option name. */
3914 /* o a pointer to a function to check if an argument is valid. */
3915 /* o a strings containing the arguments to this function. */
3916 /* ================================================================ */
3918 ctxopt_add_opt_settings(settings s
, ...)
3928 /* This part associates some command line parameters to an option. */
3929 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3935 /* The second argument must be a string containing: */
3936 /* - The name of an existing option. */
3937 /* - a list of parameters with a leading dash (-). */
3938 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3939 ptr
= va_arg(args
, char *);
3942 if (opt_name
!= NULL
)
3944 if ((opt
= locate_opt(opt_name
)) != NULL
)
3946 ptr
= va_arg(args
, char *);
3949 if (!opt_set_parms(opt_name
, params
))
3951 "duplicates parameters or bad settings for the option (%s).",
3955 fatal_internal("%s: unknown option.", opt_name
);
3959 "ctxopt_opt_add_settings: parameters: not enough arguments.");
3961 /* Here opt is a known option. */
3962 /* """"""""""""""""""""""""""" */
3963 if (opt
->params
!= NULL
)
3964 fatal_internal("Parameters are already set for %s", opt_name
);
3968 size_t l
= strlen(params
);
3970 opt
->params
= xstrdup(params
);
3971 while ((n
= strcspn(opt
->params
, " \t")) < l
)
3972 opt
->params
[n
] = '|';
3978 /* This part associates a callback function to an option. */
3979 /* This function will be called when an instance of an option */
3981 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3988 /* The second argument must be the name of an existing option. */
3989 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3990 ptr
= va_arg(args
, char *);
3992 if ((opt
= locate_opt(ptr
)) != NULL
)
3994 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
3997 /* The third argument must be the callback function. */
3998 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
3999 function
= va_arg(args
, fn
*);
4000 opt
->action
= function
;
4002 /* The fourth argument must be a pointer to an user's defined */
4003 /* variable or structure that the previous function can manage. */
4004 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4005 while ((data
= va_arg(args
, void *)) != NULL
)
4008 opt
->data
= xrealloc(opt
->data
, nb_data
* sizeof(void *));
4009 opt
->data
[nb_data
- 1] = data
;
4011 opt
->nb_data
= nb_data
;
4014 fatal_internal("%s: unknown option.", ptr
);
4018 /* This part associates a list of functions to control some */
4019 /* characteristics of the arguments of an option. */
4020 /* Each function will be called in order and must return 1 */
4021 /* to validate the arguments. */
4022 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4026 constraint_t
* cstr
;
4029 /* The second argument must be a string. */
4030 /* """"""""""""""""""""""""""""""""""""" */
4031 ptr
= va_arg(args
, char *);
4033 if ((opt
= locate_opt(ptr
)) != NULL
)
4035 typedef int fn(int, char **, char *);
4037 /* The third argument must be a function. */
4038 /* """""""""""""""""""""""""""""""""""""" */
4039 function
= va_arg(args
, fn
*);
4041 cstr
= xmalloc(sizeof(constraint_t
));
4042 cstr
->constraint
= function
;
4044 /* The fourth argument must be a string containing the argument of */
4045 /* The previous function separated by spaces or tabs. */
4046 /* Theses arguments will be passed to the previous function */
4047 /* max: 32 argument! */
4048 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4049 value
= xstrdup(va_arg(args
, char *));
4051 cstr
->to_free
= value
;
4052 cstr
->args
= xcalloc(sizeof(char *), 32);
4053 cstr
->nb_args
= str2argv(value
, cstr
->args
, 32);
4054 ll_append(opt
->constraints_list
, cstr
);
4057 fatal_internal("%s: unknown option.", ptr
);
4067 /* =============================================================== */
4068 /* This function provides a way to set the behaviour of a context. */
4069 /* =============================================================== */
4071 ctxopt_add_ctx_settings(settings s
, ...)
4080 /* Add a set of mutually incompatible options in a context. */
4081 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4082 case incompatibilities
:
4089 ptr
= va_arg(args
, char *);
4090 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4092 ptr
= va_arg(args
, char *);
4093 list
= ctx
->incomp_list
;
4097 rtrim(str
, " \t", 0);
4099 n
= strcspn(str
, " \t");
4100 if (n
> 0 && n
< strlen(str
))
4101 ll_append(list
, str
);
4104 "Not enough incompatible options in the string: \"%s\"", str
);
4107 fatal_internal("%s: unknown context.", ptr
);
4111 /* Add functions which will be called when */
4112 /* entering and exiting a context. */
4113 /* """"""""""""""""""""""""""""""""""""""" */
4121 ptr
= va_arg(args
, char *);
4122 if ((ctx
= locate_ctx(ptr
)) != NULL
)
4124 typedef int fn(char *, direction
, char *, int, void **);
4126 function
= va_arg(args
, fn
*);
4127 ctx
->action
= function
;
4129 while ((data
= va_arg(args
, void *)) != NULL
)
4132 ctx
->data
= xrealloc(ctx
->data
, nb_data
* sizeof(void *));
4133 ctx
->data
[nb_data
- 1] = data
;
4135 ctx
->nb_data
= nb_data
;
4138 fatal_internal("%s: unknown context.", ptr
);