Files
Aaron Plattner 251ec51c59 545.23.06
2023-10-17 10:03:12 -07:00

429 lines
10 KiB
C

/*
* Copyright (C) 2004 NVIDIA Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#define _GNU_SOURCE // needed for fileno
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#if defined(__sun)
#include <sys/termios.h>
#endif
#include "msg.h"
#include "common-utils.h"
/*
* verbosity, controls output of errors, warnings and other
* information.
*/
static NvVerbosity __verbosity = NV_VERBOSITY_DEFAULT;
NvVerbosity nv_get_verbosity(void)
{
return __verbosity;
}
void nv_set_verbosity(NvVerbosity level)
{
__verbosity = level;
}
/****************************************************************************/
/* Formatted I/O functions */
/****************************************************************************/
#define DEFAULT_WIDTH 75
static unsigned short __terminal_width = 0;
/*
* reset_current_terminal_width() - if new_val is zero, then use the
* TIOCGWINSZ ioctl to get the current width of the terminal, and
* assign it the value to __terminal_width. If the ioctl fails, use a
* hardcoded constant. If new_val is non-zero, then use new_val.
*/
void reset_current_terminal_width(unsigned short new_val)
{
struct winsize ws;
if (new_val) {
__terminal_width = new_val;
return;
}
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
__terminal_width = DEFAULT_WIDTH;
} else {
__terminal_width = ws.ws_col - 1;
}
}
static void format(FILE *stream, const char *prefix, const char *buf,
const int whitespace)
{
if (isatty(fileno(stream))) {
int i;
TextRows *t;
if (!__terminal_width) reset_current_terminal_width(0);
t = nv_format_text_rows(prefix, buf, __terminal_width, whitespace);
for (i = 0; i < t->n; i++) fprintf(stream, "%s\n", t->t[i]);
nv_free_text_rows(t);
} else {
fprintf(stream, "%s%s\n", prefix ? prefix : "", buf);
}
}
#define NV_FORMAT(stream, prefix, fmt, whitespace) \
do { \
char *buf; \
NV_VSNPRINTF(buf, fmt); \
format(stream, prefix, buf, whitespace); \
free (buf); \
} while (0)
/*
* nv_error_msg() - print an error message, nicely formatted using the
* format() function.
*
* This function should be used for all errors.
*/
void nv_error_msg(const char *fmt, ...)
{
if (__verbosity < NV_VERBOSITY_ERROR) return;
format(stderr, NULL, "", TRUE);
NV_FORMAT(stderr, "ERROR: ", fmt, TRUE);
format(stderr, NULL, "", TRUE);
} /* nv_error_msg() */
/*
* nv_deprecated_msg() - print a deprecation message, nicely formatted using
* the format() function.
*
* This function should be used for all deprecation messages.
*/
void nv_deprecated_msg(const char *fmt, ...)
{
if (__verbosity < NV_VERBOSITY_DEPRECATED) return;
format(stderr, NULL, "", TRUE);
NV_FORMAT(stderr, "DEPRECATED: ", fmt, TRUE);
format(stderr, NULL, "", TRUE);
}
/*
* nv_warning_msg() - print a warning message, nicely formatted using
* the format() function.
*
* This function should be used for all warnings.
*/
void nv_warning_msg(const char *fmt, ...)
{
if (__verbosity < NV_VERBOSITY_WARNING) return;
format(stderr, NULL, "", TRUE);
NV_FORMAT(stderr, "WARNING: ", fmt, TRUE);
format(stderr, NULL, "", TRUE);
} /* nv_warning_msg() */
/*
* nv_info_msg() - print an info message, nicely formatted using
* the format() function.
*
* This function should be used to display verbose information.
*/
void nv_info_msg(const char *prefix, const char *fmt, ...)
{
if (__verbosity < NV_VERBOSITY_ALL) return;
NV_FORMAT(stdout, prefix, fmt, TRUE);
} /* nv_info_msg() */
/*
* nv_info_msg_to_file() - Prints the message, just like nv_info_msg()
* using format() the difference is, it prints to any stream defined by
* the corresponding argument.
*/
void nv_info_msg_to_file(FILE *stream, const char *prefix, const char *fmt, ...)
{
if (__verbosity < NV_VERBOSITY_ALL) return;
NV_FORMAT(stream, prefix, fmt, TRUE);
} /* nv_info_msg_to_file() */
/*
* nv_msg() - print a message, nicely formatted using the format()
* function.
*
* This function should be used to display messages independent
* of the verbosity level.
*/
void nv_msg(const char *prefix, const char *fmt, ...)
{
NV_FORMAT(stdout, prefix, fmt, TRUE);
} /* nv_msg() */
/*
* nv_msg_preserve_whitespace() - Prints the message, just like nv_msg()
* using format(), the difference is, whitespace characters are not
* skipped during the text processing.
*/
void nv_msg_preserve_whitespace(const char *prefix, const char *fmt, ...)
{
NV_FORMAT(stdout, prefix, fmt, FALSE);
} /* nv_msg_preserve_whitespace() */
/*
* XXX gcc's '-ansi' option causes vsnprintf to not be defined, so
* declare the prototype here.
*/
#if defined(__STRICT_ANSI__)
int vsnprintf(char *str, size_t size, const char *format,
va_list ap);
#endif
/****************************************************************************/
/* TextRows helper functions */
/****************************************************************************/
/*
* nv_format_text_rows() - this function breaks the given string str
* into some number of rows, where each row is not longer than the
* specified width.
*
* If prefix is non-NULL, the first line is prepended with the prefix,
* and subsequent lines are indented to line up with the prefix.
*
* If word_boundary is TRUE, then attempt to only break lines on
* boundaries between words.
*/
TextRows *nv_format_text_rows(const char *prefix, const char *str, int width,
int word_boundary)
{
int len, prefix_len, z, w, i;
char *line, *buf, *local_prefix, *a, *b, *c;
TextRows *t;
/* initialize the TextRows structure */
t = (TextRows *) malloc(sizeof(TextRows));
if (!t) return NULL;
t->t = NULL;
t->n = 0;
t->m = 0;
if (!str) return t;
buf = strdup(str);
if (!buf) return t;
z = strlen(buf); /* length of entire string */
a = buf; /* pointer to the start of the string */
/* initialize the prefix fields */
if (prefix) {
prefix_len = strlen(prefix);
local_prefix = strdup(prefix);
} else {
prefix_len = 0;
local_prefix = NULL;
}
/* adjust the max width for any prefix */
w = width - prefix_len;
do {
/*
* if the string will fit on one line, point b to the end of the
* string
*/
if (z < w) b = a + z;
/*
* if the string won't fit on one line, move b to where the
* end of the line should be, and then move b back until we
* find a space; if we don't find a space before we back b all
* the way up to a, just assign b to where the line should end.
*/
else {
b = a + w;
if (word_boundary) {
while ((b >= a) && (!isspace(*b))) b--;
if (b <= a) b = a + w;
}
}
/* look for any newline between a and b, and move b to it */
for (c = a; c < b; c++) if (*c == '\n') { b = c; break; }
/*
* copy the string that starts at a and ends at b, prepending
* with a prefix, if present
*/
len = b-a;
len += prefix_len;
line = (char *) malloc(len+1);
if (local_prefix) strncpy(line, local_prefix, prefix_len);
strncpy(line + prefix_len, a, len - prefix_len);
line[len] = '\0';
/* append the new line to the array of text rows */
t->t = (char **) realloc(t->t, sizeof(char *) * (t->n + 1));
t->t[t->n] = line;
t->n++;
if (t->m < len) t->m = len;
/*
* adjust the length of the string and move the pointer to the
* beginning of the new line
*/
z -= (b - a + 1);
a = b + 1;
/* move to the first non whitespace character (excluding newlines) */
if (word_boundary && isspace(*b)) {
while ((z) && (isspace(*a)) && (*a != '\n')) a++, z--;
} else {
if (!isspace(*b)) z++, a--;
}
if (local_prefix) {
for (i = 0; i < prefix_len; i++) local_prefix[i] = ' ';
}
} while (z > 0);
if (local_prefix) free(local_prefix);
free(buf);
return t;
}
/*
* nv_text_rows_append() - append the given msg to the existing TextRows
*/
void nv_text_rows_append(TextRows *t, const char *msg)
{
int len;
t->t = realloc(t->t, sizeof(char *) * (t->n + 1));
if (msg) {
t->t[t->n] = strdup(msg);
len = strlen(msg);
if (t->m < len) t->m = len;
} else {
t->t[t->n] = NULL;
}
t->n++;
}
/*
* nv_concat_text_rows() - concatenate two text rows, storing the
* result in t0
*/
void nv_concat_text_rows(TextRows *t0, TextRows *t1)
{
int n, i;
n = t0->n + t1->n;
t0->t = realloc(t0->t, sizeof(char *) * n);
for (i = 0; i < t1->n; i++) {
t0->t[i + t0->n] = strdup(t1->t[i]);
}
t0->m = NV_MAX(t0->m, t1->m);
t0->n = n;
} /* nv_concat_text_rows() */
/*
* nv_free_text_rows() - free the TextRows data structure allocated by
* nv_format_text_rows()
*/
void nv_free_text_rows(TextRows *t)
{
int i;
if (!t) return;
for (i = 0; i < t->n; i++) free(t->t[i]);
if (t->t) free(t->t);
free(t);
} /* nv_free_text_rows() */