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