2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
51 #include "closestream.h"
57 #include "libsmartcols.h"
59 #define TABCHAR_CELLS 8
62 COLUMN_MODE_FILLCOLS
= 0,
68 struct column_control
{
69 int mode
; /* COLUMN_MODE_* */
70 size_t termwidth
; /* -1 uninilialized, 0 unlimited, >0 width (default is 80) */
72 struct libscols_table
*tab
;
74 char **tab_colnames
; /* array with column names */
75 const char *tab_name
; /* table name */
76 const char *tab_order
; /* --table-order */
78 char **tab_columns
; /* array from --table-column */
80 const char *tab_colright
; /* --table-right */
81 const char *tab_coltrunc
; /* --table-trunc */
82 const char *tab_colnoextrem
; /* --table-noextreme */
83 const char *tab_colwrap
; /* --table-wrap */
84 const char *tab_colhide
; /* --table-hide */
88 const char *tree_parent
;
90 wchar_t *input_separator
;
91 const char *output_separator
;
93 wchar_t **ents
; /* input entries */
94 size_t nents
; /* number of entries */
95 size_t maxlength
; /* longest input record (line) */
96 size_t maxncols
; /* maximal number of input columns */
98 unsigned int greedy
:1,
103 keep_empty_lines
:1, /* --keep-empty-lines */
107 static size_t width(const wchar_t *str
)
111 for (; *str
!= '\0'; str
++) {
113 int x
= wcwidth(*str
); /* don't use wcswidth(), need to ignore non-printable */
124 static wchar_t *mbs_to_wcs(const char *s
)
130 n
= mbstowcs((wchar_t *)0, s
, 0);
133 wcs
= xcalloc((n
+ 1) * sizeof(wchar_t), 1);
134 n
= mbstowcs(wcs
, s
, n
+ 1);
145 static char *wcs_to_mbs(const wchar_t *s
)
151 n
= wcstombs(NULL
, s
, 0);
152 if (n
== (size_t) -1)
155 str
= xcalloc(n
+ 1, 1);
156 if (wcstombs(str
, s
, n
) == (size_t) -1) {
166 static wchar_t *local_wcstok(struct column_control
const *const ctl
, wchar_t *p
,
169 wchar_t *result
= NULL
;
173 return wcstok(p
, ctl
->input_separator
, state
);
175 return strtok_r(p
, ctl
->input_separator
, state
);
184 p
= wcspbrk(result
, ctl
->input_separator
);
186 p
= strpbrk(result
, ctl
->input_separator
);
197 static char **split_or_error(const char *str
, const char *errmsg
)
199 char **res
= strv_split(str
, ",");
204 errx(EXIT_FAILURE
, "%s: '%s'", errmsg
, str
);
211 static void init_table(struct column_control
*ctl
)
215 ctl
->tab
= scols_new_table();
217 err(EXIT_FAILURE
, _("failed to allocate output table"));
219 scols_table_set_column_separator(ctl
->tab
, ctl
->output_separator
);
221 scols_table_enable_json(ctl
->tab
, 1);
222 scols_table_set_name(ctl
->tab
, ctl
->tab_name
? : "table");
224 scols_table_enable_noencoding(ctl
->tab
, 1);
226 scols_table_enable_maxout(ctl
->tab
, ctl
->maxout
? 1 : 0);
228 if (ctl
->tab_columns
) {
231 STRV_FOREACH(opts
, ctl
->tab_columns
) {
232 struct libscols_column
*cl
;
234 cl
= scols_table_new_column(ctl
->tab
, NULL
, 0, 0);
235 scols_column_set_properties(cl
, *opts
);
238 } else if (ctl
->tab_colnames
) {
241 STRV_FOREACH(name
, ctl
->tab_colnames
)
242 scols_table_new_column(ctl
->tab
, *name
, 0, 0);
244 scols_table_enable_noheadings(ctl
->tab
, 1);
246 if (ctl
->tab_colnames
|| ctl
->tab_columns
) {
247 if (ctl
->header_repeat
)
248 scols_table_enable_header_repeat(ctl
->tab
, 1);
249 scols_table_enable_noheadings(ctl
->tab
, !!ctl
->tab_noheadings
);
254 static struct libscols_column
*get_last_visible_column(struct column_control
*ctl
, int n
)
256 struct libscols_iter
*itr
;
257 struct libscols_column
*cl
, *res
= NULL
;
259 itr
= scols_new_iter(SCOLS_ITER_BACKWARD
);
263 while (scols_table_next_column(ctl
->tab
, itr
, &cl
) == 0) {
264 if (scols_column_get_flags(cl
) & SCOLS_FL_HIDDEN
)
273 scols_free_iter(itr
);
277 static struct libscols_column
*string_to_column(struct column_control
*ctl
, const char *str
)
279 struct libscols_column
*cl
;
281 if (isdigit_string(str
)) {
282 uint32_t n
= strtou32_or_err(str
, _("failed to parse column")) - 1;
284 cl
= scols_table_get_column(ctl
->tab
, n
);
285 } else if (strcmp(str
, "-1") == 0)
286 cl
= get_last_visible_column(ctl
, 0);
288 cl
= scols_table_get_column_by_name(ctl
->tab
, str
);
291 errx(EXIT_FAILURE
, _("undefined column name '%s'"), str
);
296 static int column_set_flag(struct libscols_column
*cl
, int fl
)
298 int cur
= scols_column_get_flags(cl
);
300 return scols_column_set_flags(cl
, cur
| fl
);
303 static int has_unnamed(const char *list
)
310 if (strcmp(list
, "-") == 0)
312 if (!strchr(list
, ','))
315 all
= split_or_error(list
, NULL
);
317 STRV_FOREACH(one
, all
) {
318 if (strcmp(*one
, "-") == 0) {
329 static void apply_columnflag_from_list(struct column_control
*ctl
, const char *list
,
330 int flag
, const char *errmsg
)
335 struct libscols_column
*cl
;
338 if (list
&& strcmp(list
, "0") == 0) {
339 struct libscols_iter
*itr
;
341 itr
= scols_new_iter(SCOLS_ITER_FORWARD
);
345 while (scols_table_next_column(ctl
->tab
, itr
, &cl
) == 0)
346 column_set_flag(cl
, flag
);
347 scols_free_iter(itr
);
351 all
= split_or_error(list
, errmsg
);
353 /* apply to columns specified by name */
354 STRV_FOREACH(one
, all
) {
357 if (strcmp(*one
, "-") == 0) {
362 /* parse range (N-M) */
363 if (strchr(*one
, '-') && parse_range(*one
, &low
, &up
, 0) == 0) {
364 for (; low
<= up
; low
++) {
366 cl
= get_last_visible_column(ctl
, (low
* -1) -1);
368 cl
= scols_table_get_column(ctl
->tab
, low
-1);
370 column_set_flag(cl
, flag
);
375 /* one item in the list */
376 cl
= string_to_column(ctl
, *one
);
378 column_set_flag(cl
, flag
);
382 /* apply flag to all columns without name */
384 struct libscols_iter
*itr
;
386 itr
= scols_new_iter(SCOLS_ITER_FORWARD
);
390 while (scols_table_next_column(ctl
->tab
, itr
, &cl
) == 0) {
391 if (!scols_column_get_name(cl
))
392 column_set_flag(cl
, flag
);
394 scols_free_iter(itr
);
398 static void reorder_table(struct column_control
*ctl
)
400 struct libscols_column
**wanted
, *last
= NULL
;
402 size_t ncols
= scols_table_get_ncols(ctl
->tab
);
403 char **order
= split_or_error(ctl
->tab_order
, _("failed to parse --table-order list"));
406 wanted
= xcalloc(ncols
, sizeof(struct libscols_column
*));
408 STRV_FOREACH(one
, order
) {
409 struct libscols_column
*cl
= string_to_column(ctl
, *one
);
411 wanted
[count
++] = cl
;
414 for (i
= 0; i
< count
; i
++) {
415 scols_table_move_column(ctl
->tab
, last
, wanted
[i
]);
423 static void create_tree(struct column_control
*ctl
)
425 struct libscols_column
*cl_tree
= string_to_column(ctl
, ctl
->tree
);
426 struct libscols_column
*cl_p
= string_to_column(ctl
, ctl
->tree_parent
);
427 struct libscols_column
*cl_i
= string_to_column(ctl
, ctl
->tree_id
);
428 struct libscols_iter
*itr_p
, *itr_i
;
429 struct libscols_line
*ln_i
;
431 if (!cl_p
|| !cl_i
|| !cl_tree
)
432 return; /* silently ignore the tree request */
434 column_set_flag(cl_tree
, SCOLS_FL_TREE
);
436 itr_p
= scols_new_iter(SCOLS_ITER_FORWARD
);
437 itr_i
= scols_new_iter(SCOLS_ITER_FORWARD
);
438 if (!itr_p
|| !itr_i
)
441 /* scan all lines for ID */
442 while (scols_table_next_line(ctl
->tab
, itr_i
, &ln_i
) == 0) {
443 struct libscols_line
*ln
;
444 struct libscols_cell
*ce
= scols_line_get_column_cell(ln_i
, cl_i
);
445 const char *id
= ce
? scols_cell_get_data(ce
) : NULL
;
450 /* see if the ID is somewhere used in parent column */
451 scols_reset_iter(itr_p
, SCOLS_ITER_FORWARD
);
452 while (scols_table_next_line(ctl
->tab
, itr_p
, &ln
) == 0) {
455 ce
= scols_line_get_column_cell(ln
, cl_p
);
456 parent
= ce
? scols_cell_get_data(ce
) : NULL
;
460 if (strcmp(id
, parent
) != 0)
462 if (scols_line_is_ancestor(ln
, ln_i
))
464 scols_line_add_child(ln_i
, ln
);
468 scols_free_iter(itr_p
);
469 scols_free_iter(itr_i
);
472 static void modify_table(struct column_control
*ctl
)
474 if (ctl
->termwidth
> 0) {
475 scols_table_set_termwidth(ctl
->tab
, ctl
->termwidth
);
476 scols_table_set_termforce(ctl
->tab
, SCOLS_TERMFORCE_ALWAYS
);
479 if (ctl
->tab_colhide
)
480 apply_columnflag_from_list(ctl
, ctl
->tab_colhide
,
481 SCOLS_FL_HIDDEN
, _("failed to parse --table-hide list"));
483 if (ctl
->tab_colright
)
484 apply_columnflag_from_list(ctl
, ctl
->tab_colright
,
485 SCOLS_FL_RIGHT
, _("failed to parse --table-right list"));
487 if (ctl
->tab_coltrunc
)
488 apply_columnflag_from_list(ctl
, ctl
->tab_coltrunc
,
489 SCOLS_FL_TRUNC
, _("failed to parse --table-trunc list"));
491 if (ctl
->tab_colnoextrem
)
492 apply_columnflag_from_list(ctl
, ctl
->tab_colnoextrem
,
493 SCOLS_FL_NOEXTREMES
, _("failed to parse --table-noextreme list"));
495 if (ctl
->tab_colwrap
)
496 apply_columnflag_from_list(ctl
, ctl
->tab_colwrap
,
497 SCOLS_FL_WRAP
, _("failed to parse --table-wrap list"));
499 if (!ctl
->tab_colnoextrem
) {
500 struct libscols_column
*cl
= get_last_visible_column(ctl
, 0);
502 column_set_flag(cl
, SCOLS_FL_NOEXTREMES
);
508 /* This must be the last step! */
514 static int add_line_to_table(struct column_control
*ctl
, wchar_t *wcs0
)
516 wchar_t *sv
= NULL
, *wcs
= wcs0
, *all
= NULL
;
518 struct libscols_line
*ln
= NULL
;
527 err(EXIT_FAILURE
, _("failed to allocate input line"));
532 wchar_t *wcdata
= local_wcstok(ctl
, wcs
, &sv
);
537 if (ctl
->maxncols
&& n
+ 1 == ctl
->maxncols
) {
538 /* Use rest of the string as column data */
539 size_t skip
= wcdata
- wcs0
;
543 if (scols_table_get_ncols(ctl
->tab
) < n
+ 1) {
544 if (scols_table_is_json(ctl
->tab
) && !ctl
->hide_unnamed
)
545 errx(EXIT_FAILURE
, _("line %zu: for JSON the name of the "
546 "column %zu is required"),
547 scols_table_get_nlines(ctl
->tab
) + 1,
549 scols_table_new_column(ctl
->tab
, NULL
, 0,
550 ctl
->hide_unnamed
? SCOLS_FL_HIDDEN
: 0);
553 ln
= scols_table_new_line(ctl
->tab
, NULL
);
555 err(EXIT_FAILURE
, _("failed to allocate output line"));
558 data
= wcs_to_mbs(wcdata
);
560 err(EXIT_FAILURE
, _("failed to allocate output data"));
561 if (scols_line_refer_data(ln
, n
, data
))
562 err(EXIT_FAILURE
, _("failed to add output data"));
565 if (ctl
->maxncols
&& n
== ctl
->maxncols
)
573 static int add_emptyline_to_table(struct column_control
*ctl
)
578 if (!scols_table_new_line(ctl
->tab
, NULL
))
579 err(EXIT_FAILURE
, _("failed to allocate output line"));
584 static void add_entry(struct column_control
*ctl
, size_t *maxents
, wchar_t *wcs
)
586 if (ctl
->nents
<= *maxents
) {
588 ctl
->ents
= xreallocarray(ctl
->ents
, *maxents
, sizeof(wchar_t *));
590 ctl
->ents
[ctl
->nents
] = wcs
;
594 static int read_input(struct column_control
*ctl
, FILE *fp
)
596 wchar_t *empty
= NULL
;
608 if (getline(&buf
, &bufsz
, fp
) < 0) {
611 err(EXIT_FAILURE
, _("read failed"));
613 str
= (char *) skip_space(buf
);
615 p
= strchr(str
, '\n');
620 if (ctl
->keep_empty_lines
) {
621 if (ctl
->mode
== COLUMN_MODE_TABLE
) {
622 add_emptyline_to_table(ctl
);
625 empty
= mbs_to_wcs("");
626 add_entry(ctl
, &maxents
, empty
);
632 wcs
= mbs_to_wcs(buf
);
635 * Convert broken sequences to \x<hex> and continue.
638 char *tmp
= mbs_invalid_encode(buf
, &tmpsz
);
641 err(EXIT_FAILURE
, _("read failed"));
642 wcs
= mbs_to_wcs(tmp
);
647 case COLUMN_MODE_TABLE
:
648 rc
= add_line_to_table(ctl
, wcs
);
652 case COLUMN_MODE_FILLCOLS
:
653 case COLUMN_MODE_FILLROWS
:
654 add_entry(ctl
, &maxents
, wcs
);
656 if (ctl
->maxlength
< len
)
657 ctl
->maxlength
= len
;
671 static void columnate_fillrows(struct column_control
*ctl
)
673 size_t chcnt
, col
, cnt
, endcol
, numcols
;
676 ctl
->maxlength
= (ctl
->maxlength
+ TABCHAR_CELLS
) & ~(TABCHAR_CELLS
- 1);
677 numcols
= ctl
->termwidth
/ ctl
->maxlength
;
678 endcol
= ctl
->maxlength
;
679 for (chcnt
= col
= 0, lp
= ctl
->ents
; /* nothing */; ++lp
) {
684 if (++col
== numcols
) {
686 endcol
= ctl
->maxlength
;
689 while ((cnt
= ((chcnt
+ TABCHAR_CELLS
) & ~(TABCHAR_CELLS
- 1))) <= endcol
) {
693 endcol
+= ctl
->maxlength
;
700 static void columnate_fillcols(struct column_control
*ctl
)
702 size_t base
, chcnt
, cnt
, col
, endcol
, numcols
, numrows
, row
;
704 ctl
->maxlength
= (ctl
->maxlength
+ TABCHAR_CELLS
) & ~(TABCHAR_CELLS
- 1);
705 numcols
= ctl
->termwidth
/ ctl
->maxlength
;
708 numrows
= ctl
->nents
/ numcols
;
709 if (ctl
->nents
% numcols
)
712 for (row
= 0; row
< numrows
; ++row
) {
713 endcol
= ctl
->maxlength
;
714 for (base
= row
, chcnt
= col
= 0; col
< numcols
; ++col
) {
715 fputws(ctl
->ents
[base
], stdout
);
716 chcnt
+= width(ctl
->ents
[base
]);
717 if ((base
+= numrows
) >= ctl
->nents
)
719 while ((cnt
= ((chcnt
+ TABCHAR_CELLS
) & ~(TABCHAR_CELLS
- 1))) <= endcol
) {
723 endcol
+= ctl
->maxlength
;
729 static void simple_print(struct column_control
*ctl
)
734 for (cnt
= ctl
->nents
, lp
= ctl
->ents
; cnt
--; ++lp
) {
740 static void __attribute__((__noreturn__
)) usage(void)
744 fputs(USAGE_HEADER
, out
);
745 fprintf(out
, _(" %s [options] [<file>...]\n"), program_invocation_short_name
);
747 fputs(USAGE_SEPARATOR
, out
);
748 fputs(_("Columnate lists.\n"), out
);
750 fputs(USAGE_OPTIONS
, out
);
751 fputs(_(" -t, --table create a table\n"), out
);
752 fputs(_(" -n, --table-name <name> table name for JSON output\n"), out
);
753 fputs(_(" -O, --table-order <columns> specify order of output columns\n"), out
);
754 fputs(_(" -C, --table-column <properties> define column\n"), out
);
755 fputs(_(" -N, --table-columns <names> comma separated columns names\n"), out
);
756 fputs(_(" -l, --table-columns-limit <num> maximal number of input columns\n"), out
);
757 fputs(_(" -E, --table-noextreme <columns> don't count long text from the columns to column width\n"), out
);
758 fputs(_(" -d, --table-noheadings don't print header\n"), out
);
759 fputs(_(" -m, --table-maxout fill all available space\n"), out
);
760 fputs(_(" -e, --table-header-repeat repeat header for each page\n"), out
);
761 fputs(_(" -H, --table-hide <columns> don't print the columns\n"), out
);
762 fputs(_(" -R, --table-right <columns> right align text in these columns\n"), out
);
763 fputs(_(" -T, --table-truncate <columns> truncate text in the columns when necessary\n"), out
);
764 fputs(_(" -W, --table-wrap <columns> wrap text in the columns when necessary\n"), out
);
765 fputs(_(" -L, --keep-empty-lines don't ignore empty lines\n"), out
);
766 fputs(_(" -J, --json use JSON output format for table\n"), out
);
768 fputs(USAGE_SEPARATOR
, out
);
769 fputs(_(" -r, --tree <column> column to use tree-like output for the table\n"), out
);
770 fputs(_(" -i, --tree-id <column> line ID to specify child-parent relation\n"), out
);
771 fputs(_(" -p, --tree-parent <column> parent to specify child-parent relation\n"), out
);
773 fputs(USAGE_SEPARATOR
, out
);
774 fputs(_(" -c, --output-width <width> width of output in number of characters\n"), out
);
775 fputs(_(" -o, --output-separator <string> columns separator for table output (default is two spaces)\n"), out
);
776 fputs(_(" -s, --separator <string> possible table delimiters\n"), out
);
777 fputs(_(" -x, --fillrows fill rows before columns\n"), out
);
780 fputs(USAGE_SEPARATOR
, out
);
781 fprintf(out
, USAGE_HELP_OPTIONS(34));
782 fprintf(out
, USAGE_MAN_TAIL("column(1)"));
787 int main(int argc
, char **argv
)
789 struct column_control ctl
= {
790 .mode
= COLUMN_MODE_FILLCOLS
,
792 .termwidth
= (size_t) -1
796 unsigned int eval
= 0; /* exit value */
798 static const struct option longopts
[] =
800 { "columns", required_argument
, NULL
, 'c' }, /* deprecated */
801 { "fillrows", no_argument
, NULL
, 'x' },
802 { "help", no_argument
, NULL
, 'h' },
803 { "json", no_argument
, NULL
, 'J' },
804 { "keep-empty-lines", no_argument
, NULL
, 'L' },
805 { "output-separator", required_argument
, NULL
, 'o' },
806 { "output-width", required_argument
, NULL
, 'c' },
807 { "separator", required_argument
, NULL
, 's' },
808 { "table", no_argument
, NULL
, 't' },
809 { "table-columns", required_argument
, NULL
, 'N' },
810 { "table-column", required_argument
, NULL
, 'C' },
811 { "table-columns-limit", required_argument
, NULL
, 'l' },
812 { "table-hide", required_argument
, NULL
, 'H' },
813 { "table-name", required_argument
, NULL
, 'n' },
814 { "table-maxout", no_argument
, NULL
, 'm' },
815 { "table-noextreme", required_argument
, NULL
, 'E' },
816 { "table-noheadings", no_argument
, NULL
, 'd' },
817 { "table-order", required_argument
, NULL
, 'O' },
818 { "table-right", required_argument
, NULL
, 'R' },
819 { "table-truncate", required_argument
, NULL
, 'T' },
820 { "table-wrap", required_argument
, NULL
, 'W' },
821 { "table-empty-lines", no_argument
, NULL
, 'L' }, /* deprecated */
822 { "table-header-repeat", no_argument
, NULL
, 'e' },
823 { "tree", required_argument
, NULL
, 'r' },
824 { "tree-id", required_argument
, NULL
, 'i' },
825 { "tree-parent", required_argument
, NULL
, 'p' },
826 { "version", no_argument
, NULL
, 'V' },
827 { NULL
, 0, NULL
, 0 },
829 static const ul_excl_t excl
[] = { /* rows and cols in ASCII order */
835 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
837 setlocale(LC_ALL
, "");
838 bindtextdomain(PACKAGE
, LOCALEDIR
);
840 close_stdout_atexit();
842 ctl
.output_separator
= " ";
843 ctl
.input_separator
= mbs_to_wcs("\t ");
845 while ((c
= getopt_long(argc
, argv
, "C:c:dE:eH:hi:Jl:LN:n:mO:o:p:R:r:s:T:tVW:x", longopts
, NULL
)) != -1) {
847 err_exclusive_options(c
, longopts
, excl
, excl_st
);
851 if (strv_extend(&ctl
.tab_columns
, optarg
))
855 if (strcmp(optarg
, "unlimited") == 0)
858 ctl
.termwidth
= strtou32_or_err(optarg
, _("invalid columns argument"));
861 ctl
.tab_noheadings
= 1;
864 ctl
.tab_colnoextrem
= optarg
;
867 ctl
.header_repeat
= 1;
870 ctl
.tab_colhide
= optarg
;
871 ctl
.hide_unnamed
= has_unnamed(ctl
.tab_colhide
);
874 ctl
.tree_id
= optarg
;
878 ctl
.mode
= COLUMN_MODE_TABLE
;
881 ctl
.keep_empty_lines
= 1;
884 ctl
.maxncols
= strtou32_or_err(optarg
, _("invalid columns limit argument"));
885 if (ctl
.maxncols
== 0)
886 errx(EXIT_FAILURE
, _("columns limit must be greater than zero"));
889 ctl
.tab_colnames
= split_or_error(optarg
, _("failed to parse column names"));
892 ctl
.tab_name
= optarg
;
898 ctl
.tab_order
= optarg
;
901 ctl
.output_separator
= optarg
;
904 ctl
.tree_parent
= optarg
;
907 ctl
.tab_colright
= optarg
;
913 free(ctl
.input_separator
);
914 ctl
.input_separator
= mbs_to_wcs(optarg
);
915 if (!ctl
.input_separator
)
916 err(EXIT_FAILURE
, _("failed to use input separator"));
920 ctl
.tab_coltrunc
= optarg
;
923 ctl
.mode
= COLUMN_MODE_TABLE
;
926 ctl
.tab_colwrap
= optarg
;
929 ctl
.mode
= COLUMN_MODE_FILLROWS
;
935 print_version(EXIT_SUCCESS
);
937 errtryhelp(EXIT_FAILURE
);
943 if (ctl
.termwidth
== (size_t) -1)
944 ctl
.termwidth
= get_terminal_width(80);
947 ctl
.mode
= COLUMN_MODE_TABLE
;
948 if (!ctl
.tree_parent
|| !ctl
.tree_id
)
949 errx(EXIT_FAILURE
, _("options --tree-id and --tree-parent are "
950 "required for tree formatting"));
953 if (ctl
.mode
!= COLUMN_MODE_TABLE
954 && (ctl
.tab_order
|| ctl
.tab_name
|| ctl
.tab_colwrap
||
955 ctl
.tab_colhide
|| ctl
.tab_coltrunc
|| ctl
.tab_colnoextrem
||
956 ctl
.tab_colright
|| ctl
.tab_colnames
|| ctl
.tab_columns
))
957 errx(EXIT_FAILURE
, _("option --table required for all --table-*"));
959 if (!ctl
.tab_colnames
&& !ctl
.tab_columns
&& ctl
.json
)
960 errx(EXIT_FAILURE
, _("option --table-columns or --table-column required for --json"));
963 eval
+= read_input(&ctl
, stdin
);
965 for (; *argv
; ++argv
) {
968 if ((fp
= fopen(*argv
, "r")) != NULL
) {
969 eval
+= read_input(&ctl
, fp
);
973 eval
+= EXIT_FAILURE
;
977 if (ctl
.mode
!= COLUMN_MODE_TABLE
) {
980 if (ctl
.maxlength
>= ctl
.termwidth
)
981 ctl
.mode
= COLUMN_MODE_SIMPLE
;
985 case COLUMN_MODE_TABLE
:
986 if (ctl
.tab
&& scols_table_get_nlines(ctl
.tab
)) {
988 eval
= scols_print_table(ctl
.tab
);
990 scols_unref_table(ctl
.tab
);
991 if (ctl
.tab_colnames
)
992 strv_free(ctl
.tab_colnames
);
994 strv_free(ctl
.tab_columns
);
997 case COLUMN_MODE_FILLCOLS
:
998 columnate_fillcols(&ctl
);
1000 case COLUMN_MODE_FILLROWS
:
1001 columnate_fillrows(&ctl
);
1003 case COLUMN_MODE_SIMPLE
:
1008 free(ctl
.input_separator
);
1010 return eval
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;