Files
s390-tools/hyptop/table.c
Bjoern Walk e61ed4f433 hyptop: Support for structured output
Introduce a new command line flag ("--format") to specify the output
format in batch mode. Valid options are "pairs" for shell-compatible key
value pairs, "csv" for CSV, "json" for a formatted JSON document, and
"json-seq" for a stream of JSON text sequences as per RFC7464[1].

[1]: https://datatracker.ietf.org/doc/html/rfc7464

Note: Specifying the --format flag implies the --batch_mode flag.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Bjoern Walk <bwalk@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-11-29 15:52:24 +01:00

1294 lines
26 KiB
C

/*
* hyptop - Show hypervisor performance data on System z
*
* Table module: Provide line mode and curses base table
*
* Copyright IBM Corp. 2010, 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 <errno.h>
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lib/util_fmt.h"
#include "lib/util_libc.h"
#include "helper.h"
#include "hyptop.h"
#include "table.h"
#define L_ROWS_EXTRA 2 /* head + last */
#define table_col_iterate(t, col, i) \
for (i = 0, col = t->col_vec[0]; col != NULL; col = t->col_vec[++i])
/*
* Is row marked?
*/
static int l_row_is_marked(struct table *t, struct table_row *row)
{
struct table_mark_key *key;
util_list_iterate(&t->mark_key_list, key) {
if (strcmp(row->entries[0].str, key->str) == 0)
return 1;
}
return 0;
}
/*
* Add mark key to table
*/
static void l_mark_key_add(struct table *t, char *str)
{
struct table_mark_key *key;
key = ht_zalloc(sizeof(*key));
util_strlcpy(key->str, str, sizeof(key->str));
util_list_add_tail(&t->mark_key_list, key);
}
/*
* Remove mark key from table
*/
static void l_mark_key_remove(struct table *t, char *str)
{
struct table_mark_key *key, *tmp;
util_list_iterate_safe(&t->mark_key_list, key, tmp) {
if (strcmp(str, key->str) == 0) {
util_list_remove(&t->mark_key_list, key);
ht_free(key);
return;
}
}
}
/*
* Delete all mark keys from table
*/
void table_row_mark_del_all(struct table *t)
{
struct table_mark_key *key, *tmp;
struct table_row *row;
util_list_iterate(&t->row_list, row)
row->marked = 0;
util_list_iterate_safe(&t->mark_key_list, key, tmp) {
util_list_remove(&t->mark_key_list, key);
ht_free(key);
}
}
/*
* Toggle mark for "row"
*/
void table_row_mark_toggle(struct table *t, struct table_row *row)
{
if (row->marked) {
l_mark_key_remove(t, row->entries[0].str);
row->marked = 0;
t->row_cnt_marked--;
if (t->row_cnt_marked == 0)
t->mode_hide_unmarked = 0;
} else {
l_mark_key_add(t, row->entries[0].str);
row->marked = 1;
t->row_cnt_marked++;
}
}
/*
* Toggle mark by key
*/
void table_row_mark_toggle_by_key(struct table *t, const char *str)
{
struct table_row *row;
util_list_iterate(&t->row_list, row) {
if (strcmp(str, row->entries[0].str) == 0)
table_row_mark_toggle(t, row);
}
}
/*
* Is column selected?
*/
static int l_col_selected(struct table *t, struct table_col *col)
{
return t->col_selected == col;
}
/*
* Get number of rows for table
*/
static int l_row_cnt(struct table *t)
{
return t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt;
}
/*
* Get number of data rows that we can display on screen
*/
static int l_row_cnt_displ(struct table *t)
{
return g.c.row_cnt - t->row_cnt_extra;
}
/*
* Alloc a new row for table
*/
struct table_row *table_row_alloc(struct table *t)
{
struct table_row *table_row;
table_row = ht_zalloc(sizeof(*table_row));
table_row->entries = ht_zalloc(sizeof(*table_row->entries) *
t->col_cnt);
return table_row;
}
/*
* Free table row
*/
static void table_row_free(struct table_row *table_row)
{
ht_free(table_row->entries);
ht_free(table_row);
}
/*
* Allocate and initialize a new table
*/
struct table *table_new(int extra_rows, int sorted, int first_bold,
int with_units)
{
struct table *t = ht_zalloc(sizeof(*t));
util_list_init(&t->row_list, struct table_row, list);
util_list_init(&t->mark_key_list, struct table_mark_key, list);
t->row_cnt_marked = 0;
if (with_units)
t->row_cnt_extra = extra_rows + L_ROWS_EXTRA + 1;
else
t->row_cnt_extra = extra_rows + L_ROWS_EXTRA;
t->attr_with_units = with_units;
t->attr_sorted_table = sorted;
t->attr_first_bold = first_bold;
return t;
}
/*
* Initialize headline for one column
*/
static void l_col_headline_init(struct table *t, struct table_col *col)
{
char *ptr;
strcpy(col->p->head_first, col->head);
ptr = strchr(col->p->head_first, tolower(col->hotkey));
assert(ptr != NULL);
*ptr = 0;
col->p->head_char[0] = col->hotkey;
strcpy(col->p->head_last, ++ptr);
if (!t->attr_sorted_table) {
util_str_toupper(col->p->head_first);
util_str_toupper(col->p->head_last);
col->p->head_char[0] = toupper(col->p->head_char[0]);
}
}
/*
* Initialize the max width values for a column
*/
static void l_col_max_width_init(struct table *t, struct table_col *col)
{
/* Units are displayed with brackets, therefore (+2) */
if (t->attr_with_units)
col->p->max_width = MAX(strlen(col->head),
strlen(col->unit->str) + 2);
else
col->p->max_width = strlen(col->head);
}
/*
* Add a new column to table
*/
void table_col_add(struct table *t, struct table_col *col)
{
col->p = ht_zalloc(sizeof(*col->p));
col->p->col_nr = t->col_cnt;
col->p->enabled = 1;
t->col_cnt++;
t->col_vec = ht_realloc(t->col_vec, sizeof(void *) *
(t->col_cnt + 1));
t->col_vec[t->col_cnt - 1] = col;
t->col_vec[t->col_cnt] = NULL;
if (!t->col_selected && t->attr_sorted_table)
t->col_selected = col;
if (t->row_last)
table_row_free(t->row_last);
t->row_last = table_row_alloc(t);
l_col_headline_init(t, col);
l_col_max_width_init(t, col);
}
/*
* Initialize last row
*/
static void l_row_last_init(struct table *t)
{
memset(t->row_last->entries, 0,
t->col_cnt * sizeof(struct table_entry));
}
/*
* Delete all rows of a table
*/
void table_row_del_all(struct table *t)
{
struct table_row *row, *tmp;
util_list_iterate_safe(&t->row_list, row, tmp) {
util_list_remove(&t->row_list, row);
table_row_free(row);
}
l_row_last_init(t);
t->row_cnt_marked = 0;
t->ready = 0;
t->row_cnt = 0;
}
/*
* Reset table
*/
void table_reset(struct table *t)
{
table_row_mark_del_all(t);
table_row_del_all(t);
t->mode_sort_inverse = 0;
t->mode_select = 0;
}
/*
* Return true, if "e1" is less than "e2"
*/
static int l_entry_less_than(enum table_col_type type, struct table_entry *e1,
struct table_entry *e2)
{
switch (type) {
case TABLE_COL_TYPE_U64:
return (e1->d.u64.v1 < e2->d.u64.v1);
case TABLE_COL_TYPE_S64:
return (e1->d.s64.v1 < e2->d.s64.v1);
case TABLE_COL_TYPE_STR:
return (strcmp(e1->str, e2->str) > 0);
}
return 0; /* Keep gcc quite */
}
/*
* Return true, if "row1" is less than "row2"
*/
static int l_row_less_than(struct table *t, struct table_row *row1,
struct table_row *row2)
{
struct table_col *col = t->col_selected;
struct table_entry *e1 = &row1->entries[col->p->col_nr];
struct table_entry *e2 = &row2->entries[col->p->col_nr];
if ((t->mode_sort_inverse && !col->p->rsort) ||
(!t->mode_sort_inverse && col->p->rsort))
return !l_entry_less_than(col->type, e1, e2);
else
return l_entry_less_than(col->type, e1, e2);
}
/*
* Calculate: e1 = e1 + e2
*/
static void l_entry_sum(enum table_col_type type, struct table_entry *e1,
struct table_entry *e2)
{
switch (type) {
case TABLE_COL_TYPE_U64:
e1->d.u64.v1 += e2->d.u64.v1;
return;
case TABLE_COL_TYPE_S64:
e1->d.s64.v1 += e2->d.s64.v1;
return;
default:
assert(0);
return;
}
}
/*
* Calculate: e1 = MAX(e1, e2)
*/
static void l_entry_max(enum table_col_type type, struct table_entry *e1,
struct table_entry *e2)
{
switch (type) {
case TABLE_COL_TYPE_U64:
e1->d.u64.v1 = MAX(e1->d.u64.v1, e2->d.u64.v1);
return;
case TABLE_COL_TYPE_S64:
e1->d.s64.v1 = MAX(e1->d.s64.v1, e2->d.s64.v1);
return;
default:
assert(0);
return;
}
}
/*
* Aggregate "row" to "last row"
*/
static void l_row_last_agg(struct table *t, struct table_row *table_row)
{
struct table_col *col;
int col_nr;
table_col_iterate(t, col, col_nr) {
struct table_entry *e_last = &t->row_last->entries[col_nr];
struct table_entry *e_new = &table_row->entries[col_nr];
if (!e_new->set)
continue;
switch (col->agg) {
case TABLE_COL_AGG_SUM:
l_entry_sum(col->type, e_last, e_new);
break;
case TABLE_COL_AGG_MAX:
l_entry_max(col->type, e_last, e_new);
break;
case TABLE_COL_AGG_NONE:
break;
}
e_last->set = 1;
}
}
/*
* Format row: Invoke unit callback and adjust max width of column
*/
static void l_row_format(struct table *t, struct table_row *row)
{
unsigned int len, col_nr;
struct table_col *col;
table_col_iterate(t, col, col_nr) {
struct table_entry *e = &row->entries[col_nr];
if (col->agg == TABLE_COL_AGG_NONE && row == t->row_last)
len = 0;
else
len = col->unit->fn(col, e);
assert(len < TABLE_STR_MAX);
if (len > col->p->max_width)
col->p->max_width = len;
}
}
/*
* Calculate last row
*/
static void l_row_last_calc(struct table *t)
{
struct table_row *row;
l_row_last_init(t);
util_list_iterate(&t->row_list, row) {
if (t->mode_hide_unmarked && !row->marked)
continue;
l_row_last_agg(t, row);
}
l_row_format(t, t->row_last);
}
/*
* Finish table after all rows have been added
*/
void table_finish(struct table *t)
{
l_row_last_calc(t);
t->ready = 1;
}
/*
* Add new row to table
*/
void table_row_add(struct table *t, struct table_row *row)
{
struct table_row *tmp;
l_row_format(t, row);
if (util_list_is_empty(&t->row_list) || !t->attr_sorted_table) {
util_list_add_tail(&t->row_list, row);
} else {
util_list_iterate(&t->row_list, tmp) {
if (l_row_less_than(t, tmp, row))
break;
}
if (tmp)
util_list_add_prev(&t->row_list, row, tmp);
else
util_list_add_tail(&t->row_list, row);
}
if (l_row_is_marked(t, row)) {
row->marked = 1;
t->row_cnt_marked++;
}
t->row_cnt++;
}
/*
* Rebuild table: Reformat all rows and adjust max width values
*/
void table_rebuild(struct table *t)
{
struct table_col *col;
struct table_row *row;
unsigned int i;
table_col_iterate(t, col, i)
l_col_max_width_init(t, col);
util_list_iterate(&t->row_list, row)
l_row_format(t, row);
l_row_format(t, t->row_last);
}
/*
* Compare callback for linked list sorting (ordering: large to small)
*/
static int l_row_cmp_fn(void *a, void *b, void *data)
{
return l_row_less_than(data, a, b) ? 1 : -1;
}
/*
* Sort table (ordering: large to small)
*/
static void l_table_sort(struct table *t)
{
util_list_sort(&t->row_list, l_row_cmp_fn, t);
}
/*
* Adjust table values for select mode (e.g. for window resize or scrolling)
*/
static void l_adjust_values_select_mode(struct table *t)
{
int row_cnt_displ = l_row_cnt_displ(t);
int row_cnt = l_row_cnt(t);
/* We went out of range with row selection */
if (t->row_nr_select >= row_cnt)
t->row_nr_select = row_cnt - 1;
/* Is selected row within visible area? */
if (t->row_nr_select < t->row_nr_begin) {
/* Selected row is above area: Scroll up */
t->row_nr_begin = t->row_nr_select;
} else if (t->row_nr_select - t->row_nr_begin >= row_cnt_displ) {
/* Selected row is below area: Scroll down */
t->row_nr_begin = MAX(t->row_nr_select - row_cnt_displ + 1, 0);
}
}
/*
* Adjust table values (e.g. for window resize or scrolling)
*/
static void l_adjust_values(struct table *t)
{
int row_cnt_displ = l_row_cnt_displ(t);
int row_cnt = l_row_cnt(t);
if (t->mode_select)
l_adjust_values_select_mode(t);
/* If we do not use the whole screen, scroll up */
if (row_cnt - t->row_nr_begin < row_cnt_displ)
t->row_nr_begin = MAX(row_cnt - row_cnt_displ, 0);
}
/*
* Number of rows to be scrolled for page scroll
*/
static int l_scroll_page_row_cnt(struct table *t)
{
/* We have two rows overlap for scrolling pages */
return l_row_cnt_displ(t) - 2;
}
/*
* Scroll table down
*/
void table_scroll_down(struct table *t, enum table_scroll_unit scroll_unit)
{
switch (scroll_unit) {
case TABLE_SCROLL_LINE:
t->row_nr_begin++;
break;
case TABLE_SCROLL_PAGE:
t->row_nr_begin += l_scroll_page_row_cnt(t);
break;
case TABLE_SCROLL_LAST:
t->row_nr_begin = t->row_cnt;
break;
}
}
/*
* Scroll table up
*/
void table_scroll_up(struct table *t, enum table_scroll_unit scroll_unit)
{
switch (scroll_unit) {
case TABLE_SCROLL_LINE:
t->row_nr_begin = MAX(t->row_nr_begin - 1, 0);
break;
case TABLE_SCROLL_PAGE:
t->row_nr_begin =
MAX(t->row_nr_begin - l_scroll_page_row_cnt(t), 0);
break;
case TABLE_SCROLL_LAST:
t->row_nr_begin = 0;
break;
}
}
/*
* Return selected row
*/
static struct table_row *l_selected_row(struct table *t)
{
struct table_row *row;
int row_nr = 0;
util_list_iterate(&t->row_list, row) {
if (t->mode_hide_unmarked && !row->marked)
continue;
if (row_nr == t->row_nr_select)
return row;
row_nr++;
}
return NULL;
}
/*
* Toggle mark for selected row
*/
static void l_row_select_mark_toggle(struct table *t)
{
struct table_row *row;
row = l_selected_row(t);
table_row_mark_toggle(t, row);
l_row_last_calc(t);
}
/*
* Switch select mode off
*/
static void l_select_mode_off(struct table *t)
{
t->mode_select = 0;
}
/*
* Switch select mode on
*/
static void l_select_mode_on(struct table *t)
{
t->mode_select = 1;
t->row_nr_select = t->row_nr_begin;
}
/*
* Get key for selected row
*/
void table_row_select_key_get(struct table *t, char str[TABLE_STR_MAX])
{
struct table_row *row;
row = l_selected_row(t);
util_strlcpy(str, row->entries[0].str, TABLE_STR_MAX);
}
/*
* Select row one page down
*/
void table_row_select_down(struct table *t, enum table_scroll_unit scroll_unit)
{
switch (scroll_unit) {
case TABLE_SCROLL_LINE:
t->row_nr_select++;
break;
case TABLE_SCROLL_PAGE:
t->row_nr_select += g.c.row_cnt - t->row_cnt_extra;
break;
case TABLE_SCROLL_LAST:
t->row_nr_select = t->row_cnt;
break;
}
}
/*
* Select row one page up
*/
void table_row_select_up(struct table *t, enum table_scroll_unit scroll_unit)
{
switch (scroll_unit) {
case TABLE_SCROLL_LINE:
t->row_nr_select = MAX(t->row_nr_select - 1, 0);
break;
case TABLE_SCROLL_PAGE:
t->row_nr_select = MAX(t->row_nr_begin -
(g.c.row_cnt - t->row_cnt_extra), 0);
break;
case TABLE_SCROLL_LAST:
t->row_nr_select = 0;
break;
}
}
/*
* Toggle "hide unmarked" mode
*/
static int l_mode_hide_unmarked_toggle(struct table *t)
{
if (t->row_cnt_marked == 0)
return -ENODEV;
t->mode_hide_unmarked = t->mode_hide_unmarked ? 0 : 1;
t->row_nr_select = 0;
l_row_last_calc(t);
return 0;
}
/*
* Is it possible to scroll down the table?
*/
static int l_can_scroll_down(struct table *t)
{
int row_cnt = t->mode_hide_unmarked ? t->row_cnt_marked : t->row_cnt;
int row_cnt_real = g.c.row_cnt - t->row_cnt_extra;
return (row_cnt - t->row_nr_begin > row_cnt_real);
}
/*
* Is it possible to scroll up the table?
*/
static int l_can_scroll_up(struct table *t)
{
return (t->row_nr_begin > 0);
}
/*
* Update the status field
*/
static void l_status_update(struct table *t)
{
struct table_entry *e_status = &t->row_last->entries[0];
if (g.o.batch_mode_specified)
return;
if (l_can_scroll_down(t) && l_can_scroll_up(t))
strcpy(e_status->str, "|");
else if (l_can_scroll_up(t))
strcpy(e_status->str, "^");
else if (l_can_scroll_down(t))
strcpy(e_status->str, "V");
else
strcpy(e_status->str, "=");
if (t->attr_sorted_table) {
strcat(e_status->str, ":");
if (t->mode_sort_inverse)
strcat(e_status->str, "^");
else
strcat(e_status->str, "V");
}
strcat(e_status->str, ":");
if (t->mode_select)
strcat(e_status->str, "S");
else
strcat(e_status->str, "N");
}
/*
* Print string with alignment
*/
static void l_str_print(struct table_col *col, const char *str)
{
char unit[10];
if (col->align == TABLE_COL_ALIGN_LEFT)
sprintf(unit, "%%-%ds", col->p->max_width);
else
sprintf(unit, "%%%ds", col->p->max_width);
hyptop_printf(unit, str);
}
/*
* Print string for "col"
*/
static void l_col_print(struct table *t, struct table_col *col, const char *str)
{
if (l_col_selected(t, col))
ht_underline_on();
if (col->p->col_nr == 0 && t->attr_first_bold)
ht_bold_on();
l_str_print(col, str);
if (l_col_selected(t, col))
ht_underline_off();
if (col->p->col_nr == 0 && t->attr_first_bold)
ht_bold_off();
}
/*
* Print status field
*/
static void l_status_print(struct table *t, const char *str)
{
ht_bold_on();
l_str_print(t->col_vec[0], str);
ht_bold_off();
}
/*
* Print headline of column
*/
static void l_col_headline_print(struct table *t, struct table_col *col)
{
unsigned int len = strlen(col->head);
char blank_str[TABLE_STR_MAX];
(void) t;
memset(blank_str, ' ', col->p->max_width - len);
blank_str[col->p->max_width - len] = 0;
if (l_col_selected(t, col))
ht_bold_on();
if (col->align == TABLE_COL_ALIGN_RIGHT)
hyptop_printf("%s", blank_str);
hyptop_printf("%s", col->p->head_first);
if (t->attr_sorted_table)
ht_underline_on();
hyptop_printf("%s", col->p->head_char);
if (t->attr_sorted_table)
ht_underline_off();
hyptop_printf("%s", col->p->head_last);
if (col->align == TABLE_COL_ALIGN_LEFT)
hyptop_printf("%s", blank_str);
if (l_col_selected(t, col))
ht_bold_off();
}
/*
* Print headline for table
*/
static void l_headline_print(struct table *t)
{
struct table_col *col;
int col_nr, first = 1;
ht_reverse_on();
/* Print all column headlines */
table_col_iterate(t, col, col_nr) {
if (!col->p->enabled)
continue;
if (first)
first = 0;
else
hyptop_printf(" ");
l_col_headline_print(t, col);
}
/* This creates a black bar to the end of the line */
hyptop_print_seek_back(0);
ht_reverse_off();
hyptop_print_nl();
}
/*
* Print unit line for table
*/
static void l_unitline_print(struct table *t)
{
struct table_col *col;
int col_nr, first = 1;
char unit_str[20];
if (!t->attr_with_units)
return;
ht_reverse_on();
/* Print all column units */
table_col_iterate(t, col, col_nr) {
if (!col->p->enabled)
continue;
if (first)
first = 0;
else
hyptop_printf(" ");
if (l_col_selected(t, col))
ht_bold_on();
snprintf(unit_str, sizeof(unit_str), "(%s)", col->unit->str);
l_str_print(col, unit_str);
if (l_col_selected(t, col))
ht_bold_off();
}
/* This creates a black bar to the end of the line */
hyptop_print_seek_back(0);
ht_reverse_off();
hyptop_print_nl();
}
/*
* Print one table row
*/
static void l_row_print(struct table *t, struct table_row *row)
{
struct table_col *col;
int first = 1, col_nr;
table_col_iterate(t, col, col_nr) {
struct table_entry *e = &row->entries[col_nr];
if (!col->p->enabled)
continue;
if (!first)
hyptop_printf(" ");
else
first = 0;
if (row == t->row_last && col_nr == 0)
l_status_print(t, e->str);
else
l_col_print(t, col, e->str);
}
}
/*
* Print table under curses
*/
static void l_table_print_curses(struct table *t)
{
struct table_row *row;
int row_nr = 0;
if (!t->ready)
return;
l_adjust_values(t);
l_status_update(t);
l_headline_print(t);
l_unitline_print(t);
util_list_iterate(&t->row_list, row) {
if (t->mode_hide_unmarked && !row->marked)
continue;
if (row_nr < t->row_nr_begin) {
row_nr++;
continue;
}
if (row_nr - t->row_nr_begin >= g.c.row_cnt - t->row_cnt_extra)
break;
if (t->mode_select && row_nr == t->row_nr_select)
ht_reverse_on();
if (row->marked)
ht_bold_on();
l_row_print(t, row);
if (t->mode_select && row_nr == t->row_nr_select) {
#ifdef WITH_SCROLL_BAR
hyptop_print_seek_back(1);
#else
hyptop_print_seek_back(0);
#endif
ht_reverse_off();
}
if (row->marked)
ht_bold_off();
hyptop_print_nl();
row_nr++;
}
ht_reverse_on();
l_row_print(t, t->row_last);
hyptop_print_seek_back(0);
ht_reverse_off();
#ifdef WITH_SCROLL_BAR
if (t->mode_hide_unmarked)
ht_print_scroll_bar(t->row_cnt_marked, t->row_nr_begin,
t->row_cnt_extra - 1, 1,
l_can_scroll_up(t),
l_can_scroll_down(t), 1);
else
ht_print_scroll_bar(t->row_cnt, t->row_nr_begin,
t->row_cnt_extra - 1, 1,
l_can_scroll_up(t),
l_can_scroll_down(t), 1);
#endif
}
/*
* Print table under batch mode
*/
static void l_table_print_all(struct table *t)
{
struct table_row *row;
l_headline_print(t);
l_unitline_print(t);
util_list_iterate(&t->row_list, row) {
l_row_print(t, row);
hyptop_print_nl();
}
l_row_print(t, t->row_last);
hyptop_print_nl();
hyptop_printf("------------------------------------------------------"
"-------------------------\n");
}
/*
* Print one table row as structured output
*
* Note: column filtering and sorting is explicitly ignored because the
* assumption is that these operations can be trivially performed by the
* consumer.
*/
static void l_row_print_formatted(struct table *t, struct table_row *row)
{
struct table_col *col;
int col_nr;
table_col_iterate(t, col, col_nr) {
unsigned int flags = 0;
struct table_entry *e = &row->entries[col_nr];
if (row == t->row_last && col_nr == 0)
continue;
if (table_col_needs_quotes(col))
flags = FMT_QUOTE;
util_fmt_pair(flags, col->head, "%s", e->str);
}
}
/*
* Print table as structured output
*/
static void l_table_print_all_formatted(struct table *t)
{
struct table_row *row;
util_fmt_obj_start(FMT_ROW, "iteration");
util_fmt_pair(FMT_PERSIST, "iteration", "%u", g.o.iterations_act);
ht_fmt_time();
ht_fmt_cpu_types();
if (strcmp(g.o.cur_win->id, "sys_list") == 0)
util_fmt_obj_start(FMT_LIST, "systems");
else
util_fmt_obj_start(FMT_LIST, "cpus");
util_list_iterate(&t->row_list, row) {
util_fmt_obj_start(FMT_ROW, "entry");
l_row_print_formatted(t, row);
util_fmt_obj_end(); /* entry */
}
util_fmt_obj_end(); /* systems[] */
util_fmt_obj_start(FMT_DEFAULT, "summary");
l_row_print_formatted(t, t->row_last);
util_fmt_obj_end(); /* summary{} */
util_fmt_obj_end(); /* iteration */
}
void table_fmt_start(void)
{
if (!g.o.format_specified)
return;
if (g.o.format != FMT_JSONSEQ)
util_fmt_obj_start(FMT_LIST, "hyptop");
}
void table_fmt_end(void)
{
if (!g.o.format_specified)
return;
if (g.o.format != FMT_JSONSEQ)
util_fmt_obj_end(); /* hyptop[] */
}
/*
* Print table to screen
*/
void table_print(struct table *t)
{
if (g.o.batch_mode_specified) {
if (!g.o.format_specified)
l_table_print_all(t);
else
l_table_print_all_formatted(t);
} else {
l_table_print_curses(t);
}
}
/*
* Return column by hotkey
*/
static struct table_col *l_col_by_hotkey(struct table *t, char hotkey)
{
struct table_col *col;
int col_nr;
table_col_iterate(t, col, col_nr) {
if (col->hotkey == hotkey)
return col;
}
return NULL;
}
/*
* Select next unit for column with "hotkey"
*/
void table_col_unit_next(struct table *t, char hotkey)
{
struct table_col *col;
int i;
col = l_col_by_hotkey(t, hotkey);
if (!col || !col->unit_fam)
assert(0);
for (i = 0; col->unit_fam[i] != NULL; i++) {
if (col->unit != col->unit_fam[i])
continue;
if (col->unit_fam[i + 1] == NULL)
col->unit = col->unit_fam[0];
else
col->unit = col->unit_fam[i + 1];
return;
}
assert(0);
}
/*
* Select previous unit for column with "hotkey"
*/
void table_col_unit_prev(struct table *t, char hotkey)
{
struct table_col *col;
int i;
col = l_col_by_hotkey(t, hotkey);
if (!col || !col->unit_fam)
assert(0);
for (i = 0; col->unit_fam[i] != NULL; i++) {
if (col->unit != col->unit_fam[i])
continue;
if (i == 0) {
int j;
for (j = 0; col->unit_fam[j] != NULL; j++) {}
col->unit = col->unit_fam[j - 1];
} else {
col->unit = col->unit_fam[i - 1];
}
return;
}
assert(0);
}
/*
* Set unit for column
*/
int table_col_unit_set(struct table *t, char hotkey, const char *str)
{
struct table_col *col;
int i;
col = l_col_by_hotkey(t, hotkey);
if (!col)
return -ENODEV;
for (i = 0; col->unit_fam[i] != NULL; i++) {
if (strcasecmp(col->unit_fam[i]->str, str) == 0) {
col->unit = col->unit_fam[i];
return 0;
}
}
return -EINVAL;
}
/*
* Select column by hotkey
*/
int table_col_select(struct table *t, char hotkey)
{
struct table_col *col;
if (!t->attr_sorted_table)
assert(0);
col = l_col_by_hotkey(t, hotkey);
if (!col || !col->p->enabled)
return -ENODEV;
if (t->col_selected == col) {
t->mode_sort_inverse = t->mode_sort_inverse ? 0 : 1;
} else {
t->mode_sort_inverse = 0;
t->col_selected = col;
}
table_rebuild(t);
l_table_sort(t);
return 0;
}
/*
* Select next column
*/
void table_col_select_next(struct table *t)
{
int i;
for (i = t->col_selected->p->col_nr + 1; i < t->col_cnt; i++) {
if (t->col_vec[i]->p->enabled)
goto found;
}
return;
found:
t->col_selected = t->col_vec[i];
l_table_sort(t);
}
/*
* Select previous column
*/
void table_col_select_prev(struct table *t)
{
int i;
for (i = t->col_selected->p->col_nr - 1; i >= 0; i--) {
if (t->col_vec[i]->p->enabled)
goto found;
}
return;
found:
t->col_selected = t->col_vec[i];
l_table_sort(t);
}
/*
* Toggle enabled status for column
*/
void table_col_enable_toggle(struct table *t, char hotkey)
{
struct table_col *col;
col = l_col_by_hotkey(t, hotkey);
if (!col || col->p->col_nr == 0)
return;
col->p->enabled = col->p->enabled ? 0 : 1;
if (col == t->col_selected)
t->col_selected = t->col_vec[0];
}
/*
* Process input for table
*/
void table_process_input(struct table *t, int c)
{
switch (c) {
case '<':
if (t->attr_sorted_table)
table_col_select_prev(t);
break;
case '>':
if (t->attr_sorted_table)
table_col_select_next(t);
break;
case '.':
if (l_mode_hide_unmarked_toggle(t) == 0)
l_select_mode_off(t);
break;
case '+':
if (!t->attr_with_units)
break;
table_col_unit_next(t, t->col_selected->hotkey);
table_rebuild(t);
break;
case '-':
if (!t->attr_with_units)
break;
table_col_unit_prev(t, t->col_selected->hotkey);
table_rebuild(t);
break;
case 'G':
if (t->mode_select)
table_row_select_down(t, TABLE_SCROLL_LAST);
else
table_scroll_down(t, TABLE_SCROLL_LAST);
break;
case 'g':
if (t->mode_select)
table_row_select_up(t, TABLE_SCROLL_LAST);
else
table_scroll_up(t, TABLE_SCROLL_LAST);
break;
case KEY_NPAGE:
if (t->mode_select)
table_row_select_down(t, TABLE_SCROLL_PAGE);
else
table_scroll_down(t, TABLE_SCROLL_PAGE);
break;
case KEY_PPAGE:
if (t->mode_select)
table_row_select_up(t, TABLE_SCROLL_PAGE);
else
table_scroll_up(t, TABLE_SCROLL_PAGE);
break;
case 'j':
case KEY_DOWN:
if (t->mode_select)
table_row_select_down(t, TABLE_SCROLL_LINE);
else
table_scroll_down(t, TABLE_SCROLL_LINE);
break;
case 'k':
case KEY_UP:
if (t->mode_select)
table_row_select_up(t, TABLE_SCROLL_LINE);
else
table_scroll_up(t, TABLE_SCROLL_LINE);
break;
case ' ':
if (t->mode_select) {
l_row_select_mark_toggle(t);
} else {
table_row_mark_del_all(t);
t->mode_hide_unmarked = 0;
}
break;
case 'l':
case KEY_RIGHT:
if (!t->mode_select)
l_select_mode_on(t);
break;
case 'h':
case KEY_LEFT:
if (t->mode_select)
l_select_mode_off(t);
break;
default:
if (t->attr_sorted_table)
table_col_select(t, c);
}
}