Loading...
Searching...
No Matches
parser_dependencies.c
Go to the documentation of this file.
/*!
\file lib/gis/parser_dependencies.c
\brief GIS Library - Argument parsing functions (dependencies between
options)
(C) 2014-2015 by the GRASS Development Team
This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.
\author Glynn Clements Jun. 2014
*/
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <grass/gis.h>
#include <grass/glocale.h>
#include "parser_local_proto.h"
struct vector {
size_t elsize;
size_t increment;
size_t count;
size_t limit;
void *data;
};
static void vector_new(struct vector *v, size_t elsize, size_t increment)
{
v->elsize = elsize;
v->increment = increment;
v->count = 0;
v->limit = 0;
v->data = NULL;
}
static void vector_append(struct vector *v, const void *data)
{
void *p;
if (v->count >= v->limit) {
v->limit += v->increment;
v->data = G_realloc(v->data, v->limit * v->elsize);
}
p = G_incr_void_ptr(v->data, v->count * v->elsize);
memcpy(p, data, v->elsize);
v->count++;
}
struct rule {
int type;
int count;
void **opts;
};
static struct vector rules = {.elsize = sizeof(struct rule), .increment = 50};
/*! \brief Set generic option rule
Supported rule types:
- RULE_EXCLUSIVE
- RULE_REQUIRED
- RULE_REQUIRES
- RULE_REQUIRES_ALL
- RULE_EXCLUDES
- RULE_COLLECTIVE
\param type rule type
\param nopts number of options in the array
\param opts array of options
*/
void G_option_rule(int type, int nopts, void **opts)
{
struct rule rule;
rule.type = type;
rule.count = nopts;
rule.opts = opts;
vector_append(&rules, &rule);
}
static void make_rule(int type, void *first, va_list ap)
{
struct vector opts;
void *opt;
vector_new(&opts, sizeof(void *), 10);
opt = first;
vector_append(&opts, &opt);
for (;;) {
opt = va_arg(ap, void *);
if (!opt)
break;
vector_append(&opts, &opt);
}
G_option_rule(type, opts.count, (void **)opts.data);
}
static int is_flag(const void *p)
{
if (st->n_flags) {
const struct Flag *flag;
for (flag = &st->first_flag; flag; flag = flag->next_flag)
if ((const void *)flag == p)
return 1;
}
if (st->n_opts) {
const struct Option *opt;
for (opt = &st->first_option; opt; opt = opt->next_opt)
if ((const void *)opt == p)
return 0;
}
G_fatal_error(_("Internal error: option or flag not found"));
}
static int is_present(const void *p)
{
if (is_flag(p)) {
const struct Flag *flag = p;
return (int)flag->answer;
}
else {
const struct Option *opt = p;
return opt->count > 0;
}
}
static char *get_name(const void *p)
{
if (is_flag(p)) {
char *s;
G_asprintf(&s, "-%c", ((const struct Flag *)p)->key);
return s;
}
else
return G_store(((const struct Option *)p)->key);
}
static int count_present(const struct rule *rule, int start)
{
int i;
int count = 0;
for (i = start; i < rule->count; i++)
if (is_present(rule->opts[i]))
count++;
return count;
}
static const char *describe_rule(const struct rule *rule, int start,
int disjunction)
{
char *s;
int i;
G_asprintf(&s, "<%s>", get_name(rule->opts[start]));
for (i = start + 1; i < rule->count - 1; i++) {
char *s0 = s;
char *ss = get_name(rule->opts[i]);
s = NULL;
G_asprintf(&s, "%s, <%s>", s0, ss);
G_free(s0);
G_free(ss);
}
if (rule->count - start > 1) {
char *s0 = s;
char *ss = get_name(rule->opts[i]);
s = NULL;
G_asprintf(&s, disjunction ? _("%s or <%s>") : _("%s and <%s>"), s0,
ss);
G_free(s0);
G_free(ss);
}
return s;
}
static void append_error(const char *msg)
{
st->error = G_realloc(st->error, sizeof(char *) * (st->n_errors + 1));
st->error[st->n_errors++] = G_store(msg);
}
/*! \brief Sets the options to be mutually exclusive.
When running the module, at most one option from a set can be
provided.
The last item of the list must be NULL.
\param first first given option
*/
void G_option_exclusive(void *first, ...)
{
va_list ap;
va_start(ap, first);
make_rule(RULE_EXCLUSIVE, first, ap);
va_end(ap);
}
static void check_exclusive(const struct rule *rule)
{
if (count_present(rule, 0) > 1) {
char *err;
G_asprintf(&err, _("Options %s are mutually exclusive"),
describe_rule(rule, 0, 0));
append_error(err);
}
}
/*! \brief Sets the options to be required.
At least one option from a set must be given.
The last item of the list must be NULL.
\param first first given option
*/
void G_option_required(void *first, ...)
{
va_list ap;
va_start(ap, first);
make_rule(RULE_REQUIRED, first, ap);
va_end(ap);
}
static void check_required(const struct rule *rule)
{
if (count_present(rule, 0) < 1) {
char *err;
G_asprintf(&err,
_("At least one of the following options is required: %s"),
describe_rule(rule, 0, 0));
append_error(err);
}
}
/*! \brief Define a list of options from which at least one option
is required if first option is present.
If the first option is present, at least one of the other
options must also be present.
The last item of the list must be NULL.
If you want all options to be provided use G_option_requires_all()
function.
If you want more than one option to be present but not all,
call this function multiple times.
\param first first given option
*/
void G_option_requires(void *first, ...)
{
va_list ap;
va_start(ap, first);
make_rule(RULE_REQUIRES, first, ap);
va_end(ap);
}
static void check_requires(const struct rule *rule)
{
if (!is_present(rule->opts[0]))
return;
if (count_present(rule, 1) < 1) {
char *err;
if (rule->count > 2)
G_asprintf(&err, _("Option <%s> requires at least one of %s"),
get_name(rule->opts[0]), describe_rule(rule, 1, 1));
else
G_asprintf(&err, _("Option <%s> requires %s"),
get_name(rule->opts[0]), describe_rule(rule, 1, 1));
append_error(err);
}
}
/*! \brief Define additionally required options for an option.
If the first option is present, all the other options must also
be present.
The last item of the list must be NULL.
If it is enough if only one option from a set is present,
use G_option_requires() function.
\see G_option_collective()
\param first first given option
*/
void G_option_requires_all(void *first, ...)
{
va_list ap;
va_start(ap, first);
make_rule(RULE_REQUIRES_ALL, first, ap);
va_end(ap);
}
static void check_requires_all(const struct rule *rule)
{
if (!is_present(rule->opts[0]))
return;
if (count_present(rule, 1) < rule->count - 1) {
char *err;
G_asprintf(&err, _("Option <%s> requires all of %s"),
get_name(rule->opts[0]), describe_rule(rule, 1, 0));
append_error(err);
}
}
/*! \brief Exclude selected options.
If the first option is present, none of the other options may also (should?)
be present.
The last item of the list must be NULL.
\param first first given option
*/
void G_option_excludes(void *first, ...)
{
va_list ap;
va_start(ap, first);
make_rule(RULE_EXCLUDES, first, ap);
va_end(ap);
}
static void check_excludes(const struct rule *rule)
{
if (!is_present(rule->opts[0]))
return;
if (count_present(rule, 1) > 0) {
char *err;
G_asprintf(&err, _("Option <%s> is mutually exclusive with all of %s"),
get_name(rule->opts[0]), describe_rule(rule, 1, 0));
append_error(err);
}
}
/*! \brief Sets the options to be collective.
If any option is present, all the other options must also be present
all or nothing from a set.
The last item of the list must be NULL.
\param first first given option
*/
void G_option_collective(void *first, ...)
{
va_list ap;
va_start(ap, first);
make_rule(RULE_COLLECTIVE, first, ap);
va_end(ap);
}
static void check_collective(const struct rule *rule)
{
int count = count_present(rule, 0);
if (count > 0 && count < rule->count) {
char *err;
G_asprintf(&err, _("Either all or none of %s must be given"),
describe_rule(rule, 0, 0));
append_error(err);
}
}
/*! \brief Check for option rules (internal use only) */
void G__check_option_rules(void)
{
unsigned int i;
for (i = 0; i < rules.count; i++) {
const struct rule *rule = &((const struct rule *)rules.data)[i];
switch (rule->type) {
case RULE_EXCLUSIVE:
check_exclusive(rule);
break;
case RULE_REQUIRED:
check_required(rule);
break;
case RULE_REQUIRES:
check_requires(rule);
break;
case RULE_REQUIRES_ALL:
check_requires_all(rule);
break;
case RULE_EXCLUDES:
check_excludes(rule);
break;
case RULE_COLLECTIVE:
check_collective(rule);
break;
default:
G_fatal_error(_("Internal error: invalid rule type: %d"),
rule->type);
break;
}
}
}
/*! \brief Describe option rules (stderr) */
void G__describe_option_rules(void)
{
unsigned int i;
for (i = 0; i < rules.count; i++) {
const struct rule *rule = &((const struct rule *)rules.data)[i];
switch (rule->type) {
case RULE_EXCLUSIVE:
fprintf(stderr, "Exclusive: %s", describe_rule(rule, 0, 0));
break;
case RULE_REQUIRED:
fprintf(stderr, "Required: %s", describe_rule(rule, 0, 1));
break;
case RULE_REQUIRES:
fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
describe_rule(rule, 1, 1));
break;
case RULE_REQUIRES_ALL:
fprintf(stderr, "Requires: %s => %s", get_name(rule->opts[0]),
describe_rule(rule, 1, 0));
break;
case RULE_EXCLUDES:
fprintf(stderr, "Excludes: %s => %s", get_name(rule->opts[0]),
describe_rule(rule, 1, 0));
break;
case RULE_COLLECTIVE:
fprintf(stderr, "Collective: %s", describe_rule(rule, 0, 0));
break;
default:
G_fatal_error(_("Internal error: invalid rule type: %d"),
rule->type);
break;
}
}
}
/*!
\brief Checks if there is any rule RULE_REQUIRED (internal use only).
\return 1 if there is such rule
\return 0 if not
*/
int G__has_required_rule(void)
{
size_t i;
for (i = 0; i < rules.count; i++) {
const struct rule *rule = &((const struct rule *)rules.data)[i];
if (rule->type == RULE_REQUIRED)
return TRUE;
}
return FALSE;
}
const struct Option *G__first_required_option_from_rules(void)
{
size_t i;
for (i = 0; i < rules.count; i++) {
const struct rule *rule = &((const struct rule *)rules.data)[i];
if (rule->type == RULE_REQUIRED) {
if (rule->count < 0)
G_fatal_error(
_("Internal error: the number of options is < 0"));
size_t j;
for (j = 0; j < (unsigned int)rule->count; j++) {
void *p = rule->opts[j];
if (is_flag(p))
continue;
else
return (const struct Option *)p;
}
}
}
return NULL;
}
static const char *const rule_types[] = {"exclusive", "required",
"requires", "requires-all",
"excludes", "collective"};
/*! \brief Describe option rules in XML format (internal use only)
\param fp file where to print XML info
*/
void G__describe_option_rules_xml(FILE *fp)
{
unsigned int i, j;
if (!rules.count)
return;
fprintf(fp, "\t<rules>\n");
for (i = 0; i < rules.count; i++) {
const struct rule *rule = &((const struct rule *)rules.data)[i];
if (rule->count < 0)
G_fatal_error(_("Internal error: the number of options is < 0"));
fprintf(fp, "\t\t<rule type=\"%s\">\n", rule_types[rule->type]);
for (j = 0; j < (unsigned int)rule->count; j++) {
void *p = rule->opts[j];
if (is_flag(p)) {
const struct Flag *flag = (const struct Flag *)p;
fprintf(fp, "\t\t\t<rule-flag key=\"%c\"/>\n", flag->key);
}
else {
const struct Option *opt = (const struct Option *)p;
fprintf(fp, "\t\t\t<rule-option key=\"%s\"/>\n", opt->key);
}
}
fprintf(fp, "\t\t</rule>\n");
}
fprintf(fp, "\t</rules>\n");
}
G_incr_void_ptr
void * G_incr_void_ptr(const void *ptr, size_t size)
Advance void pointer.
Definition alloc.c:184
G_free
void G_free(void *buf)
Free allocated memory.
Definition alloc.c:147
G_asprintf
int G_asprintf(char **out, const char *fmt,...)
Definition asprintf.c:68
NULL
#define NULL
Definition ccmath.h:32
TRUE
#define TRUE
Definition dbfopen.c:56
FALSE
#define FALSE
Definition dbfopen.c:55
G_fatal_error
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition gis/error.c:159
count
int count
G__check_option_rules
void G__check_option_rules(void)
Check for option rules (internal use only).
Definition parser_dependencies.c:403
G_option_rule
void G_option_rule(int type, int nopts, void **opts)
Set generic option rule.
Definition parser_dependencies.c:77
G_option_collective
void G_option_collective(void *first,...)
Sets the options to be collective.
Definition parser_dependencies.c:380
G__has_required_rule
int G__has_required_rule(void)
Checks if there is any rule RULE_REQUIRED (internal use only).
Definition parser_dependencies.c:481
G__describe_option_rules
void G__describe_option_rules(void)
Describe option rules (stderr).
Definition parser_dependencies.c:438
G_option_requires_all
void G_option_requires_all(void *first,...)
Define additionally required options for an option.
Definition parser_dependencies.c:318
G_option_excludes
void G_option_excludes(void *first,...)
Exclude selected options.
Definition parser_dependencies.c:349
G_option_exclusive
void G_option_exclusive(void *first,...)
Sets the options to be mutually exclusive.
Definition parser_dependencies.c:214
G__describe_option_rules_xml
void G__describe_option_rules_xml(FILE *fp)
Describe option rules in XML format (internal use only).
Definition parser_dependencies.c:526
G__first_required_option_from_rules
const struct Option * G__first_required_option_from_rules(void)
Definition parser_dependencies.c:494
G_option_required
void G_option_required(void *first,...)
Sets the options to be required.
Definition parser_dependencies.c:242
G_option_requires
void G_option_requires(void *first,...)
Define a list of options from which at least one option is required if first option is present.
Definition parser_dependencies.c:278
G_store
char * G_store(const char *s)
Copy string to allocated memory.
Definition strings.c:87
err
SYMBOL * err(FILE *fp, SYMBOL *s, char *msg)
Definition symbol/read.c:216