Make ctxopt_free_memory(...) free more memory
[ctxopt.git] / ctxopt.c
blobd0fb33b51b7d59b35619b889b9fb6ee92f1ad578
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;
1323 /* Context structure. */
1324 /* """""""""""""""""" */
1325 struct ctx_s
1327 char * name;
1328 ll_t * opt_list; /* list of options allowed in this context. */
1329 ll_t * incomp_list; /* list of strings containing incompatible names *
1330 | of options separated by spaces or tabs. */
1331 ll_t * req_list; /* list of strings containing an option name and *
1332 | all the option names where at least one of *
1333 | them is required to be also present. */
1335 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1336 void ** ctx_data);
1337 void * par_bst;
1338 int nb_data;
1339 void ** data;
1342 /* https://textik.com/#488ce3649b6c60f5 */
1343 /* */
1344 /* +--------------+ */
1345 /* |first_ctx_inst| */
1346 /* +---+----------+ */
1347 /* | */
1348 /* +--v-----+ +--------+ +--------+ +-----+ */
1349 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1350 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1351 /* | | | | | */
1352 /* | | +-v------+ | | */
1353 /* | +--+ctx_inst<-----------+ | */
1354 /* | +-+------+ | */
1355 /* | | | */
1356 /* | +-v------+ | */
1357 /* +------+ctx_inst<--------------------------+ */
1358 /* +-+------+ */
1359 /* | */
1360 /* +-v---+ */
1361 /* | ... | */
1362 /* +-----+ */
1364 /* Option structure. */
1365 /* """"""""""""""""" */
1366 struct opt_s
1368 char * name; /* option name. */
1369 char * next_ctx; /* new context this option may lead to */
1370 ll_t * ctx_list; /* list of contexts allowing this option. */
1371 char * params; /* string containing all the parameters of *
1372 | the option. */
1374 void (*action)( /* The option associated action. */
1375 char * ctx_name, /* context name. */
1376 char * opt_name, /* option name. */
1377 char * par, /* option parameter. */
1378 int nb_args, /* number of arguments. */
1379 char ** args, /* option arguments. */
1380 int nb_opt_data, /* number of option data pointers. */
1381 void ** opt_data, /* option data pointers. */
1382 int nb_ctx_data, /* nb of current context data ptrs. */
1383 void ** ctx_data /* current context data pointers. */
1386 int nb_data; /* number of the data pointers passed as argument to action. */
1387 void ** data; /* array of data pointers passed as argument to action. */
1389 int args; /* 1 if this option takes arguments else 0. */
1390 int optional; /* 1 if the option is optional, else 0. */
1391 int multiple; /* 1 if the option can appear more than one time in a *
1392 | context, else 0. */
1394 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1395 int occurrences; /* Number of option occurrences in a context. */
1396 char opt_count_oper; /* <, = or > */
1397 unsigned opt_count_mark; /* Value to be compared to with opt_count_oper. */
1399 char * arg; /* symbolic text after # describing the option argument. */
1401 int optional_args; /* 1 of option is optional else 0. */
1402 int multiple_args; /* 1 is option can appear more than once in a context *
1403 | instance. */
1405 int opt_args_count_matter; /* 1 if count is rescticted, else 0. */
1406 char opt_args_count_oper; /* <, = or > */
1407 unsigned opt_args_count_mark; /* Value to be compared to with *
1408 | opt_count_oper. */
1410 int eval_first; /* 1 if this option must be evaluated before the options *
1411 | without this mark. */
1413 ll_t * eval_before_list; /* List of pointers on options which must be *
1414 | evaluated before this option. */
1416 ll_t * constraints_list; /* List of constraint check functions pointers. */
1419 /* Context instance structure. */
1420 /* """"""""""""""""""""""""""" */
1421 struct ctx_inst_s
1423 ctx_t * ctx; /* the context whose this is an instance of */
1424 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1425 | creation of this ctx_inst structure. */
1426 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1427 | instance of this structure. */
1428 ll_t * incomp_bst_list; /* list of seen_opt_t BST. */
1429 void * seen_opt_bst; /* tree of seen_opt_t. */
1430 ll_t * opt_req_list; /* list of req_t. */
1431 ll_t * opt_inst_list; /* The list of option instances in this *
1432 | context instance. */
1433 char * par_name; /* parameter which created this instance. */
1436 /* Option instance structure. */
1437 /* """""""""""""""""""""""""" */
1438 struct opt_inst_s
1440 opt_t * opt; /* The option this is an instance of. */
1441 char * opt_name; /* The option which led to this creation. */
1442 char * par; /* The parameter which led to this creation. */
1443 ll_t * values_list; /* The list of arguments of this option. */
1444 ctx_inst_t * next_ctx_inst; /* The new context instance this option. *
1445 | instance may create. */
1448 /* Structure used to check if an option has bee seen or not */
1449 /* in a context instance. */
1450 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1451 struct seen_opt_s
1453 opt_t * opt; /* The concerned option. */
1454 char * par; /* Parameter which led to the making of this structure. */
1455 int seen; /* 1 if seen in the context instances, else 0. */
1458 /* Structure used to check if at least one instance of the options whose */
1459 /* pointers are in or_opt_list has been seen in the ctx_inst where an */
1460 /* instance or opt is also present. */
1461 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1462 struct req_s
1464 opt_t * opt; /* Option that asks for other options. */
1465 ll_t * or_opt_list; /* Required options, at least one of them *
1466 | must be present. */
1469 /* Parameter structure which links a parameter to the option it belongs to. */
1470 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1471 struct par_s
1473 char * name; /* Parameter name (with the leading -). */
1474 opt_t * opt; /* Attached option. */
1477 /* Constraint structure. */
1478 /* """"""""""""""""""""" */
1479 struct constraint_s
1481 int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1482 int nb_args;
1483 char ** args;
1484 char * to_free; /* pointer to the original string in which the array in *
1485 | args points to. This poinnter is kept there to allow *
1486 | it to be freed. */
1489 state_t * cur_state = NULL; /* Current analysis state. */
1490 static ll_t * cmdline_list; /* List of interpreted CLI words *
1491 | serves as the basis for the *
1492 | analysis of the parameters. */
1493 static ctx_t * main_ctx = NULL; /* initial context. */
1494 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1495 | instance which holds the *
1496 | options instances. */
1497 static ll_t * ctx_inst_list = NULL; /* List of the context instances. */
1499 static flags_t flags = { 0, 1 };
1501 /* ======================================================= */
1502 /* Parse a string for the next matching token. */
1503 /* */
1504 /* s: string to parse. */
1505 /* token: pre_allocated array of max tok_len characters. */
1506 /* pattern: scanf type pattern token must match. */
1507 /* pos: number of characters successfully parsed in s. */
1508 /* */
1509 /* Returns: a pointer to the first unread character or */
1510 /* to he terminating \0. */
1511 /* ======================================================= */
1512 static char *
1513 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1515 char * full_pattern;
1516 char len[3];
1517 int n;
1519 *pos = 0;
1521 n = snprintf(len, 3, "%zu", tok_len);
1522 if (n < 0)
1523 return NULL;
1525 full_pattern = xmalloc(strlen(pattern) + n + 4);
1527 strcpy(full_pattern, "%");
1528 strcat(full_pattern, len);
1529 strcat(full_pattern, pattern);
1530 strcat(full_pattern, "%n");
1532 n = sscanf(s, full_pattern, token, pos);
1534 free(full_pattern);
1536 if (n != 1)
1537 return NULL;
1539 return s + *pos;
1542 /* ****************************************** */
1543 /* Various comparison and deletion functions. */
1544 /* ****************************************** */
1546 static int
1547 ctx_compare(const void * c1, const void * c2)
1549 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1552 /* =========================== */
1553 /* Free a context_bst element. */
1554 /* =========================== */
1555 static void
1556 ctx_free(void * c)
1558 ctx_t * ctx = c;
1560 free(ctx->name);
1561 free(ctx->data);
1563 ll_destroy(ctx->opt_list, NULL);
1564 ll_destroy(ctx->incomp_list, free);
1565 ll_destroy(ctx->req_list, free);
1566 bst_destroy(ctx->par_bst, par_free);
1568 free(c);
1571 /* ============================= */
1572 /* Free a ctx_inst_list element. */
1573 /* ============================= */
1574 static void
1575 ctx_inst_free(void * ci)
1577 ctx_inst_t * ctx_inst = ci;
1579 free(ctx_inst->par_name);
1580 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1581 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1582 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1583 ll_destroy(ctx_inst->opt_req_list, req_free);
1585 free(ci);
1588 /* ============================== */
1589 /* Free an opt_inst_list element. */
1590 /* ============================== */
1591 static void
1592 opt_inst_free(void * oi)
1594 opt_inst_t * opt_inst = oi;
1596 ll_destroy(opt_inst->values_list, NULL);
1598 free(oi);
1601 /* ================================== */
1602 /* Compare two seen_opt_bst elements. */
1603 /* ================================== */
1604 static int
1605 seen_opt_compare(const void * so1, const void * so2)
1607 opt_t *o1, *o2;
1609 o1 = ((seen_opt_t *)so1)->opt;
1610 o2 = ((seen_opt_t *)so2)->opt;
1612 return strcmp(o1->name, o2->name);
1615 /* ============================ */
1616 /* Free a seen_opt_bst element. */
1617 /* ============================ */
1618 void
1619 seen_opt_free(void * so)
1621 seen_opt_t * seen_opt = so;
1623 free(seen_opt->par);
1625 free(so);
1628 /* =========================== */
1629 /* Free an incomp_bst element. */
1630 /* =========================== */
1631 static void
1632 incomp_bst_free(void * b)
1634 bst_t * bst = b;
1636 bst_destroy(bst, NULL);
1639 /* ============================= */
1640 /* Free an opt_req_list element. */
1641 /* ============================= */
1642 static void
1643 req_free(void * r)
1645 req_t * req = r;
1647 ll_destroy(req->or_opt_list, NULL);
1648 free(req);
1651 /* ================================= */
1652 /* Compare two options_bst elements. */
1653 /* ================================= */
1654 static int
1655 opt_compare(const void * o1, const void * o2)
1657 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1660 /* ============================= */
1661 /* Free an options_bst elements. */
1662 /* ============================= */
1663 void
1664 opt_free(void * o)
1666 opt_t * opt = o;
1668 free(opt->name);
1669 free(opt->next_ctx);
1670 free(opt->params);
1671 free(opt->arg);
1672 free(opt->data);
1674 ll_destroy(opt->ctx_list, NULL);
1675 ll_destroy(opt->constraints_list, constraint_free);
1676 ll_destroy(opt->eval_before_list, NULL);
1678 free(o);
1681 /* ============================= */
1682 /* Compare two par_bst elements. */
1683 /* ============================= */
1684 static int
1685 par_compare(const void * a1, const void * a2)
1687 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1690 /* ======================= */
1691 /* Free a par_bst element. */
1692 /* ======================= */
1693 static void
1694 par_free(void * p)
1696 par_t * par = p;
1698 free(par->name);
1700 free(p);
1703 /* ================================ */
1704 /* Free a constraints_list element. */
1705 /* ================================ */
1706 static void
1707 constraint_free(void * c)
1709 constraint_t * cstr = c;
1711 free(cstr->args);
1712 free(cstr->to_free);
1714 free(c);
1717 /* ******************************************************************** */
1718 /* Helper functions to locate contexts, options and parameters in a BST */
1719 /* by their names. */
1720 /* ******************************************************************** */
1722 static ctx_t *
1723 locate_ctx(char * name)
1725 bst_t * node;
1726 ctx_t ctx = { 0 };
1728 ctx.name = name;
1730 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1731 return NULL;
1732 else
1733 return node->key;
1736 static opt_t *
1737 locate_opt(char * name)
1739 bst_t * node;
1740 opt_t opt = { 0 };
1742 opt.name = name;
1744 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1745 return NULL;
1746 else
1747 return node->key;
1750 static par_t *
1751 locate_par(char * name, ctx_t * ctx)
1753 bst_t * node;
1754 par_t par = { 0 };
1755 void * bst = ctx->par_bst;
1757 par.name = name;
1759 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1760 return NULL;
1761 else
1762 return node->key;
1765 /* ====================================================================== */
1766 /* Helper function to display the dependency constraints between options. */
1767 /* These constraints are set with the ctxopt_add_opt_settings function */
1768 /* using the 'before' and 'after' arguments. */
1769 /* IN list : a list of options. */
1770 /* ====================================================================== */
1771 static void
1772 print_before_constraints(ll_t * list)
1774 ll_node_t * node = list->head;
1775 ll_node_t * before_node;
1776 opt_t * opt, *before_opt;
1777 int msg = 0;
1779 while (node != NULL)
1781 opt = node->data;
1783 if (opt->eval_before_list->len > 0)
1785 if (!msg)
1787 printf("\n If present in the command line,");
1788 msg = 1; /* Display this message only once. */
1791 before_node = opt->eval_before_list->head;
1793 printf("\n ");
1794 while (before_node != NULL)
1796 before_opt = before_node->data;
1797 printf("%s", before_opt->params);
1799 before_node = before_node->next;
1801 if (before_node != NULL)
1802 printf(" and\n ");
1804 printf(" will be evaluated after %s\n", opt->params);
1806 node = node->next;
1810 /* =================================================================== */
1811 /* Utility function to format and print the options present in a list. */
1812 /* */
1813 /* IN list : a list of options. */
1814 /* OUT has_* : a set of flags which will determine the content of the */
1815 /* explanation given after the formatted printing of the */
1816 /* options. */
1817 /* =================================================================== */
1818 static void
1819 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1820 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1821 int * has_early_eval)
1823 ll_node_t * node = list->head;
1824 opt_t * opt;
1825 char * line;
1826 char * option;
1828 line = xstrdup(" ");
1830 while (node != NULL)
1832 option = xstrdup("");
1833 opt = node->data;
1835 if (opt->optional)
1837 option = strappend(option, "[", (char *)0);
1838 *has_optional = 1;
1841 if (opt->eval_first)
1843 option = strappend(option, "*", (char *)0);
1844 *has_early_eval = 1;
1847 option = strappend(option, opt->params, (char *)0);
1849 if (opt->next_ctx != NULL)
1851 option = strappend(option, ">", opt->next_ctx, (char *)0);
1852 *has_ctx_change = 1;
1855 if (opt->multiple)
1857 if (opt->opt_count_oper != '\0')
1859 char m[4];
1860 char o[2];
1861 o[0] = opt->opt_count_oper;
1862 o[1] = '\0';
1863 snprintf(m, 3, "%u", opt->opt_count_mark);
1864 option = strappend(option, "...", o, m, (char *)0);
1865 *has_rule = 1;
1867 else
1868 option = strappend(option, "...", (char *)0);
1870 *has_ellipsis = 1;
1873 if (opt->args)
1875 if (*(opt->arg) == '#')
1876 *has_generic_arg = 1;
1878 option = strappend(option, " ", (char *)0);
1880 if (opt->optional_args)
1882 option = strappend(option, "[", opt->arg, (char *)0);
1883 *has_optional = 1;
1885 else
1886 option = strappend(option, opt->arg, (char *)0);
1888 if (opt->multiple_args)
1890 if (opt->opt_args_count_oper != '\0')
1892 char m[4];
1893 char o[2];
1894 o[0] = opt->opt_args_count_oper;
1895 o[1] = '\0';
1896 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1897 option = strappend(option, "...", o, m, (char *)0);
1898 *has_rule = 1;
1900 else
1901 option = strappend(option, "...", (char *)0);
1903 *has_ellipsis = 1;
1905 if (opt->optional_args)
1906 option = strappend(option, "]", (char *)0);
1908 if (opt->optional)
1909 option = strappend(option, "]", (char *)0);
1911 if (strlen(line) + 1 + strlen(option) < 80)
1912 line = strappend(line, option, " ", (char *)0);
1913 else
1915 printf("%s\n", line);
1916 line[2] = '\0';
1917 line = strappend(line, option, " ", (char *)0);
1920 free(option);
1922 node = node->next;
1925 printf("%s\n", line);
1927 free(line);
1930 /* ==================================================== */
1931 /* Explain the special syntactic symbols present in the */
1932 /* generated usage messages. */
1933 /* ==================================================== */
1934 static void
1935 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1936 int has_optional, int has_ellipsis, int has_rule)
1938 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1939 || has_ellipsis || has_rule)
1941 printf("\nExplanation of the syntax used above:\n");
1942 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1943 "must be entered.\n");
1944 printf("The following is just there to explain the other symbols "
1945 "displayed.\n\n");
1947 if (has_early_eval)
1948 printf("* : the parameters defined for this option will "
1949 "be evaluated first.\n");
1950 if (has_ctx_change)
1951 printf("> : the context after this symbol will be the new "
1952 "default context.\n");
1953 if (has_generic_arg)
1954 printf("#tag : argument with a hint about its meaning.\n");
1955 if (has_optional)
1956 printf("[...] : the object between square brackets is "
1957 "optional.\n");
1958 if (has_ellipsis)
1959 printf("... : several occurrences of the previous object "
1960 "are possible.\n");
1961 if (has_rule)
1962 printf("[<|=|>]number: rules constraining the number of "
1963 "parameters/arguments.\n");
1967 /* ************************************************************ */
1968 /* Various utilities and callback functions called when walking */
1969 /* through a BST. */
1970 /* ************************************************************ */
1972 static void
1973 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1975 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1977 if (kind == postorder || kind == leaf)
1979 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1981 user_rc = 1;
1982 user_string = strappend(user_string, seen_opt->opt->params, " ",
1983 (char *)0);
1988 static void
1989 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1991 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1993 if (kind == postorder || kind == leaf)
1994 if (seen_opt->seen == 1)
1996 user_rc = 1;
1997 user_object = seen_opt->par;
2001 static void
2002 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
2004 ctx_t * ctx = main_ctx;
2005 ctx_t * cur_ctx = ((bst_t *)node)->key;
2007 ll_t * list;
2009 int has_optional = 0;
2010 int has_ellipsis = 0;
2011 int has_rule = 0;
2012 int has_generic_arg = 0;
2013 int has_ctx_change = 0;
2014 int has_early_eval = 0;
2016 if (kind == postorder || kind == leaf)
2017 if (strcmp(ctx->name, cur_ctx->name) != 0)
2019 list = cur_ctx->opt_list;
2021 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
2022 print_options(list, &has_optional, &has_ellipsis, &has_rule,
2023 &has_generic_arg, &has_ctx_change, &has_early_eval);
2024 print_before_constraints(list);
2028 static void
2029 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
2031 opt_t * opt = ((bst_t *)node)->key;
2033 if (kind == postorder || kind == leaf)
2035 if (opt->params == NULL) /* opt must have associated parameters. */
2036 fatal_internal("Option %s has no registered parameter.\n", opt->name);
2038 if (opt->action == NULL) /* opt must have an action. */
2039 fatal_internal("Option %s has no registered action.\n", opt->name);
2043 static void
2044 bst_match_par_cb(const void * node, walk_order_e kind, int level)
2046 ctx_t * ctx = ((bst_t *)node)->key;
2048 if (kind == postorder || kind == leaf)
2050 char * str = xstrdup(user_string);
2052 while (*str != '\0')
2054 if (locate_par(str, ctx) != NULL)
2056 if (*user_string2 == '\0')
2057 user_string2 = strappend(user_string2, "- ", ctx->name, (char *)0);
2058 else
2059 user_string2 = strappend(user_string2, "\n- ", ctx->name, (char *)0);
2060 break;
2062 str[strlen(str) - 1] = '\0';
2064 free(str);
2068 static void
2069 match_prefix_cb(const void * node, walk_order_e kind, int level)
2071 par_t * par = ((bst_t *)node)->key;
2073 if (kind == postorder || kind == leaf)
2074 if (strpref(par->name, (char *)user_object))
2076 user_rc++;
2077 user_string = strappend(user_string, par->name, " ", (char *)0);
2081 /* ====================================================================== */
2082 /* A parameter may not be separated from its first option by spaces, in */
2083 /* this case this function looks for a valid flag as a prefix and splits */
2084 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
2085 /* option). */
2086 /* */
2087 /* IN word : the word to be checked. */
2088 /* IN ctx : the context in which the flag indexed by the word is to be */
2089 /* checked. */
2090 /* OUT pos : the offset in word pointing just after the matching prefix. */
2091 /* OUT opt : a pointer to the option associated with the new parameter */
2092 /* or NULL if none is found. */
2093 /* */
2094 /* The returned pointer must be freed by the caller. */
2095 /* ====================================================================== */
2096 static char *
2097 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
2099 char * new = NULL;
2100 int len;
2101 par_t * par;
2102 par_t tmp_par = { 0 };
2104 len = strlen(word);
2106 if (len > 2)
2108 new = xstrdup(word);
2112 new[--len] = '\0';
2113 tmp_par.name = new;
2114 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2116 if (par != NULL)
2118 *pos = len;
2119 *opt = par->opt;
2121 else
2123 free(new);
2124 new = NULL;
2127 else
2128 *pos = 0;
2130 return new;
2133 /* ============================================================= */
2134 /* If par_name is an unique abbreviation of an exiting parameter */
2135 /* in the context ctx, then return this parameter. */
2136 /* ============================================================= */
2137 static char *
2138 abbrev_expand(char * par_name, ctx_t * ctx)
2140 user_object = par_name;
2141 user_rc = 0;
2143 *user_string = '\0';
2144 bst_walk(ctx->par_bst, match_prefix_cb);
2145 rtrim(user_string, " ", 0);
2147 /* The previous bst_walk has built a string of blank separated parameters */
2148 /* all having par_name as prefix. This string is put in the user_string */
2149 /* exchange zone. The number of these words in put in user_rc. */
2150 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2151 if (user_rc == 1) /* The number of matching abbreviations. */
2152 return xstrdup(user_string);
2153 else /* There is at least tho defined parameters starting with par_name. */
2155 char * s, *first_s;
2156 par_t * par;
2157 opt_t * opt;
2158 int opt_count = 0;
2159 void * tmp_opt_bst = NULL;
2161 /* Find all the options corresponding to these words and store them */
2162 /* without duplication in a temporary BST. Only their resulting count */
2163 /* matters. */
2164 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2165 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2166 | the first word. */
2167 while (s != NULL)
2169 par = locate_par(s, ctx);
2170 opt = par->opt;
2172 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2174 /* This option as not already been seen */
2175 /* store it and increase the seen counter. */
2176 /* """"""""""""""""""""""""""""""""""""""" */
2177 bst_search(opt, &tmp_opt_bst, opt_compare);
2178 opt_count++;
2180 s = strtok(NULL, " ");
2183 /* Clean the temporary BST without removing the pointer */
2184 /* to the real options. */
2185 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2186 if (tmp_opt_bst != NULL)
2187 bst_destroy(tmp_opt_bst, NULL);
2189 if (opt_count == 1)
2190 /* All the abbreviation are leading to only one option */
2191 /* We can just continue as in the previous case. */
2192 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2193 return xstrdup(first_s);
2194 else
2195 return NULL;
2199 /* ================================================================ */
2200 /* Terminate the program if mandatory options required by a context */
2201 /* are not present. */
2202 /* ================================================================ */
2203 static void
2204 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2206 char * missing;
2208 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2209 fatal(CTXOPTMISPAR, missing);
2212 /* ====================================================== */
2213 /* Return 1 if at least one mandatory option was not seen */
2214 /* when quitting a context, else 0. */
2215 /* ====================================================== */
2216 static int
2217 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2219 user_rc = 0;
2220 *user_string = '\0';
2222 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2223 rtrim(user_string, " ", 0);
2225 *missing = user_string;
2227 return user_rc ? 1 : 0;
2230 /* ========================================================================= */
2231 /* This function terminates the program if an option or its arguments do not */
2232 /* conform to its occurrences constraint. */
2233 /* There constraints can appear by trailing >, < or = in their definition */
2234 /* given in ctxopt_new_ctx. */
2235 /* ========================================================================= */
2236 static void
2237 check_for_occurrence_issues(ctx_inst_t * ctx_inst)
2239 ctx_t * ctx = ctx_inst->ctx;
2240 opt_t * opt;
2241 ll_node_t * node;
2242 opt_inst_t * opt_inst;
2243 char * cur_opt_params = cur_state->cur_opt_params;
2244 char * cur_opt_par_name = cur_state->cur_opt_par_name;
2246 /* Checks options. */
2247 /* """"""""""""""" */
2248 node = ctx->opt_list->head;
2250 while (node != NULL)
2252 opt = node->data;
2254 /* Update current_state. */
2255 /* """"""""""""""""""""" */
2256 cur_state->cur_opt_params = opt->params;
2257 cur_state->opts_count = opt->opt_count_mark;
2258 cur_state->opt_args_count = opt->opt_args_count_mark;
2260 if (opt->opt_count_matter)
2261 switch (opt->opt_count_oper)
2263 case '=':
2264 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2265 fatal(CTXOPTCTEOPT, NULL);
2266 break;
2268 case '<':
2269 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2270 fatal(CTXOPTCTLOPT, NULL);
2271 break;
2273 case '>':
2274 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2275 fatal(CTXOPTCTGOPT, NULL);
2276 break;
2279 node = node->next;
2282 /* Checks arguments. */
2283 /* """"""""""""""""" */
2284 node = ctx_inst->opt_inst_list->head;
2285 while (node != NULL)
2287 opt_inst = node->data;
2288 opt = opt_inst->opt;
2290 /* Update current_state. */
2291 /* """"""""""""""""""""" */
2292 cur_state->cur_opt_par_name = opt_inst->par;
2293 cur_state->opts_count = opt->opt_count_mark;
2294 cur_state->opt_args_count = opt->opt_args_count_mark;
2296 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2298 if (opt->opt_args_count_matter)
2299 switch (opt->opt_args_count_oper)
2301 case '=':
2302 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2303 fatal(CTXOPTCTEARG, NULL);
2304 break;
2306 case '<':
2307 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2308 fatal(CTXOPTCTLARG, NULL);
2309 break;
2311 case '>':
2312 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2313 fatal(CTXOPTCTGARG, NULL);
2314 break;
2317 node = node->next;
2319 cur_state->cur_opt_params = cur_opt_params;
2320 cur_state->cur_opt_par_name = cur_opt_par_name;
2323 /* ====================================================================== */
2324 /* This function terminates the program if all the options which are part */
2325 /* of a group of required options by some other option are missing. */
2326 /* ====================================================================== */
2327 static void
2328 check_for_requirement_issues(ctx_inst_t * ctx_inst)
2330 ll_node_t * node;
2331 ll_node_t * req_node;
2332 req_t * req;
2333 opt_t * opt;
2334 opt_t * req_opt;
2335 bst_t * bst_node;
2336 seen_opt_t tmp_seen_opt;
2337 int found;
2338 char * needed_params = NULL;
2340 node = ctx_inst->opt_req_list->head;
2342 while (node != NULL)
2344 req = node->data;
2346 opt = req->opt;
2347 tmp_seen_opt.opt = opt;
2349 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2350 seen_opt_compare);
2352 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2354 found = 0;
2355 req_node = req->or_opt_list->head;
2357 /* needed_params accumulates the params of the options in the group. */
2358 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2359 free(needed_params); /* free can applied to the NULL pointer. */
2360 needed_params = xstrdup("");
2362 /* Go through the list of the required group of options and */
2363 /* succeed when one of them has been seen in the context. */
2364 /* otherwise a fatal error is triggered and the program is */
2365 /* terminated. */
2366 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2367 while (req_node != NULL)
2369 req_opt = req_node->data;
2370 tmp_seen_opt.opt = req_opt;
2371 needed_params = strappend(needed_params, req_opt->params, "\n ",
2372 (char *)0);
2374 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2375 seen_opt_compare);
2377 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2379 found = 1; /* A required option has been seen, */
2380 break; /* accept the group. */
2382 req_node = req_node->next;
2385 rtrim(needed_params, "\n ", 0);
2387 /* This is a fatal error if none of the options in the required */
2388 /* options group has been seen in the context. */
2389 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2390 if (!found)
2392 char * errmsg;
2394 if (req->or_opt_list->len > 1)
2395 errmsg = xstrdup("At least one of the parameters among:\n %s\n"
2396 "requested by %s must be present.\n");
2397 else
2398 errmsg = xstrdup("The parameter %s "
2399 "requested by %s must be present.\n");
2401 cur_state->req_opt_par_needed = needed_params;
2402 cur_state->req_opt_par = opt->params;
2404 fatal(CTXOPTREQPAR, errmsg);
2408 node = node->next;
2412 /* ======================================================================== */
2413 /* Parse a strings describing options and some of their characteristics */
2414 /* The input string must have follow some rules like in the examples below: */
2415 /* */
2416 /* "opt_name1 opt_name2" */
2417 /* "[opt_name1] opt_name2" */
2418 /* "[opt_name1] opt_name2..." */
2419 /* "[opt_name1 #...] opt_name2... [#]" */
2420 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2421 /* */
2422 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2423 /* means that there can be more than one occurrence of the previous thing. */
2424 /* */
2425 /* opt_name can be followed by a 'new context' change prefixed with the */
2426 /* symbol >, as in opt1>c2 by eg. */
2427 /* */
2428 /* This function returns as soon as one (or no) option has been parsed and */
2429 /* return the offset to the next option to parse. */
2430 /* */
2431 /* In case of successful parsing, an new option is allocated and its */
2432 /* pointer returned. */
2433 /* ======================================================================== */
2434 static int
2435 opt_parse(char * s, opt_t ** opt)
2437 int opt_optional = 0;
2438 int opt_multiple = 0;
2439 int opt_count_matter = 0;
2440 char opt_count_oper = '\0';
2441 unsigned opt_count_mark = 0;
2442 int opt_args = 0;
2443 char opt_arg[33] = { 0 };
2444 int opt_multiple_args = 0;
2445 int opt_args_count_matter = 0;
2446 char opt_args_count_oper = '\0';
2447 unsigned opt_args_count_mark = 0;
2448 int opt_optional_args = 0;
2449 int opt_eval_first = 0;
2451 int n;
2452 int pos;
2453 int count = 0;
2455 char * s_orig = s;
2457 char * p;
2458 char * opt_name;
2459 char * next_ctx;
2460 char token[65];
2462 *opt = NULL;
2463 memset(opt_arg, '\0', 33);
2465 /* Strip the leading blanks. */
2466 /* """"""""""""""""""""""""" */
2467 while (isblank(*s))
2468 s++;
2470 if (*s == '[') /* Start of an optional option. */
2472 opt_optional = 1;
2473 s++;
2475 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2476 if (s == NULL)
2477 return -1; /* Empty string. */
2479 /* Early EOS, only return success if the option is mandatory. */
2480 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2481 if (!*s)
2482 if (opt_optional == 1)
2483 return -(s - s_orig - 1);
2485 /* Validate the option name */
2486 /* ALPHA+(ALPHANUM|_)* */
2487 /* """""""""""""""""""""""" */
2488 p = token;
2489 if (!isalpha(*p) && *p != '*')
2490 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2492 if (*p == '*')
2493 opt_eval_first = 1;
2495 p++;
2496 while (*p)
2498 if (!isalnum(*p) && *p != '_' && *p != '>')
2499 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2500 * a number or a _ */
2501 p++;
2504 if (opt_eval_first)
2505 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2506 else
2507 opt_name = xstrdup(token);
2509 if (*s == ']')
2511 s++;
2512 while (isblank(*s))
2513 s++;
2515 goto success;
2518 /* Check if it can appear multiple times by looking for the dots. */
2519 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2520 p = strtoken(s, token, 3, "[.]", &pos);
2521 if (p)
2523 if (strcmp(token, "...") == 0)
2525 opt_multiple = 1;
2526 s = p;
2527 if (*s == '<' || *s == '=' || *s == '>')
2529 unsigned value;
2530 int offset;
2532 n = sscanf(s + 1, "%u%n", &value, &offset);
2533 if (n == 1)
2535 opt_count_matter = 1;
2536 opt_count_oper = *s;
2537 opt_count_mark = value;
2539 s += offset + 1;
2542 else
2544 free(opt_name);
2545 return -(s - s_orig - 1);
2549 if (*s == ']')
2551 /* Abort on extraneous ] if the option is mandatory. */
2552 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2553 if (!opt_optional)
2554 return -(s - s_orig - 1);
2556 s++; /* skip the ] */
2558 if (!*s || isblank(*s))
2559 goto success;
2560 else
2561 return -(s - s_orig - 1);
2564 /* A blank separates the option name and the argument tag. */
2565 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2566 if (isblank(*s))
2568 char dots[4];
2570 while (isblank(*s))
2571 s++;
2573 if (!*s)
2574 goto success;
2576 pos = 0;
2577 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2578 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2580 opt_args = 1;
2581 opt_optional_args = 1;
2582 if (n == 2)
2583 opt_multiple_args = 1; /* There were dots. */
2585 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2587 if (*s == '<' || *s == '=' || *s == '>')
2589 unsigned value;
2590 int offset;
2592 n = sscanf(s + 1, "%u%n", &value, &offset);
2593 if (n == 1)
2595 opt_args_count_matter = 1;
2596 opt_args_count_oper = *s;
2597 opt_args_count_mark = value;
2599 s += offset + 1;
2602 /* Optional arg tag must end with a ] */
2603 /* """""""""""""""""""""""""""""""""" */
2604 if (*s != ']')
2606 free(opt_name);
2607 return -(s - s_orig - 1);
2610 s++; /* Skip the ] */
2612 else
2614 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2615 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2617 opt_args = 1;
2618 if (n == 2) /* There were dots. */
2619 opt_multiple_args = 1;
2621 s += pos + !!(n == 2) * 3; /* Skip the dots */
2623 if (*s == '<' || *s == '=' || *s == '>')
2625 unsigned value;
2626 int offset;
2628 n = sscanf(s + 1, "%u%n", &value, &offset);
2629 if (n == 1)
2631 opt_args_count_matter = 1;
2632 opt_args_count_oper = *s;
2633 opt_args_count_mark = value;
2635 s += offset + 1;
2639 if (*s == ']')
2641 /* Abort on extraneous ] if the option is mandatory. */
2642 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2643 if (!opt_optional)
2644 return -(s - s_orig - 1);
2646 s++; /* skip the ] */
2648 /* Strip the following blanks. */
2649 /* """"""""""""""""""""""""""" */
2650 while (isblank(*s))
2651 s++;
2653 goto success;
2655 else if (opt_optional == 0 && (!*s || isblank(*s)))
2657 /* Strip the following blanks. */
2658 /* """"""""""""""""""""""""""" */
2659 while (isblank(*s))
2660 s++;
2662 goto success;
2664 else if (opt_args == 0) /* # was not read it is possibly the start *
2665 * of another option. */
2666 goto success;
2667 else
2668 return -(s - s_orig - 1);
2671 success:
2673 /* Strip the following blanks. */
2674 /* """"""""""""""""""""""""""" */
2675 while (isblank(*s))
2676 s++;
2678 next_ctx = NULL;
2680 if (*opt_name == '>')
2681 fatal_internal("The option name is missing in %s.", opt_name);
2683 count = strchrcount(opt_name, '>');
2684 if (count == 1)
2686 char * tmp = strchr(opt_name, '>');
2687 next_ctx = xstrdup(tmp + 1);
2688 *tmp = '\0';
2690 else if (count > 1)
2691 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2693 *opt = xmalloc(sizeof(opt_t));
2695 (*opt)->name = opt_name;
2696 (*opt)->optional = opt_optional;
2697 (*opt)->multiple = opt_multiple;
2698 (*opt)->opt_count_matter = opt_count_matter;
2699 (*opt)->opt_count_oper = opt_count_oper;
2700 (*opt)->opt_count_mark = opt_count_mark;
2701 (*opt)->args = opt_args;
2702 (*opt)->arg = xstrdup(opt_arg);
2703 (*opt)->optional_args = opt_optional_args;
2704 (*opt)->multiple_args = opt_multiple_args;
2705 (*opt)->opt_args_count_matter = opt_args_count_matter;
2706 (*opt)->opt_args_count_oper = opt_args_count_oper;
2707 (*opt)->opt_args_count_mark = opt_args_count_mark;
2708 (*opt)->eval_first = opt_eval_first;
2709 (*opt)->next_ctx = next_ctx;
2710 (*opt)->ctx_list = ll_new();
2711 (*opt)->constraints_list = ll_new();
2712 (*opt)->eval_before_list = ll_new();
2713 (*opt)->action = NULL;
2714 (*opt)->params = NULL;
2715 (*opt)->data = NULL;
2717 return s - s_orig;
2720 /* ==================================================================== */
2721 /* Try to initialize all the option in a given string */
2722 /* Each parsed option are put in a BST tree with its name as index. */
2723 /* */
2724 /* On collision, the arguments only the signature are required to be */
2725 /* the same else this is considered as an error. Options can be used in */
2726 /* more than one context and can be optional in one and mandatory in */
2727 /* another. */
2728 /* ==================================================================== */
2729 static int
2730 init_opts(char * spec, ctx_t * ctx)
2732 opt_t * opt, *bst_opt;
2733 bst_t * node;
2734 int offset;
2736 while (*spec)
2738 if ((offset = opt_parse(spec, &opt)) > 0)
2740 spec += offset;
2742 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2744 int same_next_ctx = 0;
2746 bst_opt = node->key; /* Node extracted from the BST. */
2748 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2749 same_next_ctx = 1;
2750 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2751 same_next_ctx = 0;
2752 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2753 same_next_ctx = 0;
2754 else
2755 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2757 if (bst_opt->optional_args != opt->optional_args
2758 || bst_opt->multiple_args != opt->multiple_args
2759 || bst_opt->args != opt->args || !same_next_ctx)
2761 fatal_internal("The option %s already exists with "
2762 "a different arguments signature.\n",
2763 opt->name);
2766 /* The newly created opt is already present in options_bst. */
2767 /* We can remove it. */
2768 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2769 opt_free(opt);
2771 /* The new occurrence of the option option is legal */
2772 /* append the current context ptr in the list. */
2773 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2774 ll_append(bst_opt->ctx_list, ctx);
2776 /* Append the new option to the context's options list. */
2777 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2778 ll_append(ctx->opt_list, bst_opt);
2780 else
2782 /* Initialize the option's context list with the current context. */
2783 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2784 ll_append(opt->ctx_list, ctx);
2786 /* Append the new option to the context's options list. */
2787 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2788 ll_append(ctx->opt_list, opt);
2790 /* Insert the new option in the BST. */
2791 /* """"""""""""""""""""""""""""""""" */
2792 bst_search(opt, &options_bst, opt_compare);
2795 else
2797 char * s = xstrndup(spec, -offset);
2798 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2799 free(s);
2801 exit(EXIT_FAILURE);
2805 return 1;
2808 /* ===================================================== */
2809 /* ctxopt initialization function, must be called first. */
2810 /* ===================================================== */
2811 void
2812 ctxopt_init(char * prog_name, char * init_flags)
2814 int n;
2816 contexts_bst = NULL;
2817 options_bst = NULL;
2818 char * ptr;
2820 user_rc = 0;
2821 user_value = 0;
2822 user_string = xmalloc(8);
2823 user_string2 = xmalloc(8);
2824 user_object = NULL;
2825 char flag[33], fname[31], vname[31];
2826 int invalid;
2828 ctxopt_initialized = 1;
2830 /* Initialize current_state.*/
2831 /* """""""""""""""""""""""" */
2832 cur_state = xcalloc(sizeof(state_t), 0);
2834 /* Initialize custom error function pointers to NULL. */
2835 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2836 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2837 for (n = 0; n < CTXOPTERRSIZ; n++)
2838 err_functions[n] = NULL;
2840 /* Parse init_flags if any. */
2841 /* """""""""""""""""""""""" */
2842 while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2844 if (*flag)
2846 if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2847 fatal_internal("Invalid flag assignment: %s.", flag);
2849 if (strcmp(fname, "stop_if_non_option") == 0)
2851 if (eval_yes(vname, &invalid))
2852 flags.stop_if_non_option = 1;
2853 else if (!invalid)
2854 flags.stop_if_non_option = 0;
2855 else
2856 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2858 else if (strcmp(fname, "allow_abbreviations") == 0)
2860 if (eval_yes(vname, &invalid))
2861 flags.allow_abbreviations = 1;
2862 else if (!invalid)
2863 flags.allow_abbreviations = 0;
2864 else
2865 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2867 else
2868 fatal_internal("Invalid flag name: %s.", fname);
2872 /* Update current_state. */
2873 /* """"""""""""""""""""" */
2874 if (prog_name)
2876 if (*prog_name == '\0')
2877 cur_state->prog_name = xstrdup("program_name");
2878 else if ((ptr = strrchr(prog_name, '/')))
2879 cur_state->prog_name = xstrdup(ptr + 1);
2880 else
2881 cur_state->prog_name = xstrdup(prog_name);
2883 else
2884 cur_state->prog_name = xstrdup("program_name");
2887 /* ========================================================================= */
2888 /* Utility function which create and register a par_t object in a BST */
2889 /* embedded in a context. */
2890 /* This object will have a name and a pointer to the option it refers to. */
2891 /* These object will be used to quickly find an option from a command */
2892 /* line parameter during the analysis phase. */
2893 /* */
2894 /* IN : an option name. */
2895 /* IN : a string of command line parameters to associate to the option. */
2896 /* Returns : 1 is all was fine else 0. */
2897 /* ========================================================================= */
2898 static int
2899 opt_set_parms(char * opt_name, char * par_str)
2901 char * par_name, *ctx_name;
2902 char * tmp_par_str, *end_tmp_par_str;
2903 ctx_t * ctx;
2904 opt_t * opt;
2905 bst_t * node;
2906 par_t * par, tmp_par;
2907 int rc = 1; /* return code */
2909 ll_t * list;
2910 ll_node_t * lnode;
2912 /* Look if the given option is defined. */
2913 /* """""""""""""""""""""""""""""""""""" */
2914 opt = locate_opt(opt_name);
2915 if (opt == NULL)
2916 fatal_internal("Unknown option %s.", opt_name);
2918 /* For each context using this option. */
2919 /* """"""""""""""""""""""""""""""""""" */
2920 list = opt->ctx_list;
2922 lnode = list->head;
2923 while (lnode != NULL)
2925 /* Locate the context in the contexts tree. */
2926 /* """""""""""""""""""""""""""""""""""""""" */
2927 ctx_name = ((ctx_t *)(lnode->data))->name;
2929 ctx = locate_ctx(ctx_name);
2930 if (ctx == NULL)
2931 fatal_internal("Unknown context %s.", ctx_name);
2932 else
2934 void * par_bst = ctx->par_bst;
2936 tmp_par_str = xstrdup(par_str);
2937 ltrim(tmp_par_str, " \t");
2938 rtrim(tmp_par_str, " \t", 0);
2939 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2940 if (par_name == NULL)
2941 fatal_internal("Parameters are missing for option %s.", opt_name);
2943 /* For each parameter given in par_str, creates a par_t object and */
2944 /* insert it the in the parameters BST of the context. */
2945 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2946 while (par_name != NULL)
2948 tmp_par.name = par_name;
2950 node = bst_find(&tmp_par, &par_bst, par_compare);
2951 if (node != NULL)
2953 fatal_internal("The parameter %s is already defined in context %s.",
2954 par_name, ctx->name);
2955 rc = 0;
2957 else
2959 par = xmalloc(sizeof(par_t));
2960 par->name = xstrdup(par_name);
2961 par->opt = opt; /* Link the option to this parameter */
2963 bst_search(par, &par_bst, par_compare);
2965 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2968 /* Update the value of the root of ctx->par_bst as it may have */
2969 /* been modified. */
2970 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2971 ctx->par_bst = par_bst;
2973 free(tmp_par_str);
2975 lnode = lnode->next;
2978 return rc;
2981 /* ==================================================================== */
2982 /* Create a new context instance. */
2983 /* IN ctx : a context pointer to allow this instance to */
2984 /* access the context fields */
2985 /* IN prev_ctx_inst : the context instance whose option leading to the */
2986 /* creation of this new context instance is part of */
2987 /* Returns : the new context. */
2988 /* ==================================================================== */
2989 static ctx_inst_t *
2990 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
2992 opt_t * opt;
2993 opt_inst_t * gen_opt_inst;
2994 ctx_inst_t * ctx_inst;
2995 seen_opt_t * seen_opt;
2996 char * str, *opt_name;
2997 void * bst;
2998 bst_t * bst_node;
3000 /* Keep a trace of the opt_inst which was at the origin of the creation */
3001 /* of this context instance. */
3002 /* This will serve during the evaluation of the option callbacks. */
3003 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3004 if (prev_ctx_inst != NULL)
3006 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
3008 /* Update current_state. */
3009 /* """"""""""""""""""""" */
3010 cur_state->opt_name = gen_opt_inst->opt->name;
3012 else
3013 gen_opt_inst = NULL;
3015 /* Create and initialize the new context instance. */
3016 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3017 ctx_inst = xmalloc(sizeof(ctx_inst_t));
3018 ctx_inst->ctx = ctx;
3019 ctx_inst->prev_ctx_inst = prev_ctx_inst;
3020 ctx_inst->gen_opt_inst = gen_opt_inst;
3021 ctx_inst->incomp_bst_list = ll_new();
3022 ctx_inst->opt_inst_list = ll_new();
3023 ctx_inst->opt_req_list = ll_new();
3024 ctx_inst->seen_opt_bst = NULL;
3026 ll_node_t * node;
3028 if (prev_ctx_inst == NULL)
3029 first_ctx_inst = ctx_inst;
3031 /* Initialize the occurrence counters of each opt allowed in the context. */
3032 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3033 node = ctx->opt_list->head;
3034 while (node != NULL)
3036 opt = node->data;
3037 opt->occurrences = 0;
3039 node = node->next;
3042 /* Initialize the BST containing the seen indicator for all the options */
3043 /* allowed in this context instance. */
3044 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3045 node = ctx->opt_list->head;
3046 while (node != NULL)
3048 opt = node->data;
3049 seen_opt = xmalloc(sizeof(seen_opt_t));
3050 seen_opt->opt = opt;
3051 seen_opt->par = NULL;
3052 seen_opt->seen = 0;
3054 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
3056 node = node->next;
3059 /* Initialize the BST containing the incompatibles options. */
3060 /* Incompatibles option names are read from strings found in the list */
3061 /* incomp_list present in each instance of ctx_t. */
3062 /* These names are then used to search for the object of type seen_opt_t */
3063 /* which is already present in the seen_opt_bst of the context instance. */
3064 /* in the BST. */
3065 /* Once found the seen_opt_t object in inserted in the new BST */
3066 /* At the end the new BST in added to the list incomp_bst_list. */
3067 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3068 node = ctx->incomp_list->head;
3069 while (node != NULL)
3071 bst = NULL;
3072 seen_opt_t tmp_seen_opt;
3074 str = xstrdup(node->data);
3075 ltrim(str, " \t");
3076 rtrim(str, " \t", 0);
3077 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3079 while (opt_name != NULL) /* For each option name. */
3081 if ((opt = locate_opt(opt_name)) != NULL)
3083 /* The option found is searched in the tree of potential */
3084 /* seen options. */
3085 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3086 tmp_seen_opt.opt = opt;
3088 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
3089 seen_opt_compare);
3091 if (bst_node != NULL)
3093 /* If found then it is added into the new BST tree. */
3094 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3095 seen_opt = bst_node->key;
3096 bst_search(seen_opt, &bst, seen_opt_compare);
3098 else
3099 /* Not found! That means that the option is unknown in this */
3100 /* context as all options has have a seen_opt structure in */
3101 /* seen_opt_bst. */
3102 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3103 fatal_internal("%s is not known in the context %s.", opt->name,
3104 ctx->name);
3106 else
3107 fatal_internal("Unknown option %s.", opt_name);
3109 opt_name = strtok(NULL, " \t");
3112 free(str);
3113 ll_append(ctx_inst->incomp_bst_list, bst);
3115 node = node->next;
3118 /* Initialize the list of res_t structures according to the */
3119 /* list set in the context by ctxopt_add_ctx_settings/required. */
3120 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3121 node = ctx->req_list->head;
3122 while (node != NULL)
3124 req_t * req = xmalloc(sizeof(req_t));
3126 str = xstrdup(node->data);
3127 ltrim(str, " \t");
3128 rtrim(str, " \t", 0);
3129 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3131 if ((opt = locate_opt(opt_name)) != NULL)
3133 req->opt = opt;
3134 req->or_opt_list = ll_new();
3135 while ((opt_name = strtok(NULL, " \t")) != NULL)
3137 if ((opt = locate_opt(opt_name)) != NULL)
3138 ll_append(req->or_opt_list, opt);
3139 else
3140 fatal_internal("Unknown option %s.", opt_name);
3142 ll_append(ctx_inst->opt_req_list, req);
3144 else
3145 fatal_internal("Unknown option %s.", opt_name);
3147 free(str);
3149 node = node->next;
3151 return ctx_inst;
3154 /* ====================================================================== */
3155 /* Create a list formed by all the significant command line words */
3156 /* Words beginning or ending with { or } are split. Each of these */
3157 /* symbols will get their own place in the list. */
3158 /* */
3159 /* the {...} part delimits a context, the { will not appear in the list */
3160 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3161 /* to facilitate the parsing phase. | must not be used by the end user. */
3162 /* */
3163 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3164 /* program name is not considered. */
3165 /* IN words : is the array of strings constituting the command line to */
3166 /* parse. */
3167 /* Returns : 1 on success, 0 if a { or } is missing. */
3168 /* ====================================================================== */
3169 static int
3170 ctxopt_build_cmdline_list(int nb_words, char ** words)
3172 int i;
3173 char * prev_word = NULL;
3174 char * word;
3175 char * ptr;
3176 int level = 0;
3177 ll_node_t *node, *start_node;
3179 /* The analysis is divided into three passes, this is not optimal but */
3180 /* must be done only one time. Doing that we privilege readability. */
3181 /* */
3182 /* In the following, SG is the ascii character 1d (dec 29) */
3183 /* */
3184 /* The first pass creates the list, extract the leading an trailing */
3185 /* SG '{' and '}' of each word and give them their own place in the */
3186 /* list */
3187 /* */
3188 /* The second pass transform the '{...}' blocks by a trailing SG */
3189 /* ({...} -> ...|) */
3190 /* */
3191 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3192 /* the middle in the remaining list elements and recreate the pseudo */
3193 /* argument: {} */
3194 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3196 /* If the option list is not empty, clear it before going further. */
3197 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3198 if (cmdline_list != NULL)
3200 node = cmdline_list->head;
3201 while (node != NULL)
3203 free(node->data);
3204 ll_delete(cmdline_list, node);
3205 node = cmdline_list->head;
3208 else
3209 cmdline_list = ll_new();
3211 start_node = cmdline_list->head; /* In the following loop start_node will *
3212 * contain a pointer to the current *
3213 * word stripped from its leading *
3214 * sequence of {, }. */
3215 for (i = 0; i < nb_words; i++)
3217 size_t len = strlen(words[i]);
3218 size_t start, end;
3219 char * str;
3221 str = words[i];
3223 /* Replace each occurrence of the legal word {} by the characters */
3224 /* 0x02 and 0x03 to hide them from the following process. */
3225 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3226 while ((ptr = strstr(str, "{}")) != NULL)
3228 *ptr = 0x02; /* Arbitrary values unlikely. */
3229 *(ptr + 1) = 0x03; /* present in a word */
3232 if (len > 1) /* The word contains at least 2 characters. */
3234 start = 0;
3236 /* Interpret its beginning and look for the start of the real word. */
3237 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3238 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
3240 ll_append(cmdline_list, xstrndup(str + start, 1));
3241 start++;
3242 start_node = cmdline_list->tail;
3245 end = len - 1;
3246 if (str[end] == '{' || str[end] == '}')
3248 if (end > 0 && str[end - 1] != '\\')
3250 ll_append(cmdline_list, xstrndup(str + end, 1));
3251 end--;
3252 node = cmdline_list->tail;
3254 while (str[end] == '{' || str[end] == '}')
3256 if (end > start && str[end - 1] == '\\')
3257 break;
3259 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3260 end--;
3261 node = node->prev;
3266 if (start <= end)
3268 if (start_node != NULL)
3269 ll_insert_after(cmdline_list, start_node,
3270 xstrndup(str + start, end - start + 1));
3271 else
3272 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3273 start_node = cmdline_list->tail;
3276 else if (len == 1)
3278 ll_append(cmdline_list, xstrdup(str));
3279 start_node = cmdline_list->tail;
3283 /* 2nd pass. */
3284 /* """"""""" */
3285 node = cmdline_list->head;
3287 level = 0;
3288 while (node != NULL)
3290 word = node->data;
3292 if (strcmp(word, "{") == 0)
3294 ll_node_t * old_node = node;
3295 level++;
3296 node = node->next;
3297 free(word);
3298 ll_delete(cmdline_list, old_node);
3300 else if (strcmp(word, "}") == 0)
3302 level--;
3304 if (level < 0)
3305 return 0;
3306 else
3307 *word = 0x1d;
3309 else
3310 node = node->next;
3313 if (level != 0)
3314 return 0;
3316 /* 3rd pass. */
3317 /* """"""""" */
3318 node = cmdline_list->head;
3320 while (node != NULL)
3322 word = node->data;
3324 /* Restore the original { and } characters forming the legal word {}. */
3325 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3326 while ((ptr = strchr(word, 0x02)) != NULL)
3327 *ptr = '{';
3328 while ((ptr = strchr(word, 0x03)) != NULL)
3329 *ptr = '}';
3331 /* Remove a SG if the previous element is SG. */
3332 /* """""""""""""""""""""""""""""""""""""""""" */
3333 if (strcmp(word, "\x1d") == 0)
3335 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3337 ll_node_t * old_node = node;
3338 node = node->prev;
3339 free(old_node->data);
3340 ll_delete(cmdline_list, old_node);
3343 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3344 * a parameter. Protect it. */
3346 free(node->data);
3347 node->data = xstrdup("\\-");
3350 prev_word = node->data;
3351 node = node->next;
3354 /* Clean useless and SG at the beginning and end of list. */
3355 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3356 node = cmdline_list->head;
3358 if (node == NULL)
3359 return 1;
3361 word = node->data;
3363 if (strcmp(word, "\x1d") == 0)
3365 free(word);
3366 ll_delete(cmdline_list, node);
3369 node = cmdline_list->tail;
3370 if (node == NULL)
3371 return 1;
3373 word = node->data;
3375 if (strcmp(word, "\x1d") == 0)
3377 free(word);
3378 ll_delete(cmdline_list, node);
3381 return 1;
3384 /* ===================================================================== */
3385 /* Build and analyze the command line list and create the linked data */
3386 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3387 /* This function identifies the following errors and creates an array of */
3388 /* The remaining unanalyzed arguments. */
3389 /* - detect missing arguments */
3390 /* - detect too many arguments */
3391 /* - detect unknown parameters in a context */
3392 /* - detect too many occurrences of a parameters in a context */
3393 /* - detect missing required arguments in a context */
3394 /* */
3395 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3396 /* program name is not considered */
3397 /* IN words : is the array of strings constituting the command line to */
3398 /* parse. */
3399 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3400 /* is present in the list. */
3401 /* OUT rem_args : array of remaining command line arguments if a -- */
3402 /* is present in the list. This array must be free by */
3403 /* The caller as it is allocated here. */
3404 /* ===================================================================== */
3405 void
3406 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3407 char *** rem_args)
3409 char * ctxopt_debug_env; /* Environment variable CTXOPT_DEBUG content. */
3410 int ctxopt_debug; /* 1 if ctxopt_debug_env is set and not empty. *
3411 | 0 if ctxopt_debug_env is unset or empty. */
3413 ctx_t * ctx;
3414 opt_t * opt;
3415 par_t * par;
3416 ctx_inst_t * ctx_inst;
3417 opt_inst_t * opt_inst;
3418 int expect_par = 0;
3419 int expect_arg = 0;
3420 int expect_par_or_arg = 0;
3422 ll_node_t * cli_node;
3423 bst_t * bst_node;
3424 seen_opt_t * bst_seen_opt;
3425 char * par_name;
3426 void * bst;
3428 ll_node_t * node;
3430 if (!ctxopt_build_cmdline_list(nb_words, words))
3431 fatal_internal("The command line could not be parsed: "
3432 "missing '{' or '}' detected.");
3434 if (main_ctx == NULL)
3435 fatal_internal("At least one context must have been created.");
3437 /* Check that all options has an action and at least one parameter. */
3438 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3439 bst_walk(options_bst, bst_check_opt_cb);
3441 /* CTXOPT debug setting */
3442 /* """""""""""""""""""" */
3443 ctxopt_debug_env = getenv("CTXOPT_DEBUG");
3444 if (ctxopt_debug_env != NULL && *ctxopt_debug_env != '\0')
3445 ctxopt_debug = 1;
3446 else
3447 ctxopt_debug = 0;
3449 /* Create the first ctx_inst record. */
3450 /* """"""""""""""""""""""""""""""""" */
3451 ctx = main_ctx;
3453 ctx_inst_list = ll_new();
3454 ctx_inst = new_ctx_inst(ctx, NULL);
3455 ctx_inst->par_name = NULL;
3457 /* Update current_state. */
3458 /* """"""""""""""""""""" */
3459 cur_state->ctx_name = ctx->name;
3461 ll_append(ctx_inst_list, ctx_inst);
3463 /* For each node in the command line. */
3464 /* """""""""""""""""""""""""""""""""" */
3465 cli_node = cmdline_list->head;
3466 expect_par = 1;
3467 par_name = NULL;
3469 while (cli_node != NULL)
3471 if (strcmp(cli_node->data, "--") == 0)
3472 break; /* No new parameter will be analyzed after this point. */
3474 par_name = cli_node->data;
3476 /* Replace a leading -- by a single - */
3477 /* """""""""""""""""""""""""""""""""" */
3478 if (strncmp(par_name, "--", 2) == 0)
3479 par_name += 1; /* Ignore the first dash */
3481 if (strcmp(par_name, "\x1d") == 0)
3483 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3484 check_for_occurrence_issues(ctx_inst);
3485 check_for_requirement_issues(ctx_inst);
3487 /* Forced backtracking to the previous context instance. */
3488 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3489 if (ctx_inst->prev_ctx_inst != NULL)
3491 ctx_inst = ctx_inst->prev_ctx_inst;
3492 ctx = ctx_inst->ctx;
3494 /* Update current_states. */
3495 /* """"""""""""""""""""" */
3496 cur_state->ctx_name = ctx->name;
3497 cur_state->ctx_par_name = ctx_inst->par_name;
3499 if (ctxopt_debug)
3500 fprintf(stderr,
3501 "CTXOPT_DEBUG: Context forced backtrack, "
3502 "new current context: %s.\n",
3503 ctx->name);
3505 else
3507 /* Update current_state. */
3508 /* """"""""""""""""""""" */
3509 cur_state->ctx_par_name = NULL;
3512 else if (expect_par && *par_name == '-')
3514 int pos = 0;
3515 char * prefix;
3517 /* Update current_state. */
3518 /* """"""""""""""""""""" */
3519 cur_state->cur_opt_par_name = par_name;
3520 cur_state->ctx_name = ctx->name;
3521 cur_state->ctx_par_name = ctx_inst->par_name;
3523 if (ctxopt_debug)
3524 fprintf(stderr, "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3525 par_name, cur_state->ctx_name);
3527 /* An expected parameter has been seen. */
3528 /* """""""""""""""""""""""""""""""""""" */
3529 if ((par = locate_par(par_name, ctx)) == NULL)
3531 opt_t * popt;
3532 char * word;
3534 /* Look if this parameter is an unique abbreviation of a longer */
3535 /* parameter. If this is the case then just replace it with its */
3536 /* full length version and try again. */
3537 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3538 if (flags.allow_abbreviations)
3539 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3541 cli_node->data = word;
3542 continue;
3545 /* Try to find a prefix which is a valid parameter in this context */
3546 /* If found, split the cli_node in two to build a new parameter */
3547 /* node and followed by a node containing the remaining string */
3548 /* If the new parameter corresponds to an option not taking */
3549 /* argument then prefix the remaining string whit a dash as it may */
3550 /* contain a new parameter. */
3551 /* The new parameter will be re-evaluated in the next iteration of */
3552 /* the loop. */
3553 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3554 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3555 if (prefix != NULL && pos != 0)
3557 if (ctxopt_debug)
3558 fprintf(stderr,
3559 "CTXOPT_DEBUG: Found a valid parameter "
3560 "as a prefix of %s: %s.\n",
3561 par_name, prefix);
3563 cli_node->data = prefix; /* prefix contains le name of a valid *
3564 | parameter in this context. */
3566 if (popt->args)
3568 /* The parameter may be followed by arguments. */
3569 /* ''''''''''''''''''''''''''''''''''''''''''' */
3570 if (*(par_name + pos) == '-')
3572 word = xstrdup("\\"); /* Protect the '-' */
3573 word = strappend(word, par_name + pos, (char *)0);
3575 else
3576 word = xstrdup(par_name + pos);
3578 else
3580 /* The parameter does not take arguments, the */
3581 /* following word must be a parameter or nothing */
3582 /* hence prefix it with a dash. */
3583 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3584 word = xstrdup("-");
3585 word = strappend(word, par_name + pos, (char *)0);
3588 /* Insert it after the current node in the list. */
3589 /* """"""""""""""""""""""""""""""""""""""""""""" */
3590 ll_insert_after(cmdline_list, cli_node, word);
3592 continue; /* loop */
3594 else
3596 check_for_missing_mandatory_opt(ctx_inst, par_name);
3597 check_for_occurrence_issues(ctx_inst);
3598 check_for_requirement_issues(ctx_inst);
3600 if (ctx_inst->prev_ctx_inst == NULL)
3602 char * errmsg = xstrdup("");
3604 /* Update current_state. */
3605 /* """"""""""""""""""""" */
3606 cur_state->ctx_par_name = NULL;
3608 *user_string = '\0';
3609 *user_string2 = '\0';
3611 user_string = strappend(user_string, par_name, (char *)0);
3613 bst_walk(contexts_bst, bst_match_par_cb);
3615 if (*user_string2 != '\0')
3617 errmsg = strappend(
3618 errmsg,
3619 "\nThis parameter is only valid in one of the following "
3620 "contexts:\n",
3621 user_string2,
3622 "\n\nSwitch to one of them first using the appropriate "
3623 "parameter, see below.\n",
3624 (char *)0);
3627 fatal(CTXOPTUNKPAR, errmsg);
3629 else
3631 /* Tries to backtrack and analyse the same parameter in the */
3632 /* previous context. */
3633 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3634 ctx_inst = ctx_inst->prev_ctx_inst;
3635 ctx = ctx_inst->ctx;
3637 if (ctxopt_debug)
3638 fprintf(stderr,
3639 "CTXOPT_DEBUG: Context backtrack, "
3640 "new current context: %s.\n",
3641 ctx->name);
3643 /* Update current_state. */
3644 /* """"""""""""""""""""" */
3645 cur_state->ctx_name = ctx->name;
3646 cur_state->ctx_par_name = ctx_inst->par_name;
3648 cli_node = cli_node->prev;
3652 else
3654 seen_opt_t seen_opt;
3656 /* The parameter is valid in the context, create a opt_inst and */
3657 /* append it to the ctx_inst list options list. */
3658 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3659 opt = par->opt;
3661 opt->occurrences++;
3663 opt_inst = xmalloc(sizeof(opt_inst_t));
3664 opt_inst->opt = opt;
3665 opt_inst->par = par_name;
3666 opt_inst->values_list = ll_new();
3667 opt_inst->next_ctx_inst = NULL;
3669 /* Update current_state. */
3670 /* """"""""""""""""""""" */
3671 cur_state->cur_opt_params = opt->params;
3673 /* Priority option are inserted at the start of the opt_inst list */
3674 /* but their order of appearance in the context definition must */
3675 /* be preserver so each new priority option will be placed after */
3676 /* the previous ones at the start of the opt_inst list. */
3677 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3678 if (!opt->eval_first)
3680 /* Look if we have a registered dependency in the order of the */
3681 /* evaluation of two options. */
3682 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3683 if (opt->eval_before_list->len > 0)
3685 ll_t * list = ctx_inst->opt_inst_list;
3686 ll_node_t * opt_inst_node;
3688 ll_t * before_list = opt->eval_before_list;
3689 ll_node_t * before_node = before_list->head;
3691 ll_node_t * target_node = NULL; /* If not NULL, the new node *
3692 | will be inserted before it. */
3694 /* For each entry in eval_before_list, try to find if it */
3695 /* refers to an option already entered in the context. If this */
3696 /* is the case, insert it just before it instead of putting it */
3697 /* at the end. */
3698 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3699 while (before_node != NULL)
3701 opt_inst_node = list->head;
3703 while (opt_inst_node != target_node)
3705 opt_t * tmp_opt = (((opt_inst_t *)opt_inst_node->data))->opt;
3707 /* We have found an option mentioned if the before_list */
3708 /* of the option we want to add. We can stop searching. */
3709 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3710 if (strcmp(tmp_opt->name, ((opt_t *)before_node->data)->name))
3711 opt_inst_node = opt_inst_node->next;
3712 else
3713 target_node = opt_inst_node; /* Set the target node. */
3716 before_node = before_node->next;
3719 /* Insert or append ? */
3720 /* """""""""""""""""" */
3721 if (target_node != NULL)
3722 ll_insert_before(ctx_inst->opt_inst_list, target_node, opt_inst);
3723 else
3724 ll_append(ctx_inst->opt_inst_list, opt_inst);
3726 else
3727 ll_append(ctx_inst->opt_inst_list, opt_inst);
3729 else
3731 ll_node_t * opt_inst_node = ctx_inst->opt_inst_list->head;
3732 opt_inst_t * tmp_opt_inst;
3734 while (opt_inst_node != NULL)
3736 tmp_opt_inst = opt_inst_node->data;
3737 if (!tmp_opt_inst->opt->eval_first)
3739 ll_insert_before(ctx_inst->opt_inst_list, opt_inst_node,
3740 opt_inst);
3741 break;
3743 else
3744 opt_inst_node = opt_inst_node->next;
3746 if (opt_inst_node == NULL)
3747 ll_append(ctx_inst->opt_inst_list, opt_inst);
3750 /* Check if an option was already seen in the */
3751 /* current context instance. */
3752 /* """""""""""""""""""""""""""""""""""""""""" */
3753 seen_opt.opt = opt;
3755 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3756 seen_opt_compare);
3758 /* bst_node cannot be NULL here. */
3760 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3762 if (!opt->multiple && bst_seen_opt->seen == 1)
3763 fatal(CTXOPTDUPOPT, NULL);
3765 /* Check if this option is compatible with the options already */
3766 /* seen in this context instance. */
3767 /* Look if the option is present in one on the BST present in */
3768 /* the incomp_bst_list of the context instance. */
3769 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3770 node = ctx_inst->incomp_bst_list->head;
3771 while (node != NULL)
3773 bst = node->data;
3774 user_object = NULL;
3776 /* There can only have one seen_opt object in the BST tree was */
3777 /* already seen, try to locate it, the result will be put in */
3778 /* user_object by the bst_seen_opt_seen_cb function. */
3779 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3780 bst_walk(bst, bst_seen_opt_seen_cb);
3782 /* If it is the case, look if the current option is also */
3783 /* in this BST. */
3784 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3785 if (user_object != NULL)
3787 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3789 if (bst_node != NULL)
3791 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3792 if (bst_seen_opt->seen == 0)
3793 fatal(CTXOPTINCOPT, (char *)user_object);
3797 node = node->next;
3800 /* Mark this option as seen in the current context instance. */
3801 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3802 bst_seen_opt->seen = 1;
3803 free(bst_seen_opt->par);
3804 bst_seen_opt->par = xstrdup(par_name);
3806 /* If this option leads to a next context, create a new ctx_inst */
3807 /* and switch to it for the analyse of the future parameter. */
3808 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3809 if (opt->next_ctx != NULL)
3811 ctx = locate_ctx(opt->next_ctx);
3813 if (ctx == NULL)
3814 fatal_internal("Unknown context %s.", opt->next_ctx);
3816 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3817 ctx_inst->par_name = xstrdup(par_name);
3819 ll_append(ctx_inst_list, ctx_inst);
3821 if (ctxopt_debug)
3822 fprintf(stderr,
3823 "CTXOPT_DEBUG: Context change, "
3824 "new current context: %s.\n",
3825 ctx->name);
3828 /* Look is we must expect some arguments. */
3829 /* """""""""""""""""""""""""""""""""""""" */
3830 expect_par_or_arg = 0;
3831 expect_par = 0;
3832 expect_arg = 0;
3834 if (!opt->args)
3835 expect_par = 1; /* Parameter doesn't accept any argument. */
3836 else
3838 if (!opt->optional_args)
3839 expect_arg = 1; /* Parameter has mandatory arguments. */
3840 else
3841 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3845 else if (expect_par && *par_name != '-')
3847 ll_node_t * n = cli_node->next;
3849 if (!flags.stop_if_non_option)
3850 /* Look if potential arguments must still be analyzed until the */
3851 /* end of the context/command line part to analyze/command line. */
3852 /* If this is the case we have met an extra argument. */
3853 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3854 while (n != NULL)
3856 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3857 fatal(CTXOPTUNXARG, NULL);
3859 if (*(char *)(n->data) == '-')
3860 fatal(CTXOPTUNXARG, NULL);
3862 n = n->next;
3865 break; /* An unexpected non parameter was seen, if no Potential *
3866 | arguments remain in the command line or *
3867 | flags.stop_if_non_option is set, assume that it is is *
3868 | the first of the non arguments and stop the command *
3869 | line analysis. */
3871 else if (expect_arg && *par_name != '-')
3873 ll_node_t * cstr_node;
3874 constraint_t * cstr;
3876 if (ctxopt_debug)
3877 fprintf(stderr, "CTXOPT_DEBUG: Argument: %s.\n", par_name);
3879 /* Check if the arguments of the option respects */
3880 /* the attached constraints if any. */
3881 /* """"""""""""""""""""""""""""""""""""""""""""" */
3882 cstr_node = opt->constraints_list->head;
3883 while (cstr_node != NULL)
3885 cstr = cstr_node->data;
3886 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3887 cur_state->cur_opt_par_name))
3889 fputs("\n", stderr);
3890 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3893 cstr_node = cstr_node->next;
3896 /* If the argument is valid, store it. */
3897 /* """"""""""""""""""""""""""""""""""" */
3898 if (*par_name == '\\' && *(par_name + 1) == '-')
3899 ll_append(opt_inst->values_list, par_name + 1);
3900 else
3901 ll_append(opt_inst->values_list, par_name);
3903 expect_arg = 0;
3904 expect_par = 0;
3905 expect_par_or_arg = 0;
3907 if (opt->multiple_args)
3908 expect_par_or_arg = 1;
3909 else
3910 expect_par = 1; /* Parameter takes only one argument. */
3912 else if (expect_arg && *par_name == '-')
3913 fatal(CTXOPTMISARG, NULL);
3914 else if (expect_par_or_arg)
3916 expect_arg = 0;
3917 expect_par = 0;
3918 expect_par_or_arg = 0;
3920 if (*par_name != '-')
3921 expect_arg = 1; /* Consider this word as an argument and retry. */
3922 else
3923 expect_par = 1; /* Consider this word as a parameter and retry. */
3925 cli_node = cli_node->prev;
3928 cli_node = cli_node->next;
3931 if (cmdline_list->len > 0 && par_name && *par_name == '-')
3933 if (expect_arg && !opt->optional_args)
3934 fatal(CTXOPTMISARG, NULL);
3937 /* Look if a context_instance has unseen mandatory options. */
3938 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3939 node = ctx_inst_list->head;
3940 while (node != NULL)
3942 ctx_inst = node->data;
3944 /* Update current_state. */
3945 /* """"""""""""""""""""" */
3946 cur_state->ctx_name = ctx_inst->ctx->name;
3947 cur_state->ctx_par_name = ctx_inst->par_name;
3949 check_for_missing_mandatory_opt(ctx_inst, par_name);
3950 check_for_occurrence_issues(ctx_inst);
3951 check_for_requirement_issues(ctx_inst);
3953 node = node->next;
3956 /* Allocate the array containing the remaining not analyzed */
3957 /* command line arguments. */
3958 /* NOTE: The strings in the array are just pointer to the */
3959 /* data of the generating list and must not be freed. */
3960 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3961 if (cli_node != NULL)
3963 if (strcmp((char *)cli_node->data, "--") == 0)
3964 /* The special parameter -- was encountered, the -- argument is not */
3965 /* put in the remaining arguments. */
3966 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3967 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3968 else
3969 /* A non parameter was encountered when a parameter was expected. We */
3970 /* assume that the evaluation of the remaining command line argument */
3971 /* are not the responsibility of the users code. */
3972 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3973 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3975 else
3977 *nb_rem_args = 0;
3978 *rem_args = xmalloc(sizeof(char *));
3979 (*rem_args)[0] = NULL;
3983 /* ==================================================== */
3984 /* Free ctxopt memory used for its internal structures. */
3985 /* ==================================================== */
3986 void
3987 ctxopt_free_memory(void)
3989 ll_destroy(cmdline_list, free);
3990 ll_destroy(ctx_inst_list, ctx_inst_free);
3991 bst_destroy(options_bst, opt_free);
3992 bst_destroy(contexts_bst, ctx_free);
3995 /* ==================================================================== */
3996 /* Parse the options data structures and launches the callback function */
3997 /* attached to each options instances. */
3998 /* This calls a recursive function which proceeds context per context. */
3999 /* ==================================================================== */
4000 void
4001 ctxopt_evaluate(void)
4003 evaluate_ctx_inst(first_ctx_inst);
4006 /* =================================================================== */
4007 /* Recursive function called by ctxopt_evaluate to process the list of */
4008 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
4009 /* action attached to the context and its option instances. */
4010 /* =================================================================== */
4011 static void
4012 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
4014 opt_inst_t * opt_inst;
4015 ctx_t * ctx;
4016 opt_t * opt;
4017 ll_node_t * opt_inst_node;
4018 char ** args;
4019 int nb_args;
4021 if (ctx_inst == NULL)
4022 return;
4024 ctx = ctx_inst->ctx;
4026 /* Do not evaluate the action attached to this context is there is no */
4027 /* option to evaluate. */
4028 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4029 opt_inst_node = ctx_inst->opt_inst_list->head;
4030 if (opt_inst_node == NULL)
4031 return;
4033 /* Call the entering action attached to this context if any. */
4034 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4035 if (ctx->action != NULL)
4037 if (ctx_inst->prev_ctx_inst != NULL)
4038 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
4039 ctx->nb_data, ctx->data);
4040 else
4041 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
4044 /* For each instance of options. */
4045 /* """"""""""""""""""""""""""""" */
4046 while (opt_inst_node != NULL)
4048 opt_inst = (opt_inst_t *)(opt_inst_node->data);
4049 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
4050 &args);
4051 opt = opt_inst->opt;
4053 /* Launch the attached action if any. */
4054 /* """""""""""""""""""""""""""""""""" */
4055 if (opt->action != NULL)
4056 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
4057 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
4059 if (opt_inst->next_ctx_inst != NULL)
4060 evaluate_ctx_inst(opt_inst->next_ctx_inst);
4062 if (args != NULL)
4063 free(args);
4065 opt_inst_node = opt_inst_node->next;
4068 /* Call the exiting action attached to this context if any. */
4069 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4070 if (ctx->action != NULL)
4072 if (ctx_inst->prev_ctx_inst != NULL)
4073 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
4074 ctx->nb_data, ctx->data);
4075 else
4076 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
4080 /* ============================================================ */
4081 /* Create and initializes a new context. */
4082 /* - allocate space. */
4083 /* - name it. */
4084 /* - initialize its option with a few of their characteristics. */
4085 /* ============================================================ */
4086 void
4087 ctxopt_new_ctx(char * name, char * opts_specs)
4089 ctx_t * ctx;
4090 char * p;
4092 if (!ctxopt_initialized)
4093 fatal_internal("Please call ctxopt_init first.");
4095 ctx = xmalloc(sizeof(ctx_t));
4097 /* Validates the context name: */
4098 /* ALPHA+(ALPHANUM|_)* */
4099 /* """"""""""""""""""""""""""" */
4100 p = name;
4101 if (!isalpha(*p))
4102 fatal_internal("A context name must start with a letter: %s.", name);
4104 p++;
4105 while (*p)
4107 if (!isalnum(*p) && *p != '_')
4108 fatal_internal("A context name must only contain letters, "
4109 "numbers or '_': %s.",
4110 name);
4111 p++;
4114 ctx->name = xstrdup(name);
4115 ctx->opt_list = ll_new(); /* List of options legit in this context. */
4116 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
4117 ctx->req_list = ll_new(); /* List of opts/required opts tuples (str). */
4118 ctx->par_bst = NULL;
4119 ctx->data = NULL;
4120 ctx->action = NULL;
4122 /* The first created context is the main one. */
4123 /* """""""""""""""""""""""""""""""""""""""""" */
4124 if (contexts_bst == NULL)
4126 main_ctx = ctx;
4128 cur_state->ctx_name = ctx->name;
4131 if (init_opts(opts_specs, ctx) == 0)
4132 exit(EXIT_FAILURE);
4133 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
4134 fatal_internal("The context %s already exists.", name);
4135 else
4136 bst_search(ctx, &contexts_bst, ctx_compare);
4139 /* ==================================================== */
4140 /* Display a usage screen limited to a specific context */
4141 /* IN: the context name. */
4142 /* IN: what to do after (continue or exit the program) */
4143 /* possible values: continue_after, exit_after. */
4144 /* ==================================================== */
4145 void
4146 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
4148 ctx_t * ctx;
4149 ll_t * list;
4151 int has_optional = 0;
4152 int has_ellipsis = 0;
4153 int has_rule = 0;
4154 int has_generic_arg = 0;
4155 int has_ctx_change = 0;
4156 int has_early_eval = 0;
4158 ctx = locate_ctx(ctx_name);
4159 if (ctx == NULL)
4160 fatal_internal("Unknown context %s.", ctx_name);
4162 if (cur_state->ctx_par_name == NULL)
4163 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
4164 else
4165 printf("\nSynopsis for the context introduced by %s:\n",
4166 cur_state->ctx_par_name);
4168 list = ctx->opt_list;
4169 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4170 &has_ctx_change, &has_early_eval);
4172 print_before_constraints(list);
4174 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4175 has_optional, has_ellipsis, has_rule);
4177 if (action == exit_after)
4178 exit(EXIT_FAILURE);
4181 /* =================================================== */
4182 /* Display a full usage screen about all contexts. */
4183 /* IN: what to do after (continue or exit the program) */
4184 /* possible values: continue_after, exit_after. */
4185 /* =================================================== */
4186 void
4187 ctxopt_disp_usage(usage_behaviour action)
4189 ll_t * list;
4190 int has_optional = 0;
4191 int has_ellipsis = 0;
4192 int has_rule = 0;
4193 int has_generic_arg = 0;
4194 int has_ctx_change = 0;
4195 int has_early_eval = 0;
4197 if (main_ctx == NULL)
4198 fatal_internal("At least one context must have been created.");
4200 /* Usage for the first context. */
4201 /* """""""""""""""""""""""""""" */
4202 printf("\nAllowed options in the base context:\n");
4203 list = main_ctx->opt_list;
4204 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4205 &has_ctx_change, &has_early_eval);
4207 /* Dependency constraints between options. */
4208 /* """"""""""""""""""""""""""""""""""""""" */
4209 print_before_constraints(list);
4211 /* Usage for the other contexts. */
4212 /* """"""""""""""""""""""""""""" */
4213 bst_walk(contexts_bst, bst_print_ctx_cb);
4215 /* Contextual syntactic explanations. */
4216 /* """""""""""""""""""""""""""""""""" */
4217 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4218 has_optional, has_ellipsis, has_rule);
4220 if (action == exit_after)
4221 exit(EXIT_FAILURE);
4224 /* *********************************** */
4225 /* Built-in constraint check functions */
4226 /* *********************************** */
4228 /* ============================================================= */
4229 /* This constraint checks if each arguments respects a format as */
4230 /* defined for the scanf function. */
4231 /* return 1 if yes and 0 if no. */
4232 /* ============================================================= */
4234 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
4236 int rc = 0;
4238 char x[256];
4239 char y;
4240 char * format;
4242 if (nb_args != 1)
4243 fatal_internal("Format constraint, invalid number of parameters.");
4245 if (strlen(value) > 255)
4246 value[255] = '\0';
4248 format = xstrdup(args[0]);
4249 format = strappend(format, "%c", (char *)0);
4251 rc = sscanf(value, format, x, &y);
4252 if (rc != 1)
4253 fprintf(stderr,
4254 "The argument %s of %s does not respect the imposed format %s.",
4255 value, par, args[0]);
4257 free(format);
4259 return rc == 1;
4262 /* ================================================================== */
4263 /* This constraint checks if each arguments of the option instance is */
4264 /* between a minimum and a maximum (inclusive). */
4265 /* return 1 if yes and 0 if no. */
4266 /* ================================================================== */
4268 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
4270 regex_t re;
4272 if (nb_args != 1)
4273 fatal_internal(
4274 "Regular expression constraint, invalid number of parameters.");
4276 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
4277 fatal_internal("Invalid regular expression %s.", args[0]);
4279 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
4281 fprintf(stderr,
4282 "The argument %s of %s doesn't match the constraining "
4283 "regular expression %s.",
4284 value, par, args[0]);
4285 return 0;
4288 regfree(&re);
4290 return 1;
4293 /* ================================================================== */
4294 /* This constraint checks if each arguments of the option instance is */
4295 /* between a minimum and a maximum (inclusive). */
4296 /* return 1 if yes and 0 if no. */
4297 /* ================================================================== */
4299 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
4301 long min, max;
4302 char c;
4303 char * ptr;
4304 int n;
4305 long v;
4306 int min_only = 0;
4307 int max_only = 0;
4309 if (nb_args != 2)
4310 fatal_internal("Range constraint, invalid number of parameters.");
4312 if (strcmp(args[0], ".") == 0)
4313 max_only = 1;
4314 else
4315 n = sscanf(args[0], "%ld%c", &min, &c);
4317 if (!max_only && n != 1)
4318 fatal_internal("Range constraint, min: invalid parameters.");
4320 if (strcmp(args[1], ".") == 0)
4321 min_only = 1;
4322 else
4323 n = sscanf(args[1], "%ld%c", &max, &c);
4325 if (!min_only && n != 1)
4326 fatal_internal("Range constraint, max: invalid parameters.");
4328 if (min_only && max_only)
4329 fatal_internal("Range constraint, invalid parameters.");
4331 errno = 0;
4332 v = strtol(value, &ptr, 10);
4333 if (errno || ptr == value)
4334 return 0;
4336 if (min_only)
4338 if (v < min)
4340 fprintf(stderr,
4341 "The argument %ld of %s is not greater than or equal to %ld.", v,
4342 par, min);
4343 return 0;
4345 else
4346 return 1;
4348 else if (max_only)
4350 if (v > max)
4352 fprintf(stderr,
4353 "The argument %ld of %s is not less than or equal to %ld.", v,
4354 par, max);
4355 return 0;
4357 else
4358 return 1;
4360 else if (v < min || v > max)
4362 fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
4363 par, min, max);
4364 return 0;
4367 return 1; /* check passed */
4370 /* =============================================================== */
4371 /* This function provides a way to set the behaviour of a context. */
4372 /* =============================================================== */
4373 void
4374 ctxopt_add_global_settings(settings s, ...)
4376 va_list(args);
4377 va_start(args, s);
4379 switch (s)
4381 case error_functions:
4383 typedef void fn(errors e, state_t * state);
4385 void (*function)(errors e, state_t * state);
4387 errors e;
4388 e = va_arg(args, errors);
4389 function = va_arg(args, fn *);
4390 err_functions[e] = function;
4391 break;
4394 default:
4395 break;
4397 va_end(args);
4400 /* ================================================================ */
4401 /* This function provides a way to set the behaviour of an option. */
4402 /* It can take a variable number of arguments according to its */
4403 /* first argument: */
4404 /* - parameter: */
4405 /* o a string containing an option name and all its possible */
4406 /* parameters separates by spaces, tabs or commas (char *) */
4407 /* (e.g: "help -h -help"). */
4408 /* - actions: */
4409 /* o a string containing an option name. */
4410 /* o a pointer to a function which will be called at evaluation */
4411 /* time. */
4412 /* - constraints: */
4413 /* o a string containing an option name. */
4414 /* o a pointer to a function to check if an argument is valid. */
4415 /* o a strings containing the arguments to this function. */
4416 /* ================================================================ */
4417 void
4418 ctxopt_add_opt_settings(settings s, ...)
4420 opt_t * opt;
4421 void * ptr = NULL;
4423 va_list(args);
4424 va_start(args, s);
4426 switch (s)
4428 /* This part associates some command line parameters to an option. */
4429 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4430 case parameters:
4432 char * opt_name;
4433 char * params;
4435 /* The second argument must be a string containing: */
4436 /* - The name of an existing option. */
4437 /* - a list of parameters with a leading dash (-). */
4438 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4439 ptr = va_arg(args, char *);
4440 opt_name = ptr;
4442 if (opt_name != NULL)
4444 if ((opt = locate_opt(opt_name)) != NULL)
4446 ptr = va_arg(args, char *);
4447 params = ptr;
4449 if (!opt_set_parms(opt_name, params))
4450 fatal_internal(
4451 "Duplicated parameters or bad settings for the option %s.",
4452 params);
4454 else
4455 fatal_internal("Unknown option %s.", opt_name);
4457 else
4458 fatal_internal(
4459 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4461 /* Here opt is a known option. */
4462 /* """"""""""""""""""""""""""" */
4463 if (opt->params != NULL)
4464 fatal_internal("Parameters are already set for %s.", opt_name);
4465 else
4467 size_t n;
4468 size_t l = strlen(params);
4470 opt->params = xstrdup(params);
4471 while ((n = strcspn(opt->params, " \t")) < l)
4472 opt->params[n] = '|';
4475 break;
4478 /* This part associates a callback function to an option. */
4479 /* This function will be called when an instance of an option */
4480 /* is evaluated. */
4481 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4482 case actions:
4484 void * data;
4485 void (*function)();
4486 int nb_data = 0;
4488 /* The second argument must be the name of an existing option. */
4489 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4490 ptr = va_arg(args, char *);
4492 if ((opt = locate_opt(ptr)) != NULL)
4494 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4495 void **);
4497 /* The third argument must be the callback function. */
4498 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4499 function = va_arg(args, fn *);
4500 opt->action = function;
4502 /* The fourth argument must be a pointer to an user's defined */
4503 /* variable or structure that the previous function can manage. */
4504 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4505 while ((data = va_arg(args, void *)) != NULL)
4507 nb_data++;
4508 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4509 opt->data[nb_data - 1] = data;
4511 opt->nb_data = nb_data;
4513 else
4514 fatal_internal("Unknown option %s.", ptr);
4515 break;
4518 /* This part associates a list of functions to control some */
4519 /* characteristics of the arguments of an option. */
4520 /* Each function will be called in order and must return 1 */
4521 /* to validate the arguments. */
4522 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4523 case constraints:
4525 char * value;
4526 constraint_t * cstr;
4527 int (*function)();
4529 /* The second argument must be a string. */
4530 /* """"""""""""""""""""""""""""""""""""" */
4531 ptr = va_arg(args, char *);
4533 if ((opt = locate_opt(ptr)) != NULL)
4535 typedef int fn(int, char **, char *);
4537 /* The third argument must be a function. */
4538 /* """""""""""""""""""""""""""""""""""""" */
4539 function = va_arg(args, fn *);
4541 cstr = xmalloc(sizeof(constraint_t));
4542 cstr->constraint = function;
4544 /* The fourth argument must be a string containing the argument of */
4545 /* The previous function separated by spaces or tabs. */
4546 /* Theses arguments will be passed to the previous function */
4547 /* max: 32 argument! */
4548 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4549 value = xstrdup(va_arg(args, char *));
4551 cstr->to_free = value;
4552 cstr->args = xcalloc(sizeof(char *), 32);
4553 cstr->nb_args = str2argv(value, cstr->args, 32);
4554 ll_append(opt->constraints_list, cstr);
4556 else
4557 fatal_internal("Unknown option %s.", ptr);
4558 break;
4561 /* This part allows to indicate that an option must be evaluated */
4562 /* after a list of other options. */
4563 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4564 case after:
4566 char * str;
4568 /* The second argument must be a string. */
4569 /* """"""""""""""""""""""""""""""""""""" */
4570 ptr = va_arg(args, char *);
4572 if ((opt = locate_opt(ptr)) != NULL)
4574 char * end_str;
4575 char * opt_name;
4576 opt_t * opt_before;
4578 ptr = va_arg(args, char *);
4580 str = xstrdup(ptr);
4581 ltrim(str, " \t");
4582 rtrim(str, " \t", 0);
4584 /* Feed the list of options to be evaluated after the given option. */
4585 /* This list will contain pointers to options. */
4586 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4587 opt_name = xstrtok_r(str, " \t,", &end_str);
4588 if (opt_name != NULL)
4590 if ((opt_before = locate_opt(opt_name)) != NULL)
4592 ll_append(opt->eval_before_list, opt_before);
4593 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4595 if ((opt_before = locate_opt(opt_name)) != NULL)
4596 ll_append(opt->eval_before_list, opt_before);
4597 else
4598 fatal_internal("Unknown option %s.", opt_name);
4601 else
4602 fatal_internal("Unknown option %s.", opt_name);
4604 else
4605 fatal_internal("Not enough options to be evaluated after %s.",
4606 opt->name);
4608 free(str);
4610 else
4611 fatal_internal("Unknown option %s.", ptr);
4613 break;
4616 /* This part allows to indicate that an option must be evaluated */
4617 /* before a list of other options. */
4618 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4619 case before:
4621 char * str;
4623 /* The second argument must be a string. */
4624 /* """"""""""""""""""""""""""""""""""""" */
4625 ptr = va_arg(args, char *);
4627 if ((opt = locate_opt(ptr)) != NULL)
4629 char * end_str;
4630 char * opt_name;
4631 opt_t * opt_before;
4633 ptr = va_arg(args, char *);
4635 str = xstrdup(ptr);
4636 ltrim(str, " \t");
4637 rtrim(str, " \t", 0);
4639 /* Feed the list of options to be evaluated before the given option. */
4640 /* This list will contain pointers to options. */
4641 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4642 opt_name = xstrtok_r(str, " \t,", &end_str);
4643 if (opt_name != NULL)
4645 if ((opt_before = locate_opt(opt_name)) != NULL)
4647 ll_append(opt_before->eval_before_list, opt);
4648 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4650 if ((opt_before = locate_opt(opt_name)) != NULL)
4651 ll_append(opt_before->eval_before_list, opt);
4652 else
4653 fatal_internal("Unknown option %s.", opt_name);
4656 else
4657 fatal_internal("Unknown option %s.", opt_name);
4659 else
4660 fatal_internal("Not enough options to be evaluated before %s.",
4661 opt->name);
4663 free(str);
4665 else
4666 fatal_internal("Unknown option %s.", ptr);
4668 break;
4671 default:
4672 break;
4674 va_end(args);
4677 /* =============================================================== */
4678 /* This function provides a way to set the behaviour of a context. */
4679 /* =============================================================== */
4680 void
4681 ctxopt_add_ctx_settings(settings s, ...)
4683 ctx_t * ctx;
4685 va_list(args);
4686 va_start(args, s);
4688 switch (s)
4690 /* Add a set of mutually incompatible options in a context. */
4691 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4692 case incompatibilities:
4694 void * ptr;
4695 ll_t * list;
4696 size_t n;
4697 char * str;
4699 ptr = va_arg(args, char *);
4700 if ((ctx = locate_ctx(ptr)) != NULL)
4702 ptr = va_arg(args, char *);
4703 list = ctx->incomp_list;
4705 str = xstrdup(ptr);
4706 ltrim(str, " \t");
4707 rtrim(str, " \t", 0);
4709 n = strcspn(str, " \t");
4710 if (n > 0 && n < strlen(str))
4711 ll_append(list, str);
4712 else
4713 fatal_internal(
4714 "Not enough incompatible options in the string: \"%s\".", str);
4716 else
4717 fatal_internal("Unknown context %s.", ptr);
4718 break;
4721 case requirements:
4723 void * ptr;
4724 ll_t * list;
4725 size_t n;
4726 char * str;
4728 ptr = va_arg(args, char *);
4729 if ((ctx = locate_ctx(ptr)) != NULL)
4731 ptr = va_arg(args, char *);
4732 list = ctx->req_list;
4734 str = xstrdup(ptr);
4735 ltrim(str, " \t");
4736 rtrim(str, " \t", 0);
4738 n = strcspn(str, " \t");
4739 if (n > 0 && n < strlen(str))
4740 ll_append(list, str);
4741 else
4742 fatal_internal("Not enough required options in the string: \"%s\".",
4743 str);
4745 else
4746 fatal_internal("Unknown context %s.", ptr);
4747 break;
4750 /* Add functions which will be called when */
4751 /* entering and exiting a context. */
4752 /* """"""""""""""""""""""""""""""""""""""" */
4753 case actions:
4755 void * ptr;
4756 void * data;
4757 int (*function)();
4758 int nb_data = 0;
4760 ptr = va_arg(args, char *);
4761 if ((ctx = locate_ctx(ptr)) != NULL)
4763 typedef int fn(char *, direction, char *, int, void **);
4765 function = va_arg(args, fn *);
4766 ctx->action = function;
4768 while ((data = va_arg(args, void *)) != NULL)
4770 nb_data++;
4771 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4772 ctx->data[nb_data - 1] = data;
4774 ctx->nb_data = nb_data;
4776 else
4777 fatal_internal("Unknown context %s.", ptr);
4778 break;
4781 default:
4782 break;
4784 va_end(args);