GRASS GIS 8 Programmer's Manual 8.4.1(2025)-45ca3179ab
Loading...
Searching...
No Matches
parser_dependencies.c
Go to the documentation of this file.
1/*!
2 \file lib/gis/parser_dependencies.c
3
4 \brief GIS Library - Argument parsing functions (dependencies between
5 options)
6
7 (C) 2014-2015 by the GRASS Development Team
8
9 This program is free software under the GNU General Public License
10 (>=v2). Read the file COPYING that comes with GRASS for details.
11
12 \author Glynn Clements Jun. 2014
13 */
14
15#include <stdarg.h>
16#include <string.h>
17#include <stdio.h>
18
19#include <grass/gis.h>
20#include <grass/glocale.h>
21
22#include "parser_local_proto.h"
23
24struct vector {
25 size_t elsize;
26 size_t increment;
27 size_t count;
28 size_t limit;
29 void *data;
30};
31
32static void vector_new(struct vector *v, size_t elsize, size_t increment)
33{
34 v->elsize = elsize;
35 v->increment = increment;
36 v->count = 0;
37 v->limit = 0;
38 v->data = NULL;
39}
40
41static void vector_append(struct vector *v, const void *data)
42{
43 void *p;
44
45 if (v->count >= v->limit) {
46 v->limit += v->increment;
47 v->data = G_realloc(v->data, v->limit * v->elsize);
48 }
49
50 p = G_incr_void_ptr(v->data, v->count * v->elsize);
51 memcpy(p, data, v->elsize);
52 v->count++;
53}
54
55struct rule {
56 int type;
57 int count;
58 void **opts;
59};
60
61static struct vector rules = {.elsize = sizeof(struct rule), .increment = 50};
62
63/*! \brief Set generic option rule
64
65 Supported rule types:
66 - RULE_EXCLUSIVE
67 - RULE_REQUIRED
68 - RULE_REQUIRES
69 - RULE_REQUIRES_ALL
70 - RULE_EXCLUDES
71 - RULE_COLLECTIVE
72
73 \param type rule type
74 \param nopts number of options in the array
75 \param opts array of options
76 */
77void G_option_rule(int type, int nopts, void **opts)
78{
79 struct rule rule;
80
81 rule.type = type;
82 rule.count = nopts;
83 rule.opts = opts;
84
85 vector_append(&rules, &rule);
86}
87
88static void make_rule(int type, void *first, va_list ap)
89{
90 struct vector opts;
91 void *opt;
92
93 vector_new(&opts, sizeof(void *), 10);
94
95 opt = first;
96 vector_append(&opts, &opt);
97 for (;;) {
98 opt = va_arg(ap, void *);
99
100 if (!opt)
101 break;
102 vector_append(&opts, &opt);
103 }
104
105 G_option_rule(type, opts.count, (void **)opts.data);
106}
107
108static int is_flag(const void *p)
109{
110 if (st->n_flags) {
111 const struct Flag *flag;
112
113 for (flag = &st->first_flag; flag; flag = flag->next_flag)
114 if ((const void *)flag == p)
115 return 1;
116 }
117
118 if (st->n_opts) {
119 const struct Option *opt;
120
121 for (opt = &st->first_option; opt; opt = opt->next_opt)
122 if ((const void *)opt == p)
123 return 0;
124 }
125
126 G_fatal_error(_("Internal error: option or flag not found"));
127}
128
129static int is_present(const void *p)
130{
131 if (is_flag(p)) {
132 const struct Flag *flag = p;
133
134 return (int)flag->answer;
135 }
136 else {
137 const struct Option *opt = p;
138
139 return opt->count > 0;
140 }
141}
142
143static char *get_name(const void *p)
144{
145 if (is_flag(p)) {
146 char *s;
147
148 G_asprintf(&s, "-%c", ((const struct Flag *)p)->key);
149 return s;
150 }
151 else
152 return G_store(((const struct Option *)p)->key);
153}
154
155static int count_present(const struct rule *rule, int start)
156{
157 int i;
158 int count = 0;
159
160 for (i = start; i < rule->count; i++)
161 if (is_present(rule->opts[i]))
162 count++;
163
164 return count;
165}
166
167static const char *describe_rule(const struct rule *rule, int start,
168 int disjunction)
169{
170 char *s;
171 int i;
172
173 G_asprintf(&s, "<%s>", get_name(rule->opts[start]));
174
175 for (i = start + 1; i < rule->count - 1; i++) {
176 char *s0 = s;
177 char *ss = get_name(rule->opts[i]);
178
179 s = NULL;
180 G_asprintf(&s, "%s, <%s>", s0, ss);
181 G_free(s0);
182 G_free(ss);
183 }
184
185 if (rule->count - start > 1) {
186 char *s0 = s;
187 char *ss = get_name(rule->opts[i]);
188
189 s = NULL;
190 G_asprintf(&s, disjunction ? _("%s or <%s>") : _("%s and <%s>"), s0,
191 ss);
192 G_free(s0);
193 G_free(ss);
194 }
195
196 return s;
197}
198
199static void append_error(const char *msg)
200{
201 st->error = G_realloc(st->error, sizeof(char *) * (st->n_errors + 1));
202 st->error[st->n_errors++] = G_store(msg);
203}
204
205/*! \brief Sets the options to be mutually exclusive.
206
207 When running the module, at most one option from a set can be
208 provided.
209
210 The last item of the list must be NULL.
211
212 \param first first given option
213 */
214void G_option_exclusive(void *first, ...)
215{
216 va_list ap;
217
218 va_start(ap, first);
219 make_rule(RULE_EXCLUSIVE, first, ap);
220 va_end(ap);
221}
222
223static void check_exclusive(const struct rule *rule)
224{
225 if (count_present(rule, 0) > 1) {
226 char *err;
227
228 G_asprintf(&err, _("Options %s are mutually exclusive"),
229 describe_rule(rule, 0, 0));
230 append_error(err);
231 }
232}
233
234/*! \brief Sets the options to be required.
235
236 At least one option from a set must be given.
237
238 The last item of the list must be NULL.
239
240 \param first first given option
241 */
242void G_option_required(void *first, ...)
243{
244 va_list ap;
245
246 va_start(ap, first);
247 make_rule(RULE_REQUIRED, first, ap);
248 va_end(ap);
249}
250
251static void check_required(const struct rule *rule)
252{
253 if (count_present(rule, 0) < 1) {
254 char *err;
255
257 _("At least one of the following options is required: %s"),
258 describe_rule(rule, 0, 0));
259 append_error(err);
260 }
261}
262
263/*! \brief Define a list of options from which at least one option
264 is required if first option is present.
265
266 If the first option is present, at least one of the other
267 options must also be present.
268
269 The last item of the list must be NULL.
270
271 If you want all options to be provided use G_option_requires_all()
272 function.
273 If you want more than one option to be present but not all,
274 call this function multiple times.
275
276 \param first first given option
277 */
278void G_option_requires(void *first, ...)
279{
280 va_list ap;
281
282 va_start(ap, first);
283 make_rule(RULE_REQUIRES, first, ap);
284 va_end(ap);
285}
286
287static void check_requires(const struct rule *rule)
288{
289 if (!is_present(rule->opts[0]))
290 return;
291 if (count_present(rule, 1) < 1) {
292 char *err;
293
294 if (rule->count > 2)
295 G_asprintf(&err, _("Option <%s> requires at least one of %s"),
296 get_name(rule->opts[0]), describe_rule(rule, 1, 1));
297 else
298 G_asprintf(&err, _("Option <%s> requires %s"),
299 get_name(rule->opts[0]), describe_rule(rule, 1, 1));
300 append_error(err);
301 }
302}
303
304/*! \brief Define additionally required options for an option.
305
306 If the first option is present, all the other options must also
307 be present.
308
309 The last item of the list must be NULL.
310
311 If it is enough if only one option from a set is present,
312 use G_option_requires() function.
313
314 \see G_option_collective()
315
316 \param first first given option
317 */
318void G_option_requires_all(void *first, ...)
319{
320 va_list ap;
321
322 va_start(ap, first);
323 make_rule(RULE_REQUIRES_ALL, first, ap);
324 va_end(ap);
325}
326
327static void check_requires_all(const struct rule *rule)
328{
329 if (!is_present(rule->opts[0]))
330 return;
331 if (count_present(rule, 1) < rule->count - 1) {
332 char *err;
333
334 G_asprintf(&err, _("Option <%s> requires all of %s"),
335 get_name(rule->opts[0]), describe_rule(rule, 1, 0));
336 append_error(err);
337 }
338}
339
340/*! \brief Exclude selected options.
341
342 If the first option is present, none of the other options may also (should?)
343 be present.
344
345 The last item of the list must be NULL.
346
347 \param first first given option
348 */
349void G_option_excludes(void *first, ...)
350{
351 va_list ap;
352
353 va_start(ap, first);
354 make_rule(RULE_EXCLUDES, first, ap);
355 va_end(ap);
356}
357
358static void check_excludes(const struct rule *rule)
359{
360 if (!is_present(rule->opts[0]))
361 return;
362 if (count_present(rule, 1) > 0) {
363 char *err;
364
365 G_asprintf(&err, _("Option <%s> is mutually exclusive with all of %s"),
366 get_name(rule->opts[0]), describe_rule(rule, 1, 0));
367 append_error(err);
368 }
369}
370
371/*! \brief Sets the options to be collective.
372
373 If any option is present, all the other options must also be present
374 all or nothing from a set.
375
376 The last item of the list must be NULL.
377
378 \param first first given option
379 */
380void G_option_collective(void *first, ...)
381{
382 va_list ap;
383
384 va_start(ap, first);
385 make_rule(RULE_COLLECTIVE, first, ap);
386 va_end(ap);
387}
388
389static void check_collective(const struct rule *rule)
390{
391 int count = count_present(rule, 0);
392
393 if (count > 0 && count < rule->count) {
394 char *err;
395
396 G_asprintf(&err, _("Either all or none of %s must be given"),
397 describe_rule(rule, 0, 0));
398 append_error(err);
399 }
400}
401
402/*! \brief Check for option rules (internal use only) */
404{
405 unsigned int i;
406
407 for (i = 0; i < rules.count; i++) {
408 const struct rule *rule = &((const struct rule *)rules.data)[i];
409
410 switch (rule->type) {
411 case RULE_EXCLUSIVE:
412 check_exclusive(rule);
413 break;
414 case RULE_REQUIRED:
415 check_required(rule);
416 break;
417 case RULE_REQUIRES:
418 check_requires(rule);
419 break;
420 case RULE_REQUIRES_ALL:
421 check_requires_all(rule);
422 break;
423 case RULE_EXCLUDES:
424 check_excludes(rule);
425 break;
426 case RULE_COLLECTIVE:
427 check_collective(rule);
428 break;
429 default:
430 G_fatal_error(_("Internal error: invalid rule type: %d"),
431 rule->type);
432 break;
433 }
434 }
435}
436
437/*! \brief Describe option rules (stderr) */
439{
440 unsigned int i;
441
442 for (i = 0; i < rules.count; i++) {
443 const struct rule *rule = &((const struct rule *)rules.data)[i];
444
445 switch (rule->type) {
446 case RULE_EXCLUSIVE:
447 fprintf(stderr, "Exclusive: %s", describe_rule(rule, 0, 0));
448 break;
449 case RULE_REQUIRED:
450 fprintf(stderr, "Required: %s", describe_rule(rule, 0, 1));
451 break;
452 case RULE_REQUIRES:
453 fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
454 describe_rule(rule, 1, 1));
455 break;
456 case RULE_REQUIRES_ALL:
457 fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
458 describe_rule(rule, 1, 0));
459 break;
460 case RULE_EXCLUDES:
461 fprintf(stderr, "Excludes: %s => %s", get_name(rule->opts[0]),
462 describe_rule(rule, 1, 0));
463 break;
464 case RULE_COLLECTIVE:
465 fprintf(stderr, "Collective: %s", describe_rule(rule, 0, 0));
466 break;
467 default:
468 G_fatal_error(_("Internal error: invalid rule type: %d"),
469 rule->type);
470 break;
471 }
472 }
473}
474
475/*!
476 \brief Checks if there is any rule RULE_REQUIRED (internal use only).
477
478 \return 1 if there is such rule
479 \return 0 if not
480 */
482{
483 size_t i;
484
485 for (i = 0; i < rules.count; i++) {
486 const struct rule *rule = &((const struct rule *)rules.data)[i];
487
488 if (rule->type == RULE_REQUIRED)
489 return TRUE;
490 }
491 return FALSE;
492}
493
494static const char *const rule_types[] = {"exclusive", "required",
495 "requires", "requires-all",
496 "excludes", "collective"};
497
498/*! \brief Describe option rules in XML format (internal use only)
499
500 \param fp file where to print XML info
501 */
503{
504 unsigned int i, j;
505
506 if (!rules.count)
507 return;
508
509 fprintf(fp, "\t<rules>\n");
510 for (i = 0; i < rules.count; i++) {
511 const struct rule *rule = &((const struct rule *)rules.data)[i];
512
513 if (rule->count < 0)
514 G_fatal_error(_("Internal error: the number of options is < 0"));
515
516 fprintf(fp, "\t\t<rule type=\"%s\">\n", rule_types[rule->type]);
517 for (j = 0; j < (unsigned int)rule->count; j++) {
518 void *p = rule->opts[j];
519
520 if (is_flag(p)) {
521 const struct Flag *flag = (const struct Flag *)p;
522
523 fprintf(fp, "\t\t\t<rule-flag key=\"%c\"/>\n", flag->key);
524 }
525 else {
526 const struct Option *opt = (const struct Option *)p;
527
528 fprintf(fp, "\t\t\t<rule-option key=\"%s\"/>\n", opt->key);
529 }
530 }
531 fprintf(fp, "\t\t</rule>\n");
532 }
533 fprintf(fp, "\t</rules>\n");
534}
void * G_incr_void_ptr(const void *ptr, size_t size)
Advance void pointer.
Definition alloc.c:187
void G_free(void *buf)
Free allocated memory.
Definition alloc.c:150
int G_asprintf(char **out, const char *fmt,...)
Definition asprintf.c:69
#define NULL
Definition ccmath.h:32
#define TRUE
Definition dbfopen.c:75
#define FALSE
Definition dbfopen.c:74
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition gis/error.c:159
int count
void G__check_option_rules(void)
Check for option rules (internal use only)
void G_option_rule(int type, int nopts, void **opts)
Set generic option rule.
void G_option_collective(void *first,...)
Sets the options to be collective.
int G__has_required_rule(void)
Checks if there is any rule RULE_REQUIRED (internal use only).
void G__describe_option_rules(void)
Describe option rules (stderr)
void G_option_requires_all(void *first,...)
Define additionally required options for an option.
void G_option_excludes(void *first,...)
Exclude selected options.
void G_option_exclusive(void *first,...)
Sets the options to be mutually exclusive.
void G__describe_option_rules_xml(FILE *fp)
Describe option rules in XML format (internal use only)
void G_option_required(void *first,...)
Sets the options to be required.
void G_option_requires(void *first,...)
Define a list of options from which at least one option is required if first option is present.
char * G_store(const char *s)
Copy string to allocated memory.
Definition strings.c:87
SYMBOL * err(FILE *fp, SYMBOL *s, char *msg)