Do not compile not yet used code
[ctxopt.git] / ctxopt.c
blob5c5c253b3fc5106ba90cc7bee4d53863b2a715f5
1 #include <errno.h>
2 #include <limits.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <ctype.h>
7 #include <sys/types.h>
8 #include <regex.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include "ctxopt.h"
13 /* *********************** */
14 /* Static global variables */
15 /* *********************** */
16 static void * contexts_bst;
17 static void * options_bst;
19 state_t * cur_state;
21 /* Prototypes */
23 /* ****************** */
24 /* Messages interface */
25 /* ****************** */
27 static void (**err_functions)(errors e, state_t * state);
29 static void
30 fatal_internal(const char * format, ...);
32 static void
33 fatal(errors e, char * errmsg);
35 static int user_rc; /* Used by various callback functions */
36 static int user_value; /* Used by various callback functions */
37 static char * user_string; /* Used by various callback functions */
38 static char * user_string2; /* Used by various callback functions */
39 static void * user_object; /* Used by various callback functions */
41 /* *************************** */
42 /* Memory management interface */
43 /* *************************** */
45 static void *
46 xmalloc(size_t size);
48 static void *
49 xcalloc(size_t num, size_t size);
51 static void *
52 xrealloc(void * ptr, size_t size);
54 static char *
55 xstrdup(const char * p);
57 static char *
58 xstrndup(const char * str, size_t len);
60 /* ************* */
61 /* BST interface */
62 /* ************* */
63 typedef struct bst_s bst_t;
65 typedef enum
67 preorder,
68 postorder,
69 endorder,
70 leaf
71 } walk_order_e;
73 #if 0 /* Unused yet */
74 static void *
75 bst_delete(const void * vkey, void ** vrootp,
76 int (*compar)(const void *, const void *));
77 #endif
79 static void
80 bst_destroy_recurse(bst_t * root, void (*free_action)(void *));
82 static void
83 bst_destroy(void * vrootp, void (*freefct)(void *));
85 static void *
86 bst_find(const void * vkey, void * const * vrootp,
87 int (*compar)(const void *, const void *));
89 static void *
90 bst_search(const void * vkey, void ** vrootp,
91 int (*compar)(const void *, const void *));
93 static void
94 bst_walk_recurse(const bst_t * root,
95 void (*action)(const void *, walk_order_e, int), int level);
97 static void
98 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int));
100 /* ********************* */
101 /* Linked list Interface */
102 /* ********************* */
104 typedef struct ll_node_s ll_node_t;
105 typedef struct ll_s ll_t;
107 static void
108 ll_append(ll_t * const list, void * const data);
110 static void
111 ll_prepend(ll_t * const list, void * const data);
113 static void
114 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data);
116 static void
117 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data);
119 static int
120 ll_delete(ll_t * const list, ll_node_t * node);
122 #if 0 /* Unused yet */
123 static ll_node_t *
124 ll_find(ll_t * const, void * const, int (*)(const void *, const void *));
125 #endif
127 static void
128 ll_init(ll_t * list);
130 static ll_node_t *
131 ll_new_node(void);
133 static ll_t *
134 ll_new(void);
136 static int
137 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array);
139 /* ****************** */
140 /* various interfaces */
141 /* ****************** */
143 static void
144 ltrim(char * str, const char * trim_str);
146 static void
147 rtrim(char * str, const char * trim_str, size_t min);
149 static int
150 strchrcount(char * str, char c);
152 static int
153 strpref(char * s1, char * s2);
155 static char *
156 xstrtok_r(char * str, const char * delim, char ** end);
158 /* **************** */
159 /* ctxopt interface */
160 /* **************** */
162 typedef struct opt_s opt_t;
163 typedef struct par_s par_t;
164 typedef struct ctx_s ctx_t;
165 typedef struct constraint_s constraint_t;
166 typedef struct ctx_inst_s ctx_inst_t;
167 typedef struct opt_inst_s opt_inst_t;
168 typedef struct seen_opt_s seen_opt_t;
170 static char *
171 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
173 static int
174 ctx_compare(const void * c1, const void * c2);
176 static int
177 opt_compare(const void * o1, const void * o2);
179 static int
180 par_compare(const void * a1, const void * a2);
182 static int
183 seen_opt_compare(const void * so1, const void * so2);
185 static ctx_t *
186 locate_ctx(char * name);
188 static opt_t *
189 locate_opt(char * name);
191 static par_t *
192 locate_par(char * name, ctx_t * ctx);
194 static void
195 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
196 int * has_rule, int * has_generic_arg, int * has_ctx_change,
197 int * has_early_eval);
198 static void
199 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
200 int has_optional, int has_ellipsis, int has_rule);
201 static void
202 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
204 static void
205 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
207 static void
208 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
210 static void
211 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
213 static void
214 bst_match_par_cb(const void * node, walk_order_e kind, int level);
216 static void
217 match_prefix_cb(const void * node, walk_order_e kind, int level);
219 static int
220 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
222 static int
223 opt_parse(char * s, opt_t ** opt);
225 static int
226 init_opts(char * spec, ctx_t * ctx);
228 static int
229 ctxopt_build_cmdline_list(int nb_words, char ** words);
231 static int
232 opt_set_parms(char * opt_name, char * par_str);
234 static ctx_inst_t *
235 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
237 static void
238 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
240 /* ********************** */
241 /* Message implementation */
242 /* ********************** */
244 /* ================================================================== */
245 /* Fatal error function used when an fatal condition was encountered. */
246 /* This function is reserved for the ctxopt internal usage. */
247 /* */
248 /* format : printf like format */
249 /* ... : remaining arguments interpreted using the format argument */
250 /* ================================================================== */
251 static void
252 fatal_internal(const char * format, ...)
254 va_list args;
256 fprintf(stderr, "CTXOPT: ");
258 va_start(args, format);
259 vfprintf(stderr, format, args);
260 fprintf(stderr, "\n");
261 va_end(args);
263 exit(EXIT_FAILURE);
266 /* ====================================================================== */
267 /* Generic fatal error function. This one uses the global status ctxopt */
268 /* stored in the cur_state structure and can call custom error functions. */
269 /* registered by the users for a given error identifier. */
270 /* */
271 /* e : error identifier responsible of the fatal error */
272 /* errmsg : users's provided string specific to the error e */
273 /* Note that errmsg is not used in all cases */
274 /* */
275 /* CTXOPTMISPAR Missing parameter */
276 /* CTXOPTMISARG Missing argument */
277 /* CTXOPTDUPOPT Duplicated option */
278 /* CTXOPTUNKPAR Unknown parameter */
279 /* CTXOPTINCOPT Incompatible option */
280 /* CTXOPTCTEOPT Option: bad number of occurrences */
281 /* CTXOPTCTLOPT Option: not enough occurrences */
282 /* CTXOPTCTGOPT Option: too many occurrence of */
283 /* CTXOPTCTEARG Arguments: bad number of occurrences */
284 /* CTXOPTCTLARG Arguments: not enough occurrences */
285 /* CTXOPTCTGARG Arguments: too many occurrences */
286 /* ====================================================================== */
287 static void
288 fatal(errors e, char * errmsg)
290 if (err_functions[e] != NULL)
291 err_functions[e](e, cur_state);
292 else
294 switch (e)
296 case CTXOPTNOERR:
297 break;
299 case CTXOPTMISPAR:
300 if (cur_state->ctx_par_name != NULL)
301 fprintf(stderr,
302 "Mandatory parameter(s): %s are missing in the context "
303 "introduced by %s.\n",
304 errmsg, cur_state->ctx_par_name);
305 else
306 fprintf(stderr,
307 "Mandatory parameter(s): %s are missing "
308 "in the main context.\n",
309 errmsg);
311 free(errmsg);
312 break;
314 case CTXOPTMISARG:
315 if (cur_state->pre_opt_par_name != NULL)
316 fprintf(stderr, "%s requires argument(s).\n",
317 cur_state->pre_opt_par_name);
318 else
319 fprintf(stderr, "%s requires argument(s).\n",
320 cur_state->cur_opt_par_name);
321 break;
323 case CTXOPTDUPOPT:
324 if (cur_state->pre_opt_par_name != NULL)
325 fprintf(stderr,
326 "The parameter(s) %s can only appear once in the context "
327 "introduced by %s.\n",
328 cur_state->cur_opt_par_name, cur_state->ctx_par_name);
329 else
330 fprintf(stderr,
331 "The parameter(s) %s can only appear once "
332 "in the main context.\n",
333 cur_state->cur_opt_par_name);
334 break;
336 case CTXOPTUNKPAR:
337 fprintf(stderr, "Unknown parameter: %s.\n",
338 cur_state->cur_opt_par_name);
339 fprintf(stderr, errmsg);
340 break;
342 case CTXOPTINCOPT:
343 fprintf(stderr, "%s is incompatible with %s.\n",
344 cur_state->cur_opt_par_name, errmsg);
345 break;
347 case CTXOPTCTEOPT:
348 if (cur_state->ctx_par_name)
349 fprintf(stderr,
350 "%s must appear exactly %u times in the context "
351 "introduced by %s.\n",
352 cur_state->cur_opt_par_name, cur_state->opts_count,
353 cur_state->ctx_par_name);
354 else
355 fprintf(stderr,
356 "%s must appear exactly %u times in "
357 "the main context.\n",
358 cur_state->cur_opt_par_name, cur_state->opts_count);
359 break;
361 case CTXOPTCTLOPT:
362 if (cur_state->ctx_par_name)
363 fprintf(stderr,
364 "%s must appear less than %u times in the context "
365 "introduced by %s.\n",
366 cur_state->cur_opt_par_name, cur_state->opts_count,
367 cur_state->ctx_par_name);
368 else
369 fprintf(stderr,
370 "%s must appear less than %u times in the main context.\n",
371 cur_state->cur_opt_par_name, cur_state->opts_count);
372 break;
374 case CTXOPTCTGOPT:
375 if (cur_state->ctx_par_name)
376 fprintf(stderr,
377 "%s must appear more than %u times in the context "
378 "introduced by %s.\n",
379 cur_state->cur_opt_par_name, cur_state->opts_count,
380 cur_state->ctx_par_name);
381 else
382 fprintf(stderr,
383 "%s must appear more than %u times in the main context.\n",
384 cur_state->cur_opt_par_name, cur_state->opts_count);
385 break;
387 case CTXOPTCTEARG:
388 fprintf(stderr, "%s must have exactly %u arguments.\n",
389 cur_state->cur_opt_par_name, cur_state->opt_args_count);
390 break;
392 case CTXOPTCTLARG:
393 fprintf(stderr, "%s must have less than %u arguments.\n",
394 cur_state->cur_opt_par_name, cur_state->opt_args_count);
395 break;
397 case CTXOPTCTGARG:
398 fprintf(stderr, "%s must have more than %u arguments.\n",
399 cur_state->cur_opt_par_name, cur_state->opt_args_count);
400 break;
402 case CTXOPTERRSIZ:
403 break;
407 if (cur_state->ctx_name != NULL)
408 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
410 exit(e); /* Program exist with the error id e as return code */
413 /* ******************************** */
414 /* Memory management implementation */
415 /* ******************************** */
417 /* ================= */
418 /* Customized malloc */
419 /* ================= */
420 static void *
421 xmalloc(size_t size)
423 void * allocated;
424 size_t real_size;
426 real_size = (size > 0) ? size : 1;
427 allocated = malloc(real_size);
428 if (allocated == NULL)
429 fatal_internal("Insufficient memory (attempt to malloc %lu bytes)\n",
430 (unsigned long int)size);
432 return allocated;
435 /* ================= */
436 /* Customized calloc */
437 /* ================= */
438 static void *
439 xcalloc(size_t n, size_t size)
441 void * allocated;
443 n = (n > 0) ? n : 1;
444 size = (size > 0) ? size : 1;
445 allocated = calloc(n, size);
446 if (allocated == NULL)
447 fatal_internal("Insufficient memory (attempt to calloc %lu bytes)\n",
448 (unsigned long int)size);
450 return allocated;
453 /* ================== */
454 /* Customized realloc */
455 /* ================== */
456 static void *
457 xrealloc(void * p, size_t size)
459 void * allocated;
461 allocated = realloc(p, size);
462 if (allocated == NULL && size > 0)
463 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes)\n",
464 (unsigned long int)size);
466 return allocated;
469 /* =================================== */
470 /* strdup implementation using xmalloc */
471 /* =================================== */
472 static char *
473 xstrdup(const char * p)
475 char * allocated;
477 allocated = xmalloc(strlen(p) + 1);
478 strcpy(allocated, p);
480 return allocated;
483 /* ================================================== */
484 /* strndup implementation using xmalloc */
485 /* This version guarantees that there is a final '\0' */
486 /* ================================================== */
487 static char *
488 xstrndup(const char * str, size_t len)
490 char * p;
492 p = memchr(str, '\0', len);
494 if (p)
495 len = p - str;
497 p = xmalloc(len + 1);
498 memcpy(p, str, len);
499 p[len] = '\0';
501 return p;
504 /* ************************** */
505 /* Linked list implementation */
506 /* ************************** */
508 /* Linked list node structure */
509 /* """""""""""""""""""""""""" */
510 struct ll_node_s
512 void * data;
513 struct ll_node_s * next;
514 struct ll_node_s * prev;
517 /* Linked List structure */
518 /* """"""""""""""""""""" */
519 struct ll_s
521 ll_node_t * head;
522 ll_node_t * tail;
523 long len;
526 /* ======================== */
527 /* Create a new linked list */
528 /* ======================== */
529 static ll_t *
530 ll_new(void)
532 ll_t * ret = xmalloc(sizeof(ll_t));
533 ll_init(ret);
535 return ret;
538 /* ======================== */
539 /* Initialize a linked list */
540 /* ======================== */
541 static void
542 ll_init(ll_t * list)
544 list->head = NULL;
545 list->tail = NULL;
546 list->len = 0;
549 /* ==================================================== */
550 /* Allocate the space for a new node in the linked list */
551 /* ==================================================== */
552 static ll_node_t *
553 ll_new_node(void)
555 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
557 return ret;
560 /* ==================================================================== */
561 /* Append a new node filled with its data at the end of the linked list */
562 /* The user is responsible for the memory management of the data */
563 /* ==================================================================== */
564 static void
565 ll_append(ll_t * const list, void * const data)
567 ll_node_t * node;
569 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
570 | uses xmalloc which does not return if there *
571 | is an allocation error. */
573 node->data = data;
574 node->next = NULL;
576 node->prev = list->tail;
577 if (list->tail)
578 list->tail->next = node;
579 else
580 list->head = node;
582 list->tail = node;
584 ++list->len;
587 /* =================================================================== */
588 /* Put a new node filled with its data at the beginning of the linked */
589 /* list. The user is responsible for the memory management of the data */
590 /* =================================================================== */
591 static void
592 ll_prepend(ll_t * const list, void * const data)
594 ll_node_t * node;
596 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
597 | uses xmalloc which does not return if there *
598 | is an allocation error. */
600 node->data = data;
601 node->prev = NULL;
603 node->next = list->head;
604 if (list->head)
605 list->head->prev = node;
606 else
607 list->tail = node;
609 list->head = node;
611 ++list->len;
614 /* ======================================================= */
615 /* Insert a new node before the specified node in the list */
616 /* ======================================================= */
617 static void
618 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
620 ll_node_t * new_node;
622 if (node->prev == NULL)
623 ll_prepend(list, data);
624 else
626 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
627 | uses xmalloc which does not return if there *
628 | is an allocation error. */
630 new_node->data = data;
631 new_node->next = node;
632 new_node->prev = node->prev;
633 node->prev->next = new_node;
634 node->prev = new_node;
636 ++list->len;
640 /* ====================================================== */
641 /* Insert a new node after the specified node in the list */
642 /* ====================================================== */
643 static void
644 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
646 ll_node_t * new_node;
648 if (node->next == NULL)
649 ll_append(list, data);
650 else
652 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
653 | uses xmalloc which does not return if there *
654 | is an allocation error. */
656 new_node->data = data;
657 new_node->prev = node;
658 new_node->next = node->next;
659 node->next->prev = new_node;
660 node->next = new_node;
662 ++list->len;
666 /* ================================================================ */
667 /* Remove a node from a linked list */
668 /* The memory taken by the deleted node must be freed by the caller */
669 /* ================================================================ */
670 static int
671 ll_delete(ll_t * const list, ll_node_t * node)
673 if (list->head == list->tail)
675 if (list->head != NULL)
676 list->head = list->tail = NULL;
677 else
678 return 0;
680 else if (node->prev == NULL)
682 list->head = node->next;
683 list->head->prev = NULL;
685 else if (node->next == NULL)
687 list->tail = node->prev;
688 list->tail->next = NULL;
690 else
692 node->next->prev = node->prev;
693 node->prev->next = node->next;
696 --list->len;
698 return 1;
701 #if 0 /* Unused yet */
702 /* =========================================================================*/
703 /* Find a node in the list containing data. Return the node pointer or NULL */
704 /* if not found. */
705 /* A comparison function must be provided to compare a and b (strcmp like). */
706 /* =========================================================================*/
707 static ll_node_t *
708 ll_find(ll_t * const list, void * const data,
709 int (*cmpfunc)(const void * a, const void * b))
711 ll_node_t * node;
713 if (NULL == (node = list->head))
714 return NULL;
718 if (0 == cmpfunc(node->data, data))
719 return node;
720 } while (NULL != (node = node->next));
722 return NULL;
724 #endif
726 /* ==================================================================== */
727 /* Allocates and fills an array of strings from a list */
728 /* WARNINGS: */
729 /* 1) The list node must contain strings (char *) */
730 /* 2) The strings in the resulting array MUST NOT be freed as the are */
731 /* NOT copied from the strings of the list. */
732 /* */
733 /* IN list : The list from which the array is generated */
734 /* IN start_node : The node of the list which will be the first node to */
735 /* consider to create the array */
736 /* OUT: count : The number of elements of the resulting array. */
737 /* OUT: array : The resulting array or NULL if the list is empty. */
738 /* RC : : The number of elements of the resulting array. */
739 /* ==================================================================== */
740 static int
741 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
743 int n = 0;
744 ll_node_t * node;
746 *count = 0;
748 node = start_node;
750 if (list == NULL || node == NULL)
752 *array = NULL;
754 return 0;
757 *array = xmalloc((list->len + 1) * sizeof(char *));
758 while (node != NULL)
760 (*array)[n++] = (char *)(node->data);
761 (*count)++;
763 node = node->next;
766 (*array)[*count] = NULL;
768 return *count;
771 /* ******************************************************************* */
772 /* BST (search.h compatible) implementation */
773 /* */
774 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
775 /* the AT&T man page says. */
776 /* */
777 /* Written by reading the System V Interface Definition, not the code. */
778 /* */
779 /* Totally public domain. */
780 /* ******************************************************************* */
782 struct bst_s
784 void * key;
785 struct bst_s * llink;
786 struct bst_s * rlink;
789 #if 0 /* Unused yet */
790 /* ========================== */
791 /* delete node with given key */
792 /* ========================== */
793 static void *
794 bst_delete(const void * vkey, void ** vrootp,
795 int (*compar)(const void *, const void *))
797 bst_t ** rootp = (bst_t **)vrootp;
798 bst_t * p, *q, *r;
799 int cmp;
801 if (rootp == NULL || (p = *rootp) == NULL)
802 return NULL;
804 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
806 p = *rootp;
807 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
808 : &(*rootp)->rlink; /* follow rlink branch */
809 if (*rootp == NULL)
810 return NULL; /* key not found */
812 r = (*rootp)->rlink; /* D1: */
813 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
814 q = r;
815 else if (r != NULL)
816 { /* Right link is NULL? */
817 if (r->llink == NULL)
818 { /* D2: Find successor */
819 r->llink = q;
820 q = r;
822 else
823 { /* D3: Find NULL link */
824 for (q = r->llink; q->llink != NULL; q = r->llink)
825 r = q;
826 r->llink = q->rlink;
827 q->llink = (*rootp)->llink;
828 q->rlink = (*rootp)->rlink;
831 if (p != *rootp)
832 free(*rootp); /* D4: Free node */
833 *rootp = q; /* link parent to new node */
834 return p;
836 #endif
838 /* ============== */
839 /* destroy a tree */
840 /* ============== */
841 static void
842 bst_destroy_recurse(bst_t * root, void (*free_action)(void *))
844 if (root->llink != NULL)
845 bst_destroy_recurse(root->llink, free_action);
846 if (root->rlink != NULL)
847 bst_destroy_recurse(root->rlink, free_action);
849 (*free_action)((void *)root->key);
850 free(root);
853 static void
854 bst_destroy(void * vrootp, void (*freefct)(void *))
856 bst_t * root = (bst_t *)vrootp;
858 if (root != NULL)
859 bst_destroy_recurse(root, freefct);
862 /* ======================== */
863 /* find a node, or return 0 */
864 /* ======================== */
865 static void *
866 bst_find(const void * vkey, void * const * vrootp,
867 int (*compar)(const void *, const void *))
869 bst_t * const * rootp = (bst_t * const *)vrootp;
871 if (rootp == NULL)
872 return NULL;
874 while (*rootp != NULL)
875 { /* T1: */
876 int r;
878 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
879 return *rootp; /* key found */
880 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
881 : &(*rootp)->rlink; /* T4: follow right branch */
883 return NULL;
886 /* ===================================== */
887 /* find or insert datum into search tree */
888 /* ===================================== */
889 static void *
890 bst_search(const void * vkey, void ** vrootp,
891 int (*compar)(const void *, const void *))
893 bst_t * q;
894 bst_t ** rootp = (bst_t **)vrootp;
896 if (rootp == NULL)
897 return NULL;
899 while (*rootp != NULL)
900 { /* Knuth's T1: */
901 int r;
903 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
904 return *rootp; /* we found it! */
906 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
907 : &(*rootp)->rlink; /* T4: follow right branch */
910 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
911 if (q != 0)
912 { /* make new node */
913 *rootp = q; /* link new node to old */
914 q->key = (void *)vkey; /* initialize new node */
915 q->llink = q->rlink = NULL;
917 return q;
920 /* ======================== */
921 /* Walk the nodes of a tree */
922 /* ======================== */
923 static void
924 bst_walk_recurse(const bst_t * root,
925 void (*action)(const void *, walk_order_e, int), int level)
927 if (root->llink == NULL && root->rlink == NULL)
928 (*action)(root, leaf, level);
929 else
931 (*action)(root, preorder, level);
932 if (root->llink != NULL)
933 bst_walk_recurse(root->llink, action, level + 1);
934 (*action)(root, postorder, level);
935 if (root->rlink != NULL)
936 bst_walk_recurse(root->rlink, action, level + 1);
937 (*action)(root, endorder, level);
941 static void
942 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
944 if (vroot != NULL && action != NULL)
945 bst_walk_recurse(vroot, action, 0);
948 /* *********************** */
949 /* various implementations */
950 /* *********************** */
952 /* ======================= */
953 /* Trim leading characters */
954 /* ======================= */
955 static void
956 ltrim(char * str, const char * trim_str)
958 size_t len = strlen(str);
959 size_t begin = strspn(str, trim_str);
960 size_t i;
962 if (begin > 0)
963 for (i = begin; i <= len; ++i)
964 str[i - begin] = str[i];
967 /* ================================================= */
968 /* Trim trailing characters */
969 /* The resulting string will have at least min bytes */
970 /* even if trailing spaces remain. */
971 /* ================================================= */
972 static void
973 rtrim(char * str, const char * trim_str, size_t min)
975 size_t len = strlen(str);
976 while (len > min && strchr(trim_str, str[len - 1]))
977 str[--len] = '\0';
980 /* ================================================== */
981 /* Count the number of occurrences of the character c */
982 /* in the string str. */
983 /* The str pointer is assumed to be not NULL */
984 /* ================================================== */
985 static int
986 strchrcount(char * str, char c)
988 int count = 0;
990 while (*str)
991 if (*str++ == c)
992 count++;
994 return count;
997 /* =============================================== */
998 /* Is the string str2 a prefix of the string str1? */
999 /* =============================================== */
1000 static int
1001 strpref(char * str1, char * str2)
1003 while (*str1 != '\0' && *str1 == *str2)
1005 str1++;
1006 str2++;
1009 return *str2 == '\0';
1012 /* ======================================================================== */
1013 /* Strings concatenation with dynamic memory allocation */
1014 /* IN : a variable number of char * arguments with NULL terminating */
1015 /* the sequence. */
1016 /* The first one must have been dynamically allocated and is mandatory */
1017 /* */
1018 /* Returns a new allocated string containing the concatenation of all */
1019 /* the arguments. It is the caller's responsibility to free the resulting */
1020 /* string. */
1021 /* ======================================================================== */
1022 static char *
1023 strappend(char * str, ...)
1025 size_t l;
1026 va_list args;
1027 char * s;
1029 l = 1 + strlen(str);
1030 va_start(args, str);
1032 s = va_arg(args, char *);
1034 while (s)
1036 l += strlen(s);
1037 s = va_arg(args, char *);
1040 va_end(args);
1042 str = xrealloc(str, l);
1044 va_start(args, str);
1045 s = va_arg(args, char *);
1047 while (s)
1049 strcat(str, s);
1050 s = va_arg(args, char *);
1052 va_end(args);
1054 return str;
1057 /* ====================================================================== */
1058 /* public domain strtok_r() by Charlie Gordon */
1059 /* from comp.lang.c 9/14/2007 */
1060 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1061 /* */
1062 /* (Declaration that it's public domain): */
1063 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1064 /* */
1065 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1066 /* *end == NULL */
1067 /* ====================================================================== */
1068 static char *
1069 xstrtok_r(char * str, const char * delim, char ** end)
1071 char * ret;
1073 if (str == NULL)
1074 str = *end;
1076 if (str == NULL)
1077 return NULL;
1079 str += strspn(str, delim);
1081 if (*str == '\0')
1082 return NULL;
1084 ret = str;
1086 str += strcspn(str, delim);
1088 if (*str)
1089 *str++ = '\0';
1091 *end = str;
1093 return ret;
1096 /* =========================================================== */
1097 /* Fills an array of strings from the words composing a string */
1098 /* */
1099 /* str: initial string which will be altered */
1100 /* args: array of pointers to the start of the words in str */
1101 /* max: maximum number of words used before giving up */
1102 /* return: the number of words (<=max) */
1103 /* =========================================================== */
1104 static int
1105 str2argv(char * str, char ** args, int max)
1107 int nb_args = 0;
1109 while (*str)
1111 if (nb_args >= max)
1112 return nb_args;
1114 while (*str == ' ' || *str == '\t')
1115 *(str++) = '\0';
1117 if (!*str)
1118 return nb_args;
1120 args[nb_args] = str;
1121 nb_args++;
1123 while (*str && (*str != ' ') && (*str != '\t'))
1124 str++;
1127 return nb_args;
1130 /* ********************* */
1131 /* ctxopt implementation */
1132 /* ********************* */
1134 static int ctxopt_initialized = 0; /* cap_init has not yet been called */
1136 /* context structure */
1137 /* """"""""""""""""" */
1138 struct ctx_s
1140 char * name;
1141 ll_t * opt_list; /* list of options allowed in this context */
1142 ll_t * incomp_list; /* list of strings containing incompatible names *
1143 | of options separated by spaces or tabs */
1144 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1145 void ** ctx_data);
1146 void * par_bst;
1147 int nb_data;
1148 void ** data;
1151 /* https://textik.com/#488ce3649b6c60f5 */
1152 /* */
1153 /* +--------------+ */
1154 /* |first_ctx_inst| */
1155 /* +---+----------+ */
1156 /* | */
1157 /* +--v-----+ +--------+ +--------+ +-----+ */
1158 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1159 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1160 /* | | | | | */
1161 /* | | +-v------+ | | */
1162 /* | +--+ctx_inst<-----------+ | */
1163 /* | +-+------+ | */
1164 /* | | | */
1165 /* | +-v------+ | */
1166 /* +------+ctx_inst<--------------------------+ */
1167 /* +-+------+ */
1168 /* | */
1169 /* +-v---+ */
1170 /* | ... | */
1171 /* +-----+ */
1173 /* option structure */
1174 /* """""""""""""""" */
1175 struct opt_s
1177 char * name; /* option name */
1178 char * next_ctx; /* new context this option may lead to */
1179 ll_t * ctx_list; /* list of contexts allowing this option */
1180 char * params; /* string containing all the parameters of *
1181 | the option */
1183 void (*action)( /* The option associated action */
1184 char * ctx_name, /* context name */
1185 char * opt_name, /* option name */
1186 char * par, /* option parameter */
1187 int nb_args, /* number of arguments */
1188 char ** args, /* option arguments */
1189 int nb_opt_data, /* number of option data pointers */
1190 void ** opt_data, /* option data pointers */
1191 int nb_ctx_data, /* nb of current context data ptrs */
1192 void ** ctx_data /* current context data pointers */
1195 int nb_data; /* number of the data pointers passed as argument to *
1196 | action */
1197 void ** data; /* array of data pointers passed as argument to action */
1199 int args; /* 1 if this option takes arguments else 0 */
1200 int optional; /* 1 if the option is optional, else 0 */
1201 int multiple; /* 1 if the option can appear more than one time in a *
1202 | context, else 0 */
1204 int opt_count_matter; /* 1 if we must restrict the count, else 0 */
1205 int occurrences; /* Number of option occurrences in a context */
1206 char opt_count_oper; /* <, = or > */
1207 unsigned opt_count_mark; /* Value to be compared to with opt_count_oper */
1209 char * arg; /* symbolic text after # describing the option argument */
1211 int optional_args; /* 1 of option is optional else 0 */
1212 int multiple_args; /* 1 is option can appear more than once in a context *
1213 | instance */
1215 int opt_args_count_matter; /* 1 if we must restrict the count, else 0 */
1216 char opt_args_count_oper; /* <, = or > */
1217 unsigned opt_args_count_mark; /* Value to be compared to with *
1218 | opt_count_oper */
1220 int eval_first; /* 1 if this option must be evaluated before *
1221 | the options without this mark */
1223 ll_t * constraints_list; /* List of constraint checking functions pointers. */
1226 /* context instance structure */
1227 /* """""""""""""""""""""""""" */
1228 struct ctx_inst_s
1230 ctx_t * ctx; /* the context whose this is an instance of */
1231 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1232 | creation of this ctx_inst structure. */
1233 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1234 | instance of this structure */
1235 ll_t * incomp_bst_list; /* list of seen_opt_t bst */
1236 void * seen_opt_bst; /* tree of seen_opt_t */
1237 ll_t * opt_inst_list; /* The list of option instances in this *
1238 | context instance */
1239 char * par_name; /* parameter which created this instance */
1242 /* Option instance structure */
1243 /* """"""""""""""""""""""""" */
1244 struct opt_inst_s
1246 opt_t * opt; /* The option this is an instance of */
1247 char * opt_name; /* The option which led to this creation */
1248 char * par; /* The parameter which led to this creation */
1249 ll_t * values_list; /* The list of arguments of this option */
1250 ctx_inst_t * next_ctx_inst; /* The new context instance this option *
1251 | instance may create */
1254 /* Structure used to check if an option has bee seen or not */
1255 /* in a context instance */
1256 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1257 struct seen_opt_s
1259 opt_t * opt; /* The concerned option */
1260 char * par; /* Parameter which led to the making of this structure */
1261 char * count; /* Number of seen occurrences of this parameter */
1262 int seen; /* 1 if seen in the context instances, else 0 */
1265 /* parameter structure which links a parameter to the option it belongs to */
1266 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1267 struct par_s
1269 char * name; /* Parameter name (with the leading - */
1270 opt_t * opt; /* Attached option */
1273 /* Constraint structure */
1274 /* """""""""""""""""""" */
1275 struct constraint_s
1277 int (*constraint)(int nb_args, char ** args, char * value);
1278 int nb_args;
1279 char ** args;
1282 state_t * cur_state = NULL; /* Current analysis state */
1283 static ll_t * cmdline_list; /* List of interpreted CLI words *
1284 | serves as the basis for the *
1285 | analysis of the parameters */
1286 static ctx_t * main_ctx = NULL; /* initial context instance */
1287 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1288 | instance which holds the *
1289 | options instances */
1290 static ll_t * ctx_inst_list; /* List of the context instances */
1292 /* ====================================================== */
1293 /* Parse a string for the next matching token. */
1294 /* */
1295 /* s: string to parse. */
1296 /* token: pre_allocated array of max tok_len characters */
1297 /* pattern: scanf type pattern token must match */
1298 /* pos: number of characters successfully parsed in s */
1299 /* */
1300 /* Returns: a pointer to the first unread character or */
1301 /* to he terminating \0. */
1302 /* ====================================================== */
1303 static char *
1304 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1306 char * full_pattern;
1307 char len[3];
1308 int n;
1310 *pos = 0;
1312 n = snprintf(len, 3, "%zu", tok_len);
1313 if (n < 0)
1314 return NULL;
1316 full_pattern = xmalloc(strlen(pattern) + n + 4);
1318 strcpy(full_pattern, "%");
1319 strcat(full_pattern, len);
1320 strcat(full_pattern, pattern);
1321 strcat(full_pattern, "%n");
1323 n = sscanf(s, full_pattern, token, pos);
1325 free(full_pattern);
1327 if (n != 1)
1328 return NULL;
1330 return s + *pos;
1333 /* **************************** */
1334 /* Various comparison functions */
1335 /* **************************** */
1337 static int
1338 ctx_compare(const void * c1, const void * c2)
1340 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1343 static int
1344 opt_compare(const void * o1, const void * o2)
1346 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1349 static int
1350 par_compare(const void * a1, const void * a2)
1352 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1355 static int
1356 seen_opt_compare(const void * so1, const void * so2)
1358 opt_t *o1, *o2;
1360 o1 = ((seen_opt_t *)so1)->opt;
1361 o2 = ((seen_opt_t *)so2)->opt;
1363 return strcmp(o1->name, o2->name);
1366 /* ******************************************************************** */
1367 /* Helper functions to locate contexts, options and parameters in a bst */
1368 /* by their names. */
1369 /* ******************************************************************** */
1371 static ctx_t *
1372 locate_ctx(char * name)
1374 bst_t * node;
1375 ctx_t ctx = { 0 };
1377 ctx.name = name;
1379 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1380 return NULL;
1381 else
1382 return node->key;
1385 static opt_t *
1386 locate_opt(char * name)
1388 bst_t * node;
1389 opt_t opt = { 0 };
1391 opt.name = name;
1393 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1394 return NULL;
1395 else
1396 return node->key;
1399 static par_t *
1400 locate_par(char * name, ctx_t * ctx)
1402 bst_t * node;
1403 par_t par = { 0 };
1404 void * bst = ctx->par_bst;
1406 par.name = name;
1408 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1409 return NULL;
1410 else
1411 return node->key;
1414 /* =================================================================== */
1415 /* Utility function to format and print the options present in a list. */
1416 /* */
1417 /* IN list : a list of options */
1418 /* OUT has_* : a set of flags which will determine the content of the */
1419 /* explanation given after the formatted printing of the */
1420 /* options. */
1421 /* =================================================================== */
1422 static void
1423 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1424 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1425 int * has_early_eval)
1427 ll_node_t * node = list->head;
1428 opt_t * opt;
1429 char * line;
1430 char * option;
1432 line = xstrdup(" ");
1434 while (node != NULL)
1436 option = xstrdup("");
1437 opt = node->data;
1439 if (opt->optional)
1441 option = strappend(option, "[", NULL);
1442 *has_optional = 1;
1445 if (opt->eval_first)
1447 option = strappend(option, "*", NULL);
1448 *has_early_eval = 1;
1451 option = strappend(option, opt->params, NULL);
1453 if (opt->next_ctx != NULL)
1455 option = strappend(option, ">", opt->next_ctx, NULL);
1456 *has_ctx_change = 1;
1459 if (opt->multiple)
1461 if (opt->opt_count_oper != '\0')
1463 char m[4];
1464 char o[2];
1465 o[0] = opt->opt_count_oper;
1466 o[1] = '\0';
1467 snprintf(m, 3, "%u", opt->opt_count_mark);
1468 option = strappend(option, "...", o, m, NULL);
1469 *has_rule = 1;
1471 else
1472 option = strappend(option, "...", NULL);
1474 *has_ellipsis = 1;
1477 if (opt->args)
1479 if (*(opt->arg) == '#')
1480 *has_generic_arg = 1;
1482 option = strappend(option, " ", NULL);
1484 if (opt->optional_args)
1486 option = strappend(option, "[", opt->arg, NULL);
1487 *has_optional = 1;
1489 else
1490 option = strappend(option, opt->arg, NULL);
1492 if (opt->multiple_args)
1494 if (opt->opt_args_count_oper != '\0')
1496 char m[4];
1497 char o[2];
1498 o[0] = opt->opt_args_count_oper;
1499 o[1] = '\0';
1500 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1501 option = strappend(option, "...", o, m, NULL);
1502 *has_rule = 1;
1504 else
1505 option = strappend(option, "...", NULL);
1507 *has_ellipsis = 1;
1509 if (opt->optional_args)
1510 option = strappend(option, "]", NULL);
1512 if (opt->optional)
1513 option = strappend(option, "]", NULL);
1515 if (strlen(line) + 1 + strlen(option) < 80)
1516 line = strappend(line, option, " ", NULL);
1517 else
1519 printf("%s\n", line);
1520 line[2] = '\0';
1521 line = strappend(line, option, " ", NULL);
1524 free(option);
1526 node = node->next;
1529 printf("%s\n", line);
1531 free(line);
1534 /* ==================================================== */
1535 /* Explain the special syntactic symbols present in the */
1536 /* generated usage messages. */
1537 /* ==================================================== */
1538 static void
1539 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1540 int has_optional, int has_ellipsis, int has_rule)
1542 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1543 || has_ellipsis || has_rule)
1545 printf("\nSyntactic explanations:\n");
1546 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1547 "must be entered.\n");
1548 printf("The following is just there to explain the other symbols "
1549 "displayed.\n\n");
1551 if (has_early_eval)
1552 printf("* : the parameters for this option will be "
1553 "evaluated first.\n");
1554 if (has_ctx_change)
1555 printf(
1556 "> : The context after this symbol will become the next "
1557 "default one.\n");
1558 if (has_generic_arg)
1559 printf("#tag : argument tag giving a clue to its meaning.\n");
1560 if (has_optional)
1561 printf(
1562 "[...] : the object between square brackets is optional.\n");
1563 if (has_ellipsis)
1564 printf("... : the previous object can be repeated more "
1565 "than one time.\n");
1566 if (has_rule)
1567 printf("[<|=|>]number: rules constraining the number of "
1568 "parameters/arguments.\n");
1572 /* ******************************************************* */
1573 /* Various utility and callback function call when walking */
1574 /* through a bst. */
1575 /* ******************************************************* */
1577 static void
1578 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1580 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1582 if (kind == postorder || kind == leaf)
1584 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1586 user_rc = 1;
1587 user_string = strappend(user_string, seen_opt->opt->params, " ", NULL);
1592 static void
1593 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1595 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1597 if (kind == postorder || kind == leaf)
1598 if (seen_opt->seen == 1)
1600 user_rc = 1;
1601 user_object = seen_opt->par;
1605 static void
1606 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
1608 ctx_t * ctx = main_ctx;
1609 ctx_t * cur_ctx = ((bst_t *)node)->key;
1611 ll_t * list;
1613 int has_optional = 0;
1614 int has_ellipsis = 0;
1615 int has_rule = 0;
1616 int has_generic_arg = 0;
1617 int has_ctx_change = 0;
1618 int has_early_eval = 0;
1620 if (kind == postorder || kind == leaf)
1621 if (strcmp(ctx->name, cur_ctx->name) != 0)
1623 list = cur_ctx->opt_list;
1625 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
1626 print_options(list, &has_optional, &has_ellipsis, &has_rule,
1627 &has_generic_arg, &has_ctx_change, &has_early_eval);
1631 static void
1632 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
1634 opt_t * opt = ((bst_t *)node)->key;
1636 if (kind == postorder || kind == leaf)
1638 if (opt->params == NULL) /* opt must have associated parameters */
1639 fatal_internal("Option %s has no registered parameter.\n", opt->name);
1641 if (opt->action == NULL) /* opt must have an action */
1642 fatal_internal("Option %s has no registered action.\n", opt->name);
1646 static void
1647 bst_match_par_cb(const void * node, walk_order_e kind, int level)
1649 ctx_t * ctx = ((bst_t *)node)->key;
1651 if (kind == postorder || kind == leaf)
1653 char * str = xstrdup(user_string);
1655 while (*str != '\0')
1657 if (locate_par(str, ctx) != NULL)
1659 user_string2 = strappend(user_string2, " ", ctx->name, NULL);
1660 break;
1662 str[strlen(str) - 1] = '\0';
1664 free(str);
1668 static void
1669 match_prefix_cb(const void * node, walk_order_e kind, int level)
1671 par_t * par = ((bst_t *)node)->key;
1673 if (kind == postorder || kind == leaf)
1674 if (strpref(par->name, (char *)user_object))
1676 user_rc++;
1677 user_string = strappend(user_string, par->name, " ", NULL);
1681 static void
1682 bst_null_action(void * data)
1684 ; /* nothing to do */
1687 /* ====================================================================== */
1688 /* A parameter may not be separated from its first option by spaces, in */
1689 /* this case this function looks for a valid flag as a prefix and splits */
1690 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1691 /* option) */
1692 /* */
1693 /* IN word : the word to be checked. */
1694 /* IN ctx : the context in which the flag indexed by the word is to be */
1695 /* checked. */
1696 /* OUT pos : the offset in word pointing just after the matching prefix. */
1697 /* OUT opt : a pointer to the option associated with the new parameter */
1698 /* or NULL if none is found. */
1699 /* */
1700 /* The returned pointer must be freed by the caller */
1701 /* ====================================================================== */
1702 static char *
1703 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
1705 char * new = NULL;
1706 int len;
1707 par_t * par;
1708 par_t tmp_par = { 0 };
1710 len = strlen(word);
1712 if (len > 2)
1714 new = xstrdup(word);
1718 new[--len] = '\0';
1719 tmp_par.name = new;
1720 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
1722 if (par != NULL)
1724 *pos = len;
1725 *opt = par->opt;
1727 else
1729 free(new);
1730 new = NULL;
1733 else
1734 *pos = 0;
1736 return new;
1739 /* ============================================================= */
1740 /* If par_name is an unique abbreviation of an exiting parameter */
1741 /* in the context ctx, then return this parameter. */
1742 /* ============================================================= */
1743 static char *
1744 abbrev_expand(char * par_name, ctx_t * ctx)
1746 user_object = par_name;
1747 user_rc = 0;
1749 *user_string = '\0';
1750 bst_walk(ctx->par_bst, match_prefix_cb);
1751 rtrim(user_string, " ", 0);
1753 if (user_rc == 1) /* The number of matching abbreviations */
1754 return xstrdup(user_string);
1755 else
1757 char * s, *first_s;
1758 par_t * par;
1759 opt_t * opt;
1760 int opt_count = 0;
1761 void * tmp_opt_bst = NULL;
1763 /* for each word in the matching parameters return by walking the */
1764 /* parameter's tree of this context, do: */
1765 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1766 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
1767 | the first word. */
1768 while (s != NULL)
1770 par = locate_par(s, ctx);
1771 opt = par->opt;
1773 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
1775 /* This option as not already been seen */
1776 /* store it and increase the seen counter */
1777 /* """""""""""""""""""""""""""""""""""""" */
1778 bst_search(opt, &tmp_opt_bst, opt_compare);
1779 opt_count++;
1781 s = strtok(NULL, " ");
1784 if (tmp_opt_bst != NULL)
1785 bst_destroy(tmp_opt_bst, bst_null_action);
1787 if (opt_count == 1)
1788 /* All the abbreviation lead to only one option */
1789 /* We can just continue as in the previous case. */
1790 /* """""""""""""""""""""""""""""""""""""""""""""" */
1791 return xstrdup(first_s);
1792 else
1793 return NULL;
1797 /* ================================================================= */
1798 /* Terminates the program if mandatory options required by a context */
1799 /* are not present. */
1800 /* ================================================================= */
1801 static void
1802 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
1804 char * missing;
1806 if (has_unseen_mandatory_opt(ctx_inst, &missing))
1807 fatal(CTXOPTMISPAR, missing);
1810 /* ====================================================== */
1811 /* Return 1 if at least one mandatory option was not seen */
1812 /* when quitting a context, else 0 */
1813 /* ====================================================== */
1814 static int
1815 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
1817 user_rc = 0;
1818 *user_string = '\0';
1820 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
1821 rtrim(user_string, " ", 0);
1823 *missing = user_string;
1825 return user_rc ? 1 : 0;
1828 /* ========================================================================= */
1829 /* This function terminates the program if an option or its arguments do not */
1830 /* conform to its occurrences constraint. */
1831 /* There constraints can appear by trailing >, < or = in their definition */
1832 /* given in ctxopt_new_ctx. */
1833 /* ========================================================================= */
1834 static void
1835 check_for_occurrences_issues(ctx_inst_t * ctx_inst)
1837 ctx_t * ctx = ctx_inst->ctx;
1838 opt_t * opt;
1839 ll_node_t * node;
1840 opt_inst_t * opt_inst;
1842 /* Check options */
1843 /* """"""""""""" */
1844 node = ctx->opt_list->head;
1846 while (node != NULL)
1848 opt = node->data;
1850 /* Update current_state */
1851 /* -------------------- */
1852 cur_state->opts_count = opt->opt_count_mark;
1853 cur_state->opt_args_count = opt->opt_args_count_mark;
1855 if (opt->opt_count_matter)
1856 switch (opt->opt_count_oper)
1858 case '=':
1859 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
1860 fatal(CTXOPTCTEOPT, "");
1861 break;
1863 case '<':
1864 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
1865 fatal(CTXOPTCTLOPT, "");
1866 break;
1868 case '>':
1869 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
1870 fatal(CTXOPTCTGOPT, "");
1871 break;
1874 node = node->next;
1877 /* Check arguments */
1878 /* """"""""""""""" */
1879 node = ctx_inst->opt_inst_list->head;
1880 while (node != NULL)
1882 opt_inst = node->data;
1883 opt = opt_inst->opt;
1885 /* Update current_state */
1886 /* -------------------- */
1887 cur_state->opts_count = opt->opt_count_mark;
1888 cur_state->opt_args_count = opt->opt_args_count_mark;
1890 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
1892 if (opt->opt_args_count_matter)
1893 switch (opt->opt_args_count_oper)
1895 case '=':
1896 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
1897 fatal(CTXOPTCTEARG, "");
1898 break;
1900 case '<':
1901 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
1902 fatal(CTXOPTCTLARG, "");
1903 break;
1905 case '>':
1906 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
1907 fatal(CTXOPTCTGARG, "");
1908 break;
1911 node = node->next;
1915 /* ======================================================================== */
1916 /* Parse a strings describing options and some of their characteristics */
1917 /* The input string must have follow some rules like in the examples below: */
1918 /* */
1919 /* "opt_name1 opt_name2" */
1920 /* "[opt_name1] opt_name2" */
1921 /* "[opt_name1] opt_name2..." */
1922 /* "[opt_name1 #...] opt_name2... [#]" */
1923 /* "[opt_name1 [#...]] opt_name2... [#...]" */
1924 /* */
1925 /* Where [ ] encloses an optional part, # means: has parameters and ... */
1926 /* means that there can be more than one occurrence of the previous thing. */
1927 /* */
1928 /* opt_name can be followed by a 'new context' change prefixed with the */
1929 /* symbol >, as in opt1>c2 by eg */
1930 /* */
1931 /* This function returns as soon as one (or no) option has been parsed and */
1932 /* return the offset to the next option to parse. */
1933 /* */
1934 /* In case of successful parsing, an new option is allocated and its */
1935 /* pointer returned. */
1936 /* ======================================================================== */
1937 static int
1938 opt_parse(char * s, opt_t ** opt)
1940 int opt_optional = 0;
1941 int opt_multiple = 0;
1942 int opt_count_matter = 0;
1943 char opt_count_oper = '\0';
1944 unsigned opt_count_mark = 0;
1945 int opt_args = 0;
1946 char opt_arg[33];
1947 int opt_multiple_args = 0;
1948 int opt_args_count_matter = 0;
1949 char opt_args_count_oper = '\0';
1950 unsigned opt_args_count_mark = 0;
1951 int opt_optional_args = 0;
1952 int opt_eval_first = 0;
1954 int n;
1955 int pos;
1956 int count = 0;
1958 char * s_orig = s;
1960 char * p;
1961 char * opt_name;
1962 char * next_ctx;
1963 char token[65];
1965 *opt = NULL;
1966 memset(opt_arg, '\0', 33);
1968 /* strip the leading blanks */
1969 while (isblank(*s))
1970 s++;
1972 if (*s == '[') /* Start of an optional option */
1974 opt_optional = 1;
1975 s++;
1977 s = strtoken(s, token, sizeof(token), "[^] \n\t.]", &pos);
1978 if (s == NULL)
1979 return -1; /* empty string */
1981 /* Early EOS, only return success if the option is mandatory */
1982 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1983 if (!*s)
1984 if (opt_optional == 1)
1985 return -(s - s_orig - 1);
1987 /* validate the option name */
1988 /* ALPHA+(ALPHANUM|_)* */
1989 /* """""""""""""""""""""""" */
1990 p = token;
1991 if (!isalpha(*p) && *p != '*')
1992 return -(s - s_orig - 1); /* opt_name must start with a letter */
1994 if (*p == '*')
1995 opt_eval_first = 1;
1997 p++;
1998 while (*p)
2000 if (!isalnum(*p) && *p != '_' && *p != '>')
2001 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2002 * a number or a _ */
2003 p++;
2006 if (opt_eval_first)
2007 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token */
2008 else
2009 opt_name = xstrdup(token);
2011 if (*s == ']')
2013 s++;
2014 while (isblank(*s))
2015 s++;
2017 goto success;
2020 /* Check if it can appear multiple times by looking for the dots */
2021 p = strtoken(s, token, 3, "[.]", &pos);
2022 if (p)
2024 if (strcmp(token, "...") == 0)
2026 opt_multiple = 1;
2027 s = p;
2028 if (*s == '<' || *s == '=' || *s == '>')
2030 unsigned value;
2031 int offset;
2033 n = sscanf(s + 1, "%u%n", &value, &offset);
2034 if (n == 1)
2036 opt_count_matter = 1;
2037 opt_count_oper = *s;
2038 opt_count_mark = value;
2040 s += offset + 1;
2043 else
2045 free(opt_name);
2046 return -(s - s_orig - 1);
2050 /* A blank separates the option name and the argument tag */
2051 if (isblank(*s))
2053 char dots[4];
2055 while (isblank(*s))
2056 s++;
2058 if (!*s)
2059 goto success;
2061 pos = 0;
2062 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2063 if (pos > 1 && *opt_arg == '#') /* [# has been read */
2065 opt_args = 1;
2066 opt_optional_args = 1;
2067 if (n == 2)
2068 opt_multiple_args = 1; /* there were dots */
2070 s += pos + !!(n == 2) * 3; /* skip the dots */
2072 if (*s == '<' || *s == '=' || *s == '>')
2074 unsigned value;
2075 int offset;
2077 n = sscanf(s + 1, "%u%n", &value, &offset);
2078 if (n == 1)
2080 opt_args_count_matter = 1;
2081 opt_args_count_oper = *s;
2082 opt_args_count_mark = value;
2084 s += offset + 1;
2087 /* Optional arg tag must end with a ] */
2088 if (*s != ']')
2090 free(opt_name);
2091 return -(s - s_orig - 1);
2094 s++; /* skip the ] */
2096 else
2098 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2099 if (pos > 0 && *opt_arg == '#') /* # has been read */
2101 opt_args = 1;
2102 if (n == 2) /* there were dots */
2103 opt_multiple_args = 1;
2105 s += pos + !!(n == 2) * 3; /* skip the dots */
2107 if (*s == '<' || *s == '=' || *s == '>')
2109 unsigned value;
2110 int offset;
2112 n = sscanf(s + 1, "%u%n", &value, &offset);
2113 if (n == 1)
2115 opt_args_count_matter = 1;
2116 opt_args_count_oper = *s;
2117 opt_args_count_mark = value;
2119 s += offset + 1;
2123 if (*s == ']')
2125 /* Abort on extraneous ] if the option is mandatory */
2126 if (!opt_optional)
2127 return -(s - s_orig - 1);
2129 s++; /* skip the ] */
2131 /* Strip the following blanks */
2132 while (isblank(*s))
2133 s++;
2135 goto success;
2137 else if (opt_optional == 0 && (!*s || isblank(*s)))
2139 /* Strip the following blanks */
2140 while (isblank(*s))
2141 s++;
2143 goto success;
2145 else if (opt_args == 0) /* # was not read it is possibly the start *
2146 * of another option */
2147 goto success;
2148 else
2149 return -(s - s_orig - 1);
2152 success:
2154 /* strip the following blanks */
2155 while (isblank(*s))
2156 s++;
2158 next_ctx = NULL;
2160 if (*opt_name == '>')
2161 fatal_internal("%s: option name is missing.", opt_name);
2163 count = strchrcount(opt_name, '>');
2164 if (count == 1)
2166 char * tmp = strchr(opt_name, '>');
2167 next_ctx = xstrdup(tmp + 1);
2168 *tmp = '\0';
2170 else if (count > 1)
2171 fatal_internal("%s: only one occurrence of '>' is allowed.", opt_name);
2173 *opt = xmalloc(sizeof(opt_t));
2175 (*opt)->name = opt_name;
2176 (*opt)->optional = opt_optional;
2177 (*opt)->multiple = opt_multiple;
2178 (*opt)->opt_count_matter = opt_count_matter;
2179 (*opt)->opt_count_oper = opt_count_oper;
2180 (*opt)->opt_count_mark = opt_count_mark;
2181 (*opt)->args = opt_args;
2182 (*opt)->arg = xstrdup(opt_arg);
2183 (*opt)->optional_args = opt_optional_args;
2184 (*opt)->multiple_args = opt_multiple_args;
2185 (*opt)->opt_args_count_matter = opt_args_count_matter;
2186 (*opt)->opt_args_count_oper = opt_args_count_oper;
2187 (*opt)->opt_args_count_mark = opt_args_count_mark;
2188 (*opt)->eval_first = opt_eval_first;
2189 (*opt)->next_ctx = next_ctx;
2190 (*opt)->ctx_list = ll_new();
2191 (*opt)->constraints_list = ll_new();
2192 (*opt)->action = NULL;
2193 (*opt)->data = NULL;
2195 return s - s_orig;
2198 /* ===================================================================== */
2199 /* Try to initialize all the option in a given string */
2200 /* Each parsed option are put in a bst tree with its name as index */
2201 /* */
2202 /* On collision, the arguments only the signature are required to be */
2203 /* the same else this is considered as an error. Options can be used in */
2204 /* more than one context and can be optional in one and mandatory in */
2205 /* another */
2206 /* ===================================================================== */
2207 static int
2208 init_opts(char * spec, ctx_t * ctx)
2210 opt_t * opt, *bst_opt;
2211 bst_t * node;
2212 int offset;
2214 while (*spec)
2216 if ((offset = opt_parse(spec, &opt)) > 0)
2218 spec += offset;
2220 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2222 int same_next_ctx = 0;
2224 bst_opt = node->key; /* node extracted from the BST */
2226 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2227 same_next_ctx = 1;
2228 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2229 same_next_ctx = 0;
2230 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2231 same_next_ctx = 0;
2232 else
2233 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2235 if (bst_opt->optional_args != opt->optional_args
2236 || bst_opt->multiple_args != opt->multiple_args
2237 || bst_opt->args != opt->args || !same_next_ctx)
2239 fatal_internal("option %s already exists with "
2240 "a different arguments signature.\n",
2241 opt->name);
2243 free(opt->name);
2244 free(opt);
2246 return 0;
2248 /* The new occurrence of the option option is legal */
2249 /* append the current context ptr in the list */
2250 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2251 ll_append(bst_opt->ctx_list, ctx);
2253 /* Append the new option to the context's options list */
2254 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2255 ll_append(ctx->opt_list, bst_opt);
2257 else
2259 opt->params = NULL;
2261 /* Initialize the option's context list with the current context */
2262 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2263 ll_append(opt->ctx_list, ctx);
2265 /* Append the new option to the context's options list */
2266 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2267 ll_append(ctx->opt_list, opt);
2269 /* Insert the new option in the BST */
2270 /* """""""""""""""""""""""""""""""" */
2271 bst_search(opt, &options_bst, opt_compare);
2274 else
2276 char * s = xstrndup(spec, -offset);
2277 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2278 free(s);
2280 exit(EXIT_FAILURE);
2284 return 1;
2287 /* ==================================================== */
2288 /* ctxopt initialization function, must be called first */
2289 /* ==================================================== */
2290 void
2291 ctxopt_init(char * prog_name)
2293 int n;
2295 contexts_bst = NULL;
2296 options_bst = NULL;
2297 char * ptr;
2299 user_rc = 0;
2300 user_string = xmalloc(8);
2301 user_string2 = xmalloc(8);
2302 user_object = NULL;
2304 ctxopt_initialized = 1;
2306 /* Update current_state */
2307 /* -------------------- */
2308 cur_state = xcalloc(sizeof(state_t), 0);
2310 /* Initialize custom error function pointers to NULL */
2311 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2312 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2313 for (n = 0; n < CTXOPTERRSIZ; n++)
2314 err_functions[n] = NULL;
2316 /* Update current_state */
2317 /* -------------------- */
2318 if (prog_name)
2320 if (*prog_name == '\0')
2321 cur_state->prog_name = xstrdup("program_name");
2322 else if ((ptr = strrchr(prog_name, '/')))
2323 cur_state->prog_name = xstrdup(ptr + 1);
2324 else
2325 cur_state->prog_name = xstrdup(prog_name);
2327 else
2328 cur_state->prog_name = xstrdup("program_name");
2331 /* ========================================================================= */
2332 /* Utility function which create and register a par_t object in a bst */
2333 /* embedded in a context. */
2334 /* This object will have a name and a pointer to the option it refers to. */
2335 /* These object will be used to quickly find an option from a command */
2336 /* line parameter during the analysis phase. */
2337 /* */
2338 /* IN : an option name. */
2339 /* IN : a string of command line parameters to associate to the option. */
2340 /* Returns : 1 is all was fine else 0. */
2341 /* ========================================================================= */
2342 static int
2343 opt_set_parms(char * opt_name, char * par_str)
2345 char * par_name, *ctx_name;
2346 char * tmp_par_str, *end_tmp_par_str;
2347 ctx_t * ctx;
2348 opt_t * opt;
2349 bst_t * node;
2350 par_t * par, tmp_par;
2351 int rc = 1; /* return code */
2353 ll_t * list;
2354 ll_node_t * lnode;
2356 /* Look is the given option is defined */
2357 /* """"""""""""""""""""""""""""""""""" */
2358 opt = locate_opt(opt_name);
2359 if (opt == NULL)
2360 fatal_internal("Unknown option %s", opt_name);
2362 /* For each context using this option */
2363 /* """""""""""""""""""""""""""""""""" */
2364 list = opt->ctx_list;
2366 lnode = list->head;
2367 while (lnode != NULL)
2369 /* Locate the context in the contexts tree */
2370 /* """"""""""""""""""""""""""""""""""""""" */
2371 ctx_name = ((ctx_t *)(lnode->data))->name;
2373 ctx = locate_ctx(ctx_name);
2374 if (ctx == NULL)
2375 fatal_internal("Unknown context %s", ctx_name);
2376 else
2378 void * par_bst = ctx->par_bst;
2380 tmp_par_str = xstrdup(par_str);
2381 ltrim(tmp_par_str, " \t");
2382 rtrim(tmp_par_str, " \t", 0);
2383 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2384 if (par_name == NULL)
2385 fatal_internal("Parameters are missing for option %s", opt_name);
2387 /* For each parameter given in par_str, create an par_t object and */
2388 /* insert it the in the parameters bst of the context. */
2389 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2390 while (par_name != NULL)
2392 tmp_par.name = par_name;
2394 node = bst_find(&tmp_par, &par_bst, par_compare);
2395 if (node != NULL)
2397 fatal_internal("The parameter %s is already defined in context %s",
2398 par_name, ctx->name);
2399 rc = 0;
2401 else
2403 par = xmalloc(sizeof(par_t));
2404 par->name = xstrdup(par_name);
2405 par->opt = opt; /* Link the option to this parameter */
2407 bst_search(par, &par_bst, par_compare);
2409 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2412 /* Update the value of the root of ctx->par_bst as it may have */
2413 /* been modified */
2414 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2415 ctx->par_bst = par_bst;
2417 free(tmp_par_str);
2419 lnode = lnode->next;
2422 return rc;
2425 /* ==================================================================== */
2426 /* Creation of a new context instance */
2427 /* IN ctx : a context pointer to allow this instance to */
2428 /* access the context fields */
2429 /* IN prev_ctx_inst : the context instance whose option leading to the */
2430 /* creation of this new context instance is part of */
2431 /* Returns : the new context. */
2432 /* ==================================================================== */
2433 static ctx_inst_t *
2434 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
2436 opt_t * opt;
2437 opt_inst_t * gen_opt_inst;
2438 ctx_inst_t * ctx_inst;
2439 seen_opt_t * seen_opt;
2440 char * str, *opt_name;
2441 void * bst;
2442 bst_t * bst_node;
2444 /* Keep a trace of the opt_inst which was at the origin of the creation */
2445 /* of this context instance. */
2446 /* This will serve during the evaluation of the option callbacks */
2447 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2448 if (prev_ctx_inst != NULL)
2450 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
2451 /* Update current_state */
2452 /* -------------------- */
2453 cur_state->opt_name = gen_opt_inst->opt->name;
2455 else
2456 gen_opt_inst = NULL;
2458 /* Create and initialize the new context instance */
2459 /* """""""""""""""""""""""""""""""""""""""""""""" */
2460 ctx_inst = xmalloc(sizeof(ctx_inst_t));
2461 ctx_inst->ctx = ctx;
2462 ctx_inst->prev_ctx_inst = prev_ctx_inst;
2463 ctx_inst->gen_opt_inst = gen_opt_inst;
2464 ctx_inst->incomp_bst_list = ll_new();
2465 ctx_inst->opt_inst_list = ll_new();
2466 ctx_inst->seen_opt_bst = NULL;
2468 /* Update current_state */
2469 /* -------------------- */
2470 cur_state->ctx_name = ctx->name;
2472 ll_node_t * node;
2474 if (prev_ctx_inst == NULL)
2475 first_ctx_inst = ctx_inst;
2477 /* Initialize the occurrence counters of each opt allowed in the context */
2478 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2479 node = ctx->opt_list->head;
2480 while (node != NULL)
2482 opt = node->data;
2483 opt->occurrences = 0;
2485 node = node->next;
2488 /* Initialize the bst containing the seen indicator for all the options */
2489 /* allowed in this context instance. */
2490 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2491 node = ctx->opt_list->head;
2492 while (node != NULL)
2494 opt = node->data;
2495 seen_opt = xmalloc(sizeof(seen_opt_t));
2496 seen_opt->opt = opt;
2497 seen_opt->seen = 0;
2499 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
2501 node = node->next;
2504 /* Initialize the bst containing the incompatibles options */
2505 /* Incompatibles option names are read from strings found in the list */
2506 /* incomp_list present in each instance of ctx_t. */
2507 /* These names are then used to search for the object of type seen_opt_t */
2508 /* which is already present in the seen_opt_bst of the context instance. */
2509 /* in the bst. */
2510 /* Once found the seen_opt_t object in inserted in the new bst */
2511 /* At the end the new bst in addes to the list incomp_bst_list. */
2512 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2513 node = ctx->incomp_list->head;
2514 while (node != NULL)
2516 bst = NULL;
2517 seen_opt_t tmp_seen_opt;
2519 str = xstrdup(node->data);
2520 ltrim(str, " \t");
2521 rtrim(str, " \t", 0);
2522 opt_name = strtok(str, " \t"); /* extract the first option name */
2524 while (opt_name != NULL) /* for each option name */
2526 if ((opt = locate_opt(opt_name)) != NULL)
2528 /* The option found is searched in the tree of potential */
2529 /* seen options. */
2530 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2531 tmp_seen_opt.opt = opt;
2533 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2534 seen_opt_compare);
2536 if (bst_node != NULL)
2538 /* if found it, is added into the new bst tree */
2539 /* """"""""""""""""""""""""""""""""""""""""""" */
2540 seen_opt = bst_node->key;
2541 bst_search(seen_opt, &bst, seen_opt_compare);
2543 else
2544 /* Not found! That means that the option is unknown in this */
2545 /* context as all options has have a seen_opt structure in */
2546 /* seen_opt_bst */
2547 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2548 fatal_internal("%s is not known in the context %s", opt->name,
2549 ctx->name);
2551 else
2552 fatal_internal("%s: unknown option.", opt_name);
2554 opt_name = strtok(NULL, " \t");
2557 free(str);
2558 ll_append(ctx_inst->incomp_bst_list, bst);
2560 node = node->next;
2563 return ctx_inst;
2566 /* ======================================================================== */
2567 /* Create a list formed by all the significant command line words */
2568 /* Words beginning or ending with ^,{ or } are split. Each of these */
2569 /* symbols will get their own place in the list */
2570 /* */
2571 /* - ^ forces the next option to be evaluated in the first context */
2572 /* - the {...} part delimits a context, the { will not appear in the list */
2573 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2574 /* to facilitate the parsing phase. | must not be used by the end user */
2575 /* */
2576 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2577 /* program name is not considered */
2578 /* IN words : is the array of strings constituting the command line to */
2579 /* parse. */
2580 /* Returns : 1 on success, 0 if a { or } is missing. */
2581 /* ======================================================================== */
2582 static int
2583 ctxopt_build_cmdline_list(int nb_words, char ** words)
2585 int i;
2586 char * prev_word = NULL;
2587 char * word;
2588 char * ptr;
2589 int level = 0;
2590 ll_node_t *node, *start_node;
2592 /* The analysis is divided into three passes, this is not optimal but */
2593 /* must be done only one time. Doing that we privilege readability. */
2594 /* */
2595 /* In the following, SG is the ascii character 1d (dec 29) */
2596 /* */
2597 /* The first pass creates the list, extract the leading an trailing */
2598 /* SG '{' and '}' of each word and give them their own place in the */
2599 /* list */
2600 /* */
2601 /* The second pass transform the '{...}' blocks by a trailing SG */
2602 /* ({...} -> ...|) */
2603 /* */
2604 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2605 /* the middle in the remaining list elements and recreate the pseudo */
2606 /* argument: {} */
2607 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2609 /* If the option list is not empty, clear it before going further */
2610 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2612 if (cmdline_list != NULL)
2614 node = cmdline_list->head;
2615 while (node != NULL)
2617 word = node->data;
2618 ll_delete(cmdline_list, node);
2619 free(word);
2620 free(node);
2621 node = cmdline_list->head;
2624 else
2625 cmdline_list = ll_new();
2627 start_node = cmdline_list->head; /* in the following loop start_node will *
2628 * contain a pointer to the current *
2629 * word stripped from its leading *
2630 * sequence of {, } or ^ */
2631 for (i = 0; i < nb_words; i++)
2633 size_t len = strlen(words[i]);
2634 size_t start, end;
2635 char * str;
2637 str = words[i];
2639 /* Replace each occurrence of the legal word {} by the characters */
2640 /* 0x02 and 0x03 to hide them from the following process. */
2641 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2642 while ((ptr = strstr(str, "{}")) != NULL)
2644 *ptr = 0x02; /* arbitrary values unlikely */
2645 *(ptr + 1) = 0x03; /* present in a word */
2648 if (len > 1) /* The word contains at least 2 characters */
2650 start = 0;
2652 /* Interpret its beginning and look for the start of the real word */
2653 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2654 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
2656 ll_append(cmdline_list, xstrndup(str + start, 1));
2657 start++;
2658 start_node = cmdline_list->tail;
2661 end = len - 1;
2662 if (str[end] == '{' || str[end] == '}')
2664 if (end > 0 && str[end - 1] != '\\')
2666 ll_append(cmdline_list, xstrndup(str + end, 1));
2667 end--;
2668 node = cmdline_list->tail;
2670 while (str[end] == '{' || str[end] == '}')
2672 if (end > start && str[end - 1] == '\\')
2673 break;
2675 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
2676 end--;
2677 node = node->prev;
2682 if (start <= end)
2684 if (start_node != NULL)
2685 ll_insert_after(cmdline_list, start_node,
2686 xstrndup(str + start, end - start + 1));
2687 else
2688 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
2689 start_node = cmdline_list->tail;
2692 else if (len == 1)
2694 ll_append(cmdline_list, xstrdup(str));
2695 start_node = cmdline_list->tail;
2699 /* 2nd pass */
2700 /* """""""" */
2701 node = cmdline_list->head;
2703 level = 0;
2704 while (node != NULL)
2706 word = node->data;
2708 if (strcmp(word, "{") == 0)
2710 ll_node_t * old_node = node;
2711 level++;
2712 node = node->next;
2713 ll_delete(cmdline_list, old_node);
2714 free(word);
2715 free(old_node);
2717 else if (strcmp(word, "}") == 0)
2719 level--;
2721 if (level < 0)
2722 return 0;
2723 else
2724 *word = 0x1d;
2726 else
2727 node = node->next;
2730 if (level != 0)
2731 return 0;
2733 /* 3rd pass */
2734 /* """""""" */
2735 node = cmdline_list->head;
2737 while (node != NULL)
2739 word = node->data;
2741 /* Restore the original { and } characters forming the legal word {} */
2742 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2743 while ((ptr = strchr(word, 0x02)) != NULL)
2744 *ptr = '{';
2745 while ((ptr = strchr(word, 0x03)) != NULL)
2746 *ptr = '}';
2748 /* Remove a SG if the previous element is SG */
2749 /* """"""""""""""""""""""""""""""""""""""""" */
2750 if (strcmp(word, "\x1d") == 0)
2752 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
2754 ll_node_t * old_node = node;
2755 node = node->prev;
2756 ll_delete(cmdline_list, old_node);
2757 free(old_node->data);
2758 free(old_node);
2761 else if (strcmp(word, "-") == 0) /* a single - is a legal argument, not *
2762 * a parameter. Protect it */
2764 free(node->data);
2765 node->data = xstrdup("\\-");
2768 prev_word = node->data;
2769 node = node->next;
2772 /* Clean useless and SG at the beginning and end of list */
2773 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2774 node = cmdline_list->head;
2776 if (node == NULL)
2777 return 1;
2779 word = node->data;
2781 if (strcmp(word, "\x1d") == 0)
2783 ll_delete(cmdline_list, node);
2784 free(word);
2785 free(node);
2788 node = cmdline_list->tail;
2789 if (node == NULL)
2790 return 1;
2792 word = node->data;
2794 if (strcmp(word, "\x1d") == 0)
2796 ll_delete(cmdline_list, node);
2797 free(word);
2798 free(node);
2801 return 1;
2804 /* ===================================================================== */
2805 /* Build and analyze the command line list */
2806 /* function and create the linked data structures whose data will be */
2807 /* evaluated later by ctxopt_evaluate. */
2808 /* This function identifies the following errors and creates an array of */
2809 /* The remaining unanalyzed arguments. */
2810 /* - detect missing arguments */
2811 /* - detect too many arguments */
2812 /* - detect unknown parameters in a context */
2813 /* - detect too many occurrences of a parameters in a context */
2814 /* - detect missing required arguments in a context */
2815 /* */
2816 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2817 /* program name is not considered */
2818 /* IN words : is the array of strings constituting the command line to */
2819 /* parse. */
2820 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
2821 /* is present in the list. */
2822 /* OUT rem_args : array of remaining command line arguments if a -- */
2823 /* is present in the list. This array must be free by */
2824 /* The caller as it is allocated here. */
2825 /* ===================================================================== */
2826 void
2827 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
2828 char *** rem_args)
2830 ctx_t * ctx;
2831 opt_t * opt;
2832 par_t * par;
2833 ctx_inst_t * ctx_inst;
2834 opt_inst_t * opt_inst;
2835 int expect_par = 0;
2836 int expect_arg = 0;
2837 int expect_par_or_arg = 0;
2839 ll_node_t * cli_node;
2840 bst_t * bst_node;
2841 seen_opt_t * bst_seen_opt;
2842 char * par_name;
2843 void * bst;
2845 ll_node_t * node;
2847 if (!ctxopt_build_cmdline_list(nb_words, words))
2848 fatal_internal(
2849 "The command line could not be parsed: missing { or } detected.");
2851 if (main_ctx == NULL)
2852 fatal_internal("At least one context must have been created.");
2854 /* Check that all options has an action and at least one parameter */
2855 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2856 bst_walk(options_bst, bst_check_opt_cb);
2858 /* Create the first ctx_inst record */
2859 /* """""""""""""""""""""""""""""""" */
2860 ctx = main_ctx;
2862 ctx_inst_list = ll_new();
2863 ctx_inst = new_ctx_inst(ctx, NULL);
2864 ctx_inst->par_name = NULL;
2866 ll_append(ctx_inst_list, ctx_inst);
2868 /* For each node in the command line */
2869 /* """"""""""""""""""""""""""""""""" */
2870 cli_node = cmdline_list->head;
2871 expect_par = 1;
2872 while (cli_node != NULL)
2874 if (strcmp(cli_node->data, "--") == 0)
2875 break; /* No new parameter will be analyze after this point */
2877 par_name = cli_node->data;
2879 if (strcmp(par_name, "\x1d") == 0)
2881 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
2882 check_for_occurrences_issues(ctx_inst);
2884 /* Forced backtracking to the previous context instance */
2885 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2886 if (ctx_inst->prev_ctx_inst != NULL)
2888 ctx_inst = ctx_inst->prev_ctx_inst;
2889 ctx = ctx_inst->ctx;
2891 /* Update current_state */
2892 /* -------------------- */
2893 cur_state->ctx_name = ctx->name;
2894 cur_state->ctx_par_name = ctx_inst->par_name;
2897 else if (expect_par && *par_name == '-')
2899 int pos = 0;
2900 char * prefix;
2902 /* Update current_state */
2903 /* -------------------- */
2904 cur_state->cur_opt_par_name = par_name;
2906 /* An expected parameter has been seen */
2907 /* """"""""""""""""""""""""""""""""""" */
2908 if ((par = locate_par(par_name, ctx)) == NULL)
2910 opt_t * popt;
2911 char * word;
2913 /* See if this parameter is an unique abbreviation of a longer */
2914 /* parameter. If this is the case then just replace it with its */
2915 /* full length version and try again. */
2916 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2917 if ((word = abbrev_expand(par_name, ctx)) != NULL)
2919 cli_node->data = word;
2920 continue;
2923 /* Try to find a prefix which is a valid parameter in this context */
2924 /* If found, split the cli_node in two to build a new parameter */
2925 /* node and followed by a node containing the remaining string */
2926 /* If the new parameter corresponds to an option not taking */
2927 /* argument then prefix the remaining string whit a dash as it may */
2928 /* contain a new parameter. */
2929 /* The new parameter will be re-evaluated in the next iteration of */
2930 /* the loop. */
2931 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2932 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
2933 if (prefix != NULL && pos != 0)
2935 cli_node->data = prefix; /* prefix contains le name of a valid *
2936 | parameter in this context. */
2938 if (popt->args)
2940 /* The parameter may be followed by arguments */
2941 /* '''''''''''''''''''''''''''''''''''''''''' */
2942 if (*(par_name + pos) == '-')
2944 word = xstrdup("\\"); /* Protect the '-' */
2945 word = strappend(word, par_name + pos, NULL);
2947 else
2948 word = xstrdup(par_name + pos);
2950 else
2952 /* The parameter does not take arguments, the */
2953 /* following word must be a parameter or nothing */
2954 /* hence prefix it with a dash. */
2955 /* ''''''''''''''''''''''''''''''''''''''''''''' */
2956 word = xstrdup("-");
2957 word = strappend(word, par_name + pos, NULL);
2960 /* Insert it after the current node in the list */
2961 /* """""""""""""""""""""""""""""""""""""""""""" */
2962 ll_insert_after(cmdline_list, cli_node, word);
2964 continue; /* loop */
2966 else
2968 check_for_missing_mandatory_opt(ctx_inst, par_name);
2969 check_for_occurrences_issues(ctx_inst);
2971 if (ctx_inst->prev_ctx_inst == NULL)
2973 char * errmsg = xstrdup("");
2975 *user_string = '\0';
2976 *user_string2 = '\0';
2978 user_string = strappend(user_string, par_name, NULL);
2980 bst_walk(contexts_bst, bst_match_par_cb);
2982 if (*user_string2 != '\0')
2984 errmsg = strappend(
2985 errmsg,
2986 "\nIt appears to be defined in the context(s):", user_string2,
2987 "\nAdd -h or -H for more help.", NULL);
2990 fatal(CTXOPTUNKPAR, errmsg);
2992 else
2994 /* Try to backtrack and analyse the same parameter in the */
2995 /* previous context. */
2996 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
2997 ctx_inst = ctx_inst->prev_ctx_inst;
2998 ctx = ctx_inst->ctx;
3000 /* Update current_state */
3001 /* -------------------- */
3002 cur_state->ctx_name = ctx->name;
3003 cur_state->ctx_par_name = ctx_inst->par_name;
3005 cli_node = cli_node->prev;
3009 else
3011 seen_opt_t seen_opt;
3013 /* The parameter is legal in the context, create a opt_inst and */
3014 /* append it to the ctx_inst list options list */
3015 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3016 opt = par->opt;
3018 opt->occurrences++;
3020 opt_inst = xmalloc(sizeof(opt_inst_t));
3021 opt_inst->opt = opt;
3022 opt_inst->par = par_name;
3023 opt_inst->values_list = ll_new();
3024 opt_inst->next_ctx_inst = NULL;
3026 /* Priority option inserted at the start of the opt_inst list */
3027 /* but their order of appearance in the context definition must */
3028 /* be preserver so each new priority option will be placed after */
3029 /* the previous ones at the start of the opt_inst list. */
3030 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3031 if (!opt->eval_first)
3032 ll_append(ctx_inst->opt_inst_list, opt_inst);
3033 else
3035 ll_node_t * node = ctx_inst->opt_inst_list->head;
3036 opt_inst_t * tmp_opt_inst;
3037 while (node != NULL)
3039 tmp_opt_inst = node->data;
3040 if (!tmp_opt_inst->opt->eval_first)
3042 ll_insert_before(ctx_inst->opt_inst_list, node, opt_inst);
3043 break;
3045 else
3046 node = node->next;
3048 if (node == NULL)
3049 ll_append(ctx_inst->opt_inst_list, opt_inst);
3052 /* Check if an option was already seen in the */
3053 /* current context instance */
3054 /* """""""""""""""""""""""""""""""""""""""""" */
3055 seen_opt.opt = opt;
3057 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3058 seen_opt_compare);
3060 /* bst_node cannot be NULL here */
3062 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3064 if (!opt->multiple && bst_seen_opt->seen == 1)
3065 fatal(CTXOPTDUPOPT, NULL);
3067 /* Check if this option is compatible with the options already */
3068 /* seen in this context instance. */
3069 /* Look if the option is present in one on the bst present in */
3070 /* the incomp_bst_list of the context instance */
3071 /* instance, */
3072 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3073 node = ctx_inst->incomp_bst_list->head;
3074 while (node != NULL)
3076 bst = node->data;
3077 user_object = NULL;
3079 /* They can only have one seen_opt object in the bst tree was */
3080 /* already seen, try to locate it, the result will be put in */
3081 /* user_object by the bst_seen_opt_seen_cb function */
3082 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3083 bst_walk(bst, bst_seen_opt_seen_cb);
3085 /* It it is the case, look if the current option is also */
3086 /* in this bst. */
3087 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3088 if (user_object != NULL)
3090 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3092 if (bst_node != NULL)
3094 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3095 if (bst_seen_opt->seen == 0)
3096 fatal(CTXOPTINCOPT, (char *)user_object);
3100 node = node->next;
3103 /* Mark this option seen in the current context instance */
3104 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3105 bst_seen_opt->seen = 1;
3106 bst_seen_opt->par = xstrdup(par_name);
3108 /* If this option leads to a next context, create a new ctx_inst */
3109 /* and switch to it for the analyse of the future parameter */
3110 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3111 if (opt->next_ctx != NULL)
3113 ctx = locate_ctx(opt->next_ctx);
3115 if (ctx == NULL)
3116 fatal_internal("%s: unknown context.", opt->next_ctx);
3118 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3119 ctx_inst->par_name = xstrdup(par_name);
3121 /* Update current_state */
3122 /* -------------------- */
3123 cur_state->ctx_par_name = ctx_inst->par_name;
3125 ll_append(ctx_inst_list, ctx_inst);
3128 /* See is we must expect some arguments */
3129 /* """""""""""""""""""""""""""""""""""" */
3130 expect_par_or_arg = 0;
3131 expect_par = 0;
3132 expect_arg = 0;
3134 if (!opt->args)
3135 expect_par = 1; /* Parameter doesn't accept any argument */
3136 else
3138 if (!opt->optional_args)
3139 expect_arg = 1; /* Parameter has mandatory arguments */
3140 else
3141 expect_par_or_arg = 1; /* Parameter has optional arguments */
3145 else if (expect_par && *par_name != '-')
3146 break; /* a unexpected non parameter was seen, assume it is the end *
3147 | of parameters and the start of non ctxopt managed command *
3148 | line arguments. */
3149 else if (expect_arg && *par_name != '-')
3151 ll_node_t * cstr_node;
3152 constraint_t * cstr;
3154 /* Check if the arguments of the option respects */
3155 /* the attached constraints if any */
3156 /* """"""""""""""""""""""""""""""""""""""""""""" */
3157 cstr_node = opt->constraints_list->head;
3158 while (cstr_node != NULL)
3160 cstr = cstr_node->data;
3161 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name))
3162 exit(EXIT_FAILURE);
3164 cstr_node = cstr_node->next;
3167 /* If the argument is valid, store it */
3168 /* """""""""""""""""""""""""""""""""" */
3169 if (*par_name == '\\' && *(par_name + 1) != '\0'
3170 && *(par_name + 1) == '-')
3171 ll_append(opt_inst->values_list, par_name + 1);
3172 else
3173 ll_append(opt_inst->values_list, par_name);
3175 expect_arg = 0;
3176 expect_par = 0;
3177 expect_par_or_arg = 0;
3179 if (opt->multiple_args)
3180 expect_par_or_arg = 1;
3181 else
3182 expect_par = 1; /* Parameter takes only one argument */
3184 else if (expect_arg && *par_name == '-')
3185 fatal(CTXOPTMISARG, NULL);
3186 else if (expect_par_or_arg)
3188 expect_arg = 0;
3189 expect_par = 0;
3190 expect_par_or_arg = 0;
3192 if (*par_name != '-')
3193 expect_arg = 1; /* Consider this word as an argument and retry */
3194 else
3195 expect_par = 1; /* Consider this word as a parameter and retry */
3197 cli_node = cli_node->prev;
3200 cli_node = cli_node->next;
3203 if (cmdline_list->len > 0 && *par_name == '-')
3205 if (expect_arg && !opt->optional_args)
3206 fatal(CTXOPTMISARG, NULL);
3209 /* Look if a context_instance has unseen mandatory options */
3210 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3211 node = ctx_inst_list->head;
3212 while (node != NULL)
3214 ctx_inst = (ctx_inst_t *)(node->data);
3216 /* Update current_state */
3217 /* -------------------- */
3218 cur_state->ctx_name = ctx_inst->ctx->name;
3219 cur_state->ctx_par_name = ctx_inst->par_name;
3221 check_for_missing_mandatory_opt(ctx_inst, par_name);
3222 check_for_occurrences_issues(ctx_inst);
3224 node = node->next;
3227 /* Allocate the array containing the remaining not analyzed */
3228 /* command line arguments. */
3229 /* NOTE: The strings in the array are just pointer to the */
3230 /* data of the generating list and must not be freed. */
3231 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3232 if (cli_node != NULL)
3234 if (strcmp((char *)cli_node->data, "--") == 0)
3235 /* The special parameter -- was encountered, the -- argument is not */
3236 /* put in the remaining arguments. */
3237 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3238 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3239 else
3240 /* A non parameter was encountered when a parameter was expected. We */
3241 /* assume that the evaluation of the remaining command line argument */
3242 /* are not the responsibility of the users code. */
3243 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3244 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3246 else
3248 *nb_rem_args = 0;
3249 *rem_args = xmalloc(sizeof(char *));
3250 (*rem_args)[0] = NULL;
3254 /* ===================================================================== */
3255 /* Parses the options data structures and launches the callback function */
3256 /* attached to each options instances. */
3257 /* This calls a recursive function which proceeds context per context. */
3258 /* ===================================================================== */
3259 void
3260 ctxopt_evaluate(void)
3262 evaluate_ctx_inst(first_ctx_inst);
3265 /* =================================================================== */
3266 /* Recursive function called by ctxopt_evaluate to process the list of */
3267 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3268 /* action attached to the context and its option instances */
3269 /* =================================================================== */
3270 static void
3271 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
3273 opt_inst_t * opt_inst;
3274 ctx_t * ctx;
3275 opt_t * opt;
3276 ll_node_t * opt_inst_node;
3277 char ** args;
3278 int nb_args;
3280 if (ctx_inst == NULL)
3281 return;
3283 ctx = ctx_inst->ctx;
3285 /* Call the entering action attached to this context if any */
3286 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3287 if (ctx->action != NULL)
3289 if (ctx_inst->prev_ctx_inst != NULL)
3290 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
3291 ctx->nb_data, ctx->data);
3292 else
3293 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
3296 /* For each instance of options */
3297 /* """""""""""""""""""""""""""" */
3298 opt_inst_node = ctx_inst->opt_inst_list->head;
3299 while (opt_inst_node != NULL)
3301 opt_inst = (opt_inst_t *)(opt_inst_node->data);
3302 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
3303 &args);
3304 opt = opt_inst->opt;
3306 /* Launch the attached action if any */
3307 /* """"""""""""""""""""""""""""""""" */
3308 if (opt->action != NULL)
3309 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
3310 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
3312 if (opt_inst->next_ctx_inst != NULL)
3313 evaluate_ctx_inst(opt_inst->next_ctx_inst);
3315 if (args != NULL)
3316 free(args);
3318 opt_inst_node = opt_inst_node->next;
3321 /* Call the exiting action attached to this context if any */
3322 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3323 if (ctx->action != NULL)
3325 if (ctx_inst->prev_ctx_inst != NULL)
3326 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
3327 ctx->nb_data, ctx->data);
3328 else
3329 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
3333 /* =========================================================== */
3334 /* Create and initialize a new context */
3335 /* - allocate space */
3336 /* - name it */
3337 /* - initialize its option with a few of their characteristics */
3338 /* =========================================================== */
3339 void
3340 ctxopt_new_ctx(char * name, char * opts_specs)
3342 ctx_t * ctx;
3343 bst_t * node;
3344 char * p;
3346 if (!ctxopt_initialized)
3347 fatal_internal("Please call ctxopt_init first.");
3349 ctx = xmalloc(sizeof(ctx_t));
3351 /* The first created context is the main one */
3352 /* """"""""""""""""""""""""""""""""""""""""" */
3353 if (contexts_bst == NULL)
3354 main_ctx = ctx;
3356 /* validate the context name */
3357 /* ALPHA+(ALPHANUM|_)* */
3358 /* """"""""""""""""""""""""" */
3359 p = name;
3360 if (!isalpha(*p))
3361 fatal_internal("%s: a context name must start with a letter.", name);
3363 p++;
3364 while (*p)
3366 if (!isalnum(*p) && *p != '_')
3367 fatal_internal("%s: a context name must only contain letters, "
3368 "numbers or '_'.",
3369 name);
3370 p++;
3373 ctx->name = xstrdup(name);
3374 ctx->opt_list = ll_new(); /* list of options legit in this context */
3375 ctx->incomp_list = ll_new(); /* list of incompatible options strings */
3376 ctx->par_bst = NULL;
3377 ctx->data = NULL;
3378 ctx->action = NULL;
3380 if (init_opts(opts_specs, ctx) == 0)
3381 exit(EXIT_FAILURE);
3382 if ((node = bst_find(ctx, &contexts_bst, ctx_compare)) != NULL)
3383 fatal_internal("The context %s already exists", name);
3384 else
3385 bst_search(ctx, &contexts_bst, ctx_compare);
3388 /* ==================================================== */
3389 /* Display a usage screen limited to a specific context */
3390 /* IN: the context name. */
3391 /* IN: what to do after (continue or exit the program) */
3392 /* possible values: continue_after, exit_after. */
3393 /* ==================================================== */
3394 void
3395 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
3397 ctx_t * ctx;
3398 ll_t * list;
3400 int has_optional = 0;
3401 int has_ellipsis = 0;
3402 int has_rule = 0;
3403 int has_generic_arg = 0;
3404 int has_ctx_change = 0;
3405 int has_early_eval = 0;
3407 ctx = locate_ctx(ctx_name);
3408 if (ctx == NULL)
3409 fatal_internal("%s: unknown context.", ctx_name);
3411 if (cur_state->ctx_par_name == NULL)
3412 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
3413 else
3414 printf("\nSynopsis for the context introduced by %s:\n",
3415 cur_state->ctx_par_name);
3417 list = ctx->opt_list;
3418 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3419 &has_ctx_change, &has_early_eval);
3421 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3422 has_optional, has_ellipsis, has_rule);
3424 if (action == exit_after)
3425 exit(EXIT_FAILURE);
3428 /* ==================================================== */
3429 /* Display a full usage screen about all contexts. */
3430 /* IN: what to do after (continue or exit the program) */
3431 /* possible values: continue_after, exit_after. */
3432 /* ==================================================== */
3433 void
3434 ctxopt_disp_usage(usage_behaviour action)
3436 ll_t * list;
3437 int has_optional = 0;
3438 int has_ellipsis = 0;
3439 int has_rule = 0;
3440 int has_generic_arg = 0;
3441 int has_ctx_change = 0;
3442 int has_early_eval = 0;
3444 if (main_ctx == NULL)
3445 fatal_internal("At least one context must have been created.");
3447 /* Usage for the first context */
3448 /* """"""""""""""""""""""""""" */
3449 printf("\nAllowed options in the default context:\n");
3450 list = main_ctx->opt_list;
3451 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3452 &has_ctx_change, &has_early_eval);
3454 /* Usage for the other contexts */
3455 /* """""""""""""""""""""""""""" */
3456 bst_walk(contexts_bst, bst_print_ctx_cb);
3458 /* Contextual syntactic explanations */
3459 /* """"""""""""""""""""""""""""""""" */
3460 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3461 has_optional, has_ellipsis, has_rule);
3463 if (action == exit_after)
3464 exit(EXIT_FAILURE);
3467 /* ************************************** */
3468 /* Built-in constraint checking functions */
3469 /* ************************************** */
3471 /* ================================================================ */
3472 /* This constraint checks if each arguments respects a format as */
3473 /* defined for the scanf function. */
3474 /* return 1 if yes and 0 if no */
3475 /* ================================================================ */
3477 ctxopt_format_constraint(int nb_args, char ** args, char * value)
3479 int rc = 0;
3481 char x[256];
3482 char y;
3483 char * format;
3485 if (nb_args != 1)
3486 fatal_internal("Format constraint: invalid number of parameters.");
3488 if (strlen(value) > 255)
3489 value[255] = '\0';
3491 format = xstrdup(args[0]);
3492 format = strappend(format, "%c", NULL);
3494 rc = sscanf(value, format, x, &y);
3495 if (rc != 1)
3496 fatal_internal("The argument %s does not respect the imposed format: %s",
3497 value, args[0]);
3499 free(format);
3501 return rc == 1;
3504 /* ================================================================== */
3505 /* This constraint checks if each arguments of the option instance is */
3506 /* between a minimum and a maximum (inclusive). */
3507 /* return 1 if yes and 0 if no */
3508 /* ================================================================== */
3510 ctxopt_re_constraint(int nb_args, char ** args, char * value)
3512 regex_t re;
3514 if (nb_args != 1)
3515 fatal_internal(
3516 "Regular expression constraint: invalid number of parameters.");
3518 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
3519 fatal_internal("Invalid regular expression %s", args[0]);
3521 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
3523 fatal_internal("The argument %s doesn't match the constraining "
3524 "regular expression %s",
3525 value, args[0]);
3526 return 0;
3529 regfree(&re);
3531 return 1;
3534 /* ================================================================== */
3535 /* This constraint checks if each arguments of the option instance is */
3536 /* between a minimum and a maximum (inclusive). */
3537 /* return 1 if yes and 0 if no */
3538 /* ================================================================== */
3540 ctxopt_range_constraint(int nb_args, char ** args, char * value)
3542 long min, max;
3543 char c;
3544 char * ptr;
3545 int n;
3546 long v;
3547 int min_only = 0;
3548 int max_only = 0;
3550 if (nb_args != 2)
3551 fatal_internal("Range constraint: invalid number of parameters.");
3553 if (strcmp(args[0], ".") == 0)
3554 max_only = 1;
3555 else
3556 n = sscanf(args[0], "%ld%c", &min, &c);
3558 if (!max_only && n != 1)
3559 fatal_internal("Range constraint: min: invalid parameters.");
3561 if (strcmp(args[1], ".") == 0)
3562 min_only = 1;
3563 else
3564 n = sscanf(args[1], "%ld%c", &max, &c);
3566 if (!min_only && n != 1)
3567 fatal_internal("Range constraint: max: invalid parameters.");
3569 if (min_only && max_only)
3570 fatal_internal("Range constraint: invalid parameters.");
3572 errno = 0;
3573 v = strtol(value, &ptr, 10);
3574 if (errno || ptr == value)
3575 return 0;
3577 if (min_only)
3579 if (v < min)
3581 fatal_internal("%ld is not greater or equal to %ld as requested.", v,
3582 min);
3583 return 0;
3585 else
3586 return 1;
3588 else if (max_only)
3590 if (v > max)
3592 fatal_internal("%ld is not lower or equal to %ld as requested.", v, max);
3593 return 0;
3595 else
3596 return 1;
3598 else if (v < min || v > max)
3600 fatal_internal("%ld is not between %ld and %ld as requested.", v, min, max);
3601 return 0;
3604 return 1; /* check passed */
3607 /* ============================================================== */
3608 /* This function provides a way to set the behaviour of a context */
3609 /* ============================================================== */
3610 void
3611 ctxopt_add_global_settings(settings s, ...)
3613 va_list(args);
3614 va_start(args, s);
3616 switch (s)
3618 case error_functions:
3620 void (*function)(errors e, state_t * state);
3622 errors e;
3623 e = va_arg(args, errors);
3624 function = va_arg(args, void (*)(errors e, state_t * state));
3625 err_functions[e] = function;
3626 break;
3629 default:
3630 break;
3632 va_end(args);
3635 /* ================================================================ */
3636 /* This function provides a way to set the behaviour of an option */
3637 /* It can take a variable number of arguments according to its */
3638 /* first argument: */
3639 /* - parameter: */
3640 /* o a string containing an option name and all its possible */
3641 /* parameters separates by spaces, tabs or commas (char *) */
3642 /* (e.g: "help -h -help") */
3643 /* - actions: */
3644 /* o a string containing an option name */
3645 /* o a pointer to a function which will be called at evaluation */
3646 /* time. */
3647 /* - constraints: */
3648 /* o a string containing an option name */
3649 /* o a pointer to a function to check if an argument is valid. */
3650 /* o a strings containing the arguments to this function. */
3651 /* ================================================================ */
3652 void
3653 ctxopt_add_opt_settings(settings s, ...)
3655 opt_t * opt;
3656 void * ptr = NULL;
3658 va_list(args);
3659 va_start(args, s);
3661 switch (s)
3663 /* This part associates some command line parameters to an option */
3664 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3665 case parameters:
3667 char * opt_name;
3668 char * params;
3670 /* The second argument must be a string containing: */
3671 /* - The name of an existing option */
3672 /* - a list of parameters with a leading dash (-) */
3673 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3674 ptr = va_arg(args, char *);
3675 opt_name = ptr;
3677 if (opt_name != NULL)
3679 if ((opt = locate_opt(opt_name)) != NULL)
3681 ptr = va_arg(args, char *);
3682 params = ptr;
3684 if (!opt_set_parms(opt_name, params))
3685 fatal_internal(
3686 "duplicates parameters or bad settings for the option (%s).",
3687 params);
3689 else
3690 fatal_internal("%s: unknown option.", opt_name);
3692 else
3693 fatal_internal(
3694 "ctxopt_opt_add_settings: parameters: not enough arguments.");
3696 /* Here opt is a known option */
3697 /* """""""""""""""""""""""""" */
3698 if (opt->params != NULL)
3699 fatal_internal("Parameters are already set for %s", opt_name);
3700 else
3702 size_t n;
3703 size_t l = strlen(params);
3705 opt->params = xstrdup(params);
3706 while ((n = strcspn(opt->params, " \t")) < l)
3707 opt->params[n] = '|';
3709 /* Update current_state */
3710 /* -------------------- */
3711 cur_state->opt_params = xstrdup(opt->params);
3714 break;
3717 /* This part associates a callback function to an option. */
3718 /* This function will be called as when an instance of an option */
3719 /* is evaluated. */
3720 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3721 case actions:
3723 void * data;
3724 void (*function)();
3725 int nb_data = 0;
3727 /* The second argument must be the name of an existing option */
3728 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3729 ptr = va_arg(args, char *);
3731 if ((opt = locate_opt(ptr)) != NULL)
3733 /* The third argument must be the callback function */
3734 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3735 function = va_arg(args, void (*)(char *, char *, char *, int, char **,
3736 int, void *, int, void **));
3737 opt->action = function;
3739 /* The fourth argument must be a pointer to an user's defined */
3740 /* variable or structure that the previous function can manage */
3741 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3742 while ((data = va_arg(args, void *)) != NULL)
3744 nb_data++;
3745 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
3746 opt->data[nb_data - 1] = data;
3748 opt->nb_data = nb_data;
3750 else
3751 fatal_internal("%s: unknown option.", ptr);
3752 break;
3755 /* This part associates a list of functions to control some */
3756 /* characteristics of the arguments of an option. */
3757 /* Each function will be called in order and must return 1 */
3758 /* to validate the arguments. */
3759 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3760 case constraints:
3762 char * value;
3763 constraint_t * cstr;
3764 int (*function)();
3766 /* The second argument must be a string */
3767 /* """""""""""""""""""""""""""""""""""" */
3768 ptr = va_arg(args, char *);
3770 if ((opt = locate_opt(ptr)) != NULL)
3772 /* The third argument must be a function */
3773 /* """"""""""""""""""""""""""""""""""""" */
3774 function = va_arg(args, int (*)(int, char **, char *));
3776 cstr = xmalloc(sizeof(constraint_t));
3777 cstr->constraint = function;
3779 /* The fourth argument must be a string containing the argument of */
3780 /* The previous function separated by spaces or tabs */
3781 /* Theses arguments will be passed to the previous function */
3782 /* max: 32 argument! */
3783 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3784 value = xstrdup(va_arg(args, char *));
3786 cstr->args = xcalloc(sizeof(char *), 32);
3787 cstr->nb_args = str2argv(value, cstr->args, 32);
3788 ll_append(opt->constraints_list, cstr);
3790 else
3791 fatal_internal("%s: unknown option.", ptr);
3792 break;
3795 default:
3796 break;
3798 va_end(args);
3801 /* ============================================================== */
3802 /* This function provides a way to set the behaviour of a context */
3803 /* ============================================================== */
3804 void
3805 ctxopt_add_ctx_settings(settings s, ...)
3807 ctx_t * ctx;
3809 va_list(args);
3810 va_start(args, s);
3812 switch (s)
3814 /* Add a set of mutually incompatibles option in a context. */
3815 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3816 case incompatibilities:
3818 void * ptr;
3819 ll_t * list;
3820 size_t n;
3821 char * str;
3823 ptr = va_arg(args, char *);
3824 if ((ctx = locate_ctx(ptr)) != NULL)
3826 ptr = va_arg(args, char *);
3827 list = ctx->incomp_list;
3829 str = xstrdup(ptr);
3830 ltrim(str, " \t");
3831 rtrim(str, " \t", 0);
3833 n = strcspn(str, " \t");
3834 if (n > 0 && n < strlen(str))
3835 ll_append(list, str);
3836 else
3837 fatal_internal(
3838 "Not enough incompatible options in the string: \"%s\"", str);
3840 else
3841 fatal_internal("%s: unknown context.", ptr);
3842 break;
3845 /* Add functions which be call when entering and exiting a context. */
3846 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3847 case actions:
3849 void * ptr;
3850 void * data;
3851 int (*function)();
3852 int nb_data = 0;
3854 ptr = va_arg(args, char *);
3855 if ((ctx = locate_ctx(ptr)) != NULL)
3857 function = va_arg(args,
3858 int (*)(char *, direction, char *, int, void **));
3859 ctx->action = function;
3861 while ((data = va_arg(args, void *)) != NULL)
3863 nb_data++;
3864 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
3865 ctx->data[nb_data - 1] = data;
3867 ctx->nb_data = nb_data;
3869 else
3870 fatal_internal("%s: unknown context.", ptr);
3871 break;
3874 default:
3875 break;
3877 va_end(args);