Replace bst_null_action by the NULL pointer
[ctxopt.git] / ctxopt.c
blob415d65cf577681a781443b27438608cbef91fa5d
1 /* ########################################################### */
2 /* This Software is licensed under the GPL licensed Version 2, */
3 /* please read http://www.gnu.org/copyleft/gpl.html */
4 /* ########################################################### */
6 #include <errno.h>
7 #include <limits.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <ctype.h>
12 #include <sys/types.h>
13 #include <regex.h>
14 #include <stdarg.h>
15 #include <string.h>
16 #include "ctxopt.h"
18 /* *********************** */
19 /* Static global variables */
20 /* *********************** */
21 static void * contexts_bst;
22 static void * options_bst;
24 state_t * cur_state;
26 /* Prototypes */
28 /* ****************** */
29 /* Messages interface */
30 /* ****************** */
32 static void (**err_functions)(errors e, state_t * state);
34 static void
35 fatal_internal(const char * format, ...);
37 static void
38 fatal(errors e, char * errmsg);
40 static int user_rc; /* Used by various callback functions */
41 static int user_value; /* Used by various callback functions */
42 static char * user_string; /* Used by various callback functions */
43 static char * user_string2; /* Used by various callback functions */
44 static void * user_object; /* Used by various callback functions */
46 /* *************************** */
47 /* Memory management interface */
48 /* *************************** */
50 static void *
51 xmalloc(size_t size);
53 static void *
54 xcalloc(size_t num, size_t size);
56 static void *
57 xrealloc(void * ptr, size_t size);
59 static char *
60 xstrdup(const char * p);
62 static char *
63 xstrndup(const char * str, size_t len);
65 /* ************* */
66 /* BST interface */
67 /* ************* */
68 typedef struct bst_s bst_t;
70 typedef enum
72 preorder,
73 postorder,
74 endorder,
75 leaf
76 } walk_order_e;
78 #if 0 /* Unused yet */
79 static void *
80 bst_delete(const void * vkey, void ** vrootp,
81 int (*compar)(const void *, const void *));
82 #endif
84 static void
85 bst_destroy_recurse(bst_t * root, void (*free_action)(void *));
87 static void
88 bst_destroy(void * vrootp, void (*freefct)(void *));
90 static void *
91 bst_find(const void * vkey, void * const * vrootp,
92 int (*compar)(const void *, const void *));
94 static void *
95 bst_search(const void * vkey, void ** vrootp,
96 int (*compar)(const void *, const void *));
98 static void
99 bst_walk_recurse(const bst_t * root,
100 void (*action)(const void *, walk_order_e, int), int level);
102 static void
103 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int));
105 /* ********************* */
106 /* Linked list Interface */
107 /* ********************* */
109 typedef struct ll_node_s ll_node_t;
110 typedef struct ll_s ll_t;
112 static void
113 ll_append(ll_t * const list, void * const data);
115 static void
116 ll_prepend(ll_t * const list, void * const data);
118 static void
119 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data);
121 static void
122 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data);
124 static int
125 ll_delete(ll_t * const list, ll_node_t * node);
127 #if 0 /* Unused yet */
128 static ll_node_t *
129 ll_find(ll_t * const, void * const, int (*)(const void *, const void *));
130 #endif
132 static void
133 ll_init(ll_t * list);
135 static ll_node_t *
136 ll_new_node(void);
138 static ll_t *
139 ll_new(void);
141 static int
142 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array);
144 /* ****************** */
145 /* various interfaces */
146 /* ****************** */
148 static void
149 ltrim(char * str, const char * trim_str);
151 static void
152 rtrim(char * str, const char * trim_str, size_t min);
154 static int
155 strchrcount(char * str, char c);
157 static int
158 strpref(char * s1, char * s2);
160 static char *
161 xstrtok_r(char * str, const char * delim, char ** end);
163 /* **************** */
164 /* ctxopt interface */
165 /* **************** */
167 typedef struct opt_s opt_t;
168 typedef struct par_s par_t;
169 typedef struct ctx_s ctx_t;
170 typedef struct constraint_s constraint_t;
171 typedef struct ctx_inst_s ctx_inst_t;
172 typedef struct opt_inst_s opt_inst_t;
173 typedef struct seen_opt_s seen_opt_t;
175 static char *
176 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
178 static int
179 ctx_compare(const void * c1, const void * c2);
181 static int
182 opt_compare(const void * o1, const void * o2);
184 static int
185 par_compare(const void * a1, const void * a2);
187 static int
188 seen_opt_compare(const void * so1, const void * so2);
190 static ctx_t *
191 locate_ctx(char * name);
193 static opt_t *
194 locate_opt(char * name);
196 static par_t *
197 locate_par(char * name, ctx_t * ctx);
199 static void
200 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
201 int * has_rule, int * has_generic_arg, int * has_ctx_change,
202 int * has_early_eval);
203 static void
204 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
205 int has_optional, int has_ellipsis, int has_rule);
206 static void
207 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
209 static void
210 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
212 static void
213 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
215 static void
216 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
218 static void
219 bst_match_par_cb(const void * node, walk_order_e kind, int level);
221 static void
222 match_prefix_cb(const void * node, walk_order_e kind, int level);
224 static int
225 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
227 static int
228 opt_parse(char * s, opt_t ** opt);
230 static int
231 init_opts(char * spec, ctx_t * ctx);
233 static int
234 ctxopt_build_cmdline_list(int nb_words, char ** words);
236 static int
237 opt_set_parms(char * opt_name, char * par_str);
239 static ctx_inst_t *
240 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
242 static void
243 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
245 /* ********************** */
246 /* Message implementation */
247 /* ********************** */
249 /* ================================================================== */
250 /* Fatal error function used when an fatal condition was encountered. */
251 /* This function is reserved for the ctxopt internal usage. */
252 /* */
253 /* format : printf like format */
254 /* ... : remaining arguments interpreted using the format argument */
255 /* ================================================================== */
256 static void
257 fatal_internal(const char * format, ...)
259 va_list args;
261 fprintf(stderr, "CTXOPT: ");
263 va_start(args, format);
264 vfprintf(stderr, format, args);
265 fprintf(stderr, "\n");
266 va_end(args);
268 exit(EXIT_FAILURE);
271 /* ====================================================================== */
272 /* Generic fatal error function. This one uses the global status ctxopt */
273 /* stored in the cur_state structure and can call custom error functions. */
274 /* registered by the users for a given error identifier. */
275 /* */
276 /* e : error identifier responsible of the fatal error */
277 /* errmsg : users's provided string specific to the error e */
278 /* Note that errmsg is not used in all cases */
279 /* */
280 /* CTXOPTMISPAR Missing parameter */
281 /* CTXOPTMISARG Missing argument */
282 /* CTXOPTUXPARG Unexpected argument */
283 /* CTXOPTDUPOPT Duplicated option */
284 /* CTXOPTUNKPAR Unknown parameter */
285 /* CTXOPTINCOPT Incompatible option */
286 /* CTXOPTCTEOPT Option: bad number of occurrences */
287 /* CTXOPTCTLOPT Option: not enough occurrences */
288 /* CTXOPTCTGOPT Option: too many occurrence of */
289 /* CTXOPTCTEARG Arguments: bad number of occurrences */
290 /* CTXOPTCTLARG Arguments: not enough occurrences */
291 /* CTXOPTCTGARG Arguments: too many occurrences */
292 /* ====================================================================== */
293 static void
294 fatal(errors e, char * errmsg)
296 if (err_functions[e] != NULL)
297 err_functions[e](e, cur_state);
298 else
300 switch (e)
302 case CTXOPTNOERR:
303 break;
305 case CTXOPTMISPAR:
306 if (cur_state->ctx_par_name != NULL)
307 fprintf(stderr,
308 "Mandatory parameter(s): %s are missing in the context "
309 "introduced by %s.\n",
310 errmsg, cur_state->ctx_par_name);
311 else
312 fprintf(stderr,
313 "Mandatory parameter(s): %s are missing "
314 "in the main context.\n",
315 errmsg);
317 free(errmsg);
318 break;
320 case CTXOPTUNXARG:
321 fprintf(stderr, "%s only takes one argument.\n",
322 cur_state->cur_opt_par_name);
323 break;
325 case CTXOPTMISARG:
326 if (cur_state->pre_opt_par_name != NULL)
327 fprintf(stderr, "%s requires argument(s).\n",
328 cur_state->pre_opt_par_name);
329 else
330 fprintf(stderr, "%s requires argument(s).\n",
331 cur_state->cur_opt_par_name);
332 break;
334 case CTXOPTDUPOPT:
335 if (cur_state->pre_opt_par_name != NULL)
336 fprintf(stderr,
337 "The parameter(s) %s can only appear once in the context "
338 "introduced by %s.\n",
339 cur_state->cur_opt_par_name, cur_state->ctx_par_name);
340 else
341 fprintf(stderr,
342 "The parameter(s) %s can only appear once "
343 "in the main context.\n",
344 cur_state->cur_opt_par_name);
345 break;
347 case CTXOPTUNKPAR:
348 fprintf(stderr, "Unknown parameter: %s.\n",
349 cur_state->cur_opt_par_name);
350 break;
352 case CTXOPTINCOPT:
353 fprintf(stderr, "%s is incompatible with %s.\n",
354 cur_state->cur_opt_par_name, errmsg);
355 break;
357 case CTXOPTCTEOPT:
358 if (cur_state->ctx_par_name)
359 fprintf(stderr,
360 "%s must appear exactly %u times in the context "
361 "introduced by %s.\n",
362 cur_state->cur_opt_par_name, cur_state->opts_count,
363 cur_state->ctx_par_name);
364 else
365 fprintf(stderr,
366 "%s must appear exactly %u times in "
367 "the main context.\n",
368 cur_state->cur_opt_par_name, cur_state->opts_count);
369 break;
371 case CTXOPTCTLOPT:
372 if (cur_state->ctx_par_name)
373 fprintf(stderr,
374 "%s must appear less than %u times in the context "
375 "introduced by %s.\n",
376 cur_state->cur_opt_par_name, cur_state->opts_count,
377 cur_state->ctx_par_name);
378 else
379 fprintf(stderr,
380 "%s must appear less than %u times in the main context.\n",
381 cur_state->cur_opt_par_name, cur_state->opts_count);
382 break;
384 case CTXOPTCTGOPT:
385 if (cur_state->ctx_par_name)
386 fprintf(stderr,
387 "%s must appear more than %u times in the context "
388 "introduced by %s.\n",
389 cur_state->cur_opt_par_name, cur_state->opts_count,
390 cur_state->ctx_par_name);
391 else
392 fprintf(stderr,
393 "%s must appear more than %u times in the main context.\n",
394 cur_state->cur_opt_par_name, cur_state->opts_count);
395 break;
397 case CTXOPTCTEARG:
398 fprintf(stderr, "%s must have exactly %u arguments.\n",
399 cur_state->cur_opt_par_name, cur_state->opt_args_count);
400 break;
402 case CTXOPTCTLARG:
403 fprintf(stderr, "%s must have less than %u arguments.\n",
404 cur_state->cur_opt_par_name, cur_state->opt_args_count);
405 break;
407 case CTXOPTCTGARG:
408 fprintf(stderr, "%s must have more than %u arguments.\n",
409 cur_state->cur_opt_par_name, cur_state->opt_args_count);
410 break;
412 case CTXOPTERRSIZ:
413 break;
417 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
419 exit(e); /* Program exist with the error id e as return code */
422 /* ******************************** */
423 /* Memory management implementation */
424 /* ******************************** */
426 /* ================= */
427 /* Customized malloc */
428 /* ================= */
429 static void *
430 xmalloc(size_t size)
432 void * allocated;
433 size_t real_size;
435 real_size = (size > 0) ? size : 1;
436 allocated = malloc(real_size);
437 if (allocated == NULL)
438 fatal_internal("Insufficient memory (attempt to malloc %lu bytes)\n",
439 (unsigned long int)size);
441 return allocated;
444 /* ================= */
445 /* Customized calloc */
446 /* ================= */
447 static void *
448 xcalloc(size_t n, size_t size)
450 void * allocated;
452 n = (n > 0) ? n : 1;
453 size = (size > 0) ? size : 1;
454 allocated = calloc(n, size);
455 if (allocated == NULL)
456 fatal_internal("Insufficient memory (attempt to calloc %lu bytes)\n",
457 (unsigned long int)size);
459 return allocated;
462 /* ================== */
463 /* Customized realloc */
464 /* ================== */
465 static void *
466 xrealloc(void * p, size_t size)
468 void * allocated;
470 allocated = realloc(p, size);
471 if (allocated == NULL && size > 0)
472 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes)\n",
473 (unsigned long int)size);
475 return allocated;
478 /* =================================== */
479 /* strdup implementation using xmalloc */
480 /* =================================== */
481 static char *
482 xstrdup(const char * p)
484 char * allocated;
486 allocated = xmalloc(strlen(p) + 1);
487 strcpy(allocated, p);
489 return allocated;
492 /* ================================================== */
493 /* strndup implementation using xmalloc */
494 /* This version guarantees that there is a final '\0' */
495 /* ================================================== */
496 static char *
497 xstrndup(const char * str, size_t len)
499 char * p;
501 p = memchr(str, '\0', len);
503 if (p)
504 len = p - str;
506 p = xmalloc(len + 1);
507 memcpy(p, str, len);
508 p[len] = '\0';
510 return p;
513 /* ************************** */
514 /* Linked list implementation */
515 /* ************************** */
517 /* Linked list node structure */
518 /* """""""""""""""""""""""""" */
519 struct ll_node_s
521 void * data;
522 struct ll_node_s * next;
523 struct ll_node_s * prev;
526 /* Linked List structure */
527 /* """"""""""""""""""""" */
528 struct ll_s
530 ll_node_t * head;
531 ll_node_t * tail;
532 long len;
535 /* ======================== */
536 /* Create a new linked list */
537 /* ======================== */
538 static ll_t *
539 ll_new(void)
541 ll_t * ret = xmalloc(sizeof(ll_t));
542 ll_init(ret);
544 return ret;
547 /* ======================== */
548 /* Initialize a linked list */
549 /* ======================== */
550 static void
551 ll_init(ll_t * list)
553 list->head = NULL;
554 list->tail = NULL;
555 list->len = 0;
558 /* ==================================================== */
559 /* Allocate the space for a new node in the linked list */
560 /* ==================================================== */
561 static ll_node_t *
562 ll_new_node(void)
564 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
566 return ret;
569 /* ==================================================================== */
570 /* Append a new node filled with its data at the end of the linked list */
571 /* The user is responsible for the memory management of the data */
572 /* ==================================================================== */
573 static void
574 ll_append(ll_t * const list, void * const data)
576 ll_node_t * node;
578 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
579 | uses xmalloc which does not return if there *
580 | is an allocation error. */
582 node->data = data;
583 node->next = NULL;
585 node->prev = list->tail;
586 if (list->tail)
587 list->tail->next = node;
588 else
589 list->head = node;
591 list->tail = node;
593 ++list->len;
596 /* =================================================================== */
597 /* Put a new node filled with its data at the beginning of the linked */
598 /* list. The user is responsible for the memory management of the data */
599 /* =================================================================== */
600 static void
601 ll_prepend(ll_t * const list, void * const data)
603 ll_node_t * node;
605 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
606 | uses xmalloc which does not return if there *
607 | is an allocation error. */
609 node->data = data;
610 node->prev = NULL;
612 node->next = list->head;
613 if (list->head)
614 list->head->prev = node;
615 else
616 list->tail = node;
618 list->head = node;
620 ++list->len;
623 /* ======================================================= */
624 /* Insert a new node before the specified node in the list */
625 /* ======================================================= */
626 static void
627 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
629 ll_node_t * new_node;
631 if (node->prev == NULL)
632 ll_prepend(list, data);
633 else
635 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
636 | uses xmalloc which does not return if there *
637 | is an allocation error. */
639 new_node->data = data;
640 new_node->next = node;
641 new_node->prev = node->prev;
642 node->prev->next = new_node;
643 node->prev = new_node;
645 ++list->len;
649 /* ====================================================== */
650 /* Insert a new node after the specified node in the list */
651 /* ====================================================== */
652 static void
653 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
655 ll_node_t * new_node;
657 if (node->next == NULL)
658 ll_append(list, data);
659 else
661 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
662 | uses xmalloc which does not return if there *
663 | is an allocation error. */
665 new_node->data = data;
666 new_node->prev = node;
667 new_node->next = node->next;
668 node->next->prev = new_node;
669 node->next = new_node;
671 ++list->len;
675 /* ================================================================ */
676 /* Remove a node from a linked list */
677 /* The memory taken by the deleted node must be freed by the caller */
678 /* ================================================================ */
679 static int
680 ll_delete(ll_t * const list, ll_node_t * node)
682 if (list->head == list->tail)
684 if (list->head != NULL)
685 list->head = list->tail = NULL;
686 else
687 return 0;
689 else if (node->prev == NULL)
691 list->head = node->next;
692 list->head->prev = NULL;
694 else if (node->next == NULL)
696 list->tail = node->prev;
697 list->tail->next = NULL;
699 else
701 node->next->prev = node->prev;
702 node->prev->next = node->next;
705 --list->len;
707 free(node);
709 return 1;
712 #if 0 /* Unused yet */
713 /* =========================================================================*/
714 /* Find a node in the list containing data. Return the node pointer or NULL */
715 /* if not found. */
716 /* A comparison function must be provided to compare a and b (strcmp like). */
717 /* =========================================================================*/
718 static ll_node_t *
719 ll_find(ll_t * const list, void * const data,
720 int (*cmpfunc)(const void * a, const void * b))
722 ll_node_t * node;
724 if (NULL == (node = list->head))
725 return NULL;
729 if (0 == cmpfunc(node->data, data))
730 return node;
731 } while (NULL != (node = node->next));
733 return NULL;
735 #endif
737 /* ==================================================================== */
738 /* Allocates and fills an array of strings from a list */
739 /* WARNINGS: */
740 /* 1) The list node must contain strings (char *) */
741 /* 2) The strings in the resulting array MUST NOT be freed as the are */
742 /* NOT copied from the strings of the list. */
743 /* */
744 /* IN list : The list from which the array is generated */
745 /* IN start_node : The node of the list which will be the first node to */
746 /* consider to create the array */
747 /* OUT: count : The number of elements of the resulting array. */
748 /* OUT: array : The resulting array or NULL if the list is empty. */
749 /* RC : : The number of elements of the resulting array. */
750 /* ==================================================================== */
751 static int
752 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
754 int n = 0;
755 ll_node_t * node;
757 *count = 0;
759 node = start_node;
761 if (list == NULL || node == NULL)
763 *array = NULL;
765 return 0;
768 *array = xmalloc((list->len + 1) * sizeof(char *));
769 while (node != NULL)
771 (*array)[n++] = (char *)(node->data);
772 (*count)++;
774 node = node->next;
777 (*array)[*count] = NULL;
779 return *count;
782 /* ******************************************************************* */
783 /* BST (search.h compatible) implementation */
784 /* */
785 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
786 /* the AT&T man page says. */
787 /* */
788 /* Written by reading the System V Interface Definition, not the code. */
789 /* */
790 /* Totally public domain. */
791 /* ******************************************************************* */
793 struct bst_s
795 void * key;
796 struct bst_s * llink;
797 struct bst_s * rlink;
800 #if 0 /* Unused yet */
801 /* ========================== */
802 /* delete node with given key */
803 /* ========================== */
804 static void *
805 bst_delete(const void * vkey, void ** vrootp,
806 int (*compar)(const void *, const void *))
808 bst_t ** rootp = (bst_t **)vrootp;
809 bst_t * p, *q, *r;
810 int cmp;
812 if (rootp == NULL || (p = *rootp) == NULL)
813 return NULL;
815 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
817 p = *rootp;
818 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
819 : &(*rootp)->rlink; /* follow rlink branch */
820 if (*rootp == NULL)
821 return NULL; /* key not found */
823 r = (*rootp)->rlink; /* D1: */
824 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
825 q = r;
826 else if (r != NULL)
827 { /* Right link is NULL? */
828 if (r->llink == NULL)
829 { /* D2: Find successor */
830 r->llink = q;
831 q = r;
833 else
834 { /* D3: Find NULL link */
835 for (q = r->llink; q->llink != NULL; q = r->llink)
836 r = q;
837 r->llink = q->rlink;
838 q->llink = (*rootp)->llink;
839 q->rlink = (*rootp)->rlink;
842 if (p != *rootp)
843 free(*rootp); /* D4: Free node */
844 *rootp = q; /* link parent to new node */
845 return p;
847 #endif
849 /* ============== */
850 /* destroy a tree */
851 /* ============== */
852 static void
853 bst_destroy(void * vrootp, void (*clean)(void *))
855 bst_t * root = (bst_t *)vrootp;
857 if (root == NULL)
858 return;
860 bst_destroy(root->llink, clean);
861 bst_destroy(root->rlink, clean);
863 if (clean)
864 clean((void *)root->key);
866 free(root);
869 /* ======================== */
870 /* find a node, or return 0 */
871 /* ======================== */
872 static void *
873 bst_find(const void * vkey, void * const * vrootp,
874 int (*compar)(const void *, const void *))
876 bst_t * const * rootp = (bst_t * const *)vrootp;
878 if (rootp == NULL)
879 return NULL;
881 while (*rootp != NULL)
882 { /* T1: */
883 int r;
885 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
886 return *rootp; /* key found */
887 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
888 : &(*rootp)->rlink; /* T4: follow right branch */
890 return NULL;
893 /* ===================================== */
894 /* find or insert datum into search tree */
895 /* ===================================== */
896 static void *
897 bst_search(void * vkey, void ** vrootp,
898 int (*compar)(const void *, const void *))
900 bst_t * q;
901 bst_t ** rootp = (bst_t **)vrootp;
903 if (rootp == NULL)
904 return NULL;
906 while (*rootp != NULL)
907 { /* Knuth's T1: */
908 int r;
910 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
911 return *rootp; /* we found it! */
913 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
914 : &(*rootp)->rlink; /* T4: follow right branch */
917 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
918 if (q != 0)
919 { /* make new node */
920 *rootp = q; /* link new node to old */
921 q->key = vkey; /* initialize new node */
922 q->llink = q->rlink = NULL;
924 return q;
927 /* ======================== */
928 /* Walk the nodes of a tree */
929 /* ======================== */
930 static void
931 bst_walk_recurse(const bst_t * root,
932 void (*action)(const void *, walk_order_e, int), int level)
934 if (root->llink == NULL && root->rlink == NULL)
935 (*action)(root, leaf, level);
936 else
938 (*action)(root, preorder, level);
939 if (root->llink != NULL)
940 bst_walk_recurse(root->llink, action, level + 1);
941 (*action)(root, postorder, level);
942 if (root->rlink != NULL)
943 bst_walk_recurse(root->rlink, action, level + 1);
944 (*action)(root, endorder, level);
948 static void
949 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
951 if (vroot != NULL && action != NULL)
952 bst_walk_recurse(vroot, action, 0);
955 /* *********************** */
956 /* various implementations */
957 /* *********************** */
959 /* ======================= */
960 /* Trim leading characters */
961 /* ======================= */
962 static void
963 ltrim(char * str, const char * trim_str)
965 size_t len = strlen(str);
966 size_t begin = strspn(str, trim_str);
967 size_t i;
969 if (begin > 0)
970 for (i = begin; i <= len; ++i)
971 str[i - begin] = str[i];
974 /* ================================================= */
975 /* Trim trailing characters */
976 /* The resulting string will have at least min bytes */
977 /* even if trailing spaces remain. */
978 /* ================================================= */
979 static void
980 rtrim(char * str, const char * trim_str, size_t min)
982 size_t len = strlen(str);
983 while (len > min && strchr(trim_str, str[len - 1]))
984 str[--len] = '\0';
987 /* ================================================== */
988 /* Count the number of occurrences of the character c */
989 /* in the string str. */
990 /* The str pointer is assumed to be not NULL */
991 /* ================================================== */
992 static int
993 strchrcount(char * str, char c)
995 int count = 0;
997 while (*str)
998 if (*str++ == c)
999 count++;
1001 return count;
1004 /* =============================================== */
1005 /* Is the string str2 a prefix of the string str1? */
1006 /* =============================================== */
1007 static int
1008 strpref(char * str1, char * str2)
1010 while (*str1 != '\0' && *str1 == *str2)
1012 str1++;
1013 str2++;
1016 return *str2 == '\0';
1019 /* ======================================================================== */
1020 /* Strings concatenation with dynamic memory allocation */
1021 /* IN : a variable number of char * arguments with NULL terminating */
1022 /* the sequence. */
1023 /* The first one must have been dynamically allocated and is mandatory */
1024 /* */
1025 /* Returns a new allocated string containing the concatenation of all */
1026 /* the arguments. It is the caller's responsibility to free the resulting */
1027 /* string. */
1028 /* ======================================================================== */
1029 static char *
1030 strappend(char * str, ...)
1032 size_t l;
1033 va_list args;
1034 char * s;
1036 l = 1 + strlen(str);
1037 va_start(args, str);
1039 s = va_arg(args, char *);
1041 while (s)
1043 l += strlen(s);
1044 s = va_arg(args, char *);
1047 va_end(args);
1049 str = xrealloc(str, l);
1051 va_start(args, str);
1052 s = va_arg(args, char *);
1054 while (s)
1056 strcat(str, s);
1057 s = va_arg(args, char *);
1059 va_end(args);
1061 return str;
1064 /* ====================================================================== */
1065 /* public domain strtok_r() by Charlie Gordon */
1066 /* from comp.lang.c 9/14/2007 */
1067 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1068 /* */
1069 /* (Declaration that it's public domain): */
1070 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1071 /* */
1072 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1073 /* *end == NULL */
1074 /* ====================================================================== */
1075 static char *
1076 xstrtok_r(char * str, const char * delim, char ** end)
1078 char * ret;
1080 if (str == NULL)
1081 str = *end;
1083 if (str == NULL)
1084 return NULL;
1086 str += strspn(str, delim);
1088 if (*str == '\0')
1089 return NULL;
1091 ret = str;
1093 str += strcspn(str, delim);
1095 if (*str)
1096 *str++ = '\0';
1098 *end = str;
1100 return ret;
1103 /* =========================================================== */
1104 /* Fills an array of strings from the words composing a string */
1105 /* */
1106 /* str: initial string which will be altered */
1107 /* args: array of pointers to the start of the words in str */
1108 /* max: maximum number of words used before giving up */
1109 /* return: the number of words (<=max) */
1110 /* =========================================================== */
1111 static int
1112 str2argv(char * str, char ** args, int max)
1114 int nb_args = 0;
1116 while (*str)
1118 if (nb_args >= max)
1119 return nb_args;
1121 while (*str == ' ' || *str == '\t')
1122 *(str++) = '\0';
1124 if (!*str)
1125 return nb_args;
1127 args[nb_args] = str;
1128 nb_args++;
1130 while (*str && (*str != ' ') && (*str != '\t'))
1131 str++;
1134 return nb_args;
1137 /* ********************* */
1138 /* ctxopt implementation */
1139 /* ********************* */
1141 static int ctxopt_initialized = 0; /* cap_init has not yet been called */
1143 /* context structure */
1144 /* """"""""""""""""" */
1145 struct ctx_s
1147 char * name;
1148 ll_t * opt_list; /* list of options allowed in this context */
1149 ll_t * incomp_list; /* list of strings containing incompatible names *
1150 | of options separated by spaces or tabs */
1151 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1152 void ** ctx_data);
1153 void * par_bst;
1154 int nb_data;
1155 void ** data;
1158 /* https://textik.com/#488ce3649b6c60f5 */
1159 /* */
1160 /* +--------------+ */
1161 /* |first_ctx_inst| */
1162 /* +---+----------+ */
1163 /* | */
1164 /* +--v-----+ +--------+ +--------+ +-----+ */
1165 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1166 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1167 /* | | | | | */
1168 /* | | +-v------+ | | */
1169 /* | +--+ctx_inst<-----------+ | */
1170 /* | +-+------+ | */
1171 /* | | | */
1172 /* | +-v------+ | */
1173 /* +------+ctx_inst<--------------------------+ */
1174 /* +-+------+ */
1175 /* | */
1176 /* +-v---+ */
1177 /* | ... | */
1178 /* +-----+ */
1180 /* option structure */
1181 /* """""""""""""""" */
1182 struct opt_s
1184 char * name; /* option name */
1185 char * next_ctx; /* new context this option may lead to */
1186 ll_t * ctx_list; /* list of contexts allowing this option */
1187 char * params; /* string containing all the parameters of *
1188 | the option */
1190 void (*action)( /* The option associated action */
1191 char * ctx_name, /* context name */
1192 char * opt_name, /* option name */
1193 char * par, /* option parameter */
1194 int nb_args, /* number of arguments */
1195 char ** args, /* option arguments */
1196 int nb_opt_data, /* number of option data pointers */
1197 void ** opt_data, /* option data pointers */
1198 int nb_ctx_data, /* nb of current context data ptrs */
1199 void ** ctx_data /* current context data pointers */
1202 int nb_data; /* number of the data pointers passed as argument to *
1203 | action */
1204 void ** data; /* array of data pointers passed as argument to action */
1206 int args; /* 1 if this option takes arguments else 0 */
1207 int optional; /* 1 if the option is optional, else 0 */
1208 int multiple; /* 1 if the option can appear more than one time in a *
1209 | context, else 0 */
1211 int opt_count_matter; /* 1 if we must restrict the count, else 0 */
1212 int occurrences; /* Number of option occurrences in a context */
1213 char opt_count_oper; /* <, = or > */
1214 unsigned opt_count_mark; /* Value to be compared to with opt_count_oper */
1216 char * arg; /* symbolic text after # describing the option argument */
1218 int optional_args; /* 1 of option is optional else 0 */
1219 int multiple_args; /* 1 is option can appear more than once in a context *
1220 | instance */
1222 int opt_args_count_matter; /* 1 if we must restrict the count, else 0 */
1223 char opt_args_count_oper; /* <, = or > */
1224 unsigned opt_args_count_mark; /* Value to be compared to with *
1225 | opt_count_oper */
1227 int eval_first; /* 1 if this option must be evaluated before *
1228 | the options without this mark */
1230 ll_t * constraints_list; /* List of constraint checking functions pointers. */
1233 /* context instance structure */
1234 /* """""""""""""""""""""""""" */
1235 struct ctx_inst_s
1237 ctx_t * ctx; /* the context whose this is an instance of */
1238 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1239 | creation of this ctx_inst structure. */
1240 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1241 | instance of this structure */
1242 ll_t * incomp_bst_list; /* list of seen_opt_t bst */
1243 void * seen_opt_bst; /* tree of seen_opt_t */
1244 ll_t * opt_inst_list; /* The list of option instances in this *
1245 | context instance */
1246 char * par_name; /* parameter which created this instance */
1249 /* Option instance structure */
1250 /* """"""""""""""""""""""""" */
1251 struct opt_inst_s
1253 opt_t * opt; /* The option this is an instance of */
1254 char * opt_name; /* The option which led to this creation */
1255 char * par; /* The parameter which led to this creation */
1256 ll_t * values_list; /* The list of arguments of this option */
1257 ctx_inst_t * next_ctx_inst; /* The new context instance this option *
1258 | instance may create */
1261 /* Structure used to check if an option has bee seen or not */
1262 /* in a context instance */
1263 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1264 struct seen_opt_s
1266 opt_t * opt; /* The concerned option */
1267 char * par; /* Parameter which led to the making of this structure */
1268 char * count; /* Number of seen occurrences of this parameter */
1269 int seen; /* 1 if seen in the context instances, else 0 */
1272 /* parameter structure which links a parameter to the option it belongs to */
1273 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1274 struct par_s
1276 char * name; /* Parameter name (with the leading - */
1277 opt_t * opt; /* Attached option */
1280 /* Constraint structure */
1281 /* """""""""""""""""""" */
1282 struct constraint_s
1284 int (*constraint)(int nb_args, char ** args, char * value);
1285 int nb_args;
1286 char ** args;
1289 state_t * cur_state = NULL; /* Current analysis state */
1290 static ll_t * cmdline_list; /* List of interpreted CLI words *
1291 | serves as the basis for the *
1292 | analysis of the parameters */
1293 static ctx_t * main_ctx = NULL; /* initial context */
1294 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1295 | instance which holds the *
1296 | options instances */
1297 static ll_t * ctx_inst_list; /* List of the context instances */
1299 /* ====================================================== */
1300 /* Parse a string for the next matching token. */
1301 /* */
1302 /* s: string to parse. */
1303 /* token: pre_allocated array of max tok_len characters */
1304 /* pattern: scanf type pattern token must match */
1305 /* pos: number of characters successfully parsed in s */
1306 /* */
1307 /* Returns: a pointer to the first unread character or */
1308 /* to he terminating \0. */
1309 /* ====================================================== */
1310 static char *
1311 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1313 char * full_pattern;
1314 char len[3];
1315 int n;
1317 *pos = 0;
1319 n = snprintf(len, 3, "%zu", tok_len);
1320 if (n < 0)
1321 return NULL;
1323 full_pattern = xmalloc(strlen(pattern) + n + 4);
1325 strcpy(full_pattern, "%");
1326 strcat(full_pattern, len);
1327 strcat(full_pattern, pattern);
1328 strcat(full_pattern, "%n");
1330 n = sscanf(s, full_pattern, token, pos);
1332 free(full_pattern);
1334 if (n != 1)
1335 return NULL;
1337 return s + *pos;
1340 /* **************************** */
1341 /* Various comparison functions */
1342 /* **************************** */
1344 static int
1345 ctx_compare(const void * c1, const void * c2)
1347 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1350 static int
1351 opt_compare(const void * o1, const void * o2)
1353 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1356 static int
1357 par_compare(const void * a1, const void * a2)
1359 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1362 static int
1363 seen_opt_compare(const void * so1, const void * so2)
1365 opt_t *o1, *o2;
1367 o1 = ((seen_opt_t *)so1)->opt;
1368 o2 = ((seen_opt_t *)so2)->opt;
1370 return strcmp(o1->name, o2->name);
1373 /* ******************************************************************** */
1374 /* Helper functions to locate contexts, options and parameters in a bst */
1375 /* by their names. */
1376 /* ******************************************************************** */
1378 static ctx_t *
1379 locate_ctx(char * name)
1381 bst_t * node;
1382 ctx_t ctx = { 0 };
1384 ctx.name = name;
1386 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1387 return NULL;
1388 else
1389 return node->key;
1392 static opt_t *
1393 locate_opt(char * name)
1395 bst_t * node;
1396 opt_t opt = { 0 };
1398 opt.name = name;
1400 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1401 return NULL;
1402 else
1403 return node->key;
1406 static par_t *
1407 locate_par(char * name, ctx_t * ctx)
1409 bst_t * node;
1410 par_t par = { 0 };
1411 void * bst = ctx->par_bst;
1413 par.name = name;
1415 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1416 return NULL;
1417 else
1418 return node->key;
1421 /* =================================================================== */
1422 /* Utility function to format and print the options present in a list. */
1423 /* */
1424 /* IN list : a list of options */
1425 /* OUT has_* : a set of flags which will determine the content of the */
1426 /* explanation given after the formatted printing of the */
1427 /* options. */
1428 /* =================================================================== */
1429 static void
1430 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1431 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1432 int * has_early_eval)
1434 ll_node_t * node = list->head;
1435 opt_t * opt;
1436 char * line;
1437 char * option;
1439 line = xstrdup(" ");
1441 while (node != NULL)
1443 option = xstrdup("");
1444 opt = node->data;
1446 if (opt->optional)
1448 option = strappend(option, "[", NULL);
1449 *has_optional = 1;
1452 if (opt->eval_first)
1454 option = strappend(option, "*", NULL);
1455 *has_early_eval = 1;
1458 option = strappend(option, opt->params, NULL);
1460 if (opt->next_ctx != NULL)
1462 option = strappend(option, ">", opt->next_ctx, NULL);
1463 *has_ctx_change = 1;
1466 if (opt->multiple)
1468 if (opt->opt_count_oper != '\0')
1470 char m[4];
1471 char o[2];
1472 o[0] = opt->opt_count_oper;
1473 o[1] = '\0';
1474 snprintf(m, 3, "%u", opt->opt_count_mark);
1475 option = strappend(option, "...", o, m, NULL);
1476 *has_rule = 1;
1478 else
1479 option = strappend(option, "...", NULL);
1481 *has_ellipsis = 1;
1484 if (opt->args)
1486 if (*(opt->arg) == '#')
1487 *has_generic_arg = 1;
1489 option = strappend(option, " ", NULL);
1491 if (opt->optional_args)
1493 option = strappend(option, "[", opt->arg, NULL);
1494 *has_optional = 1;
1496 else
1497 option = strappend(option, opt->arg, NULL);
1499 if (opt->multiple_args)
1501 if (opt->opt_args_count_oper != '\0')
1503 char m[4];
1504 char o[2];
1505 o[0] = opt->opt_args_count_oper;
1506 o[1] = '\0';
1507 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1508 option = strappend(option, "...", o, m, NULL);
1509 *has_rule = 1;
1511 else
1512 option = strappend(option, "...", NULL);
1514 *has_ellipsis = 1;
1516 if (opt->optional_args)
1517 option = strappend(option, "]", NULL);
1519 if (opt->optional)
1520 option = strappend(option, "]", NULL);
1522 if (strlen(line) + 1 + strlen(option) < 80)
1523 line = strappend(line, option, " ", NULL);
1524 else
1526 printf("%s\n", line);
1527 line[2] = '\0';
1528 line = strappend(line, option, " ", NULL);
1531 free(option);
1533 node = node->next;
1536 printf("%s\n", line);
1538 free(line);
1541 /* ==================================================== */
1542 /* Explain the special syntactic symbols present in the */
1543 /* generated usage messages. */
1544 /* ==================================================== */
1545 static void
1546 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1547 int has_optional, int has_ellipsis, int has_rule)
1549 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1550 || has_ellipsis || has_rule)
1552 printf("\nSyntactic explanations:\n");
1553 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1554 "must be entered.\n");
1555 printf("The following is just there to explain the other symbols "
1556 "displayed.\n\n");
1558 if (has_early_eval)
1559 printf("* : the parameters for this option will be "
1560 "evaluated first.\n");
1561 if (has_ctx_change)
1562 printf(
1563 "> : The context after this symbol will become the next "
1564 "default one.\n");
1565 if (has_generic_arg)
1566 printf("#tag : argument tag giving a clue to its meaning.\n");
1567 if (has_optional)
1568 printf(
1569 "[...] : the object between square brackets is optional.\n");
1570 if (has_ellipsis)
1571 printf("... : the previous object can be repeated more "
1572 "than one time.\n");
1573 if (has_rule)
1574 printf("[<|=|>]number: rules constraining the number of "
1575 "parameters/arguments.\n");
1579 /* ******************************************************* */
1580 /* Various utility and callback function call when walking */
1581 /* through a bst. */
1582 /* ******************************************************* */
1584 static void
1585 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1587 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1589 if (kind == postorder || kind == leaf)
1591 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1593 user_rc = 1;
1594 user_string = strappend(user_string, seen_opt->opt->params, " ", NULL);
1599 static void
1600 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1602 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1604 if (kind == postorder || kind == leaf)
1605 if (seen_opt->seen == 1)
1607 user_rc = 1;
1608 user_object = seen_opt->par;
1612 static void
1613 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
1615 ctx_t * ctx = main_ctx;
1616 ctx_t * cur_ctx = ((bst_t *)node)->key;
1618 ll_t * list;
1620 int has_optional = 0;
1621 int has_ellipsis = 0;
1622 int has_rule = 0;
1623 int has_generic_arg = 0;
1624 int has_ctx_change = 0;
1625 int has_early_eval = 0;
1627 if (kind == postorder || kind == leaf)
1628 if (strcmp(ctx->name, cur_ctx->name) != 0)
1630 list = cur_ctx->opt_list;
1632 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
1633 print_options(list, &has_optional, &has_ellipsis, &has_rule,
1634 &has_generic_arg, &has_ctx_change, &has_early_eval);
1638 static void
1639 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
1641 opt_t * opt = ((bst_t *)node)->key;
1643 if (kind == postorder || kind == leaf)
1645 if (opt->params == NULL) /* opt must have associated parameters */
1646 fatal_internal("Option %s has no registered parameter.\n", opt->name);
1648 if (opt->action == NULL) /* opt must have an action */
1649 fatal_internal("Option %s has no registered action.\n", opt->name);
1653 static void
1654 bst_match_par_cb(const void * node, walk_order_e kind, int level)
1656 ctx_t * ctx = ((bst_t *)node)->key;
1658 if (kind == postorder || kind == leaf)
1660 char * str = xstrdup(user_string);
1662 while (*str != '\0')
1664 if (locate_par(str, ctx) != NULL)
1666 user_string2 = strappend(user_string2, " ", ctx->name, NULL);
1667 break;
1669 str[strlen(str) - 1] = '\0';
1671 free(str);
1675 static void
1676 match_prefix_cb(const void * node, walk_order_e kind, int level)
1678 par_t * par = ((bst_t *)node)->key;
1680 if (kind == postorder || kind == leaf)
1681 if (strpref(par->name, (char *)user_object))
1683 user_rc++;
1684 user_string = strappend(user_string, par->name, " ", NULL);
1688 /* ====================================================================== */
1689 /* A parameter may not be separated from its first option by spaces, in */
1690 /* this case this function looks for a valid flag as a prefix and splits */
1691 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1692 /* option) */
1693 /* */
1694 /* IN word : the word to be checked. */
1695 /* IN ctx : the context in which the flag indexed by the word is to be */
1696 /* checked. */
1697 /* OUT pos : the offset in word pointing just after the matching prefix. */
1698 /* OUT opt : a pointer to the option associated with the new parameter */
1699 /* or NULL if none is found. */
1700 /* */
1701 /* The returned pointer must be freed by the caller */
1702 /* ====================================================================== */
1703 static char *
1704 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
1706 char * new = NULL;
1707 int len;
1708 par_t * par;
1709 par_t tmp_par = { 0 };
1711 len = strlen(word);
1713 if (len > 2)
1715 new = xstrdup(word);
1719 new[--len] = '\0';
1720 tmp_par.name = new;
1721 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
1723 if (par != NULL)
1725 *pos = len;
1726 *opt = par->opt;
1728 else
1730 free(new);
1731 new = NULL;
1734 else
1735 *pos = 0;
1737 return new;
1740 /* ============================================================= */
1741 /* If par_name is an unique abbreviation of an exiting parameter */
1742 /* in the context ctx, then return this parameter. */
1743 /* ============================================================= */
1744 static char *
1745 abbrev_expand(char * par_name, ctx_t * ctx)
1747 user_object = par_name;
1748 user_rc = 0;
1750 *user_string = '\0';
1751 bst_walk(ctx->par_bst, match_prefix_cb);
1752 rtrim(user_string, " ", 0);
1754 if (user_rc == 1) /* The number of matching abbreviations */
1755 return xstrdup(user_string);
1756 else
1758 char * s, *first_s;
1759 par_t * par;
1760 opt_t * opt;
1761 int opt_count = 0;
1762 void * tmp_opt_bst = NULL;
1764 /* for each word in the matching parameters return by walking the */
1765 /* parameter's tree of this context, do: */
1766 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1767 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
1768 | the first word. */
1769 while (s != NULL)
1771 par = locate_par(s, ctx);
1772 opt = par->opt;
1774 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
1776 /* This option as not already been seen */
1777 /* store it and increase the seen counter */
1778 /* """""""""""""""""""""""""""""""""""""" */
1779 bst_search(opt, &tmp_opt_bst, opt_compare);
1780 opt_count++;
1782 s = strtok(NULL, " ");
1785 /* Clean the temporary bst without removing the pointer */
1786 /* to the real options. */
1787 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
1788 if (tmp_opt_bst != NULL)
1789 bst_destroy(tmp_opt_bst, NULL);
1791 if (opt_count == 1)
1792 /* All the abbreviation lead to only one option */
1793 /* We can just continue as in the previous case. */
1794 /* """""""""""""""""""""""""""""""""""""""""""""" */
1795 return xstrdup(first_s);
1796 else
1797 return NULL;
1801 /* ================================================================= */
1802 /* Terminates the program if mandatory options required by a context */
1803 /* are not present. */
1804 /* ================================================================= */
1805 static void
1806 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
1808 char * missing;
1810 if (has_unseen_mandatory_opt(ctx_inst, &missing))
1811 fatal(CTXOPTMISPAR, missing);
1814 /* ====================================================== */
1815 /* Return 1 if at least one mandatory option was not seen */
1816 /* when quitting a context, else 0 */
1817 /* ====================================================== */
1818 static int
1819 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
1821 user_rc = 0;
1822 *user_string = '\0';
1824 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
1825 rtrim(user_string, " ", 0);
1827 *missing = user_string;
1829 return user_rc ? 1 : 0;
1832 /* ========================================================================= */
1833 /* This function terminates the program if an option or its arguments do not */
1834 /* conform to its occurrences constraint. */
1835 /* There constraints can appear by trailing >, < or = in their definition */
1836 /* given in ctxopt_new_ctx. */
1837 /* ========================================================================= */
1838 static void
1839 check_for_occurrences_issues(ctx_inst_t * ctx_inst)
1841 ctx_t * ctx = ctx_inst->ctx;
1842 opt_t * opt;
1843 ll_node_t * node;
1844 opt_inst_t * opt_inst;
1846 /* Check options */
1847 /* """"""""""""" */
1848 node = ctx->opt_list->head;
1850 while (node != NULL)
1852 opt = node->data;
1854 /* Update current_state */
1855 /* -------------------- */
1856 cur_state->opts_count = opt->opt_count_mark;
1857 cur_state->opt_args_count = opt->opt_args_count_mark;
1859 if (opt->opt_count_matter)
1860 switch (opt->opt_count_oper)
1862 case '=':
1863 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
1864 fatal(CTXOPTCTEOPT, "");
1865 break;
1867 case '<':
1868 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
1869 fatal(CTXOPTCTLOPT, "");
1870 break;
1872 case '>':
1873 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
1874 fatal(CTXOPTCTGOPT, "");
1875 break;
1878 node = node->next;
1881 /* Check arguments */
1882 /* """"""""""""""" */
1883 node = ctx_inst->opt_inst_list->head;
1884 while (node != NULL)
1886 opt_inst = node->data;
1887 opt = opt_inst->opt;
1889 /* Update current_state */
1890 /* -------------------- */
1891 cur_state->opts_count = opt->opt_count_mark;
1892 cur_state->opt_args_count = opt->opt_args_count_mark;
1894 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
1896 if (opt->opt_args_count_matter)
1897 switch (opt->opt_args_count_oper)
1899 case '=':
1900 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
1901 fatal(CTXOPTCTEARG, "");
1902 break;
1904 case '<':
1905 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
1906 fatal(CTXOPTCTLARG, "");
1907 break;
1909 case '>':
1910 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
1911 fatal(CTXOPTCTGARG, "");
1912 break;
1915 node = node->next;
1919 /* ======================================================================== */
1920 /* Parse a strings describing options and some of their characteristics */
1921 /* The input string must have follow some rules like in the examples below: */
1922 /* */
1923 /* "opt_name1 opt_name2" */
1924 /* "[opt_name1] opt_name2" */
1925 /* "[opt_name1] opt_name2..." */
1926 /* "[opt_name1 #...] opt_name2... [#]" */
1927 /* "[opt_name1 [#...]] opt_name2... [#...]" */
1928 /* */
1929 /* Where [ ] encloses an optional part, # means: has parameters and ... */
1930 /* means that there can be more than one occurrence of the previous thing. */
1931 /* */
1932 /* opt_name can be followed by a 'new context' change prefixed with the */
1933 /* symbol >, as in opt1>c2 by eg */
1934 /* */
1935 /* This function returns as soon as one (or no) option has been parsed and */
1936 /* return the offset to the next option to parse. */
1937 /* */
1938 /* In case of successful parsing, an new option is allocated and its */
1939 /* pointer returned. */
1940 /* ======================================================================== */
1941 static int
1942 opt_parse(char * s, opt_t ** opt)
1944 int opt_optional = 0;
1945 int opt_multiple = 0;
1946 int opt_count_matter = 0;
1947 char opt_count_oper = '\0';
1948 unsigned opt_count_mark = 0;
1949 int opt_args = 0;
1950 char opt_arg[33];
1951 int opt_multiple_args = 0;
1952 int opt_args_count_matter = 0;
1953 char opt_args_count_oper = '\0';
1954 unsigned opt_args_count_mark = 0;
1955 int opt_optional_args = 0;
1956 int opt_eval_first = 0;
1958 int n;
1959 int pos;
1960 int count = 0;
1962 char * s_orig = s;
1964 char * p;
1965 char * opt_name;
1966 char * next_ctx;
1967 char token[65];
1969 *opt = NULL;
1970 memset(opt_arg, '\0', 33);
1972 /* strip the leading blanks */
1973 while (isblank(*s))
1974 s++;
1976 if (*s == '[') /* Start of an optional option */
1978 opt_optional = 1;
1979 s++;
1981 s = strtoken(s, token, sizeof(token), "[^] \n\t.]", &pos);
1982 if (s == NULL)
1983 return -1; /* empty string */
1985 /* Early EOS, only return success if the option is mandatory */
1986 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1987 if (!*s)
1988 if (opt_optional == 1)
1989 return -(s - s_orig - 1);
1991 /* validate the option name */
1992 /* ALPHA+(ALPHANUM|_)* */
1993 /* """""""""""""""""""""""" */
1994 p = token;
1995 if (!isalpha(*p) && *p != '*')
1996 return -(s - s_orig - 1); /* opt_name must start with a letter */
1998 if (*p == '*')
1999 opt_eval_first = 1;
2001 p++;
2002 while (*p)
2004 if (!isalnum(*p) && *p != '_' && *p != '>')
2005 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2006 * a number or a _ */
2007 p++;
2010 if (opt_eval_first)
2011 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token */
2012 else
2013 opt_name = xstrdup(token);
2015 if (*s == ']')
2017 s++;
2018 while (isblank(*s))
2019 s++;
2021 goto success;
2024 /* Check if it can appear multiple times by looking for the dots */
2025 p = strtoken(s, token, 3, "[.]", &pos);
2026 if (p)
2028 if (strcmp(token, "...") == 0)
2030 opt_multiple = 1;
2031 s = p;
2032 if (*s == '<' || *s == '=' || *s == '>')
2034 unsigned value;
2035 int offset;
2037 n = sscanf(s + 1, "%u%n", &value, &offset);
2038 if (n == 1)
2040 opt_count_matter = 1;
2041 opt_count_oper = *s;
2042 opt_count_mark = value;
2044 s += offset + 1;
2047 else
2049 free(opt_name);
2050 return -(s - s_orig - 1);
2054 /* A blank separates the option name and the argument tag */
2055 if (isblank(*s))
2057 char dots[4];
2059 while (isblank(*s))
2060 s++;
2062 if (!*s)
2063 goto success;
2065 pos = 0;
2066 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2067 if (pos > 1 && *opt_arg == '#') /* [# has been read */
2069 opt_args = 1;
2070 opt_optional_args = 1;
2071 if (n == 2)
2072 opt_multiple_args = 1; /* there were dots */
2074 s += pos + !!(n == 2) * 3; /* skip the dots */
2076 if (*s == '<' || *s == '=' || *s == '>')
2078 unsigned value;
2079 int offset;
2081 n = sscanf(s + 1, "%u%n", &value, &offset);
2082 if (n == 1)
2084 opt_args_count_matter = 1;
2085 opt_args_count_oper = *s;
2086 opt_args_count_mark = value;
2088 s += offset + 1;
2091 /* Optional arg tag must end with a ] */
2092 if (*s != ']')
2094 free(opt_name);
2095 return -(s - s_orig - 1);
2098 s++; /* skip the ] */
2100 else
2102 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2103 if (pos > 0 && *opt_arg == '#') /* # has been read */
2105 opt_args = 1;
2106 if (n == 2) /* there were dots */
2107 opt_multiple_args = 1;
2109 s += pos + !!(n == 2) * 3; /* skip the dots */
2111 if (*s == '<' || *s == '=' || *s == '>')
2113 unsigned value;
2114 int offset;
2116 n = sscanf(s + 1, "%u%n", &value, &offset);
2117 if (n == 1)
2119 opt_args_count_matter = 1;
2120 opt_args_count_oper = *s;
2121 opt_args_count_mark = value;
2123 s += offset + 1;
2127 if (*s == ']')
2129 /* Abort on extraneous ] if the option is mandatory */
2130 if (!opt_optional)
2131 return -(s - s_orig - 1);
2133 s++; /* skip the ] */
2135 /* Strip the following blanks */
2136 while (isblank(*s))
2137 s++;
2139 goto success;
2141 else if (opt_optional == 0 && (!*s || isblank(*s)))
2143 /* Strip the following blanks */
2144 while (isblank(*s))
2145 s++;
2147 goto success;
2149 else if (opt_args == 0) /* # was not read it is possibly the start *
2150 * of another option */
2151 goto success;
2152 else
2153 return -(s - s_orig - 1);
2156 success:
2158 /* strip the following blanks */
2159 while (isblank(*s))
2160 s++;
2162 next_ctx = NULL;
2164 if (*opt_name == '>')
2165 fatal_internal("%s: option name is missing.", opt_name);
2167 count = strchrcount(opt_name, '>');
2168 if (count == 1)
2170 char * tmp = strchr(opt_name, '>');
2171 next_ctx = xstrdup(tmp + 1);
2172 *tmp = '\0';
2174 else if (count > 1)
2175 fatal_internal("%s: only one occurrence of '>' is allowed.", opt_name);
2177 *opt = xmalloc(sizeof(opt_t));
2179 (*opt)->name = opt_name;
2180 (*opt)->optional = opt_optional;
2181 (*opt)->multiple = opt_multiple;
2182 (*opt)->opt_count_matter = opt_count_matter;
2183 (*opt)->opt_count_oper = opt_count_oper;
2184 (*opt)->opt_count_mark = opt_count_mark;
2185 (*opt)->args = opt_args;
2186 (*opt)->arg = xstrdup(opt_arg);
2187 (*opt)->optional_args = opt_optional_args;
2188 (*opt)->multiple_args = opt_multiple_args;
2189 (*opt)->opt_args_count_matter = opt_args_count_matter;
2190 (*opt)->opt_args_count_oper = opt_args_count_oper;
2191 (*opt)->opt_args_count_mark = opt_args_count_mark;
2192 (*opt)->eval_first = opt_eval_first;
2193 (*opt)->next_ctx = next_ctx;
2194 (*opt)->ctx_list = ll_new();
2195 (*opt)->constraints_list = ll_new();
2196 (*opt)->action = NULL;
2197 (*opt)->data = NULL;
2199 return s - s_orig;
2202 /* ===================================================================== */
2203 /* Try to initialize all the option in a given string */
2204 /* Each parsed option are put in a bst tree with its name as index */
2205 /* */
2206 /* On collision, the arguments only the signature are required to be */
2207 /* the same else this is considered as an error. Options can be used in */
2208 /* more than one context and can be optional in one and mandatory in */
2209 /* another */
2210 /* ===================================================================== */
2211 static int
2212 init_opts(char * spec, ctx_t * ctx)
2214 opt_t * opt, *bst_opt;
2215 bst_t * node;
2216 int offset;
2218 while (*spec)
2220 if ((offset = opt_parse(spec, &opt)) > 0)
2222 spec += offset;
2224 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2226 int same_next_ctx = 0;
2228 bst_opt = node->key; /* node extracted from the BST */
2230 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2231 same_next_ctx = 1;
2232 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2233 same_next_ctx = 0;
2234 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2235 same_next_ctx = 0;
2236 else
2237 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2239 if (bst_opt->optional_args != opt->optional_args
2240 || bst_opt->multiple_args != opt->multiple_args
2241 || bst_opt->args != opt->args || !same_next_ctx)
2243 fatal_internal("option %s already exists with "
2244 "a different arguments signature.\n",
2245 opt->name);
2247 free(opt->name);
2248 free(opt);
2250 return 0;
2252 /* The new occurrence of the option option is legal */
2253 /* append the current context ptr in the list */
2254 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2255 ll_append(bst_opt->ctx_list, ctx);
2257 /* Append the new option to the context's options list */
2258 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2259 ll_append(ctx->opt_list, bst_opt);
2261 else
2263 opt->params = NULL;
2265 /* Initialize the option's context list with the current context */
2266 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2267 ll_append(opt->ctx_list, ctx);
2269 /* Append the new option to the context's options list */
2270 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2271 ll_append(ctx->opt_list, opt);
2273 /* Insert the new option in the BST */
2274 /* """""""""""""""""""""""""""""""" */
2275 bst_search(opt, &options_bst, opt_compare);
2278 else
2280 char * s = xstrndup(spec, -offset);
2281 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2282 free(s);
2284 exit(EXIT_FAILURE);
2288 return 1;
2291 /* ==================================================== */
2292 /* ctxopt initialization function, must be called first */
2293 /* ==================================================== */
2294 void
2295 ctxopt_init(char * prog_name)
2297 int n;
2299 contexts_bst = NULL;
2300 options_bst = NULL;
2301 char * ptr;
2303 user_rc = 0;
2304 user_value = 0;
2305 user_string = xmalloc(8);
2306 user_string2 = xmalloc(8);
2307 user_object = NULL;
2309 ctxopt_initialized = 1;
2311 /* Update current_state */
2312 /* -------------------- */
2313 cur_state = xcalloc(sizeof(state_t), 0);
2315 /* Initialize custom error function pointers to NULL */
2316 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2317 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2318 for (n = 0; n < CTXOPTERRSIZ; n++)
2319 err_functions[n] = NULL;
2321 /* Update current_state */
2322 /* -------------------- */
2323 if (prog_name)
2325 if (*prog_name == '\0')
2326 cur_state->prog_name = xstrdup("program_name");
2327 else if ((ptr = strrchr(prog_name, '/')))
2328 cur_state->prog_name = xstrdup(ptr + 1);
2329 else
2330 cur_state->prog_name = xstrdup(prog_name);
2332 else
2333 cur_state->prog_name = xstrdup("program_name");
2336 /* ========================================================================= */
2337 /* Utility function which create and register a par_t object in a bst */
2338 /* embedded in a context. */
2339 /* This object will have a name and a pointer to the option it refers to. */
2340 /* These object will be used to quickly find an option from a command */
2341 /* line parameter during the analysis phase. */
2342 /* */
2343 /* IN : an option name. */
2344 /* IN : a string of command line parameters to associate to the option. */
2345 /* Returns : 1 is all was fine else 0. */
2346 /* ========================================================================= */
2347 static int
2348 opt_set_parms(char * opt_name, char * par_str)
2350 char * par_name, *ctx_name;
2351 char * tmp_par_str, *end_tmp_par_str;
2352 ctx_t * ctx;
2353 opt_t * opt;
2354 bst_t * node;
2355 par_t * par, tmp_par;
2356 int rc = 1; /* return code */
2358 ll_t * list;
2359 ll_node_t * lnode;
2361 /* Look is the given option is defined */
2362 /* """"""""""""""""""""""""""""""""""" */
2363 opt = locate_opt(opt_name);
2364 if (opt == NULL)
2365 fatal_internal("Unknown option %s", opt_name);
2367 /* For each context using this option */
2368 /* """""""""""""""""""""""""""""""""" */
2369 list = opt->ctx_list;
2371 lnode = list->head;
2372 while (lnode != NULL)
2374 /* Locate the context in the contexts tree */
2375 /* """"""""""""""""""""""""""""""""""""""" */
2376 ctx_name = ((ctx_t *)(lnode->data))->name;
2378 ctx = locate_ctx(ctx_name);
2379 if (ctx == NULL)
2380 fatal_internal("Unknown context %s", ctx_name);
2381 else
2383 void * par_bst = ctx->par_bst;
2385 tmp_par_str = xstrdup(par_str);
2386 ltrim(tmp_par_str, " \t");
2387 rtrim(tmp_par_str, " \t", 0);
2388 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2389 if (par_name == NULL)
2390 fatal_internal("Parameters are missing for option %s", opt_name);
2392 /* For each parameter given in par_str, create an par_t object and */
2393 /* insert it the in the parameters bst of the context. */
2394 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2395 while (par_name != NULL)
2397 tmp_par.name = par_name;
2399 node = bst_find(&tmp_par, &par_bst, par_compare);
2400 if (node != NULL)
2402 fatal_internal("The parameter %s is already defined in context %s",
2403 par_name, ctx->name);
2404 rc = 0;
2406 else
2408 par = xmalloc(sizeof(par_t));
2409 par->name = xstrdup(par_name);
2410 par->opt = opt; /* Link the option to this parameter */
2412 bst_search(par, &par_bst, par_compare);
2414 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2417 /* Update the value of the root of ctx->par_bst as it may have */
2418 /* been modified */
2419 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2420 ctx->par_bst = par_bst;
2422 free(tmp_par_str);
2424 lnode = lnode->next;
2427 return rc;
2430 /* ==================================================================== */
2431 /* Creation of a new context instance */
2432 /* IN ctx : a context pointer to allow this instance to */
2433 /* access the context fields */
2434 /* IN prev_ctx_inst : the context instance whose option leading to the */
2435 /* creation of this new context instance is part of */
2436 /* Returns : the new context. */
2437 /* ==================================================================== */
2438 static ctx_inst_t *
2439 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
2441 opt_t * opt;
2442 opt_inst_t * gen_opt_inst;
2443 ctx_inst_t * ctx_inst;
2444 seen_opt_t * seen_opt;
2445 char * str, *opt_name;
2446 void * bst;
2447 bst_t * bst_node;
2449 /* Keep a trace of the opt_inst which was at the origin of the creation */
2450 /* of this context instance. */
2451 /* This will serve during the evaluation of the option callbacks */
2452 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2453 if (prev_ctx_inst != NULL)
2455 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
2456 /* Update current_state */
2457 /* -------------------- */
2458 cur_state->opt_name = gen_opt_inst->opt->name;
2460 else
2461 gen_opt_inst = NULL;
2463 /* Create and initialize the new context instance */
2464 /* """""""""""""""""""""""""""""""""""""""""""""" */
2465 ctx_inst = xmalloc(sizeof(ctx_inst_t));
2466 ctx_inst->ctx = ctx;
2467 ctx_inst->prev_ctx_inst = prev_ctx_inst;
2468 ctx_inst->gen_opt_inst = gen_opt_inst;
2469 ctx_inst->incomp_bst_list = ll_new();
2470 ctx_inst->opt_inst_list = ll_new();
2471 ctx_inst->seen_opt_bst = NULL;
2473 ll_node_t * node;
2475 if (prev_ctx_inst == NULL)
2476 first_ctx_inst = ctx_inst;
2478 /* Initialize the occurrence counters of each opt allowed in the context */
2479 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2480 node = ctx->opt_list->head;
2481 while (node != NULL)
2483 opt = node->data;
2484 opt->occurrences = 0;
2486 node = node->next;
2489 /* Initialize the bst containing the seen indicator for all the options */
2490 /* allowed in this context instance. */
2491 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2492 node = ctx->opt_list->head;
2493 while (node != NULL)
2495 opt = node->data;
2496 seen_opt = xmalloc(sizeof(seen_opt_t));
2497 seen_opt->opt = opt;
2498 seen_opt->seen = 0;
2500 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
2502 node = node->next;
2505 /* Initialize the bst containing the incompatibles options */
2506 /* Incompatibles option names are read from strings found in the list */
2507 /* incomp_list present in each instance of ctx_t. */
2508 /* These names are then used to search for the object of type seen_opt_t */
2509 /* which is already present in the seen_opt_bst of the context instance. */
2510 /* in the bst. */
2511 /* Once found the seen_opt_t object in inserted in the new bst */
2512 /* At the end the new bst in addes to the list incomp_bst_list. */
2513 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2514 node = ctx->incomp_list->head;
2515 while (node != NULL)
2517 bst = NULL;
2518 seen_opt_t tmp_seen_opt;
2520 str = xstrdup(node->data);
2521 ltrim(str, " \t");
2522 rtrim(str, " \t", 0);
2523 opt_name = strtok(str, " \t"); /* extract the first option name */
2525 while (opt_name != NULL) /* for each option name */
2527 if ((opt = locate_opt(opt_name)) != NULL)
2529 /* The option found is searched in the tree of potential */
2530 /* seen options. */
2531 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2532 tmp_seen_opt.opt = opt;
2534 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2535 seen_opt_compare);
2537 if (bst_node != NULL)
2539 /* if found it, is added into the new bst tree */
2540 /* """"""""""""""""""""""""""""""""""""""""""" */
2541 seen_opt = bst_node->key;
2542 bst_search(seen_opt, &bst, seen_opt_compare);
2544 else
2545 /* Not found! That means that the option is unknown in this */
2546 /* context as all options has have a seen_opt structure in */
2547 /* seen_opt_bst */
2548 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2549 fatal_internal("%s is not known in the context %s", opt->name,
2550 ctx->name);
2552 else
2553 fatal_internal("%s: unknown option.", opt_name);
2555 opt_name = strtok(NULL, " \t");
2558 free(str);
2559 ll_append(ctx_inst->incomp_bst_list, bst);
2561 node = node->next;
2564 return ctx_inst;
2567 /* ======================================================================== */
2568 /* Create a list formed by all the significant command line words */
2569 /* Words beginning or ending with ^,{ or } are split. Each of these */
2570 /* symbols will get their own place in the list */
2571 /* */
2572 /* - ^ forces the next option to be evaluated in the first context */
2573 /* - the {...} part delimits a context, the { will not appear in the list */
2574 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2575 /* to facilitate the parsing phase. | must not be used by the end user */
2576 /* */
2577 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2578 /* program name is not considered */
2579 /* IN words : is the array of strings constituting the command line to */
2580 /* parse. */
2581 /* Returns : 1 on success, 0 if a { or } is missing. */
2582 /* ======================================================================== */
2583 static int
2584 ctxopt_build_cmdline_list(int nb_words, char ** words)
2586 int i;
2587 char * prev_word = NULL;
2588 char * word;
2589 char * ptr;
2590 int level = 0;
2591 ll_node_t *node, *start_node;
2593 /* The analysis is divided into three passes, this is not optimal but */
2594 /* must be done only one time. Doing that we privilege readability. */
2595 /* */
2596 /* In the following, SG is the ascii character 1d (dec 29) */
2597 /* */
2598 /* The first pass creates the list, extract the leading an trailing */
2599 /* SG '{' and '}' of each word and give them their own place in the */
2600 /* list */
2601 /* */
2602 /* The second pass transform the '{...}' blocks by a trailing SG */
2603 /* ({...} -> ...|) */
2604 /* */
2605 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2606 /* the middle in the remaining list elements and recreate the pseudo */
2607 /* argument: {} */
2608 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2610 /* If the option list is not empty, clear it before going further */
2611 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2613 if (cmdline_list != NULL)
2615 node = cmdline_list->head;
2616 while (node != NULL)
2618 word = node->data;
2619 ll_delete(cmdline_list, node);
2620 free(word);
2621 free(node);
2622 node = cmdline_list->head;
2625 else
2626 cmdline_list = ll_new();
2628 start_node = cmdline_list->head; /* in the following loop start_node will *
2629 * contain a pointer to the current *
2630 * word stripped from its leading *
2631 * sequence of {, } or ^ */
2632 for (i = 0; i < nb_words; i++)
2634 size_t len = strlen(words[i]);
2635 size_t start, end;
2636 char * str;
2638 str = words[i];
2640 /* Replace each occurrence of the legal word {} by the characters */
2641 /* 0x02 and 0x03 to hide them from the following process. */
2642 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2643 while ((ptr = strstr(str, "{}")) != NULL)
2645 *ptr = 0x02; /* arbitrary values unlikely */
2646 *(ptr + 1) = 0x03; /* present in a word */
2649 if (len > 1) /* The word contains at least 2 characters */
2651 start = 0;
2653 /* Interpret its beginning and look for the start of the real word */
2654 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2655 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
2657 ll_append(cmdline_list, xstrndup(str + start, 1));
2658 start++;
2659 start_node = cmdline_list->tail;
2662 end = len - 1;
2663 if (str[end] == '{' || str[end] == '}')
2665 if (end > 0 && str[end - 1] != '\\')
2667 ll_append(cmdline_list, xstrndup(str + end, 1));
2668 end--;
2669 node = cmdline_list->tail;
2671 while (str[end] == '{' || str[end] == '}')
2673 if (end > start && str[end - 1] == '\\')
2674 break;
2676 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
2677 end--;
2678 node = node->prev;
2683 if (start <= end)
2685 if (start_node != NULL)
2686 ll_insert_after(cmdline_list, start_node,
2687 xstrndup(str + start, end - start + 1));
2688 else
2689 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
2690 start_node = cmdline_list->tail;
2693 else if (len == 1)
2695 ll_append(cmdline_list, xstrdup(str));
2696 start_node = cmdline_list->tail;
2700 /* 2nd pass */
2701 /* """""""" */
2702 node = cmdline_list->head;
2704 level = 0;
2705 while (node != NULL)
2707 word = node->data;
2709 if (strcmp(word, "{") == 0)
2711 ll_node_t * old_node = node;
2712 level++;
2713 node = node->next;
2714 ll_delete(cmdline_list, old_node);
2715 free(word);
2716 free(old_node);
2718 else if (strcmp(word, "}") == 0)
2720 level--;
2722 if (level < 0)
2723 return 0;
2724 else
2725 *word = 0x1d;
2727 else
2728 node = node->next;
2731 if (level != 0)
2732 return 0;
2734 /* 3rd pass */
2735 /* """""""" */
2736 node = cmdline_list->head;
2738 while (node != NULL)
2740 word = node->data;
2742 /* Restore the original { and } characters forming the legal word {} */
2743 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2744 while ((ptr = strchr(word, 0x02)) != NULL)
2745 *ptr = '{';
2746 while ((ptr = strchr(word, 0x03)) != NULL)
2747 *ptr = '}';
2749 /* Remove a SG if the previous element is SG */
2750 /* """"""""""""""""""""""""""""""""""""""""" */
2751 if (strcmp(word, "\x1d") == 0)
2753 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
2755 ll_node_t * old_node = node;
2756 node = node->prev;
2757 ll_delete(cmdline_list, old_node);
2758 free(old_node->data);
2759 free(old_node);
2762 else if (strcmp(word, "-") == 0) /* a single - is a legal argument, not *
2763 * a parameter. Protect it */
2765 free(node->data);
2766 node->data = xstrdup("\\-");
2769 prev_word = node->data;
2770 node = node->next;
2773 /* Clean useless and SG at the beginning and end of list */
2774 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2775 node = cmdline_list->head;
2777 if (node == NULL)
2778 return 1;
2780 word = node->data;
2782 if (strcmp(word, "\x1d") == 0)
2784 ll_delete(cmdline_list, node);
2785 free(word);
2786 free(node);
2789 node = cmdline_list->tail;
2790 if (node == NULL)
2791 return 1;
2793 word = node->data;
2795 if (strcmp(word, "\x1d") == 0)
2797 ll_delete(cmdline_list, node);
2798 free(word);
2799 free(node);
2802 return 1;
2805 /* ===================================================================== */
2806 /* Build and analyze the command line list */
2807 /* function and create the linked data structures whose data will be */
2808 /* evaluated later by ctxopt_evaluate. */
2809 /* This function identifies the following errors and creates an array of */
2810 /* The remaining unanalyzed arguments. */
2811 /* - detect missing arguments */
2812 /* - detect too many arguments */
2813 /* - detect unknown parameters in a context */
2814 /* - detect too many occurrences of a parameters in a context */
2815 /* - detect missing required arguments in a context */
2816 /* */
2817 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2818 /* program name is not considered */
2819 /* IN words : is the array of strings constituting the command line to */
2820 /* parse. */
2821 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
2822 /* is present in the list. */
2823 /* OUT rem_args : array of remaining command line arguments if a -- */
2824 /* is present in the list. This array must be free by */
2825 /* The caller as it is allocated here. */
2826 /* ===================================================================== */
2827 void
2828 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
2829 char *** rem_args)
2831 ctx_t * ctx;
2832 opt_t * opt;
2833 par_t * par;
2834 ctx_inst_t * ctx_inst;
2835 opt_inst_t * opt_inst;
2836 int expect_par = 0;
2837 int expect_arg = 0;
2838 int expect_par_or_arg = 0;
2840 ll_node_t * cli_node;
2841 bst_t * bst_node;
2842 seen_opt_t * bst_seen_opt;
2843 char * par_name;
2844 void * bst;
2846 ll_node_t * node;
2848 if (!ctxopt_build_cmdline_list(nb_words, words))
2849 fatal_internal(
2850 "The command line could not be parsed: missing { or } detected.");
2852 if (main_ctx == NULL)
2853 fatal_internal("At least one context must have been created.");
2855 /* Check that all options has an action and at least one parameter */
2856 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2857 bst_walk(options_bst, bst_check_opt_cb);
2859 /* Create the first ctx_inst record */
2860 /* """""""""""""""""""""""""""""""" */
2861 ctx = main_ctx;
2863 ctx_inst_list = ll_new();
2864 ctx_inst = new_ctx_inst(ctx, NULL);
2865 ctx_inst->par_name = NULL;
2867 /* Update current_state */
2868 /* -------------------- */
2869 cur_state->ctx_name = ctx->name;
2871 ll_append(ctx_inst_list, ctx_inst);
2873 /* For each node in the command line */
2874 /* """"""""""""""""""""""""""""""""" */
2875 cli_node = cmdline_list->head;
2876 expect_par = 1;
2877 while (cli_node != NULL)
2879 if (strcmp(cli_node->data, "--") == 0)
2880 break; /* No new parameter will be analyze after this point */
2882 par_name = cli_node->data;
2884 if (strcmp(par_name, "\x1d") == 0)
2886 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
2887 check_for_occurrences_issues(ctx_inst);
2889 /* Forced backtracking to the previous context instance */
2890 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2891 if (ctx_inst->prev_ctx_inst != NULL)
2893 ctx_inst = ctx_inst->prev_ctx_inst;
2894 ctx = ctx_inst->ctx;
2896 /* Update current_state */
2897 /* -------------------- */
2898 cur_state->ctx_name = ctx->name;
2899 cur_state->ctx_par_name = ctx_inst->par_name;
2901 else
2903 /* Update current_state */
2904 /* -------------------- */
2905 cur_state->ctx_par_name = NULL;
2908 else if (expect_par && *par_name == '-')
2910 int pos = 0;
2911 char * prefix;
2913 /* Update current_state */
2914 /* -------------------- */
2915 cur_state->cur_opt_par_name = par_name;
2916 cur_state->ctx_name = ctx->name;
2917 cur_state->ctx_par_name = ctx_inst->par_name;
2919 /* An expected parameter has been seen */
2920 /* """"""""""""""""""""""""""""""""""" */
2921 if ((par = locate_par(par_name, ctx)) == NULL)
2923 opt_t * popt;
2924 char * word;
2926 /* See if this parameter is an unique abbreviation of a longer */
2927 /* parameter. If this is the case then just replace it with its */
2928 /* full length version and try again. */
2929 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2930 if ((word = abbrev_expand(par_name, ctx)) != NULL)
2932 cli_node->data = word;
2933 continue;
2936 /* Try to find a prefix which is a valid parameter in this context */
2937 /* If found, split the cli_node in two to build a new parameter */
2938 /* node and followed by a node containing the remaining string */
2939 /* If the new parameter corresponds to an option not taking */
2940 /* argument then prefix the remaining string whit a dash as it may */
2941 /* contain a new parameter. */
2942 /* The new parameter will be re-evaluated in the next iteration of */
2943 /* the loop. */
2944 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2945 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
2946 if (prefix != NULL && pos != 0)
2948 cli_node->data = prefix; /* prefix contains le name of a valid *
2949 | parameter in this context. */
2951 if (popt->args)
2953 /* The parameter may be followed by arguments */
2954 /* '''''''''''''''''''''''''''''''''''''''''' */
2955 if (*(par_name + pos) == '-')
2957 word = xstrdup("\\"); /* Protect the '-' */
2958 word = strappend(word, par_name + pos, NULL);
2960 else
2961 word = xstrdup(par_name + pos);
2963 else
2965 /* The parameter does not take arguments, the */
2966 /* following word must be a parameter or nothing */
2967 /* hence prefix it with a dash. */
2968 /* ''''''''''''''''''''''''''''''''''''''''''''' */
2969 word = xstrdup("-");
2970 word = strappend(word, par_name + pos, NULL);
2973 /* Insert it after the current node in the list */
2974 /* """""""""""""""""""""""""""""""""""""""""""" */
2975 ll_insert_after(cmdline_list, cli_node, word);
2977 continue; /* loop */
2979 else
2981 check_for_missing_mandatory_opt(ctx_inst, par_name);
2982 check_for_occurrences_issues(ctx_inst);
2984 if (ctx_inst->prev_ctx_inst == NULL)
2986 char * errmsg = xstrdup("");
2988 /* Update current_state */
2989 /* -------------------- */
2990 cur_state->ctx_par_name = NULL;
2992 *user_string = '\0';
2993 *user_string2 = '\0';
2995 user_string = strappend(user_string, par_name, NULL);
2997 bst_walk(contexts_bst, bst_match_par_cb);
2999 if (*user_string2 != '\0')
3001 errmsg = strappend(
3002 errmsg,
3003 "\nIt appears to be defined in the context(s):", user_string2,
3004 "\n", NULL);
3007 fatal(CTXOPTUNKPAR, errmsg);
3009 else
3011 /* Try to backtrack and analyse the same parameter in the */
3012 /* previous context. */
3013 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3014 ctx_inst = ctx_inst->prev_ctx_inst;
3015 ctx = ctx_inst->ctx;
3017 /* Update current_state */
3018 /* -------------------- */
3019 cur_state->ctx_name = ctx->name;
3020 cur_state->ctx_par_name = ctx_inst->par_name;
3022 cli_node = cli_node->prev;
3026 else
3028 seen_opt_t seen_opt;
3030 /* The parameter is legal in the context, create a opt_inst and */
3031 /* append it to the ctx_inst list options list */
3032 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3033 opt = par->opt;
3035 opt->occurrences++;
3037 opt_inst = xmalloc(sizeof(opt_inst_t));
3038 opt_inst->opt = opt;
3039 opt_inst->par = par_name;
3040 opt_inst->values_list = ll_new();
3041 opt_inst->next_ctx_inst = NULL;
3043 /* Priority option inserted at the start of the opt_inst list */
3044 /* but their order of appearance in the context definition must */
3045 /* be preserver so each new priority option will be placed after */
3046 /* the previous ones at the start of the opt_inst list. */
3047 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3048 if (!opt->eval_first)
3049 ll_append(ctx_inst->opt_inst_list, opt_inst);
3050 else
3052 ll_node_t * node = ctx_inst->opt_inst_list->head;
3053 opt_inst_t * tmp_opt_inst;
3054 while (node != NULL)
3056 tmp_opt_inst = node->data;
3057 if (!tmp_opt_inst->opt->eval_first)
3059 ll_insert_before(ctx_inst->opt_inst_list, node, opt_inst);
3060 break;
3062 else
3063 node = node->next;
3065 if (node == NULL)
3066 ll_append(ctx_inst->opt_inst_list, opt_inst);
3069 /* Check if an option was already seen in the */
3070 /* current context instance */
3071 /* """""""""""""""""""""""""""""""""""""""""" */
3072 seen_opt.opt = opt;
3074 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3075 seen_opt_compare);
3077 /* bst_node cannot be NULL here */
3079 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3081 if (!opt->multiple && bst_seen_opt->seen == 1)
3082 fatal(CTXOPTDUPOPT, NULL);
3084 /* Check if this option is compatible with the options already */
3085 /* seen in this context instance. */
3086 /* Look if the option is present in one on the bst present in */
3087 /* the incomp_bst_list of the context instance */
3088 /* instance, */
3089 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3090 node = ctx_inst->incomp_bst_list->head;
3091 while (node != NULL)
3093 bst = node->data;
3094 user_object = NULL;
3096 /* They can only have one seen_opt object in the bst tree was */
3097 /* already seen, try to locate it, the result will be put in */
3098 /* user_object by the bst_seen_opt_seen_cb function */
3099 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3100 bst_walk(bst, bst_seen_opt_seen_cb);
3102 /* It it is the case, look if the current option is also */
3103 /* in this bst. */
3104 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3105 if (user_object != NULL)
3107 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3109 if (bst_node != NULL)
3111 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3112 if (bst_seen_opt->seen == 0)
3113 fatal(CTXOPTINCOPT, (char *)user_object);
3117 node = node->next;
3120 /* Mark this option seen in the current context instance */
3121 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3122 bst_seen_opt->seen = 1;
3123 bst_seen_opt->par = xstrdup(par_name);
3125 /* If this option leads to a next context, create a new ctx_inst */
3126 /* and switch to it for the analyse of the future parameter */
3127 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3128 if (opt->next_ctx != NULL)
3130 ctx = locate_ctx(opt->next_ctx);
3132 if (ctx == NULL)
3133 fatal_internal("%s: unknown context.", opt->next_ctx);
3135 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3136 ctx_inst->par_name = xstrdup(par_name);
3138 ll_append(ctx_inst_list, ctx_inst);
3141 /* See is we must expect some arguments */
3142 /* """""""""""""""""""""""""""""""""""" */
3143 expect_par_or_arg = 0;
3144 expect_par = 0;
3145 expect_arg = 0;
3147 if (!opt->args)
3148 expect_par = 1; /* Parameter doesn't accept any argument */
3149 else
3151 if (!opt->optional_args)
3152 expect_arg = 1; /* Parameter has mandatory arguments */
3153 else
3154 expect_par_or_arg = 1; /* Parameter has optional arguments */
3158 else if (expect_par && *par_name != '-')
3160 ll_node_t * n = cli_node->next;
3162 /* Look if potential arguments must still be analyzed until the */
3163 /* end of the context/command line part to analyze/command line. */
3164 /* If this is the case we have met an extra argument. */
3165 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3166 while (n != NULL)
3168 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3169 fatal(CTXOPTUNXARG, NULL);
3171 if (*(char *)(n->data) == '-')
3172 fatal(CTXOPTUNXARG, NULL);
3174 n = n->next;
3177 break; /* an unexpected non parameter was seen, if no Potential *
3178 | arguments remain in the command line assume that it *
3179 | is is the first of the non arguments and stop the *
3180 | command line analysis. */
3182 else if (expect_arg && *par_name != '-')
3184 ll_node_t * cstr_node;
3185 constraint_t * cstr;
3187 /* Check if the arguments of the option respects */
3188 /* the attached constraints if any */
3189 /* """"""""""""""""""""""""""""""""""""""""""""" */
3190 cstr_node = opt->constraints_list->head;
3191 while (cstr_node != NULL)
3193 cstr = cstr_node->data;
3194 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name))
3195 exit(EXIT_FAILURE);
3197 cstr_node = cstr_node->next;
3200 /* If the argument is valid, store it */
3201 /* """""""""""""""""""""""""""""""""" */
3202 if (*par_name == '\\' && *(par_name + 1) != '\0'
3203 && *(par_name + 1) == '-')
3204 ll_append(opt_inst->values_list, par_name + 1);
3205 else
3206 ll_append(opt_inst->values_list, par_name);
3208 expect_arg = 0;
3209 expect_par = 0;
3210 expect_par_or_arg = 0;
3212 if (opt->multiple_args)
3213 expect_par_or_arg = 1;
3214 else
3215 expect_par = 1; /* Parameter takes only one argument */
3217 else if (expect_arg && *par_name == '-')
3218 fatal(CTXOPTMISARG, NULL);
3219 else if (expect_par_or_arg)
3221 expect_arg = 0;
3222 expect_par = 0;
3223 expect_par_or_arg = 0;
3225 if (*par_name != '-')
3226 expect_arg = 1; /* Consider this word as an argument and retry */
3227 else
3228 expect_par = 1; /* Consider this word as a parameter and retry */
3230 cli_node = cli_node->prev;
3233 cli_node = cli_node->next;
3236 if (cmdline_list->len > 0 && *par_name == '-')
3238 if (expect_arg && !opt->optional_args)
3239 fatal(CTXOPTMISARG, NULL);
3242 /* Look if a context_instance has unseen mandatory options */
3243 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3244 node = ctx_inst_list->head;
3245 while (node != NULL)
3247 ctx_inst = (ctx_inst_t *)(node->data);
3249 /* Update current_state */
3250 /* -------------------- */
3251 cur_state->ctx_name = ctx_inst->ctx->name;
3252 cur_state->ctx_par_name = ctx_inst->par_name;
3254 check_for_missing_mandatory_opt(ctx_inst, par_name);
3255 check_for_occurrences_issues(ctx_inst);
3257 node = node->next;
3260 /* Allocate the array containing the remaining not analyzed */
3261 /* command line arguments. */
3262 /* NOTE: The strings in the array are just pointer to the */
3263 /* data of the generating list and must not be freed. */
3264 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3265 if (cli_node != NULL)
3267 if (strcmp((char *)cli_node->data, "--") == 0)
3268 /* The special parameter -- was encountered, the -- argument is not */
3269 /* put in the remaining arguments. */
3270 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3271 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3272 else
3273 /* A non parameter was encountered when a parameter was expected. We */
3274 /* assume that the evaluation of the remaining command line argument */
3275 /* are not the responsibility of the users code. */
3276 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3277 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3279 else
3281 *nb_rem_args = 0;
3282 *rem_args = xmalloc(sizeof(char *));
3283 (*rem_args)[0] = NULL;
3287 /* ===================================================================== */
3288 /* Parses the options data structures and launches the callback function */
3289 /* attached to each options instances. */
3290 /* This calls a recursive function which proceeds context per context. */
3291 /* ===================================================================== */
3292 void
3293 ctxopt_evaluate(void)
3295 evaluate_ctx_inst(first_ctx_inst);
3298 /* =================================================================== */
3299 /* Recursive function called by ctxopt_evaluate to process the list of */
3300 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3301 /* action attached to the context and its option instances */
3302 /* =================================================================== */
3303 static void
3304 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
3306 opt_inst_t * opt_inst;
3307 ctx_t * ctx;
3308 opt_t * opt;
3309 ll_node_t * opt_inst_node;
3310 char ** args;
3311 int nb_args;
3313 if (ctx_inst == NULL)
3314 return;
3316 ctx = ctx_inst->ctx;
3318 /* Do not evaluate the action attached to this context is there is no */
3319 /* option to evaluate. */
3320 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3321 opt_inst_node = ctx_inst->opt_inst_list->head;
3322 if (opt_inst_node == NULL)
3323 return;
3325 /* Call the entering action attached to this context if any */
3326 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3327 if (ctx->action != NULL)
3329 if (ctx_inst->prev_ctx_inst != NULL)
3330 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
3331 ctx->nb_data, ctx->data);
3332 else
3333 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
3336 /* For each instance of options */
3337 /* """""""""""""""""""""""""""" */
3338 while (opt_inst_node != NULL)
3340 opt_inst = (opt_inst_t *)(opt_inst_node->data);
3341 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
3342 &args);
3343 opt = opt_inst->opt;
3345 /* Launch the attached action if any */
3346 /* """"""""""""""""""""""""""""""""" */
3347 if (opt->action != NULL)
3348 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
3349 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
3351 if (opt_inst->next_ctx_inst != NULL)
3352 evaluate_ctx_inst(opt_inst->next_ctx_inst);
3354 if (args != NULL)
3355 free(args);
3357 opt_inst_node = opt_inst_node->next;
3360 /* Call the exiting action attached to this context if any */
3361 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
3362 if (ctx->action != NULL)
3364 if (ctx_inst->prev_ctx_inst != NULL)
3365 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
3366 ctx->nb_data, ctx->data);
3367 else
3368 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
3372 /* =========================================================== */
3373 /* Create and initialize a new context */
3374 /* - allocate space */
3375 /* - name it */
3376 /* - initialize its option with a few of their characteristics */
3377 /* =========================================================== */
3378 void
3379 ctxopt_new_ctx(char * name, char * opts_specs)
3381 ctx_t * ctx;
3382 bst_t * node;
3383 char * p;
3385 if (!ctxopt_initialized)
3386 fatal_internal("Please call ctxopt_init first.");
3388 ctx = xmalloc(sizeof(ctx_t));
3390 /* validate the context name */
3391 /* ALPHA+(ALPHANUM|_)* */
3392 /* """"""""""""""""""""""""" */
3393 p = name;
3394 if (!isalpha(*p))
3395 fatal_internal("%s: a context name must start with a letter.", name);
3397 p++;
3398 while (*p)
3400 if (!isalnum(*p) && *p != '_')
3401 fatal_internal("%s: a context name must only contain letters, "
3402 "numbers or '_'.",
3403 name);
3404 p++;
3407 ctx->name = xstrdup(name);
3408 ctx->opt_list = ll_new(); /* list of options legit in this context */
3409 ctx->incomp_list = ll_new(); /* list of incompatible options strings */
3410 ctx->par_bst = NULL;
3411 ctx->data = NULL;
3412 ctx->action = NULL;
3414 /* The first created context is the main one */
3415 /* """"""""""""""""""""""""""""""""""""""""" */
3416 if (contexts_bst == NULL)
3418 main_ctx = ctx;
3420 cur_state->ctx_name = ctx->name;
3423 if (init_opts(opts_specs, ctx) == 0)
3424 exit(EXIT_FAILURE);
3425 if ((node = bst_find(ctx, &contexts_bst, ctx_compare)) != NULL)
3426 fatal_internal("The context %s already exists", name);
3427 else
3428 bst_search(ctx, &contexts_bst, ctx_compare);
3431 /* ==================================================== */
3432 /* Display a usage screen limited to a specific context */
3433 /* IN: the context name. */
3434 /* IN: what to do after (continue or exit the program) */
3435 /* possible values: continue_after, exit_after. */
3436 /* ==================================================== */
3437 void
3438 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
3440 ctx_t * ctx;
3441 ll_t * list;
3443 int has_optional = 0;
3444 int has_ellipsis = 0;
3445 int has_rule = 0;
3446 int has_generic_arg = 0;
3447 int has_ctx_change = 0;
3448 int has_early_eval = 0;
3450 ctx = locate_ctx(ctx_name);
3451 if (ctx == NULL)
3452 fatal_internal("%s: unknown context.", ctx_name);
3454 if (cur_state->ctx_par_name == NULL)
3455 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
3456 else
3457 printf("\nSynopsis for the context introduced by %s:\n",
3458 cur_state->ctx_par_name);
3460 list = ctx->opt_list;
3461 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3462 &has_ctx_change, &has_early_eval);
3464 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3465 has_optional, has_ellipsis, has_rule);
3467 if (action == exit_after)
3468 exit(EXIT_FAILURE);
3471 /* ==================================================== */
3472 /* Display a full usage screen about all contexts. */
3473 /* IN: what to do after (continue or exit the program) */
3474 /* possible values: continue_after, exit_after. */
3475 /* ==================================================== */
3476 void
3477 ctxopt_disp_usage(usage_behaviour action)
3479 ll_t * list;
3480 int has_optional = 0;
3481 int has_ellipsis = 0;
3482 int has_rule = 0;
3483 int has_generic_arg = 0;
3484 int has_ctx_change = 0;
3485 int has_early_eval = 0;
3487 if (main_ctx == NULL)
3488 fatal_internal("At least one context must have been created.");
3490 /* Usage for the first context */
3491 /* """"""""""""""""""""""""""" */
3492 printf("\nAllowed options in the default context:\n");
3493 list = main_ctx->opt_list;
3494 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3495 &has_ctx_change, &has_early_eval);
3497 /* Usage for the other contexts */
3498 /* """""""""""""""""""""""""""" */
3499 bst_walk(contexts_bst, bst_print_ctx_cb);
3501 /* Contextual syntactic explanations */
3502 /* """"""""""""""""""""""""""""""""" */
3503 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3504 has_optional, has_ellipsis, has_rule);
3506 if (action == exit_after)
3507 exit(EXIT_FAILURE);
3510 /* ************************************** */
3511 /* Built-in constraint checking functions */
3512 /* ************************************** */
3514 /* ================================================================ */
3515 /* This constraint checks if each arguments respects a format as */
3516 /* defined for the scanf function. */
3517 /* return 1 if yes and 0 if no */
3518 /* ================================================================ */
3520 ctxopt_format_constraint(int nb_args, char ** args, char * value)
3522 int rc = 0;
3524 char x[256];
3525 char y;
3526 char * format;
3528 if (nb_args != 1)
3529 fatal_internal("Format constraint: invalid number of parameters.");
3531 if (strlen(value) > 255)
3532 value[255] = '\0';
3534 format = xstrdup(args[0]);
3535 format = strappend(format, "%c", NULL);
3537 rc = sscanf(value, format, x, &y);
3538 if (rc != 1)
3539 fatal_internal("The argument %s does not respect the imposed format: %s",
3540 value, args[0]);
3542 free(format);
3544 return rc == 1;
3547 /* ================================================================== */
3548 /* This constraint checks if each arguments of the option instance is */
3549 /* between a minimum and a maximum (inclusive). */
3550 /* return 1 if yes and 0 if no */
3551 /* ================================================================== */
3553 ctxopt_re_constraint(int nb_args, char ** args, char * value)
3555 regex_t re;
3557 if (nb_args != 1)
3558 fatal_internal(
3559 "Regular expression constraint: invalid number of parameters.");
3561 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
3562 fatal_internal("Invalid regular expression %s", args[0]);
3564 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
3566 fatal_internal("The argument %s doesn't match the constraining "
3567 "regular expression %s",
3568 value, args[0]);
3569 return 0;
3572 regfree(&re);
3574 return 1;
3577 /* ================================================================== */
3578 /* This constraint checks if each arguments of the option instance is */
3579 /* between a minimum and a maximum (inclusive). */
3580 /* return 1 if yes and 0 if no */
3581 /* ================================================================== */
3583 ctxopt_range_constraint(int nb_args, char ** args, char * value)
3585 long min, max;
3586 char c;
3587 char * ptr;
3588 int n;
3589 long v;
3590 int min_only = 0;
3591 int max_only = 0;
3593 if (nb_args != 2)
3594 fatal_internal("Range constraint: invalid number of parameters.");
3596 if (strcmp(args[0], ".") == 0)
3597 max_only = 1;
3598 else
3599 n = sscanf(args[0], "%ld%c", &min, &c);
3601 if (!max_only && n != 1)
3602 fatal_internal("Range constraint: min: invalid parameters.");
3604 if (strcmp(args[1], ".") == 0)
3605 min_only = 1;
3606 else
3607 n = sscanf(args[1], "%ld%c", &max, &c);
3609 if (!min_only && n != 1)
3610 fatal_internal("Range constraint: max: invalid parameters.");
3612 if (min_only && max_only)
3613 fatal_internal("Range constraint: invalid parameters.");
3615 errno = 0;
3616 v = strtol(value, &ptr, 10);
3617 if (errno || ptr == value)
3618 return 0;
3620 if (min_only)
3622 if (v < min)
3624 fatal_internal("%ld is not greater or equal to %ld as requested.", v,
3625 min);
3626 return 0;
3628 else
3629 return 1;
3631 else if (max_only)
3633 if (v > max)
3635 fatal_internal("%ld is not lower or equal to %ld as requested.", v, max);
3636 return 0;
3638 else
3639 return 1;
3641 else if (v < min || v > max)
3643 fatal_internal("%ld is not between %ld and %ld as requested.", v, min, max);
3644 return 0;
3647 return 1; /* check passed */
3650 /* ============================================================== */
3651 /* This function provides a way to set the behaviour of a context */
3652 /* ============================================================== */
3653 void
3654 ctxopt_add_global_settings(settings s, ...)
3656 va_list(args);
3657 va_start(args, s);
3659 switch (s)
3661 case error_functions:
3663 void (*function)(errors e, state_t * state);
3665 errors e;
3666 e = va_arg(args, errors);
3667 function = va_arg(args, void (*)(errors e, state_t * state));
3668 err_functions[e] = function;
3669 break;
3672 default:
3673 break;
3675 va_end(args);
3678 /* ================================================================ */
3679 /* This function provides a way to set the behaviour of an option */
3680 /* It can take a variable number of arguments according to its */
3681 /* first argument: */
3682 /* - parameter: */
3683 /* o a string containing an option name and all its possible */
3684 /* parameters separates by spaces, tabs or commas (char *) */
3685 /* (e.g: "help -h -help") */
3686 /* - actions: */
3687 /* o a string containing an option name */
3688 /* o a pointer to a function which will be called at evaluation */
3689 /* time. */
3690 /* - constraints: */
3691 /* o a string containing an option name */
3692 /* o a pointer to a function to check if an argument is valid. */
3693 /* o a strings containing the arguments to this function. */
3694 /* ================================================================ */
3695 void
3696 ctxopt_add_opt_settings(settings s, ...)
3698 opt_t * opt;
3699 void * ptr = NULL;
3701 va_list(args);
3702 va_start(args, s);
3704 switch (s)
3706 /* This part associates some command line parameters to an option */
3707 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3708 case parameters:
3710 char * opt_name;
3711 char * params;
3713 /* The second argument must be a string containing: */
3714 /* - The name of an existing option */
3715 /* - a list of parameters with a leading dash (-) */
3716 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3717 ptr = va_arg(args, char *);
3718 opt_name = ptr;
3720 if (opt_name != NULL)
3722 if ((opt = locate_opt(opt_name)) != NULL)
3724 ptr = va_arg(args, char *);
3725 params = ptr;
3727 if (!opt_set_parms(opt_name, params))
3728 fatal_internal(
3729 "duplicates parameters or bad settings for the option (%s).",
3730 params);
3732 else
3733 fatal_internal("%s: unknown option.", opt_name);
3735 else
3736 fatal_internal(
3737 "ctxopt_opt_add_settings: parameters: not enough arguments.");
3739 /* Here opt is a known option */
3740 /* """""""""""""""""""""""""" */
3741 if (opt->params != NULL)
3742 fatal_internal("Parameters are already set for %s", opt_name);
3743 else
3745 size_t n;
3746 size_t l = strlen(params);
3748 opt->params = xstrdup(params);
3749 while ((n = strcspn(opt->params, " \t")) < l)
3750 opt->params[n] = '|';
3752 /* Update current_state */
3753 /* -------------------- */
3754 cur_state->opt_params = xstrdup(opt->params);
3757 break;
3760 /* This part associates a callback function to an option. */
3761 /* This function will be called as when an instance of an option */
3762 /* is evaluated. */
3763 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3764 case actions:
3766 void * data;
3767 void (*function)();
3768 int nb_data = 0;
3770 /* The second argument must be the name of an existing option */
3771 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3772 ptr = va_arg(args, char *);
3774 if ((opt = locate_opt(ptr)) != NULL)
3776 /* The third argument must be the callback function */
3777 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3778 function = va_arg(args, void (*)(char *, char *, char *, int, char **,
3779 int, void *, int, void **));
3780 opt->action = function;
3782 /* The fourth argument must be a pointer to an user's defined */
3783 /* variable or structure that the previous function can manage */
3784 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3785 while ((data = va_arg(args, void *)) != NULL)
3787 nb_data++;
3788 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
3789 opt->data[nb_data - 1] = data;
3791 opt->nb_data = nb_data;
3793 else
3794 fatal_internal("%s: unknown option.", ptr);
3795 break;
3798 /* This part associates a list of functions to control some */
3799 /* characteristics of the arguments of an option. */
3800 /* Each function will be called in order and must return 1 */
3801 /* to validate the arguments. */
3802 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3803 case constraints:
3805 char * value;
3806 constraint_t * cstr;
3807 int (*function)();
3809 /* The second argument must be a string */
3810 /* """""""""""""""""""""""""""""""""""" */
3811 ptr = va_arg(args, char *);
3813 if ((opt = locate_opt(ptr)) != NULL)
3815 /* The third argument must be a function */
3816 /* """"""""""""""""""""""""""""""""""""" */
3817 function = va_arg(args, int (*)(int, char **, char *));
3819 cstr = xmalloc(sizeof(constraint_t));
3820 cstr->constraint = function;
3822 /* The fourth argument must be a string containing the argument of */
3823 /* The previous function separated by spaces or tabs */
3824 /* Theses arguments will be passed to the previous function */
3825 /* max: 32 argument! */
3826 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3827 value = xstrdup(va_arg(args, char *));
3829 cstr->args = xcalloc(sizeof(char *), 32);
3830 cstr->nb_args = str2argv(value, cstr->args, 32);
3831 ll_append(opt->constraints_list, cstr);
3833 else
3834 fatal_internal("%s: unknown option.", ptr);
3835 break;
3838 default:
3839 break;
3841 va_end(args);
3844 /* ============================================================== */
3845 /* This function provides a way to set the behaviour of a context */
3846 /* ============================================================== */
3847 void
3848 ctxopt_add_ctx_settings(settings s, ...)
3850 ctx_t * ctx;
3852 va_list(args);
3853 va_start(args, s);
3855 switch (s)
3857 /* Add a set of mutually incompatibles option in a context. */
3858 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3859 case incompatibilities:
3861 void * ptr;
3862 ll_t * list;
3863 size_t n;
3864 char * str;
3866 ptr = va_arg(args, char *);
3867 if ((ctx = locate_ctx(ptr)) != NULL)
3869 ptr = va_arg(args, char *);
3870 list = ctx->incomp_list;
3872 str = xstrdup(ptr);
3873 ltrim(str, " \t");
3874 rtrim(str, " \t", 0);
3876 n = strcspn(str, " \t");
3877 if (n > 0 && n < strlen(str))
3878 ll_append(list, str);
3879 else
3880 fatal_internal(
3881 "Not enough incompatible options in the string: \"%s\"", str);
3883 else
3884 fatal_internal("%s: unknown context.", ptr);
3885 break;
3888 /* Add functions which be call when entering and exiting a context. */
3889 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3890 case actions:
3892 void * ptr;
3893 void * data;
3894 int (*function)();
3895 int nb_data = 0;
3897 ptr = va_arg(args, char *);
3898 if ((ctx = locate_ctx(ptr)) != NULL)
3900 function = va_arg(args,
3901 int (*)(char *, direction, char *, int, void **));
3902 ctx->action = function;
3904 while ((data = va_arg(args, void *)) != NULL)
3906 nb_data++;
3907 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
3908 ctx->data[nb_data - 1] = data;
3910 ctx->nb_data = nb_data;
3912 else
3913 fatal_internal("%s: unknown context.", ptr);
3914 break;
3917 default:
3918 break;
3920 va_end(args);