summaryrefslogtreecommitdiffstats
path: root/ctxopt.c
diff options
context:
space:
mode:
authorpgen <p.gen.progs@gmail.com>2019-12-29 20:22:25 +0100
committerpgen <p.gen.progs@gmail.com>2020-02-22 23:34:37 +0100
commit5d51aea989338ae7be9a900e2514877bb0493efa (patch)
treefd5fb166dec1411549bea14796734d0e02098da9 /ctxopt.c
parent5bb772f4c6c5436ae0718b061b35d742eb5b8d51 (diff)
Update and fix ctxopt.c
- Allow arguments with a dash (-) when attached to their parameters or preceded by a backslash (\). - Fix a logical error in the detection of arguments when analyzing the options of a context. - Fix various typos. - Avoid modifying constant strings - Remove dead/useless code
Diffstat (limited to 'ctxopt.c')
-rw-r--r--ctxopt.c289
1 files changed, 168 insertions, 121 deletions
diff --git a/ctxopt.c b/ctxopt.c
index 0514bff..97d815b 100644
--- a/ctxopt.c
+++ b/ctxopt.c
@@ -1,4 +1,3 @@
-//#include "config.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@@ -33,9 +32,6 @@ fatal_internal(const char * format, ...);
static void
fatal(errors e, char * errmsg);
-static void
-error(const char * format, ...);
-
static int user_rc; /* Used by various callback functions */
static int user_value; /* Used by various callback functions */
static char * user_string; /* Used by various callback functions */
@@ -227,10 +223,17 @@ new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst);
static void
evaluate_ctx_inst(ctx_inst_t * ctx_inst);
-/* ====================== */
+/* ********************** */
/* Message implementation */
-/* ====================== */
+/* ********************** */
+/* ================================================================== */
+/* Fatal error function used when an fatal condition was encountered. */
+/* This function is reserved for the ctxopt internal usage. */
+/* */
+/* format : printf like format */
+/* ... : remaining arguments interpreted using the format argument */
+/* ================================================================== */
static void
fatal_internal(const char * format, ...)
{
@@ -244,6 +247,21 @@ fatal_internal(const char * format, ...)
exit(EXIT_FAILURE);
}
+/* ====================================================================== */
+/* Generic fatal error function. This one uses the global status ctxopt */
+/* stored in the cur_state structure and can call custom error functions. */
+/* registered by the users for a given error identifier. */
+/* */
+/* e : error identifier responsible of the fatal error */
+/* errmsg : users's provided string specific to the error e */
+/* Note that errmsg is not used in all cases */
+/* */
+/* e errmsg */
+/* ------------ -------------------------------- */
+/* CTXOPTMISPAR Name of the missing parameter */
+/* CTXOPTUNKPAR Name of the unknown parameter */
+/* CTXOPTINCOPT Name of incompatibles parameters */
+/* ====================================================================== */
static void
fatal(errors e, char * errmsg)
{
@@ -256,7 +274,7 @@ fatal(errors e, char * errmsg)
case CTXOPTNOERR:
break;
case CTXOPTMISPAR:
- if (cur_state->cur_opt_par_name != NULL)
+ if (cur_state->ctx_par_name != NULL)
fprintf(stderr,
"Mandatory parameter(s): %s are missing in the context "
"introduced by %s.",
@@ -298,20 +316,7 @@ fatal(errors e, char * errmsg)
if (cur_state->ctx_name != NULL)
ctxopt_ctx_disp_usage(cur_state->ctx_name, continue_after);
- exit(e);
-}
-
-static void
-error(const char * format, ...)
-{
- va_list args;
-
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- fprintf(stderr, "\n");
-
- exit(EXIT_FAILURE);
+ exit(e); /* Program exist with the error id e as return code */
}
/* ******************************** */
@@ -1074,43 +1079,53 @@ struct ctx_s
/* """""""""""""""" */
struct opt_s
{
- char * name; /* option name */
- char * next_ctx; /* new context this option may lead to */
- ll_t * ctx_list; /* list of contexts allowing this option */
- char * params; /* string containing all the parameters of the option */
+ char * name; /* option name */
+ char * next_ctx; /* new context this option may lead to */
+ ll_t * ctx_list; /* list of contexts allowing this option */
+ char * params; /* string containing all the parameters of *
+ | the option */
+
+ void (*action)( /* The option associated action */
+ char * ctx_name, /* context name */
+ char * opt_name, /* option name */
+ char * par, /* option parameter */
+ int nb_args, /* number of arguments */
+ char ** args, /* option arguments */
+ int nb_opt_data, /* number of option data pointers */
+ void ** opt_data, /* option data pointers */
+ int nb_ctx_data, /* nb of current context data ptrs */
+ void ** ctx_data /* current context data pointers */
+ );
+
+ int nb_data; /* number of the data pointers passed as argument to *
+ | action */
+ void ** data; /* array of data pointers passed as argument to action */
+
+ int args; /* 1 if this option takes arguments else 0 */
+ int optional; /* 1 if the option is optional, else 0 */
+ int multiple; /* 1 if the option can appear more than one time in a *
+ | context, else 0 */
- void (*action)(char * ctx_name, char * opt_name, char * par, int nb_args,
- char ** args, int nb_opt_data, void ** opt_data,
- int nb_ctx_data,
- void ** ctx_data); /* The option associated action */
- int nb_data; /* number of the data passed as argument to action */
- void ** data; /* void data passed as argument to action */
+ int opt_count_matter; /* 1 if we must restrict the count, else 0 */
+ int occurrences; /* Number of option occurrences in a context */
+ char opt_count_oper; /* <, = or > */
+ unsigned opt_count_mark; /* Value to be compared to with opt_count_oper */
- int args; /* pointer to the data passed to the action function */
+ char * arg; /* symbolic text after # describing the option argument */
- int optional; /* 1 if the option is optional, else 0 */
- int multiple; /* 1 if the option can appear more than one time in a *
- | context, else 0 */
+ int optional_args; /* 1 of option is optional else 0 */
+ int multiple_args; /* 1 is option can appear more than once in a context *
+ | instance */
+
+ int opt_args_count_matter; /* 1 if we must restrict the count, else 0 */
+ char opt_args_count_oper; /* <, = or > */
+ unsigned opt_args_count_mark; /* Value to be compared to with *
+ | opt_count_oper */
+
+ int eval_first; /* 1 if this option must be evaluated before *
+ | the options without this mark */
- int opt_count_matter; /* 1 if we must restrict the count, else 0 */
- int occurrences; /* Number of occurrences of the option in a context */
- char opt_count_oper; /* <, = or > */
- unsigned opt_count_mark; /* Value to be compared to with opt_count_oper */
- char * arg; /* text represent the argument in in description *
- | of the option. */
- int optional_args; /* 1 of option is optional else 0 */
- int multiple_args; /* 1 is option can appear more than once in a | context *
- | instance */
-
- int opt_args_count_matter; /* 1 if we must restrict the count, else 0 */
- char opt_args_count_oper; /* <, = or > */
- unsigned
- opt_args_count_mark; /* Value to be compared to with opt_count_oper */
- int eval_first; /* 1 if this option must be evaluated before *
- |the options without this mark */
-
- ll_t * constraints_list; /* List of constraints checking functions *
- | pointers. */
+ ll_t * constraints_list; /* List of constraint checking functions pointers. */
};
/* context instance structure */
@@ -1169,13 +1184,15 @@ struct constraint_s
char ** args;
};
-state_t * cur_state = NULL;
-static ll_t * cmdline_list;
-static ctx_t * main_ctx = NULL;
-static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
- | instance which holds the *
- | options instances */
-static ll_t * ctx_inst_list;
+state_t * cur_state = NULL; /* Current analysis state */
+static ll_t * cmdline_list; /* List of interpreted CLI words *
+ | serves as the basis for the *
+ | analysis of the parameters */
+static ctx_t * main_ctx = NULL; /* initial context instance */
+static ctx_inst_t * first_ctx_inst = NULL; /* Pointer to the fist context *
+ | instance which holds the *
+ | options instances */
+static ll_t * ctx_inst_list; /* List of the context instances */
/* ====================================================== */
/* Parse a string for the next matching token. */
@@ -1362,7 +1379,7 @@ print_options(ll_t * list, int * has_optional, int * has_ellipsis,
if (opt->args)
{
- if (strcmp(opt->arg, "#") == 0)
+ if (*(opt->arg) == '#')
*has_generic_arg = 1;
option = strappend(option, " ", NULL);
@@ -1570,7 +1587,7 @@ look_for_valid_prefix_in_word(char * word, ctx_t * ctx, int * pos, opt_t ** opt)
}
/* ============================================================= */
-/* is par_name is an unique abbreviation of an exiting parameter */
+/* If par_name is an unique abbreviation of an exiting parameter */
/* in the context ctx, then return this parameter. */
/* ============================================================= */
static char *
@@ -1618,19 +1635,19 @@ abbrev_expand(char * par_name, ctx_t * ctx)
bst_destroy(tmp_opt_bst, bst_null_action);
if (opt_count == 1)
- /* All th abbreviation lead to only one option */
- /* We can just continue as in the previous case. */
- /* """"""""""""""""""""""""""""""""""""""""""""" */
+ /* All the abbreviation lead to only one option */
+ /* We can just continue as in the previous case. */
+ /* """""""""""""""""""""""""""""""""""""""""""""" */
return xstrdup(first_s);
else
return NULL;
}
}
-/* ==================================================== */
-/* Returns 1 if mandatory options required by a context */
-/* are not present, else returns 0. */
-/* ==================================================== */
+/* ================================================================= */
+/* Terminates the program if mandatory options required by a context */
+/* are not present. */
+/* ================================================================= */
static void
check_for_missing_mandatory_opt(ctx_inst_t * ctx_inst, char * opt_par)
{
@@ -1658,6 +1675,12 @@ has_unseen_mandatory_opt(ctx_inst_t * ctx_inst, char ** missing)
return user_rc ? 1 : 0;
}
+/* ========================================================================= */
+/* This function terminates the program if an option or its arguments do not */
+/* conform to its occurrences constraint. */
+/* There constraints can appear by trailing >, < or = in their definition */
+/* given in ctxopt_new_ctx. */
+/* ========================================================================= */
static void
check_for_occurrences_issues(ctx_inst_t * ctx_inst)
{
@@ -1668,7 +1691,6 @@ check_for_occurrences_issues(ctx_inst_t * ctx_inst)
/* Check options */
/* """"""""""""" */
-
node = ctx->opt_list->head;
while (node != NULL)
{
@@ -1702,7 +1724,6 @@ check_for_occurrences_issues(ctx_inst_t * ctx_inst)
/* Check arguments */
/* """"""""""""""" */
-
node = ctx_inst->opt_inst_list->head;
while (node != NULL)
{
@@ -1789,6 +1810,10 @@ opt_parse(char * s, opt_t ** opt)
*opt = NULL;
memset(opt_arg, '\0', 33);
+ /* strip the leading blanks */
+ while (isblank(*s))
+ s++;
+
if (*s == '[') /* Start of an optional option */
{
opt_optional = 1;
@@ -1880,7 +1905,7 @@ opt_parse(char * s, opt_t ** opt)
pos = 0;
n = sscanf(s, "[%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
- if (pos > 1) /* [# was read */
+ if (pos > 1 && *opt_arg == '#') /* [# has been read */
{
opt_args = 1;
opt_optional_args = 1;
@@ -1904,7 +1929,7 @@ opt_parse(char * s, opt_t ** opt)
s += offset + 1;
}
- /* optional arg tag must end with a ] */
+ /* Optional arg tag must end with a ] */
if (*s != ']')
{
free(opt_name);
@@ -1916,7 +1941,7 @@ opt_parse(char * s, opt_t ** opt)
else
{
n = sscanf(s, "%32[^] .\t]%n%3[.]", opt_arg, &pos, dots);
- if (pos > 0) /* # was read */
+ if (pos > 0 && *opt_arg == '#') /* # has been read */
{
opt_args = 1;
if (n == 2) /* there were dots */
@@ -1948,7 +1973,7 @@ opt_parse(char * s, opt_t ** opt)
s++; /* skip the ] */
- /* strip the following blanks */
+ /* Strip the following blanks */
while (isblank(*s))
s++;
@@ -1956,7 +1981,7 @@ opt_parse(char * s, opt_t ** opt)
}
else if (opt_optional == 0 && (!*s || isblank(*s)))
{
- /* strip the following blanks */
+ /* Strip the following blanks */
while (isblank(*s))
s++;
@@ -2031,7 +2056,6 @@ init_opts(char * spec, ctx_t * ctx)
bst_t * node;
int offset;
- ltrim(spec, " \n\t");
while (*spec)
{
if ((offset = opt_parse(spec, &opt)) > 0)
@@ -2057,9 +2081,9 @@ init_opts(char * spec, ctx_t * ctx)
|| bst_opt->multiple_args != opt->multiple_args
|| bst_opt->args != opt->args || !same_next_ctx)
{
- error("option %s already exists with "
- "a different arguments signature.\n",
- opt->name);
+ fatal_internal("option %s already exists with "
+ "a different arguments signature.\n",
+ opt->name);
free(opt->name);
free(opt);
@@ -2101,6 +2125,7 @@ init_opts(char * spec, ctx_t * ctx)
exit(EXIT_FAILURE);
}
}
+
return 1;
}
@@ -2170,7 +2195,9 @@ opt_set_parms(char * opt_name, char * par_str)
void * par_bst = ctx->par_bst;
tmp_par_str = xstrdup(par_str);
- par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
+ ltrim(tmp_par_str, " \t");
+ rtrim(tmp_par_str, " \t", 0);
+ par_name = xstrtok_r(tmp_par_str, " \t,", &end_tmp_par_str);
if (par_name == NULL)
fatal_internal("Parameters are missing for option %s", opt_name);
@@ -2184,8 +2211,8 @@ opt_set_parms(char * opt_name, char * par_str)
node = bst_find(&tmp_par, &par_bst, par_compare);
if (node != NULL)
{
- error("The parameter %s is already defined in context %s", par_name,
- ctx->name);
+ fatal_internal("The parameter %s is already defined in context %s",
+ par_name, ctx->name);
rc = 0;
}
else
@@ -2302,7 +2329,9 @@ new_ctx_inst(ctx_t * ctx, ctx_inst_t * prev_ctx_inst)
bst = NULL;
seen_opt_t tmp_seen_opt;
- str = xstrdup(node->data);
+ str = xstrdup(node->data);
+ ltrim(str, " \t");
+ rtrim(str, " \t", 0);
opt_name = strtok(str, " \t"); /* extract the first option name */
while (opt_name != NULL) /* for each option name */
@@ -2369,11 +2398,12 @@ ctxopt_build_cmdline_list(int nb_words, char ** words)
int i;
char * prev_word = NULL;
char * word;
+ char * ptr;
int level = 0;
ll_node_t *node, *start_node;
/* The analysis is divided into three passes, this is not optimal but */
- /* must be done only one time ans so we can privilege the readability */
+ /* must be done only one time. Doing that we privilege readability. */
/* */
/* In the following, SG is the ascii character 1d (dec 29) */
/* */
@@ -2384,8 +2414,9 @@ ctxopt_build_cmdline_list(int nb_words, char ** words)
/* The second pass transform the '{...}' blocks by a trailing SG */
/* ({...} -> ...|) */
/* */
- /* The last pass remove the duplicated SG and check for SG, '{' or '}' */
- /* in the middle in the remaining list elements */
+ /* The last pass remove the duplicated SG, check for SG, '{' or '}' in */
+ /* the middle in the remaining list elements and recreate the pseudo */
+ /* argument: {} */
/* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
/* If the option list is not empty, clear it before going further */
@@ -2418,13 +2449,13 @@ ctxopt_build_cmdline_list(int nb_words, char ** words)
str = words[i];
- /* the word {} is not a group, do not interpret it. */
- /* """""""""""""""""""""""""""""""""""""""""""""""" */
- if (strcmp(str, "{}") == 0)
+ /* Replace each occurrence of the legal word {} by the characters */
+ /* 0x02 and 0x03 to hide them from the following process. */
+ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ while ((ptr = strstr(str, "{}")) != NULL)
{
- ll_append(cmdline_list, xstrdup(str));
- start_node = cmdline_list->tail;
- continue;
+ *ptr = 0x02; /* arbitrary values unlikely */
+ *(ptr + 1) = 0x03; /* present in a word */
}
if (len > 1) /* The word contains at least 2 characters */
@@ -2520,14 +2551,17 @@ ctxopt_build_cmdline_list(int nb_words, char ** words)
{
word = node->data;
- /* {} is a legal word */
- /* """""""""""""""""" */
- if (strcmp(word, "{}") == 0)
- goto next;
+ /* Restore the original { and } characters forming the legal word {} */
+ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */
+ while ((ptr = strchr(word, 0x02)) != NULL)
+ *ptr = '{';
+ while ((ptr = strchr(word, 0x03)) != NULL)
+ *ptr = '}';
/* Remove a SG if the previous element is SG */
/* """"""""""""""""""""""""""""""""""""""""" */
if (strcmp(word, "\x1d") == 0)
+ {
if (prev_word != NULL && (strcmp(prev_word, "\x1d") == 0))
{
ll_node_t * old_node = node;
@@ -2535,10 +2569,15 @@ ctxopt_build_cmdline_list(int nb_words, char ** words)
ll_delete(cmdline_list, old_node);
free(old_node->data);
free(old_node);
- goto next;
}
+ }
+ else if (strcmp(word, "-") == 0) /* a single - is a legal argument, not *
+ * a parameter. Protect it */
+ {
+ free(node->data);
+ node->data = xstrdup("\\-");
+ }
- next:
prev_word = node->data;
node = node->next;
}
@@ -2702,9 +2741,17 @@ ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
| parameter in this context. */
if (popt->args)
+ {
/* The parameter may be followed by arguments */
/* '''''''''''''''''''''''''''''''''''''''''' */
- word = xstrdup(par_name + pos);
+ if (*(par_name + pos) == '-')
+ {
+ word = xstrdup("\\"); /* Protect the '-' */
+ word = strappend(word, par_name + pos, NULL);
+ }
+ else
+ word = xstrdup(par_name + pos);
+ }
else
{
/* The parameter does not take arguments, the */
@@ -2729,7 +2776,6 @@ ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
if (ctx_inst->prev_ctx_inst == NULL)
{
char * errmsg = xstrdup("");
- ;
*user_string = '\0';
*user_string2 = '\0';
@@ -2743,11 +2789,9 @@ ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
errmsg = strappend(
errmsg,
"\nIt appears to be defined in the context(s):", user_string2,
- "\n'smenu -h' or 'smenu -H' can be helpful here.", NULL);
+ "\nAdd -h or -H for more help.", NULL);
}
- // fatal(CTXOPTUNKPAR, par_name, NULL, NULL, ctx_inst->par_name,
- // ctx_inst->ctx->name, errmsg, par_name);
fatal(CTXOPTUNKPAR, errmsg);
}
else
@@ -2923,7 +2967,11 @@ ctxopt_analyze(int nb_words, char ** words, int * nb_rem_args,
/* If the argument is valid, store it */
/* """""""""""""""""""""""""""""""""" */
- ll_append(opt_inst->values_list, par_name);
+ if (*par_name == '\\' && *(par_name + 1) != '\0'
+ && *(par_name + 1) == '-')
+ ll_append(opt_inst->values_list, par_name + 1);
+ else
+ ll_append(opt_inst->values_list, par_name);
expect_arg = 0;
expect_par = 0;
@@ -3157,7 +3205,7 @@ ctxopt_ctx_disp_usage(char * ctx_name, usage_behaviour action)
printf("> : given after the parameters, sets the next "
"context to go.\n");
if (has_generic_arg)
- printf("# : generic name for an argument.\n");
+ printf("#tag : argument tag giving a clue to its meaning.\n");
if (has_optional)
printf("[...] : the object is square brackets is optional.\n");
if (has_ellipsis)
@@ -3260,8 +3308,8 @@ ctxopt_format_constraint(int nb_args, char ** args, char * value)
rc = sscanf(value, format, x, &y);
if (rc != 1)
- error("The argument %s does not respect the imposed format: %s", value,
- args[0]);
+ fatal_internal("The argument %s does not respect the imposed format: %s",
+ value, args[0]);
free(format);
@@ -3287,9 +3335,9 @@ ctxopt_re_constraint(int nb_args, char ** args, char * value)
if (regexec(&re, value, (size_t)0, NULL, 0) != 0)
{
- error("The argument %s doesn't match the constraining "
- "regular expression %s",
- value, args[0]);
+ fatal_internal("The argument %s doesn't match the constraining "
+ "regular expression %s",
+ value, args[0]);
return 0;
}
@@ -3317,7 +3365,7 @@ ctxopt_range_constraint(int nb_args, char ** args, char * value)
if (nb_args != 2)
fatal_internal("Range constraint: invalid number of parameters.");
- if (strcmp(args[0], "-") == 0)
+ if (strcmp(args[0], ".") == 0)
max_only = 1;
else
n = sscanf(args[0], "%ld%c", &min, &c);
@@ -3325,7 +3373,7 @@ ctxopt_range_constraint(int nb_args, char ** args, char * value)
if (!max_only && n != 1)
fatal_internal("Range constraint: min: invalid parameters.");
- if (strcmp(args[1], "-") == 0)
+ if (strcmp(args[1], ".") == 0)
min_only = 1;
else
n = sscanf(args[1], "%ld%c", &max, &c);
@@ -3345,7 +3393,8 @@ ctxopt_range_constraint(int nb_args, char ** args, char * value)
{
if (v < min)
{
- error("%ld is not greater or equal to %ld as requested.", v, min);
+ fatal_internal("%ld is not greater or equal to %ld as requested.", v,
+ min);
return 0;
}
else
@@ -3355,7 +3404,7 @@ ctxopt_range_constraint(int nb_args, char ** args, char * value)
{
if (v > max)
{
- error("%ld is not lower or equal to %ld as requested.", v, max);
+ fatal_internal("%ld is not lower or equal to %ld as requested.", v, max);
return 0;
}
else
@@ -3363,7 +3412,7 @@ ctxopt_range_constraint(int nb_args, char ** args, char * value)
}
else if (v < min || v > max)
{
- error("%ld is not between %ld and %ld as requested.", v, min, max);
+ fatal_internal("%ld is not between %ld and %ld as requested.", v, min, max);
return 0;
}
@@ -3446,8 +3495,6 @@ ctxopt_add_opt_settings(settings s, ...)
{
ptr = va_arg(args, char *);
params = ptr;
- ltrim(params, " \t");
- rtrim(params, " \t", 0);
if (!opt_set_parms(opt_name, params))
fatal_internal(
@@ -3613,8 +3660,8 @@ ctxopt_add_ctx_settings(settings s, ...)
ptr = va_arg(args, char *);
if ((ctx = locate_ctx(ptr)) != NULL)
{
- function = va_arg(args, int (*)(char *, direction, char *, char *, int,
- void **));
+ function = va_arg(args,
+ int (*)(char *, direction, char *, int, void **));
ctx->action = function;
while ((data = va_arg(args, void *)) != NULL)