GRASS GIS 8 Programmer's Manual 8.2.0(2022)-exported
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 options)
5
6 (C) 2014-2015 by the GRASS Development Team
7
8 This program is free software under the GNU General Public License
9 (>=v2). Read the file COPYING that comes with GRASS for details.
10
11 \author Glynn Clements Jun. 2014
12*/
13
14#include <stdarg.h>
15#include <string.h>
16#include <stdio.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 = {sizeof(struct rule), 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 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 for (flag = &st->first_flag; flag; flag = flag->next_flag)
111 if ((const void *) flag == p)
112 return 1;
113 }
114
115 if (st->n_opts) {
116 const struct Option *opt;
117 for (opt = &st->first_option; opt; opt = opt->next_opt)
118 if ((const void *) opt == p)
119 return 0;
120 }
121
122 G_fatal_error(_("Internal error: option or flag not found"));
123}
124
125static int is_present(const void *p)
126{
127 if (is_flag(p)) {
128 const struct Flag *flag = p;
129 return (int) flag->answer;
130 }
131 else {
132 const struct Option *opt = p;
133 return opt->count > 0;
134 }
135}
136
137static char *get_name(const void *p)
138{
139 if (is_flag(p)) {
140 char *s;
141 G_asprintf(&s, "-%c", ((const struct Flag *) p)->key);
142 return s;
143 }
144 else
145 return G_store(((const struct Option *) p)->key);
146}
147
148static int count_present(const struct rule *rule, int start)
149{
150 int i;
151 int count = 0;
152
153 for (i = start; i < rule->count; i++)
154 if (is_present(rule->opts[i]))
155 count++;
156
157 return count;
158}
159
160static const char *describe_rule(const struct rule *rule, int start,
161 int disjunction)
162{
163 char *s;
164 int i;
165
166 G_asprintf(&s, "<%s>", get_name(rule->opts[start]));
167
168 for (i = start + 1; i < rule->count - 1; i++) {
169 char *s0 = s;
170 char *ss = get_name(rule->opts[i]);
171 s = NULL;
172 G_asprintf(&s, "%s, <%s>", s0, ss);
173 G_free(s0);
174 G_free(ss);
175 }
176
177 if (rule->count - start > 1) {
178 char *s0 = s;
179 char *ss = get_name(rule->opts[i]);
180 s = NULL;
181 G_asprintf(&s, disjunction ? _("%s or <%s>") : _("%s and <%s>"), s0, ss);
182 G_free(s0);
183 G_free(ss);
184 }
185
186 return s;
187}
188
189static void append_error(const char *msg)
190{
191 st->error = G_realloc(st->error, sizeof(char *) * (st->n_errors + 1));
192 st->error[st->n_errors++] = G_store(msg);
193}
194
195/*! \brief Sets the options to be mutually exclusive.
196
197 When running the module, at most one option from a set can be
198 provided.
199
200 \param first first given option
201*/
202void G_option_exclusive(void *first, ...)
203{
204 va_list ap;
205 va_start(ap, first);
206 make_rule(RULE_EXCLUSIVE, first, ap);
207 va_end(ap);
208}
209
210static void check_exclusive(const struct rule *rule)
211{
212 if (count_present(rule, 0) > 1) {
213 char *err;
214 G_asprintf(&err, _("Options %s are mutually exclusive"),
215 describe_rule(rule, 0, 0));
216 append_error(err);
217 }
218}
219
220/*! \brief Sets the options to be required.
221
222 At least one option from a set must be given.
223
224 \param first first given option
225*/
226void G_option_required(void *first, ...)
227{
228 va_list ap;
229 va_start(ap, first);
230 make_rule(RULE_REQUIRED, first, ap);
231 va_end(ap);
232}
233
234static void check_required(const struct rule *rule)
235{
236 if (count_present(rule, 0) < 1) {
237 char *err;
238 G_asprintf(&err, _("At least one of the following options is required: %s"),
239 describe_rule(rule, 0, 0));
240 append_error(err);
241 }
242}
243
244/*! \brief Define a list of options from which at least one option
245 is required if first option is present.
246
247 If the first option is present, at least one of the other
248 options must also be present.
249
250 If you want all options to be provided use G_option_requires_all()
251 function.
252 If you want more than one option to be present but not all,
253 call this function multiple times.
254
255 \param first first given option
256*/
257void G_option_requires(void *first, ...)
258{
259 va_list ap;
260 va_start(ap, first);
261 make_rule(RULE_REQUIRES, first, ap);
262 va_end(ap);
263}
264
265static void check_requires(const struct rule *rule)
266{
267 if (!is_present(rule->opts[0]))
268 return;
269 if (count_present(rule, 1) < 1) {
270 char *err;
271 if (rule->count > 2)
272 G_asprintf(&err, _("Option <%s> requires at least one of %s"),
273 get_name(rule->opts[0]), describe_rule(rule, 1, 1));
274 else
275 G_asprintf(&err, _("Option <%s> requires %s"),
276 get_name(rule->opts[0]), describe_rule(rule, 1, 1));
277 append_error(err);
278 }
279}
280
281/*! \brief Define additionally required options for an option.
282
283 If the first option is present, all the other options must also
284 be present.
285
286 If it is enough if only one option from a set is present,
287 use G_option_requires() function.
288
289 \see G_option_collective()
290
291 \param first first given option
292*/
293void G_option_requires_all(void *first, ...)
294{
295 va_list ap;
296 va_start(ap, first);
297 make_rule(RULE_REQUIRES_ALL, first, ap);
298 va_end(ap);
299}
300
301static void check_requires_all(const struct rule *rule)
302{
303 if (!is_present(rule->opts[0]))
304 return;
305 if (count_present(rule, 1) < rule->count - 1) {
306 char *err;
307 G_asprintf(&err, _("Option <%s> requires all of %s"),
308 get_name(rule->opts[0]), describe_rule(rule, 1, 0));
309 append_error(err);
310 }
311}
312
313/*! \brief Exclude selected options.
314
315 If the first option is present, none of the other options may also (should?)
316 be present.
317
318 \param first first given option
319*/
320void G_option_excludes(void *first, ...)
321{
322 va_list ap;
323 va_start(ap, first);
324 make_rule(RULE_EXCLUDES, first, ap);
325 va_end(ap);
326}
327
328static void check_excludes(const struct rule *rule)
329{
330 if (!is_present(rule->opts[0]))
331 return;
332 if (count_present(rule, 1) > 0) {
333 char *err;
334 G_asprintf(&err, _("Option <%s> is mutually exclusive with all of %s"),
335 get_name(rule->opts[0]), describe_rule(rule, 1, 0));
336 append_error(err);
337 }
338}
339
340/*! \brief Sets the options to be collective.
341
342 If any option is present, all the other options must also be present
343 all or nothing from a set.
344
345 \param first first given option
346*/
347void G_option_collective(void *first, ...)
348{
349 va_list ap;
350 va_start(ap, first);
351 make_rule(RULE_COLLECTIVE, first, ap);
352 va_end(ap);
353}
354
355static void check_collective(const struct rule *rule)
356{
357 int count = count_present(rule, 0);
358 if (count > 0 && count < rule->count) {
359 char *err;
360 G_asprintf(&err, _("Either all or none of %s must be given"),
361 describe_rule(rule, 0, 0));
362 append_error(err);
363 }
364}
365
366/*! \brief Check for option rules (internal use only) */
368{
369 unsigned int i;
370
371 for (i = 0; i < rules.count; i++) {
372 const struct rule *rule = &((const struct rule *) rules.data)[i];
373 switch (rule->type) {
374 case RULE_EXCLUSIVE:
375 check_exclusive(rule);
376 break;
377 case RULE_REQUIRED:
378 check_required(rule);
379 break;
380 case RULE_REQUIRES:
381 check_requires(rule);
382 break;
383 case RULE_REQUIRES_ALL:
384 check_requires_all(rule);
385 break;
386 case RULE_EXCLUDES:
387 check_excludes(rule);
388 break;
389 case RULE_COLLECTIVE:
390 check_collective(rule);
391 break;
392 default:
393 G_fatal_error(_("Internal error: invalid rule type: %d"),
394 rule->type);
395 break;
396 }
397 }
398}
399
400/*! \brief Describe option rules (stderr) */
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 switch (rule->type) {
408 case RULE_EXCLUSIVE:
409 fprintf(stderr, "Exclusive: %s", describe_rule(rule, 0, 0));
410 break;
411 case RULE_REQUIRED:
412 fprintf(stderr, "Required: %s", describe_rule(rule, 0, 1));
413 break;
414 case RULE_REQUIRES:
415 fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
416 describe_rule(rule, 1, 1));
417 break;
418 case RULE_REQUIRES_ALL:
419 fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
420 describe_rule(rule, 1, 0));
421 break;
422 case RULE_EXCLUDES:
423 fprintf(stderr, "Excludes: %s => %s", get_name(rule->opts[0]),
424 describe_rule(rule, 1, 0));
425 break;
426 case RULE_COLLECTIVE:
427 fprintf(stderr, "Collective: %s", describe_rule(rule, 0, 0));
428 break;
429 default:
430 G_fatal_error(_("Internal error: invalid rule type: %d"),
431 rule->type);
432 break;
433 }
434 }
435}
436
437/*!
438 \brief Checks if there is any rule RULE_REQUIRED (internal use only).
439
440 \return 1 if there is such rule
441 \return 0 if not
442 */
444{
445 size_t i;
446
447 for (i = 0; i < rules.count; i++) {
448 const struct rule *rule = &((const struct rule *) rules.data)[i];
449 if (rule->type == RULE_REQUIRED)
450 return TRUE;
451 }
452 return FALSE;
453}
454
455static const char * const rule_types[] = {
456 "exclusive",
457 "required",
458 "requires",
459 "requires-all",
460 "excludes",
461 "collective"
462};
463
464/*! \brief Describe option rules in XML format (internal use only)
465
466 \param fp file where to print XML info
467*/
469{
470 unsigned int i, j;
471
472 if (!rules.count)
473 return;
474
475 fprintf(fp, "\t<rules>\n");
476 for (i = 0; i < rules.count; i++) {
477 const struct rule *rule = &((const struct rule *) rules.data)[i];
478 fprintf(fp, "\t\t<rule type=\"%s\">\n", rule_types[rule->type]);
479 for (j = 0; j < rule->count; j++) {
480 void *p = rule->opts[j];
481 if (is_flag(p)) {
482 const struct Flag *flag = (const struct Flag *) p;
483 fprintf(fp, "\t\t\t<rule-flag key=\"%c\"/>\n", flag->key);
484 }
485 else {
486 const struct Option *opt = (const struct Option *) p;
487 fprintf(fp, "\t\t\t<rule-option key=\"%s\"/>\n", opt->key);
488 }
489 }
490 fprintf(fp, "\t\t</rule>\n");
491 }
492 fprintf(fp, "\t</rules>\n");
493}
494
void * G_incr_void_ptr(const void *ptr, size_t size)
Advance void pointer.
Definition: alloc.c:186
void G_free(void *buf)
Free allocated memory.
Definition: alloc.c:149
int G_asprintf(char **out, const char *fmt,...)
Definition: asprintf.c:70
#define NULL
Definition: ccmath.h:32
#define TRUE
Definition: dbfopen.c:183
#define FALSE
Definition: dbfopen.c:182
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition: gis/error.c:160
int count
struct state * st
Definition: parser.c:104
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)
Definition: symbol/read.c:220