]> git.ipfire.org Git - thirdparty/util-linux.git/blame - text-utils/column.c
libfdisk: (dos) accept start for log.partitions on template
[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>
6dbe3af9 39#include <stdio.h>
fd6b7a7f 40#include <unistd.h>
6dbe3af9
KZ
41#include <stdlib.h>
42#include <string.h>
a4cc8dfe
SK
43#include <errno.h>
44#include <getopt.h>
6dbe3af9 45
3b56eea7 46#include "nls.h"
eb76ca98 47#include "c.h"
9ea8ded3 48#include "widechar.h"
a29e40ca 49#include "xalloc.h"
02b77f7b 50#include "strutils.h"
b87cbe84 51#include "closestream.h"
3b56eea7 52#include "ttyutils.h"
11a1092a 53#include "strv.h"
be03f652 54#include "optutils.h"
eb63b9b8 55
4762ae9d 56#include "libsmartcols.h"
eb63b9b8 57
739e58ff 58#define TABCHAR_CELLS 8
683ddbd5 59
7d07df62
KZ
60enum {
61 COLUMN_MODE_FILLCOLS = 0,
62 COLUMN_MODE_FILLROWS,
63 COLUMN_MODE_TABLE,
64 COLUMN_MODE_SIMPLE
65};
66
67struct column_control {
2593c139 68 int mode; /* COLUMN_MODE_* */
0784187d 69 size_t termwidth;
2593c139 70
4762ae9d
KZ
71 struct libscols_table *tab;
72
7e947f48
KZ
73 char **tab_colnames; /* array with column names */
74 const char *tab_name; /* table name */
75 const char *tab_order; /* --table-order */
76
1ae24ec2
KZ
77 const char *tab_colright; /* --table-right */
78 const char *tab_coltrunc; /* --table-trunc */
79 const char *tab_colnoextrem; /* --table-noextreme */
68916af3 80 const char *tab_colwrap; /* --table-wrap */
9624f615 81 const char *tab_colhide; /* --table-hide */
11a1092a 82
90852c3e
KZ
83 const char *tree;
84 const char *tree_id;
85 const char *tree_parent;
86
4762ae9d
KZ
87 wchar_t *input_separator;
88 const char *output_separator;
89
0784187d
KZ
90 wchar_t **ents; /* input entries */
91 size_t nents; /* number of entries */
92 size_t maxlength; /* longest input record (line) */
7d07df62 93
9dbe8e1c
KZ
94 unsigned int greedy :1,
95 json :1;
4762ae9d 96};
7d07df62 97
8f1be588 98static size_t width(const wchar_t *str)
683ddbd5 99{
8f1be588 100 size_t width = 0;
683ddbd5
KZ
101
102 for (; *str != '\0'; str++) {
8f1be588
KZ
103#ifdef HAVE_WIDECHAR
104 int x = wcwidth(*str); /* don't use wcswidth(), need to ignore non-printable */
683ddbd5
KZ
105 if (x > 0)
106 width += x;
683ddbd5 107#else
683ddbd5
KZ
108 if (isprint(*str))
109 width++;
8f1be588 110#endif
683ddbd5
KZ
111 }
112 return width;
113}
683ddbd5 114
37d84d6d
KZ
115static wchar_t *mbs_to_wcs(const char *s)
116{
8f1be588 117#ifdef HAVE_WIDECHAR
37d84d6d
KZ
118 ssize_t n;
119 wchar_t *wcs;
120
121 n = mbstowcs((wchar_t *)0, s, 0);
122 if (n < 0)
123 return NULL;
86399c33 124 wcs = xcalloc((n + 1) * sizeof(wchar_t), 1);
37d84d6d
KZ
125 n = mbstowcs(wcs, s, n + 1);
126 if (n < 0) {
127 free(wcs);
128 return NULL;
129 }
130 return wcs;
8f1be588 131#else
8bc5195b 132 return xstrdup(s);
37d84d6d 133#endif
8f1be588 134}
37d84d6d 135
4762ae9d
KZ
136static char *wcs_to_mbs(const wchar_t *s)
137{
138#ifdef HAVE_WIDECHAR
139 size_t n;
140 char *str;
141
142 n = wcstombs(NULL, s, 0);
143 if (n == (size_t) -1)
144 return NULL;
145
86399c33 146 str = xcalloc(n + 1, 1);
4762ae9d
KZ
147 if (wcstombs(str, s, n) == (size_t) -1) {
148 free(str);
149 return NULL;
150 }
151 return str;
152#else
8bc5195b 153 return xstrdup(s);
4762ae9d
KZ
154#endif
155}
156
157static wchar_t *local_wcstok(wchar_t *p, const wchar_t *separator, int greedy, wchar_t **state)
158{
159 wchar_t *result = NULL;
160
161 if (greedy)
162 return wcstok(p, separator, state);
163 if (!p) {
164 if (!*state || !**state)
165 return NULL;
166 p = *state;
167 }
168 result = p;
169 p = wcspbrk(result, separator);
170 if (!p)
171 *state = NULL;
172 else {
173 *p = '\0';
174 *state = p + 1;
175 }
176 return result;
177}
178
dbed2f8c 179static char **split_or_error(const char *str, const char *errmsg)
11a1092a 180{
dbed2f8c
KZ
181 char **res = strv_split(str, ",");
182 if (!res) {
11a1092a
KZ
183 if (errno == ENOMEM)
184 err_oom();
dbed2f8c 185 errx(EXIT_FAILURE, "%s: '%s'", errmsg, str);
11a1092a 186 }
dbed2f8c 187 return res;
11a1092a
KZ
188}
189
4762ae9d
KZ
190static void init_table(struct column_control *ctl)
191{
192 scols_init_debug(0);
193
194 ctl->tab = scols_new_table();
195 if (!ctl->tab)
196 err(EXIT_FAILURE, _("failed to allocate output table"));
197
198 scols_table_set_column_separator(ctl->tab, ctl->output_separator);
9dbe8e1c
KZ
199 if (ctl->json) {
200 scols_table_enable_json(ctl->tab, 1);
201 scols_table_set_name(ctl->tab, ctl->tab_name ? : "table");
202 }
11a1092a
KZ
203 if (ctl->tab_colnames) {
204 char **name;
205
206 STRV_FOREACH(name, ctl->tab_colnames)
207 scols_table_new_column(ctl->tab, *name, 0, 0);
208 } else
209 scols_table_enable_noheadings(ctl->tab, 1);
4762ae9d
KZ
210}
211
dbed2f8c
KZ
212static struct libscols_column *string_to_column(struct column_control *ctl, const char *str)
213{
214 uint32_t colnum = 0;
215
216 if (isdigit_string(str))
217 colnum = strtou32_or_err(str, _("failed to parse column")) - 1;
218 else {
219 char **name;
220
221 STRV_FOREACH(name, ctl->tab_colnames) {
222 if (strcasecmp(*name, str) == 0)
223 break;
224 colnum++;
225 }
226 if (!name || !*name)
227 errx(EXIT_FAILURE, _("undefined column name '%s'"), str);
228 }
229
230 return scols_table_get_column(ctl->tab, colnum);
231}
232
c728e000
KZ
233static struct libscols_column *get_last_visible_column(struct column_control *ctl)
234{
235 struct libscols_iter *itr;
236 struct libscols_column *cl, *last = NULL;
237
238 itr = scols_new_iter(SCOLS_ITER_FORWARD);
239 if (!itr)
240 err_oom();
241
242 while (scols_table_next_column(ctl->tab, itr, &cl) == 0) {
243 if (scols_column_get_flags(cl) & SCOLS_FL_HIDDEN)
244 continue;
245 last = cl;
246 }
247
248 scols_free_iter(itr);
249 return last;
250}
3ba01db0 251
dbed2f8c
KZ
252static int column_set_flag(struct libscols_column *cl, int fl)
253{
254 int cur = scols_column_get_flags(cl);
255
256 return scols_column_set_flags(cl, cur | fl);
257}
258
3ba01db0
KZ
259static void apply_columnflag_from_list(struct column_control *ctl, const char *list,
260 int flag, const char *errmsg)
261{
262 char **all = split_or_error(list, errmsg);
263 char **one;
264
265 STRV_FOREACH(one, all) {
266 struct libscols_column *cl = string_to_column(ctl, *one);
267 if (cl)
268 column_set_flag(cl, flag);
269 }
270 strv_free(all);
271}
272
166271a9
KZ
273static void reorder_table(struct column_control *ctl)
274{
275 struct libscols_column **wanted, *last = NULL;
276 size_t i, count = 0;
277 size_t ncols = scols_table_get_ncols(ctl->tab);
278 char **order = split_or_error(ctl->tab_order, _("failed to parse --table-order list"));
279 char **one;
280
281 wanted = xcalloc(ncols, sizeof(struct libscols_column *));
282
283 STRV_FOREACH(one, order) {
284 struct libscols_column *cl = string_to_column(ctl, *one);
285 if (cl)
286 wanted[count++] = cl;
287 }
288
289 for (i = 0; i < count; i++) {
290 scols_table_move_column(ctl->tab, last, wanted[i]);
291 last = wanted[i];
292 }
293}
294
90852c3e
KZ
295static void create_tree(struct column_control *ctl)
296{
297 struct libscols_column *cl_tree = string_to_column(ctl, ctl->tree);
298 struct libscols_column *cl_p = string_to_column(ctl, ctl->tree_parent);
299 struct libscols_column *cl_i = string_to_column(ctl, ctl->tree_id);
300 struct libscols_iter *itr_p, *itr_i;
301 struct libscols_line *ln_i;
302
303 if (!cl_p || !cl_i || !cl_tree)
304 return; /* silently ignore the tree request */
305
306 column_set_flag(cl_tree, SCOLS_FL_TREE);
307
308 itr_p = scols_new_iter(SCOLS_ITER_FORWARD);
309 itr_i = scols_new_iter(SCOLS_ITER_FORWARD);
310 if (!itr_p || !itr_i)
311 err_oom();
312
313 /* scan all lines for ID */
314 while (scols_table_next_line(ctl->tab, itr_i, &ln_i) == 0) {
315 struct libscols_line *ln;
316 struct libscols_cell *ce = scols_line_get_column_cell(ln_i, cl_i);
317 const char *id = ce ? scols_cell_get_data(ce) : NULL;
318
319 if (!id)
320 continue;
321
322 /* see if the ID is somewhere used in parent column */
323 scols_reset_iter(itr_p, SCOLS_ITER_FORWARD);
324 while (scols_table_next_line(ctl->tab, itr_p, &ln) == 0) {
325 const char *parent;
326
327 ce = scols_line_get_column_cell(ln, cl_p);
328 parent = ce ? scols_cell_get_data(ce) : NULL;
329
330 if (!parent)
331 continue;
c467fdaf 332 if (strcmp(id, parent) != 0)
b0f00a94 333 continue;
c467fdaf 334 if (scols_line_is_ancestor(ln, ln_i))
b0f00a94 335 continue;
c467fdaf 336 scols_line_add_child(ln_i, ln);
90852c3e
KZ
337 }
338 }
339
340 scols_free_iter(itr_p);
341 scols_free_iter(itr_i);
342}
343
dbed2f8c
KZ
344static void modify_table(struct column_control *ctl)
345{
3ba01db0 346 scols_table_set_termwidth(ctl->tab, ctl->termwidth);
bd6b5a64 347 scols_table_set_termforce(ctl->tab, SCOLS_TERMFORCE_ALWAYS);
3ba01db0 348
3ba01db0
KZ
349 if (ctl->tab_colright)
350 apply_columnflag_from_list(ctl, ctl->tab_colright,
351 SCOLS_FL_RIGHT, _("failed to parse --table-right list"));
352
3ba01db0
KZ
353 if (ctl->tab_coltrunc)
354 apply_columnflag_from_list(ctl, ctl->tab_coltrunc,
355 SCOLS_FL_TRUNC , _("failed to parse --table-trunc list"));
1ae24ec2
KZ
356
357 if (ctl->tab_colnoextrem)
358 apply_columnflag_from_list(ctl, ctl->tab_colnoextrem,
359 SCOLS_FL_NOEXTREMES , _("failed to parse --table-noextreme list"));
68916af3
KZ
360
361 if (ctl->tab_colwrap)
362 apply_columnflag_from_list(ctl, ctl->tab_colwrap,
363 SCOLS_FL_WRAP , _("failed to parse --table-wrap list"));
364
9624f615
KZ
365 if (ctl->tab_colhide)
366 apply_columnflag_from_list(ctl, ctl->tab_colhide,
367 SCOLS_FL_HIDDEN , _("failed to parse --table-hide list"));
166271a9 368
c728e000
KZ
369 if (!ctl->tab_colnoextrem) {
370 struct libscols_column *cl = get_last_visible_column(ctl);
371 if (cl)
372 column_set_flag(cl, SCOLS_FL_NOEXTREMES);
373 }
374
90852c3e
KZ
375 if (ctl->tree)
376 create_tree(ctl);
166271a9
KZ
377
378 /* This must be the last step! */
379 if (ctl->tab_order)
380 reorder_table(ctl);
dbed2f8c
KZ
381}
382
166271a9 383
4762ae9d
KZ
384static int add_line_to_table(struct column_control *ctl, wchar_t *wcs)
385{
386 wchar_t *wcdata, *sv = NULL;
387 size_t n = 0;
388 struct libscols_line *ln = NULL;
389
390 if (!ctl->tab)
391 init_table(ctl);
392
393 while ((wcdata = local_wcstok(wcs, ctl->input_separator, ctl->greedy, &sv))) {
394 char *data;
395
cb3fdf2a
KZ
396 if (scols_table_get_ncols(ctl->tab) < n + 1) {
397 if (scols_table_is_json(ctl->tab))
398 errx(EXIT_FAILURE, _("line %zu: for JSON the name of the "
399 "column %zu is required"),
400 scols_table_get_nlines(ctl->tab) + 1,
401 n + 1);
4762ae9d 402 scols_table_new_column(ctl->tab, NULL, 0, 0);
cb3fdf2a 403 }
4762ae9d
KZ
404 if (!ln) {
405 ln = scols_table_new_line(ctl->tab, NULL);
406 if (!ln)
407 err(EXIT_FAILURE, _("failed to allocate output line"));
408 }
409 data = wcs_to_mbs(wcdata);
410 if (!data)
411 err(EXIT_FAILURE, _("failed to allocate output data"));
412 if (scols_line_refer_data(ln, n, data))
413 err(EXIT_FAILURE, _("failed to add output data"));
414 n++;
415 wcs = NULL;
416 }
417
418 return 0;
419}
420
37d84d6d
KZ
421static int read_input(struct column_control *ctl, FILE *fp)
422{
423 char *buf = NULL;
424 size_t bufsz = 0;
425 size_t maxents = 0;
4762ae9d 426 int rc = 0;
37d84d6d
KZ
427
428 do {
429 char *str, *p;
4762ae9d 430 wchar_t *wcs = NULL;
37d84d6d
KZ
431 size_t len;
432
433 if (getline(&buf, &bufsz, fp) < 0) {
434 if (feof(fp))
435 break;
4762ae9d 436 err(EXIT_FAILURE, _("read failed"));
37d84d6d
KZ
437 }
438 str = (char *) skip_space(buf);
439 if (str) {
440 p = strchr(str, '\n');
441 if (p)
442 *p = '\0';
443 }
444 if (!str || !*str)
445 continue;
446
4762ae9d
KZ
447 wcs = mbs_to_wcs(str);
448 if (!wcs)
449 err(EXIT_FAILURE, _("read failed"));
450
37d84d6d
KZ
451 switch (ctl->mode) {
452 case COLUMN_MODE_TABLE:
4762ae9d
KZ
453 rc = add_line_to_table(ctl, wcs);
454 free(wcs);
455 break;
37d84d6d
KZ
456
457 case COLUMN_MODE_FILLCOLS:
458 case COLUMN_MODE_FILLROWS:
459 if (ctl->nents <= maxents) {
460 maxents += 1000;
461 ctl->ents = xrealloc(ctl->ents,
462 maxents * sizeof(wchar_t *));
463 }
4762ae9d 464 ctl->ents[ctl->nents] = wcs;
37d84d6d
KZ
465 len = width(ctl->ents[ctl->nents]);
466 if (ctl->maxlength < len)
467 ctl->maxlength = len;
468 ctl->nents++;
469 break;
470 }
4762ae9d 471 } while (rc == 0);
37d84d6d 472
4762ae9d 473 return rc;
37d84d6d
KZ
474}
475
476
477static void columnate_fillrows(struct column_control *ctl)
478{
479 size_t chcnt, col, cnt, endcol, numcols;
480 wchar_t **lp;
481
739e58ff 482 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
37d84d6d
KZ
483 numcols = ctl->termwidth / ctl->maxlength;
484 endcol = ctl->maxlength;
485 for (chcnt = col = 0, lp = ctl->ents;; ++lp) {
486 fputws(*lp, stdout);
487 chcnt += width(*lp);
488 if (!--ctl->nents)
489 break;
490 if (++col == numcols) {
491 chcnt = col = 0;
492 endcol = ctl->maxlength;
493 putwchar('\n');
494 } else {
739e58ff 495 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
37d84d6d
KZ
496 putwchar('\t');
497 chcnt = cnt;
498 }
499 endcol += ctl->maxlength;
500 }
501 }
502 if (chcnt)
503 putwchar('\n');
504}
505
506static void columnate_fillcols(struct column_control *ctl)
507{
508 size_t base, chcnt, cnt, col, endcol, numcols, numrows, row;
509
739e58ff 510 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
37d84d6d
KZ
511 numcols = ctl->termwidth / ctl->maxlength;
512 if (!numcols)
513 numcols = 1;
514 numrows = ctl->nents / numcols;
515 if (ctl->nents % numcols)
516 ++numrows;
517
518 for (row = 0; row < numrows; ++row) {
519 endcol = ctl->maxlength;
520 for (base = row, chcnt = col = 0; col < numcols; ++col) {
521 fputws(ctl->ents[base], stdout);
522 chcnt += width(ctl->ents[base]);
523 if ((base += numrows) >= ctl->nents)
524 break;
739e58ff 525 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
37d84d6d
KZ
526 putwchar('\t');
527 chcnt = cnt;
528 }
529 endcol += ctl->maxlength;
530 }
531 putwchar('\n');
532 }
533}
534
535static void simple_print(struct column_control *ctl)
536{
537 int cnt;
538 wchar_t **lp;
539
540 for (cnt = ctl->nents, lp = ctl->ents; cnt--; ++lp) {
541 fputws(*lp, stdout);
542 putwchar('\n');
543 }
544}
545
a4cc8dfe
SK
546static void __attribute__((__noreturn__)) usage(int rc)
547{
548 FILE *out = rc == EXIT_FAILURE ? stderr : stdout;
549
3eed42f3 550 fputs(USAGE_HEADER, out);
4ce393f4 551 fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
451dbcfa
BS
552
553 fputs(USAGE_SEPARATOR, out);
554 fputs(_("Columnate lists.\n"), out);
555
3eed42f3 556 fputs(USAGE_OPTIONS, out);
d7a3bf94 557 fputs(_(" -t, --table create a table\n"), out);
7e947f48
KZ
558 fputs(_(" -n, --table-name <name> table name for JSON output\n"), out);
559 fputs(_(" -O, --table-order <columns> specify order of output columns\n"), out);
01e335c9 560 fputs(_(" -N, --table-columns <names> comma separated columns names\n"), out);
7e947f48
KZ
561 fputs(_(" -E, --table-noextreme <columns> don't count long text from the columns to column width\n"), out);
562 fputs(_(" -H, --table-hide <columns> don't print the columns\n"), out);
01e335c9 563 fputs(_(" -R, --table-right <columns> right align text in these columns\n"), out);
1ae24ec2 564 fputs(_(" -T, --table-truncate <columns> truncate text in the columns when necessary\n"), out);
68916af3 565 fputs(_(" -W, --table-wrap <columns> wrap text in the columns when necessary\n"), out);
7e947f48
KZ
566 fputs(_(" -J, --json use JSON output format for table\n"), out);
567
74cc7c25 568 fputs(USAGE_SEPARATOR, out);
90852c3e
KZ
569 fputs(_(" -r, --tree <column> column to use tree-like output for the table\n"), out);
570 fputs(_(" -i, --tree-id <column> line ID to specify child-parent relation\n"), out);
571 fputs(_(" -p, --tree-parent <column> parent to specify child-parent relation\n"), out);
572
7e947f48 573 fputs(USAGE_SEPARATOR, out);
d7a3bf94 574 fputs(_(" -c, --output-width <width> width of output in number of characters\n"), out);
7e947f48
KZ
575 fputs(_(" -o, --output-separator <string> columns separator for table output (default is two spaces)\n"), out);
576 fputs(_(" -s, --separator <string> possible table delimiters\n"), out);
d7a3bf94 577 fputs(_(" -x, --fillrows fill rows before columns\n"), out);
7e947f48 578
3eed42f3
SK
579 fputs(USAGE_SEPARATOR, out);
580 fputs(USAGE_HELP, out);
581 fputs(USAGE_VERSION, out);
582 fprintf(out, USAGE_MAN_TAIL("column(1)"));
a4cc8dfe 583
a4cc8dfe
SK
584 exit(rc);
585}
1df29104
SK
586
587int main(int argc, char **argv)
6dbe3af9 588{
2593c139
KZ
589 struct column_control ctl = {
590 .mode = COLUMN_MODE_FILLCOLS,
bd6b5a64
KZ
591 .greedy = 1,
592 .termwidth = (size_t) -1
2593c139 593 };
7d07df62 594
be03f652 595 int c;
daf093d2 596 unsigned int eval = 0; /* exit value */
daf093d2
SK
597
598 static const struct option longopts[] =
599 {
11a1092a
KZ
600 { "columns", required_argument, NULL, 'c' }, /* deprecated */
601 { "fillrows", no_argument, NULL, 'x' },
602 { "help", no_argument, NULL, 'h' },
7e947f48 603 { "json", no_argument, NULL, 'J' },
11a1092a
KZ
604 { "output-separator", required_argument, NULL, 'o' },
605 { "output-width", required_argument, NULL, 'c' },
606 { "separator", required_argument, NULL, 's' },
607 { "table", no_argument, NULL, 't' },
01e335c9 608 { "table-columns", required_argument, NULL, 'N' },
7e947f48
KZ
609 { "table-hide", required_argument, NULL, 'H' },
610 { "table-name", required_argument, NULL, 'n' },
611 { "table-noextreme", required_argument, NULL, 'E' },
612 { "table-order", required_argument, NULL, 'O' },
01e335c9 613 { "table-right", required_argument, NULL, 'R' },
3ba01db0 614 { "table-truncate", required_argument, NULL, 'T' },
68916af3 615 { "table-wrap", required_argument, NULL, 'W' },
90852c3e
KZ
616 { "tree", required_argument, NULL, 'r' },
617 { "tree-id", required_argument, NULL, 'i' },
618 { "tree-parent", required_argument, NULL, 'p' },
11a1092a 619 { "version", no_argument, NULL, 'V' },
87918040 620 { NULL, 0, NULL, 0 },
daf093d2 621 };
be03f652
KZ
622 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
623 { 'J','x' },
624 { 't','x' },
625 { 0 }
626 };
627 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
daf093d2 628
7eda085c
KZ
629 setlocale(LC_ALL, "");
630 bindtextdomain(PACKAGE, LOCALEDIR);
631 textdomain(PACKAGE);
b87cbe84 632 atexit(close_stdout);
6dbe3af9 633
4762ae9d
KZ
634 ctl.output_separator = " ";
635 ctl.input_separator = mbs_to_wcs("\t ");
6dbe3af9 636
90852c3e 637 while ((c = getopt_long(argc, argv, "c:E:H:hi:JN:n:O:o:p:R:r:s:T:tVW:x", longopts, NULL)) != -1) {
be03f652
KZ
638
639 err_exclusive_options(c, longopts, excl, excl_st);
640
641 switch(c) {
7e947f48
KZ
642 case 'c':
643 ctl.termwidth = strtou32_or_err(optarg, _("invalid columns argument"));
644 break;
1ae24ec2
KZ
645 case 'E':
646 ctl.tab_colnoextrem = optarg;
647 break;
7e947f48
KZ
648 case 'H':
649 ctl.tab_colhide = optarg;
650 break;
651 case 'h':
652 usage(EXIT_SUCCESS);
653 break;
90852c3e
KZ
654 case 'i':
655 ctl.tree_id = optarg;
656 break;
9dbe8e1c
KZ
657 case 'J':
658 ctl.json = 1;
659 ctl.mode = COLUMN_MODE_TABLE;
660 break;
11a1092a 661 case 'N':
dbed2f8c 662 ctl.tab_colnames = split_or_error(optarg, _("failed to parse column names"));
11a1092a 663 break;
7e947f48
KZ
664 case 'n':
665 ctl.tab_name = optarg;
9624f615 666 break;
7e947f48
KZ
667 case 'O':
668 ctl.tab_order = optarg;
1ae90932 669 break;
7e947f48
KZ
670 case 'o':
671 ctl.output_separator = optarg;
6dbe3af9 672 break;
90852c3e
KZ
673 case 'p':
674 ctl.tree_parent = optarg;
675 break;
dbed2f8c
KZ
676 case 'R':
677 ctl.tab_colright = optarg;
678 break;
90852c3e
KZ
679 case 'r':
680 ctl.tree = optarg;
681 break;
6dbe3af9 682 case 's':
4762ae9d
KZ
683 free(ctl.input_separator);
684 ctl.input_separator = mbs_to_wcs(optarg);
685 ctl.greedy = 0;
6dbe3af9 686 break;
7e947f48
KZ
687 case 'T':
688 ctl.tab_coltrunc = optarg;
47bd8ddc 689 break;
6dbe3af9 690 case 't':
7d07df62 691 ctl.mode = COLUMN_MODE_TABLE;
6dbe3af9 692 break;
7e947f48
KZ
693 case 'V':
694 printf(UTIL_LINUX_VERSION);
695 return EXIT_SUCCESS;
68916af3
KZ
696 case 'W':
697 ctl.tab_colwrap = optarg;
698 break;
6dbe3af9 699 case 'x':
7d07df62 700 ctl.mode = COLUMN_MODE_FILLROWS;
6dbe3af9 701 break;
6dbe3af9 702 default:
677ec86c 703 errtryhelp(EXIT_FAILURE);
be03f652 704 }
1df29104 705 }
6dbe3af9
KZ
706 argc -= optind;
707 argv += optind;
708
bd6b5a64
KZ
709 if (ctl.termwidth == (size_t) -1)
710 ctl.termwidth = get_terminal_width(80);
711
90852c3e
KZ
712 if (ctl.tree) {
713 ctl.mode = COLUMN_MODE_TABLE;
714 if (!ctl.tree_parent || !ctl.tree_id)
715 errx(EXIT_FAILURE, _("options --tree-id and --tree-parent are "
716 "required for tree formatting"));
717 }
718
7e947f48
KZ
719 if (ctl.mode != COLUMN_MODE_TABLE
720 && (ctl.tab_order || ctl.tab_name || ctl.tab_colwrap ||
721 ctl.tab_colhide || ctl.tab_coltrunc || ctl.tab_colnoextrem ||
722 ctl.tab_colright || ctl.tab_colnames))
723 errx(EXIT_FAILURE, _("option --table required for all --table-*"));
724
9dbe8e1c 725 if (ctl.tab_colnames == NULL && ctl.json)
2483c4c9 726 errx(EXIT_FAILURE, _("option --table-columns required for --json"));
9dbe8e1c 727
6dbe3af9 728 if (!*argv)
d6b63b9f 729 eval += read_input(&ctl, stdin);
1df29104 730 else
1ae90932 731 for (; *argv; ++argv) {
a46e644e
KZ
732 FILE *fp;
733
1ae90932 734 if ((fp = fopen(*argv, "r")) != NULL) {
d6b63b9f 735 eval += read_input(&ctl, fp);
daf093d2 736 fclose(fp);
1ae90932
SK
737 } else {
738 warn("%s", *argv);
daf093d2 739 eval += EXIT_FAILURE;
1ae90932 740 }
6dbe3af9
KZ
741 }
742
4762ae9d
KZ
743 if (ctl.mode != COLUMN_MODE_TABLE) {
744 if (!ctl.nents)
745 exit(eval);
746 if (ctl.maxlength >= ctl.termwidth)
747 ctl.mode = COLUMN_MODE_SIMPLE;
748 }
7d07df62
KZ
749
750 switch (ctl.mode) {
751 case COLUMN_MODE_TABLE:
da06d421
KZ
752 if (ctl.tab && scols_table_get_nlines(ctl.tab)) {
753 modify_table(&ctl);
754 eval = scols_print_table(ctl.tab);
755 }
7d07df62
KZ
756 break;
757 case COLUMN_MODE_FILLCOLS:
2593c139 758 columnate_fillcols(&ctl);
7d07df62
KZ
759 break;
760 case COLUMN_MODE_FILLROWS:
2593c139 761 columnate_fillrows(&ctl);
7d07df62
KZ
762 break;
763 case COLUMN_MODE_SIMPLE:
d6b63b9f 764 simple_print(&ctl);
7d07df62
KZ
765 break;
766 }
dcbca568 767
7d07df62 768 return eval == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
6dbe3af9 769}