mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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>
1294 lines
26 KiB
C
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);
|
|
}
|
|
}
|