]>
Commit | Line | Data |
---|---|---|
3e542c76 KZ |
1 | /* |
2 | * table.c - functions handling the data at the table level | |
3 | * | |
4 | * Copyright (C) 2010-2014 Karel Zak <kzak@redhat.com> | |
9fd9b99f | 5 | * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com> |
3e542c76 KZ |
6 | * |
7 | * This file may be redistributed under the terms of the | |
8 | * GNU Lesser General Public License. | |
9 | */ | |
1d90bcb1 OO |
10 | |
11 | /** | |
e2310281 | 12 | * SECTION: table |
1d90bcb1 | 13 | * @title: Table |
e2310281 | 14 | * @short_description: table data API |
1d90bcb1 | 15 | * |
e2310281 | 16 | * Table data manipulation API. |
1d90bcb1 OO |
17 | */ |
18 | ||
ce44112b | 19 | |
3e542c76 KZ |
20 | #include <stdlib.h> |
21 | #include <unistd.h> | |
22 | #include <string.h> | |
23 | #include <termios.h> | |
24 | #include <ctype.h> | |
25 | ||
26 | #include "nls.h" | |
27 | #include "widechar.h" | |
28 | #include "smartcolsP.h" | |
29 | ||
30 | #ifdef HAVE_WIDECHAR | |
31 | #define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */ | |
32 | #define UTF_VR "\342\224\234" /* U+251C, Vertical and right */ | |
33 | #define UTF_H "\342\224\200" /* U+2500, Horizontal */ | |
34 | #define UTF_UR "\342\224\224" /* U+2514, Up and right */ | |
35 | #endif /* !HAVE_WIDECHAR */ | |
36 | ||
37 | #define is_last_column(_tb, _cl) \ | |
38 | list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns) | |
39 | ||
40 | ||
1d90bcb1 OO |
41 | /** |
42 | * scols_new_table: | |
3e542c76 | 43 | * |
1d90bcb1 | 44 | * Returns: A newly allocated table. |
3e542c76 | 45 | */ |
0925a9dd | 46 | struct libscols_table *scols_new_table(void) |
3e542c76 KZ |
47 | { |
48 | struct libscols_table *tb; | |
49 | ||
50 | tb = calloc(1, sizeof(struct libscols_table)); | |
51 | if (!tb) | |
52 | return NULL; | |
53 | ||
3e542c76 | 54 | tb->refcount = 1; |
571441e2 | 55 | tb->out = stdout; |
3e542c76 KZ |
56 | |
57 | INIT_LIST_HEAD(&tb->tb_lines); | |
58 | INIT_LIST_HEAD(&tb->tb_columns); | |
59 | ||
710ed55d | 60 | DBG(TAB, ul_debugobj(tb, "alloc")); |
1424fe8c | 61 | return tb; |
3e542c76 KZ |
62 | } |
63 | ||
1d90bcb1 OO |
64 | /** |
65 | * scols_ref_table: | |
66 | * @tb: a pointer to a struct libscols_table instance | |
67 | * | |
68 | * Increases the refcount of @tb. | |
69 | */ | |
3e542c76 KZ |
70 | void scols_ref_table(struct libscols_table *tb) |
71 | { | |
72 | if (tb) | |
73 | tb->refcount++; | |
74 | } | |
75 | ||
1d90bcb1 OO |
76 | /** |
77 | * scols_unref_table: | |
78 | * @tb: a pointer to a struct libscols_table instance | |
79 | * | |
80 | * Decreases the refcount of @tb. | |
81 | */ | |
3e542c76 KZ |
82 | void scols_unref_table(struct libscols_table *tb) |
83 | { | |
84 | if (tb && (--tb->refcount <= 0)) { | |
710ed55d | 85 | DBG(TAB, ul_debugobj(tb, "dealloc")); |
3e542c76 KZ |
86 | scols_table_remove_lines(tb); |
87 | scols_table_remove_columns(tb); | |
88 | scols_unref_symbols(tb->symbols); | |
d1b4d14f OO |
89 | free(tb->linesep); |
90 | free(tb->colsep); | |
3e542c76 KZ |
91 | free(tb); |
92 | } | |
93 | } | |
94 | ||
1d90bcb1 OO |
95 | /** |
96 | * scols_table_add_column: | |
97 | * @tb: a pointer to a struct libscols_table instance | |
98 | * @cl: a pointer to a struct libscols_column instance | |
1d90bcb1 | 99 | * |
4418714f | 100 | * Adds @cl to @tb's column list. |
1d90bcb1 OO |
101 | * |
102 | * Returns: 0, a negative number in case of an error. | |
103 | */ | |
0925a9dd | 104 | int scols_table_add_column(struct libscols_table *tb, struct libscols_column *cl) |
3e542c76 KZ |
105 | { |
106 | assert(tb); | |
107 | assert(cl); | |
108 | ||
109 | if (!tb || !cl || !list_empty(&tb->tb_lines)) | |
110 | return -EINVAL; | |
111 | ||
0925a9dd KZ |
112 | if (cl->flags & SCOLS_FL_TREE) |
113 | tb->ntreecols++; | |
b72b824d | 114 | |
710ed55d | 115 | DBG(TAB, ul_debugobj(tb, "add column %p", cl)); |
3e542c76 KZ |
116 | list_add_tail(&cl->cl_columns, &tb->tb_columns); |
117 | cl->seqnum = tb->ncols++; | |
118 | scols_ref_column(cl); | |
119 | ||
120 | /* TODO: | |
121 | * | |
122 | * Currently it's possible to add/remove columns only if the table is | |
123 | * empty (see list_empty(tb->tb_lines) above). It would be nice to | |
124 | * enlarge/reduce lines cells[] always when we add/remove a new column. | |
125 | */ | |
126 | return 0; | |
127 | } | |
128 | ||
1d90bcb1 OO |
129 | /** |
130 | * scols_table_remove_column: | |
131 | * @tb: a pointer to a struct libscols_table instance | |
132 | * @cl: a pointer to a struct libscols_column instance | |
133 | * | |
134 | * Removes @cl from @tb. | |
135 | * | |
136 | * Returns: 0, a negative number in case of an error. | |
137 | */ | |
3e542c76 KZ |
138 | int scols_table_remove_column(struct libscols_table *tb, |
139 | struct libscols_column *cl) | |
140 | { | |
141 | assert(tb); | |
142 | assert(cl); | |
143 | ||
144 | if (!tb || !cl || !list_empty(&tb->tb_lines)) | |
145 | return -EINVAL; | |
146 | ||
0925a9dd KZ |
147 | if (cl->flags & SCOLS_FL_TREE) |
148 | tb->ntreecols--; | |
149 | ||
710ed55d | 150 | DBG(TAB, ul_debugobj(tb, "remove column %p", cl)); |
3e542c76 KZ |
151 | list_del_init(&cl->cl_columns); |
152 | tb->ncols--; | |
153 | scols_unref_column(cl); | |
154 | return 0; | |
155 | } | |
156 | ||
1d90bcb1 OO |
157 | /** |
158 | * scols_table_remove_columns: | |
159 | * @tb: a pointer to a struct libscols_table instance | |
160 | * | |
161 | * Removes all of @tb's columns. | |
162 | * | |
163 | * Returns: 0, a negative number in case of an error. | |
164 | */ | |
3e542c76 KZ |
165 | int scols_table_remove_columns(struct libscols_table *tb) |
166 | { | |
167 | assert(tb); | |
168 | ||
169 | if (!tb || !list_empty(&tb->tb_lines)) | |
170 | return -EINVAL; | |
171 | ||
710ed55d | 172 | DBG(TAB, ul_debugobj(tb, "remove all columns")); |
3e542c76 KZ |
173 | while (!list_empty(&tb->tb_columns)) { |
174 | struct libscols_column *cl = list_entry(tb->tb_columns.next, | |
175 | struct libscols_column, cl_columns); | |
176 | scols_table_remove_column(tb, cl); | |
177 | } | |
178 | return 0; | |
179 | } | |
180 | ||
181 | ||
1d90bcb1 OO |
182 | /** |
183 | * scols_table_new_column: | |
3e542c76 KZ |
184 | * @tb: table |
185 | * @name: column header | |
186 | * @whint: column width hint (absolute width: N > 1; relative width: N < 1) | |
1d90bcb1 | 187 | * @flags: flags integer |
3e542c76 KZ |
188 | * |
189 | * This is shortcut for | |
190 | * | |
191 | * cl = scols_new_column(); | |
192 | * scols_column_set_....(cl, ...); | |
193 | * scols_table_add_column(tb, cl); | |
194 | * | |
195 | * The column width is possible to define by three ways: | |
196 | * | |
197 | * @whint = 0..1 : relative width, percent of terminal width | |
198 | * | |
199 | * @whint = 1..N : absolute width, empty colum will be truncated to | |
200 | * the column header width | |
201 | * | |
202 | * @whint = 1..N | |
3e542c76 | 203 | * |
1d90bcb1 | 204 | * The column is necessary to address by |
3e542c76 KZ |
205 | * sequential number. The first defined column has the colnum = 0. For example: |
206 | * | |
207 | * scols_table_new_column(tab, "FOO", 0.5, 0); // colnum = 0 | |
208 | * scols_table_new_column(tab, "BAR", 0.5, 0); // colnum = 1 | |
209 | * . | |
210 | * . | |
211 | * scols_line_get_cell(line, 0); // FOO column | |
212 | * scols_line_get_cell(line, 1); // BAR column | |
213 | * | |
214 | * Returns: newly allocated column | |
215 | */ | |
216 | struct libscols_column *scols_table_new_column(struct libscols_table *tb, | |
217 | const char *name, | |
8b992cb5 OO |
218 | double whint, |
219 | int flags) | |
3e542c76 KZ |
220 | { |
221 | struct libscols_column *cl; | |
222 | struct libscols_cell *hr; | |
223 | ||
224 | assert (tb); | |
225 | if (!tb) | |
226 | return NULL; | |
710ed55d KZ |
227 | |
228 | DBG(TAB, ul_debugobj(tb, "new column name=%s, whint=%g, flags=%d", | |
229 | name, whint, flags)); | |
3e542c76 KZ |
230 | cl = scols_new_column(); |
231 | if (!cl) | |
232 | return NULL; | |
233 | ||
234 | /* set column name */ | |
235 | hr = scols_column_get_header(cl); | |
236 | if (!hr) | |
237 | goto err; | |
238 | if (scols_cell_set_data(hr, name)) | |
239 | goto err; | |
240 | ||
241 | scols_column_set_whint(cl, whint); | |
8b992cb5 OO |
242 | scols_column_set_flags(cl, flags); |
243 | ||
0925a9dd | 244 | if (scols_table_add_column(tb, cl)) /* this increments column ref-counter */ |
3e542c76 KZ |
245 | goto err; |
246 | ||
247 | scols_unref_column(cl); | |
248 | return cl; | |
249 | err: | |
250 | scols_unref_column(cl); | |
251 | return NULL; | |
252 | } | |
253 | ||
1d90bcb1 OO |
254 | /** |
255 | * scols_table_next_column: | |
256 | * @tb: a pointer to a struct libscols_table instance | |
257 | * @itr: a pointer to a struct libscols_iter instance | |
258 | * @cl: a pointer to a pointer to a struct libscols_column instance | |
259 | * | |
260 | * Returns the next column of @tb via @cl. | |
261 | * | |
262 | * Returns: 0, a negative value in case of an error. | |
263 | */ | |
3e542c76 KZ |
264 | int scols_table_next_column(struct libscols_table *tb, |
265 | struct libscols_iter *itr, | |
266 | struct libscols_column **cl) | |
267 | { | |
268 | int rc = 1; | |
269 | ||
270 | if (!tb || !itr || !cl) | |
271 | return -EINVAL; | |
272 | *cl = NULL; | |
273 | ||
274 | if (!itr->head) | |
275 | SCOLS_ITER_INIT(itr, &tb->tb_columns); | |
276 | if (itr->p != itr->head) { | |
277 | SCOLS_ITER_ITERATE(itr, *cl, struct libscols_column, cl_columns); | |
278 | rc = 0; | |
279 | } | |
280 | ||
281 | return rc; | |
282 | } | |
283 | ||
284 | ||
1d90bcb1 OO |
285 | /** |
286 | * scols_table_get_ncols: | |
3e542c76 KZ |
287 | * @tb: table |
288 | * | |
1d90bcb1 | 289 | * Returns: the ncols table member, a negative number in case of an error. |
3e542c76 KZ |
290 | */ |
291 | int scols_table_get_ncols(struct libscols_table *tb) | |
292 | { | |
293 | assert(tb); | |
294 | return tb ? tb->ncols : -EINVAL; | |
295 | } | |
296 | ||
e2310281 | 297 | /** |
1d90bcb1 | 298 | * scols_table_get_nlines: |
3e542c76 KZ |
299 | * @tb: table |
300 | * | |
1d90bcb1 | 301 | * Returns: the nlines table member, a negative number in case of an error. |
3e542c76 KZ |
302 | */ |
303 | int scols_table_get_nlines(struct libscols_table *tb) | |
304 | { | |
305 | assert(tb); | |
306 | return tb ? tb->nlines : -EINVAL; | |
307 | } | |
308 | ||
1d90bcb1 OO |
309 | /** |
310 | * scols_table_set_stream: | |
311 | * @tb: table | |
312 | * @stream: output stream | |
313 | * | |
314 | * Sets the output stream for table @tb. | |
315 | * | |
316 | * Returns: 0, a negative number in case of an error. | |
317 | */ | |
571441e2 KZ |
318 | int scols_table_set_stream(struct libscols_table *tb, FILE *stream) |
319 | { | |
320 | assert(tb); | |
321 | if (!tb) | |
322 | return -EINVAL; | |
323 | ||
710ed55d | 324 | DBG(TAB, ul_debugobj(tb, "setting alternative stream")); |
571441e2 KZ |
325 | tb->out = stream; |
326 | return 0; | |
327 | } | |
328 | ||
1d90bcb1 OO |
329 | /** |
330 | * scols_table_get_stream: | |
331 | * @tb: table | |
332 | * | |
333 | * Gets the output stream for table @tb. | |
334 | * | |
335 | * Returns: stream pointer, NULL in case of an error or an unset stream. | |
336 | */ | |
571441e2 KZ |
337 | FILE *scols_table_get_stream(struct libscols_table *tb) |
338 | { | |
339 | assert(tb); | |
340 | return tb ? tb->out: NULL; | |
341 | } | |
342 | ||
1d90bcb1 OO |
343 | /** |
344 | * scols_table_reduce_termwidth: | |
345 | * @tb: table | |
346 | * @reduce: width | |
347 | * | |
348 | * Reduce the output width to @reduce. | |
349 | * | |
350 | * Returns: 0, a negative value in case of an error. | |
351 | */ | |
e906be06 KZ |
352 | int scols_table_reduce_termwidth(struct libscols_table *tb, size_t reduce) |
353 | { | |
354 | assert(tb); | |
355 | if (!tb) | |
356 | return -EINVAL; | |
357 | ||
710ed55d | 358 | DBG(TAB, ul_debugobj(tb, "reduce terminal width: %zu", reduce)); |
e906be06 KZ |
359 | tb->termreduce = reduce; |
360 | return 0; | |
361 | } | |
362 | ||
1d90bcb1 OO |
363 | /** |
364 | * scols_table_get_column: | |
3e542c76 | 365 | * @tb: table |
1d90bcb1 | 366 | * @n: number of column (0..N) |
3e542c76 KZ |
367 | * |
368 | * Returns: pointer to column or NULL | |
369 | */ | |
370 | struct libscols_column *scols_table_get_column(struct libscols_table *tb, | |
371 | size_t n) | |
372 | { | |
373 | struct libscols_iter itr; | |
374 | struct libscols_column *cl; | |
375 | ||
376 | assert(tb); | |
377 | if (!tb) | |
378 | return NULL; | |
379 | if (n >= tb->ncols) | |
380 | return NULL; | |
381 | ||
382 | scols_reset_iter(&itr, SCOLS_ITER_FORWARD); | |
383 | while (scols_table_next_column(tb, &itr, &cl) == 0) { | |
384 | if (cl->seqnum == n) | |
385 | return cl; | |
386 | } | |
387 | return NULL; | |
388 | } | |
389 | ||
1d90bcb1 OO |
390 | /** |
391 | * scols_table_add_line: | |
392 | * @tb: table | |
393 | * @ln: line | |
394 | * | |
395 | * Note that this function calls scols_line_alloc_cells() if number | |
3e542c76 | 396 | * of the cells in the line is too small for @tb. |
1d90bcb1 OO |
397 | * |
398 | * Returns: 0, a negative value in case of an error. | |
3e542c76 KZ |
399 | */ |
400 | int scols_table_add_line(struct libscols_table *tb, struct libscols_line *ln) | |
401 | { | |
402 | ||
403 | assert(tb); | |
404 | assert(ln); | |
405 | ||
406 | if (!tb || !ln) | |
407 | return -EINVAL; | |
408 | ||
409 | if (tb->ncols > ln->ncells) { | |
410 | int rc = scols_line_alloc_cells(ln, tb->ncols); | |
411 | if (rc) | |
412 | return rc; | |
413 | } | |
414 | ||
710ed55d | 415 | DBG(TAB, ul_debugobj(tb, "add line %p", ln)); |
3e542c76 KZ |
416 | list_add_tail(&ln->ln_lines, &tb->tb_lines); |
417 | ln->seqnum = tb->nlines++; | |
418 | scols_ref_line(ln); | |
419 | return 0; | |
420 | } | |
421 | ||
1d90bcb1 OO |
422 | /** |
423 | * scols_table_remove_line: | |
424 | * @tb: table | |
425 | * @ln: line | |
426 | * | |
427 | * Note that this function does not destroy the parent<->child relationship between lines. | |
3e542c76 | 428 | * You have to call scols_line_remove_child() |
1d90bcb1 OO |
429 | * |
430 | * Returns: 0, a negative value in case of an error. | |
3e542c76 KZ |
431 | */ |
432 | int scols_table_remove_line(struct libscols_table *tb, | |
433 | struct libscols_line *ln) | |
434 | { | |
435 | assert(tb); | |
436 | assert(ln); | |
437 | ||
438 | if (!tb || !ln) | |
439 | return -EINVAL; | |
440 | ||
710ed55d | 441 | DBG(TAB, ul_debugobj(tb, "remove line %p", ln)); |
3e542c76 KZ |
442 | list_del_init(&ln->ln_lines); |
443 | tb->nlines--; | |
444 | scols_unref_line(ln); | |
445 | return 0; | |
446 | } | |
447 | ||
1d90bcb1 OO |
448 | /** |
449 | * scols_table_remove_lines: | |
450 | * @tb: table | |
451 | * | |
452 | * This empties the table and also destroys all the parent<->child relationships. | |
453 | */ | |
3e542c76 KZ |
454 | void scols_table_remove_lines(struct libscols_table *tb) |
455 | { | |
456 | assert(tb); | |
457 | if (!tb) | |
458 | return; | |
459 | ||
710ed55d | 460 | DBG(TAB, ul_debugobj(tb, "remove all lines")); |
3e542c76 KZ |
461 | while (!list_empty(&tb->tb_lines)) { |
462 | struct libscols_line *ln = list_entry(tb->tb_lines.next, | |
463 | struct libscols_line, ln_lines); | |
464 | if (ln->parent) | |
465 | scols_line_remove_child(ln->parent, ln); | |
466 | scols_table_remove_line(tb, ln); | |
467 | } | |
468 | } | |
469 | ||
1d90bcb1 OO |
470 | /** |
471 | * scols_table_next_line: | |
472 | * @tb: a pointer to a struct libscols_table instance | |
473 | * @itr: a pointer to a struct libscols_iter instance | |
474 | * @ln: a pointer to a pointer to a struct libscols_line instance | |
475 | * | |
476 | * Finds the next line and returns a pointer to it via @ln. | |
477 | * | |
478 | * Returns: 0, a negative value in case of an error. | |
479 | */ | |
3e542c76 KZ |
480 | int scols_table_next_line(struct libscols_table *tb, |
481 | struct libscols_iter *itr, | |
482 | struct libscols_line **ln) | |
483 | { | |
484 | int rc = 1; | |
485 | ||
486 | if (!tb || !itr || !ln) | |
487 | return -EINVAL; | |
488 | *ln = NULL; | |
489 | ||
490 | if (!itr->head) | |
491 | SCOLS_ITER_INIT(itr, &tb->tb_lines); | |
492 | if (itr->p != itr->head) { | |
493 | SCOLS_ITER_ITERATE(itr, *ln, struct libscols_line, ln_lines); | |
494 | rc = 0; | |
495 | } | |
496 | ||
497 | return rc; | |
498 | } | |
499 | ||
1d90bcb1 OO |
500 | /** |
501 | * scols_table_new_line: | |
3e542c76 KZ |
502 | * @tb: table |
503 | * @parent: parental line or NULL | |
504 | * | |
505 | * This is shortcut for | |
506 | * | |
1d90bcb1 | 507 | * ln = scols_new_line(); |
3e542c76 | 508 | * scols_table_add_line(tb, ln); |
6e792bb6 OO |
509 | * scols_line_add_child(parent, ln); |
510 | * | |
3e542c76 KZ |
511 | * |
512 | * Returns: newly allocate line | |
513 | */ | |
514 | struct libscols_line *scols_table_new_line(struct libscols_table *tb, | |
515 | struct libscols_line *parent) | |
516 | { | |
517 | struct libscols_line *ln; | |
518 | ||
519 | assert(tb); | |
520 | assert(tb->ncols); | |
521 | ||
522 | if (!tb || !tb->ncols) | |
523 | return NULL; | |
710ed55d | 524 | |
3e542c76 KZ |
525 | ln = scols_new_line(); |
526 | if (!ln) | |
527 | return NULL; | |
528 | ||
529 | if (scols_table_add_line(tb, ln)) | |
530 | goto err; | |
531 | if (parent) | |
532 | scols_line_add_child(parent, ln); | |
533 | ||
534 | scols_unref_line(ln); /* ref-counter incremented by scols_table_add_line() */ | |
535 | return ln; | |
536 | err: | |
537 | scols_unref_line(ln); | |
538 | return NULL; | |
539 | } | |
540 | ||
1d90bcb1 OO |
541 | /** |
542 | * scols_table_get_line: | |
543 | * @tb: table | |
544 | * @n: column number (0..N) | |
545 | * | |
546 | * This is a shortcut for | |
547 | * | |
548 | * ln = scols_new_line(); | |
549 | * scols_line_set_....(cl, ...); | |
550 | * scols_table_add_line(tb, ln); | |
551 | * | |
552 | * Returns: a newly allocate line | |
553 | */ | |
3e542c76 KZ |
554 | struct libscols_line *scols_table_get_line(struct libscols_table *tb, |
555 | size_t n) | |
556 | { | |
557 | struct libscols_iter itr; | |
558 | struct libscols_line *ln; | |
559 | ||
560 | assert(tb); | |
561 | if (!tb) | |
562 | return NULL; | |
563 | if (n >= tb->nlines) | |
564 | return NULL; | |
565 | ||
566 | scols_reset_iter(&itr, SCOLS_ITER_FORWARD); | |
567 | while (scols_table_next_line(tb, &itr, &ln) == 0) { | |
568 | if (ln->seqnum == n) | |
569 | return ln; | |
570 | } | |
571 | return NULL; | |
572 | } | |
573 | ||
1d90bcb1 OO |
574 | /** |
575 | * scols_copy_table: | |
576 | * @tb: table | |
577 | * | |
3e542c76 KZ |
578 | * Creates a new independent table copy, except struct libscols_symbols that |
579 | * are shared between the tables. | |
1d90bcb1 OO |
580 | * |
581 | * Returns: a newly allocated copy of @tb | |
3e542c76 KZ |
582 | */ |
583 | struct libscols_table *scols_copy_table(struct libscols_table *tb) | |
584 | { | |
585 | struct libscols_table *ret; | |
586 | struct libscols_line *ln; | |
587 | struct libscols_column *cl; | |
588 | struct libscols_iter itr; | |
589 | ||
590 | assert(tb); | |
591 | if (!tb) | |
592 | return NULL; | |
0925a9dd | 593 | ret = scols_new_table(); |
3e542c76 KZ |
594 | if (!ret) |
595 | return NULL; | |
596 | ||
710ed55d KZ |
597 | DBG(TAB, ul_debugobj(tb, "copy into %p", ret)); |
598 | ||
0925a9dd KZ |
599 | if (tb->symbols) |
600 | scols_table_set_symbols(ret, tb->symbols); | |
601 | ||
3e542c76 KZ |
602 | /* columns */ |
603 | scols_reset_iter(&itr, SCOLS_ITER_FORWARD); | |
604 | while (scols_table_next_column(tb, &itr, &cl) == 0) { | |
605 | cl = scols_copy_column(cl); | |
606 | if (!cl) | |
607 | goto err; | |
0925a9dd | 608 | if (scols_table_add_column(ret, cl)) |
3e542c76 KZ |
609 | goto err; |
610 | scols_unref_column(cl); | |
611 | } | |
612 | ||
613 | /* lines */ | |
614 | scols_reset_iter(&itr, SCOLS_ITER_FORWARD); | |
615 | while (scols_table_next_line(tb, &itr, &ln) == 0) { | |
616 | struct libscols_line *newln = scols_copy_line(ln); | |
617 | if (!newln) | |
618 | goto err; | |
619 | if (scols_table_add_line(ret, newln)) | |
620 | goto err; | |
621 | if (ln->parent) { | |
622 | struct libscols_line *p = | |
623 | scols_table_get_line(ret, ln->parent->seqnum); | |
624 | if (p) | |
625 | scols_line_add_child(p, newln); | |
626 | } | |
627 | scols_unref_line(newln); | |
628 | } | |
629 | ||
d1b4d14f OO |
630 | /* separators */ |
631 | if (scols_table_set_column_separator(ret, tb->colsep) || | |
632 | scols_table_set_line_separator(ret, tb->linesep)) | |
633 | goto err; | |
634 | ||
3e542c76 KZ |
635 | return ret; |
636 | err: | |
637 | scols_unref_table(ret); | |
638 | return NULL; | |
639 | } | |
640 | ||
1d90bcb1 OO |
641 | /** |
642 | * scols_table_set_symbols: | |
643 | * @tb: table | |
0925a9dd KZ |
644 | * @sy: symbols or NULL |
645 | * | |
646 | * Add a reference to @sy from the table. The symbols are used by library to | |
647 | * draw tree output. If no symbols are specified then library checks the | |
648 | * current environment to select ASCII or UTF8 symbols. This default behavior | |
649 | * could be controlled by scols_table_enable_ascii(). | |
1d90bcb1 OO |
650 | * |
651 | * Returns: 0, a negative value in case of an error. | |
652 | */ | |
3e542c76 KZ |
653 | int scols_table_set_symbols(struct libscols_table *tb, |
654 | struct libscols_symbols *sy) | |
655 | { | |
656 | assert(tb); | |
657 | ||
658 | if (!tb) | |
659 | return -EINVAL; | |
660 | ||
710ed55d KZ |
661 | DBG(TAB, ul_debugobj(tb, "setting alternative symbols %p", sy)); |
662 | ||
3e542c76 KZ |
663 | if (tb->symbols) /* unref old */ |
664 | scols_unref_symbols(tb->symbols); | |
665 | if (sy) { /* ref user defined */ | |
666 | tb->symbols = sy; | |
667 | scols_ref_symbols(sy); | |
668 | } else { /* default symbols */ | |
669 | tb->symbols = scols_new_symbols(); | |
670 | if (!tb->symbols) | |
671 | return -ENOMEM; | |
672 | #if defined(HAVE_WIDECHAR) | |
8a38a8d3 | 673 | if (!scols_table_is_ascii(tb) && |
3e542c76 KZ |
674 | !strcmp(nl_langinfo(CODESET), "UTF-8")) { |
675 | scols_symbols_set_branch(tb->symbols, UTF_VR UTF_H); | |
676 | scols_symbols_set_vertical(tb->symbols, UTF_V " "); | |
677 | scols_symbols_set_right(tb->symbols, UTF_UR UTF_H); | |
678 | } else | |
679 | #endif | |
680 | { | |
681 | scols_symbols_set_branch(tb->symbols, "|-"); | |
682 | scols_symbols_set_vertical(tb->symbols, "| "); | |
683 | scols_symbols_set_right(tb->symbols, "`-"); | |
684 | } | |
685 | } | |
686 | ||
687 | return 0; | |
688 | } | |
8a38a8d3 OO |
689 | /** |
690 | * scols_table_enable_colors: | |
691 | * @tb: table | |
692 | * @enable: 1 or 0 | |
693 | * | |
0925a9dd | 694 | * Enable/disable colors. |
8a38a8d3 OO |
695 | * |
696 | * Returns: 0 on success, negative number in case of an error. | |
697 | */ | |
698 | int scols_table_enable_colors(struct libscols_table *tb, int enable) | |
699 | { | |
700 | assert(tb); | |
701 | if (!tb) | |
702 | return -EINVAL; | |
710ed55d KZ |
703 | |
704 | DBG(TAB, ul_debugobj(tb, "colors: %s", enable ? "ENABLE" : "DISABLE")); | |
8a38a8d3 OO |
705 | tb->colors_wanted = enable; |
706 | return 0; | |
707 | } | |
708 | /** | |
0925a9dd | 709 | * scols_table_enable_raw: |
8a38a8d3 OO |
710 | * @tb: table |
711 | * @enable: 1 or 0 | |
712 | * | |
0925a9dd KZ |
713 | * Enable/disable raw output format. The parsable output formats |
714 | * (export and raw) are mutually exclusive. | |
8a38a8d3 OO |
715 | * |
716 | * Returns: 0 on success, negative number in case of an error. | |
717 | */ | |
0925a9dd | 718 | int scols_table_enable_raw(struct libscols_table *tb, int enable) |
8a38a8d3 OO |
719 | { |
720 | assert(tb); | |
721 | if (!tb) | |
722 | return -EINVAL; | |
0925a9dd | 723 | |
710ed55d | 724 | DBG(TAB, ul_debugobj(tb, "raw: %s", enable ? "ENABLE" : "DISABLE")); |
0925a9dd KZ |
725 | if (enable) |
726 | tb->format = SCOLS_FMT_RAW; | |
727 | else if (tb->format == SCOLS_FMT_RAW) | |
728 | tb->format = 0; | |
8a38a8d3 OO |
729 | return 0; |
730 | } | |
0925a9dd | 731 | |
8a38a8d3 | 732 | /** |
0925a9dd | 733 | * scols_table_enable_export: |
8a38a8d3 OO |
734 | * @tb: table |
735 | * @enable: 1 or 0 | |
736 | * | |
0925a9dd KZ |
737 | * Enable/disable export output format (COLUMNAME="value" ...). |
738 | * The parsable output formats (export and raw) are mutually exclusive. | |
8a38a8d3 OO |
739 | * |
740 | * Returns: 0 on success, negative number in case of an error. | |
741 | */ | |
0925a9dd | 742 | int scols_table_enable_export(struct libscols_table *tb, int enable) |
8a38a8d3 OO |
743 | { |
744 | assert(tb); | |
745 | if (!tb) | |
746 | return -EINVAL; | |
710ed55d KZ |
747 | |
748 | DBG(TAB, ul_debugobj(tb, "export: %s", enable ? "ENABLE" : "DISABLE")); | |
0925a9dd KZ |
749 | if (enable) |
750 | tb->format = SCOLS_FMT_EXPORT; | |
751 | else if (tb->format == SCOLS_FMT_EXPORT) | |
752 | tb->format = 0; | |
8a38a8d3 OO |
753 | return 0; |
754 | } | |
0925a9dd | 755 | |
8a38a8d3 | 756 | /** |
0925a9dd | 757 | * scols_table_enable_ascii: |
8a38a8d3 OO |
758 | * @tb: table |
759 | * @enable: 1 or 0 | |
760 | * | |
0925a9dd KZ |
761 | * The ASCII-only output is relevant for tree-like outputs. The library |
762 | * checks if the current environment is UTF8 compatible by default. This | |
763 | * function overrides this check and force the library to use ASCII chars | |
764 | * for the tree. | |
8a38a8d3 | 765 | * |
0925a9dd KZ |
766 | * If a custom libcols_symbols are specified (see scols_table_set_symbols() |
767 | * then ASCII flag setting is ignored. | |
8a38a8d3 OO |
768 | * |
769 | * Returns: 0 on success, negative number in case of an error. | |
770 | */ | |
0925a9dd | 771 | int scols_table_enable_ascii(struct libscols_table *tb, int enable) |
8a38a8d3 OO |
772 | { |
773 | assert(tb); | |
774 | if (!tb) | |
775 | return -EINVAL; | |
710ed55d KZ |
776 | |
777 | DBG(TAB, ul_debugobj(tb, "ascii: %s", enable ? "ENABLE" : "DISABLE")); | |
0925a9dd | 778 | tb->ascii = enable ? 1 : 0; |
8a38a8d3 OO |
779 | return 0; |
780 | } | |
0925a9dd | 781 | |
8a38a8d3 | 782 | /** |
0925a9dd | 783 | * scols_table_enable_noheadings: |
8a38a8d3 OO |
784 | * @tb: table |
785 | * @enable: 1 or 0 | |
786 | * | |
0925a9dd | 787 | * Enable/disable header line. |
8a38a8d3 OO |
788 | * |
789 | * Returns: 0 on success, negative number in case of an error. | |
790 | */ | |
0925a9dd | 791 | int scols_table_enable_noheadings(struct libscols_table *tb, int enable) |
8a38a8d3 OO |
792 | { |
793 | assert(tb); | |
794 | if (!tb) | |
795 | return -EINVAL; | |
710ed55d | 796 | DBG(TAB, ul_debugobj(tb, "noheading: %s", enable ? "ENABLE" : "DISABLE")); |
0925a9dd | 797 | tb->no_headings = enable ? 1 : 0; |
8a38a8d3 OO |
798 | return 0; |
799 | } | |
3e542c76 | 800 | |
8a38a8d3 | 801 | /** |
0925a9dd | 802 | * scols_table_enable_maxout: |
8a38a8d3 OO |
803 | * @tb: table |
804 | * @enable: 1 or 0 | |
805 | * | |
0925a9dd KZ |
806 | * The extra space after last column is ignored by default. The output |
807 | * maximization use the extra space for all columns. | |
8a38a8d3 OO |
808 | * |
809 | * Returns: 0 on success, negative number in case of an error. | |
810 | */ | |
0925a9dd | 811 | int scols_table_enable_maxout(struct libscols_table *tb, int enable) |
8a38a8d3 OO |
812 | { |
813 | assert(tb); | |
814 | if (!tb) | |
815 | return -EINVAL; | |
710ed55d | 816 | DBG(TAB, ul_debugobj(tb, "maxout: %s", enable ? "ENABLE" : "DISABLE")); |
0925a9dd | 817 | tb->maxout = enable ? 1 : 0; |
8a38a8d3 OO |
818 | return 0; |
819 | } | |
0925a9dd | 820 | |
8a38a8d3 OO |
821 | /** |
822 | * scols_table_colors_wanted: | |
823 | * @tb: table | |
824 | * | |
0925a9dd | 825 | * Returns: 1 if colors are enabled. |
8a38a8d3 OO |
826 | */ |
827 | int scols_table_colors_wanted(struct libscols_table *tb) | |
828 | { | |
829 | assert(tb); | |
0925a9dd | 830 | return tb && tb->colors_wanted; |
8a38a8d3 | 831 | } |
c0070f81 OO |
832 | |
833 | /** | |
834 | * scols_table_is_empty: | |
835 | * @tb: table | |
836 | * | |
0925a9dd | 837 | * Returns: 1 if the table is empty. |
c0070f81 OO |
838 | */ |
839 | int scols_table_is_empty(struct libscols_table *tb) | |
840 | { | |
0925a9dd | 841 | assert(tb); |
c0070f81 OO |
842 | return !tb || !tb->nlines; |
843 | } | |
0925a9dd | 844 | |
8a38a8d3 | 845 | /** |
0925a9dd | 846 | * scols_table_is_ascii: |
8a38a8d3 OO |
847 | * @tb: table |
848 | * | |
0925a9dd | 849 | * Returns: 1 if ASCII tree is enabled. |
8a38a8d3 | 850 | */ |
0925a9dd | 851 | int scols_table_is_ascii(struct libscols_table *tb) |
8a38a8d3 OO |
852 | { |
853 | assert(tb); | |
0925a9dd | 854 | return tb && tb->ascii; |
8a38a8d3 | 855 | } |
0925a9dd | 856 | |
8a38a8d3 | 857 | /** |
0925a9dd | 858 | * scols_table_is_noheadings: |
8a38a8d3 OO |
859 | * @tb: table |
860 | * | |
0925a9dd | 861 | * Returns: 1 if header output is disabled. |
8a38a8d3 | 862 | */ |
0925a9dd | 863 | int scols_table_is_noheadings(struct libscols_table *tb) |
8a38a8d3 OO |
864 | { |
865 | assert(tb); | |
0925a9dd | 866 | return tb && tb->no_headings; |
8a38a8d3 | 867 | } |
0925a9dd | 868 | |
8a38a8d3 | 869 | /** |
0925a9dd | 870 | * scols_table_is_export: |
8a38a8d3 OO |
871 | * @tb: table |
872 | * | |
0925a9dd | 873 | * Returns: 1 if export output format is enabled. |
8a38a8d3 | 874 | */ |
0925a9dd | 875 | int scols_table_is_export(struct libscols_table *tb) |
8a38a8d3 OO |
876 | { |
877 | assert(tb); | |
0925a9dd | 878 | return tb && tb->format == SCOLS_FMT_EXPORT; |
8a38a8d3 | 879 | } |
0925a9dd | 880 | |
8a38a8d3 | 881 | /** |
0925a9dd | 882 | * scols_table_is_raw: |
8a38a8d3 OO |
883 | * @tb: table |
884 | * | |
0925a9dd | 885 | * Returns: 1 if raw output format is enabled. |
8a38a8d3 | 886 | */ |
0925a9dd | 887 | int scols_table_is_raw(struct libscols_table *tb) |
8a38a8d3 OO |
888 | { |
889 | assert(tb); | |
0925a9dd | 890 | return tb && tb->format == SCOLS_FMT_RAW; |
8a38a8d3 | 891 | } |
0925a9dd KZ |
892 | |
893 | ||
8a38a8d3 | 894 | /** |
0925a9dd | 895 | * scols_table_is_maxout |
8a38a8d3 OO |
896 | * @tb: table |
897 | * | |
0925a9dd | 898 | * Returns: 1 if output maximization is enabled, negative value in case of an error. |
8a38a8d3 | 899 | */ |
0925a9dd | 900 | int scols_table_is_maxout(struct libscols_table *tb) |
8a38a8d3 OO |
901 | { |
902 | assert(tb); | |
0925a9dd | 903 | return tb && tb->maxout; |
8a38a8d3 | 904 | } |
0925a9dd | 905 | |
8a38a8d3 OO |
906 | /** |
907 | * scols_table_is_tree: | |
908 | * @tb: table | |
909 | * | |
0925a9dd | 910 | * Returns: returns 1 tree-like output is expected. |
8a38a8d3 OO |
911 | */ |
912 | int scols_table_is_tree(struct libscols_table *tb) | |
913 | { | |
914 | assert(tb); | |
0925a9dd | 915 | return tb && tb->ntreecols > 0; |
8a38a8d3 | 916 | } |
d1b4d14f OO |
917 | |
918 | /** | |
919 | * scols_table_set_column_separator: | |
920 | * @tb: table | |
921 | * @sep: separator | |
922 | * | |
923 | * Sets the column separator of @tb to @sep. | |
924 | * Please note that @sep should always take up a single cell in the output. | |
925 | * | |
926 | * Returns: 0, a negative value in case of an error. | |
927 | */ | |
4baab7df | 928 | int scols_table_set_column_separator(struct libscols_table *tb, const char *sep) |
d1b4d14f | 929 | { |
4baab7df KZ |
930 | char *p = NULL; |
931 | ||
d1b4d14f OO |
932 | assert (tb); |
933 | ||
934 | if (!tb) | |
935 | return -EINVAL; | |
936 | ||
4baab7df KZ |
937 | if (sep) { |
938 | p = strdup(sep); | |
939 | if (!p) | |
940 | return -ENOMEM; | |
941 | } | |
d1b4d14f | 942 | |
710ed55d | 943 | DBG(TAB, ul_debugobj(tb, "new columns separator: %s", sep)); |
d1b4d14f | 944 | free(tb->colsep); |
4baab7df | 945 | tb->colsep = p; |
d1b4d14f OO |
946 | return 0; |
947 | } | |
948 | ||
949 | /** | |
950 | * scols_table_set_line_separator: | |
951 | * @tb: table | |
952 | * @sep: separator | |
953 | * | |
954 | * Sets the line separator of @tb to @sep. | |
955 | * | |
956 | * Returns: 0, a negative value in case of an error. | |
957 | */ | |
4baab7df | 958 | int scols_table_set_line_separator(struct libscols_table *tb, const char *sep) |
d1b4d14f | 959 | { |
4baab7df KZ |
960 | char *p = NULL; |
961 | ||
d1b4d14f OO |
962 | assert (tb); |
963 | ||
964 | if (!tb) | |
965 | return -EINVAL; | |
966 | ||
4baab7df KZ |
967 | if (sep) { |
968 | p = strdup(sep); | |
969 | if (!p) | |
970 | return -ENOMEM; | |
971 | } | |
d1b4d14f | 972 | |
710ed55d | 973 | DBG(TAB, ul_debugobj(tb, "new lines separator: %s", sep)); |
d1b4d14f | 974 | free(tb->linesep); |
4baab7df | 975 | tb->linesep = p; |
d1b4d14f OO |
976 | return 0; |
977 | } | |
978 | ||
979 | /** | |
980 | * scols_table_get_column_separator: | |
981 | * @tb: table | |
982 | * | |
983 | * Returns: @tb column separator, NULL in case of an error | |
984 | */ | |
985 | char *scols_table_get_column_separator(struct libscols_table *tb) | |
986 | { | |
987 | assert (tb); | |
988 | ||
989 | if (!tb) | |
990 | return NULL; | |
991 | return tb->colsep; | |
992 | } | |
993 | ||
994 | /** | |
995 | * scols_table_get_line_separator: | |
996 | * @tb: table | |
997 | * | |
998 | * Returns: @tb line separator, NULL in case of an error | |
999 | */ | |
1000 | char *scols_table_get_line_separator(struct libscols_table *tb) | |
1001 | { | |
1002 | assert (tb); | |
1003 | ||
1004 | if (!tb) | |
1005 | return NULL; | |
1006 | return tb->linesep; | |
1007 | ||
1008 | } | |
57a86f9b KZ |
1009 | |
1010 | static int cells_cmp_wrapper(struct list_head *a, struct list_head *b, void *data) | |
1011 | { | |
1012 | struct libscols_column *cl = (struct libscols_column *) data; | |
1013 | struct libscols_line *ra, *rb; | |
1014 | struct libscols_cell *ca, *cb; | |
1015 | ||
1016 | assert(a); | |
1017 | assert(b); | |
1018 | assert(cl); | |
1019 | ||
1020 | ra = list_entry(a, struct libscols_line, ln_lines); | |
1021 | rb = list_entry(b, struct libscols_line, ln_lines); | |
1022 | ca = scols_line_get_cell(ra, cl->seqnum); | |
1023 | cb = scols_line_get_cell(rb, cl->seqnum); | |
1024 | ||
1025 | return cl->cmpfunc(ca, cb, cl->cmpfunc_data); | |
1026 | } | |
1027 | ||
1028 | /** | |
1029 | * scols_sort_table: | |
1030 | * @tb: table | |
1031 | * @cl: order by this column | |
1032 | * | |
1033 | * Orders the table by the column. See also scols_column_set_cmpfunc(). | |
1034 | * | |
1035 | * Returns: 0, a negative value in case of an error. | |
1036 | */ | |
1037 | int scols_sort_table(struct libscols_table *tb, struct libscols_column *cl) | |
1038 | { | |
1039 | assert(tb); | |
1040 | assert(cl); | |
1041 | ||
1042 | if (!tb || !cl) | |
1043 | return -EINVAL; | |
1044 | ||
710ed55d | 1045 | DBG(TAB, ul_debugobj(tb, "sorting table")); |
57a86f9b KZ |
1046 | list_sort(&tb->tb_lines, cells_cmp_wrapper, cl); |
1047 | return 0; | |
1048 | } |