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