(root)/
util-linux-2.39/
libsmartcols/
src/
line.c
       1  /*
       2   * line.c - functions for table handling at the line level
       3   *
       4   * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
       5   * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
       6   *
       7   * This file may be redistributed under the terms of the
       8   * GNU Lesser General Public License.
       9   */
      10  
      11  /**
      12   * SECTION: line
      13   * @title: Line
      14   * @short_description: cells container, also keeps tree (parent->child) information
      15   *
      16   * An API to access and modify per-line data and information.
      17   */
      18  
      19  
      20  #include <stdlib.h>
      21  #include <unistd.h>
      22  #include <string.h>
      23  #include <ctype.h>
      24  
      25  #include "smartcolsP.h"
      26  
      27  /**
      28   * scols_new_line:
      29   *
      30   * Note that the line is allocated without cells, the cells will be allocated
      31   * later when you add the line to the table. If you want to use the line
      32   * without table then you have to explicitly allocate the cells by
      33   * scols_line_alloc_cells().
      34   *
      35   * Returns: a pointer to a new struct libscols_line instance.
      36   */
      37  struct libscols_line *scols_new_line(void)
      38  {
      39  	struct libscols_line *ln;
      40  
      41  	ln = calloc(1, sizeof(*ln));
      42  	if (!ln)
      43  		return NULL;
      44  
      45  	DBG(LINE, ul_debugobj(ln, "alloc"));
      46  	ln->refcount = 1;
      47  	INIT_LIST_HEAD(&ln->ln_lines);
      48  	INIT_LIST_HEAD(&ln->ln_children);
      49  	INIT_LIST_HEAD(&ln->ln_branch);
      50  	INIT_LIST_HEAD(&ln->ln_groups);
      51  	return ln;
      52  }
      53  
      54  /**
      55   * scols_ref_line:
      56   * @ln: a pointer to a struct libscols_line instance
      57   *
      58   * Increases the refcount of @ln.
      59   */
      60  void scols_ref_line(struct libscols_line *ln)
      61  {
      62  	if (ln)
      63  		ln->refcount++;
      64  }
      65  
      66  /**
      67   * scols_unref_line:
      68   * @ln: a pointer to a struct libscols_line instance
      69   *
      70   * Decreases the refcount of @ln. When the count falls to zero, the instance
      71   * is automatically deallocated.
      72   */
      73  void scols_unref_line(struct libscols_line *ln)
      74  {
      75  	if (ln && --ln->refcount <= 0) {
      76  		DBG(CELL, ul_debugobj(ln, "dealloc"));
      77  		list_del(&ln->ln_lines);
      78  		list_del(&ln->ln_children);
      79  		list_del(&ln->ln_groups);
      80  		scols_unref_group(ln->group);
      81  		scols_line_free_cells(ln);
      82  		free(ln->color);
      83  		free(ln);
      84  		return;
      85  	}
      86  }
      87  
      88  /**
      89   * scols_line_free_cells:
      90   * @ln: a pointer to a struct libscols_line instance
      91   *
      92   * Frees the allocated cells referenced to by @ln.
      93   */
      94  void scols_line_free_cells(struct libscols_line *ln)
      95  {
      96  	size_t i;
      97  
      98  	if (!ln || !ln->cells)
      99  		return;
     100  
     101  	DBG(LINE, ul_debugobj(ln, "free cells"));
     102  
     103  	for (i = 0; i < ln->ncells; i++)
     104  		scols_reset_cell(&ln->cells[i]);
     105  
     106  	free(ln->cells);
     107  	ln->ncells = 0;
     108  	ln->cells = NULL;
     109  }
     110  
     111  /**
     112   * scols_line_alloc_cells:
     113   * @ln: a pointer to a struct libscols_line instance
     114   * @n: the number of elements
     115   *
     116   * Allocates space for @n cells. This function is optional,
     117   * and libsmartcols automatically allocates necessary cells
     118   * according to number of columns in the table when you add
     119   * the line to the table. See scols_table_add_line().
     120   *
     121   * Returns: 0, a negative value in case of an error.
     122   */
     123  int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
     124  {
     125  	struct libscols_cell *ce;
     126  
     127  	if (!ln)
     128  		return -EINVAL;
     129  	if (ln->ncells == n)
     130  		return 0;
     131  
     132  	if (!n) {
     133  		scols_line_free_cells(ln);
     134  		return 0;
     135  	}
     136  
     137  	DBG(LINE, ul_debugobj(ln, "alloc %zu cells", n));
     138  
     139  	ce = realloc(ln->cells, n * sizeof(struct libscols_cell));
     140  	if (!ce)
     141  		return -errno;
     142  
     143  	if (n > ln->ncells)
     144  		memset(ce + ln->ncells, 0,
     145  		       (n - ln->ncells) * sizeof(struct libscols_cell));
     146  
     147  	ln->cells = ce;
     148  	ln->ncells = n;
     149  	return 0;
     150  }
     151  
     152  int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn)
     153  {
     154  	struct libscols_cell ce;
     155  
     156  	if (!ln || newn >= ln->ncells || oldn >= ln->ncells)
     157  		return -EINVAL;
     158  	if (oldn == newn)
     159  		return 0;
     160  
     161  	DBG(LINE, ul_debugobj(ln, "move cells[%zu] -> cells[%zu]", oldn, newn));
     162  
     163  	/* remember data from old position */
     164  	memcpy(&ce, &ln->cells[oldn], sizeof(struct libscols_cell));
     165  
     166  	/* remove old position (move data behind oldn to oldn) */
     167  	if (oldn + 1 < ln->ncells)
     168  		memmove(ln->cells + oldn, ln->cells + oldn + 1,
     169  			(ln->ncells - oldn - 1) * sizeof(struct libscols_cell));
     170  
     171  	/* create a space for new position */
     172  	if (newn + 1 < ln->ncells)
     173  		memmove(ln->cells + newn + 1, ln->cells + newn,
     174  			(ln->ncells - newn - 1) * sizeof(struct libscols_cell));
     175  
     176  	/* copy original data to new position */
     177  	memcpy(&ln->cells[newn], &ce, sizeof(struct libscols_cell));
     178  	return 0;
     179  }
     180  
     181  /**
     182   * scols_line_set_userdata:
     183   * @ln: a pointer to a struct libscols_line instance
     184   * @data: user data
     185   *
     186   * Binds @data to @ln.
     187   *
     188   * Returns: 0, a negative value in case of an error.
     189   */
     190  int scols_line_set_userdata(struct libscols_line *ln, void *data)
     191  {
     192  	if (!ln)
     193  		return -EINVAL;
     194  	ln->userdata = data;
     195  	return 0;
     196  }
     197  
     198  /**
     199   * scols_line_get_userdata:
     200   * @ln: a pointer to a struct libscols_line instance
     201   *
     202   * Returns: user data
     203   */
     204  void *scols_line_get_userdata(struct libscols_line *ln)
     205  {
     206  	return ln->userdata;
     207  }
     208  
     209  /**
     210   * scols_line_remove_child:
     211   * @ln: a pointer to a struct libscols_line instance
     212   * @child: a pointer to a struct libscols_line instance
     213   *
     214   * Removes @child as a child of @ln.
     215   *
     216   * Returns: 0, a negative value in case of an error.
     217   */
     218  int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child)
     219  {
     220  	if (!ln || !child)
     221  		return -EINVAL;
     222  
     223  	DBG(LINE, ul_debugobj(ln, "remove child"));
     224  
     225  	list_del_init(&child->ln_children);
     226  	child->parent = NULL;
     227  	scols_unref_line(child);
     228  
     229  	scols_unref_line(ln);
     230  	return 0;
     231  }
     232  
     233  /**
     234   * scols_line_add_child:
     235   * @ln: a pointer to a struct libscols_line instance
     236   * @child: a pointer to a struct libscols_line instance
     237   *
     238   * Sets @child as a child of @ln.
     239   *
     240   * Returns: 0, a negative value in case of an error.
     241   */
     242  int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
     243  {
     244  	if (!ln || !child)
     245  		return -EINVAL;
     246  
     247  	DBG(LINE, ul_debugobj(ln, "add child"));
     248  	scols_ref_line(child);
     249  	scols_ref_line(ln);
     250  
     251  	/* unref old<->parent */
     252  	if (child->parent)
     253  		scols_line_remove_child(child->parent, child);
     254  
     255  	/* new reference from parent to child */
     256  	list_add_tail(&child->ln_children, &ln->ln_branch);
     257  
     258  	/* new reference from child to parent */
     259  	child->parent = ln;
     260  	return 0;
     261  }
     262  
     263  /**
     264   * scols_line_get_parent:
     265   * @ln: a pointer to a struct libscols_line instance
     266   *
     267   * Returns: a pointer to @ln's parent, NULL in case it has no parent or if there was an error.
     268   */
     269  struct libscols_line *scols_line_get_parent(const struct libscols_line *ln)
     270  {
     271  	return ln ? ln->parent : NULL;
     272  }
     273  
     274  /**
     275   * scols_line_has_children:
     276   * @ln: a pointer to a struct libscols_line instance
     277   *
     278   * Returns: 1 if @ln has any children, otherwise 0.
     279   */
     280  int scols_line_has_children(struct libscols_line *ln)
     281  {
     282  	return ln ? !list_empty(&ln->ln_branch) : 0;
     283  }
     284  
     285  /**
     286   * scols_line_next_child:
     287   * @ln: a pointer to a struct libscols_line instance
     288   * @itr: a pointer to a struct libscols_iter instance
     289   * @chld: a pointer to a pointer to a struct libscols_line instance
     290   *
     291   * Finds the next child and returns a pointer to it via @chld.
     292   *
     293   * Returns: 0, a negative value in case of an error.
     294   */
     295  int scols_line_next_child(struct libscols_line *ln,
     296  			  struct libscols_iter *itr,
     297  			  struct libscols_line **chld)
     298  {
     299  	int rc = 1;
     300  
     301  	if (!ln || !itr || !chld)
     302  		return -EINVAL;
     303  	*chld = NULL;
     304  
     305  	if (!itr->head)
     306  		SCOLS_ITER_INIT(itr, &ln->ln_branch);
     307  	if (itr->p != itr->head) {
     308  		SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
     309  		rc = 0;
     310  	}
     311  
     312  	return rc;
     313  }
     314  
     315  /* private API */
     316  int scols_line_next_group_child(struct libscols_line *ln,
     317  			  struct libscols_iter *itr,
     318  			  struct libscols_line **chld)
     319  {
     320  	int rc = 1;
     321  
     322  	if (!ln || !itr || !chld || !ln->group)
     323  		return -EINVAL;
     324  	*chld = NULL;
     325  
     326  	if (!itr->head)
     327  		SCOLS_ITER_INIT(itr, &ln->group->gr_children);
     328  	if (itr->p != itr->head) {
     329  		SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
     330  		rc = 0;
     331  	}
     332  
     333  	return rc;
     334  }
     335  
     336  /**
     337   * scols_line_is_ancestor:
     338   * @ln: line
     339   * @parent: potential parent
     340   *
     341   * The function is designed to detect circular dependencies between @ln and
     342   * @parent. It checks if @ln is not any (grand) parent in the @parent's tree.
     343   *
     344   * Since: 2.30
     345   *
     346   * Returns: 0 or 1
     347   */
     348  int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent)
     349  {
     350  	while (parent) {
     351  		if (parent == ln)
     352  			return 1;
     353  		parent = scols_line_get_parent(parent);
     354  	};
     355  	return 0;
     356  }
     357  
     358  /**
     359   * scols_line_set_color:
     360   * @ln: a pointer to a struct libscols_line instance
     361   * @color: color name or ESC sequence
     362   *
     363   * Returns: 0, a negative value in case of an error.
     364   */
     365  int scols_line_set_color(struct libscols_line *ln, const char *color)
     366  {
     367  	if (color && !color_is_sequence(color)) {
     368  		char *seq = color_get_sequence(color);
     369  		if (!seq)
     370  			return -EINVAL;
     371  		free(ln->color);
     372  		ln->color = seq;
     373  		return 0;
     374  	}
     375  	return strdup_to_struct_member(ln, color, color);
     376  }
     377  
     378  /**
     379   * scols_line_get_color:
     380   * @ln: a pointer to a struct libscols_line instance
     381   *
     382   * Returns: @ln's color string, NULL in case of an error.
     383   */
     384  const char *scols_line_get_color(const struct libscols_line *ln)
     385  {
     386  	return ln->color;
     387  }
     388  
     389  /**
     390   * scols_line_get_ncells:
     391   * @ln: a pointer to a struct libscols_line instance
     392   *
     393   * Returns: number of cells
     394   */
     395  size_t scols_line_get_ncells(const struct libscols_line *ln)
     396  {
     397  	return ln->ncells;
     398  }
     399  
     400  /**
     401   * scols_line_get_cell:
     402   * @ln: a pointer to a struct libscols_line instance
     403   * @n: cell number to retrieve
     404   *
     405   * Returns: the @n-th cell in @ln, NULL in case of an error.
     406   */
     407  struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
     408  					  size_t n)
     409  {
     410  	if (!ln || n >= ln->ncells)
     411  		return NULL;
     412  	return &ln->cells[n];
     413  }
     414  
     415  /**
     416   * scols_line_get_column_cell:
     417   * @ln: a pointer to a struct libscols_line instance
     418   * @cl: pointer to cell
     419   *
     420   * Like scols_line_get_cell() by cell is referenced by column.
     421   *
     422   * Returns: the @n-th cell in @ln, NULL in case of an error.
     423   */
     424  struct libscols_cell *scols_line_get_column_cell(
     425  			struct libscols_line *ln,
     426  			struct libscols_column *cl)
     427  {
     428  	if (!ln || !cl)
     429  		return NULL;
     430  
     431  	return scols_line_get_cell(ln, cl->seqnum);
     432  }
     433  
     434  /**
     435   * scols_line_set_data:
     436   * @ln: a pointer to a struct libscols_line instance
     437   * @n: number of the cell, whose data is to be set
     438   * @data: actual data to set
     439   *
     440   * Returns: 0, a negative value in case of an error.
     441   */
     442  int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
     443  {
     444  	struct libscols_cell *ce = scols_line_get_cell(ln, n);
     445  
     446  	if (!ce)
     447  		return -EINVAL;
     448  	return scols_cell_set_data(ce, data);
     449  }
     450  
     451  /**
     452   * scols_line_set_column_data:
     453   * @ln: a pointer to a struct libscols_line instance
     454   * @cl: column, whose data is to be set
     455   * @data: actual data to set
     456   *
     457   * The same as scols_line_set_data() but cell is referenced by column object.
     458   *
     459   * Returns: 0, a negative value in case of an error.
     460   *
     461   * Since: 2.28
     462   */
     463  int scols_line_set_column_data(struct libscols_line *ln,
     464  			       struct libscols_column *cl,
     465  			       const char *data)
     466  {
     467  	return scols_line_set_data(ln, cl->seqnum, data);
     468  }
     469  
     470  /**
     471   * scols_line_get_column_data:
     472   * @ln: a pointer to a struct libscols_line instance
     473   * @cl: column, whose data is to be get
     474   *
     475   * See also scols_cell_get_data()
     476   *
     477   * Returns: cell data or NULL.
     478   *
     479   * Since: 2.38
     480   */
     481  const char *scols_line_get_column_data(struct libscols_line *ln,
     482  			       struct libscols_column *cl)
     483  {
     484  	struct libscols_cell *cell = scols_line_get_column_cell(ln, cl);
     485  
     486  	return cell ? scols_cell_get_data(cell) : NULL;
     487  }
     488  
     489  
     490  /**
     491   * scols_line_refer_data:
     492   * @ln: a pointer to a struct libscols_line instance
     493   * @n: number of the cell which will refer to @data
     494   * @data: actual data to refer to
     495   *
     496   * Returns: 0, a negative value in case of an error.
     497   */
     498  int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
     499  {
     500  	struct libscols_cell *ce = scols_line_get_cell(ln, n);
     501  
     502  	if (!ce)
     503  		return -EINVAL;
     504  	return scols_cell_refer_data(ce, data);
     505  }
     506  
     507  /**
     508   * scols_line_refer_column_data:
     509   * @ln: a pointer to a struct libscols_line instance
     510   * @cl: column, whose data is to be set
     511   * @data: actual data to refer to
     512   *
     513   * The same as scols_line_refer_data() but cell is referenced by column object.
     514   *
     515   * Returns: 0, a negative value in case of an error.
     516   *
     517   * Since: 2.28
     518   */
     519  int scols_line_refer_column_data(struct libscols_line *ln,
     520  			       struct libscols_column *cl,
     521  			       char *data)
     522  {
     523  	return scols_line_refer_data(ln, cl->seqnum, data);
     524  }
     525  
     526  /**
     527   * scols_copy_line:
     528   * @ln: a pointer to a struct libscols_line instance
     529   *
     530   * Returns: A newly allocated copy of @ln, NULL in case of an error.
     531   */
     532  struct libscols_line *scols_copy_line(const struct libscols_line *ln)
     533  {
     534  	struct libscols_line *ret;
     535  	size_t i;
     536  
     537  	if (!ln)
     538  		return NULL;
     539  
     540  	ret = scols_new_line();
     541  	if (!ret)
     542  		return NULL;
     543  	if (scols_line_set_color(ret, ln->color))
     544  		goto err;
     545  	if (scols_line_alloc_cells(ret, ln->ncells))
     546  		goto err;
     547  
     548  	ret->userdata = ln->userdata;
     549  	ret->ncells   = ln->ncells;
     550  	ret->seqnum   = ln->seqnum;
     551  
     552  	DBG(LINE, ul_debugobj(ln, "copy"));
     553  
     554  	for (i = 0; i < ret->ncells; ++i) {
     555  		if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
     556  			goto err;
     557  	}
     558  
     559  	return ret;
     560  err:
     561  	scols_unref_line(ret);
     562  	return NULL;
     563  }