Rework argument parsing.
Heavily simplify the interface of CLI. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
+284
-280
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cli.h"
|
||||
|
||||
@@ -37,6 +37,180 @@
|
||||
*/
|
||||
#define CLI_LINE_TAB 24
|
||||
|
||||
/**
|
||||
* Declare a char VLA and concatenate a program name and a sub-command name
|
||||
* (separated by a single space) into the new array. Useful for printing command
|
||||
* line option usage summary for sub-commands.
|
||||
*
|
||||
* @param CMDNAME name of the new array variable.
|
||||
* @param PROGNAME string containing the name of the program.
|
||||
* @param CMD string continaing the name of the sub-command.
|
||||
*/
|
||||
#define CLI_CMD_NAME(CMDNAME, PROGNAME, CMD) \
|
||||
char CMDNAME[strlen ((PROGNAME)) + strlen ((CMD)) + 2]; \
|
||||
strncpy (CMDNAME, (PROGNAME), strlen ((PROGNAME))); \
|
||||
CMDNAME[strlen ((PROGNAME))] = ' '; \
|
||||
strncpy (CMDNAME + strlen ((PROGNAME)) + 1, (CMD), strlen ((CMD)) + 1)
|
||||
|
||||
/*
|
||||
* Command line option handling
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize a command line option processor.
|
||||
*
|
||||
* @return the state that should be passed to other cli_ functions.
|
||||
*/
|
||||
cli_state_t
|
||||
cli_init (const cli_opt_t *options, /**< array of option definitions, terminated by CLI_OPT_DEFAULT */
|
||||
int argc, /**< number of command line arguments */
|
||||
char **argv) /**< array of command line arguments */
|
||||
{
|
||||
return (cli_state_t)
|
||||
{
|
||||
.error = NULL,
|
||||
.arg = NULL,
|
||||
.argc = argc,
|
||||
.argv = argv,
|
||||
.opts = options
|
||||
};
|
||||
} /* cli_init */
|
||||
|
||||
/**
|
||||
* Checks whether the current argument is an option.
|
||||
*
|
||||
* Note:
|
||||
* The state->error is not NULL on error and it contains the error message.
|
||||
*
|
||||
* @return the ID of the option that was found or a CLI_OPT_ constant otherwise.
|
||||
*/
|
||||
int
|
||||
cli_consume_option (cli_state_t *state) /**< state of the command line option processor */
|
||||
{
|
||||
if (state->error != NULL)
|
||||
{
|
||||
return CLI_OPT_END;
|
||||
}
|
||||
|
||||
if (state->argc <= 0)
|
||||
{
|
||||
state->arg = NULL;
|
||||
return CLI_OPT_END;
|
||||
}
|
||||
|
||||
const char *arg = state->argv[0];
|
||||
|
||||
state->arg = arg;
|
||||
|
||||
if (arg[0] != '-')
|
||||
{
|
||||
return CLI_OPT_DEFAULT;
|
||||
}
|
||||
|
||||
if (arg[1] == '-')
|
||||
{
|
||||
arg += 2;
|
||||
|
||||
for (const cli_opt_t *opt = state->opts; opt->id != CLI_OPT_DEFAULT; opt++)
|
||||
{
|
||||
if (opt->longopt != NULL && strcmp (arg, opt->longopt) == 0)
|
||||
{
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
return opt->id;
|
||||
}
|
||||
}
|
||||
|
||||
state->error = "Unknown long option";
|
||||
return CLI_OPT_END;
|
||||
}
|
||||
|
||||
arg++;
|
||||
|
||||
for (const cli_opt_t *opt = state->opts; opt->id != CLI_OPT_DEFAULT; opt++)
|
||||
{
|
||||
if (opt->opt != NULL && strcmp (arg, opt->opt) == 0)
|
||||
{
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
return opt->id;
|
||||
}
|
||||
}
|
||||
|
||||
state->error = "Unknown option";
|
||||
return CLI_OPT_END;
|
||||
} /* cli_consume_option */
|
||||
|
||||
/**
|
||||
* Returns the next argument as string.
|
||||
*
|
||||
* Note:
|
||||
* The state->error is not NULL on error and it contains the error message.
|
||||
*
|
||||
* @return argument string
|
||||
*/
|
||||
const char *
|
||||
cli_consume_string (cli_state_t *state) /**< state of the command line option processor */
|
||||
{
|
||||
if (state->error != NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (state->argc <= 0)
|
||||
{
|
||||
state->error = "Expected string argument";
|
||||
state->arg = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state->arg = state->argv[0];
|
||||
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
return state->arg;
|
||||
} /* cli_consume_string */
|
||||
|
||||
/**
|
||||
* Returns the next argument as integer.
|
||||
*
|
||||
* Note:
|
||||
* The state->error is not NULL on error and it contains the error message.
|
||||
*
|
||||
* @return argument integer
|
||||
*/
|
||||
int
|
||||
cli_consume_int (cli_state_t *state) /**< state of the command line option processor */
|
||||
{
|
||||
if (state->error != NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->error = "Expected integer argument";
|
||||
|
||||
if (state->argc <= 0)
|
||||
{
|
||||
state->arg = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->arg = state->argv[0];
|
||||
|
||||
char *endptr;
|
||||
long int value = strtol (state->arg, &endptr, 10);
|
||||
|
||||
if (*endptr != '\0')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->error = NULL;
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
return (int) value;
|
||||
} /* cli_consume_int */
|
||||
|
||||
/*
|
||||
* Print helper functions
|
||||
*/
|
||||
@@ -66,6 +240,78 @@ cli_print_prefix (const char *str, /**< string to print */
|
||||
}
|
||||
} /* cli_print_prefix */
|
||||
|
||||
/**
|
||||
* Print usage summary of options.
|
||||
*/
|
||||
static void
|
||||
cli_opt_usage (const char *progname, /**< program name, typically argv[0] */
|
||||
const cli_opt_t *opts) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */
|
||||
{
|
||||
int length = (int) strlen (progname);
|
||||
const cli_opt_t *o = opts;
|
||||
|
||||
printf ("%s", progname);
|
||||
|
||||
while (o->id != CLI_OPT_DEFAULT)
|
||||
{
|
||||
const char *opt = o->opt;
|
||||
int opt_length = 2 + 1;
|
||||
|
||||
if (opt == NULL)
|
||||
{
|
||||
opt = o->longopt;
|
||||
opt_length++;
|
||||
}
|
||||
|
||||
opt_length += (int) strlen (opt);
|
||||
|
||||
if (length + 1 + opt_length >= CLI_LINE_LENGTH)
|
||||
{
|
||||
length = CLI_LINE_INDENT - 1;
|
||||
printf ("\n");
|
||||
cli_print_pad (length);
|
||||
}
|
||||
length += opt_length;
|
||||
|
||||
printf (" [");
|
||||
|
||||
if (o->opt != NULL)
|
||||
{
|
||||
printf ("-%s", opt);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("--%s", opt);
|
||||
}
|
||||
|
||||
if (o->meta != NULL)
|
||||
{
|
||||
printf (" %s", o->meta);
|
||||
}
|
||||
|
||||
printf ("]");
|
||||
|
||||
o++;
|
||||
}
|
||||
|
||||
if (o->meta != NULL)
|
||||
{
|
||||
const char *opt = o->meta;
|
||||
int opt_length = (int) (2 + strlen (opt));
|
||||
|
||||
if (length + 1 + opt_length >= CLI_LINE_LENGTH)
|
||||
{
|
||||
length = CLI_LINE_INDENT - 1;
|
||||
printf ("\n");
|
||||
cli_print_pad (length);
|
||||
}
|
||||
|
||||
printf (" [%s]", opt);
|
||||
}
|
||||
|
||||
printf ("\n\n");
|
||||
} /* cli_opt_usage */
|
||||
|
||||
/**
|
||||
* Print a help message wrapped into the second column.
|
||||
*/
|
||||
@@ -104,205 +350,47 @@ cli_print_help (const char *help) /**< the help message to print */
|
||||
}
|
||||
} /* cli_print_help */
|
||||
|
||||
/*
|
||||
* Command line option handling
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize a command line option processor.
|
||||
*
|
||||
* @return the state that should be passed iteratively to cli_opt_process.
|
||||
*/
|
||||
cli_opt_state_t
|
||||
cli_opt_init (const cli_opt_t *opts, /**< array of option definitions, terminated by CLI_OPT_END */
|
||||
int argc, /**< number of command line arguments */
|
||||
char **argv) /**< array of command line arguments */
|
||||
{
|
||||
return (cli_opt_state_t)
|
||||
{
|
||||
.opts = opts,
|
||||
.argc = argc,
|
||||
.argv = argv,
|
||||
.opt = NULL,
|
||||
.arg = NULL
|
||||
};
|
||||
} /* cli_opt_init */
|
||||
|
||||
/**
|
||||
* Perform one step of the command line option processor.
|
||||
*
|
||||
* @return the ID of the option that was found. (The definition of the found
|
||||
* option (if any) is available via state->opt, while the option
|
||||
* string and its arguments are available via state->arg[0..].)
|
||||
* CLI_OPT_END signals that all command line arguments are consumed.
|
||||
*/
|
||||
int
|
||||
cli_opt_process (cli_opt_state_t *state) /**< state of the command line option processor */
|
||||
{
|
||||
if (state->argc <= 0)
|
||||
{
|
||||
state->opt = NULL;
|
||||
state->arg = NULL;
|
||||
return CLI_OPT_END;
|
||||
}
|
||||
|
||||
state->arg = state->argv;
|
||||
|
||||
for (const cli_opt_t *o = state->opts; o->id != CLI_OPT_END; o++)
|
||||
{
|
||||
state->opt = o;
|
||||
|
||||
if (o->id == CLI_OPT_POSITIONAL && (state->arg[0][0] != '-' || !strcmp (state->arg[0], "-")))
|
||||
{
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
return CLI_OPT_POSITIONAL;
|
||||
}
|
||||
else if ((o->opt != NULL && !strcmp (o->opt, state->arg[0]))
|
||||
|| (o->longopt != NULL && !strcmp (o->longopt, state->arg[0])))
|
||||
{
|
||||
if (state->argc > o->argc)
|
||||
{
|
||||
state->argc -= 1 + o->argc;
|
||||
state->argv += 1 + o->argc;
|
||||
return o->id;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->argv += state->argc;
|
||||
state->argc = 0;
|
||||
return CLI_OPT_INCOMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state->opt = NULL;
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
return CLI_OPT_UNKNOWN;
|
||||
} /* cli_opt_process */
|
||||
|
||||
/**
|
||||
* Print usage summary of options.
|
||||
*/
|
||||
void
|
||||
cli_opt_usage (const char *progname, /**< program name, typically argv[0] */
|
||||
const cli_opt_t *opts) /**< array of command line option definitions, terminated by CLI_OPT_END */
|
||||
{
|
||||
int length = (int) strlen (progname);
|
||||
printf ("%s", progname);
|
||||
|
||||
for (const cli_opt_t *o = opts; o->id != CLI_OPT_END; o++)
|
||||
{
|
||||
const char *opt = o->opt != NULL ? o->opt : o->longopt;
|
||||
opt = opt != NULL ? opt : o->meta;
|
||||
|
||||
int opt_length = (int) strlen (opt);
|
||||
if (o->argc > 0)
|
||||
{
|
||||
opt_length += o->meta != NULL ? 1 + (int) strlen (o->meta) : o->argc * 2;
|
||||
}
|
||||
opt_length += o->quant == CLI_QUANT_Q || o->quant == CLI_QUANT_A ? 2 : 0;
|
||||
opt_length += o->quant == CLI_QUANT_P || o->quant == CLI_QUANT_A ? 3 : 0;
|
||||
|
||||
if (length + 1 + opt_length >= CLI_LINE_LENGTH)
|
||||
{
|
||||
length = CLI_LINE_INDENT - 1;
|
||||
printf ("\n");
|
||||
cli_print_pad (length);
|
||||
}
|
||||
length += opt_length;
|
||||
|
||||
printf (" ");
|
||||
|
||||
if (o->quant == CLI_QUANT_Q || o->quant == CLI_QUANT_A)
|
||||
{
|
||||
printf ("[");
|
||||
}
|
||||
|
||||
printf ("%s", opt);
|
||||
|
||||
if (o->argc > 0)
|
||||
{
|
||||
if (o->meta != NULL)
|
||||
{
|
||||
printf (" %s", o->meta);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < o->argc; i++)
|
||||
{
|
||||
printf (" _");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (o->quant == CLI_QUANT_Q || o->quant == CLI_QUANT_A)
|
||||
{
|
||||
printf ("]");
|
||||
}
|
||||
|
||||
if (o->quant == CLI_QUANT_P || o->quant == CLI_QUANT_A)
|
||||
{
|
||||
printf ("...");
|
||||
}
|
||||
}
|
||||
|
||||
printf ("\n");
|
||||
} /* cli_opt_usage */
|
||||
|
||||
/**
|
||||
* Print detailed help for options.
|
||||
*/
|
||||
void
|
||||
cli_opt_help (const cli_opt_t *opts) /**< array of command line option definitions, terminated by CLI_OPT_END */
|
||||
cli_help (const char *progname, /**< program name, typically argv[0] */
|
||||
const cli_opt_t *options) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */
|
||||
{
|
||||
for (const cli_opt_t *o = opts; o->id != CLI_OPT_END; o++)
|
||||
cli_opt_usage (progname, options);
|
||||
|
||||
const cli_opt_t *opt = options;
|
||||
|
||||
while (opt->id != CLI_OPT_DEFAULT)
|
||||
{
|
||||
int length = CLI_LINE_INDENT;
|
||||
cli_print_pad (length);
|
||||
cli_print_pad (CLI_LINE_INDENT);
|
||||
|
||||
if (o->opt != NULL)
|
||||
if (opt->opt != NULL)
|
||||
{
|
||||
printf ("%s", o->opt);
|
||||
length += (int) strlen (o->opt);
|
||||
printf ("-%s", opt->opt);
|
||||
length += (int) (strlen (opt->opt) + 1);
|
||||
}
|
||||
|
||||
if (o->opt != NULL && o->longopt != NULL)
|
||||
if (opt->opt != NULL && opt->longopt != NULL)
|
||||
{
|
||||
printf (", ");
|
||||
length += 2;
|
||||
}
|
||||
|
||||
if (o->longopt != NULL)
|
||||
if (opt->longopt != NULL)
|
||||
{
|
||||
printf ("%s", o->longopt);
|
||||
length += (int) strlen (o->longopt);
|
||||
printf ("--%s", opt->longopt);
|
||||
length += (int) (strlen (opt->longopt) + 2);
|
||||
}
|
||||
|
||||
if (o->opt == NULL && o->longopt == NULL)
|
||||
if (opt->meta != NULL)
|
||||
{
|
||||
printf ("%s", o->meta);
|
||||
length += (int) strlen (o->meta);
|
||||
}
|
||||
else if (o->argc > 0)
|
||||
{
|
||||
if (o->meta != NULL)
|
||||
{
|
||||
printf (" %s", o->meta);
|
||||
length += 1 + (int) strlen (o->meta);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < o->argc; i++)
|
||||
{
|
||||
printf (" _");
|
||||
}
|
||||
length += o->argc * 2;
|
||||
}
|
||||
printf (" %s", opt->meta);
|
||||
length += 1 + (int) strlen (opt->meta);
|
||||
}
|
||||
|
||||
if (o->help != NULL)
|
||||
if (opt->help != NULL)
|
||||
{
|
||||
if (length >= CLI_LINE_TAB)
|
||||
{
|
||||
@@ -312,118 +400,34 @@ cli_opt_help (const cli_opt_t *opts) /**< array of command line option definitio
|
||||
cli_print_pad (CLI_LINE_TAB - length);
|
||||
length = CLI_LINE_TAB;
|
||||
|
||||
cli_print_help (o->help);
|
||||
cli_print_help (opt->help);
|
||||
}
|
||||
|
||||
printf ("\n");
|
||||
}
|
||||
} /* cli_opt_help */
|
||||
|
||||
/*
|
||||
* Sub-command handling
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize a sub-command processor.
|
||||
*
|
||||
* @return the state that should be passed to cli_cmd_process.
|
||||
*/
|
||||
cli_cmd_state_t
|
||||
cli_cmd_init (const cli_cmd_t *cmds, /**< array of sub-command definitions, terminated by CLI_CMD_END */
|
||||
int argc, /**< number of command line arguments */
|
||||
char **argv) /**< array of command line arguments */
|
||||
{
|
||||
return (cli_cmd_state_t)
|
||||
{
|
||||
.cmds = cmds,
|
||||
.argc = argc,
|
||||
.argv = argv,
|
||||
.cmd = NULL,
|
||||
.arg = NULL
|
||||
};
|
||||
} /* cli_cmd_init */
|
||||
|
||||
/**
|
||||
* Process first element of the command line and determine whether it is a
|
||||
* defined sub-command or not.
|
||||
*
|
||||
* @return the ID of the sub-command that was found.
|
||||
*/
|
||||
int
|
||||
cli_cmd_process (cli_cmd_state_t *state) /**< state of the sub-command processor */
|
||||
{
|
||||
if (state->argc <= 0 || state->argv[0][0] == '-')
|
||||
{
|
||||
state->cmd = NULL;
|
||||
state->arg = NULL;
|
||||
return CLI_CMD_NONE;
|
||||
opt++;
|
||||
}
|
||||
|
||||
state->arg = state->argv;
|
||||
|
||||
for (const cli_cmd_t *c = state->cmds; c->id != CLI_CMD_END; c++)
|
||||
if (opt->help != NULL)
|
||||
{
|
||||
state->cmd = c;
|
||||
int length = 0;
|
||||
|
||||
if (!strcmp (c->cmd, state->argv[0]))
|
||||
if (opt->meta != NULL)
|
||||
{
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
state->cmd = c;
|
||||
return c->id;
|
||||
}
|
||||
}
|
||||
length = (int) (CLI_LINE_INDENT + strlen (opt->meta));
|
||||
|
||||
state->cmd = NULL;
|
||||
state->argc--;
|
||||
state->argv++;
|
||||
return CLI_CMD_UNKNOWN;
|
||||
} /* cli_cmd_process */
|
||||
|
||||
/**
|
||||
* Print usage summary of all sub-commands.
|
||||
*/
|
||||
void
|
||||
cli_cmd_usage (const char *progname, /**< program name, typically argv[0] */
|
||||
const cli_cmd_t *cmds) /**< array of sub-command definitions, terminated by CLI_CMD_END */
|
||||
{
|
||||
for (const cli_cmd_t *c = cmds; c->id != CLI_CMD_END; c++)
|
||||
{
|
||||
if (c->cmd != NULL)
|
||||
{
|
||||
CLI_CMD_NAME (cmdname, progname, c->cmd);
|
||||
cli_opt_usage (cmdname, c->opts);
|
||||
}
|
||||
}
|
||||
} /* cli_cmd_usage */
|
||||
|
||||
/**
|
||||
* Print help of all sub-commands.
|
||||
*/
|
||||
void
|
||||
cli_cmd_help (const cli_cmd_t *cmds) /**< array of sub-command definitions, terminated by CLI_CMD_END */
|
||||
{
|
||||
for (const cli_cmd_t *c = cmds; c->id != CLI_CMD_END; c++)
|
||||
{
|
||||
int length = CLI_LINE_INDENT;
|
||||
cli_print_pad (length);
|
||||
|
||||
printf ("%s", c->cmd);
|
||||
length += (int) strlen (c->cmd);
|
||||
|
||||
if (c->help != NULL)
|
||||
{
|
||||
if (length >= CLI_LINE_TAB)
|
||||
{
|
||||
printf ("\n");
|
||||
length = 0;
|
||||
}
|
||||
cli_print_pad (CLI_LINE_TAB - length);
|
||||
length = CLI_LINE_TAB;
|
||||
|
||||
cli_print_help (c->help);
|
||||
cli_print_pad (CLI_LINE_INDENT);
|
||||
printf ("%s", opt->meta);
|
||||
}
|
||||
|
||||
if (length >= CLI_LINE_TAB)
|
||||
{
|
||||
printf ("\n");
|
||||
length = 0;
|
||||
}
|
||||
|
||||
cli_print_pad (CLI_LINE_TAB - length);
|
||||
|
||||
cli_print_help (opt->help);
|
||||
printf ("\n");
|
||||
}
|
||||
} /* cli_cmd_help */
|
||||
} /* cli_help */
|
||||
|
||||
+26
-102
@@ -18,134 +18,58 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Types for CLI
|
||||
*/
|
||||
|
||||
/**
|
||||
* Command line option definition
|
||||
* Command line option definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int id; /**< unique ID of the option (CLI_OPT_END, CLI_OPT_POSITIONAL, or anything >= 0) */
|
||||
const char *opt; /**< short option variant (in the form of "-x") */
|
||||
const char *longopt; /**< long option variant (in the form of "--xxx") */
|
||||
int argc; /**< number of arguments of the option */
|
||||
int id; /**< unique ID of the option (CLI_OPT_DEFAULT, or anything >= 0) */
|
||||
const char *opt; /**< short option variant (in the form of "x" without dashes) */
|
||||
const char *longopt; /**< long option variant (in the form of "xxx" without dashes) */
|
||||
const char *meta; /**< name(s) of the argument(s) of the option, for display only */
|
||||
int quant; /**< quantifier of the option (CLI_QUANT_{Q,A,P,1} for ?,*,+,1), for display only */
|
||||
const char *help; /**< descriptive help message of the option */
|
||||
} cli_opt_t;
|
||||
|
||||
/**
|
||||
* Quantifiers of command line options
|
||||
* Special marker for default option which also marks the end of the option list.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
CLI_QUANT_Q, /**< ? (Question mark: optional, zero or one) */
|
||||
CLI_QUANT_A, /**< * (Asterisk, star: zero or one or more) */
|
||||
CLI_QUANT_P, /**< + (Plus sign: one or more) */
|
||||
CLI_QUANT_1 /**< 1 (One: one) */
|
||||
} cli_opt_quant_t;
|
||||
#define CLI_OPT_DEFAULT -1
|
||||
|
||||
/**
|
||||
* Common command line option IDs
|
||||
* Returned by cli_consume_option () when no more options are available
|
||||
* or an error occured.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
CLI_OPT_POSITIONAL = -1, /**< positional "option" */
|
||||
CLI_OPT_END = -2, /**< end of options marker */
|
||||
CLI_OPT_INCOMPLETE = -3, /**< incomplete option (too few arguments) */
|
||||
CLI_OPT_UNKNOWN = -4 /**< unknown option */
|
||||
} cli_opt_id_t;
|
||||
|
||||
/**
|
||||
* State of the command line option processor.
|
||||
* No fields should be accessed other than arg and opt.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
const cli_opt_t *opts;
|
||||
int argc;
|
||||
char **argv;
|
||||
const cli_opt_t *opt; /**< found option or NULL */
|
||||
char **arg; /**< array of strings for the last processed option */
|
||||
} cli_opt_state_t;
|
||||
|
||||
/**
|
||||
* Sub-command definition
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int id; /**< unique ID of the sub-command (CLI_CMD_END, or anything >= 0) */
|
||||
const char *cmd; /**< sub-command name (in the form of "xxx") */
|
||||
const cli_opt_t *opts; /**< array of associated command line option definitions, for display only */
|
||||
const char *help; /**< descriptive help message of the sub-command */
|
||||
} cli_cmd_t;
|
||||
|
||||
/**
|
||||
* Common sub-command IDs
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
CLI_CMD_END = -1, /**< end of sub-commands marker */
|
||||
CLI_CMD_NONE = -1, /**< no sub-command */
|
||||
CLI_CMD_UNKNOWN = -2 /**< unknown sub-command */
|
||||
} cli_cmd_id_t;
|
||||
#define CLI_OPT_END -2
|
||||
|
||||
/**
|
||||
* State of the sub-command processor.
|
||||
* No fields should be accessed other than arg and cmd.
|
||||
* No fields should be accessed other than error and arg.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
const cli_cmd_t *cmds;
|
||||
int argc;
|
||||
char **argv;
|
||||
const cli_cmd_t *cmd; /**< found command or NULL */
|
||||
char **arg; /**< array of strings for the processed command */
|
||||
} cli_cmd_state_t;
|
||||
/* Public fields. */
|
||||
const char *error; /**< public field for error message */
|
||||
const char *arg; /**< last processed argument as string */
|
||||
|
||||
/*
|
||||
* Functions for CLI
|
||||
*/
|
||||
|
||||
cli_opt_state_t cli_opt_init (const cli_opt_t *opts, int argc, char **argv);
|
||||
int cli_opt_process (cli_opt_state_t *state);
|
||||
void cli_opt_usage (const char *progname, const cli_opt_t *opts);
|
||||
void cli_opt_help (const cli_opt_t *opts);
|
||||
|
||||
cli_cmd_state_t cli_cmd_init (const cli_cmd_t *cmds, int argc, char **argv);
|
||||
int cli_cmd_process (cli_cmd_state_t *state);
|
||||
void cli_cmd_help (const cli_cmd_t *cmds);
|
||||
void cli_cmd_usage (const char *progname, const cli_cmd_t *cmds);
|
||||
|
||||
/*
|
||||
* Useful macros for CLI
|
||||
*/
|
||||
/* Private fields. */
|
||||
int argc; /**< remaining number of arguments */
|
||||
char **argv; /**< remaining arguments */
|
||||
const cli_opt_t *opts; /**< options */
|
||||
} cli_state_t;
|
||||
|
||||
/**
|
||||
* Macro for writing command line option definition struct literals
|
||||
* Macro for writing command line option definition struct literals.
|
||||
*/
|
||||
#define CLI_OPT_DEF(...) /*(cli_opt_t)*/ { __VA_ARGS__ }
|
||||
|
||||
/**
|
||||
* Macro for writing sub-command definition struct literals
|
||||
/*
|
||||
* Functions for CLI.
|
||||
*/
|
||||
#define CLI_CMD_DEF(...) /*(cli_cmd_t)*/ { __VA_ARGS__ }
|
||||
|
||||
/**
|
||||
* Declare a char VLA and concatenate a program name and a sub-command name
|
||||
* (separated by a single space) into the new array. Useful for printing command
|
||||
* line option usage summary for sub-commands.
|
||||
*
|
||||
* @param CMDNAME name of the new array variable.
|
||||
* @param PROGNAME string containing the name of the program.
|
||||
* @param CMD string continaing the name of the sub-command.
|
||||
*/
|
||||
#define CLI_CMD_NAME(CMDNAME, PROGNAME, CMD) \
|
||||
char CMDNAME[strlen ((PROGNAME)) + strlen ((CMD)) + 2]; \
|
||||
strncpy (CMDNAME, (PROGNAME), strlen ((PROGNAME))); \
|
||||
CMDNAME[strlen ((PROGNAME))] = ' '; \
|
||||
strncpy (CMDNAME + strlen ((PROGNAME)) + 1, (CMD), strlen ((CMD)) + 1)
|
||||
cli_state_t cli_init (const cli_opt_t *options, int argc, char **argv);
|
||||
int cli_consume_option (cli_state_t *state);
|
||||
const char * cli_consume_string (cli_state_t *state);
|
||||
int cli_consume_int (cli_state_t *state);
|
||||
void cli_help (const char *progname, const cli_opt_t *options);
|
||||
|
||||
#endif /* CLI_H */
|
||||
|
||||
+58
-51
@@ -292,41 +292,40 @@ typedef enum
|
||||
/**
|
||||
* Command line options
|
||||
*/
|
||||
static cli_opt_t main_opts[] =
|
||||
static const cli_opt_t main_opts[] =
|
||||
{
|
||||
CLI_OPT_DEF (.id = OPT_HELP, .opt = "-h", .longopt = "--help",
|
||||
CLI_OPT_DEF (.id = OPT_HELP, .opt = "h", .longopt = "help",
|
||||
.help = "print this help and exit"),
|
||||
CLI_OPT_DEF (.id = OPT_VERSION, .opt = "-v", .longopt = "--version",
|
||||
CLI_OPT_DEF (.id = OPT_VERSION, .opt = "v", .longopt = "version",
|
||||
.help = "print tool and library version and exit"),
|
||||
CLI_OPT_DEF (.id = OPT_MEM_STATS, .longopt = "--mem-stats",
|
||||
CLI_OPT_DEF (.id = OPT_MEM_STATS, .longopt = "mem-stats",
|
||||
.help = "dump memory statistics"),
|
||||
CLI_OPT_DEF (.id = OPT_PARSE_ONLY, .longopt = "--parse-only",
|
||||
CLI_OPT_DEF (.id = OPT_PARSE_ONLY, .longopt = "parse-only",
|
||||
.help = "don't execute JS input"),
|
||||
CLI_OPT_DEF (.id = OPT_SHOW_OP, .longopt = "--show-opcodes",
|
||||
CLI_OPT_DEF (.id = OPT_SHOW_OP, .longopt = "show-opcodes",
|
||||
.help = "dump parser byte-code"),
|
||||
CLI_OPT_DEF (.id = OPT_SHOW_RE_OP, .longopt = "--show-regexp-opcodes",
|
||||
CLI_OPT_DEF (.id = OPT_SHOW_RE_OP, .longopt = "show-regexp-opcodes",
|
||||
.help = "dump regexp byte-code"),
|
||||
CLI_OPT_DEF (.id = OPT_DEBUG_SERVER, .longopt = "--start-debug-server",
|
||||
CLI_OPT_DEF (.id = OPT_DEBUG_SERVER, .longopt = "start-debug-server",
|
||||
.help = "start debug server and wait for a connecting client"),
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_SNAP_GLOBAL, .longopt = "--save-snapshot-for-global", .argc = 1, .meta = "FILE",
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_SNAP_GLOBAL, .longopt = "save-snapshot-for-global", .meta = "FILE",
|
||||
.help = "save binary snapshot of parsed JS input (for execution in global context)"),
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_SNAP_EVAL, .longopt = "--save-snapshot-for-eval", .argc = 1, .meta = "FILE",
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_SNAP_EVAL, .longopt = "save-snapshot-for-eval", .meta = "FILE",
|
||||
.help = "save binary snapshot of parsed JS input (for execution in local context by eval)"),
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_LIT_LIST, .longopt = "--save-literals-list-format", .argc = 1, .meta = "FILE",
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_LIT_LIST, .longopt = "save-literals-list-format", .meta = "FILE",
|
||||
.help = "export literals found in parsed JS input (in list format)"),
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_LIT_C, .longopt = "--save-literals-c-format", .argc = 1, .meta = "FILE",
|
||||
CLI_OPT_DEF (.id = OPT_SAVE_LIT_C, .longopt = "save-literals-c-format", .meta = "FILE",
|
||||
.help = "export literals found in parsed JS input (in C source format)"),
|
||||
CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "--exec-snapshot", .argc = 1, .meta = "FILE", .quant = CLI_QUANT_A,
|
||||
CLI_OPT_DEF (.id = OPT_EXEC_SNAP, .longopt = "exec-snapshot", .meta = "FILE",
|
||||
.help = "execute input snapshot file(s)"),
|
||||
CLI_OPT_DEF (.id = OPT_LOG_LEVEL, .longopt = "--log-level", .argc = 1, .meta = "NUM",
|
||||
CLI_OPT_DEF (.id = OPT_LOG_LEVEL, .longopt = "log-level", .meta = "NUM",
|
||||
.help = "set log level (0-3)"),
|
||||
CLI_OPT_DEF (.id = OPT_ABORT_ON_FAIL, .longopt = "--abort-on-fail",
|
||||
CLI_OPT_DEF (.id = OPT_ABORT_ON_FAIL, .longopt = "abort-on-fail",
|
||||
.help = "segfault on internal failure (instead of non-zero exit code)"),
|
||||
CLI_OPT_DEF (.id = OPT_NO_PROMPT, .longopt = "--no-prompt",
|
||||
CLI_OPT_DEF (.id = OPT_NO_PROMPT, .longopt = "no-prompt",
|
||||
.help = "don't print prompt in REPL mode"),
|
||||
CLI_OPT_DEF (.id = CLI_OPT_POSITIONAL, .meta = "FILE", .quant = CLI_QUANT_A,
|
||||
.help = "input JS file(s)"),
|
||||
CLI_OPT_DEF (.id = CLI_OPT_END)
|
||||
CLI_OPT_DEF (.id = CLI_OPT_DEFAULT, .meta = "FILE",
|
||||
.help = "input JS file(s)")
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -360,8 +359,7 @@ check_usage (bool condition, /**< the condition that must hold */
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s%s\n", msg, opt != NULL ? opt : "");
|
||||
cli_opt_usage (name, main_opts);
|
||||
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "%s: %s%s\n", name, msg, opt != NULL ? opt : "");
|
||||
exit (JERRY_STANDALONE_EXIT_CODE_FAIL);
|
||||
}
|
||||
} /* check_usage */
|
||||
@@ -404,16 +402,14 @@ main (int argc,
|
||||
bool is_repl_mode = false;
|
||||
bool no_prompt = false;
|
||||
|
||||
cli_opt_state_t cli_state = cli_opt_init (main_opts, argc - 1, argv + 1);
|
||||
for (int id = cli_opt_process (&cli_state); id != CLI_OPT_END; id = cli_opt_process (&cli_state))
|
||||
cli_state_t cli_state = cli_init (main_opts, argc - 1, argv + 1);
|
||||
for (int id = cli_consume_option (&cli_state); id != CLI_OPT_END; id = cli_consume_option (&cli_state))
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case OPT_HELP:
|
||||
{
|
||||
cli_opt_usage (argv[0], main_opts);
|
||||
printf ("\n");
|
||||
cli_opt_help (main_opts);
|
||||
cli_help (argv[0], main_opts);
|
||||
return JERRY_STANDALONE_EXIT_CODE_OK;
|
||||
}
|
||||
case OPT_VERSION:
|
||||
@@ -423,7 +419,7 @@ main (int argc,
|
||||
}
|
||||
case OPT_MEM_STATS:
|
||||
{
|
||||
if (check_feature (JERRY_FEATURE_MEM_STATS, cli_state.arg[0]))
|
||||
if (check_feature (JERRY_FEATURE_MEM_STATS, cli_state.arg))
|
||||
{
|
||||
jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG);
|
||||
flags |= JERRY_INIT_MEM_STATS;
|
||||
@@ -437,7 +433,7 @@ main (int argc,
|
||||
}
|
||||
case OPT_SHOW_OP:
|
||||
{
|
||||
if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state.arg[0]))
|
||||
if (check_feature (JERRY_FEATURE_PARSER_DUMP, cli_state.arg))
|
||||
{
|
||||
jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG);
|
||||
flags |= JERRY_INIT_SHOW_OPCODES;
|
||||
@@ -446,7 +442,7 @@ main (int argc,
|
||||
}
|
||||
case OPT_SHOW_RE_OP:
|
||||
{
|
||||
if (check_feature (JERRY_FEATURE_REGEXP_DUMP, cli_state.arg[0]))
|
||||
if (check_feature (JERRY_FEATURE_REGEXP_DUMP, cli_state.arg))
|
||||
{
|
||||
jerry_port_default_set_log_level (JERRY_LOG_LEVEL_DEBUG);
|
||||
flags |= JERRY_INIT_SHOW_REGEXP_OPCODES;
|
||||
@@ -455,7 +451,7 @@ main (int argc,
|
||||
}
|
||||
case OPT_DEBUG_SERVER:
|
||||
{
|
||||
if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg[0]))
|
||||
if (check_feature (JERRY_FEATURE_DEBUGGER, cli_state.arg))
|
||||
{
|
||||
flags |= JERRY_INIT_DEBUGGER;
|
||||
}
|
||||
@@ -465,40 +461,43 @@ main (int argc,
|
||||
case OPT_SAVE_SNAP_EVAL:
|
||||
{
|
||||
check_usage (save_snapshot_file_name_p == NULL, argv[0], "Error: snapshot file name already specified", NULL);
|
||||
if (check_feature (JERRY_FEATURE_SNAPSHOT_SAVE, cli_state.arg[0]))
|
||||
if (check_feature (JERRY_FEATURE_SNAPSHOT_SAVE, cli_state.arg))
|
||||
{
|
||||
is_save_snapshot_mode = true;
|
||||
is_save_snapshot_mode_for_global_or_eval = cli_state.opt->id == OPT_SAVE_SNAP_GLOBAL;
|
||||
save_snapshot_file_name_p = cli_state.arg[1];
|
||||
is_save_snapshot_mode_for_global_or_eval = (id == OPT_SAVE_SNAP_GLOBAL);
|
||||
}
|
||||
save_snapshot_file_name_p = cli_consume_string (&cli_state);
|
||||
break;
|
||||
}
|
||||
case OPT_SAVE_LIT_LIST:
|
||||
case OPT_SAVE_LIT_C:
|
||||
{
|
||||
check_usage (save_literals_file_name_p == NULL, argv[0], "Error: literal file name already specified", NULL);
|
||||
if (check_feature (JERRY_FEATURE_SNAPSHOT_SAVE, cli_state.arg[0]))
|
||||
if (check_feature (JERRY_FEATURE_SNAPSHOT_SAVE, cli_state.arg))
|
||||
{
|
||||
is_save_literals_mode = true;
|
||||
is_save_literals_mode_in_c_format_or_list = cli_state.opt->id == OPT_SAVE_LIT_C;
|
||||
save_literals_file_name_p = cli_state.arg[1];
|
||||
is_save_literals_mode_in_c_format_or_list = (id == OPT_SAVE_LIT_C);
|
||||
}
|
||||
save_literals_file_name_p = cli_consume_string (&cli_state);
|
||||
break;
|
||||
}
|
||||
case OPT_EXEC_SNAP:
|
||||
{
|
||||
if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg[0]))
|
||||
if (check_feature (JERRY_FEATURE_SNAPSHOT_EXEC, cli_state.arg))
|
||||
{
|
||||
exec_snapshot_file_names[exec_snapshots_count++] = cli_state.arg[1];
|
||||
exec_snapshot_file_names[exec_snapshots_count++] = cli_consume_string (&cli_state);
|
||||
}
|
||||
else
|
||||
{
|
||||
cli_consume_string (&cli_state);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OPT_LOG_LEVEL:
|
||||
{
|
||||
char *endptr;
|
||||
long int log_level = strtol (cli_state.arg[1], &endptr, 10);
|
||||
check_usage (log_level >= 0 && log_level <= 3 && !*endptr,
|
||||
argv[0], "Error: wrong format for ", cli_state.arg[0]);
|
||||
long int log_level = cli_consume_int (&cli_state);
|
||||
check_usage (log_level >= 0 && log_level <= 3,
|
||||
argv[0], "Error: invalid value for --log-level: ", cli_state.arg);
|
||||
|
||||
jerry_port_default_set_log_level ((jerry_log_level_t) log_level);
|
||||
break;
|
||||
@@ -513,25 +512,33 @@ main (int argc,
|
||||
no_prompt = true;
|
||||
break;
|
||||
}
|
||||
case CLI_OPT_POSITIONAL:
|
||||
case CLI_OPT_DEFAULT:
|
||||
{
|
||||
file_names[files_counter++] = cli_state.arg[0];
|
||||
file_names[files_counter++] = cli_consume_string (&cli_state);
|
||||
break;
|
||||
}
|
||||
case CLI_OPT_INCOMPLETE:
|
||||
{
|
||||
check_usage (false, argv[0], "Error: incomplete option: ", cli_state.arg[0]);
|
||||
break;
|
||||
}
|
||||
case CLI_OPT_UNKNOWN:
|
||||
default:
|
||||
{
|
||||
check_usage (false, argv[0], "Error: unrecognized option: ", cli_state.arg[0]);
|
||||
cli_state.error = "Internal error";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cli_state.error != NULL)
|
||||
{
|
||||
if (cli_state.arg != NULL)
|
||||
{
|
||||
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s %s\n", cli_state.error, cli_state.arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Error: %s\n", cli_state.error);
|
||||
}
|
||||
|
||||
return JERRY_STANDALONE_EXIT_CODE_FAIL;
|
||||
}
|
||||
|
||||
if (is_save_snapshot_mode)
|
||||
{
|
||||
check_usage (files_counter == 1,
|
||||
|
||||
Reference in New Issue
Block a user