A column option string consists of many token separated by either space of commas. A token belongs to one of three groups: - enabling: always, never and auto - layout mode: to be implemented - other tuning, which could be negated be prefix 'no' A command line option without argument (e.g. --column) will enable column output and reuse existing settings (layout mode and options..). --no-column disables columnar output. Signed-off-by: Nguyán ThÃi Ngác Duy <pclouds@xxxxxxxxx> --- .gitignore | 1 + Makefile | 1 + column.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++ column.h | 4 ++ parse-options.h | 2 + t/t9002-column.sh | 27 +++++++++++ test-column.c | 39 ++++++++++++++++ 7 files changed, 199 insertions(+), 0 deletions(-) create mode 100755 t/t9002-column.sh create mode 100644 test-column.c diff --git a/.gitignore b/.gitignore index 3dd6ef7..a1a1202 100644 --- a/.gitignore +++ b/.gitignore @@ -162,6 +162,7 @@ /gitweb/gitweb.cgi /gitweb/static/gitweb.min.* /test-chmtime +/test-column /test-ctype /test-date /test-delta diff --git a/Makefile b/Makefile index 94e1cf0..ba9944b 100644 --- a/Makefile +++ b/Makefile @@ -417,6 +417,7 @@ PROGRAM_OBJS += http-backend.o PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS)) TEST_PROGRAMS_NEED_X += test-chmtime +TEST_PROGRAMS_NEED_X += test-column TEST_PROGRAMS_NEED_X += test-ctype TEST_PROGRAMS_NEED_X += test-date TEST_PROGRAMS_NEED_X += test-delta diff --git a/column.c b/column.c index 8422c89..b32b9f9 100644 --- a/column.c +++ b/column.c @@ -1,6 +1,7 @@ #include "cache.h" #include "column.h" #include "string-list.h" +#include "parse-options.h" #define MODE(mode) ((mode) & COL_MODE) @@ -35,3 +36,127 @@ void display_columns(const struct string_list *list, int mode, } die("BUG: invalid mode %d", MODE(mode)); } + +struct colopt { + enum { + ENABLE, + MODE, + OPTION + } type; + const char *name; + int value; +}; + +/* + * Set COL_ENABLED and COL_ENABLED_SET. If 'set' is -1, check if + * stdout is tty. + */ +static int set_enable_bit(int *mode, int set, int stdout_is_tty) +{ + if (set < 0) { /* auto */ + if (stdout_is_tty < 0) + stdout_is_tty = isatty(1); + set = stdout_is_tty || (pager_in_use() && pager_use_color); + } + if (set) + *mode = *mode | COL_ENABLED | COL_ENABLED_SET; + else + *mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET; + return 0; +} + +/* + * Set COL_MODE_*. mode is intially copied from column.ui. If + * COL_ENABLED_SET is not set, then neither 'always', 'never' nor + * 'auto' has been used. Default to 'always'. + */ +static int set_mode(int *mode, int value) +{ + *mode = (*mode & ~COL_MODE) | value; + if (!(*mode & COL_ENABLED_SET)) + *mode |= COL_ENABLED | COL_ENABLED_SET; + + return 0; +} + +/* Set or unset other COL_* */ +static int set_option(int *mode, int opt, int set) +{ + if (set) + *mode |= opt; + else + *mode &= ~opt; + return 0; +} + +static int parse_option(const char *arg, int len, + int *mode, int stdout_is_tty) +{ + struct colopt opts[] = { + { ENABLE, "always", 1 }, + { ENABLE, "never", 0 }, + { ENABLE, "auto", -1 }, + }; + int i, set, name_len; + + for (i = 0; i < ARRAY_SIZE(opts); i++) { + if (opts[i].type == OPTION) { + if (len > 2 && !strncmp(arg, "no", 2)) { + arg += 2; + len -= 2; + set = 0; + } + else + set = 1; + } + + name_len = strlen(opts[i].name); + if (len != name_len || + strncmp(arg, opts[i].name, name_len)) + continue; + + switch (opts[i].type) { + case ENABLE: return set_enable_bit(mode, opts[i].value, + stdout_is_tty); + case MODE: return set_mode(mode, opts[i].value); + case OPTION: return set_option(mode, opts[i].value, set); + default: die("BUG: Unknown option type %d", opts[i].type); + } + } + + return error("unsupported style '%s'", arg); +} + +static int parse_string(int *mode, const char *value, + int stdout_is_tty) +{ + const char *sep = " ,"; + + while (*value) { + int len = strcspn(value, sep); + if (len) { + if (parse_option(value, len, mode, stdout_is_tty)) + return -1; + + value += len; + } + value += strspn(value, sep); + } + return 0; +} + +int parseopt_column_callback(const struct option *opt, + const char *arg, int unset) +{ + int *mode = opt->value; + if (unset) { + *mode = (*mode & ~COL_ENABLED) | COL_ENABLED_SET; + return 0; + } + if (arg) + return parse_string(mode, arg, -1); + + /* no arg, turn it on */ + *mode |= COL_ENABLED | COL_ENABLED_SET; + return 0; +} diff --git a/column.h b/column.h index ffae87c..a8b24e8 100644 --- a/column.h +++ b/column.h @@ -3,9 +3,13 @@ #define COL_MODE 0x000F #define COL_ENABLED (1 << 4) +#define COL_ENABLED_SET (1 << 5) /* Has COL_ENABLED been set? */ extern int term_columns(void); extern struct string_list_item *add_to_columns(struct string_list *list, int mode, const char *string); extern void display_columns(const struct string_list *list, int mode, int width, int padding, const char *indent); +struct option; +extern int parseopt_column_callback(const struct option *opt, const char *arg, int unset); + #endif diff --git a/parse-options.h b/parse-options.h index 31ec5d2..14816b6 100644 --- a/parse-options.h +++ b/parse-options.h @@ -220,5 +220,7 @@ extern int parse_opt_tertiary(const struct option *, const char *, int); PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } #define OPT__COLOR(var, h) \ OPT_COLOR_FLAG(0, "color", (var), (h)) +#define OPT_COLUMN(s, l, v, h) \ + { OPTION_CALLBACK, (s), (l), (v), "style", (h), PARSE_OPT_OPTARG, parseopt_column_callback } #endif diff --git a/t/t9002-column.sh b/t/t9002-column.sh new file mode 100755 index 0000000..099a29f --- /dev/null +++ b/t/t9002-column.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +test_description='git column' +. ./test-lib.sh + +test_expect_success 'setup' ' + cat >lista <<\EOF +one +two +three +four +five +six +seven +eight +nine +ten +eleven +EOF +' + +test_expect_success 'never' ' + test-column --mode=never <lista >actual && + test_cmp lista actual +' + +test_done diff --git a/test-column.c b/test-column.c new file mode 100644 index 0000000..d6321a4 --- /dev/null +++ b/test-column.c @@ -0,0 +1,39 @@ +#include "cache.h" +#include "strbuf.h" +#include "parse-options.h" +#include "string-list.h" +#include "column.h" + +static const char * const builtin_column_usage[] = { + "git column [--mode=<mode>] [--width=<width>] [--left-space=<N>] [--right-space=<N>]", + NULL +}; + +int main(int argc, const char **argv) +{ + struct string_list list = STRING_LIST_INIT_DUP; + int mode = 0; + struct strbuf sb = STRBUF_INIT; + int left_space = 0, right_space = 0, padding = 1; + int term_width = term_columns(); + struct option options[] = { + OPT_INTEGER(0, "width", &term_width, "Maximum width"), + OPT_INTEGER(0, "left", &left_space, "Padding space on left border"), + OPT_INTEGER(0, "right", &right_space, "Padding space on right border"), + OPT_INTEGER(0, "padding", &padding, "Padding space between columns"), + OPT_COLUMN(0, "mode", &mode, "layout to use"), + OPT_END() + }; + + argc = parse_options(argc, argv, "", options, builtin_column_usage, 0); + + while (!strbuf_getline(&sb, stdin, '\n')) + string_list_append(&list, sb.buf); + + strbuf_setlen(&sb, left_space); + memset(sb.buf, ' ', left_space); + display_columns(&list, mode, + term_width - right_space - left_space, + padding, sb.buf); + return 0; +} -- 1.7.4.74.g639db -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html