forked from xuos/xiuos
476 lines
9.9 KiB
C
476 lines
9.9 KiB
C
/* Copyright JS Foundation and other contributors, http://js.foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "cli.h"
|
|
|
|
/*
|
|
* Fixed layout settings
|
|
*/
|
|
|
|
/**
|
|
* Wrap lines at:
|
|
*/
|
|
#define CLI_LINE_LENGTH 80
|
|
|
|
/**
|
|
* Indent various lines with:
|
|
*/
|
|
#define CLI_LINE_INDENT 2
|
|
|
|
/**
|
|
* Tab stop (for multi-column display) at:
|
|
*/
|
|
#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_p, /**< 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,
|
|
.index = 1,
|
|
.argc = argc,
|
|
.argv = argv,
|
|
.opts = options_p
|
|
};
|
|
} /* cli_init */
|
|
|
|
/**
|
|
* Use another option list.
|
|
*/
|
|
void
|
|
cli_change_opts (cli_state_t *state_p, /**< state of the command line option processor */
|
|
const cli_opt_t *options_p) /**< array of option definitions, terminated by CLI_OPT_DEFAULT */
|
|
{
|
|
state_p->opts = options_p;
|
|
} /* cli_change_opts */
|
|
|
|
/**
|
|
* Checks whether the current argument is an option.
|
|
*
|
|
* Note:
|
|
* The state_p->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_p) /**< state of the command line option processor */
|
|
{
|
|
if (state_p->error != NULL)
|
|
{
|
|
return CLI_OPT_END;
|
|
}
|
|
|
|
if (state_p->index >= state_p->argc)
|
|
{
|
|
state_p->arg = NULL;
|
|
return CLI_OPT_END;
|
|
}
|
|
|
|
const char *arg = state_p->argv[state_p->index];
|
|
|
|
state_p->arg = arg;
|
|
|
|
if (arg[0] != '-')
|
|
{
|
|
return CLI_OPT_DEFAULT;
|
|
}
|
|
|
|
if (arg[1] == '-')
|
|
{
|
|
arg += 2;
|
|
|
|
for (const cli_opt_t *opt = state_p->opts; opt->id != CLI_OPT_DEFAULT; opt++)
|
|
{
|
|
if (opt->longopt != NULL && strcmp (arg, opt->longopt) == 0)
|
|
{
|
|
state_p->index++;
|
|
return opt->id;
|
|
}
|
|
}
|
|
|
|
state_p->error = "Unknown long option";
|
|
return CLI_OPT_END;
|
|
}
|
|
|
|
arg++;
|
|
|
|
for (const cli_opt_t *opt = state_p->opts; opt->id != CLI_OPT_DEFAULT; opt++)
|
|
{
|
|
if (opt->opt != NULL && strcmp (arg, opt->opt) == 0)
|
|
{
|
|
state_p->index++;
|
|
return opt->id;
|
|
}
|
|
}
|
|
|
|
state_p->error = "Unknown option";
|
|
return CLI_OPT_END;
|
|
} /* cli_consume_option */
|
|
|
|
/**
|
|
* Returns the next argument as string.
|
|
*
|
|
* Note:
|
|
* The state_p->error is not NULL on error and it contains the error message.
|
|
*
|
|
* @return argument string
|
|
*/
|
|
const char *
|
|
cli_consume_string (cli_state_t *state_p) /**< state of the command line option processor */
|
|
{
|
|
if (state_p->error != NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (state_p->index >= state_p->argc)
|
|
{
|
|
state_p->error = "Expected string argument";
|
|
state_p->arg = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
state_p->arg = state_p->argv[state_p->index];
|
|
|
|
state_p->index++;
|
|
return state_p->arg;
|
|
} /* cli_consume_string */
|
|
|
|
/**
|
|
* Returns the next argument as integer.
|
|
*
|
|
* Note:
|
|
* The state_p->error is not NULL on error and it contains the error message.
|
|
*
|
|
* @return argument integer
|
|
*/
|
|
int
|
|
cli_consume_int (cli_state_t *state_p) /**< state of the command line option processor */
|
|
{
|
|
if (state_p->error != NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
state_p->error = "Expected integer argument";
|
|
|
|
if (state_p->index >= state_p->argc)
|
|
{
|
|
state_p->arg = NULL;
|
|
return 0;
|
|
}
|
|
|
|
state_p->arg = state_p->argv[state_p->index];
|
|
|
|
char *endptr;
|
|
long int value = strtol (state_p->arg, &endptr, 10);
|
|
|
|
if (*endptr != '\0')
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
state_p->error = NULL;
|
|
state_p->index++;
|
|
return (int) value;
|
|
} /* cli_consume_int */
|
|
|
|
/**
|
|
* Return next agument as path index.
|
|
*
|
|
* @return path index
|
|
*/
|
|
uint32_t
|
|
cli_consume_path (cli_state_t *state_p) /**< state of the command line option processor */
|
|
{
|
|
if (state_p->error != NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
uint32_t path_index = (uint32_t) state_p->index;
|
|
cli_consume_string (state_p);
|
|
|
|
return path_index;
|
|
} /* cli_consume_path */
|
|
|
|
/*
|
|
* Print helper functions
|
|
*/
|
|
|
|
/**
|
|
* Pad with spaces.
|
|
*/
|
|
static void
|
|
cli_print_pad (int cnt) /**< number of spaces to print */
|
|
{
|
|
for (int i = 0; i < cnt; i++)
|
|
{
|
|
printf (" ");
|
|
}
|
|
} /* cli_print_pad */
|
|
|
|
/**
|
|
* Print the prefix of a string.
|
|
*/
|
|
static void
|
|
cli_print_prefix (const char *str, /**< string to print */
|
|
int len) /**< length of the prefix to print */
|
|
{
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
printf ("%c", *str++);
|
|
}
|
|
} /* cli_print_prefix */
|
|
|
|
/**
|
|
* Print usage summary of options.
|
|
*/
|
|
static void
|
|
cli_opt_usage (const char *prog_name_p, /**< program name, typically argv[0] */
|
|
const char *command_name_p, /**< command name if available */
|
|
const cli_opt_t *opts_p) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */
|
|
{
|
|
int length = (int) strlen (prog_name_p);
|
|
const cli_opt_t *current_opt_p = opts_p;
|
|
|
|
printf ("%s", prog_name_p);
|
|
|
|
if (command_name_p != NULL)
|
|
{
|
|
int command_length = (int) strlen (command_name_p);
|
|
|
|
if (length + 1 + command_length > CLI_LINE_LENGTH)
|
|
{
|
|
length = CLI_LINE_INDENT - 1;
|
|
printf ("\n");
|
|
cli_print_pad (length);
|
|
}
|
|
|
|
printf (" %s", command_name_p);
|
|
}
|
|
|
|
while (current_opt_p->id != CLI_OPT_DEFAULT)
|
|
{
|
|
const char *opt_p = current_opt_p->opt;
|
|
int opt_length = 2 + 1;
|
|
|
|
if (opt_p == NULL)
|
|
{
|
|
opt_p = current_opt_p->longopt;
|
|
opt_length++;
|
|
}
|
|
|
|
opt_length += (int) strlen (opt_p);
|
|
|
|
if (length + 1 + opt_length >= CLI_LINE_LENGTH)
|
|
{
|
|
length = CLI_LINE_INDENT - 1;
|
|
printf ("\n");
|
|
cli_print_pad (length);
|
|
}
|
|
length += opt_length;
|
|
|
|
printf (" [");
|
|
|
|
if (current_opt_p->opt != NULL)
|
|
{
|
|
printf ("-%s", opt_p);
|
|
}
|
|
else
|
|
{
|
|
printf ("--%s", opt_p);
|
|
}
|
|
|
|
if (current_opt_p->meta != NULL)
|
|
{
|
|
printf (" %s", current_opt_p->meta);
|
|
}
|
|
|
|
printf ("]");
|
|
|
|
current_opt_p++;
|
|
}
|
|
|
|
if (current_opt_p->meta != NULL)
|
|
{
|
|
const char *opt_p = current_opt_p->meta;
|
|
int opt_length = (int) (2 + strlen (opt_p));
|
|
|
|
if (length + 1 + opt_length >= CLI_LINE_LENGTH)
|
|
{
|
|
length = CLI_LINE_INDENT - 1;
|
|
printf ("\n");
|
|
cli_print_pad (length);
|
|
}
|
|
|
|
printf (" [%s]", opt_p);
|
|
}
|
|
|
|
printf ("\n\n");
|
|
} /* cli_opt_usage */
|
|
|
|
/**
|
|
* Print a help message wrapped into the second column.
|
|
*/
|
|
static void
|
|
cli_print_help (const char *help) /**< the help message to print */
|
|
{
|
|
while (help != NULL && *help != 0)
|
|
{
|
|
int length = -1;
|
|
int i = 0;
|
|
for (; i < CLI_LINE_LENGTH - CLI_LINE_TAB && help[i] != 0; i++)
|
|
{
|
|
if (help[i] == ' ')
|
|
{
|
|
length = i;
|
|
}
|
|
}
|
|
if (length < 0 || i < CLI_LINE_LENGTH - CLI_LINE_TAB)
|
|
{
|
|
length = i;
|
|
}
|
|
|
|
cli_print_prefix (help, length);
|
|
|
|
help += length;
|
|
while (*help == ' ')
|
|
{
|
|
help++;
|
|
}
|
|
|
|
if (*help != 0)
|
|
{
|
|
printf ("\n");
|
|
cli_print_pad (CLI_LINE_TAB);
|
|
}
|
|
}
|
|
} /* cli_print_help */
|
|
|
|
/**
|
|
* Print detailed help for options.
|
|
*/
|
|
void
|
|
cli_help (const char *prog_name_p, /**< program name, typically argv[0] */
|
|
const char *command_name_p, /**< command name if available */
|
|
const cli_opt_t *options_p) /**< array of command line option definitions, terminated by CLI_OPT_DEFAULT */
|
|
{
|
|
cli_opt_usage (prog_name_p, command_name_p, options_p);
|
|
|
|
const cli_opt_t *opt_p = options_p;
|
|
|
|
while (opt_p->id != CLI_OPT_DEFAULT)
|
|
{
|
|
int length = CLI_LINE_INDENT;
|
|
cli_print_pad (CLI_LINE_INDENT);
|
|
|
|
if (opt_p->opt != NULL)
|
|
{
|
|
printf ("-%s", opt_p->opt);
|
|
length += (int) (strlen (opt_p->opt) + 1);
|
|
}
|
|
|
|
if (opt_p->opt != NULL && opt_p->longopt != NULL)
|
|
{
|
|
printf (", ");
|
|
length += 2;
|
|
}
|
|
|
|
if (opt_p->longopt != NULL)
|
|
{
|
|
printf ("--%s", opt_p->longopt);
|
|
length += (int) (strlen (opt_p->longopt) + 2);
|
|
}
|
|
|
|
if (opt_p->meta != NULL)
|
|
{
|
|
printf (" %s", opt_p->meta);
|
|
length += 1 + (int) strlen (opt_p->meta);
|
|
}
|
|
|
|
if (opt_p->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 (opt_p->help);
|
|
}
|
|
|
|
printf ("\n");
|
|
opt_p++;
|
|
}
|
|
|
|
if (opt_p->help != NULL)
|
|
{
|
|
int length = 0;
|
|
|
|
if (opt_p->meta != NULL)
|
|
{
|
|
length = (int) (CLI_LINE_INDENT + strlen (opt_p->meta));
|
|
|
|
cli_print_pad (CLI_LINE_INDENT);
|
|
printf ("%s", opt_p->meta);
|
|
}
|
|
|
|
if (length >= CLI_LINE_TAB)
|
|
{
|
|
printf ("\n");
|
|
length = 0;
|
|
}
|
|
|
|
cli_print_pad (CLI_LINE_TAB - length);
|
|
|
|
cli_print_help (opt_p->help);
|
|
printf ("\n");
|
|
}
|
|
} /* cli_help */
|