1*a53d2e4fSAli Mashtizadeh /*- 2*a53d2e4fSAli Mashtizadeh * SPDX-License-Identifier: BSD-2-Clause 3*a53d2e4fSAli Mashtizadeh * 4*a53d2e4fSAli Mashtizadeh * Copyright (c) 2026, Netflix, Inc. 5*a53d2e4fSAli Mashtizadeh * 6*a53d2e4fSAli Mashtizadeh * This software was developed by Ali Mashtizadeh under the sponsorship from 7*a53d2e4fSAli Mashtizadeh * Netflix, Inc. 8*a53d2e4fSAli Mashtizadeh * 9*a53d2e4fSAli Mashtizadeh * Redistribution and use in source and binary forms, with or without 10*a53d2e4fSAli Mashtizadeh * modification, are permitted provided that the following conditions 11*a53d2e4fSAli Mashtizadeh * are met: 12*a53d2e4fSAli Mashtizadeh * 1. Redistributions of source code must retain the above copyright 13*a53d2e4fSAli Mashtizadeh * notice, this list of conditions and the following disclaimer. 14*a53d2e4fSAli Mashtizadeh * 2. Redistributions in binary form must reproduce the above copyright 15*a53d2e4fSAli Mashtizadeh * notice, this list of conditions and the following disclaimer in the 16*a53d2e4fSAli Mashtizadeh * documentation and/or other materials provided with the distribution. 17*a53d2e4fSAli Mashtizadeh * 18*a53d2e4fSAli Mashtizadeh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19*a53d2e4fSAli Mashtizadeh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20*a53d2e4fSAli Mashtizadeh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21*a53d2e4fSAli Mashtizadeh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22*a53d2e4fSAli Mashtizadeh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23*a53d2e4fSAli Mashtizadeh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24*a53d2e4fSAli Mashtizadeh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25*a53d2e4fSAli Mashtizadeh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26*a53d2e4fSAli Mashtizadeh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27*a53d2e4fSAli Mashtizadeh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28*a53d2e4fSAli Mashtizadeh * SUCH DAMAGE. 29*a53d2e4fSAli Mashtizadeh * 30*a53d2e4fSAli Mashtizadeh */ 31*a53d2e4fSAli Mashtizadeh 32*a53d2e4fSAli Mashtizadeh #include <sys/ioctl.h> 33*a53d2e4fSAli Mashtizadeh 34*a53d2e4fSAli Mashtizadeh #include <inttypes.h> 35*a53d2e4fSAli Mashtizadeh #include <stdbool.h> 36*a53d2e4fSAli Mashtizadeh #include <stdint.h> 37*a53d2e4fSAli Mashtizadeh #include <stdio.h> 38*a53d2e4fSAli Mashtizadeh #include <term.h> 39*a53d2e4fSAli Mashtizadeh #include <math.h> 40*a53d2e4fSAli Mashtizadeh 41*a53d2e4fSAli Mashtizadeh #include <curses.h> 42*a53d2e4fSAli Mashtizadeh #include <unistd.h> 43*a53d2e4fSAli Mashtizadeh 44*a53d2e4fSAli Mashtizadeh #include <cxxabi.h> 45*a53d2e4fSAli Mashtizadeh #include <iomanip> 46*a53d2e4fSAli Mashtizadeh #include <ios> 47*a53d2e4fSAli Mashtizadeh #include <iostream> 48*a53d2e4fSAli Mashtizadeh #include <sstream> 49*a53d2e4fSAli Mashtizadeh #include <string> 50*a53d2e4fSAli Mashtizadeh #include <vector> 51*a53d2e4fSAli Mashtizadeh 52*a53d2e4fSAli Mashtizadeh #include "display.hh" 53*a53d2e4fSAli Mashtizadeh 54*a53d2e4fSAli Mashtizadeh enum termmode { 55*a53d2e4fSAli Mashtizadeh TERMMODE_BASIC, 56*a53d2e4fSAli Mashtizadeh TERMMODE_VT, 57*a53d2e4fSAli Mashtizadeh TERMMODE_XTERM 58*a53d2e4fSAli Mashtizadeh }; 59*a53d2e4fSAli Mashtizadeh 60*a53d2e4fSAli Mashtizadeh static termmode tmode; 61*a53d2e4fSAli Mashtizadeh static int disp_height; 62*a53d2e4fSAli Mashtizadeh static int disp_width; 63*a53d2e4fSAli Mashtizadeh 64*a53d2e4fSAli Mashtizadeh #define TITLE_TITLE 0 65*a53d2e4fSAli Mashtizadeh #define TITLE_SUBTITLE 1 66*a53d2e4fSAli Mashtizadeh 67*a53d2e4fSAli Mashtizadeh static const char **title_char; 68*a53d2e4fSAli Mashtizadeh 69*a53d2e4fSAli Mashtizadeh static const char *title_vt[] = { 70*a53d2e4fSAli Mashtizadeh "\xe2\x95\x90", "\xe2\x94\x80" 71*a53d2e4fSAli Mashtizadeh }; 72*a53d2e4fSAli Mashtizadeh static const char *title_basic[] = { "=", "-" }; 73*a53d2e4fSAli Mashtizadeh 74*a53d2e4fSAli Mashtizadeh #define ARROW_DOWN 0 75*a53d2e4fSAli Mashtizadeh #define ARROW_UP 1 76*a53d2e4fSAli Mashtizadeh 77*a53d2e4fSAli Mashtizadeh static const char **arrow_char; 78*a53d2e4fSAli Mashtizadeh 79*a53d2e4fSAli Mashtizadeh static const char *arrow_vt[] = { 80*a53d2e4fSAli Mashtizadeh "\xe2\x96\xbc", "\xe2\x96\xb2" 81*a53d2e4fSAli Mashtizadeh }; 82*a53d2e4fSAli Mashtizadeh static const char *arrow_basic[] = { "D", "U" }; 83*a53d2e4fSAli Mashtizadeh 84*a53d2e4fSAli Mashtizadeh #define BORDER_HBAR 0 85*a53d2e4fSAli Mashtizadeh #define BORDER_VBAR 1 86*a53d2e4fSAli Mashtizadeh #define BORDER_TOPRIGHT 2 87*a53d2e4fSAli Mashtizadeh #define BORDER_TOPLEFT 3 88*a53d2e4fSAli Mashtizadeh #define BORDER_BOTTOMLEFT 4 89*a53d2e4fSAli Mashtizadeh #define BORDER_BOTTOMRIGHT 5 90*a53d2e4fSAli Mashtizadeh #define BORDER_TOPSPLIT 6 91*a53d2e4fSAli Mashtizadeh #define BORDER_BOTTOMSPLIT 7 92*a53d2e4fSAli Mashtizadeh #define BORDER_RIGHTSPLIT 8 93*a53d2e4fSAli Mashtizadeh #define BORDER_LEFTSPLIT 9 94*a53d2e4fSAli Mashtizadeh #define BORDER_CROSS 10 95*a53d2e4fSAli Mashtizadeh 96*a53d2e4fSAli Mashtizadeh static const char **border_char; 97*a53d2e4fSAli Mashtizadeh 98*a53d2e4fSAli Mashtizadeh static const char *border_vt[] = { 99*a53d2e4fSAli Mashtizadeh "\xe2\x94\x80", "\xe2\x94\x82", 100*a53d2e4fSAli Mashtizadeh "\xe2\x94\x8c", "\xe2\x94\x90", "\xe2\x94\x94", "\xe2\x94\x98", 101*a53d2e4fSAli Mashtizadeh "\xe2\x94\xac", "\xe2\x94\xb4", "\xe2\x94\xa4", "\xe2\x94\x9c", 102*a53d2e4fSAli Mashtizadeh "\xe2\x94\xbc" 103*a53d2e4fSAli Mashtizadeh }; 104*a53d2e4fSAli Mashtizadeh static const char *border_basic[] = { "-", "|", "+", "+", "+", "+", "+", "+", "+", "+" }; 105*a53d2e4fSAli Mashtizadeh 106*a53d2e4fSAli Mashtizadeh static char tc_boldbuf[16]; 107*a53d2e4fSAli Mashtizadeh static char tc_sgr0buf[16]; 108*a53d2e4fSAli Mashtizadeh static char tc_afbuf[16]; 109*a53d2e4fSAli Mashtizadeh static char tc_green[16]; 110*a53d2e4fSAli Mashtizadeh static char tc_yellow[16]; 111*a53d2e4fSAli Mashtizadeh static char tc_red[16]; 112*a53d2e4fSAli Mashtizadeh 113*a53d2e4fSAli Mashtizadeh static const char *TC_BOLD; 114*a53d2e4fSAli Mashtizadeh static const char *TC_SGR0; 115*a53d2e4fSAli Mashtizadeh static const char *TC_SETAF; 116*a53d2e4fSAli Mashtizadeh 117*a53d2e4fSAli Mashtizadeh void 118*a53d2e4fSAli Mashtizadeh tcemit(const char *c) 119*a53d2e4fSAli Mashtizadeh { 120*a53d2e4fSAli Mashtizadeh if (c) 121*a53d2e4fSAli Mashtizadeh tputs(c, 1, putchar); 122*a53d2e4fSAli Mashtizadeh } 123*a53d2e4fSAli Mashtizadeh 124*a53d2e4fSAli Mashtizadeh void 125*a53d2e4fSAli Mashtizadeh tcfg(int n) 126*a53d2e4fSAli Mashtizadeh { 127*a53d2e4fSAli Mashtizadeh if (TC_SETAF) 128*a53d2e4fSAli Mashtizadeh tputs(tgoto(TC_SETAF, 0, n), 1, putchar); 129*a53d2e4fSAli Mashtizadeh } 130*a53d2e4fSAli Mashtizadeh 131*a53d2e4fSAli Mashtizadeh static void 132*a53d2e4fSAli Mashtizadeh setup_termcap(const char *term) 133*a53d2e4fSAli Mashtizadeh { 134*a53d2e4fSAli Mashtizadeh bool xcolor; 135*a53d2e4fSAli Mashtizadeh char *buf; 136*a53d2e4fSAli Mashtizadeh char *clicolor; 137*a53d2e4fSAli Mashtizadeh char termcapbuf[4096]; 138*a53d2e4fSAli Mashtizadeh 139*a53d2e4fSAli Mashtizadeh if (tgetent(termcapbuf, "xterm-256color") != 1) { 140*a53d2e4fSAli Mashtizadeh TC_BOLD = NULL; 141*a53d2e4fSAli Mashtizadeh TC_SGR0 = NULL; 142*a53d2e4fSAli Mashtizadeh TC_SETAF = NULL; 143*a53d2e4fSAli Mashtizadeh return; 144*a53d2e4fSAli Mashtizadeh } 145*a53d2e4fSAli Mashtizadeh 146*a53d2e4fSAli Mashtizadeh buf = tc_boldbuf; 147*a53d2e4fSAli Mashtizadeh TC_BOLD = tgetstr("md", &buf); 148*a53d2e4fSAli Mashtizadeh buf = tc_sgr0buf; 149*a53d2e4fSAli Mashtizadeh TC_SGR0 = tgetstr("me", &buf); 150*a53d2e4fSAli Mashtizadeh buf = tc_afbuf; 151*a53d2e4fSAli Mashtizadeh TC_SETAF = tgetstr("AF", &buf); 152*a53d2e4fSAli Mashtizadeh 153*a53d2e4fSAli Mashtizadeh clicolor = getenv("CLICOLOR"); 154*a53d2e4fSAli Mashtizadeh if (clicolor == NULL || strcmp(clicolor, "1") != 0) { 155*a53d2e4fSAli Mashtizadeh return; 156*a53d2e4fSAli Mashtizadeh } 157*a53d2e4fSAli Mashtizadeh 158*a53d2e4fSAli Mashtizadeh if (strcmp(term, "xterm-256color") == 0) { 159*a53d2e4fSAli Mashtizadeh xcolor = true; 160*a53d2e4fSAli Mashtizadeh } else { 161*a53d2e4fSAli Mashtizadeh xcolor = false; 162*a53d2e4fSAli Mashtizadeh } 163*a53d2e4fSAli Mashtizadeh 164*a53d2e4fSAli Mashtizadeh buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_GREEN : COLOR_GREEN); 165*a53d2e4fSAli Mashtizadeh if (buf) { 166*a53d2e4fSAli Mashtizadeh strlcpy(tc_green, buf, sizeof(tc_green)); 167*a53d2e4fSAli Mashtizadeh } else { 168*a53d2e4fSAli Mashtizadeh tc_green[0] = '\0'; 169*a53d2e4fSAli Mashtizadeh } 170*a53d2e4fSAli Mashtizadeh 171*a53d2e4fSAli Mashtizadeh buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_YELLOW : COLOR_YELLOW); 172*a53d2e4fSAli Mashtizadeh if (buf) { 173*a53d2e4fSAli Mashtizadeh strlcpy(tc_yellow, buf, sizeof(tc_yellow)); 174*a53d2e4fSAli Mashtizadeh } else { 175*a53d2e4fSAli Mashtizadeh tc_yellow[0] = '\0'; 176*a53d2e4fSAli Mashtizadeh } 177*a53d2e4fSAli Mashtizadeh 178*a53d2e4fSAli Mashtizadeh buf = tgoto(TC_SETAF, 1, xcolor ? COLOR256_RED : COLOR_RED); 179*a53d2e4fSAli Mashtizadeh if (buf) { 180*a53d2e4fSAli Mashtizadeh strlcpy(tc_red, buf, sizeof(tc_red)); 181*a53d2e4fSAli Mashtizadeh } else { 182*a53d2e4fSAli Mashtizadeh tc_red[0] = '\0'; 183*a53d2e4fSAli Mashtizadeh } 184*a53d2e4fSAli Mashtizadeh } 185*a53d2e4fSAli Mashtizadeh 186*a53d2e4fSAli Mashtizadeh void 187*a53d2e4fSAli Mashtizadeh setup_screen() 188*a53d2e4fSAli Mashtizadeh { 189*a53d2e4fSAli Mashtizadeh int status; 190*a53d2e4fSAli Mashtizadeh const char *term; 191*a53d2e4fSAli Mashtizadeh struct winsize wsz; 192*a53d2e4fSAli Mashtizadeh 193*a53d2e4fSAli Mashtizadeh /* Sane defaults */ 194*a53d2e4fSAli Mashtizadeh tmode = TERMMODE_BASIC; 195*a53d2e4fSAli Mashtizadeh title_char = title_basic; 196*a53d2e4fSAli Mashtizadeh arrow_char = arrow_basic; 197*a53d2e4fSAli Mashtizadeh border_char = border_basic; 198*a53d2e4fSAli Mashtizadeh disp_height = 25; 199*a53d2e4fSAli Mashtizadeh disp_width = 80; 200*a53d2e4fSAli Mashtizadeh 201*a53d2e4fSAli Mashtizadeh if (isatty(STDOUT_FILENO) == 0) { 202*a53d2e4fSAli Mashtizadeh tmode = TERMMODE_XTERM; 203*a53d2e4fSAli Mashtizadeh title_char = title_vt; 204*a53d2e4fSAli Mashtizadeh arrow_char = arrow_vt; 205*a53d2e4fSAli Mashtizadeh border_char = border_vt; 206*a53d2e4fSAli Mashtizadeh return; 207*a53d2e4fSAli Mashtizadeh } 208*a53d2e4fSAli Mashtizadeh 209*a53d2e4fSAli Mashtizadeh status = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz); 210*a53d2e4fSAli Mashtizadeh if (status < 0) { 211*a53d2e4fSAli Mashtizadeh perror("ioctl(TIOCGWINSZ)"); 212*a53d2e4fSAli Mashtizadeh return; 213*a53d2e4fSAli Mashtizadeh } 214*a53d2e4fSAli Mashtizadeh 215*a53d2e4fSAli Mashtizadeh disp_height = wsz.ws_row; 216*a53d2e4fSAli Mashtizadeh disp_width = wsz.ws_col; 217*a53d2e4fSAli Mashtizadeh 218*a53d2e4fSAli Mashtizadeh term = getenv("TERM"); 219*a53d2e4fSAli Mashtizadeh if (term == NULL) { 220*a53d2e4fSAli Mashtizadeh tmode = TERMMODE_BASIC; 221*a53d2e4fSAli Mashtizadeh title_char = title_basic; 222*a53d2e4fSAli Mashtizadeh arrow_char = arrow_basic; 223*a53d2e4fSAli Mashtizadeh border_char = border_basic; 224*a53d2e4fSAli Mashtizadeh return; 225*a53d2e4fSAli Mashtizadeh } 226*a53d2e4fSAli Mashtizadeh 227*a53d2e4fSAli Mashtizadeh if (strncmp(term, "xterm", 5) == 0) { 228*a53d2e4fSAli Mashtizadeh tmode = TERMMODE_XTERM; 229*a53d2e4fSAli Mashtizadeh title_char = title_vt; 230*a53d2e4fSAli Mashtizadeh arrow_char = arrow_vt; 231*a53d2e4fSAli Mashtizadeh border_char = border_vt; 232*a53d2e4fSAli Mashtizadeh } else if (strncmp(term, "vt", 2) == 0) { 233*a53d2e4fSAli Mashtizadeh tmode = TERMMODE_VT; 234*a53d2e4fSAli Mashtizadeh title_char = title_vt; 235*a53d2e4fSAli Mashtizadeh arrow_char = arrow_vt; 236*a53d2e4fSAli Mashtizadeh border_char = border_vt; 237*a53d2e4fSAli Mashtizadeh } 238*a53d2e4fSAli Mashtizadeh 239*a53d2e4fSAli Mashtizadeh setup_termcap(term); 240*a53d2e4fSAli Mashtizadeh } 241*a53d2e4fSAli Mashtizadeh 242*a53d2e4fSAli Mashtizadeh void 243*a53d2e4fSAli Mashtizadeh title(const std::string &t) 244*a53d2e4fSAli Mashtizadeh { 245*a53d2e4fSAli Mashtizadeh size_t i; 246*a53d2e4fSAli Mashtizadeh 247*a53d2e4fSAli Mashtizadeh if (tmode != TERMMODE_BASIC) 248*a53d2e4fSAli Mashtizadeh tcemit(TC_BOLD); 249*a53d2e4fSAli Mashtizadeh printf("%s\n", t.c_str()); 250*a53d2e4fSAli Mashtizadeh if (tmode != TERMMODE_BASIC) 251*a53d2e4fSAli Mashtizadeh tcemit(TC_SGR0); 252*a53d2e4fSAli Mashtizadeh for (i = 0; i < t.size(); i++) 253*a53d2e4fSAli Mashtizadeh printf("%s", title_char[TITLE_TITLE]); 254*a53d2e4fSAli Mashtizadeh printf("\n\n"); 255*a53d2e4fSAli Mashtizadeh } 256*a53d2e4fSAli Mashtizadeh 257*a53d2e4fSAli Mashtizadeh void 258*a53d2e4fSAli Mashtizadeh header(const std::string &h) 259*a53d2e4fSAli Mashtizadeh { 260*a53d2e4fSAli Mashtizadeh size_t i; 261*a53d2e4fSAli Mashtizadeh 262*a53d2e4fSAli Mashtizadeh if (tmode != TERMMODE_BASIC) 263*a53d2e4fSAli Mashtizadeh tcemit(TC_BOLD); 264*a53d2e4fSAli Mashtizadeh printf("%s\n", h.c_str()); 265*a53d2e4fSAli Mashtizadeh if (tmode != TERMMODE_BASIC) 266*a53d2e4fSAli Mashtizadeh tcemit(TC_SGR0); 267*a53d2e4fSAli Mashtizadeh for (i = 0; i < h.size(); i++) 268*a53d2e4fSAli Mashtizadeh printf("%s", title_char[TITLE_SUBTITLE]); 269*a53d2e4fSAli Mashtizadeh printf("\n"); 270*a53d2e4fSAli Mashtizadeh } 271*a53d2e4fSAli Mashtizadeh 272*a53d2e4fSAli Mashtizadeh void 273*a53d2e4fSAli Mashtizadeh printval(const std::string &msg, uint64_t val, siunit ui) 274*a53d2e4fSAli Mashtizadeh { 275*a53d2e4fSAli Mashtizadeh switch (ui) { 276*a53d2e4fSAli Mashtizadeh case siunit::seconds: 277*a53d2e4fSAli Mashtizadeh printf("%s: %" PRIu64 " s\n", msg.c_str(), val); 278*a53d2e4fSAli Mashtizadeh break; 279*a53d2e4fSAli Mashtizadeh case siunit::percent: 280*a53d2e4fSAli Mashtizadeh printf("%s: %" PRIu64 "%%\n", msg.c_str(), val); 281*a53d2e4fSAli Mashtizadeh break; 282*a53d2e4fSAli Mashtizadeh case siunit::cycles: 283*a53d2e4fSAli Mashtizadeh printf("%s: %" PRIu64 " cycles\n", msg.c_str(), val); 284*a53d2e4fSAli Mashtizadeh break; 285*a53d2e4fSAli Mashtizadeh } 286*a53d2e4fSAli Mashtizadeh } 287*a53d2e4fSAli Mashtizadeh 288*a53d2e4fSAli Mashtizadeh void 289*a53d2e4fSAli Mashtizadeh printval(const std::string &msg, float val, siunit ui) 290*a53d2e4fSAli Mashtizadeh { 291*a53d2e4fSAli Mashtizadeh switch (ui) { 292*a53d2e4fSAli Mashtizadeh case siunit::seconds: 293*a53d2e4fSAli Mashtizadeh printf("%s: %.2f s\n", msg.c_str(), val); 294*a53d2e4fSAli Mashtizadeh break; 295*a53d2e4fSAli Mashtizadeh case siunit::percent: 296*a53d2e4fSAli Mashtizadeh printf("%s: %6.2f%%\n", msg.c_str(), val); 297*a53d2e4fSAli Mashtizadeh break; 298*a53d2e4fSAli Mashtizadeh case siunit::cycles: 299*a53d2e4fSAli Mashtizadeh printf("%s: %.2f cycles\n", msg.c_str(), val); 300*a53d2e4fSAli Mashtizadeh break; 301*a53d2e4fSAli Mashtizadeh } 302*a53d2e4fSAli Mashtizadeh } 303*a53d2e4fSAli Mashtizadeh 304*a53d2e4fSAli Mashtizadeh std::string 305*a53d2e4fSAli Mashtizadeh format_siprefix(uint64_t count) 306*a53d2e4fSAli Mashtizadeh { 307*a53d2e4fSAli Mashtizadeh unsigned long index = 0; 308*a53d2e4fSAli Mashtizadeh char prefix[] = " kMGTP"; 309*a53d2e4fSAli Mashtizadeh char buf[10]; 310*a53d2e4fSAli Mashtizadeh 311*a53d2e4fSAli Mashtizadeh while (count > 10000 && index < sizeof(prefix)) { 312*a53d2e4fSAli Mashtizadeh index++; 313*a53d2e4fSAli Mashtizadeh count /= 1000; 314*a53d2e4fSAli Mashtizadeh } 315*a53d2e4fSAli Mashtizadeh 316*a53d2e4fSAli Mashtizadeh if (index == 0) { 317*a53d2e4fSAli Mashtizadeh snprintf(buf, sizeof(buf), "%" PRIu64, count); 318*a53d2e4fSAli Mashtizadeh } else { 319*a53d2e4fSAli Mashtizadeh snprintf(buf, sizeof(buf), "%" PRIu64 "%c", count, prefix[index]); 320*a53d2e4fSAli Mashtizadeh } 321*a53d2e4fSAli Mashtizadeh 322*a53d2e4fSAli Mashtizadeh return buf; 323*a53d2e4fSAli Mashtizadeh } 324*a53d2e4fSAli Mashtizadeh 325*a53d2e4fSAli Mashtizadeh std::string 326*a53d2e4fSAli Mashtizadeh format_binprefix(uint64_t count) 327*a53d2e4fSAli Mashtizadeh { 328*a53d2e4fSAli Mashtizadeh unsigned long index = 0; 329*a53d2e4fSAli Mashtizadeh char prefix[] = " KMGTP"; 330*a53d2e4fSAli Mashtizadeh char buf[10]; 331*a53d2e4fSAli Mashtizadeh 332*a53d2e4fSAli Mashtizadeh while (count > 10000 && index < sizeof(prefix)) { 333*a53d2e4fSAli Mashtizadeh index++; 334*a53d2e4fSAli Mashtizadeh count /= 1000; 335*a53d2e4fSAli Mashtizadeh } 336*a53d2e4fSAli Mashtizadeh 337*a53d2e4fSAli Mashtizadeh if (index == 0) { 338*a53d2e4fSAli Mashtizadeh snprintf(buf, sizeof(buf), "%" PRIu64 " B", count); 339*a53d2e4fSAli Mashtizadeh } else { 340*a53d2e4fSAli Mashtizadeh snprintf(buf, sizeof(buf), "%" PRIu64 " %ciB", count, prefix[index]); 341*a53d2e4fSAli Mashtizadeh } 342*a53d2e4fSAli Mashtizadeh 343*a53d2e4fSAli Mashtizadeh return buf; 344*a53d2e4fSAli Mashtizadeh } 345*a53d2e4fSAli Mashtizadeh 346*a53d2e4fSAli Mashtizadeh std::string 347*a53d2e4fSAli Mashtizadeh format_sample(uint64_t count, uint64_t total) 348*a53d2e4fSAli Mashtizadeh { 349*a53d2e4fSAli Mashtizadeh char buf[20]; 350*a53d2e4fSAli Mashtizadeh std::string fmt = format_siprefix(count); 351*a53d2e4fSAli Mashtizadeh 352*a53d2e4fSAli Mashtizadeh snprintf(buf, sizeof(buf), "%5s (%4.1f%%)", fmt.c_str(), 353*a53d2e4fSAli Mashtizadeh 100.0 * (float)count / (float)total); 354*a53d2e4fSAli Mashtizadeh 355*a53d2e4fSAli Mashtizadeh return buf; 356*a53d2e4fSAli Mashtizadeh } 357*a53d2e4fSAli Mashtizadeh 358*a53d2e4fSAli Mashtizadeh std::string 359*a53d2e4fSAli Mashtizadeh format_percent(uint64_t count, uint64_t total) 360*a53d2e4fSAli Mashtizadeh { 361*a53d2e4fSAli Mashtizadeh char buf[20]; 362*a53d2e4fSAli Mashtizadeh 363*a53d2e4fSAli Mashtizadeh if (total == 0) { 364*a53d2e4fSAli Mashtizadeh snprintf(buf, sizeof(buf), "%6s", ""); 365*a53d2e4fSAli Mashtizadeh } else { 366*a53d2e4fSAli Mashtizadeh snprintf(buf, sizeof(buf), "%5.1f%%", 367*a53d2e4fSAli Mashtizadeh 100.0 * (float)count / (float)total); 368*a53d2e4fSAli Mashtizadeh } 369*a53d2e4fSAli Mashtizadeh 370*a53d2e4fSAli Mashtizadeh return buf; 371*a53d2e4fSAli Mashtizadeh } 372*a53d2e4fSAli Mashtizadeh 373*a53d2e4fSAli Mashtizadeh /* fields */ 374*a53d2e4fSAli Mashtizadeh 375*a53d2e4fSAli Mashtizadeh field::field(int64_t v) 376*a53d2e4fSAli Mashtizadeh { 377*a53d2e4fSAli Mashtizadeh type = FIELD::INTEGER; 378*a53d2e4fSAli Mashtizadeh value[0] = v; 379*a53d2e4fSAli Mashtizadeh } 380*a53d2e4fSAli Mashtizadeh 381*a53d2e4fSAli Mashtizadeh field::field(float v) 382*a53d2e4fSAli Mashtizadeh { 383*a53d2e4fSAli Mashtizadeh type = FIELD::FLOAT; 384*a53d2e4fSAli Mashtizadeh fvalue = v; 385*a53d2e4fSAli Mashtizadeh } 386*a53d2e4fSAli Mashtizadeh 387*a53d2e4fSAli Mashtizadeh field::field(const std::string &s) 388*a53d2e4fSAli Mashtizadeh { 389*a53d2e4fSAli Mashtizadeh type = FIELD::STRING; 390*a53d2e4fSAli Mashtizadeh svalue = s; 391*a53d2e4fSAli Mashtizadeh } 392*a53d2e4fSAli Mashtizadeh 393*a53d2e4fSAli Mashtizadeh field::field(int64_t c, int64_t t, bool percent) 394*a53d2e4fSAli Mashtizadeh { 395*a53d2e4fSAli Mashtizadeh if (percent) 396*a53d2e4fSAli Mashtizadeh type = FIELD::PERCENT; 397*a53d2e4fSAli Mashtizadeh else 398*a53d2e4fSAli Mashtizadeh type = FIELD::SAMPLE; 399*a53d2e4fSAli Mashtizadeh value[0] = c; 400*a53d2e4fSAli Mashtizadeh value[1] = t; 401*a53d2e4fSAli Mashtizadeh } 402*a53d2e4fSAli Mashtizadeh 403*a53d2e4fSAli Mashtizadeh field::field(int64_t uc, int64_t ut, int64_t ku, int64_t kt) 404*a53d2e4fSAli Mashtizadeh { 405*a53d2e4fSAli Mashtizadeh type = FIELD::DSAMPLE; 406*a53d2e4fSAli Mashtizadeh value[0] = uc; 407*a53d2e4fSAli Mashtizadeh value[1] = ut; 408*a53d2e4fSAli Mashtizadeh value[2] = ku; 409*a53d2e4fSAli Mashtizadeh value[3] = kt; 410*a53d2e4fSAli Mashtizadeh } 411*a53d2e4fSAli Mashtizadeh 412*a53d2e4fSAli Mashtizadeh bool 413*a53d2e4fSAli Mashtizadeh field::operator>(const field &b) const 414*a53d2e4fSAli Mashtizadeh { 415*a53d2e4fSAli Mashtizadeh switch (type) { 416*a53d2e4fSAli Mashtizadeh case FIELD::STRING: 417*a53d2e4fSAli Mashtizadeh return (svalue > b.svalue); 418*a53d2e4fSAli Mashtizadeh case FIELD::FLOAT: 419*a53d2e4fSAli Mashtizadeh return (fvalue > b.fvalue); 420*a53d2e4fSAli Mashtizadeh case FIELD::INTEGER: [[fallthrough]]; 421*a53d2e4fSAli Mashtizadeh case FIELD::SAMPLE: 422*a53d2e4fSAli Mashtizadeh return (value[0] > b.value[0]); 423*a53d2e4fSAli Mashtizadeh case FIELD::DSAMPLE: 424*a53d2e4fSAli Mashtizadeh return ((value[0] + value[2]) > (b.value[0] + b.value[2])); 425*a53d2e4fSAli Mashtizadeh case FIELD::PERCENT: 426*a53d2e4fSAli Mashtizadeh return (((float)value[0] / (float)value[1]) > 427*a53d2e4fSAli Mashtizadeh ((float)b.value[0] / (float)b.value[1])); 428*a53d2e4fSAli Mashtizadeh } 429*a53d2e4fSAli Mashtizadeh } 430*a53d2e4fSAli Mashtizadeh 431*a53d2e4fSAli Mashtizadeh bool 432*a53d2e4fSAli Mashtizadeh field::operator<(const field &b) const 433*a53d2e4fSAli Mashtizadeh { 434*a53d2e4fSAli Mashtizadeh switch (type) { 435*a53d2e4fSAli Mashtizadeh case FIELD::STRING: 436*a53d2e4fSAli Mashtizadeh return (svalue < b.svalue); 437*a53d2e4fSAli Mashtizadeh case FIELD::FLOAT: 438*a53d2e4fSAli Mashtizadeh return (fvalue < b.fvalue); 439*a53d2e4fSAli Mashtizadeh case FIELD::INTEGER: [[fallthrough]]; 440*a53d2e4fSAli Mashtizadeh case FIELD::SAMPLE: 441*a53d2e4fSAli Mashtizadeh return (value[0] < b.value[0]); 442*a53d2e4fSAli Mashtizadeh case FIELD::DSAMPLE: 443*a53d2e4fSAli Mashtizadeh return ((value[0] + value[2]) < (b.value[0] + b.value[2])); 444*a53d2e4fSAli Mashtizadeh case FIELD::PERCENT: 445*a53d2e4fSAli Mashtizadeh return (((float)value[0] / (float)value[1]) < 446*a53d2e4fSAli Mashtizadeh ((float)b.value[0] / (float)b.value[1])); 447*a53d2e4fSAli Mashtizadeh } 448*a53d2e4fSAli Mashtizadeh } 449*a53d2e4fSAli Mashtizadeh 450*a53d2e4fSAli Mashtizadeh size_t 451*a53d2e4fSAli Mashtizadeh field::length() 452*a53d2e4fSAli Mashtizadeh { 453*a53d2e4fSAli Mashtizadeh return (to_string().length()); 454*a53d2e4fSAli Mashtizadeh } 455*a53d2e4fSAli Mashtizadeh 456*a53d2e4fSAli Mashtizadeh std::string 457*a53d2e4fSAli Mashtizadeh field::to_string() 458*a53d2e4fSAli Mashtizadeh { 459*a53d2e4fSAli Mashtizadeh switch (type) { 460*a53d2e4fSAli Mashtizadeh case FIELD::STRING: 461*a53d2e4fSAli Mashtizadeh return (svalue); 462*a53d2e4fSAli Mashtizadeh case FIELD::FLOAT: 463*a53d2e4fSAli Mashtizadeh return (std::to_string(fvalue)); 464*a53d2e4fSAli Mashtizadeh case FIELD::INTEGER: 465*a53d2e4fSAli Mashtizadeh return (std::to_string(value[0])); 466*a53d2e4fSAli Mashtizadeh case FIELD::SAMPLE: 467*a53d2e4fSAli Mashtizadeh return (format_sample(value[0], value[1])); 468*a53d2e4fSAli Mashtizadeh case FIELD::DSAMPLE: 469*a53d2e4fSAli Mashtizadeh return (format_sample(value[0], value[1]) + " / " + 470*a53d2e4fSAli Mashtizadeh format_sample(value[2], value[3])); 471*a53d2e4fSAli Mashtizadeh case FIELD::PERCENT: 472*a53d2e4fSAli Mashtizadeh return (format_percent(value[0], value[1])); 473*a53d2e4fSAli Mashtizadeh } 474*a53d2e4fSAli Mashtizadeh } 475*a53d2e4fSAli Mashtizadeh 476*a53d2e4fSAli Mashtizadeh /* table class */ 477*a53d2e4fSAli Mashtizadeh 478*a53d2e4fSAli Mashtizadeh table::table() : sortcol(-1), sortdir(true), cols(), align(), rows() 479*a53d2e4fSAli Mashtizadeh { 480*a53d2e4fSAli Mashtizadeh } 481*a53d2e4fSAli Mashtizadeh 482*a53d2e4fSAli Mashtizadeh table::~table() 483*a53d2e4fSAli Mashtizadeh { 484*a53d2e4fSAli Mashtizadeh } 485*a53d2e4fSAli Mashtizadeh 486*a53d2e4fSAli Mashtizadeh void 487*a53d2e4fSAli Mashtizadeh table::addcolumn(const std::string &c, bool alignleft) 488*a53d2e4fSAli Mashtizadeh { 489*a53d2e4fSAli Mashtizadeh cols.push_back(c); 490*a53d2e4fSAli Mashtizadeh align.push_back(alignleft); 491*a53d2e4fSAli Mashtizadeh } 492*a53d2e4fSAli Mashtizadeh 493*a53d2e4fSAli Mashtizadeh void 494*a53d2e4fSAli Mashtizadeh table::addrow(std::vector<field> r) 495*a53d2e4fSAli Mashtizadeh { 496*a53d2e4fSAli Mashtizadeh rows.push_back(std::move(r)); 497*a53d2e4fSAli Mashtizadeh } 498*a53d2e4fSAli Mashtizadeh 499*a53d2e4fSAli Mashtizadeh void 500*a53d2e4fSAli Mashtizadeh table::sort(int col, bool descending) 501*a53d2e4fSAli Mashtizadeh { 502*a53d2e4fSAli Mashtizadeh sortcol = col; 503*a53d2e4fSAli Mashtizadeh sortdir = descending; 504*a53d2e4fSAli Mashtizadeh if (descending) 505*a53d2e4fSAli Mashtizadeh std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b) 506*a53d2e4fSAli Mashtizadeh { return (a[col] > b[col]); }); 507*a53d2e4fSAli Mashtizadeh else 508*a53d2e4fSAli Mashtizadeh std::sort(rows.begin(), rows.end(), [col](auto &a, auto &b) 509*a53d2e4fSAli Mashtizadeh { return (a[col] < b[col]); }); 510*a53d2e4fSAli Mashtizadeh } 511*a53d2e4fSAli Mashtizadeh 512*a53d2e4fSAli Mashtizadeh void 513*a53d2e4fSAli Mashtizadeh table::print() 514*a53d2e4fSAli Mashtizadeh { 515*a53d2e4fSAli Mashtizadeh unsigned long c; 516*a53d2e4fSAli Mashtizadeh std::vector<unsigned long> width; 517*a53d2e4fSAli Mashtizadeh 518*a53d2e4fSAli Mashtizadeh // Compute column width 519*a53d2e4fSAli Mashtizadeh for (auto h : cols) 520*a53d2e4fSAli Mashtizadeh width.push_back(h.length() + 1); 521*a53d2e4fSAli Mashtizadeh if (sortcol != -1) 522*a53d2e4fSAli Mashtizadeh width[sortcol] += 1; 523*a53d2e4fSAli Mashtizadeh for (auto &r : rows) { 524*a53d2e4fSAli Mashtizadeh for (c = 0; c < r.size(); c++) { 525*a53d2e4fSAli Mashtizadeh if (width[c] < r[c].length()) 526*a53d2e4fSAli Mashtizadeh width[c] = r[c].length(); 527*a53d2e4fSAli Mashtizadeh } 528*a53d2e4fSAli Mashtizadeh } 529*a53d2e4fSAli Mashtizadeh 530*a53d2e4fSAli Mashtizadeh for (c = 0; c < width.size(); c++) { 531*a53d2e4fSAli Mashtizadeh if (align[c]) 532*a53d2e4fSAli Mashtizadeh std::cout << std::left; 533*a53d2e4fSAli Mashtizadeh std::cout << std::setw(width[c]); 534*a53d2e4fSAli Mashtizadeh tcemit(TC_BOLD); 535*a53d2e4fSAli Mashtizadeh if (c == (unsigned long)sortcol) 536*a53d2e4fSAli Mashtizadeh std::cout << cols[c] + " " + arrow_char[sortdir ? ARROW_DOWN : ARROW_UP]; 537*a53d2e4fSAli Mashtizadeh else 538*a53d2e4fSAli Mashtizadeh std::cout << cols[c]; 539*a53d2e4fSAli Mashtizadeh tcemit(TC_SGR0); 540*a53d2e4fSAli Mashtizadeh if (c != (width.size() - 1)) 541*a53d2e4fSAli Mashtizadeh std::cout << " " << border_char[BORDER_VBAR] << " "; 542*a53d2e4fSAli Mashtizadeh if (align[c]) 543*a53d2e4fSAli Mashtizadeh std::cout << std::right; 544*a53d2e4fSAli Mashtizadeh } 545*a53d2e4fSAli Mashtizadeh std::cout << std::endl; 546*a53d2e4fSAli Mashtizadeh 547*a53d2e4fSAli Mashtizadeh for (c = 0; c < width.size(); c++) { 548*a53d2e4fSAli Mashtizadeh for (unsigned long i = 0; i < width[c]; i++) 549*a53d2e4fSAli Mashtizadeh std::cout << border_char[BORDER_HBAR]; 550*a53d2e4fSAli Mashtizadeh if (c != (width.size() - 1)) 551*a53d2e4fSAli Mashtizadeh std::cout << border_char[BORDER_HBAR] << 552*a53d2e4fSAli Mashtizadeh border_char[BORDER_CROSS] << border_char[BORDER_HBAR]; 553*a53d2e4fSAli Mashtizadeh } 554*a53d2e4fSAli Mashtizadeh std::cout << std::setfill(' ') << std::endl; 555*a53d2e4fSAli Mashtizadeh 556*a53d2e4fSAli Mashtizadeh for (auto &r : rows) { 557*a53d2e4fSAli Mashtizadeh for (c = 0; c < width.size(); c++) { 558*a53d2e4fSAli Mashtizadeh if (align[c]) 559*a53d2e4fSAli Mashtizadeh std::cout << std::left; 560*a53d2e4fSAli Mashtizadeh std::cout << std::setw(width[c]) << r[c].to_string(); 561*a53d2e4fSAli Mashtizadeh if (c != (width.size() - 1)) 562*a53d2e4fSAli Mashtizadeh std::cout << " " << border_char[BORDER_VBAR] << " "; 563*a53d2e4fSAli Mashtizadeh if (align[c]) 564*a53d2e4fSAli Mashtizadeh std::cout << std::right; 565*a53d2e4fSAli Mashtizadeh } 566*a53d2e4fSAli Mashtizadeh std::cout << std::endl; 567*a53d2e4fSAli Mashtizadeh } 568*a53d2e4fSAli Mashtizadeh 569*a53d2e4fSAli Mashtizadeh std::cout << std::endl; 570*a53d2e4fSAli Mashtizadeh } 571*a53d2e4fSAli Mashtizadeh 572