Code cleanup
[ctxopt.git] / ctxopt.c
blob2c500b94b05ea980188a2d48c20ff30cad293024
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 fprintf(stderr,
362 "The parameter %s takes no arguments "
363 "or has too many arguments.\n",
364 cur_state->cur_opt_par_name);
365 break;
367 case CTXOPTMISARG:
368 if (cur_state->pre_opt_par_name != NULL)
369 fprintf(stderr, "%s requires argument(s).\n",
370 cur_state->pre_opt_par_name);
371 else
372 fprintf(stderr, "%s requires argument(s).\n",
373 cur_state->cur_opt_par_name);
374 break;
376 case CTXOPTDUPOPT:
377 if (cur_state->pre_opt_par_name != NULL)
378 fprintf(stderr,
379 "The parameter %s can only appear once in the context "
380 "introduced by %s.\n",
381 cur_state->cur_opt_par_name, cur_state->ctx_par_name);
382 else
383 fprintf(stderr,
384 "The parameter %s can only appear once "
385 "in the main context.\n",
386 cur_state->cur_opt_par_name);
387 break;
389 case CTXOPTUNKPAR:
390 fprintf(stderr, "Unknown parameter %s.\n", cur_state->cur_opt_par_name);
391 break;
393 case CTXOPTINCOPT:
394 fprintf(stderr, "The parameter %s is incompatible with %s.\n",
395 cur_state->cur_opt_par_name, errmsg);
396 break;
398 case CTXOPTCTEOPT:
399 if (cur_state->ctx_par_name)
400 fprintf(stderr,
401 "The parameter %s must appear exactly %d times "
402 "in the context introduced by %s.\n",
403 cur_state->cur_opt_par_name, cur_state->opts_count,
404 cur_state->ctx_par_name);
405 else
406 fprintf(stderr,
407 "The parameter %s must appear exactly %d times "
408 "in the main context.\n",
409 cur_state->cur_opt_par_name, cur_state->opts_count);
410 break;
412 case CTXOPTCTLOPT:
413 if (cur_state->ctx_par_name)
414 fprintf(stderr,
415 "The parameter %s must appear less than %d times "
416 "in the context introduced by %s.\n",
417 cur_state->cur_opt_par_name, cur_state->opts_count,
418 cur_state->ctx_par_name);
419 else
420 fprintf(stderr,
421 "The parameter %s must appear less than %d times "
422 "in the main context.\n",
423 cur_state->cur_opt_par_name, cur_state->opts_count);
424 break;
426 case CTXOPTCTGOPT:
427 if (cur_state->ctx_par_name)
428 fprintf(stderr,
429 "The parameter %s must appear more than %d times "
430 "in the context introduced by %s.\n",
431 cur_state->cur_opt_par_name, cur_state->opts_count,
432 cur_state->ctx_par_name);
433 else
434 fprintf(stderr,
435 "The parameter %s must appear more than %d times "
436 "in the main context.\n",
437 cur_state->cur_opt_par_name, cur_state->opts_count);
438 break;
440 case CTXOPTCTEARG:
441 fprintf(stderr, "The parameter %s must have exactly %d arguments.\n",
442 cur_state->cur_opt_par_name, cur_state->opt_args_count);
443 break;
445 case CTXOPTCTLARG:
446 fprintf(stderr, "The parameter %s must have less than %d arguments.\n",
447 cur_state->cur_opt_par_name, cur_state->opt_args_count);
448 break;
450 case CTXOPTCTGARG:
451 fprintf(stderr, "The parameter %s must have more than %d arguments.\n",
452 cur_state->cur_opt_par_name, cur_state->opt_args_count);
453 break;
455 case CTXOPTERRSIZ:
456 break;
460 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
462 exit(e); /* Exit with the error id e as return code. */
465 /* ********************************* */
466 /* Memory management implementation. */
467 /* ********************************* */
469 /* ================== */
470 /* Customized malloc. */
471 /* ================== */
472 static void *
473 xmalloc(size_t size)
475 void * allocated;
476 size_t real_size;
478 real_size = (size > 0) ? size : 1;
479 allocated = malloc(real_size);
480 if (allocated == NULL)
481 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
482 (unsigned long int)size);
484 return allocated;
487 /* ================== */
488 /* Customized calloc. */
489 /* ================== */
490 static void *
491 xcalloc(size_t n, size_t size)
493 void * allocated;
495 n = (n > 0) ? n : 1;
496 size = (size > 0) ? size : 1;
497 allocated = calloc(n, size);
498 if (allocated == NULL)
499 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
500 (unsigned long int)size);
502 return allocated;
505 /* =================== */
506 /* Customized realloc. */
507 /* =================== */
508 static void *
509 xrealloc(void * p, size_t size)
511 void * allocated;
513 allocated = realloc(p, size);
514 if (allocated == NULL && size > 0)
515 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
516 (unsigned long int)size);
518 return allocated;
521 /* ==================================== */
522 /* strdup implementation using xmalloc. */
523 /* ==================================== */
524 static char *
525 xstrdup(const char * p)
527 char * allocated;
529 allocated = xmalloc(strlen(p) + 1);
530 strcpy(allocated, p);
532 return allocated;
535 /* =================================================== */
536 /* strndup implementation using xmalloc. */
537 /* This version guarantees that there is a final '\0'. */
538 /* =================================================== */
539 static char *
540 xstrndup(const char * str, size_t len)
542 char * p;
544 p = memchr(str, '\0', len);
546 if (p)
547 len = p - str;
549 p = xmalloc(len + 1);
550 memcpy(p, str, len);
551 p[len] = '\0';
553 return p;
556 /* *************************** */
557 /* Linked list implementation. */
558 /* *************************** */
560 /* Linked list node structure. */
561 /* """"""""""""""""""""""""""" */
562 struct ll_node_s
564 void * data;
565 struct ll_node_s * next;
566 struct ll_node_s * prev;
569 /* Linked List structure. */
570 /* """""""""""""""""""""" */
571 struct ll_s
573 ll_node_t * head;
574 ll_node_t * tail;
575 long len;
578 /* ========================= */
579 /* Create a new linked list. */
580 /* ========================= */
581 static ll_t *
582 ll_new(void)
584 ll_t * ret = xmalloc(sizeof(ll_t));
585 ll_init(ret);
587 return ret;
590 /* =============================================== */
591 /* Free all the elements of a list (make it empty) */
592 /* NULL or a custom function may be used to free */
593 /* the sub components of the elements. */
594 /* =============================================== */
595 static void
596 ll_free(ll_t * const list, void (*clean)(void *))
598 if (list)
599 while (list->head)
601 /* Apply a custom cleaner if not NULL. */
602 /* """"""""""""""""""""""""""""""""""" */
603 if (clean)
604 clean(list->head->data);
606 ll_delete(list, list->head);
610 /* ==================================== */
611 /* Destroy a list and all its elements. */
612 /* ==================================== */
613 static void
614 ll_destroy(ll_t * list, void (*clean)(void *))
616 if (list)
618 ll_free(list, clean);
619 free(list);
623 /* ========================= */
624 /* Initialize a linked list. */
625 /* ========================= */
626 static void
627 ll_init(ll_t * list)
629 list->head = NULL;
630 list->tail = NULL;
631 list->len = 0;
634 /* ===================================================== */
635 /* Allocate the space for a new node in the linked list. */
636 /* ===================================================== */
637 static ll_node_t *
638 ll_new_node(void)
640 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
642 return ret;
645 /* ==================================================================== */
646 /* Append a new node filled with its data at the end of the linked list */
647 /* The user is responsible for the memory management of the data. */
648 /* ==================================================================== */
649 static void
650 ll_append(ll_t * const list, void * const data)
652 ll_node_t * node;
654 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
655 | uses xmalloc which does not return if there *
656 | is an allocation error. */
658 node->data = data;
659 node->next = NULL;
661 node->prev = list->tail;
662 if (list->tail)
663 list->tail->next = node;
664 else
665 list->head = node;
667 list->tail = node;
669 ++list->len;
672 /* ================================================================== */
673 /* Put a new node filled with its data at the beginning of the linked */
674 /* list. */
675 /* The user is responsible for the memory management of the data. */
676 /* ================================================================== */
677 static void
678 ll_prepend(ll_t * const list, void * const data)
680 ll_node_t * node;
682 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
683 | uses xmalloc which does not return if there *
684 | is an allocation error. */
686 node->data = data;
687 node->prev = NULL;
689 node->next = list->head;
690 if (list->head)
691 list->head->prev = node;
692 else
693 list->tail = node;
695 list->head = node;
697 ++list->len;
700 /* ======================================================== */
701 /* Insert a new node before the specified node in the list. */
702 /* ======================================================== */
703 static void
704 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
706 ll_node_t * new_node;
708 if (node->prev == NULL)
709 ll_prepend(list, data);
710 else
712 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
713 | uses xmalloc which does not return if there *
714 | is an allocation error. */
716 new_node->data = data;
717 new_node->next = node;
718 new_node->prev = node->prev;
719 node->prev->next = new_node;
720 node->prev = new_node;
722 ++list->len;
726 /* ======================================================= */
727 /* Insert a new node after the specified node in the list. */
728 /* ======================================================= */
729 static void
730 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
732 ll_node_t * new_node;
734 if (node->next == NULL)
735 ll_append(list, data);
736 else
738 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
739 | uses xmalloc which does not return if there *
740 | is an allocation error. */
742 new_node->data = data;
743 new_node->prev = node;
744 new_node->next = node->next;
745 node->next->prev = new_node;
746 node->next = new_node;
748 ++list->len;
752 /* ================================================================= */
753 /* Remove a node from a linked list */
754 /* The memory taken by the deleted node must be freed by the caller. */
755 /* ================================================================= */
756 static int
757 ll_delete(ll_t * const list, ll_node_t * node)
759 if (list->head == list->tail)
761 if (list->head != NULL)
762 list->head = list->tail = NULL;
763 else
764 return 0;
766 else if (node->prev == NULL)
768 list->head = node->next;
769 list->head->prev = NULL;
771 else if (node->next == NULL)
773 list->tail = node->prev;
774 list->tail->next = NULL;
776 else
778 node->next->prev = node->prev;
779 node->prev->next = node->next;
782 --list->len;
784 free(node);
786 return 1;
789 #if 0 /* Unused yet */
790 /* ======================================================================== */
791 /* Find a node in the list containing data. Return the node pointer or NULL */
792 /* if not found. */
793 /* A comparison function must be provided to compare a and b (strcmp like). */
794 /* ======================================================================== */
795 static ll_node_t *
796 ll_find(ll_t * const list, void * const data,
797 int (*cmpfunc)(const void * a, const void * b))
799 ll_node_t * node;
801 if (NULL == (node = list->head))
802 return NULL;
806 if (0 == cmpfunc(node->data, data))
807 return node;
808 } while (NULL != (node = node->next));
810 return NULL;
812 #endif
814 /* ==================================================================== */
815 /* Allocate and fill an array of strings from a list. */
816 /* WARNINGS: */
817 /* 1) The list node must contain strings (char *) */
818 /* 2) The strings in the resulting array MUST NOT be freed as the are */
819 /* NOT copied from the strings of the list. */
820 /* */
821 /* IN list : The list from which the array is generated */
822 /* IN start_node : The node of the list which will be the first node to */
823 /* consider to create the array */
824 /* OUT: count : The number of elements of the resulting array. */
825 /* OUT: array : The resulting array or NULL if the list is empty. */
826 /* RC : : The number of elements of the resulting array. */
827 /* ==================================================================== */
828 static int
829 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
831 int n = 0;
832 ll_node_t * node;
834 *count = 0;
836 node = start_node;
838 if (list == NULL || node == NULL)
840 *array = NULL;
842 return 0;
845 *array = xmalloc((list->len + 1) * sizeof(char *));
846 while (node != NULL)
848 (*array)[n++] = (char *)(node->data);
849 (*count)++;
851 node = node->next;
854 (*array)[*count] = NULL;
856 return *count;
859 /* ******************************************************************* */
860 /* BST (search.h compatible) implementation. */
861 /* */
862 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
863 /* the AT&T man page says. */
864 /* */
865 /* Written by reading the System V Interface Definition, not the code. */
866 /* */
867 /* Totally public domain. */
868 /* ******************************************************************* */
870 struct bst_s
872 void * key;
873 struct bst_s * llink;
874 struct bst_s * rlink;
877 #if 0 /* Unused yet */
878 /* =========================== */
879 /* Delete node with given key. */
880 /* =========================== */
881 static void *
882 bst_delete(const void * vkey, void ** vrootp,
883 int (*compar)(const void *, const void *))
885 bst_t ** rootp = (bst_t **)vrootp;
886 bst_t * p, *q, *r;
887 int cmp;
889 if (rootp == NULL || (p = *rootp) == NULL)
890 return NULL;
892 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
894 p = *rootp;
895 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
896 : &(*rootp)->rlink; /* follow rlink branch */
897 if (*rootp == NULL)
898 return NULL; /* key not found */
900 r = (*rootp)->rlink; /* D1: */
901 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
902 q = r;
903 else if (r != NULL)
904 { /* Right link is NULL? */
905 if (r->llink == NULL)
906 { /* D2: Find successor */
907 r->llink = q;
908 q = r;
910 else
911 { /* D3: Find NULL link */
912 for (q = r->llink; q->llink != NULL; q = r->llink)
913 r = q;
914 r->llink = q->rlink;
915 q->llink = (*rootp)->llink;
916 q->rlink = (*rootp)->rlink;
919 if (p != *rootp)
920 free(*rootp); /* D4: Free node */
921 *rootp = q; /* link parent to new node */
922 return p;
924 #endif
926 /* ===================================================================== */
927 /* Destroy a tree. */
928 /* The clean function pointer can be NULL, in this case the node content */
929 /* is not freed. */
930 /* ===================================================================== */
931 static void
932 bst_destroy(void * vrootp, void (*clean)(void *))
934 bst_t * root = (bst_t *)vrootp;
936 if (root == NULL)
937 return;
939 bst_destroy(root->llink, clean);
940 bst_destroy(root->rlink, clean);
942 if (clean)
943 clean((void *)root->key);
945 free(root);
948 /* ========================= */
949 /* Find a node, or return 0. */
950 /* ========================= */
951 static void *
952 bst_find(const void * vkey, void * const * vrootp,
953 int (*compar)(const void *, const void *))
955 bst_t * const * rootp = (bst_t * const *)vrootp;
957 if (rootp == NULL)
958 return NULL;
960 while (*rootp != NULL)
961 { /* T1: */
962 int r;
964 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
965 return *rootp; /* key found */
966 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
967 : &(*rootp)->rlink; /* T4: follow right branch */
969 return NULL;
972 /* ======================================= */
973 /* Find or inserts datum into search tree. */
974 /* ======================================= */
975 static void *
976 bst_search(void * vkey, void ** vrootp,
977 int (*compar)(const void *, const void *))
979 bst_t * q;
980 bst_t ** rootp = (bst_t **)vrootp;
982 if (rootp == NULL)
983 return NULL;
985 while (*rootp != NULL)
986 { /* Knuth's T1: */
987 int r;
989 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
990 return *rootp; /* we found it! */
992 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
993 : &(*rootp)->rlink; /* T4: follow right branch */
996 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
997 if (q != 0)
998 { /* make new node */
999 *rootp = q; /* link new node to old */
1000 q->key = vkey; /* initialize new node */
1001 q->llink = q->rlink = NULL;
1003 return q;
1006 /* ========================= */
1007 /* Walk the nodes of a tree. */
1008 /* ========================= */
1009 static void
1010 bst_walk_recurse(const bst_t * root,
1011 void (*action)(const void *, walk_order_e, int), int level)
1013 if (root->llink == NULL && root->rlink == NULL)
1014 (*action)(root, leaf, level);
1015 else
1017 (*action)(root, preorder, level);
1018 if (root->llink != NULL)
1019 bst_walk_recurse(root->llink, action, level + 1);
1020 (*action)(root, postorder, level);
1021 if (root->rlink != NULL)
1022 bst_walk_recurse(root->rlink, action, level + 1);
1023 (*action)(root, endorder, level);
1027 static void
1028 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
1030 if (vroot != NULL && action != NULL)
1031 bst_walk_recurse(vroot, action, 0);
1034 /* ************************ */
1035 /* Various implementations. */
1036 /* ************************ */
1038 /* ======================== */
1039 /* Trim leading characters. */
1040 /* ======================== */
1041 static void
1042 ltrim(char * str, const char * trim_str)
1044 size_t len = strlen(str);
1045 size_t begin = strspn(str, trim_str);
1046 size_t i;
1048 if (begin > 0)
1049 for (i = begin; i <= len; ++i)
1050 str[i - begin] = str[i];
1053 /* ================================================= */
1054 /* Trim trailing characters. */
1055 /* The resulting string will have at least min bytes */
1056 /* even if trailing spaces remain. */
1057 /* ================================================= */
1058 static void
1059 rtrim(char * str, const char * trim_str, size_t min)
1061 size_t len = strlen(str);
1062 while (len > min && strchr(trim_str, str[len - 1]))
1063 str[--len] = '\0';
1066 /* ================================================== */
1067 /* Count the number of occurrences of the character c */
1068 /* in the string str. */
1069 /* The str pointer is assumed to be not NULL. */
1070 /* ================================================== */
1071 static int
1072 strchrcount(char * str, char c)
1074 int count = 0;
1076 while (*str)
1077 if (*str++ == c)
1078 count++;
1080 return count;
1083 /* =============================================== */
1084 /* Is the string str2 a prefix of the string str1? */
1085 /* =============================================== */
1086 static int
1087 strpref(char * str1, char * str2)
1089 while (*str1 != '\0' && *str1 == *str2)
1091 str1++;
1092 str2++;
1095 return *str2 == '\0';
1098 /* ========================== */
1099 /* Like strcmp ignoring case. */
1100 /* ========================== */
1101 static int
1102 stricmp(const char * s1, const char * s2)
1104 while (tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1106 if (*s1 == '\0')
1107 return 0;
1109 s1++;
1110 s2++;
1113 return (int)tolower((unsigned char)*s1) - (int)tolower((unsigned char)*s2);
1116 /* ======================================================================== */
1117 /* Strings concatenation with dynamic memory allocation. */
1118 /* IN : a variable number of char * arguments with NULL terminating */
1119 /* the sequence. */
1120 /* The first one must have been dynamically allocated and is mandatory */
1121 /* */
1122 /* Returns a new allocated string containing the concatenation of all */
1123 /* the arguments. It is the caller's responsibility to free the resulting */
1124 /* string. */
1125 /* ======================================================================== */
1126 static char *
1127 strappend(char * str, ...)
1129 size_t l;
1130 va_list args;
1131 char * s;
1133 l = 1 + strlen(str);
1134 va_start(args, str);
1136 s = va_arg(args, char *);
1138 while (s)
1140 l += strlen(s);
1141 s = va_arg(args, char *);
1144 va_end(args);
1146 str = xrealloc(str, l);
1148 va_start(args, str);
1149 s = va_arg(args, char *);
1151 while (s)
1153 strcat(str, s);
1154 s = va_arg(args, char *);
1156 va_end(args);
1158 return str;
1161 /* ====================================================================== */
1162 /* Public domain strtok_r() by Charlie Gordon. */
1163 /* from comp.lang.c 9/14/2007 */
1164 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1165 /* */
1166 /* (Declaration that it's public domain): */
1167 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1168 /* */
1169 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1170 /* *end == NULL */
1171 /* ====================================================================== */
1172 static char *
1173 xstrtok_r(char * str, const char * delim, char ** end)
1175 char * ret;
1177 if (str == NULL)
1178 str = *end;
1180 if (str == NULL)
1181 return NULL;
1183 str += strspn(str, delim);
1185 if (*str == '\0')
1186 return NULL;
1188 ret = str;
1190 str += strcspn(str, delim);
1192 if (*str)
1193 *str++ = '\0';
1195 *end = str;
1197 return ret;
1200 /* ===================================================================== */
1201 /* Put the first word of str, truncated to len characters, in buf. */
1202 /* Return a pointer in str pointing just after the word. */
1203 /* buf must have been pre-allocated to accept at least len+1 characters. */
1204 /* Note that buf can contains a sting full of spaces is str was not */
1205 /* trimmed before the call. */
1206 /* ===================================================================== */
1207 char *
1208 get_word(char * str, char * buf, size_t len)
1210 char * s = str;
1212 /* Skip spaces. */
1213 /* """""""""""" */
1214 while (*s && isspace(*s))
1215 s++;
1217 /* Set the new string start. */
1218 /* """"""""""""""""""""""""" */
1219 str = s;
1221 /* Get the word. */
1222 /*"""""""""""""" */
1223 while (*s && !isspace(*s) && s - str < len)
1224 s++;
1226 strncpy(buf, str, s - str);
1227 buf[s - str] = 0;
1229 return s;
1232 /* ==================================================================== */
1233 /* Return 1 is value is "1" or "yes" (ignoring case). */
1234 /* Return 0 is value is "0" or "no" (ignoring case). */
1235 /* If value has another value, then set invalid to 1 and also return 0 */
1236 /* invalid is set to 0i in all the other cases. */
1237 /* ==================================================================== */
1238 static int
1239 eval_yes(char * value, int * invalid)
1241 *invalid = 0;
1243 if (strcmp(value, "1") == 0 || stricmp(value, "yes") == 0)
1244 return 1;
1245 else if (strcmp(value, "0") != 0 && stricmp(value, "no") != 0)
1246 *invalid = 1;
1248 return 0;
1251 /* =========================================================== */
1252 /* Fill an array of strings from the words composing a string. */
1253 /* */
1254 /* str: initial string which will be altered. */
1255 /* args: array of pointers to the start of the words in str. */
1256 /* max: maximum number of words used before giving up. */
1257 /* return: the number of words (<=max). */
1258 /* =========================================================== */
1259 static int
1260 str2argv(char * str, char ** args, int max)
1262 int nb_args = 0;
1264 while (*str)
1266 if (nb_args >= max)
1267 return nb_args;
1269 while (*str == ' ' || *str == '\t')
1270 *(str++) = '\0';
1272 if (!*str)
1273 return nb_args;
1275 args[nb_args] = str;
1276 nb_args++;
1278 while (*str && (*str != ' ') && (*str != '\t'))
1279 str++;
1282 return nb_args;
1285 /* ********************** */
1286 /* ctxopt implementation. */
1287 /* ********************** */
1289 static int ctxopt_initialized = 0; /* cap_init has not yet been called */
1291 /* Flags structure initialized by ctxopt_init. */
1292 /* """"""""""""""""""""""""""""""""""""""""""" */
1293 struct flags_s
1295 int stop_if_non_option;
1296 int allow_abbreviations;
1299 /* Context structure. */
1300 /* """""""""""""""""" */
1301 struct ctx_s
1303 char * name;
1304 ll_t * opt_list; /* list of options allowed in this context. */
1305 ll_t * incomp_list; /* list of strings containing incompatible names *
1306 | of options separated by spaces or tabs. */
1307 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1308 void ** ctx_data);
1309 void * par_bst;
1310 int nb_data;
1311 void ** data;
1314 /* https://textik.com/#488ce3649b6c60f5 */
1315 /* */
1316 /* +--------------+ */
1317 /* |first_ctx_inst| */
1318 /* +---+----------+ */
1319 /* | */
1320 /* +--v-----+ +--------+ +--------+ +-----+ */
1321 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1322 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1323 /* | | | | | */
1324 /* | | +-v------+ | | */
1325 /* | +--+ctx_inst<-----------+ | */
1326 /* | +-+------+ | */
1327 /* | | | */
1328 /* | +-v------+ | */
1329 /* +------+ctx_inst<--------------------------+ */
1330 /* +-+------+ */
1331 /* | */
1332 /* +-v---+ */
1333 /* | ... | */
1334 /* +-----+ */
1336 /* Option structure. */
1337 /* """"""""""""""""" */
1338 struct opt_s
1340 char * name; /* option name. */
1341 char * next_ctx; /* new context this option may lead to */
1342 ll_t * ctx_list; /* list of contexts allowing this option. */
1343 char * params; /* string containing all the parameters of *
1344 | the option. */
1346 void (*action)( /* The option associated action. */
1347 char * ctx_name, /* context name. */
1348 char * opt_name, /* option name. */
1349 char * par, /* option parameter. */
1350 int nb_args, /* number of arguments. */
1351 char ** args, /* option arguments. */
1352 int nb_opt_data, /* number of option data pointers. */
1353 void ** opt_data, /* option data pointers. */
1354 int nb_ctx_data, /* nb of current context data ptrs. */
1355 void ** ctx_data /* current context data pointers. */
1358 int nb_data; /* number of the data pointers passed as argument to action. */
1359 void ** data; /* array of data pointers passed as argument to action. */
1361 int args; /* 1 if this option takes arguments else 0. */
1362 int optional; /* 1 if the option is optional, else 0. */
1363 int multiple; /* 1 if the option can appear more than one time in a *
1364 | context, else 0. */
1366 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1367 int occurrences; /* Number of option occurrences in a context. */
1368 char opt_count_oper; /* <, = or > */
1369 unsigned opt_count_mark; /* Value to be compared to with opt_count_oper. */
1371 char * arg; /* symbolic text after # describing the option argument. */
1373 int optional_args; /* 1 of option is optional else 0. */
1374 int multiple_args; /* 1 is option can appear more than once in a context *
1375 | instance. */
1377 int opt_args_count_matter; /* 1 if count is rescticted, else 0. */
1378 char opt_args_count_oper; /* <, = or > */
1379 unsigned opt_args_count_mark; /* Value to be compared to with *
1380 | opt_count_oper. */
1382 int eval_first; /* 1 if this option must be evaluated before the options *
1383 | without this mark. */
1385 ll_t * constraints_list; /* List of constraint check functions pointers. */
1388 /* Context instance structure. */
1389 /* """"""""""""""""""""""""""" */
1390 struct ctx_inst_s
1392 ctx_t * ctx; /* the context whose this is an instance of */
1393 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1394 | creation of this ctx_inst structure. */
1395 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1396 | instance of this structure. */
1397 ll_t * incomp_bst_list; /* list of seen_opt_t BST. */
1398 void * seen_opt_bst; /* tree of seen_opt_t. */
1399 ll_t * opt_inst_list; /* The list of option instances in this *
1400 | context instance. */
1401 char * par_name; /* parameter which created this instance. */
1404 /* Option instance structure. */
1405 /* """""""""""""""""""""""""" */
1406 struct opt_inst_s
1408 opt_t * opt; /* The option this is an instance of. */
1409 char * opt_name; /* The option which led to this creation. */
1410 char * par; /* The parameter which led to this creation. */
1411 ll_t * values_list; /* The list of arguments of this option. */
1412 ctx_inst_t * next_ctx_inst; /* The new context instance this option. *
1413 | instance may create. */
1416 /* Structure used to check if an option has bee seen or not */
1417 /* in a context instance. */
1418 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1419 struct seen_opt_s
1421 opt_t * opt; /* The concerned option. */
1422 char * par; /* Parameter which led to the making of this structure. */
1423 int seen; /* 1 if seen in the context instances, else 0. */
1426 /* Parameter structure which links a parameter to the option it belongs to. */
1427 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1428 struct par_s
1430 char * name; /* Parameter name (with the leading -). */
1431 opt_t * opt; /* Attached option. */
1434 /* Constraint structure. */
1435 /* """"""""""""""""""""" */
1436 struct constraint_s
1438 int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1439 int nb_args;
1440 char ** args;
1441 char * to_free; /* pointer to the original string in which the array in *
1442 | args points to. This poinnter is kept there to allow *
1443 | it to be freed. */
1446 state_t * cur_state = NULL; /* Current analysis state. */
1447 static ll_t * cmdline_list; /* List of interpreted CLI words *
1448 | serves as the basis for the *
1449 | analysis of the parameters. */
1450 static ctx_t * main_ctx = NULL; /* initial context. */
1451 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1452 | instance which holds the *
1453 | options instances. */
1454 static ll_t * ctx_inst_list = NULL; /* List of the context instances. */
1456 static flags_t flags = { 0, 1 };
1458 /* ======================================================= */
1459 /* Parse a string for the next matching token. */
1460 /* */
1461 /* s: string to parse. */
1462 /* token: pre_allocated array of max tok_len characters. */
1463 /* pattern: scanf type pattern token must match. */
1464 /* pos: number of characters successfully parsed in s. */
1465 /* */
1466 /* Returns: a pointer to the first unread character or */
1467 /* to he terminating \0. */
1468 /* ======================================================= */
1469 static char *
1470 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1472 char * full_pattern;
1473 char len[3];
1474 int n;
1476 *pos = 0;
1478 n = snprintf(len, 3, "%zu", tok_len);
1479 if (n < 0)
1480 return NULL;
1482 full_pattern = xmalloc(strlen(pattern) + n + 4);
1484 strcpy(full_pattern, "%");
1485 strcat(full_pattern, len);
1486 strcat(full_pattern, pattern);
1487 strcat(full_pattern, "%n");
1489 n = sscanf(s, full_pattern, token, pos);
1491 free(full_pattern);
1493 if (n != 1)
1494 return NULL;
1496 return s + *pos;
1499 /* ****************************************** */
1500 /* Various comparison and deletion functions. */
1501 /* ****************************************** */
1503 static int
1504 ctx_compare(const void * c1, const void * c2)
1506 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1509 /* =========================== */
1510 /* Free a context_bst element. */
1511 /* =========================== */
1512 static void
1513 ctx_free(void * c)
1515 ctx_t * ctx = c;
1517 free(ctx->name);
1518 free(ctx->data);
1520 ll_destroy(ctx->opt_list, NULL);
1521 ll_destroy(ctx->incomp_list, free);
1522 bst_destroy(ctx->par_bst, par_free);
1524 free(c);
1527 /* ============================= */
1528 /* Free a ctx_inst_list element. */
1529 /* ============================= */
1530 static void
1531 ctx_inst_free(void * ci)
1533 ctx_inst_t * ctx_inst = ci;
1535 free(ctx_inst->par_name);
1536 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1537 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1538 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1540 free(ci);
1543 /* ============================= */
1544 /* Free a opt_inst_list element. */
1545 /* ============================= */
1546 static void
1547 opt_inst_free(void * oi)
1549 opt_inst_t * opt_inst = oi;
1551 ll_destroy(opt_inst->values_list, NULL);
1553 free(oi);
1556 /* ================================== */
1557 /* Compare two seen_opt_bst elements. */
1558 /* ================================== */
1559 static int
1560 seen_opt_compare(const void * so1, const void * so2)
1562 opt_t *o1, *o2;
1564 o1 = ((seen_opt_t *)so1)->opt;
1565 o2 = ((seen_opt_t *)so2)->opt;
1567 return strcmp(o1->name, o2->name);
1570 /* ============================ */
1571 /* Free a seen_opt_bst element. */
1572 /* ============================ */
1573 void
1574 seen_opt_free(void * so)
1576 seen_opt_t * seen_opt = so;
1578 free(seen_opt->par);
1580 free(so);
1583 /* =========================== */
1584 /* Free an incomp_bst element. */
1585 /* =========================== */
1586 static void
1587 incomp_bst_free(void * b)
1589 bst_t * bst = b;
1591 bst_destroy(bst, NULL);
1594 /* ================================= */
1595 /* Compare two options_bst elements. */
1596 /* ================================= */
1597 static int
1598 opt_compare(const void * o1, const void * o2)
1600 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1603 /* ============================= */
1604 /* Free an options_bst elements. */
1605 /* ============================= */
1606 void
1607 opt_free(void * o)
1609 opt_t * opt = o;
1611 free(opt->name);
1612 free(opt->next_ctx);
1613 free(opt->params);
1614 free(opt->arg);
1615 free(opt->data);
1617 ll_destroy(opt->ctx_list, NULL);
1618 ll_destroy(opt->constraints_list, constraint_free);
1620 free(o);
1623 /* ============================= */
1624 /* Compare two par_bst elements. */
1625 /* ============================= */
1626 static int
1627 par_compare(const void * a1, const void * a2)
1629 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1632 /* ======================= */
1633 /* Free a par_bst element. */
1634 /* ======================= */
1635 static void
1636 par_free(void * p)
1638 par_t * par = p;
1640 free(par->name);
1642 free(p);
1645 /* ================================ */
1646 /* Free a constraints_list element. */
1647 /* ================================ */
1648 static void
1649 constraint_free(void * c)
1651 constraint_t * cstr = c;
1653 free(cstr->args);
1654 free(cstr->to_free);
1656 free(c);
1659 /* ******************************************************************** */
1660 /* Helper functions to locate contexts, options and parameters in a BST */
1661 /* by their names. */
1662 /* ******************************************************************** */
1664 static ctx_t *
1665 locate_ctx(char * name)
1667 bst_t * node;
1668 ctx_t ctx = { 0 };
1670 ctx.name = name;
1672 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1673 return NULL;
1674 else
1675 return node->key;
1678 static opt_t *
1679 locate_opt(char * name)
1681 bst_t * node;
1682 opt_t opt = { 0 };
1684 opt.name = name;
1686 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1687 return NULL;
1688 else
1689 return node->key;
1692 static par_t *
1693 locate_par(char * name, ctx_t * ctx)
1695 bst_t * node;
1696 par_t par = { 0 };
1697 void * bst = ctx->par_bst;
1699 par.name = name;
1701 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1702 return NULL;
1703 else
1704 return node->key;
1707 /* =================================================================== */
1708 /* Utility function to format and print the options present in a list. */
1709 /* */
1710 /* IN list : a list of options. */
1711 /* OUT has_* : a set of flags which will determine the content of the */
1712 /* explanation given after the formatted printing of the */
1713 /* options. */
1714 /* =================================================================== */
1715 static void
1716 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1717 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1718 int * has_early_eval)
1720 ll_node_t * node = list->head;
1721 opt_t * opt;
1722 char * line;
1723 char * option;
1725 line = xstrdup(" ");
1727 while (node != NULL)
1729 option = xstrdup("");
1730 opt = node->data;
1732 if (opt->optional)
1734 option = strappend(option, "[", NULL);
1735 *has_optional = 1;
1738 if (opt->eval_first)
1740 option = strappend(option, "*", NULL);
1741 *has_early_eval = 1;
1744 option = strappend(option, opt->params, NULL);
1746 if (opt->next_ctx != NULL)
1748 option = strappend(option, ">", opt->next_ctx, NULL);
1749 *has_ctx_change = 1;
1752 if (opt->multiple)
1754 if (opt->opt_count_oper != '\0')
1756 char m[4];
1757 char o[2];
1758 o[0] = opt->opt_count_oper;
1759 o[1] = '\0';
1760 snprintf(m, 3, "%u", opt->opt_count_mark);
1761 option = strappend(option, "...", o, m, NULL);
1762 *has_rule = 1;
1764 else
1765 option = strappend(option, "...", NULL);
1767 *has_ellipsis = 1;
1770 if (opt->args)
1772 if (*(opt->arg) == '#')
1773 *has_generic_arg = 1;
1775 option = strappend(option, " ", NULL);
1777 if (opt->optional_args)
1779 option = strappend(option, "[", opt->arg, NULL);
1780 *has_optional = 1;
1782 else
1783 option = strappend(option, opt->arg, NULL);
1785 if (opt->multiple_args)
1787 if (opt->opt_args_count_oper != '\0')
1789 char m[4];
1790 char o[2];
1791 o[0] = opt->opt_args_count_oper;
1792 o[1] = '\0';
1793 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1794 option = strappend(option, "...", o, m, NULL);
1795 *has_rule = 1;
1797 else
1798 option = strappend(option, "...", NULL);
1800 *has_ellipsis = 1;
1802 if (opt->optional_args)
1803 option = strappend(option, "]", NULL);
1805 if (opt->optional)
1806 option = strappend(option, "]", NULL);
1808 if (strlen(line) + 1 + strlen(option) < 80)
1809 line = strappend(line, option, " ", NULL);
1810 else
1812 printf("%s\n", line);
1813 line[2] = '\0';
1814 line = strappend(line, option, " ", NULL);
1817 free(option);
1819 node = node->next;
1822 printf("%s\n", line);
1824 free(line);
1827 /* ==================================================== */
1828 /* Explain the special syntactic symbols present in the */
1829 /* generated usage messages. */
1830 /* ==================================================== */
1831 static void
1832 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1833 int has_optional, int has_ellipsis, int has_rule)
1835 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1836 || has_ellipsis || has_rule)
1838 printf("\nExplanation of the syntax used above:\n");
1839 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1840 "must be entered.\n");
1841 printf("The following is just there to explain the other symbols "
1842 "displayed.\n\n");
1844 if (has_early_eval)
1845 printf("* : the parameters for this option will be "
1846 "evaluated first.\n");
1847 if (has_ctx_change)
1848 printf(
1849 "> : The context after this symbol will become the next "
1850 "default one.\n");
1851 if (has_generic_arg)
1852 printf("#tag : argument tag giving a clue to its meaning.\n");
1853 if (has_optional)
1854 printf(
1855 "[...] : the object between square brackets is optional.\n");
1856 if (has_ellipsis)
1857 printf("... : several occurrences of the previous object "
1858 "are possible.\n");
1859 if (has_rule)
1860 printf("[<|=|>]number: rules constraining the number of "
1861 "parameters/arguments.\n");
1865 /* ************************************************************ */
1866 /* Various utilities and callback functions called when walking */
1867 /* through a BST. */
1868 /* ************************************************************ */
1870 static void
1871 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1873 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1875 if (kind == postorder || kind == leaf)
1877 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1879 user_rc = 1;
1880 user_string = strappend(user_string, seen_opt->opt->params, " ", NULL);
1885 static void
1886 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1888 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1890 if (kind == postorder || kind == leaf)
1891 if (seen_opt->seen == 1)
1893 user_rc = 1;
1894 user_object = seen_opt->par;
1898 static void
1899 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
1901 ctx_t * ctx = main_ctx;
1902 ctx_t * cur_ctx = ((bst_t *)node)->key;
1904 ll_t * list;
1906 int has_optional = 0;
1907 int has_ellipsis = 0;
1908 int has_rule = 0;
1909 int has_generic_arg = 0;
1910 int has_ctx_change = 0;
1911 int has_early_eval = 0;
1913 if (kind == postorder || kind == leaf)
1914 if (strcmp(ctx->name, cur_ctx->name) != 0)
1916 list = cur_ctx->opt_list;
1918 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
1919 print_options(list, &has_optional, &has_ellipsis, &has_rule,
1920 &has_generic_arg, &has_ctx_change, &has_early_eval);
1924 static void
1925 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
1927 opt_t * opt = ((bst_t *)node)->key;
1929 if (kind == postorder || kind == leaf)
1931 if (opt->params == NULL) /* opt must have associated parameters. */
1932 fatal_internal("Option %s has no registered parameter.\n", opt->name);
1934 if (opt->action == NULL) /* opt must have an action. */
1935 fatal_internal("Option %s has no registered action.\n", opt->name);
1939 static void
1940 bst_match_par_cb(const void * node, walk_order_e kind, int level)
1942 ctx_t * ctx = ((bst_t *)node)->key;
1944 if (kind == postorder || kind == leaf)
1946 char * str = xstrdup(user_string);
1948 while (*str != '\0')
1950 if (locate_par(str, ctx) != NULL)
1952 user_string2 = strappend(user_string2, " ", ctx->name, NULL);
1953 break;
1955 str[strlen(str) - 1] = '\0';
1957 free(str);
1961 static void
1962 match_prefix_cb(const void * node, walk_order_e kind, int level)
1964 par_t * par = ((bst_t *)node)->key;
1966 if (kind == postorder || kind == leaf)
1967 if (strpref(par->name, (char *)user_object))
1969 user_rc++;
1970 user_string = strappend(user_string, par->name, " ", NULL);
1974 /* ====================================================================== */
1975 /* A parameter may not be separated from its first option by spaces, in */
1976 /* this case this function looks for a valid flag as a prefix and splits */
1977 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
1978 /* option). */
1979 /* */
1980 /* IN word : the word to be checked. */
1981 /* IN ctx : the context in which the flag indexed by the word is to be */
1982 /* checked. */
1983 /* OUT pos : the offset in word pointing just after the matching prefix. */
1984 /* OUT opt : a pointer to the option associated with the new parameter */
1985 /* or NULL if none is found. */
1986 /* */
1987 /* The returned pointer must be freed by the caller. */
1988 /* ====================================================================== */
1989 static char *
1990 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
1992 char * new = NULL;
1993 int len;
1994 par_t * par;
1995 par_t tmp_par = { 0 };
1997 len = strlen(word);
1999 if (len > 2)
2001 new = xstrdup(word);
2005 new[--len] = '\0';
2006 tmp_par.name = new;
2007 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2009 if (par != NULL)
2011 *pos = len;
2012 *opt = par->opt;
2014 else
2016 free(new);
2017 new = NULL;
2020 else
2021 *pos = 0;
2023 return new;
2026 /* ============================================================= */
2027 /* If par_name is an unique abbreviation of an exiting parameter */
2028 /* in the context ctx, then return this parameter. */
2029 /* ============================================================= */
2030 static char *
2031 abbrev_expand(char * par_name, ctx_t * ctx)
2033 user_object = par_name;
2034 user_rc = 0;
2036 *user_string = '\0';
2037 bst_walk(ctx->par_bst, match_prefix_cb);
2038 rtrim(user_string, " ", 0);
2040 /* The previous bst_walk has built a string of blank separated parameters */
2041 /* all having par_name as prefix. This string is put in the user_string */
2042 /* exchange zone. The number of these words in put in user_rc. */
2043 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2044 if (user_rc == 1) /* The number of matching abbreviations. */
2045 return xstrdup(user_string);
2046 else /* There is at least tho defined parameters starting with par_name. */
2048 char * s, *first_s;
2049 par_t * par;
2050 opt_t * opt;
2051 int opt_count = 0;
2052 void * tmp_opt_bst = NULL;
2054 /* Find all the options corresponding to these words and store them */
2055 /* without duplication in a temporary BST. Only their resulting count */
2056 /* matters. */
2057 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2058 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2059 | the first word. */
2060 while (s != NULL)
2062 par = locate_par(s, ctx);
2063 opt = par->opt;
2065 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2067 /* This option as not already been seen */
2068 /* store it and increase the seen counter. */
2069 /* """"""""""""""""""""""""""""""""""""""" */
2070 bst_search(opt, &tmp_opt_bst, opt_compare);
2071 opt_count++;
2073 s = strtok(NULL, " ");
2076 /* Clean the temporary BST without removing the pointer */
2077 /* to the real options. */
2078 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2079 if (tmp_opt_bst != NULL)
2080 bst_destroy(tmp_opt_bst, NULL);
2082 if (opt_count == 1)
2083 /* All the abbreviation are leading to only one option */
2084 /* We can just continue as in the previous case. */
2085 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2086 return xstrdup(first_s);
2087 else
2088 return NULL;
2092 /* ================================================================ */
2093 /* Terminate the program if mandatory options required by a context */
2094 /* are not present. */
2095 /* ================================================================ */
2096 static void
2097 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2099 char * missing;
2101 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2102 fatal(CTXOPTMISPAR, missing);
2105 /* ====================================================== */
2106 /* Return 1 if at least one mandatory option was not seen */
2107 /* when quitting a context, else 0. */
2108 /* ====================================================== */
2109 static int
2110 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2112 user_rc = 0;
2113 *user_string = '\0';
2115 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2116 rtrim(user_string, " ", 0);
2118 *missing = user_string;
2120 return user_rc ? 1 : 0;
2123 /* ========================================================================= */
2124 /* This function terminates the program if an option or its arguments do not */
2125 /* conform to its occurrences constraint. */
2126 /* There constraints can appear by trailing >, < or = in their definition */
2127 /* given in ctxopt_new_ctx. */
2128 /* ========================================================================= */
2129 static void
2130 check_for_occurrences_issues(ctx_inst_t * ctx_inst)
2132 ctx_t * ctx = ctx_inst->ctx;
2133 opt_t * opt;
2134 ll_node_t * node;
2135 opt_inst_t * opt_inst;
2137 /* Checks options. */
2138 /* """"""""""""""" */
2139 node = ctx->opt_list->head;
2141 while (node != NULL)
2143 opt = node->data;
2145 /* Update current_state. */
2146 /* """"""""""""""""""""" */
2147 cur_state->opts_count = opt->opt_count_mark;
2148 cur_state->opt_args_count = opt->opt_args_count_mark;
2150 if (opt->opt_count_matter)
2151 switch (opt->opt_count_oper)
2153 case '=':
2154 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2155 fatal(CTXOPTCTEOPT, NULL);
2156 break;
2158 case '<':
2159 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2160 fatal(CTXOPTCTLOPT, NULL);
2161 break;
2163 case '>':
2164 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2165 fatal(CTXOPTCTGOPT, NULL);
2166 break;
2169 node = node->next;
2172 /* Checks arguments. */
2173 /* """"""""""""""""" */
2174 node = ctx_inst->opt_inst_list->head;
2175 while (node != NULL)
2177 opt_inst = node->data;
2178 opt = opt_inst->opt;
2180 /* Update current_state. */
2181 /* """"""""""""""""""""" */
2182 cur_state->opts_count = opt->opt_count_mark;
2183 cur_state->opt_args_count = opt->opt_args_count_mark;
2185 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2187 if (opt->opt_args_count_matter)
2188 switch (opt->opt_args_count_oper)
2190 case '=':
2191 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2192 fatal(CTXOPTCTEARG, NULL);
2193 break;
2195 case '<':
2196 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2197 fatal(CTXOPTCTLARG, NULL);
2198 break;
2200 case '>':
2201 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2202 fatal(CTXOPTCTGARG, NULL);
2203 break;
2206 node = node->next;
2210 /* ======================================================================== */
2211 /* Parse a strings describing options and some of their characteristics */
2212 /* The input string must have follow some rules like in the examples below: */
2213 /* */
2214 /* "opt_name1 opt_name2" */
2215 /* "[opt_name1] opt_name2" */
2216 /* "[opt_name1] opt_name2..." */
2217 /* "[opt_name1 #...] opt_name2... [#]" */
2218 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2219 /* */
2220 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2221 /* means that there can be more than one occurrence of the previous thing. */
2222 /* */
2223 /* opt_name can be followed by a 'new context' change prefixed with the */
2224 /* symbol >, as in opt1>c2 by eg. */
2225 /* */
2226 /* This function returns as soon as one (or no) option has been parsed and */
2227 /* return the offset to the next option to parse. */
2228 /* */
2229 /* In case of successful parsing, an new option is allocated and its */
2230 /* pointer returned. */
2231 /* ======================================================================== */
2232 static int
2233 opt_parse(char * s, opt_t ** opt)
2235 int opt_optional = 0;
2236 int opt_multiple = 0;
2237 int opt_count_matter = 0;
2238 char opt_count_oper = '\0';
2239 unsigned opt_count_mark = 0;
2240 int opt_args = 0;
2241 char opt_arg[33] = { 0 };
2242 int opt_multiple_args = 0;
2243 int opt_args_count_matter = 0;
2244 char opt_args_count_oper = '\0';
2245 unsigned opt_args_count_mark = 0;
2246 int opt_optional_args = 0;
2247 int opt_eval_first = 0;
2249 int n;
2250 int pos;
2251 int count = 0;
2253 char * s_orig = s;
2255 char * p;
2256 char * opt_name;
2257 char * next_ctx;
2258 char token[65];
2260 *opt = NULL;
2261 memset(opt_arg, '\0', 33);
2263 /* Strip the leading blanks. */
2264 /* """"""""""""""""""""""""" */
2265 while (isblank(*s))
2266 s++;
2268 if (*s == '[') /* Start of an optional option. */
2270 opt_optional = 1;
2271 s++;
2273 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2274 if (s == NULL)
2275 return -1; /* Empty string. */
2277 /* Early EOS, only return success if the option is mandatory. */
2278 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2279 if (!*s)
2280 if (opt_optional == 1)
2281 return -(s - s_orig - 1);
2283 /* Validate the option name */
2284 /* ALPHA+(ALPHANUM|_)* */
2285 /* """""""""""""""""""""""" */
2286 p = token;
2287 if (!isalpha(*p) && *p != '*')
2288 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2290 if (*p == '*')
2291 opt_eval_first = 1;
2293 p++;
2294 while (*p)
2296 if (!isalnum(*p) && *p != '_' && *p != '>')
2297 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2298 * a number or a _ */
2299 p++;
2302 if (opt_eval_first)
2303 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2304 else
2305 opt_name = xstrdup(token);
2307 if (*s == ']')
2309 s++;
2310 while (isblank(*s))
2311 s++;
2313 goto success;
2316 /* Check if it can appear multiple times by looking for the dots. */
2317 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2318 p = strtoken(s, token, 3, "[.]", &pos);
2319 if (p)
2321 if (strcmp(token, "...") == 0)
2323 opt_multiple = 1;
2324 s = p;
2325 if (*s == '<' || *s == '=' || *s == '>')
2327 unsigned value;
2328 int offset;
2330 n = sscanf(s + 1, "%u%n", &value, &offset);
2331 if (n == 1)
2333 opt_count_matter = 1;
2334 opt_count_oper = *s;
2335 opt_count_mark = value;
2337 s += offset + 1;
2340 else
2342 free(opt_name);
2343 return -(s - s_orig - 1);
2347 /* A blank separates the option name and the argument tag. */
2348 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2349 if (isblank(*s))
2351 char dots[4];
2353 while (isblank(*s))
2354 s++;
2356 if (!*s)
2357 goto success;
2359 pos = 0;
2360 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2361 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2363 opt_args = 1;
2364 opt_optional_args = 1;
2365 if (n == 2)
2366 opt_multiple_args = 1; /* There were dots. */
2368 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2370 if (*s == '<' || *s == '=' || *s == '>')
2372 unsigned value;
2373 int offset;
2375 n = sscanf(s + 1, "%u%n", &value, &offset);
2376 if (n == 1)
2378 opt_args_count_matter = 1;
2379 opt_args_count_oper = *s;
2380 opt_args_count_mark = value;
2382 s += offset + 1;
2385 /* Optional arg tag must end with a ] */
2386 /* """""""""""""""""""""""""""""""""" */
2387 if (*s != ']')
2389 free(opt_name);
2390 return -(s - s_orig - 1);
2393 s++; /* Skip the ] */
2395 else
2397 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2398 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2400 opt_args = 1;
2401 if (n == 2) /* There were dots. */
2402 opt_multiple_args = 1;
2404 s += pos + !!(n == 2) * 3; /* Skip the dots */
2406 if (*s == '<' || *s == '=' || *s == '>')
2408 unsigned value;
2409 int offset;
2411 n = sscanf(s + 1, "%u%n", &value, &offset);
2412 if (n == 1)
2414 opt_args_count_matter = 1;
2415 opt_args_count_oper = *s;
2416 opt_args_count_mark = value;
2418 s += offset + 1;
2422 if (*s == ']')
2424 /* Abort on extraneous ] if the option is mandatory. */
2425 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2426 if (!opt_optional)
2427 return -(s - s_orig - 1);
2429 s++; /* skip the ] */
2431 /* Strip the following blanks. */
2432 /* """"""""""""""""""""""""""" */
2433 while (isblank(*s))
2434 s++;
2436 goto success;
2438 else if (opt_optional == 0 && (!*s || isblank(*s)))
2440 /* Strip the following blanks. */
2441 /* """"""""""""""""""""""""""" */
2442 while (isblank(*s))
2443 s++;
2445 goto success;
2447 else if (opt_args == 0) /* # was not read it is possibly the start *
2448 * of another option. */
2449 goto success;
2450 else
2451 return -(s - s_orig - 1);
2454 success:
2456 /* Strip the following blanks. */
2457 /* """"""""""""""""""""""""""" */
2458 while (isblank(*s))
2459 s++;
2461 next_ctx = NULL;
2463 if (*opt_name == '>')
2464 fatal_internal("The option name is missing in %s.", opt_name);
2466 count = strchrcount(opt_name, '>');
2467 if (count == 1)
2469 char * tmp = strchr(opt_name, '>');
2470 next_ctx = xstrdup(tmp + 1);
2471 *tmp = '\0';
2473 else if (count > 1)
2474 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2476 *opt = xmalloc(sizeof(opt_t));
2478 (*opt)->name = opt_name;
2479 (*opt)->optional = opt_optional;
2480 (*opt)->multiple = opt_multiple;
2481 (*opt)->opt_count_matter = opt_count_matter;
2482 (*opt)->opt_count_oper = opt_count_oper;
2483 (*opt)->opt_count_mark = opt_count_mark;
2484 (*opt)->args = opt_args;
2485 (*opt)->arg = xstrdup(opt_arg);
2486 (*opt)->optional_args = opt_optional_args;
2487 (*opt)->multiple_args = opt_multiple_args;
2488 (*opt)->opt_args_count_matter = opt_args_count_matter;
2489 (*opt)->opt_args_count_oper = opt_args_count_oper;
2490 (*opt)->opt_args_count_mark = opt_args_count_mark;
2491 (*opt)->eval_first = opt_eval_first;
2492 (*opt)->next_ctx = next_ctx;
2493 (*opt)->ctx_list = ll_new();
2494 (*opt)->constraints_list = ll_new();
2495 (*opt)->action = NULL;
2496 (*opt)->params = NULL;
2497 (*opt)->data = NULL;
2499 return s - s_orig;
2502 /* ==================================================================== */
2503 /* Try to initialize all the option in a given string */
2504 /* Each parsed option are put in a BST tree with its name as index. */
2505 /* */
2506 /* On collision, the arguments only the signature are required to be */
2507 /* the same else this is considered as an error. Options can be used in */
2508 /* more than one context and can be optional in one and mandatory in */
2509 /* another. */
2510 /* ==================================================================== */
2511 static int
2512 init_opts(char * spec, ctx_t * ctx)
2514 opt_t * opt, *bst_opt;
2515 bst_t * node;
2516 int offset;
2518 while (*spec)
2520 if ((offset = opt_parse(spec, &opt)) > 0)
2522 spec += offset;
2524 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2526 int same_next_ctx = 0;
2528 bst_opt = node->key; /* Node extracted from the BST. */
2530 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2531 same_next_ctx = 1;
2532 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2533 same_next_ctx = 0;
2534 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2535 same_next_ctx = 0;
2536 else
2537 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2539 if (bst_opt->optional_args != opt->optional_args
2540 || bst_opt->multiple_args != opt->multiple_args
2541 || bst_opt->args != opt->args || !same_next_ctx)
2543 fatal_internal("The option %s already exists with "
2544 "a different arguments signature.\n",
2545 opt->name);
2548 /* The newly created opt is already present in options_bst. */
2549 /* We can remove it. */
2550 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2551 opt_free(opt);
2553 /* The new occurrence of the option option is legal */
2554 /* append the current context ptr in the list. */
2555 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2556 ll_append(bst_opt->ctx_list, ctx);
2558 /* Append the new option to the context's options list. */
2559 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2560 ll_append(ctx->opt_list, bst_opt);
2562 else
2564 /* Initialize the option's context list with the current context. */
2565 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2566 ll_append(opt->ctx_list, ctx);
2568 /* Append the new option to the context's options list. */
2569 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2570 ll_append(ctx->opt_list, opt);
2572 /* Insert the new option in the BST. */
2573 /* """"""""""""""""""""""""""""""""" */
2574 bst_search(opt, &options_bst, opt_compare);
2577 else
2579 char * s = xstrndup(spec, -offset);
2580 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2581 free(s);
2583 exit(EXIT_FAILURE);
2587 return 1;
2590 /* ===================================================== */
2591 /* ctxopt initialization function, must be called first. */
2592 /* ===================================================== */
2593 void
2594 ctxopt_init(char * prog_name, char * init_flags)
2596 int n;
2598 contexts_bst = NULL;
2599 options_bst = NULL;
2600 char * ptr;
2602 user_rc = 0;
2603 user_value = 0;
2604 user_string = xmalloc(8);
2605 user_string2 = xmalloc(8);
2606 user_object = NULL;
2607 char flag[33], fname[31], vname[31];
2608 int invalid;
2610 ctxopt_initialized = 1;
2612 /* Initialize current_state.*/
2613 /* """""""""""""""""""""""" */
2614 cur_state = xcalloc(sizeof(state_t), 0);
2616 /* Initialize custom error function pointers to NULL. */
2617 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2618 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2619 for (n = 0; n < CTXOPTERRSIZ; n++)
2620 err_functions[n] = NULL;
2622 /* Parse init_flags if any. */
2623 /* """""""""""""""""""""""" */
2624 while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2626 if (*flag)
2628 if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2629 fatal_internal("Invalid flag assignment: %s.", flag);
2631 if (strcmp(fname, "stop_if_non_option") == 0)
2633 if (eval_yes(vname, &invalid))
2634 flags.stop_if_non_option = 1;
2635 else if (!invalid)
2636 flags.stop_if_non_option = 0;
2637 else
2638 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2640 else if (strcmp(fname, "allow_abbreviations") == 0)
2642 if (eval_yes(vname, &invalid))
2643 flags.allow_abbreviations = 1;
2644 else if (!invalid)
2645 flags.allow_abbreviations = 0;
2646 else
2647 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2649 else
2650 fatal_internal("Invalid flag name: %s.", fname);
2654 /* Update current_state. */
2655 /* """"""""""""""""""""" */
2656 if (prog_name)
2658 if (*prog_name == '\0')
2659 cur_state->prog_name = xstrdup("program_name");
2660 else if ((ptr = strrchr(prog_name, '/')))
2661 cur_state->prog_name = xstrdup(ptr + 1);
2662 else
2663 cur_state->prog_name = xstrdup(prog_name);
2665 else
2666 cur_state->prog_name = xstrdup("program_name");
2669 /* ========================================================================= */
2670 /* Utility function which create and register a par_t object in a BST */
2671 /* embedded in a context. */
2672 /* This object will have a name and a pointer to the option it refers to. */
2673 /* These object will be used to quickly find an option from a command */
2674 /* line parameter during the analysis phase. */
2675 /* */
2676 /* IN : an option name. */
2677 /* IN : a string of command line parameters to associate to the option. */
2678 /* Returns : 1 is all was fine else 0. */
2679 /* ========================================================================= */
2680 static int
2681 opt_set_parms(char * opt_name, char * par_str)
2683 char * par_name, *ctx_name;
2684 char * tmp_par_str, *end_tmp_par_str;
2685 ctx_t * ctx;
2686 opt_t * opt;
2687 bst_t * node;
2688 par_t * par, tmp_par;
2689 int rc = 1; /* return code */
2691 ll_t * list;
2692 ll_node_t * lnode;
2694 /* Look if the given option is defined. */
2695 /* """""""""""""""""""""""""""""""""""" */
2696 opt = locate_opt(opt_name);
2697 if (opt == NULL)
2698 fatal_internal("Unknown option %s.", opt_name);
2700 /* For each context using this option. */
2701 /* """"""""""""""""""""""""""""""""""" */
2702 list = opt->ctx_list;
2704 lnode = list->head;
2705 while (lnode != NULL)
2707 /* Locate the context in the contexts tree. */
2708 /* """""""""""""""""""""""""""""""""""""""" */
2709 ctx_name = ((ctx_t *)(lnode->data))->name;
2711 ctx = locate_ctx(ctx_name);
2712 if (ctx == NULL)
2713 fatal_internal("Unknown context %s.", ctx_name);
2714 else
2716 void * par_bst = ctx->par_bst;
2718 tmp_par_str = xstrdup(par_str);
2719 ltrim(tmp_par_str, " \t");
2720 rtrim(tmp_par_str, " \t", 0);
2721 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2722 if (par_name == NULL)
2723 fatal_internal("Parameters are missing for option %s.", opt_name);
2725 /* For each parameter given in par_str, creates a par_t object and */
2726 /* insert it the in the parameters BST of the context. */
2727 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2728 while (par_name != NULL)
2730 tmp_par.name = par_name;
2732 node = bst_find(&tmp_par, &par_bst, par_compare);
2733 if (node != NULL)
2735 fatal_internal("The parameter %s is already defined in context %s.",
2736 par_name, ctx->name);
2737 rc = 0;
2739 else
2741 par = xmalloc(sizeof(par_t));
2742 par->name = xstrdup(par_name);
2743 par->opt = opt; /* Link the option to this parameter */
2745 bst_search(par, &par_bst, par_compare);
2747 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2750 /* Update the value of the root of ctx->par_bst as it may have */
2751 /* been modified. */
2752 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2753 ctx->par_bst = par_bst;
2755 free(tmp_par_str);
2757 lnode = lnode->next;
2760 return rc;
2763 /* ==================================================================== */
2764 /* Create a new context instance. */
2765 /* IN ctx : a context pointer to allow this instance to */
2766 /* access the context fields */
2767 /* IN prev_ctx_inst : the context instance whose option leading to the */
2768 /* creation of this new context instance is part of */
2769 /* Returns : the new context. */
2770 /* ==================================================================== */
2771 static ctx_inst_t *
2772 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
2774 opt_t * opt;
2775 opt_inst_t * gen_opt_inst;
2776 ctx_inst_t * ctx_inst;
2777 seen_opt_t * seen_opt;
2778 char * str, *opt_name;
2779 void * bst;
2780 bst_t * bst_node;
2782 /* Keep a trace of the opt_inst which was at the origin of the creation */
2783 /* of this context instance. */
2784 /* This will serve during the evaluation of the option callbacks. */
2785 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2786 if (prev_ctx_inst != NULL)
2788 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
2790 /* Update current_state. */
2791 /* """"""""""""""""""""" */
2792 cur_state->opt_name = gen_opt_inst->opt->name;
2794 else
2795 gen_opt_inst = NULL;
2797 /* Create and initialize the new context instance. */
2798 /* """"""""""""""""""""""""""""""""""""""""""""""" */
2799 ctx_inst = xmalloc(sizeof(ctx_inst_t));
2800 ctx_inst->ctx = ctx;
2801 ctx_inst->prev_ctx_inst = prev_ctx_inst;
2802 ctx_inst->gen_opt_inst = gen_opt_inst;
2803 ctx_inst->incomp_bst_list = ll_new();
2804 ctx_inst->opt_inst_list = ll_new();
2805 ctx_inst->seen_opt_bst = NULL;
2807 ll_node_t * node;
2809 if (prev_ctx_inst == NULL)
2810 first_ctx_inst = ctx_inst;
2812 /* Initialize the occurrence counters of each opt allowed in the context. */
2813 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2814 node = ctx->opt_list->head;
2815 while (node != NULL)
2817 opt = node->data;
2818 opt->occurrences = 0;
2820 node = node->next;
2823 /* Initialize the BST containing the seen indicator for all the options */
2824 /* allowed in this context instance. */
2825 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2826 node = ctx->opt_list->head;
2827 while (node != NULL)
2829 opt = node->data;
2830 seen_opt = xmalloc(sizeof(seen_opt_t));
2831 seen_opt->opt = opt;
2832 seen_opt->par = NULL;
2833 seen_opt->seen = 0;
2835 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
2837 node = node->next;
2840 /* Initialize the BST containing the incompatibles options. */
2841 /* Incompatibles option names are read from strings found in the list */
2842 /* incomp_list present in each instance of ctx_t. */
2843 /* These names are then used to search for the object of type seen_opt_t */
2844 /* which is already present in the seen_opt_bst of the context instance. */
2845 /* in the BST. */
2846 /* Once found the seen_opt_t object in inserted in the new BST */
2847 /* At the end the new BST in added to the list incomp_bst_list. */
2848 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2849 node = ctx->incomp_list->head;
2850 while (node != NULL)
2852 bst = NULL;
2853 seen_opt_t tmp_seen_opt;
2855 str = xstrdup(node->data);
2856 ltrim(str, " \t");
2857 rtrim(str, " \t", 0);
2858 opt_name = strtok(str, " \t"); /* Extract the first option name. */
2860 while (opt_name != NULL) /* For each option name. */
2862 if ((opt = locate_opt(opt_name)) != NULL)
2864 /* The option found is searched in the tree of potential */
2865 /* seen options. */
2866 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
2867 tmp_seen_opt.opt = opt;
2869 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2870 seen_opt_compare);
2872 if (bst_node != NULL)
2874 /* If found then it is added into the new BST tree. */
2875 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2876 seen_opt = bst_node->key;
2877 bst_search(seen_opt, &bst, seen_opt_compare);
2879 else
2880 /* Not found! That means that the option is unknown in this */
2881 /* context as all options has have a seen_opt structure in */
2882 /* seen_opt_bst. */
2883 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2884 fatal_internal("%s is not known in the context %s.", opt->name,
2885 ctx->name);
2887 else
2888 fatal_internal("Unknown option %s.", opt_name);
2890 opt_name = strtok(NULL, " \t");
2893 free(str);
2894 ll_append(ctx_inst->incomp_bst_list, bst);
2896 node = node->next;
2899 return ctx_inst;
2902 /* ====================================================================== */
2903 /* Create a list formed by all the significant command line words */
2904 /* Words beginning or ending with { or } are split. Each of these */
2905 /* symbols will get their own place in the list. */
2906 /* */
2907 /* the {...} part delimits a context, the { will not appear in the list */
2908 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
2909 /* to facilitate the parsing phase. | must not be used by the end user. */
2910 /* */
2911 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
2912 /* program name is not considered. */
2913 /* IN words : is the array of strings constituting the command line to */
2914 /* parse. */
2915 /* Returns : 1 on success, 0 if a { or } is missing. */
2916 /* ====================================================================== */
2917 static int
2918 ctxopt_build_cmdline_list(int nb_words, char ** words)
2920 int i;
2921 char * prev_word = NULL;
2922 char * word;
2923 char * ptr;
2924 int level = 0;
2925 ll_node_t *node, *start_node;
2927 /* The analysis is divided into three passes, this is not optimal but */
2928 /* must be done only one time. Doing that we privilege readability. */
2929 /* */
2930 /* In the following, SG is the ascii character 1d (dec 29) */
2931 /* */
2932 /* The first pass creates the list, extract the leading an trailing */
2933 /* SG '{' and '}' of each word and give them their own place in the */
2934 /* list */
2935 /* */
2936 /* The second pass transform the '{...}' blocks by a trailing SG */
2937 /* ({...} -> ...|) */
2938 /* */
2939 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
2940 /* the middle in the remaining list elements and recreate the pseudo */
2941 /* argument: {} */
2942 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2944 /* If the option list is not empty, clear it before going further. */
2945 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2946 if (cmdline_list != NULL)
2948 node = cmdline_list->head;
2949 while (node != NULL)
2951 free(node->data);
2952 ll_delete(cmdline_list, node);
2953 node = cmdline_list->head;
2956 else
2957 cmdline_list = ll_new();
2959 start_node = cmdline_list->head; /* In the following loop start_node will *
2960 * contain a pointer to the current *
2961 * word stripped from its leading *
2962 * sequence of {, }. */
2963 for (i = 0; i < nb_words; i++)
2965 size_t len = strlen(words[i]);
2966 size_t start, end;
2967 char * str;
2969 str = words[i];
2971 /* Replace each occurrence of the legal word {} by the characters */
2972 /* 0x02 and 0x03 to hide them from the following process. */
2973 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2974 while ((ptr = strstr(str, "{}")) != NULL)
2976 *ptr = 0x02; /* Arbitrary values unlikely. */
2977 *(ptr + 1) = 0x03; /* present in a word */
2980 if (len > 1) /* The word contains at least 2 characters. */
2982 start = 0;
2984 /* Interpret its beginning and look for the start of the real word. */
2985 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2986 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
2988 ll_append(cmdline_list, xstrndup(str + start, 1));
2989 start++;
2990 start_node = cmdline_list->tail;
2993 end = len - 1;
2994 if (str[end] == '{' || str[end] == '}')
2996 if (end > 0 && str[end - 1] != '\\')
2998 ll_append(cmdline_list, xstrndup(str + end, 1));
2999 end--;
3000 node = cmdline_list->tail;
3002 while (str[end] == '{' || str[end] == '}')
3004 if (end > start && str[end - 1] == '\\')
3005 break;
3007 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3008 end--;
3009 node = node->prev;
3014 if (start <= end)
3016 if (start_node != NULL)
3017 ll_insert_after(cmdline_list, start_node,
3018 xstrndup(str + start, end - start + 1));
3019 else
3020 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3021 start_node = cmdline_list->tail;
3024 else if (len == 1)
3026 ll_append(cmdline_list, xstrdup(str));
3027 start_node = cmdline_list->tail;
3031 /* 2nd pass. */
3032 /* """"""""" */
3033 node = cmdline_list->head;
3035 level = 0;
3036 while (node != NULL)
3038 word = node->data;
3040 if (strcmp(word, "{") == 0)
3042 ll_node_t * old_node = node;
3043 level++;
3044 node = node->next;
3045 free(word);
3046 ll_delete(cmdline_list, old_node);
3048 else if (strcmp(word, "}") == 0)
3050 level--;
3052 if (level < 0)
3053 return 0;
3054 else
3055 *word = 0x1d;
3057 else
3058 node = node->next;
3061 if (level != 0)
3062 return 0;
3064 /* 3rd pass. */
3065 /* """"""""" */
3066 node = cmdline_list->head;
3068 while (node != NULL)
3070 word = node->data;
3072 /* Restore the original { and } characters forming the legal word {}. */
3073 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3074 while ((ptr = strchr(word, 0x02)) != NULL)
3075 *ptr = '{';
3076 while ((ptr = strchr(word, 0x03)) != NULL)
3077 *ptr = '}';
3079 /* Remove a SG if the previous element is SG. */
3080 /* """""""""""""""""""""""""""""""""""""""""" */
3081 if (strcmp(word, "\x1d") == 0)
3083 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3085 ll_node_t * old_node = node;
3086 node = node->prev;
3087 free(old_node->data);
3088 ll_delete(cmdline_list, old_node);
3091 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3092 * a parameter. Protect it. */
3094 free(node->data);
3095 node->data = xstrdup("\\-");
3098 prev_word = node->data;
3099 node = node->next;
3102 /* Clean useless and SG at the beginning and end of list. */
3103 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3104 node = cmdline_list->head;
3106 if (node == NULL)
3107 return 1;
3109 word = node->data;
3111 if (strcmp(word, "\x1d") == 0)
3113 free(word);
3114 ll_delete(cmdline_list, node);
3117 node = cmdline_list->tail;
3118 if (node == NULL)
3119 return 1;
3121 word = node->data;
3123 if (strcmp(word, "\x1d") == 0)
3125 free(word);
3126 ll_delete(cmdline_list, node);
3129 return 1;
3132 /* ===================================================================== */
3133 /* Build and analyze the command line list and create the linked data */
3134 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3135 /* This function identifies the following errors and creates an array of */
3136 /* The remaining unanalyzed arguments. */
3137 /* - detect missing arguments */
3138 /* - detect too many arguments */
3139 /* - detect unknown parameters in a context */
3140 /* - detect too many occurrences of a parameters in a context */
3141 /* - detect missing required arguments in a context */
3142 /* */
3143 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3144 /* program name is not considered */
3145 /* IN words : is the array of strings constituting the command line to */
3146 /* parse. */
3147 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3148 /* is present in the list. */
3149 /* OUT rem_args : array of remaining command line arguments if a -- */
3150 /* is present in the list. This array must be free by */
3151 /* The caller as it is allocated here. */
3152 /* ===================================================================== */
3153 void
3154 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3155 char *** rem_args)
3157 ctx_t * ctx;
3158 opt_t * opt;
3159 par_t * par;
3160 ctx_inst_t * ctx_inst;
3161 opt_inst_t * opt_inst;
3162 int expect_par = 0;
3163 int expect_arg = 0;
3164 int expect_par_or_arg = 0;
3166 ll_node_t * cli_node;
3167 bst_t * bst_node;
3168 seen_opt_t * bst_seen_opt;
3169 char * par_name;
3170 void * bst;
3172 ll_node_t * node;
3174 if (!ctxopt_build_cmdline_list(nb_words, words))
3175 fatal_internal("The command line could not be parsed: "
3176 "missing '{' or '}' detected.");
3178 if (main_ctx == NULL)
3179 fatal_internal("At least one context must have been created.");
3181 /* Check that all options has an action and at least one parameter. */
3182 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3183 bst_walk(options_bst, bst_check_opt_cb);
3185 /* Create the first ctx_inst record. */
3186 /* """"""""""""""""""""""""""""""""" */
3187 ctx = main_ctx;
3189 ctx_inst_list = ll_new();
3190 ctx_inst = new_ctx_inst(ctx, NULL);
3191 ctx_inst->par_name = NULL;
3193 /* Update current_state. */
3194 /* """"""""""""""""""""" */
3195 cur_state->ctx_name = ctx->name;
3197 ll_append(ctx_inst_list, ctx_inst);
3199 /* For each node in the command line. */
3200 /* """""""""""""""""""""""""""""""""" */
3201 cli_node = cmdline_list->head;
3202 expect_par = 1;
3203 while (cli_node != NULL)
3205 if (strcmp(cli_node->data, "--") == 0)
3206 break; /* No new parameter will be analyzed after this point. */
3208 par_name = cli_node->data;
3210 /* Replace a leading -- by a single - */
3211 /* """""""""""""""""""""""""""""""""" */
3212 if (strncmp(cli_node->data, "--", 2) == 0)
3213 par_name += 1; /* Ignore the first dash */
3215 if (strcmp(par_name, "\x1d") == 0)
3217 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3218 check_for_occurrences_issues(ctx_inst);
3220 /* Forced backtracking to the previous context instance. */
3221 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3222 if (ctx_inst->prev_ctx_inst != NULL)
3224 ctx_inst = ctx_inst->prev_ctx_inst;
3225 ctx = ctx_inst->ctx;
3227 /* Update current_states. */
3228 /* """"""""""""""""""""" */
3229 cur_state->ctx_name = ctx->name;
3230 cur_state->ctx_par_name = ctx_inst->par_name;
3232 else
3234 /* Update current_state. */
3235 /* """"""""""""""""""""" */
3236 cur_state->ctx_par_name = NULL;
3239 else if (expect_par && *par_name == '-')
3241 int pos = 0;
3242 char * prefix;
3244 /* Update current_state. */
3245 /* """"""""""""""""""""" */
3246 cur_state->cur_opt_par_name = par_name;
3247 cur_state->ctx_name = ctx->name;
3248 cur_state->ctx_par_name = ctx_inst->par_name;
3250 /* An expected parameter has been seen. */
3251 /* """""""""""""""""""""""""""""""""""" */
3252 if ((par = locate_par(par_name, ctx)) == NULL)
3254 opt_t * popt;
3255 char * word;
3257 /* Look if this parameter is an unique abbreviation of a longer */
3258 /* parameter. If this is the case then just replace it with its */
3259 /* full length version and try again. */
3260 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3261 if (flags.allow_abbreviations)
3262 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3264 cli_node->data = word;
3265 continue;
3268 /* Try to find a prefix which is a valid parameter in this context */
3269 /* If found, split the cli_node in two to build a new parameter */
3270 /* node and followed by a node containing the remaining string */
3271 /* If the new parameter corresponds to an option not taking */
3272 /* argument then prefix the remaining string whit a dash as it may */
3273 /* contain a new parameter. */
3274 /* The new parameter will be re-evaluated in the next iteration of */
3275 /* the loop. */
3276 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3277 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3278 if (prefix != NULL && pos != 0)
3280 cli_node->data = prefix; /* prefix contains le name of a valid *
3281 | parameter in this context. */
3283 if (popt->args)
3285 /* The parameter may be followed by arguments. */
3286 /* ''''''''''''''''''''''''''''''''''''''''''' */
3287 if (*(par_name + pos) == '-')
3289 word = xstrdup("\\"); /* Protect the '-' */
3290 word = strappend(word, par_name + pos, NULL);
3292 else
3293 word = xstrdup(par_name + pos);
3295 else
3297 /* The parameter does not take arguments, the */
3298 /* following word must be a parameter or nothing */
3299 /* hence prefix it with a dash. */
3300 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3301 word = xstrdup("-");
3302 word = strappend(word, par_name + pos, NULL);
3305 /* Insert it after the current node in the list. */
3306 /* """"""""""""""""""""""""""""""""""""""""""""" */
3307 ll_insert_after(cmdline_list, cli_node, word);
3309 continue; /* loop */
3311 else
3313 check_for_missing_mandatory_opt(ctx_inst, par_name);
3314 check_for_occurrences_issues(ctx_inst);
3316 if (ctx_inst->prev_ctx_inst == NULL)
3318 char * errmsg = xstrdup("");
3320 /* Update current_state. */
3321 /* """"""""""""""""""""" */
3322 cur_state->ctx_par_name = NULL;
3324 *user_string = '\0';
3325 *user_string2 = '\0';
3327 user_string = strappend(user_string, par_name, NULL);
3329 bst_walk(contexts_bst, bst_match_par_cb);
3331 if (*user_string2 != '\0')
3333 errmsg = strappend(
3334 errmsg,
3335 "\nIt appears to be defined in the context(s):", user_string2,
3336 "\n", NULL);
3339 fatal(CTXOPTUNKPAR, errmsg);
3341 else
3343 /* Tries to backtrack and analyse the same parameter in the */
3344 /* previous context. */
3345 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3346 ctx_inst = ctx_inst->prev_ctx_inst;
3347 ctx = ctx_inst->ctx;
3349 /* Update current_state. */
3350 /* """"""""""""""""""""" */
3351 cur_state->ctx_name = ctx->name;
3352 cur_state->ctx_par_name = ctx_inst->par_name;
3354 cli_node = cli_node->prev;
3358 else
3360 seen_opt_t seen_opt;
3362 /* The parameter is valid in the context, create a opt_inst and */
3363 /* append it to the ctx_inst list options list. */
3364 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3365 opt = par->opt;
3367 opt->occurrences++;
3369 opt_inst = xmalloc(sizeof(opt_inst_t));
3370 opt_inst->opt = opt;
3371 opt_inst->par = par_name;
3372 opt_inst->values_list = ll_new();
3373 opt_inst->next_ctx_inst = NULL;
3375 /* Priority option are inserted at the start of the opt_inst list */
3376 /* but their order of appearance in the context definition must */
3377 /* be preserver so each new priority option will be placed after */
3378 /* the previous ones at the start of the opt_inst list. */
3379 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3380 if (!opt->eval_first)
3381 ll_append(ctx_inst->opt_inst_list, opt_inst);
3382 else
3384 ll_node_t * opt_inst_node = ctx_inst->opt_inst_list->head;
3385 opt_inst_t * tmp_opt_inst;
3387 while (opt_inst_node != NULL)
3389 tmp_opt_inst = opt_inst_node->data;
3390 if (!tmp_opt_inst->opt->eval_first)
3392 ll_insert_before(ctx_inst->opt_inst_list, opt_inst_node,
3393 opt_inst);
3394 break;
3396 else
3397 opt_inst_node = opt_inst_node->next;
3399 if (opt_inst_node == NULL)
3400 ll_append(ctx_inst->opt_inst_list, opt_inst);
3403 /* Check if an option was already seen in the */
3404 /* current context instance. */
3405 /* """""""""""""""""""""""""""""""""""""""""" */
3406 seen_opt.opt = opt;
3408 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3409 seen_opt_compare);
3411 /* bst_node cannot be NULL here. */
3413 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3415 if (!opt->multiple && bst_seen_opt->seen == 1)
3416 fatal(CTXOPTDUPOPT, NULL);
3418 /* Check if this option is compatible with the options already */
3419 /* seen in this context instance. */
3420 /* Look if the option is present in one on the BST present in */
3421 /* the incomp_bst_list of the context instance. */
3422 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3423 node = ctx_inst->incomp_bst_list->head;
3424 while (node != NULL)
3426 bst = node->data;
3427 user_object = NULL;
3429 /* There can only have one seen_opt object in the BST tree was */
3430 /* already seen, try to locate it, the result will be put in */
3431 /* user_object by the bst_seen_opt_seen_cb function. */
3432 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3433 bst_walk(bst, bst_seen_opt_seen_cb);
3435 /* If it is the case, look if the current option is also */
3436 /* in this BST. */
3437 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3438 if (user_object != NULL)
3440 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3442 if (bst_node != NULL)
3444 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3445 if (bst_seen_opt->seen == 0)
3446 fatal(CTXOPTINCOPT, (char *)user_object);
3450 node = node->next;
3453 /* Mark this option as seen in the current context instance. */
3454 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3455 bst_seen_opt->seen = 1;
3456 free(bst_seen_opt->par);
3457 bst_seen_opt->par = xstrdup(par_name);
3459 /* If this option leads to a next context, create a new ctx_inst */
3460 /* and switch to it for the analyse of the future parameter. */
3461 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3462 if (opt->next_ctx != NULL)
3464 ctx = locate_ctx(opt->next_ctx);
3466 if (ctx == NULL)
3467 fatal_internal("Unknown context %s.", opt->next_ctx);
3469 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3470 ctx_inst->par_name = xstrdup(par_name);
3472 ll_append(ctx_inst_list, ctx_inst);
3475 /* Look is we must expect some arguments. */
3476 /* """""""""""""""""""""""""""""""""""""" */
3477 expect_par_or_arg = 0;
3478 expect_par = 0;
3479 expect_arg = 0;
3481 if (!opt->args)
3482 expect_par = 1; /* Parameter doesn't accept any argument. */
3483 else
3485 if (!opt->optional_args)
3486 expect_arg = 1; /* Parameter has mandatory arguments. */
3487 else
3488 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3492 else if (expect_par && *par_name != '-')
3494 ll_node_t * n = cli_node->next;
3496 if (!flags.stop_if_non_option)
3497 /* Look if potential arguments must still be analyzed until the */
3498 /* end of the context/command line part to analyze/command line. */
3499 /* If this is the case we have met an extra argument. */
3500 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3501 while (n != NULL)
3503 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3504 fatal(CTXOPTUNXARG, NULL);
3506 if (*(char *)(n->data) == '-')
3507 fatal(CTXOPTUNXARG, NULL);
3509 n = n->next;
3512 break; /* An unexpected non parameter was seen, if no Potential *
3513 | arguments remain in the command line or *
3514 | flags.stop_if_non_option is set, assume that it is is *
3515 | the first of the non arguments and stop the command *
3516 | line analysis. */
3518 else if (expect_arg && *par_name != '-')
3520 ll_node_t * cstr_node;
3521 constraint_t * cstr;
3523 /* Check if the arguments of the option respects */
3524 /* the attached constraints if any. */
3525 /* """"""""""""""""""""""""""""""""""""""""""""" */
3526 cstr_node = opt->constraints_list->head;
3527 while (cstr_node != NULL)
3529 cstr = cstr_node->data;
3530 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3531 cur_state->cur_opt_par_name))
3533 fputs("\n", stderr);
3534 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3537 cstr_node = cstr_node->next;
3540 /* If the argument is valid, store it. */
3541 /* """"""""""""""""""""""""""""""""""" */
3542 if (*par_name == '\\' && *(par_name + 1) == '-')
3543 ll_append(opt_inst->values_list, par_name + 1);
3544 else
3545 ll_append(opt_inst->values_list, par_name);
3547 expect_arg = 0;
3548 expect_par = 0;
3549 expect_par_or_arg = 0;
3551 if (opt->multiple_args)
3552 expect_par_or_arg = 1;
3553 else
3554 expect_par = 1; /* Parameter takes only one argument. */
3556 else if (expect_arg && *par_name == '-')
3557 fatal(CTXOPTMISARG, NULL);
3558 else if (expect_par_or_arg)
3560 expect_arg = 0;
3561 expect_par = 0;
3562 expect_par_or_arg = 0;
3564 if (*par_name != '-')
3565 expect_arg = 1; /* Consider this word as an argument and retry. */
3566 else
3567 expect_par = 1; /* Consider this word as a parameter and retry. */
3569 cli_node = cli_node->prev;
3572 cli_node = cli_node->next;
3575 if (cmdline_list->len > 0 && *par_name == '-')
3577 if (expect_arg && !opt->optional_args)
3578 fatal(CTXOPTMISARG, NULL);
3581 /* Look if a context_instance has unseen mandatory options. */
3582 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3583 node = ctx_inst_list->head;
3584 while (node != NULL)
3586 ctx_inst = node->data;
3588 /* Update current_state. */
3589 /* """"""""""""""""""""" */
3590 cur_state->ctx_name = ctx_inst->ctx->name;
3591 cur_state->ctx_par_name = ctx_inst->par_name;
3593 check_for_missing_mandatory_opt(ctx_inst, par_name);
3594 check_for_occurrences_issues(ctx_inst);
3596 node = node->next;
3599 /* Allocate the array containing the remaining not analyzed */
3600 /* command line arguments. */
3601 /* NOTE: The strings in the array are just pointer to the */
3602 /* data of the generating list and must not be freed. */
3603 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3604 if (cli_node != NULL)
3606 if (strcmp((char *)cli_node->data, "--") == 0)
3607 /* The special parameter -- was encountered, the -- argument is not */
3608 /* put in the remaining arguments. */
3609 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3610 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
3611 else
3612 /* A non parameter was encountered when a parameter was expected. We */
3613 /* assume that the evaluation of the remaining command line argument */
3614 /* are not the responsibility of the users code. */
3615 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
3616 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
3618 else
3620 *nb_rem_args = 0;
3621 *rem_args = xmalloc(sizeof(char *));
3622 (*rem_args)[0] = NULL;
3626 /* ==================================================== */
3627 /* Free ctxopt memory used for its internal structures. */
3628 /* ==================================================== */
3629 void
3630 ctxopt_free_memory(void)
3632 ll_destroy(cmdline_list, NULL);
3633 ll_destroy(ctx_inst_list, ctx_inst_free);
3634 bst_destroy(options_bst, opt_free);
3635 bst_destroy(contexts_bst, ctx_free);
3638 /* ==================================================================== */
3639 /* Parse the options data structures and launches the callback function */
3640 /* attached to each options instances. */
3641 /* This calls a recursive function which proceeds context per context. */
3642 /* ==================================================================== */
3643 void
3644 ctxopt_evaluate(void)
3646 evaluate_ctx_inst(first_ctx_inst);
3649 /* =================================================================== */
3650 /* Recursive function called by ctxopt_evaluate to process the list of */
3651 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
3652 /* action attached to the context and its option instances. */
3653 /* =================================================================== */
3654 static void
3655 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
3657 opt_inst_t * opt_inst;
3658 ctx_t * ctx;
3659 opt_t * opt;
3660 ll_node_t * opt_inst_node;
3661 char ** args;
3662 int nb_args;
3664 if (ctx_inst == NULL)
3665 return;
3667 ctx = ctx_inst->ctx;
3669 /* Do not evaluate the action attached to this context is there is no */
3670 /* option to evaluate. */
3671 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3672 opt_inst_node = ctx_inst->opt_inst_list->head;
3673 if (opt_inst_node == NULL)
3674 return;
3676 /* Call the entering action attached to this context if any. */
3677 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3678 if (ctx->action != NULL)
3680 if (ctx_inst->prev_ctx_inst != NULL)
3681 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
3682 ctx->nb_data, ctx->data);
3683 else
3684 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
3687 /* For each instance of options. */
3688 /* """"""""""""""""""""""""""""" */
3689 while (opt_inst_node != NULL)
3691 opt_inst = (opt_inst_t *)(opt_inst_node->data);
3692 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
3693 &args);
3694 opt = opt_inst->opt;
3696 /* Launch the attached action if any. */
3697 /* """""""""""""""""""""""""""""""""" */
3698 if (opt->action != NULL)
3699 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
3700 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
3702 if (opt_inst->next_ctx_inst != NULL)
3703 evaluate_ctx_inst(opt_inst->next_ctx_inst);
3705 if (args != NULL)
3706 free(args);
3708 opt_inst_node = opt_inst_node->next;
3711 /* Call the exiting action attached to this context if any. */
3712 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3713 if (ctx->action != NULL)
3715 if (ctx_inst->prev_ctx_inst != NULL)
3716 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
3717 ctx->nb_data, ctx->data);
3718 else
3719 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
3723 /* ============================================================ */
3724 /* Create and initializes a new context. */
3725 /* - allocate space. */
3726 /* - name it. */
3727 /* - initialize its option with a few of their characteristics. */
3728 /* ============================================================ */
3729 void
3730 ctxopt_new_ctx(char * name, char * opts_specs)
3732 ctx_t * ctx;
3733 char * p;
3735 if (!ctxopt_initialized)
3736 fatal_internal("Please call ctxopt_init first.");
3738 ctx = xmalloc(sizeof(ctx_t));
3740 /* Validates the context name: */
3741 /* ALPHA+(ALPHANUM|_)* */
3742 /* """"""""""""""""""""""""""" */
3743 p = name;
3744 if (!isalpha(*p))
3745 fatal_internal("A context name must start with a letter: %s.", name);
3747 p++;
3748 while (*p)
3750 if (!isalnum(*p) && *p != '_')
3751 fatal_internal("A context name must only contain letters, "
3752 "numbers or '_': %s.",
3753 name);
3754 p++;
3757 ctx->name = xstrdup(name);
3758 ctx->opt_list = ll_new(); /* List of options legit in this context. */
3759 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
3760 ctx->par_bst = NULL;
3761 ctx->data = NULL;
3762 ctx->action = NULL;
3764 /* The first created context is the main one. */
3765 /* """""""""""""""""""""""""""""""""""""""""" */
3766 if (contexts_bst == NULL)
3768 main_ctx = ctx;
3770 cur_state->ctx_name = ctx->name;
3773 if (init_opts(opts_specs, ctx) == 0)
3774 exit(EXIT_FAILURE);
3775 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
3776 fatal_internal("The context %s already exists.", name);
3777 else
3778 bst_search(ctx, &contexts_bst, ctx_compare);
3781 /* ==================================================== */
3782 /* Display a usage screen limited to a specific context */
3783 /* IN: the context name. */
3784 /* IN: what to do after (continue or exit the program) */
3785 /* possible values: continue_after, exit_after. */
3786 /* ==================================================== */
3787 void
3788 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
3790 ctx_t * ctx;
3791 ll_t * list;
3793 int has_optional = 0;
3794 int has_ellipsis = 0;
3795 int has_rule = 0;
3796 int has_generic_arg = 0;
3797 int has_ctx_change = 0;
3798 int has_early_eval = 0;
3800 ctx = locate_ctx(ctx_name);
3801 if (ctx == NULL)
3802 fatal_internal("Unknown context %s.", ctx_name);
3804 if (cur_state->ctx_par_name == NULL)
3805 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
3806 else
3807 printf("\nSynopsis for the context introduced by %s:\n",
3808 cur_state->ctx_par_name);
3810 list = ctx->opt_list;
3811 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3812 &has_ctx_change, &has_early_eval);
3814 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3815 has_optional, has_ellipsis, has_rule);
3817 if (action == exit_after)
3818 exit(EXIT_FAILURE);
3821 /* =================================================== */
3822 /* Display a full usage screen about all contexts. */
3823 /* IN: what to do after (continue or exit the program) */
3824 /* possible values: continue_after, exit_after. */
3825 /* =================================================== */
3826 void
3827 ctxopt_disp_usage(usage_behaviour action)
3829 ll_t * list;
3830 int has_optional = 0;
3831 int has_ellipsis = 0;
3832 int has_rule = 0;
3833 int has_generic_arg = 0;
3834 int has_ctx_change = 0;
3835 int has_early_eval = 0;
3837 if (main_ctx == NULL)
3838 fatal_internal("At least one context must have been created.");
3840 /* Usage for the first context. */
3841 /* """""""""""""""""""""""""""" */
3842 printf("\nAllowed options in the default context:\n");
3843 list = main_ctx->opt_list;
3844 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
3845 &has_ctx_change, &has_early_eval);
3847 /* Usage for the other contexts. */
3848 /* """"""""""""""""""""""""""""" */
3849 bst_walk(contexts_bst, bst_print_ctx_cb);
3851 /* Contextual syntactic explanations. */
3852 /* """""""""""""""""""""""""""""""""" */
3853 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
3854 has_optional, has_ellipsis, has_rule);
3856 if (action == exit_after)
3857 exit(EXIT_FAILURE);
3860 /* *********************************** */
3861 /* Built-in constraint check functions */
3862 /* *********************************** */
3864 /* ============================================================= */
3865 /* This constraint checks if each arguments respects a format as */
3866 /* defined for the scanf function. */
3867 /* return 1 if yes and 0 if no. */
3868 /* ============================================================= */
3870 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
3872 int rc = 0;
3874 char x[256];
3875 char y;
3876 char * format;
3878 if (nb_args != 1)
3879 fatal_internal("Format constraint, invalid number of parameters.");
3881 if (strlen(value) > 255)
3882 value[255] = '\0';
3884 format = xstrdup(args[0]);
3885 format = strappend(format, "%c", NULL);
3887 rc = sscanf(value, format, x, &y);
3888 if (rc != 1)
3889 fprintf(stderr,
3890 "The argument %s of %s does not respect the imposed format %s.",
3891 value, par, args[0]);
3893 free(format);
3895 return rc == 1;
3898 /* ================================================================== */
3899 /* This constraint checks if each arguments of the option instance is */
3900 /* between a minimum and a maximum (inclusive). */
3901 /* return 1 if yes and 0 if no. */
3902 /* ================================================================== */
3904 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
3906 regex_t re;
3908 if (nb_args != 1)
3909 fatal_internal(
3910 "Regular expression constraint, invalid number of parameters.");
3912 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
3913 fatal_internal("Invalid regular expression %s.", args[0]);
3915 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
3917 fprintf(stderr,
3918 "The argument %s of %s doesn't match the constraining "
3919 "regular expression %s.",
3920 value, par, args[0]);
3921 return 0;
3924 regfree(&re);
3926 return 1;
3929 /* ================================================================== */
3930 /* This constraint checks if each arguments of the option instance is */
3931 /* between a minimum and a maximum (inclusive). */
3932 /* return 1 if yes and 0 if no. */
3933 /* ================================================================== */
3935 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
3937 long min, max;
3938 char c;
3939 char * ptr;
3940 int n;
3941 long v;
3942 int min_only = 0;
3943 int max_only = 0;
3945 if (nb_args != 2)
3946 fatal_internal("Range constraint, invalid number of parameters.");
3948 if (strcmp(args[0], ".") == 0)
3949 max_only = 1;
3950 else
3951 n = sscanf(args[0], "%ld%c", &min, &c);
3953 if (!max_only && n != 1)
3954 fatal_internal("Range constraint, min: invalid parameters.");
3956 if (strcmp(args[1], ".") == 0)
3957 min_only = 1;
3958 else
3959 n = sscanf(args[1], "%ld%c", &max, &c);
3961 if (!min_only && n != 1)
3962 fatal_internal("Range constraint, max: invalid parameters.");
3964 if (min_only && max_only)
3965 fatal_internal("Range constraint, invalid parameters.");
3967 errno = 0;
3968 v = strtol(value, &ptr, 10);
3969 if (errno || ptr == value)
3970 return 0;
3972 if (min_only)
3974 if (v < min)
3976 fprintf(stderr,
3977 "The argument %ld of %s is not greater than or equal to %ld.", v,
3978 par, min);
3979 return 0;
3981 else
3982 return 1;
3984 else if (max_only)
3986 if (v > max)
3988 fprintf(stderr,
3989 "The argument %ld of %s is not less than or equal to %ld.", v,
3990 par, max);
3991 return 0;
3993 else
3994 return 1;
3996 else if (v < min || v > max)
3998 fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
3999 par, min, max);
4000 return 0;
4003 return 1; /* check passed */
4006 /* =============================================================== */
4007 /* This function provides a way to set the behaviour of a context. */
4008 /* =============================================================== */
4009 void
4010 ctxopt_add_global_settings(settings s, ...)
4012 va_list(args);
4013 va_start(args, s);
4015 switch (s)
4017 case error_functions:
4019 typedef void fn(errors e, state_t * state);
4021 void (*function)(errors e, state_t * state);
4023 errors e;
4024 e = va_arg(args, errors);
4025 function = va_arg(args, fn *);
4026 err_functions[e] = function;
4027 break;
4030 default:
4031 break;
4033 va_end(args);
4036 /* ================================================================ */
4037 /* This function provides a way to set the behaviour of an option. */
4038 /* It can take a variable number of arguments according to its */
4039 /* first argument: */
4040 /* - parameter: */
4041 /* o a string containing an option name and all its possible */
4042 /* parameters separates by spaces, tabs or commas (char *) */
4043 /* (e.g: "help -h -help"). */
4044 /* - actions: */
4045 /* o a string containing an option name. */
4046 /* o a pointer to a function which will be called at evaluation */
4047 /* time. */
4048 /* - constraints: */
4049 /* o a string containing an option name. */
4050 /* o a pointer to a function to check if an argument is valid. */
4051 /* o a strings containing the arguments to this function. */
4052 /* ================================================================ */
4053 void
4054 ctxopt_add_opt_settings(settings s, ...)
4056 opt_t * opt;
4057 void * ptr = NULL;
4059 va_list(args);
4060 va_start(args, s);
4062 switch (s)
4064 /* This part associates some command line parameters to an option. */
4065 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4066 case parameters:
4068 char * opt_name;
4069 char * params;
4071 /* The second argument must be a string containing: */
4072 /* - The name of an existing option. */
4073 /* - a list of parameters with a leading dash (-). */
4074 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4075 ptr = va_arg(args, char *);
4076 opt_name = ptr;
4078 if (opt_name != NULL)
4080 if ((opt = locate_opt(opt_name)) != NULL)
4082 ptr = va_arg(args, char *);
4083 params = ptr;
4085 if (!opt_set_parms(opt_name, params))
4086 fatal_internal(
4087 "Duplicated parameters or bad settings for the option %s.",
4088 params);
4090 else
4091 fatal_internal("Unknown option %s.", opt_name);
4093 else
4094 fatal_internal(
4095 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4097 /* Here opt is a known option. */
4098 /* """"""""""""""""""""""""""" */
4099 if (opt->params != NULL)
4100 fatal_internal("Parameters are already set for %s.", opt_name);
4101 else
4103 size_t n;
4104 size_t l = strlen(params);
4106 opt->params = xstrdup(params);
4107 while ((n = strcspn(opt->params, " \t")) < l)
4108 opt->params[n] = '|';
4111 break;
4114 /* This part associates a callback function to an option. */
4115 /* This function will be called when an instance of an option */
4116 /* is evaluated. */
4117 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4118 case actions:
4120 void * data;
4121 void (*function)();
4122 int nb_data = 0;
4124 /* The second argument must be the name of an existing option. */
4125 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4126 ptr = va_arg(args, char *);
4128 if ((opt = locate_opt(ptr)) != NULL)
4130 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4131 void **);
4133 /* The third argument must be the callback function. */
4134 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4135 function = va_arg(args, fn *);
4136 opt->action = function;
4138 /* The fourth argument must be a pointer to an user's defined */
4139 /* variable or structure that the previous function can manage. */
4140 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4141 while ((data = va_arg(args, void *)) != NULL)
4143 nb_data++;
4144 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4145 opt->data[nb_data - 1] = data;
4147 opt->nb_data = nb_data;
4149 else
4150 fatal_internal("Unknown option %s.", ptr);
4151 break;
4154 /* This part associates a list of functions to control some */
4155 /* characteristics of the arguments of an option. */
4156 /* Each function will be called in order and must return 1 */
4157 /* to validate the arguments. */
4158 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4159 case constraints:
4161 char * value;
4162 constraint_t * cstr;
4163 int (*function)();
4165 /* The second argument must be a string. */
4166 /* """"""""""""""""""""""""""""""""""""" */
4167 ptr = va_arg(args, char *);
4169 if ((opt = locate_opt(ptr)) != NULL)
4171 typedef int fn(int, char **, char *);
4173 /* The third argument must be a function. */
4174 /* """""""""""""""""""""""""""""""""""""" */
4175 function = va_arg(args, fn *);
4177 cstr = xmalloc(sizeof(constraint_t));
4178 cstr->constraint = function;
4180 /* The fourth argument must be a string containing the argument of */
4181 /* The previous function separated by spaces or tabs. */
4182 /* Theses arguments will be passed to the previous function */
4183 /* max: 32 argument! */
4184 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4185 value = xstrdup(va_arg(args, char *));
4187 cstr->to_free = value;
4188 cstr->args = xcalloc(sizeof(char *), 32);
4189 cstr->nb_args = str2argv(value, cstr->args, 32);
4190 ll_append(opt->constraints_list, cstr);
4192 else
4193 fatal_internal("Unknown option %s.", ptr);
4194 break;
4197 default:
4198 break;
4200 va_end(args);
4203 /* =============================================================== */
4204 /* This function provides a way to set the behaviour of a context. */
4205 /* =============================================================== */
4206 void
4207 ctxopt_add_ctx_settings(settings s, ...)
4209 ctx_t * ctx;
4211 va_list(args);
4212 va_start(args, s);
4214 switch (s)
4216 /* Add a set of mutually incompatible options in a context. */
4217 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4218 case incompatibilities:
4220 void * ptr;
4221 ll_t * list;
4222 size_t n;
4223 char * str;
4225 ptr = va_arg(args, char *);
4226 if ((ctx = locate_ctx(ptr)) != NULL)
4228 ptr = va_arg(args, char *);
4229 list = ctx->incomp_list;
4231 str = xstrdup(ptr);
4232 ltrim(str, " \t");
4233 rtrim(str, " \t", 0);
4235 n = strcspn(str, " \t");
4236 if (n > 0 && n < strlen(str))
4237 ll_append(list, str);
4238 else
4239 fatal_internal(
4240 "Not enough incompatible options in the string: \"%s\".", str);
4242 else
4243 fatal_internal("Unknown context %s.", ptr);
4244 break;
4247 /* Add functions which will be called when */
4248 /* entering and exiting a context. */
4249 /* """"""""""""""""""""""""""""""""""""""" */
4250 case actions:
4252 void * ptr;
4253 void * data;
4254 int (*function)();
4255 int nb_data = 0;
4257 ptr = va_arg(args, char *);
4258 if ((ctx = locate_ctx(ptr)) != NULL)
4260 typedef int fn(char *, direction, char *, int, void **);
4262 function = va_arg(args, fn *);
4263 ctx->action = function;
4265 while ((data = va_arg(args, void *)) != NULL)
4267 nb_data++;
4268 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4269 ctx->data[nb_data - 1] = data;
4271 ctx->nb_data = nb_data;
4273 else
4274 fatal_internal("Unknown context %s.", ptr);
4275 break;
4278 default:
4279 break;
4281 va_end(args);