mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
A compile with sparse (make C=1) shows the following warnings: util_rec.c:211:6: warning: symbol 'rec_print_wide' was not declared. Should it be static? util_rec.c:383:6: warning: symbol 'rec_print_csv_hdr' was not declared. Should it be static? util_rec.c:404:6: warning: symbol 'rec_print_csv' was not declared. Should it be static? These functions are only used internally from function util_rec_print_hdr(), declare them as static. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
625 lines
14 KiB
C
625 lines
14 KiB
C
/**
|
|
* util - Utility function library
|
|
*
|
|
* Print records in different output formats
|
|
*
|
|
* 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 <argz.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "lib/util_base.h"
|
|
#include "lib/util_libc.h"
|
|
#include "lib/util_list.h"
|
|
#include "lib/util_panic.h"
|
|
#include "lib/util_prg.h"
|
|
#include "lib/util_rec.h"
|
|
|
|
/*
|
|
* Field structure containing the string value and secondary information
|
|
*/
|
|
struct util_rec_fld {
|
|
char *key; /* Name of the filed */
|
|
char *hdr; /* Content of the header */
|
|
size_t len; /* Length of string argz array */
|
|
char *val; /* The value of the field */
|
|
enum util_rec_align align; /* Alignment of the field */
|
|
int width; /* Field width */
|
|
struct util_list_node node; /* Pointers to previous and next field */
|
|
};
|
|
|
|
/*
|
|
* Print formats
|
|
*/
|
|
struct rec_fmt {
|
|
enum {
|
|
REC_FMT_WIDE,
|
|
REC_FMT_LONG,
|
|
REC_FMT_CSV,
|
|
} type;
|
|
union {
|
|
struct wide_p {
|
|
char *hdr_sep;
|
|
char *col_sep;
|
|
int argz_sep;
|
|
} wide_p;
|
|
struct long_p {
|
|
char *hdr_sep;
|
|
char *col_sep;
|
|
int argz_sep;
|
|
char *key;
|
|
int key_size;
|
|
int val_size;
|
|
} long_p;
|
|
struct csv_p {
|
|
char *col_sep;
|
|
int argz_sep;
|
|
} csv_p;
|
|
} d;
|
|
int indent;
|
|
};
|
|
|
|
/*
|
|
* Record structure (internal representation)
|
|
*/
|
|
/// @cond
|
|
struct util_rec {
|
|
struct util_list *list; /* List of the fields */
|
|
struct rec_fmt fmt; /* Output format */
|
|
};
|
|
/// @endcond
|
|
|
|
struct util_list *__util_rec_get_list(struct util_rec *rec)
|
|
{
|
|
return rec->list;
|
|
}
|
|
|
|
/*
|
|
* Get the field according to a distinct key
|
|
*/
|
|
static struct util_rec_fld *rec_get_fld(struct util_rec *rec, const char *key)
|
|
{
|
|
struct util_rec_fld *fld;
|
|
|
|
util_list_iterate(rec->list, fld) {
|
|
if (!strcmp(fld->key, key))
|
|
return fld;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Return the key name of a field
|
|
*
|
|
* @param[in] fld Field for query
|
|
*
|
|
* @returns Pointer to key string
|
|
*/
|
|
const char *util_rec_fld_get_key(struct util_rec_fld *fld)
|
|
{
|
|
return fld->key;
|
|
}
|
|
|
|
/**
|
|
* Create a new record with "wide" output format
|
|
*
|
|
* @param[in] hdr_sep Header separator
|
|
*
|
|
* @returns Pointer to the created record
|
|
*/
|
|
struct util_rec *util_rec_new_wide(const char *hdr_sep)
|
|
{
|
|
struct util_rec *rec = util_malloc(sizeof(struct util_rec));
|
|
|
|
rec->list = util_list_new(struct util_rec_fld, node);
|
|
rec->fmt.type = REC_FMT_WIDE;
|
|
rec->fmt.d.wide_p.hdr_sep = util_strdup(hdr_sep);
|
|
rec->fmt.d.wide_p.argz_sep = ',';
|
|
rec->fmt.indent = 0;
|
|
return rec;
|
|
}
|
|
|
|
/*
|
|
* Print the indentation characters
|
|
*/
|
|
static inline void rec_print_indention(int indent)
|
|
{
|
|
if (indent <= 0)
|
|
return;
|
|
|
|
printf("%*s", indent, "");
|
|
}
|
|
|
|
/*
|
|
* Print record separator in "wide" output format
|
|
*/
|
|
static void rec_print_wide_separator(struct util_rec *rec)
|
|
{
|
|
const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep;
|
|
int size = 0, field_count = 0;
|
|
struct util_rec_fld *fld;
|
|
char *buf;
|
|
|
|
if (!hdr_sep)
|
|
return;
|
|
|
|
util_list_iterate(rec->list, fld) {
|
|
if (fld->hdr) {
|
|
size += fld->width;
|
|
field_count++;
|
|
}
|
|
}
|
|
|
|
size += field_count - 1;
|
|
buf = util_malloc(size + 1);
|
|
memset(buf, (int)hdr_sep[0], size);
|
|
buf[size] = 0;
|
|
rec_print_indention(rec->fmt.indent);
|
|
printf("%s\n", buf);
|
|
free(buf);
|
|
}
|
|
|
|
/*
|
|
* Print record header in "wide" output format
|
|
*/
|
|
static void rec_print_wide_hdr(struct util_rec *rec)
|
|
{
|
|
const char *hdr_sep = rec->fmt.d.wide_p.hdr_sep;
|
|
int col_nr = 0, size = 0, field_count = 0;
|
|
struct util_rec_fld *fld;
|
|
char *buf;
|
|
|
|
rec_print_indention(rec->fmt.indent);
|
|
util_list_iterate(rec->list, fld) {
|
|
if (col_nr)
|
|
printf(" ");
|
|
if (fld->hdr) {
|
|
if (fld->align == UTIL_REC_ALIGN_LEFT)
|
|
printf("%-*s", fld->width, fld->hdr);
|
|
else
|
|
printf("%*s", fld->width, fld->hdr);
|
|
size += fld->width;
|
|
field_count++;
|
|
}
|
|
col_nr++;
|
|
}
|
|
printf("\n");
|
|
if (!hdr_sep)
|
|
return;
|
|
size += field_count - 1;
|
|
if (hdr_sep) {
|
|
buf = util_malloc(size + 1);
|
|
memset(buf, (int)hdr_sep[0], size);
|
|
buf[size] = 0;
|
|
rec_print_indention(rec->fmt.indent);
|
|
printf("%s\n", buf);
|
|
free(buf);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print record field values in "wide" output format
|
|
*/
|
|
static void rec_print_wide(struct util_rec *rec)
|
|
{
|
|
const char argz_sep = rec->fmt.d.wide_p.argz_sep;
|
|
struct util_rec_fld *fld;
|
|
char argz_str[PAGE_SIZE];
|
|
int fld_count = 0;
|
|
char *entry;
|
|
|
|
rec_print_indention(rec->fmt.indent);
|
|
util_list_iterate(rec->list, fld) {
|
|
if (!fld->hdr)
|
|
continue;
|
|
if (fld_count)
|
|
printf(" ");
|
|
entry = fld->val;
|
|
if (argz_count(fld->val, fld->len) > 1) {
|
|
strcpy(argz_str, entry);
|
|
while ((entry = argz_next(fld->val, fld->len, entry)))
|
|
strcat(strncat(argz_str, &argz_sep, 1), entry);
|
|
entry = argz_str;
|
|
}
|
|
if (fld->align == UTIL_REC_ALIGN_LEFT)
|
|
printf("%-*s", fld->width, entry);
|
|
else
|
|
printf("%*s", fld->width, entry);
|
|
fld_count++;
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* Free private memory of record
|
|
*/
|
|
static void rec_free_wide(struct util_rec *rec)
|
|
{
|
|
free(rec->fmt.d.wide_p.hdr_sep);
|
|
}
|
|
|
|
/**
|
|
* Create a new record with "long" output format
|
|
*
|
|
* @param[in] hdr_sep Header separator
|
|
* @param[in] col_sep Column separator
|
|
* @param[in] key Primary key of record
|
|
* @param[in] key_size Width of left column i.e. keys
|
|
* @param[in] val_size Width of right column i.e. values
|
|
*
|
|
* @returns Pointer to the created record
|
|
*/
|
|
struct util_rec *util_rec_new_long(const char *hdr_sep, const char *col_sep,
|
|
const char *key, int key_size, int val_size)
|
|
{
|
|
struct util_rec *rec = util_malloc(sizeof(struct util_rec));
|
|
|
|
rec->list = util_list_new(struct util_rec_fld, node);
|
|
rec->fmt.type = REC_FMT_LONG;
|
|
rec->fmt.d.long_p.hdr_sep = util_strdup(hdr_sep);
|
|
rec->fmt.d.long_p.col_sep = util_strdup(col_sep);
|
|
rec->fmt.d.long_p.key = util_strdup(key);
|
|
rec->fmt.d.long_p.key_size = key_size;
|
|
rec->fmt.d.long_p.val_size = val_size;
|
|
rec->fmt.d.long_p.argz_sep = ' ';
|
|
rec->fmt.indent = 0;
|
|
return rec;
|
|
}
|
|
|
|
/*
|
|
* Print field header in "long" output format
|
|
*/
|
|
static void rec_print_long_hdr(struct util_rec *rec)
|
|
{
|
|
struct long_p *p = &rec->fmt.d.long_p;
|
|
struct util_rec_fld *fld;
|
|
int len = 0;
|
|
char *buf;
|
|
|
|
fld = rec_get_fld(rec, p->key);
|
|
util_assert(fld != NULL, "Record not found\n");
|
|
util_assert(fld->hdr != NULL, "Header for field not found\n");
|
|
rec_print_indention(rec->fmt.indent);
|
|
if (p->col_sep) {
|
|
printf("%-*s %s %-*s\n", p->key_size, fld->hdr,
|
|
p->col_sep, fld->width, fld->val);
|
|
len = p->key_size + p->val_size + 3;
|
|
} else {
|
|
printf("%-*s %-*s\n", p->key_size, fld->hdr, fld->width,
|
|
fld->val);
|
|
len = p->key_size + p->val_size + 1;
|
|
}
|
|
if (!p->hdr_sep)
|
|
return;
|
|
buf = util_malloc(len + 1);
|
|
memset(buf, p->hdr_sep[0], len);
|
|
buf[len] = 0;
|
|
rec_print_indention(rec->fmt.indent);
|
|
printf("%s\n", buf);
|
|
free(buf);
|
|
}
|
|
|
|
/*
|
|
* Print record field values in "long" output format
|
|
*/
|
|
static void rec_print_long(struct util_rec *rec)
|
|
{
|
|
struct long_p *p = &rec->fmt.d.long_p;
|
|
struct util_rec_fld *fld;
|
|
char *item = NULL;
|
|
|
|
rec_print_long_hdr(rec);
|
|
|
|
util_list_iterate(rec->list, fld) {
|
|
if (!fld->hdr)
|
|
continue;
|
|
if (!strcmp(p->key, fld->key))
|
|
continue;
|
|
if (!fld->val)
|
|
continue;
|
|
rec_print_indention(rec->fmt.indent);
|
|
item = argz_next(fld->val, fld->len, item);
|
|
if (p->col_sep) {
|
|
printf(" %-*s %s %s\n",
|
|
p->key_size - 8, fld->hdr, p->col_sep, item);
|
|
while ((item = argz_next(fld->val, fld->len, item))) {
|
|
rec_print_indention(rec->fmt.indent);
|
|
printf(" %-*s %c %s\n",
|
|
p->key_size - 8, "", p->argz_sep, item);
|
|
}
|
|
} else {
|
|
printf(" %-*s %s\n",
|
|
p->key_size - 8, fld->hdr, fld->val);
|
|
while ((item = argz_next(fld->val, fld->len, item))) {
|
|
rec_print_indention(rec->fmt.indent);
|
|
printf(" %-*s %s\n",
|
|
p->key_size - 8, "", item);
|
|
}
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* Free private memory of record
|
|
*/
|
|
static void rec_free_long(struct util_rec *rec)
|
|
{
|
|
free(rec->fmt.d.long_p.hdr_sep);
|
|
free(rec->fmt.d.long_p.col_sep);
|
|
free(rec->fmt.d.long_p.key);
|
|
}
|
|
|
|
/**
|
|
* Create a new record with "csv" output format
|
|
*
|
|
* @param[in] col_sep Column separator
|
|
*
|
|
* @returns Pointer to the created record
|
|
*/
|
|
struct util_rec *util_rec_new_csv(const char *col_sep)
|
|
{
|
|
struct util_rec *rec = util_malloc(sizeof(struct util_rec));
|
|
|
|
rec->list = util_list_new(struct util_rec_fld, node);
|
|
rec->fmt.type = REC_FMT_CSV;
|
|
rec->fmt.d.csv_p.col_sep = util_strdup(col_sep);
|
|
rec->fmt.d.csv_p.argz_sep = ' ';
|
|
rec->fmt.indent = 0;
|
|
return rec;
|
|
}
|
|
|
|
/*
|
|
* Print record header in "csv" output format
|
|
*/
|
|
static void rec_print_csv_hdr(struct util_rec *rec)
|
|
{
|
|
const char *col_sep = rec->fmt.d.csv_p.col_sep;
|
|
struct util_rec_fld *fld;
|
|
int fld_count = 0;
|
|
|
|
rec_print_indention(rec->fmt.indent);
|
|
util_list_iterate(rec->list, fld) {
|
|
if (fld_count)
|
|
printf("%c", *col_sep);
|
|
if (fld->hdr) {
|
|
printf("%s", fld->hdr);
|
|
fld_count++;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* Print record field values in "csv" output format
|
|
*/
|
|
static void rec_print_csv(struct util_rec *rec)
|
|
{
|
|
const char argz_sep = rec->fmt.d.csv_p.argz_sep;
|
|
const char *col_sep = rec->fmt.d.csv_p.col_sep;
|
|
struct util_rec_fld *fld;
|
|
int fld_count = 0;
|
|
char *item = NULL;
|
|
|
|
rec_print_indention(rec->fmt.indent);
|
|
util_list_iterate(rec->list, fld) {
|
|
item = argz_next(fld->val, fld->len, item);
|
|
if (fld_count)
|
|
printf("%c", *col_sep);
|
|
if (fld->hdr) {
|
|
printf("%s", item);
|
|
while ((item = argz_next(fld->val, fld->len, item)))
|
|
printf("%c%s", argz_sep, item);
|
|
fld_count++;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/*
|
|
* Free private memory of record
|
|
*/
|
|
static void rec_free_csv(struct util_rec *rec)
|
|
{
|
|
free(rec->fmt.d.csv_p.col_sep);
|
|
}
|
|
|
|
/**
|
|
* Define a new field for the record
|
|
*
|
|
* @param[in] rec Pointer of the record
|
|
* @param[in] key Key of the filed is to be created. It should be unique
|
|
* within the record.
|
|
* @param[in] align Alignment of field
|
|
* @param[in] width Width of field
|
|
* @param[in] hdr This information is printed in record headers. If it is
|
|
* NULL, the field is prohibited form printing completely.
|
|
*/
|
|
void util_rec_def(struct util_rec *rec, const char *key,
|
|
enum util_rec_align align, int width, const char *hdr)
|
|
{
|
|
struct util_rec_fld *fld = util_malloc(sizeof(struct util_rec_fld));
|
|
|
|
fld->key = util_strdup(key);
|
|
fld->hdr = util_strdup(hdr);
|
|
fld->val = NULL;
|
|
fld->align = align;
|
|
fld->width = width;
|
|
util_list_add_tail(rec->list, fld);
|
|
}
|
|
|
|
/**
|
|
* Free record and associated fields
|
|
*
|
|
* @param[in] rec Record pointer
|
|
*/
|
|
void util_rec_free(struct util_rec *rec)
|
|
{
|
|
struct util_rec_fld *fld, *tmp;
|
|
|
|
util_list_iterate_safe(rec->list, fld, tmp) {
|
|
util_list_remove(rec->list, fld);
|
|
free(fld->key);
|
|
free(fld->hdr);
|
|
free(fld->val);
|
|
free(fld);
|
|
}
|
|
util_list_free(rec->list);
|
|
switch (rec->fmt.type) {
|
|
case REC_FMT_WIDE:
|
|
rec_free_wide(rec);
|
|
break;
|
|
case REC_FMT_LONG:
|
|
rec_free_long(rec);
|
|
break;
|
|
case REC_FMT_CSV:
|
|
rec_free_csv(rec);
|
|
break;
|
|
}
|
|
free(rec);
|
|
}
|
|
|
|
/**
|
|
* Print record field values according to output format
|
|
*
|
|
* @param[in] rec Record pointer
|
|
*/
|
|
void util_rec_print(struct util_rec *rec)
|
|
{
|
|
switch (rec->fmt.type) {
|
|
case REC_FMT_WIDE:
|
|
rec_print_wide(rec);
|
|
break;
|
|
case REC_FMT_LONG:
|
|
rec_print_long(rec);
|
|
break;
|
|
case REC_FMT_CSV:
|
|
rec_print_csv(rec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print record header according to output format
|
|
*
|
|
* @param[in] rec Record pointer
|
|
*/
|
|
void util_rec_print_hdr(struct util_rec *rec)
|
|
{
|
|
switch (rec->fmt.type) {
|
|
case REC_FMT_WIDE:
|
|
rec_print_wide_hdr(rec);
|
|
break;
|
|
case REC_FMT_LONG:
|
|
break;
|
|
case REC_FMT_CSV:
|
|
rec_print_csv_hdr(rec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print record separator according to output format
|
|
*
|
|
* @param[in] rec Record pointer
|
|
*/
|
|
void util_rec_print_separator(struct util_rec *rec)
|
|
{
|
|
switch (rec->fmt.type) {
|
|
case REC_FMT_WIDE:
|
|
rec_print_wide_separator(rec);
|
|
break;
|
|
case REC_FMT_LONG:
|
|
break;
|
|
case REC_FMT_CSV:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set a field value to an argz vector
|
|
*
|
|
* @param[in] rec Record pointer
|
|
* @param[in] key Key of the desired field
|
|
* @param[in] argz Pointer to the series of strings
|
|
* @param[in] len Length of the argz buffer
|
|
*/
|
|
void util_rec_set_argz(struct util_rec *rec, const char *key, const char *argz,
|
|
size_t len)
|
|
{
|
|
struct util_rec_fld *fld;
|
|
char *val;
|
|
|
|
fld = rec_get_fld(rec, key);
|
|
if (!fld)
|
|
return;
|
|
val = util_malloc(len);
|
|
val = memcpy(val, argz, len);
|
|
free(fld->val);
|
|
fld->val = val;
|
|
fld->len = len;
|
|
}
|
|
|
|
/**
|
|
* Set a field value to a formatted string
|
|
*
|
|
* @param[in] rec Record pointer
|
|
* @param[in] key Key of the desired field
|
|
* @param[in] fmt Format string for generation of value string
|
|
* @param[in] ... Parameters for format string
|
|
*
|
|
* @returns Pointer to the field which was modified or NULL in the case of
|
|
* any error.
|
|
*/
|
|
void util_rec_set(struct util_rec *rec, const char *key, const char *fmt, ...)
|
|
{
|
|
struct util_rec_fld *fld;
|
|
va_list ap;
|
|
char *str;
|
|
|
|
util_assert(fmt != NULL, "Parameter 'fmt' pointer must not be NULL\n");
|
|
fld = rec_get_fld(rec, key);
|
|
if (!fld)
|
|
return;
|
|
UTIL_VASPRINTF(&str, fmt, ap);
|
|
free(fld->val);
|
|
fld->val = str;
|
|
fld->len = strlen(str) + 1;
|
|
}
|
|
|
|
/**
|
|
* Return the string value of a desired field. If the field value stored in argz
|
|
* format, pointer to the first argz element is returned.
|
|
*
|
|
* @param[in] rec Record pointer
|
|
* @param[in] key Key of the field
|
|
*
|
|
* @returns If the desired field was found, the pointer to its value.
|
|
* NULL in the case of any error or if the field is empty.
|
|
*/
|
|
const char *util_rec_get(struct util_rec *rec, const char *key)
|
|
{
|
|
struct util_rec_fld *fld = rec_get_fld(rec, key);
|
|
|
|
return (fld != NULL) ? fld->val : NULL;
|
|
}
|
|
|
|
/**
|
|
* Sets the indentation of the record
|
|
*
|
|
* @param[in] rec Record pointer
|
|
* @param[in] indent Number of characters to indent
|
|
*/
|
|
void util_rec_set_indent(struct util_rec *rec, int indent)
|
|
{
|
|
rec->fmt.indent = indent;
|
|
}
|