blob: 60be2c1354480e8053cff637533959f56578289f [file] [log] [blame]
/*
* line.c - functions for table handling at the line level
*
* Copyright (C) 2014 Karel Zak <kzak@redhat.com>
* Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
*/
/**
* SECTION: line
* @title: Line
* @short_description: cells container, also keeps tree (parent->child) information
*
* An API to access and modify per-line data and information.
*/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "smartcolsP.h"
/**
* scols_new_line:
*
* Note that the line is allocated without cells, the cells will be allocated
* later when you add the line to the table. If you want to use the line
* without table then you have to explicitly allocate the cells by
* scols_line_alloc_cells().
*
* Returns: a pointer to a new struct libscols_line instance.
*/
struct libscols_line *scols_new_line(void)
{
struct libscols_line *ln;
ln = calloc(1, sizeof(*ln));
if (!ln)
return NULL;
DBG(LINE, ul_debugobj(ln, "alloc"));
ln->refcount = 1;
INIT_LIST_HEAD(&ln->ln_lines);
INIT_LIST_HEAD(&ln->ln_children);
INIT_LIST_HEAD(&ln->ln_branch);
return ln;
}
/**
* scols_ref_line:
* @ln: a pointer to a struct libscols_line instance
*
* Increases the refcount of @ln.
*/
void scols_ref_line(struct libscols_line *ln)
{
if (ln)
ln->refcount++;
}
/**
* scols_unref_line:
* @ln: a pointer to a struct libscols_line instance
*
* Decreases the refcount of @ln. When the count falls to zero, the instance
* is automatically deallocated.
*/
void scols_unref_line(struct libscols_line *ln)
{
if (ln && --ln->refcount <= 0) {
DBG(CELL, ul_debugobj(ln, "dealloc"));
list_del(&ln->ln_lines);
list_del(&ln->ln_children);
scols_line_free_cells(ln);
free(ln->color);
free(ln);
return;
}
}
/**
* scols_line_free_cells:
* @ln: a pointer to a struct libscols_line instance
*
* Frees the allocated cells referenced to by @ln.
*/
void scols_line_free_cells(struct libscols_line *ln)
{
size_t i;
if (!ln || !ln->cells)
return;
DBG(LINE, ul_debugobj(ln, "free cells"));
for (i = 0; i < ln->ncells; i++)
scols_reset_cell(&ln->cells[i]);
free(ln->cells);
ln->ncells = 0;
ln->cells = NULL;
}
/**
* scols_line_alloc_cells:
* @ln: a pointer to a struct libscols_line instance
* @n: the number of elements
*
* Allocates space for @n cells. This function is optional,
* and libsmartcols automatically allocates necessary cells
* according to number of columns in the table when you add
* the line to the table. See scols_table_add_line().
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
{
struct libscols_cell *ce;
if (!ln)
return -EINVAL;
if (ln->ncells == n)
return 0;
if (!n) {
scols_line_free_cells(ln);
return 0;
}
DBG(LINE, ul_debugobj(ln, "alloc %zu cells", n));
ce = realloc(ln->cells, n * sizeof(struct libscols_cell));
if (!ce)
return -errno;
if (n > ln->ncells)
memset(ce + ln->ncells, 0,
(n - ln->ncells) * sizeof(struct libscols_cell));
ln->cells = ce;
ln->ncells = n;
return 0;
}
int scols_line_move_cells(struct libscols_line *ln, size_t newn, size_t oldn)
{
struct libscols_cell ce;
if (!ln || newn >= ln->ncells || oldn >= ln->ncells)
return -EINVAL;
if (oldn == newn)
return 0;
DBG(LINE, ul_debugobj(ln, "move cells[%zu] -> cells[%zu]", oldn, newn));
/* remember data from old position */
memcpy(&ce, &ln->cells[oldn], sizeof(struct libscols_cell));
/* remove old position (move data behind oldn to oldn) */
if (oldn + 1 < ln->ncells)
memmove(ln->cells + oldn, ln->cells + oldn + 1,
(ln->ncells - oldn - 1) * sizeof(struct libscols_cell));
/* create a space for new position */
if (newn + 1 < ln->ncells)
memmove(ln->cells + newn + 1, ln->cells + newn,
(ln->ncells - newn - 1) * sizeof(struct libscols_cell));
/* copy original data to new position */
memcpy(&ln->cells[newn], &ce, sizeof(struct libscols_cell));
return 0;
}
/**
* scols_line_set_userdata:
* @ln: a pointer to a struct libscols_line instance
* @data: user data
*
* Binds @data to @ln.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_set_userdata(struct libscols_line *ln, void *data)
{
if (!ln)
return -EINVAL;
ln->userdata = data;
return 0;
}
/**
* scols_line_get_userdata:
* @ln: a pointer to a struct libscols_line instance
*
* Returns: user data
*/
void *scols_line_get_userdata(struct libscols_line *ln)
{
return ln->userdata;
}
/**
* scols_line_remove_child:
* @ln: a pointer to a struct libscols_line instance
* @child: a pointer to a struct libscols_line instance
*
* Removes @child as a child of @ln.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child)
{
if (!ln || !child)
return -EINVAL;
DBG(LINE, ul_debugobj(ln, "remove child"));
list_del_init(&child->ln_children);
child->parent = NULL;
scols_unref_line(child);
scols_unref_line(ln);
return 0;
}
/**
* scols_line_add_child:
* @ln: a pointer to a struct libscols_line instance
* @child: a pointer to a struct libscols_line instance
*
* Sets @child as a child of @ln.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
{
if (!ln || !child)
return -EINVAL;
DBG(LINE, ul_debugobj(ln, "add child"));
scols_ref_line(child);
scols_ref_line(ln);
/* unref old<->parent */
if (child->parent)
scols_line_remove_child(child->parent, child);
/* new reference from parent to child */
list_add_tail(&child->ln_children, &ln->ln_branch);
/* new reference from child to parent */
child->parent = ln;
return 0;
}
/**
* scols_line_get_parent:
* @ln: a pointer to a struct libscols_line instance
*
* Returns: a pointer to @ln's parent, NULL in case it has no parent or if there was an error.
*/
struct libscols_line *scols_line_get_parent(const struct libscols_line *ln)
{
return ln ? ln->parent : NULL;
}
/**
* scols_line_has_children:
* @ln: a pointer to a struct libscols_line instance
*
* Returns: 1 if @ln has any children, otherwise 0.
*/
int scols_line_has_children(struct libscols_line *ln)
{
return ln ? !list_empty(&ln->ln_branch) : 0;
}
/**
* scols_line_next_child:
* @ln: a pointer to a struct libscols_line instance
* @itr: a pointer to a struct libscols_iter instance
* @chld: a pointer to a pointer to a struct libscols_line instance
*
* Finds the next child and returns a pointer to it via @chld.
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_next_child(struct libscols_line *ln,
struct libscols_iter *itr,
struct libscols_line **chld)
{
int rc = 1;
if (!ln || !itr || !chld)
return -EINVAL;
*chld = NULL;
if (!itr->head)
SCOLS_ITER_INIT(itr, &ln->ln_branch);
if (itr->p != itr->head) {
SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
rc = 0;
}
return rc;
}
/**
* scols_line_is_ancestor:
* @ln: line
* @parent: potential parent
*
* The function is designed to detect circular dependencies between @ln and
* @parent. It checks if @ln is not any (grand) parent in the @parent's tree.
*
* Since: 2.30
*
* Returns: 0 or 1
*/
int scols_line_is_ancestor(struct libscols_line *ln, struct libscols_line *parent)
{
while (parent) {
if (parent == ln)
return 1;
parent = scols_line_get_parent(parent);
};
return 0;
}
/**
* scols_line_set_color:
* @ln: a pointer to a struct libscols_line instance
* @color: color name or ESC sequence
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_set_color(struct libscols_line *ln, const char *color)
{
if (color && isalnum(*color)) {
color = color_sequence_from_colorname(color);
if (!color)
return -EINVAL;
}
return strdup_to_struct_member(ln, color, color);
}
/**
* scols_line_get_color:
* @ln: a pointer to a struct libscols_line instance
*
* Returns: @ln's color string, NULL in case of an error.
*/
const char *scols_line_get_color(const struct libscols_line *ln)
{
return ln->color;
}
/**
* scols_line_get_ncells:
* @ln: a pointer to a struct libscols_line instance
*
* Returns: number of cells
*/
size_t scols_line_get_ncells(const struct libscols_line *ln)
{
return ln->ncells;
}
/**
* scols_line_get_cell:
* @ln: a pointer to a struct libscols_line instance
* @n: cell number to retrieve
*
* Returns: the @n-th cell in @ln, NULL in case of an error.
*/
struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
size_t n)
{
if (!ln || n >= ln->ncells)
return NULL;
return &ln->cells[n];
}
/**
* scols_line_get_column_cell:
* @ln: a pointer to a struct libscols_line instance
* @cl: pointer to cell
*
* Like scols_line_get_cell() by cell is referenced by column.
*
* Returns: the @n-th cell in @ln, NULL in case of an error.
*/
struct libscols_cell *scols_line_get_column_cell(
struct libscols_line *ln,
struct libscols_column *cl)
{
if (!ln || !cl)
return NULL;
return scols_line_get_cell(ln, cl->seqnum);
}
/**
* scols_line_set_data:
* @ln: a pointer to a struct libscols_line instance
* @n: number of the cell, whose data is to be set
* @data: actual data to set
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
{
struct libscols_cell *ce = scols_line_get_cell(ln, n);
if (!ce)
return -EINVAL;
return scols_cell_set_data(ce, data);
}
/**
* scols_line_set_column_data:
* @ln: a pointer to a struct libscols_line instance
* @cl: column, whose data is to be set
* @data: actual data to set
*
* The same as scols_line_set_data() but cell is referenced by column object.
*
* Returns: 0, a negative value in case of an error.
*
* Since: 2.28
*/
int scols_line_set_column_data(struct libscols_line *ln,
struct libscols_column *cl,
const char *data)
{
return scols_line_set_data(ln, cl->seqnum, data);
}
/**
* scols_line_refer_data:
* @ln: a pointer to a struct libscols_line instance
* @n: number of the cell which will refer to @data
* @data: actual data to refer to
*
* Returns: 0, a negative value in case of an error.
*/
int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
{
struct libscols_cell *ce = scols_line_get_cell(ln, n);
if (!ce)
return -EINVAL;
return scols_cell_refer_data(ce, data);
}
/**
* scols_line_refer_column_data:
* @ln: a pointer to a struct libscols_line instance
* @cl: column, whose data is to be set
* @data: actual data to refer to
*
* The same as scols_line_refer_data() but cell is referenced by column object.
*
* Returns: 0, a negative value in case of an error.
*
* Since: 2.28
*/
int scols_line_refer_column_data(struct libscols_line *ln,
struct libscols_column *cl,
char *data)
{
return scols_line_refer_data(ln, cl->seqnum, data);
}
/**
* scols_copy_line:
* @ln: a pointer to a struct libscols_line instance
*
* Returns: A newly allocated copy of @ln, NULL in case of an error.
*/
struct libscols_line *scols_copy_line(const struct libscols_line *ln)
{
struct libscols_line *ret;
size_t i;
if (!ln)
return NULL;
ret = scols_new_line();
if (!ret)
return NULL;
if (scols_line_set_color(ret, ln->color))
goto err;
if (scols_line_alloc_cells(ret, ln->ncells))
goto err;
ret->userdata = ln->userdata;
ret->ncells = ln->ncells;
ret->seqnum = ln->seqnum;
DBG(LINE, ul_debugobj(ln, "copy"));
for (i = 0; i < ret->ncells; ++i) {
if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
goto err;
}
return ret;
err:
scols_unref_line(ret);
return NULL;
}