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: line API
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 	return ln;
51 }
52 
53 /**
54  * scols_ref_line:
55  * @ln: a pointer to a struct libscols_line instance
56  *
57  * Increases the refcount of @ln.
58  */
scols_ref_line(struct libscols_line * ln)59 void scols_ref_line(struct libscols_line *ln)
60 {
61 	if (ln)
62 		ln->refcount++;
63 }
64 
65 /**
66  * scols_unref_line:
67  * @ln: a pointer to a struct libscols_line instance
68  *
69  * Decreases the refcount of @ln. When the count falls to zero, the instance
70  * is automatically deallocated.
71  */
scols_unref_line(struct libscols_line * ln)72 void scols_unref_line(struct libscols_line *ln)
73 {
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 		scols_line_free_cells(ln);
80 		free(ln->color);
81 		free(ln);
82 		return;
83 	}
84 }
85 
86 /**
87  * scols_line_free_cells:
88  * @ln: a pointer to a struct libscols_line instance
89  *
90  * Frees the allocated cells referenced to by @ln.
91  */
scols_line_free_cells(struct libscols_line * ln)92 void scols_line_free_cells(struct libscols_line *ln)
93 {
94 	size_t i;
95 
96 	if (!ln || !ln->cells)
97 		return;
98 
99 	DBG(LINE, ul_debugobj(ln, "free cells"));
100 
101 	for (i = 0; i < ln->ncells; i++)
102 		scols_reset_cell(&ln->cells[i]);
103 
104 	free(ln->cells);
105 	ln->ncells = 0;
106 	ln->cells = NULL;
107 }
108 
109 /**
110  * scols_line_alloc_cells:
111  * @ln: a pointer to a struct libscols_line instance
112  * @n: the number of elements
113  *
114  * Allocates space for @n cells. This function is optional,
115  * and libsmartcols automatically allocates necessary cells
116  * according to number of columns in the table when you add
117  * the line to the table. See scols_table_add_line().
118  *
119  * Returns: 0, a negative value in case of an error.
120  */
scols_line_alloc_cells(struct libscols_line * ln,size_t n)121 int scols_line_alloc_cells(struct libscols_line *ln, size_t n)
122 {
123 	struct libscols_cell *ce;
124 
125 	assert(ln);
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 /**
153  * scols_line_set_userdata:
154  * @ln: a pointer to a struct libscols_line instance
155  * @data: user data
156  *
157  * Binds @data to @ln.
158  *
159  * Returns: 0, a negative value in case of an error.
160  */
scols_line_set_userdata(struct libscols_line * ln,void * data)161 int scols_line_set_userdata(struct libscols_line *ln, void *data)
162 {
163 	assert(ln);
164 	if (!ln)
165 		return -EINVAL;
166 	ln->userdata = data;
167 	return 0;
168 }
169 
170 /**
171  * scols_line_get_userdata:
172  * @ln: a pointer to a struct libscols_line instance
173  *
174  * Returns: 0, a negative value in case of an error.
175  */
scols_line_get_userdata(struct libscols_line * ln)176 void *scols_line_get_userdata(struct libscols_line *ln)
177 {
178 	assert(ln);
179 	return ln ? ln->userdata : NULL;
180 }
181 
182 /**
183  * scols_line_remove_child:
184  * @ln: a pointer to a struct libscols_line instance
185  * @child: a pointer to a struct libscols_line instance
186  *
187  * Removes @child as a child of @ln.
188  *
189  * Returns: 0, a negative value in case of an error.
190  */
scols_line_remove_child(struct libscols_line * ln,struct libscols_line * child)191 int scols_line_remove_child(struct libscols_line *ln, struct libscols_line *child)
192 {
193 	assert(ln);
194 	assert(child);
195 
196 	if (!ln || !child)
197 		return -EINVAL;
198 
199 	DBG(LINE, ul_debugobj(ln, "remove child %p", child));
200 
201 	list_del_init(&child->ln_children);
202 	child->parent = NULL;
203 	scols_unref_line(child);
204 
205 	scols_unref_line(ln);
206 	return 0;
207 }
208 
209 /**
210  * scols_line_add_child:
211  * @ln: a pointer to a struct libscols_line instance
212  * @child: a pointer to a struct libscols_line instance
213  *
214  * Sets @child as a child of @ln.
215  *
216  * Returns: 0, a negative value in case of an error.
217  */
scols_line_add_child(struct libscols_line * ln,struct libscols_line * child)218 int scols_line_add_child(struct libscols_line *ln, struct libscols_line *child)
219 {
220 	assert(ln);
221 	assert(child);
222 
223 	if (!ln || !child)
224 		return -EINVAL;
225 
226 	/* unref old<->parent */
227 	if (child->parent)
228 		scols_line_remove_child(child->parent, child);
229 
230 	DBG(LINE, ul_debugobj(ln, "add child %p", child));
231 
232 	/* new reference from parent to child */
233 	list_add_tail(&child->ln_children, &ln->ln_branch);
234 	scols_ref_line(child);
235 
236 	/* new reference from child to parent */
237 	child->parent = ln;
238 	scols_ref_line(ln);
239 
240 	return 0;
241 }
242 
243 /**
244  * scols_line_get_parent:
245  * @ln: a pointer to a struct libscols_line instance
246  *
247  * Returns: a pointer to @ln's parent, NULL in case it has no parent or if there was an error.
248  */
scols_line_get_parent(struct libscols_line * ln)249 struct libscols_line *scols_line_get_parent(struct libscols_line *ln)
250 {
251 	assert(ln);
252 	return ln ? ln->parent : NULL;
253 }
254 
255 /**
256  * scols_line_has_children:
257  * @ln: a pointer to a struct libscols_line instance
258  *
259  * Returns: 1 if @ln has any children, otherwise 0.
260  */
scols_line_has_children(struct libscols_line * ln)261 int scols_line_has_children(struct libscols_line *ln)
262 {
263 	assert(ln);
264 	return ln ? !list_empty(&ln->ln_branch) : 0;
265 }
266 
267 /**
268  * scols_line_next_child:
269  * @ln: a pointer to a struct libscols_line instance
270  * @itr: a pointer to a struct libscols_iter instance
271  * @chld: a pointer to a pointer to a struct libscols_line instance
272  *
273  * Finds the next child and returns a pointer to it via @chld.
274  *
275  * Returns: 0, a negative value in case of an error.
276  */
scols_line_next_child(struct libscols_line * ln,struct libscols_iter * itr,struct libscols_line ** chld)277 int scols_line_next_child(struct libscols_line *ln,
278 			  struct libscols_iter *itr,
279 			  struct libscols_line **chld)
280 {
281 	int rc = 1;
282 
283 	if (!ln || !itr || !chld)
284 		return -EINVAL;
285 	*chld = NULL;
286 
287 	if (!itr->head)
288 		SCOLS_ITER_INIT(itr, &ln->ln_branch);
289 	if (itr->p != itr->head) {
290 		SCOLS_ITER_ITERATE(itr, *chld, struct libscols_line, ln_children);
291 		rc = 0;
292 	}
293 
294 	return rc;
295 }
296 
297 /**
298  * scols_line_set_color:
299  * @ln: a pointer to a struct libscols_line instance
300  * @color: color name or ESC sequence
301  *
302  * Returns: 0, a negative value in case of an error.
303  */
scols_line_set_color(struct libscols_line * ln,const char * color)304 int scols_line_set_color(struct libscols_line *ln, const char *color)
305 {
306 	char *p = NULL;
307 
308 	assert(ln);
309 	if (!ln)
310 		return -EINVAL;
311 	if (color) {
312 		if (isalnum(*color)) {
313 			color = color_sequence_from_colorname(color);
314 
315 			if (!color)
316 				return -EINVAL;
317 		}
318 		p = strdup(color);
319 		if (!p)
320 			return -ENOMEM;
321 	}
322 
323 	free(ln->color);
324 	ln->color = p;
325 	return 0;
326 }
327 
328 /**
329  * scols_line_get_color:
330  * @ln: a pointer to a struct libscols_line instance
331  *
332  * Returns: @ln's color string, NULL in case of an error.
333  */
scols_line_get_color(struct libscols_line * ln)334 const char *scols_line_get_color(struct libscols_line *ln)
335 {
336 	assert(ln);
337 	return ln ? ln->color : NULL;
338 }
339 
340 /**
341  * scols_line_get_ncells:
342  * @ln: a pointer to a struct libscols_line instance
343  *
344  * Returns: @ln's number of cells
345  */
scols_line_get_ncells(struct libscols_line * ln)346 size_t scols_line_get_ncells(struct libscols_line *ln)
347 {
348 	assert(ln);
349 	return ln ? ln->ncells : 0;
350 }
351 
352 /**
353  * scols_line_get_cell:
354  * @ln: a pointer to a struct libscols_line instance
355  * @n: cell number to retrieve
356  *
357  * Returns: the @n-th cell in @ln, NULL in case of an error.
358  */
scols_line_get_cell(struct libscols_line * ln,size_t n)359 struct libscols_cell *scols_line_get_cell(struct libscols_line *ln,
360 					  size_t n)
361 {
362 	assert(ln);
363 
364 	if (!ln || n >= ln->ncells)
365 		return NULL;
366 	return &ln->cells[n];
367 }
368 
369 /**
370  * scols_line_get_column_cell:
371  * @ln: a pointer to a struct libscols_line instance
372  * @cl: pointer to cell
373  *
374  * Like scols_line_get_cell() by cell is referenced by column.
375  *
376  * Returns: the @n-th cell in @ln, NULL in case of an error.
377  */
scols_line_get_column_cell(struct libscols_line * ln,struct libscols_column * cl)378 struct libscols_cell *scols_line_get_column_cell(
379 			struct libscols_line *ln,
380 			struct libscols_column *cl)
381 {
382 	assert(ln);
383 	assert(cl);
384 
385 	return scols_line_get_cell(ln, cl->seqnum);
386 }
387 
388 /**
389  * scols_line_set_data:
390  * @ln: a pointer to a struct libscols_cell instance
391  * @n: number of the cell, whose data is to be set
392  * @data: actual data to set
393  *
394  * Returns: 0, a negative value in case of an error.
395  */
scols_line_set_data(struct libscols_line * ln,size_t n,const char * data)396 int scols_line_set_data(struct libscols_line *ln, size_t n, const char *data)
397 {
398 	struct libscols_cell *ce = scols_line_get_cell(ln, n);
399 
400 	if (!ce)
401 		return -EINVAL;
402 	return scols_cell_set_data(ce, data);
403 }
404 
405 /**
406  * scols_line_refer_data:
407  * @ln: a pointer to a struct libscols_cell instance
408  * @n: number of the cell which will refer to @data
409  * @data: actual data to refer to
410  *
411  * Returns: 0, a negative value in case of an error.
412  */
scols_line_refer_data(struct libscols_line * ln,size_t n,char * data)413 int scols_line_refer_data(struct libscols_line *ln, size_t n, char *data)
414 {
415 	struct libscols_cell *ce = scols_line_get_cell(ln, n);
416 
417 	if (!ce)
418 		return -EINVAL;
419 	return scols_cell_refer_data(ce, data);
420 }
421 
422 /**
423  * scols_copy_line:
424  * @ln: a pointer to a struct libscols_cell instance
425  *
426  * Returns: A newly allocated copy of @ln, NULL in case of an error.
427  */
scols_copy_line(struct libscols_line * ln)428 struct libscols_line *scols_copy_line(struct libscols_line *ln)
429 {
430 	struct libscols_line *ret;
431 	size_t i;
432 
433 	assert (ln);
434 	if (!ln)
435 		return NULL;
436 
437 	ret = scols_new_line();
438 	if (!ret)
439 		return NULL;
440 	if (scols_line_set_color(ret, ln->color))
441 		goto err;
442 	if (scols_line_alloc_cells(ret, ln->ncells))
443 		goto err;
444 
445 	ret->userdata = ln->userdata;
446 	ret->ncells   = ln->ncells;
447 	ret->seqnum   = ln->seqnum;
448 
449 	DBG(LINE, ul_debugobj(ln, "copy to %p", ret));
450 
451 	for (i = 0; i < ret->ncells; ++i) {
452 		if (scols_cell_copy_content(&ret->cells[i], &ln->cells[i]))
453 			goto err;
454 	}
455 
456 	return ret;
457 err:
458 	scols_unref_line(ret);
459 	return NULL;
460 }
461 
462 
463