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