Add a -hbar|-shroll_bar option
[smenu.git] / ctxopt.c
blob756202dee281159e822dde8a662af8398c954ac6
1 /* ################################################################### */
2 /* Copyright 2015, Pierre Gentile (p.gen.progs@gmail.com) */
3 /* */
4 /* This Source Code Form is subject to the terms of the Mozilla Public */
5 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
6 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 /* ################################################################### */
9 #include <errno.h>
10 #include <stddef.h>
11 #include <limits.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <ctype.h>
16 #include <sys/types.h>
17 #include <regex.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include "ctxopt.h"
22 /* ************************ */
23 /* Static global variables. */
24 /* ************************ */
26 static void *contexts_bst;
27 static void *options_bst;
29 state_t *cur_state;
31 /* Prototypes */
33 /* ************************** */
34 /* Fatal messages prototypes. */
35 /* ************************** */
37 static void (**err_functions)(errors e, state_t *state);
39 static void
40 fatal_internal(const char *format, ...);
42 static void
43 fatal(errors e, char *errmsg);
45 static int user_rc; /* Used by various callback functions. */
46 static int user_value; /* Used by various callback functions. */
47 static char *user_string; /* Used by various callback functions. */
48 static char *user_string2; /* Used by various callback functions. */
49 static void *user_object; /* Used by various callback functions. */
51 /* ************************************ */
52 /* Memory management static prototypes. */
53 /* ************************************ */
55 static void *
56 xmalloc(size_t size);
58 static void *
59 xcalloc(size_t num, size_t size);
61 static void *
62 xrealloc(void *ptr, size_t size);
64 static char *
65 xstrdup(const char *p);
67 static char *
68 xstrndup(const char *str, size_t len);
70 /* ********************** */
71 /* BST static prototypes. */
72 /* ********************** */
74 typedef struct bst_s bst_t;
76 typedef enum
78 preorder,
79 postorder,
80 endorder,
81 leaf
82 } walk_order_e;
84 #if 0 /* Unused yet. */
85 static void *
86 bst_delete(const void * vkey, void ** vrootp,
87 int (*compar)(const void *, const void *));
88 #endif
90 static void
91 bst_destroy(void *vrootp, void (*clean)(void *));
93 static void *
94 bst_find(const void *vkey,
95 void * const *vrootp,
96 int (*compar)(const void *, const void *));
98 static void *
99 bst_search(void *vkey,
100 void **vrootp,
101 int (*compar)(const void *, const void *));
103 static void
104 bst_walk_recurse(const bst_t *root,
105 void (*action)(const void *, walk_order_e, int),
106 int level);
108 static void
109 bst_walk(const void *vroot, void (*action)(const void *, walk_order_e, int));
111 /* ****************************** */
112 /* Linked list static prototypes. */
113 /* ****************************** */
115 typedef struct ll_node_s ll_node_t;
116 typedef struct ll_s ll_t;
118 static void
119 ll_append(ll_t * const list, void * const data);
121 static void
122 ll_prepend(ll_t * const list, void * const data);
124 static void
125 ll_insert_after(ll_t * const list, ll_node_t *node, void * const data);
127 static void
128 ll_insert_before(ll_t * const list, ll_node_t *node, void * const data);
130 static int
131 ll_delete(ll_t * const list, ll_node_t *node);
133 static void
134 ll_init(ll_t *list);
136 static ll_node_t *
137 ll_new_node(void);
139 static ll_t *
140 ll_new(void);
142 static void
143 ll_free(ll_t * const list, void (*)(void *));
145 static void
146 ll_destroy(ll_t * const list, void (*)(void *));
148 static int
149 ll_strarray(ll_t *list, ll_node_t *start_node, int *count, char ***array);
151 /* ************************** */
152 /* Various static prototypes. */
153 /* ************************** */
155 static void
156 ltrim(char *str, const char *trim_str);
158 static void
159 rtrim(char *str, const char *trim_str, size_t min);
161 static int
162 strchrcount(char *str, char c);
164 static int
165 strpref(char *s1, char *s2);
167 static int
168 stricmp(const char *s1, const char *s2);
170 static char *
171 xstrtok_r(char *str, const char *delim, char **end);
173 static int
174 eval_yes(char *value, int *invalid);
176 static char *
177 get_word(char *str, char *buf, size_t len);
179 /* ************************* */
180 /* ctxopt static prototypes. */
181 /* ************************* */
183 typedef struct flags_s flags_t;
184 typedef struct opt_s opt_t;
185 typedef struct par_s par_t;
186 typedef struct ctx_s ctx_t;
187 typedef struct constraint_s constraint_t;
188 typedef struct ctx_inst_s ctx_inst_t;
189 typedef struct opt_inst_s opt_inst_t;
190 typedef struct seen_opt_s seen_opt_t;
191 typedef struct req_s req_t;
193 static char *
194 strtoken(char *s, char *token, size_t tok_len, char *pattern, int *pos);
196 static int
197 ctx_compare(const void *c1, const void *c2);
199 static void
200 ctx_free(void *o);
202 static void
203 ctx_inst_free(void *ci);
205 static void
206 opt_inst_free(void *oi);
208 static int
209 seen_opt_compare(const void *so1, const void *so2);
211 static void
212 incomp_bst_free(void *b);
214 static void
215 req_free(void *r);
217 static void
218 seen_opt_free(void *seen_opt);
220 static int
221 opt_compare(const void *o1, const void *o2);
223 static void
224 opt_free(void *o);
226 static int
227 par_compare(const void *a1, const void *a2);
229 static void
230 par_free(void *p);
232 static void
233 constraint_free(void *cstr);
235 static ctx_t *
236 locate_ctx(char *name);
238 static opt_t *
239 locate_opt(char *name);
241 static par_t *
242 locate_par(char *name, ctx_t *ctx);
244 static void
245 print_before_constraints(ll_t *list);
247 static void
248 print_options(ll_t *list,
249 int *has_optional,
250 int *has_ellipsis,
251 int *has_rule,
252 int *has_generic_arg,
253 int *has_ctx_change,
254 int *has_early_eval);
255 static void
256 print_explanations(int has_early_eval,
257 int has_ctx_change,
258 int has_generic_arg,
259 int has_optional,
260 int has_ellipsis,
261 int has_rule);
262 static void
263 bst_seen_opt_cb(const void *node, walk_order_e kind, int level);
265 static void
266 bst_seen_opt_seen_cb(const void *node, walk_order_e kind, int level);
268 static void
269 bst_print_ctx_cb(const void *node, walk_order_e kind, int level);
271 static void
272 bst_check_opt_cb(const void *node, walk_order_e kind, int level);
274 static void
275 bst_match_par_cb(const void *node, walk_order_e kind, int level);
277 static void
278 match_prefix_cb(const void *node, walk_order_e kind, int level);
280 static int
281 has_unseen_mandatory_opt(ctx_inst_t *ctx_inst, char **missing);
283 static int
284 opt_parse(char *s, opt_t **opt);
286 static int
287 init_opts(char *spec, ctx_t *ctx);
289 static int
290 ctxopt_build_cmdline_list(int nb_words, char **words);
292 static int
293 opt_set_parms(char *opt_name, char *par_str);
295 static ctx_inst_t *
296 new_ctx_inst(ctx_t *ctx, ctx_inst_t *prev_ctx_inst);
298 static void
299 evaluate_ctx_inst(ctx_inst_t *ctx_inst);
301 /* ****************************** */
302 /* Fatal messages implementation. */
303 /* ****************************** */
305 /* =================================================================== */
306 /* Fatal error function used when a fatal condition is encountered. */
307 /* This function is reserved for the ctxopt internal usage. */
308 /* */
309 /* format : printf like format. */
310 /* ... : remaining arguments interpreted using the format argument. */
311 /* =================================================================== */
312 static void
313 fatal_internal(const char *format, ...)
315 va_list args;
317 fprintf(stderr, "CTXOPT: ");
319 va_start(args, format);
320 vfprintf(stderr, format, args);
321 fprintf(stderr, "\n");
322 va_end(args);
324 exit(EXIT_FAILURE);
327 /* ====================================================================== */
328 /* Generic fatal error function. This one uses the global status ctxopt */
329 /* stored in the cur_state structure and can call custom error functions. */
330 /* registered by the users for a given error identifier if any. */
331 /* */
332 /* e : Error identifier responsible of the fatal error. */
333 /* errmsg : User's provided string specific to the error e. */
334 /* Note that errmsg is not used in all cases. */
335 /* */
336 /* CTXOPTMISPAR Missing parameter. */
337 /* CTXOPTREQPAR Option: all parameters in a required group are */
338 /* missing. */
339 /* CTXOPTMISARG Missing argument. */
340 /* CTXOPTUXPARG Unexpected argument. */
341 /* CTXOPTDUPOPT Duplicated option. */
342 /* CTXOPTUNKPAR Unknown parameter. */
343 /* CTXOPTINCOPT Incompatible option. */
344 /* CTXOPTCTEOPT Option: bad number of occurrences. */
345 /* CTXOPTCTLOPT Option: not enough occurrences. */
346 /* CTXOPTCTGOPT Option: too many occurrence of. */
347 /* CTXOPTCTEARG Arguments: bad number of occurrences. */
348 /* CTXOPTCTLARG Arguments: not enough occurrences. */
349 /* CTXOPTCTGARG Arguments: too many occurrences. */
350 /* ====================================================================== */
351 static void
352 fatal(errors e, char *errmsg)
354 if (err_functions[e] != NULL)
355 err_functions[e](e, cur_state);
356 else
358 switch (e)
360 case CTXOPTNOERR:
361 break;
363 case CTXOPTMISPAR:
364 if (cur_state->ctx_par_name != NULL)
365 fprintf(stderr,
366 "the mandatory parameter(s) %s are missing in the context "
367 "introduced by %s.\n",
368 errmsg,
369 cur_state->ctx_par_name);
370 else
371 fprintf(stderr,
372 "The mandatory parameter(s) %s are missing "
373 "in the main context.\n",
374 errmsg);
376 free(errmsg);
377 break;
379 case CTXOPTREQPAR:
380 fprintf(stderr,
381 errmsg,
382 cur_state->req_opt_par_needed,
383 cur_state->req_opt_par);
384 break;
386 case CTXOPTUNXARG:
387 if (cur_state->cur_opt_par_name != NULL)
388 fprintf(stderr,
389 "The parameter %s takes no arguments "
390 "or has too many arguments.\n",
391 cur_state->cur_opt_par_name);
392 break;
394 case CTXOPTMISARG:
395 if (cur_state->pre_opt_par_name != NULL)
396 fprintf(stderr,
397 "%s requires argument(s).\n",
398 cur_state->pre_opt_par_name);
399 else
400 fprintf(stderr,
401 "%s requires argument(s).\n",
402 cur_state->cur_opt_par_name);
403 break;
405 case CTXOPTDUPOPT:
406 if (cur_state->pre_opt_par_name != NULL)
407 fprintf(stderr,
408 "The parameter %s can only appear once in the context "
409 "introduced by %s.\n",
410 cur_state->cur_opt_params,
411 cur_state->ctx_par_name);
412 else
413 fprintf(stderr,
414 "The parameter %s can only appear once "
415 "in the main context.\n",
416 cur_state->cur_opt_params);
417 break;
419 case CTXOPTUNKPAR:
420 fprintf(stderr,
421 "Unknown parameter %s.\n%s",
422 cur_state->cur_opt_par_name,
423 errmsg);
424 break;
426 case CTXOPTINCOPT:
427 fprintf(stderr,
428 "The parameter %s is incompatible with %s.\n",
429 cur_state->cur_opt_par_name,
430 errmsg);
431 break;
433 case CTXOPTCTEOPT:
434 if (cur_state->ctx_par_name)
435 fprintf(stderr,
436 "The parameter %s must appear exactly %d times "
437 "in the context introduced by %s.\n",
438 cur_state->cur_opt_params,
439 cur_state->opts_count,
440 cur_state->ctx_par_name);
441 else
442 fprintf(stderr,
443 "The parameter %s must appear exactly %d times "
444 "in the main context.\n",
445 cur_state->cur_opt_params,
446 cur_state->opts_count);
447 break;
449 case CTXOPTCTLOPT:
450 if (cur_state->ctx_par_name)
451 fprintf(stderr,
452 "The parameter %s must appear less than %d times "
453 "in the context introduced by %s.\n",
454 cur_state->cur_opt_params,
455 cur_state->opts_count,
456 cur_state->ctx_par_name);
457 else
458 fprintf(stderr,
459 "The parameter %s must appear less than %d times "
460 "in the main context.\n",
461 cur_state->cur_opt_params,
462 cur_state->opts_count);
463 break;
465 case CTXOPTCTGOPT:
466 if (cur_state->ctx_par_name)
467 fprintf(stderr,
468 "The parameter %s must appear more than %d times "
469 "in the context introduced by %s.\n",
470 cur_state->cur_opt_params,
471 cur_state->opts_count,
472 cur_state->ctx_par_name);
473 else
474 fprintf(stderr,
475 "The parameter %s must appear more than %d times "
476 "in the main context.\n",
477 cur_state->cur_opt_params,
478 cur_state->opts_count);
479 break;
481 case CTXOPTCTEARG:
482 fprintf(stderr,
483 "The parameter %s must have exactly %d arguments.\n",
484 cur_state->cur_opt_par_name,
485 cur_state->opt_args_count);
486 break;
488 case CTXOPTCTLARG:
489 fprintf(stderr,
490 "The parameter %s must have less than %d arguments.\n",
491 cur_state->cur_opt_par_name,
492 cur_state->opt_args_count);
493 break;
495 case CTXOPTCTGARG:
496 fprintf(stderr,
497 "The parameter %s must have more than %d arguments.\n",
498 cur_state->cur_opt_par_name,
499 cur_state->opt_args_count);
500 break;
502 case CTXOPTERRSIZ:
503 break;
507 /* CTXOPTUNKPAR should display the full usage to help the user follow */
508 /* the chaining of contexts when several possible contexts have been */
509 /* identified. Otherwise, errmsg is the empty string and the display of */
510 /* the current usage is enough. */
511 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
512 if (e == CTXOPTUNKPAR && *errmsg != '\0')
513 ctxopt_disp_usage(continue_after);
514 else
515 ctxopt_ctx_disp_usage(NULL, continue_after);
517 exit(e); /* Exit with the error id e as return code. */
520 /* ********************************* */
521 /* Memory management implementation. */
522 /* ********************************* */
524 /* ================== */
525 /* Customized malloc. */
526 /* ================== */
527 static void *
528 xmalloc(size_t size)
530 void *allocated;
531 size_t real_size;
533 real_size = (size > 0) ? size : 1;
534 allocated = malloc(real_size);
535 if (allocated == NULL)
536 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
537 (unsigned long int)size);
539 return allocated;
542 /* ================== */
543 /* Customized calloc. */
544 /* ================== */
545 static void *
546 xcalloc(size_t n, size_t size)
548 void *allocated;
550 n = (n > 0) ? n : 1;
551 size = (size > 0) ? size : 1;
552 allocated = calloc(n, size);
553 if (allocated == NULL)
554 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
555 (unsigned long int)size);
557 return allocated;
560 /* =================== */
561 /* Customized realloc. */
562 /* =================== */
563 static void *
564 xrealloc(void *p, size_t size)
566 void *allocated;
568 allocated = realloc(p, size);
569 if (allocated == NULL && size > 0)
570 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
571 (unsigned long int)size);
573 return allocated;
576 /* ==================================== */
577 /* strdup implementation using xmalloc. */
578 /* ==================================== */
579 static char *
580 xstrdup(const char *p)
582 char *allocated;
584 allocated = xmalloc(strlen(p) + 1);
585 strcpy(allocated, p);
587 return allocated;
590 /* =================================================== */
591 /* strndup implementation using xmalloc. */
592 /* This version guarantees that there is a final '\0'. */
593 /* =================================================== */
594 static char *
595 xstrndup(const char *str, size_t len)
597 char *p;
599 p = memchr(str, '\0', len);
601 if (p)
602 len = p - str;
604 p = xmalloc(len + 1);
605 memcpy(p, str, len);
606 p[len] = '\0';
608 return p;
611 /* *************************** */
612 /* Linked list implementation. */
613 /* *************************** */
615 /* Linked list node structure. */
616 /* """"""""""""""""""""""""""" */
617 struct ll_node_s
619 void *data;
620 struct ll_node_s *next;
621 struct ll_node_s *prev;
624 /* Linked List structure. */
625 /* """""""""""""""""""""" */
626 struct ll_s
628 ll_node_t *head;
629 ll_node_t *tail;
630 long len;
633 /* ========================= */
634 /* Create a new linked list. */
635 /* ========================= */
636 static ll_t *
637 ll_new(void)
639 ll_t *ret = xmalloc(sizeof(ll_t));
640 ll_init(ret);
642 return ret;
645 /* =============================================== */
646 /* Free all the elements of a list (make it empty) */
647 /* NULL or a custom function may be used to free */
648 /* the sub components of the elements. */
649 /* =============================================== */
650 static void
651 ll_free(ll_t * const list, void (*clean)(void *))
653 ll_node_t *node;
655 if (list)
657 node = list->head;
659 while (node)
661 /* Apply a custom cleaner if not NULL. */
662 /* """"""""""""""""""""""""""""""""""" */
663 if (clean)
664 clean(node->data);
666 ll_delete(list, node);
668 node = list->head;
673 /* ==================================== */
674 /* Destroy a list and all its elements. */
675 /* ==================================== */
676 static void
677 ll_destroy(ll_t *list, void (*clean)(void *))
679 if (list)
681 ll_free(list, clean);
682 free(list);
686 /* ========================= */
687 /* Initialize a linked list. */
688 /* ========================= */
689 static void
690 ll_init(ll_t *list)
692 list->head = NULL;
693 list->tail = NULL;
694 list->len = 0;
697 /* ===================================================== */
698 /* Allocate the space for a new node in the linked list. */
699 /* ===================================================== */
700 static ll_node_t *
701 ll_new_node(void)
703 ll_node_t *ret = xmalloc(sizeof(ll_node_t));
705 return ret;
708 /* ==================================================================== */
709 /* Append a new node filled with its data at the end of the linked list */
710 /* The user is responsible for the memory management of the data. */
711 /* ==================================================================== */
712 static void
713 ll_append(ll_t * const list, void * const data)
715 ll_node_t *node;
717 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
718 | uses xmalloc which does not return if there *
719 | is an allocation error. */
721 node->data = data;
722 node->next = NULL; /* This node will be the last. */
723 node->prev = list->tail; /* NULL if it is a new list. */
725 if (list->tail)
726 list->tail->next = node;
727 else
728 list->head = node;
730 list->tail = node;
732 ++list->len; /* One more node in the list. */
735 /* ================================================================== */
736 /* Put a new node filled with its data at the beginning of the linked */
737 /* list. */
738 /* The user is responsible for the memory management of the data. */
739 /* ================================================================== */
740 static void
741 ll_prepend(ll_t * const list, void * const data)
743 ll_node_t *node;
745 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
746 | uses xmalloc which does not return if there *
747 | is an allocation error. */
749 node->data = data;
750 node->prev = NULL; /* This node will be the first. */
751 node->next = list->head; /* NULL if it is a new list. */
753 if (list->head)
754 list->head->prev = node;
755 else
756 list->tail = node;
758 list->head = node;
760 ++list->len; /* One more node in the list. */
763 /* ======================================================== */
764 /* Insert a new node before the specified node in the list. */
765 /* ======================================================== */
766 static void
767 ll_insert_before(ll_t * const list, ll_node_t *node, void * const data)
769 ll_node_t *new_node;
771 if (node->prev == NULL)
772 ll_prepend(list, data);
773 else
775 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
776 | uses xmalloc which does not return if there *
777 | is an allocation error. */
779 new_node->data = data;
780 new_node->next = node;
781 new_node->prev = node->prev;
783 node->prev->next = new_node;
784 node->prev = new_node;
786 ++list->len; /* One more node in the list. */
790 /* ======================================================= */
791 /* Insert a new node after the specified node in the list. */
792 /* ======================================================= */
793 static void
794 ll_insert_after(ll_t * const list, ll_node_t *node, void * const data)
796 ll_node_t *new_node;
798 if (node->next == NULL)
799 ll_append(list, data);
800 else
802 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
803 | uses xmalloc which does not return if there *
804 | is an allocation error. */
806 new_node->data = data;
807 new_node->prev = node;
808 new_node->next = node->next;
810 node->next->prev = new_node;
811 node->next = new_node;
813 ++list->len; /* One more node in the list. */
817 /* ================================================================= */
818 /* Remove a node from a linked list. */
819 /* The memory taken by the deleted node must be freed by the caller. */
820 /* ================================================================= */
821 static int
822 ll_delete(ll_t * const list, ll_node_t *node)
824 if (list->head == list->tail)
826 /* We delete the last remaining element from the list. */
827 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
828 if (list->head == NULL)
829 return 0;
831 list->head = list->tail = NULL;
833 else if (node->prev == NULL)
835 /* We delete the first element from the list. */
836 /* """""""""""""""""""""""""""""""""""""""""" */
837 list->head = node->next;
838 list->head->prev = NULL;
840 else if (node->next == NULL)
842 /* We delete the last element from the list. */
843 /* """"""""""""""""""""""""""""""""""""""""" */
844 list->tail = node->prev;
845 list->tail->next = NULL;
847 else
849 /* We delete an element from the list. */
850 /* """"""""""""""""""""""""""""""""""" */
851 node->next->prev = node->prev;
852 node->prev->next = node->next;
855 free(node);
857 --list->len; /* One less node in the list. */
859 return 1;
862 /* ==================================================================== */
863 /* Allocate and fill an array of strings from a list. */
864 /* WARNINGS: */
865 /* 1) The list node must contain strings (char *). */
866 /* 2) The strings in the resulting array MUST NOT be freed as the are */
867 /* NOT copied from the strings of the list. */
868 /* */
869 /* IN list : The list from which the array is generated. */
870 /* IN start_node : The node of the list which will be the first node to */
871 /* consider to create the array. */
872 /* OUT: count : The number of elements of the resulting array. */
873 /* OUT: array : The resulting array or NULL if the list is empty. */
874 /* RC : : The number of elements of the resulting array. */
875 /* ==================================================================== */
876 static int
877 ll_strarray(ll_t *list, ll_node_t *start_node, int *count, char ***array)
879 int n = 0;
880 ll_node_t *node;
882 *count = 0;
884 node = start_node;
886 if (list == NULL || node == NULL)
888 *array = NULL;
890 return 0;
893 *array = xmalloc((list->len + 1) * sizeof(char *));
894 while (node != NULL)
896 (*array)[n++] = (char *)(node->data);
897 (*count)++;
899 node = node->next;
902 (*array)[*count] = NULL;
904 return *count;
907 /* ******************************************************************* */
908 /* BST (search.h compatible) implementation. */
909 /* */
910 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
911 /* the AT&T man page says. */
912 /* */
913 /* Written by reading the System V Interface Definition, not the code. */
914 /* */
915 /* Totally public domain. */
916 /* ******************************************************************* */
918 struct bst_s
920 void *key;
921 struct bst_s *llink;
922 struct bst_s *rlink;
925 #if 0 /* Unused yet. */
926 /* =========================== */
927 /* Delete node with given key. */
928 /* =========================== */
929 static void *
930 bst_delete(const void * vkey, void ** vrootp,
931 int (*compar)(const void *, const void *))
933 bst_t ** rootp = (bst_t **)vrootp;
934 bst_t * p, *q, *r;
935 int cmp;
937 if (rootp == NULL || (p = *rootp) == NULL)
938 return NULL;
940 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
942 p = *rootp;
943 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
944 : &(*rootp)->rlink; /* follow rlink branch */
945 if (*rootp == NULL)
946 return NULL; /* key not found */
948 r = (*rootp)->rlink; /* D1: */
949 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
950 q = r;
951 else if (r != NULL)
952 { /* Right link is NULL? */
953 if (r->llink == NULL)
954 { /* D2: Find successor */
955 r->llink = q;
956 q = r;
958 else
959 { /* D3: Find NULL link */
960 for (q = r->llink; q->llink != NULL; q = r->llink)
961 r = q;
962 r->llink = q->rlink;
963 q->llink = (*rootp)->llink;
964 q->rlink = (*rootp)->rlink;
967 if (p != *rootp)
968 free(*rootp); /* D4: Free node */
969 *rootp = q; /* link parent to new node */
970 return p;
972 #endif
974 /* ===================================================================== */
975 /* Destroy a tree. */
976 /* The clean function pointer can be NULL, in this case the node content */
977 /* is not freed. */
978 /* ===================================================================== */
979 static void
980 bst_destroy(void *vrootp, void (*clean)(void *))
982 bst_t *root = (bst_t *)vrootp;
984 if (root == NULL)
985 return;
987 bst_destroy(root->llink, clean);
988 bst_destroy(root->rlink, clean);
990 if (clean)
991 clean((void *)root->key);
993 free(root);
996 /* ========================= */
997 /* Find a node, or return 0. */
998 /* ========================= */
999 static void *
1000 bst_find(const void *vkey,
1001 void * const *vrootp,
1002 int (*compar)(const void *, const void *))
1004 bst_t * const *rootp = (bst_t * const *)vrootp;
1006 if (rootp == NULL)
1007 return NULL;
1009 while (*rootp != NULL)
1010 { /* T1: */
1011 int r;
1013 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
1014 return *rootp; /* key found */
1015 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
1016 : &(*rootp)->rlink; /* T4: follow right branch */
1018 return NULL;
1021 /* ======================================= */
1022 /* Find or inserts datum into search tree. */
1023 /* ======================================= */
1024 static void *
1025 bst_search(void *vkey, void **vrootp, int (*compar)(const void *, const void *))
1027 bst_t *q;
1028 bst_t **rootp = (bst_t **)vrootp;
1030 if (rootp == NULL)
1031 return NULL;
1033 while (*rootp != NULL)
1034 { /* Knuth's T1: */
1035 int r;
1037 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
1038 return *rootp; /* we found it! */
1040 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
1041 : &(*rootp)->rlink; /* T4: follow right branch */
1044 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
1045 if (q != 0)
1046 { /* make new node */
1047 *rootp = q; /* link new node to old */
1048 q->key = vkey; /* initialize new node */
1049 q->llink = q->rlink = NULL;
1051 return q;
1054 /* ========================= */
1055 /* Walk the nodes of a tree. */
1056 /* ========================= */
1057 static void
1058 bst_walk_recurse(const bst_t *root,
1059 void (*action)(const void *, walk_order_e, int),
1060 int level)
1062 if (root->llink == NULL && root->rlink == NULL)
1063 (*action)(root, leaf, level);
1064 else
1066 (*action)(root, preorder, level);
1067 if (root->llink != NULL)
1068 bst_walk_recurse(root->llink, action, level + 1);
1069 (*action)(root, postorder, level);
1070 if (root->rlink != NULL)
1071 bst_walk_recurse(root->rlink, action, level + 1);
1072 (*action)(root, endorder, level);
1076 static void
1077 bst_walk(const void *vroot, void (*action)(const void *, walk_order_e, int))
1079 if (vroot != NULL && action != NULL)
1080 bst_walk_recurse(vroot, action, 0);
1083 /* ************************ */
1084 /* Various implementations. */
1085 /* ************************ */
1087 /* ======================== */
1088 /* Trim leading characters. */
1089 /* ======================== */
1090 static void
1091 ltrim(char *str, const char *trim_str)
1093 size_t len = strlen(str);
1094 size_t begin = strspn(str, trim_str);
1095 size_t i;
1097 if (begin > 0)
1098 for (i = begin; i <= len; ++i)
1099 str[i - begin] = str[i];
1102 /* ================================================= */
1103 /* Trim trailing characters. */
1104 /* The resulting string will have at least min bytes */
1105 /* even if trailing spaces remain. */
1106 /* ================================================= */
1107 static void
1108 rtrim(char *str, const char *trim_str, size_t min)
1110 size_t len = strlen(str);
1111 while (len > min && strchr(trim_str, str[len - 1]))
1112 str[--len] = '\0';
1115 /* ================================================== */
1116 /* Count the number of occurrences of the character c */
1117 /* in the string str. */
1118 /* The str pointer is assumed to be not NULL. */
1119 /* ================================================== */
1120 static int
1121 strchrcount(char *str, char c)
1123 int count = 0;
1125 while (*str)
1126 if (*str++ == c)
1127 count++;
1129 return count;
1132 /* =============================================== */
1133 /* Is the string str2 a prefix of the string str1? */
1134 /* =============================================== */
1135 static int
1136 strpref(char *str1, char *str2)
1138 while (*str1 != '\0' && *str1 == *str2)
1140 str1++;
1141 str2++;
1144 return *str2 == '\0';
1147 /* ========================== */
1148 /* Like strcmp ignoring case. */
1149 /* ========================== */
1150 static int
1151 stricmp(const char *s1, const char *s2)
1153 while (tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1155 if (*s1 == '\0')
1156 return 0;
1158 s1++;
1159 s2++;
1162 return (int)tolower((unsigned char)*s1) - (int)tolower((unsigned char)*s2);
1165 /* ====================================================================== */
1166 /* Strings concatenation with dynamic memory allocation. */
1167 /* IN : a variable number of char * arguments with NULL terminating */
1168 /* the sequence. */
1169 /* The first one must have been dynamically allocated and is */
1170 /* mandatory. */
1171 /* */
1172 /* Returns a new allocated string containing the concatenation of all */
1173 /* the arguments. It is the caller's responsibility to free the resulting */
1174 /* string. */
1175 /* ====================================================================== */
1176 static char *
1177 strappend(char *str, ...)
1179 size_t l;
1180 va_list args;
1181 char *s;
1183 l = 1 + strlen(str);
1184 va_start(args, str);
1186 s = va_arg(args, char *);
1188 while (s)
1190 l += strlen(s);
1191 s = va_arg(args, char *);
1194 va_end(args);
1196 if (l > 0)
1197 str = xrealloc(str, l);
1199 va_start(args, str);
1200 s = va_arg(args, char *);
1202 while (s)
1204 strcat(str, s);
1205 s = va_arg(args, char *);
1207 va_end(args);
1209 return str;
1212 /* ====================================================================== */
1213 /* Public domain strtok_r() by Charlie Gordon. */
1214 /* from comp.lang.c 9/14/2007 */
1215 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1216 /* */
1217 /* (Declaration that it's public domain): */
1218 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1219 /* */
1220 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1221 /* *end == NULL. */
1222 /* ====================================================================== */
1223 static char *
1224 xstrtok_r(char *str, const char *delim, char **end)
1226 char *ret;
1228 if (str == NULL)
1229 str = *end;
1231 if (str == NULL)
1232 return NULL;
1234 str += strspn(str, delim);
1236 if (*str == '\0')
1237 return NULL;
1239 ret = str;
1241 str += strcspn(str, delim);
1243 if (*str)
1244 *str++ = '\0';
1246 *end = str;
1248 return ret;
1251 /* ===================================================================== */
1252 /* Put the first word of str, truncated to len characters, in buf. */
1253 /* Return a pointer in str pointing just after the word. */
1254 /* buf must have been pre-allocated to accept at least len+1 characters. */
1255 /* Note that buf can contains a sting full of spaces is str was not */
1256 /* trimmed before the call. */
1257 /* ===================================================================== */
1258 char *
1259 get_word(char *str, char *buf, size_t len)
1261 char *s = str;
1263 /* Skip spaces. */
1264 /* """""""""""" */
1265 while (*s && isspace(*s))
1266 s++;
1268 /* Set the new string start. */
1269 /* """"""""""""""""""""""""" */
1270 str = s;
1272 /* Get the word. */
1273 /*"""""""""""""" */
1274 while (*s && !isspace(*s) && s - str < (ptrdiff_t)len)
1275 s++;
1277 strncpy(buf, str, s - str);
1278 buf[s - str] = 0;
1280 return s;
1283 /* ==================================================================== */
1284 /* Return 1 is value is "1" or "yes" (ignoring case). */
1285 /* Return 0 is value is "0" or "no" (ignoring case). */
1286 /* If value has another value, then set invalid to 1 and also return 0 */
1287 /* invalid is set to 0 in all the other cases. */
1288 /* ==================================================================== */
1289 static int
1290 eval_yes(char *value, int *invalid)
1292 *invalid = 0;
1294 if (strcmp(value, "1") == 0 || stricmp(value, "yes") == 0)
1295 return 1;
1296 else if (strcmp(value, "0") != 0 && stricmp(value, "no") != 0)
1297 *invalid = 1;
1299 return 0;
1302 /* =========================================================== */
1303 /* Fill an array of strings from the words composing a string. */
1304 /* */
1305 /* str: initial string which will be altered. */
1306 /* args: array of pointers to the start of the words in str. */
1307 /* max: maximum number of words used before giving up. */
1308 /* return: the number of words (<=max). */
1309 /* =========================================================== */
1310 static int
1311 str2argv(char *str, char **args, int max)
1313 int nb_args = 0;
1315 while (*str)
1317 if (nb_args >= max)
1318 return nb_args;
1320 while (*str == ' ' || *str == '\t')
1321 *(str++) = '\0';
1323 if (!*str)
1324 return nb_args;
1326 args[nb_args] = str;
1327 nb_args++;
1329 while (*str && (*str != ' ') && (*str != '\t'))
1330 str++;
1333 return nb_args;
1336 /* ********************** */
1337 /* ctxopt implementation. */
1338 /* ********************** */
1340 static int ctxopt_initialized = 0; /* cap_init has not yet been called. */
1342 /* Flags structure initialized by ctxopt_init. */
1343 /* """"""""""""""""""""""""""""""""""""""""""" */
1344 struct flags_s
1346 int stop_if_non_option;
1347 int allow_abbreviations;
1348 int display_usage_on_error;
1351 static flags_t flags = { 0, 1, 1 };
1353 /* Context structure. */
1354 /* """""""""""""""""" */
1355 struct ctx_s
1357 char *name;
1358 ll_t *opt_list; /* list of options allowed in this context. */
1359 ll_t *incomp_list; /* list of strings containing incompatible names *
1360 | of options separated by spaces or tabs. */
1361 ll_t *req_list; /* list of strings containing an option name and *
1362 | all the option names where at least one of *
1363 | them is required to be also present. */
1365 int (*action)(char *name,
1366 int type,
1367 char *new_ctx,
1368 int ctx_nb_data,
1369 void **ctx_data);
1370 void *par_bst;
1371 int nb_data;
1372 void **data;
1375 /* https://textik.com/#488ce3649b6c60f5 */
1376 /* */
1377 /* +--------------+ */
1378 /* |first_ctx_inst| */
1379 /* +---+----------+ */
1380 /* | */
1381 /* +--v-----+ +--------+ +--------+ +-----+ */
1382 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1383 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1384 /* | | | | | */
1385 /* | | +-v------+ | | */
1386 /* | +--+ctx_inst<-----------+ | */
1387 /* | +-+------+ | */
1388 /* | | | */
1389 /* | +-v------+ | */
1390 /* +------+ctx_inst<--------------------------+ */
1391 /* +-+------+ */
1392 /* | */
1393 /* +-v---+ */
1394 /* | ... | */
1395 /* +-----+ */
1397 /* Option structure. */
1398 /* """"""""""""""""" */
1399 struct opt_s
1401 char *name; /* option name. */
1402 char *next_ctx; /* new context this option may lead to */
1403 ll_t *ctx_list; /* list of contexts allowing this option. */
1404 char *params; /* string containing all the parameters of *
1405 | the option. */
1407 void (*action)( /* The option associated action. */
1408 char *ctx_name, /* context name. */
1409 char *opt_name, /* option name. */
1410 char *par, /* option parameter. */
1411 int nb_args, /* number of arguments. */
1412 char **args, /* option arguments. */
1413 int nb_opt_data, /* number of option data pointers. */
1414 void **opt_data, /* option data pointers. */
1415 int nb_ctx_data, /* nb of current context data ptrs. */
1416 void **ctx_data /* current context data pointers. */
1419 int visible_in_help; /* visibility in help. */
1420 int nb_data; /* number of the data pointers passed as argument to action. */
1421 void **data; /* array of data pointers passed as argument to action. */
1423 int args; /* 1 if this option takes arguments else 0. */
1424 int optional; /* 1 if the option is optional, else 0. */
1425 int multiple; /* 1 if the option can appear more than one time in a *
1426 | context, else 0. */
1428 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1429 int occurrences; /* Number of option occurrences in a context. */
1430 char opt_count_oper; /* <, = or > */
1431 int opt_count_mark; /* Value to be compared to with opt_count_oper. */
1433 char *arg; /* symbolic text after # describing the option argument. */
1435 int optional_args; /* 1 of option is optional else 0. */
1436 int multiple_args; /* 1 is option can appear more than once in a context *
1437 | instance. */
1439 int opt_args_count_matter; /* 1 if count is restricted, else 0. */
1440 char opt_args_count_oper; /* <, = or > */
1441 int opt_args_count_mark; /* Value to be compared to with *
1442 | opt_count_oper. */
1444 int eval_first; /* 1 if this option must be evaluated before the options *
1445 | without this mark. */
1447 ll_t *eval_before_list; /* List of pointers on options which must be *
1448 | evaluated before this option. */
1450 ll_t *constraints_list; /* List of constraint check functions pointers. */
1453 /* Context instance structure. */
1454 /* """"""""""""""""""""""""""" */
1455 struct ctx_inst_s
1457 ctx_t *ctx; /* the context whose this is an instance of */
1458 ctx_inst_t *prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1459 | creation of this ctx_inst structure. */
1460 opt_inst_t *gen_opt_inst; /* opt_inst which led to the creation of a *
1461 | instance of this structure. */
1462 ll_t *incomp_bst_list; /* list of seen_opt_t BST. */
1463 void *seen_opt_bst; /* tree of seen_opt_t. */
1464 ll_t *opt_req_list; /* list of req_t. */
1465 ll_t *opt_inst_list; /* The list of option instances in this *
1466 | context instance. */
1467 char *par_name; /* parameter which created this instance. */
1470 /* Option instance structure. */
1471 /* """""""""""""""""""""""""" */
1472 struct opt_inst_s
1474 opt_t *opt; /* The option this is an instance of. */
1475 char *opt_name; /* The option which led to this creation. */
1476 char *par; /* The parameter which led to this creation. */
1477 ll_t *values_list; /* The list of arguments of this option. */
1478 ctx_inst_t *next_ctx_inst; /* The new context instance this option. *
1479 | instance may create. */
1482 /* Structure used to check if an option has bee seen or not */
1483 /* in a context instance. */
1484 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1485 struct seen_opt_s
1487 opt_t *opt; /* The concerned option. */
1488 char *par; /* Parameter which led to the making of this structure. */
1489 int seen; /* 1 if seen in the context instances, else 0. */
1492 /* Structure used to check if at least one instance of the options whose */
1493 /* pointers are in or_opt_list has been seen in the ctx_inst where an */
1494 /* instance or opt is also present. */
1495 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1496 struct req_s
1498 opt_t *opt; /* Option that asks for other options. */
1499 ll_t *or_opt_list; /* Required options, at least one of them *
1500 | must be present. */
1503 /* Parameter structure which links a parameter to the option it belongs to. */
1504 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1505 struct par_s
1507 char *name; /* Parameter name (with the leading -). */
1508 opt_t *opt; /* Attached option. */
1511 /* Constraint structure. */
1512 /* """"""""""""""""""""" */
1513 struct constraint_s
1515 int (*constraint)(int nb_args, char **args, char *value, char *parameter);
1516 int nb_args;
1517 char **args;
1518 char *to_free; /* pointer to the original string in which the array in *
1519 | args points to. This pointer is kept there to allow *
1520 | it to be freed. */
1523 state_t *cur_state = NULL; /* Current analysis state. */
1524 static ll_t *cmdline_list = NULL; /* List of interpreted CLI words *
1525 | serves as the basis for the *
1526 | analysis of the parameters. */
1527 static ctx_t *main_ctx = NULL; /* initial context. */
1528 static ctx_inst_t *first_ctx_inst = NULL; /* Pointer to the fist context *
1529 | instance which holds the *
1530 | options instances. */
1531 static ll_t *ctx_inst_list = NULL; /* List of the context instances. */
1533 /* ======================================================= */
1534 /* Parse a string for the next matching token. */
1535 /* */
1536 /* s: string to parse. */
1537 /* token: pre_allocated array of max tok_len characters. */
1538 /* pattern: scanf type pattern token must match. */
1539 /* pos: number of characters successfully parsed in s. */
1540 /* */
1541 /* Returns: a pointer to the first unread character or */
1542 /* to he terminating \0. */
1543 /* ======================================================= */
1544 static char *
1545 strtoken(char *s, char *token, size_t tok_len, char *pattern, int *pos)
1547 char *full_pattern;
1548 char len[3];
1549 int n;
1551 *pos = 0;
1553 n = snprintf(len, 3, "%zu", tok_len);
1554 if (n < 0)
1555 return NULL;
1557 full_pattern = xmalloc(strlen(pattern) + n + 4);
1559 strcpy(full_pattern, "%");
1560 strcat(full_pattern, len);
1561 strcat(full_pattern, pattern);
1562 strcat(full_pattern, "%n");
1564 n = sscanf(s, full_pattern, token, pos);
1566 free(full_pattern);
1568 if (n != 1)
1569 return NULL;
1571 return s + *pos;
1574 /* ****************************************** */
1575 /* Various comparison and deletion functions. */
1576 /* ****************************************** */
1578 static int
1579 ctx_compare(const void *c1, const void *c2)
1581 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1584 /* =========================== */
1585 /* Free a context_bst element. */
1586 /* =========================== */
1587 static void
1588 ctx_free(void *c)
1590 ctx_t *ctx = c;
1592 free(ctx->name);
1593 free(ctx->data);
1595 ll_destroy(ctx->opt_list, NULL);
1596 ll_destroy(ctx->incomp_list, free);
1597 ll_destroy(ctx->req_list, free);
1598 bst_destroy(ctx->par_bst, par_free);
1600 free(c);
1603 /* ============================= */
1604 /* Free a ctx_inst_list element. */
1605 /* ============================= */
1606 static void
1607 ctx_inst_free(void *ci)
1609 ctx_inst_t *ctx_inst = ci;
1611 free(ctx_inst->par_name);
1612 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1613 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1614 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1615 ll_destroy(ctx_inst->opt_req_list, req_free);
1617 free(ci);
1620 /* ============================== */
1621 /* Free an opt_inst_list element. */
1622 /* ============================== */
1623 static void
1624 opt_inst_free(void *oi)
1626 opt_inst_t *opt_inst = oi;
1628 ll_destroy(opt_inst->values_list, NULL);
1630 free(oi);
1633 /* ================================== */
1634 /* Compare two seen_opt_bst elements. */
1635 /* ================================== */
1636 static int
1637 seen_opt_compare(const void *so1, const void *so2)
1639 opt_t *o1, *o2;
1641 o1 = ((seen_opt_t *)so1)->opt;
1642 o2 = ((seen_opt_t *)so2)->opt;
1644 return strcmp(o1->name, o2->name);
1647 /* ============================ */
1648 /* Free a seen_opt_bst element. */
1649 /* ============================ */
1650 void
1651 seen_opt_free(void *so)
1653 seen_opt_t *seen_opt = so;
1655 free(seen_opt->par);
1657 free(so);
1660 /* =========================== */
1661 /* Free an incomp_bst element. */
1662 /* =========================== */
1663 static void
1664 incomp_bst_free(void *b)
1666 bst_t *bst = b;
1668 bst_destroy(bst, NULL);
1671 /* ============================= */
1672 /* Free an opt_req_list element. */
1673 /* ============================= */
1674 static void
1675 req_free(void *r)
1677 req_t *req = r;
1679 ll_destroy(req->or_opt_list, NULL);
1680 free(req);
1683 /* ================================= */
1684 /* Compare two options_bst elements. */
1685 /* ================================= */
1686 static int
1687 opt_compare(const void *o1, const void *o2)
1689 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1692 /* ============================= */
1693 /* Free an options_bst elements. */
1694 /* ============================= */
1695 void
1696 opt_free(void *o)
1698 opt_t *opt = o;
1700 free(opt->name);
1701 free(opt->next_ctx);
1702 free(opt->params);
1703 free(opt->arg);
1704 free(opt->data);
1706 ll_destroy(opt->ctx_list, NULL);
1707 ll_destroy(opt->constraints_list, constraint_free);
1708 ll_destroy(opt->eval_before_list, NULL);
1710 free(o);
1713 /* ============================= */
1714 /* Compare two par_bst elements. */
1715 /* ============================= */
1716 static int
1717 par_compare(const void *a1, const void *a2)
1719 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1722 /* ======================= */
1723 /* Free a par_bst element. */
1724 /* ======================= */
1725 static void
1726 par_free(void *p)
1728 par_t *par = p;
1730 free(par->name);
1732 free(p);
1735 /* ================================ */
1736 /* Free a constraints_list element. */
1737 /* ================================ */
1738 static void
1739 constraint_free(void *c)
1741 constraint_t *cstr = c;
1743 free(cstr->args);
1744 free(cstr->to_free);
1746 free(c);
1749 /* ******************************************************************** */
1750 /* Helper functions to locate contexts, options and parameters in a BST */
1751 /* by their names. */
1752 /* ******************************************************************** */
1754 static ctx_t *
1755 locate_ctx(char *name)
1757 bst_t *node;
1758 ctx_t ctx = { 0 };
1760 ctx.name = name;
1762 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1763 return NULL;
1764 else
1765 return node->key;
1768 static opt_t *
1769 locate_opt(char *name)
1771 bst_t *node;
1772 opt_t opt = { 0 };
1774 opt.name = name;
1776 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1777 return NULL;
1778 else
1779 return node->key;
1782 static par_t *
1783 locate_par(char *name, ctx_t *ctx)
1785 bst_t *node;
1786 par_t par = { 0 };
1787 void *bst = ctx->par_bst;
1789 par.name = name;
1791 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1792 return NULL;
1793 else
1794 return node->key;
1797 /* ====================================================================== */
1798 /* Helper function to display the dependency constraints between options. */
1799 /* These constraints are set with the ctxopt_add_opt_settings function */
1800 /* using the 'before' and 'after' arguments. */
1801 /* IN list : a list of options. */
1802 /* ====================================================================== */
1803 static void
1804 print_before_constraints(ll_t *list)
1806 ll_node_t *node = list->head;
1807 ll_node_t *before_node;
1808 opt_t *opt, *before_opt;
1809 int msg = 0;
1811 while (node != NULL)
1813 opt = node->data;
1815 if (opt->eval_before_list->len > 0)
1817 if (!msg)
1819 printf("\n If present in the command line,");
1820 msg = 1; /* Display this message only once. */
1823 before_node = opt->eval_before_list->head;
1825 printf("\n ");
1826 while (before_node != NULL)
1828 before_opt = before_node->data;
1829 printf("%s", before_opt->params);
1831 before_node = before_node->next;
1833 if (before_node != NULL)
1834 printf(" and\n ");
1836 printf(" will be evaluated after %s\n", opt->params);
1838 node = node->next;
1842 /* =================================================================== */
1843 /* Utility function to format and print the options present in a list. */
1844 /* */
1845 /* IN list : a list of options. */
1846 /* OUT has_* : a set of flags which will determine the content of the */
1847 /* explanation given after the formatted printing of the */
1848 /* options. */
1849 /* =================================================================== */
1850 static void
1851 print_options(ll_t *list,
1852 int *has_optional,
1853 int *has_ellipsis,
1854 int *has_rule,
1855 int *has_generic_arg,
1856 int *has_ctx_change,
1857 int *has_early_eval)
1859 ll_node_t *node = list->head;
1860 opt_t *opt;
1861 char *line;
1862 char *option;
1864 line = xstrdup(" ");
1866 while (node != NULL)
1868 option = xstrdup("");
1869 opt = node->data;
1871 /* Skip option set as not visible in help. */
1872 /* """"""""""""""""""""""""""""""""""""""" */
1873 if (!opt->visible_in_help)
1875 node = node->next;
1876 continue;
1879 if (opt->optional)
1881 option = strappend(option, "[", (char *)0);
1882 *has_optional = 1;
1885 if (opt->eval_first)
1887 option = strappend(option, "*", (char *)0);
1888 *has_early_eval = 1;
1891 option = strappend(option, opt->params, (char *)0);
1893 if (opt->next_ctx != NULL)
1895 option = strappend(option, ">", opt->next_ctx, (char *)0);
1896 *has_ctx_change = 1;
1899 if (opt->multiple)
1901 if (opt->opt_count_oper != '\0')
1903 char m[4];
1904 char o[2];
1905 o[0] = opt->opt_count_oper;
1906 o[1] = '\0';
1907 snprintf(m, 3, "%u", opt->opt_count_mark);
1908 option = strappend(option, "...", o, m, (char *)0);
1909 *has_rule = 1;
1911 else
1912 option = strappend(option, "...", (char *)0);
1914 *has_ellipsis = 1;
1917 if (opt->args)
1919 if (*(opt->arg) == '#')
1920 *has_generic_arg = 1;
1922 option = strappend(option, " ", (char *)0);
1924 if (opt->optional_args)
1926 option = strappend(option, "[", opt->arg, (char *)0);
1927 *has_optional = 1;
1929 else
1930 option = strappend(option, opt->arg, (char *)0);
1932 if (opt->multiple_args)
1934 if (opt->opt_args_count_oper != '\0')
1936 char m[4];
1937 char o[2];
1938 o[0] = opt->opt_args_count_oper;
1939 o[1] = '\0';
1940 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1941 option = strappend(option, "...", o, m, (char *)0);
1942 *has_rule = 1;
1944 else
1945 option = strappend(option, "...", (char *)0);
1947 *has_ellipsis = 1;
1949 if (opt->optional_args)
1950 option = strappend(option, "]", (char *)0);
1952 if (opt->optional)
1953 option = strappend(option, "]", (char *)0);
1955 if (strlen(line) + 1 + strlen(option) < 80)
1956 line = strappend(line, option, " ", (char *)0);
1957 else
1959 printf("%s\n", line);
1960 line[2] = '\0';
1961 line = strappend(line, option, " ", (char *)0);
1964 free(option);
1966 node = node->next;
1969 printf("%s\n", line);
1971 free(line);
1974 /* ==================================================== */
1975 /* Explain the special syntactic symbols present in the */
1976 /* generated usage messages. */
1977 /* ==================================================== */
1978 static void
1979 print_explanations(int has_early_eval,
1980 int has_ctx_change,
1981 int has_generic_arg,
1982 int has_optional,
1983 int has_ellipsis,
1984 int has_rule)
1986 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1987 || has_ellipsis || has_rule)
1989 printf("\nExplanation of the syntax used above:\n");
1990 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1991 "must be entered.\n");
1992 printf("The following is just there to explain the other symbols "
1993 "displayed.\n\n");
1995 if (has_early_eval)
1996 printf("* : the parameters defined for this option will "
1997 "be evaluated first.\n");
1998 if (has_ctx_change)
1999 printf("> : the context after this symbol will be the new "
2000 "default context.\n");
2001 if (has_generic_arg)
2002 printf("#tag : argument with a hint about its meaning.\n");
2003 if (has_optional)
2004 printf("[...] : the object between square brackets is "
2005 "optional.\n");
2006 if (has_ellipsis)
2007 printf("... : several occurrences of the previous object "
2008 "are possible.\n");
2009 if (has_rule)
2010 printf("[<|=|>]number: rules constraining the number of "
2011 "parameters/arguments.\n");
2015 /* ************************************************************ */
2016 /* Various utilities and callback functions called when walking */
2017 /* through a BST. */
2018 /* ************************************************************ */
2020 static void
2021 bst_seen_opt_cb(const void *node, walk_order_e kind, int level)
2023 seen_opt_t *seen_opt = ((bst_t *)node)->key;
2025 if (kind == postorder || kind == leaf)
2027 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
2029 user_rc = 1;
2030 user_string = strappend(user_string,
2031 seen_opt->opt->params,
2032 " ",
2033 (char *)0);
2038 static void
2039 bst_seen_opt_seen_cb(const void *node, walk_order_e kind, int level)
2041 seen_opt_t *seen_opt = ((bst_t *)node)->key;
2043 if (kind == postorder || kind == leaf)
2044 if (seen_opt->seen == 1)
2046 user_rc = 1;
2047 user_object = seen_opt->par;
2051 static void
2052 bst_print_ctx_cb(const void *node, walk_order_e kind, int level)
2054 ctx_t *ctx = main_ctx;
2055 ctx_t *cur_ctx = ((bst_t *)node)->key;
2057 ll_t *list;
2059 int has_optional = 0;
2060 int has_ellipsis = 0;
2061 int has_rule = 0;
2062 int has_generic_arg = 0;
2063 int has_ctx_change = 0;
2064 int has_early_eval = 0;
2066 if (kind == postorder || kind == leaf)
2067 if (strcmp(ctx->name, cur_ctx->name) != 0)
2069 list = cur_ctx->opt_list;
2071 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
2072 print_options(list,
2073 &has_optional,
2074 &has_ellipsis,
2075 &has_rule,
2076 &has_generic_arg,
2077 &has_ctx_change,
2078 &has_early_eval);
2079 print_before_constraints(list);
2083 static void
2084 bst_check_opt_cb(const void *node, walk_order_e kind, int level)
2086 opt_t *opt = ((bst_t *)node)->key;
2088 if (kind == postorder || kind == leaf)
2090 if (opt->params == NULL) /* opt must have associated parameters. */
2091 fatal_internal("Option %s has no registered parameter.\n", opt->name);
2093 if (opt->action == NULL) /* opt must have an action. */
2094 fatal_internal("Option %s has no registered action.\n", opt->name);
2098 static void
2099 bst_match_par_cb(const void *node, walk_order_e kind, int level)
2101 ctx_t *ctx = ((bst_t *)node)->key;
2103 if (kind == postorder || kind == leaf)
2105 char *str = xstrdup(user_string);
2107 while (*str != '\0')
2109 if (locate_par(str, ctx) != NULL)
2111 if (*user_string2 == '\0')
2112 user_string2 = strappend(user_string2, "- ", ctx->name, (char *)0);
2113 else
2114 user_string2 = strappend(user_string2, "\n- ", ctx->name, (char *)0);
2115 break;
2117 str[strlen(str) - 1] = '\0';
2119 free(str);
2123 static void
2124 match_prefix_cb(const void *node, walk_order_e kind, int level)
2126 par_t *par = ((bst_t *)node)->key;
2128 if (kind == postorder || kind == leaf)
2129 if (strpref(par->name, (char *)user_object))
2131 user_rc++;
2132 user_string = strappend(user_string, par->name, " ", (char *)0);
2136 /* ====================================================================== */
2137 /* A parameter may not be separated from its first option by spaces, in */
2138 /* this case this function looks for a valid flag as a prefix and splits */
2139 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
2140 /* option). */
2141 /* */
2142 /* IN word : the word to be checked. */
2143 /* IN ctx : the context in which the flag indexed by the word is to be */
2144 /* checked. */
2145 /* OUT pos : the offset in word pointing just after the matching prefix. */
2146 /* OUT opt : a pointer to the option associated with the new parameter */
2147 /* or NULL if none is found. */
2148 /* */
2149 /* The returned pointer must be freed by the caller. */
2150 /* ====================================================================== */
2151 static char *
2152 look_for_valid_prefix_in_word(char *word, ctx_t *ctx, int *pos, opt_t **opt)
2154 char *new = NULL;
2155 int len;
2156 par_t *par;
2157 par_t tmp_par = { 0 };
2159 len = strlen(word);
2161 if (len > 2)
2163 new = xstrdup(word);
2167 new[--len] = '\0';
2168 tmp_par.name = new;
2169 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2171 if (par != NULL)
2173 *pos = len;
2174 *opt = par->opt;
2176 else
2178 free(new);
2179 new = NULL;
2182 else
2183 *pos = 0;
2185 return new;
2188 /* ============================================================= */
2189 /* If par_name is an unique abbreviation of an exiting parameter */
2190 /* in the context ctx, then return this parameter. */
2191 /* ============================================================= */
2192 static char *
2193 abbrev_expand(char *par_name, ctx_t *ctx)
2195 user_object = par_name;
2196 user_rc = 0;
2198 *user_string = '\0';
2199 bst_walk(ctx->par_bst, match_prefix_cb);
2200 rtrim(user_string, " ", 0);
2202 /* The previous bst_walk has built a string of blank separated parameters */
2203 /* all having par_name as prefix. This string is put in the user_string */
2204 /* exchange zone. The number of these words in put in user_rc. */
2205 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2206 if (user_rc == 1) /* The number of matching abbreviations. */
2207 return xstrdup(user_string);
2208 else /* There is at least two defined parameters starting with par_name. */
2210 char *s, *first_s;
2211 par_t *par;
2212 opt_t *opt;
2213 int opt_count = 0;
2214 void *tmp_opt_bst = NULL;
2216 /* Find all the options corresponding to these words and store them */
2217 /* without duplication in a temporary BST. Only their resulting count */
2218 /* matters. */
2219 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2220 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2221 | the first word. */
2222 while (s != NULL)
2224 par = locate_par(s, ctx); /* par cannot be NULL here. */
2225 opt = par->opt;
2227 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2229 /* This option as not already been seen */
2230 /* store it and increase the seen counter. */
2231 /* """"""""""""""""""""""""""""""""""""""" */
2232 bst_search(opt, &tmp_opt_bst, opt_compare);
2233 opt_count++;
2235 s = strtok(NULL, " ");
2238 /* Clean the temporary BST without removing the pointer */
2239 /* to the real options. */
2240 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2241 if (tmp_opt_bst != NULL)
2242 bst_destroy(tmp_opt_bst, NULL);
2244 if (opt_count == 1)
2245 /* All the abbreviation are leading to only one option */
2246 /* We can just continue as in the previous case. */
2247 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2248 return xstrdup(first_s);
2249 else
2250 return NULL;
2254 /* ================================================================ */
2255 /* Terminate the program if mandatory options required by a context */
2256 /* are not present. */
2257 /* ================================================================ */
2258 static void
2259 check_for_missing_mandatory_opt(ctx_inst_t *ctx_inst, char *opt_par)
2261 char *missing;
2263 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2264 fatal(CTXOPTMISPAR, missing);
2267 /* ====================================================== */
2268 /* Return 1 if at least one mandatory option was not seen */
2269 /* when quitting a context, else 0. */
2270 /* ====================================================== */
2271 static int
2272 has_unseen_mandatory_opt(ctx_inst_t *ctx_inst, char **missing)
2274 user_rc = 0;
2275 *user_string = '\0';
2277 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2278 rtrim(user_string, " ", 0);
2280 *missing = user_string;
2282 return user_rc ? 1 : 0;
2285 /* ========================================================================= */
2286 /* This function terminates the program if an option or its arguments do not */
2287 /* conform to its occurrences constraint. */
2288 /* There constraints can appear by trailing >, < or = in their definition */
2289 /* given in ctxopt_new_ctx. */
2290 /* ========================================================================= */
2291 static void
2292 check_for_occurrence_issues(ctx_inst_t *ctx_inst)
2294 ctx_t *ctx = ctx_inst->ctx;
2295 opt_t *opt;
2296 ll_node_t *node;
2297 opt_inst_t *opt_inst;
2298 char *cur_opt_params = cur_state->cur_opt_params;
2299 char *cur_opt_par_name = cur_state->cur_opt_par_name;
2301 /* Checks options. */
2302 /* """"""""""""""" */
2303 node = ctx->opt_list->head;
2305 while (node != NULL)
2307 opt = node->data;
2309 /* Update current_state. */
2310 /* """"""""""""""""""""" */
2311 cur_state->cur_opt_params = opt->params;
2312 cur_state->opts_count = opt->opt_count_mark;
2313 cur_state->opt_args_count = opt->opt_args_count_mark;
2315 if (opt->opt_count_matter)
2316 switch (opt->opt_count_oper)
2318 case '=':
2319 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2320 fatal(CTXOPTCTEOPT, "");
2321 break;
2323 case '<':
2324 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2325 fatal(CTXOPTCTLOPT, "");
2326 break;
2328 case '>':
2329 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2330 fatal(CTXOPTCTGOPT, "");
2331 break;
2334 node = node->next;
2337 /* Checks arguments. */
2338 /* """"""""""""""""" */
2339 node = ctx_inst->opt_inst_list->head;
2340 while (node != NULL)
2342 opt_inst = node->data;
2343 opt = opt_inst->opt;
2345 /* Update current_state. */
2346 /* """"""""""""""""""""" */
2347 cur_state->cur_opt_par_name = opt_inst->par;
2348 cur_state->opts_count = opt->opt_count_mark;
2349 cur_state->opt_args_count = opt->opt_args_count_mark;
2351 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2353 if (opt->opt_args_count_matter)
2354 switch (opt->opt_args_count_oper)
2356 case '=':
2357 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2358 fatal(CTXOPTCTEARG, "");
2359 break;
2361 case '<':
2362 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2363 fatal(CTXOPTCTLARG, "");
2364 break;
2366 case '>':
2367 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2368 fatal(CTXOPTCTGARG, "");
2369 break;
2372 node = node->next;
2374 cur_state->cur_opt_params = cur_opt_params;
2375 cur_state->cur_opt_par_name = cur_opt_par_name;
2378 /* ====================================================================== */
2379 /* This function terminates the program if all the options which are part */
2380 /* of a group of required options by some other option are missing. */
2381 /* ====================================================================== */
2382 static void
2383 check_for_requirement_issues(ctx_inst_t *ctx_inst)
2385 ll_node_t *node;
2386 ll_node_t *req_node;
2387 req_t *req;
2388 opt_t *opt;
2389 opt_t *req_opt;
2390 bst_t *bst_node;
2391 seen_opt_t tmp_seen_opt;
2392 int found;
2393 char *needed_params = NULL;
2395 node = ctx_inst->opt_req_list->head;
2397 while (node != NULL)
2399 req = node->data;
2401 opt = req->opt;
2402 tmp_seen_opt.opt = opt;
2404 bst_node = bst_find(&tmp_seen_opt,
2405 &(ctx_inst->seen_opt_bst),
2406 seen_opt_compare);
2408 /* TODO: make sure bst_node cannot be NULL here. */
2410 if (bst_node && ((seen_opt_t *)(bst_node->key))->seen != 0)
2412 found = 0;
2413 req_node = req->or_opt_list->head;
2415 /* needed_params accumulates the params of the options in the group. */
2416 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2417 free(needed_params); /* free can applied to the NULL pointer. */
2418 needed_params = xstrdup("");
2420 /* Go through the list of the required group of options and */
2421 /* succeed when one of them has been seen in the context. */
2422 /* otherwise a fatal error is triggered and the program is */
2423 /* terminated. */
2424 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2425 while (req_node != NULL)
2427 req_opt = req_node->data;
2428 tmp_seen_opt.opt = req_opt;
2429 needed_params = strappend(needed_params,
2430 req_opt->params,
2431 "\n ",
2432 (char *)0);
2434 bst_node = bst_find(&tmp_seen_opt,
2435 &(ctx_inst->seen_opt_bst),
2436 seen_opt_compare);
2438 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2440 found = 1; /* A required option has been seen, */
2441 break; /* accept the group. */
2443 req_node = req_node->next;
2446 rtrim(needed_params, "\n ", 0);
2448 /* This is a fatal error if none of the options in the required */
2449 /* options group has been seen in the context. */
2450 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2451 if (!found)
2453 char *errmsg;
2455 if (req->or_opt_list->len > 1)
2456 errmsg = xstrdup("At least one of the parameters among:\n %s\n"
2457 "requested by %s must be present.\n");
2458 else
2459 errmsg = xstrdup("The parameter %s "
2460 "requested by %s must be present.\n");
2462 cur_state->req_opt_par_needed = needed_params;
2463 cur_state->req_opt_par = opt->params;
2465 fatal(CTXOPTREQPAR, errmsg);
2469 node = node->next;
2473 /* ======================================================================== */
2474 /* Parse a strings describing options and some of their characteristics */
2475 /* The input string must have follow some rules like in the examples below: */
2476 /* */
2477 /* "opt_name1 opt_name2" */
2478 /* "[opt_name1] opt_name2" */
2479 /* "[opt_name1] opt_name2..." */
2480 /* "[opt_name1 #...] opt_name2... [#]" */
2481 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2482 /* */
2483 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2484 /* means that there can be more than one occurrence of the previous thing. */
2485 /* */
2486 /* opt_name can be followed by a 'new context' change prefixed with the */
2487 /* symbol >, as in opt1>c2 by eg. */
2488 /* */
2489 /* This function returns as soon as one (or no) option has been parsed and */
2490 /* return the offset to the next option to parse. */
2491 /* */
2492 /* In case of successful parsing, an new option is allocated and its */
2493 /* pointer returned. */
2494 /* ======================================================================== */
2495 static int
2496 opt_parse(char *s, opt_t **opt)
2498 int opt_optional = 0;
2499 int opt_multiple = 0;
2500 int opt_count_matter = 0;
2501 char opt_count_oper = '\0';
2502 unsigned opt_count_mark = 0;
2503 int opt_args = 0;
2504 char opt_arg[33] = { 0 };
2505 int opt_multiple_args = 0;
2506 int opt_args_count_matter = 0;
2507 char opt_args_count_oper = '\0';
2508 unsigned opt_args_count_mark = 0;
2509 int opt_optional_args = 0;
2510 int opt_eval_first = 0;
2512 int n;
2513 int pos;
2514 int count = 0;
2516 char *s_orig = s;
2518 char *p;
2519 char *opt_name = NULL;
2520 char *next_ctx;
2521 char token[65];
2523 *opt = NULL;
2524 memset(opt_arg, '\0', 33);
2526 /* Strip the leading blanks. */
2527 /* """"""""""""""""""""""""" */
2528 while (isblank(*s))
2529 s++;
2531 if (*s == '[') /* Start of an optional option. */
2533 opt_optional = 1;
2534 s++;
2536 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2537 if (s == NULL)
2538 return -1; /* Empty string. */
2540 /* Early EOS, only return success if the option is mandatory. */
2541 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2542 if (!*s)
2543 if (opt_optional == 1)
2544 return -(s - s_orig - 1);
2546 /* Validate the option name */
2547 /* ALPHA+(ALPHANUM|_)* */
2548 /* """""""""""""""""""""""" */
2549 p = token;
2550 if (!isalpha(*p) && *p != '*')
2551 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2553 if (*p == '*')
2554 opt_eval_first = 1;
2556 p++;
2557 while (*p)
2559 if (!isalnum(*p) && *p != '_' && *p != '>')
2560 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2561 * a number or a _ */
2562 p++;
2565 if (opt_eval_first)
2566 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2567 else
2568 opt_name = xstrdup(token);
2570 if (*s == ']')
2572 s++;
2573 while (isblank(*s))
2574 s++;
2576 goto success;
2579 /* Check if it can appear multiple times by looking for the dots. */
2580 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2581 p = strtoken(s, token, 3, "[.]", &pos);
2582 if (p)
2584 if (strcmp(token, "...") == 0)
2586 opt_multiple = 1;
2587 s = p;
2588 if (*s == '<' || *s == '=' || *s == '>')
2590 unsigned value;
2591 int offset;
2593 n = sscanf(s + 1, "%u%n", &value, &offset);
2594 if (n == 1)
2596 opt_count_matter = 1;
2597 opt_count_oper = *s;
2598 opt_count_mark = value;
2600 s += offset + 1;
2603 else
2605 free(opt_name);
2606 return -(s - s_orig - 1);
2610 if (*s == ']')
2612 /* Abort on extraneous ] if the option is mandatory. */
2613 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2614 if (!opt_optional)
2616 free(opt_name);
2617 return -(s - s_orig - 1);
2620 s++; /* skip the ] */
2622 if (!*s || isblank(*s))
2623 goto success;
2624 else
2626 free(opt_name);
2627 return -(s - s_orig - 1);
2631 /* A blank separates the option name and the argument tag. */
2632 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2633 if (isblank(*s))
2635 char dots[4];
2637 while (isblank(*s))
2638 s++;
2640 if (!*s)
2641 goto success;
2643 pos = 0;
2644 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2645 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2647 opt_args = 1;
2648 opt_optional_args = 1;
2649 if (n == 2)
2650 opt_multiple_args = 1; /* There were dots. */
2652 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2654 if (*s == '<' || *s == '=' || *s == '>')
2656 unsigned value;
2657 int offset;
2659 n = sscanf(s + 1, "%u%n", &value, &offset);
2660 if (n == 1)
2662 opt_args_count_matter = 1;
2663 opt_args_count_oper = *s;
2664 opt_args_count_mark = value;
2666 s += offset + 1;
2669 /* Optional arg tag must end with a ] */
2670 /* """""""""""""""""""""""""""""""""" */
2671 if (*s != ']')
2673 free(opt_name);
2674 return -(s - s_orig - 1);
2677 s++; /* Skip the ] */
2679 else
2681 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2682 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2684 opt_args = 1;
2685 if (n == 2) /* There were dots. */
2686 opt_multiple_args = 1;
2688 s += pos + !!(n == 2) * 3; /* Skip the dots. */
2690 if (*s == '<' || *s == '=' || *s == '>')
2692 unsigned value;
2693 int offset;
2695 n = sscanf(s + 1, "%u%n", &value, &offset);
2696 if (n == 1)
2698 opt_args_count_matter = 1;
2699 opt_args_count_oper = *s;
2700 opt_args_count_mark = value;
2702 s += offset + 1;
2706 if (*s == ']')
2708 /* Abort on extraneous ] if the option is mandatory. */
2709 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2710 if (!opt_optional)
2712 free(opt_name);
2713 return -(s - s_orig - 1);
2716 s++; /* skip the ] */
2718 /* Strip the following blanks. */
2719 /* """"""""""""""""""""""""""" */
2720 while (isblank(*s))
2721 s++;
2723 goto success;
2725 else if (opt_optional == 0 && (!*s || isblank(*s)))
2727 /* Strip the following blanks. */
2728 /* """"""""""""""""""""""""""" */
2729 while (isblank(*s))
2730 s++;
2732 goto success;
2734 else if (opt_args == 0) /* # was not read it is possibly the start *
2735 | of another option. */
2736 goto success;
2737 else
2739 free(opt_name);
2740 return -(s - s_orig - 1);
2744 success:
2746 /* Strip the following blanks. */
2747 /* """"""""""""""""""""""""""" */
2748 while (isblank(*s))
2749 s++;
2751 next_ctx = NULL;
2753 if (*opt_name == '>')
2754 fatal_internal("The option name is missing in %s.", opt_name);
2756 count = strchrcount(opt_name, '>');
2757 if (count == 1)
2759 char *tmp = strchr(opt_name, '>');
2760 next_ctx = xstrdup(tmp + 1);
2761 *tmp = '\0';
2763 else if (count > 1)
2764 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2766 *opt = xmalloc(sizeof(opt_t));
2768 (*opt)->name = opt_name;
2769 (*opt)->optional = opt_optional;
2770 (*opt)->multiple = opt_multiple;
2771 (*opt)->opt_count_matter = opt_count_matter;
2772 (*opt)->opt_count_oper = opt_count_oper;
2773 (*opt)->opt_count_mark = opt_count_mark;
2774 (*opt)->args = opt_args;
2775 (*opt)->arg = xstrdup(opt_arg);
2776 (*opt)->optional_args = opt_optional_args;
2777 (*opt)->multiple_args = opt_multiple_args;
2778 (*opt)->opt_args_count_matter = opt_args_count_matter;
2779 (*opt)->opt_args_count_oper = opt_args_count_oper;
2780 (*opt)->opt_args_count_mark = opt_args_count_mark;
2781 (*opt)->eval_first = opt_eval_first;
2782 (*opt)->next_ctx = next_ctx;
2783 (*opt)->ctx_list = ll_new();
2784 (*opt)->constraints_list = ll_new();
2785 (*opt)->eval_before_list = ll_new();
2786 (*opt)->action = NULL;
2787 (*opt)->params = NULL;
2788 (*opt)->data = NULL;
2789 (*opt)->visible_in_help = 1;
2791 return s - s_orig;
2794 /* ==================================================================== */
2795 /* Try to initialize all the option in a given string. */
2796 /* Each parsed option are put in a BST tree with its name as index. */
2797 /* */
2798 /* On collision, the arguments only the signature are required to be */
2799 /* the same else this is considered as an error. Options can be used in */
2800 /* more than one context and can be optional in one and mandatory in */
2801 /* another. */
2802 /* ==================================================================== */
2803 static int
2804 init_opts(char *spec, ctx_t *ctx)
2806 opt_t *opt, *bst_opt;
2807 bst_t *node;
2808 int offset;
2810 while (*spec)
2812 if ((offset = opt_parse(spec, &opt)) > 0)
2814 spec += offset;
2816 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2818 int same_next_ctx = 0;
2820 bst_opt = node->key; /* Node extracted from the BST. */
2822 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2823 same_next_ctx = 1;
2824 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2825 same_next_ctx = 0;
2826 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2827 same_next_ctx = 0;
2828 else
2829 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2831 if (bst_opt->optional_args != opt->optional_args
2832 || bst_opt->multiple_args != opt->multiple_args
2833 || bst_opt->args != opt->args || !same_next_ctx)
2835 fatal_internal("The option %s already exists with "
2836 "a different arguments signature.\n",
2837 opt->name);
2840 /* The newly created opt is already present in options_bst. */
2841 /* We can remove it. */
2842 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2843 opt_free(opt);
2845 /* The new occurrence of the option option is legal */
2846 /* append the current context ptr in the list. */
2847 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2848 ll_append(bst_opt->ctx_list, ctx);
2850 /* Append the new option to the context's options list. */
2851 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2852 ll_append(ctx->opt_list, bst_opt);
2854 else
2856 /* Initialize the option's context list with the current context. */
2857 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2858 ll_append(opt->ctx_list, ctx);
2860 /* Append the new option to the context's options list. */
2861 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2862 ll_append(ctx->opt_list, opt);
2864 /* Insert the new option in the BST. */
2865 /* """"""""""""""""""""""""""""""""" */
2866 bst_search(opt, &options_bst, opt_compare);
2869 else
2871 char *s = xstrndup(spec, -offset);
2872 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2873 free(s);
2875 exit(EXIT_FAILURE);
2879 return 1;
2882 /* ===================================================== */
2883 /* ctxopt initialization function, must be called first. */
2884 /* ===================================================== */
2885 void
2886 ctxopt_init(char *prog_name, char *init_flags)
2888 int n;
2890 contexts_bst = NULL;
2891 options_bst = NULL;
2892 char *ptr;
2894 user_rc = 0;
2895 user_value = 0;
2896 user_string = xmalloc(8);
2897 user_string2 = xmalloc(8);
2898 user_object = NULL;
2899 char flag[33], fname[31], vname[31];
2900 int invalid;
2902 ctxopt_initialized = 1;
2904 /* Initialize current_state.*/
2905 /* """""""""""""""""""""""" */
2906 cur_state = xcalloc(sizeof(state_t), 0);
2908 /* Initialize custom error function pointers to NULL. */
2909 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2910 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2911 for (n = 0; n < CTXOPTERRSIZ; n++)
2912 err_functions[n] = NULL;
2914 /* Parse init_flags if any. */
2915 /* """""""""""""""""""""""" */
2916 while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2918 if (*flag)
2920 if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2921 fatal_internal("Invalid flag assignment: %s.", flag);
2923 if (strcmp(fname, "stop_if_non_option") == 0)
2925 if (eval_yes(vname, &invalid))
2926 flags.stop_if_non_option = 1;
2927 else if (!invalid)
2928 flags.stop_if_non_option = 0;
2929 else
2930 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2932 else if (strcmp(fname, "allow_abbreviations") == 0)
2934 if (eval_yes(vname, &invalid))
2935 flags.allow_abbreviations = 1;
2936 else if (!invalid)
2937 flags.allow_abbreviations = 0;
2938 else
2939 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2941 else if (strcmp(fname, "display_usage_on_error") == 0)
2943 if (eval_yes(vname, &invalid))
2944 flags.display_usage_on_error = 1;
2945 else if (!invalid)
2946 flags.display_usage_on_error = 0;
2947 else
2948 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2950 else
2951 fatal_internal("Invalid flag name: %s.", fname);
2955 /* Update current_state. */
2956 /* """"""""""""""""""""" */
2957 if (prog_name)
2959 if (*prog_name == '\0')
2960 cur_state->prog_name = xstrdup("program_name");
2961 else if ((ptr = strrchr(prog_name, '/')))
2962 cur_state->prog_name = xstrdup(ptr + 1);
2963 else
2964 cur_state->prog_name = xstrdup(prog_name);
2966 else
2967 cur_state->prog_name = xstrdup("program_name");
2970 /* ========================================================================= */
2971 /* Utility function which create and register a par_t object in a BST */
2972 /* embedded in a context. */
2973 /* This object will have a name and a pointer to the option it refers to. */
2974 /* These object will be used to quickly find an option from a command */
2975 /* line parameter during the analysis phase. */
2976 /* */
2977 /* IN : an option name. */
2978 /* IN : a string of command line parameters to associate to the option. */
2979 /* Returns : 1 is all was fine else 0. */
2980 /* ========================================================================= */
2981 static int
2982 opt_set_parms(char *opt_name, char *par_str)
2984 char *par_name, *ctx_name;
2985 char *tmp_par_str, *end_tmp_par_str;
2986 ctx_t *ctx;
2987 opt_t *opt;
2988 bst_t *node;
2989 par_t *par, tmp_par;
2990 int rc = 1; /* Return code. */
2992 ll_t *list;
2993 ll_node_t *lnode;
2995 /* Look if the given option is defined. */
2996 /* """""""""""""""""""""""""""""""""""" */
2997 opt = locate_opt(opt_name);
2998 if (opt == NULL)
2999 fatal_internal("Unknown option %s.", opt_name);
3001 /* For each context using this option. */
3002 /* """"""""""""""""""""""""""""""""""" */
3003 list = opt->ctx_list;
3005 lnode = list->head;
3006 while (lnode != NULL)
3008 /* Locate the context in the contexts tree. */
3009 /* """""""""""""""""""""""""""""""""""""""" */
3010 ctx_name = ((ctx_t *)(lnode->data))->name;
3012 ctx = locate_ctx(ctx_name);
3013 if (ctx == NULL)
3014 fatal_internal("Unknown context %s.", ctx_name);
3015 else
3017 void *par_bst = ctx->par_bst;
3019 tmp_par_str = xstrdup(par_str);
3020 ltrim(tmp_par_str, " \t");
3021 rtrim(tmp_par_str, " \t", 0);
3022 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
3023 if (par_name == NULL)
3024 fatal_internal("Parameters are missing for option %s.", opt_name);
3026 /* For each parameter given in par_str, creates a par_t object and */
3027 /* insert it the in the parameters BST of the context. */
3028 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3029 while (par_name != NULL)
3031 tmp_par.name = par_name;
3033 node = bst_find(&tmp_par, &par_bst, par_compare);
3034 if (node != NULL)
3036 fatal_internal("The parameter %s is already defined in context %s.",
3037 par_name,
3038 ctx->name);
3039 rc = 0;
3041 else
3043 par = xmalloc(sizeof(par_t));
3044 par->name = xstrdup(par_name);
3045 par->opt = opt; /* Link the option to this parameter. */
3047 bst_search(par, &par_bst, par_compare);
3049 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
3052 /* Update the value of the root of ctx->par_bst as it may have */
3053 /* been modified. */
3054 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3055 ctx->par_bst = par_bst;
3057 free(tmp_par_str);
3059 lnode = lnode->next;
3062 return rc;
3065 /* ==================================================================== */
3066 /* Create a new context instance. */
3067 /* IN ctx : a context pointer to allow this instance to */
3068 /* access the context fields */
3069 /* IN prev_ctx_inst : the context instance whose option leading to the */
3070 /* creation of this new context instance is part of */
3071 /* Returns : the new context. */
3072 /* ==================================================================== */
3073 static ctx_inst_t *
3074 new_ctx_inst(ctx_t *ctx, ctx_inst_t *prev_ctx_inst)
3076 opt_t *opt;
3077 opt_inst_t *gen_opt_inst;
3078 ctx_inst_t *ctx_inst;
3079 seen_opt_t *seen_opt;
3080 char *str, *opt_name;
3081 void *bst;
3082 bst_t *bst_node;
3084 /* Keep a trace of the opt_inst which was at the origin of the creation */
3085 /* of this context instance. */
3086 /* This will serve during the evaluation of the option callbacks. */
3087 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3088 if (prev_ctx_inst != NULL)
3090 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
3092 /* Update current_state. */
3093 /* """"""""""""""""""""" */
3094 cur_state->opt_name = gen_opt_inst->opt->name;
3096 else
3097 gen_opt_inst = NULL;
3099 /* Create and initialize the new context instance. */
3100 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3101 ctx_inst = xmalloc(sizeof(ctx_inst_t));
3102 ctx_inst->ctx = ctx;
3103 ctx_inst->prev_ctx_inst = prev_ctx_inst;
3104 ctx_inst->gen_opt_inst = gen_opt_inst;
3105 ctx_inst->incomp_bst_list = ll_new();
3106 ctx_inst->opt_inst_list = ll_new();
3107 ctx_inst->opt_req_list = ll_new();
3108 ctx_inst->seen_opt_bst = NULL;
3110 ll_node_t *node;
3112 if (prev_ctx_inst == NULL)
3113 first_ctx_inst = ctx_inst;
3115 /* Initialize the occurrence counters of each opt allowed in the context. */
3116 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3117 node = ctx->opt_list->head;
3118 while (node != NULL)
3120 opt = node->data;
3121 opt->occurrences = 0;
3123 node = node->next;
3126 /* Initialize the BST containing the seen indicator for all the options */
3127 /* allowed in this context instance. */
3128 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3129 node = ctx->opt_list->head;
3130 while (node != NULL)
3132 opt = node->data;
3133 seen_opt = xmalloc(sizeof(seen_opt_t));
3134 seen_opt->opt = opt;
3135 seen_opt->par = NULL;
3136 seen_opt->seen = 0;
3138 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
3140 node = node->next;
3143 /* Initialize the BST containing the incompatibles options. */
3144 /* Incompatibles option names are read from strings found in the list */
3145 /* incomp_list present in each instance of ctx_t. */
3146 /* These names are then used to search for the object of type seen_opt_t */
3147 /* which is already present in the seen_opt_bst of the context instance. */
3148 /* in the BST. */
3149 /* Once found the seen_opt_t object in inserted in the new BST. */
3150 /* At the end the new BST in added to the list incomp_bst_list. */
3151 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3152 node = ctx->incomp_list->head;
3153 while (node != NULL)
3155 bst = NULL;
3156 seen_opt_t tmp_seen_opt;
3158 str = xstrdup(node->data);
3159 ltrim(str, " \t");
3160 rtrim(str, " \t", 0);
3161 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3163 while (opt_name != NULL) /* For each option name. */
3165 if ((opt = locate_opt(opt_name)) != NULL)
3167 /* The option found is searched in the tree of potential */
3168 /* seen options. */
3169 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3170 tmp_seen_opt.opt = opt;
3172 bst_node = bst_find(&tmp_seen_opt,
3173 &(ctx_inst->seen_opt_bst),
3174 seen_opt_compare);
3176 if (bst_node != NULL)
3178 /* If found then it is added into the new BST tree. */
3179 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3180 seen_opt = bst_node->key;
3181 bst_search(seen_opt, &bst, seen_opt_compare);
3183 else
3184 /* Not found! That means that the option is unknown in this */
3185 /* context as all options has have a seen_opt structure in */
3186 /* seen_opt_bst. */
3187 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3188 fatal_internal("%s is not known in the context %s.",
3189 opt->name,
3190 ctx->name);
3192 else
3193 fatal_internal("Unknown option %s.", opt_name);
3195 opt_name = strtok(NULL, " \t");
3198 free(str);
3199 ll_append(ctx_inst->incomp_bst_list, bst);
3201 node = node->next;
3204 /* Initialize the list of res_t structures according to the */
3205 /* list set in the context by ctxopt_add_ctx_settings/required. */
3206 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3207 node = ctx->req_list->head;
3208 while (node != NULL)
3210 req_t *req = xmalloc(sizeof(req_t));
3212 str = xstrdup(node->data);
3213 ltrim(str, " \t");
3214 rtrim(str, " \t", 0);
3215 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3217 if ((opt = locate_opt(opt_name)) != NULL)
3219 req->opt = opt;
3220 req->or_opt_list = ll_new();
3221 while ((opt_name = strtok(NULL, " \t")) != NULL)
3223 if ((opt = locate_opt(opt_name)) != NULL)
3224 ll_append(req->or_opt_list, opt);
3225 else
3226 fatal_internal("Unknown option %s.", opt_name);
3228 ll_append(ctx_inst->opt_req_list, req);
3230 else
3231 fatal_internal("Unknown option %s.", opt_name);
3233 free(str);
3235 node = node->next;
3237 return ctx_inst;
3240 /* ====================================================================== */
3241 /* Create a list formed by all the significant command line words */
3242 /* Words beginning or ending with { or } are split. Each of these */
3243 /* symbols will get their own place in the list. */
3244 /* */
3245 /* the {...} part delimits a context, the { will not appear in the list */
3246 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3247 /* to facilitate the parsing phase. | must not be used by the end user. */
3248 /* */
3249 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3250 /* program name is not considered. */
3251 /* IN words : is the array of strings constituting the command line to */
3252 /* parse. */
3253 /* Returns : 1 on success, 0 if a { or } is missing. */
3254 /* ====================================================================== */
3255 static int
3256 ctxopt_build_cmdline_list(int nb_words, char **words)
3258 int i;
3259 char *prev_word = NULL;
3260 char *word;
3261 char *ptr;
3262 int level = 0;
3263 ll_node_t *node, *start_node;
3265 /* The analysis is divided into three passes, this is not optimal but */
3266 /* must be done only one time. Doing that we privilege readability. */
3267 /* */
3268 /* In the following, SG is the ASCII character 1d (dec 29) */
3269 /* */
3270 /* The first pass creates the list, extract the leading an trailing */
3271 /* SG '{' and '}' of each word and give them their own place in the */
3272 /* list */
3273 /* */
3274 /* The second pass transform the '{...}' blocks by a trailing SG */
3275 /* ({...} -> ...|) */
3276 /* */
3277 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3278 /* the middle in the remaining list elements and recreate the pseudo */
3279 /* argument: {} */
3280 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3282 /* If the option list is not empty, clear it before going further. */
3283 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3284 if (cmdline_list != NULL)
3285 ll_destroy(cmdline_list, free);
3287 cmdline_list = ll_new();
3289 start_node = cmdline_list->head; /* In the following loop start_node will *
3290 | contain a pointer to the current *
3291 | word stripped from its leading *
3292 | sequence of {, }. */
3293 for (i = 0; i < nb_words; i++)
3295 size_t len = strlen(words[i]);
3296 size_t start, end;
3297 char *str;
3299 str = words[i];
3301 /* Replace each occurrence of the legal word {} by the characters */
3302 /* 0x02 and 0x03 to hide them from the following process. */
3303 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3304 while ((ptr = strstr(str, "{}")) != NULL)
3306 *ptr = 0x02; /* Arbitrary values unlikely */
3307 *(ptr + 1) = 0x03; /* present in a word. */
3310 if (len > 1) /* The word contains at least 2 characters. */
3312 start = 0;
3314 /* Interpret its beginning and look for the start of the real word. */
3315 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3316 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
3318 ll_append(cmdline_list, xstrndup(str + start, 1));
3319 start++;
3320 start_node = cmdline_list->tail;
3323 end = len - 1;
3324 if (str[end] == '{' || str[end] == '}')
3326 if (end > 0 && str[end - 1] != '\\')
3328 ll_append(cmdline_list, xstrndup(str + end, 1));
3329 end--;
3330 node = cmdline_list->tail;
3332 while (str[end] == '{' || str[end] == '}')
3334 if (end > start && str[end - 1] == '\\')
3335 break;
3337 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3338 end--;
3339 node = node->prev;
3344 if (start <= end)
3346 if (start_node != NULL)
3347 ll_insert_after(cmdline_list,
3348 start_node,
3349 xstrndup(str + start, end - start + 1));
3350 else
3351 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3352 start_node = cmdline_list->tail;
3355 else if (len == 1)
3357 ll_append(cmdline_list, xstrdup(str));
3358 start_node = cmdline_list->tail;
3362 /* 2nd pass. */
3363 /* """"""""" */
3364 node = cmdline_list->head;
3366 level = 0;
3367 while (node != NULL)
3369 word = node->data;
3371 if (strcmp(word, "{") == 0)
3373 ll_node_t *old_node = node;
3374 level++;
3375 node = node->next;
3376 free(word);
3377 ll_delete(cmdline_list, old_node);
3379 else if (strcmp(word, "}") == 0)
3381 level--;
3383 if (level < 0)
3384 return 0;
3385 else
3386 *word = 0x1d;
3388 else
3389 node = node->next;
3392 if (level != 0)
3393 return 0;
3395 /* 3rd pass. */
3396 /* """"""""" */
3397 node = cmdline_list->head;
3399 while (node != NULL)
3401 word = node->data;
3403 /* Restore the original { and } characters forming the legal word {}. */
3404 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3405 while ((ptr = strchr(word, 0x02)) != NULL)
3406 *ptr = '{';
3407 while ((ptr = strchr(word, 0x03)) != NULL)
3408 *ptr = '}';
3410 /* Remove a SG if the previous element is SG. */
3411 /* """""""""""""""""""""""""""""""""""""""""" */
3412 if (strcmp(word, "\x1d") == 0)
3414 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3416 ll_node_t *old_node = node;
3417 node = node->prev;
3418 free(old_node->data);
3419 ll_delete(cmdline_list, old_node);
3422 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3423 | a parameter. Protect it. */
3425 free(node->data);
3426 node->data = xstrdup("\\-");
3429 prev_word = node->data;
3430 node = node->next;
3433 /* Clean useless and SG at the beginning and end of list. */
3434 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3435 node = cmdline_list->head;
3437 if (node == NULL)
3438 return 1;
3440 word = node->data;
3442 if (strcmp(word, "\x1d") == 0)
3444 free(word);
3445 ll_delete(cmdline_list, node);
3448 node = cmdline_list->tail;
3449 if (node == NULL)
3450 return 1;
3452 word = node->data;
3454 if (strcmp(word, "\x1d") == 0)
3456 free(word);
3457 ll_delete(cmdline_list, node);
3460 return 1;
3463 /* ===================================================================== */
3464 /* Build and analyze the command line list and create the linked data */
3465 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3466 /* This function identifies the following errors and creates an array of */
3467 /* The remaining not yet analyzed arguments. */
3468 /* - detect missing arguments */
3469 /* - detect too many arguments */
3470 /* - detect unknown parameters in a context */
3471 /* - detect too many occurrences of a parameters in a context */
3472 /* - detect missing required arguments in a context */
3473 /* */
3474 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3475 /* program name is not considered */
3476 /* IN words : is the array of strings constituting the command line to */
3477 /* parse. */
3478 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3479 /* is present in the list. */
3480 /* OUT rem_args : array of remaining command line arguments if a -- */
3481 /* is present in the list. This array must be free by */
3482 /* The caller as it is allocated here. */
3483 /* ===================================================================== */
3484 void
3485 ctxopt_analyze(int nb_words, char **words, int *nb_rem_args, char ***rem_args)
3487 char *ctxopt_debug_env; /* Environment variable CTXOPT_DEBUG content. */
3488 int ctxopt_debug; /* 1 if ctxopt_debug_env is set and not empty. *
3489 | 0 if ctxopt_debug_env is unset or empty. */
3491 ctx_t *ctx;
3492 opt_t *opt = NULL;
3493 par_t *par;
3494 ctx_inst_t *ctx_inst;
3495 opt_inst_t *opt_inst;
3496 int expect_par = 0;
3497 int expect_arg = 0;
3498 int expect_par_or_arg = 0;
3500 ll_node_t *cli_node;
3501 bst_t *bst_node;
3502 seen_opt_t *bst_seen_opt;
3503 char *par_name;
3504 void *bst;
3506 ll_node_t *node;
3508 if (!ctxopt_build_cmdline_list(nb_words, words))
3509 fatal_internal("The command line could not be parsed: "
3510 "missing '{' or '}' detected.");
3512 if (main_ctx == NULL)
3513 fatal_internal("At least one context must have been created.");
3515 /* Check that all options has an action and at least one parameter. */
3516 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3517 bst_walk(options_bst, bst_check_opt_cb);
3519 /* CTXOPT debug setting. */
3520 /* """"""""""""""""""""" */
3521 ctxopt_debug_env = getenv("CTXOPT_DEBUG");
3522 if (ctxopt_debug_env != NULL && *ctxopt_debug_env != '\0')
3523 ctxopt_debug = 1;
3524 else
3525 ctxopt_debug = 0;
3527 /* Create the first ctx_inst record. */
3528 /* """"""""""""""""""""""""""""""""" */
3529 ctx = main_ctx;
3531 ctx_inst_list = ll_new();
3532 ctx_inst = new_ctx_inst(ctx, NULL);
3533 ctx_inst->par_name = NULL;
3535 /* Update current_state. */
3536 /* """"""""""""""""""""" */
3537 cur_state->ctx_name = ctx->name;
3539 ll_append(ctx_inst_list, ctx_inst);
3541 /* For each node in the command line. */
3542 /* """""""""""""""""""""""""""""""""" */
3543 cli_node = cmdline_list->head;
3544 expect_par = 1;
3545 par_name = NULL;
3547 while (cli_node != NULL)
3549 if (strcmp(cli_node->data, "--") == 0)
3550 break; /* No new parameter will be analyzed after this point. */
3552 par_name = cli_node->data;
3554 /* Replace a leading -- by a single - */
3555 /* """""""""""""""""""""""""""""""""" */
3556 if (strncmp(par_name, "--", 2) == 0)
3557 par_name += 1; /* Ignore the first dash. */
3559 if (strcmp(par_name, "\x1d") == 0)
3561 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3562 check_for_occurrence_issues(ctx_inst);
3563 check_for_requirement_issues(ctx_inst);
3565 /* Forced backtracking to the previous context instance. */
3566 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3567 if (ctx_inst->prev_ctx_inst != NULL)
3569 ctx_inst = ctx_inst->prev_ctx_inst;
3570 ctx = ctx_inst->ctx;
3572 /* Update current_states. */
3573 /* """"""""""""""""""""" */
3574 cur_state->ctx_name = ctx->name;
3575 cur_state->ctx_par_name = ctx_inst->par_name;
3577 if (ctxopt_debug)
3578 fprintf(stderr,
3579 "CTXOPT_DEBUG: Context forced backtrack, "
3580 "new current context: %s.\n",
3581 ctx->name);
3583 else
3585 /* Update current_state. */
3586 /* """"""""""""""""""""" */
3587 cur_state->ctx_par_name = NULL;
3590 else if (expect_par && *par_name == '-')
3592 int pos = 0;
3593 char *prefix;
3595 /* Update current_state. */
3596 /* """"""""""""""""""""" */
3597 cur_state->cur_opt_par_name = par_name;
3598 cur_state->ctx_name = ctx->name;
3599 cur_state->ctx_par_name = ctx_inst->par_name;
3601 if (ctxopt_debug)
3602 fprintf(stderr,
3603 "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3604 par_name,
3605 cur_state->ctx_name);
3607 /* An expected parameter has been seen. */
3608 /* """""""""""""""""""""""""""""""""""" */
3609 if ((par = locate_par(par_name, ctx)) == NULL)
3611 opt_t *popt;
3612 char *word;
3614 /* Look if this parameter is an unique abbreviation of a longer */
3615 /* parameter. If this is the case then just replace it with its */
3616 /* full length version and try again. */
3617 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3618 if (flags.allow_abbreviations)
3619 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3621 cli_node->data = word;
3622 continue;
3625 /* Try to find a prefix which is a valid parameter in this context */
3626 /* If found, split the cli_node in two to build a new parameter */
3627 /* node and followed by a node containing the remaining string */
3628 /* If the new parameter corresponds to an option not taking */
3629 /* argument then prefix the remaining string whit a dash as it may */
3630 /* contain a new parameter. */
3631 /* The new parameter will be re-evaluated in the next iteration of */
3632 /* the loop. */
3633 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3634 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3635 if (prefix != NULL && pos != 0)
3637 if (ctxopt_debug)
3638 fprintf(stderr,
3639 "CTXOPT_DEBUG: Found a valid parameter "
3640 "as a prefix of %s: %s.\n",
3641 par_name,
3642 prefix);
3644 cli_node->data = prefix; /* prefix contains le name of a valid *
3645 | parameter in this context. */
3647 if (popt->args)
3649 /* The parameter may be followed by arguments. */
3650 /* ''''''''''''''''''''''''''''''''''''''''''' */
3651 if (*(par_name + pos) == '-')
3653 word = xstrdup("\\"); /* Protect the '-' */
3654 word = strappend(word, par_name + pos, (char *)0);
3656 else
3657 word = xstrdup(par_name + pos);
3659 else
3661 /* The parameter does not take arguments, the */
3662 /* following word must be a parameter or nothing */
3663 /* hence prefix it with a dash. */
3664 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3665 word = xstrdup("-");
3666 word = strappend(word, par_name + pos, (char *)0);
3669 /* Insert it after the current node in the list. */
3670 /* """"""""""""""""""""""""""""""""""""""""""""" */
3671 ll_insert_after(cmdline_list, cli_node, word);
3673 continue; /* loop. */
3675 else
3677 check_for_missing_mandatory_opt(ctx_inst, par_name);
3678 check_for_occurrence_issues(ctx_inst);
3679 check_for_requirement_issues(ctx_inst);
3681 if (ctx_inst->prev_ctx_inst == NULL)
3683 char *errmsg = xstrdup("");
3685 /* Update current_state. */
3686 /* """"""""""""""""""""" */
3687 cur_state->ctx_par_name = NULL;
3689 *user_string = '\0';
3690 *user_string2 = '\0';
3692 user_string = strappend(user_string, par_name, (char *)0);
3694 bst_walk(contexts_bst, bst_match_par_cb);
3696 if (*user_string2 != '\0')
3698 char *help_msg;
3699 int count = 0;
3701 count = strchrcount(user_string2, '\n');
3703 if (flags.display_usage_on_error)
3704 help_msg = ", see below";
3705 else
3706 help_msg = "";
3708 if (count == 0) /* Only one context involved. */
3709 errmsg = strappend(
3710 errmsg,
3711 "\nThis parameter is only valid in the following "
3712 "context:\n",
3713 user_string2,
3714 "\n\nFirst switch to this context using the appropriate "
3715 "parameter",
3716 help_msg,
3717 ".\n",
3718 (char *)0);
3719 else
3720 errmsg = strappend(
3721 errmsg,
3722 "\nThis parameter is only valid in one of the following "
3723 "contexts:\n",
3724 user_string2,
3725 "\n\nFirst switch to one of them using the appropriate "
3726 "parameter",
3727 help_msg,
3728 ".\n",
3729 (char *)0);
3732 fatal(CTXOPTUNKPAR, errmsg);
3734 else
3736 /* Tries to backtrack and analyse the same parameter in the */
3737 /* previous context. */
3738 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3739 ctx_inst = ctx_inst->prev_ctx_inst;
3740 ctx = ctx_inst->ctx;
3742 if (ctxopt_debug)
3743 fprintf(stderr,
3744 "CTXOPT_DEBUG: Context backtrack, "
3745 "new current context: %s.\n",
3746 ctx->name);
3748 /* Update current_state. */
3749 /* """"""""""""""""""""" */
3750 cur_state->ctx_name = ctx->name;
3751 cur_state->ctx_par_name = ctx_inst->par_name;
3753 cli_node = cli_node->prev;
3757 else
3759 seen_opt_t seen_opt;
3761 /* The parameter is valid in the context, create a opt_inst and */
3762 /* append it to the ctx_inst list options list. */
3763 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3764 opt = par->opt;
3766 opt->occurrences++;
3768 opt_inst = xmalloc(sizeof(opt_inst_t));
3769 opt_inst->opt = opt;
3770 opt_inst->par = par_name;
3771 opt_inst->values_list = ll_new();
3772 opt_inst->next_ctx_inst = NULL;
3774 /* Update current_state. */
3775 /* """"""""""""""""""""" */
3776 cur_state->cur_opt_params = opt->params;
3778 /* Priority option are inserted at the start of the opt_inst list */
3779 /* but their order of appearance in the context definition must */
3780 /* be preserver so each new priority option will be placed after */
3781 /* the previous ones at the start of the opt_inst list. */
3782 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3783 if (!opt->eval_first)
3785 /* Look if we have a registered dependency in the order of the */
3786 /* evaluation of two options. */
3787 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3788 if (opt->eval_before_list->len > 0)
3790 ll_t *list = ctx_inst->opt_inst_list;
3791 ll_node_t *opt_inst_node;
3793 ll_t *before_list = opt->eval_before_list;
3794 ll_node_t *before_node = before_list->head;
3796 ll_node_t *target_node = NULL; /* If not NULL, the new node *
3797 | will be inserted before it. */
3799 /* For each entry in eval_before_list, try to find if it */
3800 /* refers to an option already entered in the context. If this */
3801 /* is the case, insert it just before it instead of putting it */
3802 /* at the end. */
3803 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3804 while (before_node != NULL)
3806 opt_inst_node = list->head;
3808 while (opt_inst_node != target_node)
3810 opt_t *tmp_opt = (((opt_inst_t *)opt_inst_node->data))->opt;
3812 /* We have found an option mentioned if the before_list */
3813 /* of the option we want to add. We can stop searching. */
3814 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3815 if (strcmp(tmp_opt->name, ((opt_t *)before_node->data)->name))
3816 opt_inst_node = opt_inst_node->next;
3817 else
3818 target_node = opt_inst_node; /* Set the target node. */
3821 before_node = before_node->next;
3824 /* Insert or append ? */
3825 /* """""""""""""""""" */
3826 if (target_node != NULL)
3827 ll_insert_before(ctx_inst->opt_inst_list, target_node, opt_inst);
3828 else
3829 ll_append(ctx_inst->opt_inst_list, opt_inst);
3831 else
3832 ll_append(ctx_inst->opt_inst_list, opt_inst);
3834 else
3836 ll_node_t *opt_inst_node = ctx_inst->opt_inst_list->head;
3837 opt_inst_t *tmp_opt_inst;
3839 while (opt_inst_node != NULL)
3841 tmp_opt_inst = opt_inst_node->data;
3842 if (!tmp_opt_inst->opt->eval_first)
3844 ll_insert_before(ctx_inst->opt_inst_list,
3845 opt_inst_node,
3846 opt_inst);
3847 break;
3849 else
3850 opt_inst_node = opt_inst_node->next;
3852 if (opt_inst_node == NULL)
3853 ll_append(ctx_inst->opt_inst_list, opt_inst);
3856 /* Check if an option was already seen in the */
3857 /* current context instance. */
3858 /* """""""""""""""""""""""""""""""""""""""""" */
3859 seen_opt.opt = opt;
3861 bst_node = bst_find(&seen_opt,
3862 &(ctx_inst->seen_opt_bst),
3863 seen_opt_compare);
3865 /* bst_node cannot be NULL here. */
3867 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3869 if (!opt->multiple && bst_seen_opt->seen == 1)
3870 fatal(CTXOPTDUPOPT, "");
3872 /* Check if this option is compatible with the options already */
3873 /* seen in this context instance. */
3874 /* Look if the option is present in one on the BST present in */
3875 /* the incomp_bst_list of the context instance. */
3876 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3877 node = ctx_inst->incomp_bst_list->head;
3878 while (node != NULL)
3880 bst = node->data;
3881 user_object = NULL;
3883 /* There can only have one seen_opt object in the BST tree was */
3884 /* already seen, try to locate it, the result will be put in */
3885 /* user_object by the bst_seen_opt_seen_cb function. */
3886 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3887 bst_walk(bst, bst_seen_opt_seen_cb);
3889 /* If it is the case, look if the current option is also */
3890 /* in this BST. */
3891 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3892 if (user_object != NULL)
3894 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3896 if (bst_node != NULL)
3898 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3899 if (bst_seen_opt->seen == 0)
3900 fatal(CTXOPTINCOPT, (char *)user_object);
3904 node = node->next;
3907 /* Mark this option as seen in the current context instance. */
3908 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3909 bst_seen_opt->seen = 1;
3910 free(bst_seen_opt->par);
3911 bst_seen_opt->par = xstrdup(par_name);
3913 /* If this option leads to a next context, create a new ctx_inst */
3914 /* and switch to it for the analyse of the future parameter. */
3915 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3916 if (opt->next_ctx != NULL)
3918 ctx = locate_ctx(opt->next_ctx);
3920 if (ctx == NULL)
3921 fatal_internal("Unknown context %s.", opt->next_ctx);
3923 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3924 ctx_inst->par_name = xstrdup(par_name);
3926 ll_append(ctx_inst_list, ctx_inst);
3928 if (ctxopt_debug)
3929 fprintf(stderr,
3930 "CTXOPT_DEBUG: Context change, "
3931 "new current context: %s.\n",
3932 ctx->name);
3935 /* Look is we must expect some arguments. */
3936 /* """""""""""""""""""""""""""""""""""""" */
3937 expect_par_or_arg = 0;
3938 expect_par = 0;
3939 expect_arg = 0;
3941 if (!opt->args)
3942 expect_par = 1; /* Parameter doesn't accept any argument. */
3943 else
3945 if (!opt->optional_args)
3946 expect_arg = 1; /* Parameter has mandatory arguments. */
3947 else
3948 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3952 else if (expect_par && *par_name != '-')
3954 ll_node_t *n = cli_node->next;
3956 if (!flags.stop_if_non_option)
3957 /* Look if potential arguments must still be analyzed until the */
3958 /* end of the context/command line part to analyze/command line. */
3959 /* If this is the case we have met an extra argument. */
3960 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3961 while (n != NULL)
3963 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3964 fatal(CTXOPTUNXARG, "");
3966 if (*(char *)(n->data) == '-')
3967 fatal(CTXOPTUNXARG, "");
3969 n = n->next;
3972 break; /* An unexpected non parameter was seen, if no Potential *
3973 | arguments remain in the command line or *
3974 | flags.stop_if_non_option is set, assume that it is is *
3975 | the first of the non arguments and stop the command *
3976 | line analysis. */
3978 else if (expect_arg && *par_name != '-')
3980 ll_node_t *cstr_node;
3981 constraint_t *cstr;
3983 if (ctxopt_debug)
3984 fprintf(stderr, "CTXOPT_DEBUG: Argument: %s.\n", par_name);
3986 /* Check if the arguments of the option respects */
3987 /* the attached constraints if any. */
3988 /* """"""""""""""""""""""""""""""""""""""""""""" */
3989 cstr_node = opt->constraints_list->head;
3990 while (cstr_node != NULL)
3992 cstr = cstr_node->data;
3993 if (!cstr->constraint(cstr->nb_args,
3994 cstr->args,
3995 par_name,
3996 cur_state->cur_opt_par_name))
3998 fputs("\n", stderr);
3999 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
4002 cstr_node = cstr_node->next;
4005 /* If the argument is valid, store it. */
4006 /* """"""""""""""""""""""""""""""""""" */
4007 if (*par_name == '\\' && *(par_name + 1) == '-')
4008 ll_append(opt_inst->values_list, par_name + 1);
4009 else
4010 ll_append(opt_inst->values_list, par_name);
4012 expect_arg = 0;
4013 expect_par = 0;
4014 expect_par_or_arg = 0;
4016 if (opt->multiple_args)
4017 expect_par_or_arg = 1;
4018 else
4019 expect_par = 1; /* Parameter takes only one argument. */
4021 else if (expect_arg && *par_name == '-')
4022 fatal(CTXOPTMISARG, "");
4023 else if (expect_par_or_arg)
4025 expect_arg = 0;
4026 expect_par = 0;
4027 expect_par_or_arg = 0;
4029 if (*par_name != '-')
4030 expect_arg = 1; /* Consider this word as an argument and retry. */
4031 else
4032 expect_par = 1; /* Consider this word as a parameter and retry. */
4034 cli_node = cli_node->prev;
4037 cli_node = cli_node->next;
4040 if (cmdline_list->len > 0 && par_name && *par_name == '-')
4042 if (expect_arg && opt && !opt->optional_args)
4043 fatal(CTXOPTMISARG, "");
4046 /* Look if a context_instance has unseen mandatory options. */
4047 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4048 node = ctx_inst_list->head;
4049 while (node != NULL)
4051 ctx_inst = node->data;
4053 /* Update current_state. */
4054 /* """"""""""""""""""""" */
4055 cur_state->ctx_name = ctx_inst->ctx->name;
4056 cur_state->ctx_par_name = ctx_inst->par_name;
4058 check_for_missing_mandatory_opt(ctx_inst, par_name);
4059 check_for_occurrence_issues(ctx_inst);
4060 check_for_requirement_issues(ctx_inst);
4062 node = node->next;
4065 /* Allocate the array containing the remaining not analyzed */
4066 /* command line arguments. */
4067 /* NOTE: The strings in the array are just pointer to the */
4068 /* data of the generating list and must not be freed. */
4069 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4070 if (cli_node != NULL)
4072 if (strcmp((char *)cli_node->data, "--") == 0)
4073 /* The special parameter -- was encountered, the -- argument is not */
4074 /* put in the remaining arguments. */
4075 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4076 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
4077 else
4078 /* A non parameter was encountered when a parameter was expected. We */
4079 /* assume that the evaluation of the remaining command line argument */
4080 /* are not the responsibility of the users code. */
4081 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4082 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
4084 else
4086 *nb_rem_args = 0;
4087 *rem_args = xmalloc(sizeof(char *));
4088 (*rem_args)[0] = NULL;
4092 /* ==================================================== */
4093 /* Free ctxopt memory used for its internal structures. */
4094 /* ==================================================== */
4095 void
4096 ctxopt_free_memory(void)
4098 ll_destroy(cmdline_list, free);
4099 ll_destroy(ctx_inst_list, ctx_inst_free);
4100 bst_destroy(options_bst, opt_free);
4101 bst_destroy(contexts_bst, ctx_free);
4104 /* ==================================================================== */
4105 /* Parse the options data structures and launches the callback function */
4106 /* attached to each options instances. */
4107 /* This calls a recursive function which proceeds context per context. */
4108 /* ==================================================================== */
4109 void
4110 ctxopt_evaluate(void)
4112 evaluate_ctx_inst(first_ctx_inst);
4115 /* =================================================================== */
4116 /* Recursive function called by ctxopt_evaluate to process the list of */
4117 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
4118 /* action attached to the context and its option instances. */
4119 /* =================================================================== */
4120 static void
4121 evaluate_ctx_inst(ctx_inst_t *ctx_inst)
4123 opt_inst_t *opt_inst;
4124 ctx_t *ctx;
4125 opt_t *opt;
4126 ll_node_t *opt_inst_node;
4127 char **args;
4128 int nb_args;
4130 if (ctx_inst == NULL)
4131 return;
4133 ctx = ctx_inst->ctx;
4135 /* Do not evaluate the action attached to this context is there is no */
4136 /* option to evaluate. */
4137 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4138 opt_inst_node = ctx_inst->opt_inst_list->head;
4139 if (opt_inst_node == NULL)
4140 return;
4142 /* Call the entering action attached to this context if any. */
4143 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4144 if (ctx->action != NULL)
4146 if (ctx_inst->prev_ctx_inst != NULL)
4147 ctx->action(ctx->name,
4148 entering,
4149 ctx_inst->prev_ctx_inst->ctx->name,
4150 ctx->nb_data,
4151 ctx->data);
4152 else
4153 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
4156 /* For each instance of options. */
4157 /* """"""""""""""""""""""""""""" */
4158 while (opt_inst_node != NULL)
4160 opt_inst = (opt_inst_t *)(opt_inst_node->data);
4161 ll_strarray(opt_inst->values_list,
4162 opt_inst->values_list->head,
4163 &nb_args,
4164 &args);
4165 opt = opt_inst->opt;
4167 /* Launch the attached action if any. */
4168 /* """""""""""""""""""""""""""""""""" */
4169 if (opt->action != NULL)
4170 opt->action(ctx->name,
4171 opt->name,
4172 opt_inst->par,
4173 nb_args,
4174 args,
4175 opt->nb_data,
4176 opt->data,
4177 ctx->nb_data,
4178 ctx->data);
4180 if (opt_inst->next_ctx_inst != NULL)
4181 evaluate_ctx_inst(opt_inst->next_ctx_inst);
4183 if (args != NULL)
4184 free(args);
4186 opt_inst_node = opt_inst_node->next;
4189 /* Call the exiting action attached to this context if any. */
4190 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4191 if (ctx->action != NULL)
4193 if (ctx_inst->prev_ctx_inst != NULL)
4194 ctx->action(ctx->name,
4195 exiting,
4196 ctx_inst->prev_ctx_inst->ctx->name,
4197 ctx->nb_data,
4198 ctx->data);
4199 else
4200 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
4204 /* ============================================================ */
4205 /* Create and initializes a new context. */
4206 /* - allocate space. */
4207 /* - name it. */
4208 /* - initialize its option with a few of their characteristics. */
4209 /* ============================================================ */
4210 void
4211 ctxopt_new_ctx(char *name, char *opts_specs)
4213 ctx_t *ctx;
4214 char *p;
4216 if (!ctxopt_initialized)
4217 fatal_internal("Please call ctxopt_init first.");
4219 ctx = xmalloc(sizeof(ctx_t));
4221 /* Validates the context name: */
4222 /* ALPHA+(ALPHANUM|_)* */
4223 /* """"""""""""""""""""""""""" */
4224 p = name;
4225 if (*p == '\0' || !isalpha(*p))
4226 fatal_internal("A context name must start with a letter: %s.", name);
4228 p++;
4229 while (*p)
4231 if (!isalnum(*p) && *p != '_')
4232 fatal_internal("A context name must only contain letters, "
4233 "numbers or '_': %s.",
4234 name);
4235 p++;
4238 ctx->name = xstrdup(name);
4239 ctx->opt_list = ll_new(); /* List of options legit in this context. */
4240 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
4241 ctx->req_list = ll_new(); /* List of opts/required opts tuples (str). */
4242 ctx->par_bst = NULL;
4243 ctx->data = NULL;
4244 ctx->action = NULL;
4246 /* The first created context is the main one. */
4247 /* """""""""""""""""""""""""""""""""""""""""" */
4248 if (contexts_bst == NULL)
4250 main_ctx = ctx;
4252 cur_state->ctx_name = ctx->name;
4255 if (init_opts(opts_specs, ctx) == 0)
4256 exit(EXIT_FAILURE);
4257 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
4258 fatal_internal("The context %s already exists.", name);
4259 else
4260 bst_search(ctx, &contexts_bst, ctx_compare);
4263 /* ==================================================== */
4264 /* Display a usage screen limited to a specific context */
4265 /* IN: the context name. */
4266 /* IN: what to do after (continue or exit the program) */
4267 /* possible values: continue_after, exit_after. */
4268 /* ==================================================== */
4269 void
4270 ctxopt_ctx_disp_usage(char *ctx_name, usage_behaviour action)
4272 ctx_t *ctx;
4273 ll_t *list;
4275 int has_optional = 0;
4276 int has_ellipsis = 0;
4277 int has_rule = 0;
4278 int has_generic_arg = 0;
4279 int has_ctx_change = 0;
4280 int has_early_eval = 0;
4282 if (!flags.display_usage_on_error)
4283 return;
4285 ctx = NULL;
4287 if (ctx_name != NULL)
4288 ctx = locate_ctx(ctx_name);
4289 else
4290 ctx = locate_ctx(cur_state->ctx_name);
4292 if (ctx == NULL)
4293 fatal_internal("Unknown context %s.", ctx_name);
4295 if (cur_state->ctx_par_name == NULL)
4296 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
4297 else
4298 printf("\nSynopsis for the context introduced by %s:\n",
4299 cur_state->ctx_par_name);
4301 list = ctx->opt_list;
4302 print_options(list,
4303 &has_optional,
4304 &has_ellipsis,
4305 &has_rule,
4306 &has_generic_arg,
4307 &has_ctx_change,
4308 &has_early_eval);
4310 print_before_constraints(list);
4312 print_explanations(has_early_eval,
4313 has_ctx_change,
4314 has_generic_arg,
4315 has_optional,
4316 has_ellipsis,
4317 has_rule);
4319 if (action == exit_after)
4320 exit(EXIT_FAILURE);
4323 /* =================================================== */
4324 /* Display a full usage screen about all contexts. */
4325 /* IN: what to do after (continue or exit the program) */
4326 /* possible values: continue_after, exit_after. */
4327 /* =================================================== */
4328 void
4329 ctxopt_disp_usage(usage_behaviour action)
4331 ll_t *list;
4332 int has_optional = 0;
4333 int has_ellipsis = 0;
4334 int has_rule = 0;
4335 int has_generic_arg = 0;
4336 int has_ctx_change = 0;
4337 int has_early_eval = 0;
4339 if (!flags.display_usage_on_error)
4340 return;
4342 if (main_ctx == NULL)
4343 fatal_internal("At least one context must have been created.");
4345 /* Usage for the first context. */
4346 /* """""""""""""""""""""""""""" */
4347 printf("\nAllowed options in the base context:\n");
4348 list = main_ctx->opt_list;
4349 print_options(list,
4350 &has_optional,
4351 &has_ellipsis,
4352 &has_rule,
4353 &has_generic_arg,
4354 &has_ctx_change,
4355 &has_early_eval);
4357 /* Dependency constraints between options. */
4358 /* """"""""""""""""""""""""""""""""""""""" */
4359 print_before_constraints(list);
4361 /* Usage for the other contexts. */
4362 /* """"""""""""""""""""""""""""" */
4363 bst_walk(contexts_bst, bst_print_ctx_cb);
4365 /* Contextual syntactic explanations. */
4366 /* """""""""""""""""""""""""""""""""" */
4367 print_explanations(has_early_eval,
4368 has_ctx_change,
4369 has_generic_arg,
4370 has_optional,
4371 has_ellipsis,
4372 has_rule);
4374 if (action == exit_after)
4375 exit(EXIT_FAILURE);
4378 /* ************************************ */
4379 /* Built-in constraint check functions. */
4380 /* ************************************ */
4382 /* ============================================================= */
4383 /* This constraint checks if each arguments respects a format as */
4384 /* defined for the scanf function. */
4385 /* return 1 if yes and 0 if no. */
4386 /* ============================================================= */
4388 ctxopt_format_constraint(int nb_args, char **args, char *value, char *par)
4390 int rc = 0;
4392 char x[256];
4393 char y;
4394 char *format;
4396 if (nb_args != 1)
4397 fatal_internal("Format constraint, invalid number of parameters.");
4399 if (strlen(value) > 255)
4400 value[255] = '\0';
4402 format = xstrdup(args[0]);
4403 format = strappend(format, "%c", (char *)0);
4405 rc = sscanf(value, format, x, &y);
4406 if (rc != 1)
4407 fprintf(stderr,
4408 "The argument %s of %s does not respect the imposed format %s.",
4409 value,
4410 par,
4411 args[0]);
4413 free(format);
4415 return rc == 1;
4418 /* ================================================================== */
4419 /* This constraint checks if each arguments of the option instance is */
4420 /* between a minimum and a maximum (inclusive). */
4421 /* return 1 if yes and 0 if no. */
4422 /* ================================================================== */
4424 ctxopt_re_constraint(int nb_args, char **args, char *value, char *par)
4426 regex_t re;
4428 if (nb_args != 1)
4429 fatal_internal(
4430 "Regular expression constraint, invalid number of parameters.");
4432 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
4433 fatal_internal("Invalid regular expression %s.", args[0]);
4435 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
4437 fprintf(stderr,
4438 "The argument %s of %s doesn't match the constraining "
4439 "regular expression %s.",
4440 value,
4441 par,
4442 args[0]);
4443 return 0;
4446 regfree(&re);
4448 return 1;
4451 /* ================================================================== */
4452 /* This constraint checks if each arguments of the option instance is */
4453 /* between a minimum and a maximum (inclusive). */
4454 /* return 1 if yes and 0 if no. */
4455 /* ================================================================== */
4457 ctxopt_range_constraint(int nb_args, char **args, char *value, char *par)
4459 long min = LONG_MIN, max = LONG_MAX;
4460 char c;
4461 char *ptr;
4462 int n;
4463 long v;
4464 int min_only = 0;
4465 int max_only = 0;
4467 if (nb_args != 2)
4468 fatal_internal("Range constraint, invalid number of parameters.");
4470 n = 0;
4471 if (strcmp(args[0], ".") == 0)
4472 max_only = 1;
4473 else
4474 n = sscanf(args[0], "%ld%c", &min, &c);
4476 if (!max_only && n != 1)
4477 fatal_internal("Range constraint, min: invalid parameters.");
4479 n = 0;
4480 if (strcmp(args[1], ".") == 0)
4481 min_only = 1;
4482 else
4483 n = sscanf(args[1], "%ld%c", &max, &c);
4485 if (!min_only && n != 1)
4486 fatal_internal("Range constraint, max: invalid parameters.");
4488 if (min_only && max_only)
4489 fatal_internal("Range constraint, invalid parameters.");
4491 errno = 0;
4492 v = strtol(value, &ptr, 10);
4493 if (errno || ptr == value)
4494 return 0;
4496 if (min_only)
4498 if (v < min)
4500 fprintf(stderr,
4501 "The argument %ld of %s is not greater than or equal to %ld.",
4503 par,
4504 min);
4505 return 0;
4507 else
4508 return 1;
4510 else if (max_only)
4512 if (v > max)
4514 fprintf(stderr,
4515 "The argument %ld of %s is not less than or equal to %ld.",
4517 par,
4518 max);
4519 return 0;
4521 else
4522 return 1;
4524 else if (v < min || v > max)
4526 fprintf(stderr,
4527 "The argument %ld of %s is not between %ld and %ld.",
4529 par,
4530 min,
4531 max);
4532 return 0;
4535 return 1; /* Check passed. */
4538 /* =============================================================== */
4539 /* This function provides a way to set the behaviour of a context. */
4540 /* =============================================================== */
4541 void
4542 ctxopt_add_global_settings(settings s, ...)
4544 va_list(args);
4545 va_start(args, s);
4547 switch (s)
4549 case error_functions:
4551 typedef void fn(errors e, state_t * state);
4553 void (*function)(errors e, state_t *state);
4555 errors e;
4556 e = va_arg(args, errors);
4557 function = va_arg(args, fn *);
4558 err_functions[e] = function;
4559 break;
4562 default:
4563 break;
4565 va_end(args);
4568 /* ================================================================ */
4569 /* This function provides a way to set the behaviour of an option. */
4570 /* It can take a variable number of arguments according to its */
4571 /* first argument: */
4572 /* - parameter: */
4573 /* o a string containing an option name and all its possible */
4574 /* parameters separates by spaces, tabs or commas (char *) */
4575 /* (e.g: "help -h -help"). */
4576 /* - actions: */
4577 /* o a string containing an option name. */
4578 /* o a pointer to a function which will be called at evaluation */
4579 /* time. */
4580 /* - constraints: */
4581 /* o a string containing an option name. */
4582 /* o a pointer to a function to check if an argument is valid. */
4583 /* o a strings containing the arguments to this function. */
4584 /* - visible_in_help: */
4585 /* o a string containing an option name. */
4586 /* o the string "yes" or "no" (case insensitive) which will */
4587 /* determine if the option must be seen in help/usage. */
4588 /* ================================================================ */
4589 void
4590 ctxopt_add_opt_settings(settings s, ...)
4592 opt_t *opt;
4593 void *ptr = NULL;
4595 va_list(args);
4596 va_start(args, s);
4598 switch (s)
4600 /* This part associates some command line parameters to an option. */
4601 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4602 case parameters:
4604 char *opt_name = NULL;
4605 char *params;
4607 /* The second argument must be a string containing: */
4608 /* - The name of an existing option. */
4609 /* - a list of parameters with a leading dash (-). */
4610 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4611 ptr = va_arg(args, char *);
4612 opt_name = ptr;
4614 if (opt_name != NULL)
4616 if ((opt = locate_opt(opt_name)) != NULL)
4618 ptr = va_arg(args, char *);
4619 params = ptr;
4621 if (!opt_set_parms(opt_name, params))
4622 fatal_internal(
4623 "Duplicated parameters or bad settings for the option %s.",
4624 params);
4626 else
4627 fatal_internal("Unknown option %s.", opt_name);
4629 else
4630 fatal_internal(
4631 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4633 /* Here opt is a known option. */
4634 /* """"""""""""""""""""""""""" */
4635 if (opt->params != NULL)
4636 fatal_internal("Parameters are already set for %s.", opt_name);
4637 else
4639 size_t n;
4640 size_t l = strlen(params);
4642 opt->params = xstrdup(params);
4643 while ((n = strcspn(opt->params, " \t")) < l)
4644 opt->params[n] = '|';
4647 break;
4650 /* This part associates a callback function to an option. */
4651 /* This function will be called when an instance of an option */
4652 /* is evaluated. */
4653 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4654 case actions:
4656 void *data;
4657 void (*function)();
4658 int nb_data = 0;
4660 /* The second argument must be the name of an existing option. */
4661 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4662 ptr = va_arg(args, char *);
4664 if ((opt = locate_opt(ptr)) != NULL)
4666 typedef void
4667 fn(char *, char *, char *, int, char **, int, void *, int, void **);
4669 /* The third argument must be the callback function. */
4670 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4671 function = va_arg(args, fn *);
4672 opt->action = function;
4674 /* The fourth argument must be a pointer to an user's defined */
4675 /* variable or structure that the previous function can manage. */
4676 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4677 while ((data = va_arg(args, void *)) != NULL)
4679 nb_data++;
4680 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4681 opt->data[nb_data - 1] = data;
4683 opt->nb_data = nb_data;
4685 else
4686 fatal_internal("Unknown option %s.", ptr);
4687 break;
4690 /* This part associates a list of functions to control some */
4691 /* characteristics of the arguments of an option. */
4692 /* Each function will be called in order and must return 1 */
4693 /* to validate the arguments. */
4694 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4695 case constraints:
4697 char *value;
4698 constraint_t *cstr;
4699 int (*function)();
4701 /* The second argument must be a string. */
4702 /* """"""""""""""""""""""""""""""""""""" */
4703 ptr = va_arg(args, char *);
4705 if ((opt = locate_opt(ptr)) != NULL)
4707 typedef int fn(int, char **, char *);
4709 /* The third argument must be a function. */
4710 /* """""""""""""""""""""""""""""""""""""" */
4711 function = va_arg(args, fn *);
4713 cstr = xmalloc(sizeof(constraint_t));
4714 cstr->constraint = function;
4716 /* The fourth argument must be a string containing the argument of */
4717 /* The previous function separated by spaces or tabs. */
4718 /* Theses arguments will be passed to the previous function */
4719 /* max: 32 argument! */
4720 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4721 value = xstrdup(va_arg(args, char *));
4723 cstr->to_free = value;
4724 cstr->args = xcalloc(sizeof(char *), 32);
4725 cstr->nb_args = str2argv(value, cstr->args, 32);
4726 ll_append(opt->constraints_list, cstr);
4728 else
4729 fatal_internal("Unknown option %s.", ptr);
4730 break;
4733 /* This part allows to indicate that an option must be evaluated */
4734 /* after a list of other options. */
4735 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4736 case after:
4738 char *str;
4740 /* The second argument must be a string. */
4741 /* """"""""""""""""""""""""""""""""""""" */
4742 ptr = va_arg(args, char *);
4744 if ((opt = locate_opt(ptr)) != NULL)
4746 char *end_str;
4747 char *opt_name;
4748 opt_t *opt_before;
4750 ptr = va_arg(args, char *);
4752 str = xstrdup(ptr);
4753 ltrim(str, " \t");
4754 rtrim(str, " \t", 0);
4756 /* Feed the list of options to be evaluated after the given option. */
4757 /* This list will contain pointers to options. */
4758 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4759 opt_name = xstrtok_r(str, " \t,", &end_str);
4760 if (opt_name != NULL)
4762 if ((opt_before = locate_opt(opt_name)) != NULL)
4764 ll_append(opt->eval_before_list, opt_before);
4765 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4767 if ((opt_before = locate_opt(opt_name)) != NULL)
4768 ll_append(opt->eval_before_list, opt_before);
4769 else
4770 fatal_internal("Unknown option %s.", opt_name);
4773 else
4774 fatal_internal("Unknown option %s.", opt_name);
4776 else
4777 fatal_internal("Not enough options to be evaluated after %s.",
4778 opt->name);
4780 free(str);
4782 else
4783 fatal_internal("Unknown option %s.", ptr);
4785 break;
4788 /* This part allows to indicate that an option must be evaluated */
4789 /* before a list of other options. */
4790 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4791 case before:
4793 char *str;
4795 /* The second argument must be a string. */
4796 /* """"""""""""""""""""""""""""""""""""" */
4797 ptr = va_arg(args, char *);
4799 if ((opt = locate_opt(ptr)) != NULL)
4801 char *end_str;
4802 char *opt_name;
4803 opt_t *opt_before;
4805 ptr = va_arg(args, char *);
4807 str = xstrdup(ptr);
4808 ltrim(str, " \t");
4809 rtrim(str, " \t", 0);
4811 /* Feed the list of options to be evaluated before the given option. */
4812 /* This list will contain pointers to options. */
4813 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4814 opt_name = xstrtok_r(str, " \t,", &end_str);
4815 if (opt_name != NULL)
4817 if ((opt_before = locate_opt(opt_name)) != NULL)
4819 ll_append(opt_before->eval_before_list, opt);
4820 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4822 if ((opt_before = locate_opt(opt_name)) != NULL)
4823 ll_append(opt_before->eval_before_list, opt);
4824 else
4825 fatal_internal("Unknown option %s.", opt_name);
4828 else
4829 fatal_internal("Unknown option %s.", opt_name);
4831 else
4832 fatal_internal("Not enough options to be evaluated before %s.",
4833 opt->name);
4835 free(str);
4837 else
4838 fatal_internal("Unknown option %s.", ptr);
4840 break;
4843 case visible_in_help:
4845 char *opt_name;
4846 char *toggle;
4848 /* The second argument must be a string containing */
4849 /* The name of an existing option. */
4850 /* """"""""""""""""""""""""""""""""""""""""""""""" */
4851 ptr = va_arg(args, char *);
4852 opt_name = ptr;
4854 if (opt_name != NULL)
4856 if ((opt = locate_opt(opt_name)) != NULL)
4858 ptr = va_arg(args, char *);
4859 toggle = ptr;
4861 if (stricmp(toggle, "yes") == 0)
4862 opt->visible_in_help = 1;
4863 else if (stricmp(toggle, "no") == 0)
4864 opt->visible_in_help = 0;
4865 else
4866 fatal_internal("The value for the visible_in_help setting must be "
4867 "\"yes\" or \"no\" (case insensitive).",
4868 toggle);
4870 else
4871 fatal_internal("Unknown option %s.", opt_name);
4873 else
4874 fatal_internal(
4875 "ctxopt_opt_add_settings: visible_in_help: not enough arguments.");
4877 break;
4880 default:
4881 break;
4883 va_end(args);
4886 /* =============================================================== */
4887 /* This function provides a way to set the behaviour of a context. */
4888 /* =============================================================== */
4889 void
4890 ctxopt_add_ctx_settings(settings s, ...)
4892 ctx_t *ctx;
4894 va_list(args);
4895 va_start(args, s);
4897 switch (s)
4899 /* Add a set of mutually incompatible options in a context. */
4900 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4901 case incompatibilities:
4903 void *ptr;
4904 ll_t *list;
4905 size_t n;
4906 char *str;
4908 ptr = va_arg(args, char *);
4909 if ((ctx = locate_ctx(ptr)) != NULL)
4911 ptr = va_arg(args, char *);
4912 list = ctx->incomp_list;
4914 str = xstrdup(ptr);
4915 ltrim(str, " \t");
4916 rtrim(str, " \t", 0);
4918 n = strcspn(str, " \t");
4919 if (n > 0 && n < strlen(str))
4920 ll_append(list, str);
4921 else
4922 fatal_internal(
4923 "Not enough incompatible options in the string: \"%s\".",
4924 str);
4926 else
4927 fatal_internal("Unknown context %s.", ptr);
4928 break;
4931 case requirements:
4933 void *ptr;
4934 ll_t *list;
4935 size_t n;
4936 char *str;
4938 ptr = va_arg(args, char *);
4939 if ((ctx = locate_ctx(ptr)) != NULL)
4941 ptr = va_arg(args, char *);
4942 list = ctx->req_list;
4944 str = xstrdup(ptr);
4945 ltrim(str, " \t");
4946 rtrim(str, " \t", 0);
4948 n = strcspn(str, " \t");
4949 if (n > 0 && n < strlen(str))
4950 ll_append(list, str);
4951 else
4952 fatal_internal("Not enough required options in the string: \"%s\".",
4953 str);
4955 else
4956 fatal_internal("Unknown context %s.", ptr);
4957 break;
4960 /* Add functions which will be called when */
4961 /* entering and exiting a context. */
4962 /* """"""""""""""""""""""""""""""""""""""" */
4963 case actions:
4965 void *ptr;
4966 void *data;
4967 int (*function)();
4968 int nb_data = 0;
4970 ptr = va_arg(args, char *);
4971 if ((ctx = locate_ctx(ptr)) != NULL)
4973 typedef int fn(char *, direction, char *, int, void **);
4975 function = va_arg(args, fn *);
4976 ctx->action = function;
4978 while ((data = va_arg(args, void *)) != NULL)
4980 nb_data++;
4981 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4982 ctx->data[nb_data - 1] = data;
4984 ctx->nb_data = nb_data;
4986 else
4987 fatal_internal("Unknown context %s.", ptr);
4988 break;
4991 default:
4992 break;
4994 va_end(args);