Typos and grammar fixes in comments
[ctxopt.git] / ctxopt.c
blobbf1ec7a0cddd71db0361edc44b41e3d74b358371
1 /* ########################################################### */
2 /* This Software is licensed under the GPL licensed Version 2, */
3 /* please read http://www.gnu.org/copyleft/gpl.html */
4 /* ########################################################### */
6 #include <errno.h>
7 #include <limits.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <ctype.h>
12 #include <sys/types.h>
13 #include <regex.h>
14 #include <stdarg.h>
15 #include <string.h>
16 #include "ctxopt.h"
18 /* ************************ */
19 /* Static global variables. */
20 /* ************************ */
22 static void * contexts_bst;
23 static void * options_bst;
25 state_t * cur_state;
27 /* Prototypes */
29 /* ************************** */
30 /* Fatal messages prototypes. */
31 /* ************************** */
33 static void (**err_functions)(errors e, state_t * state);
35 static void
36 fatal_internal(const char * format, ...);
38 static void
39 fatal(errors e, char * errmsg);
41 static int user_rc; /* Used by various callback functions */
42 static int user_value; /* Used by various callback functions */
43 static char * user_string; /* Used by various callback functions */
44 static char * user_string2; /* Used by various callback functions */
45 static void * user_object; /* Used by various callback functions */
47 /* ************************************ */
48 /* Memory management static prototypes. */
49 /* ************************************ */
51 static void *
52 xmalloc(size_t size);
54 static void *
55 xcalloc(size_t num, size_t size);
57 static void *
58 xrealloc(void * ptr, size_t size);
60 static char *
61 xstrdup(const char * p);
63 static char *
64 xstrndup(const char * str, size_t len);
66 /* ********************** */
67 /* BST static prototypes. */
68 /* ********************** */
70 typedef struct bst_s bst_t;
72 typedef enum
74 preorder,
75 postorder,
76 endorder,
77 leaf
78 } walk_order_e;
80 static void *
81 bst_delete(const void * vkey, void ** vrootp,
82 int (*compar)(const void *, const void *));
84 static void
85 bst_destroy(void * vrootp, void (*clean)(void *));
87 static void *
88 bst_find(const void * vkey, void * const * vrootp,
89 int (*compar)(const void *, const void *));
91 static void *
92 bst_search(void * vkey, void ** vrootp,
93 int (*compar)(const void *, const void *));
95 static void
96 bst_walk_recurse(const bst_t * root,
97 void (*action)(const void *, walk_order_e, int), int level);
99 static void
100 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int));
102 /* ****************************** */
103 /* Linked list static prototypes. */
104 /* ****************************** */
106 typedef struct ll_node_s ll_node_t;
107 typedef struct ll_s ll_t;
109 static void
110 ll_append(ll_t * const list, void * const data);
112 static void
113 ll_prepend(ll_t * const list, void * const data);
115 static void
116 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data);
118 static void
119 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data);
121 static int
122 ll_delete(ll_t * const list, ll_node_t * node);
124 #if 0 /* Unused yet */
125 static ll_node_t *
126 ll_find(ll_t * const, void * const, int (*)(const void *, const void *));
127 #endif
129 static void
130 ll_init(ll_t * list);
132 static ll_node_t *
133 ll_new_node(void);
135 static ll_t *
136 ll_new(void);
138 static void
139 ll_free(ll_t * const list, void (*)(void *));
141 static void
142 ll_destroy(ll_t * const list, void (*)(void *));
144 static int
145 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array);
147 /* ************************** */
148 /* Various static prototypes. */
149 /* ************************** */
151 static void
152 ltrim(char * str, const char * trim_str);
154 static void
155 rtrim(char * str, const char * trim_str, size_t min);
157 static int
158 strchrcount(char * str, char c);
160 static int
161 strpref(char * s1, char * s2);
163 static char *
164 xstrtok_r(char * str, const char * delim, char ** end);
166 /* ************************* */
167 /* ctxopt static prototypes. */
168 /* ************************* */
170 typedef struct opt_s opt_t;
171 typedef struct par_s par_t;
172 typedef struct ctx_s ctx_t;
173 typedef struct constraint_s constraint_t;
174 typedef struct ctx_inst_s ctx_inst_t;
175 typedef struct opt_inst_s opt_inst_t;
176 typedef struct seen_opt_s seen_opt_t;
178 static char *
179 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
181 static int
182 ctx_compare(const void * c1, const void * c2);
184 static void
185 ctx_free(void * o);
187 static void
188 ctx_inst_free(void * ci);
190 static void
191 opt_inst_free(void * oi);
193 static int
194 seen_opt_compare(const void * so1, const void * so2);
196 static void
197 incomp_bst_free(void * b);
199 static void
200 seen_opt_free(void * seen_opt);
202 static int
203 opt_compare(const void * o1, const void * o2);
205 static void
206 opt_free(void * o);
208 static int
209 par_compare(const void * a1, const void * a2);
211 static void
212 par_free(void * p);
214 static void
215 constraint_free(void * cstr);
217 static ctx_t *
218 locate_ctx(char * name);
220 static opt_t *
221 locate_opt(char * name);
223 static par_t *
224 locate_par(char * name, ctx_t * ctx);
226 static void
227 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
228 int * has_rule, int * has_generic_arg, int * has_ctx_change,
229 int * has_early_eval);
230 static void
231 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
232 int has_optional, int has_ellipsis, int has_rule);
233 static void
234 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
236 static void
237 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
239 static void
240 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
242 static void
243 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
245 static void
246 bst_match_par_cb(const void * node, walk_order_e kind, int level);
248 static void
249 match_prefix_cb(const void * node, walk_order_e kind, int level);
251 static int
252 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
254 static int
255 opt_parse(char * s, opt_t ** opt);
257 static int
258 init_opts(char * spec, ctx_t * ctx);
260 static int
261 ctxopt_build_cmdline_list(int nb_words, char ** words);
263 static int
264 opt_set_parms(char * opt_name, char * par_str);
266 static ctx_inst_t *
267 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
269 static void
270 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
272 /* ****************************** */
273 /* Fatal messages implementation. */
274 /* ****************************** */
276 /* ================================================================== */
277 /* Fatal error function used when a fatal condition is encountered. */
278 /* This function is reserved for the ctxopt internal usage. */
279 /* */
280 /* format : printf like format */
281 /* ... : remaining arguments interpreted using the format argument */
282 /* ================================================================== */
283 static void
284 fatal_internal(const char * format, ...)
286 va_list args;
288 fprintf(stderr, "CTXOPT: ");
290 va_start(args, format);
291 vfprintf(stderr, format, args);
292 fprintf(stderr, "\n");
293 va_end(args);
295 exit(EXIT_FAILURE);
298 /* ====================================================================== */
299 /* Generic fatal error function. This one uses the global status ctxopt */
300 /* stored in the cur_state structure and can call custom error functions. */
301 /* registered by the users for a given error identifier if any. */
302 /* */
303 /* e : Error identifier responsible of the fatal error */
304 /* errmsg : Users's provided string specific to the error e */
305 /* Note that errmsg is not used in all cases */
306 /* */
307 /* CTXOPTMISPAR Missing parameter */
308 /* CTXOPTMISARG Missing argument */
309 /* CTXOPTUXPARG Unexpected argument */
310 /* CTXOPTDUPOPT Duplicated option */
311 /* CTXOPTUNKPAR Unknown parameter */
312 /* CTXOPTINCOPT Incompatible option */
313 /* CTXOPTCTEOPT Option: bad number of occurrences */
314 /* CTXOPTCTLOPT Option: not enough occurrences */
315 /* CTXOPTCTGOPT Option: too many occurrence of */
316 /* CTXOPTCTEARG Arguments: bad number of occurrences */
317 /* CTXOPTCTLARG Arguments: not enough occurrences */
318 /* CTXOPTCTGARG Arguments: too many occurrences */
319 /* ====================================================================== */
320 static void
321 fatal(errors e, char * errmsg)
323 if (err_functions[e] != NULL)
324 err_functions[e](e, cur_state);
325 else
327 switch (e)
329 case CTXOPTNOERR:
330 break;
332 case CTXOPTMISPAR:
333 if (cur_state->ctx_par_name != NULL)
334 fprintf(stderr,
335 "Mandatory parameter(s): %s are missing in the context "
336 "introduced by %s.\n",
337 errmsg, cur_state->ctx_par_name);
338 else
339 fprintf(stderr,
340 "Mandatory parameter(s): %s are missing "
341 "in the main context.\n",
342 errmsg);
344 free(errmsg);
345 break;
347 case CTXOPTUNXARG:
348 fprintf(stderr, "%s only takes one argument.\n",
349 cur_state->cur_opt_par_name);
350 break;
352 case CTXOPTMISARG:
353 if (cur_state->pre_opt_par_name != NULL)
354 fprintf(stderr, "%s requires argument(s).\n",
355 cur_state->pre_opt_par_name);
356 else
357 fprintf(stderr, "%s requires argument(s).\n",
358 cur_state->cur_opt_par_name);
359 break;
361 case CTXOPTDUPOPT:
362 if (cur_state->pre_opt_par_name != NULL)
363 fprintf(stderr,
364 "The parameter(s) %s can only appear once in the context "
365 "introduced by %s.\n",
366 cur_state->cur_opt_par_name, cur_state->ctx_par_name);
367 else
368 fprintf(stderr,
369 "The parameter(s) %s can only appear once "
370 "in the main context.\n",
371 cur_state->cur_opt_par_name);
372 break;
374 case CTXOPTUNKPAR:
375 fprintf(stderr, "Unknown parameter: %s.\n",
376 cur_state->cur_opt_par_name);
377 break;
379 case CTXOPTINCOPT:
380 fprintf(stderr, "%s is incompatible with %s.\n",
381 cur_state->cur_opt_par_name, errmsg);
382 break;
384 case CTXOPTCTEOPT:
385 if (cur_state->ctx_par_name)
386 fprintf(stderr,
387 "%s must appear exactly %u times in the context "
388 "introduced by %s.\n",
389 cur_state->cur_opt_par_name, cur_state->opts_count,
390 cur_state->ctx_par_name);
391 else
392 fprintf(stderr,
393 "%s must appear exactly %u times in "
394 "the main context.\n",
395 cur_state->cur_opt_par_name, cur_state->opts_count);
396 break;
398 case CTXOPTCTLOPT:
399 if (cur_state->ctx_par_name)
400 fprintf(stderr,
401 "%s must appear less than %u times in the context "
402 "introduced by %s.\n",
403 cur_state->cur_opt_par_name, cur_state->opts_count,
404 cur_state->ctx_par_name);
405 else
406 fprintf(stderr,
407 "%s must appear less than %u times in the main context.\n",
408 cur_state->cur_opt_par_name, cur_state->opts_count);
409 break;
411 case CTXOPTCTGOPT:
412 if (cur_state->ctx_par_name)
413 fprintf(stderr,
414 "%s must appear more than %u times in the context "
415 "introduced by %s.\n",
416 cur_state->cur_opt_par_name, cur_state->opts_count,
417 cur_state->ctx_par_name);
418 else
419 fprintf(stderr,
420 "%s must appear more than %u times in the main context.\n",
421 cur_state->cur_opt_par_name, cur_state->opts_count);
422 break;
424 case CTXOPTCTEARG:
425 fprintf(stderr, "%s must have exactly %u arguments.\n",
426 cur_state->cur_opt_par_name, cur_state->opt_args_count);
427 break;
429 case CTXOPTCTLARG:
430 fprintf(stderr, "%s must have less than %u arguments.\n",
431 cur_state->cur_opt_par_name, cur_state->opt_args_count);
432 break;
434 case CTXOPTCTGARG:
435 fprintf(stderr, "%s must have more than %u arguments.\n",
436 cur_state->cur_opt_par_name, cur_state->opt_args_count);
437 break;
439 case CTXOPTERRSIZ:
440 break;
444 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
446 exit(e); /* Exit with the error id e as return code. */
449 /* ********************************* */
450 /* Memory management implementation. */
451 /* ********************************* */
453 /* ================== */
454 /* Customized malloc. */
455 /* ================== */
456 static void *
457 xmalloc(size_t size)
459 void * allocated;
460 size_t real_size;
462 real_size = (size > 0) ? size : 1;
463 allocated = malloc(real_size);
464 if (allocated == NULL)
465 fatal_internal("Insufficient memory (attempt to malloc %lu bytes)\n",
466 (unsigned long int)size);
468 return allocated;
471 /* ================== */
472 /* Customized calloc. */
473 /* ================== */
474 static void *
475 xcalloc(size_t n, size_t size)
477 void * allocated;
479 n = (n > 0) ? n : 1;
480 size = (size > 0) ? size : 1;
481 allocated = calloc(n, size);
482 if (allocated == NULL)
483 fatal_internal("Insufficient memory (attempt to calloc %lu bytes)\n",
484 (unsigned long int)size);
486 return allocated;
489 /* =================== */
490 /* Customized realloc. */
491 /* =================== */
492 static void *
493 xrealloc(void * p, size_t size)
495 void * allocated;
497 allocated = realloc(p, size);
498 if (allocated == NULL && size > 0)
499 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes)\n",
500 (unsigned long int)size);
502 return allocated;
505 /* ==================================== */
506 /* strdup implementation using xmalloc. */
507 /* ==================================== */
508 static char *
509 xstrdup(const char * p)
511 char * allocated;
513 allocated = xmalloc(strlen(p) + 1);
514 strcpy(allocated, p);
516 return allocated;
519 /* =================================================== */
520 /* strndup implementation using xmalloc. */
521 /* This version guarantees that there is a final '\0'. */
522 /* =================================================== */
523 static char *
524 xstrndup(const char * str, size_t len)
526 char * p;
528 p = memchr(str, '\0', len);
530 if (p)
531 len = p - str;
533 p = xmalloc(len + 1);
534 memcpy(p, str, len);
535 p[len] = '\0';
537 return p;
540 /* *************************** */
541 /* Linked list implementation. */
542 /* *************************** */
544 /* Linked list node structure. */
545 /* """"""""""""""""""""""""""" */
546 struct ll_node_s
548 void * data;
549 struct ll_node_s * next;
550 struct ll_node_s * prev;
553 /* Linked List structure. */
554 /* """""""""""""""""""""" */
555 struct ll_s
557 ll_node_t * head;
558 ll_node_t * tail;
559 long len;
562 /* ========================= */
563 /* Create a new linked list. */
564 /* ========================= */
565 static ll_t *
566 ll_new(void)
568 ll_t * ret = xmalloc(sizeof(ll_t));
569 ll_init(ret);
571 return ret;
574 /* =============================================== */
575 /* Free all the elements of a list (make it empty) */
576 /* NULL or a custom function may be used to free */
577 /* the sub components of the elements. */
578 /* =============================================== */
579 static void
580 ll_free(ll_t * const list, void (*clean)(void *))
582 ll_node_t * node;
584 if (list)
585 while (list->head)
587 /* Apply a custom cleaner if not NULL. */
588 /* """"""""""""""""""""""""""""""""""" */
589 if (clean)
590 clean(list->head->data);
592 ll_delete(list, list->head);
596 /* ==================================== */
597 /* Destroy a list and all its elements. */
598 /* ==================================== */
599 static void
600 ll_destroy(ll_t * list, void (*clean)(void *))
602 if (list)
604 ll_free(list, clean);
605 free(list);
609 /* ========================= */
610 /* Initialize a linked list. */
611 /* ========================= */
612 static void
613 ll_init(ll_t * list)
615 list->head = NULL;
616 list->tail = NULL;
617 list->len = 0;
620 /* ===================================================== */
621 /* Allocate the space for a new node in the linked list. */
622 /* ===================================================== */
623 static ll_node_t *
624 ll_new_node(void)
626 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
628 return ret;
631 /* ==================================================================== */
632 /* Append a new node filled with its data at the end of the linked list */
633 /* The user is responsible for the memory management of the data. */
634 /* ==================================================================== */
635 static void
636 ll_append(ll_t * const list, void * const data)
638 ll_node_t * node;
640 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
641 | uses xmalloc which does not return if there *
642 | is an allocation error. */
644 node->data = data;
645 node->next = NULL;
647 node->prev = list->tail;
648 if (list->tail)
649 list->tail->next = node;
650 else
651 list->head = node;
653 list->tail = node;
655 ++list->len;
658 /* ================================================================== */
659 /* Put a new node filled with its data at the beginning of the linked */
660 /* list. */
661 /* The user is responsible for the memory management of the data. */
662 /* ================================================================== */
663 static void
664 ll_prepend(ll_t * const list, void * const data)
666 ll_node_t * node;
668 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
669 | uses xmalloc which does not return if there *
670 | is an allocation error. */
672 node->data = data;
673 node->prev = NULL;
675 node->next = list->head;
676 if (list->head)
677 list->head->prev = node;
678 else
679 list->tail = node;
681 list->head = node;
683 ++list->len;
686 /* ======================================================== */
687 /* Insert a new node before the specified node in the list. */
688 /* ======================================================== */
689 static void
690 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
692 ll_node_t * new_node;
694 if (node->prev == NULL)
695 ll_prepend(list, data);
696 else
698 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
699 | uses xmalloc which does not return if there *
700 | is an allocation error. */
702 new_node->data = data;
703 new_node->next = node;
704 new_node->prev = node->prev;
705 node->prev->next = new_node;
706 node->prev = new_node;
708 ++list->len;
712 /* ======================================================= */
713 /* Insert a new node after the specified node in the list. */
714 /* ======================================================= */
715 static void
716 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
718 ll_node_t * new_node;
720 if (node->next == NULL)
721 ll_append(list, data);
722 else
724 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
725 | uses xmalloc which does not return if there *
726 | is an allocation error. */
728 new_node->data = data;
729 new_node->prev = node;
730 new_node->next = node->next;
731 node->next->prev = new_node;
732 node->next = new_node;
734 ++list->len;
738 /* ================================================================= */
739 /* Remove a node from a linked list */
740 /* The memory taken by the deleted node must be freed by the caller. */
741 /* ================================================================= */
742 static int
743 ll_delete(ll_t * const list, ll_node_t * node)
745 if (list->head == list->tail)
747 if (list->head != NULL)
748 list->head = list->tail = NULL;
749 else
750 return 0;
752 else if (node->prev == NULL)
754 list->head = node->next;
755 list->head->prev = NULL;
757 else if (node->next == NULL)
759 list->tail = node->prev;
760 list->tail->next = NULL;
762 else
764 node->next->prev = node->prev;
765 node->prev->next = node->next;
768 --list->len;
770 free(node);
772 return 1;
775 #if 0 /* Unused yet */
776 /* ======================================================================== */
777 /* Find a node in the list containing data. Return the node pointer or NULL */
778 /* if not found. */
779 /* A comparison function must be provided to compare a and b (strcmp like). */
780 /* ======================================================================== */
781 static ll_node_t *
782 ll_find(ll_t * const list, void * const data,
783 int (*cmpfunc)(const void * a, const void * b))
785 ll_node_t * node;
787 if (NULL == (node = list->head))
788 return NULL;
792 if (0 == cmpfunc(node->data, data))
793 return node;
794 } while (NULL != (node = node->next));
796 return NULL;
798 #endif
800 /* ==================================================================== */
801 /* Allocate and fill an array of strings from a list. */
802 /* WARNINGS: */
803 /* 1) The list node must contain strings (char *) */
804 /* 2) The strings in the resulting array MUST NOT be freed as the are */
805 /* NOT copied from the strings of the list. */
806 /* */
807 /* IN list : The list from which the array is generated */
808 /* IN start_node : The node of the list which will be the first node to */
809 /* consider to create the array */
810 /* OUT: count : The number of elements of the resulting array. */
811 /* OUT: array : The resulting array or NULL if the list is empty. */
812 /* RC : : The number of elements of the resulting array. */
813 /* ==================================================================== */
814 static int
815 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
817 int n = 0;
818 ll_node_t * node;
820 *count = 0;
822 node = start_node;
824 if (list == NULL || node == NULL)
826 *array = NULL;
828 return 0;
831 *array = xmalloc((list->len + 1) * sizeof(char *));
832 while (node != NULL)
834 (*array)[n++] = (char *)(node->data);
835 (*count)++;
837 node = node->next;
840 (*array)[*count] = NULL;
842 return *count;
845 /* ******************************************************************* */
846 /* BST (search.h compatible) implementation. */
847 /* */
848 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
849 /* the AT&T man page says. */
850 /* */
851 /* Written by reading the System V Interface Definition, not the code. */
852 /* */
853 /* Totally public domain. */
854 /* ******************************************************************* */
856 struct bst_s
858 void * key;
859 struct bst_s * llink;
860 struct bst_s * rlink;
863 #if 0 /* Unused yet */
864 /* =========================== */
865 /* Delete node with given key. */
866 /* =========================== */
867 static void *
868 bst_delete(const void * vkey, void ** vrootp,
869 int (*compar)(const void *, const void *))
871 bst_t ** rootp = (bst_t **)vrootp;
872 bst_t * p, *q, *r;
873 int cmp;
875 if (rootp == NULL || (p = *rootp) == NULL)
876 return NULL;
878 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
880 p = *rootp;
881 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
882 : &(*rootp)->rlink; /* follow rlink branch */
883 if (*rootp == NULL)
884 return NULL; /* key not found */
886 r = (*rootp)->rlink; /* D1: */
887 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
888 q = r;
889 else if (r != NULL)
890 { /* Right link is NULL? */
891 if (r->llink == NULL)
892 { /* D2: Find successor */
893 r->llink = q;
894 q = r;
896 else
897 { /* D3: Find NULL link */
898 for (q = r->llink; q->llink != NULL; q = r->llink)
899 r = q;
900 r->llink = q->rlink;
901 q->llink = (*rootp)->llink;
902 q->rlink = (*rootp)->rlink;
905 if (p != *rootp)
906 free(*rootp); /* D4: Free node */
907 *rootp = q; /* link parent to new node */
908 return p;
910 #endif
912 /* ===================================================================== */
913 /* Destroy a tree. */
914 /* The clean function pointer can be NULL, in this case the node content */
915 /* is not freed. */
916 /* ===================================================================== */
917 static void
918 bst_destroy(void * vrootp, void (*clean)(void *))
920 bst_t * root = (bst_t *)vrootp;
922 if (root == NULL)
923 return;
925 bst_destroy(root->llink, clean);
926 bst_destroy(root->rlink, clean);
928 if (clean)
929 clean((void *)root->key);
931 free(root);
934 /* ========================= */
935 /* Find a node, or return 0. */
936 /* ========================= */
937 static void *
938 bst_find(const void * vkey, void * const * vrootp,
939 int (*compar)(const void *, const void *))
941 bst_t * const * rootp = (bst_t * const *)vrootp;
943 if (rootp == NULL)
944 return NULL;
946 while (*rootp != NULL)
947 { /* T1: */
948 int r;
950 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
951 return *rootp; /* key found */
952 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
953 : &(*rootp)->rlink; /* T4: follow right branch */
955 return NULL;
958 /* ======================================= */
959 /* Find or inserts datum into search tree. */
960 /* ======================================= */
961 static void *
962 bst_search(void * vkey, void ** vrootp,
963 int (*compar)(const void *, const void *))
965 bst_t * q;
966 bst_t ** rootp = (bst_t **)vrootp;
968 if (rootp == NULL)
969 return NULL;
971 while (*rootp != NULL)
972 { /* Knuth's T1: */
973 int r;
975 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
976 return *rootp; /* we found it! */
978 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
979 : &(*rootp)->rlink; /* T4: follow right branch */
982 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
983 if (q != 0)
984 { /* make new node */
985 *rootp = q; /* link new node to old */
986 q->key = vkey; /* initialize new node */
987 q->llink = q->rlink = NULL;
989 return q;
992 /* ========================= */
993 /* Walk the nodes of a tree. */
994 /* ========================= */
995 static void
996 bst_walk_recurse(const bst_t * root,
997 void (*action)(const void *, walk_order_e, int), int level)
999 if (root->llink == NULL && root->rlink == NULL)
1000 (*action)(root, leaf, level);
1001 else
1003 (*action)(root, preorder, level);
1004 if (root->llink != NULL)
1005 bst_walk_recurse(root->llink, action, level + 1);
1006 (*action)(root, postorder, level);
1007 if (root->rlink != NULL)
1008 bst_walk_recurse(root->rlink, action, level + 1);
1009 (*action)(root, endorder, level);
1013 static void
1014 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
1016 if (vroot != NULL && action != NULL)
1017 bst_walk_recurse(vroot, action, 0);
1020 /* ************************ */
1021 /* Various implementations. */
1022 /* ************************ */
1024 /* ======================== */
1025 /* Trim leading characters. */
1026 /* ======================== */
1027 static void
1028 ltrim(char * str, const char * trim_str)
1030 size_t len = strlen(str);
1031 size_t begin = strspn(str, trim_str);
1032 size_t i;
1034 if (begin > 0)
1035 for (i = begin; i <= len; ++i)
1036 str[i - begin] = str[i];
1039 /* ================================================= */
1040 /* Trim trailing characters. */
1041 /* The resulting string will have at least min bytes */
1042 /* even if trailing spaces remain. */
1043 /* ================================================= */
1044 static void
1045 rtrim(char * str, const char * trim_str, size_t min)
1047 size_t len = strlen(str);
1048 while (len > min && strchr(trim_str, str[len - 1]))
1049 str[--len] = '\0';
1052 /* ================================================== */
1053 /* Count the number of occurrences of the character c */
1054 /* in the string str. */
1055 /* The str pointer is assumed to be not NULL. */
1056 /* ================================================== */
1057 static int
1058 strchrcount(char * str, char c)
1060 int count = 0;
1062 while (*str)
1063 if (*str++ == c)
1064 count++;
1066 return count;
1069 /* =============================================== */
1070 /* Is the string str2 a prefix of the string str1? */
1071 /* =============================================== */
1072 static int
1073 strpref(char * str1, char * str2)
1075 while (*str1 != '\0' && *str1 == *str2)
1077 str1++;
1078 str2++;
1081 return *str2 == '\0';
1084 /* ======================================================================== */
1085 /* Strings concatenation with dynamic memory allocation. */
1086 /* IN : a variable number of char * arguments with NULL terminating */
1087 /* the sequence. */
1088 /* The first one must have been dynamically allocated and is mandatory */
1089 /* */
1090 /* Returns a new allocated string containing the concatenation of all */
1091 /* the arguments. It is the caller's responsibility to free the resulting */
1092 /* string. */
1093 /* ======================================================================== */
1094 static char *
1095 strappend(char * str, ...)
1097 size_t l;
1098 va_list args;
1099 char * s;
1101 l = 1 + strlen(str);
1102 va_start(args, str);
1104 s = va_arg(args, char *);
1106 while (s)
1108 l += strlen(s);
1109 s = va_arg(args, char *);
1112 va_end(args);
1114 str = xrealloc(str, l);
1116 va_start(args, str);
1117 s = va_arg(args, char *);
1119 while (s)
1121 strcat(str, s);
1122 s = va_arg(args, char *);
1124 va_end(args);
1126 return str;
1129 /* ====================================================================== */
1130 /* Public domain strtok_r() by Charlie Gordon. */
1131 /* from comp.lang.c 9/14/2007 */
1132 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1133 /* */
1134 /* (Declaration that it's public domain): */
1135 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1136 /* */
1137 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1138 /* *end == NULL */
1139 /* ====================================================================== */
1140 static char *
1141 xstrtok_r(char * str, const char * delim, char ** end)
1143 char * ret;
1145 if (str == NULL)
1146 str = *end;
1148 if (str == NULL)
1149 return NULL;
1151 str += strspn(str, delim);
1153 if (*str == '\0')
1154 return NULL;
1156 ret = str;
1158 str += strcspn(str, delim);
1160 if (*str)
1161 *str++ = '\0';
1163 *end = str;
1165 return ret;
1168 /* =========================================================== */
1169 /* Fill an array of strings from the words composing a string. */
1170 /* */
1171 /* str: initial string which will be altered. */
1172 /* args: array of pointers to the start of the words in str. */
1173 /* max: maximum number of words used before giving up. */
1174 /* return: the number of words (<=max). */
1175 /* =========================================================== */
1176 static int
1177 str2argv(char * str, char ** args, int max)
1179 int nb_args = 0;
1181 while (*str)
1183 if (nb_args >= max)
1184 return nb_args;
1186 while (*str == ' ' || *str == '\t')
1187 *(str++) = '\0';
1189 if (!*str)
1190 return nb_args;
1192 args[nb_args] = str;
1193 nb_args++;
1195 while (*str && (*str != ' ') && (*str != '\t'))
1196 str++;
1199 return nb_args;
1202 /* ********************** */
1203 /* ctxopt implementation. */
1204 /* ********************** */
1206 static int ctxopt_initialized = 0; /* cap_init has not yet been called */
1208 /* Context structure. */
1209 /* """""""""""""""""" */
1210 struct ctx_s
1212 char * name;
1213 ll_t * opt_list; /* list of options allowed in this context. */
1214 ll_t * incomp_list; /* list of strings containing incompatible names *
1215 | of options separated by spaces or tabs. */
1216 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1217 void ** ctx_data);
1218 void * par_bst;
1219 int nb_data;
1220 void ** data;
1223 /* https://textik.com/#488ce3649b6c60f5 */
1224 /* */
1225 /* +--------------+ */
1226 /* |first_ctx_inst| */
1227 /* +---+----------+ */
1228 /* | */
1229 /* +--v-----+ +--------+ +--------+ +-----+ */
1230 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1231 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1232 /* | | | | | */
1233 /* | | +-v------+ | | */
1234 /* | +--+ctx_inst<-----------+ | */
1235 /* | +-+------+ | */
1236 /* | | | */
1237 /* | +-v------+ | */
1238 /* +------+ctx_inst<--------------------------+ */
1239 /* +-+------+ */
1240 /* | */
1241 /* +-v---+ */
1242 /* | ... | */
1243 /* +-----+ */
1245 /* Option structure. */
1246 /* """"""""""""""""" */
1247 struct opt_s
1249 char * name; /* option name. */
1250 char * next_ctx; /* new context this option may lead to */
1251 ll_t * ctx_list; /* list of contexts allowing this option. */
1252 char * params; /* string containing all the parameters of *
1253 | the option. */
1255 void (*action)( /* The option associated action. */
1256 char * ctx_name, /* context name. */
1257 char * opt_name, /* option name. */
1258 char * par, /* option parameter. */
1259 int nb_args, /* number of arguments. */
1260 char ** args, /* option arguments. */
1261 int nb_opt_data, /* number of option data pointers. */
1262 void ** opt_data, /* option data pointers. */
1263 int nb_ctx_data, /* nb of current context data ptrs. */
1264 void ** ctx_data /* current context data pointers. */
1267 int nb_data; /* number of the data pointers passed as argument to action. */
1268 void ** data; /* array of data pointers passed as argument to action. */
1270 int args; /* 1 if this option takes arguments else 0. */
1271 int optional; /* 1 if the option is optional, else 0. */
1272 int multiple; /* 1 if the option can appear more than one time in a *
1273 | context, else 0. */
1275 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1276 int occurrences; /* Number of option occurrences in a context. */
1277 char opt_count_oper; /* <, = or > */
1278 unsigned opt_count_mark; /* Value to be compared to with opt_count_oper. */
1280 char * arg; /* symbolic text after # describing the option argument. */
1282 int optional_args; /* 1 of option is optional else 0. */
1283 int multiple_args; /* 1 is option can appear more than once in a context *
1284 | instance. */
1286 int opt_args_count_matter; /* 1 if count is rescticted, else 0. */
1287 char opt_args_count_oper; /* <, = or > */
1288 unsigned opt_args_count_mark; /* Value to be compared to with *
1289 | opt_count_oper. */
1291 int eval_first; /* 1 if this option must be evaluated before the options *
1292 | without this mark. */
1294 ll_t * constraints_list; /* List of constraint check functions pointers. */
1297 /* Context instance structure. */
1298 /* """"""""""""""""""""""""""" */
1299 struct ctx_inst_s
1301 ctx_t * ctx; /* the context whose this is an instance of */
1302 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1303 | creation of this ctx_inst structure. */
1304 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1305 | instance of this structure. */
1306 ll_t * incomp_bst_list; /* list of seen_opt_t BST. */
1307 void * seen_opt_bst; /* tree of seen_opt_t. */
1308 ll_t * opt_inst_list; /* The list of option instances in this *
1309 | context instance. */
1310 char * par_name; /* parameter which created this instance. */
1313 /* Option instance structure. */
1314 /* """""""""""""""""""""""""" */
1315 struct opt_inst_s
1317 opt_t * opt; /* The option this is an instance of. */
1318 char * opt_name; /* The option which led to this creation. */
1319 char * par; /* The parameter which led to this creation. */
1320 ll_t * values_list; /* The list of arguments of this option. */
1321 ctx_inst_t * next_ctx_inst; /* The new context instance this option. *
1322 | instance may create. */
1325 /* Structure used to check if an option has bee seen or not */
1326 /* in a context instance. */
1327 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1328 struct seen_opt_s
1330 opt_t * opt; /* The concerned option. */
1331 char * par; /* Parameter which led to the making of this structure. */
1332 int seen; /* 1 if seen in the context instances, else 0. */
1335 /* Parameter structure which links a parameter to the option it belongs to. */
1336 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1337 struct par_s
1339 char * name; /* Parameter name (with the leading -). */
1340 opt_t * opt; /* Attached option. */
1343 /* Constraint structure. */
1344 /* """"""""""""""""""""" */
1345 struct constraint_s
1347 int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1348 int nb_args;
1349 char ** args;
1350 char * to_free; /* pointer to the original string in which the array in *
1351 | args points to. This poinnter is kept there to allow *
1352 | it to be freed. */
1355 state_t * cur_state = NULL; /* Current analysis state. */
1356 static ll_t * cmdline_list; /* List of interpreted CLI words *
1357 | serves as the basis for the *
1358 | analysis of the parameters. */
1359 static ctx_t * main_ctx = NULL; /* initial context. */
1360 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1361 | instance which holds the *
1362 | options instances. */
1363 static ll_t * ctx_inst_list = NULL; /* List of the context instances. */
1365 /* ======================================================= */
1366 /* Parse a string for the next matching token. */
1367 /* */
1368 /* s: string to parse. */
1369 /* token: pre_allocated array of max tok_len characters. */
1370 /* pattern: scanf type pattern token must match. */
1371 /* pos: number of characters successfully parsed in s. */
1372 /* */
1373 /* Returns: a pointer to the first unread character or */
1374 /* to he terminating \0. */
1375 /* ======================================================= */
1376 static char *
1377 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1379 char * full_pattern;
1380 char len[3];
1381 int n;
1383 *pos = 0;
1385 n = snprintf(len, 3, "%zu", tok_len);
1386 if (n < 0)
1387 return NULL;
1389 full_pattern = xmalloc(strlen(pattern) + n + 4);
1391 strcpy(full_pattern, "%");
1392 strcat(full_pattern, len);
1393 strcat(full_pattern, pattern);
1394 strcat(full_pattern, "%n");
1396 n = sscanf(s, full_pattern, token, pos);
1398 free(full_pattern);
1400 if (n != 1)
1401 return NULL;
1403 return s + *pos;
1406 /* ****************************************** */
1407 /* Various comparison and deletion functions. */
1408 /* ****************************************** */
1410 static int
1411 ctx_compare(const void * c1, const void * c2)
1413 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1416 /* =========================== */
1417 /* Free a context_bst element. */
1418 /* =========================== */
1419 static void
1420 ctx_free(void * c)
1422 ctx_t * ctx = c;
1424 free(ctx->name);
1425 free(ctx->data);
1427 ll_destroy(ctx->opt_list, NULL);
1428 ll_destroy(ctx->incomp_list, free);
1429 bst_destroy(ctx->par_bst, par_free);
1431 free(c);
1434 /* ============================= */
1435 /* Free a ctx_inst_list element. */
1436 /* ============================= */
1437 static void
1438 ctx_inst_free(void * ci)
1440 ctx_inst_t * ctx_inst = ci;
1442 free(ctx_inst->par_name);
1443 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1444 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1445 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1447 free(ci);
1450 /* ============================= */
1451 /* Free a opt_inst_list element. */
1452 /* ============================= */
1453 static void
1454 opt_inst_free(void * oi)
1456 opt_inst_t * opt_inst = oi;
1458 ll_destroy(opt_inst->values_list, NULL);
1460 free(oi);
1463 /* ================================== */
1464 /* Compare two seen_opt_bst elements. */
1465 /* ================================== */
1466 static int
1467 seen_opt_compare(const void * so1, const void * so2)
1469 opt_t *o1, *o2;
1471 o1 = ((seen_opt_t *)so1)->opt;
1472 o2 = ((seen_opt_t *)so2)->opt;
1474 return strcmp(o1->name, o2->name);
1477 /* ============================ */
1478 /* Free a seen_opt_bst element. */
1479 /* ============================ */
1480 void
1481 seen_opt_free(void * so)
1483 seen_opt_t * seen_opt = so;
1485 free(seen_opt->par);
1487 free(so);
1490 /* =========================== */
1491 /* Free an incomp_bst element. */
1492 /* =========================== */
1493 static void
1494 incomp_bst_free(void * b)
1496 bst_t * bst = b;
1498 bst_destroy(bst, NULL);
1501 /* ================================= */
1502 /* Compare two options_bst elements. */
1503 /* ================================= */
1504 static int
1505 opt_compare(const void * o1, const void * o2)
1507 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1510 /* ============================= */
1511 /* Free an options_bst elements. */
1512 /* ============================= */
1513 void
1514 opt_free(void * o)
1516 opt_t * opt = o;
1518 free(opt->name);
1519 free(opt->next_ctx);
1520 free(opt->params);
1521 free(opt->arg);
1522 free(opt->data);
1524 ll_destroy(opt->ctx_list, NULL);
1525 ll_destroy(opt->constraints_list, constraint_free);
1527 free(o);
1530 /* ============================= */
1531 /* Compare two par_bst elements. */
1532 /* ============================= */
1533 static int
1534 par_compare(const void * a1, const void * a2)
1536 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1539 /* ======================= */
1540 /* Free a par_bst element. */
1541 /* ======================= */
1542 static void
1543 par_free(void * p)
1545 par_t * par = p;
1547 free(par->name);
1549 free(p);
1552 /* ================================ */
1553 /* Free a constraints_list element. */
1554 /* ================================ */
1555 static void
1556 constraint_free(void * c)
1558 constraint_t * cstr = c;
1560 free(cstr->args);
1561 free(cstr->to_free);
1563 free(c);
1566 /* ******************************************************************** */
1567 /* Helper functions to locate contexts, options and parameters in a BST */
1568 /* by their names. */
1569 /* ******************************************************************** */
1571 static ctx_t *
1572 locate_ctx(char * name)
1574 bst_t * node;
1575 ctx_t ctx = { 0 };
1577 ctx.name = name;
1579 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1580 return NULL;
1581 else
1582 return node->key;
1585 static opt_t *
1586 locate_opt(char * name)
1588 bst_t * node;
1589 opt_t opt = { 0 };
1591 opt.name = name;
1593 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1594 return NULL;
1595 else
1596 return node->key;
1599 static par_t *
1600 locate_par(char * name, ctx_t * ctx)
1602 bst_t * node;
1603 par_t par = { 0 };
1604 void * bst = ctx->par_bst;
1606 par.name = name;
1608 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1609 return NULL;
1610 else
1611 return node->key;
1614 /* =================================================================== */
1615 /* Utility function to format and print the options present in a list. */
1616 /* */
1617 /* IN list : a list of options. */
1618 /* OUT has_* : a set of flags which will determine the content of the */
1619 /* explanation given after the formatted printing of the */
1620 /* options. */
1621 /* =================================================================== */
1622 static void
1623 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1624 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1625 int * has_early_eval)
1627 ll_node_t * node = list->head;
1628 opt_t * opt;
1629 char * line;
1630 char * option;
1632 line = xstrdup(" ");
1634 while (node != NULL)
1636 option = xstrdup("");
1637 opt = node->data;
1639 if (opt->optional)
1641 option = strappend(option, "[", NULL);
1642 *has_optional = 1;
1645 if (opt->eval_first)
1647 option = strappend(option, "*", NULL);
1648 *has_early_eval = 1;
1651 option = strappend(option, opt->params, NULL);
1653 if (opt->next_ctx != NULL)
1655 option = strappend(option, ">", opt->next_ctx, NULL);
1656 *has_ctx_change = 1;
1659 if (opt->multiple)
1661 if (opt->opt_count_oper != '\0')
1663 char m[4];
1664 char o[2];
1665 o[0] = opt->opt_count_oper;
1666 o[1] = '\0';
1667 snprintf(m, 3, "%u", opt->opt_count_mark);
1668 option = strappend(option, "...", o, m, NULL);
1669 *has_rule = 1;
1671 else
1672 option = strappend(option, "...", NULL);
1674 *has_ellipsis = 1;
1677 if (opt->args)
1679 if (*(opt->arg) == '#')
1680 *has_generic_arg = 1;
1682 option = strappend(option, " ", NULL);
1684 if (opt->optional_args)
1686 option = strappend(option, "[", opt->arg, NULL);
1687 *has_optional = 1;
1689 else
1690 option = strappend(option, opt->arg, NULL);
1692 if (opt->multiple_args)
1694 if (opt->opt_args_count_oper != '\0')
1696 char m[4];
1697 char o[2];
1698 o[0] = opt->opt_args_count_oper;
1699 o[1] = '\0';
1700 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1701 option = strappend(option, "...", o, m, NULL);
1702 *has_rule = 1;
1704 else
1705 option = strappend(option, "...", NULL);
1707 *has_ellipsis = 1;
1709 if (opt->optional_args)
1710 option = strappend(option, "]", NULL);
1712 if (opt->optional)
1713 option = strappend(option, "]", NULL);
1715 if (strlen(line) + 1 + strlen(option) < 80)
1716 line = strappend(line, option, " ", NULL);
1717 else
1719 printf("%s\n", line);
1720 line[2] = '\0';
1721 line = strappend(line, option, " ", NULL);
1724 free(option);
1726 node = node->next;
1729 printf("%s\n", line);
1731 free(line);
1734 /* ==================================================== */
1735 /* Explain the special syntactic symbols present in the */
1736 /* generated usage messages. */
1737 /* ==================================================== */
1738 static void
1739 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1740 int has_optional, int has_ellipsis, int has_rule)
1742 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1743 || has_ellipsis || has_rule)
1745 printf("\nSyntactic explanations:\n");
1746 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1747 "must be entered.\n");
1748 printf("The following is just there to explain the other symbols "
1749 "displayed.\n\n");
1751 if (has_early_eval)
1752 printf("* : the parameters for this option will be "
1753 "evaluated first.\n");
1754 if (has_ctx_change)
1755 printf(
1756 "> : The context after this symbol will become the next "
1757 "default one.\n");
1758 if (has_generic_arg)
1759 printf("#tag : argument tag giving a clue to its meaning.\n");
1760 if (has_optional)
1761 printf(
1762 "[...] : the object between square brackets is optional.\n");
1763 if (has_ellipsis)
1764 printf("... : the previous object can be repeated more "
1765 "than one time.\n");
1766 if (has_rule)
1767 printf("[<|=|>]number: rules constraining the number of "
1768 "parameters/arguments.\n");
1772 /* ************************************************************ */
1773 /* Various utilities and callback functions called when walking */
1774 /* through a BST. */
1775 /* ************************************************************ */
1777 static void
1778 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1780 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1782 if (kind == postorder || kind == leaf)
1784 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1786 user_rc = 1;
1787 user_string = strappend(user_string, seen_opt->opt->params, " ", NULL);
1792 static void
1793 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1795 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1797 if (kind == postorder || kind == leaf)
1798 if (seen_opt->seen == 1)
1800 user_rc = 1;
1801 user_object = seen_opt->par;
1805 static void
1806 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
1808 ctx_t * ctx = main_ctx;
1809 ctx_t * cur_ctx = ((bst_t *)node)->key;
1811 ll_t * list;
1813 int has_optional = 0;
1814 int has_ellipsis = 0;
1815 int has_rule = 0;
1816 int has_generic_arg = 0;
1817 int has_ctx_change = 0;
1818 int has_early_eval = 0;
1820 if (kind == postorder || kind == leaf)
1821 if (strcmp(ctx->name, cur_ctx->name) != 0)
1823 list = cur_ctx->opt_list;
1825 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
1826 print_options(list, &has_optional, &has_ellipsis, &has_rule,
1827 &has_generic_arg, &has_ctx_change, &has_early_eval);
1831 static void
1832 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
1834 opt_t * opt = ((bst_t *)node)->key;
1836 if (kind == postorder || kind == leaf)
1838 if (opt->params == NULL) /* opt must have associated parameters. */
1839 fatal_internal("Option %s has no registered parameter.\n", opt->name);
1841 if (opt->action == NULL) /* opt must have an action. */
1842 fatal_internal("Option %s has no registered action.\n", opt->name);
1846 static void
1847 bst_match_par_cb(const void * node, walk_order_e kind, int level)
1849 ctx_t * ctx = ((bst_t *)node)->key;
1851 if (kind == postorder || kind == leaf)
1853 char * str = xstrdup(user_string);
1855 while (*str != '\0')
1857 if (locate_par(str, ctx) != NULL)
1859 user_string2 = strappend(user_string2, " ", ctx->name, NULL);
1860 break;
1862 str[strlen(str) - 1] = '\0';
1864 free(str);
1868 static void
1869 match_prefix_cb(const void * node, walk_order_e kind, int level)
1871 par_t * par = ((bst_t *)node)->key;
1873 if (kind == postorder || kind == leaf)
1874 if (strpref(par->name, (char *)user_object))
1876 user_rc++;
1877 user_string = strappend(user_string, par->name, " ", NULL);
1881 /* ====================================================================== */
1882 /* A parameter may not be separated from its first option by spaces, in */
1883 /* this case this function looks for a valid flag as a prefix and splits */
1884 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1885 /* option). */
1886 /* */
1887 /* IN word : the word to be checked. */
1888 /* IN ctx : the context in which the flag indexed by the word is to be */
1889 /* checked. */
1890 /* OUT pos : the offset in word pointing just after the matching prefix. */
1891 /* OUT opt : a pointer to the option associated with the new parameter */
1892 /* or NULL if none is found. */
1893 /* */
1894 /* The returned pointer must be freed by the caller. */
1895 /* ====================================================================== */
1896 static char *
1897 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
1899 char * new = NULL;
1900 int len;
1901 par_t * par;
1902 par_t tmp_par = { 0 };
1904 len = strlen(word);
1906 if (len > 2)
1908 new = xstrdup(word);
1912 new[--len] = '\0';
1913 tmp_par.name = new;
1914 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
1916 if (par != NULL)
1918 *pos = len;
1919 *opt = par->opt;
1921 else
1923 free(new);
1924 new = NULL;
1927 else
1928 *pos = 0;
1930 return new;
1933 /* ============================================================= */
1934 /* If par_name is an unique abbreviation of an exiting parameter */
1935 /* in the context ctx, then return this parameter. */
1936 /* ============================================================= */
1937 static char *
1938 abbrev_expand(char * par_name, ctx_t * ctx)
1940 user_object = par_name;
1941 user_rc = 0;
1943 *user_string = '\0';
1944 bst_walk(ctx->par_bst, match_prefix_cb);
1945 rtrim(user_string, " ", 0);
1947 /* The previous bst_walk has built a string of blank separated parameters */
1948 /* all having par_name as prefix. This string is put in the user_string */
1949 /* exchange zone. The number of these words in put in user_rc. */
1950 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1951 if (user_rc == 1) /* The number of matching abbreviations. */
1952 return xstrdup(user_string);
1953 else /* There is at least tho defined parameters starting with par_name. */
1955 char * s, *first_s;
1956 par_t * par;
1957 opt_t * opt;
1958 int opt_count = 0;
1959 void * tmp_opt_bst = NULL;
1961 /* Find all the options corresponding to these words and store them */
1962 /* without duplication in a temporary BST. Only their resulting count */
1963 /* matters. */
1964 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1965 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
1966 | the first word. */
1967 while (s != NULL)
1969 par = locate_par(s, ctx);
1970 opt = par->opt;
1972 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
1974 /* This option as not already been seen */
1975 /* store it and increase the seen counter. */
1976 /* """"""""""""""""""""""""""""""""""""""" */
1977 bst_search(opt, &tmp_opt_bst, opt_compare);
1978 opt_count++;
1980 s = strtok(NULL, " ");
1983 /* Clean the temporary BST without removing the pointer */
1984 /* to the real options. */
1985 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1986 if (tmp_opt_bst != NULL)
1987 bst_destroy(tmp_opt_bst, NULL);
1989 if (opt_count == 1)
1990 /* All the abbreviation are leading to only one option */
1991 /* We can just continue as in the previous case. */
1992 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
1993 return xstrdup(first_s);
1994 else
1995 return NULL;
1999 /* ================================================================ */
2000 /* Terminate the program if mandatory options required by a context */
2001 /* are not present. */
2002 /* ================================================================ */
2003 static void
2004 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2006 char * missing;
2008 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2009 fatal(CTXOPTMISPAR, missing);
2012 /* ====================================================== */
2013 /* Return 1 if at least one mandatory option was not seen */
2014 /* when quitting a context, else 0. */
2015 /* ====================================================== */
2016 static int
2017 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2019 user_rc = 0;
2020 *user_string = '\0';
2022 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2023 rtrim(user_string, " ", 0);
2025 *missing = user_string;
2027 return user_rc ? 1 : 0;
2030 /* ========================================================================= */
2031 /* This function terminates the program if an option or its arguments do not */
2032 /* conform to its occurrences constraint. */
2033 /* There constraints can appear by trailing >, < or = in their definition */
2034 /* given in ctxopt_new_ctx. */
2035 /* ========================================================================= */
2036 static void
2037 check_for_occurrences_issues(ctx_inst_t * ctx_inst)
2039 ctx_t * ctx = ctx_inst->ctx;
2040 opt_t * opt;
2041 ll_node_t * node;
2042 opt_inst_t * opt_inst;
2044 /* Checks options. */
2045 /* """"""""""""""" */
2046 node = ctx->opt_list->head;
2048 while (node != NULL)
2050 opt = node->data;
2052 /* Update current_state. */
2053 /* """"""""""""""""""""" */
2054 cur_state->opts_count = opt->opt_count_mark;
2055 cur_state->opt_args_count = opt->opt_args_count_mark;
2057 if (opt->opt_count_matter)
2058 switch (opt->opt_count_oper)
2060 case '=':
2061 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2062 fatal(CTXOPTCTEOPT, "");
2063 break;
2065 case '<':
2066 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2067 fatal(CTXOPTCTLOPT, "");
2068 break;
2070 case '>':
2071 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2072 fatal(CTXOPTCTGOPT, "");
2073 break;
2076 node = node->next;
2079 /* Checks arguments. */
2080 /* """"""""""""""""" */
2081 node = ctx_inst->opt_inst_list->head;
2082 while (node != NULL)
2084 opt_inst = node->data;
2085 opt = opt_inst->opt;
2087 /* Update current_state. */
2088 /* """"""""""""""""""""" */
2089 cur_state->opts_count = opt->opt_count_mark;
2090 cur_state->opt_args_count = opt->opt_args_count_mark;
2092 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2094 if (opt->opt_args_count_matter)
2095 switch (opt->opt_args_count_oper)
2097 case '=':
2098 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2099 fatal(CTXOPTCTEARG, "");
2100 break;
2102 case '<':
2103 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2104 fatal(CTXOPTCTLARG, "");
2105 break;
2107 case '>':
2108 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2109 fatal(CTXOPTCTGARG, "");
2110 break;
2113 node = node->next;
2117 /* ======================================================================== */
2118 /* Parse a strings describing options and some of their characteristics */
2119 /* The input string must have follow some rules like in the examples below: */
2120 /* */
2121 /* "opt_name1 opt_name2" */
2122 /* "[opt_name1] opt_name2" */
2123 /* "[opt_name1] opt_name2..." */
2124 /* "[opt_name1 #...] opt_name2... [#]" */
2125 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2126 /* */
2127 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2128 /* means that there can be more than one occurrence of the previous thing. */
2129 /* */
2130 /* opt_name can be followed by a 'new context' change prefixed with the */
2131 /* symbol >, as in opt1>c2 by eg. */
2132 /* */
2133 /* This function returns as soon as one (or no) option has been parsed and */
2134 /* return the offset to the next option to parse. */
2135 /* */
2136 /* In case of successful parsing, an new option is allocated and its */
2137 /* pointer returned. */
2138 /* ======================================================================== */
2139 static int
2140 opt_parse(char * s, opt_t ** opt)
2142 int opt_optional = 0;
2143 int opt_multiple = 0;
2144 int opt_count_matter = 0;
2145 char opt_count_oper = '\0';
2146 unsigned opt_count_mark = 0;
2147 int opt_args = 0;
2148 char opt_arg[33] = { 0 };
2149 int opt_multiple_args = 0;
2150 int opt_args_count_matter = 0;
2151 char opt_args_count_oper = '\0';
2152 unsigned opt_args_count_mark = 0;
2153 int opt_optional_args = 0;
2154 int opt_eval_first = 0;
2156 int n;
2157 int pos;
2158 int count = 0;
2160 char * s_orig = s;
2162 char * p;
2163 char * opt_name;
2164 char * next_ctx;
2165 char token[65];
2167 *opt = NULL;
2168 memset(opt_arg, '\0', 33);
2170 /* Strip the leading blanks. */
2171 /* """"""""""""""""""""""""" */
2172 while (isblank(*s))
2173 s++;
2175 if (*s == '[') /* Start of an optional option. */
2177 opt_optional = 1;
2178 s++;
2180 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2181 if (s == NULL)
2182 return -1; /* Empty string. */
2184 /* Early EOS, only return success if the option is mandatory. */
2185 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2186 if (!*s)
2187 if (opt_optional == 1)
2188 return -(s - s_orig - 1);
2190 /* Validate the option name */
2191 /* ALPHA+(ALPHANUM|_)* */
2192 /* """""""""""""""""""""""" */
2193 p = token;
2194 if (!isalpha(*p) && *p != '*')
2195 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2197 if (*p == '*')
2198 opt_eval_first = 1;
2200 p++;
2201 while (*p)
2203 if (!isalnum(*p) && *p != '_' && *p != '>')
2204 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2205 * a number or a _ */
2206 p++;
2209 if (opt_eval_first)
2210 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2211 else
2212 opt_name = xstrdup(token);
2214 if (*s == ']')
2216 s++;
2217 while (isblank(*s))
2218 s++;
2220 goto success;
2223 /* Check if it can appear multiple times by looking for the dots. */
2224 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2225 p = strtoken(s, token, 3, "[.]", &pos);
2226 if (p)
2228 if (strcmp(token, "...") == 0)
2230 opt_multiple = 1;
2231 s = p;
2232 if (*s == '<' || *s == '=' || *s == '>')
2234 unsigned value;
2235 int offset;
2237 n = sscanf(s + 1, "%u%n", &value, &offset);
2238 if (n == 1)
2240 opt_count_matter = 1;
2241 opt_count_oper = *s;
2242 opt_count_mark = value;
2244 s += offset + 1;
2247 else
2249 free(opt_name);
2250 return -(s - s_orig - 1);
2254 /* A blank separates the option name and the argument tag. */
2255 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2256 if (isblank(*s))
2258 char dots[4];
2260 while (isblank(*s))
2261 s++;
2263 if (!*s)
2264 goto success;
2266 pos = 0;
2267 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2268 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2270 opt_args = 1;
2271 opt_optional_args = 1;
2272 if (n == 2)
2273 opt_multiple_args = 1; /* There were dots. */
2275 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2277 if (*s == '<' || *s == '=' || *s == '>')
2279 unsigned value;
2280 int offset;
2282 n = sscanf(s + 1, "%u%n", &value, &offset);
2283 if (n == 1)
2285 opt_args_count_matter = 1;
2286 opt_args_count_oper = *s;
2287 opt_args_count_mark = value;
2289 s += offset + 1;
2292 /* Optional arg tag must end with a ] */
2293 /* """""""""""""""""""""""""""""""""" */
2294 if (*s != ']')
2296 free(opt_name);
2297 return -(s - s_orig - 1);
2300 s++; /* Skip the ] */
2302 else
2304 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2305 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2307 opt_args = 1;
2308 if (n == 2) /* There were dots. */
2309 opt_multiple_args = 1;
2311 s += pos + !!(n == 2) * 3; /* Skip the dots */
2313 if (*s == '<' || *s == '=' || *s == '>')
2315 unsigned value;
2316 int offset;
2318 n = sscanf(s + 1, "%u%n", &value, &offset);
2319 if (n == 1)
2321 opt_args_count_matter = 1;
2322 opt_args_count_oper = *s;
2323 opt_args_count_mark = value;
2325 s += offset + 1;
2329 if (*s == ']')
2331 /* Abort on extraneous ] if the option is mandatory. */
2332 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2333 if (!opt_optional)
2334 return -(s - s_orig - 1);
2336 s++; /* skip the ] */
2338 /* Strip the following blanks. */
2339 /* """"""""""""""""""""""""""" */
2340 while (isblank(*s))
2341 s++;
2343 goto success;
2345 else if (opt_optional == 0 && (!*s || isblank(*s)))
2347 /* Strip the following blanks. */
2348 /* """"""""""""""""""""""""""" */
2349 while (isblank(*s))
2350 s++;
2352 goto success;
2354 else if (opt_args == 0) /* # was not read it is possibly the start *
2355 * of another option. */
2356 goto success;
2357 else
2358 return -(s - s_orig - 1);
2361 success:
2363 /* Strip the following blanks. */
2364 /* """"""""""""""""""""""""""" */
2365 while (isblank(*s))
2366 s++;
2368 next_ctx = NULL;
2370 if (*opt_name == '>')
2371 fatal_internal("%s: option name is missing.", opt_name);
2373 count = strchrcount(opt_name, '>');
2374 if (count == 1)
2376 char * tmp = strchr(opt_name, '>');
2377 next_ctx = xstrdup(tmp + 1);
2378 *tmp = '\0';
2380 else if (count > 1)
2381 fatal_internal("%s: only one occurrence of '>' is allowed.", opt_name);
2383 *opt = xmalloc(sizeof(opt_t));
2385 (*opt)->name = opt_name;
2386 (*opt)->optional = opt_optional;
2387 (*opt)->multiple = opt_multiple;
2388 (*opt)->opt_count_matter = opt_count_matter;
2389 (*opt)->opt_count_oper = opt_count_oper;
2390 (*opt)->opt_count_mark = opt_count_mark;
2391 (*opt)->args = opt_args;
2392 (*opt)->arg = xstrdup(opt_arg);
2393 (*opt)->optional_args = opt_optional_args;
2394 (*opt)->multiple_args = opt_multiple_args;
2395 (*opt)->opt_args_count_matter = opt_args_count_matter;
2396 (*opt)->opt_args_count_oper = opt_args_count_oper;
2397 (*opt)->opt_args_count_mark = opt_args_count_mark;
2398 (*opt)->eval_first = opt_eval_first;
2399 (*opt)->next_ctx = next_ctx;
2400 (*opt)->ctx_list = ll_new();
2401 (*opt)->constraints_list = ll_new();
2402 (*opt)->action = NULL;
2403 (*opt)->params = NULL;
2404 (*opt)->data = NULL;
2406 return s - s_orig;
2409 /* ==================================================================== */
2410 /* Try to initialize all the option in a given string */
2411 /* Each parsed option are put in a BST tree with its name as index. */
2412 /* */
2413 /* On collision, the arguments only the signature are required to be */
2414 /* the same else this is considered as an error. Options can be used in */
2415 /* more than one context and can be optional in one and mandatory in */
2416 /* another. */
2417 /* ==================================================================== */
2418 static int
2419 init_opts(char * spec, ctx_t * ctx)
2421 opt_t * opt, *bst_opt;
2422 bst_t * node;
2423 int offset;
2425 while (*spec)
2427 if ((offset = opt_parse(spec, &opt)) > 0)
2429 spec += offset;
2431 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2433 int same_next_ctx = 0;
2435 bst_opt = node->key; /* Node extracted from the BST. */
2437 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2438 same_next_ctx = 1;
2439 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2440 same_next_ctx = 0;
2441 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2442 same_next_ctx = 0;
2443 else
2444 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2446 if (bst_opt->optional_args != opt->optional_args
2447 || bst_opt->multiple_args != opt->multiple_args
2448 || bst_opt->args != opt->args || !same_next_ctx)
2450 fatal_internal("option %s already exists with "
2451 "a different arguments signature.\n",
2452 opt->name);
2455 /* The newly created opt is already present in options_bst. */
2456 /* We can remove it. */
2457 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2458 opt_free(opt);
2460 /* The new occurrence of the option option is legal */
2461 /* append the current context ptr in the list. */
2462 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2463 ll_append(bst_opt->ctx_list, ctx);
2465 /* Append the new option to the context's options list. */
2466 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2467 ll_append(ctx->opt_list, bst_opt);
2469 else
2471 /* Initialize the option's context list with the current context. */
2472 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2473 ll_append(opt->ctx_list, ctx);
2475 /* Append the new option to the context's options list. */
2476 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2477 ll_append(ctx->opt_list, opt);
2479 /* Insert the new option in the BST. */
2480 /* """"""""""""""""""""""""""""""""" */
2481 bst_search(opt, &options_bst, opt_compare);
2484 else
2486 char * s = xstrndup(spec, -offset);
2487 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2488 free(s);
2490 exit(EXIT_FAILURE);
2494 return 1;
2497 /* ===================================================== */
2498 /* ctxopt initialization function, must be called first. */
2499 /* ===================================================== */
2500 void
2501 ctxopt_init(char * prog_name)
2503 int n;
2505 contexts_bst = NULL;
2506 options_bst = NULL;
2507 char * ptr;
2509 user_rc = 0;
2510 user_value = 0;
2511 user_string = xmalloc(8);
2512 user_string2 = xmalloc(8);
2513 user_object = NULL;
2515 ctxopt_initialized = 1;
2517 /* Update current_state.*/
2518 /* """"""""""""""""""""" */
2519 cur_state = xcalloc(sizeof(state_t), 0);
2521 /* Initialize custom error function pointers to NULL. */
2522 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2523 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2524 for (n = 0; n < CTXOPTERRSIZ; n++)
2525 err_functions[n] = NULL;
2527 /* Update current_state. */
2528 /* """"""""""""""""""""" */
2529 if (prog_name)
2531 if (*prog_name == '\0')
2532 cur_state->prog_name = xstrdup("program_name");
2533 else if ((ptr = strrchr(prog_name, '/')))
2534 cur_state->prog_name = xstrdup(ptr + 1);
2535 else
2536 cur_state->prog_name = xstrdup(prog_name);
2538 else
2539 cur_state->prog_name = xstrdup("program_name");
2542 /* ========================================================================= */
2543 /* Utility function which create and register a par_t object in a BST */
2544 /* embedded in a context. */
2545 /* This object will have a name and a pointer to the option it refers to. */
2546 /* These object will be used to quickly find an option from a command */
2547 /* line parameter during the analysis phase. */
2548 /* */
2549 /* IN : an option name. */
2550 /* IN : a string of command line parameters to associate to the option. */
2551 /* Returns : 1 is all was fine else 0. */
2552 /* ========================================================================= */
2553 static int
2554 opt_set_parms(char * opt_name, char * par_str)
2556 char * par_name, *ctx_name;
2557 char * tmp_par_str, *end_tmp_par_str;
2558 ctx_t * ctx;
2559 opt_t * opt;
2560 bst_t * node;
2561 par_t * par, tmp_par;
2562 int rc = 1; /* return code */
2564 ll_t * list;
2565 ll_node_t * lnode;
2567 /* Look if the given option is defined. */
2568 /* """""""""""""""""""""""""""""""""""" */
2569 opt = locate_opt(opt_name);
2570 if (opt == NULL)
2571 fatal_internal("Unknown option %s", opt_name);
2573 /* For each context using this option. */
2574 /* """"""""""""""""""""""""""""""""""" */
2575 list = opt->ctx_list;
2577 lnode = list->head;
2578 while (lnode != NULL)
2580 /* Locate the context in the contexts tree. */
2581 /* """""""""""""""""""""""""""""""""""""""" */
2582 ctx_name = ((ctx_t *)(lnode->data))->name;
2584 ctx = locate_ctx(ctx_name);
2585 if (ctx == NULL)
2586 fatal_internal("Unknown context %s", ctx_name);
2587 else
2589 void * par_bst = ctx->par_bst;
2591 tmp_par_str = xstrdup(par_str);
2592 ltrim(tmp_par_str, " \t");
2593 rtrim(tmp_par_str, " \t", 0);
2594 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2595 if (par_name == NULL)
2596 fatal_internal("Parameters are missing for option %s", opt_name);
2598 /* For each parameter given in par_str, creates a par_t object and */
2599 /* insert it the in the parameters BST of the context. */
2600 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2601 while (par_name != NULL)
2603 tmp_par.name = par_name;
2605 node = bst_find(&tmp_par, &par_bst, par_compare);
2606 if (node != NULL)
2608 fatal_internal("The parameter %s is already defined in context %s",
2609 par_name, ctx->name);
2610 rc = 0;
2612 else
2614 par = xmalloc(sizeof(par_t));
2615 par->name = xstrdup(par_name);
2616 par->opt = opt; /* Link the option to this parameter */
2618 bst_search(par, &par_bst, par_compare);
2620 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2623 /* Update the value of the root of ctx->par_bst as it may have */
2624 /* been modified. */
2625 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2626 ctx->par_bst = par_bst;
2628 free(tmp_par_str);
2630 lnode = lnode->next;
2633 return rc;
2636 /* ==================================================================== */
2637 /* Create a new context instance. */
2638 /* IN ctx : a context pointer to allow this instance to */
2639 /* access the context fields */
2640 /* IN prev_ctx_inst : the context instance whose option leading to the */
2641 /* creation of this new context instance is part of */
2642 /* Returns : the new context. */
2643 /* ==================================================================== */
2644 static ctx_inst_t *
2645 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
2647 opt_t * opt;
2648 opt_inst_t * gen_opt_inst;
2649 ctx_inst_t * ctx_inst;
2650 seen_opt_t * seen_opt;
2651 char * str, *opt_name;
2652 void * bst;
2653 bst_t * bst_node;
2655 /* Keep a trace of the opt_inst which was at the origin of the creation */
2656 /* of this context instance. */
2657 /* This will serve during the evaluation of the option callbacks. */
2658 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2659 if (prev_ctx_inst != NULL)
2661 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
2663 /* Update current_state. */
2664 /* """"""""""""""""""""" */
2665 cur_state->opt_name = gen_opt_inst->opt->name;
2667 else
2668 gen_opt_inst = NULL;
2670 /* Create and initialize the new context instance. */
2671 /* """"""""""""""""""""""""""""""""""""""""""""""" */
2672 ctx_inst = xmalloc(sizeof(ctx_inst_t));
2673 ctx_inst->ctx = ctx;
2674 ctx_inst->prev_ctx_inst = prev_ctx_inst;
2675 ctx_inst->gen_opt_inst = gen_opt_inst;
2676 ctx_inst->incomp_bst_list = ll_new();
2677 ctx_inst->opt_inst_list = ll_new();
2678 ctx_inst->seen_opt_bst = NULL;
2680 ll_node_t * node;
2682 if (prev_ctx_inst == NULL)
2683 first_ctx_inst = ctx_inst;
2685 /* Initialize the occurrence counters of each opt allowed in the context. */
2686 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2687 node = ctx->opt_list->head;
2688 while (node != NULL)
2690 opt = node->data;
2691 opt->occurrences = 0;
2693 node = node->next;
2696 /* Initialize the BST containing the seen indicator for all the options */
2697 /* allowed in this context instance. */
2698 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2699 node = ctx->opt_list->head;
2700 while (node != NULL)
2702 opt = node->data;
2703 seen_opt = xmalloc(sizeof(seen_opt_t));
2704 seen_opt->opt = opt;
2705 seen_opt->par = NULL;
2706 seen_opt->seen = 0;
2708 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
2710 node = node->next;
2713 /* Initialize the BST containing the incompatibles options. */
2714 /* Incompatibles option names are read from strings found in the list */
2715 /* incomp_list present in each instance of ctx_t. */
2716 /* These names are then used to search for the object of type seen_opt_t */
2717 /* which is already present in the seen_opt_bst of the context instance. */
2718 /* in the BST. */
2719 /* Once found the seen_opt_t object in inserted in the new BST */
2720 /* At the end the new BST in added to the list incomp_bst_list. */
2721 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2722 node = ctx->incomp_list->head;
2723 while (node != NULL)
2725 bst = NULL;
2726 seen_opt_t tmp_seen_opt;
2728 str = xstrdup(node->data);
2729 ltrim(str, " \t");
2730 rtrim(str, " \t", 0);
2731 opt_name = strtok(str, " \t"); /* Extract the first option name. */
2733 while (opt_name != NULL) /* For each option name. */
2735 if ((opt = locate_opt(opt_name)) != NULL)
2737 /* The option found is searched in the tree of potential */
2738 /* seen options. */
2739 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2740 tmp_seen_opt.opt = opt;
2742 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2743 seen_opt_compare);
2745 if (bst_node != NULL)
2747 /* If found then it is added into the new BST tree. */
2748 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2749 seen_opt = bst_node->key;
2750 bst_search(seen_opt, &bst, seen_opt_compare);
2752 else
2753 /* Not found! That means that the option is unknown in this */
2754 /* context as all options has have a seen_opt structure in */
2755 /* seen_opt_bst. */
2756 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2757 fatal_internal("%s is not known in the context %s", opt->name,
2758 ctx->name);
2760 else
2761 fatal_internal("%s: unknown option.", opt_name);
2763 opt_name = strtok(NULL, " \t");
2766 free(str);
2767 ll_append(ctx_inst->incomp_bst_list, bst);
2769 node = node->next;
2772 return ctx_inst;
2775 /* ====================================================================== */
2776 /* Create a list formed by all the significant command line words */
2777 /* Words beginning or ending with { or } are split. Each of these */
2778 /* symbols will get their own place in the list. */
2779 /* */
2780 /* the {...} part delimits a context, the { will not appear in the list */
2781 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2782 /* to facilitate the parsing phase. | must not be used by the end user. */
2783 /* */
2784 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2785 /* program name is not considered. */
2786 /* IN words : is the array of strings constituting the command line to */
2787 /* parse. */
2788 /* Returns : 1 on success, 0 if a { or } is missing. */
2789 /* ====================================================================== */
2790 static int
2791 ctxopt_build_cmdline_list(int nb_words, char ** words)
2793 int i;
2794 char * prev_word = NULL;
2795 char * word;
2796 char * ptr;
2797 int level = 0;
2798 ll_node_t *node, *start_node;
2800 /* The analysis is divided into three passes, this is not optimal but */
2801 /* must be done only one time. Doing that we privilege readability. */
2802 /* */
2803 /* In the following, SG is the ascii character 1d (dec 29) */
2804 /* */
2805 /* The first pass creates the list, extract the leading an trailing */
2806 /* SG '{' and '}' of each word and give them their own place in the */
2807 /* list */
2808 /* */
2809 /* The second pass transform the '{...}' blocks by a trailing SG */
2810 /* ({...} -> ...|) */
2811 /* */
2812 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2813 /* the middle in the remaining list elements and recreate the pseudo */
2814 /* argument: {} */
2815 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2817 /* If the option list is not empty, clear it before going further. */
2818 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2819 if (cmdline_list != NULL)
2821 node = cmdline_list->head;
2822 while (node != NULL)
2824 free(node->data);
2825 ll_delete(cmdline_list, node);
2826 node = cmdline_list->head;
2829 else
2830 cmdline_list = ll_new();
2832 start_node = cmdline_list->head; /* In the following loop start_node will *
2833 * contain a pointer to the current *
2834 * word stripped from its leading *
2835 * sequence of {, }. */
2836 for (i = 0; i < nb_words; i++)
2838 size_t len = strlen(words[i]);
2839 size_t start, end;
2840 char * str;
2842 str = words[i];
2844 /* Replace each occurrence of the legal word {} by the characters */
2845 /* 0x02 and 0x03 to hide them from the following process. */
2846 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2847 while ((ptr = strstr(str, "{}")) != NULL)
2849 *ptr = 0x02; /* Arbitrary values unlikely. */
2850 *(ptr + 1) = 0x03; /* present in a word */
2853 if (len > 1) /* The word contains at least 2 characters. */
2855 start = 0;
2857 /* Interpret its beginning and look for the start of the real word. */
2858 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2859 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
2861 ll_append(cmdline_list, xstrndup(str + start, 1));
2862 start++;
2863 start_node = cmdline_list->tail;
2866 end = len - 1;
2867 if (str[end] == '{' || str[end] == '}')
2869 if (end > 0 && str[end - 1] != '\\')
2871 ll_append(cmdline_list, xstrndup(str + end, 1));
2872 end--;
2873 node = cmdline_list->tail;
2875 while (str[end] == '{' || str[end] == '}')
2877 if (end > start && str[end - 1] == '\\')
2878 break;
2880 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
2881 end--;
2882 node = node->prev;
2887 if (start <= end)
2889 if (start_node != NULL)
2890 ll_insert_after(cmdline_list, start_node,
2891 xstrndup(str + start, end - start + 1));
2892 else
2893 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
2894 start_node = cmdline_list->tail;
2897 else if (len == 1)
2899 ll_append(cmdline_list, xstrdup(str));
2900 start_node = cmdline_list->tail;
2904 /* 2nd pass. */
2905 /* """"""""" */
2906 node = cmdline_list->head;
2908 level = 0;
2909 while (node != NULL)
2911 word = node->data;
2913 if (strcmp(word, "{") == 0)
2915 ll_node_t * old_node = node;
2916 level++;
2917 node = node->next;
2918 free(word);
2919 ll_delete(cmdline_list, old_node);
2921 else if (strcmp(word, "}") == 0)
2923 level--;
2925 if (level < 0)
2926 return 0;
2927 else
2928 *word = 0x1d;
2930 else
2931 node = node->next;
2934 if (level != 0)
2935 return 0;
2937 /* 3rd pass. */
2938 /* """"""""" */
2939 node = cmdline_list->head;
2941 while (node != NULL)
2943 word = node->data;
2945 /* Restore the original { and } characters forming the legal word {}. */
2946 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2947 while ((ptr = strchr(word, 0x02)) != NULL)
2948 *ptr = '{';
2949 while ((ptr = strchr(word, 0x03)) != NULL)
2950 *ptr = '}';
2952 /* Remove a SG if the previous element is SG. */
2953 /* """""""""""""""""""""""""""""""""""""""""" */
2954 if (strcmp(word, "\x1d") == 0)
2956 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
2958 ll_node_t * old_node = node;
2959 node = node->prev;
2960 free(old_node->data);
2961 ll_delete(cmdline_list, old_node);
2964 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
2965 * a parameter. Protect it. */
2967 free(node->data);
2968 node->data = xstrdup("\\-");
2971 prev_word = node->data;
2972 node = node->next;
2975 /* Clean useless and SG at the beginning and end of list. */
2976 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
2977 node = cmdline_list->head;
2979 if (node == NULL)
2980 return 1;
2982 word = node->data;
2984 if (strcmp(word, "\x1d") == 0)
2986 free(word);
2987 ll_delete(cmdline_list, node);
2990 node = cmdline_list->tail;
2991 if (node == NULL)
2992 return 1;
2994 word = node->data;
2996 if (strcmp(word, "\x1d") == 0)
2998 free(word);
2999 ll_delete(cmdline_list, node);
3002 return 1;
3005 /* ===================================================================== */
3006 /* Build and analyze the command line list and create the linked data */
3007 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3008 /* This function identifies the following errors and creates an array of */
3009 /* The remaining unanalyzed arguments. */
3010 /* - detect missing arguments */
3011 /* - detect too many arguments */
3012 /* - detect unknown parameters in a context */
3013 /* - detect too many occurrences of a parameters in a context */
3014 /* - detect missing required arguments in a context */
3015 /* */
3016 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3017 /* program name is not considered */
3018 /* IN words : is the array of strings constituting the command line to */
3019 /* parse. */
3020 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3021 /* is present in the list. */
3022 /* OUT rem_args : array of remaining command line arguments if a -- */
3023 /* is present in the list. This array must be free by */
3024 /* The caller as it is allocated here. */
3025 /* ===================================================================== */
3026 void
3027 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3028 char *** rem_args)
3030 ctx_t * ctx;
3031 opt_t * opt;
3032 par_t * par;
3033 ctx_inst_t * ctx_inst;
3034 opt_inst_t * opt_inst;
3035 int expect_par = 0;
3036 int expect_arg = 0;
3037 int expect_par_or_arg = 0;
3039 ll_node_t * cli_node;
3040 bst_t * bst_node;
3041 seen_opt_t * bst_seen_opt;
3042 char * par_name;
3043 void * bst;
3045 ll_node_t * node;
3047 if (!ctxopt_build_cmdline_list(nb_words, words))
3048 fatal_internal(
3049 "The command line could not be parsed: missing { or } detected.");
3051 if (main_ctx == NULL)
3052 fatal_internal("At least one context must have been created.");
3054 /* Check that all options has an action and at least one parameter. */
3055 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3056 bst_walk(options_bst, bst_check_opt_cb);
3058 /* Create the first ctx_inst record. */
3059 /* """"""""""""""""""""""""""""""""" */
3060 ctx = main_ctx;
3062 ctx_inst_list = ll_new();
3063 ctx_inst = new_ctx_inst(ctx, NULL);
3064 ctx_inst->par_name = NULL;
3066 /* Update current_state. */
3067 /* """"""""""""""""""""" */
3068 cur_state->ctx_name = ctx->name;
3070 ll_append(ctx_inst_list, ctx_inst);
3072 /* For each node in the command line. */
3073 /* """""""""""""""""""""""""""""""""" */
3074 cli_node = cmdline_list->head;
3075 expect_par = 1;
3076 while (cli_node != NULL)
3078 if (strcmp(cli_node->data, "--") == 0)
3079 break; /* No new parameter will be analyzed after this point. */
3081 par_name = cli_node->data;
3083 if (strcmp(par_name, "\x1d") == 0)
3085 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3086 check_for_occurrences_issues(ctx_inst);
3088 /* Forced backtracking to the previous context instance. */
3089 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3090 if (ctx_inst->prev_ctx_inst != NULL)
3092 ctx_inst = ctx_inst->prev_ctx_inst;
3093 ctx = ctx_inst->ctx;
3095 /* Update current_states. */
3096 /* """"""""""""""""""""" */
3097 cur_state->ctx_name = ctx->name;
3098 cur_state->ctx_par_name = ctx_inst->par_name;
3100 else
3102 /* Update current_state. */
3103 /* """"""""""""""""""""" */
3104 cur_state->ctx_par_name = NULL;
3107 else if (expect_par && *par_name == '-')
3109 int pos = 0;
3110 char * prefix;
3112 /* Update current_state. */
3113 /* """"""""""""""""""""" */
3114 cur_state->cur_opt_par_name = par_name;
3115 cur_state->ctx_name = ctx->name;
3116 cur_state->ctx_par_name = ctx_inst->par_name;
3118 /* An expected parameter has been seen. */
3119 /* """""""""""""""""""""""""""""""""""" */
3120 if ((par = locate_par(par_name, ctx)) == NULL)
3122 opt_t * popt;
3123 char * word;
3125 /* Look if this parameter is an unique abbreviation of a longer */
3126 /* parameter. If this is the case then just replace it with its */
3127 /* full length version and try again. */
3128 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3129 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3131 cli_node->data = word;
3132 continue;
3135 /* Try to find a prefix which is a valid parameter in this context */
3136 /* If found, split the cli_node in two to build a new parameter */
3137 /* node and followed by a node containing the remaining string */
3138 /* If the new parameter corresponds to an option not taking */
3139 /* argument then prefix the remaining string whit a dash as it may */
3140 /* contain a new parameter. */
3141 /* The new parameter will be re-evaluated in the next iteration of */
3142 /* the loop. */
3143 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3144 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3145 if (prefix != NULL && pos != 0)
3147 cli_node->data = prefix; /* prefix contains le name of a valid *
3148 | parameter in this context. */
3150 if (popt->args)
3152 /* The parameter may be followed by arguments. */
3153 /* ''''''''''''''''''''''''''''''''''''''''''' */
3154 if (*(par_name + pos) == '-')
3156 word = xstrdup("\\"); /* Protect the '-' */
3157 word = strappend(word, par_name + pos, NULL);
3159 else
3160 word = xstrdup(par_name + pos);
3162 else
3164 /* The parameter does not take arguments, the */
3165 /* following word must be a parameter or nothing */
3166 /* hence prefix it with a dash. */
3167 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3168 word = xstrdup("-");
3169 word = strappend(word, par_name + pos, NULL);
3172 /* Insert it after the current node in the list. */
3173 /* """"""""""""""""""""""""""""""""""""""""""""" */
3174 ll_insert_after(cmdline_list, cli_node, word);
3176 continue; /* loop */
3178 else
3180 check_for_missing_mandatory_opt(ctx_inst, par_name);
3181 check_for_occurrences_issues(ctx_inst);
3183 if (ctx_inst->prev_ctx_inst == NULL)
3185 char * errmsg = xstrdup("");
3187 /* Update current_state. */
3188 /* """"""""""""""""""""" */
3189 cur_state->ctx_par_name = NULL;
3191 *user_string = '\0';
3192 *user_string2 = '\0';
3194 user_string = strappend(user_string, par_name, NULL);
3196 bst_walk(contexts_bst, bst_match_par_cb);
3198 if (*user_string2 != '\0')
3200 errmsg = strappend(
3201 errmsg,
3202 "\nIt appears to be defined in the context(s):", user_string2,
3203 "\n", NULL);
3206 fatal(CTXOPTUNKPAR, errmsg);
3208 else
3210 /* Tries to backtrack and analyse the same parameter in the */
3211 /* previous context. */
3212 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3213 ctx_inst = ctx_inst->prev_ctx_inst;
3214 ctx = ctx_inst->ctx;
3216 /* Update current_state. */
3217 /* """"""""""""""""""""" */
3218 cur_state->ctx_name = ctx->name;
3219 cur_state->ctx_par_name = ctx_inst->par_name;
3221 cli_node = cli_node->prev;
3225 else
3227 seen_opt_t seen_opt;
3229 /* The parameter is valid in the context, create a opt_inst and */
3230 /* append it to the ctx_inst list options list. */
3231 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3232 opt = par->opt;
3234 opt->occurrences++;
3236 opt_inst = xmalloc(sizeof(opt_inst_t));
3237 opt_inst->opt = opt;
3238 opt_inst->par = par_name;
3239 opt_inst->values_list = ll_new();
3240 opt_inst->next_ctx_inst = NULL;
3242 /* Priority option are inserted at the start of the opt_inst list */
3243 /* but their order of appearance in the context definition must */
3244 /* be preserver so each new priority option will be placed after */
3245 /* the previous ones at the start of the opt_inst list. */
3246 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3247 if (!opt->eval_first)
3248 ll_append(ctx_inst->opt_inst_list, opt_inst);
3249 else
3251 ll_node_t * node = ctx_inst->opt_inst_list->head;
3252 opt_inst_t * tmp_opt_inst;
3253 while (node != NULL)
3255 tmp_opt_inst = node->data;
3256 if (!tmp_opt_inst->opt->eval_first)
3258 ll_insert_before(ctx_inst->opt_inst_list, node, opt_inst);
3259 break;
3261 else
3262 node = node->next;
3264 if (node == NULL)
3265 ll_append(ctx_inst->opt_inst_list, opt_inst);
3268 /* Check if an option was already seen in the */
3269 /* current context instance. */
3270 /* """""""""""""""""""""""""""""""""""""""""" */
3271 seen_opt.opt = opt;
3273 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3274 seen_opt_compare);
3276 /* bst_node cannot be NULL here. */
3278 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3280 if (!opt->multiple && bst_seen_opt->seen == 1)
3281 fatal(CTXOPTDUPOPT, NULL);
3283 /* Check if this option is compatible with the options already */
3284 /* seen in this context instance. */
3285 /* Look if the option is present in one on the BST present in */
3286 /* the incomp_bst_list of the context instance. */
3287 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3288 node = ctx_inst->incomp_bst_list->head;
3289 while (node != NULL)
3291 bst = node->data;
3292 user_object = NULL;
3294 /* There can only have one seen_opt object in the BST tree was */
3295 /* already seen, try to locate it, the result will be put in */
3296 /* user_object by the bst_seen_opt_seen_cb function. */
3297 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3298 bst_walk(bst, bst_seen_opt_seen_cb);
3300 /* If it is the case, look if the current option is also */
3301 /* in this BST. */
3302 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3303 if (user_object != NULL)
3305 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3307 if (bst_node != NULL)
3309 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3310 if (bst_seen_opt->seen == 0)
3311 fatal(CTXOPTINCOPT, (char *)user_object);
3315 node = node->next;
3318 /* Mark this option as seen in the current context instance. */
3319 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3320 bst_seen_opt->seen = 1;
3321 free(bst_seen_opt->par);
3322 bst_seen_opt->par = xstrdup(par_name);
3324 /* If this option leads to a next context, create a new ctx_inst */
3325 /* and switch to it for the analyse of the future parameter. */
3326 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3327 if (opt->next_ctx != NULL)
3329 ctx = locate_ctx(opt->next_ctx);
3331 if (ctx == NULL)
3332 fatal_internal("%s: unknown context.", opt->next_ctx);
3334 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3335 ctx_inst->par_name = xstrdup(par_name);
3337 ll_append(ctx_inst_list, ctx_inst);
3340 /* Look is we must expect some arguments. */
3341 /* """""""""""""""""""""""""""""""""""""" */
3342 expect_par_or_arg = 0;
3343 expect_par = 0;
3344 expect_arg = 0;
3346 if (!opt->args)
3347 expect_par = 1; /* Parameter doesn't accept any argument. */
3348 else
3350 if (!opt->optional_args)
3351 expect_arg = 1; /* Parameter has mandatory arguments. */
3352 else
3353 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3357 else if (expect_par && *par_name != '-')
3359 ll_node_t * n = cli_node->next;
3361 /* Look if potential arguments must still be analyzed until the */
3362 /* end of the context/command line part to analyze/command line. */
3363 /* If this is the case we have met an extra argument. */
3364 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3365 while (n != NULL)
3367 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3368 fatal(CTXOPTUNXARG, NULL);
3370 if (*(char *)(n->data) == '-')
3371 fatal(CTXOPTUNXARG, NULL);
3373 n = n->next;
3376 break; /* An unexpected non parameter was seen, if no Potential *
3377 | arguments remain in the command line assume that it *
3378 | is is the first of the non arguments and stop the *
3379 | command line analysis. */
3381 else if (expect_arg && *par_name != '-')
3383 ll_node_t * cstr_node;
3384 constraint_t * cstr;
3386 /* Check if the arguments of the option respects */
3387 /* the attached constraints if any. */
3388 /* """"""""""""""""""""""""""""""""""""""""""""" */
3389 cstr_node = opt->constraints_list->head;
3390 while (cstr_node != NULL)
3392 cstr = cstr_node->data;
3393 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3394 cur_state->cur_opt_par_name))
3396 fputs("\n", stderr);
3397 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3400 cstr_node = cstr_node->next;
3403 /* If the argument is valid, store it. */
3404 /* """"""""""""""""""""""""""""""""""" */
3405 if (*par_name == '\\' && *(par_name + 1) == '-')
3406 ll_append(opt_inst->values_list, par_name + 1);
3407 else
3408 ll_append(opt_inst->values_list, par_name);
3410 expect_arg = 0;
3411 expect_par = 0;
3412 expect_par_or_arg = 0;
3414 if (opt->multiple_args)
3415 expect_par_or_arg = 1;
3416 else
3417 expect_par = 1; /* Parameter takes only one argument. */
3419 else if (expect_arg && *par_name == '-')
3420 fatal(CTXOPTMISARG, NULL);
3421 else if (expect_par_or_arg)
3423 expect_arg = 0;
3424 expect_par = 0;
3425 expect_par_or_arg = 0;
3427 if (*par_name != '-')
3428 expect_arg = 1; /* Consider this word as an argument and retry. */
3429 else
3430 expect_par = 1; /* Consider this word as a parameter and retry. */
3432 cli_node = cli_node->prev;
3435 cli_node = cli_node->next;
3438 if (cmdline_list->len > 0 && *par_name == '-')
3440 if (expect_arg && !opt->optional_args)
3441 fatal(CTXOPTMISARG, NULL);
3444 /* Look if a context_instance has unseen mandatory options. */
3445 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3446 node = ctx_inst_list->head;
3447 while (node != NULL)
3449 ctx_inst = node->data;
3451 /* Update current_state. */
3452 /* """"""""""""""""""""" */
3453 cur_state->ctx_name = ctx_inst->ctx->name;
3454 cur_state->ctx_par_name = ctx_inst->par_name;
3456 check_for_missing_mandatory_opt(ctx_inst, par_name);
3457 check_for_occurrences_issues(ctx_inst);
3459 node = node->next;
3462 /* Allocate the array containing the remaining not analyzed */
3463 /* command line arguments. */
3464 /* NOTE: The strings in the array are just pointer to the */
3465 /* data of the generating list and must not be freed. */
3466 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3467 if (cli_node != NULL)
3469 if (strcmp((char *)cli_node->data, "--") == 0)
3470 /* The special parameter -- was encountered, the -- argument is not */
3471 /* put in the remaining arguments. */
3472 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3473 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3474 else
3475 /* A non parameter was encountered when a parameter was expected. We */
3476 /* assume that the evaluation of the remaining command line argument */
3477 /* are not the responsibility of the users code. */
3478 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3479 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3481 else
3483 *nb_rem_args = 0;
3484 *rem_args = xmalloc(sizeof(char *));
3485 (*rem_args)[0] = NULL;
3489 /* ==================================================== */
3490 /* Free ctxopt memory used for its internal structures. */
3491 /* ==================================================== */
3492 void
3493 ctxopt_free_memory(void)
3495 ll_destroy(cmdline_list, NULL);
3496 ll_destroy(ctx_inst_list, ctx_inst_free);
3497 bst_destroy(options_bst, opt_free);
3498 bst_destroy(contexts_bst, ctx_free);
3501 /* ==================================================================== */
3502 /* Parse the options data structures and launches the callback function */
3503 /* attached to each options instances. */
3504 /* This calls a recursive function which proceeds context per context. */
3505 /* ==================================================================== */
3506 void
3507 ctxopt_evaluate(void)
3509 evaluate_ctx_inst(first_ctx_inst);
3512 /* =================================================================== */
3513 /* Recursive function called by ctxopt_evaluate to process the list of */
3514 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3515 /* action attached to the context and its option instances. */
3516 /* =================================================================== */
3517 static void
3518 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
3520 opt_inst_t * opt_inst;
3521 ctx_t * ctx;
3522 opt_t * opt;
3523 ll_node_t * opt_inst_node;
3524 char ** args;
3525 int nb_args;
3527 if (ctx_inst == NULL)
3528 return;
3530 ctx = ctx_inst->ctx;
3532 /* Do not evaluate the action attached to this context is there is no */
3533 /* option to evaluate. */
3534 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3535 opt_inst_node = ctx_inst->opt_inst_list->head;
3536 if (opt_inst_node == NULL)
3537 return;
3539 /* Call the entering action attached to this context if any. */
3540 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3541 if (ctx->action != NULL)
3543 if (ctx_inst->prev_ctx_inst != NULL)
3544 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
3545 ctx->nb_data, ctx->data);
3546 else
3547 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
3550 /* For each instance of options. */
3551 /* """"""""""""""""""""""""""""" */
3552 while (opt_inst_node != NULL)
3554 opt_inst = (opt_inst_t *)(opt_inst_node->data);
3555 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
3556 &args);
3557 opt = opt_inst->opt;
3559 /* Launch the attached action if any. */
3560 /* """""""""""""""""""""""""""""""""" */
3561 if (opt->action != NULL)
3562 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
3563 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
3565 if (opt_inst->next_ctx_inst != NULL)
3566 evaluate_ctx_inst(opt_inst->next_ctx_inst);
3568 if (args != NULL)
3569 free(args);
3571 opt_inst_node = opt_inst_node->next;
3574 /* Call the exiting action attached to this context if any. */
3575 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3576 if (ctx->action != NULL)
3578 if (ctx_inst->prev_ctx_inst != NULL)
3579 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
3580 ctx->nb_data, ctx->data);
3581 else
3582 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
3586 /* ============================================================ */
3587 /* Create and initializes a new context. */
3588 /* - allocate space. */
3589 /* - name it. */
3590 /* - initialize its option with a few of their characteristics. */
3591 /* ============================================================ */
3592 void
3593 ctxopt_new_ctx(char * name, char * opts_specs)
3595 ctx_t * ctx;
3596 bst_t * node;
3597 char * p;
3599 if (!ctxopt_initialized)
3600 fatal_internal("Please call ctxopt_init first.");
3602 ctx = xmalloc(sizeof(ctx_t));
3604 /* Validates the context name: */
3605 /* ALPHA+(ALPHANUM|_)* */
3606 /* """"""""""""""""""""""""""" */
3607 p = name;
3608 if (!isalpha(*p))
3609 fatal_internal("%s: a context name must start with a letter.", name);
3611 p++;
3612 while (*p)
3614 if (!isalnum(*p) && *p != '_')
3615 fatal_internal("%s: a context name must only contain letters, "
3616 "numbers or '_'.",
3617 name);
3618 p++;
3621 ctx->name = xstrdup(name);
3622 ctx->opt_list = ll_new(); /* List of options legit in this context. */
3623 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
3624 ctx->par_bst = NULL;
3625 ctx->data = NULL;
3626 ctx->action = NULL;
3628 /* The first created context is the main one. */
3629 /* """""""""""""""""""""""""""""""""""""""""" */
3630 if (contexts_bst == NULL)
3632 main_ctx = ctx;
3634 cur_state->ctx_name = ctx->name;
3637 if (init_opts(opts_specs, ctx) == 0)
3638 exit(EXIT_FAILURE);
3639 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
3640 fatal_internal("The context %s already exists", name);
3641 else
3642 bst_search(ctx, &contexts_bst, ctx_compare);
3645 /* ==================================================== */
3646 /* Display a usage screen limited to a specific context */
3647 /* IN: the context name. */
3648 /* IN: what to do after (continue or exit the program) */
3649 /* possible values: continue_after, exit_after. */
3650 /* ==================================================== */
3651 void
3652 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
3654 ctx_t * ctx;
3655 ll_t * list;
3657 int has_optional = 0;
3658 int has_ellipsis = 0;
3659 int has_rule = 0;
3660 int has_generic_arg = 0;
3661 int has_ctx_change = 0;
3662 int has_early_eval = 0;
3664 ctx = locate_ctx(ctx_name);
3665 if (ctx == NULL)
3666 fatal_internal("%s: unknown context.", ctx_name);
3668 if (cur_state->ctx_par_name == NULL)
3669 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
3670 else
3671 printf("\nSynopsis for the context introduced by %s:\n",
3672 cur_state->ctx_par_name);
3674 list = ctx->opt_list;
3675 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3676 &has_ctx_change, &has_early_eval);
3678 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3679 has_optional, has_ellipsis, has_rule);
3681 if (action == exit_after)
3682 exit(EXIT_FAILURE);
3685 /* =================================================== */
3686 /* Display a full usage screen about all contexts. */
3687 /* IN: what to do after (continue or exit the program) */
3688 /* possible values: continue_after, exit_after. */
3689 /* =================================================== */
3690 void
3691 ctxopt_disp_usage(usage_behaviour action)
3693 ll_t * list;
3694 int has_optional = 0;
3695 int has_ellipsis = 0;
3696 int has_rule = 0;
3697 int has_generic_arg = 0;
3698 int has_ctx_change = 0;
3699 int has_early_eval = 0;
3701 if (main_ctx == NULL)
3702 fatal_internal("At least one context must have been created.");
3704 /* Usage for the first context. */
3705 /* """""""""""""""""""""""""""" */
3706 printf("\nAllowed options in the default context:\n");
3707 list = main_ctx->opt_list;
3708 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3709 &has_ctx_change, &has_early_eval);
3711 /* Usage for the other contexts. */
3712 /* """"""""""""""""""""""""""""" */
3713 bst_walk(contexts_bst, bst_print_ctx_cb);
3715 /* Contextual syntactic explanations. */
3716 /* """""""""""""""""""""""""""""""""" */
3717 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3718 has_optional, has_ellipsis, has_rule);
3720 if (action == exit_after)
3721 exit(EXIT_FAILURE);
3724 /* *********************************** */
3725 /* Built-in constraint check functions */
3726 /* *********************************** */
3728 /* ============================================================= */
3729 /* This constraint checks if each arguments respects a format as */
3730 /* defined for the scanf function. */
3731 /* return 1 if yes and 0 if no. */
3732 /* ============================================================= */
3734 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
3736 int rc = 0;
3738 char x[256];
3739 char y;
3740 char * format;
3742 if (nb_args != 1)
3743 fatal_internal("Format constraint: invalid number of parameters.");
3745 if (strlen(value) > 255)
3746 value[255] = '\0';
3748 format = xstrdup(args[0]);
3749 format = strappend(format, "%c", NULL);
3751 rc = sscanf(value, format, x, &y);
3752 if (rc != 1)
3753 fprintf(stderr,
3754 "The argument %s of %s does not respect the imposed format: %s.",
3755 value, par, args[0]);
3757 free(format);
3759 return rc == 1;
3762 /* ================================================================== */
3763 /* This constraint checks if each arguments of the option instance is */
3764 /* between a minimum and a maximum (inclusive). */
3765 /* return 1 if yes and 0 if no. */
3766 /* ================================================================== */
3768 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
3770 regex_t re;
3772 if (nb_args != 1)
3773 fatal_internal(
3774 "Regular expression constraint: invalid number of parameters.");
3776 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
3777 fatal_internal("Invalid regular expression %s.", args[0]);
3779 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
3781 fprintf(stderr,
3782 "The argument %s of %s doesn't match the constraining "
3783 "regular expression %s.",
3784 value, par, args[0]);
3785 return 0;
3788 regfree(&re);
3790 return 1;
3793 /* ================================================================== */
3794 /* This constraint checks if each arguments of the option instance is */
3795 /* between a minimum and a maximum (inclusive). */
3796 /* return 1 if yes and 0 if no. */
3797 /* ================================================================== */
3799 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
3801 long min, max;
3802 char c;
3803 char * ptr;
3804 int n;
3805 long v;
3806 int min_only = 0;
3807 int max_only = 0;
3809 if (nb_args != 2)
3810 fatal_internal("Range constraint: invalid number of parameters.");
3812 if (strcmp(args[0], ".") == 0)
3813 max_only = 1;
3814 else
3815 n = sscanf(args[0], "%ld%c", &min, &c);
3817 if (!max_only && n != 1)
3818 fatal_internal("Range constraint: min: invalid parameters.");
3820 if (strcmp(args[1], ".") == 0)
3821 min_only = 1;
3822 else
3823 n = sscanf(args[1], "%ld%c", &max, &c);
3825 if (!min_only && n != 1)
3826 fatal_internal("Range constraint: max: invalid parameters.");
3828 if (min_only && max_only)
3829 fatal_internal("Range constraint: invalid parameters.");
3831 errno = 0;
3832 v = strtol(value, &ptr, 10);
3833 if (errno || ptr == value)
3834 return 0;
3836 if (min_only)
3838 if (v < min)
3840 fprintf(stderr,
3841 "The argument %ld of %s is not greater than or equal to %ld.", v,
3842 par, min);
3843 return 0;
3845 else
3846 return 1;
3848 else if (max_only)
3850 if (v > max)
3852 fprintf(stderr,
3853 "The argument %ld of %s is not less than or equal to %ld.", v,
3854 par, max);
3855 return 0;
3857 else
3858 return 1;
3860 else if (v < min || v > max)
3862 fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
3863 par, min, max);
3864 return 0;
3867 return 1; /* check passed */
3870 /* =============================================================== */
3871 /* This function provides a way to set the behaviour of a context. */
3872 /* =============================================================== */
3873 void
3874 ctxopt_add_global_settings(settings s, ...)
3876 va_list(args);
3877 va_start(args, s);
3879 switch (s)
3881 case error_functions:
3883 typedef void fn(errors e, state_t * state);
3885 void (*function)(errors e, state_t * state);
3887 errors e;
3888 e = va_arg(args, errors);
3889 function = va_arg(args, fn *);
3890 err_functions[e] = function;
3891 break;
3894 default:
3895 break;
3897 va_end(args);
3900 /* ================================================================ */
3901 /* This function provides a way to set the behaviour of an option. */
3902 /* It can take a variable number of arguments according to its */
3903 /* first argument: */
3904 /* - parameter: */
3905 /* o a string containing an option name and all its possible */
3906 /* parameters separates by spaces, tabs or commas (char *) */
3907 /* (e.g: "help -h -help"). */
3908 /* - actions: */
3909 /* o a string containing an option name. */
3910 /* o a pointer to a function which will be called at evaluation */
3911 /* time. */
3912 /* - constraints: */
3913 /* o a string containing an option name. */
3914 /* o a pointer to a function to check if an argument is valid. */
3915 /* o a strings containing the arguments to this function. */
3916 /* ================================================================ */
3917 void
3918 ctxopt_add_opt_settings(settings s, ...)
3920 opt_t * opt;
3921 void * ptr = NULL;
3923 va_list(args);
3924 va_start(args, s);
3926 switch (s)
3928 /* This part associates some command line parameters to an option. */
3929 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3930 case parameters:
3932 char * opt_name;
3933 char * params;
3935 /* The second argument must be a string containing: */
3936 /* - The name of an existing option. */
3937 /* - a list of parameters with a leading dash (-). */
3938 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3939 ptr = va_arg(args, char *);
3940 opt_name = ptr;
3942 if (opt_name != NULL)
3944 if ((opt = locate_opt(opt_name)) != NULL)
3946 ptr = va_arg(args, char *);
3947 params = ptr;
3949 if (!opt_set_parms(opt_name, params))
3950 fatal_internal(
3951 "duplicates parameters or bad settings for the option (%s).",
3952 params);
3954 else
3955 fatal_internal("%s: unknown option.", opt_name);
3957 else
3958 fatal_internal(
3959 "ctxopt_opt_add_settings: parameters: not enough arguments.");
3961 /* Here opt is a known option. */
3962 /* """"""""""""""""""""""""""" */
3963 if (opt->params != NULL)
3964 fatal_internal("Parameters are already set for %s", opt_name);
3965 else
3967 size_t n;
3968 size_t l = strlen(params);
3970 opt->params = xstrdup(params);
3971 while ((n = strcspn(opt->params, " \t")) < l)
3972 opt->params[n] = '|';
3975 break;
3978 /* This part associates a callback function to an option. */
3979 /* This function will be called when an instance of an option */
3980 /* is evaluated. */
3981 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3982 case actions:
3984 void * data;
3985 void (*function)();
3986 int nb_data = 0;
3988 /* The second argument must be the name of an existing option. */
3989 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3990 ptr = va_arg(args, char *);
3992 if ((opt = locate_opt(ptr)) != NULL)
3994 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
3995 void **);
3997 /* The third argument must be the callback function. */
3998 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
3999 function = va_arg(args, fn *);
4000 opt->action = function;
4002 /* The fourth argument must be a pointer to an user's defined */
4003 /* variable or structure that the previous function can manage. */
4004 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4005 while ((data = va_arg(args, void *)) != NULL)
4007 nb_data++;
4008 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4009 opt->data[nb_data - 1] = data;
4011 opt->nb_data = nb_data;
4013 else
4014 fatal_internal("%s: unknown option.", ptr);
4015 break;
4018 /* This part associates a list of functions to control some */
4019 /* characteristics of the arguments of an option. */
4020 /* Each function will be called in order and must return 1 */
4021 /* to validate the arguments. */
4022 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4023 case constraints:
4025 char * value;
4026 constraint_t * cstr;
4027 int (*function)();
4029 /* The second argument must be a string. */
4030 /* """"""""""""""""""""""""""""""""""""" */
4031 ptr = va_arg(args, char *);
4033 if ((opt = locate_opt(ptr)) != NULL)
4035 typedef int fn(int, char **, char *);
4037 /* The third argument must be a function. */
4038 /* """""""""""""""""""""""""""""""""""""" */
4039 function = va_arg(args, fn *);
4041 cstr = xmalloc(sizeof(constraint_t));
4042 cstr->constraint = function;
4044 /* The fourth argument must be a string containing the argument of */
4045 /* The previous function separated by spaces or tabs. */
4046 /* Theses arguments will be passed to the previous function */
4047 /* max: 32 argument! */
4048 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4049 value = xstrdup(va_arg(args, char *));
4051 cstr->to_free = value;
4052 cstr->args = xcalloc(sizeof(char *), 32);
4053 cstr->nb_args = str2argv(value, cstr->args, 32);
4054 ll_append(opt->constraints_list, cstr);
4056 else
4057 fatal_internal("%s: unknown option.", ptr);
4058 break;
4061 default:
4062 break;
4064 va_end(args);
4067 /* =============================================================== */
4068 /* This function provides a way to set the behaviour of a context. */
4069 /* =============================================================== */
4070 void
4071 ctxopt_add_ctx_settings(settings s, ...)
4073 ctx_t * ctx;
4075 va_list(args);
4076 va_start(args, s);
4078 switch (s)
4080 /* Add a set of mutually incompatible options in a context. */
4081 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4082 case incompatibilities:
4084 void * ptr;
4085 ll_t * list;
4086 size_t n;
4087 char * str;
4089 ptr = va_arg(args, char *);
4090 if ((ctx = locate_ctx(ptr)) != NULL)
4092 ptr = va_arg(args, char *);
4093 list = ctx->incomp_list;
4095 str = xstrdup(ptr);
4096 ltrim(str, " \t");
4097 rtrim(str, " \t", 0);
4099 n = strcspn(str, " \t");
4100 if (n > 0 && n < strlen(str))
4101 ll_append(list, str);
4102 else
4103 fatal_internal(
4104 "Not enough incompatible options in the string: \"%s\"", str);
4106 else
4107 fatal_internal("%s: unknown context.", ptr);
4108 break;
4111 /* Add functions which will be called when */
4112 /* entering and exiting a context. */
4113 /* """"""""""""""""""""""""""""""""""""""" */
4114 case actions:
4116 void * ptr;
4117 void * data;
4118 int (*function)();
4119 int nb_data = 0;
4121 ptr = va_arg(args, char *);
4122 if ((ctx = locate_ctx(ptr)) != NULL)
4124 typedef int fn(char *, direction, char *, int, void **);
4126 function = va_arg(args, fn *);
4127 ctx->action = function;
4129 while ((data = va_arg(args, void *)) != NULL)
4131 nb_data++;
4132 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4133 ctx->data[nb_data - 1] = data;
4135 ctx->nb_data = nb_data;
4137 else
4138 fatal_internal("%s: unknown context.", ptr);
4139 break;
4142 default:
4143 break;
4145 va_end(args);