forked from Minki/linux
e7f01d1e3d
Several places were expecting that the value returned was the number of characters printed, not what would be printed if there was space. Fix it by using the scnprintf and vscnprintf variants we inherited from the kernel sources. Some corner cases where the number of printed characters were not accounted were fixed too. Reported-by: Anton Blanchard <anton@samba.org> Cc: Anton Blanchard <anton@samba.org> Cc: Eric B Munson <emunson@mgebm.net> Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Yanmin Zhang <yanmin_zhang@linux.intel.com> Cc: stable@kernel.org Link: http://lkml.kernel.org/n/tip-kwxo2eh29cxmd8ilixi2005x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
326 lines
6.6 KiB
C
326 lines
6.6 KiB
C
#include <linux/kernel.h>
|
|
#include "cache.h"
|
|
#include "color.h"
|
|
|
|
int perf_use_color_default = -1;
|
|
|
|
static int parse_color(const char *name, int len)
|
|
{
|
|
static const char * const color_names[] = {
|
|
"normal", "black", "red", "green", "yellow",
|
|
"blue", "magenta", "cyan", "white"
|
|
};
|
|
char *end;
|
|
int i;
|
|
|
|
for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
|
|
const char *str = color_names[i];
|
|
if (!strncasecmp(name, str, len) && !str[len])
|
|
return i - 1;
|
|
}
|
|
i = strtol(name, &end, 10);
|
|
if (end - name == len && i >= -1 && i <= 255)
|
|
return i;
|
|
return -2;
|
|
}
|
|
|
|
static int parse_attr(const char *name, int len)
|
|
{
|
|
static const int attr_values[] = { 1, 2, 4, 5, 7 };
|
|
static const char * const attr_names[] = {
|
|
"bold", "dim", "ul", "blink", "reverse"
|
|
};
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
|
|
const char *str = attr_names[i];
|
|
if (!strncasecmp(name, str, len) && !str[len])
|
|
return attr_values[i];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void color_parse(const char *value, const char *var, char *dst)
|
|
{
|
|
color_parse_mem(value, strlen(value), var, dst);
|
|
}
|
|
|
|
void color_parse_mem(const char *value, int value_len, const char *var,
|
|
char *dst)
|
|
{
|
|
const char *ptr = value;
|
|
int len = value_len;
|
|
int attr = -1;
|
|
int fg = -2;
|
|
int bg = -2;
|
|
|
|
if (!strncasecmp(value, "reset", len)) {
|
|
strcpy(dst, PERF_COLOR_RESET);
|
|
return;
|
|
}
|
|
|
|
/* [fg [bg]] [attr] */
|
|
while (len > 0) {
|
|
const char *word = ptr;
|
|
int val, wordlen = 0;
|
|
|
|
while (len > 0 && !isspace(word[wordlen])) {
|
|
wordlen++;
|
|
len--;
|
|
}
|
|
|
|
ptr = word + wordlen;
|
|
while (len > 0 && isspace(*ptr)) {
|
|
ptr++;
|
|
len--;
|
|
}
|
|
|
|
val = parse_color(word, wordlen);
|
|
if (val >= -1) {
|
|
if (fg == -2) {
|
|
fg = val;
|
|
continue;
|
|
}
|
|
if (bg == -2) {
|
|
bg = val;
|
|
continue;
|
|
}
|
|
goto bad;
|
|
}
|
|
val = parse_attr(word, wordlen);
|
|
if (val < 0 || attr != -1)
|
|
goto bad;
|
|
attr = val;
|
|
}
|
|
|
|
if (attr >= 0 || fg >= 0 || bg >= 0) {
|
|
int sep = 0;
|
|
|
|
*dst++ = '\033';
|
|
*dst++ = '[';
|
|
if (attr >= 0) {
|
|
*dst++ = '0' + attr;
|
|
sep++;
|
|
}
|
|
if (fg >= 0) {
|
|
if (sep++)
|
|
*dst++ = ';';
|
|
if (fg < 8) {
|
|
*dst++ = '3';
|
|
*dst++ = '0' + fg;
|
|
} else {
|
|
dst += sprintf(dst, "38;5;%d", fg);
|
|
}
|
|
}
|
|
if (bg >= 0) {
|
|
if (sep++)
|
|
*dst++ = ';';
|
|
if (bg < 8) {
|
|
*dst++ = '4';
|
|
*dst++ = '0' + bg;
|
|
} else {
|
|
dst += sprintf(dst, "48;5;%d", bg);
|
|
}
|
|
}
|
|
*dst++ = 'm';
|
|
}
|
|
*dst = 0;
|
|
return;
|
|
bad:
|
|
die("bad color value '%.*s' for variable '%s'", value_len, value, var);
|
|
}
|
|
|
|
int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty)
|
|
{
|
|
if (value) {
|
|
if (!strcasecmp(value, "never"))
|
|
return 0;
|
|
if (!strcasecmp(value, "always"))
|
|
return 1;
|
|
if (!strcasecmp(value, "auto"))
|
|
goto auto_color;
|
|
}
|
|
|
|
/* Missing or explicit false to turn off colorization */
|
|
if (!perf_config_bool(var, value))
|
|
return 0;
|
|
|
|
/* any normal truth value defaults to 'auto' */
|
|
auto_color:
|
|
if (stdout_is_tty < 0)
|
|
stdout_is_tty = isatty(1);
|
|
if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
|
|
char *term = getenv("TERM");
|
|
if (term && strcmp(term, "dumb"))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int perf_color_default_config(const char *var, const char *value, void *cb)
|
|
{
|
|
if (!strcmp(var, "color.ui")) {
|
|
perf_use_color_default = perf_config_colorbool(var, value, -1);
|
|
return 0;
|
|
}
|
|
|
|
return perf_default_config(var, value, cb);
|
|
}
|
|
|
|
static int __color_vsnprintf(char *bf, size_t size, const char *color,
|
|
const char *fmt, va_list args, const char *trail)
|
|
{
|
|
int r = 0;
|
|
|
|
/*
|
|
* Auto-detect:
|
|
*/
|
|
if (perf_use_color_default < 0) {
|
|
if (isatty(1) || pager_in_use())
|
|
perf_use_color_default = 1;
|
|
else
|
|
perf_use_color_default = 0;
|
|
}
|
|
|
|
if (perf_use_color_default && *color)
|
|
r += scnprintf(bf, size, "%s", color);
|
|
r += vscnprintf(bf + r, size - r, fmt, args);
|
|
if (perf_use_color_default && *color)
|
|
r += scnprintf(bf + r, size - r, "%s", PERF_COLOR_RESET);
|
|
if (trail)
|
|
r += scnprintf(bf + r, size - r, "%s", trail);
|
|
return r;
|
|
}
|
|
|
|
static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,
|
|
va_list args, const char *trail)
|
|
{
|
|
int r = 0;
|
|
|
|
/*
|
|
* Auto-detect:
|
|
*/
|
|
if (perf_use_color_default < 0) {
|
|
if (isatty(fileno(fp)) || pager_in_use())
|
|
perf_use_color_default = 1;
|
|
else
|
|
perf_use_color_default = 0;
|
|
}
|
|
|
|
if (perf_use_color_default && *color)
|
|
r += fprintf(fp, "%s", color);
|
|
r += vfprintf(fp, fmt, args);
|
|
if (perf_use_color_default && *color)
|
|
r += fprintf(fp, "%s", PERF_COLOR_RESET);
|
|
if (trail)
|
|
r += fprintf(fp, "%s", trail);
|
|
return r;
|
|
}
|
|
|
|
int color_vsnprintf(char *bf, size_t size, const char *color,
|
|
const char *fmt, va_list args)
|
|
{
|
|
return __color_vsnprintf(bf, size, color, fmt, args, NULL);
|
|
}
|
|
|
|
int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args)
|
|
{
|
|
return __color_vfprintf(fp, color, fmt, args, NULL);
|
|
}
|
|
|
|
int color_snprintf(char *bf, size_t size, const char *color,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int r;
|
|
|
|
va_start(args, fmt);
|
|
r = color_vsnprintf(bf, size, color, fmt, args);
|
|
va_end(args);
|
|
return r;
|
|
}
|
|
|
|
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int r;
|
|
|
|
va_start(args, fmt);
|
|
r = color_vfprintf(fp, color, fmt, args);
|
|
va_end(args);
|
|
return r;
|
|
}
|
|
|
|
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int r;
|
|
va_start(args, fmt);
|
|
r = __color_vfprintf(fp, color, fmt, args, "\n");
|
|
va_end(args);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* This function splits the buffer by newlines and colors the lines individually.
|
|
*
|
|
* Returns 0 on success.
|
|
*/
|
|
int color_fwrite_lines(FILE *fp, const char *color,
|
|
size_t count, const char *buf)
|
|
{
|
|
if (!*color)
|
|
return fwrite(buf, count, 1, fp) != 1;
|
|
|
|
while (count) {
|
|
char *p = memchr(buf, '\n', count);
|
|
|
|
if (p != buf && (fputs(color, fp) < 0 ||
|
|
fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||
|
|
fputs(PERF_COLOR_RESET, fp) < 0))
|
|
return -1;
|
|
if (!p)
|
|
return 0;
|
|
if (fputc('\n', fp) < 0)
|
|
return -1;
|
|
count -= p + 1 - buf;
|
|
buf = p + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *get_percent_color(double percent)
|
|
{
|
|
const char *color = PERF_COLOR_NORMAL;
|
|
|
|
/*
|
|
* We color high-overhead entries in red, mid-overhead
|
|
* entries in green - and keep the low overhead places
|
|
* normal:
|
|
*/
|
|
if (percent >= MIN_RED)
|
|
color = PERF_COLOR_RED;
|
|
else {
|
|
if (percent > MIN_GREEN)
|
|
color = PERF_COLOR_GREEN;
|
|
}
|
|
return color;
|
|
}
|
|
|
|
int percent_color_fprintf(FILE *fp, const char *fmt, double percent)
|
|
{
|
|
int r;
|
|
const char *color;
|
|
|
|
color = get_percent_color(percent);
|
|
r = color_fprintf(fp, color, fmt, percent);
|
|
|
|
return r;
|
|
}
|
|
|
|
int percent_color_snprintf(char *bf, size_t size, const char *fmt, double percent)
|
|
{
|
|
const char *color = get_percent_color(percent);
|
|
return color_snprintf(bf, size, color, fmt, percent);
|
|
}
|