]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/column.c
column: pass control struct to local_wcstok()
[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(struct column_control const *const ctl, wchar_t *p,
162 wchar_t **state)
163 {
164 wchar_t *result = NULL;
165
166 if (ctl->greedy)
167 #ifdef HAVE_WIDECHAR
168 return wcstok(p, ctl->input_separator, state);
169 #else
170 return strtok_r(p, ctl->input_separator, state);
171 #endif
172 if (!p) {
173 if (!*state)
174 return NULL;
175 p = *state;
176 }
177 result = p;
178 #ifdef HAVE_WIDECHAR
179 p = wcspbrk(result, ctl->input_separator);
180 #else
181 p = strpbrk(result, ctl->input_separator);
182 #endif
183 if (!p)
184 *state = NULL;
185 else {
186 *p = '\0';
187 *state = p + 1;
188 }
189 return result;
190 }
191
192 static char **split_or_error(const char *str, const char *errmsg)
193 {
194 char **res = strv_split(str, ",");
195 if (!res) {
196 if (errno == ENOMEM)
197 err_oom();
198 errx(EXIT_FAILURE, "%s: '%s'", errmsg, str);
199 }
200 return res;
201 }
202
203 static 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);
212 if (ctl->json) {
213 scols_table_enable_json(ctl->tab, 1);
214 scols_table_set_name(ctl->tab, ctl->tab_name ? : "table");
215 } else
216 scols_table_enable_noencoding(ctl->tab, 1);
217
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);
223 if (ctl->header_repeat)
224 scols_table_enable_header_repeat(ctl->tab, 1);
225 scols_table_enable_noheadings(ctl->tab, !!ctl->tab_noheadings);
226 } else
227 scols_table_enable_noheadings(ctl->tab, 1);
228 }
229
230 static 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
251 static 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 }
269
270 static 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
277 static 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;
282 int unnamed = 0;
283
284 STRV_FOREACH(one, all) {
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);
292 if (cl)
293 column_set_flag(cl, flag);
294 }
295 strv_free(all);
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 }
314 }
315
316 static 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 }
336
337 free(wanted);
338 strv_free(order);
339 }
340
341 static 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;
378 if (strcmp(id, parent) != 0)
379 continue;
380 if (scols_line_is_ancestor(ln, ln_i))
381 continue;
382 scols_line_add_child(ln_i, ln);
383 }
384 }
385
386 scols_free_iter(itr_p);
387 scols_free_iter(itr_i);
388 }
389
390 static void modify_table(struct column_control *ctl)
391 {
392 scols_table_set_termwidth(ctl->tab, ctl->termwidth);
393 scols_table_set_termforce(ctl->tab, SCOLS_TERMFORCE_ALWAYS);
394
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
399 if (ctl->tab_coltrunc)
400 apply_columnflag_from_list(ctl, ctl->tab_coltrunc,
401 SCOLS_FL_TRUNC , _("failed to parse --table-trunc list"));
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"));
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
411 if (ctl->tab_colhide)
412 apply_columnflag_from_list(ctl, ctl->tab_colhide,
413 SCOLS_FL_HIDDEN , _("failed to parse --table-hide list"));
414
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
421 if (ctl->tree)
422 create_tree(ctl);
423
424 /* This must be the last step! */
425 if (ctl->tab_order)
426 reorder_table(ctl);
427 }
428
429
430 static 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
439 while ((wcdata = local_wcstok(ctl, wcs, &sv))) {
440 char *data;
441
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);
448 scols_table_new_column(ctl->tab, NULL, 0, 0);
449 }
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 }
455
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
468 static 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
479 static int read_input(struct column_control *ctl, FILE *fp)
480 {
481 char *buf = NULL;
482 size_t bufsz = 0;
483 size_t maxents = 0;
484 int rc = 0;
485
486 /* Read input */
487 do {
488 char *str, *p;
489 wchar_t *wcs = NULL;
490 size_t len;
491
492 if (getline(&buf, &bufsz, fp) < 0) {
493 if (feof(fp))
494 break;
495 err(EXIT_FAILURE, _("read failed"));
496 }
497 str = (char *) skip_space(buf);
498 if (str) {
499 p = strchr(str, '\n');
500 if (p)
501 *p = '\0';
502 }
503 if (!str || !*str) {
504 if (ctl->mode == COLUMN_MODE_TABLE && ctl->tab_empty_lines)
505 add_emptyline_to_table(ctl);
506 continue;
507 }
508
509 wcs = mbs_to_wcs(buf);
510 if (!wcs) {
511 /*
512 * Convert broken sequences to \x<hex> and continue.
513 */
514 size_t tmpsz = 0;
515 char *tmp = mbs_invalid_encode(buf, &tmpsz);
516
517 if (!tmp)
518 err(EXIT_FAILURE, _("read failed"));
519 wcs = mbs_to_wcs(tmp);
520 free(tmp);
521 }
522
523 switch (ctl->mode) {
524 case COLUMN_MODE_TABLE:
525 rc = add_line_to_table(ctl, wcs);
526 free(wcs);
527 break;
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 }
536 ctl->ents[ctl->nents] = wcs;
537 len = width(ctl->ents[ctl->nents]);
538 if (ctl->maxlength < len)
539 ctl->maxlength = len;
540 ctl->nents++;
541 break;
542 default:
543 free(wcs);
544 break;
545 }
546 } while (rc == 0);
547
548 return rc;
549 }
550
551
552 static void columnate_fillrows(struct column_control *ctl)
553 {
554 size_t chcnt, col, cnt, endcol, numcols;
555 wchar_t **lp;
556
557 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
558 numcols = ctl->termwidth / ctl->maxlength;
559 endcol = ctl->maxlength;
560 for (chcnt = col = 0, lp = ctl->ents; /* nothing */; ++lp) {
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 {
570 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
571 putwchar('\t');
572 chcnt = cnt;
573 }
574 endcol += ctl->maxlength;
575 }
576 }
577 if (chcnt)
578 putwchar('\n');
579 }
580
581 static void columnate_fillcols(struct column_control *ctl)
582 {
583 size_t base, chcnt, cnt, col, endcol, numcols, numrows, row;
584
585 ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
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;
600 while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
601 putwchar('\t');
602 chcnt = cnt;
603 }
604 endcol += ctl->maxlength;
605 }
606 putwchar('\n');
607 }
608 }
609
610 static 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
621 static void __attribute__((__noreturn__)) usage(void)
622 {
623 FILE *out = stdout;
624
625 fputs(USAGE_HEADER, out);
626 fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
627
628 fputs(USAGE_SEPARATOR, out);
629 fputs(_("Columnate lists.\n"), out);
630
631 fputs(USAGE_OPTIONS, out);
632 fputs(_(" -t, --table create a table\n"), out);
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);
635 fputs(_(" -N, --table-columns <names> comma separated columns names\n"), out);
636 fputs(_(" -E, --table-noextreme <columns> don't count long text from the columns to column width\n"), out);
637 fputs(_(" -d, --table-noheadings don't print header\n"), out);
638 fputs(_(" -e, --table-header-repeat repeat header for each page\n"), out);
639 fputs(_(" -H, --table-hide <columns> don't print the columns\n"), out);
640 fputs(_(" -R, --table-right <columns> right align text in these columns\n"), out);
641 fputs(_(" -T, --table-truncate <columns> truncate text in the columns when necessary\n"), out);
642 fputs(_(" -W, --table-wrap <columns> wrap text in the columns when necessary\n"), out);
643 fputs(_(" -L, --table-empty-lines don't ignore empty lines\n"), out);
644 fputs(_(" -J, --json use JSON output format for table\n"), out);
645
646 fputs(USAGE_SEPARATOR, out);
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
651 fputs(USAGE_SEPARATOR, out);
652 fputs(_(" -c, --output-width <width> width of output in number of characters\n"), out);
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);
655 fputs(_(" -x, --fillrows fill rows before columns\n"), out);
656
657
658 fputs(USAGE_SEPARATOR, out);
659 printf(USAGE_HELP_OPTIONS(34));
660 printf(USAGE_MAN_TAIL("column(1)"));
661
662 exit(EXIT_SUCCESS);
663 }
664
665 int main(int argc, char **argv)
666 {
667 struct column_control ctl = {
668 .mode = COLUMN_MODE_FILLCOLS,
669 .greedy = 1,
670 .termwidth = (size_t) -1
671 };
672
673 int c;
674 unsigned int eval = 0; /* exit value */
675
676 static const struct option longopts[] =
677 {
678 { "columns", required_argument, NULL, 'c' }, /* deprecated */
679 { "fillrows", no_argument, NULL, 'x' },
680 { "help", no_argument, NULL, 'h' },
681 { "json", no_argument, NULL, 'J' },
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' },
686 { "table-columns", required_argument, NULL, 'N' },
687 { "table-hide", required_argument, NULL, 'H' },
688 { "table-name", required_argument, NULL, 'n' },
689 { "table-noextreme", required_argument, NULL, 'E' },
690 { "table-noheadings", no_argument, NULL, 'd' },
691 { "table-order", required_argument, NULL, 'O' },
692 { "table-right", required_argument, NULL, 'R' },
693 { "table-truncate", required_argument, NULL, 'T' },
694 { "table-wrap", required_argument, NULL, 'W' },
695 { "table-empty-lines", no_argument, NULL, 'L' },
696 { "table-header-repeat", no_argument, NULL, 'e' },
697 { "tree", required_argument, NULL, 'r' },
698 { "tree-id", required_argument, NULL, 'i' },
699 { "tree-parent", required_argument, NULL, 'p' },
700 { "version", no_argument, NULL, 'V' },
701 { NULL, 0, NULL, 0 },
702 };
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;
709
710 setlocale(LC_ALL, "");
711 bindtextdomain(PACKAGE, LOCALEDIR);
712 textdomain(PACKAGE);
713 close_stdout_atexit();
714
715 ctl.output_separator = " ";
716 ctl.input_separator = mbs_to_wcs("\t ");
717
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) {
719
720 err_exclusive_options(c, longopts, excl, excl_st);
721
722 switch(c) {
723 case 'c':
724 ctl.termwidth = strtou32_or_err(optarg, _("invalid columns argument"));
725 break;
726 case 'd':
727 ctl.tab_noheadings = 1;
728 break;
729 case 'E':
730 ctl.tab_colnoextrem = optarg;
731 break;
732 case 'e':
733 ctl.header_repeat = 1;
734 break;
735 case 'H':
736 ctl.tab_colhide = optarg;
737 break;
738 case 'i':
739 ctl.tree_id = optarg;
740 break;
741 case 'J':
742 ctl.json = 1;
743 ctl.mode = COLUMN_MODE_TABLE;
744 break;
745 case 'L':
746 ctl.tab_empty_lines = 1;
747 break;
748 case 'N':
749 ctl.tab_colnames = split_or_error(optarg, _("failed to parse column names"));
750 break;
751 case 'n':
752 ctl.tab_name = optarg;
753 break;
754 case 'O':
755 ctl.tab_order = optarg;
756 break;
757 case 'o':
758 ctl.output_separator = optarg;
759 break;
760 case 'p':
761 ctl.tree_parent = optarg;
762 break;
763 case 'R':
764 ctl.tab_colright = optarg;
765 break;
766 case 'r':
767 ctl.tree = optarg;
768 break;
769 case 's':
770 free(ctl.input_separator);
771 ctl.input_separator = mbs_to_wcs(optarg);
772 ctl.greedy = 0;
773 break;
774 case 'T':
775 ctl.tab_coltrunc = optarg;
776 break;
777 case 't':
778 ctl.mode = COLUMN_MODE_TABLE;
779 break;
780 case 'W':
781 ctl.tab_colwrap = optarg;
782 break;
783 case 'x':
784 ctl.mode = COLUMN_MODE_FILLROWS;
785 break;
786
787 case 'h':
788 usage();
789 case 'V':
790 print_version(EXIT_SUCCESS);
791 default:
792 errtryhelp(EXIT_FAILURE);
793 }
794 }
795 argc -= optind;
796 argv += optind;
797
798 if (ctl.termwidth == (size_t) -1)
799 ctl.termwidth = get_terminal_width(80);
800
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
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
814 if (ctl.tab_colnames == NULL && ctl.json)
815 errx(EXIT_FAILURE, _("option --table-columns required for --json"));
816
817 if (!*argv)
818 eval += read_input(&ctl, stdin);
819 else
820 for (; *argv; ++argv) {
821 FILE *fp;
822
823 if ((fp = fopen(*argv, "r")) != NULL) {
824 eval += read_input(&ctl, fp);
825 fclose(fp);
826 } else {
827 warn("%s", *argv);
828 eval += EXIT_FAILURE;
829 }
830 }
831
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 }
838
839 switch (ctl.mode) {
840 case COLUMN_MODE_TABLE:
841 if (ctl.tab && scols_table_get_nlines(ctl.tab)) {
842 modify_table(&ctl);
843 eval = scols_print_table(ctl.tab);
844 }
845 break;
846 case COLUMN_MODE_FILLCOLS:
847 columnate_fillcols(&ctl);
848 break;
849 case COLUMN_MODE_FILLROWS:
850 columnate_fillrows(&ctl);
851 break;
852 case COLUMN_MODE_SIMPLE:
853 simple_print(&ctl);
854 break;
855 }
856
857 return eval == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
858 }