(root)/
util-linux-2.39/
libsmartcols/
src/
print.c
       1   /* print.c - functions to print table
       2   *
       3   * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com>
       4   * Copyright (C) 2016 Igor Gnatenko <i.gnatenko.brain@gmail.com>
       5   *
       6   * This file may be redistributed under the terms of the
       7   * GNU Lesser General Public License.
       8   */
       9  
      10  /**
      11   * SECTION: table_print
      12   * @title: Table print
      13   * @short_description: output functions
      14   *
      15   * Table output API.
      16   */
      17  
      18  #include <stdlib.h>
      19  #include <unistd.h>
      20  #include <string.h>
      21  #include <termios.h>
      22  #include <ctype.h>
      23  
      24  #include "mbsalign.h"
      25  #include "carefulputc.h"
      26  #include "smartcolsP.h"
      27  
      28  /* Fallback for symbols
      29   *
      30   * Note that by default library define all the symbols, but in case user does
      31   * not define all symbols or if we extended the symbols struct then we need
      32   * fallback to be more robust and backwardly compatible.
      33   */
      34  #define titlepadding_symbol(tb)	((tb)->symbols->title_padding ? (tb)->symbols->title_padding : " ")
      35  #define branch_symbol(tb)	((tb)->symbols->tree_branch ? (tb)->symbols->tree_branch : "|-")
      36  #define vertical_symbol(tb)	((tb)->symbols->tree_vert ? (tb)->symbols->tree_vert : "| ")
      37  #define right_symbol(tb)	((tb)->symbols->tree_right ? (tb)->symbols->tree_right : "`-")
      38  
      39  #define grp_vertical_symbol(tb)	((tb)->symbols->group_vert ? (tb)->symbols->group_vert : "|")
      40  #define grp_horizontal_symbol(tb) ((tb)->symbols->group_horz ? (tb)->symbols->group_horz : "-")
      41  #define grp_m_first_symbol(tb)	((tb)->symbols->group_first_member ? (tb)->symbols->group_first_member : ",->")
      42  #define grp_m_last_symbol(tb)	((tb)->symbols->group_last_member ? (tb)->symbols->group_last_member : "\\->")
      43  #define grp_m_middle_symbol(tb)	((tb)->symbols->group_middle_member ? (tb)->symbols->group_middle_member : "|->")
      44  #define grp_c_middle_symbol(tb)	((tb)->symbols->group_middle_child ? (tb)->symbols->group_middle_child : "|-")
      45  #define grp_c_last_symbol(tb)	((tb)->symbols->group_last_child ? (tb)->symbols->group_last_child : "`-")
      46  
      47  #define cellpadding_symbol(tb)  ((tb)->padding_debug ? "." : \
      48  				 ((tb)->symbols->cell_padding ? (tb)->symbols->cell_padding: " "))
      49  
      50  #define want_repeat_header(tb)	(!(tb)->header_repeat || (tb)->header_next <= (tb)->termlines_used)
      51  
      52  static int is_next_columns_empty(
      53  			struct libscols_table *tb,
      54  			struct libscols_column *cl,
      55  			struct libscols_line *ln)
      56  {
      57  	struct libscols_iter itr;
      58  
      59  	if (!tb || !cl)
      60  		return 0;
      61  	if (is_last_column(cl))
      62  		return 1;
      63  	if (!ln)
      64  		return 0;
      65  
      66  	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
      67  	scols_table_set_columns_iter(tb, &itr, cl);
      68  
      69  	/* skip current column */
      70  	scols_table_next_column(tb, &itr, &cl);
      71  
      72  	while (scols_table_next_column(tb, &itr, &cl) == 0) {
      73  		struct libscols_cell *ce;
      74  		const char *data = NULL;
      75  
      76                  if (scols_column_is_hidden(cl))
      77                          continue;
      78  		if (scols_column_is_tree(cl))
      79  			return 0;
      80  
      81  		ce = scols_line_get_cell(ln, cl->seqnum);
      82  		if (ce)
      83  			data = scols_cell_get_data(ce);
      84  		if (data && *data)
      85  			return 0;
      86  	}
      87  	return 1;
      88  }
      89  
      90  /* returns pointer to the end of used data */
      91  static int tree_ascii_art_to_buffer(struct libscols_table *tb,
      92  				    struct libscols_line *ln,
      93  				    struct ul_buffer *buf)
      94  {
      95  	const char *art;
      96  	int rc;
      97  
      98  	assert(ln);
      99  	assert(buf);
     100  
     101  	if (!ln->parent)
     102  		return 0;
     103  
     104  	rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
     105  	if (rc)
     106  		return rc;
     107  
     108  	if (is_last_child(ln))
     109  		art = "  ";
     110  	else
     111  		art = vertical_symbol(tb);
     112  
     113  	return ul_buffer_append_string(buf, art);
     114  }
     115  
     116  static int grpset_is_empty(	struct libscols_table *tb,
     117  				size_t idx,
     118  				size_t *rest)
     119  {
     120  	size_t i;
     121  
     122  	for (i = idx; i < tb->grpset_size; i++) {
     123  		if (tb->grpset[i] == NULL) {
     124  			if (rest)
     125  				(*rest)++;
     126  		} else
     127  			return 0;
     128  	}
     129  	return 1;
     130  }
     131  
     132  static int groups_ascii_art_to_buffer(	struct libscols_table *tb,
     133  				struct libscols_line *ln,
     134  				struct ul_buffer *buf,
     135  				int empty)
     136  {
     137  	int filled = 0;
     138  	size_t i, rest = 0;
     139  	const char *filler = cellpadding_symbol(tb);
     140  
     141  	if (!has_groups(tb))
     142  		return 0;
     143  
     144  	DBG(LINE, ul_debugobj(ln, "printing groups chart"));
     145  
     146  	if (tb->is_dummy_print)
     147  		return 0;		/* allocate grpset[] only */
     148  
     149  	for (i = 0; i < tb->grpset_size; i += SCOLS_GRPSET_CHUNKSIZ) {
     150  		struct libscols_group *gr = tb->grpset[i];
     151  
     152  		if (!gr) {
     153  			ul_buffer_append_ntimes(buf, SCOLS_GRPSET_CHUNKSIZ, cellpadding_symbol(tb));
     154  			continue;
     155  		}
     156  
     157  		/*
     158  		 * Empty cells (multi-line entries, etc.), print vertical symbols only
     159  		 * to show that the group continues.
     160  		 */
     161  		if (empty) {
     162  			switch (gr->state) {
     163  			case SCOLS_GSTATE_FIRST_MEMBER:
     164  			case SCOLS_GSTATE_MIDDLE_MEMBER:
     165  			case SCOLS_GSTATE_CONT_MEMBERS:
     166  				ul_buffer_append_string(buf, grp_vertical_symbol(tb));
     167  				ul_buffer_append_ntimes(buf, 2, filler);
     168  				break;
     169  
     170  			case SCOLS_GSTATE_LAST_MEMBER:
     171  			case SCOLS_GSTATE_MIDDLE_CHILD:
     172  			case SCOLS_GSTATE_CONT_CHILDREN:
     173  				ul_buffer_append_string(buf, filler);
     174  				ul_buffer_append_string(buf, grp_vertical_symbol(tb));
     175  				ul_buffer_append_string(buf, filler);
     176  				break;
     177  			case SCOLS_GSTATE_LAST_CHILD:
     178  				ul_buffer_append_ntimes(buf, 3, filler);
     179  				break;
     180  			}
     181  			continue;
     182  		}
     183  
     184  		/*
     185  		 * Regular cell
     186  		 */
     187  		switch (gr->state) {
     188  		case SCOLS_GSTATE_FIRST_MEMBER:
     189  			ul_buffer_append_string(buf, grp_m_first_symbol(tb));
     190  			break;
     191  		case SCOLS_GSTATE_MIDDLE_MEMBER:
     192  			ul_buffer_append_string(buf, grp_m_middle_symbol(tb));
     193  			break;
     194  		case SCOLS_GSTATE_LAST_MEMBER:
     195  			ul_buffer_append_string(buf, grp_m_last_symbol(tb));
     196  			break;
     197  		case SCOLS_GSTATE_CONT_MEMBERS:
     198  			ul_buffer_append_string(buf, grp_vertical_symbol(tb));
     199  			ul_buffer_append_ntimes(buf, 2, filler);
     200  			break;
     201  		case SCOLS_GSTATE_MIDDLE_CHILD:
     202  			ul_buffer_append_string(buf, filler);
     203  			ul_buffer_append_string(buf, grp_c_middle_symbol(tb));
     204  			if (grpset_is_empty(tb, i + SCOLS_GRPSET_CHUNKSIZ, &rest)) {
     205  				ul_buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
     206  				filled = 1;
     207  			}
     208  			filler = grp_horizontal_symbol(tb);
     209  			break;
     210  		case SCOLS_GSTATE_LAST_CHILD:
     211  			ul_buffer_append_string(buf, cellpadding_symbol(tb));
     212  			ul_buffer_append_string(buf, grp_c_last_symbol(tb));
     213  			if (grpset_is_empty(tb, i + SCOLS_GRPSET_CHUNKSIZ, &rest)) {
     214  				ul_buffer_append_ntimes(buf, rest+1, grp_horizontal_symbol(tb));
     215  				filled = 1;
     216  			}
     217  			filler = grp_horizontal_symbol(tb);
     218  			break;
     219  		case SCOLS_GSTATE_CONT_CHILDREN:
     220  			ul_buffer_append_string(buf, filler);
     221  			ul_buffer_append_string(buf, grp_vertical_symbol(tb));
     222  			ul_buffer_append_string(buf, filler);
     223  			break;
     224  		}
     225  
     226  		if (filled)
     227  			break;
     228  	}
     229  
     230  	if (!filled)
     231  		ul_buffer_append_string(buf, filler);
     232  	return 0;
     233  }
     234  
     235  static int has_pending_data(struct libscols_table *tb)
     236  {
     237  	struct libscols_column *cl;
     238  	struct libscols_iter itr;
     239  
     240  	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
     241  	while (scols_table_next_column(tb, &itr, &cl) == 0) {
     242  		if (scols_column_is_hidden(cl))
     243  			continue;
     244  		if (cl->pending_data)
     245  			return 1;
     246  	}
     247  	return 0;
     248  }
     249  
     250  static void fputs_color_reset(struct libscols_table *tb)
     251  {
     252  	if (tb->cur_color) {
     253  		fputs(UL_COLOR_RESET, tb->out);
     254  		tb->cur_color = NULL;
     255  	}
     256  }
     257  
     258  static void fputs_color(struct libscols_table *tb, const char *color)
     259  {
     260  	if (tb->cur_color)
     261  		fputs_color_reset(tb);
     262  
     263  	tb->cur_color = color;
     264  	if (color)
     265  		fputs(color, tb->out);
     266  }
     267  
     268  static const char *get_cell_color(struct libscols_table *tb,
     269  				struct libscols_column *cl,
     270  				struct libscols_line *ln,
     271  				struct libscols_cell *ce)
     272  {
     273  	const char *color = NULL;
     274  
     275  	if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN)
     276  		return NULL;
     277  	if (ce)
     278  		color = ce->color;
     279  	if (!color && (!ln || !ln->color) && cl)
     280  		color = cl->color;
     281  	return color;
     282  }
     283  
     284  /* switch from line color to cell/column color */
     285  static void fputs_color_cell_open(struct libscols_table *tb,
     286  				struct libscols_column *cl,
     287  				struct libscols_line *ln,
     288  				struct libscols_cell *ce)
     289  {
     290  	const char *color = get_cell_color(tb, cl, ln, ce);
     291  
     292  	if (color)
     293  		fputs_color(tb, color);
     294  }
     295  
     296  /* switch from cell/column color to line color or reset */
     297  static void fputs_color_cell_close(struct libscols_table *tb,
     298  				struct libscols_column *cl,
     299  				struct libscols_line *ln,
     300  				struct libscols_cell *ce)
     301  {
     302  	const char *color = get_cell_color(tb, cl, ln, ce);
     303  
     304  	if (color)
     305  		fputs_color(tb, ln ? ln->color : NULL);
     306  }
     307  
     308  /* switch to line color */
     309  static void fputs_color_line_open(struct libscols_table *tb,
     310  				struct libscols_line *ln)
     311  {
     312  	if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN)
     313  		return;
     314  	fputs_color(tb, ln ? ln->color : NULL);
     315  }
     316  
     317  /* switch off all colors */
     318  static void fputs_color_line_close(struct libscols_table *tb)
     319  {
     320  	if (!tb || !tb->colors_wanted || tb->format != SCOLS_FMT_HUMAN)
     321  		return;
     322  	fputs_color_reset(tb);
     323  }
     324  
     325  /* print padding or ASCII-art instead of data of @cl */
     326  static void print_empty_cell(struct libscols_table *tb,
     327  			  struct libscols_column *cl,
     328  			  struct libscols_line *ln,	/* optional */
     329  			  struct libscols_cell *ce,
     330  			  size_t bufsz)
     331  {
     332  	size_t len_pad = 0;		/* in screen cells as opposed to bytes */
     333  
     334  	DBG(COL, ul_debugobj(cl, " printing empty cell"));
     335  
     336  	fputs_color_cell_open(tb, cl, ln, ce);
     337  
     338  	/* generate tree/group ASCII-art rather than padding
     339  	 */
     340  	if (ln && scols_column_is_tree(cl)) {
     341  		struct ul_buffer art = UL_INIT_BUFFER;
     342  		char *data;
     343  
     344  		if (ul_buffer_alloc_data(&art, bufsz) != 0)
     345  			goto done;
     346  
     347  		if (cl->is_groups)
     348  			groups_ascii_art_to_buffer(tb, ln, &art, 1);
     349  
     350  		tree_ascii_art_to_buffer(tb, ln, &art);
     351  
     352  		if (!list_empty(&ln->ln_branch) && has_pending_data(tb))
     353  			ul_buffer_append_string(&art, vertical_symbol(tb));
     354  
     355  		if (scols_table_is_noencoding(tb))
     356  			data = ul_buffer_get_data(&art, NULL, &len_pad);
     357  		else
     358  			data = ul_buffer_get_safe_data(&art, NULL, &len_pad, NULL);
     359  
     360  		if (data && len_pad)
     361  			fputs(data, tb->out);
     362  		ul_buffer_free_data(&art);
     363  	}
     364  
     365  done:
     366  	/* minout -- don't fill */
     367  	if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
     368  		fputs_color_cell_close(tb, cl, ln, ce);
     369  		return;
     370  	}
     371  
     372  	/* default -- fill except last column */
     373  	if (!scols_table_is_maxout(tb) && is_last_column(cl)) {
     374  		fputs_color_cell_close(tb, cl, ln, ce);
     375  		return;
     376  	}
     377  
     378  	/* fill rest of cell with space */
     379  	for(; len_pad < cl->width; ++len_pad)
     380  		fputs(cellpadding_symbol(tb), tb->out);
     381  
     382  	fputs_color_cell_close(tb, cl, ln, ce);
     383  
     384  	if (!is_last_column(cl))
     385  		fputs(colsep(tb), tb->out);
     386  }
     387  
     388  
     389  
     390  /* Fill the start of a line with padding (or with tree ascii-art).
     391   *
     392   * This is necessary after a long non-truncated column, as this requires the
     393   * next column to be printed on the next line. For example (see 'DDD'):
     394   *
     395   * aaa bbb ccc ddd eee
     396   * AAA BBB CCCCCCC
     397   *             DDD EEE
     398   * ^^^^^^^^^^^^
     399   *  new line padding
     400   */
     401  static void print_newline_padding(struct libscols_table *tb,
     402  				  struct libscols_column *cl,
     403  				  struct libscols_line *ln,	/* optional */
     404  				  struct libscols_cell *ce,
     405  				  size_t bufsz)
     406  {
     407  	size_t i;
     408  
     409  	assert(tb);
     410  	assert(cl);
     411  
     412  	DBG(LINE, ul_debugobj(ln, "printing newline padding"));
     413  
     414  	fputs(linesep(tb), tb->out);		/* line break */
     415  	tb->termlines_used++;
     416  
     417  	fputs_color_line_open(tb, ln);
     418  
     419  	/* fill cells after line break */
     420  	for (i = 0; i <= (size_t) cl->seqnum; i++)
     421  		print_empty_cell(tb, scols_table_get_column(tb, i), ln, ce, bufsz);
     422  
     423  	fputs_color_line_close(tb);
     424  }
     425  
     426  /*
     427   * Pending data
     428   *
     429   * The first line in the multi-line cells (columns with SCOLS_FL_WRAP flag) is
     430   * printed as usually and output is truncated to match column width.
     431   *
     432   * The rest of the long text is printed on next extra line(s). The extra lines
     433   * don't exist in the table (not represented by libscols_line). The data for
     434   * the extra lines are stored in libscols_column->pending_data_buf and the
     435   * function print_line() adds extra lines until the buffer is not empty in all
     436   * columns.
     437   */
     438  
     439  /* set data that will be printed by extra lines */
     440  static int set_pending_data(struct libscols_column *cl, const char *data, size_t sz)
     441  {
     442  	char *p = NULL;
     443  
     444  	if (data && *data) {
     445  		DBG(COL, ul_debugobj(cl, "setting pending data"));
     446  		assert(sz);
     447  		p = strdup(data);
     448  		if (!p)
     449  			return -ENOMEM;
     450  	}
     451  
     452  	free(cl->pending_data_buf);
     453  	cl->pending_data_buf = p;
     454  	cl->pending_data_sz = sz;
     455  	cl->pending_data = cl->pending_data_buf;
     456  	return 0;
     457  }
     458  
     459  /* the next extra line has been printed, move pending data cursor */
     460  static int step_pending_data(struct libscols_column *cl, size_t bytes)
     461  {
     462  	DBG(COL, ul_debugobj(cl, "step pending data %zu -= %zu", cl->pending_data_sz, bytes));
     463  
     464  	if (bytes >= cl->pending_data_sz)
     465  		return set_pending_data(cl, NULL, 0);
     466  
     467  	cl->pending_data += bytes;
     468  	cl->pending_data_sz -= bytes;
     469  	return 0;
     470  }
     471  
     472  /* print next pending data for the column @cl */
     473  static int print_pending_data(
     474  		struct libscols_table *tb,
     475  		struct libscols_column *cl,
     476  		struct libscols_line *ln,	/* optional */
     477  		struct libscols_cell *ce)
     478  {
     479  	size_t width = cl->width, bytes;
     480  	size_t len = width, i;
     481  	char *data;
     482  	char *nextchunk = NULL;
     483  
     484  	if (!cl->pending_data)
     485  		return 0;
     486  	if (!width)
     487  		return -EINVAL;
     488  
     489  	DBG(COL, ul_debugobj(cl, "printing pending data"));
     490  
     491  	data = strdup(cl->pending_data);
     492  	if (!data)
     493  		goto err;
     494  
     495  	if (scols_column_is_customwrap(cl)
     496  	    && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
     497  		bytes = nextchunk - data;
     498  
     499  		len = scols_table_is_noencoding(tb) ?
     500  				mbs_nwidth(data, bytes) :
     501  				mbs_safe_nwidth(data, bytes, NULL);
     502  	} else
     503  		bytes = mbs_truncate(data, &len);
     504  
     505  	if (bytes == (size_t) -1)
     506  		goto err;
     507  
     508  	if (bytes)
     509  		step_pending_data(cl, bytes);
     510  
     511  	fputs_color_cell_open(tb, cl, ln, ce);
     512  
     513  	fputs(data, tb->out);
     514  	free(data);
     515  
     516  	/* minout -- don't fill */
     517  	if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
     518  		fputs_color_cell_close(tb, cl, ln, ce);
     519  		return 0;
     520  	}
     521  
     522  	/* default -- fill except last column */
     523  	if (!scols_table_is_maxout(tb) && is_last_column(cl)) {
     524  		fputs_color_cell_close(tb, cl, ln, ce);
     525  		return 0;
     526  	}
     527  
     528  	/* fill rest of cell with space */
     529  	for(i = len; i < width; i++)
     530  		fputs(cellpadding_symbol(tb), tb->out);
     531  
     532  	fputs_color_cell_close(tb, cl, ln, ce);
     533  
     534  	if (!is_last_column(cl))
     535  		fputs(colsep(tb), tb->out);
     536  
     537  	return 0;
     538  err:
     539  	free(data);
     540  	return -errno;
     541  }
     542  
     543  static void print_json_data(struct libscols_table *tb,
     544  			    struct libscols_column *cl,
     545  			    const char *name,
     546  			    char *data)
     547  {
     548  	switch (cl->json_type) {
     549  	case SCOLS_JSON_STRING:
     550  		/* name: "aaa" */
     551  		ul_jsonwrt_value_s(&tb->json, name, data);
     552  		break;
     553  	case SCOLS_JSON_NUMBER:
     554  		/* name: 123 */
     555  		ul_jsonwrt_value_raw(&tb->json, name, data);
     556  		break;
     557  	case SCOLS_JSON_BOOLEAN:
     558  	case SCOLS_JSON_BOOLEAN_OPTIONAL:
     559  		/* name: true|false|null */
     560  		if (cl->json_type == SCOLS_JSON_BOOLEAN_OPTIONAL && (!*data || !strcmp(data, "-"))) {
     561  			ul_jsonwrt_value_null(&tb->json, name);
     562  		} else {
     563  			ul_jsonwrt_value_boolean(&tb->json, name,
     564  					!*data ? 0 :
     565  					*data == '0' ? 0 :
     566  					*data == 'N' || *data == 'n' ? 0 : 1);
     567  		}
     568  		break;
     569  	case SCOLS_JSON_ARRAY_STRING:
     570  	case SCOLS_JSON_ARRAY_NUMBER:
     571  		/* name: [ "aaa", "bbb", "ccc" ] */
     572  		ul_jsonwrt_array_open(&tb->json, name);
     573  
     574  		if (!scols_column_is_customwrap(cl))
     575  			ul_jsonwrt_value_s(&tb->json, NULL, data);
     576  		else do {
     577  				char *next = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data);
     578  
     579  				if (cl->json_type == SCOLS_JSON_ARRAY_STRING)
     580  					ul_jsonwrt_value_s(&tb->json, NULL, data);
     581  				else
     582  					ul_jsonwrt_value_raw(&tb->json, NULL, data);
     583  				data = next;
     584  		} while (data);
     585  
     586  		ul_jsonwrt_array_close(&tb->json);
     587  		break;
     588  	}
     589  }
     590  
     591  static int print_data(struct libscols_table *tb,
     592  		      struct libscols_column *cl,
     593  		      struct libscols_line *ln,	/* optional */
     594  		      struct libscols_cell *ce,	/* optional */
     595  		      struct ul_buffer *buf)
     596  {
     597  	size_t len = 0, i, width, bytes;
     598  	char *data, *nextchunk;
     599  	const char *name = NULL;
     600  	int is_last;
     601  
     602  	assert(tb);
     603  	assert(cl);
     604  
     605  	data = ul_buffer_get_data(buf, NULL, NULL);
     606  	if (!data)
     607  		data = "";
     608  
     609  	if (tb->format != SCOLS_FMT_HUMAN) {
     610  		name = scols_table_is_shellvar(tb) ?
     611  				scols_column_get_name_as_shellvar(cl) :
     612  				scols_column_get_name(cl);
     613  	}
     614  
     615  	is_last = is_last_column(cl);
     616  
     617  	if (is_last && scols_table_is_json(tb) &&
     618  	    scols_table_is_tree(tb) && has_children(ln))
     619  		/* "children": [] is the real last value */
     620  		is_last = 0;
     621  
     622  	switch (tb->format) {
     623  	case SCOLS_FMT_RAW:
     624  		fputs_nonblank(data, tb->out);
     625  		if (!is_last)
     626  			fputs(colsep(tb), tb->out);
     627  		return 0;
     628  
     629  	case SCOLS_FMT_EXPORT:
     630  		fputs(name, tb->out);
     631  		fputc('=', tb->out);
     632  		fputs_quoted(data, tb->out);
     633  		if (!is_last)
     634  			fputs(colsep(tb), tb->out);
     635  		return 0;
     636  
     637  	case SCOLS_FMT_JSON:
     638  		print_json_data(tb, cl, name, data);
     639  		return 0;
     640  
     641  	case SCOLS_FMT_HUMAN:
     642  		break;		/* continue below */
     643  	}
     644  
     645  	/* Encode. Note that 'len' and 'width' are number of cells, not bytes.
     646  	 */
     647  	if (scols_table_is_noencoding(tb))
     648  		data = ul_buffer_get_data(buf, &bytes, &len);
     649  	else
     650  		data = ul_buffer_get_safe_data(buf, &bytes, &len, scols_column_get_safechars(cl));
     651  
     652  	if (!data)
     653  		data = "";
     654  	width = cl->width;
     655  
     656  	/* custom multi-line cell based */
     657  	if (*data && scols_column_is_customwrap(cl)
     658  	    && (nextchunk = cl->wrap_nextchunk(cl, data, cl->wrapfunc_data))) {
     659  		set_pending_data(cl, nextchunk, bytes - (nextchunk - data));
     660  		bytes = nextchunk - data;
     661  
     662  		len = scols_table_is_noencoding(tb) ?
     663  				mbs_nwidth(data, bytes) :
     664  				mbs_safe_nwidth(data, bytes, NULL);
     665  	}
     666  
     667  	if (is_last
     668  	    && len < width
     669  	    && !scols_table_is_maxout(tb)
     670  	    && !scols_column_is_right(cl))
     671  		width = len;
     672  
     673  	/* truncate data */
     674  	if (len > width && scols_column_is_trunc(cl)) {
     675  		len = width;
     676  		bytes = mbs_truncate(data, &len);	/* updates 'len' */
     677  	}
     678  
     679  	/* standard multi-line cell */
     680  	if (len > width && scols_column_is_wrap(cl)
     681  	    && !scols_column_is_customwrap(cl)) {
     682  		set_pending_data(cl, data, bytes);
     683  
     684  		len = width;
     685  		bytes = mbs_truncate(data, &len);
     686  		if (bytes  != (size_t) -1 && bytes > 0)
     687  			step_pending_data(cl, bytes);
     688  	}
     689  
     690  	if (bytes == (size_t) -1) {
     691  		bytes = len = 0;
     692  		data = NULL;
     693  	}
     694  
     695  	fputs_color_cell_open(tb, cl, ln, ce);
     696  
     697  	if (data && *data) {
     698  		if (scols_column_is_right(cl)) {
     699  			for (i = len; i < width; i++)
     700  				fputs(cellpadding_symbol(tb), tb->out);
     701  			len = width;
     702  		}
     703  		fputs(data, tb->out);
     704  
     705  	}
     706  
     707  	/* minout -- don't fill */
     708  	if (scols_table_is_minout(tb) && is_next_columns_empty(tb, cl, ln)) {
     709  		fputs_color_cell_close(tb, cl, ln, ce);
     710  		return 0;
     711  	}
     712  
     713  	/* default -- fill except last column */
     714  	if (!scols_table_is_maxout(tb) && is_last) {
     715  		fputs_color_cell_close(tb, cl, ln, ce);
     716  		return 0;
     717  	}
     718  
     719  	/* fill rest of cell with space */
     720  	for(i = len; i < width; i++)
     721  		fputs(cellpadding_symbol(tb), tb->out);
     722  
     723  	fputs_color_cell_close(tb, cl, ln, ce);
     724  
     725  	if (len > width && !scols_column_is_trunc(cl)) {
     726  		DBG(COL, ul_debugobj(cl, "*** data len=%zu > column width=%zu", len, width));
     727  		print_newline_padding(tb, cl, ln, ce, ul_buffer_get_bufsiz(buf));	/* next column starts on next line */
     728  
     729  	} else if (!is_last)
     730  		fputs(colsep(tb), tb->out);		/* columns separator */
     731  
     732  	return 0;
     733  }
     734  
     735  int __cell_to_buffer(struct libscols_table *tb,
     736  			  struct libscols_line *ln,
     737  			  struct libscols_column *cl,
     738  			  struct ul_buffer *buf)
     739  {
     740  	const char *data;
     741  	struct libscols_cell *ce;
     742  	int rc = 0;
     743  
     744  	assert(tb);
     745  	assert(ln);
     746  	assert(cl);
     747  	assert(buf);
     748  	assert(cl->seqnum <= tb->ncols);
     749  
     750  	ul_buffer_reset_data(buf);
     751  
     752  	ce = scols_line_get_cell(ln, cl->seqnum);
     753  	data = ce ? scols_cell_get_data(ce) : NULL;
     754  
     755  	if (!scols_column_is_tree(cl))
     756  		return data ? ul_buffer_append_string(buf, data) : 0;
     757  
     758  	/*
     759  	 * Group stuff
     760  	 */
     761  	if (!scols_table_is_json(tb) && cl->is_groups)
     762  		rc = groups_ascii_art_to_buffer(tb, ln, buf, 0);
     763  
     764  	/*
     765  	 * Tree stuff
     766  	 */
     767  	if (!rc && ln->parent && !scols_table_is_json(tb)) {
     768  		rc = tree_ascii_art_to_buffer(tb, ln->parent, buf);
     769  
     770  		if (!rc && is_last_child(ln))
     771  			rc = ul_buffer_append_string(buf, right_symbol(tb));
     772  		else if (!rc)
     773  			rc = ul_buffer_append_string(buf, branch_symbol(tb));
     774  	}
     775  
     776  	if (!rc && (ln->parent || cl->is_groups) && !scols_table_is_json(tb))
     777  		ul_buffer_save_pointer(buf, SCOLS_BUFPTR_TREEEND);
     778  
     779  	if (!rc && data)
     780  		rc = ul_buffer_append_string(buf, data);
     781  	return rc;
     782  }
     783  
     784  /*
     785   * Prints data. Data can be printed in more formats (raw, NAME=xxx pairs), and
     786   * control and non-printable characters can be encoded in the \x?? encoding.
     787   */
     788  static int print_line(struct libscols_table *tb,
     789  		      struct libscols_line *ln,
     790  		      struct ul_buffer *buf)
     791  {
     792  	int rc = 0, pending = 0;
     793  	struct libscols_column *cl;
     794  	struct libscols_iter itr;
     795  
     796  	assert(ln);
     797  
     798  	DBG(LINE, ul_debugobj(ln, "     printing line"));
     799  
     800  	fputs_color_line_open(tb, ln);
     801  
     802  	/* regular line */
     803  	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
     804  	while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
     805  		if (scols_column_is_hidden(cl))
     806  			continue;
     807  		rc = __cell_to_buffer(tb, ln, cl, buf);
     808  		if (rc == 0)
     809  			rc = print_data(tb, cl, ln,
     810  					scols_line_get_cell(ln, cl->seqnum),
     811  					buf);
     812  		if (rc == 0 && cl->pending_data)
     813  			pending = 1;
     814  	}
     815  	fputs_color_line_close(tb);
     816  
     817  	/* extra lines of the multi-line cells */
     818  	while (rc == 0 && pending) {
     819  		DBG(LINE, ul_debugobj(ln, "printing pending data"));
     820  		pending = 0;
     821  		fputs(linesep(tb), tb->out);
     822  		fputs_color_line_open(tb, ln);
     823  		tb->termlines_used++;
     824  		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
     825  		while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
     826  			if (scols_column_is_hidden(cl))
     827  				continue;
     828  			if (cl->pending_data) {
     829  				rc = print_pending_data(tb, cl, ln, scols_line_get_cell(ln, cl->seqnum));
     830  				if (rc == 0 && cl->pending_data)
     831  					pending = 1;
     832  			} else
     833  				print_empty_cell(tb, cl, ln, NULL, ul_buffer_get_bufsiz(buf));
     834  		}
     835  		fputs_color_line_close(tb);
     836  	}
     837  
     838  	return 0;
     839  }
     840  
     841  int __scols_print_title(struct libscols_table *tb)
     842  {
     843  	int rc;
     844  	mbs_align_t align;
     845  	size_t width, len = 0, bufsz, titlesz;
     846  	char *title = NULL, *buf = NULL;
     847  
     848  	assert(tb);
     849  
     850  	if (!tb->title.data)
     851  		return 0;
     852  
     853  	DBG(TAB, ul_debugobj(tb, "printing title"));
     854  
     855  	/* encode data */
     856  	if (tb->no_encode) {
     857  		len = bufsz = strlen(tb->title.data) + 1;
     858  		buf = strdup(tb->title.data);
     859  		if (!buf) {
     860  			rc = -ENOMEM;
     861  			goto done;
     862  		}
     863  	} else {
     864  		bufsz = mbs_safe_encode_size(strlen(tb->title.data)) + 1;
     865  		if (bufsz == 1) {
     866  			DBG(TAB, ul_debugobj(tb, "title is empty string -- ignore"));
     867  			return 0;
     868  		}
     869  		buf = malloc(bufsz);
     870  		if (!buf) {
     871  			rc = -ENOMEM;
     872  			goto done;
     873  		}
     874  
     875  		if (!mbs_safe_encode_to_buffer(tb->title.data, &len, buf, NULL) ||
     876  		    !len || len == (size_t) -1) {
     877  			rc = -EINVAL;
     878  			goto done;
     879  		}
     880  	}
     881  
     882  	/* truncate and align */
     883  	width = tb->is_term ? tb->termwidth : 80;
     884  	titlesz = width + bufsz;
     885  
     886  	title = malloc(titlesz);
     887  	if (!title) {
     888  		rc = -EINVAL;
     889  		goto done;
     890  	}
     891  
     892  	switch (scols_cell_get_alignment(&tb->title)) {
     893  	case SCOLS_CELL_FL_RIGHT:
     894  		align = MBS_ALIGN_RIGHT;
     895  		break;
     896  	case SCOLS_CELL_FL_CENTER:
     897  		align = MBS_ALIGN_CENTER;
     898  		break;
     899  	case SCOLS_CELL_FL_LEFT:
     900  	default:
     901  		align = MBS_ALIGN_LEFT;
     902  		/*
     903  		 * Don't print extra blank chars after the title if on left
     904  		 * (that's same as we use for the last column in the table).
     905  		 */
     906  		if (len < width
     907  		    && !scols_table_is_maxout(tb)
     908  		    && isblank(*titlepadding_symbol(tb)))
     909  			width = len;
     910  		break;
     911  
     912  	}
     913  
     914  	/* copy from buf to title and align to width with title_padding */
     915  	rc = mbsalign_with_padding(buf, title, titlesz,
     916  			&width, align,
     917  			0, (int) *titlepadding_symbol(tb));
     918  
     919  	if (rc == -1) {
     920  		rc = -EINVAL;
     921  		goto done;
     922  	}
     923  
     924  
     925  	if (tb->colors_wanted)
     926  		fputs_color(tb, tb->title.color);
     927  
     928  	fputs(title, tb->out);
     929  
     930  	if (tb->colors_wanted)
     931  		fputs_color_reset(tb);
     932  
     933  	fputc('\n', tb->out);
     934  	rc = 0;
     935  done:
     936  	free(buf);
     937  	free(title);
     938  	DBG(TAB, ul_debugobj(tb, "printing title done [rc=%d]", rc));
     939  	return rc;
     940  }
     941  
     942  int __scols_print_header(struct libscols_table *tb, struct ul_buffer *buf)
     943  {
     944  	int rc = 0;
     945  	struct libscols_column *cl;
     946  	struct libscols_iter itr;
     947  
     948  	assert(tb);
     949  
     950  	if ((tb->header_printed == 1 && tb->header_repeat == 0) ||
     951  	    scols_table_is_noheadings(tb) ||
     952  	    scols_table_is_export(tb) ||
     953  	    scols_table_is_json(tb) ||
     954  	    list_empty(&tb->tb_lines))
     955  		return 0;
     956  
     957  	DBG(TAB, ul_debugobj(tb, "printing header"));
     958  
     959  	/* set the width according to the size of the data */
     960  	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
     961  	while (rc == 0 && scols_table_next_column(tb, &itr, &cl) == 0) {
     962  		if (scols_column_is_hidden(cl))
     963  			continue;
     964  
     965  		ul_buffer_reset_data(buf);
     966  
     967  		if (cl->is_groups
     968  		    && scols_table_is_tree(tb) && scols_column_is_tree(cl)) {
     969  			size_t i;
     970  			for (i = 0; i < tb->grpset_size + 1; i++) {
     971  				rc = ul_buffer_append_data(buf, " ", 1);
     972  				if (rc)
     973  					break;
     974  			}
     975  		}
     976  		if (!rc)
     977  			rc = ul_buffer_append_string(buf,
     978  					scols_table_is_shellvar(tb) ?
     979  						scols_column_get_name_as_shellvar(cl) :
     980  						scols_column_get_name(cl));
     981  		if (!rc)
     982  			rc = print_data(tb, cl, NULL, &cl->header, buf);
     983  	}
     984  
     985  	if (rc == 0) {
     986  		fputs(linesep(tb), tb->out);
     987  		tb->termlines_used++;
     988  	}
     989  
     990  	tb->header_printed = 1;
     991  	tb->header_next = tb->termlines_used + tb->termheight;
     992  	if (tb->header_repeat)
     993  		DBG(TAB, ul_debugobj(tb, "\tnext header: %zu [current=%zu, rc=%d]",
     994  					tb->header_next, tb->termlines_used, rc));
     995  	return rc;
     996  }
     997  
     998  
     999  int __scols_print_range(struct libscols_table *tb,
    1000  			struct ul_buffer *buf,
    1001  			struct libscols_iter *itr,
    1002  			struct libscols_line *end)
    1003  {
    1004  	int rc = 0;
    1005  	struct libscols_line *ln;
    1006  
    1007  	assert(tb);
    1008  	DBG(TAB, ul_debugobj(tb, "printing range"));
    1009  
    1010  	while (rc == 0 && scols_table_next_line(tb, itr, &ln) == 0) {
    1011  
    1012  		int last = scols_iter_is_last(itr);
    1013  
    1014  		if (scols_table_is_json(tb))
    1015  			ul_jsonwrt_object_open(&tb->json, NULL);
    1016  
    1017  		rc = print_line(tb, ln, buf);
    1018  
    1019  		if (scols_table_is_json(tb))
    1020  			ul_jsonwrt_object_close(&tb->json);
    1021  		else if (last == 0 && tb->no_linesep == 0) {
    1022  			fputs(linesep(tb), tb->out);
    1023  			tb->termlines_used++;
    1024  		}
    1025  
    1026  		if (end && ln == end)
    1027  			break;
    1028  
    1029  		if (!last && want_repeat_header(tb))
    1030  			__scols_print_header(tb, buf);
    1031  	}
    1032  
    1033  	return rc;
    1034  
    1035  }
    1036  
    1037  int __scols_print_table(struct libscols_table *tb, struct ul_buffer *buf)
    1038  {
    1039  	struct libscols_iter itr;
    1040  
    1041  	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
    1042  	return __scols_print_range(tb, buf, &itr, NULL);
    1043  }
    1044  
    1045  /* scols_walk_tree() callback to print tree line */
    1046  static int print_tree_line(struct libscols_table *tb,
    1047  			   struct libscols_line *ln,
    1048  			   struct libscols_column *cl __attribute__((__unused__)),
    1049  			   void *data)
    1050  {
    1051  	struct ul_buffer *buf = (struct ul_buffer *) data;
    1052  	int rc;
    1053  
    1054  	DBG(LINE, ul_debugobj(ln, "   printing tree line"));
    1055  
    1056  	if (scols_table_is_json(tb))
    1057  		ul_jsonwrt_object_open(&tb->json, NULL);
    1058  
    1059  	rc = print_line(tb, ln, buf);
    1060  	if (rc)
    1061  		return rc;
    1062  
    1063  	if (has_children(ln)) {
    1064  		if (scols_table_is_json(tb))
    1065  			ul_jsonwrt_array_open(&tb->json, "children");
    1066  		else {
    1067  			/* between parent and child is separator */
    1068  			fputs(linesep(tb), tb->out);
    1069  			tb->termlines_used++;
    1070  		}
    1071  	} else {
    1072  		int last;
    1073  
    1074  		/* terminate all open last children for JSON */
    1075  		if (scols_table_is_json(tb)) {
    1076  			do {
    1077  				last = (is_child(ln) && is_last_child(ln)) ||
    1078  				       (is_tree_root(ln) && is_last_tree_root(tb, ln));
    1079  
    1080  				ul_jsonwrt_object_close(&tb->json);
    1081  				if (last && is_child(ln))
    1082  					ul_jsonwrt_array_close(&tb->json);
    1083  				ln = ln->parent;
    1084  			} while(ln && last);
    1085  
    1086  		} else if (tb->no_linesep == 0) {
    1087  			int last_in_tree = scols_walk_is_last(tb, ln);
    1088  
    1089  			if (last_in_tree == 0) {
    1090  				/* standard output */
    1091  				fputs(linesep(tb), tb->out);
    1092  				tb->termlines_used++;
    1093  			}
    1094  		}
    1095  	}
    1096  
    1097  	return 0;
    1098  }
    1099  
    1100  int __scols_print_tree(struct libscols_table *tb, struct ul_buffer *buf)
    1101  {
    1102  	assert(tb);
    1103  	DBG(TAB, ul_debugobj(tb, "----printing-tree-----"));
    1104  
    1105  	return scols_walk_tree(tb, NULL, print_tree_line, (void *) buf);
    1106  }
    1107  
    1108  static size_t strlen_line(struct libscols_line *ln)
    1109  {
    1110  	size_t i, sz = 0;
    1111  
    1112  	assert(ln);
    1113  
    1114  	for (i = 0; i < ln->ncells; i++) {
    1115  		struct libscols_cell *ce = scols_line_get_cell(ln, i);
    1116  		const char *data = ce ? scols_cell_get_data(ce) : NULL;
    1117  
    1118  		sz += data ? strlen(data) : 0;
    1119  	}
    1120  
    1121  	return sz;
    1122  }
    1123  
    1124  void __scols_cleanup_printing(struct libscols_table *tb, struct ul_buffer *buf)
    1125  {
    1126  	if (!tb)
    1127  		return;
    1128  
    1129  	ul_buffer_free_data(buf);
    1130  
    1131  	if (tb->priv_symbols) {
    1132  		scols_table_set_symbols(tb, NULL);
    1133  		tb->priv_symbols = 0;
    1134  	}
    1135  }
    1136  
    1137  int __scols_initialize_printing(struct libscols_table *tb, struct ul_buffer *buf)
    1138  {
    1139  	size_t bufsz, extra_bufsz = 0;
    1140  	struct libscols_line *ln;
    1141  	struct libscols_iter itr;
    1142  	int rc;
    1143  
    1144  	DBG(TAB, ul_debugobj(tb, "initialize printing"));
    1145  
    1146  	if (!tb->symbols) {
    1147  		rc = scols_table_set_default_symbols(tb);
    1148  		if (rc)
    1149  			goto err;
    1150  		tb->priv_symbols = 1;
    1151  	} else
    1152  		tb->priv_symbols = 0;
    1153  
    1154  	if (tb->format == SCOLS_FMT_HUMAN)
    1155  		tb->is_term = tb->termforce == SCOLS_TERMFORCE_NEVER  ? 0 :
    1156  			      tb->termforce == SCOLS_TERMFORCE_ALWAYS ? 1 :
    1157  			      isatty(STDOUT_FILENO);
    1158  
    1159  	if (tb->is_term) {
    1160  		size_t width = (size_t) scols_table_get_termwidth(tb);
    1161  
    1162  		if (tb->termreduce > 0 && tb->termreduce < width) {
    1163  			width -= tb->termreduce;
    1164  			scols_table_set_termwidth(tb, width);
    1165  		}
    1166  		bufsz = width;
    1167  	} else
    1168  		bufsz = BUFSIZ;
    1169  
    1170  	if (!tb->is_term || tb->format != SCOLS_FMT_HUMAN || scols_table_is_tree(tb))
    1171  		tb->header_repeat = 0;
    1172  
    1173  	/*
    1174  	 * Estimate extra space necessary for tree, JSON or another output
    1175  	 * decoration.
    1176  	 */
    1177  	if (scols_table_is_tree(tb))
    1178  		extra_bufsz += tb->nlines * strlen(vertical_symbol(tb));
    1179  
    1180  	switch (tb->format) {
    1181  	case SCOLS_FMT_RAW:
    1182  		extra_bufsz += tb->ncols;			/* separator between columns */
    1183  		break;
    1184  	case SCOLS_FMT_JSON:
    1185  		ul_jsonwrt_init(&tb->json, tb->out, 0);
    1186  		extra_bufsz += tb->nlines * 3;		/* indentation */
    1187  		/* fallthrough */
    1188  	case SCOLS_FMT_EXPORT:
    1189  	{
    1190  		struct libscols_column *cl;
    1191  
    1192  		scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
    1193  
    1194  		while (scols_table_next_column(tb, &itr, &cl) == 0) {
    1195  			if (scols_column_is_hidden(cl))
    1196  				continue;
    1197  
    1198  			if (scols_column_get_name(cl))
    1199  				extra_bufsz += strlen(scols_column_get_name(cl));	/* data */
    1200  			extra_bufsz += 2;						/* separators */
    1201  		}
    1202  		break;
    1203  	}
    1204  	case SCOLS_FMT_HUMAN:
    1205  		break;
    1206  	}
    1207  
    1208  	/*
    1209  	 * Enlarge buffer if necessary, the buffer should be large enough to
    1210  	 * store line data and tree ascii art (or another decoration).
    1211  	 */
    1212  	scols_reset_iter(&itr, SCOLS_ITER_FORWARD);
    1213  	while (scols_table_next_line(tb, &itr, &ln) == 0) {
    1214  		size_t sz;
    1215  
    1216  		sz = strlen_line(ln) + extra_bufsz;
    1217  		if (sz > bufsz)
    1218  			bufsz = sz;
    1219  	}
    1220  
    1221  	/* pre-allocate space for data */
    1222  	rc = ul_buffer_alloc_data(buf, bufsz + 1);	/* data + space for \0 */
    1223  	if (rc)
    1224  		goto err;
    1225  
    1226  	/*
    1227  	 * Make sure groups members are in the same orders as the tree
    1228  	 */
    1229  	if (has_groups(tb) && scols_table_is_tree(tb))
    1230  		scols_groups_fix_members_order(tb);
    1231  
    1232  	if (tb->format == SCOLS_FMT_HUMAN) {
    1233  		rc = __scols_calculate(tb, buf);
    1234  		if (rc != 0)
    1235  			goto err;
    1236  	}
    1237  
    1238  	return 0;
    1239  err:
    1240  	__scols_cleanup_printing(tb, buf);
    1241  	return rc;
    1242  }
    1243