]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/column.c
column: fix outputing empty column at the end of line
[thirdparty/util-linux.git] / text-utils / column.c
1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
6 *
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 */
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <getopt.h>
45
46 #include "nls.h"
47 #include "c.h"
48 #include "widechar.h"
49 #include "xalloc.h"
50 #include "strutils.h"
51 #include "closestream.h"
52 #include "ttyutils.h"
53 #include "strv.h"
54 #include "optutils.h"
55 #include "mbsalign.h"
56
57 #include "libsmartcols.h"
58
59 #define TABCHAR_CELLS 8
60
61 enum {
62 COLUMN_MODE_FILLCOLS = 0,
63 COLUMN_MODE_FILLROWS,
64 COLUMN_MODE_TABLE,
65 COLUMN_MODE_SIMPLE
66 };
67
68 struct column_control {
69 int mode; /* COLUMN_MODE_* */
70 size_t termwidth;
71
72 struct libscols_table *tab;
73
74 char **tab_colnames; /* array with column names */
75 const char *tab_name; /* table name */
76 const char *tab_order; /* --table-order */
77
78 const char *tab_colright; /* --table-right */
79 const char *tab_coltrunc; /* --table-trunc */
80 const char *tab_colnoextrem; /* --table-noextreme */
81 const char *tab_colwrap; /* --table-wrap */
82 const char *tab_colhide; /* --table-hide */
83
84 const char *tree;
85 const char *tree_id;
86 const char *tree_parent;
87
88 wchar_t *input_separator;
89 const char *output_separator;
90
91 wchar_t **ents; /* input entries */
92 size_t nents; /* number of entries */
93 size_t maxlength; /* longest input record (line) */
94
95 unsigned int greedy :1,
96 json :1,
97 header_repeat :1,
98 tab_empty_lines :1, /* --table-empty-lines */
99 tab_noheadings :1;
100 };
101
102 static size_t width(const wchar_t *str)
103 {
104 size_t width = 0;
105
106 for (; *str != '\0'; str++) {
107 #ifdef HAVE_WIDECHAR
108 int x = wcwidth(*str); /* don't use wcswidth(), need to ignore non-printable */
109 if (x > 0)
110 width += x;
111 #else
112 if (isprint(*str))
113 width++;
114 #endif
115 }
116 return width;
117 }
118
119 static wchar_t *mbs_to_wcs(const char *s)
120 {
121 #ifdef HAVE_WIDECHAR
122 ssize_t n;
123 wchar_t *wcs;
124
125 n = mbstowcs((wchar_t *)0, s, 0);
126 if (n < 0)
127 return NULL;
128 wcs = xcalloc((n + 1) * sizeof(wchar_t), 1);
129 n = mbstowcs(wcs, s, n + 1);
130 if (n < 0) {
131 free(wcs);
132 return NULL;
133 }
134 return wcs;
135 #else
136 return xstrdup(s);
137 #endif
138 }
139
140 static 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
150 str = xcalloc(n + 1, 1);
151 if (wcstombs(str, s, n) == (size_t) -1) {
152 free(str);
153 return NULL;
154 }
155 return str;
156 #else
157 return xstrdup(s);
158 #endif
159 }
160
161 static 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)
166 #ifdef HAVE_WIDECHAR
167 return wcstok(p, separator, state);
168 #else
169 return strtok_r(p, separator, state);
170 #endif
171 if (!p) {
172 if (!*state)
173 return NULL;
174 p = *state;
175 }
176 result = p;
177 #ifdef HAVE_WIDECHAR
178 p = wcspbrk(result, separator);
179 #else
180 p = strpbrk(result, separator);
181 #endif
182 if (!p)
183 *state = NULL;
184 else {
185 *p = '\0';
186 *state = p + 1;
187 }
188 return result;
189 }
190
191 static char **split_or_error(const char *str, const char *errmsg)
192 {
193 char **res = strv_split(str, ",");
194 if (!res) {
195 if (errno == ENOMEM)
196 err_oom();
197 errx(EXIT_FAILURE, "%s: '%s'", errmsg, str);
198 }
199 return res;
200 }
201
202 static 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);
211 if (ctl->json) {
212 scols_table_enable_json(ctl->tab, 1);
213 scols_table_set_name(ctl->tab, ctl->tab_name ? : "table");
214 } else
215 scols_table_enable_noencoding(ctl->tab, 1);
216
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);
222 if (ctl->header_repeat)
223 scols_table_enable_header_repeat(ctl->tab, 1);
224 scols_table_enable_noheadings(ctl->tab, !!ctl->tab_noheadings);
225 } else
226 scols_table_enable_noheadings(ctl->tab, 1);
227 }
228
229 static 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
250 static 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 }
268
269 static 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
276 static 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;
281 int unnamed = 0;
282
283 STRV_FOREACH(one, all) {
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);
291 if (cl)
292 column_set_flag(cl, flag);
293 }
294 strv_free(all);
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 }
313 }
314
315 static 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 }
335
336 free(wanted);
337 strv_free(order);
338 }
339
340 static 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;
377 if (strcmp(id, parent) != 0)
378 continue;
379 if (scols_line_is_ancestor(ln, ln_i))
380 continue;
381 scols_line_add_child(ln_i, ln);
382 }
383 }
384
385 scols_free_iter(itr_p);
386 scols_free_iter(itr_i);
387 }
388
389 static void modify_table(struct column_control *ctl)
390 {
391 scols_table_set_termwidth(ctl->tab, ctl->termwidth);
392 scols_table_set_termforce(ctl->tab, SCOLS_TERMFORCE_ALWAYS);
393
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
398 if (ctl->tab_coltrunc)
399 apply_columnflag_from_list(ctl, ctl->tab_coltrunc,
400 SCOLS_FL_TRUNC , _("failed to parse --table-trunc list"));
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"));
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
410 if (ctl->tab_colhide)
411 apply_columnflag_from_list(ctl, ctl->tab_colhide,
412 SCOLS_FL_HIDDEN , _("failed to parse --table-hide list"));
413
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
420 if (ctl->tree)
421 create_tree(ctl);
422
423 /* This must be the last step! */
424 if (ctl->tab_order)
425 reorder_table(ctl);
426 }
427
428
429 static 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
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);
447 scols_table_new_column(ctl->tab, NULL, 0, 0);
448 }
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 }
454
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
467 static 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
478 static int read_input(struct column_control *ctl, FILE *fp)
479 {
480 char *buf = NULL;
481 size_t bufsz = 0;
482 size_t maxents = 0;
483 int rc = 0;
484
485 /* Read input */
486 do {
487 char *str, *p;
488 wchar_t *wcs = NULL;
489 size_t len;
490
491 if (getline(&buf, &bufsz, fp) < 0) {
492 if (feof(fp))
493 break;
494 err(EXIT_FAILURE, _("read failed"));
495 }
496 str = (char *) skip_space(buf);
497 if (str) {
498 p = strchr(str, '\n');
499 if (p)
500 *p = '\0';
501 }
502 if (!str || !*str) {
503 if (ctl->mode == COLUMN_MODE_TABLE && ctl->tab_empty_lines)
504 add_emptyline_to_table(ctl);
505 continue;
506 }
507
508 wcs = mbs_to_wcs(buf);
509 if (!wcs) {
510 /*
511 * Convert broken sequences to \x<hex> and continue.
512 */
513 size_t tmpsz = 0;
514 char *tmp = mbs_invalid_encode(buf, &tmpsz);
515
516 if (!tmp)
517 err(EXIT_FAILURE, _("read failed"));
518 wcs = mbs_to_wcs(tmp);
519 free(tmp);
520 }
521
522 switch (ctl->mode) {
523 case COLUMN_MODE_TABLE:
524 rc = add_line_to_table(ctl, wcs);
525 free(wcs);
526 break;
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 }
535 ctl->ents[ctl->nents] = wcs;
536 len = width(ctl->ents[ctl->nents]);
537 if (ctl->maxlength < len)
538 ctl->maxlength = len;
539 ctl->nents++;
540 break;
541 default:
542 free(wcs);
543 break;
544 }
545 } while (rc == 0);
546
547 return rc;
548 }
549
550
551 static void columnate_fillrows(struct column_control *ctl)
552 {
553 size_t chcnt, col, cnt, endcol, numcols;
554 wchar_t **lp;
555
556 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
557 numcols = ctl->termwidth / ctl->maxlength;
558 endcol = ctl->maxlength;
559 for (chcnt = col = 0, lp = ctl->ents; /* nothing */; ++lp) {
560 fputws(*lp, stdout);
561 chcnt += width(*lp);
562 if (!--ctl->nents)
563 break;
564 if (++col == numcols) {
565 chcnt = col = 0;
566 endcol = ctl->maxlength;
567 putwchar('\n');
568 } else {
569 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
570 putwchar('\t');
571 chcnt = cnt;
572 }
573 endcol += ctl->maxlength;
574 }
575 }
576 if (chcnt)
577 putwchar('\n');
578 }
579
580 static void columnate_fillcols(struct column_control *ctl)
581 {
582 size_t base, chcnt, cnt, col, endcol, numcols, numrows, row;
583
584 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
585 numcols = ctl->termwidth / ctl->maxlength;
586 if (!numcols)
587 numcols = 1;
588 numrows = ctl->nents / numcols;
589 if (ctl->nents % numcols)
590 ++numrows;
591
592 for (row = 0; row < numrows; ++row) {
593 endcol = ctl->maxlength;
594 for (base = row, chcnt = col = 0; col < numcols; ++col) {
595 fputws(ctl->ents[base], stdout);
596 chcnt += width(ctl->ents[base]);
597 if ((base += numrows) >= ctl->nents)
598 break;
599 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
600 putwchar('\t');
601 chcnt = cnt;
602 }
603 endcol += ctl->maxlength;
604 }
605 putwchar('\n');
606 }
607 }
608
609 static void simple_print(struct column_control *ctl)
610 {
611 int cnt;
612 wchar_t **lp;
613
614 for (cnt = ctl->nents, lp = ctl->ents; cnt--; ++lp) {
615 fputws(*lp, stdout);
616 putwchar('\n');
617 }
618 }
619
620 static void __attribute__((__noreturn__)) usage(void)
621 {
622 FILE *out = stdout;
623
624 fputs(USAGE_HEADER, out);
625 fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
626
627 fputs(USAGE_SEPARATOR, out);
628 fputs(_("Columnate lists.\n"), out);
629
630 fputs(USAGE_OPTIONS, out);
631 fputs(_(" -t, --table create a table\n"), out);
632 fputs(_(" -n, --table-name <name> table name for JSON output\n"), out);
633 fputs(_(" -O, --table-order <columns> specify order of output columns\n"), out);
634 fputs(_(" -N, --table-columns <names> comma separated columns names\n"), out);
635 fputs(_(" -E, --table-noextreme <columns> don't count long text from the columns to column width\n"), out);
636 fputs(_(" -d, --table-noheadings don't print header\n"), out);
637 fputs(_(" -e, --table-header-repeat repeat header for each page\n"), out);
638 fputs(_(" -H, --table-hide <columns> don't print the columns\n"), out);
639 fputs(_(" -R, --table-right <columns> right align text in these columns\n"), out);
640 fputs(_(" -T, --table-truncate <columns> truncate text in the columns when necessary\n"), out);
641 fputs(_(" -W, --table-wrap <columns> wrap text in the columns when necessary\n"), out);
642 fputs(_(" -L, --table-empty-lines don't ignore empty lines\n"), out);
643 fputs(_(" -J, --json use JSON output format for table\n"), out);
644
645 fputs(USAGE_SEPARATOR, out);
646 fputs(_(" -r, --tree <column> column to use tree-like output for the table\n"), out);
647 fputs(_(" -i, --tree-id <column> line ID to specify child-parent relation\n"), out);
648 fputs(_(" -p, --tree-parent <column> parent to specify child-parent relation\n"), out);
649
650 fputs(USAGE_SEPARATOR, out);
651 fputs(_(" -c, --output-width <width> width of output in number of characters\n"), out);
652 fputs(_(" -o, --output-separator <string> columns separator for table output (default is two spaces)\n"), out);
653 fputs(_(" -s, --separator <string> possible table delimiters\n"), out);
654 fputs(_(" -x, --fillrows fill rows before columns\n"), out);
655
656
657 fputs(USAGE_SEPARATOR, out);
658 printf(USAGE_HELP_OPTIONS(34));
659 printf(USAGE_MAN_TAIL("column(1)"));
660
661 exit(EXIT_SUCCESS);
662 }
663
664 int main(int argc, char **argv)
665 {
666 struct column_control ctl = {
667 .mode = COLUMN_MODE_FILLCOLS,
668 .greedy = 1,
669 .termwidth = (size_t) -1
670 };
671
672 int c;
673 unsigned int eval = 0; /* exit value */
674
675 static const struct option longopts[] =
676 {
677 { "columns", required_argument, NULL, 'c' }, /* deprecated */
678 { "fillrows", no_argument, NULL, 'x' },
679 { "help", no_argument, NULL, 'h' },
680 { "json", no_argument, NULL, 'J' },
681 { "output-separator", required_argument, NULL, 'o' },
682 { "output-width", required_argument, NULL, 'c' },
683 { "separator", required_argument, NULL, 's' },
684 { "table", no_argument, NULL, 't' },
685 { "table-columns", required_argument, NULL, 'N' },
686 { "table-hide", required_argument, NULL, 'H' },
687 { "table-name", required_argument, NULL, 'n' },
688 { "table-noextreme", required_argument, NULL, 'E' },
689 { "table-noheadings", no_argument, NULL, 'd' },
690 { "table-order", required_argument, NULL, 'O' },
691 { "table-right", required_argument, NULL, 'R' },
692 { "table-truncate", required_argument, NULL, 'T' },
693 { "table-wrap", required_argument, NULL, 'W' },
694 { "table-empty-lines", no_argument, NULL, 'L' },
695 { "table-header-repeat", no_argument, NULL, 'e' },
696 { "tree", required_argument, NULL, 'r' },
697 { "tree-id", required_argument, NULL, 'i' },
698 { "tree-parent", required_argument, NULL, 'p' },
699 { "version", no_argument, NULL, 'V' },
700 { NULL, 0, NULL, 0 },
701 };
702 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
703 { 'J','x' },
704 { 't','x' },
705 { 0 }
706 };
707 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
708
709 setlocale(LC_ALL, "");
710 bindtextdomain(PACKAGE, LOCALEDIR);
711 textdomain(PACKAGE);
712 close_stdout_atexit();
713
714 ctl.output_separator = " ";
715 ctl.input_separator = mbs_to_wcs("\t ");
716
717 while ((c = getopt_long(argc, argv, "c:dE:eH:hi:JLN:n:O:o:p:R:r:s:T:tVW:x", longopts, NULL)) != -1) {
718
719 err_exclusive_options(c, longopts, excl, excl_st);
720
721 switch(c) {
722 case 'c':
723 ctl.termwidth = strtou32_or_err(optarg, _("invalid columns argument"));
724 break;
725 case 'd':
726 ctl.tab_noheadings = 1;
727 break;
728 case 'E':
729 ctl.tab_colnoextrem = optarg;
730 break;
731 case 'e':
732 ctl.header_repeat = 1;
733 break;
734 case 'H':
735 ctl.tab_colhide = optarg;
736 break;
737 case 'i':
738 ctl.tree_id = optarg;
739 break;
740 case 'J':
741 ctl.json = 1;
742 ctl.mode = COLUMN_MODE_TABLE;
743 break;
744 case 'L':
745 ctl.tab_empty_lines = 1;
746 break;
747 case 'N':
748 ctl.tab_colnames = split_or_error(optarg, _("failed to parse column names"));
749 break;
750 case 'n':
751 ctl.tab_name = optarg;
752 break;
753 case 'O':
754 ctl.tab_order = optarg;
755 break;
756 case 'o':
757 ctl.output_separator = optarg;
758 break;
759 case 'p':
760 ctl.tree_parent = optarg;
761 break;
762 case 'R':
763 ctl.tab_colright = optarg;
764 break;
765 case 'r':
766 ctl.tree = optarg;
767 break;
768 case 's':
769 free(ctl.input_separator);
770 ctl.input_separator = mbs_to_wcs(optarg);
771 ctl.greedy = 0;
772 break;
773 case 'T':
774 ctl.tab_coltrunc = optarg;
775 break;
776 case 't':
777 ctl.mode = COLUMN_MODE_TABLE;
778 break;
779 case 'W':
780 ctl.tab_colwrap = optarg;
781 break;
782 case 'x':
783 ctl.mode = COLUMN_MODE_FILLROWS;
784 break;
785
786 case 'h':
787 usage();
788 case 'V':
789 print_version(EXIT_SUCCESS);
790 default:
791 errtryhelp(EXIT_FAILURE);
792 }
793 }
794 argc -= optind;
795 argv += optind;
796
797 if (ctl.termwidth == (size_t) -1)
798 ctl.termwidth = get_terminal_width(80);
799
800 if (ctl.tree) {
801 ctl.mode = COLUMN_MODE_TABLE;
802 if (!ctl.tree_parent || !ctl.tree_id)
803 errx(EXIT_FAILURE, _("options --tree-id and --tree-parent are "
804 "required for tree formatting"));
805 }
806
807 if (ctl.mode != COLUMN_MODE_TABLE
808 && (ctl.tab_order || ctl.tab_name || ctl.tab_colwrap ||
809 ctl.tab_colhide || ctl.tab_coltrunc || ctl.tab_colnoextrem ||
810 ctl.tab_colright || ctl.tab_colnames))
811 errx(EXIT_FAILURE, _("option --table required for all --table-*"));
812
813 if (ctl.tab_colnames == NULL && ctl.json)
814 errx(EXIT_FAILURE, _("option --table-columns required for --json"));
815
816 if (!*argv)
817 eval += read_input(&ctl, stdin);
818 else
819 for (; *argv; ++argv) {
820 FILE *fp;
821
822 if ((fp = fopen(*argv, "r")) != NULL) {
823 eval += read_input(&ctl, fp);
824 fclose(fp);
825 } else {
826 warn("%s", *argv);
827 eval += EXIT_FAILURE;
828 }
829 }
830
831 if (ctl.mode != COLUMN_MODE_TABLE) {
832 if (!ctl.nents)
833 exit(eval);
834 if (ctl.maxlength >= ctl.termwidth)
835 ctl.mode = COLUMN_MODE_SIMPLE;
836 }
837
838 switch (ctl.mode) {
839 case COLUMN_MODE_TABLE:
840 if (ctl.tab && scols_table_get_nlines(ctl.tab)) {
841 modify_table(&ctl);
842 eval = scols_print_table(ctl.tab);
843 }
844 break;
845 case COLUMN_MODE_FILLCOLS:
846 columnate_fillcols(&ctl);
847 break;
848 case COLUMN_MODE_FILLROWS:
849 columnate_fillrows(&ctl);
850 break;
851 case COLUMN_MODE_SIMPLE:
852 simple_print(&ctl);
853 break;
854 }
855
856 return eval == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
857 }