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  */
scols_new_line(void)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  */
scols_ref_line(struct libscols_line * ln)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  */
scols_unref_line(struct libscols_line * ln)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  */
scols_line_free_cells(struct libscols_line * ln)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  */
scols_line_alloc_cells(struct libscols_line * ln,size_t n)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 
scols_line_move_cells(struct libscols_line * ln,size_t newn,size_t oldn)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  */
scols_line_set_userdata(struct libscols_line * ln,void * data)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  */
scols_line_get_userdata(struct libscols_line * ln)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  */
scols_line_remove_child(struct libscols_line * ln,struct libscols_line * child)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  */
scols_line_add_child(struct libscols_line * ln,struct libscols_line * child)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  */
scols_line_get_parent(const struct libscols_line * ln)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  */
scols_line_has_children(struct libscols_line * ln)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  */
scols_line_next_child(struct libscols_line * ln,struct libscols_iter * itr,struct libscols_line ** chld)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 */
scols_line_next_group_child(struct libscols_line * ln,struct libscols_iter * itr,struct libscols_line ** chld)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  */
scols_line_is_ancestor(struct libscols_line * ln,struct libscols_line * parent)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  */
scols_line_set_color(struct libscols_line * ln,const char * color)365 int scols_line_set_color(struct libscols_line *ln, const char *color)
366 {
367 	if (color && isalnum(*color)) {
368 		color = color_sequence_from_colorname(color);
369 		if (!color)
370 			return -EINVAL;
371 	}
372 	return strdup_to_struct_member(ln, color, color);
373 }
374 
375 /**
376  * scols_line_get_color:
377  * @ln: a pointer to a struct libscols_line instance
378  *
379  * Returns: @ln's color string, NULL in case of an error.
380  */
scols_line_get_color(const struct libscols_line * ln)381 const char *scols_line_get_color(const struct libscols_line *ln)
382 {
383 	return ln->color;
384 }
385 
386 /**
387  * scols_line_get_ncells:
388  * @ln: a pointer to a struct libscols_line instance
389  *
390  * Returns: number of cells
391  */
scols_line_get_ncells(const struct libscols_line * ln)392 size_t scols_line_get_ncells(const struct libscols_line *ln)
393 {
394 	return ln->ncells;
395 }
396 
397 /**
398  * scols_line_get_cell:
399  * @ln: a pointer to a struct libscols_line instance
400  * @n: cell number to retrieve
401  *
402  * Returns: the @n-th cell in @ln, NULL in case of an error.
403  */
scols_line_get_cell(struct libscols_line * ln,size_t n)404 struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
405 					  size_t n)
406 {
407 	if (!ln || n >= ln->ncells)
408 		return NULL;
409 	return &ln->cells[n];
410 }
411 
412 /**
413  * scols_line_get_column_cell:
414  * @ln: a pointer to a struct libscols_line instance
415  * @cl: pointer to cell
416  *
417  * Like scols_line_get_cell() by cell is referenced by column.
418  *
419  * Returns: the @n-th cell in @ln, NULL in case of an error.
420  */
scols_line_get_column_cell(struct libscols_line * ln,struct libscols_column * cl)421 struct libscols_cell *scols_line_get_column_cell(
422 			struct libscols_line *ln,
423 			struct libscols_column *cl)
424 {
425 	if (!ln || !cl)
426 		return NULL;
427 
428 	return scols_line_get_cell(ln, cl->seqnum);
429 }
430 
431 /**
432  * scols_line_set_data:
433  * @ln: a pointer to a struct libscols_line instance
434  * @n: number of the cell, whose data is to be set
435  * @data: actual data to set
436  *
437  * Returns: 0, a negative value in case of an error.
438  */
scols_line_set_data(struct libscols_line * ln,size_t n,const char * data)439 int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
440 {
441 	struct libscols_cell *ce = scols_line_get_cell(ln, n);
442 
443 	if (!ce)
444 		return -EINVAL;
445 	return scols_cell_set_data(ce, data);
446 }
447 
448 /**
449  * scols_line_set_column_data:
450  * @ln: a pointer to a struct libscols_line instance
451  * @cl: column, whose data is to be set
452  * @data: actual data to set
453  *
454  * The same as scols_line_set_data() but cell is referenced by column object.
455  *
456  * Returns: 0, a negative value in case of an error.
457  *
458  * Since: 2.28
459  */
scols_line_set_column_data(struct libscols_line * ln,struct libscols_column * cl,const char * data)460 int scols_line_set_column_data(struct libscols_line *ln,
461 			       struct libscols_column *cl,
462 			       const char *data)
463 {
464 	return scols_line_set_data(ln, cl->seqnum, data);
465 }
466 
467 /**
468  * scols_line_refer_data:
469  * @ln: a pointer to a struct libscols_line instance
470  * @n: number of the cell which will refer to @data
471  * @data: actual data to refer to
472  *
473  * Returns: 0, a negative value in case of an error.
474  */
scols_line_refer_data(struct libscols_line * ln,size_t n,char * data)475 int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
476 {
477 	struct libscols_cell *ce = scols_line_get_cell(ln, n);
478 
479 	if (!ce)
480 		return -EINVAL;
481 	return scols_cell_refer_data(ce, data);
482 }
483 
484 /**
485  * scols_line_refer_column_data:
486  * @ln: a pointer to a struct libscols_line instance
487  * @cl: column, whose data is to be set
488  * @data: actual data to refer to
489  *
490  * The same as scols_line_refer_data() but cell is referenced by column object.
491  *
492  * Returns: 0, a negative value in case of an error.
493  *
494  * Since: 2.28
495  */
scols_line_refer_column_data(struct libscols_line * ln,struct libscols_column * cl,char * data)496 int scols_line_refer_column_data(struct libscols_line *ln,
497 			       struct libscols_column *cl,
498 			       char *data)
499 {
500 	return scols_line_refer_data(ln, cl->seqnum, data);
501 }
502 
503 /**
504  * scols_copy_line:
505  * @ln: a pointer to a struct libscols_line instance
506  *
507  * Returns: A newly allocated copy of @ln, NULL in case of an error.
508  */
scols_copy_line(const struct libscols_line * ln)509 struct libscols_line *scols_copy_line(const struct libscols_line *ln)
510 {
511 	struct libscols_line *ret;
512 	size_t i;
513 
514 	if (!ln)
515 		return NULL;
516 
517 	ret = scols_new_line();
518 	if (!ret)
519 		return NULL;
520 	if (scols_line_set_color(ret, ln->color))
521 		goto err;
522 	if (scols_line_alloc_cells(ret, ln->ncells))
523 		goto err;
524 
525 	ret->userdata = ln->userdata;
526 	ret->ncells   = ln->ncells;
527 	ret->seqnum   = ln->seqnum;
528 
529 	DBG(LINE, ul_debugobj(ln, "copy"));
530 
531 	for (i = 0; i < ret->ncells; ++i) {
532 		if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
533 			goto err;
534 	}
535 
536 	return ret;
537 err:
538 	scols_unref_line(ret);
539 	return NULL;
540 }
541