#include <sys/ioctl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <term.h>
#include <math.h>
#include <curses.h>
#include <unistd.h>
#include <cxxabi.h>
#include <iomanip>
#include <ios>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "display.hh"
enum termmode {
TERMMODE_BASIC,
TERMMODE_VT,
TERMMODE_XTERM
};
static termmode tmode;
static int disp_height;
static int disp_width;
#define TITLE_TITLE 0
#define TITLE_SUBTITLE 1
static const char **title_char;
static const char *title_vt[] = {
"\xe2\x95\x90", "\xe2\x94\x80"
};
static const char *title_basic[] = { "=", "-" };
#define ARROW_DOWN 0
#define ARROW_UP 1
static const char **arrow_char;
static const char *arrow_vt[] = {
"\xe2\x96\xbc", "\xe2\x96\xb2"
};
static const char *arrow_basic[] = { "D", "U" };
#define BORDER_HBAR 0
#define BORDER_VBAR 1
#define BORDER_TOPRIGHT 2
#define BORDER_TOPLEFT 3
#define BORDER_BOTTOMLEFT 4
#define BORDER_BOTTOMRIGHT 5
#define BORDER_TOPSPLIT 6
#define BORDER_BOTTOMSPLIT 7
#define BORDER_RIGHTSPLIT 8
#define BORDER_LEFTSPLIT 9
#define BORDER_CROSS 10
static const char **border_char;
static const char *border_vt[] = {
"\xe2\x94\x80", "\xe2\x94\x82",
"\xe2\x94\x8c", "\xe2\x94\x90", "\xe2\x94\x94", "\xe2\x94\x98",
"\xe2\x94\xac", "\xe2\x94\xb4", "\xe2\x94\xa4", "\xe2\x94\x9c",
"\xe2\x94\xbc"
};
static const char *border_basic[] = { "-", "|", "+", "+", "+", "+", "+", "+", "+", "+" };
static char tc_boldbuf[16];
static char tc_sgr0buf[16];
static char tc_afbuf[16];
static char tc_green[16];
static char tc_yellow[16];
static char tc_red[16];
static const char *TC_BOLD;
static const char *TC_SGR0;
static const char *TC_SETAF;
void
tcemit(const char *c)
{
if (c)
tputs(c, 1, putchar);
}
void
tcfg(int n)
{
if (TC_SETAF)
tputs(tgoto(TC_SETAF, 0, n), 1, putchar);
}
static void
setup_termcap(const char *term)
{
bool xcolor;
char *buf;
char *clicolor;
char termcapbuf[4096];
if (tgetent(termcapbuf, "xterm-256color") != 1) {
TC_BOLD = NULL;
TC_SGR0 = NULL;
TC_SETAF = NULL;
return;
}
buf = tc_boldbuf;
TC_BOLD = tgetstr("md", &buf);
buf = tc_sgr0buf;
TC_SGR0 = tgetstr("me", &buf);
buf = tc_afbuf;
TC_SETAF = tgetstr("AF", &buf);
clicolor = getenv("CLICOLOR");
if (clicolor == NULL || strcmp(clicolor, "1") != 0) {
return;
}
if (strcmp(term, "xterm-256color") == 0) {
xcolor = true;
} else {
xcolor = false;
}
buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_GREEN : COLOR_GREEN);
if (buf) {
strlcpy(tc_green, buf, sizeof(tc_green));
} else {
tc_green[0] = '\0';
}
buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_YELLOW : COLOR_YELLOW);
if (buf) {
strlcpy(tc_yellow, buf, sizeof(tc_yellow));
} else {
tc_yellow[0] = '\0';
}
buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_RED : COLOR_RED);
if (buf) {
strlcpy(tc_red, buf, sizeof(tc_red));
} else {
tc_red[0] = '\0';
}
}
void
setup_screen()
{
int status;
const char *term;
struct winsize wsz;
tmode = TERMMODE_BASIC;
title_char = title_basic;
arrow_char = arrow_basic;
border_char = border_basic;
disp_height = 25;
disp_width = 80;
if (isatty(STDOUT_FILENO) == 0) {
tmode = TERMMODE_XTERM;
title_char = title_vt;
arrow_char = arrow_vt;
border_char = border_vt;
return;
}
status = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz);
if (status < 0) {
perror("ioctl(TIOCGWINSZ)");
return;
}
disp_height = wsz.ws_row;
disp_width = wsz.ws_col;
term = getenv("TERM");
if (term == NULL) {
tmode = TERMMODE_BASIC;
title_char = title_basic;
arrow_char = arrow_basic;
border_char = border_basic;
return;
}
if (strncmp(term, "xterm", 5) == 0) {
tmode = TERMMODE_XTERM;
title_char = title_vt;
arrow_char = arrow_vt;
border_char = border_vt;
} else if (strncmp(term, "vt", 2) == 0) {
tmode = TERMMODE_VT;
title_char = title_vt;
arrow_char = arrow_vt;
border_char = border_vt;
}
setup_termcap(term);
}
void
title(const std::string &t)
{
size_t i;
if (tmode != TERMMODE_BASIC)
tcemit(TC_BOLD);
printf("%s\n", t.c_str());
if (tmode != TERMMODE_BASIC)
tcemit(TC_SGR0);
for (i = 0; i < t.size(); i++)
printf("%s", title_char[TITLE_TITLE]);
printf("\n\n");
}
void
header(const std::string &h)
{
size_t i;
if (tmode != TERMMODE_BASIC)
tcemit(TC_BOLD);
printf("%s\n", h.c_str());
if (tmode != TERMMODE_BASIC)
tcemit(TC_SGR0);
for (i = 0; i < h.size(); i++)
printf("%s", title_char[TITLE_SUBTITLE]);
printf("\n");
}
void
printval(const std::string &msg, uint64_t val, siunit ui)
{
switch (ui) {
case siunit::seconds:
printf("%s: %" PRIu64 " s\n", msg.c_str(), val);
break;
case siunit::percent:
printf("%s: %" PRIu64 "%%\n", msg.c_str(), val);
break;
case siunit::cycles:
printf("%s: %" PRIu64 " cycles\n", msg.c_str(), val);
break;
}
}
void
printval(const std::string &msg, float val, siunit ui)
{
switch (ui) {
case siunit::seconds:
printf("%s: %.2f s\n", msg.c_str(), val);
break;
case siunit::percent:
printf("%s: %6.2f%%\n", msg.c_str(), val);
break;
case siunit::cycles:
printf("%s: %.2f cycles\n", msg.c_str(), val);
break;
}
}
std::string
format_siprefix(uint64_t count)
{
unsigned long index = 0;
char prefix[] = " kMGTP";
char buf[10];
while (count > 10000 && index < sizeof(prefix)) {
index++;
count /= 1000;
}
if (index == 0) {
snprintf(buf, sizeof(buf), "%" PRIu64, count);
} else {
snprintf(buf, sizeof(buf), "%" PRIu64 "%c", count, prefix[index]);
}
return buf;
}
std::string
format_binprefix(uint64_t count)
{
unsigned long index = 0;
char prefix[] = " KMGTP";
char buf[10];
while (count > 10000 && index < sizeof(prefix)) {
index++;
count /= 1000;
}
if (index == 0) {
snprintf(buf, sizeof(buf), "%" PRIu64 " B", count);
} else {
snprintf(buf, sizeof(buf), "%" PRIu64 " %ciB", count, prefix[index]);
}
return buf;
}
std::string
format_sample(uint64_t count, uint64_t total)
{
char buf[20];
std::string fmt = format_siprefix(count);
snprintf(buf, sizeof(buf), "%5s (%4.1f%%)", fmt.c_str(),
100.0 * (float)count / (float)total);
return buf;
}
std::string
format_percent(uint64_t count, uint64_t total)
{
char buf[20];
if (total == 0) {
snprintf(buf, sizeof(buf), "%6s", "");
} else {
snprintf(buf, sizeof(buf), "%5.1f%%",
100.0 * (float)count / (float)total);
}
return buf;
}
field::field(int64_t v)
{
type = FIELD::INTEGER;
value[0] = v;
}
field::field(float v)
{
type = FIELD::FLOAT;
fvalue = v;
}
field::field(const std::string &s)
{
type = FIELD::STRING;
svalue = s;
}
field::field(int64_t c, int64_t t, bool percent)
{
if (percent)
type = FIELD::PERCENT;
else
type = FIELD::SAMPLE;
value[0] = c;
value[1] = t;
}
field::field(int64_t uc, int64_t ut, int64_t ku, int64_t kt)
{
type = FIELD::DSAMPLE;
value[0] = uc;
value[1] = ut;
value[2] = ku;
value[3] = kt;
}
bool
field::operator>(const field &b) const
{
switch (type) {
case FIELD::STRING:
return (svalue > b.svalue);
case FIELD::FLOAT:
return (fvalue > b.fvalue);
case FIELD::INTEGER: [[fallthrough]];
case FIELD::SAMPLE:
return (value[0] > b.value[0]);
case FIELD::DSAMPLE:
return ((value[0] + value[2]) > (b.value[0] + b.value[2]));
case FIELD::PERCENT:
return (((float)value[0] / (float)value[1]) >
((float)b.value[0] / (float)b.value[1]));
}
}
bool
field::operator<(const field &b) const
{
switch (type) {
case FIELD::STRING:
return (svalue < b.svalue);
case FIELD::FLOAT:
return (fvalue < b.fvalue);
case FIELD::INTEGER: [[fallthrough]];
case FIELD::SAMPLE:
return (value[0] < b.value[0]);
case FIELD::DSAMPLE:
return ((value[0] + value[2]) < (b.value[0] + b.value[2]));
case FIELD::PERCENT:
return (((float)value[0] / (float)value[1]) <
((float)b.value[0] / (float)b.value[1]));
}
}
size_t
field::length()
{
return (to_string().length());
}
std::string
field::to_string()
{
switch (type) {
case FIELD::STRING:
return (svalue);
case FIELD::FLOAT:
return (std::to_string(fvalue));
case FIELD::INTEGER:
return (std::to_string(value[0]));
case FIELD::SAMPLE:
return (format_sample(value[0], value[1]));
case FIELD::DSAMPLE:
return (format_sample(value[0], value[1]) + " / " +
format_sample(value[2], value[3]));
case FIELD::PERCENT:
return (format_percent(value[0], value[1]));
}
}
table::table() : sortcol(-1), sortdir(true), cols(), align(), rows()
{
}
table::~table()
{
}
void
table::addcolumn(const std::string &c, bool alignleft)
{
cols.push_back(c);
align.push_back(alignleft);
}
void
table::addrow(std::vector<field> r)
{
rows.push_back(std::move(r));
}
void
table::sort(int col, bool descending)
{
sortcol = col;
sortdir = descending;
if (descending)
std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b)
{ return (a[col] > b[col]); });
else
std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b)
{ return (a[col] < b[col]); });
}
void
table::print()
{
unsigned long c;
std::vector<unsigned long> width;
for (auto h : cols)
width.push_back(h.length() + 1);
if (sortcol != -1)
width[sortcol] += 1;
for (auto &r : rows) {
for (c = 0; c < r.size(); c++) {
if (width[c] < r[c].length())
width[c] = r[c].length();
}
}
for (c = 0; c < width.size(); c++) {
if (align[c])
std::cout << std::left;
std::cout << std::setw(width[c]);
tcemit(TC_BOLD);
if (c == (unsigned long)sortcol)
std::cout << cols[c] + " " + arrow_char[sortdir ? ARROW_DOWN : ARROW_UP];
else
std::cout << cols[c];
tcemit(TC_SGR0);
if (c != (width.size() - 1))
std::cout << " " << border_char[BORDER_VBAR] << " ";
if (align[c])
std::cout << std::right;
}
std::cout << std::endl;
for (c = 0; c < width.size(); c++) {
for (unsigned long i = 0; i < width[c]; i++)
std::cout << border_char[BORDER_HBAR];
if (c != (width.size() - 1))
std::cout << border_char[BORDER_HBAR] <<
border_char[BORDER_CROSS] << border_char[BORDER_HBAR];
}
std::cout << std::setfill(' ') << std::endl;
for (auto &r : rows) {
for (c = 0; c < width.size(); c++) {
if (align[c])
std::cout << std::left;
std::cout << std::setw(width[c]) << r[c].to_string();
if (c != (width.size() - 1))
std::cout << " " << border_char[BORDER_VBAR] << " ";
if (align[c])
std::cout << std::right;
}
std::cout << std::endl;
}
std::cout << std::endl;
}