GRASS GIS 8 Programmer's Manual  8.3.2(2024)-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
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 
24 struct vector {
25  size_t elsize;
26  size_t increment;
27  size_t count;
28  size_t limit;
29  void *data;
30 };
31 
32 static 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 
41 static 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 
55 struct rule {
56  int type;
57  int count;
58  void **opts;
59 };
60 
61 static 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  */
77 void 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 
88 static 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 
108 static 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 
129 static 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 
143 static 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 
155 static 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 
167 static 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 
199 static 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  */
212 void 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 
221 static 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  */
238 void 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 
247 static void check_required(const struct rule *rule)
248 {
249  if (count_present(rule, 0) < 1) {
250  char *err;
251 
252  G_asprintf(&err,
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  */
272 void 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 
281 static 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  */
310 void 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 
319 static 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  */
339 void 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 
348 static 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  */
368 void 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 
377 static 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 
482 static 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)
Definition: symbol/read.c:216