| /* |
| * 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); |
| 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; |
| } |