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