3 #include "string-list.h"
4 #include "parse-options.h"
7 #define XY2LINEAR(d, x, y) (COL_LAYOUT((d)->colopts) == COL_COLUMN ? \
8 (x) * (d)->rows + (y) : \
12 const struct string_list
*list
;
14 struct column_options opts
;
17 int *len
; /* cell length */
20 /* return length of 's' in letters, ANSI escapes stripped */
21 static int item_length(unsigned int colopts
, const char *s
)
24 struct strbuf str
= STRBUF_INIT
;
26 strbuf_addstr(&str
, s
);
27 while ((s
= strstr(str
.buf
+ i
, "\033[")) != NULL
) {
28 int len
= strspn(s
+ 2, "0123456789;");
30 strbuf_remove(&str
, i
, len
+ 3); /* \033[<len><func char> */
32 len
= utf8_strwidth(str
.buf
);
38 * Calculate cell width, rows and cols for a table of equal cells, given
39 * table width and how many spaces between cells.
41 static void layout(struct column_data
*data
, int *width
)
46 for (i
= 0; i
< data
->list
->nr
; i
++)
47 if (*width
< data
->len
[i
])
48 *width
= data
->len
[i
];
50 *width
+= data
->opts
.padding
;
52 data
->cols
= (data
->opts
.width
- strlen(data
->opts
.indent
)) / *width
;
56 data
->rows
= DIV_ROUND_UP(data
->list
->nr
, data
->cols
);
59 /* Display without layout when not enabled */
60 static void display_plain(const struct string_list
*list
,
61 const char *indent
, const char *nl
)
65 for (i
= 0; i
< list
->nr
; i
++)
66 printf("%s%s%s", indent
, list
->items
[i
].string
, nl
);
69 /* Print a cell to stdout with all necessary leading/traling space */
70 static int display_cell(struct column_data
*data
, int initial_width
,
71 const char *empty_cell
, int x
, int y
)
75 i
= XY2LINEAR(data
, x
, y
);
76 if (i
>= data
->list
->nr
)
79 if (COL_LAYOUT(data
->colopts
) == COL_COLUMN
)
80 newline
= i
+ data
->rows
>= data
->list
->nr
;
82 newline
= x
== data
->cols
- 1 || i
== data
->list
->nr
- 1;
85 x
== 0 ? data
->opts
.indent
: "",
86 data
->list
->items
[i
].string
,
87 newline
? data
->opts
.nl
: empty_cell
+ len
);
91 /* Display COL_COLUMN or COL_ROW */
92 static void display_table(const struct string_list
*list
,
94 const struct column_options
*opts
)
96 struct column_data data
;
97 int x
, y
, i
, initial_width
;
100 memset(&data
, 0, sizeof(data
));
102 data
.colopts
= colopts
;
105 data
.len
= xmalloc(sizeof(*data
.len
) * list
->nr
);
106 for (i
= 0; i
< list
->nr
; i
++)
107 data
.len
[i
] = item_length(colopts
, list
->items
[i
].string
);
109 layout(&data
, &initial_width
);
111 empty_cell
= xmalloc(initial_width
+ 1);
112 memset(empty_cell
, ' ', initial_width
);
113 empty_cell
[initial_width
] = '\0';
114 for (y
= 0; y
< data
.rows
; y
++) {
115 for (x
= 0; x
< data
.cols
; x
++)
116 if (display_cell(&data
, initial_width
, empty_cell
, x
, y
))
124 void print_columns(const struct string_list
*list
, unsigned int colopts
,
125 const struct column_options
*opts
)
127 struct column_options nopts
;
131 assert((colopts
& COL_ENABLE_MASK
) != COL_AUTO
);
133 memset(&nopts
, 0, sizeof(nopts
));
134 nopts
.indent
= opts
&& opts
->indent
? opts
->indent
: "";
135 nopts
.nl
= opts
&& opts
->nl
? opts
->nl
: "\n";
136 nopts
.padding
= opts
? opts
->padding
: 1;
137 nopts
.width
= opts
&& opts
->width
? opts
->width
: term_columns() - 1;
138 if (!column_active(colopts
)) {
139 display_plain(list
, "", "\n");
142 switch (COL_LAYOUT(colopts
)) {
144 display_plain(list
, nopts
.indent
, nopts
.nl
);
148 display_table(list
, colopts
, &nopts
);
151 die("BUG: invalid layout mode %d", COL_LAYOUT(colopts
));
155 int finalize_colopts(unsigned int *colopts
, int stdout_is_tty
)
157 if ((*colopts
& COL_ENABLE_MASK
) == COL_AUTO
) {
158 if (stdout_is_tty
< 0)
159 stdout_is_tty
= isatty(1);
160 *colopts
&= ~COL_ENABLE_MASK
;
162 *colopts
|= COL_ENABLED
;
176 static int parse_option(const char *arg
, int len
, unsigned int *colopts
,
179 struct colopt opts
[] = {
180 { "always", COL_ENABLED
, COL_ENABLE_MASK
},
181 { "never", COL_DISABLED
, COL_ENABLE_MASK
},
182 { "auto", COL_AUTO
, COL_ENABLE_MASK
},
183 { "plain", COL_PLAIN
, COL_LAYOUT_MASK
},
184 { "column", COL_COLUMN
, COL_LAYOUT_MASK
},
185 { "row", COL_ROW
, COL_LAYOUT_MASK
},
189 for (i
= 0; i
< ARRAY_SIZE(opts
); i
++) {
190 int arg_len
= len
, name_len
;
191 const char *arg_str
= arg
;
193 name_len
= strlen(opts
[i
].name
);
194 if (arg_len
!= name_len
||
195 strncmp(arg_str
, opts
[i
].name
, name_len
))
198 switch (opts
[i
].mask
) {
199 case COL_ENABLE_MASK
:
200 *group_set
|= ENABLE_SET
;
202 case COL_LAYOUT_MASK
:
203 *group_set
|= LAYOUT_SET
;
208 *colopts
= (*colopts
& ~opts
[i
].mask
) | opts
[i
].value
;
212 return error("unsupported option '%s'", arg
);
215 static int parse_config(unsigned int *colopts
, const char *value
)
217 const char *sep
= " ,";
221 int len
= strcspn(value
, sep
);
223 if (parse_option(value
, len
, colopts
, &group_set
))
228 value
+= strspn(value
, sep
);
231 * Setting layout implies "always" if neither always, never
232 * nor auto is specified.
234 * Current value in COL_ENABLE_MASK is disregarded. This means if
235 * you set column.ui = auto and pass --column=row, then "auto"
236 * will become "always".
238 if ((group_set
& LAYOUT_SET
) && !(group_set
& ENABLE_SET
))
239 *colopts
= (*colopts
& ~COL_ENABLE_MASK
) | COL_ENABLED
;
243 static int column_config(const char *var
, const char *value
,
244 const char *key
, unsigned int *colopts
)
247 return config_error_nonbool(var
);
248 if (parse_config(colopts
, value
))
249 return error("invalid column.%s mode %s", key
, value
);
253 int git_column_config(const char *var
, const char *value
,
254 const char *command
, unsigned int *colopts
)
256 const char *it
= skip_prefix(var
, "column.");
260 if (!strcmp(it
, "ui"))
261 return column_config(var
, value
, "ui", colopts
);
263 if (command
&& !strcmp(it
, command
))
264 return column_config(var
, value
, it
, colopts
);
269 int parseopt_column_callback(const struct option
*opt
,
270 const char *arg
, int unset
)
272 unsigned int *colopts
= opt
->value
;
273 *colopts
|= COL_PARSEOPT
;
274 *colopts
&= ~COL_ENABLE_MASK
;
275 if (unset
) /* --no-column == never */
277 /* --column == always unless "arg" states otherwise */
278 *colopts
|= COL_ENABLED
;
280 return parse_config(colopts
, arg
);