mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
453 lines
10 KiB
C
453 lines
10 KiB
C
/*
|
|
* zdev - Modify and display the persistent configuration of devices
|
|
*
|
|
* Copyright IBM Corp. 2016, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "lib/util_base.h"
|
|
|
|
#include "misc.h"
|
|
#include "table.h"
|
|
|
|
/* Search for a column in @columns which matches the specified @name. Return
|
|
* corresponding struct column on success, NULL otherwise. */
|
|
struct column *table_get_column(struct column *columns, const char *name)
|
|
{
|
|
struct column *c;
|
|
int i;
|
|
|
|
for (i = 0; columns[i].name; i++) {
|
|
c = &columns[i];
|
|
if (strcasecmp(c->name, name) == 0)
|
|
return c;
|
|
if (strchr(c->name, ':') && starts_with_nocase(name, c->name))
|
|
return c;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* cell - Representation of a single table cell
|
|
* @id: Column ID of the cell
|
|
* @heading: Actual column heading
|
|
* @value: Cell contents
|
|
* @width: Total width of cell
|
|
* @align: Alignment of cell text
|
|
*
|
|
*/
|
|
struct cell {
|
|
char *value;
|
|
char *heading;
|
|
int id;
|
|
int width;
|
|
align_t align;
|
|
};
|
|
|
|
/* Used for debugging. */
|
|
void cells_print(struct cell *cells, int indent)
|
|
{
|
|
int i;
|
|
|
|
printf("%*scells at %p\n", indent, "", (void *) cells);
|
|
if (!cells)
|
|
return;
|
|
indent += 2;
|
|
for (i = 0; cells[i].heading; i++) {
|
|
printf("%*scells[%d]:\n", indent, "", i);
|
|
printf("%*svalue=%s\n", indent + 2, "", cells[i].value);
|
|
printf("%*sheading=%s\n", indent + 2, "", cells[i].heading);
|
|
printf("%*sid=%d\n", indent + 2, "", cells[i].id);
|
|
printf("%*swidth=%d\n", indent + 2, "", cells[i].width);
|
|
printf("%*salign=%d\n", indent + 2, "", cells[i].align);
|
|
}
|
|
}
|
|
|
|
static void cells_free(struct cell *cells)
|
|
{
|
|
int i;
|
|
struct cell *c;
|
|
|
|
if (!cells)
|
|
return;
|
|
for (i = 0; cells[i].heading; i++) {
|
|
c = &cells[i];
|
|
free(c->value);
|
|
free(c->heading);
|
|
}
|
|
free(cells);
|
|
}
|
|
|
|
/* Return a newly allocated array of struct cells containing a struct cell for
|
|
* each default column in the specified table. */
|
|
static struct cell *cells_get_default(struct column *columns)
|
|
{
|
|
int i, num;
|
|
struct cell *cells;
|
|
|
|
/* Determine number of default columns. */
|
|
num = 0;
|
|
for (i = 0; columns[i].name; i++) {
|
|
if (!columns[i].def)
|
|
continue;
|
|
num++;
|
|
}
|
|
|
|
/* Allocated cells array. */
|
|
cells = misc_malloc((num + 1) * sizeof(struct cell));
|
|
|
|
/* Initialize cells for default columns. */
|
|
num = 0;
|
|
for (i = 0; columns[i].name; i++) {
|
|
if (!columns[i].def)
|
|
continue;
|
|
cells[num].heading = misc_strdup(columns[i].name);
|
|
cells[num].id = columns[i].id;
|
|
cells[num].align = columns[i].align;
|
|
num++;
|
|
}
|
|
|
|
return cells;
|
|
}
|
|
|
|
/* Return a newly allocated array of struct cells containing a struct cell for
|
|
* each column definition whose name was specified in strlist @names. */
|
|
static struct cell *cells_get(struct column *columns, struct util_list *names)
|
|
{
|
|
unsigned long num;
|
|
struct cell *cells;
|
|
int i;
|
|
const struct column *column;
|
|
struct strlist_node *s;
|
|
|
|
num = util_list_len(names);
|
|
cells = misc_malloc((num + 1) * sizeof(struct cell));
|
|
|
|
i = 0;
|
|
util_list_iterate(names, s) {
|
|
column = table_get_column(columns, s->str);
|
|
if (!column)
|
|
goto err_unknown;
|
|
cells[i].heading = misc_strdup(s->str);
|
|
cells[i].id = column->id;
|
|
cells[i].align = column->align;
|
|
i++;
|
|
}
|
|
|
|
return cells;
|
|
|
|
err_unknown:
|
|
cells_free(cells);
|
|
error("Unknown column name specified: %s\n", s->str);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Determine maximum width for values and headings in table. */
|
|
static void cells_get_width(struct cell *cells, struct util_list *items,
|
|
table_value_cb_t value_cb, void *data)
|
|
{
|
|
int i, len;
|
|
struct cell *c;
|
|
struct ptrlist_node *p;
|
|
char *val;
|
|
|
|
/* Initialize widths. */
|
|
for (i = 0; cells[i].heading; i++)
|
|
cells[i].width = strlen(cells[i].heading);
|
|
|
|
/* Determine largest width from items. */
|
|
util_list_iterate(items, p) {
|
|
for (i = 0; cells[i].heading; i++) {
|
|
c = &cells[i];
|
|
val = value_cb(p->ptr, c->id, c->heading, data);
|
|
if (!val)
|
|
continue;
|
|
len = strlen(val);
|
|
free(val);
|
|
if (c->width < len)
|
|
c->width = len;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return the optimal space delimiter for the specified cells. */
|
|
static const char *cells_get_space(struct cell *cells, struct util_list *items,
|
|
table_value_cb_t value_cb, void *data,
|
|
int indent)
|
|
{
|
|
char *val;
|
|
int columns, i, off_one, off_two, last, wrap_one, wrap_two, len;
|
|
struct ptrlist_node *p;
|
|
struct cell *c;
|
|
|
|
/* Quick exit on special case to make processing simpler. */
|
|
if (!cells[0].heading)
|
|
return " ";
|
|
|
|
/* Determine column offset of last column. */
|
|
off_one = indent;
|
|
off_two = indent;
|
|
for (i = 0; cells[i + 1].heading; i++) {
|
|
off_one += cells[i].width + 1;
|
|
off_two += cells[i].width + 2;
|
|
}
|
|
last = i;
|
|
|
|
/* Determine how many rows would wrap with either one or two spaces. */
|
|
columns = get_columns();
|
|
wrap_one = 0;
|
|
wrap_two = 0;
|
|
c = &cells[last];
|
|
util_list_iterate(items, p) {
|
|
val = value_cb(p->ptr, c->id, c->heading, data);
|
|
if (val) {
|
|
len = strlen(val);
|
|
free(val);
|
|
} else
|
|
len = 0;
|
|
if (off_one + len >= columns)
|
|
wrap_one++;
|
|
if (off_two + len >= columns)
|
|
wrap_two++;
|
|
}
|
|
|
|
/* Use two spaces if the number of lines that would wrap is the same
|
|
* for one and two spaces. */
|
|
if (wrap_two <= wrap_one)
|
|
return " ";
|
|
|
|
return " ";
|
|
}
|
|
|
|
/* Get all values for an item. */
|
|
static void cells_get_values(struct cell *cells, void *item,
|
|
table_value_cb_t value_cb, void *data)
|
|
{
|
|
int i;
|
|
struct cell *c;
|
|
|
|
for (i = 0; cells[i].heading; i++) {
|
|
c = &cells[i];
|
|
free(c->value);
|
|
c->value = value_cb(item, c->id, c->heading, data);
|
|
}
|
|
|
|
}
|
|
|
|
/* Print all headings. */
|
|
static void print_heading(struct cell *cells, const char *space, int indent)
|
|
{
|
|
int i, width;
|
|
struct cell *c;
|
|
|
|
if (indent > 0)
|
|
printf("%*s", indent, "");
|
|
for (i = 0; cells[i].heading; i++) {
|
|
c = &cells[i];
|
|
|
|
/* Don't print spaces in last cell. */
|
|
if (!cells[i + 1].heading)
|
|
width = 0;
|
|
else if (c->align == align_left)
|
|
width = -c->width;
|
|
else
|
|
width = c->width;
|
|
|
|
printf("%s%*s", i > 0 ? space : "", width, c->heading);
|
|
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* Print all cells in a row in list format. */
|
|
static void print_row(struct cell *cells, const char *space, int indent,
|
|
int wrap)
|
|
{
|
|
int i, width, columns, offset, slen;
|
|
struct cell *c;
|
|
|
|
columns = get_columns();
|
|
slen = strlen(space);
|
|
offset = indent;
|
|
|
|
if (indent > 0)
|
|
printf("%*s", indent, "");
|
|
|
|
for (i = 0; cells[i].heading; i++) {
|
|
c = &cells[i];
|
|
|
|
/* Don't print spaces in last cell. */
|
|
if (!cells[i + 1].heading)
|
|
width = 0;
|
|
else if (c->align == align_left)
|
|
width = -c->width;
|
|
else
|
|
width = c->width;
|
|
|
|
if (!wrap)
|
|
goto do_print;
|
|
|
|
offset += (i > 0 ? slen : 0);
|
|
if (c->value)
|
|
offset += MAX((size_t) abs(width), strlen(c->value));
|
|
else
|
|
offset += abs(width);
|
|
|
|
if (offset >= columns && i > 0) {
|
|
/* Printing this cell would wrap the line - start on
|
|
* the next line, indented to the second column. */
|
|
offset = indent + cells[0].width;
|
|
printf("\n%*s", offset, "");
|
|
if (c->value) {
|
|
offset += MAX((size_t) abs(width),
|
|
strlen(c->value));
|
|
} else
|
|
offset += abs(width);
|
|
}
|
|
|
|
do_print:
|
|
printf("%s%*s", i > 0 ? space : "", width,
|
|
c->value ? c->value : "");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* Print all cells in a row in pairs format. */
|
|
static void print_row_pairs(struct cell *cells)
|
|
{
|
|
int i;
|
|
struct cell *c;
|
|
char *val;
|
|
|
|
for (i = 0; cells[i].heading; i++) {
|
|
c = &cells[i];
|
|
val = quote_str(c->value ? c->value : "", 1);
|
|
printf("%s%s=%s", i > 0 ? " " : "", c->heading, val);
|
|
free(val);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* Print a table. Either in list form or in pairs form (if @pairs is set).
|
|
* In list form, print a heading if @heading is set. Show data from items
|
|
* in ptrlist @items. Specify column names of columns to be printed in
|
|
* @names. If @wrap is set, wrap overlong lines. , */
|
|
exit_code_t table_print(struct column *columns, table_value_cb_t get_value_cb,
|
|
void *data, struct util_list *items,
|
|
struct util_list *names, int heading, int pairs,
|
|
int indent, int wrap)
|
|
{
|
|
struct cell *cells;
|
|
struct ptrlist_node *p;
|
|
const char *space = " ";
|
|
|
|
/* Initialize cells array. */
|
|
if (!names || util_list_is_empty(names))
|
|
cells = cells_get_default(columns);
|
|
else {
|
|
cells = cells_get(columns, names);
|
|
if (!cells)
|
|
return EXIT_UNKNOWN_COLUMN;
|
|
}
|
|
|
|
if (!pairs) {
|
|
cells_get_width(cells, items, get_value_cb, data);
|
|
if (wrap) {
|
|
space = cells_get_space(cells, items, get_value_cb,
|
|
data, indent);
|
|
} else
|
|
space = " ";
|
|
if (heading)
|
|
print_heading(cells, space, indent);
|
|
}
|
|
|
|
/* Print rows. */
|
|
util_list_iterate(items, p) {
|
|
cells_get_values(cells, p->ptr, get_value_cb, data);
|
|
if (pairs)
|
|
print_row_pairs(cells);
|
|
else
|
|
print_row(cells, space, indent, wrap);
|
|
}
|
|
|
|
cells_free(cells);
|
|
|
|
return EXIT_OK;
|
|
}
|
|
|
|
exit_code_t table_check_columns(struct column *columns, struct util_list *names)
|
|
{
|
|
struct cell *cells;
|
|
|
|
if (!names || util_list_is_empty(names))
|
|
return EXIT_OK;
|
|
|
|
cells = cells_get(columns, names);
|
|
if (!cells)
|
|
return EXIT_UNKNOWN_COLUMN;
|
|
|
|
cells_free(cells);
|
|
|
|
return EXIT_OK;
|
|
}
|
|
|
|
enum {
|
|
column_name,
|
|
column_desc,
|
|
};
|
|
|
|
static struct column *columns_table = COLUMN_ARRAY(
|
|
COLUMN("COLUMN", align_left, column_name, 1, ""),
|
|
COLUMN("DESCRIPTION", align_left, column_desc, 1, "")
|
|
);
|
|
|
|
static char *columns_table_get_value(void *item, int id, const char *heading,
|
|
void *data)
|
|
{
|
|
struct column *c = item;
|
|
|
|
switch (id) {
|
|
case column_name:
|
|
return misc_strdup(c->name);
|
|
case column_desc:
|
|
return misc_strdup(c->desc);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Print a table listing available columns and their description. */
|
|
void table_print_columns(struct column *columns, struct util_list *names,
|
|
int heading, int pairs)
|
|
{
|
|
int i;
|
|
struct util_list *items;
|
|
|
|
items = ptrlist_new();
|
|
for (i = 0; columns[i].name; i++)
|
|
ptrlist_add(items, &columns[i]);
|
|
table_print(columns_table, columns_table_get_value, NULL, items, names,
|
|
heading, pairs, 0, 0);
|
|
ptrlist_free(items, 0);
|
|
}
|
|
|
|
/* Change the default state of column with @id in table @columns to @def. */
|
|
void table_set_default(struct column *columns, int id, int def)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; columns[i].name; i++) {
|
|
if (columns[i].id == id) {
|
|
columns[i].def = def;
|
|
break;
|
|
}
|
|
}
|
|
}
|