GRASS GIS 8 Programmer's Manual  8.2.2dev(2023)-3d2c704037
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 
23 struct vector {
24  size_t elsize;
25  size_t increment;
26  size_t count;
27  size_t limit;
28  void *data;
29 };
30 
31 static 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 
40 static 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 
54 struct rule {
55  int type;
56  int count;
57  void **opts;
58 };
59 
60 static 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 */
76 void 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 
87 static 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 
106 static 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 
125 static 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 
137 static 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 
148 static 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 
160 static 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 
189 static 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 */
202 void 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 
210 static 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 */
226 void 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 
234 static 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 */
257 void 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 
265 static 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 */
293 void 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 
301 static 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 */
320 void 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 
328 static 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 */
347 void 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 
355 static 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 
455 static 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 
#define TRUE
Definition: gis.h:59
int G__has_required_rule(void)
Checks if there is any rule RULE_REQUIRED (internal use only).
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
int count
Definition: gis.h:563
void G_option_required(void *first,...)
Sets the options to be required.
char answer
Definition: gis.h:574
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:149
int count
void G__check_option_rules(void)
Check for option rules (internal use only)
#define G_incr_void_ptr(ptr, size)
Definition: defs/gis.h:100
#define NULL
Definition: ccmath.h:32
void G_option_excludes(void *first,...)
Exclude selected options.
SYMBOL * err(FILE *fp, SYMBOL *s, char *msg)
Definition: symbol/read.c:220
struct state * st
Definition: parser.c:104
void G_option_rule(int type, int nopts, void **opts)
Set generic option rule.
#define FALSE
Definition: gis.h:63
Structure that stores flag info.
Definition: gis.h:571
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 key
Definition: gis.h:573
void G_option_exclusive(void *first,...)
Sets the options to be mutually exclusive.
struct Flag * next_flag
Definition: gis.h:580
void G__describe_option_rules(void)
Describe option rules (stderr)
void G__describe_option_rules_xml(FILE *fp)
Describe option rules in XML format (internal use only)
void G_option_requires_all(void *first,...)
Define additionally required options for an option.
Structure that stores option information.
Definition: gis.h:542
#define G_realloc(p, n)
Definition: defs/gis.h:114
#define _(str)
Definition: glocale.h:10
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
const char * key
Definition: gis.h:544
void G_option_collective(void *first,...)
Sets the options to be collective.
int G_asprintf(char **, const char *,...) __attribute__((format(printf
struct Option * next_opt
Definition: gis.h:558