lib/tt.c: Goodbye!

Signed-off-by: Ondrej Oprala <ooprala@redhat.com>
This commit is contained in:
Ondrej Oprala 2014-03-27 17:46:06 +01:00 committed by Karel Zak
parent fe7af530a9
commit 10a628c8b3
8 changed files with 3 additions and 1120 deletions

View File

@ -5,7 +5,7 @@
lslogins(1)
----------
- a new command to list information about accounts
- based on lib/tt.c
- based on libsmartcols
- see passwd -S and solarix/aux logins commands
- use infor from libc passwd struct, lastlog and wtmp/btmp logs
- expected columns USERNAME, UID, GID, STATUS, GROUPS, SHELL, HOME,
@ -55,11 +55,10 @@ docs
- (!) add API documentation to libuuid
lib/tt.c
libsmartcols
--------
- allows to sort columns, for example sort lsblk(8) output by SIZE
- support colors (per column, per line, per field)
login-utils:
-----------

View File

@ -43,7 +43,6 @@ dist_noinst_HEADERS += \
include/sysfs.h \
include/timer.h \
include/timeutils.h \
include/tt.h \
include/ttyutils.h \
include/wholedisk.h \
include/widechar.h \

View File

@ -1,115 +0,0 @@
/*
* Prints table or tree. See lib/table.c for more details and example.
*
* Copyright (C) 2010 Karel Zak <kzak@redhat.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
*/
#ifndef UTIL_LINUX_TT_H
#define UTIL_LINUX_TT_H
#include "list.h"
enum {
/*
* Global flags
*/
TT_FL_RAW = (1 << 1),
TT_FL_ASCII = (1 << 2),
TT_FL_NOHEADINGS = (1 << 3),
TT_FL_EXPORT = (1 << 4),
TT_FL_MAX = (1 << 5), /* maximalize column width if possible */
/*
* Column flags
*/
TT_FL_TRUNC = (1 << 10), /* truncate fields data if necessary */
TT_FL_TREE = (1 << 11), /* use tree "ascii art" */
TT_FL_RIGHT = (1 << 12), /* align to the right */
TT_FL_STRICTWIDTH = (1 << 13), /* don't reduce width if column is empty */
TT_FL_NOEXTREMES = (1 << 14), /* ignore extreme fields when count column width*/
TT_FL_FREEDATA = (1 << 15), /* free() data in tt_free_table() */
};
struct tt {
FILE *out; /* output stream */
size_t ncols; /* number of columns */
size_t termwidth; /* terminal width */
size_t termreduce; /* reduce the original termwidth */
int is_term; /* is a tty? */
int flags;
int first_run;
struct list_head tb_columns;
struct list_head tb_lines; /* lines as specified by user */
struct list_head tb_output; /* lines in output order */
const struct tt_symbols *symbols;
};
struct tt_column {
const char *name; /* header */
size_t seqnum;
size_t width; /* real column width */
size_t width_min; /* minimal width (usually header width) */
size_t width_max; /* maximal width */
size_t width_avg; /* average width, used to detect extreme fields */
double width_hint; /* hint (N < 1 is in percent of termwidth) */
int flags;
int is_extreme;
struct list_head cl_columns;
};
struct tt_line {
struct tt *table;
char **data;
void *userdata;
size_t data_sz; /* strlen of all data */
struct list_head ln_lines; /* table lines */
struct list_head ln_output; /* lines in output order */
struct list_head ln_branch; /* begin of branch (head of ln_children) */
struct list_head ln_children;
struct tt_line *parent;
};
extern struct tt *tt_new_table(int flags);
extern int tt_get_flags(struct tt *tb);
extern void tt_set_flags(struct tt *tb, int flags);
extern void tt_set_termreduce(struct tt *tb, size_t re);
extern void tt_free_table(struct tt *tb);
extern void tt_remove_lines(struct tt *tb);
extern int tt_print_table(struct tt *tb);
extern int tt_print_table_to_string(struct tt *tb, char **data);
extern void tt_set_stream(struct tt *tb, FILE *out);
extern struct tt_column *tt_define_column(struct tt *tb, const char *name,
double whint, int flags);
extern struct tt_column *tt_get_column(struct tt *tb, size_t colnum);
extern struct tt_line *tt_add_line(struct tt *tb, struct tt_line *parent);
extern int tt_get_output_line(struct tt *tb, int i, struct tt_line **ln);
extern int tt_line_set_data(struct tt_line *ln, int colnum, char *data);
extern int tt_line_set_userdata(struct tt_line *ln, void *data);
extern void *tt_line_get_userdata(struct tt_line *ln);
extern void tt_fputs_quoted(const char *data, FILE *out);
extern void tt_fputs_nonblank(const char *data, FILE *out);
extern size_t tb_get_nlines(struct tt *tb);
static inline int tt_is_empty(struct tt *tb)
{
return !tb || list_empty(&tb->tb_lines);
}
#endif /* UTIL_LINUX_TT_H */

