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>
master
Jeff Mahoney 2018-03-05 21:29:30 -05:00 committed by David Sterba
parent add5079974
commit a1a5000984
6 changed files with 105 additions and 13 deletions

56
btrfs.c
View File

@ -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);
}

View File

@ -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), \
}
/*

View File

@ -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)

View File

@ -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);

View File

@ -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. */

View File

@ -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;