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