View File

@ -22,7 +22,6 @@ libcommon_la_SOURCES = \
lib/setproctitle.c \
lib/strutils.c \
lib/sysfs.c \
lib/tt.c \
lib/wholedisk.c \
lib/timeutils.c \
lib/ttyutils.c \
@ -56,7 +55,6 @@ check_PROGRAMS += \
test_procutils \
test_randutils \
test_strutils \
test_tt \
test_ttyutils \
test_wholedisk
@ -122,10 +120,6 @@ endif
test_fileutils_SOURCES = lib/fileutils.c
test_fileutils_CFLAGS = -DTEST_PROGRAM
test_tt_SOURCES = lib/tt.c
test_tt_CFLAGS = -DTEST_PROGRAM
test_tt_LDADD = libcommon.la
test_canonicalize_SOURCES = lib/canonicalize.c
test_canonicalize_CFLAGS = -DTEST_PROGRAM_CANONICALIZE

992
lib/tt.c
View File

@ -1,992 +0,0 @@
/*
* TT - Table or Tree, features:
* - column width could be defined as absolute or relative to the terminal width
* - allows to truncate or wrap data in columns
* - prints tree if parent->child relation is defined
* - draws the tree by ASCII or UTF8 lines (depends on terminal setting)
*
* Copyright (C) 2010 Karel Zak <kzak@redhat.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <ctype.h>
#include "c.h"
#include "nls.h"
#include "widechar.h"
#include "tt.h"
#include "mbsalign.h"
#include "ttyutils.h"
struct tt_symbols {
const char *branch;
const char *vert;
const char *right;
};
static const struct tt_symbols ascii_tt_symbols = {
.branch = "|-",
.vert = "| ",
.right = "`-",
};
#ifdef HAVE_WIDECHAR
#define UTF_V "\342\224\202" /* U+2502, Vertical line drawing char */
#define UTF_VR "\342\224\234" /* U+251C, Vertical and right */
#define UTF_H "\342\224\200" /* U+2500, Horizontal */
#define UTF_UR "\342\224\224" /* U+2514, Up and right */
static const struct tt_symbols utf8_tt_symbols = {
.branch = UTF_VR UTF_H,
.vert = UTF_V " ",
.right = UTF_UR UTF_H,
};
#endif /* !HAVE_WIDECHAR */
#define is_last_column(_tb, _cl) \
list_entry_is_last(&(_cl)->cl_columns, &(_tb)->tb_columns)
/*
* @flags: TT_FL_* flags (usually TT_FL_{ASCII,RAW})
*
* Returns: newly allocated table
*/
struct tt *tt_new_table(int flags)
{
struct tt *tb;
tb = calloc(1, sizeof(struct tt));
if (!tb)
return NULL;
tb->flags = flags;
tb->out = stdout;
INIT_LIST_HEAD(&tb->tb_lines);
INIT_LIST_HEAD(&tb->tb_output);
INIT_LIST_HEAD(&tb->tb_columns);
#if defined(HAVE_WIDECHAR)
if (!(flags & TT_FL_ASCII) && !strcmp(nl_langinfo(CODESET), "UTF-8"))
tb->symbols = &utf8_tt_symbols;
else
#endif
tb->symbols = &ascii_tt_symbols;
tb->first_run = TRUE;
return tb;
}
/*
* Be careful, the best way is to use:
*
* tt_set_flags(tb, tb_get_flags(tb) | TT_FL_xxx));
*/
void tt_set_flags(struct tt *tb, int flags)
{
if (tb)
tb->flags = flags;
}
int tt_get_flags(struct tt *tb)
{
return tb ? tb->flags : 0;
}
void tt_set_stream(struct tt *tb, FILE *out)
{
if (!tb)
return;
tb->out = out;
}
/*
* Reduce terminal size, the final table size will be
* termwidth - termreduce.
*/
void tt_set_termreduce(struct tt *tb, size_t re)
{
if (!tb)
return;
tb->termreduce = re;
}
size_t tb_get_nlines(struct tt *tb)
{
struct list_head *p;
size_t ct = 0;
if (tt_is_empty(tb))
return 0;
list_for_each(p, &tb->tb_lines)
ct++;
return ct;
}
void tt_remove_lines(struct tt *tb)
{
if (!tb)
return;
while (!list_empty(&tb->tb_lines)) {
struct list_head *p;
struct tt_line *ln = list_entry(tb->tb_lines.next,
struct tt_line, ln_lines);
list_del(&ln->ln_lines);
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
if ((cl->flags & TT_FL_FREEDATA) || (tb->flags & TT_FL_FREEDATA))
free(ln->data[cl->seqnum]);
}
free(ln->data);
free(ln);
}
}
void tt_free_table(struct tt *tb)
{
if (!tb)
return;
tt_remove_lines(tb);
while (!list_empty(&tb->tb_columns)) {
struct tt_column *cl = list_entry(tb->tb_columns.next,
struct tt_column, cl_columns);
list_del(&cl->cl_columns);
free(cl);
}
free(tb);
}
/*
* @tb: table
* @name: column header
* @whint: column width hint (absolute width: N > 1; relative width: N < 1)
* @flags: usually TT_FL_{TREE,TRUNCATE}
*
* The column width is possible to define by three ways:
*
* @whint = 0..1 : relative width, percent of terminal width
*
* @whint = 1..N : absolute width, empty colum will be truncated to
* the column header width
*
* @whint = 1..N
* @flags = TT_FL_STRICTWIDTH
* : absolute width, empty colum won't be truncated
*
* The column is necessary to address (for example for tt_line_set_data()) by
* sequential number. The first defined column has the colnum = 0. For example:
*
* tt_define_column(tab, "FOO", 0.5, 0); // colnum = 0
* tt_define_column(tab, "BAR", 0.5, 0); // colnum = 1
* .
* .
* tt_line_set_data(line, 0, "foo-data"); // FOO column
* tt_line_set_data(line, 1, "bar-data"); // BAR column
*
* Returns: newly allocated column definition
*/
struct tt_column *tt_define_column(struct tt *tb, const char *name,
double whint, int flags)
{
struct tt_column *cl;
if (!tb)
return NULL;
cl = calloc(1, sizeof(*cl));
if (!cl)
return NULL;
cl->name = name;
cl->width_hint = whint;
cl->flags = flags;
cl->seqnum = tb->ncols++;
if (flags & TT_FL_TREE)
tb->flags |= TT_FL_TREE;
INIT_LIST_HEAD(&cl->cl_columns);
list_add_tail(&cl->cl_columns, &tb->tb_columns);
return cl;
}
/*
* @tb: table
* @parent: parental line or NULL
*
* Returns: newly allocate line
*/
struct tt_line *tt_add_line(struct tt *tb, struct tt_line *parent)
{
struct tt_line *ln = NULL;
if (!tb || !tb->ncols)
goto err;
ln = calloc(1, sizeof(*ln));
if (!ln)
goto err;
ln->data = calloc(tb->ncols, sizeof(char *));
if (!ln->data)
goto err;
ln->table = tb;
ln->parent = parent;
INIT_LIST_HEAD(&ln->ln_lines);
INIT_LIST_HEAD(&ln->ln_output);
INIT_LIST_HEAD(&ln->ln_children);
INIT_LIST_HEAD(&ln->ln_branch);
list_add_tail(&ln->ln_lines, &tb->tb_lines);
if (parent)
list_add_tail(&ln->ln_children, &parent->ln_branch);
return ln;
err:
free(ln);
return NULL;
}
/*
* @tb: table
* @colnum: number of column (0..N)
*
* Returns: pointer to column or NULL
*/
struct tt_column *tt_get_column(struct tt *tb, size_t colnum)
{
struct list_head *p;
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
if (cl->seqnum == colnum)
return cl;
}
return NULL;
}
/*
* @ln: line
* @colnum: number of column (0..N)
* @data: printable data
*
* Stores data that will be printed to the table cell.
*/
int tt_line_set_data(struct tt_line *ln, int colnum, char *data)
{
struct tt_column *cl;
if (!ln)
return -1;
cl = tt_get_column(ln->table, colnum);
if (!cl)
return -1;
if (ln->data[cl->seqnum]) {
size_t sz = strlen(ln->data[cl->seqnum]);;
ln->data_sz = ln->data_sz > sz ? ln->data_sz - sz : 0;
}
ln->data[cl->seqnum] = data;
if (data)
ln->data_sz += strlen(data);
return 0;
}
int tt_line_set_userdata(struct tt_line *ln, void *data)
{
if (!ln)
return -1;
ln->userdata = data;
return 0;
}
void *tt_line_get_userdata(struct tt_line *ln)
{
return ln ? ln->userdata : NULL;
}
static char *line_get_ascii_art(struct tt_line *ln, char *buf, size_t *bufsz)
{
const char *art;
size_t len;
if (!ln->parent)
return buf;
buf = line_get_ascii_art(ln->parent, buf, bufsz);
if (!buf)
return NULL;
if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
art = " ";
else
art = ln->table->symbols->vert;
len = strlen(art);
if (*bufsz < len)
return NULL; /* no space, internal error */
memcpy(buf, art, len);
*bufsz -= len;
return buf + len;
}
static char *line_get_data(struct tt_line *ln, struct tt_column *cl,
char *buf, size_t bufsz)
{
const char *data = ln->data[cl->seqnum];
const struct tt_symbols *sym;
char *p = buf;
memset(buf, 0, bufsz);
if (!data)
return NULL;
if (!(cl->flags & TT_FL_TREE)) {
strncpy(buf, data, bufsz);
buf[bufsz - 1] = '\0';
return buf;
}
/*
* Tree stuff
*/
if (ln->parent) {
p = line_get_ascii_art(ln->parent, buf, &bufsz);
if (!p)
return NULL;
}
sym = ln->table->symbols;
if (!ln->parent)
snprintf(p, bufsz, "%s", data); /* root node */
else if (list_entry_is_last(&ln->ln_children, &ln->parent->ln_branch))
snprintf(p, bufsz, "%s%s", sym->right, data); /* last chaild */
else
snprintf(p, bufsz, "%s%s", sym->branch, data); /* any child */
return buf;
}
/*
* This function counts column width.
*
* For the TT_FL_NOEXTREMES columns is possible to call this function two
* times. The first pass counts width and average width. If the column
* contains too large fields (width greater than 2 * average) then the column
* is marked as "extreme". In the second pass all extreme fields are ignored
* and column width is counted from non-extreme fields only.
*/
static void count_column_width(struct tt *tb, struct tt_column *cl,
char *buf, size_t bufsz)
{
struct list_head *lp;
int count = 0;
size_t sum = 0;
cl->width = 0;
list_for_each(lp, &tb->tb_lines) {
struct tt_line *ln = list_entry(lp, struct tt_line, ln_lines);
char *data = line_get_data(ln, cl, buf, bufsz);
size_t len = data ? mbs_safe_width(data) : 0;
if (len == (size_t) -1) /* ignore broken multibyte strings */
len = 0;
if (len > cl->width_max)
cl->width_max = len;
if (cl->is_extreme && len > cl->width_avg * 2)
continue;
else if (cl->flags & TT_FL_NOEXTREMES) {
sum += len;
count++;
}
if (len > cl->width)
cl->width = len;
}
if (count && cl->width_avg == 0) {
cl->width_avg = sum / count;
if (cl->width_max > cl->width_avg * 2)
cl->is_extreme = 1;
}
/* check and set minimal column width */
if (cl->name)
cl->width_min = mbs_safe_width(cl->name);
/* enlarge to minimal width */
if (cl->width < cl->width_min && !(cl->flags & TT_FL_STRICTWIDTH))
cl->width = cl->width_min;
/* use relative size for large columns */
else if (cl->width_hint >= 1 && cl->width < (size_t) cl->width_hint &&
cl->width_min < (size_t) cl->width_hint)
cl->width = (size_t) cl->width_hint;
}
/*
* This is core of the tt_* voodo...
*/
static void recount_widths(struct tt *tb, char *buf, size_t bufsz)
{
struct list_head *p;
size_t width = 0; /* output width */
int trunc_only;
int extremes = 0;
/* set basic columns width
*/
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
count_column_width(tb, cl, buf, bufsz);
width += cl->width + (is_last_column(tb, cl) ? 0 : 1);
extremes += cl->is_extreme;
}
if (!tb->is_term)
return;
/* reduce columns with extreme fields
*/
if (width > tb->termwidth && extremes) {
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl = list_entry(p, struct tt_column, cl_columns);
size_t org_width;
if (!cl->is_extreme)
continue;
org_width = cl->width;
count_column_width(tb, cl, buf, bufsz);
if (org_width > cl->width)
width -= org_width - cl->width;
else
extremes--; /* hmm... nothing reduced */
}
}
/* Cool, we have extra space, use it! */
if (width < tb->termwidth) {
/* try to found extreme column which fits into available space
*/
if (extremes) {
/* enlarge the first extreme column */
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
size_t add;
if (!cl->is_extreme)
continue;
/* this column is tooo large, ignore?
if (cl->width_max - cl->width >
(tb->termwidth - width))
continue;
*/
add = tb->termwidth - width;
if (add && cl->width + add > cl->width_max)
add = cl->width_max - cl->width;
cl->width += add;
width += add;
if (width == tb->termwidth)
break;
}
}
if (width < tb->termwidth && (tb->flags & TT_FL_MAX)) {
/* try enlarge all columns */
while (width < tb->termwidth) {
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
cl->width++;
width++;
if (width == tb->termwidth)
break;
}
}
} else if (width < tb->termwidth) {
/* enalarge the last column */
struct tt_column *cl = list_entry(
tb->tb_columns.prev, struct tt_column, cl_columns);
if (!(cl->flags & TT_FL_RIGHT) && tb->termwidth - width > 0) {
cl->width += tb->termwidth - width;
width = tb->termwidth;
}
}
}
/* bad, we have to reduce output width, this is done in two steps:
* 1/ reduce columns with a relative width and with truncate flag
* 2) reduce columns with a relative width without truncate flag
*/
trunc_only = 1;
while (width > tb->termwidth) {
size_t org = width;
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
if (width <= tb->termwidth)
break;
if (cl->width_hint > 1 && !(cl->flags & TT_FL_TRUNC))
continue; /* never truncate columns with absolute sizes */
if (cl->flags & TT_FL_TREE)
continue; /* never truncate the tree */
if (trunc_only && !(cl->flags & TT_FL_TRUNC))
continue;
if (cl->width == cl->width_min)
continue;
/* truncate column with relative sizes */
if (cl->width_hint < 1 && cl->width > 0 && width > 0 &&
cl->width > cl->width_hint * tb->termwidth) {
cl->width--;
width--;
}
/* truncate column with absolute size */
if (cl->width_hint > 1 && cl->width > 0 && width > 0 &&
!trunc_only) {
cl->width--;
width--;
}
}
if (org == width) {
if (trunc_only)
trunc_only = 0;
else
break;
}
}
/*
fprintf(stderr, "terminal: %d, output: %d\n", tb->termwidth, width);
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
fprintf(stderr, "width: %s=%zd [hint=%d, avg=%zd, max=%zd, extreme=%s]\n",
cl->name, cl->width,
cl->width_hint > 1 ? (int) cl->width_hint :
(int) (cl->width_hint * tb->termwidth),
cl->width_avg,
cl->width_max,
cl->is_extreme ? "yes" : "not");
}
*/
return;
}
void tt_fputs_quoted(const char *data, FILE *out)
{
const char *p;
fputc('"', out);
for (p = data; p && *p; p++) {
if ((unsigned char) *p == 0x22 || /* " */
(unsigned char) *p == 0x5c || /* \ */
!isprint((unsigned char) *p) ||
iscntrl((unsigned char) *p)) {
fprintf(out, "\\x%02x", (unsigned char) *p);
} else
fputc(*p, out);
}
fputc('"', out);
}
void tt_fputs_nonblank(const char *data, FILE *out)
{
const char *p;
for (p = data; p && *p; p++) {
if (isblank((unsigned char) *p) ||
(unsigned char) *p == 0x5c || /* \ */
!isprint((unsigned char) *p) ||
iscntrl((unsigned char) *p)) {
fprintf(out, "\\x%02x", (unsigned char) *p);
} else
fputc(*p, out);
}
}
/*
* Prints data, data maybe be printed in more formats (raw, NAME=xxx pairs) and
* control and non-printable chars maybe encoded in \x?? hex encoding.
*/
static void print_data(struct tt *tb, struct tt_column *cl, char *data)
{
size_t len = 0, i, width;
char *buf;
if (!data)
data = "";
/* raw mode */
if (tb->flags & TT_FL_RAW) {
tt_fputs_nonblank(data, tb->out);
if (!is_last_column(tb, cl))
fputc(' ', tb->out);
return;
}
/* NAME=value mode */
if (tb->flags & TT_FL_EXPORT) {
fprintf(tb->out, "%s=", cl->name);
tt_fputs_quoted(data, tb->out);
if (!is_last_column(tb, cl))
fputc(' ', tb->out);
return;
}
/* note that 'len' and 'width' are number of cells, not bytes */
buf = mbs_safe_encode(data, &len);
data = buf;
if (!data)
data = "";
if (!len || len == (size_t) -1) {
len = 0;
data = NULL;
}
width = cl->width;
if (is_last_column(tb, cl) && len < width && !(tb->flags & TT_FL_MAX))
width = len;
/* truncate data */
if (len > width && (cl->flags & TT_FL_TRUNC)) {
if (data)
len = mbs_truncate(data, &width);
if (!data || len == (size_t) -1) {
len = 0;
data = NULL;
}
}
if (data) {
if (!(tb->flags & TT_FL_RAW) && (cl->flags & TT_FL_RIGHT)) {
size_t xw = cl->width;
fprintf(tb->out, "%*s", (int) xw, data);
if (len < xw)
len = xw;
}
else
fputs(data, tb->out);
}
for (i = len; i < width; i++)
fputc(' ', tb->out); /* padding */
if (!is_last_column(tb, cl)) {
if (len > width && !(cl->flags & TT_FL_TRUNC)) {
fputc('\n', tb->out);
for (i = 0; i <= (size_t) cl->seqnum; i++) {
struct tt_column *x = tt_get_column(tb, i);
printf("%*s ", -((int)x->width), " ");
}
} else
fputc(' ', tb->out); /* columns separator */
}
free(buf);
}
static void print_line(struct tt_line *ln, char *buf, size_t bufsz)
{
struct list_head *p;
struct tt *tb = ln->table;
/* set width according to the size of data
*/
list_for_each(p, &ln->table->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
print_data(ln->table, cl, line_get_data(ln, cl, buf, bufsz));
}
fputc('\n', tb->out);
list_add_tail(&ln->ln_output, &tb->tb_output);
}
static void print_header(struct tt *tb, char *buf, size_t bufsz)
{
struct list_head *p;
if (!tb->first_run ||
(tb->flags & TT_FL_NOHEADINGS) ||
(tb->flags & TT_FL_EXPORT) ||
list_empty(&tb->tb_lines))
return;
/* set width according to the size of data
*/
list_for_each(p, &tb->tb_columns) {
struct tt_column *cl =
list_entry(p, struct tt_column, cl_columns);
strncpy(buf, cl->name, bufsz);
buf[bufsz - 1] = '\0';
print_data(tb, cl, buf);
}
fputc('\n', tb->out);
}
static void print_table(struct tt *tb, char *buf, size_t bufsz)
{
struct list_head *p;
print_header(tb, buf, bufsz);
list_for_each(p, &tb->tb_lines) {
struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
print_line(ln, buf, bufsz);
}
}
static void print_tree_line(struct tt_line *ln, char *buf, size_t bufsz)
{
struct list_head *p;
print_line(ln, buf, bufsz);
if (list_empty(&ln->ln_branch))
return;
/* print all children */
list_for_each(p, &ln->ln_branch) {
struct tt_line *chld =
list_entry(p, struct tt_line, ln_children);
print_tree_line(chld, buf, bufsz);
}
}
static void print_tree(struct tt *tb, char *buf, size_t bufsz)
{
struct list_head *p;
print_header(tb, buf, bufsz);
list_for_each(p, &tb->tb_lines) {
struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
if (ln->parent)
continue;
print_tree_line(ln, buf, bufsz);
}
}
/*
* @tb: table
*
* Prints the table to tb->out
*/
int tt_print_table(struct tt *tb)
{
char *line;
size_t line_sz;
struct list_head *p;
if (!tb)
return -1;
if (tb->first_run) {
tb->is_term = isatty(STDOUT_FILENO);
if (tb->is_term && !tb->termwidth)
tb->termwidth = get_terminal_width();
if (tb->termwidth <= 0)
tb->termwidth = 80;
tb->termwidth -= tb->termreduce;
}
line_sz = tb->termwidth;
list_for_each(p, &tb->tb_lines) {
struct tt_line *ln = list_entry(p, struct tt_line, ln_lines);
if (ln->data_sz > line_sz)
line_sz = ln->data_sz;
}
line_sz++; /* make a space for \0 */
line = malloc(line_sz);
if (!line)
return -1;
if (tb->first_run &&
!((tb->flags & TT_FL_RAW) || (tb->flags & TT_FL_EXPORT)))
recount_widths(tb, line, line_sz);
if (tb->flags & TT_FL_TREE)
print_tree(tb, line, line_sz);
else
print_table(tb, line, line_sz);
free(line);
tb->first_run = FALSE;
return 0;
}
int tt_get_output_line(struct tt *tb, int i, struct tt_line **ln)
{
struct list_head *p;
if (!tb || !ln)
return -EINVAL;
list_for_each(p, &tb->tb_output) {
*ln = list_entry(p, struct tt_line, ln_output);
if (i-- == 0)
return 0;
}
return -1;
}
/*
* @tb: table
*
* Prints the table to string
*/
int tt_print_table_to_string(struct tt *tb, char **data)
{
FILE *stream;
size_t sz;
if (!tb)
return -EINVAL;
/* create a streem for output */
stream = open_memstream(data, &sz);
if (!stream)
return -ENOMEM;
tt_set_stream(tb, stream);
tt_print_table(tb);
fclose(stream);
return 0;
}
#ifdef TEST_PROGRAM
#include <errno.h>
enum { MYCOL_NAME, MYCOL_FOO, MYCOL_BAR, MYCOL_PATH };
int main(int argc, char *argv[])
{
struct tt *tb;
struct tt_line *ln, *pr, *root;
int flags = 0, notree = 0, i;
if (argc == 2 && !strcmp(argv[1], "--help")) {
printf("%s [--ascii | --raw | --list]\n",
program_invocation_short_name);
return EXIT_SUCCESS;
} else if (argc == 2 && !strcmp(argv[1], "--ascii")) {
flags |= TT_FL_ASCII;
} else if (argc == 2 && !strcmp(argv[1], "--raw")) {
flags |= TT_FL_RAW;
notree = 1;
} else if (argc == 2 && !strcmp(argv[1], "--export")) {
flags |= TT_FL_EXPORT;
notree = 1;
} else if (argc == 2 && !strcmp(argv[1], "--list"))
notree = 1;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
tb = tt_new_table(flags);
if (!tb)
err(EXIT_FAILURE, "table initialization failed");
tt_define_column(tb, "NAME", 0.3, notree ? 0 : TT_FL_TREE);
tt_define_column(tb, "FOO", 0.3, TT_FL_TRUNC);
tt_define_column(tb, "BAR", 0.3, 0);
tt_define_column(tb, "PATH", 0.3, 0);
for (i = 0; i < 2; i++) {
root = ln = tt_add_line(tb, NULL);
tt_line_set_data(ln, MYCOL_NAME, "AAA");
tt_line_set_data(ln, MYCOL_FOO, "a-foo-foo");
tt_line_set_data(ln, MYCOL_BAR, "barBar-A");
tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA");
pr = ln = tt_add_line(tb, ln);
tt_line_set_data(ln, MYCOL_NAME, "AAA.A");
tt_line_set_data(ln, MYCOL_FOO, "a.a-foo-foo");
tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A");
tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A");
ln = tt_add_line(tb, pr);
tt_line_set_data(ln, MYCOL_NAME, "AAA.A.AAA");
tt_line_set_data(ln, MYCOL_FOO, "a.a.a-foo-foo");
tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.A");
tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/AAA");
ln = tt_add_line(tb, root);
tt_line_set_data(ln, MYCOL_NAME, "AAA.B");
tt_line_set_data(ln, MYCOL_FOO, "a.b-foo-foo");
tt_line_set_data(ln, MYCOL_BAR, "barBar-A.B");
tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/B");
ln = tt_add_line(tb, pr);
tt_line_set_data(ln, MYCOL_NAME, "AAA.A.BBB");
tt_line_set_data(ln, MYCOL_FOO, "a.a.b-foo-foo");
tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.BBB");
tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/BBB");
ln = tt_add_line(tb, pr);
tt_line_set_data(ln, MYCOL_NAME, "AAA.A.CCC");
tt_line_set_data(ln, MYCOL_FOO, "a.a.c-foo-foo");
tt_line_set_data(ln, MYCOL_BAR, "barBar-A.A.CCC");
tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/A/CCC");
ln = tt_add_line(tb, root);
tt_line_set_data(ln, MYCOL_NAME, "AAA.C");
tt_line_set_data(ln, MYCOL_FOO, "a.c-foo-foo");
tt_line_set_data(ln, MYCOL_BAR, "barBar-A.C");
tt_line_set_data(ln, MYCOL_PATH, "/mnt/AAA/C");
}
tt_print_table(tb);
tt_free_table(tb);
return EXIT_SUCCESS;
}
#endif

View File

@ -26,7 +26,6 @@ test_mangle:
test_procutils:
test_strutils:
test_sysfs:
test_tt:
test_wholedisk:
mkfs: libblkid libuuid
partitions: libblkid libuuid

View File

@ -28,7 +28,6 @@ test_mangle:
test_procutils:
test_strutils:
test_sysfs:
test_tt:
test_wholedisk:
mkfs: libblkid libuuid
partitions: libblkid libuuid

View File

@ -118,7 +118,7 @@ if [ -n "$SUBTESTS" ]; then
fi
done
else
if [ ! -f "$top_builddir/test_tt" ]; then
if [ ! -f "$top_builddir/test_ttyutils" ]; then
echo "Tests not compiled! Run 'make check' to fix the problem."
exit 1
fi