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