mirror of
https://github.com/kdave/btrfs-progs
synced 2025-03-25 04:16:32 +00:00
btrfs-progs: add support for output formats
This adds a global --format option to request extended output formats from each command. We currently only support text mode. Command help reports what output formats are available for each command. Global help reports what valid formats are. If an invalid format is requested, an error is reported and lists the valid formats. Each command sets a bitmask that describes which formats it is capable of outputting. If a globally valid format is requested of a command that doesn't support it, an error is reported and command usage dumped. Commands don't need to specify that they support text output. All commands are required to output text. Signed-off-by: Jeff Mahoney <jeffm@suse.com> [ use global config instead of passing cmd_context ] Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
add5079974
commit
a1a5000984
56
btrfs.c
56
btrfs.c
@ -26,7 +26,7 @@
|
||||
#include "common/help.h"
|
||||
|
||||
static const char * const btrfs_cmd_group_usage[] = {
|
||||
"btrfs [--help] [--version] <group> [<group>...] <command> [<args>]",
|
||||
"btrfs [--help] [--version] [--format <format>] <group> [<group>...] <command> [<args>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -98,6 +98,19 @@ parse_command_token(const char *arg, const struct cmd_group *grp)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static void check_output_format(const struct cmd_struct *cmd)
|
||||
{
|
||||
if (cmd->next)
|
||||
return;
|
||||
|
||||
if (!(cmd->flags & bconf.output_format & CMD_FORMAT_MASK)) {
|
||||
fprintf(stderr,
|
||||
"ERROR: output format %s is unsupported for this command\n",
|
||||
output_format_name(bconf.output_format));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_help_options_next_level(const struct cmd_struct *cmd,
|
||||
int argc, char **argv)
|
||||
{
|
||||
@ -132,6 +145,7 @@ int handle_command_group(const struct cmd_struct *cmd, int argc,
|
||||
subcmd = parse_command_token(argv[0], cmd->next);
|
||||
|
||||
handle_help_options_next_level(subcmd, argc, argv);
|
||||
check_output_format(subcmd);
|
||||
|
||||
fixup_argv0(argv, subcmd->token);
|
||||
return cmd_execute(subcmd, argc, argv);
|
||||
@ -168,6 +182,39 @@ static int cmd_version(const struct cmd_struct *unused, int argc, char **argv)
|
||||
}
|
||||
static DEFINE_SIMPLE_COMMAND(version, "version");
|
||||
|
||||
static void print_output_formats(FILE *outf)
|
||||
{
|
||||
int i;
|
||||
|
||||
fputs("Options for --format are:", outf);
|
||||
for (i = 0; i < ARRAY_SIZE(output_formats); i++)
|
||||
fprintf(outf, "%s%s", i ? ", " : " ", output_formats[i].name);
|
||||
fputs("\n", outf);
|
||||
}
|
||||
|
||||
static void handle_output_format(const char *format)
|
||||
{
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(output_formats); i++) {
|
||||
if (!strcasecmp(format, output_formats[i].name)) {
|
||||
bconf.output_format = output_formats[i].value;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print error for invalid format */
|
||||
if (!found) {
|
||||
bconf.output_format = CMD_FORMAT_TEXT;
|
||||
fprintf(stderr, "error: invalid output format \"%s\"\n\n",
|
||||
format);
|
||||
print_output_formats(stderr);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse global options, between binary name and first non-option argument
|
||||
* after processing all valid options (including those with arguments).
|
||||
@ -176,10 +223,11 @@ static DEFINE_SIMPLE_COMMAND(version, "version");
|
||||
*/
|
||||
static int handle_global_options(int argc, char **argv)
|
||||
{
|
||||
enum { OPT_HELP = 256, OPT_VERSION, OPT_FULL };
|
||||
enum { OPT_HELP = 256, OPT_VERSION, OPT_FULL, OPT_FORMAT };
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, OPT_HELP },
|
||||
{ "version", no_argument, NULL, OPT_VERSION },
|
||||
{ "format", required_argument, NULL, OPT_FORMAT },
|
||||
{ "full", no_argument, NULL, OPT_FULL },
|
||||
{ NULL, 0, NULL, 0}
|
||||
};
|
||||
@ -200,6 +248,9 @@ static int handle_global_options(int argc, char **argv)
|
||||
case OPT_HELP: break;
|
||||
case OPT_VERSION: break;
|
||||
case OPT_FULL: break;
|
||||
case OPT_FORMAT:
|
||||
handle_output_format(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown global option: %s\n",
|
||||
argv[optind - 1]);
|
||||
@ -231,6 +282,7 @@ static void handle_special_globals(int shift, int argc, char **argv)
|
||||
usage_command_group(&btrfs_cmd_group, true, false);
|
||||
else
|
||||
cmd_execute(&cmd_struct_help, argc, argv);
|
||||
print_output_formats(stdout);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,11 @@
|
||||
enum {
|
||||
CMD_HIDDEN = (1 << 0), /* should not be in help listings */
|
||||
CMD_ALIAS = (1 << 1), /* alias of next command in cmd_group */
|
||||
CMD_FORMAT_TEXT = (1 << 2), /* output as plain text */
|
||||
};
|
||||
|
||||
#define CMD_FORMAT_MASK (CMD_FORMAT_TEXT)
|
||||
|
||||
struct cmd_struct {
|
||||
const char *token;
|
||||
int (*fn)(const struct cmd_struct *cmd, int argc, char **argv);
|
||||
@ -72,7 +75,7 @@ struct cmd_struct {
|
||||
.fn = (_fn), \
|
||||
.usagestr = (_usagestr), \
|
||||
.next = (_group), \
|
||||
.flags = (_flags), \
|
||||
.flags = CMD_FORMAT_TEXT | (_flags), \
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -28,6 +28,11 @@
|
||||
#define USAGE_LONG 2U
|
||||
#define USAGE_OPTIONS 4U
|
||||
#define USAGE_LISTING 8U
|
||||
#define USAGE_FORMAT 16U
|
||||
|
||||
const struct format_desc output_formats[1] = {
|
||||
{ .value = CMD_FORMAT_TEXT, .name = "text" },
|
||||
};
|
||||
|
||||
static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
|
||||
|
||||
@ -124,8 +129,21 @@ void clean_args_no_options_relaxed(const struct cmd_struct *cmd,
|
||||
optind = 2;
|
||||
}
|
||||
|
||||
const char *output_format_name(unsigned int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(output_formats); i++) {
|
||||
if (output_formats[i].value == value)
|
||||
return output_formats[i].name;
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
static int do_usage_one_command(const char * const *usagestr,
|
||||
unsigned int flags, FILE *outf)
|
||||
unsigned int flags, unsigned int cmd_flags,
|
||||
FILE *outf)
|
||||
{
|
||||
int pad = 4;
|
||||
const char *prefix = "usage: ";
|
||||
@ -196,8 +214,9 @@ static int do_usage_one_command(const char * const *usagestr,
|
||||
}
|
||||
|
||||
static int usage_command_internal(const char * const *usagestr,
|
||||
const char *token, bool full, bool lst,
|
||||
bool alias, FILE *outf)
|
||||
const char *token, unsigned cmd_flags,
|
||||
bool full,
|
||||
bool lst, bool alias, FILE *outf)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
int ret;
|
||||
@ -205,11 +224,11 @@ static int usage_command_internal(const char * const *usagestr,
|
||||
if (!alias)
|
||||
flags |= USAGE_SHORT;
|
||||
if (full)
|
||||
flags |= USAGE_LONG | USAGE_OPTIONS;
|
||||
flags |= USAGE_LONG | USAGE_OPTIONS | USAGE_FORMAT;
|
||||
if (lst)
|
||||
flags |= USAGE_LISTING;
|
||||
|
||||
ret = do_usage_one_command(usagestr, flags, outf);
|
||||
ret = do_usage_one_command(usagestr, flags, cmd_flags, outf);
|
||||
switch (ret) {
|
||||
case -1:
|
||||
fprintf(outf, "No usage for '%s'\n", token);
|
||||
@ -223,19 +242,22 @@ static int usage_command_internal(const char * const *usagestr,
|
||||
}
|
||||
|
||||
static void usage_command_usagestr(const char * const *usagestr,
|
||||
const char *token, bool full, bool err)
|
||||
const char *token, unsigned int flags,
|
||||
bool full, bool err)
|
||||
{
|
||||
FILE *outf = err ? stderr : stdout;
|
||||
int ret;
|
||||
|
||||
ret = usage_command_internal(usagestr, token, full, false, false, outf);
|
||||
ret = usage_command_internal(usagestr, token, flags,
|
||||
full, false, false, outf);
|
||||
if (!ret)
|
||||
fputc('\n', outf);
|
||||
}
|
||||
|
||||
void usage_command(const struct cmd_struct *cmd, bool full, bool err)
|
||||
{
|
||||
usage_command_usagestr(cmd->usagestr, cmd->token, full, err);
|
||||
usage_command_usagestr(cmd->usagestr, cmd->token,
|
||||
cmd->flags, full, err);
|
||||
}
|
||||
|
||||
__attribute__((noreturn))
|
||||
@ -286,7 +308,7 @@ void usage_unknown_option(const struct cmd_struct *cmd, char **argv)
|
||||
__attribute__((noreturn))
|
||||
void usage(const struct cmd_struct *cmd)
|
||||
{
|
||||
usage_command_usagestr(cmd->usagestr, NULL, true, true);
|
||||
usage_command_usagestr(cmd->usagestr, NULL, 0, true, true);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -310,7 +332,8 @@ static void usage_command_group_internal(const struct cmd_group *grp, bool full,
|
||||
do_sep = 0;
|
||||
}
|
||||
|
||||
usage_command_internal(cmd->usagestr, cmd->token, full,
|
||||
usage_command_internal(cmd->usagestr, cmd->token,
|
||||
cmd->flags, full,
|
||||
true, cmd->flags & CMD_ALIAS,
|
||||
outf);
|
||||
if (cmd->flags & CMD_ALIAS)
|
||||
|
@ -55,6 +55,18 @@
|
||||
struct cmd_struct;
|
||||
struct cmd_group;
|
||||
|
||||
/*
|
||||
* Descriptor of output format
|
||||
*/
|
||||
struct format_desc {
|
||||
unsigned int value;
|
||||
char name[8];
|
||||
};
|
||||
|
||||
extern const struct format_desc output_formats[1];
|
||||
|
||||
const char *output_format_name(unsigned int value);
|
||||
|
||||
__attribute__((noreturn))
|
||||
void usage_unknown_option(const struct cmd_struct *cmd, char **argv);
|
||||
|
||||
|
@ -2610,6 +2610,7 @@ u8 rand_u8(void)
|
||||
|
||||
void btrfs_config_init(void)
|
||||
{
|
||||
bconf.output_format = CMD_FORMAT_TEXT;
|
||||
}
|
||||
|
||||
/* Returns total size of main memory in bytes, -1UL if error. */
|
||||
|
@ -177,6 +177,7 @@ void print_all_devices(struct list_head *devices);
|
||||
* functions without extra context passing.
|
||||
*/
|
||||
struct btrfs_config {
|
||||
unsigned int output_format;
|
||||
};
|
||||
extern struct btrfs_config bconf;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user