1 /* ################################################################### */
2 /* This Source Code Form is subject to the terms of the Mozilla Public */
3 /* License, v. 2.0. If a copy of the MPL was not distributed with this */
4 /* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 /* ################################################################### */
6
7 #include <errno.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <ctype.h>
13 #include <sys/types.h>
14 #include <regex.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include "ctxopt.h"
18
19 /* ************************ */
20 /* Static global variables. */
21 /* ************************ */
22
23 static void * contexts_bst;
24 static void * options_bst;
25
26 state_t * cur_state;
27
28 /* Prototypes */
29
30 /* ************************** */
31 /* Fatal messages prototypes. */
32 /* ************************** */
33
34 static void (**err_functions)(errors e, state_t * state);
35
36 static void
37 fatal_internal(const char * format, ...);
38
39 static void
40 fatal(errors e, char * errmsg);
41
42 static int user_rc; /* Used by various callback functions. */
43 static int user_value; /* Used by various callback functions. */
44 static char * user_string; /* Used by various callback functions. */
45 static char * user_string2; /* Used by various callback functions. */
46 static void * user_object; /* Used by various callback functions. */
47
48 /* ************************************ */
49 /* Memory management static prototypes. */
50 /* ************************************ */
51
52 static void *
53 xmalloc(size_t size);
54
55 static void *
56 xcalloc(size_t num, size_t size);
57
58 static void *
59 xrealloc(void * ptr, size_t size);
60
61 static char *
62 xstrdup(const char * p);
63
64 static char *
65 xstrndup(const char * str, size_t len);
66
67 /* ********************** */
68 /* BST static prototypes. */
69 /* ********************** */
70
71 typedef struct bst_s bst_t;
72
73 typedef enum
74 {
75 preorder,
76 postorder,
77 endorder,
78 leaf
79 } walk_order_e;
80
81 #if 0 /* Unused yet. */
82 static void *
83 bst_delete(const void * vkey, void ** vrootp,
84 int (*compar)(const void *, const void *));
85 #endif
86
87 static void
88 bst_destroy(void * vrootp, void (*clean)(void *));
89
90 static void *
91 bst_find(const void * vkey, void * const * vrootp,
92 int (*compar)(const void *, const void *));
93
94 static void *
95 bst_search(void * vkey, void ** vrootp,
96 int (*compar)(const void *, const void *));
97
98 static void
99 bst_walk_recurse(const bst_t * root,
100 void (*action)(const void *, walk_order_e, int), int level);
101
102 static void
103 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int));
104
105 /* ****************************** */
106 /* Linked list static prototypes. */
107 /* ****************************** */
108
109 typedef struct ll_node_s ll_node_t;
110 typedef struct ll_s ll_t;
111
112 static void
113 ll_append(ll_t * const list, void * const data);
114
115 static void
116 ll_prepend(ll_t * const list, void * const data);
117
118 static void
119 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data);
120
121 static void
122 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data);
123
124 static int
125 ll_delete(ll_t * const list, ll_node_t * node);
126
127 #if 0 /* Unused yet. */
128 static ll_node_t *
129 ll_find(ll_t * const, void * const, int (*)(const void *, const void *));
130 #endif
131
132 static void
133 ll_init(ll_t * list);
134
135 static ll_node_t *
136 ll_new_node(void);
137
138 static ll_t *
139 ll_new(void);
140
141 static void
142 ll_free(ll_t * const list, void (*)(void *));
143
144 static void
145 ll_destroy(ll_t * const list, void (*)(void *));
146
147 static int
148 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array);
149
150 /* ************************** */
151 /* Various static prototypes. */
152 /* ************************** */
153
154 static void
155 ltrim(char * str, const char * trim_str);
156
157 static void
158 rtrim(char * str, const char * trim_str, size_t min);
159
160 static int
161 strchrcount(char * str, char c);
162
163 static int
164 strpref(char * s1, char * s2);
165
166 static int
167 stricmp(const char * s1, const char * s2);
168
169 static char *
170 xstrtok_r(char * str, const char * delim, char ** end);
171
172 static int
173 eval_yes(char * value, int * invalid);
174
175 static char *
176 get_word(char * str, char * buf, size_t len);
177
178 /* ************************* */
179 /* ctxopt static prototypes. */
180 /* ************************* */
181
182 typedef struct flags_s flags_t;
183 typedef struct opt_s opt_t;
184 typedef struct par_s par_t;
185 typedef struct ctx_s ctx_t;
186 typedef struct constraint_s constraint_t;
187 typedef struct ctx_inst_s ctx_inst_t;
188 typedef struct opt_inst_s opt_inst_t;
189 typedef struct seen_opt_s seen_opt_t;
190 typedef struct req_s req_t;
191
192 static char *
193 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos);
194
195 static int
196 ctx_compare(const void * c1, const void * c2);
197
198 static void
199 ctx_free(void * o);
200
201 static void
202 ctx_inst_free(void * ci);
203
204 static void
205 opt_inst_free(void * oi);
206
207 static int
208 seen_opt_compare(const void * so1, const void * so2);
209
210 static void
211 incomp_bst_free(void * b);
212
213 static void
214 req_free(void * r);
215
216 static void
217 seen_opt_free(void * seen_opt);
218
219 static int
220 opt_compare(const void * o1, const void * o2);
221
222 static void
223 opt_free(void * o);
224
225 static int
226 par_compare(const void * a1, const void * a2);
227
228 static void
229 par_free(void * p);
230
231 static void
232 constraint_free(void * cstr);
233
234 static ctx_t *
235 locate_ctx(char * name);
236
237 static opt_t *
238 locate_opt(char * name);
239
240 static par_t *
241 locate_par(char * name, ctx_t * ctx);
242
243 static void
244 print_before_constraints(ll_t * list);
245
246 static void
247 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
248 int * has_rule, int * has_generic_arg, int * has_ctx_change,
249 int * has_early_eval);
250 static void
251 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
252 int has_optional, int has_ellipsis, int has_rule);
253 static void
254 bst_seen_opt_cb(const void * node, walk_order_e kind, int level);
255
256 static void
257 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level);
258
259 static void
260 bst_print_ctx_cb(const void * node, walk_order_e kind, int level);
261
262 static void
263 bst_check_opt_cb(const void * node, walk_order_e kind, int level);
264
265 static void
266 bst_match_par_cb(const void * node, walk_order_e kind, int level);
267
268 static void
269 match_prefix_cb(const void * node, walk_order_e kind, int level);
270
271 static int
272 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing);
273
274 static int
275 opt_parse(char * s, opt_t ** opt);
276
277 static int
278 init_opts(char * spec, ctx_t * ctx);
279
280 static int
281 ctxopt_build_cmdline_list(int nb_words, char ** words);
282
283 static int
284 opt_set_parms(char * opt_name, char * par_str);
285
286 static ctx_inst_t *
287 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
288
289 static void
290 evaluate_ctx_inst(ctx_inst_t * ctx_inst);
291
292 /* ****************************** */
293 /* Fatal messages implementation. */
294 /* ****************************** */
295
296 /* =================================================================== */
297 /* Fatal error function used when a fatal condition is encountered. */
298 /* This function is reserved for the ctxopt internal usage. */
299 /* */
300 /* format : printf like format. */
301 /* ... : remaining arguments interpreted using the format argument. */
302 /* =================================================================== */
303 static void
fatal_internal(const char * format,...)304 fatal_internal(const char * format, ...)
305 {
306 va_list args;
307
308 fprintf(stderr, "CTXOPT: ");
309
310 va_start(args, format);
311 vfprintf(stderr, format, args);
312 fprintf(stderr, "\n");
313 va_end(args);
314
315 exit(EXIT_FAILURE);
316 }
317
318 /* ====================================================================== */
319 /* Generic fatal error function. This one uses the global status ctxopt */
320 /* stored in the cur_state structure and can call custom error functions. */
321 /* registered by the users for a given error identifier if any. */
322 /* */
323 /* e : Error identifier responsible of the fatal error. */
324 /* errmsg : Users's provided string specific to the error e. */
325 /* Note that errmsg is not used in all cases. */
326 /* */
327 /* CTXOPTMISPAR Missing parameter. */
328 /* CTXOPTREQPAR Option: all parameters in a required group are */
329 /* missing. */
330 /* CTXOPTMISARG Missing argument. */
331 /* CTXOPTUXPARG Unexpected argument. */
332 /* CTXOPTDUPOPT Duplicated option. */
333 /* CTXOPTUNKPAR Unknown parameter. */
334 /* CTXOPTINCOPT Incompatible option. */
335 /* CTXOPTCTEOPT Option: bad number of occurrences. */
336 /* CTXOPTCTLOPT Option: not enough occurrences. */
337 /* CTXOPTCTGOPT Option: too many occurrence of. */
338 /* CTXOPTCTEARG Arguments: bad number of occurrences. */
339 /* CTXOPTCTLARG Arguments: not enough occurrences. */
340 /* CTXOPTCTGARG Arguments: too many occurrences. */
341 /* ====================================================================== */
342 static void
fatal(errors e,char * errmsg)343 fatal(errors e, char * errmsg)
344 {
345 if (err_functions[e] != NULL)
346 err_functions[e](e, cur_state);
347 else
348 {
349 switch (e)
350 {
351 case CTXOPTNOERR:
352 break;
353
354 case CTXOPTMISPAR:
355 if (cur_state->ctx_par_name != NULL)
356 fprintf(stderr,
357 "the mandatory parameter(s) %s are missing in the context "
358 "introduced by %s.\n",
359 errmsg, cur_state->ctx_par_name);
360 else
361 fprintf(stderr,
362 "The mandatory parameter(s) %s are missing "
363 "in the main context.\n",
364 errmsg);
365
366 free(errmsg);
367 break;
368
369 case CTXOPTREQPAR:
370 fprintf(stderr, errmsg, cur_state->req_opt_par_needed,
371 cur_state->req_opt_par);
372 break;
373
374 case CTXOPTUNXARG:
375 if (cur_state->cur_opt_par_name != NULL)
376 fprintf(stderr,
377 "The parameter %s takes no arguments "
378 "or has too many arguments.\n",
379 cur_state->cur_opt_par_name);
380 break;
381
382 case CTXOPTMISARG:
383 if (cur_state->pre_opt_par_name != NULL)
384 fprintf(stderr, "%s requires argument(s).\n",
385 cur_state->pre_opt_par_name);
386 else
387 fprintf(stderr, "%s requires argument(s).\n",
388 cur_state->cur_opt_par_name);
389 break;
390
391 case CTXOPTDUPOPT:
392 if (cur_state->pre_opt_par_name != NULL)
393 fprintf(stderr,
394 "The parameter %s can only appear once in the context "
395 "introduced by %s.\n",
396 cur_state->cur_opt_params, cur_state->ctx_par_name);
397 else
398 fprintf(stderr,
399 "The parameter %s can only appear once "
400 "in the main context.\n",
401 cur_state->cur_opt_params);
402 break;
403
404 case CTXOPTUNKPAR:
405 fprintf(stderr, "Unknown parameter %s.\n%s",
406 cur_state->cur_opt_par_name, errmsg);
407 break;
408
409 case CTXOPTINCOPT:
410 fprintf(stderr, "The parameter %s is incompatible with %s.\n",
411 cur_state->cur_opt_par_name, errmsg);
412 break;
413
414 case CTXOPTCTEOPT:
415 if (cur_state->ctx_par_name)
416 fprintf(stderr,
417 "The parameter %s must appear exactly %d times "
418 "in the context introduced by %s.\n",
419 cur_state->cur_opt_params, cur_state->opts_count,
420 cur_state->ctx_par_name);
421 else
422 fprintf(stderr,
423 "The parameter %s must appear exactly %d times "
424 "in the main context.\n",
425 cur_state->cur_opt_params, cur_state->opts_count);
426 break;
427
428 case CTXOPTCTLOPT:
429 if (cur_state->ctx_par_name)
430 fprintf(stderr,
431 "The parameter %s must appear less than %d times "
432 "in the context introduced by %s.\n",
433 cur_state->cur_opt_params, cur_state->opts_count,
434 cur_state->ctx_par_name);
435 else
436 fprintf(stderr,
437 "The parameter %s must appear less than %d times "
438 "in the main context.\n",
439 cur_state->cur_opt_params, cur_state->opts_count);
440 break;
441
442 case CTXOPTCTGOPT:
443 if (cur_state->ctx_par_name)
444 fprintf(stderr,
445 "The parameter %s must appear more than %d times "
446 "in the context introduced by %s.\n",
447 cur_state->cur_opt_params, cur_state->opts_count,
448 cur_state->ctx_par_name);
449 else
450 fprintf(stderr,
451 "The parameter %s must appear more than %d times "
452 "in the main context.\n",
453 cur_state->cur_opt_params, cur_state->opts_count);
454 break;
455
456 case CTXOPTCTEARG:
457 fprintf(stderr, "The parameter %s must have exactly %d arguments.\n",
458 cur_state->cur_opt_par_name, cur_state->opt_args_count);
459 break;
460
461 case CTXOPTCTLARG:
462 fprintf(stderr, "The parameter %s must have less than %d arguments.\n",
463 cur_state->cur_opt_par_name, cur_state->opt_args_count);
464 break;
465
466 case CTXOPTCTGARG:
467 fprintf(stderr, "The parameter %s must have more than %d arguments.\n",
468 cur_state->cur_opt_par_name, cur_state->opt_args_count);
469 break;
470
471 case CTXOPTERRSIZ:
472 break;
473 }
474 }
475
476 /* CTXOPTUNKPAR should display the full usage to help the user follow */
477 /* the chaining of contexts when several possible contexts have been */
478 /* identified. Otherwise, errmsg is the empty string and the display of */
479 /* the current usage is enough. */
480 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
481 if (e == CTXOPTUNKPAR && *errmsg != '\0')
482 ctxopt_disp_usage(continue_after);
483 else
484 ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
485
486 exit(e); /* Exit with the error id e as return code. */
487 }
488
489 /* ********************************* */
490 /* Memory management implementation. */
491 /* ********************************* */
492
493 /* ================== */
494 /* Customized malloc. */
495 /* ================== */
496 static void *
xmalloc(size_t size)497 xmalloc(size_t size)
498 {
499 void * allocated;
500 size_t real_size;
501
502 real_size = (size > 0) ? size : 1;
503 allocated = malloc(real_size);
504 if (allocated == NULL)
505 fatal_internal("Insufficient memory (attempt to malloc %lu bytes).\n",
506 (unsigned long int)size);
507
508 return allocated;
509 }
510
511 /* ================== */
512 /* Customized calloc. */
513 /* ================== */
514 static void *
xcalloc(size_t n,size_t size)515 xcalloc(size_t n, size_t size)
516 {
517 void * allocated;
518
519 n = (n > 0) ? n : 1;
520 size = (size > 0) ? size : 1;
521 allocated = calloc(n, size);
522 if (allocated == NULL)
523 fatal_internal("Insufficient memory (attempt to calloc %lu bytes).\n",
524 (unsigned long int)size);
525
526 return allocated;
527 }
528
529 /* =================== */
530 /* Customized realloc. */
531 /* =================== */
532 static void *
xrealloc(void * p,size_t size)533 xrealloc(void * p, size_t size)
534 {
535 void * allocated;
536
537 allocated = realloc(p, size);
538 if (allocated == NULL && size > 0)
539 fatal_internal("Insufficient memory (attempt to xrealloc %lu bytes).\n",
540 (unsigned long int)size);
541
542 return allocated;
543 }
544
545 /* ==================================== */
546 /* strdup implementation using xmalloc. */
547 /* ==================================== */
548 static char *
xstrdup(const char * p)549 xstrdup(const char * p)
550 {
551 char * allocated;
552
553 allocated = xmalloc(strlen(p) + 1);
554 strcpy(allocated, p);
555
556 return allocated;
557 }
558
559 /* =================================================== */
560 /* strndup implementation using xmalloc. */
561 /* This version guarantees that there is a final '\0'. */
562 /* =================================================== */
563 static char *
xstrndup(const char * str,size_t len)564 xstrndup(const char * str, size_t len)
565 {
566 char * p;
567
568 p = memchr(str, '\0', len);
569
570 if (p)
571 len = p - str;
572
573 p = xmalloc(len + 1);
574 memcpy(p, str, len);
575 p[len] = '\0';
576
577 return p;
578 }
579
580 /* *************************** */
581 /* Linked list implementation. */
582 /* *************************** */
583
584 /* Linked list node structure. */
585 /* """"""""""""""""""""""""""" */
586 struct ll_node_s
587 {
588 void * data;
589 struct ll_node_s * next;
590 struct ll_node_s * prev;
591 };
592
593 /* Linked List structure. */
594 /* """""""""""""""""""""" */
595 struct ll_s
596 {
597 ll_node_t * head;
598 ll_node_t * tail;
599 long len;
600 };
601
602 /* ========================= */
603 /* Create a new linked list. */
604 /* ========================= */
605 static ll_t *
ll_new(void)606 ll_new(void)
607 {
608 ll_t * ret = xmalloc(sizeof(ll_t));
609 ll_init(ret);
610
611 return ret;
612 }
613
614 /* =============================================== */
615 /* Free all the elements of a list (make it empty) */
616 /* NULL or a custom function may be used to free */
617 /* the sub components of the elements. */
618 /* =============================================== */
619 static void
ll_free(ll_t * const list,void (* clean)(void *))620 ll_free(ll_t * const list, void (*clean)(void *))
621 {
622 if (list)
623 while (list->head)
624 {
625 /* Apply a custom cleaner if not NULL. */
626 /* """"""""""""""""""""""""""""""""""" */
627 if (clean)
628 clean(list->head->data);
629
630 ll_delete(list, list->head);
631 }
632 }
633
634 /* ==================================== */
635 /* Destroy a list and all its elements. */
636 /* ==================================== */
637 static void
ll_destroy(ll_t * list,void (* clean)(void *))638 ll_destroy(ll_t * list, void (*clean)(void *))
639 {
640 if (list)
641 {
642 ll_free(list, clean);
643 free(list);
644 }
645 }
646
647 /* ========================= */
648 /* Initialize a linked list. */
649 /* ========================= */
650 static void
ll_init(ll_t * list)651 ll_init(ll_t * list)
652 {
653 list->head = NULL;
654 list->tail = NULL;
655 list->len = 0;
656 }
657
658 /* ===================================================== */
659 /* Allocate the space for a new node in the linked list. */
660 /* ===================================================== */
661 static ll_node_t *
ll_new_node(void)662 ll_new_node(void)
663 {
664 ll_node_t * ret = xmalloc(sizeof(ll_node_t));
665
666 return ret;
667 }
668
669 /* ==================================================================== */
670 /* Append a new node filled with its data at the end of the linked list */
671 /* The user is responsible for the memory management of the data. */
672 /* ==================================================================== */
673 static void
ll_append(ll_t * const list,void * const data)674 ll_append(ll_t * const list, void * const data)
675 {
676 ll_node_t * node;
677
678 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
679 | uses xmalloc which does not return if there *
680 | is an allocation error. */
681
682 node->data = data;
683 node->next = NULL;
684
685 node->prev = list->tail;
686 if (list->tail)
687 list->tail->next = node;
688 else
689 list->head = node;
690
691 list->tail = node;
692
693 ++list->len;
694 }
695
696 /* ================================================================== */
697 /* Put a new node filled with its data at the beginning of the linked */
698 /* list. */
699 /* The user is responsible for the memory management of the data. */
700 /* ================================================================== */
701 static void
ll_prepend(ll_t * const list,void * const data)702 ll_prepend(ll_t * const list, void * const data)
703 {
704 ll_node_t * node;
705
706 node = ll_new_node(); /* ll_new_node cannot return NULL because it *
707 | uses xmalloc which does not return if there *
708 | is an allocation error. */
709
710 node->data = data;
711 node->prev = NULL;
712
713 node->next = list->head;
714 if (list->head)
715 list->head->prev = node;
716 else
717 list->tail = node;
718
719 list->head = node;
720
721 ++list->len;
722 }
723
724 /* ======================================================== */
725 /* Insert a new node before the specified node in the list. */
726 /* ======================================================== */
727 static void
ll_insert_before(ll_t * const list,ll_node_t * node,void * const data)728 ll_insert_before(ll_t * const list, ll_node_t * node, void * const data)
729 {
730 ll_node_t * new_node;
731
732 if (node->prev == NULL)
733 ll_prepend(list, data);
734 else
735 {
736 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
737 | uses xmalloc which does not return if there *
738 | is an allocation error. */
739
740 new_node->data = data;
741 new_node->next = node;
742 new_node->prev = node->prev;
743 node->prev->next = new_node;
744 node->prev = new_node;
745
746 ++list->len;
747 }
748 }
749
750 /* ======================================================= */
751 /* Insert a new node after the specified node in the list. */
752 /* ======================================================= */
753 static void
ll_insert_after(ll_t * const list,ll_node_t * node,void * const data)754 ll_insert_after(ll_t * const list, ll_node_t * node, void * const data)
755 {
756 ll_node_t * new_node;
757
758 if (node->next == NULL)
759 ll_append(list, data);
760 else
761 {
762 new_node = ll_new_node(); /* ll_new_node cannot return NULL because it *
763 | uses xmalloc which does not return if there *
764 | is an allocation error. */
765
766 new_node->data = data;
767 new_node->prev = node;
768 new_node->next = node->next;
769 node->next->prev = new_node;
770 node->next = new_node;
771
772 ++list->len;
773 }
774 }
775
776 /* ================================================================= */
777 /* Remove a node from a linked list. */
778 /* The memory taken by the deleted node must be freed by the caller. */
779 /* ================================================================= */
780 static int
ll_delete(ll_t * const list,ll_node_t * node)781 ll_delete(ll_t * const list, ll_node_t * node)
782 {
783 if (list->head == list->tail)
784 {
785 if (list->head != NULL)
786 list->head = list->tail = NULL;
787 else
788 return 0;
789 }
790 else if (node->prev == NULL)
791 {
792 list->head = node->next;
793 list->head->prev = NULL;
794 }
795 else if (node->next == NULL)
796 {
797 list->tail = node->prev;
798 list->tail->next = NULL;
799 }
800 else
801 {
802 node->next->prev = node->prev;
803 node->prev->next = node->next;
804 }
805
806 --list->len;
807
808 free(node);
809
810 return 1;
811 }
812
813 #if 0 /* Unused yet */
814 /* ======================================================================== */
815 /* Find a node in the list containing data. Return the node pointer or NULL */
816 /* if not found. */
817 /* A comparison function must be provided to compare a and b (strcmp like). */
818 /* ======================================================================== */
819 static ll_node_t *
820 ll_find(ll_t * const list, void * const data,
821 int (*cmpfunc)(const void * a, const void * b))
822 {
823 ll_node_t * node;
824
825 if (NULL == (node = list->head))
826 return NULL;
827
828 do
829 {
830 if (0 == cmpfunc(node->data, data))
831 return node;
832 } while (NULL != (node = node->next));
833
834 return NULL;
835 }
836 #endif
837
838 /* ==================================================================== */
839 /* Allocate and fill an array of strings from a list. */
840 /* WARNINGS: */
841 /* 1) The list node must contain strings (char *). */
842 /* 2) The strings in the resulting array MUST NOT be freed as the are */
843 /* NOT copied from the strings of the list. */
844 /* */
845 /* IN list : The list from which the array is generated. */
846 /* IN start_node : The node of the list which will be the first node to */
847 /* consider to create the array. */
848 /* OUT: count : The number of elements of the resulting array. */
849 /* OUT: array : The resulting array or NULL if the list is empty. */
850 /* RC : : The number of elements of the resulting array. */
851 /* ==================================================================== */
852 static int
ll_strarray(ll_t * list,ll_node_t * start_node,int * count,char *** array)853 ll_strarray(ll_t * list, ll_node_t * start_node, int * count, char *** array)
854 {
855 int n = 0;
856 ll_node_t * node;
857
858 *count = 0;
859
860 node = start_node;
861
862 if (list == NULL || node == NULL)
863 {
864 *array = NULL;
865
866 return 0;
867 }
868
869 *array = xmalloc((list->len + 1) * sizeof(char *));
870 while (node != NULL)
871 {
872 (*array)[n++] = (char *)(node->data);
873 (*count)++;
874
875 node = node->next;
876 }
877
878 (*array)[*count] = NULL;
879
880 return *count;
881 }
882
883 /* ******************************************************************* */
884 /* BST (search.h compatible) implementation. */
885 /* */
886 /* Tree search generalized from Knuth (6.2.2) Algorithm T just like */
887 /* the AT&T man page says. */
888 /* */
889 /* Written by reading the System V Interface Definition, not the code. */
890 /* */
891 /* Totally public domain. */
892 /* ******************************************************************* */
893
894 struct bst_s
895 {
896 void * key;
897 struct bst_s * llink;
898 struct bst_s * rlink;
899 };
900
901 #if 0 /* Unused yet. */
902 /* =========================== */
903 /* Delete node with given key. */
904 /* =========================== */
905 static void *
906 bst_delete(const void * vkey, void ** vrootp,
907 int (*compar)(const void *, const void *))
908 {
909 bst_t ** rootp = (bst_t **)vrootp;
910 bst_t * p, *q, *r;
911 int cmp;
912
913 if (rootp == NULL || (p = *rootp) == NULL)
914 return NULL;
915
916 while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0)
917 {
918 p = *rootp;
919 rootp = (cmp < 0) ? &(*rootp)->llink /* follow llink branch */
920 : &(*rootp)->rlink; /* follow rlink branch */
921 if (*rootp == NULL)
922 return NULL; /* key not found */
923 }
924 r = (*rootp)->rlink; /* D1: */
925 if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
926 q = r;
927 else if (r != NULL)
928 { /* Right link is NULL? */
929 if (r->llink == NULL)
930 { /* D2: Find successor */
931 r->llink = q;
932 q = r;
933 }
934 else
935 { /* D3: Find NULL link */
936 for (q = r->llink; q->llink != NULL; q = r->llink)
937 r = q;
938 r->llink = q->rlink;
939 q->llink = (*rootp)->llink;
940 q->rlink = (*rootp)->rlink;
941 }
942 }
943 if (p != *rootp)
944 free(*rootp); /* D4: Free node */
945 *rootp = q; /* link parent to new node */
946 return p;
947 }
948 #endif
949
950 /* ===================================================================== */
951 /* Destroy a tree. */
952 /* The clean function pointer can be NULL, in this case the node content */
953 /* is not freed. */
954 /* ===================================================================== */
955 static void
bst_destroy(void * vrootp,void (* clean)(void *))956 bst_destroy(void * vrootp, void (*clean)(void *))
957 {
958 bst_t * root = (bst_t *)vrootp;
959
960 if (root == NULL)
961 return;
962
963 bst_destroy(root->llink, clean);
964 bst_destroy(root->rlink, clean);
965
966 if (clean)
967 clean((void *)root->key);
968
969 free(root);
970 }
971
972 /* ========================= */
973 /* Find a node, or return 0. */
974 /* ========================= */
975 static void *
bst_find(const void * vkey,void * const * vrootp,int (* compar)(const void *,const void *))976 bst_find(const void * vkey, void * const * vrootp,
977 int (*compar)(const void *, const void *))
978 {
979 bst_t * const * rootp = (bst_t * const *)vrootp;
980
981 if (rootp == NULL)
982 return NULL;
983
984 while (*rootp != NULL)
985 { /* T1: */
986 int r;
987
988 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
989 return *rootp; /* key found */
990 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
991 : &(*rootp)->rlink; /* T4: follow right branch */
992 }
993 return NULL;
994 }
995
996 /* ======================================= */
997 /* Find or inserts datum into search tree. */
998 /* ======================================= */
999 static void *
bst_search(void * vkey,void ** vrootp,int (* compar)(const void *,const void *))1000 bst_search(void * vkey, void ** vrootp,
1001 int (*compar)(const void *, const void *))
1002 {
1003 bst_t * q;
1004 bst_t ** rootp = (bst_t **)vrootp;
1005
1006 if (rootp == NULL)
1007 return NULL;
1008
1009 while (*rootp != NULL)
1010 { /* Knuth's T1: */
1011 int r;
1012
1013 if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
1014 return *rootp; /* we found it! */
1015
1016 rootp = (r < 0) ? &(*rootp)->llink /* T3: follow left branch */
1017 : &(*rootp)->rlink; /* T4: follow right branch */
1018 }
1019
1020 q = xmalloc(sizeof(bst_t)); /* T5: key not found */
1021 if (q != 0)
1022 { /* make new node */
1023 *rootp = q; /* link new node to old */
1024 q->key = vkey; /* initialize new node */
1025 q->llink = q->rlink = NULL;
1026 }
1027 return q;
1028 }
1029
1030 /* ========================= */
1031 /* Walk the nodes of a tree. */
1032 /* ========================= */
1033 static void
bst_walk_recurse(const bst_t * root,void (* action)(const void *,walk_order_e,int),int level)1034 bst_walk_recurse(const bst_t * root,
1035 void (*action)(const void *, walk_order_e, int), int level)
1036 {
1037 if (root->llink == NULL && root->rlink == NULL)
1038 (*action)(root, leaf, level);
1039 else
1040 {
1041 (*action)(root, preorder, level);
1042 if (root->llink != NULL)
1043 bst_walk_recurse(root->llink, action, level + 1);
1044 (*action)(root, postorder, level);
1045 if (root->rlink != NULL)
1046 bst_walk_recurse(root->rlink, action, level + 1);
1047 (*action)(root, endorder, level);
1048 }
1049 }
1050
1051 static void
bst_walk(const void * vroot,void (* action)(const void *,walk_order_e,int))1052 bst_walk(const void * vroot, void (*action)(const void *, walk_order_e, int))
1053 {
1054 if (vroot != NULL && action != NULL)
1055 bst_walk_recurse(vroot, action, 0);
1056 }
1057
1058 /* ************************ */
1059 /* Various implementations. */
1060 /* ************************ */
1061
1062 /* ======================== */
1063 /* Trim leading characters. */
1064 /* ======================== */
1065 static void
ltrim(char * str,const char * trim_str)1066 ltrim(char * str, const char * trim_str)
1067 {
1068 size_t len = strlen(str);
1069 size_t begin = strspn(str, trim_str);
1070 size_t i;
1071
1072 if (begin > 0)
1073 for (i = begin; i <= len; ++i)
1074 str[i - begin] = str[i];
1075 }
1076
1077 /* ================================================= */
1078 /* Trim trailing characters. */
1079 /* The resulting string will have at least min bytes */
1080 /* even if trailing spaces remain. */
1081 /* ================================================= */
1082 static void
rtrim(char * str,const char * trim_str,size_t min)1083 rtrim(char * str, const char * trim_str, size_t min)
1084 {
1085 size_t len = strlen(str);
1086 while (len > min && strchr(trim_str, str[len - 1]))
1087 str[--len] = '\0';
1088 }
1089
1090 /* ================================================== */
1091 /* Count the number of occurrences of the character c */
1092 /* in the string str. */
1093 /* The str pointer is assumed to be not NULL. */
1094 /* ================================================== */
1095 static int
strchrcount(char * str,char c)1096 strchrcount(char * str, char c)
1097 {
1098 int count = 0;
1099
1100 while (*str)
1101 if (*str++ == c)
1102 count++;
1103
1104 return count;
1105 }
1106
1107 /* =============================================== */
1108 /* Is the string str2 a prefix of the string str1? */
1109 /* =============================================== */
1110 static int
strpref(char * str1,char * str2)1111 strpref(char * str1, char * str2)
1112 {
1113 while (*str1 != '\0' && *str1 == *str2)
1114 {
1115 str1++;
1116 str2++;
1117 }
1118
1119 return *str2 == '\0';
1120 }
1121
1122 /* ========================== */
1123 /* Like strcmp ignoring case. */
1124 /* ========================== */
1125 static int
stricmp(const char * s1,const char * s2)1126 stricmp(const char * s1, const char * s2)
1127 {
1128 while (tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1129 {
1130 if (*s1 == '\0')
1131 return 0;
1132
1133 s1++;
1134 s2++;
1135 }
1136
1137 return (int)tolower((unsigned char)*s1) - (int)tolower((unsigned char)*s2);
1138 }
1139
1140 /* ====================================================================== */
1141 /* Strings concatenation with dynamic memory allocation. */
1142 /* IN : a variable number of char * arguments with NULL terminating */
1143 /* the sequence. */
1144 /* The first one must have been dynamically allocated and is */
1145 /* mandatory. */
1146 /* */
1147 /* Returns a new allocated string containing the concatenation of all */
1148 /* the arguments. It is the caller's responsibility to free the resulting */
1149 /* string. */
1150 /* ====================================================================== */
1151 static char *
strappend(char * str,...)1152 strappend(char * str, ...)
1153 {
1154 size_t l;
1155 va_list args;
1156 char * s;
1157
1158 l = 1 + strlen(str);
1159 va_start(args, str);
1160
1161 s = va_arg(args, char *);
1162
1163 while (s)
1164 {
1165 l += strlen(s);
1166 s = va_arg(args, char *);
1167 }
1168
1169 va_end(args);
1170
1171 str = xrealloc(str, l);
1172
1173 va_start(args, str);
1174 s = va_arg(args, char *);
1175
1176 while (s)
1177 {
1178 strcat(str, s);
1179 s = va_arg(args, char *);
1180 }
1181 va_end(args);
1182
1183 return str;
1184 }
1185
1186 /* ====================================================================== */
1187 /* Public domain strtok_r() by Charlie Gordon. */
1188 /* from comp.lang.c 9/14/2007 */
1189 /* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684 */
1190 /* */
1191 /* (Declaration that it's public domain): */
1192 /* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c */
1193 /* */
1194 /* Also, fixed by Fletcher T. Penney --- added the "return NULL" when */
1195 /* *end == NULL. */
1196 /* ====================================================================== */
1197 static char *
xstrtok_r(char * str,const char * delim,char ** end)1198 xstrtok_r(char * str, const char * delim, char ** end)
1199 {
1200 char * ret;
1201
1202 if (str == NULL)
1203 str = *end;
1204
1205 if (str == NULL)
1206 return NULL;
1207
1208 str += strspn(str, delim);
1209
1210 if (*str == '\0')
1211 return NULL;
1212
1213 ret = str;
1214
1215 str += strcspn(str, delim);
1216
1217 if (*str)
1218 *str++ = '\0';
1219
1220 *end = str;
1221
1222 return ret;
1223 }
1224
1225 /* ===================================================================== */
1226 /* Put the first word of str, truncated to len characters, in buf. */
1227 /* Return a pointer in str pointing just after the word. */
1228 /* buf must have been pre-allocated to accept at least len+1 characters. */
1229 /* Note that buf can contains a sting full of spaces is str was not */
1230 /* trimmed before the call. */
1231 /* ===================================================================== */
1232 char *
get_word(char * str,char * buf,size_t len)1233 get_word(char * str, char * buf, size_t len)
1234 {
1235 char * s = str;
1236
1237 /* Skip spaces. */
1238 /* """""""""""" */
1239 while (*s && isspace(*s))
1240 s++;
1241
1242 /* Set the new string start. */
1243 /* """"""""""""""""""""""""" */
1244 str = s;
1245
1246 /* Get the word. */
1247 /*"""""""""""""" */
1248 while (*s && !isspace(*s) && s - str < len)
1249 s++;
1250
1251 strncpy(buf, str, s - str);
1252 buf[s - str] = 0;
1253
1254 return s;
1255 }
1256
1257 /* ==================================================================== */
1258 /* Return 1 is value is "1" or "yes" (ignoring case). */
1259 /* Return 0 is value is "0" or "no" (ignoring case). */
1260 /* If value has another value, then set invalid to 1 and also return 0 */
1261 /* invalid is set to 0 in all the other cases. */
1262 /* ==================================================================== */
1263 static int
eval_yes(char * value,int * invalid)1264 eval_yes(char * value, int * invalid)
1265 {
1266 *invalid = 0;
1267
1268 if (strcmp(value, "1") == 0 || stricmp(value, "yes") == 0)
1269 return 1;
1270 else if (strcmp(value, "0") != 0 && stricmp(value, "no") != 0)
1271 *invalid = 1;
1272
1273 return 0;
1274 }
1275
1276 /* =========================================================== */
1277 /* Fill an array of strings from the words composing a string. */
1278 /* */
1279 /* str: initial string which will be altered. */
1280 /* args: array of pointers to the start of the words in str. */
1281 /* max: maximum number of words used before giving up. */
1282 /* return: the number of words (<=max). */
1283 /* =========================================================== */
1284 static int
str2argv(char * str,char ** args,int max)1285 str2argv(char * str, char ** args, int max)
1286 {
1287 int nb_args = 0;
1288
1289 while (*str)
1290 {
1291 if (nb_args >= max)
1292 return nb_args;
1293
1294 while (*str == ' ' || *str == '\t')
1295 *(str++) = '\0';
1296
1297 if (!*str)
1298 return nb_args;
1299
1300 args[nb_args] = str;
1301 nb_args++;
1302
1303 while (*str && (*str != ' ') && (*str != '\t'))
1304 str++;
1305 }
1306
1307 return nb_args;
1308 }
1309
1310 /* ********************** */
1311 /* ctxopt implementation. */
1312 /* ********************** */
1313
1314 static int ctxopt_initialized = 0; /* cap_init has not yet been called. */
1315
1316 /* Flags structure initialized by ctxopt_init. */
1317 /* """"""""""""""""""""""""""""""""""""""""""" */
1318 struct flags_s
1319 {
1320 int stop_if_non_option;
1321 int allow_abbreviations;
1322 int display_usage_on_error;
1323 };
1324
1325 static flags_t flags = { 0, 1, 1 };
1326
1327 /* Context structure. */
1328 /* """""""""""""""""" */
1329 struct ctx_s
1330 {
1331 char * name;
1332 ll_t * opt_list; /* list of options allowed in this context. */
1333 ll_t * incomp_list; /* list of strings containing incompatible names *
1334 | of options separated by spaces or tabs. */
1335 ll_t * req_list; /* list of strings containing an option name and *
1336 | all the option names where at least one of *
1337 | them is required to be also present. */
1338
1339 int (*action)(char * name, int type, char * new_ctx, int ctx_nb_data,
1340 void ** ctx_data);
1341 void * par_bst;
1342 int nb_data;
1343 void ** data;
1344 };
1345
1346 /* https://textik.com/#488ce3649b6c60f5 */
1347 /* */
1348 /* +--------------+ */
1349 /* |first_ctx_inst| */
1350 /* +---+----------+ */
1351 /* | */
1352 /* +--v-----+ +--------+ +--------+ +-----+ */
1353 /* +---+-->ctx_inst+------>opt_inst+----->opt_inst+------> ... | */
1354 /* | | +-+------+ +----+---+ +----+---+ +-----+ */
1355 /* | | | | | */
1356 /* | | +-v------+ | | */
1357 /* | +--+ctx_inst<-----------+ | */
1358 /* | +-+------+ | */
1359 /* | | | */
1360 /* | +-v------+ | */
1361 /* +------+ctx_inst<--------------------------+ */
1362 /* +-+------+ */
1363 /* | */
1364 /* +-v---+ */
1365 /* | ... | */
1366 /* +-----+ */
1367
1368 /* Option structure. */
1369 /* """"""""""""""""" */
1370 struct opt_s
1371 {
1372 char * name; /* option name. */
1373 char * next_ctx; /* new context this option may lead to */
1374 ll_t * ctx_list; /* list of contexts allowing this option. */
1375 char * params; /* string containing all the parameters of *
1376 | the option. */
1377
1378 void (*action)( /* The option associated action. */
1379 char * ctx_name, /* context name. */
1380 char * opt_name, /* option name. */
1381 char * par, /* option parameter. */
1382 int nb_args, /* number of arguments. */
1383 char ** args, /* option arguments. */
1384 int nb_opt_data, /* number of option data pointers. */
1385 void ** opt_data, /* option data pointers. */
1386 int nb_ctx_data, /* nb of current context data ptrs. */
1387 void ** ctx_data /* current context data pointers. */
1388 );
1389
1390 int nb_data; /* number of the data pointers passed as argument to action. */
1391 void ** data; /* array of data pointers passed as argument to action. */
1392
1393 int args; /* 1 if this option takes arguments else 0. */
1394 int optional; /* 1 if the option is optional, else 0. */
1395 int multiple; /* 1 if the option can appear more than one time in a *
1396 | context, else 0. */
1397
1398 int opt_count_matter; /* 1 if we must restrict the count, else 0. */
1399 int occurrences; /* Number of option occurrences in a context. */
1400 char opt_count_oper; /* <, = or > */
1401 int opt_count_mark; /* Value to be compared to with opt_count_oper. */
1402
1403 char * arg; /* symbolic text after # describing the option argument. */
1404
1405 int optional_args; /* 1 of option is optional else 0. */
1406 int multiple_args; /* 1 is option can appear more than once in a context *
1407 | instance. */
1408
1409 int opt_args_count_matter; /* 1 if count is rescticted, else 0. */
1410 char opt_args_count_oper; /* <, = or > */
1411 int opt_args_count_mark; /* Value to be compared to with *
1412 | opt_count_oper. */
1413
1414 int eval_first; /* 1 if this option must be evaluated before the options *
1415 | without this mark. */
1416
1417 ll_t * eval_before_list; /* List of pointers on options which must be *
1418 | evaluated before this option. */
1419
1420 ll_t * constraints_list; /* List of constraint check functions pointers. */
1421 };
1422
1423 /* Context instance structure. */
1424 /* """"""""""""""""""""""""""" */
1425 struct ctx_inst_s
1426 {
1427 ctx_t * ctx; /* the context whose this is an instance of */
1428 ctx_inst_t * prev_ctx_inst; /* ctx_inst of the opt_inst which led to the *
1429 | creation of this ctx_inst structure. */
1430 opt_inst_t * gen_opt_inst; /* opt_inst which led to the creation of a *
1431 | instance of this structure. */
1432 ll_t * incomp_bst_list; /* list of seen_opt_t BST. */
1433 void * seen_opt_bst; /* tree of seen_opt_t. */
1434 ll_t * opt_req_list; /* list of req_t. */
1435 ll_t * opt_inst_list; /* The list of option instances in this *
1436 | context instance. */
1437 char * par_name; /* parameter which created this instance. */
1438 };
1439
1440 /* Option instance structure. */
1441 /* """""""""""""""""""""""""" */
1442 struct opt_inst_s
1443 {
1444 opt_t * opt; /* The option this is an instance of. */
1445 char * opt_name; /* The option which led to this creation. */
1446 char * par; /* The parameter which led to this creation. */
1447 ll_t * values_list; /* The list of arguments of this option. */
1448 ctx_inst_t * next_ctx_inst; /* The new context instance this option. *
1449 | instance may create. */
1450 };
1451
1452 /* Structure used to check if an option has bee seen or not */
1453 /* in a context instance. */
1454 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1455 struct seen_opt_s
1456 {
1457 opt_t * opt; /* The concerned option. */
1458 char * par; /* Parameter which led to the making of this structure. */
1459 int seen; /* 1 if seen in the context instances, else 0. */
1460 };
1461
1462 /* Structure used to check if at least one instance of the options whose */
1463 /* pointers are in or_opt_list has been seen in the ctx_inst where an */
1464 /* instance or opt is also present. */
1465 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1466 struct req_s
1467 {
1468 opt_t * opt; /* Option that asks for other options. */
1469 ll_t * or_opt_list; /* Required options, at least one of them *
1470 | must be present. */
1471 };
1472
1473 /* Parameter structure which links a parameter to the option it belongs to. */
1474 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
1475 struct par_s
1476 {
1477 char * name; /* Parameter name (with the leading -). */
1478 opt_t * opt; /* Attached option. */
1479 };
1480
1481 /* Constraint structure. */
1482 /* """"""""""""""""""""" */
1483 struct constraint_s
1484 {
1485 int (*constraint)(int nb_args, char ** args, char * value, char * parameter);
1486 int nb_args;
1487 char ** args;
1488 char * to_free; /* pointer to the original string in which the array in *
1489 | args points to. This poinnter is kept there to allow *
1490 | it to be freed. */
1491 };
1492
1493 state_t * cur_state = NULL; /* Current analysis state. */
1494 static ll_t * cmdline_list = NULL; /* List of interpreted CLI words *
1495 | serves as the basis for the *
1496 | analysis of the parameters. */
1497 static ctx_t * main_ctx = NULL; /* initial context. */
1498 static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
1499 | instance which holds the *
1500 | options instances. */
1501 static ll_t * ctx_inst_list = NULL; /* List of the context instances. */
1502
1503 /* ======================================================= */
1504 /* Parse a string for the next matching token. */
1505 /* */
1506 /* s: string to parse. */
1507 /* token: pre_allocated array of max tok_len characters. */
1508 /* pattern: scanf type pattern token must match. */
1509 /* pos: number of characters successfully parsed in s. */
1510 /* */
1511 /* Returns: a pointer to the first unread character or */
1512 /* to he terminating \0. */
1513 /* ======================================================= */
1514 static char *
strtoken(char * s,char * token,size_t tok_len,char * pattern,int * pos)1515 strtoken(char * s, char * token, size_t tok_len, char * pattern, int * pos)
1516 {
1517 char * full_pattern;
1518 char len[3];
1519 int n;
1520
1521 *pos = 0;
1522
1523 n = snprintf(len, 3, "%zu", tok_len);
1524 if (n < 0)
1525 return NULL;
1526
1527 full_pattern = xmalloc(strlen(pattern) + n + 4);
1528
1529 strcpy(full_pattern, "%");
1530 strcat(full_pattern, len);
1531 strcat(full_pattern, pattern);
1532 strcat(full_pattern, "%n");
1533
1534 n = sscanf(s, full_pattern, token, pos);
1535
1536 free(full_pattern);
1537
1538 if (n != 1)
1539 return NULL;
1540
1541 return s + *pos;
1542 }
1543
1544 /* ****************************************** */
1545 /* Various comparison and deletion functions. */
1546 /* ****************************************** */
1547
1548 static int
ctx_compare(const void * c1,const void * c2)1549 ctx_compare(const void * c1, const void * c2)
1550 {
1551 return strcmp(((ctx_t *)c1)->name, ((ctx_t *)c2)->name);
1552 }
1553
1554 /* =========================== */
1555 /* Free a context_bst element. */
1556 /* =========================== */
1557 static void
ctx_free(void * c)1558 ctx_free(void * c)
1559 {
1560 ctx_t * ctx = c;
1561
1562 free(ctx->name);
1563 free(ctx->data);
1564
1565 ll_destroy(ctx->opt_list, NULL);
1566 ll_destroy(ctx->incomp_list, free);
1567 ll_destroy(ctx->req_list, free);
1568 bst_destroy(ctx->par_bst, par_free);
1569
1570 free(c);
1571 }
1572
1573 /* ============================= */
1574 /* Free a ctx_inst_list element. */
1575 /* ============================= */
1576 static void
ctx_inst_free(void * ci)1577 ctx_inst_free(void * ci)
1578 {
1579 ctx_inst_t * ctx_inst = ci;
1580
1581 free(ctx_inst->par_name);
1582 ll_destroy(ctx_inst->incomp_bst_list, incomp_bst_free);
1583 bst_destroy(ctx_inst->seen_opt_bst, seen_opt_free);
1584 ll_destroy(ctx_inst->opt_inst_list, opt_inst_free);
1585 ll_destroy(ctx_inst->opt_req_list, req_free);
1586
1587 free(ci);
1588 }
1589
1590 /* ============================== */
1591 /* Free an opt_inst_list element. */
1592 /* ============================== */
1593 static void
opt_inst_free(void * oi)1594 opt_inst_free(void * oi)
1595 {
1596 opt_inst_t * opt_inst = oi;
1597
1598 ll_destroy(opt_inst->values_list, NULL);
1599
1600 free(oi);
1601 }
1602
1603 /* ================================== */
1604 /* Compare two seen_opt_bst elements. */
1605 /* ================================== */
1606 static int
seen_opt_compare(const void * so1,const void * so2)1607 seen_opt_compare(const void * so1, const void * so2)
1608 {
1609 opt_t *o1, *o2;
1610
1611 o1 = ((seen_opt_t *)so1)->opt;
1612 o2 = ((seen_opt_t *)so2)->opt;
1613
1614 return strcmp(o1->name, o2->name);
1615 }
1616
1617 /* ============================ */
1618 /* Free a seen_opt_bst element. */
1619 /* ============================ */
1620 void
seen_opt_free(void * so)1621 seen_opt_free(void * so)
1622 {
1623 seen_opt_t * seen_opt = so;
1624
1625 free(seen_opt->par);
1626
1627 free(so);
1628 }
1629
1630 /* =========================== */
1631 /* Free an incomp_bst element. */
1632 /* =========================== */
1633 static void
incomp_bst_free(void * b)1634 incomp_bst_free(void * b)
1635 {
1636 bst_t * bst = b;
1637
1638 bst_destroy(bst, NULL);
1639 }
1640
1641 /* ============================= */
1642 /* Free an opt_req_list element. */
1643 /* ============================= */
1644 static void
req_free(void * r)1645 req_free(void * r)
1646 {
1647 req_t * req = r;
1648
1649 ll_destroy(req->or_opt_list, NULL);
1650 free(req);
1651 }
1652
1653 /* ================================= */
1654 /* Compare two options_bst elements. */
1655 /* ================================= */
1656 static int
opt_compare(const void * o1,const void * o2)1657 opt_compare(const void * o1, const void * o2)
1658 {
1659 return strcmp(((opt_t *)o1)->name, ((opt_t *)o2)->name);
1660 }
1661
1662 /* ============================= */
1663 /* Free an options_bst elements. */
1664 /* ============================= */
1665 void
opt_free(void * o)1666 opt_free(void * o)
1667 {
1668 opt_t * opt = o;
1669
1670 free(opt->name);
1671 free(opt->next_ctx);
1672 free(opt->params);
1673 free(opt->arg);
1674 free(opt->data);
1675
1676 ll_destroy(opt->ctx_list, NULL);
1677 ll_destroy(opt->constraints_list, constraint_free);
1678 ll_destroy(opt->eval_before_list, NULL);
1679
1680 free(o);
1681 }
1682
1683 /* ============================= */
1684 /* Compare two par_bst elements. */
1685 /* ============================= */
1686 static int
par_compare(const void * a1,const void * a2)1687 par_compare(const void * a1, const void * a2)
1688 {
1689 return strcmp(((par_t *)a1)->name, ((par_t *)a2)->name);
1690 }
1691
1692 /* ======================= */
1693 /* Free a par_bst element. */
1694 /* ======================= */
1695 static void
par_free(void * p)1696 par_free(void * p)
1697 {
1698 par_t * par = p;
1699
1700 free(par->name);
1701
1702 free(p);
1703 }
1704
1705 /* ================================ */
1706 /* Free a constraints_list element. */
1707 /* ================================ */
1708 static void
constraint_free(void * c)1709 constraint_free(void * c)
1710 {
1711 constraint_t * cstr = c;
1712
1713 free(cstr->args);
1714 free(cstr->to_free);
1715
1716 free(c);
1717 }
1718
1719 /* ******************************************************************** */
1720 /* Helper functions to locate contexts, options and parameters in a BST */
1721 /* by their names. */
1722 /* ******************************************************************** */
1723
1724 static ctx_t *
locate_ctx(char * name)1725 locate_ctx(char * name)
1726 {
1727 bst_t * node;
1728 ctx_t ctx = { 0 };
1729
1730 ctx.name = name;
1731
1732 if ((node = bst_find(&ctx, &contexts_bst, ctx_compare)) == NULL)
1733 return NULL;
1734 else
1735 return node->key;
1736 }
1737
1738 static opt_t *
locate_opt(char * name)1739 locate_opt(char * name)
1740 {
1741 bst_t * node;
1742 opt_t opt = { 0 };
1743
1744 opt.name = name;
1745
1746 if ((node = bst_find(&opt, &options_bst, opt_compare)) == NULL)
1747 return NULL;
1748 else
1749 return node->key;
1750 }
1751
1752 static par_t *
locate_par(char * name,ctx_t * ctx)1753 locate_par(char * name, ctx_t * ctx)
1754 {
1755 bst_t * node;
1756 par_t par = { 0 };
1757 void * bst = ctx->par_bst;
1758
1759 par.name = name;
1760
1761 if ((node = bst_find(&par, &bst, par_compare)) == NULL)
1762 return NULL;
1763 else
1764 return node->key;
1765 }
1766
1767 /* ====================================================================== */
1768 /* Helper function to display the dependency constraints between options. */
1769 /* These constraints are set with the ctxopt_add_opt_settings function */
1770 /* using the 'before' and 'after' arguments. */
1771 /* IN list : a list of options. */
1772 /* ====================================================================== */
1773 static void
print_before_constraints(ll_t * list)1774 print_before_constraints(ll_t * list)
1775 {
1776 ll_node_t * node = list->head;
1777 ll_node_t * before_node;
1778 opt_t * opt, *before_opt;
1779 int msg = 0;
1780
1781 while (node != NULL)
1782 {
1783 opt = node->data;
1784
1785 if (opt->eval_before_list->len > 0)
1786 {
1787 if (!msg)
1788 {
1789 printf("\n If present in the command line,");
1790 msg = 1; /* Display this message only once. */
1791 }
1792
1793 before_node = opt->eval_before_list->head;
1794
1795 printf("\n ");
1796 while (before_node != NULL)
1797 {
1798 before_opt = before_node->data;
1799 printf("%s", before_opt->params);
1800
1801 before_node = before_node->next;
1802
1803 if (before_node != NULL)
1804 printf(" and\n ");
1805 }
1806 printf(" will be evaluated after %s\n", opt->params);
1807 }
1808 node = node->next;
1809 }
1810 }
1811
1812 /* =================================================================== */
1813 /* Utility function to format and print the options present in a list. */
1814 /* */
1815 /* IN list : a list of options. */
1816 /* OUT has_* : a set of flags which will determine the content of the */
1817 /* explanation given after the formatted printing of the */
1818 /* options. */
1819 /* =================================================================== */
1820 static void
print_options(ll_t * list,int * has_optional,int * has_ellipsis,int * has_rule,int * has_generic_arg,int * has_ctx_change,int * has_early_eval)1821 print_options(ll_t * list, int * has_optional, int * has_ellipsis,
1822 int * has_rule, int * has_generic_arg, int * has_ctx_change,
1823 int * has_early_eval)
1824 {
1825 ll_node_t * node = list->head;
1826 opt_t * opt;
1827 char * line;
1828 char * option;
1829
1830 line = xstrdup(" ");
1831
1832 while (node != NULL)
1833 {
1834 option = xstrdup("");
1835 opt = node->data;
1836
1837 if (opt->optional)
1838 {
1839 option = strappend(option, "[", (char *)0);
1840 *has_optional = 1;
1841 }
1842
1843 if (opt->eval_first)
1844 {
1845 option = strappend(option, "*", (char *)0);
1846 *has_early_eval = 1;
1847 }
1848
1849 option = strappend(option, opt->params, (char *)0);
1850
1851 if (opt->next_ctx != NULL)
1852 {
1853 option = strappend(option, ">", opt->next_ctx, (char *)0);
1854 *has_ctx_change = 1;
1855 }
1856
1857 if (opt->multiple)
1858 {
1859 if (opt->opt_count_oper != '\0')
1860 {
1861 char m[4];
1862 char o[2];
1863 o[0] = opt->opt_count_oper;
1864 o[1] = '\0';
1865 snprintf(m, 3, "%u", opt->opt_count_mark);
1866 option = strappend(option, "...", o, m, (char *)0);
1867 *has_rule = 1;
1868 }
1869 else
1870 option = strappend(option, "...", (char *)0);
1871
1872 *has_ellipsis = 1;
1873 }
1874
1875 if (opt->args)
1876 {
1877 if (*(opt->arg) == '#')
1878 *has_generic_arg = 1;
1879
1880 option = strappend(option, " ", (char *)0);
1881
1882 if (opt->optional_args)
1883 {
1884 option = strappend(option, "[", opt->arg, (char *)0);
1885 *has_optional = 1;
1886 }
1887 else
1888 option = strappend(option, opt->arg, (char *)0);
1889
1890 if (opt->multiple_args)
1891 {
1892 if (opt->opt_args_count_oper != '\0')
1893 {
1894 char m[4];
1895 char o[2];
1896 o[0] = opt->opt_args_count_oper;
1897 o[1] = '\0';
1898 snprintf(m, 3, "%u", opt->opt_args_count_mark);
1899 option = strappend(option, "...", o, m, (char *)0);
1900 *has_rule = 1;
1901 }
1902 else
1903 option = strappend(option, "...", (char *)0);
1904
1905 *has_ellipsis = 1;
1906 }
1907 if (opt->optional_args)
1908 option = strappend(option, "]", (char *)0);
1909 }
1910 if (opt->optional)
1911 option = strappend(option, "]", (char *)0);
1912
1913 if (strlen(line) + 1 + strlen(option) < 80)
1914 line = strappend(line, option, " ", (char *)0);
1915 else
1916 {
1917 printf("%s\n", line);
1918 line[2] = '\0';
1919 line = strappend(line, option, " ", (char *)0);
1920 }
1921
1922 free(option);
1923
1924 node = node->next;
1925 }
1926
1927 printf("%s\n", line);
1928
1929 free(line);
1930 }
1931
1932 /* ==================================================== */
1933 /* Explain the special syntactic symbols present in the */
1934 /* generated usage messages. */
1935 /* ==================================================== */
1936 static void
print_explanations(int has_early_eval,int has_ctx_change,int has_generic_arg,int has_optional,int has_ellipsis,int has_rule)1937 print_explanations(int has_early_eval, int has_ctx_change, int has_generic_arg,
1938 int has_optional, int has_ellipsis, int has_rule)
1939 {
1940 if (has_early_eval || has_ctx_change || has_generic_arg || has_optional
1941 || has_ellipsis || has_rule)
1942 {
1943 printf("\nExplanation of the syntax used above:\n");
1944 printf("Only the parameters (prefixed by -) and the arguments, if any, "
1945 "must be entered.\n");
1946 printf("The following is just there to explain the other symbols "
1947 "displayed.\n\n");
1948
1949 if (has_early_eval)
1950 printf("* : the parameters defined for this option will "
1951 "be evaluated first.\n");
1952 if (has_ctx_change)
1953 printf("> : the context after this symbol will be the new "
1954 "default context.\n");
1955 if (has_generic_arg)
1956 printf("#tag : argument with a hint about its meaning.\n");
1957 if (has_optional)
1958 printf("[...] : the object between square brackets is "
1959 "optional.\n");
1960 if (has_ellipsis)
1961 printf("... : several occurrences of the previous object "
1962 "are possible.\n");
1963 if (has_rule)
1964 printf("[<|=|>]number: rules constraining the number of "
1965 "parameters/arguments.\n");
1966 }
1967 }
1968
1969 /* ************************************************************ */
1970 /* Various utilities and callback functions called when walking */
1971 /* through a BST. */
1972 /* ************************************************************ */
1973
1974 static void
bst_seen_opt_cb(const void * node,walk_order_e kind,int level)1975 bst_seen_opt_cb(const void * node, walk_order_e kind, int level)
1976 {
1977 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1978
1979 if (kind == postorder || kind == leaf)
1980 {
1981 if ((!seen_opt->opt->optional) && seen_opt->seen == 0)
1982 {
1983 user_rc = 1;
1984 user_string = strappend(user_string, seen_opt->opt->params, " ",
1985 (char *)0);
1986 }
1987 }
1988 }
1989
1990 static void
bst_seen_opt_seen_cb(const void * node,walk_order_e kind,int level)1991 bst_seen_opt_seen_cb(const void * node, walk_order_e kind, int level)
1992 {
1993 seen_opt_t * seen_opt = ((bst_t *)node)->key;
1994
1995 if (kind == postorder || kind == leaf)
1996 if (seen_opt->seen == 1)
1997 {
1998 user_rc = 1;
1999 user_object = seen_opt->par;
2000 }
2001 }
2002
2003 static void
bst_print_ctx_cb(const void * node,walk_order_e kind,int level)2004 bst_print_ctx_cb(const void * node, walk_order_e kind, int level)
2005 {
2006 ctx_t * ctx = main_ctx;
2007 ctx_t * cur_ctx = ((bst_t *)node)->key;
2008
2009 ll_t * list;
2010
2011 int has_optional = 0;
2012 int has_ellipsis = 0;
2013 int has_rule = 0;
2014 int has_generic_arg = 0;
2015 int has_ctx_change = 0;
2016 int has_early_eval = 0;
2017
2018 if (kind == postorder || kind == leaf)
2019 if (strcmp(ctx->name, cur_ctx->name) != 0)
2020 {
2021 list = cur_ctx->opt_list;
2022
2023 printf("\nAllowed options in the context %s:\n", cur_ctx->name);
2024 print_options(list, &has_optional, &has_ellipsis, &has_rule,
2025 &has_generic_arg, &has_ctx_change, &has_early_eval);
2026 print_before_constraints(list);
2027 }
2028 }
2029
2030 static void
bst_check_opt_cb(const void * node,walk_order_e kind,int level)2031 bst_check_opt_cb(const void * node, walk_order_e kind, int level)
2032 {
2033 opt_t * opt = ((bst_t *)node)->key;
2034
2035 if (kind == postorder || kind == leaf)
2036 {
2037 if (opt->params == NULL) /* opt must have associated parameters. */
2038 fatal_internal("Option %s has no registered parameter.\n", opt->name);
2039
2040 if (opt->action == NULL) /* opt must have an action. */
2041 fatal_internal("Option %s has no registered action.\n", opt->name);
2042 }
2043 }
2044
2045 static void
bst_match_par_cb(const void * node,walk_order_e kind,int level)2046 bst_match_par_cb(const void * node, walk_order_e kind, int level)
2047 {
2048 ctx_t * ctx = ((bst_t *)node)->key;
2049
2050 if (kind == postorder || kind == leaf)
2051 {
2052 char * str = xstrdup(user_string);
2053
2054 while (*str != '\0')
2055 {
2056 if (locate_par(str, ctx) != NULL)
2057 {
2058 if (*user_string2 == '\0')
2059 user_string2 = strappend(user_string2, "- ", ctx->name, (char *)0);
2060 else
2061 user_string2 = strappend(user_string2, "\n- ", ctx->name, (char *)0);
2062 break;
2063 }
2064 str[strlen(str) - 1] = '\0';
2065 }
2066 free(str);
2067 }
2068 }
2069
2070 static void
match_prefix_cb(const void * node,walk_order_e kind,int level)2071 match_prefix_cb(const void * node, walk_order_e kind, int level)
2072 {
2073 par_t * par = ((bst_t *)node)->key;
2074
2075 if (kind == postorder || kind == leaf)
2076 if (strpref(par->name, (char *)user_object))
2077 {
2078 user_rc++;
2079 user_string = strappend(user_string, par->name, " ", (char *)0);
2080 }
2081 }
2082
2083 /* ====================================================================== */
2084 /* A parameter may not be separated from its first option by spaces, in */
2085 /* this case this function looks for a valid flag as a prefix and splits */
2086 /* the command line queue (eg: "-pa1" -> "-pa" "1" if "-pa" is a valid */
2087 /* option). */
2088 /* */
2089 /* IN word : the word to be checked. */
2090 /* IN ctx : the context in which the flag indexed by the word is to be */
2091 /* checked. */
2092 /* OUT pos : the offset in word pointing just after the matching prefix. */
2093 /* OUT opt : a pointer to the option associated with the new parameter */
2094 /* or NULL if none is found. */
2095 /* */
2096 /* The returned pointer must be freed by the caller. */
2097 /* ====================================================================== */
2098 static char *
look_for_valid_prefix_in_word(char * word,ctx_t * ctx,int * pos,opt_t ** opt)2099 look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
2100 {
2101 char * new = NULL;
2102 int len;
2103 par_t * par;
2104 par_t tmp_par = { 0 };
2105
2106 len = strlen(word);
2107
2108 if (len > 2)
2109 {
2110 new = xstrdup(word);
2111
2112 do
2113 {
2114 new[--len] = '\0';
2115 tmp_par.name = new;
2116 } while ((par = locate_par(tmp_par.name, ctx)) == NULL && len > 2);
2117
2118 if (par != NULL)
2119 {
2120 *pos = len;
2121 *opt = par->opt;
2122 }
2123 else
2124 {
2125 free(new);
2126 new = NULL;
2127 }
2128 }
2129 else
2130 *pos = 0;
2131
2132 return new;
2133 }
2134
2135 /* ============================================================= */
2136 /* If par_name is an unique abbreviation of an exiting parameter */
2137 /* in the context ctx, then return this parameter. */
2138 /* ============================================================= */
2139 static char *
abbrev_expand(char * par_name,ctx_t * ctx)2140 abbrev_expand(char * par_name, ctx_t * ctx)
2141 {
2142 user_object = par_name;
2143 user_rc = 0;
2144
2145 *user_string = '\0';
2146 bst_walk(ctx->par_bst, match_prefix_cb);
2147 rtrim(user_string, " ", 0);
2148
2149 /* The previous bst_walk has built a string of blank separated parameters */
2150 /* all having par_name as prefix. This string is put in the user_string */
2151 /* exchange zone. The number of these words in put in user_rc. */
2152 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2153 if (user_rc == 1) /* The number of matching abbreviations. */
2154 return xstrdup(user_string);
2155 else /* There is at least tho defined parameters starting with par_name. */
2156 {
2157 char * s, *first_s;
2158 par_t * par;
2159 opt_t * opt;
2160 int opt_count = 0;
2161 void * tmp_opt_bst = NULL;
2162
2163 /* Find all the options corresponding to these words and store them */
2164 /* without duplication in a temporary BST. Only their resulting count */
2165 /* matters. */
2166 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2167 s = first_s = strtok(user_string, " "); /* first_s holds a copy of *
2168 | the first word. */
2169 while (s != NULL)
2170 {
2171 par = locate_par(s, ctx);
2172 opt = par->opt;
2173
2174 if (bst_find(opt, &tmp_opt_bst, opt_compare) == NULL)
2175 {
2176 /* This option as not already been seen */
2177 /* store it and increase the seen counter. */
2178 /* """"""""""""""""""""""""""""""""""""""" */
2179 bst_search(opt, &tmp_opt_bst, opt_compare);
2180 opt_count++;
2181 }
2182 s = strtok(NULL, " ");
2183 }
2184
2185 /* Clean the temporary BST without removing the pointer */
2186 /* to the real options. */
2187 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2188 if (tmp_opt_bst != NULL)
2189 bst_destroy(tmp_opt_bst, NULL);
2190
2191 if (opt_count == 1)
2192 /* All the abbreviation are leading to only one option */
2193 /* We can just continue as in the previous case. */
2194 /* """"""""""""""""""""""""""""""""""""""""""""""""""" */
2195 return xstrdup(first_s);
2196 else
2197 return NULL;
2198 }
2199 }
2200
2201 /* ================================================================ */
2202 /* Terminate the program if mandatory options required by a context */
2203 /* are not present. */
2204 /* ================================================================ */
2205 static void
check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst,char * opt_par)2206 check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
2207 {
2208 char * missing;
2209
2210 if (has_unseen_mandatory_opt(ctx_inst, &missing))
2211 fatal(CTXOPTMISPAR, missing);
2212 }
2213
2214 /* ====================================================== */
2215 /* Return 1 if at least one mandatory option was not seen */
2216 /* when quitting a context, else 0. */
2217 /* ====================================================== */
2218 static int
has_unseen_mandatory_opt(ctx_inst_t * ctx_inst,char ** missing)2219 has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
2220 {
2221 user_rc = 0;
2222 *user_string = '\0';
2223
2224 bst_walk(ctx_inst->seen_opt_bst, bst_seen_opt_cb);
2225 rtrim(user_string, " ", 0);
2226
2227 *missing = user_string;
2228
2229 return user_rc ? 1 : 0;
2230 }
2231
2232 /* ========================================================================= */
2233 /* This function terminates the program if an option or its arguments do not */
2234 /* conform to its occurrences constraint. */
2235 /* There constraints can appear by trailing >, < or = in their definition */
2236 /* given in ctxopt_new_ctx. */
2237 /* ========================================================================= */
2238 static void
check_for_occurrence_issues(ctx_inst_t * ctx_inst)2239 check_for_occurrence_issues(ctx_inst_t * ctx_inst)
2240 {
2241 ctx_t * ctx = ctx_inst->ctx;
2242 opt_t * opt;
2243 ll_node_t * node;
2244 opt_inst_t * opt_inst;
2245 char * cur_opt_params = cur_state->cur_opt_params;
2246 char * cur_opt_par_name = cur_state->cur_opt_par_name;
2247
2248 /* Checks options. */
2249 /* """"""""""""""" */
2250 node = ctx->opt_list->head;
2251
2252 while (node != NULL)
2253 {
2254 opt = node->data;
2255
2256 /* Update current_state. */
2257 /* """"""""""""""""""""" */
2258 cur_state->cur_opt_params = opt->params;
2259 cur_state->opts_count = opt->opt_count_mark;
2260 cur_state->opt_args_count = opt->opt_args_count_mark;
2261
2262 if (opt->opt_count_matter)
2263 switch (opt->opt_count_oper)
2264 {
2265 case '=':
2266 if (opt->occurrences > 0 && opt->opt_count_mark != opt->occurrences)
2267 fatal(CTXOPTCTEOPT, "");
2268 break;
2269
2270 case '<':
2271 if (opt->occurrences > 0 && opt->opt_count_mark <= opt->occurrences)
2272 fatal(CTXOPTCTLOPT, "");
2273 break;
2274
2275 case '>':
2276 if (opt->occurrences > 0 && opt->opt_count_mark >= opt->occurrences)
2277 fatal(CTXOPTCTGOPT, "");
2278 break;
2279 }
2280
2281 node = node->next;
2282 }
2283
2284 /* Checks arguments. */
2285 /* """"""""""""""""" */
2286 node = ctx_inst->opt_inst_list->head;
2287 while (node != NULL)
2288 {
2289 opt_inst = node->data;
2290 opt = opt_inst->opt;
2291
2292 /* Update current_state. */
2293 /* """"""""""""""""""""" */
2294 cur_state->cur_opt_par_name = opt_inst->par;
2295 cur_state->opts_count = opt->opt_count_mark;
2296 cur_state->opt_args_count = opt->opt_args_count_mark;
2297
2298 int nb_values = opt_inst->values_list->len; /* Number of arguments of opt */
2299
2300 if (opt->opt_args_count_matter)
2301 switch (opt->opt_args_count_oper)
2302 {
2303 case '=':
2304 if (nb_values > 0 && opt->opt_args_count_mark != nb_values)
2305 fatal(CTXOPTCTEARG, "");
2306 break;
2307
2308 case '<':
2309 if (nb_values > 0 && opt->opt_args_count_mark <= nb_values)
2310 fatal(CTXOPTCTLARG, "");
2311 break;
2312
2313 case '>':
2314 if (nb_values > 0 && opt->opt_args_count_mark >= nb_values)
2315 fatal(CTXOPTCTGARG, "");
2316 break;
2317 }
2318
2319 node = node->next;
2320 }
2321 cur_state->cur_opt_params = cur_opt_params;
2322 cur_state->cur_opt_par_name = cur_opt_par_name;
2323 }
2324
2325 /* ====================================================================== */
2326 /* This function terminates the program if all the options which are part */
2327 /* of a group of required options by some other option are missing. */
2328 /* ====================================================================== */
2329 static void
check_for_requirement_issues(ctx_inst_t * ctx_inst)2330 check_for_requirement_issues(ctx_inst_t * ctx_inst)
2331 {
2332 ll_node_t * node;
2333 ll_node_t * req_node;
2334 req_t * req;
2335 opt_t * opt;
2336 opt_t * req_opt;
2337 bst_t * bst_node;
2338 seen_opt_t tmp_seen_opt;
2339 int found;
2340 char * needed_params = NULL;
2341
2342 node = ctx_inst->opt_req_list->head;
2343
2344 while (node != NULL)
2345 {
2346 req = node->data;
2347
2348 opt = req->opt;
2349 tmp_seen_opt.opt = opt;
2350
2351 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2352 seen_opt_compare);
2353
2354 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2355 {
2356 found = 0;
2357 req_node = req->or_opt_list->head;
2358
2359 /* needed_params accumulates the params of the options in the group. */
2360 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2361 free(needed_params); /* free can applied to the NULL pointer. */
2362 needed_params = xstrdup("");
2363
2364 /* Go through the list of the required group of options and */
2365 /* succeed when one of them has been seen in the context. */
2366 /* otherwise a fatal error is triggered and the program is */
2367 /* terminated. */
2368 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2369 while (req_node != NULL)
2370 {
2371 req_opt = req_node->data;
2372 tmp_seen_opt.opt = req_opt;
2373 needed_params = strappend(needed_params, req_opt->params, "\n ",
2374 (char *)0);
2375
2376 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
2377 seen_opt_compare);
2378
2379 if (((seen_opt_t *)(bst_node->key))->seen != 0)
2380 {
2381 found = 1; /* A required option has been seen, */
2382 break; /* accept the group. */
2383 }
2384 req_node = req_node->next;
2385 }
2386
2387 rtrim(needed_params, "\n ", 0);
2388
2389 /* This is a fatal error if none of the options in the required */
2390 /* options group has been seen in the context. */
2391 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2392 if (!found)
2393 {
2394 char * errmsg;
2395
2396 if (req->or_opt_list->len > 1)
2397 errmsg = xstrdup("At least one of the parameters among:\n %s\n"
2398 "requested by %s must be present.\n");
2399 else
2400 errmsg = xstrdup("The parameter %s "
2401 "requested by %s must be present.\n");
2402
2403 cur_state->req_opt_par_needed = needed_params;
2404 cur_state->req_opt_par = opt->params;
2405
2406 fatal(CTXOPTREQPAR, errmsg);
2407 }
2408 }
2409
2410 node = node->next;
2411 }
2412 }
2413
2414 /* ======================================================================== */
2415 /* Parse a strings describing options and some of their characteristics */
2416 /* The input string must have follow some rules like in the examples below: */
2417 /* */
2418 /* "opt_name1 opt_name2" */
2419 /* "[opt_name1] opt_name2" */
2420 /* "[opt_name1] opt_name2..." */
2421 /* "[opt_name1 #...] opt_name2... [#]" */
2422 /* "[opt_name1 [#...]] opt_name2... [#...]" */
2423 /* */
2424 /* Where [ ] encloses an optional part, # means: has parameters and ... */
2425 /* means that there can be more than one occurrence of the previous thing. */
2426 /* */
2427 /* opt_name can be followed by a 'new context' change prefixed with the */
2428 /* symbol >, as in opt1>c2 by eg. */
2429 /* */
2430 /* This function returns as soon as one (or no) option has been parsed and */
2431 /* return the offset to the next option to parse. */
2432 /* */
2433 /* In case of successful parsing, an new option is allocated and its */
2434 /* pointer returned. */
2435 /* ======================================================================== */
2436 static int
opt_parse(char * s,opt_t ** opt)2437 opt_parse(char * s, opt_t ** opt)
2438 {
2439 int opt_optional = 0;
2440 int opt_multiple = 0;
2441 int opt_count_matter = 0;
2442 char opt_count_oper = '\0';
2443 unsigned opt_count_mark = 0;
2444 int opt_args = 0;
2445 char opt_arg[33] = { 0 };
2446 int opt_multiple_args = 0;
2447 int opt_args_count_matter = 0;
2448 char opt_args_count_oper = '\0';
2449 unsigned opt_args_count_mark = 0;
2450 int opt_optional_args = 0;
2451 int opt_eval_first = 0;
2452
2453 int n;
2454 int pos;
2455 int count = 0;
2456
2457 char * s_orig = s;
2458
2459 char * p;
2460 char * opt_name = NULL;
2461 char * next_ctx;
2462 char token[65];
2463
2464 *opt = NULL;
2465 memset(opt_arg, '\0', 33);
2466
2467 /* Strip the leading blanks. */
2468 /* """"""""""""""""""""""""" */
2469 while (isblank(*s))
2470 s++;
2471
2472 if (*s == '[') /* Start of an optional option. */
2473 {
2474 opt_optional = 1;
2475 s++;
2476 }
2477 s = strtoken(s, token, sizeof(token) - 1, "[^] \n\t.]", &pos);
2478 if (s == NULL)
2479 return -1; /* Empty string. */
2480
2481 /* Early EOS, only return success if the option is mandatory. */
2482 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2483 if (!*s)
2484 if (opt_optional == 1)
2485 return -(s - s_orig - 1);
2486
2487 /* Validate the option name */
2488 /* ALPHA+(ALPHANUM|_)* */
2489 /* """""""""""""""""""""""" */
2490 p = token;
2491 if (!isalpha(*p) && *p != '*')
2492 return -(s - s_orig - 1); /* opt_name must start with a letter. */
2493
2494 if (*p == '*')
2495 opt_eval_first = 1;
2496
2497 p++;
2498 while (*p)
2499 {
2500 if (!isalnum(*p) && *p != '_' && *p != '>')
2501 return -(s - s_orig - 1); /* opt_name must contain a letter, *
2502 * a number or a _ */
2503 p++;
2504 }
2505
2506 if (opt_eval_first)
2507 opt_name = xstrdup(token + 1); /* Ignore the first '*' in token. */
2508 else
2509 opt_name = xstrdup(token);
2510
2511 if (*s == ']')
2512 {
2513 s++;
2514 while (isblank(*s))
2515 s++;
2516
2517 goto success;
2518 }
2519
2520 /* Check if it can appear multiple times by looking for the dots. */
2521 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2522 p = strtoken(s, token, 3, "[.]", &pos);
2523 if (p)
2524 {
2525 if (strcmp(token, "...") == 0)
2526 {
2527 opt_multiple = 1;
2528 s = p;
2529 if (*s == '<' || *s == '=' || *s == '>')
2530 {
2531 unsigned value;
2532 int offset;
2533
2534 n = sscanf(s + 1, "%u%n", &value, &offset);
2535 if (n == 1)
2536 {
2537 opt_count_matter = 1;
2538 opt_count_oper = *s;
2539 opt_count_mark = value;
2540 }
2541 s += offset + 1;
2542 }
2543 }
2544 else
2545 {
2546 free(opt_name);
2547 return -(s - s_orig - 1);
2548 }
2549 }
2550
2551 if (*s == ']')
2552 {
2553 /* Abort on extraneous ] if the option is mandatory. */
2554 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2555 if (!opt_optional)
2556 {
2557 free(opt_name);
2558 return -(s - s_orig - 1);
2559 }
2560
2561 s++; /* skip the ] */
2562
2563 if (!*s || isblank(*s))
2564 goto success;
2565 else
2566 {
2567 free(opt_name);
2568 return -(s - s_orig - 1);
2569 }
2570 }
2571
2572 /* A blank separates the option name and the argument tag. */
2573 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */
2574 if (isblank(*s))
2575 {
2576 char dots[4];
2577
2578 while (isblank(*s))
2579 s++;
2580
2581 if (!*s)
2582 goto success;
2583
2584 pos = 0;
2585 n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2586 if (pos > 1 && *opt_arg == '#') /* [# has been read. */
2587 {
2588 opt_args = 1;
2589 opt_optional_args = 1;
2590 if (n == 2)
2591 opt_multiple_args = 1; /* There were dots. */
2592
2593 s += pos + !!(n == 2) * 3; /* Skips the dots. */
2594
2595 if (*s == '<' || *s == '=' || *s == '>')
2596 {
2597 unsigned value;
2598 int offset;
2599
2600 n = sscanf(s + 1, "%u%n", &value, &offset);
2601 if (n == 1)
2602 {
2603 opt_args_count_matter = 1;
2604 opt_args_count_oper = *s;
2605 opt_args_count_mark = value;
2606 }
2607 s += offset + 1;
2608 }
2609
2610 /* Optional arg tag must end with a ] */
2611 /* """""""""""""""""""""""""""""""""" */
2612 if (*s != ']')
2613 {
2614 free(opt_name);
2615 return -(s - s_orig - 1);
2616 }
2617
2618 s++; /* Skip the ] */
2619 }
2620 else
2621 {
2622 n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
2623 if (pos > 0 && *opt_arg == '#') /* # has been read. */
2624 {
2625 opt_args = 1;
2626 if (n == 2) /* There were dots. */
2627 opt_multiple_args = 1;
2628
2629 s += pos + !!(n == 2) * 3; /* Skip the dots. */
2630
2631 if (*s == '<' || *s == '=' || *s == '>')
2632 {
2633 unsigned value;
2634 int offset;
2635
2636 n = sscanf(s + 1, "%u%n", &value, &offset);
2637 if (n == 1)
2638 {
2639 opt_args_count_matter = 1;
2640 opt_args_count_oper = *s;
2641 opt_args_count_mark = value;
2642 }
2643 s += offset + 1;
2644 }
2645 }
2646 }
2647 if (*s == ']')
2648 {
2649 /* Abort on extraneous ] if the option is mandatory. */
2650 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
2651 if (!opt_optional)
2652 {
2653 free(opt_name);
2654 return -(s - s_orig - 1);
2655 }
2656
2657 s++; /* skip the ] */
2658
2659 /* Strip the following blanks. */
2660 /* """"""""""""""""""""""""""" */
2661 while (isblank(*s))
2662 s++;
2663
2664 goto success;
2665 }
2666 else if (opt_optional == 0 && (!*s || isblank(*s)))
2667 {
2668 /* Strip the following blanks. */
2669 /* """"""""""""""""""""""""""" */
2670 while (isblank(*s))
2671 s++;
2672
2673 goto success;
2674 }
2675 else if (opt_args == 0) /* # was not read it is possibly the start *
2676 * of another option. */
2677 goto success;
2678 else
2679 {
2680 free(opt_name);
2681 return -(s - s_orig - 1);
2682 }
2683 }
2684
2685 success:
2686
2687 /* Strip the following blanks. */
2688 /* """"""""""""""""""""""""""" */
2689 while (isblank(*s))
2690 s++;
2691
2692 next_ctx = NULL;
2693
2694 if (*opt_name == '>')
2695 fatal_internal("The option name is missing in %s.", opt_name);
2696
2697 count = strchrcount(opt_name, '>');
2698 if (count == 1)
2699 {
2700 char * tmp = strchr(opt_name, '>');
2701 next_ctx = xstrdup(tmp + 1);
2702 *tmp = '\0';
2703 }
2704 else if (count > 1)
2705 fatal_internal("Only one occurrence of '>' is allowed in %s.", opt_name);
2706
2707 *opt = xmalloc(sizeof(opt_t));
2708
2709 (*opt)->name = opt_name;
2710 (*opt)->optional = opt_optional;
2711 (*opt)->multiple = opt_multiple;
2712 (*opt)->opt_count_matter = opt_count_matter;
2713 (*opt)->opt_count_oper = opt_count_oper;
2714 (*opt)->opt_count_mark = opt_count_mark;
2715 (*opt)->args = opt_args;
2716 (*opt)->arg = xstrdup(opt_arg);
2717 (*opt)->optional_args = opt_optional_args;
2718 (*opt)->multiple_args = opt_multiple_args;
2719 (*opt)->opt_args_count_matter = opt_args_count_matter;
2720 (*opt)->opt_args_count_oper = opt_args_count_oper;
2721 (*opt)->opt_args_count_mark = opt_args_count_mark;
2722 (*opt)->eval_first = opt_eval_first;
2723 (*opt)->next_ctx = next_ctx;
2724 (*opt)->ctx_list = ll_new();
2725 (*opt)->constraints_list = ll_new();
2726 (*opt)->eval_before_list = ll_new();
2727 (*opt)->action = NULL;
2728 (*opt)->params = NULL;
2729 (*opt)->data = NULL;
2730
2731 return s - s_orig;
2732 }
2733
2734 /* ==================================================================== */
2735 /* Try to initialize all the option in a given string. */
2736 /* Each parsed option are put in a BST tree with its name as index. */
2737 /* */
2738 /* On collision, the arguments only the signature are required to be */
2739 /* the same else this is considered as an error. Options can be used in */
2740 /* more than one context and can be optional in one and mandatory in */
2741 /* another. */
2742 /* ==================================================================== */
2743 static int
init_opts(char * spec,ctx_t * ctx)2744 init_opts(char * spec, ctx_t * ctx)
2745 {
2746 opt_t * opt, *bst_opt;
2747 bst_t * node;
2748 int offset;
2749
2750 while (*spec)
2751 {
2752 if ((offset = opt_parse(spec, &opt)) > 0)
2753 {
2754 spec += offset;
2755
2756 if ((node = bst_find(opt, &options_bst, opt_compare)) != NULL)
2757 {
2758 int same_next_ctx = 0;
2759
2760 bst_opt = node->key; /* Node extracted from the BST. */
2761
2762 if (bst_opt->next_ctx == NULL && opt->next_ctx == NULL)
2763 same_next_ctx = 1;
2764 else if (bst_opt->next_ctx == NULL && opt->next_ctx != NULL)
2765 same_next_ctx = 0;
2766 else if (bst_opt->next_ctx != NULL && opt->next_ctx == NULL)
2767 same_next_ctx = 0;
2768 else
2769 same_next_ctx = strcmp(bst_opt->next_ctx, opt->next_ctx) == 0;
2770
2771 if (bst_opt->optional_args != opt->optional_args
2772 || bst_opt->multiple_args != opt->multiple_args
2773 || bst_opt->args != opt->args || !same_next_ctx)
2774 {
2775 fatal_internal("The option %s already exists with "
2776 "a different arguments signature.\n",
2777 opt->name);
2778 }
2779
2780 /* The newly created opt is already present in options_bst. */
2781 /* We can remove it. */
2782 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2783 opt_free(opt);
2784
2785 /* The new occurrence of the option option is legal */
2786 /* append the current context ptr in the list. */
2787 /* """""""""""""""""""""""""""""""""""""""""""""""" */
2788 ll_append(bst_opt->ctx_list, ctx);
2789
2790 /* Append the new option to the context's options list. */
2791 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2792 ll_append(ctx->opt_list, bst_opt);
2793 }
2794 else
2795 {
2796 /* Initialize the option's context list with the current context. */
2797 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2798 ll_append(opt->ctx_list, ctx);
2799
2800 /* Append the new option to the context's options list. */
2801 /* """""""""""""""""""""""""""""""""""""""""""""""""""" */
2802 ll_append(ctx->opt_list, opt);
2803
2804 /* Insert the new option in the BST. */
2805 /* """"""""""""""""""""""""""""""""" */
2806 bst_search(opt, &options_bst, opt_compare);
2807 }
2808 }
2809 else
2810 {
2811 char * s = xstrndup(spec, -offset);
2812 printf("%s <---\nSyntax error at or before offset %d\n", s, -offset);
2813 free(s);
2814
2815 exit(EXIT_FAILURE);
2816 }
2817 }
2818
2819 return 1;
2820 }
2821
2822 /* ===================================================== */
2823 /* ctxopt initialization function, must be called first. */
2824 /* ===================================================== */
2825 void
ctxopt_init(char * prog_name,char * init_flags)2826 ctxopt_init(char * prog_name, char * init_flags)
2827 {
2828 int n;
2829
2830 contexts_bst = NULL;
2831 options_bst = NULL;
2832 char * ptr;
2833
2834 user_rc = 0;
2835 user_value = 0;
2836 user_string = xmalloc(8);
2837 user_string2 = xmalloc(8);
2838 user_object = NULL;
2839 char flag[33], fname[31], vname[31];
2840 int invalid;
2841
2842 ctxopt_initialized = 1;
2843
2844 /* Initialize current_state.*/
2845 /* """""""""""""""""""""""" */
2846 cur_state = xcalloc(sizeof(state_t), 0);
2847
2848 /* Initialize custom error function pointers to NULL. */
2849 /* """""""""""""""""""""""""""""""""""""""""""""""""" */
2850 err_functions = xmalloc(CTXOPTERRSIZ * sizeof(void *));
2851 for (n = 0; n < CTXOPTERRSIZ; n++)
2852 err_functions[n] = NULL;
2853
2854 /* Parse init_flags if any. */
2855 /* """""""""""""""""""""""" */
2856 while (*init_flags && (init_flags = get_word(init_flags, flag, 32)))
2857 {
2858 if (*flag)
2859 {
2860 if (sscanf(flag, "%30[^=]=%30[^=]", fname, vname) != 2)
2861 fatal_internal("Invalid flag assignment: %s.", flag);
2862
2863 if (strcmp(fname, "stop_if_non_option") == 0)
2864 {
2865 if (eval_yes(vname, &invalid))
2866 flags.stop_if_non_option = 1;
2867 else if (!invalid)
2868 flags.stop_if_non_option = 0;
2869 else
2870 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2871 }
2872 else if (strcmp(fname, "allow_abbreviations") == 0)
2873 {
2874 if (eval_yes(vname, &invalid))
2875 flags.allow_abbreviations = 1;
2876 else if (!invalid)
2877 flags.allow_abbreviations = 0;
2878 else
2879 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2880 }
2881 else if (strcmp(fname, "display_usage_on_error") == 0)
2882 {
2883 if (eval_yes(vname, &invalid))
2884 flags.display_usage_on_error = 1;
2885 else if (!invalid)
2886 flags.display_usage_on_error = 0;
2887 else
2888 fatal_internal("Invalid flag value for %s: %s.", fname, vname);
2889 }
2890 else
2891 fatal_internal("Invalid flag name: %s.", fname);
2892 }
2893 }
2894
2895 /* Update current_state. */
2896 /* """"""""""""""""""""" */
2897 if (prog_name)
2898 {
2899 if (*prog_name == '\0')
2900 cur_state->prog_name = xstrdup("program_name");
2901 else if ((ptr = strrchr(prog_name, '/')))
2902 cur_state->prog_name = xstrdup(ptr + 1);
2903 else
2904 cur_state->prog_name = xstrdup(prog_name);
2905 }
2906 else
2907 cur_state->prog_name = xstrdup("program_name");
2908 }
2909
2910 /* ========================================================================= */
2911 /* Utility function which create and register a par_t object in a BST */
2912 /* embedded in a context. */
2913 /* This object will have a name and a pointer to the option it refers to. */
2914 /* These object will be used to quickly find an option from a command */
2915 /* line parameter during the analysis phase. */
2916 /* */
2917 /* IN : an option name. */
2918 /* IN : a string of command line parameters to associate to the option. */
2919 /* Returns : 1 is all was fine else 0. */
2920 /* ========================================================================= */
2921 static int
opt_set_parms(char * opt_name,char * par_str)2922 opt_set_parms(char * opt_name, char * par_str)
2923 {
2924 char * par_name, *ctx_name;
2925 char * tmp_par_str, *end_tmp_par_str;
2926 ctx_t * ctx;
2927 opt_t * opt;
2928 bst_t * node;
2929 par_t * par, tmp_par;
2930 int rc = 1; /* Return code. */
2931
2932 ll_t * list;
2933 ll_node_t * lnode;
2934
2935 /* Look if the given option is defined. */
2936 /* """""""""""""""""""""""""""""""""""" */
2937 opt = locate_opt(opt_name);
2938 if (opt == NULL)
2939 fatal_internal("Unknown option %s.", opt_name);
2940
2941 /* For each context using this option. */
2942 /* """"""""""""""""""""""""""""""""""" */
2943 list = opt->ctx_list;
2944
2945 lnode = list->head;
2946 while (lnode != NULL)
2947 {
2948 /* Locate the context in the contexts tree. */
2949 /* """""""""""""""""""""""""""""""""""""""" */
2950 ctx_name = ((ctx_t *)(lnode->data))->name;
2951
2952 ctx = locate_ctx(ctx_name);
2953 if (ctx == NULL)
2954 fatal_internal("Unknown context %s.", ctx_name);
2955 else
2956 {
2957 void * par_bst = ctx->par_bst;
2958
2959 tmp_par_str = xstrdup(par_str);
2960 ltrim(tmp_par_str, " \t");
2961 rtrim(tmp_par_str, " \t", 0);
2962 par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
2963 if (par_name == NULL)
2964 fatal_internal("Parameters are missing for option %s.", opt_name);
2965
2966 /* For each parameter given in par_str, creates a par_t object and */
2967 /* insert it the in the parameters BST of the context. */
2968 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2969 while (par_name != NULL)
2970 {
2971 tmp_par.name = par_name;
2972
2973 node = bst_find(&tmp_par, &par_bst, par_compare);
2974 if (node != NULL)
2975 {
2976 fatal_internal("The parameter %s is already defined in context %s.",
2977 par_name, ctx->name);
2978 rc = 0;
2979 }
2980 else
2981 {
2982 par = xmalloc(sizeof(par_t));
2983 par->name = xstrdup(par_name);
2984 par->opt = opt; /* Link the option to this parameter. */
2985
2986 bst_search(par, &par_bst, par_compare);
2987 }
2988 par_name = xstrtok_r(NULL, " \t,", &end_tmp_par_str);
2989 }
2990
2991 /* Update the value of the root of ctx->par_bst as it may have */
2992 /* been modified. */
2993 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
2994 ctx->par_bst = par_bst;
2995
2996 free(tmp_par_str);
2997 }
2998 lnode = lnode->next;
2999 }
3000
3001 return rc;
3002 }
3003
3004 /* ==================================================================== */
3005 /* Create a new context instance. */
3006 /* IN ctx : a context pointer to allow this instance to */
3007 /* access the context fields */
3008 /* IN prev_ctx_inst : the context instance whose option leading to the */
3009 /* creation of this new context instance is part of */
3010 /* Returns : the new context. */
3011 /* ==================================================================== */
3012 static ctx_inst_t *
new_ctx_inst(ctx_t * ctx,ctx_inst_t * prev_ctx_inst)3013 new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
3014 {
3015 opt_t * opt;
3016 opt_inst_t * gen_opt_inst;
3017 ctx_inst_t * ctx_inst;
3018 seen_opt_t * seen_opt;
3019 char * str, *opt_name;
3020 void * bst;
3021 bst_t * bst_node;
3022
3023 /* Keep a trace of the opt_inst which was at the origin of the creation */
3024 /* of this context instance. */
3025 /* This will serve during the evaluation of the option callbacks. */
3026 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3027 if (prev_ctx_inst != NULL)
3028 {
3029 gen_opt_inst = (opt_inst_t *)(prev_ctx_inst->opt_inst_list->tail->data);
3030
3031 /* Update current_state. */
3032 /* """"""""""""""""""""" */
3033 cur_state->opt_name = gen_opt_inst->opt->name;
3034 }
3035 else
3036 gen_opt_inst = NULL;
3037
3038 /* Create and initialize the new context instance. */
3039 /* """"""""""""""""""""""""""""""""""""""""""""""" */
3040 ctx_inst = xmalloc(sizeof(ctx_inst_t));
3041 ctx_inst->ctx = ctx;
3042 ctx_inst->prev_ctx_inst = prev_ctx_inst;
3043 ctx_inst->gen_opt_inst = gen_opt_inst;
3044 ctx_inst->incomp_bst_list = ll_new();
3045 ctx_inst->opt_inst_list = ll_new();
3046 ctx_inst->opt_req_list = ll_new();
3047 ctx_inst->seen_opt_bst = NULL;
3048
3049 ll_node_t * node;
3050
3051 if (prev_ctx_inst == NULL)
3052 first_ctx_inst = ctx_inst;
3053
3054 /* Initialize the occurrence counters of each opt allowed in the context. */
3055 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3056 node = ctx->opt_list->head;
3057 while (node != NULL)
3058 {
3059 opt = node->data;
3060 opt->occurrences = 0;
3061
3062 node = node->next;
3063 }
3064
3065 /* Initialize the BST containing the seen indicator for all the options */
3066 /* allowed in this context instance. */
3067 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3068 node = ctx->opt_list->head;
3069 while (node != NULL)
3070 {
3071 opt = node->data;
3072 seen_opt = xmalloc(sizeof(seen_opt_t));
3073 seen_opt->opt = opt;
3074 seen_opt->par = NULL;
3075 seen_opt->seen = 0;
3076
3077 bst_search(seen_opt, &(ctx_inst->seen_opt_bst), seen_opt_compare);
3078
3079 node = node->next;
3080 }
3081
3082 /* Initialize the BST containing the incompatibles options. */
3083 /* Incompatibles option names are read from strings found in the list */
3084 /* incomp_list present in each instance of ctx_t. */
3085 /* These names are then used to search for the object of type seen_opt_t */
3086 /* which is already present in the seen_opt_bst of the context instance. */
3087 /* in the BST. */
3088 /* Once found the seen_opt_t object in inserted in the new BST. */
3089 /* At the end the new BST in added to the list incomp_bst_list. */
3090 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3091 node = ctx->incomp_list->head;
3092 while (node != NULL)
3093 {
3094 bst = NULL;
3095 seen_opt_t tmp_seen_opt;
3096
3097 str = xstrdup(node->data);
3098 ltrim(str, " \t");
3099 rtrim(str, " \t", 0);
3100 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3101
3102 while (opt_name != NULL) /* For each option name. */
3103 {
3104 if ((opt = locate_opt(opt_name)) != NULL)
3105 {
3106 /* The option found is searched in the tree of potential */
3107 /* seen options. */
3108 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3109 tmp_seen_opt.opt = opt;
3110
3111 bst_node = bst_find(&tmp_seen_opt, &(ctx_inst->seen_opt_bst),
3112 seen_opt_compare);
3113
3114 if (bst_node != NULL)
3115 {
3116 /* If found then it is added into the new BST tree. */
3117 /* """""""""""""""""""""""""""""""""""""""""""""""" */
3118 seen_opt = bst_node->key;
3119 bst_search(seen_opt, &bst, seen_opt_compare);
3120 }
3121 else
3122 /* Not found! That means that the option is unknown in this */
3123 /* context as all options has have a seen_opt structure in */
3124 /* seen_opt_bst. */
3125 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3126 fatal_internal("%s is not known in the context %s.", opt->name,
3127 ctx->name);
3128 }
3129 else
3130 fatal_internal("Unknown option %s.", opt_name);
3131
3132 opt_name = strtok(NULL, " \t");
3133 }
3134
3135 free(str);
3136 ll_append(ctx_inst->incomp_bst_list, bst);
3137
3138 node = node->next;
3139 }
3140
3141 /* Initialize the list of res_t structures according to the */
3142 /* list set in the context by ctxopt_add_ctx_settings/required. */
3143 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3144 node = ctx->req_list->head;
3145 while (node != NULL)
3146 {
3147 req_t * req = xmalloc(sizeof(req_t));
3148
3149 str = xstrdup(node->data);
3150 ltrim(str, " \t");
3151 rtrim(str, " \t", 0);
3152 opt_name = strtok(str, " \t"); /* Extract the first option name. */
3153
3154 if ((opt = locate_opt(opt_name)) != NULL)
3155 {
3156 req->opt = opt;
3157 req->or_opt_list = ll_new();
3158 while ((opt_name = strtok(NULL, " \t")) != NULL)
3159 {
3160 if ((opt = locate_opt(opt_name)) != NULL)
3161 ll_append(req->or_opt_list, opt);
3162 else
3163 fatal_internal("Unknown option %s.", opt_name);
3164 }
3165 ll_append(ctx_inst->opt_req_list, req);
3166 }
3167 else
3168 fatal_internal("Unknown option %s.", opt_name);
3169
3170 free(str);
3171
3172 node = node->next;
3173 }
3174 return ctx_inst;
3175 }
3176
3177 /* ====================================================================== */
3178 /* Create a list formed by all the significant command line words */
3179 /* Words beginning or ending with { or } are split. Each of these */
3180 /* symbols will get their own place in the list. */
3181 /* */
3182 /* the {...} part delimits a context, the { will not appear in the list */
3183 /* and the } will be replaced by a | in the resulting list (cmdline_list) */
3184 /* to facilitate the parsing phase. | must not be used by the end user. */
3185 /* */
3186 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3187 /* program name is not considered. */
3188 /* IN words : is the array of strings constituting the command line to */
3189 /* parse. */
3190 /* Returns : 1 on success, 0 if a { or } is missing. */
3191 /* ====================================================================== */
3192 static int
ctxopt_build_cmdline_list(int nb_words,char ** words)3193 ctxopt_build_cmdline_list(int nb_words, char ** words)
3194 {
3195 int i;
3196 char * prev_word = NULL;
3197 char * word;
3198 char * ptr;
3199 int level = 0;
3200 ll_node_t *node, *start_node;
3201
3202 /* The analysis is divided into three passes, this is not optimal but */
3203 /* must be done only one time. Doing that we privilege readability. */
3204 /* */
3205 /* In the following, SG is the ascii character 1d (dec 29) */
3206 /* */
3207 /* The first pass creates the list, extract the leading an trailing */
3208 /* SG '{' and '}' of each word and give them their own place in the */
3209 /* list */
3210 /* */
3211 /* The second pass transform the '{...}' blocks by a trailing SG */
3212 /* ({...} -> ...|) */
3213 /* */
3214 /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
3215 /* the middle in the remaining list elements and recreate the pseudo */
3216 /* argument: {} */
3217 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3218
3219 /* If the option list is not empty, clear it before going further. */
3220 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3221 if (cmdline_list != NULL)
3222 ll_destroy(cmdline_list, free);
3223
3224 cmdline_list = ll_new();
3225
3226 start_node = cmdline_list->head; /* In the following loop start_node will *
3227 * contain a pointer to the current *
3228 * word stripped from its leading *
3229 * sequence of {, }. */
3230 for (i = 0; i < nb_words; i++)
3231 {
3232 size_t len = strlen(words[i]);
3233 size_t start, end;
3234 char * str;
3235
3236 str = words[i];
3237
3238 /* Replace each occurrence of the legal word {} by the characters */
3239 /* 0x02 and 0x03 to hide them from the following process. */
3240 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3241 while ((ptr = strstr(str, "{}")) != NULL)
3242 {
3243 *ptr = 0x02; /* Arbitrary values unlikely */
3244 *(ptr + 1) = 0x03; /* present in a word. */
3245 }
3246
3247 if (len > 1) /* The word contains at least 2 characters. */
3248 {
3249 start = 0;
3250
3251 /* Interpret its beginning and look for the start of the real word. */
3252 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3253 while (start <= len - 1 && (str[start] == '{' || str[start] == '}'))
3254 {
3255 ll_append(cmdline_list, xstrndup(str + start, 1));
3256 start++;
3257 start_node = cmdline_list->tail;
3258 }
3259
3260 end = len - 1;
3261 if (str[end] == '{' || str[end] == '}')
3262 {
3263 if (end > 0 && str[end - 1] != '\\')
3264 {
3265 ll_append(cmdline_list, xstrndup(str + end, 1));
3266 end--;
3267 node = cmdline_list->tail;
3268
3269 while (str[end] == '{' || str[end] == '}')
3270 {
3271 if (end > start && str[end - 1] == '\\')
3272 break;
3273
3274 ll_insert_before(cmdline_list, node, xstrndup(str + end, 1));
3275 end--;
3276 node = node->prev;
3277 }
3278 }
3279 }
3280
3281 if (start <= end)
3282 {
3283 if (start_node != NULL)
3284 ll_insert_after(cmdline_list, start_node,
3285 xstrndup(str + start, end - start + 1));
3286 else
3287 ll_append(cmdline_list, xstrndup(str + start, end - start + 1));
3288 start_node = cmdline_list->tail;
3289 }
3290 }
3291 else if (len == 1)
3292 {
3293 ll_append(cmdline_list, xstrdup(str));
3294 start_node = cmdline_list->tail;
3295 }
3296 }
3297
3298 /* 2nd pass. */
3299 /* """"""""" */
3300 node = cmdline_list->head;
3301
3302 level = 0;
3303 while (node != NULL)
3304 {
3305 word = node->data;
3306
3307 if (strcmp(word, "{") == 0)
3308 {
3309 ll_node_t * old_node = node;
3310 level++;
3311 node = node->next;
3312 free(word);
3313 ll_delete(cmdline_list, old_node);
3314 }
3315 else if (strcmp(word, "}") == 0)
3316 {
3317 level--;
3318
3319 if (level < 0)
3320 return 0;
3321 else
3322 *word = 0x1d;
3323 }
3324 else
3325 node = node->next;
3326 }
3327
3328 if (level != 0)
3329 return 0;
3330
3331 /* 3rd pass. */
3332 /* """"""""" */
3333 node = cmdline_list->head;
3334
3335 while (node != NULL)
3336 {
3337 word = node->data;
3338
3339 /* Restore the original { and } characters forming the legal word {}. */
3340 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3341 while ((ptr = strchr(word, 0x02)) != NULL)
3342 *ptr = '{';
3343 while ((ptr = strchr(word, 0x03)) != NULL)
3344 *ptr = '}';
3345
3346 /* Remove a SG if the previous element is SG. */
3347 /* """""""""""""""""""""""""""""""""""""""""" */
3348 if (strcmp(word, "\x1d") == 0)
3349 {
3350 if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
3351 {
3352 ll_node_t * old_node = node;
3353 node = node->prev;
3354 free(old_node->data);
3355 ll_delete(cmdline_list, old_node);
3356 }
3357 }
3358 else if (strcmp(word, "-") == 0) /* A single - is a legal argument, not *
3359 * a parameter. Protect it. */
3360 {
3361 free(node->data);
3362 node->data = xstrdup("\\-");
3363 }
3364
3365 prev_word = node->data;
3366 node = node->next;
3367 }
3368
3369 /* Clean useless and SG at the beginning and end of list. */
3370 /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */
3371 node = cmdline_list->head;
3372
3373 if (node == NULL)
3374 return 1;
3375
3376 word = node->data;
3377
3378 if (strcmp(word, "\x1d") == 0)
3379 {
3380 free(word);
3381 ll_delete(cmdline_list, node);
3382 }
3383
3384 node = cmdline_list->tail;
3385 if (node == NULL)
3386 return 1;
3387
3388 word = node->data;
3389
3390 if (strcmp(word, "\x1d") == 0)
3391 {
3392 free(word);
3393 ll_delete(cmdline_list, node);
3394 }
3395
3396 return 1;
3397 }
3398
3399 /* ===================================================================== */
3400 /* Build and analyze the command line list and create the linked data */
3401 /* structures whose data will be evaluated later by ctxopt_evaluate. */
3402 /* This function identifies the following errors and creates an array of */
3403 /* The remaining unanalyzed arguments. */
3404 /* - detect missing arguments */
3405 /* - detect too many arguments */
3406 /* - detect unknown parameters in a context */
3407 /* - detect too many occurrences of a parameters in a context */
3408 /* - detect missing required arguments in a context */
3409 /* */
3410 /* IN nb_word : number of word to parse, this is typically argc-1 as the */
3411 /* program name is not considered */
3412 /* IN words : is the array of strings constituting the command line to */
3413 /* parse. */
3414 /* OUT nb_rem_args : nb of remaining command line arguments if a -- */
3415 /* is present in the list. */
3416 /* OUT rem_args : array of remaining command line arguments if a -- */
3417 /* is present in the list. This array must be free by */
3418 /* The caller as it is allocated here. */
3419 /* ===================================================================== */
3420 void
ctxopt_analyze(int nb_words,char ** words,int * nb_rem_args,char *** rem_args)3421 ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
3422 char *** rem_args)
3423 {
3424 char * ctxopt_debug_env; /* Environment variable CTXOPT_DEBUG content. */
3425 int ctxopt_debug; /* 1 if ctxopt_debug_env is set and not empty. *
3426 | 0 if ctxopt_debug_env is unset or empty. */
3427
3428 ctx_t * ctx;
3429 opt_t * opt;
3430 par_t * par;
3431 ctx_inst_t * ctx_inst;
3432 opt_inst_t * opt_inst;
3433 int expect_par = 0;
3434 int expect_arg = 0;
3435 int expect_par_or_arg = 0;
3436
3437 ll_node_t * cli_node;
3438 bst_t * bst_node;
3439 seen_opt_t * bst_seen_opt;
3440 char * par_name;
3441 void * bst;
3442
3443 ll_node_t * node;
3444
3445 if (!ctxopt_build_cmdline_list(nb_words, words))
3446 fatal_internal("The command line could not be parsed: "
3447 "missing '{' or '}' detected.");
3448
3449 if (main_ctx == NULL)
3450 fatal_internal("At least one context must have been created.");
3451
3452 /* Check that all options has an action and at least one parameter. */
3453 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3454 bst_walk(options_bst, bst_check_opt_cb);
3455
3456 /* CTXOPT debug setting. */
3457 /* """"""""""""""""""""" */
3458 ctxopt_debug_env = getenv("CTXOPT_DEBUG");
3459 if (ctxopt_debug_env != NULL && *ctxopt_debug_env != '\0')
3460 ctxopt_debug = 1;
3461 else
3462 ctxopt_debug = 0;
3463
3464 /* Create the first ctx_inst record. */
3465 /* """"""""""""""""""""""""""""""""" */
3466 ctx = main_ctx;
3467
3468 ctx_inst_list = ll_new();
3469 ctx_inst = new_ctx_inst(ctx, NULL);
3470 ctx_inst->par_name = NULL;
3471
3472 /* Update current_state. */
3473 /* """"""""""""""""""""" */
3474 cur_state->ctx_name = ctx->name;
3475
3476 ll_append(ctx_inst_list, ctx_inst);
3477
3478 /* For each node in the command line. */
3479 /* """""""""""""""""""""""""""""""""" */
3480 cli_node = cmdline_list->head;
3481 expect_par = 1;
3482 par_name = NULL;
3483
3484 while (cli_node != NULL)
3485 {
3486 if (strcmp(cli_node->data, "--") == 0)
3487 break; /* No new parameter will be analyzed after this point. */
3488
3489 par_name = cli_node->data;
3490
3491 /* Replace a leading -- by a single - */
3492 /* """""""""""""""""""""""""""""""""" */
3493 if (strncmp(par_name, "--", 2) == 0)
3494 par_name += 1; /* Ignore the first dash. */
3495
3496 if (strcmp(par_name, "\x1d") == 0)
3497 {
3498 check_for_missing_mandatory_opt(ctx_inst, (char *)(cli_node->prev->data));
3499 check_for_occurrence_issues(ctx_inst);
3500 check_for_requirement_issues(ctx_inst);
3501
3502 /* Forced backtracking to the previous context instance. */
3503 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3504 if (ctx_inst->prev_ctx_inst != NULL)
3505 {
3506 ctx_inst = ctx_inst->prev_ctx_inst;
3507 ctx = ctx_inst->ctx;
3508
3509 /* Update current_states. */
3510 /* """"""""""""""""""""" */
3511 cur_state->ctx_name = ctx->name;
3512 cur_state->ctx_par_name = ctx_inst->par_name;
3513
3514 if (ctxopt_debug)
3515 fprintf(stderr,
3516 "CTXOPT_DEBUG: Context forced backtrack, "
3517 "new current context: %s.\n",
3518 ctx->name);
3519 }
3520 else
3521 {
3522 /* Update current_state. */
3523 /* """"""""""""""""""""" */
3524 cur_state->ctx_par_name = NULL;
3525 }
3526 }
3527 else if (expect_par && *par_name == '-')
3528 {
3529 int pos = 0;
3530 char * prefix;
3531
3532 /* Update current_state. */
3533 /* """"""""""""""""""""" */
3534 cur_state->cur_opt_par_name = par_name;
3535 cur_state->ctx_name = ctx->name;
3536 cur_state->ctx_par_name = ctx_inst->par_name;
3537
3538 if (ctxopt_debug)
3539 fprintf(stderr, "CTXOPT_DEBUG: Parameter: %s. Current context: %s.\n",
3540 par_name, cur_state->ctx_name);
3541
3542 /* An expected parameter has been seen. */
3543 /* """""""""""""""""""""""""""""""""""" */
3544 if ((par = locate_par(par_name, ctx)) == NULL)
3545 {
3546 opt_t * popt;
3547 char * word;
3548
3549 /* Look if this parameter is an unique abbreviation of a longer */
3550 /* parameter. If this is the case then just replace it with its */
3551 /* full length version and try again. */
3552 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3553 if (flags.allow_abbreviations)
3554 if ((word = abbrev_expand(par_name, ctx)) != NULL)
3555 {
3556 cli_node->data = word;
3557 continue;
3558 }
3559
3560 /* Try to find a prefix which is a valid parameter in this context */
3561 /* If found, split the cli_node in two to build a new parameter */
3562 /* node and followed by a node containing the remaining string */
3563 /* If the new parameter corresponds to an option not taking */
3564 /* argument then prefix the remaining string whit a dash as it may */
3565 /* contain a new parameter. */
3566 /* The new parameter will be re-evaluated in the next iteration of */
3567 /* the loop. */
3568 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""*/
3569 prefix = look_for_valid_prefix_in_word(par_name, ctx, &pos, &popt);
3570 if (prefix != NULL && pos != 0)
3571 {
3572 if (ctxopt_debug)
3573 fprintf(stderr,
3574 "CTXOPT_DEBUG: Found a valid parameter "
3575 "as a prefix of %s: %s.\n",
3576 par_name, prefix);
3577
3578 cli_node->data = prefix; /* prefix contains le name of a valid *
3579 | parameter in this context. */
3580
3581 if (popt->args)
3582 {
3583 /* The parameter may be followed by arguments. */
3584 /* ''''''''''''''''''''''''''''''''''''''''''' */
3585 if (*(par_name + pos) == '-')
3586 {
3587 word = xstrdup("\\"); /* Protect the '-' */
3588 word = strappend(word, par_name + pos, (char *)0);
3589 }
3590 else
3591 word = xstrdup(par_name + pos);
3592 }
3593 else
3594 {
3595 /* The parameter does not take arguments, the */
3596 /* following word must be a parameter or nothing */
3597 /* hence prefix it with a dash. */
3598 /* ''''''''''''''''''''''''''''''''''''''''''''' */
3599 word = xstrdup("-");
3600 word = strappend(word, par_name + pos, (char *)0);
3601 }
3602
3603 /* Insert it after the current node in the list. */
3604 /* """"""""""""""""""""""""""""""""""""""""""""" */
3605 ll_insert_after(cmdline_list, cli_node, word);
3606
3607 continue; /* loop. */
3608 }
3609 else
3610 {
3611 check_for_missing_mandatory_opt(ctx_inst, par_name);
3612 check_for_occurrence_issues(ctx_inst);
3613 check_for_requirement_issues(ctx_inst);
3614
3615 if (ctx_inst->prev_ctx_inst == NULL)
3616 {
3617 char * errmsg = xstrdup("");
3618
3619 /* Update current_state. */
3620 /* """"""""""""""""""""" */
3621 cur_state->ctx_par_name = NULL;
3622
3623 *user_string = '\0';
3624 *user_string2 = '\0';
3625
3626 user_string = strappend(user_string, par_name, (char *)0);
3627
3628 bst_walk(contexts_bst, bst_match_par_cb);
3629
3630 if (*user_string2 != '\0')
3631 {
3632 char * help_msg;
3633 int count = 0;
3634
3635 count = strchrcount(user_string2, '\n');
3636
3637 if (flags.display_usage_on_error)
3638 help_msg = ", see below";
3639 else
3640 help_msg = "";
3641
3642 if (count == 0) /* Only one context involved. */
3643 errmsg = strappend(
3644 errmsg,
3645 "\nThis parameter is only valid in the following "
3646 "context:\n",
3647 user_string2,
3648 "\n\nFirst switch to this context using the appropriate "
3649 "parameter",
3650 help_msg, ".\n", (char *)0);
3651 else
3652 errmsg = strappend(
3653 errmsg,
3654 "\nThis parameter is only valid in one of the following "
3655 "contexts:\n",
3656 user_string2,
3657 "\n\nFirst switch to one of them using the appropriate "
3658 "parameter",
3659 help_msg, ".\n", (char *)0);
3660 }
3661
3662 fatal(CTXOPTUNKPAR, errmsg);
3663 }
3664 else
3665 {
3666 /* Tries to backtrack and analyse the same parameter in the */
3667 /* previous context. */
3668 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3669 ctx_inst = ctx_inst->prev_ctx_inst;
3670 ctx = ctx_inst->ctx;
3671
3672 if (ctxopt_debug)
3673 fprintf(stderr,
3674 "CTXOPT_DEBUG: Context backtrack, "
3675 "new current context: %s.\n",
3676 ctx->name);
3677
3678 /* Update current_state. */
3679 /* """"""""""""""""""""" */
3680 cur_state->ctx_name = ctx->name;
3681 cur_state->ctx_par_name = ctx_inst->par_name;
3682
3683 cli_node = cli_node->prev;
3684 }
3685 }
3686 }
3687 else
3688 {
3689 seen_opt_t seen_opt;
3690
3691 /* The parameter is valid in the context, create a opt_inst and */
3692 /* append it to the ctx_inst list options list. */
3693 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3694 opt = par->opt;
3695
3696 opt->occurrences++;
3697
3698 opt_inst = xmalloc(sizeof(opt_inst_t));
3699 opt_inst->opt = opt;
3700 opt_inst->par = par_name;
3701 opt_inst->values_list = ll_new();
3702 opt_inst->next_ctx_inst = NULL;
3703
3704 /* Update current_state. */
3705 /* """"""""""""""""""""" */
3706 cur_state->cur_opt_params = opt->params;
3707
3708 /* Priority option are inserted at the start of the opt_inst list */
3709 /* but their order of appearance in the context definition must */
3710 /* be preserver so each new priority option will be placed after */
3711 /* the previous ones at the start of the opt_inst list. */
3712 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3713 if (!opt->eval_first)
3714 {
3715 /* Look if we have a registered dependency in the order of the */
3716 /* evaluation of two options. */
3717 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3718 if (opt->eval_before_list->len > 0)
3719 {
3720 ll_t * list = ctx_inst->opt_inst_list;
3721 ll_node_t * opt_inst_node;
3722
3723 ll_t * before_list = opt->eval_before_list;
3724 ll_node_t * before_node = before_list->head;
3725
3726 ll_node_t * target_node = NULL; /* If not NULL, the new node *
3727 | will be inserted before it. */
3728
3729 /* For each entry in eval_before_list, try to find if it */
3730 /* refers to an option already entered in the context. If this */
3731 /* is the case, insert it just before it instead of putting it */
3732 /* at the end. */
3733 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3734 while (before_node != NULL)
3735 {
3736 opt_inst_node = list->head;
3737
3738 while (opt_inst_node != target_node)
3739 {
3740 opt_t * tmp_opt = (((opt_inst_t *)opt_inst_node->data))->opt;
3741
3742 /* We have found an option mentioned if the before_list */
3743 /* of the option we want to add. We can stop searching. */
3744 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3745 if (strcmp(tmp_opt->name, ((opt_t *)before_node->data)->name))
3746 opt_inst_node = opt_inst_node->next;
3747 else
3748 target_node = opt_inst_node; /* Set the target node. */
3749 }
3750
3751 before_node = before_node->next;
3752 }
3753
3754 /* Insert or append ? */
3755 /* """""""""""""""""" */
3756 if (target_node != NULL)
3757 ll_insert_before(ctx_inst->opt_inst_list, target_node, opt_inst);
3758 else
3759 ll_append(ctx_inst->opt_inst_list, opt_inst);
3760 }
3761 else
3762 ll_append(ctx_inst->opt_inst_list, opt_inst);
3763 }
3764 else
3765 {
3766 ll_node_t * opt_inst_node = ctx_inst->opt_inst_list->head;
3767 opt_inst_t * tmp_opt_inst;
3768
3769 while (opt_inst_node != NULL)
3770 {
3771 tmp_opt_inst = opt_inst_node->data;
3772 if (!tmp_opt_inst->opt->eval_first)
3773 {
3774 ll_insert_before(ctx_inst->opt_inst_list, opt_inst_node,
3775 opt_inst);
3776 break;
3777 }
3778 else
3779 opt_inst_node = opt_inst_node->next;
3780 }
3781 if (opt_inst_node == NULL)
3782 ll_append(ctx_inst->opt_inst_list, opt_inst);
3783 }
3784
3785 /* Check if an option was already seen in the */
3786 /* current context instance. */
3787 /* """""""""""""""""""""""""""""""""""""""""" */
3788 seen_opt.opt = opt;
3789
3790 bst_node = bst_find(&seen_opt, &(ctx_inst->seen_opt_bst),
3791 seen_opt_compare);
3792
3793 /* bst_node cannot be NULL here. */
3794
3795 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3796
3797 if (!opt->multiple && bst_seen_opt->seen == 1)
3798 fatal(CTXOPTDUPOPT, "");
3799
3800 /* Check if this option is compatible with the options already */
3801 /* seen in this context instance. */
3802 /* Look if the option is present in one on the BST present in */
3803 /* the incomp_bst_list of the context instance. */
3804 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3805 node = ctx_inst->incomp_bst_list->head;
3806 while (node != NULL)
3807 {
3808 bst = node->data;
3809 user_object = NULL;
3810
3811 /* There can only have one seen_opt object in the BST tree was */
3812 /* already seen, try to locate it, the result will be put in */
3813 /* user_object by the bst_seen_opt_seen_cb function. */
3814 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3815 bst_walk(bst, bst_seen_opt_seen_cb);
3816
3817 /* If it is the case, look if the current option is also */
3818 /* in this BST. */
3819 /* """"""""""""""""""""""""""""""""""""""""""""""""""""" */
3820 if (user_object != NULL)
3821 {
3822 bst_node = bst_find(bst_seen_opt, &bst, seen_opt_compare);
3823
3824 if (bst_node != NULL)
3825 {
3826 bst_seen_opt = (seen_opt_t *)(bst_node->key);
3827 if (bst_seen_opt->seen == 0)
3828 fatal(CTXOPTINCOPT, (char *)user_object);
3829 }
3830 }
3831
3832 node = node->next;
3833 }
3834
3835 /* Mark this option as seen in the current context instance. */
3836 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3837 bst_seen_opt->seen = 1;
3838 free(bst_seen_opt->par);
3839 bst_seen_opt->par = xstrdup(par_name);
3840
3841 /* If this option leads to a next context, create a new ctx_inst */
3842 /* and switch to it for the analyse of the future parameter. */
3843 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3844 if (opt->next_ctx != NULL)
3845 {
3846 ctx = locate_ctx(opt->next_ctx);
3847
3848 if (ctx == NULL)
3849 fatal_internal("Unknown context %s.", opt->next_ctx);
3850
3851 opt_inst->next_ctx_inst = ctx_inst = new_ctx_inst(ctx, ctx_inst);
3852 ctx_inst->par_name = xstrdup(par_name);
3853
3854 ll_append(ctx_inst_list, ctx_inst);
3855
3856 if (ctxopt_debug)
3857 fprintf(stderr,
3858 "CTXOPT_DEBUG: Context change, "
3859 "new current context: %s.\n",
3860 ctx->name);
3861 }
3862
3863 /* Look is we must expect some arguments. */
3864 /* """""""""""""""""""""""""""""""""""""" */
3865 expect_par_or_arg = 0;
3866 expect_par = 0;
3867 expect_arg = 0;
3868
3869 if (!opt->args)
3870 expect_par = 1; /* Parameter doesn't accept any argument. */
3871 else
3872 {
3873 if (!opt->optional_args)
3874 expect_arg = 1; /* Parameter has mandatory arguments. */
3875 else
3876 expect_par_or_arg = 1; /* Parameter has optional arguments. */
3877 }
3878 }
3879 }
3880 else if (expect_par && *par_name != '-')
3881 {
3882 ll_node_t * n = cli_node->next;
3883
3884 if (!flags.stop_if_non_option)
3885 /* Look if potential arguments must still be analyzed until the */
3886 /* end of the context/command line part to analyze/command line. */
3887 /* If this is the case we have met an extra argument. */
3888 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3889 while (n != NULL)
3890 {
3891 if (strcmp(n->data, "--") == 0 || strcmp(n->data, "\x1d") == 0)
3892 fatal(CTXOPTUNXARG, "");
3893
3894 if (*(char *)(n->data) == '-')
3895 fatal(CTXOPTUNXARG, "");
3896
3897 n = n->next;
3898 }
3899
3900 break; /* An unexpected non parameter was seen, if no Potential *
3901 | arguments remain in the command line or *
3902 | flags.stop_if_non_option is set, assume that it is is *
3903 | the first of the non arguments and stop the command *
3904 | line analysis. */
3905 }
3906 else if (expect_arg && *par_name != '-')
3907 {
3908 ll_node_t * cstr_node;
3909 constraint_t * cstr;
3910
3911 if (ctxopt_debug)
3912 fprintf(stderr, "CTXOPT_DEBUG: Argument: %s.\n", par_name);
3913
3914 /* Check if the arguments of the option respects */
3915 /* the attached constraints if any. */
3916 /* """"""""""""""""""""""""""""""""""""""""""""" */
3917 cstr_node = opt->constraints_list->head;
3918 while (cstr_node != NULL)
3919 {
3920 cstr = cstr_node->data;
3921 if (!cstr->constraint(cstr->nb_args, cstr->args, par_name,
3922 cur_state->cur_opt_par_name))
3923 {
3924 fputs("\n", stderr);
3925 ctxopt_ctx_disp_usage(cur_state->ctx_name, exit_after);
3926 }
3927
3928 cstr_node = cstr_node->next;
3929 }
3930
3931 /* If the argument is valid, store it. */
3932 /* """"""""""""""""""""""""""""""""""" */
3933 if (*par_name == '\\' && *(par_name + 1) == '-')
3934 ll_append(opt_inst->values_list, par_name + 1);
3935 else
3936 ll_append(opt_inst->values_list, par_name);
3937
3938 expect_arg = 0;
3939 expect_par = 0;
3940 expect_par_or_arg = 0;
3941
3942 if (opt->multiple_args)
3943 expect_par_or_arg = 1;
3944 else
3945 expect_par = 1; /* Parameter takes only one argument. */
3946 }
3947 else if (expect_arg && *par_name == '-')
3948 fatal(CTXOPTMISARG, "");
3949 else if (expect_par_or_arg)
3950 {
3951 expect_arg = 0;
3952 expect_par = 0;
3953 expect_par_or_arg = 0;
3954
3955 if (*par_name != '-')
3956 expect_arg = 1; /* Consider this word as an argument and retry. */
3957 else
3958 expect_par = 1; /* Consider this word as a parameter and retry. */
3959
3960 cli_node = cli_node->prev;
3961 }
3962
3963 cli_node = cli_node->next;
3964 }
3965
3966 if (cmdline_list->len > 0 && par_name && *par_name == '-')
3967 {
3968 if (expect_arg && opt && !opt->optional_args)
3969 fatal(CTXOPTMISARG, "");
3970 }
3971
3972 /* Look if a context_instance has unseen mandatory options. */
3973 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3974 node = ctx_inst_list->head;
3975 while (node != NULL)
3976 {
3977 ctx_inst = node->data;
3978
3979 /* Update current_state. */
3980 /* """"""""""""""""""""" */
3981 cur_state->ctx_name = ctx_inst->ctx->name;
3982 cur_state->ctx_par_name = ctx_inst->par_name;
3983
3984 check_for_missing_mandatory_opt(ctx_inst, par_name);
3985 check_for_occurrence_issues(ctx_inst);
3986 check_for_requirement_issues(ctx_inst);
3987
3988 node = node->next;
3989 }
3990
3991 /* Allocate the array containing the remaining not analyzed */
3992 /* command line arguments. */
3993 /* NOTE: The strings in the array are just pointer to the */
3994 /* data of the generating list and must not be freed. */
3995 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
3996 if (cli_node != NULL)
3997 {
3998 if (strcmp((char *)cli_node->data, "--") == 0)
3999 /* The special parameter -- was encountered, the -- argument is not */
4000 /* put in the remaining arguments. */
4001 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4002 ll_strarray(cmdline_list, cli_node->next, nb_rem_args, rem_args);
4003 else
4004 /* A non parameter was encountered when a parameter was expected. We */
4005 /* assume that the evaluation of the remaining command line argument */
4006 /* are not the responsibility of the users code. */
4007 /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
4008 ll_strarray(cmdline_list, cli_node, nb_rem_args, rem_args);
4009 }
4010 else
4011 {
4012 *nb_rem_args = 0;
4013 *rem_args = xmalloc(sizeof(char *));
4014 (*rem_args)[0] = NULL;
4015 }
4016 }
4017
4018 /* ==================================================== */
4019 /* Free ctxopt memory used for its internal structures. */
4020 /* ==================================================== */
4021 void
ctxopt_free_memory(void)4022 ctxopt_free_memory(void)
4023 {
4024 ll_destroy(cmdline_list, free);
4025 ll_destroy(ctx_inst_list, ctx_inst_free);
4026 bst_destroy(options_bst, opt_free);
4027 bst_destroy(contexts_bst, ctx_free);
4028 }
4029
4030 /* ==================================================================== */
4031 /* Parse the options data structures and launches the callback function */
4032 /* attached to each options instances. */
4033 /* This calls a recursive function which proceeds context per context. */
4034 /* ==================================================================== */
4035 void
ctxopt_evaluate(void)4036 ctxopt_evaluate(void)
4037 {
4038 evaluate_ctx_inst(first_ctx_inst);
4039 }
4040
4041 /* =================================================================== */
4042 /* Recursive function called by ctxopt_evaluate to process the list of */
4043 /* the opt_inst present in a ctx_inst and attempt to evaluate the */
4044 /* action attached to the context and its option instances. */
4045 /* =================================================================== */
4046 static void
evaluate_ctx_inst(ctx_inst_t * ctx_inst)4047 evaluate_ctx_inst(ctx_inst_t * ctx_inst)
4048 {
4049 opt_inst_t * opt_inst;
4050 ctx_t * ctx;
4051 opt_t * opt;
4052 ll_node_t * opt_inst_node;
4053 char ** args;
4054 int nb_args;
4055
4056 if (ctx_inst == NULL)
4057 return;
4058
4059 ctx = ctx_inst->ctx;
4060
4061 /* Do not evaluate the action attached to this context is there is no */
4062 /* option to evaluate. */
4063 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4064 opt_inst_node = ctx_inst->opt_inst_list->head;
4065 if (opt_inst_node == NULL)
4066 return;
4067
4068 /* Call the entering action attached to this context if any. */
4069 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4070 if (ctx->action != NULL)
4071 {
4072 if (ctx_inst->prev_ctx_inst != NULL)
4073 ctx->action(ctx->name, entering, ctx_inst->prev_ctx_inst->ctx->name,
4074 ctx->nb_data, ctx->data);
4075 else
4076 ctx->action(ctx->name, entering, NULL, ctx->nb_data, ctx->data);
4077 }
4078
4079 /* For each instance of options. */
4080 /* """"""""""""""""""""""""""""" */
4081 while (opt_inst_node != NULL)
4082 {
4083 opt_inst = (opt_inst_t *)(opt_inst_node->data);
4084 ll_strarray(opt_inst->values_list, opt_inst->values_list->head, &nb_args,
4085 &args);
4086 opt = opt_inst->opt;
4087
4088 /* Launch the attached action if any. */
4089 /* """""""""""""""""""""""""""""""""" */
4090 if (opt->action != NULL)
4091 opt->action(ctx->name, opt->name, opt_inst->par, nb_args, args,
4092 opt->nb_data, opt->data, ctx->nb_data, ctx->data);
4093
4094 if (opt_inst->next_ctx_inst != NULL)
4095 evaluate_ctx_inst(opt_inst->next_ctx_inst);
4096
4097 if (args != NULL)
4098 free(args);
4099
4100 opt_inst_node = opt_inst_node->next;
4101 }
4102
4103 /* Call the exiting action attached to this context if any. */
4104 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4105 if (ctx->action != NULL)
4106 {
4107 if (ctx_inst->prev_ctx_inst != NULL)
4108 ctx->action(ctx->name, exiting, ctx_inst->prev_ctx_inst->ctx->name,
4109 ctx->nb_data, ctx->data);
4110 else
4111 ctx->action(ctx->name, exiting, NULL, ctx->nb_data, ctx->data);
4112 }
4113 }
4114
4115 /* ============================================================ */
4116 /* Create and initializes a new context. */
4117 /* - allocate space. */
4118 /* - name it. */
4119 /* - initialize its option with a few of their characteristics. */
4120 /* ============================================================ */
4121 void
ctxopt_new_ctx(char * name,char * opts_specs)4122 ctxopt_new_ctx(char * name, char * opts_specs)
4123 {
4124 ctx_t * ctx;
4125 char * p;
4126
4127 if (!ctxopt_initialized)
4128 fatal_internal("Please call ctxopt_init first.");
4129
4130 ctx = xmalloc(sizeof(ctx_t));
4131
4132 /* Validates the context name: */
4133 /* ALPHA+(ALPHANUM|_)* */
4134 /* """"""""""""""""""""""""""" */
4135 p = name;
4136 if (!isalpha(*p))
4137 fatal_internal("A context name must start with a letter: %s.", name);
4138
4139 p++;
4140 while (*p)
4141 {
4142 if (!isalnum(*p) && *p != '_')
4143 fatal_internal("A context name must only contain letters, "
4144 "numbers or '_': %s.",
4145 name);
4146 p++;
4147 }
4148
4149 ctx->name = xstrdup(name);
4150 ctx->opt_list = ll_new(); /* List of options legit in this context. */
4151 ctx->incomp_list = ll_new(); /* List of incompatible options strings. */
4152 ctx->req_list = ll_new(); /* List of opts/required opts tuples (str). */
4153 ctx->par_bst = NULL;
4154 ctx->data = NULL;
4155 ctx->action = NULL;
4156
4157 /* The first created context is the main one. */
4158 /* """""""""""""""""""""""""""""""""""""""""" */
4159 if (contexts_bst == NULL)
4160 {
4161 main_ctx = ctx;
4162
4163 cur_state->ctx_name = ctx->name;
4164 }
4165
4166 if (init_opts(opts_specs, ctx) == 0)
4167 exit(EXIT_FAILURE);
4168 if (bst_find(ctx, &contexts_bst, ctx_compare) != NULL)
4169 fatal_internal("The context %s already exists.", name);
4170 else
4171 bst_search(ctx, &contexts_bst, ctx_compare);
4172 }
4173
4174 /* ==================================================== */
4175 /* Display a usage screen limited to a specific context */
4176 /* IN: the context name. */
4177 /* IN: what to do after (continue or exit the program) */
4178 /* possible values: continue_after, exit_after. */
4179 /* ==================================================== */
4180 void
ctxopt_ctx_disp_usage(char * ctx_name,usage_behaviour action)4181 ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
4182 {
4183 ctx_t * ctx;
4184 ll_t * list;
4185
4186 int has_optional = 0;
4187 int has_ellipsis = 0;
4188 int has_rule = 0;
4189 int has_generic_arg = 0;
4190 int has_ctx_change = 0;
4191 int has_early_eval = 0;
4192
4193 if (!flags.display_usage_on_error)
4194 return;
4195
4196 ctx = locate_ctx(ctx_name);
4197 if (ctx == NULL)
4198 fatal_internal("Unknown context %s.", ctx_name);
4199
4200 if (cur_state->ctx_par_name == NULL)
4201 printf("\nSynopsis:\n%s \\\n", cur_state->prog_name);
4202 else
4203 printf("\nSynopsis for the context introduced by %s:\n",
4204 cur_state->ctx_par_name);
4205
4206 list = ctx->opt_list;
4207 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4208 &has_ctx_change, &has_early_eval);
4209
4210 print_before_constraints(list);
4211
4212 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4213 has_optional, has_ellipsis, has_rule);
4214
4215 if (action == exit_after)
4216 exit(EXIT_FAILURE);
4217 }
4218
4219 /* =================================================== */
4220 /* Display a full usage screen about all contexts. */
4221 /* IN: what to do after (continue or exit the program) */
4222 /* possible values: continue_after, exit_after. */
4223 /* =================================================== */
4224 void
ctxopt_disp_usage(usage_behaviour action)4225 ctxopt_disp_usage(usage_behaviour action)
4226 {
4227 ll_t * list;
4228 int has_optional = 0;
4229 int has_ellipsis = 0;
4230 int has_rule = 0;
4231 int has_generic_arg = 0;
4232 int has_ctx_change = 0;
4233 int has_early_eval = 0;
4234
4235 if (!flags.display_usage_on_error)
4236 return;
4237
4238 if (main_ctx == NULL)
4239 fatal_internal("At least one context must have been created.");
4240
4241 /* Usage for the first context. */
4242 /* """""""""""""""""""""""""""" */
4243 printf("\nAllowed options in the base context:\n");
4244 list = main_ctx->opt_list;
4245 print_options(list, &has_optional, &has_ellipsis, &has_rule, &has_generic_arg,
4246 &has_ctx_change, &has_early_eval);
4247
4248 /* Dependency constraints between options. */
4249 /* """"""""""""""""""""""""""""""""""""""" */
4250 print_before_constraints(list);
4251
4252 /* Usage for the other contexts. */
4253 /* """"""""""""""""""""""""""""" */
4254 bst_walk(contexts_bst, bst_print_ctx_cb);
4255
4256 /* Contextual syntactic explanations. */
4257 /* """""""""""""""""""""""""""""""""" */
4258 print_explanations(has_early_eval, has_ctx_change, has_generic_arg,
4259 has_optional, has_ellipsis, has_rule);
4260
4261 if (action == exit_after)
4262 exit(EXIT_FAILURE);
4263 }
4264
4265 /* ************************************ */
4266 /* Built-in constraint check functions. */
4267 /* ************************************ */
4268
4269 /* ============================================================= */
4270 /* This constraint checks if each arguments respects a format as */
4271 /* defined for the scanf function. */
4272 /* return 1 if yes and 0 if no. */
4273 /* ============================================================= */
4274 int
ctxopt_format_constraint(int nb_args,char ** args,char * value,char * par)4275 ctxopt_format_constraint(int nb_args, char ** args, char * value, char * par)
4276 {
4277 int rc = 0;
4278
4279 char x[256];
4280 char y;
4281 char * format;
4282
4283 if (nb_args != 1)
4284 fatal_internal("Format constraint, invalid number of parameters.");
4285
4286 if (strlen(value) > 255)
4287 value[255] = '\0';
4288
4289 format = xstrdup(args[0]);
4290 format = strappend(format, "%c", (char *)0);
4291
4292 rc = sscanf(value, format, x, &y);
4293 if (rc != 1)
4294 fprintf(stderr,
4295 "The argument %s of %s does not respect the imposed format %s.",
4296 value, par, args[0]);
4297
4298 free(format);
4299
4300 return rc == 1;
4301 }
4302
4303 /* ================================================================== */
4304 /* This constraint checks if each arguments of the option instance is */
4305 /* between a minimum and a maximum (inclusive). */
4306 /* return 1 if yes and 0 if no. */
4307 /* ================================================================== */
4308 int
ctxopt_re_constraint(int nb_args,char ** args,char * value,char * par)4309 ctxopt_re_constraint(int nb_args, char ** args, char * value, char * par)
4310 {
4311 regex_t re;
4312
4313 if (nb_args != 1)
4314 fatal_internal(
4315 "Regular expression constraint, invalid number of parameters.");
4316
4317 if (regcomp(&re, args[0], REG_EXTENDED) != 0)
4318 fatal_internal("Invalid regular expression %s.", args[0]);
4319
4320 if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
4321 {
4322 fprintf(stderr,
4323 "The argument %s of %s doesn't match the constraining "
4324 "regular expression %s.",
4325 value, par, args[0]);
4326 return 0;
4327 }
4328
4329 regfree(&re);
4330
4331 return 1;
4332 }
4333
4334 /* ================================================================== */
4335 /* This constraint checks if each arguments of the option instance is */
4336 /* between a minimum and a maximum (inclusive). */
4337 /* return 1 if yes and 0 if no. */
4338 /* ================================================================== */
4339 int
ctxopt_range_constraint(int nb_args,char ** args,char * value,char * par)4340 ctxopt_range_constraint(int nb_args, char ** args, char * value, char * par)
4341 {
4342 long min, max;
4343 char c;
4344 char * ptr;
4345 int n;
4346 long v;
4347 int min_only = 0;
4348 int max_only = 0;
4349
4350 if (nb_args != 2)
4351 fatal_internal("Range constraint, invalid number of parameters.");
4352
4353 if (strcmp(args[0], ".") == 0)
4354 max_only = 1;
4355 else
4356 n = sscanf(args[0], "%ld%c", &min, &c);
4357
4358 if (!max_only && n != 1)
4359 fatal_internal("Range constraint, min: invalid parameters.");
4360
4361 if (strcmp(args[1], ".") == 0)
4362 min_only = 1;
4363 else
4364 n = sscanf(args[1], "%ld%c", &max, &c);
4365
4366 if (!min_only && n != 1)
4367 fatal_internal("Range constraint, max: invalid parameters.");
4368
4369 if (min_only && max_only)
4370 fatal_internal("Range constraint, invalid parameters.");
4371
4372 errno = 0;
4373 v = strtol(value, &ptr, 10);
4374 if (errno || ptr == value)
4375 return 0;
4376
4377 if (min_only)
4378 {
4379 if (v < min)
4380 {
4381 fprintf(stderr,
4382 "The argument %ld of %s is not greater than or equal to %ld.", v,
4383 par, min);
4384 return 0;
4385 }
4386 else
4387 return 1;
4388 }
4389 else if (max_only)
4390 {
4391 if (v > max)
4392 {
4393 fprintf(stderr,
4394 "The argument %ld of %s is not less than or equal to %ld.", v,
4395 par, max);
4396 return 0;
4397 }
4398 else
4399 return 1;
4400 }
4401 else if (v < min || v > max)
4402 {
4403 fprintf(stderr, "The argument %ld of %s is not between %ld and %ld.", v,
4404 par, min, max);
4405 return 0;
4406 }
4407
4408 return 1; /* Check passed. */
4409 }
4410
4411 /* =============================================================== */
4412 /* This function provides a way to set the behaviour of a context. */
4413 /* =============================================================== */
4414 void
ctxopt_add_global_settings(settings s,...)4415 ctxopt_add_global_settings(settings s, ...)
4416 {
4417 va_list(args);
4418 va_start(args, s);
4419
4420 switch (s)
4421 {
4422 case error_functions:
4423 {
4424 typedef void fn(errors e, state_t * state);
4425
4426 void (*function)(errors e, state_t * state);
4427
4428 errors e;
4429 e = va_arg(args, errors);
4430 function = va_arg(args, fn *);
4431 err_functions[e] = function;
4432 break;
4433 }
4434
4435 default:
4436 break;
4437 }
4438 va_end(args);
4439 }
4440
4441 /* ================================================================ */
4442 /* This function provides a way to set the behaviour of an option. */
4443 /* It can take a variable number of arguments according to its */
4444 /* first argument: */
4445 /* - parameter: */
4446 /* o a string containing an option name and all its possible */
4447 /* parameters separates by spaces, tabs or commas (char *) */
4448 /* (e.g: "help -h -help"). */
4449 /* - actions: */
4450 /* o a string containing an option name. */
4451 /* o a pointer to a function which will be called at evaluation */
4452 /* time. */
4453 /* - constraints: */
4454 /* o a string containing an option name. */
4455 /* o a pointer to a function to check if an argument is valid. */
4456 /* o a strings containing the arguments to this function. */
4457 /* ================================================================ */
4458 void
ctxopt_add_opt_settings(settings s,...)4459 ctxopt_add_opt_settings(settings s, ...)
4460 {
4461 opt_t * opt = NULL;
4462 void * ptr = NULL;
4463
4464 va_list(args);
4465 va_start(args, s);
4466
4467 switch (s)
4468 {
4469 /* This part associates some command line parameters to an option. */
4470 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4471 case parameters:
4472 {
4473 char * opt_name;
4474 char * params;
4475
4476 /* The second argument must be a string containing: */
4477 /* - The name of an existing option. */
4478 /* - a list of parameters with a leading dash (-). */
4479 /* """""""""""""""""""""""""""""""""""""""""""""""" */
4480 ptr = va_arg(args, char *);
4481 opt_name = ptr;
4482
4483 if (opt_name != NULL)
4484 {
4485 if ((opt = locate_opt(opt_name)) != NULL)
4486 {
4487 ptr = va_arg(args, char *);
4488 params = ptr;
4489
4490 if (!opt_set_parms(opt_name, params))
4491 fatal_internal(
4492 "Duplicated parameters or bad settings for the option %s.",
4493 params);
4494 }
4495 else
4496 fatal_internal("Unknown option %s.", opt_name);
4497 }
4498 else
4499 fatal_internal(
4500 "ctxopt_opt_add_settings: parameters: not enough arguments.");
4501
4502 /* Here opt is a known option. */
4503 /* """"""""""""""""""""""""""" */
4504 if (opt->params != NULL)
4505 fatal_internal("Parameters are already set for %s.", opt_name);
4506 else
4507 {
4508 size_t n;
4509 size_t l = strlen(params);
4510
4511 opt->params = xstrdup(params);
4512 while ((n = strcspn(opt->params, " \t")) < l)
4513 opt->params[n] = '|';
4514 }
4515
4516 break;
4517 }
4518
4519 /* This part associates a callback function to an option. */
4520 /* This function will be called when an instance of an option */
4521 /* is evaluated. */
4522 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4523 case actions:
4524 {
4525 void * data;
4526 void (*function)();
4527 int nb_data = 0;
4528
4529 /* The second argument must be the name of an existing option. */
4530 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4531 ptr = va_arg(args, char *);
4532
4533 if ((opt = locate_opt(ptr)) != NULL)
4534 {
4535 typedef void fn(char *, char *, char *, int, char **, int, void *, int,
4536 void **);
4537
4538 /* The third argument must be the callback function. */
4539 /* """"""""""""""""""""""""""""""""""""""""""""""""" */
4540 function = va_arg(args, fn *);
4541 opt->action = function;
4542
4543 /* The fourth argument must be a pointer to an user's defined */
4544 /* variable or structure that the previous function can manage. */
4545 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4546 while ((data = va_arg(args, void *)) != NULL)
4547 {
4548 nb_data++;
4549 opt->data = xrealloc(opt->data, nb_data * sizeof(void *));
4550 opt->data[nb_data - 1] = data;
4551 }
4552 opt->nb_data = nb_data;
4553 }
4554 else
4555 fatal_internal("Unknown option %s.", ptr);
4556 break;
4557 }
4558
4559 /* This part associates a list of functions to control some */
4560 /* characteristics of the arguments of an option. */
4561 /* Each function will be called in order and must return 1 */
4562 /* to validate the arguments. */
4563 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4564 case constraints:
4565 {
4566 char * value;
4567 constraint_t * cstr;
4568 int (*function)();
4569
4570 /* The second argument must be a string. */
4571 /* """"""""""""""""""""""""""""""""""""" */
4572 ptr = va_arg(args, char *);
4573
4574 if ((opt = locate_opt(ptr)) != NULL)
4575 {
4576 typedef int fn(int, char **, char *);
4577
4578 /* The third argument must be a function. */
4579 /* """""""""""""""""""""""""""""""""""""" */
4580 function = va_arg(args, fn *);
4581
4582 cstr = xmalloc(sizeof(constraint_t));
4583 cstr->constraint = function;
4584
4585 /* The fourth argument must be a string containing the argument of */
4586 /* The previous function separated by spaces or tabs. */
4587 /* Theses arguments will be passed to the previous function */
4588 /* max: 32 argument! */
4589 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4590 value = xstrdup(va_arg(args, char *));
4591
4592 cstr->to_free = value;
4593 cstr->args = xcalloc(sizeof(char *), 32);
4594 cstr->nb_args = str2argv(value, cstr->args, 32);
4595 ll_append(opt->constraints_list, cstr);
4596 }
4597 else
4598 fatal_internal("Unknown option %s.", ptr);
4599 break;
4600 }
4601
4602 /* This part allows to indicate that an option must be evaluated */
4603 /* after a list of other options. */
4604 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4605 case after:
4606 {
4607 char * str;
4608
4609 /* The second argument must be a string. */
4610 /* """"""""""""""""""""""""""""""""""""" */
4611 ptr = va_arg(args, char *);
4612
4613 if ((opt = locate_opt(ptr)) != NULL)
4614 {
4615 char * end_str;
4616 char * opt_name;
4617 opt_t * opt_before;
4618
4619 ptr = va_arg(args, char *);
4620
4621 str = xstrdup(ptr);
4622 ltrim(str, " \t");
4623 rtrim(str, " \t", 0);
4624
4625 /* Feed the list of options to be evaluated after the given option. */
4626 /* This list will contain pointers to options. */
4627 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4628 opt_name = xstrtok_r(str, " \t,", &end_str);
4629 if (opt_name != NULL)
4630 {
4631 if ((opt_before = locate_opt(opt_name)) != NULL)
4632 {
4633 ll_append(opt->eval_before_list, opt_before);
4634 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4635 {
4636 if ((opt_before = locate_opt(opt_name)) != NULL)
4637 ll_append(opt->eval_before_list, opt_before);
4638 else
4639 fatal_internal("Unknown option %s.", opt_name);
4640 }
4641 }
4642 else
4643 fatal_internal("Unknown option %s.", opt_name);
4644 }
4645 else
4646 fatal_internal("Not enough options to be evaluated after %s.",
4647 opt->name);
4648
4649 free(str);
4650 }
4651 else
4652 fatal_internal("Unknown option %s.", ptr);
4653
4654 break;
4655 }
4656
4657 /* This part allows to indicate that an option must be evaluated */
4658 /* before a list of other options. */
4659 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4660 case before:
4661 {
4662 char * str;
4663
4664 /* The second argument must be a string. */
4665 /* """"""""""""""""""""""""""""""""""""" */
4666 ptr = va_arg(args, char *);
4667
4668 if ((opt = locate_opt(ptr)) != NULL)
4669 {
4670 char * end_str;
4671 char * opt_name;
4672 opt_t * opt_before;
4673
4674 ptr = va_arg(args, char *);
4675
4676 str = xstrdup(ptr);
4677 ltrim(str, " \t");
4678 rtrim(str, " \t", 0);
4679
4680 /* Feed the list of options to be evaluated before the given option. */
4681 /* This list will contain pointers to options. */
4682 /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4683 opt_name = xstrtok_r(str, " \t,", &end_str);
4684 if (opt_name != NULL)
4685 {
4686 if ((opt_before = locate_opt(opt_name)) != NULL)
4687 {
4688 ll_append(opt_before->eval_before_list, opt);
4689 while ((opt_name = xstrtok_r(NULL, " \t,", &end_str)) != NULL)
4690 {
4691 if ((opt_before = locate_opt(opt_name)) != NULL)
4692 ll_append(opt_before->eval_before_list, opt);
4693 else
4694 fatal_internal("Unknown option %s.", opt_name);
4695 }
4696 }
4697 else
4698 fatal_internal("Unknown option %s.", opt_name);
4699 }
4700 else
4701 fatal_internal("Not enough options to be evaluated before %s.",
4702 opt->name);
4703
4704 free(str);
4705 }
4706 else
4707 fatal_internal("Unknown option %s.", ptr);
4708
4709 break;
4710 }
4711
4712 default:
4713 break;
4714 }
4715 va_end(args);
4716 }
4717
4718 /* =============================================================== */
4719 /* This function provides a way to set the behaviour of a context. */
4720 /* =============================================================== */
4721 void
ctxopt_add_ctx_settings(settings s,...)4722 ctxopt_add_ctx_settings(settings s, ...)
4723 {
4724 ctx_t * ctx;
4725
4726 va_list(args);
4727 va_start(args, s);
4728
4729 switch (s)
4730 {
4731 /* Add a set of mutually incompatible options in a context. */
4732 /* """""""""""""""""""""""""""""""""""""""""""""""""""""""" */
4733 case incompatibilities:
4734 {
4735 void * ptr;
4736 ll_t * list;
4737 size_t n;
4738 char * str;
4739
4740 ptr = va_arg(args, char *);
4741 if ((ctx = locate_ctx(ptr)) != NULL)
4742 {
4743 ptr = va_arg(args, char *);
4744 list = ctx->incomp_list;
4745
4746 str = xstrdup(ptr);
4747 ltrim(str, " \t");
4748 rtrim(str, " \t", 0);
4749
4750 n = strcspn(str, " \t");
4751 if (n > 0 && n < strlen(str))
4752 ll_append(list, str);
4753 else
4754 fatal_internal(
4755 "Not enough incompatible options in the string: \"%s\".", str);
4756 }
4757 else
4758 fatal_internal("Unknown context %s.", ptr);
4759 break;
4760 }
4761
4762 case requirements:
4763 {
4764 void * ptr;
4765 ll_t * list;
4766 size_t n;
4767 char * str;
4768
4769 ptr = va_arg(args, char *);
4770 if ((ctx = locate_ctx(ptr)) != NULL)
4771 {
4772 ptr = va_arg(args, char *);
4773 list = ctx->req_list;
4774
4775 str = xstrdup(ptr);
4776 ltrim(str, " \t");
4777 rtrim(str, " \t", 0);
4778
4779 n = strcspn(str, " \t");
4780 if (n > 0 && n < strlen(str))
4781 ll_append(list, str);
4782 else
4783 fatal_internal("Not enough required options in the string: \"%s\".",
4784 str);
4785 }
4786 else
4787 fatal_internal("Unknown context %s.", ptr);
4788 break;
4789 }
4790
4791 /* Add functions which will be called when */
4792 /* entering and exiting a context. */
4793 /* """"""""""""""""""""""""""""""""""""""" */
4794 case actions:
4795 {
4796 void * ptr;
4797 void * data;
4798 int (*function)();
4799 int nb_data = 0;
4800
4801 ptr = va_arg(args, char *);
4802 if ((ctx = locate_ctx(ptr)) != NULL)
4803 {
4804 typedef int fn(char *, direction, char *, int, void **);
4805
4806 function = va_arg(args, fn *);
4807 ctx->action = function;
4808
4809 while ((data = va_arg(args, void *)) != NULL)
4810 {
4811 nb_data++;
4812 ctx->data = xrealloc(ctx->data, nb_data * sizeof(void *));
4813 ctx->data[nb_data - 1] = data;
4814 }
4815 ctx->nb_data = nb_data;
4816 }
4817 else
4818 fatal_internal("Unknown context %s.", ptr);
4819 break;
4820 }
4821
4822 default:
4823 break;
4824 }
4825 va_end(args);
4826 }
4827