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