/* * line.c - functions for table handling at the line level * * Copyright (C) 2014 Karel Zak * Copyright (C) 2014 Ondrej Oprala * * 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 #include #include #include #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); INIT_LIST_HEAD(&ln->ln_groups); 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); list_del(&ln->ln_groups); scols_unref_group(ln->group); 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; } /* private API */ int scols_line_next_group_child(struct libscols_line *ln, struct libscols_iter *itr, struct libscols_line **chld) { int rc = 1; if (!ln || !itr || !chld || !ln->group) return -EINVAL; *chld = NULL; if (!itr->head) SCOLS_ITER_INIT(itr, &ln->group->gr_children); 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; }