[variadic] Use (char *)0 as last arg, not NULL
[ctxopt.git] / ctxopt.c
blob5c20370dbf3f356147051810ee16e96c3ceaf642
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;
191 static char *
192 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
194 static int
195 ctx_compare(const void * c1, const void * c2);
197 static void
198 ctx_free(void * o);
200 static void
201 ctx_inst_free(void * ci);
203 static void
204 opt_inst_free(void * oi);
206 static int
207 seen_opt_compare(const void * so1, const void * so2);
209 static void
210 incomp_bst_free(void * b);
212 static void
213 seen_opt_free(void * seen_opt);
215 static int
216 opt_compare(const void * o1, const void * o2);
218 static void
219 opt_free(void * o);
221 static int
222 par_compare(const void * a1, const void * a2);
224 static void
225 par_free(void * p);
227 static void
228 constraint_free(void * cstr);
230 static ctx_t *
231 locate_ctx(char * name);
233 static opt_t *
234 locate_opt(char * name);
236 static par_t *
237 locate_par(char * name, ctx_t * ctx);
239 static void
240 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
241 int * has_rule, int * has_generic_arg, int * has_ctx_change,
242 int * has_early_eval);
243 static void
244 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
245 int has_optional, int has_ellipsis, int has_rule);
246 static void
247 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
249 static void
250 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
252 static void
253 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
255 static void
256 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
258 static void
259 bst_match_par_cb(const void * node, walk_order_e kind, int level);
261 static void
262 match_prefix_cb(const void * node, walk_order_e kind, int level);
264 static int
265 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
267 static int
268 opt_parse(char * s, opt_t ** opt);
270 static int
271 init_opts(char * spec, ctx_t * ctx);
273 static int
274 ctxopt_build_cmdline_list(int nb_words, char ** words);
276 static int
277 opt_set_parms(char * opt_name, char * par_str);
279 static ctx_inst_t *
280 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
282 static void
283 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
285 /* ****************************** */
286 /* Fatal messages implementation. */
287 /* ****************************** */
289 /* ================================================================== */
290 /* Fatal error function used when a fatal condition is encountered. */
291 /* This function is reserved for the ctxopt internal usage. */
292 /* */
293 /* format : printf like format */
294 /* ... : remaining arguments interpreted using the format argument */
295 /* ================================================================== */
296 static void
297 fatal_internal(const char * format, ...)
299 va_list args;
301 fprintf(stderr, "CTXOPT: ");
303 va_start(args, format);
304 vfprintf(stderr, format, args);
305 fprintf(stderr, "\n");
306 va_end(args);
308 exit(EXIT_FAILURE);
311 /* ====================================================================== */
312 /* Generic fatal error function. This one uses the global status ctxopt */
313 /* stored in the cur_state structure and can call custom error functions. */
314 /* registered by the users for a given error identifier if any. */
315 /* */
316 /* e : Error identifier responsible of the fatal error */
317 /* errmsg : Users's provided string specific to the error e */
318 /* Note that errmsg is not used in all cases */
319 /* */
320 /* CTXOPTMISPAR Missing parameter */
321 /* CTXOPTMISARG Missing argument */
322 /* CTXOPTUXPARG Unexpected argument */
323 /* CTXOPTDUPOPT Duplicated option */
324 /* CTXOPTUNKPAR Unknown parameter */
325 /* CTXOPTINCOPT Incompatible option */
326 /* CTXOPTCTEOPT Option: bad number of occurrences */
327 /* CTXOPTCTLOPT Option: not enough occurrences */
328 /* CTXOPTCTGOPT Option: too many occurrence of */
329 /* CTXOPTCTEARG Arguments: bad number of occurrences */
330 /* CTXOPTCTLARG Arguments: not enough occurrences */
331 /* CTXOPTCTGARG Arguments: too many occurrences */
332 /* ====================================================================== */
333 static void
334 fatal(errors e, char * errmsg)
336 if (err_functions[e] != NULL)
337 err_functions[e](e, cur_state);
338 else
340 switch (e)
342 case CTXOPTNOERR:
343 break;
345 case CTXOPTMISPAR:
346 if (cur_state->ctx_par_name != NULL)
347 fprintf(stderr,
348 "the mandatory parameter(s) %s are missing in the context "
349 "introduced by %s.\n",
350 errmsg, cur_state->ctx_par_name);
351 else
352 fprintf(stderr,
353 "The mandatory parameter(s) %s are missing "
354 "in the main context.\n",
355 errmsg);
357 free(errmsg);
358 break;
360 case CTXOPTUNXARG:
361 if (cur_state->cur_opt_par_name != NULL)
362 fprintf(stderr,
363 "The parameter %s takes no arguments "
364 "or has too many arguments.\n",
365 cur_state->cur_opt_par_name);
366 break;
368 case CTXOPTMISARG:
369 if (cur_state->pre_opt_par_name != NULL)
370 fprintf(stderr, "%s requires argument(s).\n",
371 cur_state->pre_opt_par_name);
372 else
373 fprintf(stderr, "%s requires argument(s).\n",
374 cur_state->cur_opt_par_name);
375 break;
377 case CTXOPTDUPOPT:
378 if (cur_state->pre_opt_par_name != NULL)
379 fprintf(stderr,
380 "The parameter %s can only appear once in the context "
381 "introduced by %s.\n",
382 cur_state->cur_opt_params, cur_state->ctx_par_name);
383 else
384 fprintf(stderr,
385 "The parameter %s can only appear once "
386 "in the main context.\n",
387 cur_state->cur_opt_params);
388 break;
390 case CTXOPTUNKPAR:
391 fprintf(stderr, "Unknown parameter %s.\n%s",
392 cur_state->cur_opt_par_name, errmsg);
393 break;
395 case CTXOPTINCOPT:
396 fprintf(stderr, "The parameter %s is incompatible with %s.\n",
397 cur_state->cur_opt_par_name, errmsg);
398 break;
400 case CTXOPTCTEOPT:
401 if (cur_state->ctx_par_name)
402 fprintf(stderr,
403 "The parameter %s must appear exactly %d times "
404 "in the context introduced by %s.\n",
405 cur_state->cur_opt_params, cur_state->opts_count,
406 cur_state->ctx_par_name);
407 else
408 fprintf(stderr,
409 "The parameter %s must appear exactly %d times "
410 "in the main context.\n",
411 cur_state->cur_opt_params, cur_state->opts_count);
412 break;
414 case CTXOPTCTLOPT:
415 if (cur_state->ctx_par_name)
416 fprintf(stderr,
417 "The parameter %s must appear less than %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 less than %d times "
424 "in the main context.\n",
425 cur_state->cur_opt_params, cur_state->opts_count);
426 break;
428 case CTXOPTCTGOPT:
429 if (cur_state->ctx_par_name)
430 fprintf(stderr,
431 "The parameter %s must appear more 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 more than %d times "
438 "in the main context.\n",
439 cur_state->cur_opt_params, cur_state->opts_count);
440 break;
442 case CTXOPTCTEARG:
443 fprintf(stderr, "The parameter %s must have exactly %d arguments.\n",
444 cur_state->cur_opt_par_name, cur_state->opt_args_count);
445 break;
447 case CTXOPTCTLARG:
448 fprintf(stderr, "The parameter %s must have less than %d arguments.\n",
449 cur_state->cur_opt_par_name, cur_state->opt_args_count);
450 break;
452 case CTXOPTCTGARG:
453 fprintf(stderr, "The parameter %s must have more than %d arguments.\n",
454 cur_state->cur_opt_par_name, cur_state->opt_args_count);
455 break;
457 case CTXOPTERRSIZ:
458 break;
462 /* CTXOPTUNKPAR should display the full usage to help the user follow */
463 /* the chaining of contexts when several possible contexts have been */
464 /* identified. Otherwise, errmsg is the empty string and the display of */
465 /* the current usage is enough. */
466 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
467 if (e == CTXOPTUNKPAR && *errmsg != '\0')
468 ctxopt_disp_usage(continue_after);
469 else
470 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
472 exit(e); /* Exit with the error id e as return code. */
475 /* ********************************* */
476 /* Memory management implementation. */
477 /* ********************************* */
479 /* ================== */
480 /* Customized malloc. */
481 /* ================== */
482 static void *
483 xmalloc(size_t size)
485 void * allocated;
486 size_t real_size;
488 real_size = (size > 0) ? size : 1;
489 allocated = malloc(real_size);
490 if (allocated == NULL)
491 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
492 (unsigned long int)size);
494 return allocated;
497 /* ================== */
498 /* Customized calloc. */
499 /* ================== */
500 static void *
501 xcalloc(size_t n, size_t size)
503 void * allocated;
505 n = (n > 0) ? n : 1;
506 size = (size > 0) ? size : 1;
507 allocated = calloc(n, size);
508 if (allocated == NULL)
509 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
510 (unsigned long int)size);
512 return allocated;
515 /* =================== */
516 /* Customized realloc. */
517 /* =================== */
518 static void *
519 xrealloc(void * p, size_t size)
521 void * allocated;
523 allocated = realloc(p, size);
524 if (allocated == NULL && size > 0)
525 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
526 (unsigned long int)size);
528 return allocated;
531 /* ==================================== */
532 /* strdup implementation using xmalloc. */
533 /* ==================================== */
534 static char *
535 xstrdup(const char * p)
537 char * allocated;
539 allocated = xmalloc(strlen(p) + 1);
540 strcpy(allocated, p);
542 return allocated;
545 /* =================================================== */
546 /* strndup implementation using xmalloc. */
547 /* This version guarantees that there is a final '\0'. */
548 /* =================================================== */
549 static char *
550 xstrndup(const char * str, size_t len)
552 char * p;
554 p = memchr(str, '\0', len);
556 if (p)
557 len = p - str;
559 p = xmalloc(len + 1);
560 memcpy(p, str, len);
561 p[len] = '\0';
563 return p;
566 /* *************************** */
567 /* Linked list implementation. */
568 /* *************************** */
570 /* Linked list node structure. */
571 /* """"""""""""""""""""""""""" */
572 struct ll_node_s
574 void * data;
575 struct ll_node_s * next;
576 struct ll_node_s * prev;
579 /* Linked List structure. */
580 /* """""""""""""""""""""" */
581 struct ll_s
583 ll_node_t * head;
584 ll_node_t * tail;
585 long len;
588 /* ========================= */
589 /* Create a new linked list. */
590 /* ========================= */
591 static ll_t *
592 ll_new(void)
594 ll_t * ret = xmalloc(sizeof(ll_t));
595 ll_init(ret);
597 return ret;
600 /* =============================================== */
601 /* Free all the elements of a list (make it empty) */
602 /* NULL or a custom function may be used to free */
603 /* the sub components of the elements. */
604 /* =============================================== */
605 static void
606 ll_free(ll_t * const list, void (*clean)(void *))
608 if (list)
609 while (list->head)
611 /* Apply a custom cleaner if not NULL. */
612 /* """"""""""""""""""""""""""""""""""" */
613 if (clean)
614 clean(list->head->data);
616 ll_delete(list, list->head);
620 /* ==================================== */
621 /* Destroy a list and all its elements. */
622 /* ==================================== */
623 static void
624 ll_destroy(ll_t * list, void (*clean)(void *))
626 if (list)
628 ll_free(list, clean);
629 free(list);
633 /* ========================= */
634 /* Initialize a linked list. */
635 /* ========================= */
636 static void
637 ll_init(ll_t * list)
639 list->head = NULL;
640 list->tail = NULL;
641 list->len = 0;
644 /* ===================================================== */
645 /* Allocate the space for a new node in the linked list. */
646 /* ===================================================== */
647 static ll_node_t *
648 ll_new_node(void)
650 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
652 return ret;
655 /* ==================================================================== */
656 /* Append a new node filled with its data at the end of the linked list */
657 /* The user is responsible for the memory management of the data. */
658 /* ==================================================================== */
659 static void
660 ll_append(ll_t * const list, void * const data)
662 ll_node_t * node;
664 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
665 | uses xmalloc which does not return if there *
666 | is an allocation error. */
668 node->data = data;
669 node->next = NULL;
671 node->prev = list->tail;
672 if (list->tail)
673 list->tail->next = node;
674 else
675 list->head = node;
677 list->tail = node;
679 ++list->len;
682 /* ================================================================== */
683 /* Put a new node filled with its data at the beginning of the linked */
684 /* list. */
685 /* The user is responsible for the memory management of the data. */
686 /* ================================================================== */
687 static void
688 ll_prepend(ll_t * const list, void * const data)
690 ll_node_t * node;
692 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
693 | uses xmalloc which does not return if there *
694 | is an allocation error. */
696 node->data = data;
697 node->prev = NULL;
699 node->next = list->head;
700 if (list->head)
701 list->head->prev = node;
702 else
703 list->tail = node;
705 list->head = node;
707 ++list->len;
710 /* ======================================================== */
711 /* Insert a new node before the specified node in the list. */
712 /* ======================================================== */
713 static void
714 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
716 ll_node_t * new_node;
718 if (node->prev == NULL)
719 ll_prepend(list, data);
720 else
722 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
723 | uses xmalloc which does not return if there *
724 | is an allocation error. */
726 new_node->data = data;
727 new_node->next = node;
728 new_node->prev = node->prev;
729 node->prev->next = new_node;
730 node->prev = new_node;
732 ++list->len;
736 /* ======================================================= */
737 /* Insert a new node after the specified node in the list. */
738 /* ======================================================= */
739 static void
740 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
742 ll_node_t * new_node;
744 if (node->next == NULL)
745 ll_append(list, data);
746 else
748 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
749 | uses xmalloc which does not return if there *
750 | is an allocation error. */
752 new_node->data = data;
753 new_node->prev = node;
754 new_node->next = node->next;
755 node->next->prev = new_node;
756 node->next = new_node;
758 ++list->len;
762 /* ================================================================= */
763 /* Remove a node from a linked list */
764 /* The memory taken by the deleted node must be freed by the caller. */
765 /* ================================================================= */
766 static int
767 ll_delete(ll_t * const list, ll_node_t * node)
769 if (list->head == list->tail)
771 if (list->head != NULL)
772 list->head = list->tail = NULL;
773 else
774 return 0;
776 else if (node->prev == NULL)
778 list->head = node->next;
779 list->head->prev = NULL;
781 else if (node->next == NULL)
783 list->tail = node->prev;
784 list->tail->next = NULL;
786 else
788 node->next->prev = node->prev;
789 node->prev->next = node->next;
792 --list->len;
794 free(node);
796 return 1;
799 #if 0 /* Unused yet */
800 /* ======================================================================== */
801 /* Find a node in the list containing data. Return the node pointer or NULL */
802 /* if not found. */
803 /* A comparison function must be provided to compare a and b (strcmp like). */
804 /* ======================================================================== */
805 static ll_node_t *
806 ll_find(ll_t * const list, void * const data,
807 int (*cmpfunc)(const void * a, const void * b))
809 ll_node_t * node;
811 if (NULL == (node = list->head))
812 return NULL;
816 if (0 == cmpfunc(node->data, data))
817 return node;
818 } while (NULL != (node = node->next));
820 return NULL;
822 #endif
824 /* ==================================================================== */
825 /* Allocate and fill an array of strings from a list. */
826 /* WARNINGS: */
827 /* 1) The list node must contain strings (char *) */
828 /* 2) The strings in the resulting array MUST NOT be freed as the are */
829 /* NOT copied from the strings of the list. */
830 /* */
831 /* IN list : The list from which the array is generated */
832 /* IN start_node : The node of the list which will be the first node to */
833 /* consider to create the array */
834 /* OUT: count : The number of elements of the resulting array. */
835 /* OUT: array : The resulting array or NULL if the list is empty. */
836 /* RC : : The number of elements of the resulting array. */
837 /* ==================================================================== */
838 static int
839 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
841 int n = 0;
842 ll_node_t * node;
844 *count = 0;
846 node = start_node;
848 if (list == NULL || node == NULL)
850 *array = NULL;
852 return 0;
855 *array = xmalloc((list->len + 1) * sizeof(char *));
856 while (node != NULL)
858 (*array)[n++] = (char *)(node->data);
859 (*count)++;
861 node = node->next;
864 (*array)[*count] = NULL;
866 return *count;
869 /* ******************************************************************* */
870 /* BST (search.h compatible) implementation. */
871 /* */
872 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
873 /* the AT&T man page says. */
874 /* */
875 /* Written by reading the System V Interface Definition, not the code. */
876 /* */
877 /* Totally public domain. */
878 /* ******************************************************************* */
880 struct bst_s
882 void * key;
883 struct bst_s * llink;
884 struct bst_s * rlink;
887 #if 0 /* Unused yet */
888 /* =========================== */
889 /* Delete node with given key. */
890 /* =========================== */
891 static void *
892 bst_delete(const void * vkey, void ** vrootp,
893 int (*compar)(const void *, const void *))
895 bst_t ** rootp = (bst_t **)vrootp;
896 bst_t * p, *q, *r;
897 int cmp;
899 if (rootp == NULL || (p = *rootp) == NULL)
900 return NULL;
902 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
904 p = *rootp;
905 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
906 : &(*rootp)->rlink; /* follow rlink branch */
907 if (*rootp == NULL)
908 return NULL; /* key not found */
910 r = (*rootp)->rlink; /* D1: */
911 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
912 q = r;
913 else if (r != NULL)
914 { /* Right link is NULL? */
915 if (r->llink == NULL)
916 { /* D2: Find successor */
917 r->llink = q;
918 q = r;
920 else
921 { /* D3: Find NULL link */
922 for (q = r->llink; q->llink != NULL; q = r->llink)
923 r = q;
924 r->llink = q->rlink;
925 q->llink = (*rootp)->llink;
926 q->rlink = (*rootp)->rlink;
929 if (p != *rootp)
930 free(*rootp); /* D4: Free node */
931 *rootp = q; /* link parent to new node */
932 return p;
934 #endif
936 /* ===================================================================== */
937 /* Destroy a tree. */
938 /* The clean function pointer can be NULL, in this case the node content */
939 /* is not freed. */
940 /* ===================================================================== */
941 static void
942 bst_destroy(void * vrootp, void (*clean)(void *))
944 bst_t * root = (bst_t *)vrootp;
946 if (root == NULL)
947 return;
949 bst_destroy(root->llink, clean);
950 bst_destroy(root->rlink, clean);
952 if (clean)
953 clean((void *)root->key);
955 free(root);
958 /* ========================= */
959 /* Find a node, or return 0. */
960 /* ========================= */
961 static void *
962 bst_find(const void * vkey, void * const * vrootp,
963 int (*compar)(const void *, const void *))
965 bst_t * const * rootp = (bst_t * const *)vrootp;
967 if (rootp == NULL)
968 return NULL;
970 while (*rootp != NULL)
971 { /* T1: */
972 int r;
974 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
975 return *rootp; /* key found */
976 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
977 : &(*rootp)->rlink; /* T4: follow right branch */
979 return NULL;
982 /* ======================================= */
983 /* Find or inserts datum into search tree. */
984 /* ======================================= */
985 static void *
986 bst_search(void * vkey, void ** vrootp,
987 int (*compar)(const void *, const void *))
989 bst_t * q;
990 bst_t ** rootp = (bst_t **)vrootp;
992 if (rootp == NULL)
993 return NULL;
995 while (*rootp != NULL)
996 { /* Knuth's T1: */
997 int r;
999 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
1000 return *rootp; /* we found it! */
1002 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
1003 : &(*rootp)->rlink; /* T4: follow right branch */
1006 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
1007 if (q != 0)
1008 { /* make new node */
1009 *rootp = q; /* link new node to old */
1010 q->key = vkey; /* initialize new node */
1011 q->llink = q->rlink = NULL;
1013 return q;
1016 /* ========================= */
1017 /* Walk the nodes of a tree. */
1018 /* ========================= */
1019 static void
1020 bst_walk_recurse(const bst_t * root,
1021 void (*action)(const void *, walk_order_e, int), int level)
1023 if (root->llink == NULL && root->rlink == NULL)
1024 (*action)(root, leaf, level);
1025 else
1027 (*action)(root, preorder, level);
1028 if (root->llink != NULL)
1029 bst_walk_recurse(root->llink, action, level + 1);
1030 (*action)(root, postorder, level);
1031 if (root->rlink != NULL)
1032 bst_walk_recurse(root->rlink, action, level + 1);
1033 (*action)(root, endorder, level);
1037 static void
1038 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
1040 if (vroot != NULL && action != NULL)
1041 bst_walk_recurse(vroot, action, 0);
1044 /* ************************ */
1045 /* Various implementations. */
1046 /* ************************ */
1048 /* ======================== */
1049 /* Trim leading characters. */
1050 /* ======================== */
1051 static void
1052 ltrim(char * str, const char * trim_str)
1054 size_t len = strlen(str);
1055 size_t begin = strspn(str, trim_str);
1056 size_t i;
1058 if (begin > 0)
1059 for (i = begin; i <= len; ++i)
1060 str[i - begin] = str[i];
1063 /* ================================================= */
1064 /* Trim trailing characters. */
1065 /* The resulting string will have at least min bytes */
1066 /* even if trailing spaces remain. */
1067 /* ================================================= */
1068 static void
1069 rtrim(char * str, const char * trim_str, size_t min)
1071 size_t len = strlen(str);
1072 while (len > min && strchr(trim_str, str[len - 1]))
1073 str[--len] = '\0';
1076 /* ================================================== */
1077 /* Count the number of occurrences of the character c */
1078 /* in the string str. */
1079 /* The str pointer is assumed to be not NULL. */
1080 /* ================================================== */
1081 static int
1082 strchrcount(char * str, char c)
1084 int count = 0;
1086 while (*str)
1087 if (*str++ == c)
1088 count++;
1090 return count;
1093 /* =============================================== */
1094 /* Is the string str2 a prefix of the string str1? */
1095 /* =============================================== */
1096 static int
1097 strpref(char * str1, char * str2)
1099 while (*str1 != '\0' && *str1 == *str2)
1101 str1++;
1102 str2++;
1105 return *str2 == '\0';
1108 /* ========================== */
1109 /* Like strcmp ignoring case. */
1110 /* ========================== */
1111 static int
1112 stricmp(const char * s1, const char * s2)
1114 while (tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1116 if (*s1 == '\0')
1117 return 0;
1119 s1++;
1120 s2++;
1123 return (int)tolower((unsigned char)*s1) - (int)tolower((unsigned char)*s2);
1126 /* ======================================================================== */
1127 /* Strings concatenation with dynamic memory allocation. */
1128 /* IN : a variable number of char * arguments with NULL terminating */
1129 /* the sequence. */
1130 /* The first one must have been dynamically allocated and is mandatory */
1131 /* */
1132 /* Returns a new allocated string containing the concatenation of all */
1133 /* the arguments. It is the caller's responsibility to free the resulting */
1134 /* string. */
1135 /* ======================================================================== */
1136 static char *
1137 strappend(char * str, ...)
1139 size_t l;
1140 va_list args;
1141 char * s;
1143 l = 1 + strlen(str);
1144 va_start(args, str);
1146 s = va_arg(args, char *);
1148 while (s)
1150 l += strlen(s);
1151 s = va_arg(args, char *);
1154 va_end(args);
1156 str = xrealloc(str, l);
1158 va_start(args, str);
1159 s = va_arg(args, char *);
1161 while (s)
1163 strcat(str, s);
1164 s = va_arg(args, char *);
1166 va_end(args);
1168 return str;
1171 /* ====================================================================== */
1172 /* Public domain strtok_r() by Charlie Gordon. */
1173 /* from comp.lang.c 9/14/2007 */
1174 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1175 /* */
1176 /* (Declaration that it's public domain): */
1177 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1178 /* */
1179 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1180 /* *end == NULL */
1181 /* ====================================================================== */
1182 static char *
1183 xstrtok_r(char * str, const char * delim, char ** end)
1185 char * ret;
1187 if (str == NULL)
1188 str = *end;
1190 if (str == NULL)
1191 return NULL;
1193 str += strspn(str, delim);
1195 if (*str == '\0')
1196 return NULL;
1198 ret = str;
1200 str += strcspn(str, delim);
1202 if (*str)
1203 *str++ = '\0';
1205 *end = str;
1207 return ret;
1210 /* ===================================================================== */
1211 /* Put the first word of str, truncated to len characters, in buf. */
1212 /* Return a pointer in str pointing just after the word. */
1213 /* buf must have been pre-allocated to accept at least len+1 characters. */
1214 /* Note that buf can contains a sting full of spaces is str was not */
1215 /* trimmed before the call. */
1216 /* ===================================================================== */
1217 char *
1218 get_word(char * str, char * buf, size_t len)
1220 char * s = str;
1222 /* Skip spaces. */
1223 /* """""""""""" */
1224 while (*s && isspace(*s))
1225 s++;
1227 /* Set the new string start. */
1228 /* """"""""""""""""""""""""" */
1229 str = s;
1231 /* Get the word. */
1232 /*"""""""""""""" */
1233 while (*s && !isspace(*s) && s - str < len)
1234 s++;
1236 strncpy(buf, str, s - str);
1237 buf[s - str] = 0;
1239 return s;
1242 /* ==================================================================== */
1243 /* Return 1 is value is "1" or "yes" (ignoring case). */
1244 /* Return 0 is value is "0" or "no" (ignoring case). */
1245 /* If value has another value, then set invalid to 1 and also return 0 */
1246 /* invalid is set to 0i in all the other cases. */
1247 /* ==================================================================== */
1248 static int
1249 eval_yes(char * value, int * invalid)
1251 *invalid = 0;
1253 if (strcmp(value, "1") == 0 || stricmp(value, "yes") == 0)
1254 return 1;
1255 else if (strcmp(value, "0") != 0 && stricmp(value, "no") != 0)
1256 *invalid = 1;
1258 return 0;
1261 /* =========================================================== */
1262 /* Fill an array of strings from the words composing a string. */
1263 /* */
1264 /* str: initial string which will be altered. */
1265 /* args: array of pointers to the start of the words in str. */
1266 /* max: maximum number of words used before giving up. */
1267 /* return: the number of words (<=max). */
1268 /* =========================================================== */
1269 static int
1270 str2argv(char * str, char ** args, int max)
1272 int nb_args = 0;
1274 while (*str)
1276 if (nb_args >= max)
1277 return nb_args;
1279 while (*str == ' ' || *str == '\t')
1280 *(str++) = '\0';
1282 if (!*str)
1283 return nb_args;
1285 args[nb_args] = str;
1286 nb_args++;
1288 while (*str && (*str != ' ') && (*str != '\t'))
1289 str++;
1292 return nb_args;
1295 /* ********************** */
1296 /* ctxopt implementation. */
1297 /* ********************** */
1299 static int ctxopt_initialized = 0; /* cap_init has not yet been called */
1301 /* Flags structure initialized by ctxopt_init. */
1302 /* """"""""""""""""""""""""""""""""""""""""""" */
1303 struct flags_s
1305 int stop_if_non_option;
1306 int allow_abbreviations;
1309 /* Context structure. */
1310 /* """""""""""""""""" */
1311 struct ctx_s
1313 char * name;
1314 ll_t * opt_list; /* list of options allowed in this context. */
1315 ll_t * incomp_list; /* list of strings containing incompatible names *
1316 | of options separated by spaces or tabs. */
1317 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1318 void ** ctx_data);
1319 void * par_bst;
1320 int nb_data;
1321 void ** data;
1324 /* https://textik.com/#488ce3649b6c60f5 */
1325 /* */
1326 /* +--------------+ */
1327 /* |first_ctx_inst| */
1328 /* +---+----------+ */
1329 /* | */
1330 /* +--v-----+ +--------+ +--------+ +-----+ */
1331 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1332 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1333 /* | | | | | */
1334 /* | | +-v------+ | | */
1335 /* | +--+ctx_inst<-----------+ | */
1336 /* | +-+------+ | */
1337 /* | | | */
1338 /* | +-v------+ | */
1339 /* +------+ctx_inst<--------------------------+ */
1340 /* +-+------+ */
1341 /* | */
1342 /* +-v---+ */
1343 /* | ... | */
1344 /* +-----+ */
1346 /* Option structure. */
1347 /* """"""""""""""""" */
1348 struct opt_s
1350 char * name; /* option name. */
1351 char * next_ctx; /* new context this option may lead to */
1352 ll_t * ctx_list; /* list of contexts allowing this option. */
1353 char * params; /* string containing all the parameters of *
1354 | the option. */
1356 void (*action)( /* The option associated action. */
1357 char * ctx_name, /* context name. */
1358 char * opt_name, /* option name. */
1359 char * par, /* option parameter. */
1360 int nb_args, /* number of arguments. */
1361 char ** args, /* option arguments. */
1362 int nb_opt_data, /* number of option data pointers. */
1363 void ** opt_data, /* option data pointers. */
1364 int nb_ctx_data, /* nb of current context data ptrs. */
1365 void ** ctx_data /* current context data pointers. */
1368 int nb_data; /* number of the data pointers passed as argument to action. */
1369 void ** data; /* array of data pointers passed as argument to action. */
1371 int args; /* 1 if this option takes arguments else 0. */
1372 int optional; /* 1 if the option is optional, else 0. */
1373 int multiple; /* 1 if the option can appear more than one time in a *
1374 | context, else 0. */
1376 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1377 int occurrences; /* Number of option occurrences in a context. */
1378 char opt_count_oper; /* <, = or > */
1379 unsigned opt_count_mark; /* Value to be compared to with opt_count_oper. */
1381 char * arg; /* symbolic text after # describing the option argument. */
1383 int optional_args; /* 1 of option is optional else 0. */
1384 int multiple_args; /* 1 is option can appear more than once in a context *
1385 | instance. */
1387 int opt_args_count_matter; /* 1 if count is rescticted, else 0. */
1388 char opt_args_count_oper; /* <, = or > */
1389 unsigned opt_args_count_mark; /* Value to be compared to with *
1390 | opt_count_oper. */
1392 int eval_first; /* 1 if this option must be evaluated before the options *
1393 | without this mark. */
1395 ll_t * eval_before_list; /* List of pointers on options which must be *
1396 | evaluated before this option. */
1398 ll_t * constraints_list; /* List of constraint check functions pointers. */
1401 /* Context instance structure. */
1402 /* """"""""""""""""""""""""""" */
1403 struct ctx_inst_s
1405 ctx_t * ctx; /* the context whose this is an instance of */
1406 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1407 | creation of this ctx_inst structure. */
1408 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1409 | instance of this structure. */
1410 ll_t * incomp_bst_list; /* list of seen_opt_t BST. */
1411 void * seen_opt_bst; /* tree of seen_opt_t. */
1412 ll_t * opt_inst_list; /* The list of option instances in this *
1413 | context instance. */
1414 char * par_name; /* parameter which created this instance. */
1417 /* Option instance structure. */
1418 /* """""""""""""""""""""""""" */
1419 struct opt_inst_s
1421 opt_t * opt; /* The option this is an instance of. */
1422 char * opt_name; /* The option which led to this creation. */
1423 char * par; /* The parameter which led to this creation. */
1424 ll_t * values_list; /* The list of arguments of this option. */
1425 ctx_inst_t * next_ctx_inst; /* The new context instance this option. *
1426 | instance may create. */
1429 /* Structure used to check if an option has bee seen or not */
1430 /* in a context instance. */
1431 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1432 struct seen_opt_s
1434 opt_t * opt; /* The concerned option. */
1435 char * par; /* Parameter which led to the making of this structure. */
1436 int seen; /* 1 if seen in the context instances, else 0. */
1439 /* Parameter structure which links a parameter to the option it belongs to. */
1440 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1441 struct par_s
1443 char * name; /* Parameter name (with the leading -). */
1444 opt_t * opt; /* Attached option. */
1447 /* Constraint structure. */
1448 /* """"""""""""""""""""" */
1449 struct constraint_s
1451 int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1452 int nb_args;
1453 char ** args;
1454 char * to_free; /* pointer to the original string in which the array in *
1455 | args points to. This poinnter is kept there to allow *
1456 | it to be freed. */
1459 state_t * cur_state = NULL; /* Current analysis state. */
1460 static ll_t * cmdline_list; /* List of interpreted CLI words *
1461 | serves as the basis for the *
1462 | analysis of the parameters. */
1463 static ctx_t * main_ctx = NULL; /* initial context. */
1464 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1465 | instance which holds the *
1466 | options instances. */
1467 static ll_t * ctx_inst_list = NULL; /* List of the context instances. */
1469 static flags_t flags = { 0, 1 };
1471 /* ======================================================= */
1472 /* Parse a string for the next matching token. */
1473 /* */
1474 /* s: string to parse. */
1475 /* token: pre_allocated array of max tok_len characters. */
1476 /* pattern: scanf type pattern token must match. */
1477 /* pos: number of characters successfully parsed in s. */
1478 /* */
1479 /* Returns: a pointer to the first unread character or */
1480 /* to he terminating \0. */
1481 /* ======================================================= */
1482 static char *
1483 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1485 char * full_pattern;
1486 char len[3];
1487 int n;
1489 *pos = 0;
1491 n = snprintf(len, 3, "%zu", tok_len);
1492 if (n < 0)
1493 return NULL;
1495 full_pattern = xmalloc(strlen(pattern) + n + 4);
1497 strcpy(full_pattern, "%");
1498 strcat(full_pattern, len);
1499 strcat(full_pattern, pattern);
1500 strcat(full_pattern, "%n");
1502 n = sscanf(s, full_pattern, token, pos);
1504 free(full_pattern);
1506 if (n != 1)
1507 return NULL;
1509 return s + *pos;
1512 /* ****************************************** */
1513 /* Various comparison and deletion functions. */
1514 /* ****************************************** */
1516 static int
1517 ctx_compare(const void * c1, const void * c2)
1519 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1522 /* =========================== */
1523 /* Free a context_bst element. */
1524 /* =========================== */
1525 static void
1526 ctx_free(void * c)
1528 ctx_t * ctx = c;
1530 free(ctx->name);
1531 free(ctx->data);
1533 ll_destroy(ctx->opt_list, NULL);
1534 ll_destroy(ctx->incomp_list, free);
1535 bst_destroy(ctx->par_bst, par_free);
1537 free(c);
1540 /* ============================= */
1541 /* Free a ctx_inst_list element. */
1542 /* ============================= */
1543 static void
1544 ctx_inst_free(void * ci)
1546 ctx_inst_t * ctx_inst = ci;
1548 free(ctx_inst->par_name);
1549 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1550 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1551 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1553 free(ci);
1556 /* ============================= */
1557 /* Free a opt_inst_list element. */
1558 /* ============================= */
1559 static void
1560 opt_inst_free(void * oi)
1562 opt_inst_t * opt_inst = oi;
1564 ll_destroy(opt_inst->values_list, NULL);
1566 free(oi);
1569 /* ================================== */
1570 /* Compare two seen_opt_bst elements. */
1571 /* ================================== */
1572 static int
1573 seen_opt_compare(const void * so1, const void * so2)
1575 opt_t *o1, *o2;
1577 o1 = ((seen_opt_t *)so1)->opt;
1578 o2 = ((seen_opt_t *)so2)->opt;
1580 return strcmp(o1->name, o2->name);
1583 /* ============================ */
1584 /* Free a seen_opt_bst element. */
1585 /* ============================ */
1586 void
1587 seen_opt_free(void * so)
1589 seen_opt_t * seen_opt = so;
1591 free(seen_opt->par);
1593 free(so);
1596 /* =========================== */
1597 /* Free an incomp_bst element. */
1598 /* =========================== */
1599 static void
1600 incomp_bst_free(void * b)
1602 bst_t * bst = b;
1604 bst_destroy(bst, NULL);
1607 /* ================================= */
1608 /* Compare two options_bst elements. */
1609 /* ================================= */
1610 static int
1611 opt_compare(const void * o1, const void * o2)
1613 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1616 /* ============================= */
1617 /* Free an options_bst elements. */
1618 /* ============================= */
1619 void
1620 opt_free(void * o)
1622 opt_t * opt = o;
1624 free(opt->name);
1625 free(opt->next_ctx);
1626 free(opt->params);
1627 free(opt->arg);
1628 free(opt->data);
1630 ll_destroy(opt->ctx_list, NULL);
1631 ll_destroy(opt->constraints_list, constraint_free);
1633 free(o);
1636 /* ============================= */
1637 /* Compare two par_bst elements. */
1638 /* ============================= */
1639 static int
1640 par_compare(const void * a1, const void * a2)
1642 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1645 /* ======================= */
1646 /* Free a par_bst element. */
1647 /* ======================= */
1648 static void
1649 par_free(void * p)
1651 par_t * par = p;
1653 free(par->name);
1655 free(p);
1658 /* ================================ */
1659 /* Free a constraints_list element. */
1660 /* ================================ */
1661 static void
1662 constraint_free(void * c)
1664 constraint_t * cstr = c;
1666 free(cstr->args);
1667 free(cstr->to_free);
1669 free(c);
1672 /* ******************************************************************** */
1673 /* Helper functions to locate contexts, options and parameters in a BST */
1674 /* by their names. */
1675 /* ******************************************************************** */
1677 static ctx_t *
1678 locate_ctx(char * name)
1680 bst_t * node;
1681 ctx_t ctx = { 0 };
1683 ctx.name = name;
1685 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1686 return NULL;
1687 else
1688 return node->key;
1691 static opt_t *
1692 locate_opt(char * name)
1694 bst_t * node;
1695 opt_t opt = { 0 };
1697 opt.name = name;
1699 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1700 return NULL;
1701 else
1702 return node->key;
1705 static par_t *
1706 locate_par(char * name, ctx_t * ctx)
1708 bst_t * node;
1709 par_t par = { 0 };
1710 void * bst = ctx->par_bst;
1712 par.name = name;
1714 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1715 return NULL;
1716 else
1717 return node->key;
1720 /* =================================================================== */
1721 /* Utility function to format and print the options present in a list. */
1722 /* */
1723 /* IN list : a list of options. */
1724 /* OUT has_* : a set of flags which will determine the content of the */
1725 /* explanation given after the formatted printing of the */
1726 /* options. */
1727 /* =================================================================== */
1728 static void
1729 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1730 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1731 int * has_early_eval)
1733 ll_node_t * node = list->head;
1734 opt_t * opt;
1735 char * line;
1736 char * option;
1738 line = xstrdup(" ");
1740 while (node != NULL)
1742 option = xstrdup("");
1743 opt = node->data;
1745 if (opt->optional)
1747 option = strappend(option, "[", (char *)0);
1748 *has_optional = 1;
1751 if (opt->eval_first)
1753 option = strappend(option, "*", (char *)0);
1754 *has_early_eval = 1;
1757 option = strappend(option, opt->params, (char *)0);
1759 if (opt->next_ctx != NULL)
1761 option = strappend(option, ">", opt->next_ctx, (char *)0);
1762 *has_ctx_change = 1;
1765 if (opt->multiple)
1767 if (opt->opt_count_oper != '\0')
1769 char m[4];
1770 char o[2];
1771 o[0] = opt->opt_count_oper;
1772 o[1] = '\0';
1773 snprintf(m, 3, "%u", opt->opt_count_mark);
1774 option = strappend(option, "...", o, m, (char *)0);
1775 *has_rule = 1;
1777 else
1778 option = strappend(option, "...", (char *)0);
1780 *has_ellipsis = 1;
1783 if (opt->args)
1785 if (*(opt->arg) == '#')
1786 *has_generic_arg = 1;
1788 option = strappend(option, " ", (char *)0);
1790 if (opt->optional_args)
1792 option = strappend(option, "[", opt->arg, (char *)0);
1793 *has_optional = 1;
1795 else
1796 option = strappend(option, opt->arg, (char *)0);
1798 if (opt->multiple_args)
1800 if (opt->opt_args_count_oper != '\0')
1802 char m[4];
1803 char o[2];
1804 o[0] = opt->opt_args_count_oper;
1805 o[1] = '\0';
1806 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1807 option = strappend(option, "...", o, m, (char *)0);
1808 *has_rule = 1;
1810 else
1811 option = strappend(option, "...", (char *)0);
1813 *has_ellipsis = 1;
1815 if (opt->optional_args)
1816 option = strappend(option, "]", (char *)0);
1818 if (opt->optional)
1819 option = strappend(option, "]", (char *)0);
1821 if (strlen(line) + 1 + strlen(option) < 80)
1822 line = strappend(line, option, " ", (char *)0);
1823 else
1825 printf("%s\n", line);
1826 line[2] = '\0';
1827 line = strappend(line, option, " ", (char *)0);
1830 free(option);
1832 node = node->next;
1835 printf("%s\n", line);
1837 free(line);
1840 /* ==================================================== */
1841 /* Explain the special syntactic symbols present in the */
1842 /* generated usage messages. */
1843 /* ==================================================== */
1844 static void
1845 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1846 int has_optional, int has_ellipsis, int has_rule)
1848 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1849 || has_ellipsis || has_rule)
1851 printf("\nExplanation of the syntax used above:\n");
1852 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1853 "must be entered.\n");
1854 printf("The following is just there to explain the other symbols "
1855 "displayed.\n\n");
1857 if (has_early_eval)
1858 printf("* : the parameters for this option will be "
1859 "evaluated first.\n");
1860 if (has_ctx_change)
1861 printf(
1862 "> : The context after this symbol will become the next "
1863 "default one.\n");
1864 if (has_generic_arg)
1865 printf("#tag : argument tag giving a clue to its meaning.\n");
1866 if (has_optional)
1867 printf(
1868 "[...] : the object between square brackets is optional.\n");
1869 if (has_ellipsis)
1870 printf("... : several occurrences of the previous object "
1871 "are possible.\n");
1872 if (has_rule)
1873 printf("[<|=|>]number: rules constraining the number of "
1874 "parameters/arguments.\n");
1878 /* ************************************************************ */
1879 /* Various utilities and callback functions called when walking */
1880 /* through a BST. */
1881 /* ************************************************************ */
1883 static void
1884 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1886 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1888 if (kind == postorder || kind == leaf)
1890 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1892 user_rc = 1;
1893 user_string = strappend(user_string, seen_opt->opt->params, " ",
1894 (char *)0);
1899 static void
1900 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1902 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1904 if (kind == postorder || kind == leaf)
1905 if (seen_opt->seen == 1)
1907 user_rc = 1;
1908 user_object = seen_opt->par;
1912 static void
1913 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
1915 ctx_t * ctx = main_ctx;
1916 ctx_t * cur_ctx = ((bst_t *)node)->key;
1918 ll_t * list;
1920 int has_optional = 0;
1921 int has_ellipsis = 0;
1922 int has_rule = 0;
1923 int has_generic_arg = 0;
1924 int has_ctx_change = 0;
1925 int has_early_eval = 0;
1927 if (kind == postorder || kind == leaf)
1928 if (strcmp(ctx->name, cur_ctx->name) != 0)
1930 list = cur_ctx->opt_list;
1932 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
1933 print_options(list, &has_optional, &has_ellipsis, &has_rule,
1934 &has_generic_arg, &has_ctx_change, &has_early_eval);
1938 static void
1939 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
1941 opt_t * opt = ((bst_t *)node)->key;
1943 if (kind == postorder || kind == leaf)
1945 if (opt->params == NULL) /* opt must have associated parameters. */
1946 fatal_internal("Option %s has no registered parameter.\n", opt->name);
1948 if (opt->action == NULL) /* opt must have an action. */
1949 fatal_internal("Option %s has no registered action.\n", opt->name);
1953 static void
1954 bst_match_par_cb(const void * node, walk_order_e kind, int level)
1956 ctx_t * ctx = ((bst_t *)node)->key;
1958 if (kind == postorder || kind == leaf)
1960 char * str = xstrdup(user_string);
1962 while (*str != '\0')
1964 if (locate_par(str, ctx) != NULL)
1966 if (*user_string2 == '\0')
1967 user_string2 = strappend(user_string2, "- ", ctx->name, (char *)0);
1968 else
1969 user_string2 = strappend(user_string2, "\n- ", ctx->name, (char *)0);
1970 break;
1972 str[strlen(str) - 1] = '\0';
1974 free(str);
1978 static void
1979 match_prefix_cb(const void * node, walk_order_e kind, int level)
1981 par_t * par = ((bst_t *)node)->key;
1983 if (kind == postorder || kind == leaf)
1984 if (strpref(par->name, (char *)user_object))
1986 user_rc++;
1987 user_string = strappend(user_string, par->name, " ", (char *)0);
1991 /* ====================================================================== */
1992 /* A parameter may not be separated from its first option by spaces, in */
1993 /* this case this function looks for a valid flag as a prefix and splits */
1994 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1995 /* option). */
1996 /* */
1997 /* IN word : the word to be checked. */
1998 /* IN ctx : the context in which the flag indexed by the word is to be */
1999 /* checked. */
2000 /* OUT pos : the offset in word pointing just after the matching prefix. */
2001 /* OUT opt : a pointer to the option associated with the new parameter */
2002 /* or NULL if none is found. */
2003 /* */
2004 /* The returned pointer must be freed by the caller. */
2005 /* ====================================================================== */
2006 static char *
2007 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
2009 char * new = NULL;
2010 int len;
2011 par_t * par;
2012 par_t tmp_par = { 0 };
2014 len = strlen(word);
2016 if (len > 2)
2018 new = xstrdup(word);
2022 new[--len] = '\0';
2023 tmp_par.name = new;
2024 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2026 if (par != NULL)
2028 *pos = len;
2029 *opt = par->opt;
2031 else
2033 free(new);
2034 new = NULL;
2037 else
2038 *pos = 0;
2040 return new;
2043 /* ============================================================= */
2044 /* If par_name is an unique abbreviation of an exiting parameter */
2045 /* in the context ctx, then return this parameter. */
2046 /* ============================================================= */
2047 static char *
2048 abbrev_expand(char * par_name, ctx_t * ctx)
2050 user_object = par_name;
2051 user_rc = 0;
2053 *user_string = '\0';
2054 bst_walk(ctx->par_bst, match_prefix_cb);
2055 rtrim(user_string, " ", 0);
2057 /* The previous bst_walk has built a string of blank separated parameters */
2058 /* all having par_name as prefix. This string is put in the user_string */
2059 /* exchange zone. The number of these words in put in user_rc. */
2060 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2061 if (user_rc == 1) /* The number of matching abbreviations. */
2062 return xstrdup(user_string);
2063 else /* There is at least tho defined parameters starting with par_name. */
2065 char * s, *first_s;
2066 par_t * par;
2067 opt_t * opt;
2068 int opt_count = 0;
2069 void * tmp_opt_bst = NULL;
2071 /* Find all the options corresponding to these words and store them */
2072 /* without duplication in a temporary BST. Only their resulting count */
2073 /* matters. */
2074 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2075 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2076 | the first word. */
2077 while (s != NULL)
2079 par = locate_par(s, ctx);
2080 opt = par->opt;
2082 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2084 /* This option as not already been seen */
2085 /* store it and increase the seen counter. */
2086 /* """"""""""""""""""""""""""""""""""""""" */
2087 bst_search(opt, &tmp_opt_bst, opt_compare);
2088 opt_count++;
2090 s = strtok(NULL, " ");
2093 /* Clean the temporary BST without removing the pointer */
2094 /* to the real options. */
2095 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2096 if (tmp_opt_bst != NULL)
2097 bst_destroy(tmp_opt_bst, NULL);
2099 if (opt_count == 1)
2100 /* All the abbreviation are leading to only one option */
2101 /* We can just continue as in the previous case. */
2102 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2103 return xstrdup(first_s);
2104 else
2105 return NULL;
2109 /* ================================================================ */
2110 /* Terminate the program if mandatory options required by a context */
2111 /* are not present. */
2112 /* ================================================================ */
2113 static void
2114 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2116 char * missing;
2118 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2119 fatal(CTXOPTMISPAR, missing);
2122 /* ====================================================== */
2123 /* Return 1 if at least one mandatory option was not seen */
2124 /* when quitting a context, else 0. */
2125 /* ====================================================== */
2126 static int
2127 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2129 user_rc = 0;
2130 *user_string = '\0';
2132 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2133 rtrim(user_string, " ", 0);
2135 *missing = user_string;
2137 return user_rc ? 1 : 0;
2140 /* ========================================================================= */
2141 /* This function terminates the program if an option or its arguments do not */
2142 /* conform to its occurrences constraint. */
2143 /* There constraints can appear by trailing >, < or = in their definition */
2144 /* given in ctxopt_new_ctx. */
2145 /* ========================================================================= */
2146 static void
2147 check_for_occurrences_issues(ctx_inst_t * ctx_inst)
2149 ctx_t * ctx = ctx_inst->ctx;
2150 opt_t * opt;
2151 ll_node_t * node;
2152 opt_inst_t * opt_inst;
2153 char * cur_opt_params = cur_state->cur_opt_params;
2154 char * cur_opt_par_name = cur_state->cur_opt_par_name;
2156 /* Checks options. */
2157 /* """"""""""""""" */
2158 node = ctx->opt_list->head;
2160 while (node != NULL)
2162 opt = node->data;
2164 /* Update current_state. */
2165 /* """"""""""""""""""""" */
2166 cur_state->cur_opt_params = opt->params;
2167 cur_state->opts_count = opt->opt_count_mark;
2168 cur_state->opt_args_count = opt->opt_args_count_mark;
2170 if (opt->opt_count_matter)
2171 switch (opt->opt_count_oper)
2173 case '=':
2174 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2175 fatal(CTXOPTCTEOPT, NULL);
2176 break;
2178 case '<':
2179 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2180 fatal(CTXOPTCTLOPT, NULL);
2181 break;
2183 case '>':
2184 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2185 fatal(CTXOPTCTGOPT, NULL);
2186 break;
2189 node = node->next;
2192 /* Checks arguments. */
2193 /* """"""""""""""""" */
2194 node = ctx_inst->opt_inst_list->head;
2195 while (node != NULL)
2197 opt_inst = node->data;
2198 opt = opt_inst->opt;
2200 /* Update current_state. */
2201 /* """"""""""""""""""""" */
2202 cur_state->cur_opt_par_name = opt_inst->par;
2203 cur_state->opts_count = opt->opt_count_mark;
2204 cur_state->opt_args_count = opt->opt_args_count_mark;
2206 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2208 if (opt->opt_args_count_matter)
2209 switch (opt->opt_args_count_oper)
2211 case '=':
2212 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2213 fatal(CTXOPTCTEARG, NULL);
2214 break;
2216 case '<':
2217 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2218 fatal(CTXOPTCTLARG, NULL);
2219 break;
2221 case '>':
2222 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2223 fatal(CTXOPTCTGARG, NULL);
2224 break;
2227 node = node->next;
2229 cur_state->cur_opt_params = cur_opt_params;
2230 cur_state->cur_opt_par_name = cur_opt_par_name;
2233 /* ======================================================================== */
2234 /* Parse a strings describing options and some of their characteristics */
2235 /* The input string must have follow some rules like in the examples below: */
2236 /* */
2237 /* "opt_name1 opt_name2" */
2238 /* "[opt_name1] opt_name2" */
2239 /* "[opt_name1] opt_name2..." */
2240 /* "[opt_name1 #...] opt_name2... [#]" */
2241 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2242 /* */
2243 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2244 /* means that there can be more than one occurrence of the previous thing. */
2245 /* */
2246 /* opt_name can be followed by a 'new context' change prefixed with the */
2247 /* symbol >, as in opt1>c2 by eg. */
2248 /* */
2249 /* This function returns as soon as one (or no) option has been parsed and */
2250 /* return the offset to the next option to parse. */
2251 /* */
2252 /* In case of successful parsing, an new option is allocated and its */
2253 /* pointer returned. */
2254 /* ======================================================================== */
2255 static int
2256 opt_parse(char * s, opt_t ** opt)
2258 int opt_optional = 0;
2259 int opt_multiple = 0;
2260 int opt_count_matter = 0;
2261 char opt_count_oper = '\0';
2262 unsigned opt_count_mark = 0;
2263 int opt_args = 0;
2264 char opt_arg[33] = { 0 };
2265 int opt_multiple_args = 0;
2266 int opt_args_count_matter = 0;
2267 char opt_args_count_oper = '\0';
2268 unsigned opt_args_count_mark = 0;
2269 int opt_optional_args = 0;
2270 int opt_eval_first = 0;
2272 int n;
2273 int pos;
2274 int count = 0;
2276 char * s_orig = s;
2278 char * p;
2279 char * opt_name;
2280 char * next_ctx;
2281 char token[65];
2283 *opt = NULL;
2284 memset(opt_arg, '\0', 33);
2286 /* Strip the leading blanks. */
2287 /* """"""""""""""""""""""""" */
2288 while (isblank(*s))
2289 s++;
2291 if (*s == '[') /* Start of an optional option. */
2293 opt_optional = 1;
2294 s++;
2296 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2297 if (s == NULL)
2298 return -1; /* Empty string. */
2300 /* Early EOS, only return success if the option is mandatory. */
2301 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2302 if (!*s)
2303 if (opt_optional == 1)
2304 return -(s - s_orig - 1);
2306 /* Validate the option name */
2307 /* ALPHA+(ALPHANUM|_)* */
2308 /* """""""""""""""""""""""" */
2309 p = token;
2310 if (!isalpha(*p) && *p != '*')
2311 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2313 if (*p == '*')
2314 opt_eval_first = 1;
2316 p++;
2317 while (*p)
2319 if (!isalnum(*p) && *p != '_' && *p != '>')
2320 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2321 * a number or a _ */
2322 p++;
2325 if (opt_eval_first)
2326 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2327 else
2328 opt_name = xstrdup(token);
2330 if (*s == ']')
2332 s++;
2333 while (isblank(*s))
2334 s++;
2336 goto success;
2339 /* Check if it can appear multiple times by looking for the dots. */
2340 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2341 p = strtoken(s, token, 3, "[.]", &pos);
2342 if (p)
2344 if (strcmp(token, "...") == 0)
2346 opt_multiple = 1;
2347 s = p;
2348 if (*s == '<' || *s == '=' || *s == '>')
2350 unsigned value;
2351 int offset;
2353 n = sscanf(s + 1, "%u%n", &value, &offset);
2354 if (n == 1)
2356 opt_count_matter = 1;
2357 opt_count_oper = *s;
2358 opt_count_mark = value;
2360 s += offset + 1;
2363 else
2365 free(opt_name);
2366 return -(s - s_orig - 1);
2370 if (*s == ']')
2372 /* Abort on extraneous ] if the option is mandatory. */
2373 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2374 if (!opt_optional)
2375 return -(s - s_orig - 1);
2377 s++; /* skip the ] */
2379 if (!*s || isblank(*s))
2380 goto success;
2381 else
2382 return -(s - s_orig - 1);
2385 /* A blank separates the option name and the argument tag. */
2386 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2387 if (isblank(*s))
2389 char dots[4];
2391 while (isblank(*s))
2392 s++;
2394 if (!*s)
2395 goto success;
2397 pos = 0;
2398 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2399 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2401 opt_args = 1;
2402 opt_optional_args = 1;
2403 if (n == 2)
2404 opt_multiple_args = 1; /* There were dots. */
2406 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2408 if (*s == '<' || *s == '=' || *s == '>')
2410 unsigned value;
2411 int offset;
2413 n = sscanf(s + 1, "%u%n", &value, &offset);
2414 if (n == 1)
2416 opt_args_count_matter = 1;
2417 opt_args_count_oper = *s;
2418 opt_args_count_mark = value;
2420 s += offset + 1;
2423 /* Optional arg tag must end with a ] */
2424 /* """""""""""""""""""""""""""""""""" */
2425 if (*s != ']')
2427 free(opt_name);
2428 return -(s - s_orig - 1);
2431 s++; /* Skip the ] */
2433 else
2435 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2436 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2438 opt_args = 1;
2439 if (n == 2) /* There were dots. */
2440 opt_multiple_args = 1;
2442 s += pos + !!(n == 2) * 3; /* Skip the dots */
2444 if (*s == '<' || *s == '=' || *s == '>')
2446 unsigned value;
2447 int offset;
2449 n = sscanf(s + 1, "%u%n", &value, &offset);
2450 if (n == 1)
2452 opt_args_count_matter = 1;
2453 opt_args_count_oper = *s;
2454 opt_args_count_mark = value;
2456 s += offset + 1;
2460 if (*s == ']')
2462 /* Abort on extraneous ] if the option is mandatory. */
2463 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2464 if (!opt_optional)
2465 return -(s - s_orig - 1);
2467 s++; /* skip the ] */
2469 /* Strip the following blanks. */
2470 /* """"""""""""""""""""""""""" */
2471 while (isblank(*s))
2472 s++;
2474 goto success;
2476 else if (opt_optional == 0 && (!*s || isblank(*s)))
2478 /* Strip the following blanks. */
2479 /* """"""""""""""""""""""""""" */
2480 while (isblank(*s))
2481 s++;
2483 goto success;
2485 else if (opt_args == 0) /* # was not read it is possibly the start *
2486 * of another option. */
2487 goto success;
2488 else
2489 return -(s - s_orig - 1);
2492 success:
2494 /* Strip the following blanks. */
2495 /* """"""""""""""""""""""""""" */
2496 while (isblank(*s))
2497 s++;
2499 next_ctx = NULL;
2501 if (*opt_name == '>')
2502 fatal_internal("The option name is missing in %s.", opt_name);
2504 count = strchrcount(opt_name, '>');
2505 if (count == 1)
2507 char * tmp = strchr(opt_name, '>');
2508 next_ctx = xstrdup(tmp + 1);
2509 *tmp = '\0';
2511 else if (count > 1)
2512 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2514 *opt = xmalloc(sizeof(opt_t));
2516 (*opt)->name = opt_name;
2517 (*opt)->optional = opt_optional;
2518 (*opt)->multiple = opt_multiple;
2519 (*opt)->opt_count_matter = opt_count_matter;
2520 (*opt)->opt_count_oper = opt_count_oper;
2521 (*opt)->opt_count_mark = opt_count_mark;
2522 (*opt)->args = opt_args;
2523 (*opt)->arg = xstrdup(opt_arg);
2524 (*opt)->optional_args = opt_optional_args;
2525 (*opt)->multiple_args = opt_multiple_args;
2526 (*opt)->opt_args_count_matter = opt_args_count_matter;
2527 (*opt)->opt_args_count_oper = opt_args_count_oper;
2528 (*opt)->opt_args_count_mark = opt_args_count_mark;
2529 (*opt)->eval_first = opt_eval_first;
2530 (*opt)->next_ctx = next_ctx;
2531 (*opt)->ctx_list = ll_new();
2532 (*opt)->constraints_list = ll_new();
2533 (*opt)->eval_before_list = ll_new();
2534 (*opt)->action = NULL;
2535 (*opt)->params = NULL;
2536 (*opt)->data = NULL;
2538 return s - s_orig;
2541 /* ==================================================================== */
2542 /* Try to initialize all the option in a given string */
2543 /* Each parsed option are put in a BST tree with its name as index. */
2544 /* */
2545 /* On collision, the arguments only the signature are required to be */
2546 /* the same else this is considered as an error. Options can be used in */
2547 /* more than one context and can be optional in one and mandatory in */
2548 /* another. */
2549 /* ==================================================================== */
2550 static int
2551 init_opts(char * spec, ctx_t * ctx)
2553 opt_t * opt, *bst_opt;
2554 bst_t * node;
2555 int offset;
2557 while (*spec)
2559 if ((offset = opt_parse(spec, &opt)) > 0)
2561 spec += offset;
2563 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2565 int same_next_ctx = 0;
2567 bst_opt = node->key; /* Node extracted from the BST. */
2569 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2570 same_next_ctx = 1;
2571 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2572 same_next_ctx = 0;
2573 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2574 same_next_ctx = 0;
2575 else
2576 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2578 if (bst_opt->optional_args != opt->optional_args
2579 || bst_opt->multiple_args != opt->multiple_args
2580 || bst_opt->args != opt->args || !same_next_ctx)
2582 fatal_internal("The option %s already exists with "
2583 "a different arguments signature.\n",
2584 opt->name);
2587 /* The newly created opt is already present in options_bst. */
2588 /* We can remove it. */
2589 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2590 opt_free(opt);
2592 /* The new occurrence of the option option is legal */
2593 /* append the current context ptr in the list. */
2594 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2595 ll_append(bst_opt->ctx_list, ctx);
2597 /* Append the new option to the context's options list. */
2598 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2599 ll_append(ctx->opt_list, bst_opt);
2601 else
2603 /* Initialize the option's context list with the current context. */
2604 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2605 ll_append(opt->ctx_list, ctx);
2607 /* Append the new option to the context's options list. */
2608 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2609 ll_append(ctx->opt_list, opt);
2611 /* Insert the new option in the BST. */
2612 /* """"""""""""""""""""""""""""""""" */
2613 bst_search(opt, &options_bst, opt_compare);
2616 else
2618 char * s = xstrndup(spec, -offset);
2619 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2620 free(s);
2622 exit(EXIT_FAILURE);
2626 return 1;
2629 /* ===================================================== */
2630 /* ctxopt initialization function, must be called first. */
2631 /* ===================================================== */
2632 void
2633 ctxopt_init(char * prog_name, char * init_flags)
2635 int n;
2637 contexts_bst = NULL;
2638 options_bst = NULL;
2639 char * ptr;
2641 user_rc = 0;
2642 user_value = 0;
2643 user_string = xmalloc(8);
2644 user_string2 = xmalloc(8);
2645 user_object = NULL;
2646 char flag[33], fname[31], vname[31];
2647 int invalid;
2649 ctxopt_initialized = 1;
2651 /* Initialize current_state.*/
2652 /* """""""""""""""""""""""" */
2653 cur_state = xcalloc(sizeof(state_t), 0);
2655 /* Initialize custom error function pointers to NULL. */
2656 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2657 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2658 for (n = 0; n < CTXOPTERRSIZ; n++)
2659 err_functions[n] = NULL;
2661 /* Parse init_flags if any. */
2662 /* """""""""""""""""""""""" */
2663 while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2665 if (*flag)
2667 if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2668 fatal_internal("Invalid flag assignment: %s.", flag);
2670 if (strcmp(fname, "stop_if_non_option") == 0)
2672 if (eval_yes(vname, &invalid))
2673 flags.stop_if_non_option = 1;
2674 else if (!invalid)
2675 flags.stop_if_non_option = 0;
2676 else
2677 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2679 else if (strcmp(fname, "allow_abbreviations") == 0)
2681 if (eval_yes(vname, &invalid))
2682 flags.allow_abbreviations = 1;
2683 else if (!invalid)
2684 flags.allow_abbreviations = 0;
2685 else
2686 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2688 else
2689 fatal_internal("Invalid flag name: %s.", fname);
2693 /* Update current_state. */
2694 /* """"""""""""""""""""" */
2695 if (prog_name)
2697 if (*prog_name == '\0')
2698 cur_state->prog_name = xstrdup("program_name");
2699 else if ((ptr = strrchr(prog_name, '/')))
2700 cur_state->prog_name = xstrdup(ptr + 1);
2701 else
2702 cur_state->prog_name = xstrdup(prog_name);
2704 else
2705 cur_state->prog_name = xstrdup("program_name");
2708 /* ========================================================================= */
2709 /* Utility function which create and register a par_t object in a BST */
2710 /* embedded in a context. */
2711 /* This object will have a name and a pointer to the option it refers to. */
2712 /* These object will be used to quickly find an option from a command */
2713 /* line parameter during the analysis phase. */
2714 /* */
2715 /* IN : an option name. */
2716 /* IN : a string of command line parameters to associate to the option. */
2717 /* Returns : 1 is all was fine else 0. */
2718 /* ========================================================================= */
2719 static int
2720 opt_set_parms(char * opt_name, char * par_str)
2722 char * par_name, *ctx_name;
2723 char * tmp_par_str, *end_tmp_par_str;
2724 ctx_t * ctx;
2725 opt_t * opt;
2726 bst_t * node;
2727 par_t * par, tmp_par;
2728 int rc = 1; /* return code */
2730 ll_t * list;
2731 ll_node_t * lnode;
2733 /* Look if the given option is defined. */
2734 /* """""""""""""""""""""""""""""""""""" */
2735 opt = locate_opt(opt_name);
2736 if (opt == NULL)
2737 fatal_internal("Unknown option %s.", opt_name);
2739 /* For each context using this option. */
2740 /* """"""""""""""""""""""""""""""""""" */
2741 list = opt->ctx_list;
2743 lnode = list->head;
2744 while (lnode != NULL)
2746 /* Locate the context in the contexts tree. */
2747 /* """""""""""""""""""""""""""""""""""""""" */
2748 ctx_name = ((ctx_t *)(lnode->data))->name;
2750 ctx = locate_ctx(ctx_name);
2751 if (ctx == NULL)
2752 fatal_internal("Unknown context %s.", ctx_name);
2753 else
2755 void * par_bst = ctx->par_bst;
2757 tmp_par_str = xstrdup(par_str);
2758 ltrim(tmp_par_str, " \t");
2759 rtrim(tmp_par_str, " \t", 0);
2760 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2761 if (par_name == NULL)
2762 fatal_internal("Parameters are missing for option %s.", opt_name);
2764 /* For each parameter given in par_str, creates a par_t object and */
2765 /* insert it the in the parameters BST of the context. */
2766 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2767 while (par_name != NULL)
2769 tmp_par.name = par_name;
2771 node = bst_find(&tmp_par, &par_bst, par_compare);
2772 if (node != NULL)
2774 fatal_internal("The parameter %s is already defined in context %s.",
2775 par_name, ctx->name);
2776 rc = 0;
2778 else
2780 par = xmalloc(sizeof(par_t));
2781 par->name = xstrdup(par_name);
2782 par->opt = opt; /* Link the option to this parameter */
2784 bst_search(par, &par_bst, par_compare);
2786 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2789 /* Update the value of the root of ctx->par_bst as it may have */
2790 /* been modified. */
2791 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2792 ctx->par_bst = par_bst;
2794 free(tmp_par_str);
2796 lnode = lnode->next;
2799 return rc;
2802 /* ==================================================================== */
2803 /* Create a new context instance. */
2804 /* IN ctx : a context pointer to allow this instance to */
2805 /* access the context fields */
2806 /* IN prev_ctx_inst : the context instance whose option leading to the */
2807 /* creation of this new context instance is part of */
2808 /* Returns : the new context. */
2809 /* ==================================================================== */
2810 static ctx_inst_t *
2811 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
2813 opt_t * opt;
2814 opt_inst_t * gen_opt_inst;
2815 ctx_inst_t * ctx_inst;
2816 seen_opt_t * seen_opt;
2817 char * str, *opt_name;
2818 void * bst;
2819 bst_t * bst_node;
2821 /* Keep a trace of the opt_inst which was at the origin of the creation */
2822 /* of this context instance. */
2823 /* This will serve during the evaluation of the option callbacks. */
2824 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2825 if (prev_ctx_inst != NULL)
2827 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
2829 /* Update current_state. */
2830 /* """"""""""""""""""""" */
2831 cur_state->opt_name = gen_opt_inst->opt->name;
2833 else
2834 gen_opt_inst = NULL;
2836 /* Create and initialize the new context instance. */
2837 /* """"""""""""""""""""""""""""""""""""""""""""""" */
2838 ctx_inst = xmalloc(sizeof(ctx_inst_t));
2839 ctx_inst->ctx = ctx;
2840 ctx_inst->prev_ctx_inst = prev_ctx_inst;
2841 ctx_inst->gen_opt_inst = gen_opt_inst;
2842 ctx_inst->incomp_bst_list = ll_new();
2843 ctx_inst->opt_inst_list = ll_new();
2844 ctx_inst->seen_opt_bst = NULL;
2846 ll_node_t * node;
2848 if (prev_ctx_inst == NULL)
2849 first_ctx_inst = ctx_inst;
2851 /* Initialize the occurrence counters of each opt allowed in the context. */
2852 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2853 node = ctx->opt_list->head;
2854 while (node != NULL)
2856 opt = node->data;
2857 opt->occurrences = 0;
2859 node = node->next;
2862 /* Initialize the BST containing the seen indicator for all the options */
2863 /* allowed in this context instance. */
2864 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2865 node = ctx->opt_list->head;
2866 while (node != NULL)
2868 opt = node->data;
2869 seen_opt = xmalloc(sizeof(seen_opt_t));
2870 seen_opt->opt = opt;
2871 seen_opt->par = NULL;
2872 seen_opt->seen = 0;
2874 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
2876 node = node->next;
2879 /* Initialize the BST containing the incompatibles options. */
2880 /* Incompatibles option names are read from strings found in the list */
2881 /* incomp_list present in each instance of ctx_t. */
2882 /* These names are then used to search for the object of type seen_opt_t */
2883 /* which is already present in the seen_opt_bst of the context instance. */
2884 /* in the BST. */
2885 /* Once found the seen_opt_t object in inserted in the new BST */
2886 /* At the end the new BST in added to the list incomp_bst_list. */
2887 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2888 node = ctx->incomp_list->head;
2889 while (node != NULL)
2891 bst = NULL;
2892 seen_opt_t tmp_seen_opt;
2894 str = xstrdup(node->data);
2895 ltrim(str, " \t");
2896 rtrim(str, " \t", 0);
2897 opt_name = strtok(str, " \t"); /* Extract the first option name. */
2899 while (opt_name != NULL) /* For each option name. */
2901 if ((opt = locate_opt(opt_name)) != NULL)
2903 /* The option found is searched in the tree of potential */
2904 /* seen options. */
2905 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2906 tmp_seen_opt.opt = opt;
2908 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2909 seen_opt_compare);
2911 if (bst_node != NULL)
2913 /* If found then it is added into the new BST tree. */
2914 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2915 seen_opt = bst_node->key;
2916 bst_search(seen_opt, &bst, seen_opt_compare);
2918 else
2919 /* Not found! That means that the option is unknown in this */
2920 /* context as all options has have a seen_opt structure in */
2921 /* seen_opt_bst. */
2922 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2923 fatal_internal("%s is not known in the context %s.", opt->name,
2924 ctx->name);
2926 else
2927 fatal_internal("Unknown option %s.", opt_name);
2929 opt_name = strtok(NULL, " \t");
2932 free(str);
2933 ll_append(ctx_inst->incomp_bst_list, bst);
2935 node = node->next;
2938 return ctx_inst;
2941 /* ====================================================================== */
2942 /* Create a list formed by all the significant command line words */
2943 /* Words beginning or ending with { or } are split. Each of these */
2944 /* symbols will get their own place in the list. */
2945 /* */
2946 /* the {...} part delimits a context, the { will not appear in the list */
2947 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2948 /* to facilitate the parsing phase. | must not be used by the end user. */
2949 /* */
2950 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2951 /* program name is not considered. */
2952 /* IN words : is the array of strings constituting the command line to */
2953 /* parse. */
2954 /* Returns : 1 on success, 0 if a { or } is missing. */
2955 /* ====================================================================== */
2956 static int
2957 ctxopt_build_cmdline_list(int nb_words, char ** words)
2959 int i;
2960 char * prev_word = NULL;
2961 char * word;
2962 char * ptr;
2963 int level = 0;
2964 ll_node_t *node, *start_node;
2966 /* The analysis is divided into three passes, this is not optimal but */
2967 /* must be done only one time. Doing that we privilege readability. */
2968 /* */
2969 /* In the following, SG is the ascii character 1d (dec 29) */
2970 /* */
2971 /* The first pass creates the list, extract the leading an trailing */
2972 /* SG '{' and '}' of each word and give them their own place in the */
2973 /* list */
2974 /* */
2975 /* The second pass transform the '{...}' blocks by a trailing SG */
2976 /* ({...} -> ...|) */
2977 /* */
2978 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2979 /* the middle in the remaining list elements and recreate the pseudo */
2980 /* argument: {} */
2981 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2983 /* If the option list is not empty, clear it before going further. */
2984 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2985 if (cmdline_list != NULL)
2987 node = cmdline_list->head;
2988 while (node != NULL)
2990 free(node->data);
2991 ll_delete(cmdline_list, node);
2992 node = cmdline_list->head;
2995 else
2996 cmdline_list = ll_new();
2998 start_node = cmdline_list->head; /* In the following loop start_node will *
2999 * contain a pointer to the current *
3000 * word stripped from its leading *
3001 * sequence of {, }. */
3002 for (i = 0; i < nb_words; i++)
3004 size_t len = strlen(words[i]);
3005 size_t start, end;
3006 char * str;
3008 str = words[i];
3010 /* Replace each occurrence of the legal word {} by the characters */
3011 /* 0x02 and 0x03 to hide them from the following process. */
3012 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3013 while ((ptr = strstr(str, "{}")) != NULL)
3015 *ptr = 0x02; /* Arbitrary values unlikely. */
3016 *(ptr + 1) = 0x03; /* present in a word */
3019 if (len > 1) /* The word contains at least 2 characters. */
3021 start = 0;
3023 /* Interpret its beginning and look for the start of the real word. */
3024 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3025 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
3027 ll_append(cmdline_list, xstrndup(str + start, 1));
3028 start++;
3029 start_node = cmdline_list->tail;
3032 end = len - 1;
3033 if (str[end] == '{' || str[end] == '}')
3035 if (end > 0 && str[end - 1] != '\\')
3037 ll_append(cmdline_list, xstrndup(str + end, 1));
3038 end--;
3039 node = cmdline_list->tail;
3041 while (str[end] == '{' || str[end] == '}')
3043 if (end > start && str[end - 1] == '\\')
3044 break;
3046 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3047 end--;
3048 node = node->prev;
3053 if (start <= end)
3055 if (start_node != NULL)
3056 ll_insert_after(cmdline_list, start_node,
3057 xstrndup(str + start, end - start + 1));
3058 else
3059 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3060 start_node = cmdline_list->tail;
3063 else if (len == 1)
3065 ll_append(cmdline_list, xstrdup(str));
3066 start_node = cmdline_list->tail;
3070 /* 2nd pass. */
3071 /* """"""""" */
3072 node = cmdline_list->head;
3074 level = 0;
3075 while (node != NULL)
3077 word = node->data;
3079 if (strcmp(word, "{") == 0)
3081 ll_node_t * old_node = node;
3082 level++;
3083 node = node->next;
3084 free(word);
3085 ll_delete(cmdline_list, old_node);
3087 else if (strcmp(word, "}") == 0)
3089 level--;
3091 if (level < 0)
3092 return 0;
3093 else
3094 *word = 0x1d;
3096 else
3097 node = node->next;
3100 if (level != 0)
3101 return 0;
3103 /* 3rd pass. */
3104 /* """"""""" */
3105 node = cmdline_list->head;
3107 while (node != NULL)
3109 word = node->data;
3111 /* Restore the original { and } characters forming the legal word {}. */
3112 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3113 while ((ptr = strchr(word, 0x02)) != NULL)
3114 *ptr = '{';
3115 while ((ptr = strchr(word, 0x03)) != NULL)
3116 *ptr = '}';
3118 /* Remove a SG if the previous element is SG. */
3119 /* """""""""""""""""""""""""""""""""""""""""" */
3120 if (strcmp(word, "\x1d") == 0)
3122 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3124 ll_node_t * old_node = node;
3125 node = node->prev;
3126 free(old_node->data);
3127 ll_delete(cmdline_list, old_node);
3130 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3131 * a parameter. Protect it. */
3133 free(node->data);
3134 node->data = xstrdup("\\-");
3137 prev_word = node->data;
3138 node = node->next;
3141 /* Clean useless and SG at the beginning and end of list. */
3142 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3143 node = cmdline_list->head;
3145 if (node == NULL)
3146 return 1;
3148 word = node->data;
3150 if (strcmp(word, "\x1d") == 0)
3152 free(word);
3153 ll_delete(cmdline_list, node);
3156 node = cmdline_list->tail;
3157 if (node == NULL)
3158 return 1;
3160 word = node->data;
3162 if (strcmp(word, "\x1d") == 0)
3164 free(word);
3165 ll_delete(cmdline_list, node);
3168 return 1;
3171 /* ===================================================================== */
3172 /* Build and analyze the command line list and create the linked data */
3173 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3174 /* This function identifies the following errors and creates an array of */
3175 /* The remaining unanalyzed arguments. */
3176 /* - detect missing arguments */
3177 /* - detect too many arguments */
3178 /* - detect unknown parameters in a context */
3179 /* - detect too many occurrences of a parameters in a context */
3180 /* - detect missing required arguments in a context */
3181 /* */
3182 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3183 /* program name is not considered */
3184 /* IN words : is the array of strings constituting the command line to */
3185 /* parse. */
3186 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3187 /* is present in the list. */
3188 /* OUT rem_args : array of remaining command line arguments if a -- */
3189 /* is present in the list. This array must be free by */
3190 /* The caller as it is allocated here. */
3191 /* ===================================================================== */
3192 void
3193 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3194 char *** rem_args)
3196 char * ctxopt_debug_env; /* Environment variable CTXOPT_DEBUG content. */
3197 int ctxopt_debug; /* 1 if ctxopt_debug_env is set and not empty. *
3198 | 0 if ctxopt_debug_env is unset or empty. */
3200 ctx_t * ctx;
3201 opt_t * opt;
3202 par_t * par;
3203 ctx_inst_t * ctx_inst;
3204 opt_inst_t * opt_inst;
3205 int expect_par = 0;
3206 int expect_arg = 0;
3207 int expect_par_or_arg = 0;
3209 ll_node_t * cli_node;
3210 bst_t * bst_node;
3211 seen_opt_t * bst_seen_opt;
3212 char * par_name;
3213 void * bst;
3215 ll_node_t * node;
3217 if (!ctxopt_build_cmdline_list(nb_words, words))
3218 fatal_internal("The command line could not be parsed: "
3219 "missing '{' or '}' detected.");
3221 if (main_ctx == NULL)
3222 fatal_internal("At least one context must have been created.");
3224 /* Check that all options has an action and at least one parameter. */
3225 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3226 bst_walk(options_bst, bst_check_opt_cb);
3228 /* CTXOPT debug setting */
3229 /* """""""""""""""""""" */
3230 ctxopt_debug_env = getenv("CTXOPT_DEBUG");
3231 if (ctxopt_debug_env != NULL && *ctxopt_debug_env != '\0')
3232 ctxopt_debug = 1;
3233 else
3234 ctxopt_debug = 0;
3236 /* Create the first ctx_inst record. */
3237 /* """"""""""""""""""""""""""""""""" */
3238 ctx = main_ctx;
3240 ctx_inst_list = ll_new();
3241 ctx_inst = new_ctx_inst(ctx, NULL);
3242 ctx_inst->par_name = NULL;
3244 /* Update current_state. */
3245 /* """"""""""""""""""""" */
3246 cur_state->ctx_name = ctx->name;
3248 ll_append(ctx_inst_list, ctx_inst);
3250 /* For each node in the command line. */
3251 /* """""""""""""""""""""""""""""""""" */
3252 cli_node = cmdline_list->head;
3253 expect_par = 1;
3254 while (cli_node != NULL)
3256 if (strcmp(cli_node->data, "--") == 0)
3257 break; /* No new parameter will be analyzed after this point. */
3259 par_name = cli_node->data;
3261 /* Replace a leading -- by a single - */
3262 /* """""""""""""""""""""""""""""""""" */
3263 if (strncmp(cli_node->data, "--", 2) == 0)
3264 par_name += 1; /* Ignore the first dash */
3266 if (strcmp(par_name, "\x1d") == 0)
3268 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3269 check_for_occurrences_issues(ctx_inst);
3271 /* Forced backtracking to the previous context instance. */
3272 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3273 if (ctx_inst->prev_ctx_inst != NULL)
3275 ctx_inst = ctx_inst->prev_ctx_inst;
3276 ctx = ctx_inst->ctx;
3278 /* Update current_states. */
3279 /* """"""""""""""""""""" */
3280 cur_state->ctx_name = ctx->name;
3281 cur_state->ctx_par_name = ctx_inst->par_name;
3283 if (ctxopt_debug)
3284 fprintf(stderr,
3285 "CTXOPT_DEBUG: Context forced backtrack, "
3286 "new current context: %s.\n",
3287 ctx->name);
3289 else
3291 /* Update current_state. */
3292 /* """"""""""""""""""""" */
3293 cur_state->ctx_par_name = NULL;
3296 else if (expect_par && *par_name == '-')
3298 int pos = 0;
3299 char * prefix;
3301 /* Update current_state. */
3302 /* """"""""""""""""""""" */
3303 cur_state->cur_opt_par_name = par_name;
3304 cur_state->ctx_name = ctx->name;
3305 cur_state->ctx_par_name = ctx_inst->par_name;
3307 if (ctxopt_debug)
3308 fprintf(stderr, "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3309 par_name, cur_state->ctx_name);
3311 /* An expected parameter has been seen. */
3312 /* """""""""""""""""""""""""""""""""""" */
3313 if ((par = locate_par(par_name, ctx)) == NULL)
3315 opt_t * popt;
3316 char * word;
3318 /* Look if this parameter is an unique abbreviation of a longer */
3319 /* parameter. If this is the case then just replace it with its */
3320 /* full length version and try again. */
3321 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3322 if (flags.allow_abbreviations)
3323 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3325 cli_node->data = word;
3326 continue;
3329 /* Try to find a prefix which is a valid parameter in this context */
3330 /* If found, split the cli_node in two to build a new parameter */
3331 /* node and followed by a node containing the remaining string */
3332 /* If the new parameter corresponds to an option not taking */
3333 /* argument then prefix the remaining string whit a dash as it may */
3334 /* contain a new parameter. */
3335 /* The new parameter will be re-evaluated in the next iteration of */
3336 /* the loop. */
3337 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3338 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3339 if (prefix != NULL && pos != 0)
3341 if (ctxopt_debug)
3342 fprintf(stderr,
3343 "CTXOPT_DEBUG: Found a valid parameter "
3344 "as a prefix of %s: %s.\n",
3345 par_name, prefix);
3347 cli_node->data = prefix; /* prefix contains le name of a valid *
3348 | parameter in this context. */
3350 if (popt->args)
3352 /* The parameter may be followed by arguments. */
3353 /* ''''''''''''''''''''''''''''''''''''''''''' */
3354 if (*(par_name + pos) == '-')
3356 word = xstrdup("\\"); /* Protect the '-' */
3357 word = strappend(word, par_name + pos, (char *)0);
3359 else
3360 word = xstrdup(par_name + pos);
3362 else
3364 /* The parameter does not take arguments, the */
3365 /* following word must be a parameter or nothing */
3366 /* hence prefix it with a dash. */
3367 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3368 word = xstrdup("-");
3369 word = strappend(word, par_name + pos, (char *)0);
3372 /* Insert it after the current node in the list. */
3373 /* """"""""""""""""""""""""""""""""""""""""""""" */
3374 ll_insert_after(cmdline_list, cli_node, word);
3376 continue; /* loop */
3378 else
3380 check_for_missing_mandatory_opt(ctx_inst, par_name);
3381 check_for_occurrences_issues(ctx_inst);
3383 if (ctx_inst->prev_ctx_inst == NULL)
3385 char * errmsg = xstrdup("");
3387 /* Update current_state. */
3388 /* """"""""""""""""""""" */
3389 cur_state->ctx_par_name = NULL;
3391 *user_string = '\0';
3392 *user_string2 = '\0';
3394 user_string = strappend(user_string, par_name, (char *)0);
3396 bst_walk(contexts_bst, bst_match_par_cb);
3398 if (*user_string2 != '\0')
3400 errmsg = strappend(
3401 errmsg,
3402 "\nThis parameter is only valid in one of the following "
3403 "contexts:\n",
3404 user_string2,
3405 "\n\nSwitch to one of them first using the appropriate "
3406 "parameter, see below.\n",
3407 (char *)0);
3410 fatal(CTXOPTUNKPAR, errmsg);
3412 else
3414 /* Tries to backtrack and analyse the same parameter in the */
3415 /* previous context. */
3416 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3417 ctx_inst = ctx_inst->prev_ctx_inst;
3418 ctx = ctx_inst->ctx;
3420 if (ctxopt_debug)
3421 fprintf(stderr,
3422 "CTXOPT_DEBUG: Context backtrack, "
3423 "new current context: %s.\n",
3424 ctx->name);
3426 /* Update current_state. */
3427 /* """"""""""""""""""""" */
3428 cur_state->ctx_name = ctx->name;
3429 cur_state->ctx_par_name = ctx_inst->par_name;
3431 cli_node = cli_node->prev;
3435 else
3437 seen_opt_t seen_opt;
3439 /* The parameter is valid in the context, create a opt_inst and */
3440 /* append it to the ctx_inst list options list. */
3441 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3442 opt = par->opt;
3444 opt->occurrences++;
3446 opt_inst = xmalloc(sizeof(opt_inst_t));
3447 opt_inst->opt = opt;
3448 opt_inst->par = par_name;
3449 opt_inst->values_list = ll_new();
3450 opt_inst->next_ctx_inst = NULL;
3452 /* Update current_state. */
3453 /* """"""""""""""""""""" */
3454 cur_state->cur_opt_params = opt->params;
3456 /* Priority option are inserted at the start of the opt_inst list */
3457 /* but their order of appearance in the context definition must */
3458 /* be preserver so each new priority option will be placed after */
3459 /* the previous ones at the start of the opt_inst list. */
3460 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3461 if (!opt->eval_first)
3463 /* Look if we have a registered dependency in the order of the */
3464 /* evaluation of two options. */
3465 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3466 if (opt->eval_before_list->len > 0)
3468 ll_t * list = ctx_inst->opt_inst_list;
3469 ll_node_t * opt_inst_node;
3471 ll_t * before_list = opt->eval_before_list;
3472 ll_node_t * before_node = before_list->head;
3474 ll_node_t * target_node = NULL; /* If not NULL, the new node *
3475 | will be inserted before it. */
3477 /* For each entry in eval_before_list, try to find if it */
3478 /* refers to an option already entered in the context. If this */
3479 /* is the case, insert it just before it instead of putting it */
3480 /* at the end. */
3481 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3482 while (before_node != NULL)
3484 opt_inst_node = list->head;
3486 while (opt_inst_node != target_node)
3488 opt_t * tmp_opt = (((opt_inst_t *)opt_inst_node->data))->opt;
3490 /* We have found an option mentioned if the before_list */
3491 /* of the option we want to add. We can stop searching. */
3492 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3493 if (strcmp(tmp_opt->name, ((opt_t *)before_node->data)->name))
3494 opt_inst_node = opt_inst_node->next;
3495 else
3496 target_node = opt_inst_node; /* Set the target node. */
3499 before_node = before_node->next;
3502 /* Insert or append ? */
3503 /* """""""""""""""""" */
3504 if (target_node != NULL)
3505 ll_insert_before(ctx_inst->opt_inst_list, target_node, opt_inst);
3506 else
3507 ll_append(ctx_inst->opt_inst_list, opt_inst);
3509 else
3510 ll_append(ctx_inst->opt_inst_list, opt_inst);
3512 else
3514 ll_node_t * opt_inst_node = ctx_inst->opt_inst_list->head;
3515 opt_inst_t * tmp_opt_inst;
3517 while (opt_inst_node != NULL)
3519 tmp_opt_inst = opt_inst_node->data;
3520 if (!tmp_opt_inst->opt->eval_first)
3522 ll_insert_before(ctx_inst->opt_inst_list, opt_inst_node,
3523 opt_inst);
3524 break;
3526 else
3527 opt_inst_node = opt_inst_node->next;
3529 if (opt_inst_node == NULL)
3530 ll_append(ctx_inst->opt_inst_list, opt_inst);
3533 /* Check if an option was already seen in the */
3534 /* current context instance. */
3535 /* """""""""""""""""""""""""""""""""""""""""" */
3536 seen_opt.opt = opt;
3538 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3539 seen_opt_compare);
3541 /* bst_node cannot be NULL here. */
3543 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3545 if (!opt->multiple && bst_seen_opt->seen == 1)
3546 fatal(CTXOPTDUPOPT, NULL);
3548 /* Check if this option is compatible with the options already */
3549 /* seen in this context instance. */
3550 /* Look if the option is present in one on the BST present in */
3551 /* the incomp_bst_list of the context instance. */
3552 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3553 node = ctx_inst->incomp_bst_list->head;
3554 while (node != NULL)
3556 bst = node->data;
3557 user_object = NULL;
3559 /* There can only have one seen_opt object in the BST tree was */
3560 /* already seen, try to locate it, the result will be put in */
3561 /* user_object by the bst_seen_opt_seen_cb function. */
3562 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3563 bst_walk(bst, bst_seen_opt_seen_cb);
3565 /* If it is the case, look if the current option is also */
3566 /* in this BST. */
3567 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3568 if (user_object != NULL)
3570 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3572 if (bst_node != NULL)
3574 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3575 if (bst_seen_opt->seen == 0)
3576 fatal(CTXOPTINCOPT, (char *)user_object);
3580 node = node->next;
3583 /* Mark this option as seen in the current context instance. */
3584 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3585 bst_seen_opt->seen = 1;
3586 free(bst_seen_opt->par);
3587 bst_seen_opt->par = xstrdup(par_name);
3589 /* If this option leads to a next context, create a new ctx_inst */
3590 /* and switch to it for the analyse of the future parameter. */
3591 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3592 if (opt->next_ctx != NULL)
3594 ctx = locate_ctx(opt->next_ctx);
3596 if (ctx == NULL)
3597 fatal_internal("Unknown context %s.", opt->next_ctx);
3599 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3600 ctx_inst->par_name = xstrdup(par_name);
3602 ll_append(ctx_inst_list, ctx_inst);
3604 if (ctxopt_debug)
3605 fprintf(stderr,
3606 "CTXOPT_DEBUG: Context change, "
3607 "new current context: %s.\n",
3608 ctx->name);
3611 /* Look is we must expect some arguments. */
3612 /* """""""""""""""""""""""""""""""""""""" */
3613 expect_par_or_arg = 0;
3614 expect_par = 0;
3615 expect_arg = 0;
3617 if (!opt->args)
3618 expect_par = 1; /* Parameter doesn't accept any argument. */
3619 else
3621 if (!opt->optional_args)
3622 expect_arg = 1; /* Parameter has mandatory arguments. */
3623 else
3624 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3628 else if (expect_par && *par_name != '-')
3630 ll_node_t * n = cli_node->next;
3632 if (!flags.stop_if_non_option)
3633 /* Look if potential arguments must still be analyzed until the */
3634 /* end of the context/command line part to analyze/command line. */
3635 /* If this is the case we have met an extra argument. */
3636 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3637 while (n != NULL)
3639 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3640 fatal(CTXOPTUNXARG, NULL);
3642 if (*(char *)(n->data) == '-')
3643 fatal(CTXOPTUNXARG, NULL);
3645 n = n->next;
3648 break; /* An unexpected non parameter was seen, if no Potential *
3649 | arguments remain in the command line or *
3650 | flags.stop_if_non_option is set, assume that it is is *
3651 | the first of the non arguments and stop the command *
3652 | line analysis. */
3654 else if (expect_arg && *par_name != '-')
3656 ll_node_t * cstr_node;
3657 constraint_t * cstr;
3659 if (ctxopt_debug)
3660 fprintf(stderr, "CTXOPT_DEBUG: Argument: %s.\n", par_name);
3662 /* Check if the arguments of the option respects */
3663 /* the attached constraints if any. */
3664 /* """"""""""""""""""""""""""""""""""""""""""""" */
3665 cstr_node = opt->constraints_list->head;
3666 while (cstr_node != NULL)
3668 cstr = cstr_node->data;
3669 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3670 cur_state->cur_opt_par_name))
3672 fputs("\n", stderr);
3673 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3676 cstr_node = cstr_node->next;
3679 /* If the argument is valid, store it. */
3680 /* """"""""""""""""""""""""""""""""""" */
3681 if (*par_name == '\\' && *(par_name + 1) == '-')
3682 ll_append(opt_inst->values_list, par_name + 1);
3683 else
3684 ll_append(opt_inst->values_list, par_name);
3686 expect_arg = 0;
3687 expect_par = 0;
3688 expect_par_or_arg = 0;
3690 if (opt->multiple_args)
3691 expect_par_or_arg = 1;
3692 else
3693 expect_par = 1; /* Parameter takes only one argument. */
3695 else if (expect_arg && *par_name == '-')
3696 fatal(CTXOPTMISARG, NULL);
3697 else if (expect_par_or_arg)
3699 expect_arg = 0;
3700 expect_par = 0;
3701 expect_par_or_arg = 0;
3703 if (*par_name != '-')
3704 expect_arg = 1; /* Consider this word as an argument and retry. */
3705 else
3706 expect_par = 1; /* Consider this word as a parameter and retry. */
3708 cli_node = cli_node->prev;
3711 cli_node = cli_node->next;
3714 if (cmdline_list->len > 0 && *par_name == '-')
3716 if (expect_arg && !opt->optional_args)
3717 fatal(CTXOPTMISARG, NULL);
3720 /* Look if a context_instance has unseen mandatory options. */
3721 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3722 node = ctx_inst_list->head;
3723 while (node != NULL)
3725 ctx_inst = node->data;
3727 /* Update current_state. */
3728 /* """"""""""""""""""""" */
3729 cur_state->ctx_name = ctx_inst->ctx->name;
3730 cur_state->ctx_par_name = ctx_inst->par_name;
3732 check_for_missing_mandatory_opt(ctx_inst, par_name);
3733 check_for_occurrences_issues(ctx_inst);
3735 node = node->next;
3738 /* Allocate the array containing the remaining not analyzed */
3739 /* command line arguments. */
3740 /* NOTE: The strings in the array are just pointer to the */
3741 /* data of the generating list and must not be freed. */
3742 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3743 if (cli_node != NULL)
3745 if (strcmp((char *)cli_node->data, "--") == 0)
3746 /* The special parameter -- was encountered, the -- argument is not */
3747 /* put in the remaining arguments. */
3748 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3749 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3750 else
3751 /* A non parameter was encountered when a parameter was expected. We */
3752 /* assume that the evaluation of the remaining command line argument */
3753 /* are not the responsibility of the users code. */
3754 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3755 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3757 else
3759 *nb_rem_args = 0;
3760 *rem_args = xmalloc(sizeof(char *));
3761 (*rem_args)[0] = NULL;
3765 /* ==================================================== */
3766 /* Free ctxopt memory used for its internal structures. */
3767 /* ==================================================== */
3768 void
3769 ctxopt_free_memory(void)
3771 ll_destroy(cmdline_list, free);
3772 ll_destroy(ctx_inst_list, ctx_inst_free);
3773 bst_destroy(options_bst, opt_free);
3774 bst_destroy(contexts_bst, ctx_free);
3777 /* ==================================================================== */
3778 /* Parse the options data structures and launches the callback function */
3779 /* attached to each options instances. */
3780 /* This calls a recursive function which proceeds context per context. */
3781 /* ==================================================================== */
3782 void
3783 ctxopt_evaluate(void)
3785 evaluate_ctx_inst(first_ctx_inst);
3788 /* =================================================================== */
3789 /* Recursive function called by ctxopt_evaluate to process the list of */
3790 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3791 /* action attached to the context and its option instances. */
3792 /* =================================================================== */
3793 static void
3794 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
3796 opt_inst_t * opt_inst;
3797 ctx_t * ctx;
3798 opt_t * opt;
3799 ll_node_t * opt_inst_node;
3800 char ** args;
3801 int nb_args;
3803 if (ctx_inst == NULL)
3804 return;
3806 ctx = ctx_inst->ctx;
3808 /* Do not evaluate the action attached to this context is there is no */
3809 /* option to evaluate. */
3810 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3811 opt_inst_node = ctx_inst->opt_inst_list->head;
3812 if (opt_inst_node == NULL)
3813 return;
3815 /* Call the entering action attached to this context if any. */
3816 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3817 if (ctx->action != NULL)
3819 if (ctx_inst->prev_ctx_inst != NULL)
3820 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
3821 ctx->nb_data, ctx->data);
3822 else
3823 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
3826 /* For each instance of options. */
3827 /* """"""""""""""""""""""""""""" */
3828 while (opt_inst_node != NULL)
3830 opt_inst = (opt_inst_t *)(opt_inst_node->data);
3831 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
3832 &args);
3833 opt = opt_inst->opt;
3835 /* Launch the attached action if any. */
3836 /* """""""""""""""""""""""""""""""""" */
3837 if (opt->action != NULL)
3838 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
3839 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
3841 if (opt_inst->next_ctx_inst != NULL)
3842 evaluate_ctx_inst(opt_inst->next_ctx_inst);
3844 if (args != NULL)
3845 free(args);
3847 opt_inst_node = opt_inst_node->next;
3850 /* Call the exiting action attached to this context if any. */
3851 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3852 if (ctx->action != NULL)
3854 if (ctx_inst->prev_ctx_inst != NULL)
3855 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
3856 ctx->nb_data, ctx->data);
3857 else
3858 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
3862 /* ============================================================ */
3863 /* Create and initializes a new context. */
3864 /* - allocate space. */
3865 /* - name it. */
3866 /* - initialize its option with a few of their characteristics. */
3867 /* ============================================================ */
3868 void
3869 ctxopt_new_ctx(char * name, char * opts_specs)
3871 ctx_t * ctx;
3872 char * p;
3874 if (!ctxopt_initialized)
3875 fatal_internal("Please call ctxopt_init first.");
3877 ctx = xmalloc(sizeof(ctx_t));
3879 /* Validates the context name: */
3880 /* ALPHA+(ALPHANUM|_)* */
3881 /* """"""""""""""""""""""""""" */
3882 p = name;
3883 if (!isalpha(*p))
3884 fatal_internal("A context name must start with a letter: %s.", name);
3886 p++;
3887 while (*p)
3889 if (!isalnum(*p) && *p != '_')
3890 fatal_internal("A context name must only contain letters, "
3891 "numbers or '_': %s.",
3892 name);
3893 p++;
3896 ctx->name = xstrdup(name);
3897 ctx->opt_list = ll_new(); /* List of options legit in this context. */
3898 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
3899 ctx->par_bst = NULL;
3900 ctx->data = NULL;
3901 ctx->action = NULL;
3903 /* The first created context is the main one. */
3904 /* """""""""""""""""""""""""""""""""""""""""" */
3905 if (contexts_bst == NULL)
3907 main_ctx = ctx;
3909 cur_state->ctx_name = ctx->name;
3912 if (init_opts(opts_specs, ctx) == 0)
3913 exit(EXIT_FAILURE);
3914 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
3915 fatal_internal("The context %s already exists.", name);
3916 else
3917 bst_search(ctx, &contexts_bst, ctx_compare);
3920 /* ==================================================== */
3921 /* Display a usage screen limited to a specific context */
3922 /* IN: the context name. */
3923 /* IN: what to do after (continue or exit the program) */
3924 /* possible values: continue_after, exit_after. */
3925 /* ==================================================== */
3926 void
3927 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
3929 ctx_t * ctx;
3930 ll_t * list;
3932 int has_optional = 0;
3933 int has_ellipsis = 0;
3934 int has_rule = 0;
3935 int has_generic_arg = 0;
3936 int has_ctx_change = 0;
3937 int has_early_eval = 0;
3939 ctx = locate_ctx(ctx_name);
3940 if (ctx == NULL)
3941 fatal_internal("Unknown context %s.", ctx_name);
3943 if (cur_state->ctx_par_name == NULL)
3944 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
3945 else
3946 printf("\nSynopsis for the context introduced by %s:\n",
3947 cur_state->ctx_par_name);
3949 list = ctx->opt_list;
3950 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3951 &has_ctx_change, &has_early_eval);
3953 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3954 has_optional, has_ellipsis, has_rule);
3956 if (action == exit_after)
3957 exit(EXIT_FAILURE);
3960 /* =================================================== */
3961 /* Display a full usage screen about all contexts. */
3962 /* IN: what to do after (continue or exit the program) */
3963 /* possible values: continue_after, exit_after. */
3964 /* =================================================== */
3965 void
3966 ctxopt_disp_usage(usage_behaviour action)
3968 ll_t * list;
3969 int has_optional = 0;
3970 int has_ellipsis = 0;
3971 int has_rule = 0;
3972 int has_generic_arg = 0;
3973 int has_ctx_change = 0;
3974 int has_early_eval = 0;
3976 if (main_ctx == NULL)
3977 fatal_internal("At least one context must have been created.");
3979 /* Usage for the first context. */
3980 /* """""""""""""""""""""""""""" */
3981 printf("\nAllowed options in the base context:\n");
3982 list = main_ctx->opt_list;
3983 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3984 &has_ctx_change, &has_early_eval);
3986 /* Usage for the other contexts. */
3987 /* """"""""""""""""""""""""""""" */
3988 bst_walk(contexts_bst, bst_print_ctx_cb);
3990 /* Contextual syntactic explanations. */
3991 /* """""""""""""""""""""""""""""""""" */
3992 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3993 has_optional, has_ellipsis, has_rule);
3995 if (action == exit_after)
3996 exit(EXIT_FAILURE);
3999 /* *********************************** */
4000 /* Built-in constraint check functions */
4001 /* *********************************** */
4003 /* ============================================================= */
4004 /* This constraint checks if each arguments respects a format as */
4005 /* defined for the scanf function. */
4006 /* return 1 if yes and 0 if no. */
4007 /* ============================================================= */
4009 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
4011 int rc = 0;
4013 char x[256];
4014 char y;
4015 char * format;
4017 if (nb_args != 1)
4018 fatal_internal("Format constraint, invalid number of parameters.");
4020 if (strlen(value) > 255)
4021 value[255] = '\0';
4023 format = xstrdup(args[0]);
4024 format = strappend(format, "%c", (char *)0);
4026 rc = sscanf(value, format, x, &y);
4027 if (rc != 1)
4028 fprintf(stderr,
4029 "The argument %s of %s does not respect the imposed format %s.",
4030 value, par, args[0]);
4032 free(format);
4034 return rc == 1;
4037 /* ================================================================== */
4038 /* This constraint checks if each arguments of the option instance is */
4039 /* between a minimum and a maximum (inclusive). */
4040 /* return 1 if yes and 0 if no. */
4041 /* ================================================================== */
4043 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
4045 regex_t re;
4047 if (nb_args != 1)
4048 fatal_internal(
4049 "Regular expression constraint, invalid number of parameters.");
4051 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
4052 fatal_internal("Invalid regular expression %s.", args[0]);
4054 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
4056 fprintf(stderr,
4057 "The argument %s of %s doesn't match the constraining "
4058 "regular expression %s.",
4059 value, par, args[0]);
4060 return 0;
4063 regfree(&re);
4065 return 1;
4068 /* ================================================================== */
4069 /* This constraint checks if each arguments of the option instance is */
4070 /* between a minimum and a maximum (inclusive). */
4071 /* return 1 if yes and 0 if no. */
4072 /* ================================================================== */
4074 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
4076 long min, max;
4077 char c;
4078 char * ptr;
4079 int n;
4080 long v;
4081 int min_only = 0;
4082 int max_only = 0;
4084 if (nb_args != 2)
4085 fatal_internal("Range constraint, invalid number of parameters.");
4087 if (strcmp(args[0], ".") == 0)
4088 max_only = 1;
4089 else
4090 n = sscanf(args[0], "%ld%c", &min, &c);
4092 if (!max_only && n != 1)
4093 fatal_internal("Range constraint, min: invalid parameters.");
4095 if (strcmp(args[1], ".") == 0)
4096 min_only = 1;
4097 else
4098 n = sscanf(args[1], "%ld%c", &max, &c);
4100 if (!min_only && n != 1)
4101 fatal_internal("Range constraint, max: invalid parameters.");
4103 if (min_only && max_only)
4104 fatal_internal("Range constraint, invalid parameters.");
4106 errno = 0;
4107 v = strtol(value, &ptr, 10);
4108 if (errno || ptr == value)
4109 return 0;
4111 if (min_only)
4113 if (v < min)
4115 fprintf(stderr,
4116 "The argument %ld of %s is not greater than or equal to %ld.", v,
4117 par, min);
4118 return 0;
4120 else
4121 return 1;
4123 else if (max_only)
4125 if (v > max)
4127 fprintf(stderr,
4128 "The argument %ld of %s is not less than or equal to %ld.", v,
4129 par, max);
4130 return 0;
4132 else
4133 return 1;
4135 else if (v < min || v > max)
4137 fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
4138 par, min, max);
4139 return 0;
4142 return 1; /* check passed */
4145 /* =============================================================== */
4146 /* This function provides a way to set the behaviour of a context. */
4147 /* =============================================================== */
4148 void
4149 ctxopt_add_global_settings(settings s, ...)
4151 va_list(args);
4152 va_start(args, s);
4154 switch (s)
4156 case error_functions:
4158 typedef void fn(errors e, state_t * state);
4160 void (*function)(errors e, state_t * state);
4162 errors e;
4163 e = va_arg(args, errors);
4164 function = va_arg(args, fn *);
4165 err_functions[e] = function;
4166 break;
4169 default:
4170 break;
4172 va_end(args);
4175 /* ================================================================ */
4176 /* This function provides a way to set the behaviour of an option. */
4177 /* It can take a variable number of arguments according to its */
4178 /* first argument: */
4179 /* - parameter: */
4180 /* o a string containing an option name and all its possible */
4181 /* parameters separates by spaces, tabs or commas (char *) */
4182 /* (e.g: "help -h -help"). */
4183 /* - actions: */
4184 /* o a string containing an option name. */
4185 /* o a pointer to a function which will be called at evaluation */
4186 /* time. */
4187 /* - constraints: */
4188 /* o a string containing an option name. */
4189 /* o a pointer to a function to check if an argument is valid. */
4190 /* o a strings containing the arguments to this function. */
4191 /* ================================================================ */
4192 void
4193 ctxopt_add_opt_settings(settings s, ...)
4195 opt_t * opt;
4196 void * ptr = NULL;
4198 va_list(args);
4199 va_start(args, s);
4201 switch (s)
4203 /* This part associates some command line parameters to an option. */
4204 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4205 case parameters:
4207 char * opt_name;
4208 char * params;
4210 /* The second argument must be a string containing: */
4211 /* - The name of an existing option. */
4212 /* - a list of parameters with a leading dash (-). */
4213 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4214 ptr = va_arg(args, char *);
4215 opt_name = ptr;
4217 if (opt_name != NULL)
4219 if ((opt = locate_opt(opt_name)) != NULL)
4221 ptr = va_arg(args, char *);
4222 params = ptr;
4224 if (!opt_set_parms(opt_name, params))
4225 fatal_internal(
4226 "Duplicated parameters or bad settings for the option %s.",
4227 params);
4229 else
4230 fatal_internal("Unknown option %s.", opt_name);
4232 else
4233 fatal_internal(
4234 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4236 /* Here opt is a known option. */
4237 /* """"""""""""""""""""""""""" */
4238 if (opt->params != NULL)
4239 fatal_internal("Parameters are already set for %s.", opt_name);
4240 else
4242 size_t n;
4243 size_t l = strlen(params);
4245 opt->params = xstrdup(params);
4246 while ((n = strcspn(opt->params, " \t")) < l)
4247 opt->params[n] = '|';
4250 break;
4253 /* This part associates a callback function to an option. */
4254 /* This function will be called when an instance of an option */
4255 /* is evaluated. */
4256 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4257 case actions:
4259 void * data;
4260 void (*function)();
4261 int nb_data = 0;
4263 /* The second argument must be the name of an existing option. */
4264 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4265 ptr = va_arg(args, char *);
4267 if ((opt = locate_opt(ptr)) != NULL)
4269 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4270 void **);
4272 /* The third argument must be the callback function. */
4273 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4274 function = va_arg(args, fn *);
4275 opt->action = function;
4277 /* The fourth argument must be a pointer to an user's defined */
4278 /* variable or structure that the previous function can manage. */
4279 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4280 while ((data = va_arg(args, void *)) != NULL)
4282 nb_data++;
4283 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4284 opt->data[nb_data - 1] = data;
4286 opt->nb_data = nb_data;
4288 else
4289 fatal_internal("Unknown option %s.", ptr);
4290 break;
4293 /* This part associates a list of functions to control some */
4294 /* characteristics of the arguments of an option. */
4295 /* Each function will be called in order and must return 1 */
4296 /* to validate the arguments. */
4297 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4298 case constraints:
4300 char * value;
4301 constraint_t * cstr;
4302 int (*function)();
4304 /* The second argument must be a string. */
4305 /* """"""""""""""""""""""""""""""""""""" */
4306 ptr = va_arg(args, char *);
4308 if ((opt = locate_opt(ptr)) != NULL)
4310 typedef int fn(int, char **, char *);
4312 /* The third argument must be a function. */
4313 /* """""""""""""""""""""""""""""""""""""" */
4314 function = va_arg(args, fn *);
4316 cstr = xmalloc(sizeof(constraint_t));
4317 cstr->constraint = function;
4319 /* The fourth argument must be a string containing the argument of */
4320 /* The previous function separated by spaces or tabs. */
4321 /* Theses arguments will be passed to the previous function */
4322 /* max: 32 argument! */
4323 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4324 value = xstrdup(va_arg(args, char *));
4326 cstr->to_free = value;
4327 cstr->args = xcalloc(sizeof(char *), 32);
4328 cstr->nb_args = str2argv(value, cstr->args, 32);
4329 ll_append(opt->constraints_list, cstr);
4331 else
4332 fatal_internal("Unknown option %s.", ptr);
4333 break;
4336 /* This part allows to indicate that an option must be evaluated */
4337 /* after a list of other options. */
4338 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4339 case after:
4341 char * str;
4342 ll_t * list;
4344 /* The second argument must be a string. */
4345 /* """"""""""""""""""""""""""""""""""""" */
4346 ptr = va_arg(args, char *);
4348 if ((opt = locate_opt(ptr)) != NULL)
4350 char * end_str;
4351 char * opt_name;
4352 opt_t * opt_before;
4354 ptr = va_arg(args, char *);
4356 str = xstrdup(ptr);
4357 ltrim(str, " \t");
4358 rtrim(str, " \t", 0);
4360 /* Feed the list of options to be evaluated after the given option. */
4361 /* This list will contain pointers to options. */
4362 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4363 opt_name = xstrtok_r(str, " \t,", &end_str);
4364 if (opt_name != NULL)
4366 if ((opt_before = locate_opt(opt_name)) != NULL)
4368 ll_append(opt->eval_before_list, opt_before);
4369 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4371 if ((opt_before = locate_opt(opt_name)) != NULL)
4372 ll_append(opt->eval_before_list, opt_before);
4373 else
4374 fatal_internal("Unknown option %s.", opt_name);
4377 else
4378 fatal_internal("Unknown option %s.", opt_name);
4380 else
4381 fatal_internal("Not enough options to be evaluated after %s.",
4382 opt->name);
4384 free(str);
4386 else
4387 fatal_internal("Unknown option %s.", ptr);
4389 break;
4392 /* This part allows to indicate that an option must be evaluated */
4393 /* before a list of other options. */
4394 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4395 case before:
4397 char * str;
4398 ll_t * list;
4400 /* The second argument must be a string. */
4401 /* """"""""""""""""""""""""""""""""""""" */
4402 ptr = va_arg(args, char *);
4404 if ((opt = locate_opt(ptr)) != NULL)
4406 char * end_str;
4407 char * opt_name;
4408 opt_t * opt_before;
4410 ptr = va_arg(args, char *);
4412 str = xstrdup(ptr);
4413 ltrim(str, " \t");
4414 rtrim(str, " \t", 0);
4416 /* Feed the list of options to be evaluated before the given option. */
4417 /* This list will contain pointers to options. */
4418 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4419 opt_name = xstrtok_r(str, " \t,", &end_str);
4420 if (opt_name != NULL)
4422 if ((opt_before = locate_opt(opt_name)) != NULL)
4424 ll_append(opt_before->eval_before_list, opt);
4425 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4427 if ((opt_before = locate_opt(opt_name)) != NULL)
4428 ll_append(opt_before->eval_before_list, opt);
4429 else
4430 fatal_internal("Unknown option %s.", opt_name);
4433 else
4434 fatal_internal("Unknown option %s.", opt_name);
4436 else
4437 fatal_internal("Not enough options to be evaluated before %s.",
4438 opt->name);
4440 free(str);
4442 else
4443 fatal_internal("Unknown option %s.", ptr);
4445 break;
4448 default:
4449 break;
4451 va_end(args);
4454 /* =============================================================== */
4455 /* This function provides a way to set the behaviour of a context. */
4456 /* =============================================================== */
4457 void
4458 ctxopt_add_ctx_settings(settings s, ...)
4460 ctx_t * ctx;
4462 va_list(args);
4463 va_start(args, s);
4465 switch (s)
4467 /* Add a set of mutually incompatible options in a context. */
4468 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4469 case incompatibilities:
4471 void * ptr;
4472 ll_t * list;
4473 size_t n;
4474 char * str;
4476 ptr = va_arg(args, char *);
4477 if ((ctx = locate_ctx(ptr)) != NULL)
4479 ptr = va_arg(args, char *);
4480 list = ctx->incomp_list;
4482 str = xstrdup(ptr);
4483 ltrim(str, " \t");
4484 rtrim(str, " \t", 0);
4486 n = strcspn(str, " \t");
4487 if (n > 0 && n < strlen(str))
4488 ll_append(list, str);
4489 else
4490 fatal_internal(
4491 "Not enough incompatible options in the string: \"%s\".", str);
4493 else
4494 fatal_internal("Unknown context %s.", ptr);
4495 break;
4498 /* Add functions which will be called when */
4499 /* entering and exiting a context. */
4500 /* """"""""""""""""""""""""""""""""""""""" */
4501 case actions:
4503 void * ptr;
4504 void * data;
4505 int (*function)();
4506 int nb_data = 0;
4508 ptr = va_arg(args, char *);
4509 if ((ctx = locate_ctx(ptr)) != NULL)
4511 typedef int fn(char *, direction, char *, int, void **);
4513 function = va_arg(args, fn *);
4514 ctx->action = function;
4516 while ((data = va_arg(args, void *)) != NULL)
4518 nb_data++;
4519 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4520 ctx->data[nb_data - 1] = data;
4522 ctx->nb_data = nb_data;
4524 else
4525 fatal_internal("Unknown context %s.", ptr);
4526 break;
4529 default:
4530 break;
4532 va_end(args);