]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/tt.c
build-sys: provide alternatives for err, errx, warn and warnx
[thirdparty/util-linux.git] / lib / tt.c
CommitLineData
c15b21e4
KZ
1/*
2 * TT - Table or Tree, features:
3 * - column width could be defined as absolute or relative to the terminal width
4 * - allows to truncate or wrap data in columns
5 * - prints tree if parent->child relation is defined
6 * - draws the tree by ASCII or UTF8 lines (depends on terminal setting)
7 *
8 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
9 *
10 * This file may be redistributed under the terms of the
11 * GNU Lesser General Public License.
12 */
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <string.h>
17#include <termios.h>
c15b21e4
KZ
18#ifdef HAVE_SYS_IOCTL_H
19#include <sys/ioctl.h>
20#endif
21
22#include "nls.h"
23#include "widechar.h"
24#include "c.h"
25#include "tt.h"
26
27struct tt_symbols {
28 const char *branch;
29 const char *vert;
30 const char *right;
31};
32
33static const struct tt_symbols ascii_tt_symbols = {
34 .branch = "|-",
35 .vert = "| ",
36 .right = "`-",
37};
38
39#ifdef HAVE_WIDECHAR
40#define mbs_width(_s) mbstowcs(NULL, _s, 0)
41
42#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */
43#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */
44#define UTF_H "\342\224\200" /* U+2500, Horizontal */
45#define UTF_UR "\342\224\224" /* U+2514, Up and right */
46
47static const struct tt_symbols utf8_tt_symbols = {
48 .branch = UTF_VR UTF_H,
49 .vert = UTF_V " ",
50 .right = UTF_UR UTF_H,
51};
52
53#else /* !HAVE_WIDECHAR */
54# define mbs_width strlen(_s)
55#endif /* !HAVE_WIDECHAR */
56
57#define is_last_column(_tb, _cl) \
58 list_last_entry(&(_cl)->cl_columns, &(_tb)->tb_columns)
59
60/* TODO: move to lib/mbalign.c */
61#ifdef HAVE_WIDECHAR
62static size_t wc_truncate (wchar_t *wc, size_t width)
63{
64 size_t cells = 0;
65 int next_cells = 0;
66
67 while (*wc)
68 {
69 next_cells = wcwidth (*wc);
70 if (next_cells == -1) /* non printable */
71 {
72 *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
73 next_cells = 1;
74 }
75 if (cells + next_cells > width)
76 break;
77 cells += next_cells;
78 wc++;
79 }
80 *wc = L'\0';
81 return cells;
82}
83#endif
84
85
86/* TODO: move to lib/mbalign.c */
87static size_t mbs_truncate(char *str, size_t width)
88{
89 size_t bytes = strlen(str) + 1;
90#ifdef HAVE_WIDECHAR
91 size_t sz = mbs_width(str);
92 wchar_t *wcs = NULL;
93 int rc = -1;
94
95 if (sz <= width)
96 return sz; /* truncate is unnecessary */
97
98 if (sz == (size_t) -1)
99 goto done;
100
101 wcs = malloc(sz * sizeof(wchar_t));
102 if (!wcs)
103 goto done;
104
105 if (!mbstowcs(wcs, str, sz))
106 goto done;
107 rc = wc_truncate(wcs, width);
108 wcstombs(str, wcs, bytes);
109done:
110 free(wcs);
111 return rc;
112#else
113 if (width < bytes) {
114 str[width] = '\0';
115 return width;
116 }
117 return bytes; /* truncate is unnecessary */
118#endif
119}
120
121/*
122 * @flags: TT_FL_* flags (usually TT_FL_{ASCII,RAW})
123 *
124 * Returns: newly allocated table
125 */
126struct tt *tt_new_table(int flags)
127{
128 struct tt *tb;
129
130 tb = calloc(1, sizeof(struct tt));
131 if (!tb)
132 return NULL;
133
134 tb->flags = flags;
135 INIT_LIST_HEAD(&tb->tb_lines);
136 INIT_LIST_HEAD(&tb->tb_columns);
137
f7a29259 138#if defined(HAVE_WIDECHAR)
c15b21e4
KZ
139 if (!(flags & TT_FL_ASCII) && !strcmp(nl_langinfo(CODESET), "UTF-8"))
140 tb->symbols = &utf8_tt_symbols;
141 else
142#endif
143 tb->symbols = &ascii_tt_symbols;
144 return tb;
145}
146
147void tt_free_table(struct tt *tb)
148{
149 if (!tb)
150 return;
151 while (!list_empty(&tb->tb_lines)) {
152 struct tt_line *ln = list_entry(tb->tb_lines.next,
153 struct tt_line, ln_lines);
154 list_del(&ln->ln_lines);
155 free(ln->data);
156 free(ln);
157 }
158 while (!list_empty(&tb->tb_columns)) {
159 struct tt_column *cl = list_entry(tb->tb_columns.next,
160 struct tt_column, cl_columns);
161 list_del(&cl->cl_columns);
162 free(cl);
163 }
164 free(tb);
165}
166
167/*
168 * @tb: table
169 * @name: column header
170 * @whint: column width hint (absolute width: N > 1; relative width: N < 1)
171 * @flags: usually TT_FL_{TREE,TRUNCATE}
172 *
173 * The column is necessary to address (for example for tt_line_set_data()) by
174 * sequential number. The first defined column has the colnum = 0. For example:
175 *
176 * tt_define_column(tab, "FOO", 0.5, 0); // colnum = 0
177 * tt_define_column(tab, "BAR", 0.5, 0); // colnum = 1
178 * .
179 * .
180 * tt_line_set_data(line, 0, "foo-data"); // FOO column
181 * tt_line_set_data(line, 1, "bar-data"); // BAR column
182 *
183 * Returns: newly allocated column definition
184 */
185struct tt_column *tt_define_column(struct tt *tb, const char *name,
186 double whint, int flags)
187{
188 struct tt_column *cl;
189
190 if (!tb)
191 return NULL;
192 cl = calloc(1, sizeof(*cl));
193 if (!cl)
194 return NULL;
195
196 cl->name = name;
197 cl->width_hint = whint;
198 cl->flags = flags;
199 cl->seqnum = tb->ncols++;
200
201 if (flags & TT_FL_TREE)
202 tb->flags |= TT_FL_TREE;
203
204 INIT_LIST_HEAD(&cl->cl_columns);
205 list_add_tail(&cl->cl_columns, &tb->tb_columns);
206 return cl;
207}
208
209/*
210 * @tb: table
211 * @parent: parental line or NULL
212 *
213 * Returns: newly allocate line
214 */
215struct tt_line *tt_add_line(struct tt *tb, struct tt_line *parent)
216{
217 struct tt_line *ln = NULL;
218
219 if (!tb || !tb->ncols)
220 goto err;
221 ln = calloc(1, sizeof(*ln));
222 if (!ln)
223 goto err;
224 ln->data = calloc(tb->ncols, sizeof(char *));
225 if (!ln->data)
226 goto err;
227
228 ln->table = tb;
229 ln->parent = parent;
230 INIT_LIST_HEAD(&ln->ln_lines);
231 INIT_LIST_HEAD(&ln->ln_children);
232 INIT_LIST_HEAD(&ln->ln_branch);
233
234 list_add_tail(&ln->ln_lines, &tb->tb_lines);
235
236 if (parent)
237 list_add_tail(&ln->ln_children, &parent->ln_branch);
238 return ln;
239err:
240 free(ln);
241 return NULL;
242}
243
244/*
245 * @tb: table
246 * @colnum: number of column (0..N)
247 *
248 * Returns: pointer to column or NULL
249 */
250struct tt_column *tt_get_column(struct tt *tb, int colnum)
251{
252 struct list_head *p;
253
254 list_for_each(p, &tb->tb_columns) {
255 struct tt_column *cl =
256 list_entry(p, struct tt_column, cl_columns);
257 if (cl->seqnum == colnum)
258 return cl;
259 }
260 return NULL;
261}
262
263/*
264 * @ln: line
265 * @colnum: number of column (0..N)
266 * @data: printable data
267 *
268 * Stores data that will be printed to the table cell.
269 */
270int tt_line_set_data(struct tt_line *ln, int colnum, const char *data)
271{
272 struct tt_column *cl;
273
274 if (!ln)
275 return -1;
276 cl = tt_get_column(ln->table, colnum);
277 if (!cl)
278 return -1;
1f51db36
KZ
279
280 if (ln->data[cl->seqnum])
281 ln->data_sz -= strlen(ln->data[cl->seqnum]);
282
c15b21e4 283 ln->data[cl->seqnum] = data;
1f51db36
KZ
284 if (data)
285 ln->data_sz += strlen(data);
c15b21e4
KZ
286 return 0;
287}
288
289static int get_terminal_width(void)
290{
291#ifdef TIOCGSIZE
292 struct ttysize t_win;
293#endif
294#ifdef TIOCGWINSZ
295 struct winsize w_win;
296#endif
297 const char *cp;
298
299#ifdef TIOCGSIZE
300 if (ioctl (0, TIOCGSIZE, &t_win) == 0)
301 return t_win.ts_cols;
302#endif
303#ifdef TIOCGWINSZ
304 if (ioctl (0, TIOCGWINSZ, &w_win) == 0)
305 return w_win.ws_col;
306#endif
307 cp = getenv("COLUMNS");
308 if (cp)
309 return strtol(cp, NULL, 10);
310 return 0;
311}
312
049caefd
KZ
313int tt_line_set_userdata(struct tt_line *ln, void *data)
314{
315 if (!ln)
316 return -1;
317 ln->userdata = data;
318 return 0;
319}
320
c15b21e4
KZ
321static char *line_get_ascii_art(struct tt_line *ln, char *buf, size_t *bufsz)
322{
323 const char *art;
324 size_t len;
325
326 if (!ln->parent)
327 return buf;
328
329 buf = line_get_ascii_art(ln->parent, buf, bufsz);
330 if (!buf)
331 return NULL;
332
333 if (list_last_entry(&ln->ln_children, &ln->parent->ln_branch))
f872ed1f 334 art = " ";
c15b21e4
KZ
335 else
336 art = ln->table->symbols->vert;
337
338 len = strlen(art);
339 if (*bufsz < len)
340 return NULL; /* no space, internal error */
341
342 memcpy(buf, art, len);
343 *bufsz -= len;
344 return buf + len;
345}
346
347static char *line_get_data(struct tt_line *ln, struct tt_column *cl,
348 char *buf, size_t bufsz)
349{
350 const char *data = ln->data[cl->seqnum];
351 const struct tt_symbols *sym;
352 char *p = buf;
353
354 memset(buf, 0, bufsz);
355
356 if (!data)
357 return NULL;
358 if (!(cl->flags & TT_FL_TREE)) {
359 strncpy(buf, data, bufsz);
360 buf[bufsz - 1] = '\0';
361 return buf;
362 }
363 if (ln->parent) {
364 p = line_get_ascii_art(ln->parent, buf, &bufsz);
365 if (!p)
366 return NULL;
367 }
368
369 sym = ln->table->symbols;
370
371 if (!ln->parent)
372 snprintf(p, bufsz, "%s", data); /* root node */
373 else if (list_last_entry(&ln->ln_children, &ln->parent->ln_branch))
374 snprintf(p, bufsz, "%s%s", sym->right, data); /* last chaild */
375 else
376 snprintf(p, bufsz, "%s%s", sym->branch, data); /* any child */
377
378 return buf;
379}
380
381static void recount_widths(struct tt *tb, char *buf, size_t bufsz)
382{
383 struct list_head *p;
384 int width = 0, trunc_only;
385
386 /* set width according to the size of data
387 */
388 list_for_each(p, &tb->tb_columns) {
389 struct tt_column *cl =
390 list_entry(p, struct tt_column, cl_columns);
391 struct list_head *lp;
392
393 list_for_each(lp, &tb->tb_lines) {
394 struct tt_line *ln =
395 list_entry(lp, struct tt_line, ln_lines);
396
397 char *data = line_get_data(ln, cl, buf, bufsz);
398 size_t len = data ? mbs_width(data) : 0;
399
400 if (cl->width < len)
401 cl->width = len;
402 }
403 }
404
405 /* set minimal width (= size of column header)
406 */
407 list_for_each(p, &tb->tb_columns) {
408 struct tt_column *cl =
409 list_entry(p, struct tt_column, cl_columns);
410
3e451589
KZ
411 if (cl->name)
412 cl->width_min = mbs_width(cl->name);
c15b21e4 413
ac3d410c
KZ
414 if (cl->width < cl->width_min)
415 cl->width = cl->width_min;
3e451589 416
ac3d410c 417 else if (cl->width_hint >= 1 &&
3e451589 418 cl->width < (int) cl->width_hint &&
ac3d410c 419 cl->width_min < (int) cl->width_hint)
3e451589 420
c15b21e4
KZ
421 cl->width = (int) cl->width_hint;
422
423 width += cl->width + (is_last_column(tb, cl) ? 0 : 1);
424 }
425
426 if (width == tb->termwidth)
427 goto leave;
428 if (width < tb->termwidth) {
429 /* cool, use the extra space for the last column */
430 struct tt_column *cl = list_entry(
431 tb->tb_columns.prev, struct tt_column, cl_columns);
432
3e451589
KZ
433 if (!(cl->flags & TT_FL_RIGHT))
434 cl->width += tb->termwidth - width;
c15b21e4
KZ
435 goto leave;
436 }
437
438 /* bad, we have to reduce output width, this is done in two steps:
439 * 1/ reduce columns with a relative width and with truncate flag
440 * 2) reduce columns with a relative width without truncate flag
441 */
442 trunc_only = 1;
443 while(width > tb->termwidth) {
444 int org = width;
445
446 list_for_each(p, &tb->tb_columns) {
447 struct tt_column *cl =
448 list_entry(p, struct tt_column, cl_columns);
449
450 if (width <= tb->termwidth)
451 break;
452 if (cl->width_hint > 1)
453 continue; /* never truncate columns with absolute sizes */
454 if (cl->flags & TT_FL_TREE)
455 continue; /* never truncate the tree */
3e451589 456 if (trunc_only && !(cl->flags & TT_FL_TRUNC))
c15b21e4 457 continue;
ac3d410c
KZ
458 if (cl->width == cl->width_min)
459 continue;
c15b21e4
KZ
460 if (cl->width > cl->width_hint * tb->termwidth) {
461 cl->width--;
462 width--;
463 }
464 }
465 if (org == width) {
466 if (trunc_only)
467 trunc_only = 0;
468 else
469 break;
470 }
471 }
472leave:
e7baa971 473/*
c15b21e4
KZ
474 fprintf(stderr, "terminal: %d, output: %d\n", tb->termwidth, width);
475
476 list_for_each(p, &tb->tb_columns) {
477 struct tt_column *cl =
478 list_entry(p, struct tt_column, cl_columns);
479
3e451589 480 fprintf(stderr, "width: %s=%d [hint=%d]\n",
c15b21e4
KZ
481 cl->name, cl->width,
482 cl->width_hint > 1 ? (int) cl->width_hint :
483 (int) (cl->width_hint * tb->termwidth));
484 }
e7baa971 485*/
c15b21e4
KZ
486 return;
487}
488
489/* note that this function modifies @data
490 */
491static void print_data(struct tt *tb, struct tt_column *cl, char *data)
492{
493 size_t len, i;
494 int width;
495
496 if (!data)
497 data = "";
498
499 /* raw mode */
500 if (tb->flags & TT_FL_RAW) {
501 fputs(data, stdout);
502 if (!is_last_column(tb, cl))
503 fputc(' ', stdout);
504 return;
505 }
506
507 /* note that 'len' and 'width' are number of cells, not bytes */
508 len = mbs_width(data);
509
510 if (!len || len == (size_t) -1) {
511 len = 0;
512 data = NULL;
513 }
514 width = cl->width;
515
516 if (is_last_column(tb, cl) && len < width)
517 width = len;
518
519 /* truncate data */
3e451589 520 if (len > width && (cl->flags & TT_FL_TRUNC)) {
c15b21e4
KZ
521 len = mbs_truncate(data, width);
522 if (!data || len == (size_t) -1) {
523 len = 0;
524 data = NULL;
525 }
526 }
3e451589
KZ
527 if (data) {
528 if (!(tb->flags & TT_FL_RAW) && (cl->flags & TT_FL_RIGHT)) {
529 int xw = cl->width;
530 fprintf(stdout, "%*s", xw, data);
531 if (len < xw)
532 len = xw;
533 }
534 else
535 fputs(data, stdout);
536 }
c15b21e4
KZ
537 for (i = len; i < width; i++)
538 fputc(' ', stdout); /* padding */
539
540 if (!is_last_column(tb, cl)) {
3e451589 541 if (len > width && !(cl->flags & TT_FL_TRUNC)) {
c15b21e4
KZ
542 fputc('\n', stdout);
543 for (i = 0; i <= cl->seqnum; i++) {
544 struct tt_column *x = tt_get_column(tb, i);
545 printf("%*s ", -x->width, " ");
546 }
547 } else
548 fputc(' ', stdout); /* columns separator */
549 }
550}
551
552static void print_line(struct tt_line *ln, char *buf, size_t bufsz)
553{
554 struct list_head *p;
555
556 /* set width according to the size of data
557 */
558 list_for_each(p, &ln->table->tb_columns) {
559 struct tt_column *cl =
560 list_entry(p, struct tt_column, cl_columns);
561
562 print_data(ln->table, cl, line_get_data(ln, cl, buf, bufsz));
563 }
564 fputc('\n', stdout);
565}
566
567static void print_header(struct tt *tb, char *buf, size_t bufsz)
568{
569 struct list_head *p;
570
46e9ff0a 571 if ((tb->flags & TT_FL_NOHEADINGS) || list_empty(&tb->tb_lines))
c15b21e4
KZ
572 return;
573
574 /* set width according to the size of data
575 */
576 list_for_each(p, &tb->tb_columns) {
577 struct tt_column *cl =
578 list_entry(p, struct tt_column, cl_columns);
579
580 strncpy(buf, cl->name, bufsz);
581 buf[bufsz - 1] = '\0';
582 print_data(tb, cl, buf);
583 }
584 fputc('\n', stdout);
585}
586
587static void print_table(struct tt *tb, char *buf, size_t bufsz)
588{
589 struct list_head *p;
590
591 print_header(tb, buf, bufsz);
592
593 list_for_each(p, &tb->tb_lines) {
594 struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
595
596 print_line(ln, buf, bufsz);
597 }
598}
599
600static void print_tree_line(struct tt_line *ln, char *buf, size_t bufsz)
601{
602 struct list_head *p;
603
604 print_line(ln, buf, bufsz);
605
606 if (list_empty(&ln->ln_branch))
607 return;
608
609 /* print all children */
610 list_for_each(p, &ln->ln_branch) {
611 struct tt_line *chld =
612 list_entry(p, struct tt_line, ln_children);
613 print_tree_line(chld, buf, bufsz);
614 }
615}
616
617static void print_tree(struct tt *tb, char *buf, size_t bufsz)
618{
619 struct list_head *p;
620
621 print_header(tb, buf, bufsz);
622
623 list_for_each(p, &tb->tb_lines) {
624 struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
625
626 if (ln->parent)
627 continue;
628
629 print_tree_line(ln, buf, bufsz);
630 }
631}
632
633/*
634 * @tb: table
635 *
636 * Prints the table to stdout
637 */
638int tt_print_table(struct tt *tb)
639{
640 char *line;
1f51db36
KZ
641 size_t line_sz;
642 struct list_head *p;
c15b21e4
KZ
643
644 if (!tb)
645 return -1;
646 if (!tb->termwidth) {
647 tb->termwidth = get_terminal_width();
648 if (tb->termwidth <= 0)
649 tb->termwidth = 80;
650 }
1f51db36
KZ
651
652 line_sz = tb->termwidth;
653
654 list_for_each(p, &tb->tb_lines) {
655 struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
656 if (ln->data_sz > line_sz)
657 line_sz = ln->data_sz;
658 }
659
660 line = malloc(line_sz);
c15b21e4
KZ
661 if (!line)
662 return -1;
663 if (!(tb->flags & TT_FL_RAW))
1f51db36 664 recount_widths(tb, line, line_sz);
c15b21e4 665 if (tb->flags & TT_FL_TREE)
1f51db36 666 print_tree(tb, line, line_sz);
c15b21e4 667 else
1f51db36 668 print_table(tb, line, line_sz);
c15b21e4
KZ
669
670 free(line);
671 return 0;
672}
673
3e451589
KZ
674int tt_parse_columns_list(const char *list, int cols[], int *ncols,
675 int (name2id)(const char *, size_t))
676{
677 const char *begin = NULL, *p;
678
679 if (!list || !*list || !cols || !ncols || !name2id)
680 return -1;
681
682 *ncols = 0;
683
684 for (p = list; p && *p; p++) {
685 const char *end = NULL;
686 int id;
687
688 if (!begin)
689 begin = p; /* begin of the column name */
690 if (*p == ',')
691 end = p; /* terminate the name */
692 if (*(p + 1) == '\0')
693 end = p + 1; /* end of string */
694 if (!begin || !end)
695 continue;
696 if (end <= begin)
697 return -1;
698
699 id = name2id(begin, end - begin);
700 if (id == -1)
701 return -1;
702 cols[ *ncols ] = id;
703 (*ncols)++;
704 begin = NULL;
705 if (end && !*end)
706 break;
707 }
708 return 0;
709}
710
c15b21e4 711#ifdef TEST_PROGRAM
c15b21e4
KZ
712#include <errno.h>
713
714enum { MYCOL_NAME, MYCOL_FOO, MYCOL_BAR, MYCOL_PATH };
715
716int main(int argc, char *argv[])
717{
718 struct tt *tb;
719 struct tt_line *ln, *pr, *root;
720 int flags = 0, notree = 0, i;
721
722 if (argc == 2 && !strcmp(argv[1], "--help")) {
723 printf("%s [--ascii | --raw | --list]\n",
724 program_invocation_short_name);
725 return EXIT_SUCCESS;
726 } else if (argc == 2 && !strcmp(argv[1], "--ascii"))
727 flags |= TT_FL_ASCII;
728 else if (argc == 2 && !strcmp(argv[1], "--raw")) {
729 flags |= TT_FL_RAW;
730 notree = 1;
731 } else if (argc == 2 && !strcmp(argv[1], "--list"))
732 notree = 1;
733
734 setlocale(LC_ALL, "");
735 bindtextdomain(PACKAGE, LOCALEDIR);
736 textdomain(PACKAGE);
737
738 tb = tt_new_table(flags);
739 if (!tb)
740 err(EXIT_FAILURE, "table initialization failed");
741
742 tt_define_column(tb, "NAME", 0.3, notree ? 0 : TT_FL_TREE);
3e451589 743 tt_define_column(tb, "FOO", 0.3, TT_FL_TRUNC);
c15b21e4
KZ
744 tt_define_column(tb, "BAR", 0.3, 0);
745 tt_define_column(tb, "PATH", 0.3, 0);
746
747 for (i = 0; i < 2; i++) {
748 root = ln = tt_add_line(tb, NULL);
749 tt_line_set_data(ln, MYCOL_NAME, "AAA");
750 tt_line_set_data(ln, MYCOL_FOO, "a-foo-foo");
751 tt_line_set_data(ln, MYCOL_BAR, "barBar-A");
752 tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA");
753
754 pr = ln = tt_add_line(tb, ln);
755 tt_line_set_data(ln, MYCOL_NAME, "AAA.A");
756 tt_line_set_data(ln, MYCOL_FOO, "a.a-foo-foo");
757 tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A");
758 tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A");
759
760 ln = tt_add_line(tb, pr);
761 tt_line_set_data(ln, MYCOL_NAME, "AAA.A.AAA");
762 tt_line_set_data(ln, MYCOL_FOO, "a.a.a-foo-foo");
763 tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.A");
764 tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/AAA");
765
766 ln = tt_add_line(tb, root);
767 tt_line_set_data(ln, MYCOL_NAME, "AAA.B");
768 tt_line_set_data(ln, MYCOL_FOO, "a.b-foo-foo");
769 tt_line_set_data(ln, MYCOL_BAR, "barBar-A.B");
770 tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/B");
771
772 ln = tt_add_line(tb, pr);
773 tt_line_set_data(ln, MYCOL_NAME, "AAA.A.BBB");
774 tt_line_set_data(ln, MYCOL_FOO, "a.a.b-foo-foo");
775 tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.BBB");
776 tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/BBB");
777
778 ln = tt_add_line(tb, pr);
779 tt_line_set_data(ln, MYCOL_NAME, "AAA.A.CCC");
780 tt_line_set_data(ln, MYCOL_FOO, "a.a.c-foo-foo");
781 tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.CCC");
782 tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/CCC");
783
784 ln = tt_add_line(tb, root);
785 tt_line_set_data(ln, MYCOL_NAME, "AAA.C");
786 tt_line_set_data(ln, MYCOL_FOO, "a.c-foo-foo");
787 tt_line_set_data(ln, MYCOL_BAR, "barBar-A.C");
788 tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/C");
789 }
790
791 tt_print_table(tb);
792 tt_free_table(tb);
793
794 return EXIT_SUCCESS;
795}
796#endif