Add a "display usage on error" setting
[ctxopt.git] / ctxopt.c
blob172f34ae6e1e308aec440cce8f651cd5f5e7b1b2
1 /* ################################################################### */
2 /* This Source Code Form is subject to the terms of the Mozilla Public */
3 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
4 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 /* ################################################################### */
7 #include <errno.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <ctype.h>
13 #include <sys/types.h>
14 #include <regex.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include "ctxopt.h"
19 /* ************************ */
20 /* Static global variables. */
21 /* ************************ */
23 static void * contexts_bst;
24 static void * options_bst;
26 state_t * cur_state;
28 /* Prototypes */
30 /* ************************** */
31 /* Fatal messages prototypes. */
32 /* ************************** */
34 static void (**err_functions)(errors e, state_t * state);
36 static void
37 fatal_internal(const char * format, ...);
39 static void
40 fatal(errors e, char * errmsg);
42 static int user_rc; /* Used by various callback functions */
43 static int user_value; /* Used by various callback functions */
44 static char * user_string; /* Used by various callback functions */
45 static char * user_string2; /* Used by various callback functions */
46 static void * user_object; /* Used by various callback functions */
48 /* ************************************ */
49 /* Memory management static prototypes. */
50 /* ************************************ */
52 static void *
53 xmalloc(size_t size);
55 static void *
56 xcalloc(size_t num, size_t size);
58 static void *
59 xrealloc(void * ptr, size_t size);
61 static char *
62 xstrdup(const char * p);
64 static char *
65 xstrndup(const char * str, size_t len);
67 /* ********************** */
68 /* BST static prototypes. */
69 /* ********************** */
71 typedef struct bst_s bst_t;
73 typedef enum
75 preorder,
76 postorder,
77 endorder,
78 leaf
79 } walk_order_e;
81 #if 0 /* Unused yet */
82 static void *
83 bst_delete(const void * vkey, void ** vrootp,
84 int (*compar)(const void *, const void *));
85 #endif
87 static void
88 bst_destroy(void * vrootp, void (*clean)(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(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 static prototypes. */
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 void
142 ll_free(ll_t * const list, void (*)(void *));
144 static void
145 ll_destroy(ll_t * const list, void (*)(void *));
147 static int
148 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array);
150 /* ************************** */
151 /* Various static prototypes. */
152 /* ************************** */
154 static void
155 ltrim(char * str, const char * trim_str);
157 static void
158 rtrim(char * str, const char * trim_str, size_t min);
160 static int
161 strchrcount(char * str, char c);
163 static int
164 strpref(char * s1, char * s2);
166 static int
167 stricmp(const char * s1, const char * s2);
169 static char *
170 xstrtok_r(char * str, const char * delim, char ** end);
172 static int
173 eval_yes(char * value, int * invalid);
175 static char *
176 get_word(char * str, char * buf, size_t len);
178 /* ************************* */
179 /* ctxopt static prototypes. */
180 /* ************************* */
182 typedef struct flags_s flags_t;
183 typedef struct opt_s opt_t;
184 typedef struct par_s par_t;
185 typedef struct ctx_s ctx_t;
186 typedef struct constraint_s constraint_t;
187 typedef struct ctx_inst_s ctx_inst_t;
188 typedef struct opt_inst_s opt_inst_t;
189 typedef struct seen_opt_s seen_opt_t;
190 typedef struct req_s req_t;
192 static char *
193 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
195 static int
196 ctx_compare(const void * c1, const void * c2);
198 static void
199 ctx_free(void * o);
201 static void
202 ctx_inst_free(void * ci);
204 static void
205 opt_inst_free(void * oi);
207 static int
208 seen_opt_compare(const void * so1, const void * so2);
210 static void
211 incomp_bst_free(void * b);
213 static void
214 req_free(void * r);
216 static void
217 seen_opt_free(void * seen_opt);
219 static int
220 opt_compare(const void * o1, const void * o2);
222 static void
223 opt_free(void * o);
225 static int
226 par_compare(const void * a1, const void * a2);
228 static void
229 par_free(void * p);
231 static void
232 constraint_free(void * cstr);
234 static ctx_t *
235 locate_ctx(char * name);
237 static opt_t *
238 locate_opt(char * name);
240 static par_t *
241 locate_par(char * name, ctx_t * ctx);
243 static void
244 print_before_constraints(ll_t * list);
246 static void
247 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
248 int * has_rule, int * has_generic_arg, int * has_ctx_change,
249 int * has_early_eval);
250 static void
251 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
252 int has_optional, int has_ellipsis, int has_rule);
253 static void
254 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
256 static void
257 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
259 static void
260 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
262 static void
263 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
265 static void
266 bst_match_par_cb(const void * node, walk_order_e kind, int level);
268 static void
269 match_prefix_cb(const void * node, walk_order_e kind, int level);
271 static int
272 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
274 static int
275 opt_parse(char * s, opt_t ** opt);
277 static int
278 init_opts(char * spec, ctx_t * ctx);
280 static int
281 ctxopt_build_cmdline_list(int nb_words, char ** words);
283 static int
284 opt_set_parms(char * opt_name, char * par_str);
286 static ctx_inst_t *
287 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
289 static void
290 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
292 /* ****************************** */
293 /* Fatal messages implementation. */
294 /* ****************************** */
296 /* ================================================================== */
297 /* Fatal error function used when a fatal condition is encountered. */
298 /* This function is reserved for the ctxopt internal usage. */
299 /* */
300 /* format : printf like format */
301 /* ... : remaining arguments interpreted using the format argument */
302 /* ================================================================== */
303 static void
304 fatal_internal(const char * format, ...)
306 va_list args;
308 fprintf(stderr, "CTXOPT: ");
310 va_start(args, format);
311 vfprintf(stderr, format, args);
312 fprintf(stderr, "\n");
313 va_end(args);
315 exit(EXIT_FAILURE);
318 /* ====================================================================== */
319 /* Generic fatal error function. This one uses the global status ctxopt */
320 /* stored in the cur_state structure and can call custom error functions. */
321 /* registered by the users for a given error identifier if any. */
322 /* */
323 /* e : Error identifier responsible of the fatal error */
324 /* errmsg : Users's provided string specific to the error e */
325 /* Note that errmsg is not used in all cases */
326 /* */
327 /* CTXOPTMISPAR Missing parameter */
328 /* CTXOPTREQPAR Option: all parameters in a required group are */
329 /* missing. */
330 /* CTXOPTMISARG Missing argument */
331 /* CTXOPTUXPARG Unexpected argument */
332 /* CTXOPTDUPOPT Duplicated option */
333 /* CTXOPTUNKPAR Unknown parameter */
334 /* CTXOPTINCOPT Incompatible option */
335 /* CTXOPTCTEOPT Option: bad number of occurrences */
336 /* CTXOPTCTLOPT Option: not enough occurrences */
337 /* CTXOPTCTGOPT Option: too many occurrence of */
338 /* CTXOPTCTEARG Arguments: bad number of occurrences */
339 /* CTXOPTCTLARG Arguments: not enough occurrences */
340 /* CTXOPTCTGARG Arguments: too many occurrences */
341 /* ====================================================================== */
342 static void
343 fatal(errors e, char * errmsg)
345 if (err_functions[e] != NULL)
346 err_functions[e](e, cur_state);
347 else
349 switch (e)
351 case CTXOPTNOERR:
352 break;
354 case CTXOPTMISPAR:
355 if (cur_state->ctx_par_name != NULL)
356 fprintf(stderr,
357 "the mandatory parameter(s) %s are missing in the context "
358 "introduced by %s.\n",
359 errmsg, cur_state->ctx_par_name);
360 else
361 fprintf(stderr,
362 "The mandatory parameter(s) %s are missing "
363 "in the main context.\n",
364 errmsg);
366 free(errmsg);
367 break;
369 case CTXOPTREQPAR:
370 fprintf(stderr, errmsg, cur_state->req_opt_par_needed,
371 cur_state->req_opt_par);
372 break;
374 case CTXOPTUNXARG:
375 if (cur_state->cur_opt_par_name != NULL)
376 fprintf(stderr,
377 "The parameter %s takes no arguments "
378 "or has too many arguments.\n",
379 cur_state->cur_opt_par_name);
380 break;
382 case CTXOPTMISARG:
383 if (cur_state->pre_opt_par_name != NULL)
384 fprintf(stderr, "%s requires argument(s).\n",
385 cur_state->pre_opt_par_name);
386 else
387 fprintf(stderr, "%s requires argument(s).\n",
388 cur_state->cur_opt_par_name);
389 break;
391 case CTXOPTDUPOPT:
392 if (cur_state->pre_opt_par_name != NULL)
393 fprintf(stderr,
394 "The parameter %s can only appear once in the context "
395 "introduced by %s.\n",
396 cur_state->cur_opt_params, cur_state->ctx_par_name);
397 else
398 fprintf(stderr,
399 "The parameter %s can only appear once "
400 "in the main context.\n",
401 cur_state->cur_opt_params);
402 break;
404 case CTXOPTUNKPAR:
405 fprintf(stderr, "Unknown parameter %s.\n%s",
406 cur_state->cur_opt_par_name, errmsg);
407 break;
409 case CTXOPTINCOPT:
410 fprintf(stderr, "The parameter %s is incompatible with %s.\n",
411 cur_state->cur_opt_par_name, errmsg);
412 break;
414 case CTXOPTCTEOPT:
415 if (cur_state->ctx_par_name)
416 fprintf(stderr,
417 "The parameter %s must appear exactly %d times "
418 "in the context introduced by %s.\n",
419 cur_state->cur_opt_params, cur_state->opts_count,
420 cur_state->ctx_par_name);
421 else
422 fprintf(stderr,
423 "The parameter %s must appear exactly %d times "
424 "in the main context.\n",
425 cur_state->cur_opt_params, cur_state->opts_count);
426 break;
428 case CTXOPTCTLOPT:
429 if (cur_state->ctx_par_name)
430 fprintf(stderr,
431 "The parameter %s must appear less than %d times "
432 "in the context introduced by %s.\n",
433 cur_state->cur_opt_params, cur_state->opts_count,
434 cur_state->ctx_par_name);
435 else
436 fprintf(stderr,
437 "The parameter %s must appear less than %d times "
438 "in the main context.\n",
439 cur_state->cur_opt_params, cur_state->opts_count);
440 break;
442 case CTXOPTCTGOPT:
443 if (cur_state->ctx_par_name)
444 fprintf(stderr,
445 "The parameter %s must appear more than %d times "
446 "in the context introduced by %s.\n",
447 cur_state->cur_opt_params, cur_state->opts_count,
448 cur_state->ctx_par_name);
449 else
450 fprintf(stderr,
451 "The parameter %s must appear more than %d times "
452 "in the main context.\n",
453 cur_state->cur_opt_params, cur_state->opts_count);
454 break;
456 case CTXOPTCTEARG:
457 fprintf(stderr, "The parameter %s must have exactly %d arguments.\n",
458 cur_state->cur_opt_par_name, cur_state->opt_args_count);
459 break;
461 case CTXOPTCTLARG:
462 fprintf(stderr, "The parameter %s must have less than %d arguments.\n",
463 cur_state->cur_opt_par_name, cur_state->opt_args_count);
464 break;
466 case CTXOPTCTGARG:
467 fprintf(stderr, "The parameter %s must have more than %d arguments.\n",
468 cur_state->cur_opt_par_name, cur_state->opt_args_count);
469 break;
471 case CTXOPTERRSIZ:
472 break;
476 /* CTXOPTUNKPAR should display the full usage to help the user follow */
477 /* the chaining of contexts when several possible contexts have been */
478 /* identified. Otherwise, errmsg is the empty string and the display of */
479 /* the current usage is enough. */
480 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
481 if (e == CTXOPTUNKPAR && *errmsg != '\0')
482 ctxopt_disp_usage(continue_after);
483 else
484 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
486 exit(e); /* Exit with the error id e as return code. */
489 /* ********************************* */
490 /* Memory management implementation. */
491 /* ********************************* */
493 /* ================== */
494 /* Customized malloc. */
495 /* ================== */
496 static void *
497 xmalloc(size_t size)
499 void * allocated;
500 size_t real_size;
502 real_size = (size > 0) ? size : 1;
503 allocated = malloc(real_size);
504 if (allocated == NULL)
505 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
506 (unsigned long int)size);
508 return allocated;
511 /* ================== */
512 /* Customized calloc. */
513 /* ================== */
514 static void *
515 xcalloc(size_t n, size_t size)
517 void * allocated;
519 n = (n > 0) ? n : 1;
520 size = (size > 0) ? size : 1;
521 allocated = calloc(n, size);
522 if (allocated == NULL)
523 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
524 (unsigned long int)size);
526 return allocated;
529 /* =================== */
530 /* Customized realloc. */
531 /* =================== */
532 static void *
533 xrealloc(void * p, size_t size)
535 void * allocated;
537 allocated = realloc(p, size);
538 if (allocated == NULL && size > 0)
539 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
540 (unsigned long int)size);
542 return allocated;
545 /* ==================================== */
546 /* strdup implementation using xmalloc. */
547 /* ==================================== */
548 static char *
549 xstrdup(const char * p)
551 char * allocated;
553 allocated = xmalloc(strlen(p) + 1);
554 strcpy(allocated, p);
556 return allocated;
559 /* =================================================== */
560 /* strndup implementation using xmalloc. */
561 /* This version guarantees that there is a final '\0'. */
562 /* =================================================== */
563 static char *
564 xstrndup(const char * str, size_t len)
566 char * p;
568 p = memchr(str, '\0', len);
570 if (p)
571 len = p - str;
573 p = xmalloc(len + 1);
574 memcpy(p, str, len);
575 p[len] = '\0';
577 return p;
580 /* *************************** */
581 /* Linked list implementation. */
582 /* *************************** */
584 /* Linked list node structure. */
585 /* """"""""""""""""""""""""""" */
586 struct ll_node_s
588 void * data;
589 struct ll_node_s * next;
590 struct ll_node_s * prev;
593 /* Linked List structure. */
594 /* """""""""""""""""""""" */
595 struct ll_s
597 ll_node_t * head;
598 ll_node_t * tail;
599 long len;
602 /* ========================= */
603 /* Create a new linked list. */
604 /* ========================= */
605 static ll_t *
606 ll_new(void)
608 ll_t * ret = xmalloc(sizeof(ll_t));
609 ll_init(ret);
611 return ret;
614 /* =============================================== */
615 /* Free all the elements of a list (make it empty) */
616 /* NULL or a custom function may be used to free */
617 /* the sub components of the elements. */
618 /* =============================================== */
619 static void
620 ll_free(ll_t * const list, void (*clean)(void *))
622 if (list)
623 while (list->head)
625 /* Apply a custom cleaner if not NULL. */
626 /* """"""""""""""""""""""""""""""""""" */
627 if (clean)
628 clean(list->head->data);
630 ll_delete(list, list->head);
634 /* ==================================== */
635 /* Destroy a list and all its elements. */
636 /* ==================================== */
637 static void
638 ll_destroy(ll_t * list, void (*clean)(void *))
640 if (list)
642 ll_free(list, clean);
643 free(list);
647 /* ========================= */
648 /* Initialize a linked list. */
649 /* ========================= */
650 static void
651 ll_init(ll_t * list)
653 list->head = NULL;
654 list->tail = NULL;
655 list->len = 0;
658 /* ===================================================== */
659 /* Allocate the space for a new node in the linked list. */
660 /* ===================================================== */
661 static ll_node_t *
662 ll_new_node(void)
664 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
666 return ret;
669 /* ==================================================================== */
670 /* Append a new node filled with its data at the end of the linked list */
671 /* The user is responsible for the memory management of the data. */
672 /* ==================================================================== */
673 static void
674 ll_append(ll_t * const list, void * const data)
676 ll_node_t * node;
678 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
679 | uses xmalloc which does not return if there *
680 | is an allocation error. */
682 node->data = data;
683 node->next = NULL;
685 node->prev = list->tail;
686 if (list->tail)
687 list->tail->next = node;
688 else
689 list->head = node;
691 list->tail = node;
693 ++list->len;
696 /* ================================================================== */
697 /* Put a new node filled with its data at the beginning of the linked */
698 /* list. */
699 /* The user is responsible for the memory management of the data. */
700 /* ================================================================== */
701 static void
702 ll_prepend(ll_t * const list, void * const data)
704 ll_node_t * node;
706 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
707 | uses xmalloc which does not return if there *
708 | is an allocation error. */
710 node->data = data;
711 node->prev = NULL;
713 node->next = list->head;
714 if (list->head)
715 list->head->prev = node;
716 else
717 list->tail = node;
719 list->head = node;
721 ++list->len;
724 /* ======================================================== */
725 /* Insert a new node before the specified node in the list. */
726 /* ======================================================== */
727 static void
728 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
730 ll_node_t * new_node;
732 if (node->prev == NULL)
733 ll_prepend(list, data);
734 else
736 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
737 | uses xmalloc which does not return if there *
738 | is an allocation error. */
740 new_node->data = data;
741 new_node->next = node;
742 new_node->prev = node->prev;
743 node->prev->next = new_node;
744 node->prev = new_node;
746 ++list->len;
750 /* ======================================================= */
751 /* Insert a new node after the specified node in the list. */
752 /* ======================================================= */
753 static void
754 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
756 ll_node_t * new_node;
758 if (node->next == NULL)
759 ll_append(list, data);
760 else
762 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
763 | uses xmalloc which does not return if there *
764 | is an allocation error. */
766 new_node->data = data;
767 new_node->prev = node;
768 new_node->next = node->next;
769 node->next->prev = new_node;
770 node->next = new_node;
772 ++list->len;
776 /* ================================================================= */
777 /* Remove a node from a linked list */
778 /* The memory taken by the deleted node must be freed by the caller. */
779 /* ================================================================= */
780 static int
781 ll_delete(ll_t * const list, ll_node_t * node)
783 if (list->head == list->tail)
785 if (list->head != NULL)
786 list->head = list->tail = NULL;
787 else
788 return 0;
790 else if (node->prev == NULL)
792 list->head = node->next;
793 list->head->prev = NULL;
795 else if (node->next == NULL)
797 list->tail = node->prev;
798 list->tail->next = NULL;
800 else
802 node->next->prev = node->prev;
803 node->prev->next = node->next;
806 --list->len;
808 free(node);
810 return 1;
813 #if 0 /* Unused yet */
814 /* ======================================================================== */
815 /* Find a node in the list containing data. Return the node pointer or NULL */
816 /* if not found. */
817 /* A comparison function must be provided to compare a and b (strcmp like). */
818 /* ======================================================================== */
819 static ll_node_t *
820 ll_find(ll_t * const list, void * const data,
821 int (*cmpfunc)(const void * a, const void * b))
823 ll_node_t * node;
825 if (NULL == (node = list->head))
826 return NULL;
830 if (0 == cmpfunc(node->data, data))
831 return node;
832 } while (NULL != (node = node->next));
834 return NULL;
836 #endif
838 /* ==================================================================== */
839 /* Allocate and fill an array of strings from a list. */
840 /* WARNINGS: */
841 /* 1) The list node must contain strings (char *) */
842 /* 2) The strings in the resulting array MUST NOT be freed as the are */
843 /* NOT copied from the strings of the list. */
844 /* */
845 /* IN list : The list from which the array is generated */
846 /* IN start_node : The node of the list which will be the first node to */
847 /* consider to create the array */
848 /* OUT: count : The number of elements of the resulting array. */
849 /* OUT: array : The resulting array or NULL if the list is empty. */
850 /* RC : : The number of elements of the resulting array. */
851 /* ==================================================================== */
852 static int
853 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
855 int n = 0;
856 ll_node_t * node;
858 *count = 0;
860 node = start_node;
862 if (list == NULL || node == NULL)
864 *array = NULL;
866 return 0;
869 *array = xmalloc((list->len + 1) * sizeof(char *));
870 while (node != NULL)
872 (*array)[n++] = (char *)(node->data);
873 (*count)++;
875 node = node->next;
878 (*array)[*count] = NULL;
880 return *count;
883 /* ******************************************************************* */
884 /* BST (search.h compatible) implementation. */
885 /* */
886 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
887 /* the AT&T man page says. */
888 /* */
889 /* Written by reading the System V Interface Definition, not the code. */
890 /* */
891 /* Totally public domain. */
892 /* ******************************************************************* */
894 struct bst_s
896 void * key;
897 struct bst_s * llink;
898 struct bst_s * rlink;
901 #if 0 /* Unused yet */
902 /* =========================== */
903 /* Delete node with given key. */
904 /* =========================== */
905 static void *
906 bst_delete(const void * vkey, void ** vrootp,
907 int (*compar)(const void *, const void *))
909 bst_t ** rootp = (bst_t **)vrootp;
910 bst_t * p, *q, *r;
911 int cmp;
913 if (rootp == NULL || (p = *rootp) == NULL)
914 return NULL;
916 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
918 p = *rootp;
919 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
920 : &(*rootp)->rlink; /* follow rlink branch */
921 if (*rootp == NULL)
922 return NULL; /* key not found */
924 r = (*rootp)->rlink; /* D1: */
925 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
926 q = r;
927 else if (r != NULL)
928 { /* Right link is NULL? */
929 if (r->llink == NULL)
930 { /* D2: Find successor */
931 r->llink = q;
932 q = r;
934 else
935 { /* D3: Find NULL link */
936 for (q = r->llink; q->llink != NULL; q = r->llink)
937 r = q;
938 r->llink = q->rlink;
939 q->llink = (*rootp)->llink;
940 q->rlink = (*rootp)->rlink;
943 if (p != *rootp)
944 free(*rootp); /* D4: Free node */
945 *rootp = q; /* link parent to new node */
946 return p;
948 #endif
950 /* ===================================================================== */
951 /* Destroy a tree. */
952 /* The clean function pointer can be NULL, in this case the node content */
953 /* is not freed. */
954 /* ===================================================================== */
955 static void
956 bst_destroy(void * vrootp, void (*clean)(void *))
958 bst_t * root = (bst_t *)vrootp;
960 if (root == NULL)
961 return;
963 bst_destroy(root->llink, clean);
964 bst_destroy(root->rlink, clean);
966 if (clean)
967 clean((void *)root->key);
969 free(root);
972 /* ========================= */
973 /* Find a node, or return 0. */
974 /* ========================= */
975 static void *
976 bst_find(const void * vkey, void * const * vrootp,
977 int (*compar)(const void *, const void *))
979 bst_t * const * rootp = (bst_t * const *)vrootp;
981 if (rootp == NULL)
982 return NULL;
984 while (*rootp != NULL)
985 { /* T1: */
986 int r;
988 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
989 return *rootp; /* key found */
990 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
991 : &(*rootp)->rlink; /* T4: follow right branch */
993 return NULL;
996 /* ======================================= */
997 /* Find or inserts datum into search tree. */
998 /* ======================================= */
999 static void *
1000 bst_search(void * vkey, void ** vrootp,
1001 int (*compar)(const void *, const void *))
1003 bst_t * q;
1004 bst_t ** rootp = (bst_t **)vrootp;
1006 if (rootp == NULL)
1007 return NULL;
1009 while (*rootp != NULL)
1010 { /* Knuth's T1: */
1011 int r;
1013 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
1014 return *rootp; /* we found it! */
1016 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
1017 : &(*rootp)->rlink; /* T4: follow right branch */
1020 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
1021 if (q != 0)
1022 { /* make new node */
1023 *rootp = q; /* link new node to old */
1024 q->key = vkey; /* initialize new node */
1025 q->llink = q->rlink = NULL;
1027 return q;
1030 /* ========================= */
1031 /* Walk the nodes of a tree. */
1032 /* ========================= */
1033 static void
1034 bst_walk_recurse(const bst_t * root,
1035 void (*action)(const void *, walk_order_e, int), int level)
1037 if (root->llink == NULL && root->rlink == NULL)
1038 (*action)(root, leaf, level);
1039 else
1041 (*action)(root, preorder, level);
1042 if (root->llink != NULL)
1043 bst_walk_recurse(root->llink, action, level + 1);
1044 (*action)(root, postorder, level);
1045 if (root->rlink != NULL)
1046 bst_walk_recurse(root->rlink, action, level + 1);
1047 (*action)(root, endorder, level);
1051 static void
1052 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
1054 if (vroot != NULL && action != NULL)
1055 bst_walk_recurse(vroot, action, 0);
1058 /* ************************ */
1059 /* Various implementations. */
1060 /* ************************ */
1062 /* ======================== */
1063 /* Trim leading characters. */
1064 /* ======================== */
1065 static void
1066 ltrim(char * str, const char * trim_str)
1068 size_t len = strlen(str);
1069 size_t begin = strspn(str, trim_str);
1070 size_t i;
1072 if (begin > 0)
1073 for (i = begin; i <= len; ++i)
1074 str[i - begin] = str[i];
1077 /* ================================================= */
1078 /* Trim trailing characters. */
1079 /* The resulting string will have at least min bytes */
1080 /* even if trailing spaces remain. */
1081 /* ================================================= */
1082 static void
1083 rtrim(char * str, const char * trim_str, size_t min)
1085 size_t len = strlen(str);
1086 while (len > min && strchr(trim_str, str[len - 1]))
1087 str[--len] = '\0';
1090 /* ================================================== */
1091 /* Count the number of occurrences of the character c */
1092 /* in the string str. */
1093 /* The str pointer is assumed to be not NULL. */
1094 /* ================================================== */
1095 static int
1096 strchrcount(char * str, char c)
1098 int count = 0;
1100 while (*str)
1101 if (*str++ == c)
1102 count++;
1104 return count;
1107 /* =============================================== */
1108 /* Is the string str2 a prefix of the string str1? */
1109 /* =============================================== */
1110 static int
1111 strpref(char * str1, char * str2)
1113 while (*str1 != '\0' && *str1 == *str2)
1115 str1++;
1116 str2++;
1119 return *str2 == '\0';
1122 /* ========================== */
1123 /* Like strcmp ignoring case. */
1124 /* ========================== */
1125 static int
1126 stricmp(const char * s1, const char * s2)
1128 while (tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1130 if (*s1 == '\0')
1131 return 0;
1133 s1++;
1134 s2++;
1137 return (int)tolower((unsigned char)*s1) - (int)tolower((unsigned char)*s2);
1140 /* ======================================================================== */
1141 /* Strings concatenation with dynamic memory allocation. */
1142 /* IN : a variable number of char * arguments with NULL terminating */
1143 /* the sequence. */
1144 /* The first one must have been dynamically allocated and is mandatory */
1145 /* */
1146 /* Returns a new allocated string containing the concatenation of all */
1147 /* the arguments. It is the caller's responsibility to free the resulting */
1148 /* string. */
1149 /* ======================================================================== */
1150 static char *
1151 strappend(char * str, ...)
1153 size_t l;
1154 va_list args;
1155 char * s;
1157 l = 1 + strlen(str);
1158 va_start(args, str);
1160 s = va_arg(args, char *);
1162 while (s)
1164 l += strlen(s);
1165 s = va_arg(args, char *);
1168 va_end(args);
1170 str = xrealloc(str, l);
1172 va_start(args, str);
1173 s = va_arg(args, char *);
1175 while (s)
1177 strcat(str, s);
1178 s = va_arg(args, char *);
1180 va_end(args);
1182 return str;
1185 /* ====================================================================== */
1186 /* Public domain strtok_r() by Charlie Gordon. */
1187 /* from comp.lang.c 9/14/2007 */
1188 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1189 /* */
1190 /* (Declaration that it's public domain): */
1191 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1192 /* */
1193 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1194 /* *end == NULL */
1195 /* ====================================================================== */
1196 static char *
1197 xstrtok_r(char * str, const char * delim, char ** end)
1199 char * ret;
1201 if (str == NULL)
1202 str = *end;
1204 if (str == NULL)
1205 return NULL;
1207 str += strspn(str, delim);
1209 if (*str == '\0')
1210 return NULL;
1212 ret = str;
1214 str += strcspn(str, delim);
1216 if (*str)
1217 *str++ = '\0';
1219 *end = str;
1221 return ret;
1224 /* ===================================================================== */
1225 /* Put the first word of str, truncated to len characters, in buf. */
1226 /* Return a pointer in str pointing just after the word. */
1227 /* buf must have been pre-allocated to accept at least len+1 characters. */
1228 /* Note that buf can contains a sting full of spaces is str was not */
1229 /* trimmed before the call. */
1230 /* ===================================================================== */
1231 char *
1232 get_word(char * str, char * buf, size_t len)
1234 char * s = str;
1236 /* Skip spaces. */
1237 /* """""""""""" */
1238 while (*s && isspace(*s))
1239 s++;
1241 /* Set the new string start. */
1242 /* """"""""""""""""""""""""" */
1243 str = s;
1245 /* Get the word. */
1246 /*"""""""""""""" */
1247 while (*s && !isspace(*s) && s - str < len)
1248 s++;
1250 strncpy(buf, str, s - str);
1251 buf[s - str] = 0;
1253 return s;
1256 /* ==================================================================== */
1257 /* Return 1 is value is "1" or "yes" (ignoring case). */
1258 /* Return 0 is value is "0" or "no" (ignoring case). */
1259 /* If value has another value, then set invalid to 1 and also return 0 */
1260 /* invalid is set to 0i in all the other cases. */
1261 /* ==================================================================== */
1262 static int
1263 eval_yes(char * value, int * invalid)
1265 *invalid = 0;
1267 if (strcmp(value, "1") == 0 || stricmp(value, "yes") == 0)
1268 return 1;
1269 else if (strcmp(value, "0") != 0 && stricmp(value, "no") != 0)
1270 *invalid = 1;
1272 return 0;
1275 /* =========================================================== */
1276 /* Fill an array of strings from the words composing a string. */
1277 /* */
1278 /* str: initial string which will be altered. */
1279 /* args: array of pointers to the start of the words in str. */
1280 /* max: maximum number of words used before giving up. */
1281 /* return: the number of words (<=max). */
1282 /* =========================================================== */
1283 static int
1284 str2argv(char * str, char ** args, int max)
1286 int nb_args = 0;
1288 while (*str)
1290 if (nb_args >= max)
1291 return nb_args;
1293 while (*str == ' ' || *str == '\t')
1294 *(str++) = '\0';
1296 if (!*str)
1297 return nb_args;
1299 args[nb_args] = str;
1300 nb_args++;
1302 while (*str && (*str != ' ') && (*str != '\t'))
1303 str++;
1306 return nb_args;
1309 /* ********************** */
1310 /* ctxopt implementation. */
1311 /* ********************** */
1313 static int ctxopt_initialized = 0; /* cap_init has not yet been called */
1315 /* Flags structure initialized by ctxopt_init. */
1316 /* """"""""""""""""""""""""""""""""""""""""""" */
1317 struct flags_s
1319 int stop_if_non_option;
1320 int allow_abbreviations;
1321 int display_usage_on_error;
1324 static flags_t flags = { 0, 1, 1 };
1326 /* Context structure. */
1327 /* """""""""""""""""" */
1328 struct ctx_s
1330 char * name;
1331 ll_t * opt_list; /* list of options allowed in this context. */
1332 ll_t * incomp_list; /* list of strings containing incompatible names *
1333 | of options separated by spaces or tabs. */
1334 ll_t * req_list; /* list of strings containing an option name and *
1335 | all the option names where at least one of *
1336 | them is required to be also present. */
1338 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1339 void ** ctx_data);
1340 void * par_bst;
1341 int nb_data;
1342 void ** data;
1345 /* https://textik.com/#488ce3649b6c60f5 */
1346 /* */
1347 /* +--------------+ */
1348 /* |first_ctx_inst| */
1349 /* +---+----------+ */
1350 /* | */
1351 /* +--v-----+ +--------+ +--------+ +-----+ */
1352 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1353 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1354 /* | | | | | */
1355 /* | | +-v------+ | | */
1356 /* | +--+ctx_inst<-----------+ | */
1357 /* | +-+------+ | */
1358 /* | | | */
1359 /* | +-v------+ | */
1360 /* +------+ctx_inst<--------------------------+ */
1361 /* +-+------+ */
1362 /* | */
1363 /* +-v---+ */
1364 /* | ... | */
1365 /* +-----+ */
1367 /* Option structure. */
1368 /* """"""""""""""""" */
1369 struct opt_s
1371 char * name; /* option name. */
1372 char * next_ctx; /* new context this option may lead to */
1373 ll_t * ctx_list; /* list of contexts allowing this option. */
1374 char * params; /* string containing all the parameters of *
1375 | the option. */
1377 void (*action)( /* The option associated action. */
1378 char * ctx_name, /* context name. */
1379 char * opt_name, /* option name. */
1380 char * par, /* option parameter. */
1381 int nb_args, /* number of arguments. */
1382 char ** args, /* option arguments. */
1383 int nb_opt_data, /* number of option data pointers. */
1384 void ** opt_data, /* option data pointers. */
1385 int nb_ctx_data, /* nb of current context data ptrs. */
1386 void ** ctx_data /* current context data pointers. */
1389 int nb_data; /* number of the data pointers passed as argument to action. */
1390 void ** data; /* array of data pointers passed as argument to action. */
1392 int args; /* 1 if this option takes arguments else 0. */
1393 int optional; /* 1 if the option is optional, else 0. */
1394 int multiple; /* 1 if the option can appear more than one time in a *
1395 | context, else 0. */
1397 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1398 int occurrences; /* Number of option occurrences in a context. */
1399 char opt_count_oper; /* <, = or > */
1400 int opt_count_mark; /* Value to be compared to with opt_count_oper. */
1402 char * arg; /* symbolic text after # describing the option argument. */
1404 int optional_args; /* 1 of option is optional else 0. */
1405 int multiple_args; /* 1 is option can appear more than once in a context *
1406 | instance. */
1408 int opt_args_count_matter; /* 1 if count is rescticted, else 0. */
1409 char opt_args_count_oper; /* <, = or > */
1410 int opt_args_count_mark; /* Value to be compared to with *
1411 | opt_count_oper. */
1413 int eval_first; /* 1 if this option must be evaluated before the options *
1414 | without this mark. */
1416 ll_t * eval_before_list; /* List of pointers on options which must be *
1417 | evaluated before this option. */
1419 ll_t * constraints_list; /* List of constraint check functions pointers. */
1422 /* Context instance structure. */
1423 /* """"""""""""""""""""""""""" */
1424 struct ctx_inst_s
1426 ctx_t * ctx; /* the context whose this is an instance of */
1427 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1428 | creation of this ctx_inst structure. */
1429 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1430 | instance of this structure. */
1431 ll_t * incomp_bst_list; /* list of seen_opt_t BST. */
1432 void * seen_opt_bst; /* tree of seen_opt_t. */
1433 ll_t * opt_req_list; /* list of req_t. */
1434 ll_t * opt_inst_list; /* The list of option instances in this *
1435 | context instance. */
1436 char * par_name; /* parameter which created this instance. */
1439 /* Option instance structure. */
1440 /* """""""""""""""""""""""""" */
1441 struct opt_inst_s
1443 opt_t * opt; /* The option this is an instance of. */
1444 char * opt_name; /* The option which led to this creation. */
1445 char * par; /* The parameter which led to this creation. */
1446 ll_t * values_list; /* The list of arguments of this option. */
1447 ctx_inst_t * next_ctx_inst; /* The new context instance this option. *
1448 | instance may create. */
1451 /* Structure used to check if an option has bee seen or not */
1452 /* in a context instance. */
1453 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1454 struct seen_opt_s
1456 opt_t * opt; /* The concerned option. */
1457 char * par; /* Parameter which led to the making of this structure. */
1458 int seen; /* 1 if seen in the context instances, else 0. */
1461 /* Structure used to check if at least one instance of the options whose */
1462 /* pointers are in or_opt_list has been seen in the ctx_inst where an */
1463 /* instance or opt is also present. */
1464 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1465 struct req_s
1467 opt_t * opt; /* Option that asks for other options. */
1468 ll_t * or_opt_list; /* Required options, at least one of them *
1469 | must be present. */
1472 /* Parameter structure which links a parameter to the option it belongs to. */
1473 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1474 struct par_s
1476 char * name; /* Parameter name (with the leading -). */
1477 opt_t * opt; /* Attached option. */
1480 /* Constraint structure. */
1481 /* """"""""""""""""""""" */
1482 struct constraint_s
1484 int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1485 int nb_args;
1486 char ** args;
1487 char * to_free; /* pointer to the original string in which the array in *
1488 | args points to. This poinnter is kept there to allow *
1489 | it to be freed. */
1492 state_t * cur_state = NULL; /* Current analysis state. */
1493 static ll_t * cmdline_list = NULL; /* List of interpreted CLI words *
1494 | serves as the basis for the *
1495 | analysis of the parameters. */
1496 static ctx_t * main_ctx = NULL; /* initial context. */
1497 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1498 | instance which holds the *
1499 | options instances. */
1500 static ll_t * ctx_inst_list = NULL; /* List of the context instances. */
1502 /* ======================================================= */
1503 /* Parse a string for the next matching token. */
1504 /* */
1505 /* s: string to parse. */
1506 /* token: pre_allocated array of max tok_len characters. */
1507 /* pattern: scanf type pattern token must match. */
1508 /* pos: number of characters successfully parsed in s. */
1509 /* */
1510 /* Returns: a pointer to the first unread character or */
1511 /* to he terminating \0. */
1512 /* ======================================================= */
1513 static char *
1514 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1516 char * full_pattern;
1517 char len[3];
1518 int n;
1520 *pos = 0;
1522 n = snprintf(len, 3, "%zu", tok_len);
1523 if (n < 0)
1524 return NULL;
1526 full_pattern = xmalloc(strlen(pattern) + n + 4);
1528 strcpy(full_pattern, "%");
1529 strcat(full_pattern, len);
1530 strcat(full_pattern, pattern);
1531 strcat(full_pattern, "%n");
1533 n = sscanf(s, full_pattern, token, pos);
1535 free(full_pattern);
1537 if (n != 1)
1538 return NULL;
1540 return s + *pos;
1543 /* ****************************************** */
1544 /* Various comparison and deletion functions. */
1545 /* ****************************************** */
1547 static int
1548 ctx_compare(const void * c1, const void * c2)
1550 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1553 /* =========================== */
1554 /* Free a context_bst element. */
1555 /* =========================== */
1556 static void
1557 ctx_free(void * c)
1559 ctx_t * ctx = c;
1561 free(ctx->name);
1562 free(ctx->data);
1564 ll_destroy(ctx->opt_list, NULL);
1565 ll_destroy(ctx->incomp_list, free);
1566 ll_destroy(ctx->req_list, free);
1567 bst_destroy(ctx->par_bst, par_free);
1569 free(c);
1572 /* ============================= */
1573 /* Free a ctx_inst_list element. */
1574 /* ============================= */
1575 static void
1576 ctx_inst_free(void * ci)
1578 ctx_inst_t * ctx_inst = ci;
1580 free(ctx_inst->par_name);
1581 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1582 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1583 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1584 ll_destroy(ctx_inst->opt_req_list, req_free);
1586 free(ci);
1589 /* ============================== */
1590 /* Free an opt_inst_list element. */
1591 /* ============================== */
1592 static void
1593 opt_inst_free(void * oi)
1595 opt_inst_t * opt_inst = oi;
1597 ll_destroy(opt_inst->values_list, NULL);
1599 free(oi);
1602 /* ================================== */
1603 /* Compare two seen_opt_bst elements. */
1604 /* ================================== */
1605 static int
1606 seen_opt_compare(const void * so1, const void * so2)
1608 opt_t *o1, *o2;
1610 o1 = ((seen_opt_t *)so1)->opt;
1611 o2 = ((seen_opt_t *)so2)->opt;
1613 return strcmp(o1->name, o2->name);
1616 /* ============================ */
1617 /* Free a seen_opt_bst element. */
1618 /* ============================ */
1619 void
1620 seen_opt_free(void * so)
1622 seen_opt_t * seen_opt = so;
1624 free(seen_opt->par);
1626 free(so);
1629 /* =========================== */
1630 /* Free an incomp_bst element. */
1631 /* =========================== */
1632 static void
1633 incomp_bst_free(void * b)
1635 bst_t * bst = b;
1637 bst_destroy(bst, NULL);
1640 /* ============================= */
1641 /* Free an opt_req_list element. */
1642 /* ============================= */
1643 static void
1644 req_free(void * r)
1646 req_t * req = r;
1648 ll_destroy(req->or_opt_list, NULL);
1649 free(req);
1652 /* ================================= */
1653 /* Compare two options_bst elements. */
1654 /* ================================= */
1655 static int
1656 opt_compare(const void * o1, const void * o2)
1658 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1661 /* ============================= */
1662 /* Free an options_bst elements. */
1663 /* ============================= */
1664 void
1665 opt_free(void * o)
1667 opt_t * opt = o;
1669 free(opt->name);
1670 free(opt->next_ctx);
1671 free(opt->params);
1672 free(opt->arg);
1673 free(opt->data);
1675 ll_destroy(opt->ctx_list, NULL);
1676 ll_destroy(opt->constraints_list, constraint_free);
1677 ll_destroy(opt->eval_before_list, NULL);
1679 free(o);
1682 /* ============================= */
1683 /* Compare two par_bst elements. */
1684 /* ============================= */
1685 static int
1686 par_compare(const void * a1, const void * a2)
1688 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1691 /* ======================= */
1692 /* Free a par_bst element. */
1693 /* ======================= */
1694 static void
1695 par_free(void * p)
1697 par_t * par = p;
1699 free(par->name);
1701 free(p);
1704 /* ================================ */
1705 /* Free a constraints_list element. */
1706 /* ================================ */
1707 static void
1708 constraint_free(void * c)
1710 constraint_t * cstr = c;
1712 free(cstr->args);
1713 free(cstr->to_free);
1715 free(c);
1718 /* ******************************************************************** */
1719 /* Helper functions to locate contexts, options and parameters in a BST */
1720 /* by their names. */
1721 /* ******************************************************************** */
1723 static ctx_t *
1724 locate_ctx(char * name)
1726 bst_t * node;
1727 ctx_t ctx = { 0 };
1729 ctx.name = name;
1731 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1732 return NULL;
1733 else
1734 return node->key;
1737 static opt_t *
1738 locate_opt(char * name)
1740 bst_t * node;
1741 opt_t opt = { 0 };
1743 opt.name = name;
1745 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1746 return NULL;
1747 else
1748 return node->key;
1751 static par_t *
1752 locate_par(char * name, ctx_t * ctx)
1754 bst_t * node;
1755 par_t par = { 0 };
1756 void * bst = ctx->par_bst;
1758 par.name = name;
1760 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1761 return NULL;
1762 else
1763 return node->key;
1766 /* ====================================================================== */
1767 /* Helper function to display the dependency constraints between options. */
1768 /* These constraints are set with the ctxopt_add_opt_settings function */
1769 /* using the 'before' and 'after' arguments. */
1770 /* IN list : a list of options. */
1771 /* ====================================================================== */
1772 static void
1773 print_before_constraints(ll_t * list)
1775 ll_node_t * node = list->head;
1776 ll_node_t * before_node;
1777 opt_t * opt, *before_opt;
1778 int msg = 0;
1780 while (node != NULL)
1782 opt = node->data;
1784 if (opt->eval_before_list->len > 0)
1786 if (!msg)
1788 printf("\n If present in the command line,");
1789 msg = 1; /* Display this message only once. */
1792 before_node = opt->eval_before_list->head;
1794 printf("\n ");
1795 while (before_node != NULL)
1797 before_opt = before_node->data;
1798 printf("%s", before_opt->params);
1800 before_node = before_node->next;
1802 if (before_node != NULL)
1803 printf(" and\n ");
1805 printf(" will be evaluated after %s\n", opt->params);
1807 node = node->next;
1811 /* =================================================================== */
1812 /* Utility function to format and print the options present in a list. */
1813 /* */
1814 /* IN list : a list of options. */
1815 /* OUT has_* : a set of flags which will determine the content of the */
1816 /* explanation given after the formatted printing of the */
1817 /* options. */
1818 /* =================================================================== */
1819 static void
1820 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1821 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1822 int * has_early_eval)
1824 ll_node_t * node = list->head;
1825 opt_t * opt;
1826 char * line;
1827 char * option;
1829 line = xstrdup(" ");
1831 while (node != NULL)
1833 option = xstrdup("");
1834 opt = node->data;
1836 if (opt->optional)
1838 option = strappend(option, "[", (char *)0);
1839 *has_optional = 1;
1842 if (opt->eval_first)
1844 option = strappend(option, "*", (char *)0);
1845 *has_early_eval = 1;
1848 option = strappend(option, opt->params, (char *)0);
1850 if (opt->next_ctx != NULL)
1852 option = strappend(option, ">", opt->next_ctx, (char *)0);
1853 *has_ctx_change = 1;
1856 if (opt->multiple)
1858 if (opt->opt_count_oper != '\0')
1860 char m[4];
1861 char o[2];
1862 o[0] = opt->opt_count_oper;
1863 o[1] = '\0';
1864 snprintf(m, 3, "%u", opt->opt_count_mark);
1865 option = strappend(option, "...", o, m, (char *)0);
1866 *has_rule = 1;
1868 else
1869 option = strappend(option, "...", (char *)0);
1871 *has_ellipsis = 1;
1874 if (opt->args)
1876 if (*(opt->arg) == '#')
1877 *has_generic_arg = 1;
1879 option = strappend(option, " ", (char *)0);
1881 if (opt->optional_args)
1883 option = strappend(option, "[", opt->arg, (char *)0);
1884 *has_optional = 1;
1886 else
1887 option = strappend(option, opt->arg, (char *)0);
1889 if (opt->multiple_args)
1891 if (opt->opt_args_count_oper != '\0')
1893 char m[4];
1894 char o[2];
1895 o[0] = opt->opt_args_count_oper;
1896 o[1] = '\0';
1897 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1898 option = strappend(option, "...", o, m, (char *)0);
1899 *has_rule = 1;
1901 else
1902 option = strappend(option, "...", (char *)0);
1904 *has_ellipsis = 1;
1906 if (opt->optional_args)
1907 option = strappend(option, "]", (char *)0);
1909 if (opt->optional)
1910 option = strappend(option, "]", (char *)0);
1912 if (strlen(line) + 1 + strlen(option) < 80)
1913 line = strappend(line, option, " ", (char *)0);
1914 else
1916 printf("%s\n", line);
1917 line[2] = '\0';
1918 line = strappend(line, option, " ", (char *)0);
1921 free(option);
1923 node = node->next;
1926 printf("%s\n", line);
1928 free(line);
1931 /* ==================================================== */
1932 /* Explain the special syntactic symbols present in the */
1933 /* generated usage messages. */
1934 /* ==================================================== */
1935 static void
1936 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1937 int has_optional, int has_ellipsis, int has_rule)
1939 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1940 || has_ellipsis || has_rule)
1942 printf("\nExplanation of the syntax used above:\n");
1943 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1944 "must be entered.\n");
1945 printf("The following is just there to explain the other symbols "
1946 "displayed.\n\n");
1948 if (has_early_eval)
1949 printf("* : the parameters defined for this option will "
1950 "be evaluated first.\n");
1951 if (has_ctx_change)
1952 printf("> : the context after this symbol will be the new "
1953 "default context.\n");
1954 if (has_generic_arg)
1955 printf("#tag : argument with a hint about its meaning.\n");
1956 if (has_optional)
1957 printf("[...] : the object between square brackets is "
1958 "optional.\n");
1959 if (has_ellipsis)
1960 printf("... : several occurrences of the previous object "
1961 "are possible.\n");
1962 if (has_rule)
1963 printf("[<|=|>]number: rules constraining the number of "
1964 "parameters/arguments.\n");
1968 /* ************************************************************ */
1969 /* Various utilities and callback functions called when walking */
1970 /* through a BST. */
1971 /* ************************************************************ */
1973 static void
1974 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1976 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1978 if (kind == postorder || kind == leaf)
1980 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1982 user_rc = 1;
1983 user_string = strappend(user_string, seen_opt->opt->params, " ",
1984 (char *)0);
1989 static void
1990 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1992 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1994 if (kind == postorder || kind == leaf)
1995 if (seen_opt->seen == 1)
1997 user_rc = 1;
1998 user_object = seen_opt->par;
2002 static void
2003 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
2005 ctx_t * ctx = main_ctx;
2006 ctx_t * cur_ctx = ((bst_t *)node)->key;
2008 ll_t * list;
2010 int has_optional = 0;
2011 int has_ellipsis = 0;
2012 int has_rule = 0;
2013 int has_generic_arg = 0;
2014 int has_ctx_change = 0;
2015 int has_early_eval = 0;
2017 if (kind == postorder || kind == leaf)
2018 if (strcmp(ctx->name, cur_ctx->name) != 0)
2020 list = cur_ctx->opt_list;
2022 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
2023 print_options(list, &has_optional, &has_ellipsis, &has_rule,
2024 &has_generic_arg, &has_ctx_change, &has_early_eval);
2025 print_before_constraints(list);
2029 static void
2030 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
2032 opt_t * opt = ((bst_t *)node)->key;
2034 if (kind == postorder || kind == leaf)
2036 if (opt->params == NULL) /* opt must have associated parameters. */
2037 fatal_internal("Option %s has no registered parameter.\n", opt->name);
2039 if (opt->action == NULL) /* opt must have an action. */
2040 fatal_internal("Option %s has no registered action.\n", opt->name);
2044 static void
2045 bst_match_par_cb(const void * node, walk_order_e kind, int level)
2047 ctx_t * ctx = ((bst_t *)node)->key;
2049 if (kind == postorder || kind == leaf)
2051 char * str = xstrdup(user_string);
2053 while (*str != '\0')
2055 if (locate_par(str, ctx) != NULL)
2057 if (*user_string2 == '\0')
2058 user_string2 = strappend(user_string2, "- ", ctx->name, (char *)0);
2059 else
2060 user_string2 = strappend(user_string2, "\n- ", ctx->name, (char *)0);
2061 break;
2063 str[strlen(str) - 1] = '\0';
2065 free(str);
2069 static void
2070 match_prefix_cb(const void * node, walk_order_e kind, int level)
2072 par_t * par = ((bst_t *)node)->key;
2074 if (kind == postorder || kind == leaf)
2075 if (strpref(par->name, (char *)user_object))
2077 user_rc++;
2078 user_string = strappend(user_string, par->name, " ", (char *)0);
2082 /* ====================================================================== */
2083 /* A parameter may not be separated from its first option by spaces, in */
2084 /* this case this function looks for a valid flag as a prefix and splits */
2085 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
2086 /* option). */
2087 /* */
2088 /* IN word : the word to be checked. */
2089 /* IN ctx : the context in which the flag indexed by the word is to be */
2090 /* checked. */
2091 /* OUT pos : the offset in word pointing just after the matching prefix. */
2092 /* OUT opt : a pointer to the option associated with the new parameter */
2093 /* or NULL if none is found. */
2094 /* */
2095 /* The returned pointer must be freed by the caller. */
2096 /* ====================================================================== */
2097 static char *
2098 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
2100 char * new = NULL;
2101 int len;
2102 par_t * par;
2103 par_t tmp_par = { 0 };
2105 len = strlen(word);
2107 if (len > 2)
2109 new = xstrdup(word);
2113 new[--len] = '\0';
2114 tmp_par.name = new;
2115 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2117 if (par != NULL)
2119 *pos = len;
2120 *opt = par->opt;
2122 else
2124 free(new);
2125 new = NULL;
2128 else
2129 *pos = 0;
2131 return new;
2134 /* ============================================================= */
2135 /* If par_name is an unique abbreviation of an exiting parameter */
2136 /* in the context ctx, then return this parameter. */
2137 /* ============================================================= */
2138 static char *
2139 abbrev_expand(char * par_name, ctx_t * ctx)
2141 user_object = par_name;
2142 user_rc = 0;
2144 *user_string = '\0';
2145 bst_walk(ctx->par_bst, match_prefix_cb);
2146 rtrim(user_string, " ", 0);
2148 /* The previous bst_walk has built a string of blank separated parameters */
2149 /* all having par_name as prefix. This string is put in the user_string */
2150 /* exchange zone. The number of these words in put in user_rc. */
2151 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2152 if (user_rc == 1) /* The number of matching abbreviations. */
2153 return xstrdup(user_string);
2154 else /* There is at least tho defined parameters starting with par_name. */
2156 char * s, *first_s;
2157 par_t * par;
2158 opt_t * opt;
2159 int opt_count = 0;
2160 void * tmp_opt_bst = NULL;
2162 /* Find all the options corresponding to these words and store them */
2163 /* without duplication in a temporary BST. Only their resulting count */
2164 /* matters. */
2165 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2166 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2167 | the first word. */
2168 while (s != NULL)
2170 par = locate_par(s, ctx);
2171 opt = par->opt;
2173 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2175 /* This option as not already been seen */
2176 /* store it and increase the seen counter. */
2177 /* """"""""""""""""""""""""""""""""""""""" */
2178 bst_search(opt, &tmp_opt_bst, opt_compare);
2179 opt_count++;
2181 s = strtok(NULL, " ");
2184 /* Clean the temporary BST without removing the pointer */
2185 /* to the real options. */
2186 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2187 if (tmp_opt_bst != NULL)
2188 bst_destroy(tmp_opt_bst, NULL);
2190 if (opt_count == 1)
2191 /* All the abbreviation are leading to only one option */
2192 /* We can just continue as in the previous case. */
2193 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2194 return xstrdup(first_s);
2195 else
2196 return NULL;
2200 /* ================================================================ */
2201 /* Terminate the program if mandatory options required by a context */
2202 /* are not present. */
2203 /* ================================================================ */
2204 static void
2205 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2207 char * missing;
2209 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2210 fatal(CTXOPTMISPAR, missing);
2213 /* ====================================================== */
2214 /* Return 1 if at least one mandatory option was not seen */
2215 /* when quitting a context, else 0. */
2216 /* ====================================================== */
2217 static int
2218 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2220 user_rc = 0;
2221 *user_string = '\0';
2223 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2224 rtrim(user_string, " ", 0);
2226 *missing = user_string;
2228 return user_rc ? 1 : 0;
2231 /* ========================================================================= */
2232 /* This function terminates the program if an option or its arguments do not */
2233 /* conform to its occurrences constraint. */
2234 /* There constraints can appear by trailing >, < or = in their definition */
2235 /* given in ctxopt_new_ctx. */
2236 /* ========================================================================= */
2237 static void
2238 check_for_occurrence_issues(ctx_inst_t * ctx_inst)
2240 ctx_t * ctx = ctx_inst->ctx;
2241 opt_t * opt;
2242 ll_node_t * node;
2243 opt_inst_t * opt_inst;
2244 char * cur_opt_params = cur_state->cur_opt_params;
2245 char * cur_opt_par_name = cur_state->cur_opt_par_name;
2247 /* Checks options. */
2248 /* """"""""""""""" */
2249 node = ctx->opt_list->head;
2251 while (node != NULL)
2253 opt = node->data;
2255 /* Update current_state. */
2256 /* """"""""""""""""""""" */
2257 cur_state->cur_opt_params = opt->params;
2258 cur_state->opts_count = opt->opt_count_mark;
2259 cur_state->opt_args_count = opt->opt_args_count_mark;
2261 if (opt->opt_count_matter)
2262 switch (opt->opt_count_oper)
2264 case '=':
2265 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2266 fatal(CTXOPTCTEOPT, "");
2267 break;
2269 case '<':
2270 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2271 fatal(CTXOPTCTLOPT, "");
2272 break;
2274 case '>':
2275 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2276 fatal(CTXOPTCTGOPT, "");
2277 break;
2280 node = node->next;
2283 /* Checks arguments. */
2284 /* """"""""""""""""" */
2285 node = ctx_inst->opt_inst_list->head;
2286 while (node != NULL)
2288 opt_inst = node->data;
2289 opt = opt_inst->opt;
2291 /* Update current_state. */
2292 /* """"""""""""""""""""" */
2293 cur_state->cur_opt_par_name = opt_inst->par;
2294 cur_state->opts_count = opt->opt_count_mark;
2295 cur_state->opt_args_count = opt->opt_args_count_mark;
2297 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2299 if (opt->opt_args_count_matter)
2300 switch (opt->opt_args_count_oper)
2302 case '=':
2303 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2304 fatal(CTXOPTCTEARG, "");
2305 break;
2307 case '<':
2308 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2309 fatal(CTXOPTCTLARG, "");
2310 break;
2312 case '>':
2313 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2314 fatal(CTXOPTCTGARG, "");
2315 break;
2318 node = node->next;
2320 cur_state->cur_opt_params = cur_opt_params;
2321 cur_state->cur_opt_par_name = cur_opt_par_name;
2324 /* ====================================================================== */
2325 /* This function terminates the program if all the options which are part */
2326 /* of a group of required options by some other option are missing. */
2327 /* ====================================================================== */
2328 static void
2329 check_for_requirement_issues(ctx_inst_t * ctx_inst)
2331 ll_node_t * node;
2332 ll_node_t * req_node;
2333 req_t * req;
2334 opt_t * opt;
2335 opt_t * req_opt;
2336 bst_t * bst_node;
2337 seen_opt_t tmp_seen_opt;
2338 int found;
2339 char * needed_params = NULL;
2341 node = ctx_inst->opt_req_list->head;
2343 while (node != NULL)
2345 req = node->data;
2347 opt = req->opt;
2348 tmp_seen_opt.opt = opt;
2350 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2351 seen_opt_compare);
2353 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2355 found = 0;
2356 req_node = req->or_opt_list->head;
2358 /* needed_params accumulates the params of the options in the group. */
2359 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2360 free(needed_params); /* free can applied to the NULL pointer. */
2361 needed_params = xstrdup("");
2363 /* Go through the list of the required group of options and */
2364 /* succeed when one of them has been seen in the context. */
2365 /* otherwise a fatal error is triggered and the program is */
2366 /* terminated. */
2367 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2368 while (req_node != NULL)
2370 req_opt = req_node->data;
2371 tmp_seen_opt.opt = req_opt;
2372 needed_params = strappend(needed_params, req_opt->params, "\n ",
2373 (char *)0);
2375 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2376 seen_opt_compare);
2378 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2380 found = 1; /* A required option has been seen, */
2381 break; /* accept the group. */
2383 req_node = req_node->next;
2386 rtrim(needed_params, "\n ", 0);
2388 /* This is a fatal error if none of the options in the required */
2389 /* options group has been seen in the context. */
2390 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2391 if (!found)
2393 char * errmsg;
2395 if (req->or_opt_list->len > 1)
2396 errmsg = xstrdup("At least one of the parameters among:\n %s\n"
2397 "requested by %s must be present.\n");
2398 else
2399 errmsg = xstrdup("The parameter %s "
2400 "requested by %s must be present.\n");
2402 cur_state->req_opt_par_needed = needed_params;
2403 cur_state->req_opt_par = opt->params;
2405 fatal(CTXOPTREQPAR, errmsg);
2409 node = node->next;
2413 /* ======================================================================== */
2414 /* Parse a strings describing options and some of their characteristics */
2415 /* The input string must have follow some rules like in the examples below: */
2416 /* */
2417 /* "opt_name1 opt_name2" */
2418 /* "[opt_name1] opt_name2" */
2419 /* "[opt_name1] opt_name2..." */
2420 /* "[opt_name1 #...] opt_name2... [#]" */
2421 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2422 /* */
2423 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2424 /* means that there can be more than one occurrence of the previous thing. */
2425 /* */
2426 /* opt_name can be followed by a 'new context' change prefixed with the */
2427 /* symbol >, as in opt1>c2 by eg. */
2428 /* */
2429 /* This function returns as soon as one (or no) option has been parsed and */
2430 /* return the offset to the next option to parse. */
2431 /* */
2432 /* In case of successful parsing, an new option is allocated and its */
2433 /* pointer returned. */
2434 /* ======================================================================== */
2435 static int
2436 opt_parse(char * s, opt_t ** opt)
2438 int opt_optional = 0;
2439 int opt_multiple = 0;
2440 int opt_count_matter = 0;
2441 char opt_count_oper = '\0';
2442 unsigned opt_count_mark = 0;
2443 int opt_args = 0;
2444 char opt_arg[33] = { 0 };
2445 int opt_multiple_args = 0;
2446 int opt_args_count_matter = 0;
2447 char opt_args_count_oper = '\0';
2448 unsigned opt_args_count_mark = 0;
2449 int opt_optional_args = 0;
2450 int opt_eval_first = 0;
2452 int n;
2453 int pos;
2454 int count = 0;
2456 char * s_orig = s;
2458 char * p;
2459 char * opt_name = NULL;
2460 char * next_ctx;
2461 char token[65];
2463 *opt = NULL;
2464 memset(opt_arg, '\0', 33);
2466 /* Strip the leading blanks. */
2467 /* """"""""""""""""""""""""" */
2468 while (isblank(*s))
2469 s++;
2471 if (*s == '[') /* Start of an optional option. */
2473 opt_optional = 1;
2474 s++;
2476 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2477 if (s == NULL)
2478 return -1; /* Empty string. */
2480 /* Early EOS, only return success if the option is mandatory. */
2481 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2482 if (!*s)
2483 if (opt_optional == 1)
2484 return -(s - s_orig - 1);
2486 /* Validate the option name */
2487 /* ALPHA+(ALPHANUM|_)* */
2488 /* """""""""""""""""""""""" */
2489 p = token;
2490 if (!isalpha(*p) && *p != '*')
2491 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2493 if (*p == '*')
2494 opt_eval_first = 1;
2496 p++;
2497 while (*p)
2499 if (!isalnum(*p) && *p != '_' && *p != '>')
2500 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2501 * a number or a _ */
2502 p++;
2505 if (opt_eval_first)
2506 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2507 else
2508 opt_name = xstrdup(token);
2510 if (*s == ']')
2512 s++;
2513 while (isblank(*s))
2514 s++;
2516 goto success;
2519 /* Check if it can appear multiple times by looking for the dots. */
2520 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2521 p = strtoken(s, token, 3, "[.]", &pos);
2522 if (p)
2524 if (strcmp(token, "...") == 0)
2526 opt_multiple = 1;
2527 s = p;
2528 if (*s == '<' || *s == '=' || *s == '>')
2530 unsigned value;
2531 int offset;
2533 n = sscanf(s + 1, "%u%n", &value, &offset);
2534 if (n == 1)
2536 opt_count_matter = 1;
2537 opt_count_oper = *s;
2538 opt_count_mark = value;
2540 s += offset + 1;
2543 else
2545 free(opt_name);
2546 return -(s - s_orig - 1);
2550 if (*s == ']')
2552 /* Abort on extraneous ] if the option is mandatory. */
2553 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2554 if (!opt_optional)
2556 free(opt_name);
2557 return -(s - s_orig - 1);
2560 s++; /* skip the ] */
2562 if (!*s || isblank(*s))
2563 goto success;
2564 else
2566 free(opt_name);
2567 return -(s - s_orig - 1);
2571 /* A blank separates the option name and the argument tag. */
2572 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2573 if (isblank(*s))
2575 char dots[4];
2577 while (isblank(*s))
2578 s++;
2580 if (!*s)
2581 goto success;
2583 pos = 0;
2584 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2585 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2587 opt_args = 1;
2588 opt_optional_args = 1;
2589 if (n == 2)
2590 opt_multiple_args = 1; /* There were dots. */
2592 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2594 if (*s == '<' || *s == '=' || *s == '>')
2596 unsigned value;
2597 int offset;
2599 n = sscanf(s + 1, "%u%n", &value, &offset);
2600 if (n == 1)
2602 opt_args_count_matter = 1;
2603 opt_args_count_oper = *s;
2604 opt_args_count_mark = value;
2606 s += offset + 1;
2609 /* Optional arg tag must end with a ] */
2610 /* """""""""""""""""""""""""""""""""" */
2611 if (*s != ']')
2613 free(opt_name);
2614 return -(s - s_orig - 1);
2617 s++; /* Skip the ] */
2619 else
2621 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2622 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2624 opt_args = 1;
2625 if (n == 2) /* There were dots. */
2626 opt_multiple_args = 1;
2628 s += pos + !!(n == 2) * 3; /* Skip the dots */
2630 if (*s == '<' || *s == '=' || *s == '>')
2632 unsigned value;
2633 int offset;
2635 n = sscanf(s + 1, "%u%n", &value, &offset);
2636 if (n == 1)
2638 opt_args_count_matter = 1;
2639 opt_args_count_oper = *s;
2640 opt_args_count_mark = value;
2642 s += offset + 1;
2646 if (*s == ']')
2648 /* Abort on extraneous ] if the option is mandatory. */
2649 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2650 if (!opt_optional)
2652 free(opt_name);
2653 return -(s - s_orig - 1);
2656 s++; /* skip the ] */
2658 /* Strip the following blanks. */
2659 /* """"""""""""""""""""""""""" */
2660 while (isblank(*s))
2661 s++;
2663 goto success;
2665 else if (opt_optional == 0 && (!*s || isblank(*s)))
2667 /* Strip the following blanks. */
2668 /* """"""""""""""""""""""""""" */
2669 while (isblank(*s))
2670 s++;
2672 goto success;
2674 else if (opt_args == 0) /* # was not read it is possibly the start *
2675 * of another option. */
2676 goto success;
2677 else
2679 free(opt_name);
2680 return -(s - s_orig - 1);
2684 success:
2686 /* Strip the following blanks. */
2687 /* """"""""""""""""""""""""""" */
2688 while (isblank(*s))
2689 s++;
2691 next_ctx = NULL;
2693 if (*opt_name == '>')
2694 fatal_internal("The option name is missing in %s.", opt_name);
2696 count = strchrcount(opt_name, '>');
2697 if (count == 1)
2699 char * tmp = strchr(opt_name, '>');
2700 next_ctx = xstrdup(tmp + 1);
2701 *tmp = '\0';
2703 else if (count > 1)
2704 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2706 *opt = xmalloc(sizeof(opt_t));
2708 (*opt)->name = opt_name;
2709 (*opt)->optional = opt_optional;
2710 (*opt)->multiple = opt_multiple;
2711 (*opt)->opt_count_matter = opt_count_matter;
2712 (*opt)->opt_count_oper = opt_count_oper;
2713 (*opt)->opt_count_mark = opt_count_mark;
2714 (*opt)->args = opt_args;
2715 (*opt)->arg = xstrdup(opt_arg);
2716 (*opt)->optional_args = opt_optional_args;
2717 (*opt)->multiple_args = opt_multiple_args;
2718 (*opt)->opt_args_count_matter = opt_args_count_matter;
2719 (*opt)->opt_args_count_oper = opt_args_count_oper;
2720 (*opt)->opt_args_count_mark = opt_args_count_mark;
2721 (*opt)->eval_first = opt_eval_first;
2722 (*opt)->next_ctx = next_ctx;
2723 (*opt)->ctx_list = ll_new();
2724 (*opt)->constraints_list = ll_new();
2725 (*opt)->eval_before_list = ll_new();
2726 (*opt)->action = NULL;
2727 (*opt)->params = NULL;
2728 (*opt)->data = NULL;
2730 return s - s_orig;
2733 /* ==================================================================== */
2734 /* Try to initialize all the option in a given string */
2735 /* Each parsed option are put in a BST tree with its name as index. */
2736 /* */
2737 /* On collision, the arguments only the signature are required to be */
2738 /* the same else this is considered as an error. Options can be used in */
2739 /* more than one context and can be optional in one and mandatory in */
2740 /* another. */
2741 /* ==================================================================== */
2742 static int
2743 init_opts(char * spec, ctx_t * ctx)
2745 opt_t * opt, *bst_opt;
2746 bst_t * node;
2747 int offset;
2749 while (*spec)
2751 if ((offset = opt_parse(spec, &opt)) > 0)
2753 spec += offset;
2755 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2757 int same_next_ctx = 0;
2759 bst_opt = node->key; /* Node extracted from the BST. */
2761 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2762 same_next_ctx = 1;
2763 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2764 same_next_ctx = 0;
2765 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2766 same_next_ctx = 0;
2767 else
2768 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2770 if (bst_opt->optional_args != opt->optional_args
2771 || bst_opt->multiple_args != opt->multiple_args
2772 || bst_opt->args != opt->args || !same_next_ctx)
2774 fatal_internal("The option %s already exists with "
2775 "a different arguments signature.\n",
2776 opt->name);
2779 /* The newly created opt is already present in options_bst. */
2780 /* We can remove it. */
2781 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2782 opt_free(opt);
2784 /* The new occurrence of the option option is legal */
2785 /* append the current context ptr in the list. */
2786 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2787 ll_append(bst_opt->ctx_list, ctx);
2789 /* Append the new option to the context's options list. */
2790 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2791 ll_append(ctx->opt_list, bst_opt);
2793 else
2795 /* Initialize the option's context list with the current context. */
2796 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2797 ll_append(opt->ctx_list, ctx);
2799 /* Append the new option to the context's options list. */
2800 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2801 ll_append(ctx->opt_list, opt);
2803 /* Insert the new option in the BST. */
2804 /* """"""""""""""""""""""""""""""""" */
2805 bst_search(opt, &options_bst, opt_compare);
2808 else
2810 char * s = xstrndup(spec, -offset);
2811 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2812 free(s);
2814 exit(EXIT_FAILURE);
2818 return 1;
2821 /* ===================================================== */
2822 /* ctxopt initialization function, must be called first. */
2823 /* ===================================================== */
2824 void
2825 ctxopt_init(char * prog_name, char * init_flags)
2827 int n;
2829 contexts_bst = NULL;
2830 options_bst = NULL;
2831 char * ptr;
2833 user_rc = 0;
2834 user_value = 0;
2835 user_string = xmalloc(8);
2836 user_string2 = xmalloc(8);
2837 user_object = NULL;
2838 char flag[33], fname[31], vname[31];
2839 int invalid;
2841 ctxopt_initialized = 1;
2843 /* Initialize current_state.*/
2844 /* """""""""""""""""""""""" */
2845 cur_state = xcalloc(sizeof(state_t), 0);
2847 /* Initialize custom error function pointers to NULL. */
2848 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2849 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2850 for (n = 0; n < CTXOPTERRSIZ; n++)
2851 err_functions[n] = NULL;
2853 /* Parse init_flags if any. */
2854 /* """""""""""""""""""""""" */
2855 while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2857 if (*flag)
2859 if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2860 fatal_internal("Invalid flag assignment: %s.", flag);
2862 if (strcmp(fname, "stop_if_non_option") == 0)
2864 if (eval_yes(vname, &invalid))
2865 flags.stop_if_non_option = 1;
2866 else if (!invalid)
2867 flags.stop_if_non_option = 0;
2868 else
2869 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2871 else if (strcmp(fname, "allow_abbreviations") == 0)
2873 if (eval_yes(vname, &invalid))
2874 flags.allow_abbreviations = 1;
2875 else if (!invalid)
2876 flags.allow_abbreviations = 0;
2877 else
2878 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2880 else if (strcmp(fname, "display_usage_on_error") == 0)
2882 if (eval_yes(vname, &invalid))
2883 flags.display_usage_on_error = 1;
2884 else if (!invalid)
2885 flags.display_usage_on_error = 0;
2886 else
2887 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2889 else
2890 fatal_internal("Invalid flag name: %s.", fname);
2894 /* Update current_state. */
2895 /* """"""""""""""""""""" */
2896 if (prog_name)
2898 if (*prog_name == '\0')
2899 cur_state->prog_name = xstrdup("program_name");
2900 else if ((ptr = strrchr(prog_name, '/')))
2901 cur_state->prog_name = xstrdup(ptr + 1);
2902 else
2903 cur_state->prog_name = xstrdup(prog_name);
2905 else
2906 cur_state->prog_name = xstrdup("program_name");
2909 /* ========================================================================= */
2910 /* Utility function which create and register a par_t object in a BST */
2911 /* embedded in a context. */
2912 /* This object will have a name and a pointer to the option it refers to. */
2913 /* These object will be used to quickly find an option from a command */
2914 /* line parameter during the analysis phase. */
2915 /* */
2916 /* IN : an option name. */
2917 /* IN : a string of command line parameters to associate to the option. */
2918 /* Returns : 1 is all was fine else 0. */
2919 /* ========================================================================= */
2920 static int
2921 opt_set_parms(char * opt_name, char * par_str)
2923 char * par_name, *ctx_name;
2924 char * tmp_par_str, *end_tmp_par_str;
2925 ctx_t * ctx;
2926 opt_t * opt;
2927 bst_t * node;
2928 par_t * par, tmp_par;
2929 int rc = 1; /* return code */
2931 ll_t * list;
2932 ll_node_t * lnode;
2934 /* Look if the given option is defined. */
2935 /* """""""""""""""""""""""""""""""""""" */
2936 opt = locate_opt(opt_name);
2937 if (opt == NULL)
2938 fatal_internal("Unknown option %s.", opt_name);
2940 /* For each context using this option. */
2941 /* """"""""""""""""""""""""""""""""""" */
2942 list = opt->ctx_list;
2944 lnode = list->head;
2945 while (lnode != NULL)
2947 /* Locate the context in the contexts tree. */
2948 /* """""""""""""""""""""""""""""""""""""""" */
2949 ctx_name = ((ctx_t *)(lnode->data))->name;
2951 ctx = locate_ctx(ctx_name);
2952 if (ctx == NULL)
2953 fatal_internal("Unknown context %s.", ctx_name);
2954 else
2956 void * par_bst = ctx->par_bst;
2958 tmp_par_str = xstrdup(par_str);
2959 ltrim(tmp_par_str, " \t");
2960 rtrim(tmp_par_str, " \t", 0);
2961 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2962 if (par_name == NULL)
2963 fatal_internal("Parameters are missing for option %s.", opt_name);
2965 /* For each parameter given in par_str, creates a par_t object and */
2966 /* insert it the in the parameters BST of the context. */
2967 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2968 while (par_name != NULL)
2970 tmp_par.name = par_name;
2972 node = bst_find(&tmp_par, &par_bst, par_compare);
2973 if (node != NULL)
2975 fatal_internal("The parameter %s is already defined in context %s.",
2976 par_name, ctx->name);
2977 rc = 0;
2979 else
2981 par = xmalloc(sizeof(par_t));
2982 par->name = xstrdup(par_name);
2983 par->opt = opt; /* Link the option to this parameter */
2985 bst_search(par, &par_bst, par_compare);
2987 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2990 /* Update the value of the root of ctx->par_bst as it may have */
2991 /* been modified. */
2992 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2993 ctx->par_bst = par_bst;
2995 free(tmp_par_str);
2997 lnode = lnode->next;
3000 return rc;
3003 /* ==================================================================== */
3004 /* Create a new context instance. */
3005 /* IN ctx : a context pointer to allow this instance to */
3006 /* access the context fields */
3007 /* IN prev_ctx_inst : the context instance whose option leading to the */
3008 /* creation of this new context instance is part of */
3009 /* Returns : the new context. */
3010 /* ==================================================================== */
3011 static ctx_inst_t *
3012 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
3014 opt_t * opt;
3015 opt_inst_t * gen_opt_inst;
3016 ctx_inst_t * ctx_inst;
3017 seen_opt_t * seen_opt;
3018 char * str, *opt_name;
3019 void * bst;
3020 bst_t * bst_node;
3022 /* Keep a trace of the opt_inst which was at the origin of the creation */
3023 /* of this context instance. */
3024 /* This will serve during the evaluation of the option callbacks. */
3025 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3026 if (prev_ctx_inst != NULL)
3028 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
3030 /* Update current_state. */
3031 /* """"""""""""""""""""" */
3032 cur_state->opt_name = gen_opt_inst->opt->name;
3034 else
3035 gen_opt_inst = NULL;
3037 /* Create and initialize the new context instance. */
3038 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3039 ctx_inst = xmalloc(sizeof(ctx_inst_t));
3040 ctx_inst->ctx = ctx;
3041 ctx_inst->prev_ctx_inst = prev_ctx_inst;
3042 ctx_inst->gen_opt_inst = gen_opt_inst;
3043 ctx_inst->incomp_bst_list = ll_new();
3044 ctx_inst->opt_inst_list = ll_new();
3045 ctx_inst->opt_req_list = ll_new();
3046 ctx_inst->seen_opt_bst = NULL;
3048 ll_node_t * node;
3050 if (prev_ctx_inst == NULL)
3051 first_ctx_inst = ctx_inst;
3053 /* Initialize the occurrence counters of each opt allowed in the context. */
3054 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3055 node = ctx->opt_list->head;
3056 while (node != NULL)
3058 opt = node->data;
3059 opt->occurrences = 0;
3061 node = node->next;
3064 /* Initialize the BST containing the seen indicator for all the options */
3065 /* allowed in this context instance. */
3066 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3067 node = ctx->opt_list->head;
3068 while (node != NULL)
3070 opt = node->data;
3071 seen_opt = xmalloc(sizeof(seen_opt_t));
3072 seen_opt->opt = opt;
3073 seen_opt->par = NULL;
3074 seen_opt->seen = 0;
3076 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
3078 node = node->next;
3081 /* Initialize the BST containing the incompatibles options. */
3082 /* Incompatibles option names are read from strings found in the list */
3083 /* incomp_list present in each instance of ctx_t. */
3084 /* These names are then used to search for the object of type seen_opt_t */
3085 /* which is already present in the seen_opt_bst of the context instance. */
3086 /* in the BST. */
3087 /* Once found the seen_opt_t object in inserted in the new BST */
3088 /* At the end the new BST in added to the list incomp_bst_list. */
3089 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3090 node = ctx->incomp_list->head;
3091 while (node != NULL)
3093 bst = NULL;
3094 seen_opt_t tmp_seen_opt;
3096 str = xstrdup(node->data);
3097 ltrim(str, " \t");
3098 rtrim(str, " \t", 0);
3099 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3101 while (opt_name != NULL) /* For each option name. */
3103 if ((opt = locate_opt(opt_name)) != NULL)
3105 /* The option found is searched in the tree of potential */
3106 /* seen options. */
3107 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3108 tmp_seen_opt.opt = opt;
3110 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
3111 seen_opt_compare);
3113 if (bst_node != NULL)
3115 /* If found then it is added into the new BST tree. */
3116 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3117 seen_opt = bst_node->key;
3118 bst_search(seen_opt, &bst, seen_opt_compare);
3120 else
3121 /* Not found! That means that the option is unknown in this */
3122 /* context as all options has have a seen_opt structure in */
3123 /* seen_opt_bst. */
3124 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3125 fatal_internal("%s is not known in the context %s.", opt->name,
3126 ctx->name);
3128 else
3129 fatal_internal("Unknown option %s.", opt_name);
3131 opt_name = strtok(NULL, " \t");
3134 free(str);
3135 ll_append(ctx_inst->incomp_bst_list, bst);
3137 node = node->next;
3140 /* Initialize the list of res_t structures according to the */
3141 /* list set in the context by ctxopt_add_ctx_settings/required. */
3142 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3143 node = ctx->req_list->head;
3144 while (node != NULL)
3146 req_t * req = xmalloc(sizeof(req_t));
3148 str = xstrdup(node->data);
3149 ltrim(str, " \t");
3150 rtrim(str, " \t", 0);
3151 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3153 if ((opt = locate_opt(opt_name)) != NULL)
3155 req->opt = opt;
3156 req->or_opt_list = ll_new();
3157 while ((opt_name = strtok(NULL, " \t")) != NULL)
3159 if ((opt = locate_opt(opt_name)) != NULL)
3160 ll_append(req->or_opt_list, opt);
3161 else
3162 fatal_internal("Unknown option %s.", opt_name);
3164 ll_append(ctx_inst->opt_req_list, req);
3166 else
3167 fatal_internal("Unknown option %s.", opt_name);
3169 free(str);
3171 node = node->next;
3173 return ctx_inst;
3176 /* ====================================================================== */
3177 /* Create a list formed by all the significant command line words */
3178 /* Words beginning or ending with { or } are split. Each of these */
3179 /* symbols will get their own place in the list. */
3180 /* */
3181 /* the {...} part delimits a context, the { will not appear in the list */
3182 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3183 /* to facilitate the parsing phase. | must not be used by the end user. */
3184 /* */
3185 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3186 /* program name is not considered. */
3187 /* IN words : is the array of strings constituting the command line to */
3188 /* parse. */
3189 /* Returns : 1 on success, 0 if a { or } is missing. */
3190 /* ====================================================================== */
3191 static int
3192 ctxopt_build_cmdline_list(int nb_words, char ** words)
3194 int i;
3195 char * prev_word = NULL;
3196 char * word;
3197 char * ptr;
3198 int level = 0;
3199 ll_node_t *node, *start_node;
3201 /* The analysis is divided into three passes, this is not optimal but */
3202 /* must be done only one time. Doing that we privilege readability. */
3203 /* */
3204 /* In the following, SG is the ascii character 1d (dec 29) */
3205 /* */
3206 /* The first pass creates the list, extract the leading an trailing */
3207 /* SG '{' and '}' of each word and give them their own place in the */
3208 /* list */
3209 /* */
3210 /* The second pass transform the '{...}' blocks by a trailing SG */
3211 /* ({...} -> ...|) */
3212 /* */
3213 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3214 /* the middle in the remaining list elements and recreate the pseudo */
3215 /* argument: {} */
3216 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3218 /* If the option list is not empty, clear it before going further. */
3219 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3220 if (cmdline_list != NULL)
3221 ll_destroy(cmdline_list, free);
3223 cmdline_list = ll_new();
3225 start_node = cmdline_list->head; /* In the following loop start_node will *
3226 * contain a pointer to the current *
3227 * word stripped from its leading *
3228 * sequence of {, }. */
3229 for (i = 0; i < nb_words; i++)
3231 size_t len = strlen(words[i]);
3232 size_t start, end;
3233 char * str;
3235 str = words[i];
3237 /* Replace each occurrence of the legal word {} by the characters */
3238 /* 0x02 and 0x03 to hide them from the following process. */
3239 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3240 while ((ptr = strstr(str, "{}")) != NULL)
3242 *ptr = 0x02; /* Arbitrary values unlikely. */
3243 *(ptr + 1) = 0x03; /* present in a word */
3246 if (len > 1) /* The word contains at least 2 characters. */
3248 start = 0;
3250 /* Interpret its beginning and look for the start of the real word. */
3251 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3252 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
3254 ll_append(cmdline_list, xstrndup(str + start, 1));
3255 start++;
3256 start_node = cmdline_list->tail;
3259 end = len - 1;
3260 if (str[end] == '{' || str[end] == '}')
3262 if (end > 0 && str[end - 1] != '\\')
3264 ll_append(cmdline_list, xstrndup(str + end, 1));
3265 end--;
3266 node = cmdline_list->tail;
3268 while (str[end] == '{' || str[end] == '}')
3270 if (end > start && str[end - 1] == '\\')
3271 break;
3273 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3274 end--;
3275 node = node->prev;
3280 if (start <= end)
3282 if (start_node != NULL)
3283 ll_insert_after(cmdline_list, start_node,
3284 xstrndup(str + start, end - start + 1));
3285 else
3286 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3287 start_node = cmdline_list->tail;
3290 else if (len == 1)
3292 ll_append(cmdline_list, xstrdup(str));
3293 start_node = cmdline_list->tail;
3297 /* 2nd pass. */
3298 /* """"""""" */
3299 node = cmdline_list->head;
3301 level = 0;
3302 while (node != NULL)
3304 word = node->data;
3306 if (strcmp(word, "{") == 0)
3308 ll_node_t * old_node = node;
3309 level++;
3310 node = node->next;
3311 free(word);
3312 ll_delete(cmdline_list, old_node);
3314 else if (strcmp(word, "}") == 0)
3316 level--;
3318 if (level < 0)
3319 return 0;
3320 else
3321 *word = 0x1d;
3323 else
3324 node = node->next;
3327 if (level != 0)
3328 return 0;
3330 /* 3rd pass. */
3331 /* """"""""" */
3332 node = cmdline_list->head;
3334 while (node != NULL)
3336 word = node->data;
3338 /* Restore the original { and } characters forming the legal word {}. */
3339 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3340 while ((ptr = strchr(word, 0x02)) != NULL)
3341 *ptr = '{';
3342 while ((ptr = strchr(word, 0x03)) != NULL)
3343 *ptr = '}';
3345 /* Remove a SG if the previous element is SG. */
3346 /* """""""""""""""""""""""""""""""""""""""""" */
3347 if (strcmp(word, "\x1d") == 0)
3349 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3351 ll_node_t * old_node = node;
3352 node = node->prev;
3353 free(old_node->data);
3354 ll_delete(cmdline_list, old_node);
3357 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3358 * a parameter. Protect it. */
3360 free(node->data);
3361 node->data = xstrdup("\\-");
3364 prev_word = node->data;
3365 node = node->next;
3368 /* Clean useless and SG at the beginning and end of list. */
3369 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3370 node = cmdline_list->head;
3372 if (node == NULL)
3373 return 1;
3375 word = node->data;
3377 if (strcmp(word, "\x1d") == 0)
3379 free(word);
3380 ll_delete(cmdline_list, node);
3383 node = cmdline_list->tail;
3384 if (node == NULL)
3385 return 1;
3387 word = node->data;
3389 if (strcmp(word, "\x1d") == 0)
3391 free(word);
3392 ll_delete(cmdline_list, node);
3395 return 1;
3398 /* ===================================================================== */
3399 /* Build and analyze the command line list and create the linked data */
3400 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3401 /* This function identifies the following errors and creates an array of */
3402 /* The remaining unanalyzed arguments. */
3403 /* - detect missing arguments */
3404 /* - detect too many arguments */
3405 /* - detect unknown parameters in a context */
3406 /* - detect too many occurrences of a parameters in a context */
3407 /* - detect missing required arguments in a context */
3408 /* */
3409 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3410 /* program name is not considered */
3411 /* IN words : is the array of strings constituting the command line to */
3412 /* parse. */
3413 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3414 /* is present in the list. */
3415 /* OUT rem_args : array of remaining command line arguments if a -- */
3416 /* is present in the list. This array must be free by */
3417 /* The caller as it is allocated here. */
3418 /* ===================================================================== */
3419 void
3420 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3421 char *** rem_args)
3423 char * ctxopt_debug_env; /* Environment variable CTXOPT_DEBUG content. */
3424 int ctxopt_debug; /* 1 if ctxopt_debug_env is set and not empty. *
3425 | 0 if ctxopt_debug_env is unset or empty. */
3427 ctx_t * ctx;
3428 opt_t * opt;
3429 par_t * par;
3430 ctx_inst_t * ctx_inst;
3431 opt_inst_t * opt_inst;
3432 int expect_par = 0;
3433 int expect_arg = 0;
3434 int expect_par_or_arg = 0;
3436 ll_node_t * cli_node;
3437 bst_t * bst_node;
3438 seen_opt_t * bst_seen_opt;
3439 char * par_name;
3440 void * bst;
3442 ll_node_t * node;
3444 if (!ctxopt_build_cmdline_list(nb_words, words))
3445 fatal_internal("The command line could not be parsed: "
3446 "missing '{' or '}' detected.");
3448 if (main_ctx == NULL)
3449 fatal_internal("At least one context must have been created.");
3451 /* Check that all options has an action and at least one parameter. */
3452 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3453 bst_walk(options_bst, bst_check_opt_cb);
3455 /* CTXOPT debug setting */
3456 /* """""""""""""""""""" */
3457 ctxopt_debug_env = getenv("CTXOPT_DEBUG");
3458 if (ctxopt_debug_env != NULL && *ctxopt_debug_env != '\0')
3459 ctxopt_debug = 1;
3460 else
3461 ctxopt_debug = 0;
3463 /* Create the first ctx_inst record. */
3464 /* """"""""""""""""""""""""""""""""" */
3465 ctx = main_ctx;
3467 ctx_inst_list = ll_new();
3468 ctx_inst = new_ctx_inst(ctx, NULL);
3469 ctx_inst->par_name = NULL;
3471 /* Update current_state. */
3472 /* """"""""""""""""""""" */
3473 cur_state->ctx_name = ctx->name;
3475 ll_append(ctx_inst_list, ctx_inst);
3477 /* For each node in the command line. */
3478 /* """""""""""""""""""""""""""""""""" */
3479 cli_node = cmdline_list->head;
3480 expect_par = 1;
3481 par_name = NULL;
3483 while (cli_node != NULL)
3485 if (strcmp(cli_node->data, "--") == 0)
3486 break; /* No new parameter will be analyzed after this point. */
3488 par_name = cli_node->data;
3490 /* Replace a leading -- by a single - */
3491 /* """""""""""""""""""""""""""""""""" */
3492 if (strncmp(par_name, "--", 2) == 0)
3493 par_name += 1; /* Ignore the first dash */
3495 if (strcmp(par_name, "\x1d") == 0)
3497 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3498 check_for_occurrence_issues(ctx_inst);
3499 check_for_requirement_issues(ctx_inst);
3501 /* Forced backtracking to the previous context instance. */
3502 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3503 if (ctx_inst->prev_ctx_inst != NULL)
3505 ctx_inst = ctx_inst->prev_ctx_inst;
3506 ctx = ctx_inst->ctx;
3508 /* Update current_states. */
3509 /* """"""""""""""""""""" */
3510 cur_state->ctx_name = ctx->name;
3511 cur_state->ctx_par_name = ctx_inst->par_name;
3513 if (ctxopt_debug)
3514 fprintf(stderr,
3515 "CTXOPT_DEBUG: Context forced backtrack, "
3516 "new current context: %s.\n",
3517 ctx->name);
3519 else
3521 /* Update current_state. */
3522 /* """"""""""""""""""""" */
3523 cur_state->ctx_par_name = NULL;
3526 else if (expect_par && *par_name == '-')
3528 int pos = 0;
3529 char * prefix;
3531 /* Update current_state. */
3532 /* """"""""""""""""""""" */
3533 cur_state->cur_opt_par_name = par_name;
3534 cur_state->ctx_name = ctx->name;
3535 cur_state->ctx_par_name = ctx_inst->par_name;
3537 if (ctxopt_debug)
3538 fprintf(stderr, "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3539 par_name, cur_state->ctx_name);
3541 /* An expected parameter has been seen. */
3542 /* """""""""""""""""""""""""""""""""""" */
3543 if ((par = locate_par(par_name, ctx)) == NULL)
3545 opt_t * popt;
3546 char * word;
3548 /* Look if this parameter is an unique abbreviation of a longer */
3549 /* parameter. If this is the case then just replace it with its */
3550 /* full length version and try again. */
3551 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3552 if (flags.allow_abbreviations)
3553 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3555 cli_node->data = word;
3556 continue;
3559 /* Try to find a prefix which is a valid parameter in this context */
3560 /* If found, split the cli_node in two to build a new parameter */
3561 /* node and followed by a node containing the remaining string */
3562 /* If the new parameter corresponds to an option not taking */
3563 /* argument then prefix the remaining string whit a dash as it may */
3564 /* contain a new parameter. */
3565 /* The new parameter will be re-evaluated in the next iteration of */
3566 /* the loop. */
3567 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3568 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3569 if (prefix != NULL && pos != 0)
3571 if (ctxopt_debug)
3572 fprintf(stderr,
3573 "CTXOPT_DEBUG: Found a valid parameter "
3574 "as a prefix of %s: %s.\n",
3575 par_name, prefix);
3577 cli_node->data = prefix; /* prefix contains le name of a valid *
3578 | parameter in this context. */
3580 if (popt->args)
3582 /* The parameter may be followed by arguments. */
3583 /* ''''''''''''''''''''''''''''''''''''''''''' */
3584 if (*(par_name + pos) == '-')
3586 word = xstrdup("\\"); /* Protect the '-' */
3587 word = strappend(word, par_name + pos, (char *)0);
3589 else
3590 word = xstrdup(par_name + pos);
3592 else
3594 /* The parameter does not take arguments, the */
3595 /* following word must be a parameter or nothing */
3596 /* hence prefix it with a dash. */
3597 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3598 word = xstrdup("-");
3599 word = strappend(word, par_name + pos, (char *)0);
3602 /* Insert it after the current node in the list. */
3603 /* """"""""""""""""""""""""""""""""""""""""""""" */
3604 ll_insert_after(cmdline_list, cli_node, word);
3606 continue; /* loop */
3608 else
3610 check_for_missing_mandatory_opt(ctx_inst, par_name);
3611 check_for_occurrence_issues(ctx_inst);
3612 check_for_requirement_issues(ctx_inst);
3614 if (ctx_inst->prev_ctx_inst == NULL)
3616 char * errmsg = xstrdup("");
3618 /* Update current_state. */
3619 /* """"""""""""""""""""" */
3620 cur_state->ctx_par_name = NULL;
3622 *user_string = '\0';
3623 *user_string2 = '\0';
3625 user_string = strappend(user_string, par_name, (char *)0);
3627 bst_walk(contexts_bst, bst_match_par_cb);
3629 if (*user_string2 != '\0')
3631 char * help_msg;
3633 if (flags.display_usage_on_error)
3634 help_msg = "see below.\n";
3635 else
3636 help_msg = "\nrefer to the manual for more information.\n";
3638 errmsg = strappend(
3639 errmsg,
3640 "\nThis parameter is only valid in one of the following "
3641 "contexts:\n",
3642 user_string2,
3643 "\n\nSwitch to one of them first using the appropriate "
3644 "parameter, ",
3645 help_msg, (char *)0);
3648 fatal(CTXOPTUNKPAR, errmsg);
3650 else
3652 /* Tries to backtrack and analyse the same parameter in the */
3653 /* previous context. */
3654 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3655 ctx_inst = ctx_inst->prev_ctx_inst;
3656 ctx = ctx_inst->ctx;
3658 if (ctxopt_debug)
3659 fprintf(stderr,
3660 "CTXOPT_DEBUG: Context backtrack, "
3661 "new current context: %s.\n",
3662 ctx->name);
3664 /* Update current_state. */
3665 /* """"""""""""""""""""" */
3666 cur_state->ctx_name = ctx->name;
3667 cur_state->ctx_par_name = ctx_inst->par_name;
3669 cli_node = cli_node->prev;
3673 else
3675 seen_opt_t seen_opt;
3677 /* The parameter is valid in the context, create a opt_inst and */
3678 /* append it to the ctx_inst list options list. */
3679 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3680 opt = par->opt;
3682 opt->occurrences++;
3684 opt_inst = xmalloc(sizeof(opt_inst_t));
3685 opt_inst->opt = opt;
3686 opt_inst->par = par_name;
3687 opt_inst->values_list = ll_new();
3688 opt_inst->next_ctx_inst = NULL;
3690 /* Update current_state. */
3691 /* """"""""""""""""""""" */
3692 cur_state->cur_opt_params = opt->params;
3694 /* Priority option are inserted at the start of the opt_inst list */
3695 /* but their order of appearance in the context definition must */
3696 /* be preserver so each new priority option will be placed after */
3697 /* the previous ones at the start of the opt_inst list. */
3698 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3699 if (!opt->eval_first)
3701 /* Look if we have a registered dependency in the order of the */
3702 /* evaluation of two options. */
3703 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3704 if (opt->eval_before_list->len > 0)
3706 ll_t * list = ctx_inst->opt_inst_list;
3707 ll_node_t * opt_inst_node;
3709 ll_t * before_list = opt->eval_before_list;
3710 ll_node_t * before_node = before_list->head;
3712 ll_node_t * target_node = NULL; /* If not NULL, the new node *
3713 | will be inserted before it. */
3715 /* For each entry in eval_before_list, try to find if it */
3716 /* refers to an option already entered in the context. If this */
3717 /* is the case, insert it just before it instead of putting it */
3718 /* at the end. */
3719 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3720 while (before_node != NULL)
3722 opt_inst_node = list->head;
3724 while (opt_inst_node != target_node)
3726 opt_t * tmp_opt = (((opt_inst_t *)opt_inst_node->data))->opt;
3728 /* We have found an option mentioned if the before_list */
3729 /* of the option we want to add. We can stop searching. */
3730 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3731 if (strcmp(tmp_opt->name, ((opt_t *)before_node->data)->name))
3732 opt_inst_node = opt_inst_node->next;
3733 else
3734 target_node = opt_inst_node; /* Set the target node. */
3737 before_node = before_node->next;
3740 /* Insert or append ? */
3741 /* """""""""""""""""" */
3742 if (target_node != NULL)
3743 ll_insert_before(ctx_inst->opt_inst_list, target_node, opt_inst);
3744 else
3745 ll_append(ctx_inst->opt_inst_list, opt_inst);
3747 else
3748 ll_append(ctx_inst->opt_inst_list, opt_inst);
3750 else
3752 ll_node_t * opt_inst_node = ctx_inst->opt_inst_list->head;
3753 opt_inst_t * tmp_opt_inst;
3755 while (opt_inst_node != NULL)
3757 tmp_opt_inst = opt_inst_node->data;
3758 if (!tmp_opt_inst->opt->eval_first)
3760 ll_insert_before(ctx_inst->opt_inst_list, opt_inst_node,
3761 opt_inst);
3762 break;
3764 else
3765 opt_inst_node = opt_inst_node->next;
3767 if (opt_inst_node == NULL)
3768 ll_append(ctx_inst->opt_inst_list, opt_inst);
3771 /* Check if an option was already seen in the */
3772 /* current context instance. */
3773 /* """""""""""""""""""""""""""""""""""""""""" */
3774 seen_opt.opt = opt;
3776 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3777 seen_opt_compare);
3779 /* bst_node cannot be NULL here. */
3781 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3783 if (!opt->multiple && bst_seen_opt->seen == 1)
3784 fatal(CTXOPTDUPOPT, "");
3786 /* Check if this option is compatible with the options already */
3787 /* seen in this context instance. */
3788 /* Look if the option is present in one on the BST present in */
3789 /* the incomp_bst_list of the context instance. */
3790 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3791 node = ctx_inst->incomp_bst_list->head;
3792 while (node != NULL)
3794 bst = node->data;
3795 user_object = NULL;
3797 /* There can only have one seen_opt object in the BST tree was */
3798 /* already seen, try to locate it, the result will be put in */
3799 /* user_object by the bst_seen_opt_seen_cb function. */
3800 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3801 bst_walk(bst, bst_seen_opt_seen_cb);
3803 /* If it is the case, look if the current option is also */
3804 /* in this BST. */
3805 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3806 if (user_object != NULL)
3808 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3810 if (bst_node != NULL)
3812 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3813 if (bst_seen_opt->seen == 0)
3814 fatal(CTXOPTINCOPT, (char *)user_object);
3818 node = node->next;
3821 /* Mark this option as seen in the current context instance. */
3822 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3823 bst_seen_opt->seen = 1;
3824 free(bst_seen_opt->par);
3825 bst_seen_opt->par = xstrdup(par_name);
3827 /* If this option leads to a next context, create a new ctx_inst */
3828 /* and switch to it for the analyse of the future parameter. */
3829 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3830 if (opt->next_ctx != NULL)
3832 ctx = locate_ctx(opt->next_ctx);
3834 if (ctx == NULL)
3835 fatal_internal("Unknown context %s.", opt->next_ctx);
3837 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3838 ctx_inst->par_name = xstrdup(par_name);
3840 ll_append(ctx_inst_list, ctx_inst);
3842 if (ctxopt_debug)
3843 fprintf(stderr,
3844 "CTXOPT_DEBUG: Context change, "
3845 "new current context: %s.\n",
3846 ctx->name);
3849 /* Look is we must expect some arguments. */
3850 /* """""""""""""""""""""""""""""""""""""" */
3851 expect_par_or_arg = 0;
3852 expect_par = 0;
3853 expect_arg = 0;
3855 if (!opt->args)
3856 expect_par = 1; /* Parameter doesn't accept any argument. */
3857 else
3859 if (!opt->optional_args)
3860 expect_arg = 1; /* Parameter has mandatory arguments. */
3861 else
3862 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3866 else if (expect_par && *par_name != '-')
3868 ll_node_t * n = cli_node->next;
3870 if (!flags.stop_if_non_option)
3871 /* Look if potential arguments must still be analyzed until the */
3872 /* end of the context/command line part to analyze/command line. */
3873 /* If this is the case we have met an extra argument. */
3874 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3875 while (n != NULL)
3877 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3878 fatal(CTXOPTUNXARG, "");
3880 if (*(char *)(n->data) == '-')
3881 fatal(CTXOPTUNXARG, "");
3883 n = n->next;
3886 break; /* An unexpected non parameter was seen, if no Potential *
3887 | arguments remain in the command line or *
3888 | flags.stop_if_non_option is set, assume that it is is *
3889 | the first of the non arguments and stop the command *
3890 | line analysis. */
3892 else if (expect_arg && *par_name != '-')
3894 ll_node_t * cstr_node;
3895 constraint_t * cstr;
3897 if (ctxopt_debug)
3898 fprintf(stderr, "CTXOPT_DEBUG: Argument: %s.\n", par_name);
3900 /* Check if the arguments of the option respects */
3901 /* the attached constraints if any. */
3902 /* """"""""""""""""""""""""""""""""""""""""""""" */
3903 cstr_node = opt->constraints_list->head;
3904 while (cstr_node != NULL)
3906 cstr = cstr_node->data;
3907 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3908 cur_state->cur_opt_par_name))
3910 fputs("\n", stderr);
3911 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3914 cstr_node = cstr_node->next;
3917 /* If the argument is valid, store it. */
3918 /* """"""""""""""""""""""""""""""""""" */
3919 if (*par_name == '\\' && *(par_name + 1) == '-')
3920 ll_append(opt_inst->values_list, par_name + 1);
3921 else
3922 ll_append(opt_inst->values_list, par_name);
3924 expect_arg = 0;
3925 expect_par = 0;
3926 expect_par_or_arg = 0;
3928 if (opt->multiple_args)
3929 expect_par_or_arg = 1;
3930 else
3931 expect_par = 1; /* Parameter takes only one argument. */
3933 else if (expect_arg && *par_name == '-')
3934 fatal(CTXOPTMISARG, "");
3935 else if (expect_par_or_arg)
3937 expect_arg = 0;
3938 expect_par = 0;
3939 expect_par_or_arg = 0;
3941 if (*par_name != '-')
3942 expect_arg = 1; /* Consider this word as an argument and retry. */
3943 else
3944 expect_par = 1; /* Consider this word as a parameter and retry. */
3946 cli_node = cli_node->prev;
3949 cli_node = cli_node->next;
3952 if (cmdline_list->len > 0 && par_name && *par_name == '-')
3954 if (expect_arg && opt && !opt->optional_args)
3955 fatal(CTXOPTMISARG, "");
3958 /* Look if a context_instance has unseen mandatory options. */
3959 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3960 node = ctx_inst_list->head;
3961 while (node != NULL)
3963 ctx_inst = node->data;
3965 /* Update current_state. */
3966 /* """"""""""""""""""""" */
3967 cur_state->ctx_name = ctx_inst->ctx->name;
3968 cur_state->ctx_par_name = ctx_inst->par_name;
3970 check_for_missing_mandatory_opt(ctx_inst, par_name);
3971 check_for_occurrence_issues(ctx_inst);
3972 check_for_requirement_issues(ctx_inst);
3974 node = node->next;
3977 /* Allocate the array containing the remaining not analyzed */
3978 /* command line arguments. */
3979 /* NOTE: The strings in the array are just pointer to the */
3980 /* data of the generating list and must not be freed. */
3981 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3982 if (cli_node != NULL)
3984 if (strcmp((char *)cli_node->data, "--") == 0)
3985 /* The special parameter -- was encountered, the -- argument is not */
3986 /* put in the remaining arguments. */
3987 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3988 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3989 else
3990 /* A non parameter was encountered when a parameter was expected. We */
3991 /* assume that the evaluation of the remaining command line argument */
3992 /* are not the responsibility of the users code. */
3993 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3994 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3996 else
3998 *nb_rem_args = 0;
3999 *rem_args = xmalloc(sizeof(char *));
4000 (*rem_args)[0] = NULL;
4004 /* ==================================================== */
4005 /* Free ctxopt memory used for its internal structures. */
4006 /* ==================================================== */
4007 void
4008 ctxopt_free_memory(void)
4010 ll_destroy(cmdline_list, free);
4011 ll_destroy(ctx_inst_list, ctx_inst_free);
4012 bst_destroy(options_bst, opt_free);
4013 bst_destroy(contexts_bst, ctx_free);
4016 /* ==================================================================== */
4017 /* Parse the options data structures and launches the callback function */
4018 /* attached to each options instances. */
4019 /* This calls a recursive function which proceeds context per context. */
4020 /* ==================================================================== */
4021 void
4022 ctxopt_evaluate(void)
4024 evaluate_ctx_inst(first_ctx_inst);
4027 /* =================================================================== */
4028 /* Recursive function called by ctxopt_evaluate to process the list of */
4029 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
4030 /* action attached to the context and its option instances. */
4031 /* =================================================================== */
4032 static void
4033 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
4035 opt_inst_t * opt_inst;
4036 ctx_t * ctx;
4037 opt_t * opt;
4038 ll_node_t * opt_inst_node;
4039 char ** args;
4040 int nb_args;
4042 if (ctx_inst == NULL)
4043 return;
4045 ctx = ctx_inst->ctx;
4047 /* Do not evaluate the action attached to this context is there is no */
4048 /* option to evaluate. */
4049 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4050 opt_inst_node = ctx_inst->opt_inst_list->head;
4051 if (opt_inst_node == NULL)
4052 return;
4054 /* Call the entering action attached to this context if any. */
4055 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4056 if (ctx->action != NULL)
4058 if (ctx_inst->prev_ctx_inst != NULL)
4059 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
4060 ctx->nb_data, ctx->data);
4061 else
4062 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
4065 /* For each instance of options. */
4066 /* """"""""""""""""""""""""""""" */
4067 while (opt_inst_node != NULL)
4069 opt_inst = (opt_inst_t *)(opt_inst_node->data);
4070 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
4071 &args);
4072 opt = opt_inst->opt;
4074 /* Launch the attached action if any. */
4075 /* """""""""""""""""""""""""""""""""" */
4076 if (opt->action != NULL)
4077 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
4078 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
4080 if (opt_inst->next_ctx_inst != NULL)
4081 evaluate_ctx_inst(opt_inst->next_ctx_inst);
4083 if (args != NULL)
4084 free(args);
4086 opt_inst_node = opt_inst_node->next;
4089 /* Call the exiting action attached to this context if any. */
4090 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4091 if (ctx->action != NULL)
4093 if (ctx_inst->prev_ctx_inst != NULL)
4094 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
4095 ctx->nb_data, ctx->data);
4096 else
4097 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
4101 /* ============================================================ */
4102 /* Create and initializes a new context. */
4103 /* - allocate space. */
4104 /* - name it. */
4105 /* - initialize its option with a few of their characteristics. */
4106 /* ============================================================ */
4107 void
4108 ctxopt_new_ctx(char * name, char * opts_specs)
4110 ctx_t * ctx;
4111 char * p;
4113 if (!ctxopt_initialized)
4114 fatal_internal("Please call ctxopt_init first.");
4116 ctx = xmalloc(sizeof(ctx_t));
4118 /* Validates the context name: */
4119 /* ALPHA+(ALPHANUM|_)* */
4120 /* """"""""""""""""""""""""""" */
4121 p = name;
4122 if (!isalpha(*p))
4123 fatal_internal("A context name must start with a letter: %s.", name);
4125 p++;
4126 while (*p)
4128 if (!isalnum(*p) && *p != '_')
4129 fatal_internal("A context name must only contain letters, "
4130 "numbers or '_': %s.",
4131 name);
4132 p++;
4135 ctx->name = xstrdup(name);
4136 ctx->opt_list = ll_new(); /* List of options legit in this context. */
4137 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
4138 ctx->req_list = ll_new(); /* List of opts/required opts tuples (str). */
4139 ctx->par_bst = NULL;
4140 ctx->data = NULL;
4141 ctx->action = NULL;
4143 /* The first created context is the main one. */
4144 /* """""""""""""""""""""""""""""""""""""""""" */
4145 if (contexts_bst == NULL)
4147 main_ctx = ctx;
4149 cur_state->ctx_name = ctx->name;
4152 if (init_opts(opts_specs, ctx) == 0)
4153 exit(EXIT_FAILURE);
4154 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
4155 fatal_internal("The context %s already exists.", name);
4156 else
4157 bst_search(ctx, &contexts_bst, ctx_compare);
4160 /* ==================================================== */
4161 /* Display a usage screen limited to a specific context */
4162 /* IN: the context name. */
4163 /* IN: what to do after (continue or exit the program) */
4164 /* possible values: continue_after, exit_after. */
4165 /* ==================================================== */
4166 void
4167 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
4169 ctx_t * ctx;
4170 ll_t * list;
4172 int has_optional = 0;
4173 int has_ellipsis = 0;
4174 int has_rule = 0;
4175 int has_generic_arg = 0;
4176 int has_ctx_change = 0;
4177 int has_early_eval = 0;
4179 if (!flags.display_usage_on_error)
4180 return;
4182 ctx = locate_ctx(ctx_name);
4183 if (ctx == NULL)
4184 fatal_internal("Unknown context %s.", ctx_name);
4186 if (cur_state->ctx_par_name == NULL)
4187 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
4188 else
4189 printf("\nSynopsis for the context introduced by %s:\n",
4190 cur_state->ctx_par_name);
4192 list = ctx->opt_list;
4193 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4194 &has_ctx_change, &has_early_eval);
4196 print_before_constraints(list);
4198 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4199 has_optional, has_ellipsis, has_rule);
4201 if (action == exit_after)
4202 exit(EXIT_FAILURE);
4205 /* =================================================== */
4206 /* Display a full usage screen about all contexts. */
4207 /* IN: what to do after (continue or exit the program) */
4208 /* possible values: continue_after, exit_after. */
4209 /* =================================================== */
4210 void
4211 ctxopt_disp_usage(usage_behaviour action)
4213 ll_t * list;
4214 int has_optional = 0;
4215 int has_ellipsis = 0;
4216 int has_rule = 0;
4217 int has_generic_arg = 0;
4218 int has_ctx_change = 0;
4219 int has_early_eval = 0;
4221 if (!flags.display_usage_on_error)
4222 return;
4224 if (main_ctx == NULL)
4225 fatal_internal("At least one context must have been created.");
4227 /* Usage for the first context. */
4228 /* """""""""""""""""""""""""""" */
4229 printf("\nAllowed options in the base context:\n");
4230 list = main_ctx->opt_list;
4231 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4232 &has_ctx_change, &has_early_eval);
4234 /* Dependency constraints between options. */
4235 /* """"""""""""""""""""""""""""""""""""""" */
4236 print_before_constraints(list);
4238 /* Usage for the other contexts. */
4239 /* """"""""""""""""""""""""""""" */
4240 bst_walk(contexts_bst, bst_print_ctx_cb);
4242 /* Contextual syntactic explanations. */
4243 /* """""""""""""""""""""""""""""""""" */
4244 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4245 has_optional, has_ellipsis, has_rule);
4247 if (action == exit_after)
4248 exit(EXIT_FAILURE);
4251 /* *********************************** */
4252 /* Built-in constraint check functions */
4253 /* *********************************** */
4255 /* ============================================================= */
4256 /* This constraint checks if each arguments respects a format as */
4257 /* defined for the scanf function. */
4258 /* return 1 if yes and 0 if no. */
4259 /* ============================================================= */
4261 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
4263 int rc = 0;
4265 char x[256];
4266 char y;
4267 char * format;
4269 if (nb_args != 1)
4270 fatal_internal("Format constraint, invalid number of parameters.");
4272 if (strlen(value) > 255)
4273 value[255] = '\0';
4275 format = xstrdup(args[0]);
4276 format = strappend(format, "%c", (char *)0);
4278 rc = sscanf(value, format, x, &y);
4279 if (rc != 1)
4280 fprintf(stderr,
4281 "The argument %s of %s does not respect the imposed format %s.",
4282 value, par, args[0]);
4284 free(format);
4286 return rc == 1;
4289 /* ================================================================== */
4290 /* This constraint checks if each arguments of the option instance is */
4291 /* between a minimum and a maximum (inclusive). */
4292 /* return 1 if yes and 0 if no. */
4293 /* ================================================================== */
4295 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
4297 regex_t re;
4299 if (nb_args != 1)
4300 fatal_internal(
4301 "Regular expression constraint, invalid number of parameters.");
4303 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
4304 fatal_internal("Invalid regular expression %s.", args[0]);
4306 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
4308 fprintf(stderr,
4309 "The argument %s of %s doesn't match the constraining "
4310 "regular expression %s.",
4311 value, par, args[0]);
4312 return 0;
4315 regfree(&re);
4317 return 1;
4320 /* ================================================================== */
4321 /* This constraint checks if each arguments of the option instance is */
4322 /* between a minimum and a maximum (inclusive). */
4323 /* return 1 if yes and 0 if no. */
4324 /* ================================================================== */
4326 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
4328 long min, max;
4329 char c;
4330 char * ptr;
4331 int n;
4332 long v;
4333 int min_only = 0;
4334 int max_only = 0;
4336 if (nb_args != 2)
4337 fatal_internal("Range constraint, invalid number of parameters.");
4339 if (strcmp(args[0], ".") == 0)
4340 max_only = 1;
4341 else
4342 n = sscanf(args[0], "%ld%c", &min, &c);
4344 if (!max_only && n != 1)
4345 fatal_internal("Range constraint, min: invalid parameters.");
4347 if (strcmp(args[1], ".") == 0)
4348 min_only = 1;
4349 else
4350 n = sscanf(args[1], "%ld%c", &max, &c);
4352 if (!min_only && n != 1)
4353 fatal_internal("Range constraint, max: invalid parameters.");
4355 if (min_only && max_only)
4356 fatal_internal("Range constraint, invalid parameters.");
4358 errno = 0;
4359 v = strtol(value, &ptr, 10);
4360 if (errno || ptr == value)
4361 return 0;
4363 if (min_only)
4365 if (v < min)
4367 fprintf(stderr,
4368 "The argument %ld of %s is not greater than or equal to %ld.", v,
4369 par, min);
4370 return 0;
4372 else
4373 return 1;
4375 else if (max_only)
4377 if (v > max)
4379 fprintf(stderr,
4380 "The argument %ld of %s is not less than or equal to %ld.", v,
4381 par, max);
4382 return 0;
4384 else
4385 return 1;
4387 else if (v < min || v > max)
4389 fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
4390 par, min, max);
4391 return 0;
4394 return 1; /* check passed */
4397 /* =============================================================== */
4398 /* This function provides a way to set the behaviour of a context. */
4399 /* =============================================================== */
4400 void
4401 ctxopt_add_global_settings(settings s, ...)
4403 va_list(args);
4404 va_start(args, s);
4406 switch (s)
4408 case error_functions:
4410 typedef void fn(errors e, state_t * state);
4412 void (*function)(errors e, state_t * state);
4414 errors e;
4415 e = va_arg(args, errors);
4416 function = va_arg(args, fn *);
4417 err_functions[e] = function;
4418 break;
4421 default:
4422 break;
4424 va_end(args);
4427 /* ================================================================ */
4428 /* This function provides a way to set the behaviour of an option. */
4429 /* It can take a variable number of arguments according to its */
4430 /* first argument: */
4431 /* - parameter: */
4432 /* o a string containing an option name and all its possible */
4433 /* parameters separates by spaces, tabs or commas (char *) */
4434 /* (e.g: "help -h -help"). */
4435 /* - actions: */
4436 /* o a string containing an option name. */
4437 /* o a pointer to a function which will be called at evaluation */
4438 /* time. */
4439 /* - constraints: */
4440 /* o a string containing an option name. */
4441 /* o a pointer to a function to check if an argument is valid. */
4442 /* o a strings containing the arguments to this function. */
4443 /* ================================================================ */
4444 void
4445 ctxopt_add_opt_settings(settings s, ...)
4447 opt_t * opt;
4448 void * ptr = NULL;
4450 va_list(args);
4451 va_start(args, s);
4453 switch (s)
4455 /* This part associates some command line parameters to an option. */
4456 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4457 case parameters:
4459 char * opt_name;
4460 char * params;
4462 /* The second argument must be a string containing: */
4463 /* - The name of an existing option. */
4464 /* - a list of parameters with a leading dash (-). */
4465 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4466 ptr = va_arg(args, char *);
4467 opt_name = ptr;
4469 if (opt_name != NULL)
4471 if ((opt = locate_opt(opt_name)) != NULL)
4473 ptr = va_arg(args, char *);
4474 params = ptr;
4476 if (!opt_set_parms(opt_name, params))
4477 fatal_internal(
4478 "Duplicated parameters or bad settings for the option %s.",
4479 params);
4481 else
4482 fatal_internal("Unknown option %s.", opt_name);
4484 else
4485 fatal_internal(
4486 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4488 /* Here opt is a known option. */
4489 /* """"""""""""""""""""""""""" */
4490 if (opt->params != NULL)
4491 fatal_internal("Parameters are already set for %s.", opt_name);
4492 else
4494 size_t n;
4495 size_t l = strlen(params);
4497 opt->params = xstrdup(params);
4498 while ((n = strcspn(opt->params, " \t")) < l)
4499 opt->params[n] = '|';
4502 break;
4505 /* This part associates a callback function to an option. */
4506 /* This function will be called when an instance of an option */
4507 /* is evaluated. */
4508 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4509 case actions:
4511 void * data;
4512 void (*function)();
4513 int nb_data = 0;
4515 /* The second argument must be the name of an existing option. */
4516 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4517 ptr = va_arg(args, char *);
4519 if ((opt = locate_opt(ptr)) != NULL)
4521 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4522 void **);
4524 /* The third argument must be the callback function. */
4525 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4526 function = va_arg(args, fn *);
4527 opt->action = function;
4529 /* The fourth argument must be a pointer to an user's defined */
4530 /* variable or structure that the previous function can manage. */
4531 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4532 while ((data = va_arg(args, void *)) != NULL)
4534 nb_data++;
4535 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4536 opt->data[nb_data - 1] = data;
4538 opt->nb_data = nb_data;
4540 else
4541 fatal_internal("Unknown option %s.", ptr);
4542 break;
4545 /* This part associates a list of functions to control some */
4546 /* characteristics of the arguments of an option. */
4547 /* Each function will be called in order and must return 1 */
4548 /* to validate the arguments. */
4549 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4550 case constraints:
4552 char * value;
4553 constraint_t * cstr;
4554 int (*function)();
4556 /* The second argument must be a string. */
4557 /* """"""""""""""""""""""""""""""""""""" */
4558 ptr = va_arg(args, char *);
4560 if ((opt = locate_opt(ptr)) != NULL)
4562 typedef int fn(int, char **, char *);
4564 /* The third argument must be a function. */
4565 /* """""""""""""""""""""""""""""""""""""" */
4566 function = va_arg(args, fn *);
4568 cstr = xmalloc(sizeof(constraint_t));
4569 cstr->constraint = function;
4571 /* The fourth argument must be a string containing the argument of */
4572 /* The previous function separated by spaces or tabs. */
4573 /* Theses arguments will be passed to the previous function */
4574 /* max: 32 argument! */
4575 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4576 value = xstrdup(va_arg(args, char *));
4578 cstr->to_free = value;
4579 cstr->args = xcalloc(sizeof(char *), 32);
4580 cstr->nb_args = str2argv(value, cstr->args, 32);
4581 ll_append(opt->constraints_list, cstr);
4583 else
4584 fatal_internal("Unknown option %s.", ptr);
4585 break;
4588 /* This part allows to indicate that an option must be evaluated */
4589 /* after a list of other options. */
4590 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4591 case after:
4593 char * str;
4595 /* The second argument must be a string. */
4596 /* """"""""""""""""""""""""""""""""""""" */
4597 ptr = va_arg(args, char *);
4599 if ((opt = locate_opt(ptr)) != NULL)
4601 char * end_str;
4602 char * opt_name;
4603 opt_t * opt_before;
4605 ptr = va_arg(args, char *);
4607 str = xstrdup(ptr);
4608 ltrim(str, " \t");
4609 rtrim(str, " \t", 0);
4611 /* Feed the list of options to be evaluated after the given option. */
4612 /* This list will contain pointers to options. */
4613 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4614 opt_name = xstrtok_r(str, " \t,", &end_str);
4615 if (opt_name != NULL)
4617 if ((opt_before = locate_opt(opt_name)) != NULL)
4619 ll_append(opt->eval_before_list, opt_before);
4620 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4622 if ((opt_before = locate_opt(opt_name)) != NULL)
4623 ll_append(opt->eval_before_list, opt_before);
4624 else
4625 fatal_internal("Unknown option %s.", opt_name);
4628 else
4629 fatal_internal("Unknown option %s.", opt_name);
4631 else
4632 fatal_internal("Not enough options to be evaluated after %s.",
4633 opt->name);
4635 free(str);
4637 else
4638 fatal_internal("Unknown option %s.", ptr);
4640 break;
4643 /* This part allows to indicate that an option must be evaluated */
4644 /* before a list of other options. */
4645 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4646 case before:
4648 char * str;
4650 /* The second argument must be a string. */
4651 /* """"""""""""""""""""""""""""""""""""" */
4652 ptr = va_arg(args, char *);
4654 if ((opt = locate_opt(ptr)) != NULL)
4656 char * end_str;
4657 char * opt_name;
4658 opt_t * opt_before;
4660 ptr = va_arg(args, char *);
4662 str = xstrdup(ptr);
4663 ltrim(str, " \t");
4664 rtrim(str, " \t", 0);
4666 /* Feed the list of options to be evaluated before the given option. */
4667 /* This list will contain pointers to options. */
4668 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4669 opt_name = xstrtok_r(str, " \t,", &end_str);
4670 if (opt_name != NULL)
4672 if ((opt_before = locate_opt(opt_name)) != NULL)
4674 ll_append(opt_before->eval_before_list, opt);
4675 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4677 if ((opt_before = locate_opt(opt_name)) != NULL)
4678 ll_append(opt_before->eval_before_list, opt);
4679 else
4680 fatal_internal("Unknown option %s.", opt_name);
4683 else
4684 fatal_internal("Unknown option %s.", opt_name);
4686 else
4687 fatal_internal("Not enough options to be evaluated before %s.",
4688 opt->name);
4690 free(str);
4692 else
4693 fatal_internal("Unknown option %s.", ptr);
4695 break;
4698 default:
4699 break;
4701 va_end(args);
4704 /* =============================================================== */
4705 /* This function provides a way to set the behaviour of a context. */
4706 /* =============================================================== */
4707 void
4708 ctxopt_add_ctx_settings(settings s, ...)
4710 ctx_t * ctx;
4712 va_list(args);
4713 va_start(args, s);
4715 switch (s)
4717 /* Add a set of mutually incompatible options in a context. */
4718 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4719 case incompatibilities:
4721 void * ptr;
4722 ll_t * list;
4723 size_t n;
4724 char * str;
4726 ptr = va_arg(args, char *);
4727 if ((ctx = locate_ctx(ptr)) != NULL)
4729 ptr = va_arg(args, char *);
4730 list = ctx->incomp_list;
4732 str = xstrdup(ptr);
4733 ltrim(str, " \t");
4734 rtrim(str, " \t", 0);
4736 n = strcspn(str, " \t");
4737 if (n > 0 && n < strlen(str))
4738 ll_append(list, str);
4739 else
4740 fatal_internal(
4741 "Not enough incompatible options in the string: \"%s\".", str);
4743 else
4744 fatal_internal("Unknown context %s.", ptr);
4745 break;
4748 case requirements:
4750 void * ptr;
4751 ll_t * list;
4752 size_t n;
4753 char * str;
4755 ptr = va_arg(args, char *);
4756 if ((ctx = locate_ctx(ptr)) != NULL)
4758 ptr = va_arg(args, char *);
4759 list = ctx->req_list;
4761 str = xstrdup(ptr);
4762 ltrim(str, " \t");
4763 rtrim(str, " \t", 0);
4765 n = strcspn(str, " \t");
4766 if (n > 0 && n < strlen(str))
4767 ll_append(list, str);
4768 else
4769 fatal_internal("Not enough required options in the string: \"%s\".",
4770 str);
4772 else
4773 fatal_internal("Unknown context %s.", ptr);
4774 break;
4777 /* Add functions which will be called when */
4778 /* entering and exiting a context. */
4779 /* """"""""""""""""""""""""""""""""""""""" */
4780 case actions:
4782 void * ptr;
4783 void * data;
4784 int (*function)();
4785 int nb_data = 0;
4787 ptr = va_arg(args, char *);
4788 if ((ctx = locate_ctx(ptr)) != NULL)
4790 typedef int fn(char *, direction, char *, int, void **);
4792 function = va_arg(args, fn *);
4793 ctx->action = function;
4795 while ((data = va_arg(args, void *)) != NULL)
4797 nb_data++;
4798 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4799 ctx->data[nb_data - 1] = data;
4801 ctx->nb_data = nb_data;
4803 else
4804 fatal_internal("Unknown context %s.", ptr);
4805 break;
4808 default:
4809 break;
4811 va_end(args);