forked from Mirror/frr
lib: add table generator
Allows for easy preparation of tabular output. Supports: -- Padding -- Alignment -- Styling
This commit is contained in:
parent
f43cd318b2
commit
f051edd156
|
@ -37,6 +37,7 @@ libfrr_la_SOURCES = \
|
||||||
module.c \
|
module.c \
|
||||||
hook.c \
|
hook.c \
|
||||||
frr_pthread.c \
|
frr_pthread.c \
|
||||||
|
termtable.c \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h
|
BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h
|
||||||
|
@ -81,6 +82,7 @@ pkginclude_HEADERS = \
|
||||||
sha256.h \
|
sha256.h \
|
||||||
frr_pthread.h \
|
frr_pthread.h \
|
||||||
vrf_int.h \
|
vrf_int.h \
|
||||||
|
termtable.h \
|
||||||
# end
|
# end
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
|
|
487
lib/termtable.c
Normal file
487
lib/termtable.c
Normal file
|
@ -0,0 +1,487 @@
|
||||||
|
/*
|
||||||
|
* ASCII table generator.
|
||||||
|
* Copyright (C) 2017 Cumulus Networks
|
||||||
|
* Quentin Young
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; see the file COPYING; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <zebra.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "termtable.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
struct ttable_style ttable_styles[] = {
|
||||||
|
{ // default ascii
|
||||||
|
.corner = '+',
|
||||||
|
.rownums_on = false,
|
||||||
|
.indent = 1,
|
||||||
|
.border.top = '-',
|
||||||
|
.border.bottom = '-',
|
||||||
|
.border.left = '|',
|
||||||
|
.border.right = '|',
|
||||||
|
.border.top_on = true,
|
||||||
|
.border.bottom_on = true,
|
||||||
|
.border.left_on = true,
|
||||||
|
.border.right_on = true,
|
||||||
|
.cell.lpad = 1,
|
||||||
|
.cell.rpad = 1,
|
||||||
|
.cell.align = LEFT,
|
||||||
|
.cell.border.bottom = '-',
|
||||||
|
.cell.border.bottom_on = true,
|
||||||
|
.cell.border.top = '-',
|
||||||
|
.cell.border.top_on = false,
|
||||||
|
.cell.border.right = '|',
|
||||||
|
.cell.border.right_on = true,
|
||||||
|
.cell.border.left = '|',
|
||||||
|
.cell.border.left_on = false,
|
||||||
|
}, { // blank, suitable for plaintext alignment
|
||||||
|
.corner = ' ',
|
||||||
|
.rownums_on = false,
|
||||||
|
.indent = 1,
|
||||||
|
.border.top = ' ',
|
||||||
|
.border.bottom = ' ',
|
||||||
|
.border.left = ' ',
|
||||||
|
.border.right = ' ',
|
||||||
|
.border.top_on = false,
|
||||||
|
.border.bottom_on = false,
|
||||||
|
.border.left_on = false,
|
||||||
|
.border.right_on = false,
|
||||||
|
.cell.lpad = 0,
|
||||||
|
.cell.rpad = 3,
|
||||||
|
.cell.align = LEFT,
|
||||||
|
.cell.border.bottom = ' ',
|
||||||
|
.cell.border.bottom_on = false,
|
||||||
|
.cell.border.top = ' ',
|
||||||
|
.cell.border.top_on = false,
|
||||||
|
.cell.border.right = ' ',
|
||||||
|
.cell.border.right_on = false,
|
||||||
|
.cell.border.left = ' ',
|
||||||
|
.cell.border.left_on = false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
void ttable_del(struct ttable *tt)
|
||||||
|
{
|
||||||
|
for (int i = tt->nrows - 1; i >= 0; i--)
|
||||||
|
ttable_del_row(tt, i);
|
||||||
|
|
||||||
|
XFREE(MTYPE_TMP, tt->table);
|
||||||
|
XFREE(MTYPE_TMP, tt);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ttable *ttable_new(struct ttable_style *style)
|
||||||
|
{
|
||||||
|
struct ttable *tt;
|
||||||
|
|
||||||
|
tt = XCALLOC(MTYPE_TMP, sizeof(struct ttable));
|
||||||
|
tt->style = *style;
|
||||||
|
tt->nrows = 0;
|
||||||
|
tt->ncols = 0;
|
||||||
|
tt->size = 0;
|
||||||
|
tt->table = NULL;
|
||||||
|
|
||||||
|
return tt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts or appends a new row at the specified index.
|
||||||
|
*
|
||||||
|
* If the index is -1, the row is added to the end of the table. Otherwise the
|
||||||
|
* index must be a valid index into tt->table.
|
||||||
|
*
|
||||||
|
* If the table already has at least one row (and therefore a determinate
|
||||||
|
* number of columns), a format string specifying a number of columns not equal
|
||||||
|
* to tt->ncols will result in a no-op and a return value of NULL.
|
||||||
|
*
|
||||||
|
* @param tt table to insert into
|
||||||
|
* @param i insertion index; inserted row will be (i + 1)'th row
|
||||||
|
* @param format printf format string as in ttable_[add|insert]_row()
|
||||||
|
* @param ap pre-initialized variadic list of arguments for format string
|
||||||
|
*
|
||||||
|
* @return pointer to the first cell of allocated row
|
||||||
|
*/
|
||||||
|
static struct ttable_cell *ttable_insert_row_va(struct ttable *tt, int i,
|
||||||
|
const char *format, va_list ap)
|
||||||
|
{
|
||||||
|
assert(i >= -1 && i < tt->nrows);
|
||||||
|
|
||||||
|
char *res, *orig, *section;
|
||||||
|
const char *f;
|
||||||
|
struct ttable_cell *row;
|
||||||
|
int col = 0;
|
||||||
|
int ncols = 0;
|
||||||
|
|
||||||
|
/* count how many columns we have */
|
||||||
|
f = format;
|
||||||
|
for (; f[ncols]; f[ncols] == '|' ? ncols++ : *f++)
|
||||||
|
;
|
||||||
|
ncols++;
|
||||||
|
|
||||||
|
if (tt->ncols == 0)
|
||||||
|
tt->ncols = ncols;
|
||||||
|
else if (ncols != tt->ncols)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* reallocate chunk if necessary */
|
||||||
|
while (tt->size < (tt->nrows + 1) * sizeof(struct ttable_cell *)) {
|
||||||
|
tt->size = MAX(2 * tt->size, 2 * sizeof(struct ttable_cell *));
|
||||||
|
tt->table = XREALLOC(MTYPE_TMP, tt->table, tt->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CALLOC a block of cells */
|
||||||
|
row = XCALLOC(MTYPE_TMP, tt->ncols * sizeof(struct ttable_cell));
|
||||||
|
|
||||||
|
res = NULL;
|
||||||
|
vasprintf(&res, format, ap);
|
||||||
|
|
||||||
|
orig = res;
|
||||||
|
|
||||||
|
while (res) {
|
||||||
|
section = strsep(&res, "|");
|
||||||
|
row[col].text = XSTRDUP(MTYPE_TMP, section);
|
||||||
|
row[col].style = tt->style.cell;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(orig);
|
||||||
|
|
||||||
|
/* insert row */
|
||||||
|
if (i == -1 || i == tt->nrows)
|
||||||
|
tt->table[tt->nrows] = row;
|
||||||
|
else {
|
||||||
|
memmove(&tt->table[i + 1], &tt->table[i],
|
||||||
|
(tt->nrows - i) * sizeof(struct ttable_cell *));
|
||||||
|
tt->table[i] = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
tt->nrows++;
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ttable_cell *ttable_insert_row(struct ttable *tt, unsigned int i,
|
||||||
|
const char *format, ...)
|
||||||
|
{
|
||||||
|
struct ttable_cell *ret;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
ret = ttable_insert_row_va(tt, i, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ttable_cell *ttable_add_row(struct ttable *tt, const char *format, ...)
|
||||||
|
{
|
||||||
|
struct ttable_cell *ret;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
ret = ttable_insert_row_va(tt, -1, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ttable_del_row(struct ttable *tt, unsigned int i)
|
||||||
|
{
|
||||||
|
assert((int) i < tt->nrows);
|
||||||
|
|
||||||
|
for (int j = 0; j < tt->ncols; j++)
|
||||||
|
XFREE(MTYPE_TMP, tt->table[i][j].text);
|
||||||
|
|
||||||
|
XFREE(MTYPE_TMP, tt->table[i]);
|
||||||
|
|
||||||
|
memmove(&tt->table[i], &tt->table[i + 1],
|
||||||
|
(tt->nrows - i - 1) * sizeof(struct ttable_cell *));
|
||||||
|
|
||||||
|
tt->nrows--;
|
||||||
|
|
||||||
|
if (tt->nrows == 0)
|
||||||
|
tt->ncols = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ttable_align(struct ttable *tt, unsigned int row, unsigned int col,
|
||||||
|
unsigned int nrow, unsigned int ncol, enum ttable_align align)
|
||||||
|
{
|
||||||
|
assert((int) row < tt->nrows);
|
||||||
|
assert((int) col < tt->ncols);
|
||||||
|
assert((int) row + (int) nrow <= tt->nrows);
|
||||||
|
assert((int) col + (int) ncol <= tt->ncols);
|
||||||
|
|
||||||
|
for (unsigned int i = row; i < row + nrow; i++)
|
||||||
|
for (unsigned int j = col; j < col + ncol; j++)
|
||||||
|
tt->table[i][j].style.align = align;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ttable_cell_pad(struct ttable_cell *cell, enum ttable_align align,
|
||||||
|
short pad)
|
||||||
|
{
|
||||||
|
if (align == LEFT)
|
||||||
|
cell->style.lpad = pad;
|
||||||
|
else
|
||||||
|
cell->style.rpad = pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ttable_pad(struct ttable *tt, unsigned int row, unsigned int col,
|
||||||
|
unsigned int nrow, unsigned int ncol, enum ttable_align align,
|
||||||
|
short pad)
|
||||||
|
{
|
||||||
|
assert((int) row < tt->nrows);
|
||||||
|
assert((int) col < tt->ncols);
|
||||||
|
assert((int) row + (int) nrow <= tt->nrows);
|
||||||
|
assert((int) col + (int) ncol <= tt->ncols);
|
||||||
|
|
||||||
|
for (unsigned int i = row; i < row + nrow; i++)
|
||||||
|
for (unsigned int j = col; j < col + ncol; j++)
|
||||||
|
ttable_cell_pad(&tt->table[i][j], align, pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ttable_restyle(struct ttable *tt)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tt->nrows; i++)
|
||||||
|
for (int j = 0; j < tt->ncols; j++)
|
||||||
|
tt->table[i][j].style = tt->style.cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ttable_colseps(struct ttable *tt, unsigned int col,
|
||||||
|
enum ttable_align align, bool on, char sep)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tt->nrows; i++) {
|
||||||
|
if (align == RIGHT) {
|
||||||
|
tt->table[i][col].style.border.right_on = on;
|
||||||
|
tt->table[i][col].style.border.right = sep;
|
||||||
|
} else {
|
||||||
|
tt->table[i][col].style.border.left_on = on;
|
||||||
|
tt->table[i][col].style.border.left = sep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ttable_rowseps(struct ttable *tt, unsigned int row,
|
||||||
|
enum ttable_align align, bool on, char sep)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tt->ncols; i++) {
|
||||||
|
if (align == TOP) {
|
||||||
|
tt->table[row][i].style.border.top_on = on;
|
||||||
|
tt->table[row][i].style.border.top = sep;
|
||||||
|
} else {
|
||||||
|
tt->table[row][i].style.border.bottom_on = on;
|
||||||
|
tt->table[row][i].style.border.bottom = sep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ttable_dump(struct ttable *tt, const char *newline)
|
||||||
|
{
|
||||||
|
char *buf; // print buffer
|
||||||
|
size_t pos; // position in buffer
|
||||||
|
size_t nl_len; // strlen(newline)
|
||||||
|
int cw[tt->ncols]; // calculated column widths
|
||||||
|
int nlines; // total number of newlines / table lines
|
||||||
|
size_t width; // length of one line, with newline
|
||||||
|
int abspad; // calculated whitespace for sprintf
|
||||||
|
char *left; // left part of line
|
||||||
|
size_t lsize; // size of above
|
||||||
|
char *right; // right part of line
|
||||||
|
size_t rsize; // size of above
|
||||||
|
struct ttable_cell *cell, *row; // iteration pointers
|
||||||
|
|
||||||
|
nl_len = strlen(newline);
|
||||||
|
|
||||||
|
/* calculate width of each column */
|
||||||
|
memset(cw, 0x00, sizeof(int) * tt->ncols);
|
||||||
|
|
||||||
|
for (int j = 0; j < tt->ncols; j++)
|
||||||
|
for (int i = 0, cellw = 0; i < tt->nrows; i++) {
|
||||||
|
cell = &tt->table[i][j];
|
||||||
|
cellw = 0;
|
||||||
|
cellw += (int) strlen(cell->text);
|
||||||
|
cellw += cell->style.lpad;
|
||||||
|
cellw += cell->style.rpad;
|
||||||
|
if (j != 0)
|
||||||
|
cellw += cell->style.border.left_on ? 1 : 0;
|
||||||
|
if (j != tt->ncols - 1)
|
||||||
|
cellw += cell->style.border.right_on ? 1 : 0;
|
||||||
|
cw[j] = MAX(cw[j], cellw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate overall line width, including newline */
|
||||||
|
width = 0;
|
||||||
|
width += tt->style.indent;
|
||||||
|
width += tt->style.border.left_on ? 1 : 0;
|
||||||
|
width += tt->style.border.right_on ? 1 : 0;
|
||||||
|
width += strlen(newline);
|
||||||
|
for (int i = 0; i < tt->ncols; i++)
|
||||||
|
width += cw[i];
|
||||||
|
|
||||||
|
/* calculate number of lines en total */
|
||||||
|
nlines = tt->nrows;
|
||||||
|
nlines += tt->style.border.top_on ? 1 : 0;
|
||||||
|
nlines += tt->style.border.bottom_on ? 1 : 1; // makes life easier
|
||||||
|
for (int i = 0; i < tt->nrows; i++) {
|
||||||
|
/* if leftmost cell has top / bottom border, whole row does */
|
||||||
|
nlines += tt->table[i][0].style.border.top_on ? 1 : 0;
|
||||||
|
nlines += tt->table[i][0].style.border.bottom_on ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize left & right */
|
||||||
|
lsize = tt->style.indent + (tt->style.border.left_on ? 1 : 0);
|
||||||
|
left = XCALLOC(MTYPE_TMP, lsize);
|
||||||
|
rsize = nl_len + (tt->style.border.right_on ? 1 : 0);
|
||||||
|
right = XCALLOC(MTYPE_TMP, rsize);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < lsize; i++)
|
||||||
|
left[i] = ' ';
|
||||||
|
|
||||||
|
if (tt->style.border.left_on)
|
||||||
|
left[lsize - 1] = tt->style.border.left;
|
||||||
|
|
||||||
|
if (tt->style.border.right_on) {
|
||||||
|
right[0] = tt->style.border.right;
|
||||||
|
memcpy(&right[1], newline, nl_len);
|
||||||
|
} else
|
||||||
|
memcpy(&right[0], newline, nl_len);
|
||||||
|
|
||||||
|
|
||||||
|
/* allocate print buffer */
|
||||||
|
buf = XCALLOC(MTYPE_TMP, width * (nlines + 1) + 1);
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
if (tt->style.border.top_on) {
|
||||||
|
memcpy(&buf[pos], left, lsize);
|
||||||
|
pos += lsize;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < width - lsize - rsize; i++)
|
||||||
|
buf[pos++] = tt->style.border.top;
|
||||||
|
|
||||||
|
memcpy(&buf[pos], right, rsize);
|
||||||
|
pos += rsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tt->nrows; i++) {
|
||||||
|
row = tt->table[i];
|
||||||
|
|
||||||
|
/* if top border and not first row, print top row border */
|
||||||
|
if (row[0].style.border.top_on && i != 0) {
|
||||||
|
memcpy(&buf[pos], left, lsize);
|
||||||
|
pos += lsize;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < width - lsize - rsize; i++)
|
||||||
|
buf[pos++] = row[0].style.border.top;
|
||||||
|
|
||||||
|
pos -= width - lsize - rsize;
|
||||||
|
for (int k = 0; k < tt->ncols; k++) {
|
||||||
|
if (k != 0 && row[k].style.border.left_on)
|
||||||
|
buf[pos] = tt->style.corner;
|
||||||
|
pos += cw[k];
|
||||||
|
if (row[k].style.border.right_on
|
||||||
|
&& k != tt->ncols - 1)
|
||||||
|
buf[pos - 1] = tt->style.corner;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&buf[pos], right, rsize);
|
||||||
|
pos += rsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&buf[pos], left, lsize);
|
||||||
|
pos += lsize;
|
||||||
|
|
||||||
|
for (int j = 0; j < tt->ncols; j++) {
|
||||||
|
/* if left border && not first col print left border */
|
||||||
|
if (row[j].style.border.left_on && j != 0)
|
||||||
|
buf[pos++] = row[j].style.border.left;
|
||||||
|
|
||||||
|
/* print left padding */
|
||||||
|
for (int i = 0; i < row[j].style.lpad; i++)
|
||||||
|
buf[pos++] = ' ';
|
||||||
|
|
||||||
|
/* calculate padding for sprintf */
|
||||||
|
abspad = cw[j];
|
||||||
|
abspad -= row[j].style.rpad;
|
||||||
|
abspad -= row[j].style.lpad;
|
||||||
|
if (j != 0)
|
||||||
|
abspad -= row[j].style.border.left_on ? 1 : 0;
|
||||||
|
if (j != tt->ncols - 1)
|
||||||
|
abspad -= row[j].style.border.right_on ? 1 : 0;
|
||||||
|
|
||||||
|
/* print text */
|
||||||
|
const char *fmt;
|
||||||
|
if (row[j].style.align == LEFT)
|
||||||
|
fmt = "%-*s";
|
||||||
|
else
|
||||||
|
fmt = "%*s";
|
||||||
|
|
||||||
|
pos += sprintf(&buf[pos], fmt, abspad, row[j].text);
|
||||||
|
|
||||||
|
/* print right padding */
|
||||||
|
for (int i = 0; i < row[j].style.rpad; i++)
|
||||||
|
buf[pos++] = ' ';
|
||||||
|
|
||||||
|
/* if right border && not last col print right border */
|
||||||
|
if (row[j].style.border.right_on && j != tt->ncols - 1)
|
||||||
|
buf[pos++] = row[j].style.border.right;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&buf[pos], right, rsize);
|
||||||
|
pos += rsize;
|
||||||
|
|
||||||
|
/* if bottom border and not last row, print bottom border */
|
||||||
|
if (row[0].style.border.bottom_on && i != tt->nrows - 1) {
|
||||||
|
memcpy(&buf[pos], left, lsize);
|
||||||
|
pos += lsize;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < width - lsize - rsize; i++)
|
||||||
|
buf[pos++] = row[0].style.border.bottom;
|
||||||
|
|
||||||
|
pos -= width - lsize - rsize;
|
||||||
|
for (int k = 0; k < tt->ncols; k++) {
|
||||||
|
if (k != 0 && row[k].style.border.left_on)
|
||||||
|
buf[pos] = tt->style.corner;
|
||||||
|
pos += cw[k];
|
||||||
|
if (row[k].style.border.right_on
|
||||||
|
&& k != tt->ncols - 1)
|
||||||
|
buf[pos - 1] = tt->style.corner;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&buf[pos], right, rsize);
|
||||||
|
pos += rsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!buf[pos]); /* pos == & of first \0 in buf */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tt->style.border.bottom_on) {
|
||||||
|
memcpy(&buf[pos], left, lsize);
|
||||||
|
pos += lsize;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < width - lsize - rsize; i++)
|
||||||
|
buf[pos++] = tt->style.border.bottom;
|
||||||
|
|
||||||
|
memcpy(&buf[pos], right, rsize);
|
||||||
|
pos += rsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[pos] = '\0';
|
||||||
|
|
||||||
|
XFREE(MTYPE_TMP, left);
|
||||||
|
XFREE(MTYPE_TMP, right);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
293
lib/termtable.h
Normal file
293
lib/termtable.h
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
/*
|
||||||
|
* ASCII table generator.
|
||||||
|
* Copyright (C) 2017 Cumulus Networks
|
||||||
|
* Quentin Young
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; see the file COPYING; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#ifndef _TERMTABLE_H_
|
||||||
|
#define _TERMTABLE_H_
|
||||||
|
|
||||||
|
enum ttable_align {
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
TOP,
|
||||||
|
BOTTOM,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ttable_border {
|
||||||
|
char top;
|
||||||
|
char bottom;
|
||||||
|
char left;
|
||||||
|
char right;
|
||||||
|
|
||||||
|
bool top_on;
|
||||||
|
bool bottom_on;
|
||||||
|
bool left_on;
|
||||||
|
bool right_on;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* cell style and cell */
|
||||||
|
struct ttable_cellstyle {
|
||||||
|
short lpad;
|
||||||
|
short rpad;
|
||||||
|
enum ttable_align align;
|
||||||
|
struct ttable_border border;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ttable_cell {
|
||||||
|
char *text;
|
||||||
|
struct ttable_cellstyle style;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* table style and table */
|
||||||
|
struct ttable_style {
|
||||||
|
char corner; /* intersection */
|
||||||
|
int indent; /* left table indent */
|
||||||
|
bool rownums_on; /* show row numbers; unimplemented */
|
||||||
|
|
||||||
|
struct ttable_border border;
|
||||||
|
struct ttable_cellstyle cell;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ttable {
|
||||||
|
int nrows; /* number of rows */
|
||||||
|
int ncols; /* number of cols */
|
||||||
|
struct ttable_cell **table; /* table, row x col */
|
||||||
|
size_t size; /* size */
|
||||||
|
struct ttable_style style; /* style */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* some predefined styles */
|
||||||
|
#define TTSTYLE_ASCII 0
|
||||||
|
#define TTSTYLE_BLANK 1
|
||||||
|
|
||||||
|
extern struct ttable_style ttable_styles[2];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new table with the default style, which looks like this:
|
||||||
|
*
|
||||||
|
* +----------+----------+
|
||||||
|
* | column 1 | column 2 |
|
||||||
|
* +----------+----------+
|
||||||
|
* | data... | data!! |
|
||||||
|
* +----------+----------+
|
||||||
|
* | datums | 12345 |
|
||||||
|
* +----------+----------+
|
||||||
|
*
|
||||||
|
* @return the created table
|
||||||
|
*/
|
||||||
|
struct ttable *ttable_new(struct ttable_style *tts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a table and releases all associated resources.
|
||||||
|
*
|
||||||
|
* @param tt the table to destroy
|
||||||
|
*/
|
||||||
|
void ttable_del(struct ttable *tt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes an individual cell.
|
||||||
|
*
|
||||||
|
* @param cell the cell to destroy
|
||||||
|
*/
|
||||||
|
void ttable_cell_del(struct ttable_cell *cell);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new row at the given index.
|
||||||
|
*
|
||||||
|
* The row contents are determined by a format string. The format string has
|
||||||
|
* the same form as a regular printf format string, except that columns are
|
||||||
|
* delimited by '|'. For example, to make the first column of the table above,
|
||||||
|
* the call is:
|
||||||
|
*
|
||||||
|
* ttable_insert_row(<tt>, <n>, "%s|%s", "column 1", "column 2");
|
||||||
|
*
|
||||||
|
* All features of printf format strings are permissible here.
|
||||||
|
*
|
||||||
|
* Caveats:
|
||||||
|
* - At present you cannot insert '|' into a cell's contents.
|
||||||
|
* - If there are N columns, '|' must appear n-1 times or the row will not be
|
||||||
|
* created
|
||||||
|
*
|
||||||
|
* @param tt table to insert row into
|
||||||
|
* @param row the row number (begins at 0)
|
||||||
|
* @param format column-separated format string
|
||||||
|
* @param ... arguments to format string
|
||||||
|
*
|
||||||
|
* @return pointer to the first cell in the created row, or NULL if not enough
|
||||||
|
* columns were specified
|
||||||
|
*/
|
||||||
|
struct ttable_cell *ttable_insert_row(struct ttable *tt, unsigned int row,
|
||||||
|
const char *format, ...);
|
||||||
|
/**
|
||||||
|
* Inserts a new row at the end of the table.
|
||||||
|
*
|
||||||
|
* The row contents are determined by a format string. The format string has
|
||||||
|
* the same form as a regular printf format string, except that columns are
|
||||||
|
* delimited by '|'. For example, to make the first column of the table above,
|
||||||
|
* the call is:
|
||||||
|
*
|
||||||
|
* ttable_add_row(<tt>, "%s|%s", "column 1", "column 2");
|
||||||
|
*
|
||||||
|
* All features of printf format strings are permissible here.
|
||||||
|
*
|
||||||
|
* Caveats:
|
||||||
|
* - At present you cannot insert '|' into a cell's contents.
|
||||||
|
* - If there are N columns, '|' must appear n-1 times or the row will not be
|
||||||
|
* created
|
||||||
|
*
|
||||||
|
* @param tt table to insert row into
|
||||||
|
* @param format column-separated format string
|
||||||
|
* @param ... arguments to format string
|
||||||
|
*
|
||||||
|
* @return pointer to the first cell in the created row, or NULL if not enough
|
||||||
|
* columns were specified
|
||||||
|
*/
|
||||||
|
struct ttable_cell *ttable_add_row(struct ttable *tt, const char *format, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a row from the table.
|
||||||
|
*
|
||||||
|
* @param tt table to delete row from
|
||||||
|
* @param row the row number (begins at 0)
|
||||||
|
*/
|
||||||
|
void ttable_del_row(struct ttable *tt, unsigned int row);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets alignment for a range of cells.
|
||||||
|
*
|
||||||
|
* Available alignments are LEFT and RIGHT. Cell contents will be aligned
|
||||||
|
* accordingly, while respecting padding (if any). Suppose a cell has:
|
||||||
|
*
|
||||||
|
* lpad = 1
|
||||||
|
* rpad = 1
|
||||||
|
* align = RIGHT
|
||||||
|
* text = 'datums'
|
||||||
|
*
|
||||||
|
* The cell would look like:
|
||||||
|
*
|
||||||
|
* +-------------------+
|
||||||
|
* | datums |
|
||||||
|
* +-------------------+
|
||||||
|
*
|
||||||
|
* On the other hand:
|
||||||
|
*
|
||||||
|
* lpad = 1
|
||||||
|
* rpad = 10
|
||||||
|
* align = RIGHT
|
||||||
|
* text = 'datums'
|
||||||
|
*
|
||||||
|
* +-------------------+
|
||||||
|
* | datums |
|
||||||
|
* +-------------------+
|
||||||
|
*
|
||||||
|
* The default alignment is LEFT.
|
||||||
|
*
|
||||||
|
* @param tt the table to set alignment on
|
||||||
|
* @param srow starting row index
|
||||||
|
* @param scol starting column index
|
||||||
|
* @param nrow # rows to align
|
||||||
|
* @param ncol # cols to align
|
||||||
|
* @param align the alignment to set
|
||||||
|
*/
|
||||||
|
void ttable_align(struct ttable *tt, unsigned int srow, unsigned int scol,
|
||||||
|
unsigned int erow, unsigned int ecol,
|
||||||
|
enum ttable_align align);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets padding for a range of cells.
|
||||||
|
*
|
||||||
|
* Available padding options are LEFT and RIGHT (the alignment enum is reused).
|
||||||
|
* Both options may be set. Padding is treated as though it is stuck to the
|
||||||
|
* walls of the cell. Suppose a cell has:
|
||||||
|
*
|
||||||
|
* lpad = 4
|
||||||
|
* rpad = 2
|
||||||
|
* align = RIGHT
|
||||||
|
* text = 'datums'
|
||||||
|
*
|
||||||
|
* The cell padding, marked by '.', would look like:
|
||||||
|
*
|
||||||
|
* +--------------+
|
||||||
|
* | .datums. |
|
||||||
|
* +--------------+
|
||||||
|
*
|
||||||
|
* If the column is wider than the cell, the cell contents are aligned in an
|
||||||
|
* additional padded field according to the cell alignment.
|
||||||
|
*
|
||||||
|
* +--------------------+
|
||||||
|
* | Data!!!11!~~~~~:-) |
|
||||||
|
* +--------------------+
|
||||||
|
* | . datums. |
|
||||||
|
* +--------------------+
|
||||||
|
*
|
||||||
|
* @param tt the table to set padding on
|
||||||
|
* @param srow starting row index
|
||||||
|
* @param scol starting column index
|
||||||
|
* @param nrow # rows to pad
|
||||||
|
* @param ncol # cols to pad
|
||||||
|
* @param align LEFT or RIGHT
|
||||||
|
* @param pad # spaces to pad with
|
||||||
|
*/
|
||||||
|
void ttable_pad(struct ttable *tt, unsigned int srow, unsigned int scol,
|
||||||
|
unsigned int nrow, unsigned int ncol, enum ttable_align align,
|
||||||
|
short pad);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restyle all cells according to table.cell.style.
|
||||||
|
*
|
||||||
|
* @param tt table to restyle
|
||||||
|
*/
|
||||||
|
void ttable_restyle(struct ttable *tt);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn left/right column separators on or off for specified column.
|
||||||
|
*
|
||||||
|
* @param tt table
|
||||||
|
* @param col column index
|
||||||
|
* @param align left or right separators
|
||||||
|
* @param on true/false for on/off
|
||||||
|
* @param sep character to use
|
||||||
|
*/
|
||||||
|
void ttable_colseps(struct ttable *tt, unsigned int col,
|
||||||
|
enum ttable_align align, bool on, char sep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn bottom row separators on or off for specified row.
|
||||||
|
*
|
||||||
|
* @param tt table
|
||||||
|
* @param row row index
|
||||||
|
* @param align left or right separators
|
||||||
|
* @param on true/false for on/off
|
||||||
|
* @param sep character to use
|
||||||
|
*/
|
||||||
|
void ttable_rowseps(struct ttable *tt, unsigned int row,
|
||||||
|
enum ttable_align align, bool on, char sep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps a table to a heap-allocated string.
|
||||||
|
*
|
||||||
|
* Caller must free this string after use with
|
||||||
|
*
|
||||||
|
* XFREE (MTYPE_TMP, str);
|
||||||
|
*
|
||||||
|
* @param tt the table to dump
|
||||||
|
* @param newline the desired newline sequence to use, null terminated.
|
||||||
|
* @return table in text form
|
||||||
|
*/
|
||||||
|
char *ttable_dump(struct ttable *tt, const char *newline);
|
||||||
|
|
||||||
|
#endif /* _TERMTABLE_H */
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
|
@ -43,3 +43,4 @@ __pycache__
|
||||||
/lib/test_table
|
/lib/test_table
|
||||||
/lib/test_timer_correctness
|
/lib/test_timer_correctness
|
||||||
/lib/test_timer_performance
|
/lib/test_timer_performance
|
||||||
|
/lib/test_ttable
|
||||||
|
|
|
@ -44,6 +44,7 @@ check_PROGRAMS = \
|
||||||
lib/test_table \
|
lib/test_table \
|
||||||
lib/test_timer_correctness \
|
lib/test_timer_correctness \
|
||||||
lib/test_timer_performance \
|
lib/test_timer_performance \
|
||||||
|
lib/test_ttable \
|
||||||
lib/cli/test_cli \
|
lib/cli/test_cli \
|
||||||
lib/cli/test_commands \
|
lib/cli/test_commands \
|
||||||
$(TESTS_BGPD)
|
$(TESTS_BGPD)
|
||||||
|
@ -84,6 +85,7 @@ lib_test_timer_correctness_SOURCES = lib/test_timer_correctness.c \
|
||||||
helpers/c/prng.c
|
helpers/c/prng.c
|
||||||
lib_test_timer_performance_SOURCES = lib/test_timer_performance.c \
|
lib_test_timer_performance_SOURCES = lib/test_timer_performance.c \
|
||||||
helpers/c/prng.c
|
helpers/c/prng.c
|
||||||
|
lib_test_ttable_SOURCES = lib/test_ttable.c
|
||||||
lib_cli_test_cli_SOURCES = lib/cli/test_cli.c lib/cli/common_cli.c
|
lib_cli_test_cli_SOURCES = lib/cli/test_cli.c lib/cli/common_cli.c
|
||||||
lib_cli_test_commands_SOURCES = lib/cli/test_commands_defun.c \
|
lib_cli_test_commands_SOURCES = lib/cli/test_commands_defun.c \
|
||||||
lib/cli/test_commands.c \
|
lib/cli/test_commands.c \
|
||||||
|
@ -112,6 +114,7 @@ lib_test_stream_LDADD = $(ALL_TESTS_LDADD)
|
||||||
lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm
|
lib_test_table_LDADD = $(ALL_TESTS_LDADD) -lm
|
||||||
lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD)
|
lib_test_timer_correctness_LDADD = $(ALL_TESTS_LDADD)
|
||||||
lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD)
|
lib_test_timer_performance_LDADD = $(ALL_TESTS_LDADD)
|
||||||
|
lib_test_ttable_LDADD = $(ALL_TESTS_LDADD)
|
||||||
lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD)
|
lib_cli_test_cli_LDADD = $(ALL_TESTS_LDADD)
|
||||||
lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD)
|
lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD)
|
||||||
bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD)
|
bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD)
|
||||||
|
@ -140,7 +143,8 @@ EXTRA_DIST = \
|
||||||
lib/test_stream.py \
|
lib/test_stream.py \
|
||||||
lib/test_stream.refout \
|
lib/test_stream.refout \
|
||||||
lib/test_table.py \
|
lib/test_table.py \
|
||||||
lib/test_timer_correctness.py
|
lib/test_timer_correctness.py \
|
||||||
|
lib/test_ttable.py
|
||||||
|
|
||||||
.PHONY: tests.xml
|
.PHONY: tests.xml
|
||||||
tests.xml: $(check_PROGRAMS)
|
tests.xml: $(check_PROGRAMS)
|
||||||
|
|
183
tests/lib/test_ttable.c
Normal file
183
tests/lib/test_ttable.c
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* ASCII table generator.
|
||||||
|
* Copyright (C) 2017 Cumulus Networks
|
||||||
|
* Quentin Young
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; see the file COPYING; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
#include <zebra.h>
|
||||||
|
#include <termtable.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *table;
|
||||||
|
|
||||||
|
struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_ASCII]);
|
||||||
|
|
||||||
|
/* test printf compatibility and dimension counters */
|
||||||
|
ttable_add_row(tt, "%s|%s|%s", "Column 1", "Column 2", "Column 3");
|
||||||
|
assert(tt->ncols == 3);
|
||||||
|
assert(tt->nrows == 1);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* add new row with 1 column, assert that it is not added */
|
||||||
|
assert(ttable_add_row(tt, "%s", "Garbage") == NULL);
|
||||||
|
assert(tt->ncols == 3);
|
||||||
|
assert(tt->nrows == 1);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* add new row, assert that it is added */
|
||||||
|
assert(ttable_add_row(tt, "%s|%s|%s", "a", "b", "c"));
|
||||||
|
assert(tt->ncols == 3);
|
||||||
|
assert(tt->nrows == 2);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* add empty row, assert that it is added */
|
||||||
|
assert(ttable_add_row(tt, "||"));
|
||||||
|
assert(tt->ncols == 3);
|
||||||
|
assert(tt->nrows == 3);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* delete 1st row, assert that it is removed */
|
||||||
|
ttable_del_row(tt, 0);
|
||||||
|
assert(tt->ncols == 3);
|
||||||
|
assert(tt->nrows == 2);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* delete last row, assert that it is removed */
|
||||||
|
ttable_del_row(tt, 0);
|
||||||
|
assert(tt->ncols == 3);
|
||||||
|
assert(tt->nrows == 1);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* delete the remaining row, check dumping an empty table */
|
||||||
|
ttable_del_row(tt, 0);
|
||||||
|
assert(tt->ncols == 0);
|
||||||
|
assert(tt->nrows == 0);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* add new row */
|
||||||
|
ttable_add_row(tt, "%s|%s||%s|%9d", "slick", "black", "triple", 1337);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 1);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* add bigger row */
|
||||||
|
ttable_add_row(tt, "%s|%s||%s|%s",
|
||||||
|
"nebula dusk session streets twilight "
|
||||||
|
"pioneer beats yeah",
|
||||||
|
"prarie dog", "cornmeal", ":O -*_-*");
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 2);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* insert new row at beginning */
|
||||||
|
ttable_insert_row(tt, 0, "%s|%s||%d|%lf", "converting", "vegetarians",
|
||||||
|
2, 2015.0);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 3);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* insert new row at end */
|
||||||
|
ttable_insert_row(tt, tt->nrows - 1, "%s|%s||%d|%ld", "converting",
|
||||||
|
"vegetarians", 1, 2003L);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 4);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* insert new row at middle */
|
||||||
|
ttable_insert_row(tt, 1, "%s|%s||%s|%ld", "she", "pioneer", "aki", 1l);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 5);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* set alignment */
|
||||||
|
ttable_align(tt, 0, 1, 2, 2, LEFT);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 5);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
ttable_align(tt, 0, 1, 5, 1, RIGHT);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 5);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* set padding */
|
||||||
|
ttable_pad(tt, 0, 1, 1, 1, RIGHT, 2);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 5);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
ttable_pad(tt, 0, 0, 5, 4, LEFT, 2);
|
||||||
|
assert(tt->ncols == 5);
|
||||||
|
assert(tt->nrows == 5);
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* restyle */
|
||||||
|
tt->style.cell.border.bottom_on = false;
|
||||||
|
tt->style.cell.border.top_on = false;
|
||||||
|
tt->style.cell.border.right_on = false;
|
||||||
|
tt->style.cell.border.left_on = false;
|
||||||
|
ttable_restyle(tt);
|
||||||
|
|
||||||
|
/* top & double bottom border for top row */
|
||||||
|
ttable_rowseps(tt, 0, BOTTOM, true, '-');
|
||||||
|
ttable_rowseps(tt, 1, TOP, true, '-');
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* column separators for leftmost column */
|
||||||
|
ttable_colseps(tt, 0, RIGHT, true, '|');
|
||||||
|
table = ttable_dump(tt, "\n");
|
||||||
|
fprintf(stdout, "%s\n", table);
|
||||||
|
XFREE(MTYPE_TMP, table);
|
||||||
|
|
||||||
|
/* delete table */
|
||||||
|
ttable_del(tt);
|
||||||
|
}
|
4
tests/lib/test_ttable.py
Normal file
4
tests/lib/test_ttable.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import frrtest
|
||||||
|
|
||||||
|
class TestTTable(frrtest.TestRefOut):
|
||||||
|
program = './test_ttable'
|
143
tests/lib/test_ttable.refout
Normal file
143
tests/lib/test_ttable.refout
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
|--------------------------------|
|
||||||
|
| Column 1 | Column 2 | Column 3 |
|
||||||
|
|--------------------------------|
|
||||||
|
|
||||||
|
|--------------------------------|
|
||||||
|
| Column 1 | Column 2 | Column 3 |
|
||||||
|
|--------------------------------|
|
||||||
|
|
||||||
|
|--------------------------------|
|
||||||
|
| Column 1 | Column 2 | Column 3 |
|
||||||
|
|----------+----------+----------|
|
||||||
|
| a | b | c |
|
||||||
|
|--------------------------------|
|
||||||
|
|
||||||
|
|--------------------------------|
|
||||||
|
| Column 1 | Column 2 | Column 3 |
|
||||||
|
|----------+----------+----------|
|
||||||
|
| a | b | c |
|
||||||
|
|----------+----------+----------|
|
||||||
|
| | | |
|
||||||
|
|--------------------------------|
|
||||||
|
|
||||||
|
|-----------|
|
||||||
|
| a | b | c |
|
||||||
|
|---+---+---|
|
||||||
|
| | | |
|
||||||
|
|-----------|
|
||||||
|
|
||||||
|
|--------|
|
||||||
|
| | | |
|
||||||
|
|--------|
|
||||||
|
|
||||||
|
||
|
||||||
|
||
|
||||||
|
|
||||||
|
|---------------------------------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------|
|
||||||
|
|
||||||
|
|------------------------------------------------------------------------------------------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------------------------+------------+--+----------+-----------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians | | 2 | 2015.000000 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians | | 2 | 2015.000000 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| converting | vegetarians | | 1 | 2003 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians | | 2 | 2015.000000 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| she | pioneer | | aki | 1 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| converting | vegetarians | | 1 | 2003 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians | | 2 | 2015.000000 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| she | pioneer | | aki | 1 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| converting | vegetarians | | 1 | 2003 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians | | 2 | 2015.000000 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| she | pioneer | | aki | 1 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| converting | vegetarians | | 1 | 2003 |
|
||||||
|
|---------------------------------------------------------+-------------+--+----------+-------------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|---------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|----------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians | | 2 | 2015.000000 |
|
||||||
|
|---------------------------------------------------------+--------------+--+----------+-------------|
|
||||||
|
| she | pioneer | | aki | 1 |
|
||||||
|
|---------------------------------------------------------+--------------+--+----------+-------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|---------------------------------------------------------+--------------+--+----------+-------------|
|
||||||
|
| converting | vegetarians | | 1 | 2003 |
|
||||||
|
|---------------------------------------------------------+--------------+--+----------+-------------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|----------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|--------------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians | | 2 | 2015.000000 |
|
||||||
|
|----------------------------------------------------------+---------------+---+-----------+-------------|
|
||||||
|
| she | pioneer | | aki | 1 |
|
||||||
|
|----------------------------------------------------------+---------------+---+-----------+-------------|
|
||||||
|
| slick | black | | triple | 1337 |
|
||||||
|
|----------------------------------------------------------+---------------+---+-----------+-------------|
|
||||||
|
| converting | vegetarians | | 1 | 2003 |
|
||||||
|
|----------------------------------------------------------+---------------+---+-----------+-------------|
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog | | cornmeal | :O -*_-* |
|
||||||
|
|--------------------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|-----------------------------------------------------------------------------------------------|
|
||||||
|
| converting vegetarians 2 2015.000000 |
|
||||||
|
|-----------------------------------------------------------------------------------------------|
|
||||||
|
|-----------------------------------------------------------------------------------------------|
|
||||||
|
| she pioneer aki 1 |
|
||||||
|
| slick black triple 1337 |
|
||||||
|
| converting vegetarians 1 2003 |
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah prarie dog cornmeal :O -*_-* |
|
||||||
|
|-----------------------------------------------------------------------------------------------|
|
||||||
|
|
||||||
|
|------------------------------------------------------------------------------------------------|
|
||||||
|
| converting | vegetarians 2 2015.000000 |
|
||||||
|
|---------------------------------------------------------+--------------------------------------|
|
||||||
|
|---------------------------------------------------------+--------------------------------------|
|
||||||
|
| she | pioneer aki 1 |
|
||||||
|
| slick | black triple 1337 |
|
||||||
|
| converting | vegetarians 1 2003 |
|
||||||
|
| nebula dusk session streets twilight pioneer beats yeah | prarie dog cornmeal :O -*_-* |
|
||||||
|
|------------------------------------------------------------------------------------------------|
|
||||||
|
|
Loading…
Reference in a new issue