It solves problems with alignment of columns. Width of each column is calculated by its biggest cell. Should solve unicode bug. In future, it may be implemented in virsh, virt-admin... This API has 5 public functions: - vshTableNew - adds new table and defines its header - vshTableRowAppend - appends new row (for same number of columns as in header) - vshTablePrintToStdout - vshTablePrintToString - vshTableFree https://bugzilla.redhat.com/show_bug.cgi?id=1574624 https://bugzilla.redhat.com/show_bug.cgi?id=1584630 Signed-off-by: Simon Kobyda <skobyda@xxxxxxxxxx> --- tools/Makefile.am | 4 +- tools/vsh-table.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++ tools/vsh-table.h | 42 ++++++ 3 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 tools/vsh-table.c create mode 100644 tools/vsh-table.h diff --git a/tools/Makefile.am b/tools/Makefile.am index 26c887649e..ed23575aa7 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -144,7 +144,9 @@ libvirt_shell_la_LIBADD = \ $(READLINE_LIBS) \ ../gnulib/lib/libgnu.la \ $(NULL) -libvirt_shell_la_SOURCES = vsh.c vsh.h +libvirt_shell_la_SOURCES = \ + vsh.c vsh.h \ + vsh-table.c vsh-table.h virt_host_validate_SOURCES = \ virt-host-validate.c \ diff --git a/tools/vsh-table.c b/tools/vsh-table.c new file mode 100644 index 0000000000..11dc807ba9 --- /dev/null +++ b/tools/vsh-table.c @@ -0,0 +1,367 @@ +/* + * vsh-table.c: table printing helper + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Simon Kobyda <skobyda@xxxxxxxxxx> + * + */ + +#include <config.h> +#include "vsh-table.h" +#include "virsh-util.h" + +#include <string.h> +#include <stdarg.h> +#include <stddef.h> + +#include "viralloc.h" +#include "virbuffer.h" +#include "virstring.h" + +typedef void (*vshPrintCB)(vshControl *ctl, const char *fmt, ...); + +struct _vshTableRow { + char **cells; + size_t ncells; +}; + +struct _vshTable { + vshTableRowPtr *rows; + size_t nrows; +}; + +static void +vshTableRowFree(vshTableRowPtr row) +{ + size_t i; + + if (!row) + return; + + for (i = 0; i < row->ncells; i++) + VIR_FREE(row->cells[i]); + + VIR_FREE(row->cells); + VIR_FREE(row); +} + +void +vshTableFree(vshTablePtr table) +{ + size_t i; + + if (!table) + return; + + for (i = 0; i < table->nrows; i++) + vshTableRowFree(table->rows[i]); + VIR_FREE(table->rows); + VIR_FREE(table); +} + +/** + * vshTableRowNew: + * @size: expected number of cells in row. + * @arg: the first argument. + * @ap: list of variadic arguments + * + * Creates a new row in the table. Each argument passed + * represents a cell in the row. If the number of cells is not + * equal to @size, then NULL is returned. However, if @size is + * equal to 0, the aforementioned check is suppressed. + * + * Returns: pointer to vshTableRowPtr row or NULL. + */ +static vshTableRowPtr +vshTableRowNew(size_t size, const char *arg, va_list ap) +{ + vshTableRowPtr row = NULL; + char *tmp = NULL; + + if (!arg) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Table row cannot be empty")); + goto error; + } + + if (VIR_ALLOC(row) < 0) + goto error; + + while (arg) { + if (VIR_STRDUP(tmp, arg) < 0) + goto error; + + if (VIR_APPEND_ELEMENT(row->cells, row->ncells, tmp) < 0) + goto error; + + arg = va_arg(ap, const char *); + } + + if (size && row->ncells != size) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Incorrect number of cells in a table row")); + goto error; + } + + return row; + + error: + vshTableRowFree(row); + return NULL; +} + +/** + * vshTableNew: + * @arg: List of column names (NULL terminated) + * + * Creates a new table. + * + * Returns: pointer to table or NULL. + */ +vshTablePtr +vshTableNew(const char *arg, ...) +{ + vshTablePtr table; + vshTableRowPtr header = NULL; + va_list ap; + + if (VIR_ALLOC(table) < 0) + goto error; + + va_start(ap, arg); + header = vshTableRowNew(0, arg, ap); + va_end(ap); + + if (!header) + goto error; + + if (VIR_APPEND_ELEMENT(table->rows, table->nrows, header) < 0) + goto error; + + return table; + error: + vshTableRowFree(header); + vshTableFree(table); + return NULL; +} + +/** + * vshTableRowAppend: + * @table: table to append to + * @arg: cells of the row (NULL terminated) + * + * Appends new row into the @table. The number of cells in the row has + * to be equal to the number of cells in the table header. + * + * Returns: 0 if succeeded, -1 if failed. + */ +int +vshTableRowAppend(vshTablePtr table, const char *arg, ...) +{ + vshTableRowPtr row = NULL; + size_t ncolumns = table->rows[0]->ncells; + va_list ap; + int ret = -1; + + va_start(ap, arg); + row = vshTableRowNew(ncolumns, arg, ap); + va_end(ap); + + if (!row) + goto cleanup; + + if (VIR_APPEND_ELEMENT(table->rows, table->nrows, row) < 0) + goto cleanup; + + ret = 0; + cleanup: + vshTableRowFree(row); + return ret; +} + +/** + * vshTableGetColumnsWidths: + * @table: table + * @maxwidths: maximum count of characters for each columns + * @widths: count of characters for each cell in the table + * + * Fills passed @maxwidths and @widths arrays with maximum number + * of characters for columns and number of character per each + * table cell, respectively. + * + * Tries to handle unicode strings. + */ +static void +vshTableGetColumnsWidths(vshTablePtr table, + size_t **maxwidths, + size_t ***widths) +{ + size_t i; + size_t j; + + for (i = 0; i < table->nrows; i++) { + vshTableRowPtr row = table->rows[i]; + + for (j = 0; j < row->ncells; j++) { + size_t len; + + len = mbstowcs(NULL, row->cells[j], 0); + + if (len == (size_t) -1) + len = strlen(row->cells[j]); + + (*widths)[i][j] = len; + if (len > (*maxwidths)[j]) + (*maxwidths)[j] = len; + } + } +} + +/** + * vshTableRowPrint: + * @ctl virtshell control structure + * @row: table to append to + * @maxwidths: maximum count of characters for each columns + * @widths: count of character for each cell in this row + * @printCB function for priting table + * @buf: buffer to store table (only if @toStdout == true) + * @toStdout: whetever print table to Stdout or return in buffer + */ +static void +vshTableRowPrint(vshControl *ctl, + vshTableRowPtr row, + size_t *maxwidths, + size_t *widths, + vshPrintCB printCB, + virBufferPtr buf, + bool toStdout) +{ + size_t i; + size_t j; + + if (toStdout) { + for (i = 0; i < row->ncells; i++) { + printCB(ctl, " %s", row->cells[i]); + + for (j = 0; j < maxwidths[i] - widths[i] + 2; j++) + printCB(ctl, " "); + } + printCB(ctl, "\n"); + } else { + for (i = 0; i < row->ncells; i++) { + virBufferAsprintf(buf, " %s", row->cells[i]); + + for (j = 0; j < maxwidths[i] - widths[i] + 2; j++) + virBufferAddStr(buf, " "); + } + virBufferAddStr(buf, "\n"); + } + +} + +/** + * vshTablePrint: + * @ctl virtshell control structure + * @table: table to print + * @toStdout: whetever to print to stdout (true) or return in string (false) + * + * Prints table. To get an alignment of columns right, function + * fills 2d array @widths with count of characters in each cell and + * array @maxwidths maximum count of character in each column. + * Function then prints tables header and content. + * + * Returns string containing table, or NULL if table was printed to + * stdout + */ +static char * +vshTablePrint(vshControl *ctl, vshTablePtr table, bool toStdout) +{ + size_t i; + size_t j; + size_t *maxwidths; + size_t **widths; + virBuffer buf = VIR_BUFFER_INITIALIZER; + char *ret = NULL; + + if (VIR_ALLOC_N(maxwidths, table->rows[0]->ncells)) + goto cleanup; + + if (VIR_ALLOC_N(widths, table->nrows)) + goto cleanup; + + /* retrieve widths of columns */ + for (i = 0; i < table->nrows; i++) { + if (VIR_ALLOC_N(widths[i], table->rows[0]->ncells)) + goto cleanup; + } + + vshTableGetColumnsWidths(table, &maxwidths, &widths); + + /* print header */ + VIR_WARNINGS_NO_PRINTF + vshTableRowPrint(ctl, table->rows[0], maxwidths, widths[0], + vshPrintExtra, &buf, toStdout); + VIR_WARNINGS_RESET + + /* print dividing line */ + if (toStdout) { + for (i = 0; i < table->rows[0]->ncells; i++) { + for (j = 0; j < maxwidths[i] + 3; j++) + vshPrintExtra(ctl, "-"); + } + vshPrintExtra(ctl, "\n"); + } else { + for (i = 0; i < table->rows[0]->ncells; i++) { + for (j = 0; j < maxwidths[i] + 3; j++) + virBufferAddStr(&buf, "-"); + } + virBufferAddStr(&buf, "\n"); + } + + /* print content */ + for (i = 1; i < table->nrows; i++) { + VIR_WARNINGS_NO_PRINTF + vshTableRowPrint(ctl, table->rows[i], maxwidths, widths[0], + vshPrintExtra, &buf, toStdout); + VIR_WARNINGS_RESET + } + + if (!toStdout) + ret = virBufferContentAndReset(&buf); + + cleanup: + VIR_FREE(maxwidths); + for (i = 0; i < table->nrows; i++) + VIR_FREE(widths[i]); + VIR_FREE(widths); + return ret; +} + + +void +vshTablePrintToStdout(vshControl *ctl, vshTablePtr table) +{ + vshTablePrint(ctl, table, true); +} + +char * +vshTablePrintToString(vshControl *ctl, vshTablePtr table) +{ + return vshTablePrint(ctl, table, false); +} diff --git a/tools/vsh-table.h b/tools/vsh-table.h new file mode 100644 index 0000000000..41600fe06f --- /dev/null +++ b/tools/vsh-table.h @@ -0,0 +1,42 @@ +/* + * vsh-table.h: table printing helper + * + * Copyright (C) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Simon Kobyda <skobyda@xxxxxxxxxx> + * + */ + +#ifndef VSH_TABLE_H +# define VSH_TABLE_H + +# include "vsh.h" + +/* forward declarations */ +typedef struct _vshTable vshTable; +typedef struct _vshTableRow vshTableRow; +typedef vshTable *vshTablePtr; +typedef vshTableRow *vshTableRowPtr; + +void vshTableFree(vshTablePtr table); +vshTablePtr vshTableNew(const char *format, ...); +int vshTableRowAppend(vshTablePtr table, const char *arg, ...); +void vshTablePrintToStdout(vshControl *ctl, vshTablePtr table); +char *vshTablePrintToString(vshControl *ctl, vshTablePtr table); + +#endif /* VSH_TABLE_H */ -- 2.17.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list