{"thread":{"id":"26446","subject":"[PATCH/RFC 0/7] Column output","startedAt":"2011-02-08T15:22:14Z","lastAt":"2011-02-10T02:54:48Z","messageCount":20,"participants":["Nguyễn Thái Ngọc Duy","Jeff King","Nguyen Thai Ngoc Duy","Jonathan Nieder","Junio C Hamano","Miles Bader"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"160696","messageId":"1297178541-31124-1-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":null,"subject":"[PATCH/RFC 0/7] Column output","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:14Z","receivedAt":"2011-02-08T15:22:14Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This series was made a year ago [1]. I'm quite happy with it so far\nand also tired of maintaining off-tree. So here's another attempt to\nclean it up and put it upstream.\n\nIn short, the series is very simple: give porcelain commands column\noutput, just like good old \"ls\". There could be a few more candidates,\nI believe René Scharfe mentioned other files in \"git status\".\n\nAnother thing is how you want to customize this. Currently it supports\ntwo modes: equal columns, and dense mode, specified with\n--column=column or --column=dense. Obviously not very convenient.\nThere's core.column but I'm not sure how to specify modes there, and\nif people like some more modes (fill rows before columns for example).\n\n[1] http://mid.gmane.org/1267963785-473-1-git-send-email-pclouds@gmail.com\n\nNguyễn Thái Ngọc Duy (7):\n  Move term_columns() to pager.c and save terminal width before pager\n  Add column layout\n  parseopt: OPT_COLUMN to set struct column_layout.mode\n  add core.column\n  help: reuse struct column_layout\n  tag: support column output with --column\n  branch: support column output with --column\n\n .gitignore                   |    1 +\n Documentation/config.txt     |    8 ++\n Documentation/git-branch.txt |    8 ++\n Documentation/git-tag.txt    |   11 ++-\n Makefile                     |    3 +\n builtin/branch.c             |   22 ++++-\n builtin/tag.c                |   19 ++++-\n cache.h                      |    2 +\n column.c                     |  195 ++++++++++++++++++++++++++++++++++++++++++\n column.h                     |   23 +++++\n config.c                     |   16 ++++\n environment.c                |    1 +\n help.c                       |   55 +++----------\n pager.c                      |   32 +++++++\n parse-options.h              |    2 +\n t/t9002-column.sh            |  108 +++++++++++++++++++++++\n test-column.c                |   59 +++++++++++++\n 17 files changed, 514 insertions(+), 51 deletions(-)\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n create mode 100644 test-column.c\n\n-- \n1.7.2.2\n"},{"id":"160697","messageId":"1297178541-31124-2-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 1/7] Move term_columns() to pager.c and save terminal width before pager","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:15Z","receivedAt":"2011-02-08T15:22:15Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"term_columns() checks for terminal width via ioctl(1). After\nredirecting, stdin is no longer terminal to get terminal width.\n\nCheck terminal width and save it before redirect stdin in setup_pager()\nand let term_columns() reuse the value.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n cache.h |    1 +\n help.c  |   22 ----------------------\n pager.c |   32 ++++++++++++++++++++++++++++++++\n 3 files changed, 33 insertions(+), 22 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex d83d68c..bcbd5f2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1045,6 +1045,7 @@ extern void setup_pager(void);\n extern const char *pager_program;\n extern int pager_in_use(void);\n extern int pager_use_color;\n+extern int term_columns();\n \n extern const char *editor_program;\n extern const char *askpass_program;\ndiff --git a/help.c b/help.c\nindex 7654f1b..1344208 100644\n--- a/help.c\n+++ b/help.c\n@@ -5,28 +5,6 @@\n #include \"help.h\"\n #include \"common-cmds.h\"\n \n-/* most GUI terminals set COLUMNS (although some don't export it) */\n-static int term_columns(void)\n-{\n-\tchar *col_string = getenv(\"COLUMNS\");\n-\tint n_cols;\n-\n-\tif (col_string && (n_cols = atoi(col_string)) > 0)\n-\t\treturn n_cols;\n-\n-#ifdef TIOCGWINSZ\n-\t{\n-\t\tstruct winsize ws;\n-\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n-\t\t\tif (ws.ws_col)\n-\t\t\t\treturn ws.ws_col;\n-\t\t}\n-\t}\n-#endif\n-\n-\treturn 80;\n-}\n-\n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\n \tstruct cmdname *ent = xmalloc(sizeof(*ent) + len + 1);\ndiff --git a/pager.c b/pager.c\nindex dac358f..ad447cf 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -12,6 +12,7 @@\n  */\n \n static int spawned_pager;\n+static int max_columns;\n \n #ifndef WIN32\n static void pager_preexec(void)\n@@ -80,6 +81,15 @@ void setup_pager(void)\n \n \tspawned_pager = 1; /* means we are emitting to terminal */\n \n+#ifdef TIOCGWINSZ\n+\t{\n+\t\tstruct winsize ws;\n+\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n+\t\t\tif (ws.ws_col)\n+\t\t\t\tmax_columns = ws.ws_col;\n+\t\t}\n+\t}\n+#endif\n \t/* spawn the pager */\n \tpager_argv[0] = pager;\n \tpager_process.use_shell = 1;\n@@ -116,3 +126,25 @@ int pager_in_use(void)\n \tenv = getenv(\"GIT_PAGER_IN_USE\");\n \treturn env ? git_config_bool(\"GIT_PAGER_IN_USE\", env) : 0;\n }\n+\n+int term_columns()\n+{\n+\tchar *col_string = getenv(\"COLUMNS\");\n+\tint n_cols;\n+\n+\tif (col_string && (n_cols = atoi(col_string)) > 0)\n+\t\treturn n_cols;\n+\n+\telse if (spawned_pager && max_columns)\n+\t\treturn max_columns;\n+#ifdef TIOCGWINSZ\n+\telse {\n+\t\tstruct winsize ws;\n+\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n+\t\t\tif (ws.ws_col)\n+\t\t\t\treturn ws.ws_col;\n+\t\t}\n+\t}\n+#endif\n+\treturn 80;\n+}\n-- \n1.7.2.2\n"},{"id":"160699","messageId":"1297178541-31124-3-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 2/7] Add column layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:16Z","receivedAt":"2011-02-08T15:22:16Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This mainly add display_columns() that will display a table of items,\nfilling columns before rows.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n .gitignore        |    1 +\n Makefile          |    3 +\n column.c          |  177 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n column.h          |   20 ++++++\n t/t9002-column.sh |  108 ++++++++++++++++++++++++++++++++\n test-column.c     |   59 ++++++++++++++++++\n 6 files changed, 368 insertions(+), 0 deletions(-)\n create mode 100644 column.c\n create mode 100644 column.h\n create mode 100755 t/t9002-column.sh\n create mode 100644 test-column.c\n\ndiff --git a/.gitignore b/.gitignore\nindex 3dd6ef7..a1a1202 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -162,6 +162,7 @@\n /gitweb/gitweb.cgi\n /gitweb/static/gitweb.min.*\n /test-chmtime\n+/test-column\n /test-ctype\n /test-date\n /test-delta\ndiff --git a/Makefile b/Makefile\nindex 775ee83..6d74956 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -417,6 +417,7 @@ PROGRAM_OBJS += http-backend.o\n PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))\n \n TEST_PROGRAMS_NEED_X += test-chmtime\n+TEST_PROGRAMS_NEED_X += test-column\n TEST_PROGRAMS_NEED_X += test-ctype\n TEST_PROGRAMS_NEED_X += test-date\n TEST_PROGRAMS_NEED_X += test-delta\n@@ -498,6 +499,7 @@ LIB_H += builtin.h\n LIB_H += cache.h\n LIB_H += cache-tree.h\n LIB_H += color.h\n+LIB_H += column.h\n LIB_H += commit.h\n LIB_H += compat/bswap.h\n LIB_H += compat/cygwin.h\n@@ -575,6 +577,7 @@ LIB_OBJS += branch.o\n LIB_OBJS += bundle.o\n LIB_OBJS += cache-tree.o\n LIB_OBJS += color.o\n+LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n LIB_OBJS += config.o\ndiff --git a/column.c b/column.c\nnew file mode 100644\nindex 0000000..4b92fa5\n--- /dev/null\n+++ b/column.c\n@@ -0,0 +1,177 @@\n+#include \"cache.h\"\n+#include \"column.h\"\n+#include \"parse-options.h\"\n+\n+static int item_length(const struct column_layout *c, const char *s)\n+{\n+\tint a_len = 0;\n+\n+\tif (!(c->mode & COL_ANSI))\n+\t\treturn strlen(s);\n+\n+\twhile ((s = strstr(s, \"\\033[\")) != NULL) {\n+\t\tint len = strspn(s+2, \"0123456789;\");\n+\t\ts += len+3; /* \\033[<len><func char> */\n+\t\ta_len += len+3;\n+\t}\n+\treturn a_len;\n+}\n+\n+static void layout(const struct column_layout *c,\n+\t\t   int total_width, int padding,\n+\t\t   int *width,\n+\t\t   int *rows, int *cols)\n+{\n+\tint i;\n+\n+\t*width = 0;\n+\t/* Find maximum column width */\n+\tfor (i = 0; i < c->items.nr; i++) {\n+\t\tconst char *s = c->items.items[i].string;\n+\t\tint len = item_length(c, s);\n+\t\tif (*width < len)\n+\t\t\t*width = len;\n+\t}\n+\t*width += padding;\n+\n+\t*cols = total_width / *width;\n+\tif (*cols == 0)\n+\t\t*cols = 1;\n+\t/* items.nr <= rows*cols -> rows >= items.nr/cols */\n+\t*rows = c->items.nr / *cols;\n+\tif (c->items.nr > *rows * *cols)\n+\t\t(*rows)++;\n+}\n+\n+static int squeeze_columns(const struct column_layout *c,\n+\t\t\t   int *width, int padding,\n+\t\t\t   int rows, int cols)\n+{\n+\tint x, y, i, len, item_len, spare = 0;\n+\tconst char *s;\n+\n+\tfor (x = 0; x < cols; x++) {\n+\t\tfor (y = len = 0; y < rows; y++) {\n+\t\t\ti = x * rows + y;\n+\t\t\tif (i >= c->items.nr)\n+\t\t\t\tbreak;\n+\t\t\ts = c->items.items[i].string;\n+\t\t\titem_len = item_length(c, s);\n+\t\t\tif (len < item_len)\n+\t\t\t\tlen = item_len;\n+\t\t}\n+\t\tlen += padding;\n+\t\tif (len < width[x]) {\n+\t\t\tspare += width[x] - len;\n+\t\t\twidth[x] = len;\n+\t\t}\n+\t}\n+\treturn spare;\n+}\n+\n+static void relayout(const struct column_layout *c,\n+\t\t     int padding, int spare,\n+\t\t     int *initial_width, int **width,\n+\t\t     int *rows, int *cols)\n+{\n+\tint new_rows, new_cols, new_initial_width;\n+\tint i, *new_width, new_spare, total_width;\n+\n+\t/*\n+\t * Assume all columns have same width, we would need\n+\t * initial_width*cols. But then after squeezing, we have\n+\t * \"spare\" more chars. Assume a new total_width with\n+\t * additional chars, then re-squeeze to see if it fits\n+\t * c->width.\n+\t */\n+\ttotal_width = (*initial_width)*(*cols) + spare;\n+\tlayout(c, total_width, padding,\n+\t       &new_initial_width, &new_rows, &new_cols);\n+\tnew_width = xmalloc(sizeof(*new_width) * new_cols);\n+\tfor (i = 0; i < new_cols; i++)\n+\t\tnew_width[i] = new_initial_width;\n+\tnew_spare = squeeze_columns(c, new_width, padding, new_rows, new_cols);\n+\n+\t/* Does it fit? */\n+\tif (total_width - new_spare < c->width) {\n+\t\tfree(*width);\n+\t\t*width = new_width;\n+\t\t*initial_width = new_initial_width;\n+\t\t*rows = new_rows;\n+\t\t*cols = new_cols;\n+\t}\n+}\n+\n+static void display_columns_first(const struct column_layout *c,\n+\t\t\t\t  int padding, const char *indent)\n+{\n+\tint x, y, i, cols, rows, initial_width, *width;\n+\tchar *empty_cell;\n+\n+\tlayout(c, c->width, padding, &initial_width, &rows, &cols);\n+\twidth = xmalloc(sizeof(*width) * cols);\n+\tfor (i = 0; i < cols; i++)\n+\t\twidth[i] = initial_width;\n+\n+\tif (c->mode & COL_DENSE) {\n+\t\tint spare = squeeze_columns(c, width, padding, rows, cols);\n+\t\t/* is it worth relayout? */\n+\t\tif (spare >= initial_width/2)\n+\t\t\trelayout(c, padding, spare,\n+\t\t\t\t &initial_width, &width, &rows, &cols);\n+\t}\n+\n+\tempty_cell = xmalloc(initial_width + 1);\n+\tmemset(empty_cell, ' ', initial_width);\n+\tempty_cell[initial_width] = '\\0';\n+\tfor (y = 0; y < rows; y++) {\n+\t\tfor (x = 0; x < cols; x++) {\n+\t\t\tconst char *s;\n+\t\t\tint len;\n+\n+\t\t\ti = x * rows + y;\n+\t\t\tif (i >= c->items.nr)\n+\t\t\t\tbreak;\n+\t\t\ts = c->items.items[i].string;\n+\t\t\tlen = item_length(c, s);\n+\t\t\tif (width[x] < initial_width)\n+\t\t\t\tlen += initial_width - width[x];\n+\n+\t\t\tprintf(\"%s%s%s\",\n+\t\t\t       x == 0 ? indent : \"\",\n+\t\t\t       c->items.items[i].string,\n+\t\t\t       /* If the next column at same row is\n+\t\t\t\t  out of range, end the line. Else pad\n+\t\t\t\t  some space.  */\n+\t\t\t       i + rows >= c->items.nr ? \"\\n\" : empty_cell + len);\n+\t\t}\n+\t}\n+}\n+\n+static void display_plain(const struct column_layout *c, const char *indent)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < c->items.nr; i++)\n+\t\tprintf(\"%s%s\\n\", indent, c->items.items[i].string);\n+}\n+\n+void display_columns(const struct column_layout *c, int padding, const char *indent)\n+{\n+\tint mode = c->mode & COL_MODE;\n+\tif (!indent)\n+\t\tindent = \"\";\n+\tif (c->width <= 1)\n+\t\tmode = COL_PLAIN;\n+\tswitch (mode) {\n+\tcase COL_PLAIN:\n+\t\tdisplay_plain(c, indent);\n+\t\tbreak;\n+\n+\tcase COL_COLUMN_FIRST:\n+\t\tdisplay_columns_first(c, padding, indent);\n+\t\tbreak;\n+\tdefault:\n+\t\tdie(\"BUG: invalid mode %d\", c->mode & COL_MODE);\n+\t}\n+}\ndiff --git a/column.h b/column.h\nnew file mode 100644\nindex 0000000..34435b0\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,20 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+#include \"string-list.h\"\n+\n+#define COL_MODE          0x000F\n+#define COL_PLAIN         0 /* Single column */\n+#define COL_COLUMN_FIRST  1 /* Fill columns before rows */\n+#define COL_ANSI          (1 << 4) /* Remove ANSI from string length */\n+#define COL_DENSE         (1 << 5) /* \"Ragged-right\" mode, relayout if enough space */\n+\n+struct column_layout {\n+\tint mode;\n+\tint width;\n+\tstruct string_list items;\n+};\n+\n+extern void display_columns(const struct column_layout *c, int padding, const char *indent);\n+\n+#endif\ndiff --git a/t/t9002-column.sh b/t/t9002-column.sh\nnew file mode 100755\nindex 0000000..1d030b0\n--- /dev/null\n+++ b/t/t9002-column.sh\n@@ -0,0 +1,108 @@\n+#!/bin/sh\n+\n+test_description='git column'\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\tcat >lista <<\\EOF\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+'\n+\n+test_expect_success '80 columns' '\n+\tcat >expected <<\\EOF &&\n+one    two    three  four   five   six    seven  eight  nine   ten    eleven\n+EOF\n+\tCOLUMNS=80 test-column < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'COLUMNS = 1' '\n+\tcat >expected <<\\EOF &&\n+one\n+two\n+three\n+four\n+five\n+six\n+seven\n+eight\n+nine\n+ten\n+eleven\n+EOF\n+\tCOLUMNS=1 test-column < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success 'width = 1' '\n+\ttest-column --width=1 < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+COLUMNS=20\n+export COLUMNS\n+\n+test_expect_success '20 columns' '\n+\tcat >expected <<\\EOF &&\n+one    seven\n+two    eight\n+three  nine\n+four   ten\n+five   eleven\n+six\n+EOF\n+\ttest-column < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, densed' '\n+\tcat >expected <<\\EOF &&\n+one   seven\n+two   eight\n+three nine\n+four  ten\n+five  eleven\n+six\n+EOF\n+\ttest-column --mode=column,dense < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, padding 2' '\n+\tcat >expected <<\\EOF &&\n+one     seven\n+two     eight\n+three   nine\n+four    ten\n+five    eleven\n+six\n+EOF\n+\ttest-column --padding 2 < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_expect_success '20 columns, left indented' '\n+\tcat >expected <<\\EOF &&\n+  one    seven\n+  two    eight\n+  three  nine\n+  four   ten\n+  five   eleven\n+  six\n+EOF\n+\ttest-column --left=2 < lista > actual &&\n+\ttest_cmp expected actual\n+'\n+\n+test_done\ndiff --git a/test-column.c b/test-column.c\nnew file mode 100644\nindex 0000000..4f56cd8\n--- /dev/null\n+++ b/test-column.c\n@@ -0,0 +1,59 @@\n+#include \"cache.h\"\n+#include \"strbuf.h\"\n+#include \"parse-options.h\"\n+#include \"column.h\"\n+\n+static const char * const builtin_column_usage[] = {\n+\t\"git column [--mode=<mode>] [--width=<width>] [--left-space=<N>] [--right-space=<N>]\",\n+\tNULL\n+};\n+\n+int main(int argc, const char **argv)\n+{\n+\tstruct column_layout c;\n+\tconst char *mode = NULL;\n+\tstruct strbuf sb = STRBUF_INIT;\n+\tint left_space = 0, right_space = 0, padding = 1;\n+\tint term_width = term_columns();\n+\tstruct option options[] = {\n+\t\tOPT_INTEGER(0, \"width\", &term_width, \"Maximum width\"),\n+\t\tOPT_INTEGER(0, \"left\", &left_space, \"Padding space on left border\"),\n+\t\tOPT_INTEGER(0, \"right\", &right_space, \"Padding space on right border\"),\n+\t\tOPT_INTEGER(0, \"padding\", &padding, \"Padding space between columns\"),\n+\t\tOPT_STRING(0, \"mode\", &mode, \"mode\", \"Which layout mode to use\"),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, \"\", options, builtin_column_usage, 0);\n+\n+\tmemset(&c, 0, sizeof(c));\n+\tc.width = term_width - right_space - left_space;\n+\tc.items.strdup_strings = 1;\n+\n+\twhile (mode && *mode) {\n+\t\tint len = strcspn(mode, \",\");\n+\t\tif (len == 6 && !strncmp(mode, \"column\", len))\n+\t\t\tc.mode |= COL_COLUMN_FIRST;\n+\t\telse if (len == 5 && !strncmp(mode, \"dense\", len))\n+\t\t\tc.mode |= COL_DENSE;\n+\t\telse if (len == 4 && !strncmp(mode, \"ansi\", len))\n+\t\t\tc.mode |= COL_ANSI;\n+\t\telse\n+\t\t\tdie(\"Unknown mode %s\", mode);\n+\t\tmode += len;\n+\t\twhile (*mode == ',')\n+\t\t\tmode++;\n+\t}\n+\tif (!c.mode)\n+\t\tc.mode = COL_COLUMN_FIRST;\n+\tif ((c.mode & COL_MODE) == 0)\n+\t\tdie(\"Invalid mode '%s'\", mode);\n+\n+\twhile (!strbuf_getline(&sb, stdin, '\\n'))\n+\t\tstring_list_append(&c.items, sb.buf);\n+\n+\tstrbuf_setlen(&sb, left_space);\n+\tmemset(sb.buf, ' ', left_space);\n+\tdisplay_columns(&c, padding, sb.buf);\n+\treturn 0;\n+}\n-- \n1.7.2.2\n"},{"id":"160698","messageId":"1297178541-31124-4-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 3/7] parseopt: OPT_COLUMN to set struct column_layout.mode","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:17Z","receivedAt":"2011-02-08T15:22:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n column.c        |   18 ++++++++++++++++++\n column.h        |    3 +++\n parse-options.h |    2 ++\n 3 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/column.c b/column.c\nindex 4b92fa5..c4a90d5 100644\n--- a/column.c\n+++ b/column.c\n@@ -175,3 +175,21 @@ void display_columns(const struct column_layout *c, int padding, const char *ind\n \t\tdie(\"BUG: invalid mode %d\", c->mode & COL_MODE);\n \t}\n }\n+\n+int parseopt_column_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tstruct column_layout *cp = opt->value;\n+\tif (unset) {\n+\t\tcp->mode = COL_PLAIN;\n+\t\treturn 0;\n+\t}\n+\tif (!arg || !strcmp(arg, \"column\")) {\n+\t\tcp->mode |= COL_COLUMN_FIRST;\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(arg, \"dense\")) {\n+\t\tcp->mode |= COL_DENSE;\n+\t\treturn 0;\n+\t}\n+\treturn error(\"unsupported style '%s'\", arg);\n+}\ndiff --git a/column.h b/column.h\nindex 34435b0..7f0ae74 100644\n--- a/column.h\n+++ b/column.h\n@@ -17,4 +17,7 @@ struct column_layout {\n \n extern void display_columns(const struct column_layout *c, int padding, const char *indent);\n \n+struct option;\n+extern int parseopt_column_callback(const struct option *opt, const char *arg, int unset);\n+\n #endif\ndiff --git a/parse-options.h b/parse-options.h\nindex 31ec5d2..2fed453 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -145,6 +145,8 @@ struct option {\n #define OPT_COLOR_FLAG(s, l, v, h) \\\n \t{ OPTION_CALLBACK, (s), (l), (v), \"when\", (h), PARSE_OPT_OPTARG, \\\n \t\tparse_opt_color_flag_cb, (intptr_t)\"always\" }\n+#define OPT_COLUMN(s, l, v, h) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), \"style\", (h), PARSE_OPT_OPTARG, parseopt_column_callback }\n \n \n /* parse_options() will filter out the processed options and leave the\n-- \n1.7.2.2\n"},{"id":"160701","messageId":"1297178541-31124-5-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 4/7] add core.column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:18Z","receivedAt":"2011-02-08T15:22:18Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"This takes auto, never or always to enable column output for\nporcelain commands.\n\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/config.txt |    8 ++++++++\n cache.h                  |    1 +\n config.c                 |   16 ++++++++++++++++\n environment.c            |    1 +\n 4 files changed, 26 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c5e1835..6d7151b 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -567,6 +567,14 @@ core.sparseCheckout::\n \tEnable \"sparse checkout\" feature. See section \"Sparse checkout\" in\n \tlinkgit:git-read-tree[1] for more information.\n \n+core.column::\n+\tSpecify whether a command should output in columns. Only commands\n+\tthat support `--column` will be affected by this. This variable can\n+\ttake either `never` (do not output in columns), `auto` (output in\n+\tcolumns if the output is to a terminal) or `always` (always output in\n+\tcolumns). Setting `--column` or `--no-column` will override this\n+\tvariable. This option defaults to never.\n+\n add.ignore-errors::\n add.ignoreErrors::\n \tTells 'git add' to continue adding files when some files cannot be\ndiff --git a/cache.h b/cache.h\nindex bcbd5f2..1fa7656 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -559,6 +559,7 @@ extern int read_replace_refs;\n extern int fsync_object_files;\n extern int core_preload_index;\n extern int core_apply_sparse_checkout;\n+extern int core_column;\n \n enum safe_crlf {\n \tSAFE_CRLF_FALSE = 0,\ndiff --git a/config.c b/config.c\nindex 625e051..071899f 100644\n--- a/config.c\n+++ b/config.c\n@@ -9,6 +9,8 @@\n #include \"exec_cmd.h\"\n #include \"strbuf.h\"\n #include \"quote.h\"\n+#include \"color.h\"\n+#include \"column.h\"\n \n #define MAXNAME (256)\n \n@@ -660,6 +662,20 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.column\")) {\n+\t\tswitch (git_config_colorbool(var, value, -1)) {\n+\t\tcase 0:\n+\t\t\tcore_column = COL_PLAIN;\n+\t\t\tbreak;\n+\t\tcase 1:\n+\t\t\tcore_column = COL_COLUMN_FIRST;\n+\t\t\tbreak;\n+\t\tcase -1:\n+\t\t\treturn -1;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n \t/* Add other config variables here and to Documentation/config.txt. */\n \treturn 0;\n }\ndiff --git a/environment.c b/environment.c\nindex 9564475..b420ea2 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -56,6 +56,7 @@ char *notes_ref_name;\n int grafts_replace_parents = 1;\n int core_apply_sparse_checkout;\n struct startup_info *startup_info;\n+int core_column;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\n-- \n1.7.2.2\n"},{"id":"160700","messageId":"1297178541-31124-6-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 5/7] help: reuse struct column_layout","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:19Z","receivedAt":"2011-02-08T15:22:19Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n help.c |   33 +++++++++++----------------------\n 1 files changed, 11 insertions(+), 22 deletions(-)\n\ndiff --git a/help.c b/help.c\nindex 1344208..dc06f63 100644\n--- a/help.c\n+++ b/help.c\n@@ -4,6 +4,7 @@\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\n+#include \"column.h\"\n \n void add_cmdname(struct cmdnames *cmds, const char *name, int len)\n {\n@@ -72,29 +73,17 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n \n static void pretty_print_string_list(struct cmdnames *cmds, int longest)\n {\n-\tint cols = 1, rows;\n-\tint space = longest + 1; /* min 1 SP between words */\n-\tint max_cols = term_columns() - 1; /* don't print *on* the edge */\n-\tint i, j;\n-\n-\tif (space < max_cols)\n-\t\tcols = max_cols / space;\n-\trows = DIV_ROUND_UP(cmds->cnt, cols);\n+\tstruct column_layout c;\n+\tint i;\n \n-\tfor (i = 0; i < rows; i++) {\n-\t\tprintf(\"  \");\n+\tmemset(&c, 0, sizeof(c));\n+\tc.mode = COL_COLUMN_FIRST;\n+\tc.width = term_columns();\n \n-\t\tfor (j = 0; j < cols; j++) {\n-\t\t\tint n = j * rows + i;\n-\t\t\tint size = space;\n-\t\t\tif (n >= cmds->cnt)\n-\t\t\t\tbreak;\n-\t\t\tif (j == cols-1 || n + rows >= cmds->cnt)\n-\t\t\t\tsize = 1;\n-\t\t\tprintf(\"%-*s\", size, cmds->names[n]->name);\n-\t\t}\n-\t\tputchar('\\n');\n-\t}\n+\tfor (i = 0; i < cmds->cnt; i++)\n+\t\tstring_list_append(&c.items, cmds->names[i]->name);\n+\tdisplay_columns(&c, 2, \"  \");\n+\tstring_list_clear(&c.items, 0);\n }\n \n static int is_executable(const char *name)\n-- \n1.7.2.2\n"},{"id":"160702","messageId":"1297178541-31124-7-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 6/7] tag: support column output with --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:20Z","receivedAt":"2011-02-08T15:22:20Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-tag.txt |   11 ++++++++++-\n builtin/tag.c             |   19 ++++++++++++++++---\n 2 files changed, 26 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt\nindex 8b169e3..7a3ae28 100644\n--- a/Documentation/git-tag.txt\n+++ b/Documentation/git-tag.txt\n@@ -12,7 +12,8 @@ SYNOPSIS\n 'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n \t<tagname> [<commit> | <object>]\n 'git tag' -d <tagname>...\n-'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]\n+'git tag' [-n[<num>]] -l [--[no-]column[=<options>[,<option>]*]]\n+\t[--contains <commit>] [<pattern>]\n 'git tag' -v <tagname>...\n \n DESCRIPTION\n@@ -71,6 +72,14 @@ OPTIONS\n \tList tags with names that match the given pattern (or all if no pattern is given).\n \tTyping \"git tag\" without arguments, also lists all tags.\n \n+--column::\n+\tShow tags in columns. This option is only applicable if `git tag` is\n+\tused to list tags without annotation lines.\n+\n+--no-column::\n+\tShow tags in a single list. This option is used to override core.columns\n+\tif set. This option is only applicable if `git tag` is used to list tags.\n+\n --contains <commit>::\n \tOnly list tags which contain the specified commit.\n \ndiff --git a/builtin/tag.c b/builtin/tag.c\nindex aa1f87d..b74b8bf 100644\n--- a/builtin/tag.c\n+++ b/builtin/tag.c\n@@ -12,6 +12,7 @@\n #include \"tag.h\"\n #include \"run-command.h\"\n #include \"parse-options.h\"\n+#include \"column.h\"\n \n static const char * const git_tag_usage[] = {\n \t\"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]\",\n@@ -22,6 +23,7 @@ static const char * const git_tag_usage[] = {\n };\n \n static char signingkey[1000];\n+static struct column_layout layout;\n \n struct tag_filter {\n \tconst char *pattern;\n@@ -52,7 +54,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n \t\t}\n \n \t\tif (!filter->lines) {\n-\t\t\tprintf(\"%s\\n\", refname);\n+\t\t\tstring_list_append(&layout.items, refname);\n \t\t\treturn 0;\n \t\t}\n \t\tprintf(\"%-15s \", refname);\n@@ -383,6 +385,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING('u', NULL, &keyid, \"key-id\",\n \t\t\t\t\t\"use another key to sign the tag\"),\n \t\tOPT__FORCE(&force, \"replace the tag if exists\"),\n+\t\tOPT_COLUMN(0, \"column\", &layout, \"show tag list in columns\" ),\n \n \t\tOPT_GROUP(\"Tag listing options\"),\n \t\t{\n@@ -395,6 +398,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \t};\n \n \tgit_config(git_tag_config, NULL);\n+\tlayout.mode = core_column;\n+\tlayout.width = term_columns();\n+\tlayout.items.strdup_strings = 1;\n \n \targc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);\n \n@@ -413,9 +419,16 @@ int cmd_tag(int argc, const char **argv, const char *prefix)\n \n \tif (list + delete + verify > 1)\n \t\tusage_with_options(git_tag_usage, options);\n-\tif (list)\n-\t\treturn list_tags(argv[0], lines == -1 ? 0 : lines,\n+\tif (list) {\n+\t\tint ret;\n+\n+\t\tif (lines != -1)\n+\t\t\tlayout.mode = COL_PLAIN;\n+\t\tret =  list_tags(argv[0], lines == -1 ? 0 : lines,\n \t\t\t\t with_commit);\n+\t\tdisplay_columns(&layout, 2, \"\");\n+\t\treturn ret;\n+\t}\n \tif (lines != -1)\n \t\tdie(\"-n option is only allowed with -l.\");\n \tif (with_commit)\n-- \n1.7.2.2\n"},{"id":"160703","messageId":"1297178541-31124-8-git-send-email-pclouds@gmail.com","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"[PATCH 7/7] branch: support column output with --column","fromName":"Nguyễn Thái Ngọc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-08T15:22:21Z","receivedAt":"2011-02-08T15:22:21Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"\nSigned-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>\n---\n Documentation/git-branch.txt |    8 ++++++++\n builtin/branch.c             |   22 +++++++++++++++++++---\n 2 files changed, 27 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 9106d38..3ec997a 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -9,6 +9,7 @@ SYNOPSIS\n --------\n [verse]\n 'git branch' [--color[=<when>] | --no-color] [-r | -a]\n+\t[--[no-]column[=<options>[,<option>]*]]\n \t[-v [--abbrev=<length> | --no-abbrev]]\n \t[(--merged | --no-merged | --contains) [<commit>]]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n@@ -99,6 +100,13 @@ OPTIONS\n \tdefault to color output.\n \tSame as `--color=never`.\n \n+--column::\n+\tShow branches in columns. This option is ignored in verbose mode.\n+\n+--no-column::\n+\tShow branches in a single list. This option is used to override\n+\tcore.columns if set.\n+\n -r::\n \tList or delete (if used with -d) the remote-tracking branches.\n \ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 9e546e4..803fa5f 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -15,6 +15,7 @@\n #include \"branch.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"column.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -53,6 +54,8 @@ static enum merge_filter {\n } merge_filter;\n static unsigned char merge_filter_ref[20];\n \n+static struct column_layout layout;\n+\n static int parse_branch_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -451,7 +454,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,\n \t\tstrbuf_release(&stat);\n \t\tstrbuf_release(&subject);\n \t}\n-\tprintf(\"%s\\n\", out.buf);\n+\tstring_list_append(&layout.items, out.buf);\n \tstrbuf_release(&name);\n \tstrbuf_release(&out);\n }\n@@ -660,6 +663,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\t\tPARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,\n \t\t\topt_parse_merge_filter, (intptr_t) \"HEAD\",\n \t\t},\n+\t\tOPT_COLUMN(0, \"column\", &layout, \"list branches in columns\" ),\n \t\tOPT_END(),\n \t};\n \n@@ -686,6 +690,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t}\n \thashcpy(merge_filter_ref, head_sha1);\n \n+\tmemset(&layout, 0, sizeof(layout));\n+\tlayout.mode = core_column;\n+\tlayout.width = term_columns();\n+\tlayout.items.strdup_strings = 1;\n+\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \tif (!!delete + !!rename + !!force_create > 1)\n@@ -693,8 +702,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \tif (delete)\n \t\treturn delete_branches(argc, argv, delete > 1, kinds);\n-\telse if (argc == 0)\n-\t\treturn print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\telse if (argc == 0) {\n+\t\tint ret;\n+\t\tif (verbose)\n+\t\t\tlayout.mode = COL_PLAIN;\n+\n+\t\tret = print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n+\t\tdisplay_columns(&layout, 2, \"\");\n+\t\treturn ret;\n+\t}\n \telse if (rename && (argc == 1))\n \t\trename_branch(head, argv[0], rename > 1);\n \telse if (rename && (argc == 2))\n-- \n1.7.2.2\n"},{"id":"160715","messageId":"20110208224745.GA14190@sigill.intra.peff.net","threadId":"26446","inReplyTo":"1297178541-31124-1-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH/RFC 0/7] Column output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-02-08T22:47:45Z","receivedAt":"2011-02-08T22:47:45Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Feb 08, 2011 at 10:22:14PM +0700, Nguyễn Thái Ngọc Duy wrote:\n\n> In short, the series is very simple: give porcelain commands column\n> output, just like good old \"ls\". There could be a few more candidates,\n> I believe René Scharfe mentioned other files in \"git status\".\n\nI don't really care for column output myself, so maybe you have a good\nreason why my idea won't work. But why not use the BSD column program?\nI.e.:\n\n  git config pager.tag column\n  git config pager.branch column\n\nTrue it will use \"column\" no matter whether the specific branch or tag\noperation produces a lot of output or not, but in general that should be\nOK. Most \"action\" commands like \"tag -d\" or \"branch new old\" just\nproduce one line of output and look fine piped through column.\n\n-Peff\n"},{"id":"160719","messageId":"AANLkTi=WKnj8M2Uh+ACSay1STgOhWAjQbmspYSja3y0+@mail.gmail.com","threadId":"26446","inReplyTo":"20110208224745.GA14190@sigill.intra.peff.net","subject":"Re: [PATCH/RFC 0/7] Column output","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T00:13:45Z","receivedAt":"2011-02-09T00:13:45Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2011/2/9 Jeff King <peff@peff.net>:\n> On Tue, Feb 08, 2011 at 10:22:14PM +0700, Nguyễn Thái Ngọc Duy wrote:\n>\n>> In short, the series is very simple: give porcelain commands column\n>> output, just like good old \"ls\". There could be a few more candidates,\n>> I believe René Scharfe mentioned other files in \"git status\".\n>\n> I don't really care for column output myself, so maybe you have a good\n> reason why my idea won't work. But why not use the BSD column program?\n\nSolaris did not have one last time I checked. Windows obviously does\nnot either, but I don't use msysgit so it does not count.\n-- \nDuy\n"},{"id":"160734","messageId":"20110209051456.GA28159@elie","threadId":"26446","inReplyTo":"1297178541-31124-2-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 1/7] Move term_columns() to pager.c and save terminal width before pager","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-02-09T05:14:56Z","receivedAt":"2011-02-09T05:14:56Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Nguyễn Thái Ngọc Duy wrote:\n\n> term_columns() checks for terminal width via ioctl(1).\n\nioctl(2). :)\n\nI like the idea of checking width before launching a pager\nand exporting it, yes.\n\n> --- a/cache.h\n> +++ b/cache.h\n> @@ -1045,6 +1045,7 @@ extern void setup_pager(void);\n>  extern const char *pager_program;\n>  extern int pager_in_use(void);\n>  extern int pager_use_color;\n> +extern int term_columns();\n\nShould say (void) rather than leaving the parameter list unspecified,\nno?\n\nIt will only be needed by a few translation units, I hope.  Putting\nit in column.h can avoid some pointless recompilation.\n\n[...]\n> +++ b/pager.c\n> @@ -12,6 +12,7 @@\n>   */\n>  \n>  static int spawned_pager;\n> +static int max_columns;\n\nCould be stored in the COLUMNS environment variable, but an integer\nvariable like this is is simpler.\n\n[...]\n> @@ -80,6 +81,15 @@ void setup_pager(void)\n>  \n>  \tspawned_pager = 1; /* means we are emitting to terminal */\n>  \n> +#ifdef TIOCGWINSZ\n> +\t{\n> +\t\tstruct winsize ws;\n> +\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n> +\t\t\tif (ws.ws_col)\n> +\t\t\t\tmax_columns = ws.ws_col;\n> +\t\t}\n> +\t}\n> +#endif\n\nMm, repeated code.\n\nHow about something like this on top?  Not sure if the\n\"if (ws.ws_col)\" is useful.  I've left it alone.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n Makefile |    1 +\n cache.h  |    1 -\n column.h |    6 ++++++\n help.c   |    1 +\n pager.c  |   48 ++++++++++++++++++++++++++----------------------\n 5 files changed, 34 insertions(+), 23 deletions(-)\n create mode 100644 column.h\n\ndiff --git a/Makefile b/Makefile\nindex 775ee83..ed9e94b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1956,6 +1956,7 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h\n builtin/commit.o builtin/revert.o wt-status.o: wt-status.h\n builtin/tar-tree.o archive-tar.o: tar.h\n connect.o transport.o http-backend.o: url.h\n+pager.o help.o: column.h\n http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h\n http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h\n \ndiff --git a/cache.h b/cache.h\nindex bcbd5f2..d83d68c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1045,7 +1045,6 @@ extern void setup_pager(void);\n extern const char *pager_program;\n extern int pager_in_use(void);\n extern int pager_use_color;\n-extern int term_columns();\n \n extern const char *editor_program;\n extern const char *askpass_program;\ndiff --git a/column.h b/column.h\nnew file mode 100644\nindex 0000000..55d8067\n--- /dev/null\n+++ b/column.h\n@@ -0,0 +1,6 @@\n+#ifndef COLUMN_H\n+#define COLUMN_H\n+\n+extern int term_columns(void);\n+\n+#endif\ndiff --git a/help.c b/help.c\nindex 1344208..62a479b 100644\n--- a/help.c\n+++ b/help.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n #include \"exec_cmd.h\"\n+#include \"column.h\"\n #include \"levenshtein.h\"\n #include \"help.h\"\n #include \"common-cmds.h\"\ndiff --git a/pager.c b/pager.c\nindex ad447cf..e6f7d86 100644\n--- a/pager.c\n+++ b/pager.c\n@@ -1,6 +1,7 @@\n #include \"cache.h\"\n #include \"run-command.h\"\n #include \"sigchain.h\"\n+#include \"column.h\"\n \n #ifndef DEFAULT_PAGER\n #define DEFAULT_PAGER \"less\"\n@@ -14,6 +15,21 @@\n static int spawned_pager;\n static int max_columns;\n \n+#ifdef TIOCGWINSZ\n+static int retrieve_terminal_width(void)\n+{\n+\tstruct winsize ws;\n+\tif (ioctl(1, TIOCGWINSZ, &ws))\t/* e.g., ENOSYS */\n+\t\treturn 0;\n+\treturn ws.ws_col;\n+}\n+#else\n+static int retrieve_terminal_width(void)\n+{\n+\treturn 0;\n+}\n+#endif\n+\n #ifndef WIN32\n static void pager_preexec(void)\n {\n@@ -75,21 +91,17 @@ const char *git_pager(int stdout_is_tty)\n void setup_pager(void)\n {\n \tconst char *pager = git_pager(isatty(1));\n+\tint width;\n \n \tif (!pager)\n \t\treturn;\n \n \tspawned_pager = 1; /* means we are emitting to terminal */\n \n-#ifdef TIOCGWINSZ\n-\t{\n-\t\tstruct winsize ws;\n-\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n-\t\t\tif (ws.ws_col)\n-\t\t\t\tmax_columns = ws.ws_col;\n-\t\t}\n-\t}\n-#endif\n+\twidth = retrieve_terminal_width();\n+\tif (width)\n+\t\tmax_columns = width;\n+\n \t/* spawn the pager */\n \tpager_argv[0] = pager;\n \tpager_process.use_shell = 1;\n@@ -127,24 +139,16 @@ int pager_in_use(void)\n \treturn env ? git_config_bool(\"GIT_PAGER_IN_USE\", env) : 0;\n }\n \n-int term_columns()\n+int term_columns(void)\n {\n \tchar *col_string = getenv(\"COLUMNS\");\n \tint n_cols;\n \n \tif (col_string && (n_cols = atoi(col_string)) > 0)\n \t\treturn n_cols;\n-\n-\telse if (spawned_pager && max_columns)\n+\tif (spawned_pager && max_columns)\n \t\treturn max_columns;\n-#ifdef TIOCGWINSZ\n-\telse {\n-\t\tstruct winsize ws;\n-\t\tif (!ioctl(1, TIOCGWINSZ, &ws)) {\n-\t\t\tif (ws.ws_col)\n-\t\t\t\treturn ws.ws_col;\n-\t\t}\n-\t}\n-#endif\n-\treturn 80;\n+\n+\tn_cols = retrieve_terminal_width();\n+\treturn n_cols ? n_cols : 80;\n }\n-- \n1.7.4\n"},{"id":"160735","messageId":"20110209054210.GA2019@elie","threadId":"26446","inReplyTo":"AANLkTi=WKnj8M2Uh+ACSay1STgOhWAjQbmspYSja3y0+@mail.gmail.com","subject":"Re: [PATCH/RFC 0/7] Column output","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-02-09T05:42:10Z","receivedAt":"2011-02-09T05:42:10Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Some general reflections.\n\nNguyen Thai Ngoc Duy wrote:\n> 2011/2/9 Jeff King <peff@peff.net>:\n\n>> I don't really care for column output myself,\n\nMe neither.  I tricked myself into reading with a vague hope that it\nmight lead to a porcelain-level \"git ls\" command.\n\n[...]\n>> But why not use the BSD column program?\n>\n> Solaris did not have one last time I checked. Windows obviously does\n> not either, but I don't use msysgit so it does not count.\n\nA good reason to port the \"column\" program, no?  I admit I find this a\ncompelling excuse not to bother with getting --column options and\ncore.column configuration right.\n\nAll that said.\n\nPatches 1, 2, and 5 replace git help's existing columnation code\nwith something more reusable and functional.  After those patches,\n\n\tgit --paginate help -a\n\nno longer forces 80-column output.  Another application of\nterm_columns could be to teach \"git log\" and \"git log --graph\"\nto wrap to terminal width some day.\n\nSo if I ran the world, I would put those three patches before\n--column et al in a possible reroll. :)\n\nThanks for some clear and pleasant reading.\nJonathan\n"},{"id":"160736","messageId":"AANLkTik+siMEgLu9NnhjPttwqsexMwHnHLBJGoCfVxHW@mail.gmail.com","threadId":"26446","inReplyTo":"20110209054210.GA2019@elie","subject":"Re: [PATCH/RFC 0/7] Column output","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T05:59:22Z","receivedAt":"2011-02-09T05:59:22Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Feb 9, 2011 at 12:42 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> Some general reflections.\n>\n> Nguyen Thai Ngoc Duy wrote:\n>> 2011/2/9 Jeff King <peff@peff.net>:\n>\n>>> I don't really care for column output myself,\n>\n> Me neither.  I tricked myself into reading with a vague hope that it\n> might lead to a porcelain-level \"git ls\" command.\n\nWait for it. I have something hackish but usable. Non-recursive,\ncolorized column output. Not full of feature as GNU ls though (for\nexample, sorting by date can't be implemented).\n-- \nDuy\n"},{"id":"160737","messageId":"20110209073655.GA2135@elie","threadId":"26446","inReplyTo":"1297178541-31124-3-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 2/7] Add column layout","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-02-09T07:36:55Z","receivedAt":"2011-02-09T07:36:55Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Nguyễn Thái Ngọc Duy wrote:\n\n> +++ b/column.c\n> @@ -0,0 +1,177 @@\n[...]\n> +static int item_length(const struct column_layout *c, const char *s)\n> +{\n> +\tint a_len = 0;\n> +\n> +\tif (!(c->mode & COL_ANSI))\n> +\t\treturn strlen(s);\n> +\n> +\twhile ((s = strstr(s, \"\\033[\")) != NULL) {\n> +\t\tint len = strspn(s+2, \"0123456789;\");\n> +\t\ts += len+3; /* \\033[<len><func char> */\n> +\t\ta_len += len+3;\n> +\t}\n> +\treturn a_len;\n> +}\n\nI think you mean \"return strlen(orig_s) - a_len\".\n\nSomething like the following could be more obvious, though\nit is unfortunately verbose.\n\n\tint len = 0;\n\twhile (*s) {\n\t\tconst char *next;\n\n\t\t/* \\033[<len><func char> */\n\t\tif (!prefixcmp(s, \"\\033[\")) {\n\t\t\ts += strlen(\"\\033[\");\n\t\t\ts += strspn(s, \"0123456789;\");\n\t\t\tif (!*s)\n\t\t\t\t... handle somehow ...\n\t\t\ts++;\n\t\t}\n\n\t\tnext = strchrnul(s, '\\033');\n\t\tlen += next - s;\n\t\ts = next;\n\t}\n\nBoth miscompute the width of \"Dépôt\".  Something like this can do ok\nif the string is modifiable and we know it is UTF-8:\n\n\t\tchar save;\n\t\t...\n\t\tnext = strchrnul(s, '\\033');\n\n\t\t/*\n\t\t * NEEDSWORK: a utf8_strwidth variant that\n\t\t * accepts a memory area not terminated by \\0\n\t\t * would avoid this ugliness.\n\t\t */\n\t\tsave = *next;\n\t\t*next = '\\0';\n\t\tlen += utf8_strwidth(s);\n\t\t*next = save;\n\nPOSIX does not provide a strwidth function, so if we want to handle\nencodings like SHIFT-JIS then something uglier[1] might come to life.\n\n> +static void layout(const struct column_layout *c,\n> +\t\t   int total_width, int padding,\n> +\t\t   int *width,\n> +\t\t   int *rows, int *cols)\n> +{\n[...]\n> +\t*rows = c->items.nr / *cols;\n> +\tif (c->items.nr > *rows * *cols)\n> +\t\t(*rows)++;\n\nMaybe\n\t*rows = DIV_ROUND_UP(c->items.nr, *cols);\n?\n\n> +static int squeeze_columns(const struct column_layout *c,\n> +\t\t\t   int *width, int padding,\n> +\t\t\t   int rows, int cols)\n\nWhat does this function do?\n\nAh, it is for computing smaller, unequal column widths.\n\n> +{\n> +\tint x, y, i, len, item_len, spare = 0;\n> +\tconst char *s;\n> +\n> +\tfor (x = 0; x < cols; x++) {\n\n\t\t/* Find minimum width for column. */\n\t\tint len = 0;\n\n> +\t\tfor (y = len = 0; y < rows; y++) {\n[...]\n> +\t\tlen += padding;\n> +\t\tif (len < width[x]) {\n> +\t\t\tspare += width[x] - len;\n> +\t\t\twidth[x] = len;\n> +\t\t}\n\nThis \"if\" is unnecessary, I hope.  A simple\n\n\t\tassert(len <= width[x]);\n\nwould check that.\n\n[...]\n> +static void relayout(const struct column_layout *c,\n> +\t\t     int padding, int spare,\n> +\t\t     int *initial_width, int **width,\n> +\t\t     int *rows, int *cols)\n> +{\n> +\tint new_rows, new_cols, new_initial_width;\n> +\tint i, *new_width, new_spare, total_width;\n> +\n> +\t/*\n> +\t * Assume all columns have same width, we would need\n> +\t * initial_width*cols. But then after squeezing, we have\n> +\t * \"spare\" more chars. Assume a new total_width with\n> +\t * additional chars, then re-squeeze to see if it fits\n> +\t * c->width.\n> +\t */\n\nMight be easier to debug if this part were deferred to a separate\npatch. :)\n\n> +\ttotal_width = (*initial_width)*(*cols) + spare;\n\nAn odd heuristic.  Does it work well in practice?\n\n> +static void display_columns_first(const struct column_layout *c,\n> +\t\t\t\t  int padding, const char *indent)\n> +{\n[...]\n> +\tfor (y = 0; y < rows; y++) {\n> +\t\tfor (x = 0; x < cols; x++) {\n[...]\n> +\t\t\tconst char *s;\n> +\t\t\tint len;\n> +\n> +\t\t\ti = x * rows + y;\n> +\t\t\tif (i >= c->items.nr)\n> +\t\t\t\tbreak;\n> +\t\t\ts = c->items.items[i].string;\n> +\t\t\tlen = item_length(c, s);\n> +\t\t\tif (width[x] < initial_width)\n> +\t\t\t\tlen += initial_width - width[x];\n\nThe \"if\" is unnecessary, I hope.  (With that hope being checkable\nby assert(width[x] <= initial_width).)\n\n> +\n> +\t\t\tprintf(\"%s%s%s\",\n> +\t\t\t       x == 0 ? indent : \"\",\n> +\t\t\t       c->items.items[i].string,\n> +\t\t\t       /* If the next column at same row is\n> +\t\t\t\t  out of range, end the line. Else pad\n> +\t\t\t\t  some space.  */\n> +\t\t\t       i + rows >= c->items.nr ? \"\\n\" : empty_cell + len);\n\nNice.  The loop body could be a separate display_cell() function for\neasier contemplation.\n\n[...]\n> +++ b/column.h\n> @@ -0,0 +1,20 @@\n[...]\n> +#define COL_DENSE         (1 << 5) /* \"Ragged-right\" mode, relayout if enough space */\n\nI might be confused, but would it not be clearer to call it \"unequal\ncolumn width\" mode?  In other words, is it about columns not all\nhaving the same width as one another or about having a ragged right\nmargin?\n\nThanks for a pleasant read.\nJonathan\n\n[1] Based on tuklib_mbstr_width:\n\n\tsize_t remaining = strlen(s);\n\tsize_t len = 0;\n\tmbstate_t state;\n\n\tmemset(&state, 0, sizeof(state));\n\twhile (remaining) {\n\t\twchar_t ch;\n\t\tsize_t nbytes;\n\t\tint width;\n\n\t\tif (!prefixcmp(s, \"\\033[\"))\n\t\t\t...\n\t\tnbytes = mbrtowc(&ch, s, remaining, &state);\n\t\tif (!nbytes || nbytes > remaining)\n\t\t\t... handle error ...\n\t\ts += nbytes;\n\t\tremaining -= nbytes;\n\t\twidth = wcwidth(ch);\n\t\tif (width < 0)\n\t\t\t... handle nonprintable character ...\n\t\tlen += width;\n\t}\n"},{"id":"160738","messageId":"20110209073953.GB2135@elie","threadId":"26446","inReplyTo":"1297178541-31124-6-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 5/7] help: reuse struct column_layout","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-02-09T07:39:53Z","receivedAt":"2011-02-09T07:39:53Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Nguyễn Thái Ngọc Duy wrote:\n\n[...]\n> +++ b/help.c\n> @@ -72,29 +73,17 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n[...]\n> +\tstruct column_layout c;\n> +\tint i;\n> +\n> +\tmemset(&c, 0, sizeof(c));\n\nMight be nice to have a COLUMN_LAYOUT_INIT, I suppose.\n\nLooks sane.  What output changes would be noticeable, if any?\n"},{"id":"160746","messageId":"AANLkTinTvTdng2MpVmPfF3O1c08mFEEr1x_pv1_1unBX@mail.gmail.com","threadId":"26446","inReplyTo":"20110209073953.GB2135@elie","subject":"Re: [PATCH 5/7] help: reuse struct column_layout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T11:21:33Z","receivedAt":"2011-02-09T11:21:33Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2011/2/9 Jonathan Nieder <jrnieder@gmail.com>:\n> Nguyễn Thái Ngọc Duy wrote:\n>\n> [...]\n>> +++ b/help.c\n>> @@ -72,29 +73,17 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)\n> [...]\n>> +     struct column_layout c;\n>> +     int i;\n>> +\n>> +     memset(&c, 0, sizeof(c));\n>\n> Might be nice to have a COLUMN_LAYOUT_INIT, I suppose.\n>\n> Looks sane.  What output changes would be noticeable, if any?\n\nTwo spaces between column instead of one. Will fix.\n-- \nDuy\n"},{"id":"160747","messageId":"AANLkTimuae1rDn2+UH3mg1zBS5LsS5++8z1-yY0cBmhB@mail.gmail.com","threadId":"26446","inReplyTo":"20110209073655.GA2135@elie","subject":"Re: [PATCH 2/7] Add column layout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-09T11:24:23Z","receivedAt":"2011-02-09T11:24:23Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2011/2/9 Jonathan Nieder <jrnieder@gmail.com>:\n> Nguyễn Thái Ngọc Duy wrote:\n>\n>> +++ b/column.c\n>> @@ -0,0 +1,177 @@\n> [...]\n>> +static int item_length(const struct column_layout *c, const char *s)\n>> +{\n>> +     int a_len = 0;\n>> +\n>> +     if (!(c->mode & COL_ANSI))\n>> +             return strlen(s);\n>> +\n>> +     while ((s = strstr(s, \"\\033[\")) != NULL) {\n>> +             int len = strspn(s+2, \"0123456789;\");\n>> +             s += len+3; /* \\033[<len><func char> */\n>> +             a_len += len+3;\n>> +     }\n>> +     return a_len;\n>> +}\n>\n> I think you mean \"return strlen(orig_s) - a_len\".\n>\n> Something like the following could be more obvious, though\n> it is unfortunately verbose.\n>\n>        int len = 0;\n>        while (*s) {\n>                const char *next;\n>\n>                /* \\033[<len><func char> */\n>                if (!prefixcmp(s, \"\\033[\")) {\n>                        s += strlen(\"\\033[\");\n>                        s += strspn(s, \"0123456789;\");\n>                        if (!*s)\n>                                ... handle somehow ...\n>                        s++;\n>                }\n>\n>                next = strchrnul(s, '\\033');\n>                len += next - s;\n>                s = next;\n>        }\n>\n> Both miscompute the width of \"Dépôt\".  Something like this can do ok\n> if the string is modifiable and we know it is UTF-8:\n>\n>                char save;\n>                ...\n>                next = strchrnul(s, '\\033');\n>\n>                /*\n>                 * NEEDSWORK: a utf8_strwidth variant that\n>                 * accepts a memory area not terminated by \\0\n>                 * would avoid this ugliness.\n>                 */\n>                save = *next;\n>                *next = '\\0';\n>                len += utf8_strwidth(s);\n>                *next = save;\n>\n> POSIX does not provide a strwidth function, so if we want to handle\n> encodings like SHIFT-JIS then something uglier[1] might come to life.\n\nI think UTF-8 is enough. Git does not produce complex ansi escape\ncodes. Perhaps checking them at the beginning and reset code at the\nend is enough.\n\n> [...]\n>> +static void relayout(const struct column_layout *c,\n>> +                  int padding, int spare,\n>> +                  int *initial_width, int **width,\n>> +                  int *rows, int *cols)\n>> +{\n>> +     int new_rows, new_cols, new_initial_width;\n>> +     int i, *new_width, new_spare, total_width;\n>> +\n>> +     /*\n>> +      * Assume all columns have same width, we would need\n>> +      * initial_width*cols. But then after squeezing, we have\n>> +      * \"spare\" more chars. Assume a new total_width with\n>> +      * additional chars, then re-squeeze to see if it fits\n>> +      * c->width.\n>> +      */\n>\n> Might be easier to debug if this part were deferred to a separate\n> patch. :)\n>\n>> +     total_width = (*initial_width)*(*cols) + spare;\n>\n> An odd heuristic.  Does it work well in practice?\n\nIt seems so. If it does not, I guess I would need to look into how GNU\nls does it.\n-- \nDuy\n"},{"id":"160787","messageId":"7vvd0sop15.fsf@alter.siamese.dyndns.org","threadId":"26446","inReplyTo":"1297178541-31124-7-git-send-email-pclouds@gmail.com","subject":"Re: [PATCH 6/7] tag: support column output with --column","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-02-09T21:51:02Z","receivedAt":"2011-02-09T21:51:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n\n> @@ -52,7 +54,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n>  \t\t}\n>  \n>  \t\tif (!filter->lines) {\n> -\t\t\tprintf(\"%s\\n\", refname);\n> +\t\t\tstring_list_append(&layout.items, refname);\n\nHmm, is this robbing streaming output from people who do not care about\ncolumnar output?\n\nI tend to agree with others who suggested you to port column to platforms\nthat lack it.\n"},{"id":"160809","messageId":"AANLkTinHyX+uiO+Oj3N3NAEjbZmE=vbKsA+C+tQ6B-Db@mail.gmail.com","threadId":"26446","inReplyTo":"7vvd0sop15.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 6/7] tag: support column output with --column","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-02-10T02:35:38Z","receivedAt":"2011-02-10T02:35:38Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"2011/2/10 Junio C Hamano <gitster@pobox.com>:\n> Nguyễn Thái Ngọc Duy  <pclouds@gmail.com> writes:\n>\n>> @@ -52,7 +54,7 @@ static int show_reference(const char *refname, const unsigned char *sha1,\n>>               }\n>>\n>>               if (!filter->lines) {\n>> -                     printf(\"%s\\n\", refname);\n>> +                     string_list_append(&layout.items, refname);\n>\n> Hmm, is this robbing streaming output from people who do not care about\n> columnar output?\n\nYes, but tag list is short enough streaming does not really matter. I\ncan check if column mode is active there, if it's not, just printf.\n\n> I tend to agree with others who suggested you to port column to platforms\n> that lack it.\n\nThe first version of this series created git-column and made a pipe to\ngit-column when column output is active. I'll resurrect the command\nthen. Original column does not understand ANSI escape codes for\ncoloring and mess up column output.\n-- \nDuy\n"},{"id":"160812","messageId":"87aai4r43r.fsf@catnip.gol.com","threadId":"26446","inReplyTo":"AANLkTinHyX+uiO+Oj3N3NAEjbZmE=vbKsA+C+tQ6B-Db@mail.gmail.com","subject":"Re: [PATCH 6/7] tag: support column output with --column","fromName":"Miles Bader","fromEmail":"miles@gnu.org","sentAt":"2011-02-10T02:54:48Z","receivedAt":"2011-02-10T02:54:48Z","isPatch":true,"sender":{"key":"miles@gnu.org","avatar":"https://gravatar.com/avatar/01069b69593af7bff28e2f97afeb3644ae6fe2f5f56cb3a8cf34c5fb8c36efe5?d=mp&s=160"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>> Hmm, is this robbing streaming output from people who do not care about\n>> columnar output?\n>\n> Yes, but tag list is short enough streaming does not really matter. I\n> can check if column mode is active there, if it's not, just printf.\n\n...but it's important to note, I think that in many projects the tag\nlist is at least long enough that columnar output would be hugely\nuseful...\n\n-miles\n\n-- \nCustard, n. A vile concoction produced by a malevolent conspiracy of the hen,\nthe cow, and the cook.\n"}]}