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