GRASS GIS 8 Programmer's Manual 8.3.2(2024)-exported
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 \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 \param first first given option
237 */
238void G_option_required(void *first, ...)
239{
240 va_list ap;
241
242 va_start(ap, first);
243 make_rule(RULE_REQUIRED, first, ap);
244 va_end(ap);
245}
246
247static void check_required(const struct rule *rule)
248{
249 if (count_present(rule, 0) < 1) {
250 char *err;
251
253 _("At least one of the following options is required: %s"),
254 describe_rule(rule, 0, 0));
255 append_error(err);
256 }
257}
258
259/*! \brief Define a list of options from which at least one option
260 is required if first option is present.
261
262 If the first option is present, at least one of the other
263 options must also be present.
264
265 If you want all options to be provided use G_option_requires_all()
266 function.
267 If you want more than one option to be present but not all,
268 call this function multiple times.
269
270 \param first first given option
271 */
272void G_option_requires(void *first, ...)
273{
274 va_list ap;
275
276 va_start(ap, first);
277 make_rule(RULE_REQUIRES, first, ap);
278 va_end(ap);
279}
280
281static void check_requires(const struct rule *rule)
282{
283 if (!is_present(rule->opts[0]))
284 return;
285 if (count_present(rule, 1) < 1) {
286 char *err;
287
288 if (rule->count > 2)
289 G_asprintf(&err, _("Option <%s> requires at least one of %s"),
290 get_name(rule->opts[0]), describe_rule(rule, 1, 1));
291 else
292 G_asprintf(&err, _("Option <%s> requires %s"),
293 get_name(rule->opts[0]), describe_rule(rule, 1, 1));
294 append_error(err);
295 }
296}
297
298/*! \brief Define additionally required options for an option.
299
300 If the first option is present, all the other options must also
301 be present.
302
303 If it is enough if only one option from a set is present,
304 use G_option_requires() function.
305
306 \see G_option_collective()
307
308 \param first first given option
309 */
310void G_option_requires_all(void *first, ...)
311{
312 va_list ap;
313
314 va_start(ap, first);
315 make_rule(RULE_REQUIRES_ALL, first, ap);
316 va_end(ap);
317}
318
319static void check_requires_all(const struct rule *rule)
320{
321 if (!is_present(rule->opts[0]))
322 return;
323 if (count_present(rule, 1) < rule->count - 1) {
324 char *err;
325
326 G_asprintf(&err, _("Option <%s> requires all of %s"),
327 get_name(rule->opts[0]), describe_rule(rule, 1, 0));
328 append_error(err);
329 }
330}
331
332/*! \brief Exclude selected options.
333
334 If the first option is present, none of the other options may also (should?)
335 be present.
336
337 \param first first given option
338 */
339void G_option_excludes(void *first, ...)
340{
341 va_list ap;
342
343 va_start(ap, first);
344 make_rule(RULE_EXCLUDES, first, ap);
345 va_end(ap);
346}
347
348static void check_excludes(const struct rule *rule)
349{
350 if (!is_present(rule->opts[0]))
351 return;
352 if (count_present(rule, 1) > 0) {
353 char *err;
354
355 G_asprintf(&err, _("Option <%s> is mutually exclusive with all of %s"),
356 get_name(rule->opts[0]), describe_rule(rule, 1, 0));
357 append_error(err);
358 }
359}
360
361/*! \brief Sets the options to be collective.
362
363 If any option is present, all the other options must also be present
364 all or nothing from a set.
365
366 \param first first given option
367 */
368void G_option_collective(void *first, ...)
369{
370 va_list ap;
371
372 va_start(ap, first);
373 make_rule(RULE_COLLECTIVE, first, ap);
374 va_end(ap);
375}
376
377static void check_collective(const struct rule *rule)
378{
379 int count = count_present(rule, 0);
380
381 if (count > 0 && count < rule->count) {
382 char *err;
383
384 G_asprintf(&err, _("Either all or none of %s must be given"),
385 describe_rule(rule, 0, 0));
386 append_error(err);
387 }
388}
389
390/*! \brief Check for option rules (internal use only) */
392{
393 unsigned int i;
394
395 for (i = 0; i < rules.count; i++) {
396 const struct rule *rule = &((const struct rule *)rules.data)[i];
397
398 switch (rule->type) {
399 case RULE_EXCLUSIVE:
400 check_exclusive(rule);
401 break;
402 case RULE_REQUIRED:
403 check_required(rule);
404 break;
405 case RULE_REQUIRES:
406 check_requires(rule);
407 break;
408 case RULE_REQUIRES_ALL:
409 check_requires_all(rule);
410 break;
411 case RULE_EXCLUDES:
412 check_excludes(rule);
413 break;
414 case RULE_COLLECTIVE:
415 check_collective(rule);
416 break;
417 default:
418 G_fatal_error(_("Internal error: invalid rule type: %d"),
419 rule->type);
420 break;
421 }
422 }
423}
424
425/*! \brief Describe option rules (stderr) */
427{
428 unsigned int i;
429
430 for (i = 0; i < rules.count; i++) {
431 const struct rule *rule = &((const struct rule *)rules.data)[i];
432
433 switch (rule->type) {
434 case RULE_EXCLUSIVE:
435 fprintf(stderr, "Exclusive: %s", describe_rule(rule, 0, 0));
436 break;
437 case RULE_REQUIRED:
438 fprintf(stderr, "Required: %s", describe_rule(rule, 0, 1));
439 break;
440 case RULE_REQUIRES:
441 fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
442 describe_rule(rule, 1, 1));
443 break;
444 case RULE_REQUIRES_ALL:
445 fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
446 describe_rule(rule, 1, 0));
447 break;
448 case RULE_EXCLUDES:
449 fprintf(stderr, "Excludes: %s => %s", get_name(rule->opts[0]),
450 describe_rule(rule, 1, 0));
451 break;
452 case RULE_COLLECTIVE:
453 fprintf(stderr, "Collective: %s", describe_rule(rule, 0, 0));
454 break;
455 default:
456 G_fatal_error(_("Internal error: invalid rule type: %d"),
457 rule->type);
458 break;
459 }
460 }
461}
462
463/*!
464 \brief Checks if there is any rule RULE_REQUIRED (internal use only).
465
466 \return 1 if there is such rule
467 \return 0 if not
468 */
470{
471 size_t i;
472
473 for (i = 0; i < rules.count; i++) {
474 const struct rule *rule = &((const struct rule *)rules.data)[i];
475
476 if (rule->type == RULE_REQUIRED)
477 return TRUE;
478 }
479 return FALSE;
480}
481
482static const char *const rule_types[] = {"exclusive", "required",
483 "requires", "requires-all",
484 "excludes", "collective"};
485
486/*! \brief Describe option rules in XML format (internal use only)
487
488 \param fp file where to print XML info
489 */
491{
492 unsigned int i, j;
493
494 if (!rules.count)
495 return;
496
497 fprintf(fp, "\t<rules>\n");
498 for (i = 0; i < rules.count; i++) {
499 const struct rule *rule = &((const struct rule *)rules.data)[i];
500
501 if (rule->count < 0)
502 G_fatal_error(_("Internal error: the number of options is < 0"));
503
504 fprintf(fp, "\t\t<rule type=\"%s\">\n", rule_types[rule->type]);
505 for (j = 0; j < (unsigned int)rule->count; j++) {
506 void *p = rule->opts[j];
507
508 if (is_flag(p)) {
509 const struct Flag *flag = (const struct Flag *)p;
510
511 fprintf(fp, "\t\t\t<rule-flag key=\"%c\"/>\n", flag->key);
512 }
513 else {
514 const struct Option *opt = (const struct Option *)p;
515
516 fprintf(fp, "\t\t\t<rule-option key=\"%s\"/>\n", opt->key);
517 }
518 }
519 fprintf(fp, "\t\t</rule>\n");
520 }
521 fprintf(fp, "\t</rules>\n");
522}
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
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)