Files
s390-tools/zdev/src/table.c
Daniel S. Haischt 889293e557 zdev: implement --shell command line switch
Fix issue in the lszdev command where column names if using the --pairs
command line switch can contain characters that are not allowed to be
used as variable names in a shell environment.

Replace bad characters in column names by an underscore if the --shell
command line switch is specified along with the --pairs switch.

The additional --shell switch mimics what is already available in the
lsblk command and thus it shall be used along with the --pairs switch.

Signed-off-by: Daniel S. Haischt <modean@linux.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2022-12-07 16:50:16 +01:00

494 lines
11 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 <ctype.h>
#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;
}
bool is_shell_char(const char c)
{
/* check whether character is alphabetic */
if (tolower(c) >= 'a' && tolower(c) <= 'z')
return true;
if (isdigit(c) || c == '_')
return true;
return false;
}
/* replaces bad characters that won't work if using them
* as part of variable names in a shell environment
*/
char *replace_bad_chars(const char *value)
{
int i;
char *copy, c;
/* remove bad characters and replace them by an underscore */
copy = misc_strdup(value);
for (i = 0; (c = value[i]); i++) {
/*
* check whether character is alphabetic,
* first character of an env var can't be a digit
*/
if (!is_shell_char(c) || (i == 0 && isdigit(c)))
c = '_';
/* copy character into string copy */
copy[i] = c;
}
return copy;
}
/* 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 shell)
{
int i;
struct cell *c;
char *val;
char *heading;
for (i = 0; cells[i].heading; i++) {
c = &cells[i];
val = quote_str(c->value ? c->value : "", 1);
if (shell)
heading = replace_bad_chars(c->heading);
else
heading = misc_strdup(c->heading);
printf("%s%s=%s", i > 0 ? " " : "", heading, val);
free(val);
free(heading);
}
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, int shell)
{
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, shell);
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, 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;
}
}
}