]> git.ipfire.org Git - thirdparty/util-linux.git/blame - text-utils/column.c
taskset: Accept 0 pid for current process
[thirdparty/util-linux.git] / text-utils / column.c
CommitLineData
6dbe3af9
KZ
1/*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
7d07df62
KZ
5 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
6 *
6dbe3af9
KZ
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
6dbe3af9
KZ
35#include <sys/types.h>
36#include <sys/ioctl.h>
37
38#include <ctype.h>
63d79371
ZJS
39#include <errno.h>
40#include <stdbool.h>
6dbe3af9
KZ
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
63d79371 44#include <unistd.h>
a4cc8dfe 45#include <getopt.h>
6dbe3af9 46
3b56eea7 47#include "nls.h"
eb76ca98 48#include "c.h"
9ea8ded3 49#include "widechar.h"
a29e40ca 50#include "xalloc.h"
02b77f7b 51#include "strutils.h"
b87cbe84 52#include "closestream.h"
3b56eea7 53#include "ttyutils.h"
11a1092a 54#include "strv.h"
be03f652 55#include "optutils.h"
dda229c7 56#include "mbsalign.h"
eb63b9b8 57
4762ae9d 58#include "libsmartcols.h"
eb63b9b8 59
739e58ff 60#define TABCHAR_CELLS 8
683ddbd5 61
7d07df62
KZ
62enum {
63 COLUMN_MODE_FILLCOLS = 0,
64 COLUMN_MODE_FILLROWS,
65 COLUMN_MODE_TABLE,
66 COLUMN_MODE_SIMPLE
67};
68
69struct column_control {
2593c139 70 int mode; /* COLUMN_MODE_* */
35d5d655 71 size_t termwidth; /* -1 uninilialized, 0 unlimited, >0 width (default is 80) */
2593c139 72
4762ae9d
KZ
73 struct libscols_table *tab;
74
7e947f48
KZ
75 char **tab_colnames; /* array with column names */
76 const char *tab_name; /* table name */
77 const char *tab_order; /* --table-order */
78
b4526713
KZ
79 char **tab_columns; /* array from --table-column */
80
1ae24ec2
KZ
81 const char *tab_colright; /* --table-right */
82 const char *tab_coltrunc; /* --table-trunc */
83 const char *tab_colnoextrem; /* --table-noextreme */
68916af3 84 const char *tab_colwrap; /* --table-wrap */
9624f615 85 const char *tab_colhide; /* --table-hide */
11a1092a 86
90852c3e
KZ
87 const char *tree;
88 const char *tree_id;
89 const char *tree_parent;
90
4762ae9d
KZ
91 wchar_t *input_separator;
92 const char *output_separator;
93
0784187d
KZ
94 wchar_t **ents; /* input entries */
95 size_t nents; /* number of entries */
96 size_t maxlength; /* longest input record (line) */
cb776288 97 size_t maxncols; /* maximal number of input columns */
aec8bf94 98 size_t mincolsep; /* minimal spaces between columns */
7d07df62 99
63d79371
ZJS
100 bool greedy,
101 json,
102 header_repeat,
103 hide_unnamed,
104 maxout : 1,
105 keep_empty_lines, /* --keep-empty-lines */
106 tab_noheadings,
107 use_spaces;
4762ae9d 108};
7d07df62 109
e76a9a28
JR
110typedef enum {
111 ANSI_CHR = 'A',
55cc02c9 112 ANSI_ESC = 0x1b,
e76a9a28
JR
113 ANSI_SGR = '[',
114 ANSI_OSC = ']',
9131a310
JR
115 ANSI_LNK = '8',
116 ANSI_LBL = 0x7,
b787803e
JR
117 ANSI_LSP = ';',
118 ANSI_LSG = 'M',
9131a310 119 ANSI_END = '\\'
55cc02c9 120} ansi_esc_states;
e76a9a28 121
8c25fc4b
JR
122/**
123 * Count how many characters are non-printable due to ANSI X3.41 escape codes.
124 *
9131a310
JR
125 * It detects and count Fe Escape and OSC 8 links sequences. These sequences contains
126 * characters that normally are printable, but due to being part of a escape sequence
127 * are ignored when displayed in console terminals.
8c25fc4b 128 */
9131a310 129static inline size_t ansi_esc_width(ansi_esc_states *state, size_t *found, const wchar_t *str, int chw)
55cc02c9 130{
e76a9a28
JR
131 switch (*state) {
132 case ANSI_CHR:
55cc02c9
JR
133 // ANSI X3.41 escape sequences begin with ESC ( written as \x1b \033 or ^[ )
134 if (*str == 0x1b)
e76a9a28 135 *state = ANSI_ESC;
55cc02c9 136 // Ignore 1 byte C1 control codes (0x80–0x9F) due to conflict with UTF-8 and CP-1252
e76a9a28
JR
137 return 0;
138 case ANSI_ESC:
55cc02c9 139 // Fe escape sequences allows the range 0x40 to 0x5f
e76a9a28 140 switch (*str) {
55cc02c9 141 case '[': // CSI - Control Sequence Introducer
e76a9a28
JR
142 *state = ANSI_SGR;
143 break;
55cc02c9 144 case ']': // OSC - Operating System Command
e76a9a28
JR
145 *state = ANSI_OSC;
146 break;
55cc02c9
JR
147 case '_': // APC - Application Program Command
148 case 'P': // DCS - Device Control String
149 case '^': // PM - Privacy Message
9131a310 150 *state = ANSI_END;
e76a9a28
JR
151 break;
152 default:
153 *state = ANSI_CHR;
154 return 0;
155 }
156 *found = 1;
157 return 0;
158 case ANSI_SGR:
9131a310 159 *found += chw;
55cc02c9
JR
160 // Fe escape sequences allows the range 0x30-0x3f
161 // However SGR (Select Graphic Rendition) only uses: 0-9 ';' ':'
e76a9a28
JR
162 if (*str >= '0' && *str <= '?')
163 return 0;
55cc02c9 164 // Fe ends with the range 0x40-0x7e but SGR ends with 'm'
e76a9a28
JR
165 if (*str <= '@' && *str >= '~')
166 *found = 0;
167 break;
e76a9a28 168 case ANSI_OSC:
9131a310
JR
169 *found += chw;
170 if (*str == ANSI_LNK) // OSC8-Link
171 *state = ANSI_LNK;
172 else
173 *state = ANSI_END; // other command sequences are ignored
174 return 0;
175 case ANSI_LNK: // OSC8 Terminal Hiperlink Sequence
176 switch (*str) {
177 case 0x7: // Separated by BEL
178 *state = ANSI_LBL; //# \e]8;;LINK\aTEXT\e]8;;\a #
e76a9a28 179 break;
b787803e 180 case 0x1b: // OSC8-Link separated by ESC-BACKSLASH
9131a310
JR
181 *found += 2;
182 *state = ANSI_LBL; //# \e]8;;LINK\e\\TEXT\e]8;;\e\\ #
55cc02c9 183 break;
9131a310 184 default:
b787803e 185 *found += chw;
9131a310
JR
186 }
187 return 0; // ignore link width
188 case ANSI_LBL:
189 if (*str == 0x1b) { // Link label goes until ESC BACKSLASH
190 *found += chw;
b787803e 191 *state = ANSI_LSP;
9131a310 192 }
e76a9a28 193 return 0;
b787803e
JR
194 case ANSI_LSP:
195 *found += chw;
196 if (*str == '[') // SGR FG/BG colors nested inside OSC8-Link sequence
197 *state = ANSI_LSG;
198 else
199 *state = ANSI_END; //# Link label ends with \e[8;;\e\\ #
200 return 0;
201 case ANSI_LSG: //# \e]8;;LINK\e\\\e[1;34mTEXT\e[0m\e]8;;\e\\ #
202 *found += chw;
203 if (*str < '0' || *str > '?') // SGR color sequence ends with 'm'
204 *state = ANSI_LBL;
205 return 0;
9131a310
JR
206 case ANSI_END:
207 switch (*str) {
b787803e 208 case 0x1b: // APC/OSC8-Links ends with ESC-BACKSLASH
9131a310 209 *found += chw;
e76a9a28 210 break;
9131a310 211 case 0x7: // APC/OSC/OSC8-Links ends with BEL
0e28ab36 212#ifdef HAVE_WIDECHAR
9131a310 213 case 0x9c: // APC/DCS/DM ends with ST (String Terminator)
0e28ab36 214#endif
9131a310
JR
215 break;
216 default:
217 *found += chw;
e76a9a28
JR
218 return 0;
219 }
9131a310 220 }
e76a9a28
JR
221 size_t res = *found;
222 *state = ANSI_CHR;
223 *found = 0;
224 return res;
225}
226
8f1be588 227static size_t width(const wchar_t *str)
683ddbd5 228{
e76a9a28
JR
229 size_t count = 0;
230 size_t found = 0;
55cc02c9 231 ansi_esc_states state = ANSI_CHR;
683ddbd5
KZ
232
233 for (; *str != '\0'; str++) {
8f1be588
KZ
234#ifdef HAVE_WIDECHAR
235 int x = wcwidth(*str); /* don't use wcswidth(), need to ignore non-printable */
683ddbd5 236#else
9131a310 237 int x = isprint(*str) ? 1 : 0;
8f1be588 238#endif
9131a310
JR
239 int chw = x > 0 ? x : 0;
240 size_t nonpr = ansi_esc_width(&state, &found, str, chw);
241 count += chw - nonpr;
683ddbd5 242 }
e76a9a28 243 return count;
683ddbd5 244}
683ddbd5 245
37d84d6d
KZ
246static wchar_t *mbs_to_wcs(const char *s)
247{
8f1be588 248#ifdef HAVE_WIDECHAR
37d84d6d
KZ
249 ssize_t n;
250 wchar_t *wcs;
251
252 n = mbstowcs((wchar_t *)0, s, 0);
253 if (n < 0)
254 return NULL;
86399c33 255 wcs = xcalloc((n + 1) * sizeof(wchar_t), 1);
37d84d6d
KZ
256 n = mbstowcs(wcs, s, n + 1);
257 if (n < 0) {
258 free(wcs);
259 return NULL;
260 }
261 return wcs;
8f1be588 262#else
8bc5195b 263 return xstrdup(s);
37d84d6d 264#endif
8f1be588 265}
37d84d6d 266
4762ae9d
KZ
267static char *wcs_to_mbs(const wchar_t *s)
268{
269#ifdef HAVE_WIDECHAR
270 size_t n;
271 char *str;
272
273 n = wcstombs(NULL, s, 0);
274 if (n == (size_t) -1)
275 return NULL;
276
86399c33 277 str = xcalloc(n + 1, 1);
4762ae9d
KZ
278 if (wcstombs(str, s, n) == (size_t) -1) {
279 free(str);
280 return NULL;
281 }
282 return str;
283#else
8bc5195b 284 return xstrdup(s);
4762ae9d
KZ
285#endif
286}
287
073abd4c
SK
288static wchar_t *local_wcstok(struct column_control const *const ctl, wchar_t *p,
289 wchar_t **state)
4762ae9d
KZ
290{
291 wchar_t *result = NULL;
292
073abd4c 293 if (ctl->greedy)
ccedd0d7 294#ifdef HAVE_WIDECHAR
073abd4c 295 return wcstok(p, ctl->input_separator, state);
ccedd0d7 296#else
073abd4c 297 return strtok_r(p, ctl->input_separator, state);
ccedd0d7 298#endif
4762ae9d 299 if (!p) {
325bfd53 300 if (!*state)
4762ae9d
KZ
301 return NULL;
302 p = *state;
303 }
304 result = p;
ccedd0d7 305#ifdef HAVE_WIDECHAR
073abd4c 306 p = wcspbrk(result, ctl->input_separator);
ccedd0d7 307#else
073abd4c 308 p = strpbrk(result, ctl->input_separator);
ccedd0d7 309#endif
4762ae9d
KZ
310 if (!p)
311 *state = NULL;
312 else {
313 *p = '\0';
314 *state = p + 1;
315 }
316 return result;
317}
318
dbed2f8c 319static char **split_or_error(const char *str, const char *errmsg)
11a1092a 320{
f39ffccf 321 char **res = ul_strv_split(str, ",");
dbed2f8c 322 if (!res) {
11a1092a
KZ
323 if (errno == ENOMEM)
324 err_oom();
fd75b968
KZ
325 if (errmsg)
326 errx(EXIT_FAILURE, "%s: '%s'", errmsg, str);
327 else
328 return NULL;
11a1092a 329 }
dbed2f8c 330 return res;
11a1092a
KZ
331}
332
4762ae9d
KZ
333static void init_table(struct column_control *ctl)
334{
335 scols_init_debug(0);
336
337 ctl->tab = scols_new_table();
338 if (!ctl->tab)
339 err(EXIT_FAILURE, _("failed to allocate output table"));
340
341 scols_table_set_column_separator(ctl->tab, ctl->output_separator);
9dbe8e1c
KZ
342 if (ctl->json) {
343 scols_table_enable_json(ctl->tab, 1);
344 scols_table_set_name(ctl->tab, ctl->tab_name ? : "table");
00e8e677
KZ
345 } else
346 scols_table_enable_noencoding(ctl->tab, 1);
347
6be39224
KZ
348 scols_table_enable_maxout(ctl->tab, ctl->maxout ? 1 : 0);
349
b4526713
KZ
350 if (ctl->tab_columns) {
351 char **opts;
352
f39ffccf 353 UL_STRV_FOREACH(opts, ctl->tab_columns) {
b4526713
KZ
354 struct libscols_column *cl;
355
356 cl = scols_table_new_column(ctl->tab, NULL, 0, 0);
357 scols_column_set_properties(cl, *opts);
358 }
359
360 } else if (ctl->tab_colnames) {
11a1092a
KZ
361 char **name;
362
f39ffccf 363 UL_STRV_FOREACH(name, ctl->tab_colnames)
11a1092a 364 scols_table_new_column(ctl->tab, *name, 0, 0);
b4526713
KZ
365 } else
366 scols_table_enable_noheadings(ctl->tab, 1);
367
368 if (ctl->tab_colnames || ctl->tab_columns) {
d9eddf72
KZ
369 if (ctl->header_repeat)
370 scols_table_enable_header_repeat(ctl->tab, 1);
785e5436 371 scols_table_enable_noheadings(ctl->tab, !!ctl->tab_noheadings);
b4526713
KZ
372 }
373
4762ae9d
KZ
374}
375
09b9b466 376static struct libscols_column *get_last_visible_column(struct column_control *ctl, int n)
c728e000
KZ
377{
378 struct libscols_iter *itr;
09b9b466 379 struct libscols_column *cl, *res = NULL;
c728e000 380
09b9b466 381 itr = scols_new_iter(SCOLS_ITER_BACKWARD);
c728e000
KZ
382 if (!itr)
383 err_oom();
384
385 while (scols_table_next_column(ctl->tab, itr, &cl) == 0) {
386 if (scols_column_get_flags(cl) & SCOLS_FL_HIDDEN)
387 continue;
09b9b466
KZ
388 if (n == 0) {
389 res = cl;
390 break;
391 }
392 n--;
c728e000
KZ
393 }
394
395 scols_free_iter(itr);
09b9b466 396 return res;
c728e000 397}
3ba01db0 398
32e4c34f
KZ
399static struct libscols_column *string_to_column(struct column_control *ctl, const char *str)
400{
401 struct libscols_column *cl;
402
403 if (isdigit_string(str)) {
404 uint32_t n = strtou32_or_err(str, _("failed to parse column")) - 1;
405
406 cl = scols_table_get_column(ctl->tab, n);
407 } else if (strcmp(str, "-1") == 0)
09b9b466 408 cl = get_last_visible_column(ctl, 0);
32e4c34f
KZ
409 else
410 cl = scols_table_get_column_by_name(ctl->tab, str);
411
412 if (!cl)
413 errx(EXIT_FAILURE, _("undefined column name '%s'"), str);
414
415 return cl;
416}
417
dbed2f8c
KZ
418static int column_set_flag(struct libscols_column *cl, int fl)
419{
420 int cur = scols_column_get_flags(cl);
421
422 return scols_column_set_flags(cl, cur | fl);
423}
424
fd75b968
KZ
425static int has_unnamed(const char *list)
426{
427 char **all, **one;
e9fe4887 428 int rc = 0;
fd75b968
KZ
429
430 if (!list)
431 return 0;
432 if (strcmp(list, "-") == 0)
433 return 1;
434 if (!strchr(list, ','))
435 return 0;
436
437 all = split_or_error(list, NULL);
438 if (all) {
f39ffccf 439 UL_STRV_FOREACH(one, all) {
e9fe4887
KZ
440 if (strcmp(*one, "-") == 0) {
441 rc = 1;
442 break;
443 }
fd75b968 444 }
f39ffccf 445 ul_strv_free(all);
fd75b968
KZ
446 }
447
e9fe4887 448 return rc;
fd75b968
KZ
449}
450
3ba01db0
KZ
451static void apply_columnflag_from_list(struct column_control *ctl, const char *list,
452 int flag, const char *errmsg)
453{
21b16433 454 char **all;
3ba01db0 455 char **one;
b5de9e69 456 int unnamed = 0;
21b16433 457 struct libscols_column *cl;
3ba01db0 458
21b16433
KZ
459 /* apply to all */
460 if (list && strcmp(list, "0") == 0) {
461 struct libscols_iter *itr;
462
463 itr = scols_new_iter(SCOLS_ITER_FORWARD);
464 if (!itr)
465 err_oom();
b5de9e69 466
21b16433
KZ
467 while (scols_table_next_column(ctl->tab, itr, &cl) == 0)
468 column_set_flag(cl, flag);
469 scols_free_iter(itr);
1dbe96fb 470 return;
21b16433
KZ
471 }
472
473 all = split_or_error(list, errmsg);
474
475 /* apply to columns specified by name */
f39ffccf 476 UL_STRV_FOREACH(one, all) {
131a4434
KZ
477 int low = 0, up = 0;
478
fd75b968 479 if (strcmp(*one, "-") == 0) {
b5de9e69
KZ
480 unnamed = 1;
481 continue;
482 }
131a4434
KZ
483
484 /* parse range (N-M) */
dbbe9e7d 485 if (strchr(*one, '-') && ul_parse_range(*one, &low, &up, 0) == 0) {
131a4434 486 for (; low <= up; low++) {
09b9b466
KZ
487 if (low < 0)
488 cl = get_last_visible_column(ctl, (low * -1) -1);
489 else
490 cl = scols_table_get_column(ctl->tab, low-1);
131a4434
KZ
491 if (cl)
492 column_set_flag(cl, flag);
493 }
494 continue;
495 }
496
497 /* one item in the list */
b5de9e69 498 cl = string_to_column(ctl, *one);
3ba01db0
KZ
499 if (cl)
500 column_set_flag(cl, flag);
501 }
f39ffccf 502 ul_strv_free(all);
b5de9e69
KZ
503
504 /* apply flag to all columns without name */
505 if (unnamed) {
506 struct libscols_iter *itr;
b5de9e69
KZ
507
508 itr = scols_new_iter(SCOLS_ITER_FORWARD);
509 if (!itr)
510 err_oom();
511
512 while (scols_table_next_column(ctl->tab, itr, &cl) == 0) {
6fd0e359 513 if (!scols_column_get_name(cl))
b5de9e69
KZ
514 column_set_flag(cl, flag);
515 }
516 scols_free_iter(itr);
517 }
3ba01db0
KZ
518}
519
166271a9
KZ
520static void reorder_table(struct column_control *ctl)
521{
522 struct libscols_column **wanted, *last = NULL;
523 size_t i, count = 0;
524 size_t ncols = scols_table_get_ncols(ctl->tab);
525 char **order = split_or_error(ctl->tab_order, _("failed to parse --table-order list"));
526 char **one;
527
528 wanted = xcalloc(ncols, sizeof(struct libscols_column *));
529
f39ffccf 530 UL_STRV_FOREACH(one, order) {
166271a9
KZ
531 struct libscols_column *cl = string_to_column(ctl, *one);
532 if (cl)
533 wanted[count++] = cl;
534 }
535
536 for (i = 0; i < count; i++) {
537 scols_table_move_column(ctl->tab, last, wanted[i]);
538 last = wanted[i];
539 }
d6580917
KZ
540
541 free(wanted);
f39ffccf 542 ul_strv_free(order);
166271a9
KZ
543}
544
90852c3e
KZ
545static void create_tree(struct column_control *ctl)
546{
547 struct libscols_column *cl_tree = string_to_column(ctl, ctl->tree);
548 struct libscols_column *cl_p = string_to_column(ctl, ctl->tree_parent);
549 struct libscols_column *cl_i = string_to_column(ctl, ctl->tree_id);
550 struct libscols_iter *itr_p, *itr_i;
551 struct libscols_line *ln_i;
552
553 if (!cl_p || !cl_i || !cl_tree)
554 return; /* silently ignore the tree request */
555
556 column_set_flag(cl_tree, SCOLS_FL_TREE);
557
558 itr_p = scols_new_iter(SCOLS_ITER_FORWARD);
559 itr_i = scols_new_iter(SCOLS_ITER_FORWARD);
560 if (!itr_p || !itr_i)
561 err_oom();
562
563 /* scan all lines for ID */
564 while (scols_table_next_line(ctl->tab, itr_i, &ln_i) == 0) {
565 struct libscols_line *ln;
566 struct libscols_cell *ce = scols_line_get_column_cell(ln_i, cl_i);
567 const char *id = ce ? scols_cell_get_data(ce) : NULL;
568
569 if (!id)
570 continue;
571
572 /* see if the ID is somewhere used in parent column */
573 scols_reset_iter(itr_p, SCOLS_ITER_FORWARD);
574 while (scols_table_next_line(ctl->tab, itr_p, &ln) == 0) {
575 const char *parent;
576
577 ce = scols_line_get_column_cell(ln, cl_p);
578 parent = ce ? scols_cell_get_data(ce) : NULL;
579
580 if (!parent)
581 continue;
c467fdaf 582 if (strcmp(id, parent) != 0)
b0f00a94 583 continue;
c467fdaf 584 if (scols_line_is_ancestor(ln, ln_i))
b0f00a94 585 continue;
c467fdaf 586 scols_line_add_child(ln_i, ln);
90852c3e
KZ
587 }
588 }
589
590 scols_free_iter(itr_p);
591 scols_free_iter(itr_i);
592}
593
dbed2f8c
KZ
594static void modify_table(struct column_control *ctl)
595{
35d5d655
KZ
596 if (ctl->termwidth > 0) {
597 scols_table_set_termwidth(ctl->tab, ctl->termwidth);
598 scols_table_set_termforce(ctl->tab, SCOLS_TERMFORCE_ALWAYS);
599 }
3ba01db0 600
d83a5f8c
KZ
601 if (ctl->tab_colhide)
602 apply_columnflag_from_list(ctl, ctl->tab_colhide,
603 SCOLS_FL_HIDDEN , _("failed to parse --table-hide list"));
604
3ba01db0
KZ
605 if (ctl->tab_colright)
606 apply_columnflag_from_list(ctl, ctl->tab_colright,
607 SCOLS_FL_RIGHT, _("failed to parse --table-right list"));
608
3ba01db0
KZ
609 if (ctl->tab_coltrunc)
610 apply_columnflag_from_list(ctl, ctl->tab_coltrunc,
611 SCOLS_FL_TRUNC , _("failed to parse --table-trunc list"));
1ae24ec2
KZ
612
613 if (ctl->tab_colnoextrem)
614 apply_columnflag_from_list(ctl, ctl->tab_colnoextrem,
615 SCOLS_FL_NOEXTREMES , _("failed to parse --table-noextreme list"));
68916af3
KZ
616
617 if (ctl->tab_colwrap)
618 apply_columnflag_from_list(ctl, ctl->tab_colwrap,
619 SCOLS_FL_WRAP , _("failed to parse --table-wrap list"));
620
c728e000 621 if (!ctl->tab_colnoextrem) {
09b9b466 622 struct libscols_column *cl = get_last_visible_column(ctl, 0);
c728e000
KZ
623 if (cl)
624 column_set_flag(cl, SCOLS_FL_NOEXTREMES);
625 }
626
90852c3e
KZ
627 if (ctl->tree)
628 create_tree(ctl);
166271a9
KZ
629
630 /* This must be the last step! */
631 if (ctl->tab_order)
632 reorder_table(ctl);
dbed2f8c
KZ
633}
634
166271a9 635
ea160a2b 636static int add_line_to_table(struct column_control *ctl, wchar_t *wcs0)
4762ae9d 637{
a6dd313b
KZ
638 wchar_t *sv = NULL, *wcs = wcs0, *all = NULL;
639 size_t n = 0;
4762ae9d
KZ
640 struct libscols_line *ln = NULL;
641
a6dd313b 642
4762ae9d
KZ
643 if (!ctl->tab)
644 init_table(ctl);
4aacf57d 645
a6dd313b
KZ
646 if (ctl->maxncols) {
647 all = wcsdup(wcs0);
648 if (!all)
649 err(EXIT_FAILURE, _("failed to allocate input line"));
650 }
4aacf57d 651
ea160a2b 652 do {
4762ae9d 653 char *data;
a6dd313b
KZ
654 wchar_t *wcdata = local_wcstok(ctl, wcs, &sv);
655
656 if (!wcdata)
657 break;
4762ae9d 658
4aacf57d 659 if (ctl->maxncols && n + 1 == ctl->maxncols) {
a6dd313b
KZ
660 /* Use rest of the string as column data */
661 size_t skip = wcdata - wcs0;
662 wcdata = all + skip;
8ac75e31
KZ
663 }
664
cb3fdf2a 665 if (scols_table_get_ncols(ctl->tab) < n + 1) {
fd75b968 666 if (scols_table_is_json(ctl->tab) && !ctl->hide_unnamed)
cb3fdf2a
KZ
667 errx(EXIT_FAILURE, _("line %zu: for JSON the name of the "
668 "column %zu is required"),
669 scols_table_get_nlines(ctl->tab) + 1,
670 n + 1);
fd75b968
KZ
671 scols_table_new_column(ctl->tab, NULL, 0,
672 ctl->hide_unnamed ? SCOLS_FL_HIDDEN : 0);
cb3fdf2a 673 }
4762ae9d
KZ
674 if (!ln) {
675 ln = scols_table_new_line(ctl->tab, NULL);
676 if (!ln)
677 err(EXIT_FAILURE, _("failed to allocate output line"));
678 }
5c7b67fb 679
4762ae9d
KZ
680 data = wcs_to_mbs(wcdata);
681 if (!data)
682 err(EXIT_FAILURE, _("failed to allocate output data"));
683 if (scols_line_refer_data(ln, n, data))
684 err(EXIT_FAILURE, _("failed to add output data"));
685 n++;
686 wcs = NULL;
ea160a2b
KZ
687 if (ctl->maxncols && n == ctl->maxncols)
688 break;
689 } while (1);
4762ae9d 690
a6dd313b 691 free(all);
4762ae9d
KZ
692 return 0;
693}
694
2698f9ba
KZ
695static int add_emptyline_to_table(struct column_control *ctl)
696{
697 if (!ctl->tab)
698 init_table(ctl);
699
700 if (!scols_table_new_line(ctl->tab, NULL))
701 err(EXIT_FAILURE, _("failed to allocate output line"));
702
703 return 0;
704}
705
aae0bf77
LH
706static void add_entry(struct column_control *ctl, size_t *maxents, wchar_t *wcs)
707{
708 if (ctl->nents <= *maxents) {
709 *maxents += 1000;
64d6d400 710 ctl->ents = xreallocarray(ctl->ents, *maxents, sizeof(wchar_t *));
aae0bf77
LH
711 }
712 ctl->ents[ctl->nents] = wcs;
713 ctl->nents++;
714}
715
37d84d6d
KZ
716static int read_input(struct column_control *ctl, FILE *fp)
717{
aae0bf77 718 wchar_t *empty = NULL;
37d84d6d
KZ
719 char *buf = NULL;
720 size_t bufsz = 0;
721 size_t maxents = 0;
651c5d42 722 int rc = 0;
37d84d6d 723
5c7b67fb 724 /* Read input */
37d84d6d
KZ
725 do {
726 char *str, *p;
4762ae9d 727 wchar_t *wcs = NULL;
37d84d6d
KZ
728 size_t len;
729
730 if (getline(&buf, &bufsz, fp) < 0) {
731 if (feof(fp))
732 break;
4762ae9d 733 err(EXIT_FAILURE, _("read failed"));
37d84d6d
KZ
734 }
735 str = (char *) skip_space(buf);
736 if (str) {
737 p = strchr(str, '\n');
738 if (p)
739 *p = '\0';
740 }
2698f9ba 741 if (!str || !*str) {
f01e54d7 742 if (ctl->keep_empty_lines) {
aae0bf77
LH
743 if (ctl->mode == COLUMN_MODE_TABLE) {
744 add_emptyline_to_table(ctl);
745 } else {
746 if (!empty)
747 empty = mbs_to_wcs("");
748 add_entry(ctl, &maxents, empty);
749 }
750 }
37d84d6d 751 continue;
2698f9ba 752 }
37d84d6d 753
651c5d42 754 wcs = mbs_to_wcs(buf);
dda229c7
KZ
755 if (!wcs) {
756 /*
757 * Convert broken sequences to \x<hex> and continue.
758 */
759 size_t tmpsz = 0;
651c5d42 760 char *tmp = mbs_invalid_encode(buf, &tmpsz);
dda229c7
KZ
761
762 if (!tmp)
763 err(EXIT_FAILURE, _("read failed"));
764 wcs = mbs_to_wcs(tmp);
765 free(tmp);
766 }
4762ae9d 767
37d84d6d
KZ
768 switch (ctl->mode) {
769 case COLUMN_MODE_TABLE:
4762ae9d
KZ
770 rc = add_line_to_table(ctl, wcs);
771 free(wcs);
772 break;
37d84d6d
KZ
773
774 case COLUMN_MODE_FILLCOLS:
775 case COLUMN_MODE_FILLROWS:
aae0bf77
LH
776 add_entry(ctl, &maxents, wcs);
777 len = width(wcs);
37d84d6d
KZ
778 if (ctl->maxlength < len)
779 ctl->maxlength = len;
37d84d6d 780 break;
dd45b90e
KZ
781 default:
782 free(wcs);
783 break;
37d84d6d 784 }
4762ae9d 785 } while (rc == 0);
37d84d6d 786
aad8a607
TW
787 free(buf);
788
4762ae9d 789 return rc;
37d84d6d
KZ
790}
791
792
793static void columnate_fillrows(struct column_control *ctl)
794{
aec8bf94 795 size_t chcnt, col, cnt, endcol, numcols, remains;
37d84d6d
KZ
796 wchar_t **lp;
797
aec8bf94 798 if (ctl->use_spaces)
799 ctl->maxlength += ctl->mincolsep;
800 else
801 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
37d84d6d 802 numcols = ctl->termwidth / ctl->maxlength;
aec8bf94 803 remains = ctl->termwidth % ctl->maxlength;
804 if (ctl->use_spaces && remains + ctl->mincolsep >= ctl->maxlength)
805 numcols++;
37d84d6d 806 endcol = ctl->maxlength;
f4d37838 807 for (chcnt = col = 0, lp = ctl->ents; /* nothing */; ++lp) {
37d84d6d
KZ
808 fputws(*lp, stdout);
809 chcnt += width(*lp);
810 if (!--ctl->nents)
811 break;
812 if (++col == numcols) {
813 chcnt = col = 0;
814 endcol = ctl->maxlength;
815 putwchar('\n');
816 } else {
aec8bf94 817 if (ctl->use_spaces) {
818 while (chcnt < endcol) {
819 putwchar(' ');
820 chcnt++;
821 }
822 } else {
823 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
824 putwchar('\t');
825 chcnt = cnt;
826 }
37d84d6d
KZ
827 }
828 endcol += ctl->maxlength;
829 }
830 }
831 if (chcnt)
832 putwchar('\n');
833}
834
835static void columnate_fillcols(struct column_control *ctl)
836{
aec8bf94 837 size_t base, chcnt, cnt, col, endcol, numcols, numrows, row, remains;
37d84d6d 838
aec8bf94 839 if (ctl->use_spaces)
840 ctl->maxlength += ctl->mincolsep;
841 else
842 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
37d84d6d 843 numcols = ctl->termwidth / ctl->maxlength;
aec8bf94 844 remains = ctl->termwidth % ctl->maxlength;
37d84d6d
KZ
845 if (!numcols)
846 numcols = 1;
aec8bf94 847 if (ctl->use_spaces && remains + ctl->mincolsep >= ctl->maxlength)
848 numcols++;
37d84d6d
KZ
849 numrows = ctl->nents / numcols;
850 if (ctl->nents % numcols)
851 ++numrows;
852
853 for (row = 0; row < numrows; ++row) {
854 endcol = ctl->maxlength;
855 for (base = row, chcnt = col = 0; col < numcols; ++col) {
856 fputws(ctl->ents[base], stdout);
857 chcnt += width(ctl->ents[base]);
858 if ((base += numrows) >= ctl->nents)
859 break;
aec8bf94 860 if (ctl->use_spaces) {
861 while (chcnt < endcol) {
862 putwchar(' ');
863 chcnt++;
864 }
865 } else {
866 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
867 putwchar('\t');
868 chcnt = cnt;
869 }
37d84d6d
KZ
870 }
871 endcol += ctl->maxlength;
872 }
873 putwchar('\n');
874 }
875}
876
877static void simple_print(struct column_control *ctl)
878{
879 int cnt;
880 wchar_t **lp;
881
882 for (cnt = ctl->nents, lp = ctl->ents; cnt--; ++lp) {
883 fputws(*lp, stdout);
884 putwchar('\n');
885 }
886}
887
fa2cd89a 888static void __attribute__((__noreturn__)) usage(void)
a4cc8dfe 889{
fa2cd89a 890 FILE *out = stdout;
a4cc8dfe 891
3eed42f3 892 fputs(USAGE_HEADER, out);
4ce393f4 893 fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
451dbcfa
BS
894
895 fputs(USAGE_SEPARATOR, out);
896 fputs(_("Columnate lists.\n"), out);
897
3eed42f3 898 fputs(USAGE_OPTIONS, out);
d7a3bf94 899 fputs(_(" -t, --table create a table\n"), out);
7e947f48
KZ
900 fputs(_(" -n, --table-name <name> table name for JSON output\n"), out);
901 fputs(_(" -O, --table-order <columns> specify order of output columns\n"), out);
8a59ff19 902 fputs(_(" -C, --table-column <properties> define column\n"), out);
01e335c9 903 fputs(_(" -N, --table-columns <names> comma separated columns names\n"), out);
ea160a2b 904 fputs(_(" -l, --table-columns-limit <num> maximal number of input columns\n"), out);
7e947f48 905 fputs(_(" -E, --table-noextreme <columns> don't count long text from the columns to column width\n"), out);
785e5436 906 fputs(_(" -d, --table-noheadings don't print header\n"), out);
6be39224 907 fputs(_(" -m, --table-maxout fill all available space\n"), out);
d9eddf72 908 fputs(_(" -e, --table-header-repeat repeat header for each page\n"), out);
7e947f48 909 fputs(_(" -H, --table-hide <columns> don't print the columns\n"), out);
01e335c9 910 fputs(_(" -R, --table-right <columns> right align text in these columns\n"), out);
1ae24ec2 911 fputs(_(" -T, --table-truncate <columns> truncate text in the columns when necessary\n"), out);
68916af3 912 fputs(_(" -W, --table-wrap <columns> wrap text in the columns when necessary\n"), out);
f01e54d7 913 fputs(_(" -L, --keep-empty-lines don't ignore empty lines\n"), out);
7e947f48
KZ
914 fputs(_(" -J, --json use JSON output format for table\n"), out);
915
74cc7c25 916 fputs(USAGE_SEPARATOR, out);
90852c3e
KZ
917 fputs(_(" -r, --tree <column> column to use tree-like output for the table\n"), out);
918 fputs(_(" -i, --tree-id <column> line ID to specify child-parent relation\n"), out);
919 fputs(_(" -p, --tree-parent <column> parent to specify child-parent relation\n"), out);
920
7e947f48 921 fputs(USAGE_SEPARATOR, out);
d7a3bf94 922 fputs(_(" -c, --output-width <width> width of output in number of characters\n"), out);
7e947f48
KZ
923 fputs(_(" -o, --output-separator <string> columns separator for table output (default is two spaces)\n"), out);
924 fputs(_(" -s, --separator <string> possible table delimiters\n"), out);
d7a3bf94 925 fputs(_(" -x, --fillrows fill rows before columns\n"), out);
aec8bf94 926 fputs(_(" -S, --use-spaces <number> minimal whitespaces between columns (no tabs)\n"), out);
7e947f48 927
dda229c7 928
3eed42f3 929 fputs(USAGE_SEPARATOR, out);
bad4c729
MY
930 fprintf(out, USAGE_HELP_OPTIONS(34));
931 fprintf(out, USAGE_MAN_TAIL("column(1)"));
a4cc8dfe 932
fa2cd89a 933 exit(EXIT_SUCCESS);
a4cc8dfe 934}
1df29104
SK
935
936int main(int argc, char **argv)
6dbe3af9 937{
2593c139
KZ
938 struct column_control ctl = {
939 .mode = COLUMN_MODE_FILLCOLS,
bd6b5a64
KZ
940 .greedy = 1,
941 .termwidth = (size_t) -1
2593c139 942 };
7d07df62 943
be03f652 944 int c;
daf093d2 945 unsigned int eval = 0; /* exit value */
daf093d2
SK
946
947 static const struct option longopts[] =
948 {
11a1092a
KZ
949 { "columns", required_argument, NULL, 'c' }, /* deprecated */
950 { "fillrows", no_argument, NULL, 'x' },
951 { "help", no_argument, NULL, 'h' },
7e947f48 952 { "json", no_argument, NULL, 'J' },
f01e54d7 953 { "keep-empty-lines", no_argument, NULL, 'L' },
11a1092a
KZ
954 { "output-separator", required_argument, NULL, 'o' },
955 { "output-width", required_argument, NULL, 'c' },
956 { "separator", required_argument, NULL, 's' },
957 { "table", no_argument, NULL, 't' },
01e335c9 958 { "table-columns", required_argument, NULL, 'N' },
b4526713 959 { "table-column", required_argument, NULL, 'C' },
ea160a2b 960 { "table-columns-limit", required_argument, NULL, 'l' },
7e947f48
KZ
961 { "table-hide", required_argument, NULL, 'H' },
962 { "table-name", required_argument, NULL, 'n' },
6be39224 963 { "table-maxout", no_argument, NULL, 'm' },
7e947f48 964 { "table-noextreme", required_argument, NULL, 'E' },
785e5436 965 { "table-noheadings", no_argument, NULL, 'd' },
7e947f48 966 { "table-order", required_argument, NULL, 'O' },
01e335c9 967 { "table-right", required_argument, NULL, 'R' },
3ba01db0 968 { "table-truncate", required_argument, NULL, 'T' },
68916af3 969 { "table-wrap", required_argument, NULL, 'W' },
f01e54d7 970 { "table-empty-lines", no_argument, NULL, 'L' }, /* deprecated */
d9eddf72 971 { "table-header-repeat", no_argument, NULL, 'e' },
90852c3e
KZ
972 { "tree", required_argument, NULL, 'r' },
973 { "tree-id", required_argument, NULL, 'i' },
974 { "tree-parent", required_argument, NULL, 'p' },
aec8bf94 975 { "use-spaces", required_argument, NULL, 'S' },
11a1092a 976 { "version", no_argument, NULL, 'V' },
87918040 977 { NULL, 0, NULL, 0 },
daf093d2 978 };
be03f652 979 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
b4526713 980 { 'C','N' },
be03f652
KZ
981 { 'J','x' },
982 { 't','x' },
983 { 0 }
984 };
985 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
daf093d2 986
7eda085c
KZ
987 setlocale(LC_ALL, "");
988 bindtextdomain(PACKAGE, LOCALEDIR);
989 textdomain(PACKAGE);
2c308875 990 close_stdout_atexit();
6dbe3af9 991
4762ae9d
KZ
992 ctl.output_separator = " ";
993 ctl.input_separator = mbs_to_wcs("\t ");
6dbe3af9 994
aec8bf94 995 while ((c = getopt_long(argc, argv, "C:c:dE:eH:hi:Jl:LN:n:mO:o:p:R:r:S:s:T:tVW:x", longopts, NULL)) != -1) {
be03f652
KZ
996
997 err_exclusive_options(c, longopts, excl, excl_st);
998
999 switch(c) {
b4526713 1000 case 'C':
f39ffccf 1001 if (ul_strv_extend(&ctl.tab_columns, optarg))
e9fe4887 1002 err_oom();
b4526713 1003 break;
7e947f48 1004 case 'c':
35d5d655
KZ
1005 if (strcmp(optarg, "unlimited") == 0)
1006 ctl.termwidth = 0;
1007 else
1008 ctl.termwidth = strtou32_or_err(optarg, _("invalid columns argument"));
7e947f48 1009 break;
785e5436
KZ
1010 case 'd':
1011 ctl.tab_noheadings = 1;
1012 break;
1ae24ec2
KZ
1013 case 'E':
1014 ctl.tab_colnoextrem = optarg;
1015 break;
d9eddf72
KZ
1016 case 'e':
1017 ctl.header_repeat = 1;
1018 break;
7e947f48
KZ
1019 case 'H':
1020 ctl.tab_colhide = optarg;
fd75b968 1021 ctl.hide_unnamed = has_unnamed(ctl.tab_colhide);
7e947f48 1022 break;
90852c3e
KZ
1023 case 'i':
1024 ctl.tree_id = optarg;
1025 break;
9dbe8e1c
KZ
1026 case 'J':
1027 ctl.json = 1;
1028 ctl.mode = COLUMN_MODE_TABLE;
1029 break;
2698f9ba 1030 case 'L':
f01e54d7 1031 ctl.keep_empty_lines = 1;
2698f9ba 1032 break;
ea160a2b
KZ
1033 case 'l':
1034 ctl.maxncols = strtou32_or_err(optarg, _("invalid columns limit argument"));
1035 if (ctl.maxncols == 0)
1036 errx(EXIT_FAILURE, _("columns limit must be greater than zero"));
1037 break;
11a1092a 1038 case 'N':
dbed2f8c 1039 ctl.tab_colnames = split_or_error(optarg, _("failed to parse column names"));
11a1092a 1040 break;
7e947f48
KZ
1041 case 'n':
1042 ctl.tab_name = optarg;
9624f615 1043 break;
6be39224
KZ
1044 case 'm':
1045 ctl.maxout = 1;
1046 break;
7e947f48
KZ
1047 case 'O':
1048 ctl.tab_order = optarg;
1ae90932 1049 break;
7e947f48
KZ
1050 case 'o':
1051 ctl.output_separator = optarg;
6dbe3af9 1052 break;
90852c3e
KZ
1053 case 'p':
1054 ctl.tree_parent = optarg;
1055 break;
dbed2f8c
KZ
1056 case 'R':
1057 ctl.tab_colright = optarg;
1058 break;
90852c3e
KZ
1059 case 'r':
1060 ctl.tree = optarg;
1061 break;
aec8bf94 1062 case 'S':
1063 ctl.use_spaces = 1;
1064 ctl.mincolsep = strtou32_or_err(optarg, _("invalid spaces argument"));
1065 break;
6dbe3af9 1066 case 's':
4762ae9d
KZ
1067 free(ctl.input_separator);
1068 ctl.input_separator = mbs_to_wcs(optarg);
97143318 1069 if (!ctl.input_separator)
455e609f 1070 err(EXIT_FAILURE, _("failed to parse input separator"));
4762ae9d 1071 ctl.greedy = 0;
6dbe3af9 1072 break;
7e947f48
KZ
1073 case 'T':
1074 ctl.tab_coltrunc = optarg;
47bd8ddc 1075 break;
6dbe3af9 1076 case 't':
7d07df62 1077 ctl.mode = COLUMN_MODE_TABLE;
6dbe3af9 1078 break;
68916af3
KZ
1079 case 'W':
1080 ctl.tab_colwrap = optarg;
1081 break;
6dbe3af9 1082 case 'x':
7d07df62 1083 ctl.mode = COLUMN_MODE_FILLROWS;
6dbe3af9 1084 break;
2c308875
KZ
1085
1086 case 'h':
1087 usage();
1088 case 'V':
1089 print_version(EXIT_SUCCESS);
6dbe3af9 1090 default:
677ec86c 1091 errtryhelp(EXIT_FAILURE);
be03f652 1092 }
1df29104 1093 }
6dbe3af9
KZ
1094 argc -= optind;
1095 argv += optind;
1096
bd6b5a64
KZ
1097 if (ctl.termwidth == (size_t) -1)
1098 ctl.termwidth = get_terminal_width(80);
1099
90852c3e
KZ
1100 if (ctl.tree) {
1101 ctl.mode = COLUMN_MODE_TABLE;
1102 if (!ctl.tree_parent || !ctl.tree_id)
1103 errx(EXIT_FAILURE, _("options --tree-id and --tree-parent are "
1104 "required for tree formatting"));
1105 }
1106
7e947f48
KZ
1107 if (ctl.mode != COLUMN_MODE_TABLE
1108 && (ctl.tab_order || ctl.tab_name || ctl.tab_colwrap ||
1109 ctl.tab_colhide || ctl.tab_coltrunc || ctl.tab_colnoextrem ||
b4526713 1110 ctl.tab_colright || ctl.tab_colnames || ctl.tab_columns))
7e947f48
KZ
1111 errx(EXIT_FAILURE, _("option --table required for all --table-*"));
1112
b4526713
KZ
1113 if (!ctl.tab_colnames && !ctl.tab_columns && ctl.json)
1114 errx(EXIT_FAILURE, _("option --table-columns or --table-column required for --json"));
9dbe8e1c 1115
6dbe3af9 1116 if (!*argv)
d6b63b9f 1117 eval += read_input(&ctl, stdin);
1df29104 1118 else
1ae90932 1119 for (; *argv; ++argv) {
a46e644e
KZ
1120 FILE *fp;
1121
1ae90932 1122 if ((fp = fopen(*argv, "r")) != NULL) {
d6b63b9f 1123 eval += read_input(&ctl, fp);
daf093d2 1124 fclose(fp);
1ae90932
SK
1125 } else {
1126 warn("%s", *argv);
daf093d2 1127 eval += EXIT_FAILURE;
1ae90932 1128 }
6dbe3af9
KZ
1129 }
1130
4762ae9d
KZ
1131 if (ctl.mode != COLUMN_MODE_TABLE) {
1132 if (!ctl.nents)
1133 exit(eval);
1134 if (ctl.maxlength >= ctl.termwidth)
1135 ctl.mode = COLUMN_MODE_SIMPLE;
1136 }
7d07df62
KZ
1137
1138 switch (ctl.mode) {
1139 case COLUMN_MODE_TABLE:
da06d421
KZ
1140 if (ctl.tab && scols_table_get_nlines(ctl.tab)) {
1141 modify_table(&ctl);
1142 eval = scols_print_table(ctl.tab);
b4526713
KZ
1143
1144 scols_unref_table(ctl.tab);
1145 if (ctl.tab_colnames)
f39ffccf 1146 ul_strv_free(ctl.tab_colnames);
b4526713 1147 if (ctl.tab_columns)
f39ffccf 1148 ul_strv_free(ctl.tab_columns);
da06d421 1149 }
7d07df62
KZ
1150 break;
1151 case COLUMN_MODE_FILLCOLS:
2593c139 1152 columnate_fillcols(&ctl);
7d07df62
KZ
1153 break;
1154 case COLUMN_MODE_FILLROWS:
2593c139 1155 columnate_fillrows(&ctl);
7d07df62
KZ
1156 break;
1157 case COLUMN_MODE_SIMPLE:
d6b63b9f 1158 simple_print(&ctl);
7d07df62
KZ
1159 break;
1160 }
dcbca568 1161
b4526713
KZ
1162 free(ctl.input_separator);
1163
7d07df62 1164 return eval == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
6dbe3af9 1165}