(root)/
util-linux-2.39/
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;	/* -1 uninilialized, 0 unlimited, >0 width (default is 80) */
      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  	char **tab_columns;	/* array from --table-column */
      79  
      80  	const char *tab_colright;	/* --table-right */
      81  	const char *tab_coltrunc;	/* --table-trunc */
      82  	const char *tab_colnoextrem;	/* --table-noextreme */
      83  	const char *tab_colwrap;	/* --table-wrap */
      84  	const char *tab_colhide;	/* --table-hide */
      85  
      86  	const char *tree;
      87  	const char *tree_id;
      88  	const char *tree_parent;
      89  
      90  	wchar_t *input_separator;
      91  	const char *output_separator;
      92  
      93  	wchar_t	**ents;		/* input entries */
      94  	size_t	nents;		/* number of entries */
      95  	size_t	maxlength;	/* longest input record (line) */
      96  	size_t  maxncols;	/* maximal number of input columns */
      97  
      98  	unsigned int greedy :1,
      99  		     json :1,
     100  		     header_repeat :1,
     101  		     hide_unnamed :1,
     102  		     maxout : 1,
     103  		     keep_empty_lines :1,	/* --keep-empty-lines */
     104  		     tab_noheadings :1;
     105  };
     106  
     107  static size_t width(const wchar_t *str)
     108  {
     109  	size_t width = 0;
     110  
     111  	for (; *str != '\0'; str++) {
     112  #ifdef HAVE_WIDECHAR
     113  		int x = wcwidth(*str);	/* don't use wcswidth(), need to ignore non-printable */
     114  		if (x > 0)
     115  			width += x;
     116  #else
     117  		if (isprint(*str))
     118  			width++;
     119  #endif
     120  	}
     121  	return width;
     122  }
     123  
     124  static wchar_t *mbs_to_wcs(const char *s)
     125  {
     126  #ifdef HAVE_WIDECHAR
     127  	ssize_t n;
     128  	wchar_t *wcs;
     129  
     130  	n = mbstowcs((wchar_t *)0, s, 0);
     131  	if (n < 0)
     132  		return NULL;
     133  	wcs = xcalloc((n + 1) * sizeof(wchar_t), 1);
     134  	n = mbstowcs(wcs, s, n + 1);
     135  	if (n < 0) {
     136  		free(wcs);
     137  		return NULL;
     138  	}
     139  	return wcs;
     140  #else
     141  	return xstrdup(s);
     142  #endif
     143  }
     144  
     145  static char *wcs_to_mbs(const wchar_t *s)
     146  {
     147  #ifdef HAVE_WIDECHAR
     148  	size_t n;
     149  	char *str;
     150  
     151  	n = wcstombs(NULL, s, 0);
     152  	if (n == (size_t) -1)
     153  		return NULL;
     154  
     155  	str = xcalloc(n + 1, 1);
     156  	if (wcstombs(str, s, n) == (size_t) -1) {
     157  		free(str);
     158  		return NULL;
     159  	}
     160  	return str;
     161  #else
     162  	return xstrdup(s);
     163  #endif
     164  }
     165  
     166  static wchar_t *local_wcstok(struct column_control const *const ctl, wchar_t *p,
     167  			     wchar_t **state)
     168  {
     169  	wchar_t *result = NULL;
     170  
     171  	if (ctl->greedy)
     172  #ifdef HAVE_WIDECHAR
     173  		return wcstok(p, ctl->input_separator, state);
     174  #else
     175  		return strtok_r(p, ctl->input_separator, state);
     176  #endif
     177  	if (!p) {
     178  		if (!*state)
     179  			return NULL;
     180  		p = *state;
     181  	}
     182  	result = p;
     183  #ifdef HAVE_WIDECHAR
     184  	p = wcspbrk(result, ctl->input_separator);
     185  #else
     186  	p = strpbrk(result, ctl->input_separator);
     187  #endif
     188  	if (!p)
     189  		*state = NULL;
     190  	else {
     191  		*p = '\0';
     192  		*state = p + 1;
     193  	}
     194  	return result;
     195  }
     196  
     197  static char **split_or_error(const char *str, const char *errmsg)
     198  {
     199  	char **res = strv_split(str, ",");
     200  	if (!res) {
     201  		if (errno == ENOMEM)
     202  			err_oom();
     203  		if (errmsg)
     204  			errx(EXIT_FAILURE, "%s: '%s'", errmsg, str);
     205  		else
     206  			return NULL;
     207  	}
     208  	return res;
     209  }
     210  
     211  static void init_table(struct column_control *ctl)
     212  {
     213  	scols_init_debug(0);
     214  
     215  	ctl->tab = scols_new_table();
     216  	if (!ctl->tab)
     217  		err(EXIT_FAILURE, _("failed to allocate output table"));
     218  
     219  	scols_table_set_column_separator(ctl->tab, ctl->output_separator);
     220  	if (ctl->json) {
     221  		scols_table_enable_json(ctl->tab, 1);
     222  		scols_table_set_name(ctl->tab, ctl->tab_name ? : "table");
     223  	} else
     224  		scols_table_enable_noencoding(ctl->tab, 1);
     225  
     226  	scols_table_enable_maxout(ctl->tab, ctl->maxout ? 1 : 0);
     227  
     228  	if (ctl->tab_columns) {
     229  		char **opts;
     230  
     231  		STRV_FOREACH(opts, ctl->tab_columns) {
     232  			struct libscols_column *cl;
     233  
     234  			cl = scols_table_new_column(ctl->tab, NULL, 0, 0);
     235  			scols_column_set_properties(cl, *opts);
     236  		}
     237  
     238  	} else if (ctl->tab_colnames) {
     239  		char **name;
     240  
     241  		STRV_FOREACH(name, ctl->tab_colnames)
     242  			scols_table_new_column(ctl->tab, *name, 0, 0);
     243  	} else
     244  		scols_table_enable_noheadings(ctl->tab, 1);
     245  
     246  	if (ctl->tab_colnames || ctl->tab_columns) {
     247  		if (ctl->header_repeat)
     248  			scols_table_enable_header_repeat(ctl->tab, 1);
     249  		scols_table_enable_noheadings(ctl->tab, !!ctl->tab_noheadings);
     250  	}
     251  
     252  }
     253  
     254  static struct libscols_column *get_last_visible_column(struct column_control *ctl, int n)
     255  {
     256  	struct libscols_iter *itr;
     257  	struct libscols_column *cl, *res = NULL;
     258  
     259  	itr = scols_new_iter(SCOLS_ITER_BACKWARD);
     260  	if (!itr)
     261  		err_oom();
     262  
     263  	while (scols_table_next_column(ctl->tab, itr, &cl) == 0) {
     264  		if (scols_column_get_flags(cl) & SCOLS_FL_HIDDEN)
     265  			continue;
     266  		if (n == 0) {
     267  			res = cl;
     268  			break;
     269  		}
     270  		n--;
     271  	}
     272  
     273  	scols_free_iter(itr);
     274  	return res;
     275  }
     276  
     277  static struct libscols_column *string_to_column(struct column_control *ctl, const char *str)
     278  {
     279  	struct libscols_column *cl;
     280  
     281  	if (isdigit_string(str)) {
     282  		uint32_t n = strtou32_or_err(str, _("failed to parse column")) - 1;
     283  
     284  		cl = scols_table_get_column(ctl->tab, n);
     285  	} else if (strcmp(str, "-1") == 0)
     286  		cl = get_last_visible_column(ctl, 0);
     287  	else
     288  		cl = scols_table_get_column_by_name(ctl->tab, str);
     289  
     290  	if (!cl)
     291  		errx(EXIT_FAILURE, _("undefined column name '%s'"), str);
     292  
     293  	return cl;
     294  }
     295  
     296  static int column_set_flag(struct libscols_column *cl, int fl)
     297  {
     298  	int cur = scols_column_get_flags(cl);
     299  
     300  	return scols_column_set_flags(cl, cur | fl);
     301  }
     302  
     303  static int has_unnamed(const char *list)
     304  {
     305  	char **all, **one;
     306  	int rc = 0;
     307  
     308  	if (!list)
     309  		return 0;
     310  	if (strcmp(list, "-") == 0)
     311  		return 1;
     312  	if (!strchr(list, ','))
     313  		return 0;
     314  
     315  	all = split_or_error(list, NULL);
     316  	if (all) {
     317  		STRV_FOREACH(one, all) {
     318  			if (strcmp(*one, "-") == 0) {
     319  				rc = 1;
     320  				break;
     321  			}
     322  		}
     323  		strv_free(all);
     324  	}
     325  
     326  	return rc;
     327  }
     328  
     329  static void apply_columnflag_from_list(struct column_control *ctl, const char *list,
     330  					 int flag, const char *errmsg)
     331  {
     332  	char **all;
     333  	char **one;
     334  	int unnamed = 0;
     335  	struct libscols_column *cl;
     336  
     337  	/* apply to all */
     338  	if (list && strcmp(list, "0") == 0) {
     339  		struct libscols_iter *itr;
     340  
     341  		itr = scols_new_iter(SCOLS_ITER_FORWARD);
     342  		if (!itr)
     343  			err_oom();
     344  
     345  		while (scols_table_next_column(ctl->tab, itr, &cl) == 0)
     346  			column_set_flag(cl, flag);
     347  		scols_free_iter(itr);
     348  		return;
     349  	}
     350  
     351  	all = split_or_error(list, errmsg);
     352  
     353  	/* apply to columns specified by name */
     354  	STRV_FOREACH(one, all) {
     355  		int low = 0, up = 0;
     356  
     357  		if (strcmp(*one, "-") == 0) {
     358  			unnamed = 1;
     359  			continue;
     360  		}
     361  
     362  		/* parse range (N-M) */
     363  		if (strchr(*one, '-') && parse_range(*one, &low, &up, 0) == 0) {
     364  			for (; low <= up; low++) {
     365  				if (low < 0)
     366  					cl = get_last_visible_column(ctl, (low * -1) -1);
     367  				else
     368  					cl = scols_table_get_column(ctl->tab, low-1);
     369  				if (cl)
     370  					column_set_flag(cl, flag);
     371  			}
     372  			continue;
     373  		}
     374  
     375  		/* one item in the list */
     376  		cl = string_to_column(ctl, *one);
     377  		if (cl)
     378  			column_set_flag(cl, flag);
     379  	}
     380  	strv_free(all);
     381  
     382  	/* apply flag to all columns without name */
     383  	if (unnamed) {
     384  		struct libscols_iter *itr;
     385  
     386  		itr = scols_new_iter(SCOLS_ITER_FORWARD);
     387  		if (!itr)
     388  			err_oom();
     389  
     390  		while (scols_table_next_column(ctl->tab, itr, &cl) == 0) {
     391  			if (!scols_column_get_name(cl))
     392  				column_set_flag(cl, flag);
     393  		}
     394  		scols_free_iter(itr);
     395  	}
     396  }
     397  
     398  static void reorder_table(struct column_control *ctl)
     399  {
     400  	struct libscols_column **wanted, *last = NULL;
     401  	size_t i, count = 0;
     402  	size_t ncols = scols_table_get_ncols(ctl->tab);
     403  	char **order = split_or_error(ctl->tab_order, _("failed to parse --table-order list"));
     404  	char **one;
     405  
     406  	wanted = xcalloc(ncols, sizeof(struct libscols_column *));
     407  
     408  	STRV_FOREACH(one, order) {
     409  		struct libscols_column *cl = string_to_column(ctl, *one);
     410  		if (cl)
     411  			wanted[count++] = cl;
     412  	}
     413  
     414  	for (i = 0; i < count; i++) {
     415  		scols_table_move_column(ctl->tab, last, wanted[i]);
     416  		last = wanted[i];
     417  	}
     418  
     419  	free(wanted);
     420  	strv_free(order);
     421  }
     422  
     423  static void create_tree(struct column_control *ctl)
     424  {
     425  	struct libscols_column *cl_tree = string_to_column(ctl, ctl->tree);
     426  	struct libscols_column *cl_p = string_to_column(ctl, ctl->tree_parent);
     427  	struct libscols_column *cl_i = string_to_column(ctl, ctl->tree_id);
     428  	struct libscols_iter *itr_p, *itr_i;
     429  	struct libscols_line *ln_i;
     430  
     431  	if (!cl_p || !cl_i || !cl_tree)
     432  		return;			/* silently ignore the tree request */
     433  
     434  	column_set_flag(cl_tree, SCOLS_FL_TREE);
     435  
     436  	itr_p = scols_new_iter(SCOLS_ITER_FORWARD);
     437  	itr_i = scols_new_iter(SCOLS_ITER_FORWARD);
     438  	if (!itr_p || !itr_i)
     439  		err_oom();
     440  
     441  	/* scan all lines for ID */
     442  	while (scols_table_next_line(ctl->tab, itr_i, &ln_i) == 0) {
     443  		struct libscols_line *ln;
     444  		struct libscols_cell *ce = scols_line_get_column_cell(ln_i, cl_i);
     445  		const char *id = ce ? scols_cell_get_data(ce) : NULL;
     446  
     447  		if (!id)
     448  			continue;
     449  
     450  		/* see if the ID is somewhere used in parent column */
     451  		scols_reset_iter(itr_p, SCOLS_ITER_FORWARD);
     452  		while (scols_table_next_line(ctl->tab, itr_p, &ln) == 0) {
     453  			const char *parent;
     454  
     455  			ce = scols_line_get_column_cell(ln, cl_p);
     456  			parent = ce ? scols_cell_get_data(ce) : NULL;
     457  
     458  			if (!parent)
     459  				continue;
     460  			if (strcmp(id, parent) != 0)
     461  				continue;
     462  			if (scols_line_is_ancestor(ln, ln_i))
     463  				continue;
     464  			scols_line_add_child(ln_i, ln);
     465  		}
     466  	}
     467  
     468  	scols_free_iter(itr_p);
     469  	scols_free_iter(itr_i);
     470  }
     471  
     472  static void modify_table(struct column_control *ctl)
     473  {
     474  	if (ctl->termwidth > 0) {
     475  		scols_table_set_termwidth(ctl->tab, ctl->termwidth);
     476  		scols_table_set_termforce(ctl->tab, SCOLS_TERMFORCE_ALWAYS);
     477  	}
     478  
     479  	if (ctl->tab_colhide)
     480  		apply_columnflag_from_list(ctl, ctl->tab_colhide,
     481  				SCOLS_FL_HIDDEN , _("failed to parse --table-hide list"));
     482  
     483  	if (ctl->tab_colright)
     484  		apply_columnflag_from_list(ctl, ctl->tab_colright,
     485  				SCOLS_FL_RIGHT, _("failed to parse --table-right list"));
     486  
     487  	if (ctl->tab_coltrunc)
     488  		apply_columnflag_from_list(ctl, ctl->tab_coltrunc,
     489  				SCOLS_FL_TRUNC , _("failed to parse --table-trunc list"));
     490  
     491  	if (ctl->tab_colnoextrem)
     492  		apply_columnflag_from_list(ctl, ctl->tab_colnoextrem,
     493  				SCOLS_FL_NOEXTREMES , _("failed to parse --table-noextreme list"));
     494  
     495  	if (ctl->tab_colwrap)
     496  		apply_columnflag_from_list(ctl, ctl->tab_colwrap,
     497  				SCOLS_FL_WRAP , _("failed to parse --table-wrap list"));
     498  
     499  	if (!ctl->tab_colnoextrem) {
     500  		struct libscols_column *cl = get_last_visible_column(ctl, 0);
     501  		if (cl)
     502  			column_set_flag(cl, SCOLS_FL_NOEXTREMES);
     503  	}
     504  
     505  	if (ctl->tree)
     506  		create_tree(ctl);
     507  
     508  	/* This must be the last step! */
     509  	if (ctl->tab_order)
     510  		reorder_table(ctl);
     511  }
     512  
     513  
     514  static int add_line_to_table(struct column_control *ctl, wchar_t *wcs0)
     515  {
     516  	wchar_t *wcdata, *sv = NULL, *wcs = wcs0;
     517  	size_t n = 0, nchars = 0, skip = 0, len;
     518  	struct libscols_line *ln = NULL;
     519  
     520  	if (!ctl->tab)
     521  		init_table(ctl);
     522  
     523  	len = wcslen(wcs0);
     524  
     525  	do {
     526  		char *data;
     527  
     528  		if (ctl->maxncols && n + 1 == ctl->maxncols) {
     529  			if (nchars + skip < len)
     530  				wcdata = wcs0 + (nchars + skip);
     531  			else
     532  				wcdata = NULL;
     533  		} else {
     534  			wcdata = local_wcstok(ctl, wcs, &sv);
     535  
     536  			/* For the default separator ('greedy' mode) it uses
     537  			 * strtok() and it skips leading white chars. In this
     538  			 * case we need to remember size of the ignored white
     539  			 * chars due to wcdata calculation in maxncols case */
     540  			if (wcdata && ctl->greedy
     541  			    && n == 0 && nchars == 0 && wcdata > wcs)
     542  				skip = wcdata - wcs;
     543  		}
     544  
     545  		if (!wcdata)
     546  			break;
     547  		if (scols_table_get_ncols(ctl->tab) < n + 1) {
     548  			if (scols_table_is_json(ctl->tab) && !ctl->hide_unnamed)
     549  				errx(EXIT_FAILURE, _("line %zu: for JSON the name of the "
     550  					"column %zu is required"),
     551  					scols_table_get_nlines(ctl->tab) + 1,
     552  					n + 1);
     553  			scols_table_new_column(ctl->tab, NULL, 0,
     554  					ctl->hide_unnamed ? SCOLS_FL_HIDDEN : 0);
     555  		}
     556  		if (!ln) {
     557  			ln = scols_table_new_line(ctl->tab, NULL);
     558  			if (!ln)
     559  				err(EXIT_FAILURE, _("failed to allocate output line"));
     560  		}
     561  
     562  		nchars += wcslen(wcdata) + 1;
     563  
     564  		data = wcs_to_mbs(wcdata);
     565  		if (!data)
     566  			err(EXIT_FAILURE, _("failed to allocate output data"));
     567  		if (scols_line_refer_data(ln, n, data))
     568  			err(EXIT_FAILURE, _("failed to add output data"));
     569  		n++;
     570  		wcs = NULL;
     571  		if (ctl->maxncols && n == ctl->maxncols)
     572  			break;
     573  	} while (1);
     574  
     575  	return 0;
     576  }
     577  
     578  static int add_emptyline_to_table(struct column_control *ctl)
     579  {
     580  	if (!ctl->tab)
     581  		init_table(ctl);
     582  
     583  	if (!scols_table_new_line(ctl->tab, NULL))
     584  		err(EXIT_FAILURE, _("failed to allocate output line"));
     585  
     586  	return 0;
     587  }
     588  
     589  static void add_entry(struct column_control *ctl, size_t *maxents, wchar_t *wcs)
     590  {
     591  	if (ctl->nents <= *maxents) {
     592  		*maxents += 1000;
     593  		ctl->ents = xrealloc(ctl->ents, *maxents * sizeof(wchar_t *));
     594  	}
     595  	ctl->ents[ctl->nents] = wcs;
     596  	ctl->nents++;
     597  }
     598  
     599  static int read_input(struct column_control *ctl, FILE *fp)
     600  {
     601  	wchar_t *empty = NULL;
     602  	char *buf = NULL;
     603  	size_t bufsz = 0;
     604  	size_t maxents = 0;
     605  	int rc = 0;
     606  
     607  	/* Read input */
     608  	do {
     609  		char *str, *p;
     610  		wchar_t *wcs = NULL;
     611  		size_t len;
     612  
     613  		if (getline(&buf, &bufsz, fp) < 0) {
     614  			if (feof(fp))
     615  				break;
     616  			err(EXIT_FAILURE, _("read failed"));
     617  		}
     618  		str = (char *) skip_space(buf);
     619  		if (str) {
     620  			p = strchr(str, '\n');
     621  			if (p)
     622  				*p = '\0';
     623  		}
     624  		if (!str || !*str) {
     625  			if (ctl->keep_empty_lines) {
     626  				if (ctl->mode == COLUMN_MODE_TABLE) {
     627  					add_emptyline_to_table(ctl);
     628  				} else {
     629  					if (!empty)
     630  						empty = mbs_to_wcs("");
     631  					add_entry(ctl, &maxents, empty);
     632  				}
     633  			}
     634  			continue;
     635  		}
     636  
     637  		wcs = mbs_to_wcs(buf);
     638  		if (!wcs) {
     639  			/*
     640  			 * Convert broken sequences to \x<hex> and continue.
     641  			 */
     642  			size_t tmpsz = 0;
     643  			char *tmp = mbs_invalid_encode(buf, &tmpsz);
     644  
     645  			if (!tmp)
     646  				err(EXIT_FAILURE, _("read failed"));
     647  			wcs = mbs_to_wcs(tmp);
     648  			free(tmp);
     649  		}
     650  
     651  		switch (ctl->mode) {
     652  		case COLUMN_MODE_TABLE:
     653  			rc = add_line_to_table(ctl, wcs);
     654  			free(wcs);
     655  			break;
     656  
     657  		case COLUMN_MODE_FILLCOLS:
     658  		case COLUMN_MODE_FILLROWS:
     659  			add_entry(ctl, &maxents, wcs);
     660  			len = width(wcs);
     661  			if (ctl->maxlength < len)
     662  				ctl->maxlength = len;
     663  			break;
     664  		default:
     665  			free(wcs);
     666  			break;
     667  		}
     668  	} while (rc == 0);
     669  
     670  	return rc;
     671  }
     672  
     673  
     674  static void columnate_fillrows(struct column_control *ctl)
     675  {
     676  	size_t chcnt, col, cnt, endcol, numcols;
     677  	wchar_t **lp;
     678  
     679  	ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
     680  	numcols = ctl->termwidth / ctl->maxlength;
     681  	endcol = ctl->maxlength;
     682  	for (chcnt = col = 0, lp = ctl->ents; /* nothing */; ++lp) {
     683  		fputws(*lp, stdout);
     684  		chcnt += width(*lp);
     685  		if (!--ctl->nents)
     686  			break;
     687  		if (++col == numcols) {
     688  			chcnt = col = 0;
     689  			endcol = ctl->maxlength;
     690  			putwchar('\n');
     691  		} else {
     692  			while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
     693  				putwchar('\t');
     694  				chcnt = cnt;
     695  			}
     696  			endcol += ctl->maxlength;
     697  		}
     698  	}
     699  	if (chcnt)
     700  		putwchar('\n');
     701  }
     702  
     703  static void columnate_fillcols(struct column_control *ctl)
     704  {
     705  	size_t base, chcnt, cnt, col, endcol, numcols, numrows, row;
     706  
     707  	ctl->maxlength = (ctl->maxlength + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1);
     708  	numcols = ctl->termwidth / ctl->maxlength;
     709  	if (!numcols)
     710  		numcols = 1;
     711  	numrows = ctl->nents / numcols;
     712  	if (ctl->nents % numcols)
     713  		++numrows;
     714  
     715  	for (row = 0; row < numrows; ++row) {
     716  		endcol = ctl->maxlength;
     717  		for (base = row, chcnt = col = 0; col < numcols; ++col) {
     718  			fputws(ctl->ents[base], stdout);
     719  			chcnt += width(ctl->ents[base]);
     720  			if ((base += numrows) >= ctl->nents)
     721  				break;
     722  			while ((cnt = ((chcnt + TABCHAR_CELLS) & ~(TABCHAR_CELLS - 1))) <= endcol) {
     723  				putwchar('\t');
     724  				chcnt = cnt;
     725  			}
     726  			endcol += ctl->maxlength;
     727  		}
     728  		putwchar('\n');
     729  	}
     730  }
     731  
     732  static void simple_print(struct column_control *ctl)
     733  {
     734  	int cnt;
     735  	wchar_t **lp;
     736  
     737  	for (cnt = ctl->nents, lp = ctl->ents; cnt--; ++lp) {
     738  		fputws(*lp, stdout);
     739  		putwchar('\n');
     740  	}
     741  }
     742  
     743  static void __attribute__((__noreturn__)) usage(void)
     744  {
     745  	FILE *out = stdout;
     746  
     747  	fputs(USAGE_HEADER, out);
     748  	fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name);
     749  
     750  	fputs(USAGE_SEPARATOR, out);
     751  	fputs(_("Columnate lists.\n"), out);
     752  
     753  	fputs(USAGE_OPTIONS, out);
     754  	fputs(_(" -t, --table                      create a table\n"), out);
     755  	fputs(_(" -n, --table-name <name>          table name for JSON output\n"), out);
     756  	fputs(_(" -O, --table-order <columns>      specify order of output columns\n"), out);
     757  	fputs(_(" -C, --table-column <properties>  define column\n"), out);
     758  	fputs(_(" -N, --table-columns <names>      comma separated columns names\n"), out);
     759  	fputs(_(" -l, --table-columns-limit <num>  maximal number of input columns\n"), out);
     760  	fputs(_(" -E, --table-noextreme <columns>  don't count long text from the columns to column width\n"), out);
     761  	fputs(_(" -d, --table-noheadings           don't print header\n"), out);
     762  	fputs(_(" -m, --table-maxout               fill all available space\n"), out);
     763  	fputs(_(" -e, --table-header-repeat        repeat header for each page\n"), out);
     764  	fputs(_(" -H, --table-hide <columns>       don't print the columns\n"), out);
     765  	fputs(_(" -R, --table-right <columns>      right align text in these columns\n"), out);
     766  	fputs(_(" -T, --table-truncate <columns>   truncate text in the columns when necessary\n"), out);
     767  	fputs(_(" -W, --table-wrap <columns>       wrap text in the columns when necessary\n"), out);
     768  	fputs(_(" -L, --keep-empty-lines           don't ignore empty lines\n"), out);
     769  	fputs(_(" -J, --json                       use JSON output format for table\n"), out);
     770  
     771  	fputs(USAGE_SEPARATOR, out);
     772  	fputs(_(" -r, --tree <column>              column to use tree-like output for the table\n"), out);
     773  	fputs(_(" -i, --tree-id <column>           line ID to specify child-parent relation\n"), out);
     774  	fputs(_(" -p, --tree-parent <column>       parent to specify child-parent relation\n"), out);
     775  
     776  	fputs(USAGE_SEPARATOR, out);
     777  	fputs(_(" -c, --output-width <width>       width of output in number of characters\n"), out);
     778  	fputs(_(" -o, --output-separator <string>  columns separator for table output (default is two spaces)\n"), out);
     779  	fputs(_(" -s, --separator <string>         possible table delimiters\n"), out);
     780  	fputs(_(" -x, --fillrows                   fill rows before columns\n"), out);
     781  
     782  
     783  	fputs(USAGE_SEPARATOR, out);
     784  	printf(USAGE_HELP_OPTIONS(34));
     785  	printf(USAGE_MAN_TAIL("column(1)"));
     786  
     787  	exit(EXIT_SUCCESS);
     788  }
     789  
     790  int main(int argc, char **argv)
     791  {
     792  	struct column_control ctl = {
     793  		.mode = COLUMN_MODE_FILLCOLS,
     794  		.greedy = 1,
     795  		.termwidth = (size_t) -1
     796  	};
     797  
     798  	int c;
     799  	unsigned int eval = 0;		/* exit value */
     800  
     801  	static const struct option longopts[] =
     802  	{
     803  		{ "columns",             required_argument, NULL, 'c' }, /* deprecated */
     804  		{ "fillrows",            no_argument,       NULL, 'x' },
     805  		{ "help",                no_argument,       NULL, 'h' },
     806  		{ "json",                no_argument,       NULL, 'J' },
     807  		{ "keep-empty-lines",    no_argument,       NULL, 'L' },
     808  		{ "output-separator",    required_argument, NULL, 'o' },
     809  		{ "output-width",        required_argument, NULL, 'c' },
     810  		{ "separator",           required_argument, NULL, 's' },
     811  		{ "table",               no_argument,       NULL, 't' },
     812  		{ "table-columns",       required_argument, NULL, 'N' },
     813  		{ "table-column",        required_argument, NULL, 'C' },
     814  		{ "table-columns-limit", required_argument, NULL, 'l' },
     815  		{ "table-hide",          required_argument, NULL, 'H' },
     816  		{ "table-name",          required_argument, NULL, 'n' },
     817  		{ "table-maxout",        no_argument,       NULL, 'm' },
     818  		{ "table-noextreme",     required_argument, NULL, 'E' },
     819  		{ "table-noheadings",    no_argument,       NULL, 'd' },
     820  		{ "table-order",         required_argument, NULL, 'O' },
     821  		{ "table-right",         required_argument, NULL, 'R' },
     822  		{ "table-truncate",      required_argument, NULL, 'T' },
     823  		{ "table-wrap",          required_argument, NULL, 'W' },
     824  		{ "table-empty-lines",   no_argument,       NULL, 'L' }, /* deprecated */
     825  		{ "table-header-repeat", no_argument,       NULL, 'e' },
     826  		{ "tree",                required_argument, NULL, 'r' },
     827  		{ "tree-id",             required_argument, NULL, 'i' },
     828  		{ "tree-parent",         required_argument, NULL, 'p' },
     829  		{ "version",             no_argument,       NULL, 'V' },
     830  		{ NULL,	0, NULL, 0 },
     831  	};
     832  	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
     833  		{ 'C','N' },
     834  		{ 'J','x' },
     835  		{ 't','x' },
     836  		{ 0 }
     837  	};
     838  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     839  
     840  	setlocale(LC_ALL, "");
     841  	bindtextdomain(PACKAGE, LOCALEDIR);
     842  	textdomain(PACKAGE);
     843  	close_stdout_atexit();
     844  
     845  	ctl.output_separator = "  ";
     846  	ctl.input_separator = mbs_to_wcs("\t ");
     847  
     848  	while ((c = getopt_long(argc, argv, "C:c:dE:eH:hi:Jl:LN:n:mO:o:p:R:r:s:T:tVW:x", longopts, NULL)) != -1) {
     849  
     850  		err_exclusive_options(c, longopts, excl, excl_st);
     851  
     852  		switch(c) {
     853  		case 'C':
     854  			if (strv_extend(&ctl.tab_columns, optarg))
     855  				err_oom();
     856  			break;
     857  		case 'c':
     858  			if (strcmp(optarg, "unlimited") == 0)
     859  				ctl.termwidth = 0;
     860  			else
     861  				ctl.termwidth = strtou32_or_err(optarg, _("invalid columns argument"));
     862  			break;
     863  		case 'd':
     864  			ctl.tab_noheadings = 1;
     865  			break;
     866  		case 'E':
     867  			ctl.tab_colnoextrem = optarg;
     868  			break;
     869  		case 'e':
     870  			ctl.header_repeat = 1;
     871  			break;
     872  		case 'H':
     873  			ctl.tab_colhide = optarg;
     874  			ctl.hide_unnamed = has_unnamed(ctl.tab_colhide);
     875  			break;
     876  		case 'i':
     877  			ctl.tree_id = optarg;
     878  			break;
     879  		case 'J':
     880  			ctl.json = 1;
     881  			ctl.mode = COLUMN_MODE_TABLE;
     882  			break;
     883  		case 'L':
     884  			ctl.keep_empty_lines = 1;
     885  			break;
     886  		case 'l':
     887  			ctl.maxncols = strtou32_or_err(optarg, _("invalid columns limit argument"));
     888  			if (ctl.maxncols == 0)
     889  				errx(EXIT_FAILURE, _("columns limit must be greater than zero"));
     890  			break;
     891  		case 'N':
     892  			ctl.tab_colnames = split_or_error(optarg, _("failed to parse column names"));
     893  			break;
     894  		case 'n':
     895  			ctl.tab_name = optarg;
     896  			break;
     897  		case 'm':
     898  			ctl.maxout = 1;
     899  			break;
     900  		case 'O':
     901  			ctl.tab_order = optarg;
     902  			break;
     903  		case 'o':
     904  			ctl.output_separator = optarg;
     905  			break;
     906  		case 'p':
     907  			ctl.tree_parent = optarg;
     908  			break;
     909  		case 'R':
     910  			ctl.tab_colright = optarg;
     911  			break;
     912  		case 'r':
     913  			ctl.tree = optarg;
     914  			break;
     915  		case 's':
     916  			free(ctl.input_separator);
     917  			ctl.input_separator = mbs_to_wcs(optarg);
     918  			if (!ctl.input_separator)
     919  				err(EXIT_FAILURE, _("failed to use input separator"));
     920  			ctl.greedy = 0;
     921  			break;
     922  		case 'T':
     923  			ctl.tab_coltrunc = optarg;
     924  			break;
     925  		case 't':
     926  			ctl.mode = COLUMN_MODE_TABLE;
     927  			break;
     928  		case 'W':
     929  			ctl.tab_colwrap = optarg;
     930  			break;
     931  		case 'x':
     932  			ctl.mode = COLUMN_MODE_FILLROWS;
     933  			break;
     934  
     935  		case 'h':
     936  			usage();
     937  		case 'V':
     938  			print_version(EXIT_SUCCESS);
     939  		default:
     940  			errtryhelp(EXIT_FAILURE);
     941  		}
     942  	}
     943  	argc -= optind;
     944  	argv += optind;
     945  
     946  	if (ctl.termwidth == (size_t) -1)
     947  		ctl.termwidth = get_terminal_width(80);
     948  
     949  	if (ctl.tree) {
     950  		ctl.mode = COLUMN_MODE_TABLE;
     951  		if (!ctl.tree_parent || !ctl.tree_id)
     952  			errx(EXIT_FAILURE, _("options --tree-id and --tree-parent are "
     953  					     "required for tree formatting"));
     954  	}
     955  
     956  	if (ctl.mode != COLUMN_MODE_TABLE
     957  	    && (ctl.tab_order || ctl.tab_name || ctl.tab_colwrap ||
     958  		ctl.tab_colhide || ctl.tab_coltrunc || ctl.tab_colnoextrem ||
     959  		ctl.tab_colright || ctl.tab_colnames || ctl.tab_columns))
     960  		errx(EXIT_FAILURE, _("option --table required for all --table-*"));
     961  
     962  	if (!ctl.tab_colnames && !ctl.tab_columns && ctl.json)
     963  		errx(EXIT_FAILURE, _("option --table-columns or --table-column required for --json"));
     964  
     965  	if (!*argv)
     966  		eval += read_input(&ctl, stdin);
     967  	else
     968  		for (; *argv; ++argv) {
     969  			FILE *fp;
     970  
     971  			if ((fp = fopen(*argv, "r")) != NULL) {
     972  				eval += read_input(&ctl, fp);
     973  				fclose(fp);
     974  			} else {
     975  				warn("%s", *argv);
     976  				eval += EXIT_FAILURE;
     977  			}
     978  		}
     979  
     980  	if (ctl.mode != COLUMN_MODE_TABLE) {
     981  		if (!ctl.nents)
     982  			exit(eval);
     983  		if (ctl.maxlength >= ctl.termwidth)
     984  			ctl.mode = COLUMN_MODE_SIMPLE;
     985  	}
     986  
     987  	switch (ctl.mode) {
     988  	case COLUMN_MODE_TABLE:
     989  		if (ctl.tab && scols_table_get_nlines(ctl.tab)) {
     990  			modify_table(&ctl);
     991  			eval = scols_print_table(ctl.tab);
     992  
     993  			scols_unref_table(ctl.tab);
     994  			if (ctl.tab_colnames)
     995  				strv_free(ctl.tab_colnames);
     996  			if (ctl.tab_columns)
     997  				strv_free(ctl.tab_columns);
     998  		}
     999  		break;
    1000  	case COLUMN_MODE_FILLCOLS:
    1001  		columnate_fillcols(&ctl);
    1002  		break;
    1003  	case COLUMN_MODE_FILLROWS:
    1004  		columnate_fillrows(&ctl);
    1005  		break;
    1006  	case COLUMN_MODE_SIMPLE:
    1007  		simple_print(&ctl);
    1008  		break;
    1009  	}
    1010  
    1011  	free(ctl.input_separator);
    1012  
    1013  	return eval == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
    1014  }