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 /* *********************** */
21 static void * contexts_bst
;
22 static void * options_bst
;
28 /* ****************** */
29 /* Messages interface */
30 /* ****************** */
32 static void (**err_functions
)(errors e
, state_t
* state
);
35 fatal_internal(const char * format
, ...);
38 fatal(errors e
, char * errmsg
);
40 static int user_rc
; /* Used by various callback functions */
41 static int user_value
; /* Used by various callback functions */
42 static char * user_string
; /* Used by various callback functions */
43 static char * user_string2
; /* Used by various callback functions */
44 static void * user_object
; /* Used by various callback functions */
46 /* *************************** */
47 /* Memory management interface */
48 /* *************************** */
54 xcalloc(size_t num
, size_t size
);
57 xrealloc(void * ptr
, size_t size
);
60 xstrdup(const char * p
);
63 xstrndup(const char * str
, size_t len
);
68 typedef struct bst_s bst_t
;
78 #if 0 /* Unused yet */
80 bst_delete(const void * vkey
, void ** vrootp
,
81 int (*compar
)(const void *, const void *));
85 bst_destroy_recurse(bst_t
* root
, void (*free_action
)(void *));
88 bst_destroy(void * vrootp
, void (*freefct
)(void *));
91 bst_find(const void * vkey
, void * const * vrootp
,
92 int (*compar
)(const void *, const void *));
95 bst_search(const void * vkey
, void ** vrootp
,
96 int (*compar
)(const void *, const void *));
99 bst_walk_recurse(const bst_t
* root
,
100 void (*action
)(const void *, walk_order_e
, int), int level
);
103 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int));
105 /* ********************* */
106 /* Linked list Interface */
107 /* ********************* */
109 typedef struct ll_node_s ll_node_t
;
110 typedef struct ll_s ll_t
;
113 ll_append(ll_t
* const list
, void * const data
);
116 ll_prepend(ll_t
* const list
, void * const data
);
119 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
);
122 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
);
125 ll_delete(ll_t
* const list
, ll_node_t
* node
);
127 #if 0 /* Unused yet */
129 ll_find(ll_t
* const, void * const, int (*)(const void *, const void *));
133 ll_init(ll_t
* list
);
142 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
);
144 /* ****************** */
145 /* various interfaces */
146 /* ****************** */
149 ltrim(char * str
, const char * trim_str
);
152 rtrim(char * str
, const char * trim_str
, size_t min
);
155 strchrcount(char * str
, char c
);
158 strpref(char * s1
, char * s2
);
161 xstrtok_r(char * str
, const char * delim
, char ** end
);
163 /* **************** */
164 /* ctxopt interface */
165 /* **************** */
167 typedef struct opt_s opt_t
;
168 typedef struct par_s par_t
;
169 typedef struct ctx_s ctx_t
;
170 typedef struct constraint_s constraint_t
;
171 typedef struct ctx_inst_s ctx_inst_t
;
172 typedef struct opt_inst_s opt_inst_t
;
173 typedef struct seen_opt_s seen_opt_t
;
176 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
);
179 ctx_compare(const void * c1
, const void * c2
);
182 opt_compare(const void * o1
, const void * o2
);
185 par_compare(const void * a1
, const void * a2
);
188 seen_opt_compare(const void * so1
, const void * so2
);
191 locate_ctx(char * name
);
194 locate_opt(char * name
);
197 locate_par(char * name
, ctx_t
* ctx
);
200 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
201 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
202 int * has_early_eval
);
204 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
205 int has_optional
, int has_ellipsis
, int has_rule
);
207 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
);
210 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
);
213 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
);
216 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
);
219 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
);
222 match_prefix_cb(const void * node
, walk_order_e kind
, int level
);
225 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
);
228 opt_parse(char * s
, opt_t
** opt
);
231 init_opts(char * spec
, ctx_t
* ctx
);
234 ctxopt_build_cmdline_list(int nb_words
, char ** words
);
237 opt_set_parms(char * opt_name
, char * par_str
);
240 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
);
243 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
);
245 /* ********************** */
246 /* Message implementation */
247 /* ********************** */
249 /* ================================================================== */
250 /* Fatal error function used when an fatal condition was encountered. */
251 /* This function is reserved for the ctxopt internal usage. */
253 /* format : printf like format */
254 /* ... : remaining arguments interpreted using the format argument */
255 /* ================================================================== */
257 fatal_internal(const char * format
, ...)
261 fprintf(stderr
, "CTXOPT: ");
263 va_start(args
, format
);
264 vfprintf(stderr
, format
, args
);
265 fprintf(stderr
, "\n");
271 /* ====================================================================== */
272 /* Generic fatal error function. This one uses the global status ctxopt */
273 /* stored in the cur_state structure and can call custom error functions. */
274 /* registered by the users for a given error identifier. */
276 /* e : error identifier responsible of the fatal error */
277 /* errmsg : users's provided string specific to the error e */
278 /* Note that errmsg is not used in all cases */
280 /* CTXOPTMISPAR Missing parameter */
281 /* CTXOPTMISARG Missing argument */
282 /* CTXOPTUXPARG Unexpected argument */
283 /* CTXOPTDUPOPT Duplicated option */
284 /* CTXOPTUNKPAR Unknown parameter */
285 /* CTXOPTINCOPT Incompatible option */
286 /* CTXOPTCTEOPT Option: bad number of occurrences */
287 /* CTXOPTCTLOPT Option: not enough occurrences */
288 /* CTXOPTCTGOPT Option: too many occurrence of */
289 /* CTXOPTCTEARG Arguments: bad number of occurrences */
290 /* CTXOPTCTLARG Arguments: not enough occurrences */
291 /* CTXOPTCTGARG Arguments: too many occurrences */
292 /* ====================================================================== */
294 fatal(errors e
, char * errmsg
)
296 if (err_functions
[e
] != NULL
)
297 err_functions
[e
](e
, cur_state
);
306 if (cur_state
->ctx_par_name
!= NULL
)
308 "Mandatory parameter(s): %s are missing in the context "
309 "introduced by %s.\n",
310 errmsg
, cur_state
->ctx_par_name
);
313 "Mandatory parameter(s): %s are missing "
314 "in the main context.\n",
321 fprintf(stderr
, "%s only takes one argument.\n",
322 cur_state
->cur_opt_par_name
);
326 if (cur_state
->pre_opt_par_name
!= NULL
)
327 fprintf(stderr
, "%s requires argument(s).\n",
328 cur_state
->pre_opt_par_name
);
330 fprintf(stderr
, "%s requires argument(s).\n",
331 cur_state
->cur_opt_par_name
);
335 if (cur_state
->pre_opt_par_name
!= NULL
)
337 "The parameter(s) %s can only appear once in the context "
338 "introduced by %s.\n",
339 cur_state
->cur_opt_par_name
, cur_state
->ctx_par_name
);
342 "The parameter(s) %s can only appear once "
343 "in the main context.\n",
344 cur_state
->cur_opt_par_name
);
348 fprintf(stderr
, "Unknown parameter: %s.\n",
349 cur_state
->cur_opt_par_name
);
353 fprintf(stderr
, "%s is incompatible with %s.\n",
354 cur_state
->cur_opt_par_name
, errmsg
);
358 if (cur_state
->ctx_par_name
)
360 "%s must appear exactly %u times in the context "
361 "introduced by %s.\n",
362 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
363 cur_state
->ctx_par_name
);
366 "%s must appear exactly %u times in "
367 "the main context.\n",
368 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
372 if (cur_state
->ctx_par_name
)
374 "%s must appear less than %u times in the context "
375 "introduced by %s.\n",
376 cur_state
->cur_opt_par_name
, cur_state
->opts_count
,
377 cur_state
->ctx_par_name
);
380 "%s must appear less than %u times in the main context.\n",
381 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
385 if (cur_state
->ctx_par_name
)
387 "%s must appear more than %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 more than %u times in the main context.\n",
394 cur_state
->cur_opt_par_name
, cur_state
->opts_count
);
398 fprintf(stderr
, "%s must have exactly %u arguments.\n",
399 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
403 fprintf(stderr
, "%s must have less than %u arguments.\n",
404 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
408 fprintf(stderr
, "%s must have more than %u arguments.\n",
409 cur_state
->cur_opt_par_name
, cur_state
->opt_args_count
);
417 ctxopt_ctx_disp_usage(cur_state
->ctx_name
, continue_after
);
419 exit(e
); /* Program exist with the error id e as return code */
422 /* ******************************** */
423 /* Memory management implementation */
424 /* ******************************** */
426 /* ================= */
427 /* Customized malloc */
428 /* ================= */
435 real_size
= (size
> 0) ? size
: 1;
436 allocated
= malloc(real_size
);
437 if (allocated
== NULL
)
438 fatal_internal("Insufficient memory (attempt to malloc %lu bytes)\n",
439 (unsigned long int)size
);
444 /* ================= */
445 /* Customized calloc */
446 /* ================= */
448 xcalloc(size_t n
, size_t size
)
453 size
= (size
> 0) ? size
: 1;
454 allocated
= calloc(n
, size
);
455 if (allocated
== NULL
)
456 fatal_internal("Insufficient memory (attempt to calloc %lu bytes)\n",
457 (unsigned long int)size
);
462 /* ================== */
463 /* Customized realloc */
464 /* ================== */
466 xrealloc(void * p
, size_t size
)
470 allocated
= realloc(p
, size
);
471 if (allocated
== NULL
&& size
> 0)
472 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes)\n",
473 (unsigned long int)size
);
478 /* =================================== */
479 /* strdup implementation using xmalloc */
480 /* =================================== */
482 xstrdup(const char * p
)
486 allocated
= xmalloc(strlen(p
) + 1);
487 strcpy(allocated
, p
);
492 /* ================================================== */
493 /* strndup implementation using xmalloc */
494 /* This version guarantees that there is a final '\0' */
495 /* ================================================== */
497 xstrndup(const char * str
, size_t len
)
501 p
= memchr(str
, '\0', len
);
506 p
= xmalloc(len
+ 1);
513 /* ************************** */
514 /* Linked list implementation */
515 /* ************************** */
517 /* Linked list node structure */
518 /* """""""""""""""""""""""""" */
522 struct ll_node_s
* next
;
523 struct ll_node_s
* prev
;
526 /* Linked List structure */
527 /* """"""""""""""""""""" */
535 /* ======================== */
536 /* Create a new linked list */
537 /* ======================== */
541 ll_t
* ret
= xmalloc(sizeof(ll_t
));
547 /* ======================== */
548 /* Initialize a linked list */
549 /* ======================== */
558 /* ==================================================== */
559 /* Allocate the space for a new node in the linked list */
560 /* ==================================================== */
564 ll_node_t
* ret
= xmalloc(sizeof(ll_node_t
));
569 /* ==================================================================== */
570 /* Append a new node filled with its data at the end of the linked list */
571 /* The user is responsible for the memory management of the data */
572 /* ==================================================================== */
574 ll_append(ll_t
* const list
, void * const data
)
578 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
579 | uses xmalloc which does not return if there *
580 | is an allocation error. */
585 node
->prev
= list
->tail
;
587 list
->tail
->next
= node
;
596 /* =================================================================== */
597 /* Put a new node filled with its data at the beginning of the linked */
598 /* list. The user is responsible for the memory management of the data */
599 /* =================================================================== */
601 ll_prepend(ll_t
* const list
, void * const data
)
605 node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
606 | uses xmalloc which does not return if there *
607 | is an allocation error. */
612 node
->next
= list
->head
;
614 list
->head
->prev
= node
;
623 /* ======================================================= */
624 /* Insert a new node before the specified node in the list */
625 /* ======================================================= */
627 ll_insert_before(ll_t
* const list
, ll_node_t
* node
, void * const data
)
629 ll_node_t
* new_node
;
631 if (node
->prev
== NULL
)
632 ll_prepend(list
, data
);
635 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
636 | uses xmalloc which does not return if there *
637 | is an allocation error. */
639 new_node
->data
= data
;
640 new_node
->next
= node
;
641 new_node
->prev
= node
->prev
;
642 node
->prev
->next
= new_node
;
643 node
->prev
= new_node
;
649 /* ====================================================== */
650 /* Insert a new node after the specified node in the list */
651 /* ====================================================== */
653 ll_insert_after(ll_t
* const list
, ll_node_t
* node
, void * const data
)
655 ll_node_t
* new_node
;
657 if (node
->next
== NULL
)
658 ll_append(list
, data
);
661 new_node
= ll_new_node(); /* ll_new_node cannot return NULL because it *
662 | uses xmalloc which does not return if there *
663 | is an allocation error. */
665 new_node
->data
= data
;
666 new_node
->prev
= node
;
667 new_node
->next
= node
->next
;
668 node
->next
->prev
= new_node
;
669 node
->next
= new_node
;
675 /* ================================================================ */
676 /* Remove a node from a linked list */
677 /* The memory taken by the deleted node must be freed by the caller */
678 /* ================================================================ */
680 ll_delete(ll_t
* const list
, ll_node_t
* node
)
682 if (list
->head
== list
->tail
)
684 if (list
->head
!= NULL
)
685 list
->head
= list
->tail
= NULL
;
689 else if (node
->prev
== NULL
)
691 list
->head
= node
->next
;
692 list
->head
->prev
= NULL
;
694 else if (node
->next
== NULL
)
696 list
->tail
= node
->prev
;
697 list
->tail
->next
= NULL
;
701 node
->next
->prev
= node
->prev
;
702 node
->prev
->next
= node
->next
;
712 #if 0 /* Unused yet */
713 /* =========================================================================*/
714 /* Find a node in the list containing data. Return the node pointer or NULL */
716 /* A comparison function must be provided to compare a and b (strcmp like). */
717 /* =========================================================================*/
719 ll_find(ll_t
* const list
, void * const data
,
720 int (*cmpfunc
)(const void * a
, const void * b
))
724 if (NULL
== (node
= list
->head
))
729 if (0 == cmpfunc(node
->data
, data
))
731 } while (NULL
!= (node
= node
->next
));
737 /* ==================================================================== */
738 /* Allocates and fills an array of strings from a list */
740 /* 1) The list node must contain strings (char *) */
741 /* 2) The strings in the resulting array MUST NOT be freed as the are */
742 /* NOT copied from the strings of the list. */
744 /* IN list : The list from which the array is generated */
745 /* IN start_node : The node of the list which will be the first node to */
746 /* consider to create the array */
747 /* OUT: count : The number of elements of the resulting array. */
748 /* OUT: array : The resulting array or NULL if the list is empty. */
749 /* RC : : The number of elements of the resulting array. */
750 /* ==================================================================== */
752 ll_strarray(ll_t
* list
, ll_node_t
* start_node
, int * count
, char *** array
)
761 if (list
== NULL
|| node
== NULL
)
768 *array
= xmalloc((list
->len
+ 1) * sizeof(char *));
771 (*array
)[n
++] = (char *)(node
->data
);
777 (*array
)[*count
] = NULL
;
782 /* ******************************************************************* */
783 /* BST (search.h compatible) implementation */
785 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
786 /* the AT&T man page says. */
788 /* Written by reading the System V Interface Definition, not the code. */
790 /* Totally public domain. */
791 /* ******************************************************************* */
796 struct bst_s
* llink
;
797 struct bst_s
* rlink
;
800 #if 0 /* Unused yet */
801 /* ========================== */
802 /* delete node with given key */
803 /* ========================== */
805 bst_delete(const void * vkey
, void ** vrootp
,
806 int (*compar
)(const void *, const void *))
808 bst_t
** rootp
= (bst_t
**)vrootp
;
812 if (rootp
== NULL
|| (p
= *rootp
) == NULL
)
815 while ((cmp
= (*compar
)(vkey
, (*rootp
)->key
)) != 0)
818 rootp
= (cmp
< 0) ? &(*rootp
)->llink
/* follow llink branch */
819 : &(*rootp
)->rlink
; /* follow rlink branch */
821 return NULL
; /* key not found */
823 r
= (*rootp
)->rlink
; /* D1: */
824 if ((q
= (*rootp
)->llink
) == NULL
) /* Left NULL? */
827 { /* Right link is NULL? */
828 if (r
->llink
== NULL
)
829 { /* D2: Find successor */
834 { /* D3: Find NULL link */
835 for (q
= r
->llink
; q
->llink
!= NULL
; q
= r
->llink
)
838 q
->llink
= (*rootp
)->llink
;
839 q
->rlink
= (*rootp
)->rlink
;
843 free(*rootp
); /* D4: Free node */
844 *rootp
= q
; /* link parent to new node */
853 bst_destroy(void * vrootp
, void (*clean
)(void *))
855 bst_t
* root
= (bst_t
*)vrootp
;
860 bst_destroy(root
->llink
, clean
);
861 bst_destroy(root
->rlink
, clean
);
864 clean((void *)root
->key
);
869 /* ======================== */
870 /* find a node, or return 0 */
871 /* ======================== */
873 bst_find(const void * vkey
, void * const * vrootp
,
874 int (*compar
)(const void *, const void *))
876 bst_t
* const * rootp
= (bst_t
* const *)vrootp
;
881 while (*rootp
!= NULL
)
885 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
886 return *rootp
; /* key found */
887 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
888 : &(*rootp
)->rlink
; /* T4: follow right branch */
893 /* ===================================== */
894 /* find or insert datum into search tree */
895 /* ===================================== */
897 bst_search(void * vkey
, void ** vrootp
,
898 int (*compar
)(const void *, const void *))
901 bst_t
** rootp
= (bst_t
**)vrootp
;
906 while (*rootp
!= NULL
)
910 if ((r
= (*compar
)(vkey
, (*rootp
)->key
)) == 0) /* T2: */
911 return *rootp
; /* we found it! */
913 rootp
= (r
< 0) ? &(*rootp
)->llink
/* T3: follow left branch */
914 : &(*rootp
)->rlink
; /* T4: follow right branch */
917 q
= xmalloc(sizeof(bst_t
)); /* T5: key not found */
919 { /* make new node */
920 *rootp
= q
; /* link new node to old */
921 q
->key
= vkey
; /* initialize new node */
922 q
->llink
= q
->rlink
= NULL
;
927 /* ======================== */
928 /* Walk the nodes of a tree */
929 /* ======================== */
931 bst_walk_recurse(const bst_t
* root
,
932 void (*action
)(const void *, walk_order_e
, int), int level
)
934 if (root
->llink
== NULL
&& root
->rlink
== NULL
)
935 (*action
)(root
, leaf
, level
);
938 (*action
)(root
, preorder
, level
);
939 if (root
->llink
!= NULL
)
940 bst_walk_recurse(root
->llink
, action
, level
+ 1);
941 (*action
)(root
, postorder
, level
);
942 if (root
->rlink
!= NULL
)
943 bst_walk_recurse(root
->rlink
, action
, level
+ 1);
944 (*action
)(root
, endorder
, level
);
949 bst_walk(const void * vroot
, void (*action
)(const void *, walk_order_e
, int))
951 if (vroot
!= NULL
&& action
!= NULL
)
952 bst_walk_recurse(vroot
, action
, 0);
955 /* *********************** */
956 /* various implementations */
957 /* *********************** */
959 /* ======================= */
960 /* Trim leading characters */
961 /* ======================= */
963 ltrim(char * str
, const char * trim_str
)
965 size_t len
= strlen(str
);
966 size_t begin
= strspn(str
, trim_str
);
970 for (i
= begin
; i
<= len
; ++i
)
971 str
[i
- begin
] = str
[i
];
974 /* ================================================= */
975 /* Trim trailing characters */
976 /* The resulting string will have at least min bytes */
977 /* even if trailing spaces remain. */
978 /* ================================================= */
980 rtrim(char * str
, const char * trim_str
, size_t min
)
982 size_t len
= strlen(str
);
983 while (len
> min
&& strchr(trim_str
, str
[len
- 1]))
987 /* ================================================== */
988 /* Count the number of occurrences of the character c */
989 /* in the string str. */
990 /* The str pointer is assumed to be not NULL */
991 /* ================================================== */
993 strchrcount(char * str
, char c
)
1004 /* =============================================== */
1005 /* Is the string str2 a prefix of the string str1? */
1006 /* =============================================== */
1008 strpref(char * str1
, char * str2
)
1010 while (*str1
!= '\0' && *str1
== *str2
)
1016 return *str2
== '\0';
1019 /* ======================================================================== */
1020 /* Strings concatenation with dynamic memory allocation */
1021 /* IN : a variable number of char * arguments with NULL terminating */
1023 /* The first one must have been dynamically allocated and is mandatory */
1025 /* Returns a new allocated string containing the concatenation of all */
1026 /* the arguments. It is the caller's responsibility to free the resulting */
1028 /* ======================================================================== */
1030 strappend(char * str
, ...)
1036 l
= 1 + strlen(str
);
1037 va_start(args
, str
);
1039 s
= va_arg(args
, char *);
1044 s
= va_arg(args
, char *);
1049 str
= xrealloc(str
, l
);
1051 va_start(args
, str
);
1052 s
= va_arg(args
, char *);
1057 s
= va_arg(args
, char *);
1064 /* ====================================================================== */
1065 /* public domain strtok_r() by Charlie Gordon */
1066 /* from comp.lang.c 9/14/2007 */
1067 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1069 /* (Declaration that it's public domain): */
1070 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1072 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1074 /* ====================================================================== */
1076 xstrtok_r(char * str
, const char * delim
, char ** end
)
1086 str
+= strspn(str
, delim
);
1093 str
+= strcspn(str
, delim
);
1103 /* =========================================================== */
1104 /* Fills an array of strings from the words composing a string */
1106 /* str: initial string which will be altered */
1107 /* args: array of pointers to the start of the words in str */
1108 /* max: maximum number of words used before giving up */
1109 /* return: the number of words (<=max) */
1110 /* =========================================================== */
1112 str2argv(char * str
, char ** args
, int max
)
1121 while (*str
== ' ' || *str
== '\t')
1127 args
[nb_args
] = str
;
1130 while (*str
&& (*str
!= ' ') && (*str
!= '\t'))
1137 /* ********************* */
1138 /* ctxopt implementation */
1139 /* ********************* */
1141 static int ctxopt_initialized
= 0; /* cap_init has not yet been called */
1143 /* context structure */
1144 /* """"""""""""""""" */
1148 ll_t
* opt_list
; /* list of options allowed in this context */
1149 ll_t
* incomp_list
; /* list of strings containing incompatible names *
1150 | of options separated by spaces or tabs */
1151 int (*action
)(char * name
, int type
, char * new_ctx
, int ctx_nb_data
,
1158 /* https://textik.com/#488ce3649b6c60f5 */
1160 /* +--------------+ */
1161 /* |first_ctx_inst| */
1162 /* +---+----------+ */
1164 /* +--v-----+ +--------+ +--------+ +-----+ */
1165 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1166 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1168 /* | | +-v------+ | | */
1169 /* | +--+ctx_inst<-----------+ | */
1170 /* | +-+------+ | */
1172 /* | +-v------+ | */
1173 /* +------+ctx_inst<--------------------------+ */
1180 /* option structure */
1181 /* """""""""""""""" */
1184 char * name
; /* option name */
1185 char * next_ctx
; /* new context this option may lead to */
1186 ll_t
* ctx_list
; /* list of contexts allowing this option */
1187 char * params
; /* string containing all the parameters of *
1190 void (*action
)( /* The option associated action */
1191 char * ctx_name
, /* context name */
1192 char * opt_name
, /* option name */
1193 char * par
, /* option parameter */
1194 int nb_args
, /* number of arguments */
1195 char ** args
, /* option arguments */
1196 int nb_opt_data
, /* number of option data pointers */
1197 void ** opt_data
, /* option data pointers */
1198 int nb_ctx_data
, /* nb of current context data ptrs */
1199 void ** ctx_data
/* current context data pointers */
1202 int nb_data
; /* number of the data pointers passed as argument to *
1204 void ** data
; /* array of data pointers passed as argument to action */
1206 int args
; /* 1 if this option takes arguments else 0 */
1207 int optional
; /* 1 if the option is optional, else 0 */
1208 int multiple
; /* 1 if the option can appear more than one time in a *
1209 | context, else 0 */
1211 int opt_count_matter
; /* 1 if we must restrict the count, else 0 */
1212 int occurrences
; /* Number of option occurrences in a context */
1213 char opt_count_oper
; /* <, = or > */
1214 unsigned opt_count_mark
; /* Value to be compared to with opt_count_oper */
1216 char * arg
; /* symbolic text after # describing the option argument */
1218 int optional_args
; /* 1 of option is optional else 0 */
1219 int multiple_args
; /* 1 is option can appear more than once in a context *
1222 int opt_args_count_matter
; /* 1 if we must restrict the count, else 0 */
1223 char opt_args_count_oper
; /* <, = or > */
1224 unsigned opt_args_count_mark
; /* Value to be compared to with *
1227 int eval_first
; /* 1 if this option must be evaluated before *
1228 | the options without this mark */
1230 ll_t
* constraints_list
; /* List of constraint checking functions pointers. */
1233 /* context instance structure */
1234 /* """""""""""""""""""""""""" */
1237 ctx_t
* ctx
; /* the context whose this is an instance of */
1238 ctx_inst_t
* prev_ctx_inst
; /* ctx_inst of the opt_inst which led to the *
1239 | creation of this ctx_inst structure. */
1240 opt_inst_t
* gen_opt_inst
; /* opt_inst which led to the creation of a *
1241 | instance of this structure */
1242 ll_t
* incomp_bst_list
; /* list of seen_opt_t bst */
1243 void * seen_opt_bst
; /* tree of seen_opt_t */
1244 ll_t
* opt_inst_list
; /* The list of option instances in this *
1245 | context instance */
1246 char * par_name
; /* parameter which created this instance */
1249 /* Option instance structure */
1250 /* """"""""""""""""""""""""" */
1253 opt_t
* opt
; /* The option this is an instance of */
1254 char * opt_name
; /* The option which led to this creation */
1255 char * par
; /* The parameter which led to this creation */
1256 ll_t
* values_list
; /* The list of arguments of this option */
1257 ctx_inst_t
* next_ctx_inst
; /* The new context instance this option *
1258 | instance may create */
1261 /* Structure used to check if an option has bee seen or not */
1262 /* in a context instance */
1263 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1266 opt_t
* opt
; /* The concerned option */
1267 char * par
; /* Parameter which led to the making of this structure */
1268 char * count
; /* Number of seen occurrences of this parameter */
1269 int seen
; /* 1 if seen in the context instances, else 0 */
1272 /* parameter structure which links a parameter to the option it belongs to */
1273 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1276 char * name
; /* Parameter name (with the leading - */
1277 opt_t
* opt
; /* Attached option */
1280 /* Constraint structure */
1281 /* """""""""""""""""""" */
1284 int (*constraint
)(int nb_args
, char ** args
, char * value
);
1289 state_t
* cur_state
= NULL
; /* Current analysis state */
1290 static ll_t
* cmdline_list
; /* List of interpreted CLI words *
1291 | serves as the basis for the *
1292 | analysis of the parameters */
1293 static ctx_t
* main_ctx
= NULL
; /* initial context */
1294 static ctx_inst_t
* first_ctx_inst
= NULL
; /* Pointer to the fist context *
1295 | instance which holds the *
1296 | options instances */
1297 static ll_t
* ctx_inst_list
; /* List of the context instances */
1299 /* ====================================================== */
1300 /* Parse a string for the next matching token. */
1302 /* s: string to parse. */
1303 /* token: pre_allocated array of max tok_len characters */
1304 /* pattern: scanf type pattern token must match */
1305 /* pos: number of characters successfully parsed in s */
1307 /* Returns: a pointer to the first unread character or */
1308 /* to he terminating \0. */
1309 /* ====================================================== */
1311 strtoken(char * s
, char * token
, size_t tok_len
, char * pattern
, int * pos
)
1313 char * full_pattern
;
1319 n
= snprintf(len
, 3, "%zu", tok_len
);
1323 full_pattern
= xmalloc(strlen(pattern
) + n
+ 4);
1325 strcpy(full_pattern
, "%");
1326 strcat(full_pattern
, len
);
1327 strcat(full_pattern
, pattern
);
1328 strcat(full_pattern
, "%n");
1330 n
= sscanf(s
, full_pattern
, token
, pos
);
1340 /* **************************** */
1341 /* Various comparison functions */
1342 /* **************************** */
1345 ctx_compare(const void * c1
, const void * c2
)
1347 return strcmp(((ctx_t
*)c1
)->name
, ((ctx_t
*)c2
)->name
);
1351 opt_compare(const void * o1
, const void * o2
)
1353 return strcmp(((opt_t
*)o1
)->name
, ((opt_t
*)o2
)->name
);
1357 par_compare(const void * a1
, const void * a2
)
1359 return strcmp(((par_t
*)a1
)->name
, ((par_t
*)a2
)->name
);
1363 seen_opt_compare(const void * so1
, const void * so2
)
1367 o1
= ((seen_opt_t
*)so1
)->opt
;
1368 o2
= ((seen_opt_t
*)so2
)->opt
;
1370 return strcmp(o1
->name
, o2
->name
);
1373 /* ******************************************************************** */
1374 /* Helper functions to locate contexts, options and parameters in a bst */
1375 /* by their names. */
1376 /* ******************************************************************** */
1379 locate_ctx(char * name
)
1386 if ((node
= bst_find(&ctx
, &contexts_bst
, ctx_compare
)) == NULL
)
1393 locate_opt(char * name
)
1400 if ((node
= bst_find(&opt
, &options_bst
, opt_compare
)) == NULL
)
1407 locate_par(char * name
, ctx_t
* ctx
)
1411 void * bst
= ctx
->par_bst
;
1415 if ((node
= bst_find(&par
, &bst
, par_compare
)) == NULL
)
1421 /* =================================================================== */
1422 /* Utility function to format and print the options present in a list. */
1424 /* IN list : a list of options */
1425 /* OUT has_* : a set of flags which will determine the content of the */
1426 /* explanation given after the formatted printing of the */
1428 /* =================================================================== */
1430 print_options(ll_t
* list
, int * has_optional
, int * has_ellipsis
,
1431 int * has_rule
, int * has_generic_arg
, int * has_ctx_change
,
1432 int * has_early_eval
)
1434 ll_node_t
* node
= list
->head
;
1439 line
= xstrdup(" ");
1441 while (node
!= NULL
)
1443 option
= xstrdup("");
1448 option
= strappend(option
, "[", NULL
);
1452 if (opt
->eval_first
)
1454 option
= strappend(option
, "*", NULL
);
1455 *has_early_eval
= 1;
1458 option
= strappend(option
, opt
->params
, NULL
);
1460 if (opt
->next_ctx
!= NULL
)
1462 option
= strappend(option
, ">", opt
->next_ctx
, NULL
);
1463 *has_ctx_change
= 1;
1468 if (opt
->opt_count_oper
!= '\0')
1472 o
[0] = opt
->opt_count_oper
;
1474 snprintf(m
, 3, "%u", opt
->opt_count_mark
);
1475 option
= strappend(option
, "...", o
, m
, NULL
);
1479 option
= strappend(option
, "...", NULL
);
1486 if (*(opt
->arg
) == '#')
1487 *has_generic_arg
= 1;
1489 option
= strappend(option
, " ", NULL
);
1491 if (opt
->optional_args
)
1493 option
= strappend(option
, "[", opt
->arg
, NULL
);
1497 option
= strappend(option
, opt
->arg
, NULL
);
1499 if (opt
->multiple_args
)
1501 if (opt
->opt_args_count_oper
!= '\0')
1505 o
[0] = opt
->opt_args_count_oper
;
1507 snprintf(m
, 3, "%u", opt
->opt_args_count_mark
);
1508 option
= strappend(option
, "...", o
, m
, NULL
);
1512 option
= strappend(option
, "...", NULL
);
1516 if (opt
->optional_args
)
1517 option
= strappend(option
, "]", NULL
);
1520 option
= strappend(option
, "]", NULL
);
1522 if (strlen(line
) + 1 + strlen(option
) < 80)
1523 line
= strappend(line
, option
, " ", NULL
);
1526 printf("%s\n", line
);
1528 line
= strappend(line
, option
, " ", NULL
);
1536 printf("%s\n", line
);
1541 /* ==================================================== */
1542 /* Explain the special syntactic symbols present in the */
1543 /* generated usage messages. */
1544 /* ==================================================== */
1546 print_explanations(int has_early_eval
, int has_ctx_change
, int has_generic_arg
,
1547 int has_optional
, int has_ellipsis
, int has_rule
)
1549 if (has_early_eval
|| has_ctx_change
|| has_generic_arg
|| has_optional
1550 || has_ellipsis
|| has_rule
)
1552 printf("\nSyntactic explanations:\n");
1553 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1554 "must be entered.\n");
1555 printf("The following is just there to explain the other symbols "
1559 printf("* : the parameters for this option will be "
1560 "evaluated first.\n");
1563 "> : The context after this symbol will become the next "
1565 if (has_generic_arg
)
1566 printf("#tag : argument tag giving a clue to its meaning.\n");
1569 "[...] : the object between square brackets is optional.\n");
1571 printf("... : the previous object can be repeated more "
1572 "than one time.\n");
1574 printf("[<|=|>]number: rules constraining the number of "
1575 "parameters/arguments.\n");
1579 /* ******************************************************* */
1580 /* Various utility and callback function call when walking */
1581 /* through a bst. */
1582 /* ******************************************************* */
1585 bst_seen_opt_cb(const void * node
, walk_order_e kind
, int level
)
1587 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1589 if (kind
== postorder
|| kind
== leaf
)
1591 if ((!seen_opt
->opt
->optional
) && seen_opt
->seen
== 0)
1594 user_string
= strappend(user_string
, seen_opt
->opt
->params
, " ", NULL
);
1600 bst_seen_opt_seen_cb(const void * node
, walk_order_e kind
, int level
)
1602 seen_opt_t
* seen_opt
= ((bst_t
*)node
)->key
;
1604 if (kind
== postorder
|| kind
== leaf
)
1605 if (seen_opt
->seen
== 1)
1608 user_object
= seen_opt
->par
;
1613 bst_print_ctx_cb(const void * node
, walk_order_e kind
, int level
)
1615 ctx_t
* ctx
= main_ctx
;
1616 ctx_t
* cur_ctx
= ((bst_t
*)node
)->key
;
1620 int has_optional
= 0;
1621 int has_ellipsis
= 0;
1623 int has_generic_arg
= 0;
1624 int has_ctx_change
= 0;
1625 int has_early_eval
= 0;
1627 if (kind
== postorder
|| kind
== leaf
)
1628 if (strcmp(ctx
->name
, cur_ctx
->name
) != 0)
1630 list
= cur_ctx
->opt_list
;
1632 printf("\nAllowed options in the context %s:\n", cur_ctx
->name
);
1633 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
,
1634 &has_generic_arg
, &has_ctx_change
, &has_early_eval
);
1639 bst_check_opt_cb(const void * node
, walk_order_e kind
, int level
)
1641 opt_t
* opt
= ((bst_t
*)node
)->key
;
1643 if (kind
== postorder
|| kind
== leaf
)
1645 if (opt
->params
== NULL
) /* opt must have associated parameters */
1646 fatal_internal("Option %s has no registered parameter.\n", opt
->name
);
1648 if (opt
->action
== NULL
) /* opt must have an action */
1649 fatal_internal("Option %s has no registered action.\n", opt
->name
);
1654 bst_match_par_cb(const void * node
, walk_order_e kind
, int level
)
1656 ctx_t
* ctx
= ((bst_t
*)node
)->key
;
1658 if (kind
== postorder
|| kind
== leaf
)
1660 char * str
= xstrdup(user_string
);
1662 while (*str
!= '\0')
1664 if (locate_par(str
, ctx
) != NULL
)
1666 user_string2
= strappend(user_string2
, " ", ctx
->name
, NULL
);
1669 str
[strlen(str
) - 1] = '\0';
1676 match_prefix_cb(const void * node
, walk_order_e kind
, int level
)
1678 par_t
* par
= ((bst_t
*)node
)->key
;
1680 if (kind
== postorder
|| kind
== leaf
)
1681 if (strpref(par
->name
, (char *)user_object
))
1684 user_string
= strappend(user_string
, par
->name
, " ", NULL
);
1688 /* ====================================================================== */
1689 /* A parameter may not be separated from its first option by spaces, in */
1690 /* this case this function looks for a valid flag as a prefix and splits */
1691 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1694 /* IN word : the word to be checked. */
1695 /* IN ctx : the context in which the flag indexed by the word is to be */
1697 /* OUT pos : the offset in word pointing just after the matching prefix. */
1698 /* OUT opt : a pointer to the option associated with the new parameter */
1699 /* or NULL if none is found. */
1701 /* The returned pointer must be freed by the caller */
1702 /* ====================================================================== */
1704 look_for_valid_prefix_in_word(char * word
, ctx_t
* ctx
, int * pos
, opt_t
** opt
)
1709 par_t tmp_par
= { 0 };
1715 new = xstrdup(word
);
1721 } while ((par
= locate_par(tmp_par
.name
, ctx
)) == NULL
&& len
> 2);
1740 /* ============================================================= */
1741 /* If par_name is an unique abbreviation of an exiting parameter */
1742 /* in the context ctx, then return this parameter. */
1743 /* ============================================================= */
1745 abbrev_expand(char * par_name
, ctx_t
* ctx
)
1747 user_object
= par_name
;
1750 *user_string
= '\0';
1751 bst_walk(ctx
->par_bst
, match_prefix_cb
);
1752 rtrim(user_string
, " ", 0);
1754 if (user_rc
== 1) /* The number of matching abbreviations */
1755 return xstrdup(user_string
);
1762 void * tmp_opt_bst
= NULL
;
1764 /* for each word in the matching parameters return by walking the */
1765 /* parameter's tree of this context, do: */
1766 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1767 s
= first_s
= strtok(user_string
, " "); /* first_s holds a copy of *
1768 | the first word. */
1771 par
= locate_par(s
, ctx
);
1774 if (bst_find(opt
, &tmp_opt_bst
, opt_compare
) == NULL
)
1776 /* This option as not already been seen */
1777 /* store it and increase the seen counter */
1778 /* """""""""""""""""""""""""""""""""""""" */
1779 bst_search(opt
, &tmp_opt_bst
, opt_compare
);
1782 s
= strtok(NULL
, " ");
1785 /* Clean the temporary bst without removing the pointer */
1786 /* to the real options. */
1787 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1788 if (tmp_opt_bst
!= NULL
)
1789 bst_destroy(tmp_opt_bst
, NULL
);
1792 /* All the abbreviation lead to only one option */
1793 /* We can just continue as in the previous case. */
1794 /* """""""""""""""""""""""""""""""""""""""""""""" */
1795 return xstrdup(first_s
);
1801 /* ================================================================= */
1802 /* Terminates the program if mandatory options required by a context */
1803 /* are not present. */
1804 /* ================================================================= */
1806 check_for_missing_mandatory_opt(ctx_inst_t
* ctx_inst
, char * opt_par
)
1810 if (has_unseen_mandatory_opt(ctx_inst
, &missing
))
1811 fatal(CTXOPTMISPAR
, missing
);
1814 /* ====================================================== */
1815 /* Return 1 if at least one mandatory option was not seen */
1816 /* when quitting a context, else 0 */
1817 /* ====================================================== */
1819 has_unseen_mandatory_opt(ctx_inst_t
* ctx_inst
, char ** missing
)
1822 *user_string
= '\0';
1824 bst_walk(ctx_inst
->seen_opt_bst
, bst_seen_opt_cb
);
1825 rtrim(user_string
, " ", 0);
1827 *missing
= user_string
;
1829 return user_rc
? 1 : 0;
1832 /* ========================================================================= */
1833 /* This function terminates the program if an option or its arguments do not */
1834 /* conform to its occurrences constraint. */
1835 /* There constraints can appear by trailing >, < or = in their definition */
1836 /* given in ctxopt_new_ctx. */
1837 /* ========================================================================= */
1839 check_for_occurrences_issues(ctx_inst_t
* ctx_inst
)
1841 ctx_t
* ctx
= ctx_inst
->ctx
;
1844 opt_inst_t
* opt_inst
;
1848 node
= ctx
->opt_list
->head
;
1850 while (node
!= NULL
)
1854 /* Update current_state */
1855 /* -------------------- */
1856 cur_state
->opts_count
= opt
->opt_count_mark
;
1857 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
1859 if (opt
->opt_count_matter
)
1860 switch (opt
->opt_count_oper
)
1863 if (opt
->occurrences
> 0 && opt
->opt_count_mark
!= opt
->occurrences
)
1864 fatal(CTXOPTCTEOPT
, "");
1868 if (opt
->occurrences
> 0 && opt
->opt_count_mark
<= opt
->occurrences
)
1869 fatal(CTXOPTCTLOPT
, "");
1873 if (opt
->occurrences
> 0 && opt
->opt_count_mark
>= opt
->occurrences
)
1874 fatal(CTXOPTCTGOPT
, "");
1881 /* Check arguments */
1882 /* """"""""""""""" */
1883 node
= ctx_inst
->opt_inst_list
->head
;
1884 while (node
!= NULL
)
1886 opt_inst
= node
->data
;
1887 opt
= opt_inst
->opt
;
1889 /* Update current_state */
1890 /* -------------------- */
1891 cur_state
->opts_count
= opt
->opt_count_mark
;
1892 cur_state
->opt_args_count
= opt
->opt_args_count_mark
;
1894 int nb_values
= opt_inst
->values_list
->len
; /* Number of arguments of opt */
1896 if (opt
->opt_args_count_matter
)
1897 switch (opt
->opt_args_count_oper
)
1900 if (nb_values
> 0 && opt
->opt_args_count_mark
!= nb_values
)
1901 fatal(CTXOPTCTEARG
, "");
1905 if (nb_values
> 0 && opt
->opt_args_count_mark
<= nb_values
)
1906 fatal(CTXOPTCTLARG
, "");
1910 if (nb_values
> 0 && opt
->opt_args_count_mark
>= nb_values
)
1911 fatal(CTXOPTCTGARG
, "");
1919 /* ======================================================================== */
1920 /* Parse a strings describing options and some of their characteristics */
1921 /* The input string must have follow some rules like in the examples below: */
1923 /* "opt_name1 opt_name2" */
1924 /* "[opt_name1] opt_name2" */
1925 /* "[opt_name1] opt_name2..." */
1926 /* "[opt_name1 #...] opt_name2... [#]" */
1927 /* "[opt_name1 [#...]] opt_name2... [#...]" */
1929 /* Where [ ] encloses an optional part, # means: has parameters and ... */
1930 /* means that there can be more than one occurrence of the previous thing. */
1932 /* opt_name can be followed by a 'new context' change prefixed with the */
1933 /* symbol >, as in opt1>c2 by eg */
1935 /* This function returns as soon as one (or no) option has been parsed and */
1936 /* return the offset to the next option to parse. */
1938 /* In case of successful parsing, an new option is allocated and its */
1939 /* pointer returned. */
1940 /* ======================================================================== */
1942 opt_parse(char * s
, opt_t
** opt
)
1944 int opt_optional
= 0;
1945 int opt_multiple
= 0;
1946 int opt_count_matter
= 0;
1947 char opt_count_oper
= '\0';
1948 unsigned opt_count_mark
= 0;
1951 int opt_multiple_args
= 0;
1952 int opt_args_count_matter
= 0;
1953 char opt_args_count_oper
= '\0';
1954 unsigned opt_args_count_mark
= 0;
1955 int opt_optional_args
= 0;
1956 int opt_eval_first
= 0;
1970 memset(opt_arg
, '\0', 33);
1972 /* strip the leading blanks */
1976 if (*s
== '[') /* Start of an optional option */
1981 s
= strtoken(s
, token
, sizeof(token
) - 1, "[^] \n\t.]", &pos
);
1983 return -1; /* empty string */
1985 /* Early EOS, only return success if the option is mandatory */
1986 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1988 if (opt_optional
== 1)
1989 return -(s
- s_orig
- 1);
1991 /* validate the option name */
1992 /* ALPHA+(ALPHANUM|_)* */
1993 /* """""""""""""""""""""""" */
1995 if (!isalpha(*p
) && *p
!= '*')
1996 return -(s
- s_orig
- 1); /* opt_name must start with a letter */
2004 if (!isalnum(*p
) && *p
!= '_' && *p
!= '>')
2005 return -(s
- s_orig
- 1); /* opt_name must contain a letter, *
2006 * a number or a _ */
2011 opt_name
= xstrdup(token
+ 1); /* Ignore the first '*' in token */
2013 opt_name
= xstrdup(token
);
2024 /* Check if it can appear multiple times by looking for the dots */
2025 p
= strtoken(s
, token
, 3, "[.]", &pos
);
2028 if (strcmp(token
, "...") == 0)
2032 if (*s
== '<' || *s
== '=' || *s
== '>')
2037 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2040 opt_count_matter
= 1;
2041 opt_count_oper
= *s
;
2042 opt_count_mark
= value
;
2050 return -(s
- s_orig
- 1);
2054 /* A blank separates the option name and the argument tag */
2066 n
= sscanf(s
, "[%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2067 if (pos
> 1 && *opt_arg
== '#') /* [# has been read */
2070 opt_optional_args
= 1;
2072 opt_multiple_args
= 1; /* there were dots */
2074 s
+= pos
+ !!(n
== 2) * 3; /* skip the dots */
2076 if (*s
== '<' || *s
== '=' || *s
== '>')
2081 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2084 opt_args_count_matter
= 1;
2085 opt_args_count_oper
= *s
;
2086 opt_args_count_mark
= value
;
2091 /* Optional arg tag must end with a ] */
2095 return -(s
- s_orig
- 1);
2098 s
++; /* skip the ] */
2102 n
= sscanf(s
, "%32[^] .\t]%n%3[.]", opt_arg
, &pos
, dots
);
2103 if (pos
> 0 && *opt_arg
== '#') /* # has been read */
2106 if (n
== 2) /* there were dots */
2107 opt_multiple_args
= 1;
2109 s
+= pos
+ !!(n
== 2) * 3; /* skip the dots */
2111 if (*s
== '<' || *s
== '=' || *s
== '>')
2116 n
= sscanf(s
+ 1, "%u%n", &value
, &offset
);
2119 opt_args_count_matter
= 1;
2120 opt_args_count_oper
= *s
;
2121 opt_args_count_mark
= value
;
2129 /* Abort on extraneous ] if the option is mandatory */
2131 return -(s
- s_orig
- 1);
2133 s
++; /* skip the ] */
2135 /* Strip the following blanks */
2141 else if (opt_optional
== 0 && (!*s
|| isblank(*s
)))
2143 /* Strip the following blanks */
2149 else if (opt_args
== 0) /* # was not read it is possibly the start *
2150 * of another option */
2153 return -(s
- s_orig
- 1);
2158 /* strip the following blanks */
2164 if (*opt_name
== '>')
2165 fatal_internal("%s: option name is missing.", opt_name
);
2167 count
= strchrcount(opt_name
, '>');
2170 char * tmp
= strchr(opt_name
, '>');
2171 next_ctx
= xstrdup(tmp
+ 1);
2175 fatal_internal("%s: only one occurrence of '>' is allowed.", opt_name
);
2177 *opt
= xmalloc(sizeof(opt_t
));
2179 (*opt
)->name
= opt_name
;
2180 (*opt
)->optional
= opt_optional
;
2181 (*opt
)->multiple
= opt_multiple
;
2182 (*opt
)->opt_count_matter
= opt_count_matter
;
2183 (*opt
)->opt_count_oper
= opt_count_oper
;
2184 (*opt
)->opt_count_mark
= opt_count_mark
;
2185 (*opt
)->args
= opt_args
;
2186 (*opt
)->arg
= xstrdup(opt_arg
);
2187 (*opt
)->optional_args
= opt_optional_args
;
2188 (*opt
)->multiple_args
= opt_multiple_args
;
2189 (*opt
)->opt_args_count_matter
= opt_args_count_matter
;
2190 (*opt
)->opt_args_count_oper
= opt_args_count_oper
;
2191 (*opt
)->opt_args_count_mark
= opt_args_count_mark
;
2192 (*opt
)->eval_first
= opt_eval_first
;
2193 (*opt
)->next_ctx
= next_ctx
;
2194 (*opt
)->ctx_list
= ll_new();
2195 (*opt
)->constraints_list
= ll_new();
2196 (*opt
)->action
= NULL
;
2197 (*opt
)->data
= NULL
;
2202 /* ===================================================================== */
2203 /* Try to initialize all the option in a given string */
2204 /* Each parsed option are put in a bst tree with its name as index */
2206 /* On collision, the arguments only the signature are required to be */
2207 /* the same else this is considered as an error. Options can be used in */
2208 /* more than one context and can be optional in one and mandatory in */
2210 /* ===================================================================== */
2212 init_opts(char * spec
, ctx_t
* ctx
)
2214 opt_t
* opt
, *bst_opt
;
2220 if ((offset
= opt_parse(spec
, &opt
)) > 0)
2224 if ((node
= bst_find(opt
, &options_bst
, opt_compare
)) != NULL
)
2226 int same_next_ctx
= 0;
2228 bst_opt
= node
->key
; /* node extracted from the BST */
2230 if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
== NULL
)
2232 else if (bst_opt
->next_ctx
== NULL
&& opt
->next_ctx
!= NULL
)
2234 else if (bst_opt
->next_ctx
!= NULL
&& opt
->next_ctx
== NULL
)
2237 same_next_ctx
= strcmp(bst_opt
->next_ctx
, opt
->next_ctx
) == 0;
2239 if (bst_opt
->optional_args
!= opt
->optional_args
2240 || bst_opt
->multiple_args
!= opt
->multiple_args
2241 || bst_opt
->args
!= opt
->args
|| !same_next_ctx
)
2243 fatal_internal("option %s already exists with "
2244 "a different arguments signature.\n",
2252 /* The new occurrence of the option option is legal */
2253 /* append the current context ptr in the list */
2254 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2255 ll_append(bst_opt
->ctx_list
, ctx
);
2257 /* Append the new option to the context's options list */
2258 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2259 ll_append(ctx
->opt_list
, bst_opt
);
2265 /* Initialize the option's context list with the current context */
2266 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2267 ll_append(opt
->ctx_list
, ctx
);
2269 /* Append the new option to the context's options list */
2270 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2271 ll_append(ctx
->opt_list
, opt
);
2273 /* Insert the new option in the BST */
2274 /* """""""""""""""""""""""""""""""" */
2275 bst_search(opt
, &options_bst
, opt_compare
);
2280 char * s
= xstrndup(spec
, -offset
);
2281 printf("%s <---\nSyntax error at or before offset %d\n", s
, -offset
);
2291 /* ==================================================== */
2292 /* ctxopt initialization function, must be called first */
2293 /* ==================================================== */
2295 ctxopt_init(char * prog_name
)
2299 contexts_bst
= NULL
;
2305 user_string
= xmalloc(8);
2306 user_string2
= xmalloc(8);
2309 ctxopt_initialized
= 1;
2311 /* Update current_state */
2312 /* -------------------- */
2313 cur_state
= xcalloc(sizeof(state_t
), 0);
2315 /* Initialize custom error function pointers to NULL */
2316 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2317 err_functions
= xmalloc(CTXOPTERRSIZ
* sizeof(void *));
2318 for (n
= 0; n
< CTXOPTERRSIZ
; n
++)
2319 err_functions
[n
] = NULL
;
2321 /* Update current_state */
2322 /* -------------------- */
2325 if (*prog_name
== '\0')
2326 cur_state
->prog_name
= xstrdup("program_name");
2327 else if ((ptr
= strrchr(prog_name
, '/')))
2328 cur_state
->prog_name
= xstrdup(ptr
+ 1);
2330 cur_state
->prog_name
= xstrdup(prog_name
);
2333 cur_state
->prog_name
= xstrdup("program_name");
2336 /* ========================================================================= */
2337 /* Utility function which create and register a par_t object in a bst */
2338 /* embedded in a context. */
2339 /* This object will have a name and a pointer to the option it refers to. */
2340 /* These object will be used to quickly find an option from a command */
2341 /* line parameter during the analysis phase. */
2343 /* IN : an option name. */
2344 /* IN : a string of command line parameters to associate to the option. */
2345 /* Returns : 1 is all was fine else 0. */
2346 /* ========================================================================= */
2348 opt_set_parms(char * opt_name
, char * par_str
)
2350 char * par_name
, *ctx_name
;
2351 char * tmp_par_str
, *end_tmp_par_str
;
2355 par_t
* par
, tmp_par
;
2356 int rc
= 1; /* return code */
2361 /* Look is the given option is defined */
2362 /* """"""""""""""""""""""""""""""""""" */
2363 opt
= locate_opt(opt_name
);
2365 fatal_internal("Unknown option %s", opt_name
);
2367 /* For each context using this option */
2368 /* """""""""""""""""""""""""""""""""" */
2369 list
= opt
->ctx_list
;
2372 while (lnode
!= NULL
)
2374 /* Locate the context in the contexts tree */
2375 /* """"""""""""""""""""""""""""""""""""""" */
2376 ctx_name
= ((ctx_t
*)(lnode
->data
))->name
;
2378 ctx
= locate_ctx(ctx_name
);
2380 fatal_internal("Unknown context %s", ctx_name
);
2383 void * par_bst
= ctx
->par_bst
;
2385 tmp_par_str
= xstrdup(par_str
);
2386 ltrim(tmp_par_str
, " \t");
2387 rtrim(tmp_par_str
, " \t", 0);
2388 par_name
= xstrtok_r(tmp_par_str
, " \t,", &end_tmp_par_str
);
2389 if (par_name
== NULL
)
2390 fatal_internal("Parameters are missing for option %s", opt_name
);
2392 /* For each parameter given in par_str, create an par_t object and */
2393 /* insert it the in the parameters bst of the context. */
2394 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2395 while (par_name
!= NULL
)
2397 tmp_par
.name
= par_name
;
2399 node
= bst_find(&tmp_par
, &par_bst
, par_compare
);
2402 fatal_internal("The parameter %s is already defined in context %s",
2403 par_name
, ctx
->name
);
2408 par
= xmalloc(sizeof(par_t
));
2409 par
->name
= xstrdup(par_name
);
2410 par
->opt
= opt
; /* Link the option to this parameter */
2412 bst_search(par
, &par_bst
, par_compare
);
2414 par_name
= xstrtok_r(NULL
, " \t,", &end_tmp_par_str
);
2417 /* Update the value of the root of ctx->par_bst as it may have */
2419 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2420 ctx
->par_bst
= par_bst
;
2424 lnode
= lnode
->next
;
2430 /* ==================================================================== */
2431 /* Creation of a new context instance */
2432 /* IN ctx : a context pointer to allow this instance to */
2433 /* access the context fields */
2434 /* IN prev_ctx_inst : the context instance whose option leading to the */
2435 /* creation of this new context instance is part of */
2436 /* Returns : the new context. */
2437 /* ==================================================================== */
2439 new_ctx_inst(ctx_t
* ctx
, ctx_inst_t
* prev_ctx_inst
)
2442 opt_inst_t
* gen_opt_inst
;
2443 ctx_inst_t
* ctx_inst
;
2444 seen_opt_t
* seen_opt
;
2445 char * str
, *opt_name
;
2449 /* Keep a trace of the opt_inst which was at the origin of the creation */
2450 /* of this context instance. */
2451 /* This will serve during the evaluation of the option callbacks */
2452 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2453 if (prev_ctx_inst
!= NULL
)
2455 gen_opt_inst
= (opt_inst_t
*)(prev_ctx_inst
->opt_inst_list
->tail
->data
);
2456 /* Update current_state */
2457 /* -------------------- */
2458 cur_state
->opt_name
= gen_opt_inst
->opt
->name
;
2461 gen_opt_inst
= NULL
;
2463 /* Create and initialize the new context instance */
2464 /* """""""""""""""""""""""""""""""""""""""""""""" */
2465 ctx_inst
= xmalloc(sizeof(ctx_inst_t
));
2466 ctx_inst
->ctx
= ctx
;
2467 ctx_inst
->prev_ctx_inst
= prev_ctx_inst
;
2468 ctx_inst
->gen_opt_inst
= gen_opt_inst
;
2469 ctx_inst
->incomp_bst_list
= ll_new();
2470 ctx_inst
->opt_inst_list
= ll_new();
2471 ctx_inst
->seen_opt_bst
= NULL
;
2475 if (prev_ctx_inst
== NULL
)
2476 first_ctx_inst
= ctx_inst
;
2478 /* Initialize the occurrence counters of each opt allowed in the context */
2479 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2480 node
= ctx
->opt_list
->head
;
2481 while (node
!= NULL
)
2484 opt
->occurrences
= 0;
2489 /* Initialize the bst containing the seen indicator for all the options */
2490 /* allowed in this context instance. */
2491 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2492 node
= ctx
->opt_list
->head
;
2493 while (node
!= NULL
)
2496 seen_opt
= xmalloc(sizeof(seen_opt_t
));
2497 seen_opt
->opt
= opt
;
2500 bst_search(seen_opt
, &(ctx_inst
->seen_opt_bst
), seen_opt_compare
);
2505 /* Initialize the bst containing the incompatibles options */
2506 /* Incompatibles option names are read from strings found in the list */
2507 /* incomp_list present in each instance of ctx_t. */
2508 /* These names are then used to search for the object of type seen_opt_t */
2509 /* which is already present in the seen_opt_bst of the context instance. */
2511 /* Once found the seen_opt_t object in inserted in the new bst */
2512 /* At the end the new bst in addes to the list incomp_bst_list. */
2513 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2514 node
= ctx
->incomp_list
->head
;
2515 while (node
!= NULL
)
2518 seen_opt_t tmp_seen_opt
;
2520 str
= xstrdup(node
->data
);
2522 rtrim(str
, " \t", 0);
2523 opt_name
= strtok(str
, " \t"); /* extract the first option name */
2525 while (opt_name
!= NULL
) /* for each option name */
2527 if ((opt
= locate_opt(opt_name
)) != NULL
)
2529 /* The option found is searched in the tree of potential */
2531 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2532 tmp_seen_opt
.opt
= opt
;
2534 bst_node
= bst_find(&tmp_seen_opt
, &(ctx_inst
->seen_opt_bst
),
2537 if (bst_node
!= NULL
)
2539 /* if found it, is added into the new bst tree */
2540 /* """"""""""""""""""""""""""""""""""""""""""" */
2541 seen_opt
= bst_node
->key
;
2542 bst_search(seen_opt
, &bst
, seen_opt_compare
);
2545 /* Not found! That means that the option is unknown in this */
2546 /* context as all options has have a seen_opt structure in */
2548 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2549 fatal_internal("%s is not known in the context %s", opt
->name
,
2553 fatal_internal("%s: unknown option.", opt_name
);
2555 opt_name
= strtok(NULL
, " \t");
2559 ll_append(ctx_inst
->incomp_bst_list
, bst
);
2567 /* ======================================================================== */
2568 /* Create a list formed by all the significant command line words */
2569 /* Words beginning or ending with ^,{ or } are split. Each of these */
2570 /* symbols will get their own place in the list */
2572 /* - ^ forces the next option to be evaluated in the first context */
2573 /* - the {...} part delimits a context, the { will not appear in the list */
2574 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2575 /* to facilitate the parsing phase. | must not be used by the end user */
2577 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2578 /* program name is not considered */
2579 /* IN words : is the array of strings constituting the command line to */
2581 /* Returns : 1 on success, 0 if a { or } is missing. */
2582 /* ======================================================================== */
2584 ctxopt_build_cmdline_list(int nb_words
, char ** words
)
2587 char * prev_word
= NULL
;
2591 ll_node_t
*node
, *start_node
;
2593 /* The analysis is divided into three passes, this is not optimal but */
2594 /* must be done only one time. Doing that we privilege readability. */
2596 /* In the following, SG is the ascii character 1d (dec 29) */
2598 /* The first pass creates the list, extract the leading an trailing */
2599 /* SG '{' and '}' of each word and give them their own place in the */
2602 /* The second pass transform the '{...}' blocks by a trailing SG */
2603 /* ({...} -> ...|) */
2605 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2606 /* the middle in the remaining list elements and recreate the pseudo */
2608 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2610 /* If the option list is not empty, clear it before going further */
2611 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2613 if (cmdline_list
!= NULL
)
2615 node
= cmdline_list
->head
;
2616 while (node
!= NULL
)
2619 ll_delete(cmdline_list
, node
);
2622 node
= cmdline_list
->head
;
2626 cmdline_list
= ll_new();
2628 start_node
= cmdline_list
->head
; /* in the following loop start_node will *
2629 * contain a pointer to the current *
2630 * word stripped from its leading *
2631 * sequence of {, } or ^ */
2632 for (i
= 0; i
< nb_words
; i
++)
2634 size_t len
= strlen(words
[i
]);
2640 /* Replace each occurrence of the legal word {} by the characters */
2641 /* 0x02 and 0x03 to hide them from the following process. */
2642 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2643 while ((ptr
= strstr(str
, "{}")) != NULL
)
2645 *ptr
= 0x02; /* arbitrary values unlikely */
2646 *(ptr
+ 1) = 0x03; /* present in a word */
2649 if (len
> 1) /* The word contains at least 2 characters */
2653 /* Interpret its beginning and look for the start of the real word */
2654 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2655 while (start
<= len
- 1 && (str
[start
] == '{' || str
[start
] == '}'))
2657 ll_append(cmdline_list
, xstrndup(str
+ start
, 1));
2659 start_node
= cmdline_list
->tail
;
2663 if (str
[end
] == '{' || str
[end
] == '}')
2665 if (end
> 0 && str
[end
- 1] != '\\')
2667 ll_append(cmdline_list
, xstrndup(str
+ end
, 1));
2669 node
= cmdline_list
->tail
;
2671 while (str
[end
] == '{' || str
[end
] == '}')
2673 if (end
> start
&& str
[end
- 1] == '\\')
2676 ll_insert_before(cmdline_list
, node
, xstrndup(str
+ end
, 1));
2685 if (start_node
!= NULL
)
2686 ll_insert_after(cmdline_list
, start_node
,
2687 xstrndup(str
+ start
, end
- start
+ 1));
2689 ll_append(cmdline_list
, xstrndup(str
+ start
, end
- start
+ 1));
2690 start_node
= cmdline_list
->tail
;
2695 ll_append(cmdline_list
, xstrdup(str
));
2696 start_node
= cmdline_list
->tail
;
2702 node
= cmdline_list
->head
;
2705 while (node
!= NULL
)
2709 if (strcmp(word
, "{") == 0)
2711 ll_node_t
* old_node
= node
;
2714 ll_delete(cmdline_list
, old_node
);
2718 else if (strcmp(word
, "}") == 0)
2736 node
= cmdline_list
->head
;
2738 while (node
!= NULL
)
2742 /* Restore the original { and } characters forming the legal word {} */
2743 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2744 while ((ptr
= strchr(word
, 0x02)) != NULL
)
2746 while ((ptr
= strchr(word
, 0x03)) != NULL
)
2749 /* Remove a SG if the previous element is SG */
2750 /* """"""""""""""""""""""""""""""""""""""""" */
2751 if (strcmp(word
, "\x1d") == 0)
2753 if (prev_word
!= NULL
&& (strcmp(prev_word
, "\x1d") == 0))
2755 ll_node_t
* old_node
= node
;
2757 ll_delete(cmdline_list
, old_node
);
2758 free(old_node
->data
);
2762 else if (strcmp(word
, "-") == 0) /* a single - is a legal argument, not *
2763 * a parameter. Protect it */
2766 node
->data
= xstrdup("\\-");
2769 prev_word
= node
->data
;
2773 /* Clean useless and SG at the beginning and end of list */
2774 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2775 node
= cmdline_list
->head
;
2782 if (strcmp(word
, "\x1d") == 0)
2784 ll_delete(cmdline_list
, node
);
2789 node
= cmdline_list
->tail
;
2795 if (strcmp(word
, "\x1d") == 0)
2797 ll_delete(cmdline_list
, node
);
2805 /* ===================================================================== */
2806 /* Build and analyze the command line list */
2807 /* function and create the linked data structures whose data will be */
2808 /* evaluated later by ctxopt_evaluate. */
2809 /* This function identifies the following errors and creates an array of */
2810 /* The remaining unanalyzed arguments. */
2811 /* - detect missing arguments */
2812 /* - detect too many arguments */
2813 /* - detect unknown parameters in a context */
2814 /* - detect too many occurrences of a parameters in a context */
2815 /* - detect missing required arguments in a context */
2817 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2818 /* program name is not considered */
2819 /* IN words : is the array of strings constituting the command line to */
2821 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
2822 /* is present in the list. */
2823 /* OUT rem_args : array of remaining command line arguments if a -- */
2824 /* is present in the list. This array must be free by */
2825 /* The caller as it is allocated here. */
2826 /* ===================================================================== */
2828 ctxopt_analyze(int nb_words
, char ** words
, int * nb_rem_args
,
2834 ctx_inst_t
* ctx_inst
;
2835 opt_inst_t
* opt_inst
;
2838 int expect_par_or_arg
= 0;
2840 ll_node_t
* cli_node
;
2842 seen_opt_t
* bst_seen_opt
;
2848 if (!ctxopt_build_cmdline_list(nb_words
, words
))
2850 "The command line could not be parsed: missing { or } detected.");
2852 if (main_ctx
== NULL
)
2853 fatal_internal("At least one context must have been created.");
2855 /* Check that all options has an action and at least one parameter */
2856 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2857 bst_walk(options_bst
, bst_check_opt_cb
);
2859 /* Create the first ctx_inst record */
2860 /* """""""""""""""""""""""""""""""" */
2863 ctx_inst_list
= ll_new();
2864 ctx_inst
= new_ctx_inst(ctx
, NULL
);
2865 ctx_inst
->par_name
= NULL
;
2867 /* Update current_state */
2868 /* -------------------- */
2869 cur_state
->ctx_name
= ctx
->name
;
2871 ll_append(ctx_inst_list
, ctx_inst
);
2873 /* For each node in the command line */
2874 /* """"""""""""""""""""""""""""""""" */
2875 cli_node
= cmdline_list
->head
;
2877 while (cli_node
!= NULL
)
2879 if (strcmp(cli_node
->data
, "--") == 0)
2880 break; /* No new parameter will be analyze after this point */
2882 par_name
= cli_node
->data
;
2884 if (strcmp(par_name
, "\x1d") == 0)
2886 check_for_missing_mandatory_opt(ctx_inst
, (char *)(cli_node
->prev
->data
));
2887 check_for_occurrences_issues(ctx_inst
);
2889 /* Forced backtracking to the previous context instance */
2890 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2891 if (ctx_inst
->prev_ctx_inst
!= NULL
)
2893 ctx_inst
= ctx_inst
->prev_ctx_inst
;
2894 ctx
= ctx_inst
->ctx
;
2896 /* Update current_state */
2897 /* -------------------- */
2898 cur_state
->ctx_name
= ctx
->name
;
2899 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
2903 /* Update current_state */
2904 /* -------------------- */
2905 cur_state
->ctx_par_name
= NULL
;
2908 else if (expect_par
&& *par_name
== '-')
2913 /* Update current_state */
2914 /* -------------------- */
2915 cur_state
->cur_opt_par_name
= par_name
;
2916 cur_state
->ctx_name
= ctx
->name
;
2917 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
2919 /* An expected parameter has been seen */
2920 /* """"""""""""""""""""""""""""""""""" */
2921 if ((par
= locate_par(par_name
, ctx
)) == NULL
)
2926 /* See if this parameter is an unique abbreviation of a longer */
2927 /* parameter. If this is the case then just replace it with its */
2928 /* full length version and try again. */
2929 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2930 if ((word
= abbrev_expand(par_name
, ctx
)) != NULL
)
2932 cli_node
->data
= word
;
2936 /* Try to find a prefix which is a valid parameter in this context */
2937 /* If found, split the cli_node in two to build a new parameter */
2938 /* node and followed by a node containing the remaining string */
2939 /* If the new parameter corresponds to an option not taking */
2940 /* argument then prefix the remaining string whit a dash as it may */
2941 /* contain a new parameter. */
2942 /* The new parameter will be re-evaluated in the next iteration of */
2944 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2945 prefix
= look_for_valid_prefix_in_word(par_name
, ctx
, &pos
, &popt
);
2946 if (prefix
!= NULL
&& pos
!= 0)
2948 cli_node
->data
= prefix
; /* prefix contains le name of a valid *
2949 | parameter in this context. */
2953 /* The parameter may be followed by arguments */
2954 /* '''''''''''''''''''''''''''''''''''''''''' */
2955 if (*(par_name
+ pos
) == '-')
2957 word
= xstrdup("\\"); /* Protect the '-' */
2958 word
= strappend(word
, par_name
+ pos
, NULL
);
2961 word
= xstrdup(par_name
+ pos
);
2965 /* The parameter does not take arguments, the */
2966 /* following word must be a parameter or nothing */
2967 /* hence prefix it with a dash. */
2968 /* ''''''''''''''''''''''''''''''''''''''''''''' */
2969 word
= xstrdup("-");
2970 word
= strappend(word
, par_name
+ pos
, NULL
);
2973 /* Insert it after the current node in the list */
2974 /* """""""""""""""""""""""""""""""""""""""""""" */
2975 ll_insert_after(cmdline_list
, cli_node
, word
);
2977 continue; /* loop */
2981 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
2982 check_for_occurrences_issues(ctx_inst
);
2984 if (ctx_inst
->prev_ctx_inst
== NULL
)
2986 char * errmsg
= xstrdup("");
2988 /* Update current_state */
2989 /* -------------------- */
2990 cur_state
->ctx_par_name
= NULL
;
2992 *user_string
= '\0';
2993 *user_string2
= '\0';
2995 user_string
= strappend(user_string
, par_name
, NULL
);
2997 bst_walk(contexts_bst
, bst_match_par_cb
);
2999 if (*user_string2
!= '\0')
3003 "\nIt appears to be defined in the context(s):", user_string2
,
3007 fatal(CTXOPTUNKPAR
, errmsg
);
3011 /* Try to backtrack and analyse the same parameter in the */
3012 /* previous context. */
3013 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3014 ctx_inst
= ctx_inst
->prev_ctx_inst
;
3015 ctx
= ctx_inst
->ctx
;
3017 /* Update current_state */
3018 /* -------------------- */
3019 cur_state
->ctx_name
= ctx
->name
;
3020 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3022 cli_node
= cli_node
->prev
;
3028 seen_opt_t seen_opt
;
3030 /* The parameter is legal in the context, create a opt_inst and */
3031 /* append it to the ctx_inst list options list */
3032 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3037 opt_inst
= xmalloc(sizeof(opt_inst_t
));
3038 opt_inst
->opt
= opt
;
3039 opt_inst
->par
= par_name
;
3040 opt_inst
->values_list
= ll_new();
3041 opt_inst
->next_ctx_inst
= NULL
;
3043 /* Priority option inserted at the start of the opt_inst list */
3044 /* but their order of appearance in the context definition must */
3045 /* be preserver so each new priority option will be placed after */
3046 /* the previous ones at the start of the opt_inst list. */
3047 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3048 if (!opt
->eval_first
)
3049 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3052 ll_node_t
* node
= ctx_inst
->opt_inst_list
->head
;
3053 opt_inst_t
* tmp_opt_inst
;
3054 while (node
!= NULL
)
3056 tmp_opt_inst
= node
->data
;
3057 if (!tmp_opt_inst
->opt
->eval_first
)
3059 ll_insert_before(ctx_inst
->opt_inst_list
, node
, opt_inst
);
3066 ll_append(ctx_inst
->opt_inst_list
, opt_inst
);
3069 /* Check if an option was already seen in the */
3070 /* current context instance */
3071 /* """""""""""""""""""""""""""""""""""""""""" */
3074 bst_node
= bst_find(&seen_opt
, &(ctx_inst
->seen_opt_bst
),
3077 /* bst_node cannot be NULL here */
3079 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3081 if (!opt
->multiple
&& bst_seen_opt
->seen
== 1)
3082 fatal(CTXOPTDUPOPT
, NULL
);
3084 /* Check if this option is compatible with the options already */
3085 /* seen in this context instance. */
3086 /* Look if the option is present in one on the bst present in */
3087 /* the incomp_bst_list of the context instance */
3089 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3090 node
= ctx_inst
->incomp_bst_list
->head
;
3091 while (node
!= NULL
)
3096 /* They can only have one seen_opt object in the bst tree was */
3097 /* already seen, try to locate it, the result will be put in */
3098 /* user_object by the bst_seen_opt_seen_cb function */
3099 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3100 bst_walk(bst
, bst_seen_opt_seen_cb
);
3102 /* It it is the case, look if the current option is also */
3104 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3105 if (user_object
!= NULL
)
3107 bst_node
= bst_find(bst_seen_opt
, &bst
, seen_opt_compare
);
3109 if (bst_node
!= NULL
)
3111 bst_seen_opt
= (seen_opt_t
*)(bst_node
->key
);
3112 if (bst_seen_opt
->seen
== 0)
3113 fatal(CTXOPTINCOPT
, (char *)user_object
);
3120 /* Mark this option seen in the current context instance */
3121 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3122 bst_seen_opt
->seen
= 1;
3123 bst_seen_opt
->par
= xstrdup(par_name
);
3125 /* If this option leads to a next context, create a new ctx_inst */
3126 /* and switch to it for the analyse of the future parameter */
3127 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3128 if (opt
->next_ctx
!= NULL
)
3130 ctx
= locate_ctx(opt
->next_ctx
);
3133 fatal_internal("%s: unknown context.", opt
->next_ctx
);
3135 opt_inst
->next_ctx_inst
= ctx_inst
= new_ctx_inst(ctx
, ctx_inst
);
3136 ctx_inst
->par_name
= xstrdup(par_name
);
3138 ll_append(ctx_inst_list
, ctx_inst
);
3141 /* See is we must expect some arguments */
3142 /* """""""""""""""""""""""""""""""""""" */
3143 expect_par_or_arg
= 0;
3148 expect_par
= 1; /* Parameter doesn't accept any argument */
3151 if (!opt
->optional_args
)
3152 expect_arg
= 1; /* Parameter has mandatory arguments */
3154 expect_par_or_arg
= 1; /* Parameter has optional arguments */
3158 else if (expect_par
&& *par_name
!= '-')
3160 ll_node_t
* n
= cli_node
->next
;
3162 /* Look if potential arguments must still be analyzed until the */
3163 /* end of the context/command line part to analyze/command line. */
3164 /* If this is the case we have met an extra argument. */
3165 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3168 if (strcmp(n
->data
, "--") == 0 || strcmp(n
->data
, "\x1d") == 0)
3169 fatal(CTXOPTUNXARG
, NULL
);
3171 if (*(char *)(n
->data
) == '-')
3172 fatal(CTXOPTUNXARG
, NULL
);
3177 break; /* an unexpected non parameter was seen, if no Potential *
3178 | arguments remain in the command line assume that it *
3179 | is is the first of the non arguments and stop the *
3180 | command line analysis. */
3182 else if (expect_arg
&& *par_name
!= '-')
3184 ll_node_t
* cstr_node
;
3185 constraint_t
* cstr
;
3187 /* Check if the arguments of the option respects */
3188 /* the attached constraints if any */
3189 /* """"""""""""""""""""""""""""""""""""""""""""" */
3190 cstr_node
= opt
->constraints_list
->head
;
3191 while (cstr_node
!= NULL
)
3193 cstr
= cstr_node
->data
;
3194 if (!cstr
->constraint(cstr
->nb_args
, cstr
->args
, par_name
))
3197 cstr_node
= cstr_node
->next
;
3200 /* If the argument is valid, store it */
3201 /* """""""""""""""""""""""""""""""""" */
3202 if (*par_name
== '\\' && *(par_name
+ 1) == '-')
3203 ll_append(opt_inst
->values_list
, par_name
+ 1);
3205 ll_append(opt_inst
->values_list
, par_name
);
3209 expect_par_or_arg
= 0;
3211 if (opt
->multiple_args
)
3212 expect_par_or_arg
= 1;
3214 expect_par
= 1; /* Parameter takes only one argument */
3216 else if (expect_arg
&& *par_name
== '-')
3217 fatal(CTXOPTMISARG
, NULL
);
3218 else if (expect_par_or_arg
)
3222 expect_par_or_arg
= 0;
3224 if (*par_name
!= '-')
3225 expect_arg
= 1; /* Consider this word as an argument and retry */
3227 expect_par
= 1; /* Consider this word as a parameter and retry */
3229 cli_node
= cli_node
->prev
;
3232 cli_node
= cli_node
->next
;
3235 if (cmdline_list
->len
> 0 && *par_name
== '-')
3237 if (expect_arg
&& !opt
->optional_args
)
3238 fatal(CTXOPTMISARG
, NULL
);
3241 /* Look if a context_instance has unseen mandatory options */
3242 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3243 node
= ctx_inst_list
->head
;
3244 while (node
!= NULL
)
3246 ctx_inst
= (ctx_inst_t
*)(node
->data
);
3248 /* Update current_state */
3249 /* -------------------- */
3250 cur_state
->ctx_name
= ctx_inst
->ctx
->name
;
3251 cur_state
->ctx_par_name
= ctx_inst
->par_name
;
3253 check_for_missing_mandatory_opt(ctx_inst
, par_name
);
3254 check_for_occurrences_issues(ctx_inst
);
3259 /* Allocate the array containing the remaining not analyzed */
3260 /* command line arguments. */
3261 /* NOTE: The strings in the array are just pointer to the */
3262 /* data of the generating list and must not be freed. */
3263 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3264 if (cli_node
!= NULL
)
3266 if (strcmp((char *)cli_node
->data
, "--") == 0)
3267 /* The special parameter -- was encountered, the -- argument is not */
3268 /* put in the remaining arguments. */
3269 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3270 ll_strarray(cmdline_list
, cli_node
->next
, nb_rem_args
, rem_args
);
3272 /* A non parameter was encountered when a parameter was expected. We */
3273 /* assume that the evaluation of the remaining command line argument */
3274 /* are not the responsibility of the users code. */
3275 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3276 ll_strarray(cmdline_list
, cli_node
, nb_rem_args
, rem_args
);
3281 *rem_args
= xmalloc(sizeof(char *));
3282 (*rem_args
)[0] = NULL
;
3286 /* ===================================================================== */
3287 /* Parses the options data structures and launches the callback function */
3288 /* attached to each options instances. */
3289 /* This calls a recursive function which proceeds context per context. */
3290 /* ===================================================================== */
3292 ctxopt_evaluate(void)
3294 evaluate_ctx_inst(first_ctx_inst
);
3297 /* =================================================================== */
3298 /* Recursive function called by ctxopt_evaluate to process the list of */
3299 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3300 /* action attached to the context and its option instances */
3301 /* =================================================================== */
3303 evaluate_ctx_inst(ctx_inst_t
* ctx_inst
)
3305 opt_inst_t
* opt_inst
;
3308 ll_node_t
* opt_inst_node
;
3312 if (ctx_inst
== NULL
)
3315 ctx
= ctx_inst
->ctx
;
3317 /* Do not evaluate the action attached to this context is there is no */
3318 /* option to evaluate. */
3319 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3320 opt_inst_node
= ctx_inst
->opt_inst_list
->head
;
3321 if (opt_inst_node
== NULL
)
3324 /* Call the entering action attached to this context if any */
3325 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3326 if (ctx
->action
!= NULL
)
3328 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3329 ctx
->action(ctx
->name
, entering
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3330 ctx
->nb_data
, ctx
->data
);
3332 ctx
->action(ctx
->name
, entering
, NULL
, ctx
->nb_data
, ctx
->data
);
3335 /* For each instance of options */
3336 /* """""""""""""""""""""""""""" */
3337 while (opt_inst_node
!= NULL
)
3339 opt_inst
= (opt_inst_t
*)(opt_inst_node
->data
);
3340 ll_strarray(opt_inst
->values_list
, opt_inst
->values_list
->head
, &nb_args
,
3342 opt
= opt_inst
->opt
;
3344 /* Launch the attached action if any */
3345 /* """"""""""""""""""""""""""""""""" */
3346 if (opt
->action
!= NULL
)
3347 opt
->action(ctx
->name
, opt
->name
, opt_inst
->par
, nb_args
, args
,
3348 opt
->nb_data
, opt
->data
, ctx
->nb_data
, ctx
->data
);
3350 if (opt_inst
->next_ctx_inst
!= NULL
)
3351 evaluate_ctx_inst(opt_inst
->next_ctx_inst
);
3356 opt_inst_node
= opt_inst_node
->next
;
3359 /* Call the exiting action attached to this context if any */
3360 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3361 if (ctx
->action
!= NULL
)
3363 if (ctx_inst
->prev_ctx_inst
!= NULL
)
3364 ctx
->action(ctx
->name
, exiting
, ctx_inst
->prev_ctx_inst
->ctx
->name
,
3365 ctx
->nb_data
, ctx
->data
);
3367 ctx
->action(ctx
->name
, exiting
, NULL
, ctx
->nb_data
, ctx
->data
);
3371 /* =========================================================== */
3372 /* Create and initialize a new context */
3373 /* - allocate space */
3375 /* - initialize its option with a few of their characteristics */
3376 /* =========================================================== */
3378 ctxopt_new_ctx(char * name
, char * opts_specs
)
3384 if (!ctxopt_initialized
)
3385 fatal_internal("Please call ctxopt_init first.");
3387 ctx
= xmalloc(sizeof(ctx_t
));
3389 /* validate the context name */
3390 /* ALPHA+(ALPHANUM|_)* */
3391 /* """"""""""""""""""""""""" */
3394 fatal_internal("%s: a context name must start with a letter.", name
);
3399 if (!isalnum(*p
) && *p
!= '_')
3400 fatal_internal("%s: a context name must only contain letters, "
3406 ctx
->name
= xstrdup(name
);
3407 ctx
->opt_list
= ll_new(); /* list of options legit in this context */
3408 ctx
->incomp_list
= ll_new(); /* list of incompatible options strings */
3409 ctx
->par_bst
= NULL
;
3413 /* The first created context is the main one */
3414 /* """"""""""""""""""""""""""""""""""""""""" */
3415 if (contexts_bst
== NULL
)
3419 cur_state
->ctx_name
= ctx
->name
;
3422 if (init_opts(opts_specs
, ctx
) == 0)
3424 if ((node
= bst_find(ctx
, &contexts_bst
, ctx_compare
)) != NULL
)
3425 fatal_internal("The context %s already exists", name
);
3427 bst_search(ctx
, &contexts_bst
, ctx_compare
);
3430 /* ==================================================== */
3431 /* Display a usage screen limited to a specific context */
3432 /* IN: the context name. */
3433 /* IN: what to do after (continue or exit the program) */
3434 /* possible values: continue_after, exit_after. */
3435 /* ==================================================== */
3437 ctxopt_ctx_disp_usage(char * ctx_name
, usage_behaviour action
)
3442 int has_optional
= 0;
3443 int has_ellipsis
= 0;
3445 int has_generic_arg
= 0;
3446 int has_ctx_change
= 0;
3447 int has_early_eval
= 0;
3449 ctx
= locate_ctx(ctx_name
);
3451 fatal_internal("%s: unknown context.", ctx_name
);
3453 if (cur_state
->ctx_par_name
== NULL
)
3454 printf("\nSynopsis:\n%s \\\n", cur_state
->prog_name
);
3456 printf("\nSynopsis for the context introduced by %s:\n",
3457 cur_state
->ctx_par_name
);
3459 list
= ctx
->opt_list
;
3460 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3461 &has_ctx_change
, &has_early_eval
);
3463 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3464 has_optional
, has_ellipsis
, has_rule
);
3466 if (action
== exit_after
)
3470 /* ==================================================== */
3471 /* Display a full usage screen about all contexts. */
3472 /* IN: what to do after (continue or exit the program) */
3473 /* possible values: continue_after, exit_after. */
3474 /* ==================================================== */
3476 ctxopt_disp_usage(usage_behaviour action
)
3479 int has_optional
= 0;
3480 int has_ellipsis
= 0;
3482 int has_generic_arg
= 0;
3483 int has_ctx_change
= 0;
3484 int has_early_eval
= 0;
3486 if (main_ctx
== NULL
)
3487 fatal_internal("At least one context must have been created.");
3489 /* Usage for the first context */
3490 /* """"""""""""""""""""""""""" */
3491 printf("\nAllowed options in the default context:\n");
3492 list
= main_ctx
->opt_list
;
3493 print_options(list
, &has_optional
, &has_ellipsis
, &has_rule
, &has_generic_arg
,
3494 &has_ctx_change
, &has_early_eval
);
3496 /* Usage for the other contexts */
3497 /* """""""""""""""""""""""""""" */
3498 bst_walk(contexts_bst
, bst_print_ctx_cb
);
3500 /* Contextual syntactic explanations */
3501 /* """"""""""""""""""""""""""""""""" */
3502 print_explanations(has_early_eval
, has_ctx_change
, has_generic_arg
,
3503 has_optional
, has_ellipsis
, has_rule
);
3505 if (action
== exit_after
)
3509 /* ************************************** */
3510 /* Built-in constraint checking functions */
3511 /* ************************************** */
3513 /* ================================================================ */
3514 /* This constraint checks if each arguments respects a format as */
3515 /* defined for the scanf function. */
3516 /* return 1 if yes and 0 if no */
3517 /* ================================================================ */
3519 ctxopt_format_constraint(int nb_args
, char ** args
, char * value
)
3528 fatal_internal("Format constraint: invalid number of parameters.");
3530 if (strlen(value
) > 255)
3533 format
= xstrdup(args
[0]);
3534 format
= strappend(format
, "%c", NULL
);
3536 rc
= sscanf(value
, format
, x
, &y
);
3538 fatal_internal("The argument %s does not respect the imposed format: %s",
3546 /* ================================================================== */
3547 /* This constraint checks if each arguments of the option instance is */
3548 /* between a minimum and a maximum (inclusive). */
3549 /* return 1 if yes and 0 if no */
3550 /* ================================================================== */
3552 ctxopt_re_constraint(int nb_args
, char ** args
, char * value
)
3558 "Regular expression constraint: invalid number of parameters.");
3560 if (regcomp(&re
, args
[0], REG_EXTENDED
) != 0)
3561 fatal_internal("Invalid regular expression %s", args
[0]);
3563 if (regexec(&re
, value
, (size_t)0, NULL
, 0) != 0)
3565 fatal_internal("The argument %s doesn't match the constraining "
3566 "regular expression %s",
3576 /* ================================================================== */
3577 /* This constraint checks if each arguments of the option instance is */
3578 /* between a minimum and a maximum (inclusive). */
3579 /* return 1 if yes and 0 if no */
3580 /* ================================================================== */
3582 ctxopt_range_constraint(int nb_args
, char ** args
, char * value
)
3593 fatal_internal("Range constraint: invalid number of parameters.");
3595 if (strcmp(args
[0], ".") == 0)
3598 n
= sscanf(args
[0], "%ld%c", &min
, &c
);
3600 if (!max_only
&& n
!= 1)
3601 fatal_internal("Range constraint: min: invalid parameters.");
3603 if (strcmp(args
[1], ".") == 0)
3606 n
= sscanf(args
[1], "%ld%c", &max
, &c
);
3608 if (!min_only
&& n
!= 1)
3609 fatal_internal("Range constraint: max: invalid parameters.");
3611 if (min_only
&& max_only
)
3612 fatal_internal("Range constraint: invalid parameters.");
3615 v
= strtol(value
, &ptr
, 10);
3616 if (errno
|| ptr
== value
)
3623 fatal_internal("%ld is not greater or equal to %ld as requested.", v
,
3634 fatal_internal("%ld is not lower or equal to %ld as requested.", v
, max
);
3640 else if (v
< min
|| v
> max
)
3642 fatal_internal("%ld is not between %ld and %ld as requested.", v
, min
, max
);
3646 return 1; /* check passed */
3649 /* ============================================================== */
3650 /* This function provides a way to set the behaviour of a context */
3651 /* ============================================================== */
3653 ctxopt_add_global_settings(settings s
, ...)
3660 case error_functions
:
3662 void (*function
)(errors e
, state_t
* state
);
3665 e
= va_arg(args
, errors
);
3666 function
= va_arg(args
, void (*)(errors e
, state_t
* state
));
3667 err_functions
[e
] = function
;
3677 /* ================================================================ */
3678 /* This function provides a way to set the behaviour of an option */
3679 /* It can take a variable number of arguments according to its */
3680 /* first argument: */
3682 /* o a string containing an option name and all its possible */
3683 /* parameters separates by spaces, tabs or commas (char *) */
3684 /* (e.g: "help -h -help") */
3686 /* o a string containing an option name */
3687 /* o a pointer to a function which will be called at evaluation */
3689 /* - constraints: */
3690 /* o a string containing an option name */
3691 /* o a pointer to a function to check if an argument is valid. */
3692 /* o a strings containing the arguments to this function. */
3693 /* ================================================================ */
3695 ctxopt_add_opt_settings(settings s
, ...)
3705 /* This part associates some command line parameters to an option */
3706 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3712 /* The second argument must be a string containing: */
3713 /* - The name of an existing option */
3714 /* - a list of parameters with a leading dash (-) */
3715 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3716 ptr
= va_arg(args
, char *);
3719 if (opt_name
!= NULL
)
3721 if ((opt
= locate_opt(opt_name
)) != NULL
)
3723 ptr
= va_arg(args
, char *);
3726 if (!opt_set_parms(opt_name
, params
))
3728 "duplicates parameters or bad settings for the option (%s).",
3732 fatal_internal("%s: unknown option.", opt_name
);
3736 "ctxopt_opt_add_settings: parameters: not enough arguments.");
3738 /* Here opt is a known option */
3739 /* """""""""""""""""""""""""" */
3740 if (opt
->params
!= NULL
)
3741 fatal_internal("Parameters are already set for %s", opt_name
);
3745 size_t l
= strlen(params
);
3747 opt
->params
= xstrdup(params
);
3748 while ((n
= strcspn(opt
->params
, " \t")) < l
)
3749 opt
->params
[n
] = '|';
3755 /* This part associates a callback function to an option. */
3756 /* This function will be called as when an instance of an option */
3758 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3765 /* The second argument must be the name of an existing option */
3766 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3767 ptr
= va_arg(args
, char *);
3769 if ((opt
= locate_opt(ptr
)) != NULL
)
3771 /* The third argument must be the callback function */
3772 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3773 function
= va_arg(args
, void (*)(char *, char *, char *, int, char **,
3774 int, void *, int, void **));
3775 opt
->action
= function
;
3777 /* The fourth argument must be a pointer to an user's defined */
3778 /* variable or structure that the previous function can manage */
3779 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3780 while ((data
= va_arg(args
, void *)) != NULL
)
3783 opt
->data
= xrealloc(opt
->data
, nb_data
* sizeof(void *));
3784 opt
->data
[nb_data
- 1] = data
;
3786 opt
->nb_data
= nb_data
;
3789 fatal_internal("%s: unknown option.", ptr
);
3793 /* This part associates a list of functions to control some */
3794 /* characteristics of the arguments of an option. */
3795 /* Each function will be called in order and must return 1 */
3796 /* to validate the arguments. */
3797 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3801 constraint_t
* cstr
;
3804 /* The second argument must be a string */
3805 /* """""""""""""""""""""""""""""""""""" */
3806 ptr
= va_arg(args
, char *);
3808 if ((opt
= locate_opt(ptr
)) != NULL
)
3810 /* The third argument must be a function */
3811 /* """"""""""""""""""""""""""""""""""""" */
3812 function
= va_arg(args
, int (*)(int, char **, char *));
3814 cstr
= xmalloc(sizeof(constraint_t
));
3815 cstr
->constraint
= function
;
3817 /* The fourth argument must be a string containing the argument of */
3818 /* The previous function separated by spaces or tabs */
3819 /* Theses arguments will be passed to the previous function */
3820 /* max: 32 argument! */
3821 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3822 value
= xstrdup(va_arg(args
, char *));
3824 cstr
->args
= xcalloc(sizeof(char *), 32);
3825 cstr
->nb_args
= str2argv(value
, cstr
->args
, 32);
3826 ll_append(opt
->constraints_list
, cstr
);
3829 fatal_internal("%s: unknown option.", ptr
);
3839 /* ============================================================== */
3840 /* This function provides a way to set the behaviour of a context */
3841 /* ============================================================== */
3843 ctxopt_add_ctx_settings(settings s
, ...)
3852 /* Add a set of mutually incompatibles option in a context. */
3853 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3854 case incompatibilities
:
3861 ptr
= va_arg(args
, char *);
3862 if ((ctx
= locate_ctx(ptr
)) != NULL
)
3864 ptr
= va_arg(args
, char *);
3865 list
= ctx
->incomp_list
;
3869 rtrim(str
, " \t", 0);
3871 n
= strcspn(str
, " \t");
3872 if (n
> 0 && n
< strlen(str
))
3873 ll_append(list
, str
);
3876 "Not enough incompatible options in the string: \"%s\"", str
);
3879 fatal_internal("%s: unknown context.", ptr
);
3883 /* Add functions which be call when entering and exiting a context. */
3884 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3892 ptr
= va_arg(args
, char *);
3893 if ((ctx
= locate_ctx(ptr
)) != NULL
)
3895 function
= va_arg(args
,
3896 int (*)(char *, direction
, char *, int, void **));
3897 ctx
->action
= function
;
3899 while ((data
= va_arg(args
, void *)) != NULL
)
3902 ctx
->data
= xrealloc(ctx
->data
, nb_data
* sizeof(void *));
3903 ctx
->data
[nb_data
- 1] = data
;
3905 ctx
->nb_data
= nb_data
;
3908 fatal_internal("%s: unknown context.", ptr
);