diff options
Diffstat (limited to 'src/libeinfo')
-rw-r--r-- | src/libeinfo/.gitignore | 1 | ||||
-rw-r--r-- | src/libeinfo/Makefile | 12 | ||||
-rw-r--r-- | src/libeinfo/einfo.h | 144 | ||||
-rw-r--r-- | src/libeinfo/einfo.map | 35 | ||||
-rw-r--r-- | src/libeinfo/libeinfo.c | 1070 |
5 files changed, 1262 insertions, 0 deletions
diff --git a/src/libeinfo/.gitignore b/src/libeinfo/.gitignore new file mode 100644 index 00000000..373284db --- /dev/null +++ b/src/libeinfo/.gitignore @@ -0,0 +1 @@ +libeinfo.so.1 diff --git a/src/libeinfo/Makefile b/src/libeinfo/Makefile new file mode 100644 index 00000000..ec756b47 --- /dev/null +++ b/src/libeinfo/Makefile @@ -0,0 +1,12 @@ +LIB= einfo +SHLIB_MAJOR= 1 +SRCS= libeinfo.c +INCS= einfo.h +VERSION_MAP= einfo.map + +CPPFLAGS+= -I../includes + +MK= ../../mk +include ${MK}/lib.mk +include ${MK}/cc.mk +include ${MK}/termcap.mk diff --git a/src/libeinfo/einfo.h b/src/libeinfo/einfo.h new file mode 100644 index 00000000..31a891f8 --- /dev/null +++ b/src/libeinfo/einfo.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __EINFO_H__ +#define __EINFO_H__ + +#if defined(__GNUC__) +# define EINFO_PRINTF(a, b) __attribute__((__format__(__printf__, a, b))) +# define EINFO_XPRINTF(a, b) __attribute__((__noreturn__,__format__(__printf__, a, b))) +#else +# define EINFO_PRINTF(a, b) +# define EINFO_XPRINTF(a, b) +#endif + +#include <sys/types.h> +#include <stdbool.h> + +/* Although OpenRC requires C99, linking to us should not. */ +#ifdef restrict +# define EINFO_RESTRICT restrict +#else +# ifdef __restrict +# define EINFO_RESTRICT __restrict +# else +# define EINFO_RESTRICT +# endif +#endif + +__BEGIN_DECLS + +/*! @brief Color types to use */ +typedef enum +{ + ECOLOR_NORMAL = 1, + ECOLOR_GOOD = 2, + ECOLOR_WARN = 3, + ECOLOR_BAD = 4, + ECOLOR_HILITE = 5, + ECOLOR_BRACKET = 6 +} ECOLOR; + +/*! @brief Returns the ASCII code for the color */ +const char *ecolor(ECOLOR); + +/*! @brief Writes to syslog. */ +void elog(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); + +/*! + * @brief Display informational messages. + * + * The einfo family of functions display messages in a consistent manner + * across applications. Basically they prefix the message with + * " * ". If the terminal can handle color then we color the * based on + * the command used. Otherwise we are identical to the printf function. + * + * - einfo - green + * - ewarn - yellow + * - eerror - red + * + * The n suffix denotes that no new line should be printed. + * The v suffix means only print if EINFO_VERBOSE is yes. + */ +/*@{*/ +int einfon(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarnn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int eerrorn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int einfo(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +void ewarnx(const char * __EINFO_RESTRICT, ...) EINFO_XPRINTF(1, 2); +int eerror(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +void eerrorx(const char * __EINFO_RESTRICT, ...) EINFO_XPRINTF(1, 2); + +int einfovn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarnvn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ebeginvn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int eendvn(int, const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int ewendvn(int, const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int einfov(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarnv(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +/*@}*/ + +/*! @ingroup ebegin + * @brief Display informational messages that may take some time. + * + * Similar to einfo, but we add ... to the end of the message */ +/*@{*/ +int ebeginv(const char * EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ebegin(const char * EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +/*@}*/ + +/*! @ingroup eend + * @brief End an ebegin. + * + * If you ebegin, you should eend also. + * eend places [ ok ] or [ !! ] at the end of the terminal line depending on + * retval (0 or ok, anything else for !!) + * + * ebracket allows you to specifiy the position, color and message */ +/*@{*/ +int eend(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int ewend(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +void ebracket(int, ECOLOR, const char * EINFO_RESTRICT); + +int eendv(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int ewendv(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +/*@}*/ + +/*! @ingroup eindent + * @brief Indents the einfo lines. + * + * For each indent you should outdent when done */ +/*@{*/ +void eindent(void); +void eoutdent(void); +void eindentv(void); +void eoutdentv(void); + +/*! @brief Prefix each einfo line with something */ +void eprefix(const char * EINFO_RESTRICT); + +__END_DECLS +#endif diff --git a/src/libeinfo/einfo.map b/src/libeinfo/einfo.map new file mode 100644 index 00000000..428a8954 --- /dev/null +++ b/src/libeinfo/einfo.map @@ -0,0 +1,35 @@ +EINFO_1.0 { +global: + ecolor; + elog; + einfon; + ewarnn; + eerrorn; + einfo; + ewarn; + ewarnx; + eerror; + eerrorx; + einfovn; + ewarnvn; + ebeginvn; + eendvn; + ewendvn; + einfov; + ewarnv; + ebeginv; + ebegin; + eend; + ewend; + ebracket; + eendv; + ewendv; + eindent; + eoutdent; + eindentv; + eoutdentv; + eprefix; + +local: + *; +}; diff --git a/src/libeinfo/libeinfo.c b/src/libeinfo/libeinfo.c new file mode 100644 index 00000000..9791051f --- /dev/null +++ b/src/libeinfo/libeinfo.c @@ -0,0 +1,1070 @@ +/* + einfo.c + Informational functions +*/ + +/* + * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +const char libeinfo_copyright[] = "Copyright (c) 2007-2008 Roy Marples"; + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <syslog.h> +#ifdef HAVE_TERMCAP +# include <termcap.h> +#endif +#include <unistd.h> + +#include "einfo.h" +#include "helpers.h" +#include "hidden-visibility.h" + +hidden_proto(ecolor) +hidden_proto(ebegin) +hidden_proto(ebeginv) +hidden_proto(ebracket) +hidden_proto(eend) +hidden_proto(eendv) +hidden_proto(eerror) +hidden_proto(eerrorn) +hidden_proto(eerrorx) +hidden_proto(eindent) +hidden_proto(eindentv) +hidden_proto(einfo) +hidden_proto(einfon) +hidden_proto(einfov) +hidden_proto(einfovn) +hidden_proto(elog) +hidden_proto(eoutdent) +hidden_proto(eoutdentv) +hidden_proto(eprefix) +hidden_proto(ewarn) +hidden_proto(ewarnn) +hidden_proto(ewarnv) +hidden_proto(ewarnvn) +hidden_proto(ewarnx) +hidden_proto(ewend) +hidden_proto(ewendv) + +/* Incase we cannot work out how many columns from ioctl, supply a default */ +#define DEFAULT_COLS 80 + +#define OK "ok" +#define NOT_OK "!!" + +/* Number of spaces for an indent */ +#define INDENT_WIDTH 2 + +/* How wide can the indent go? */ +#define INDENT_MAX 40 + +/* Default colours */ +#define GOOD 2 +#define WARN 3 +#define BAD 1 +#define HILITE 6 +#define BRACKET 4 + +/* We fallback to these escape codes if termcap isn't available + * like say /usr isn't mounted */ +#define AF "\033[3%dm" +#define CE "\033[K" +#define CH "\033[%dC" +#define MD "\033[1m" +#define ME "\033[m" +#define UP "\033[A" + +#define _GET_CAP(_d, _c) strlcpy(_d, tgoto(_c, 0, 0), sizeof(_d)); +#define _ASSIGN_CAP(_v) do { \ + _v = p; \ + p += strlcpy(p, tmp, sizeof(ebuffer) - (p - ebuffer)) + 1; \ + } while (0) + +/* A pointer to a string to prefix to einfo/ewarn/eerror messages */ +static const char *_eprefix = NULL; + +/* Buffers and structures to hold the final colours */ +static char ebuffer[100]; +struct ecolor { + ECOLOR color; + int def; + const char *name; +}; +static char nullstr = '\0'; + +static const struct ecolor ecolors[] = { + { ECOLOR_GOOD, GOOD, "good" }, + { ECOLOR_WARN, WARN, "warn" }, + { ECOLOR_BAD, BAD, "bad" }, + { ECOLOR_HILITE, HILITE, "hilite" }, + { ECOLOR_BRACKET, BRACKET, "bracket" }, + { ECOLOR_NORMAL, 0, NULL }, +}; +static const char *ecolors_str[ARRAY_SIZE(ecolors)]; + +static char *flush = NULL; +static char *up = NULL; +static char *goto_column = NULL; + +static const char *term = NULL; +static bool term_is_cons25 = false; + +/* Termcap buffers and pointers + * Static buffers suck hard, but some termcap implementations require them */ +#ifdef HAVE_TERMCAP +static char termcapbuf[2048]; +static char tcapbuf[512]; +#else +/* No curses support, so we hardcode a list of colour capable terms + * Only terminals without "color" in the name need to be explicitly listed */ +static const char *const color_terms[] = { + "Eterm", + "ansi", + "con132x25", + "con132x30", + "con132x43", + "con132x60", + "con80x25", + "con80x28", + "con80x30", + "con80x43", + "con80x50", + "con80x60", + "cons25", + "console", + "cygwin", + "dtterm", + "gnome", + "konsole", + "kterm", + "linux", + "linux-c", + "mlterm", + "putty", + "rxvt", + "rxvt-cygwin", + "rxvt-cygwin-native", + "rxvt-unicode", + "screen", + "screen-bce", + "screen-w", + "screen.linux", + "vt100", + "vt220", + "wsvt25", + "xterm", + "xterm-debian", + NULL +}; +#endif + +/* strlcat and strlcpy are nice, shame glibc does not define them */ +#ifdef __GLIBC__ +# if ! defined (__UCLIBC__) && ! defined (__dietlibc__) +static size_t +strlcat(char *dst, const char *src, size_t size) +{ + char *d = dst; + const char *s = src; + size_t src_n = size; + size_t dst_n; + + while (src_n-- != 0 && *d != '\0') + d++; + dst_n = d - dst; + src_n = size - dst_n; + + if (src_n == 0) + return dst_n + strlen(src); + + while (*s != '\0') { + if (src_n != 1) { + *d++ = *s; + src_n--; + } + s++; + } + *d = '\0'; + + return dst_n + (s - src); +} +# endif +#endif + +static bool +yesno(const char *value) +{ + if (!value) { + errno = ENOENT; + return false; + } + + if (strcasecmp(value, "yes") == 0 || + strcasecmp(value, "y") == 0 || + strcasecmp(value, "true") == 0 || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "1") == 0) + return true; + + if (strcasecmp(value, "no") != 0 && + strcasecmp(value, "n") != 0 && + strcasecmp(value, "false") != 0 && + strcasecmp(value, "off") != 0 && + strcasecmp(value, "0") != 0) + errno = EINVAL; + + return false; +} + +static bool +noyes(const char *value) +{ + int serrno = errno; + bool retval; + + errno = 0; + retval = yesno(value); + if (errno == 0) { + retval = !retval; + errno = serrno; + } + + return retval; +} + +static bool +is_quiet(void) +{ + return yesno(getenv("EINFO_QUIET")); +} + +static bool +is_really_quiet(void) +{ + return yesno(getenv("EERROR_QUIET")); +} + +static bool +is_verbose(void) +{ + return yesno(getenv ("EINFO_VERBOSE")); +} + +/* Fake tgoto call - very crapy, but works for our needs */ +#ifndef HAVE_TERMCAP +static char * +tgoto(const char *cap, int col, int line) +{ + static char buf[20]; + char *p, *e, c, dbuf[6]; + int oncol = 0, which = line, i; + + p = buf; + e = p + sizeof(buf); + while ((c = *cap++)) { + if (c != '%' || ((c = *cap++) == '%')) { + *p++ = c; + if (p >= e) { + errno = E2BIG; + return NULL; + } + continue; + } + switch (c) { + case '3': + case '2': + case 'd': + i = 0; + do + dbuf[i++] = which % 10 | '0'; + while ((which /= 10)); + if (c != 'd') { + c -= '0'; + if (i > c) { + errno = EINVAL; + return NULL; + } + while (i < c) + dbuf[i++] = '0'; + } + if (p + i >= e) { + errno = E2BIG; + return NULL; + } + do + *p++ = dbuf[--i]; + while (i); + break; + case 'r': + oncol = 0; + break; + case 'i': + col++; + line++; + which++; + continue; + default: + errno = EINVAL; + return NULL; + } + + oncol = 1 - oncol; + which = oncol ? col : line; + } + *p = '\0'; + return buf; +} +#endif + +static bool +colour_terminal(FILE * EINFO_RESTRICT f) +{ + static int in_colour = -1; + char *e, *ee, *end, *d, *p; + int c; + const char *_af = NULL, *_ce = NULL, *_ch = NULL; + const char *_md = NULL, *_me = NULL, *_up = NULL; + const char *bold; + char tmp[100]; + unsigned int i = 0; +#ifdef HAVE_TERMCAP + char *bp; +#endif + + if (f && !isatty(fileno(f))) + return false; + + if (noyes(getenv("EINFO_COLOR"))) + return false; + + if (in_colour == 0) + return false; + if (in_colour == 1) + return true; + + term_is_cons25 = false; + if (!term) { + term = getenv("TERM"); + if (!term) + return false; + } + if (strcmp(term, "cons25") == 0) + term_is_cons25 = true; + +#ifdef HAVE_TERMCAP + /* Check termcap to see if we can do colour or not */ + if (tgetent(termcapbuf, term) == 1) { + bp = tcapbuf; + _af = tgetstr("AF", &bp); + _ce = tgetstr("ce", &bp); + _ch = tgetstr("ch", &bp); + /* Our ch use also works with RI .... for now */ + if (!_ch) + _ch = tgetstr("RI", &bp); + _md = tgetstr("md", &bp); + _me = tgetstr("me", &bp); + _up = tgetstr("up", &bp); + } + + /* Cheat here as vanilla BSD has the whole termcap info in /usr + * which is not available to us when we boot */ + if (term_is_cons25 || strcmp(term, "wsvt25") == 0) { +#else + if (strstr(term, "color")) + in_colour = 1; + + while (color_terms[i] && in_colour != 1) { + if (strcmp(color_terms[i], term) == 0) { + in_colour = 1; + } + i++; + } + + if (in_colour != 1) { + in_colour = 0; + return false; + } +#endif + if (!_af) + _af = AF; + if (!_ce) + _ce = CE; + if (!_ch) + _ch = CH; + if (!_md) + _md = MD; + if (!_me) + _me = ME; + if (!_up) + _up = UP; +#ifdef HAVE_TERMCAP + } + + if (!_af || !_ce || !_me || !_md || !_up) { + in_colour = 0; + return false; + } + + /* Many termcap databases don't have ch or RI even though they + * do work */ + if (!_ch) + _ch = CH; +#endif + + /* Now setup our colours */ + p = ebuffer; + for (i = 0; i < ARRAY_SIZE(ecolors); ++i) { + tmp[0] = '\0'; + if (ecolors[i].name) { + bold = _md; + c = ecolors[i].def; + + /* See if the user wants to override the colour + * We use a :col;bold: format like 2;1: for bold green + * and 1;0: for a normal red */ + if ((e = getenv("EINFO_COLOR"))) { + ee = strstr(e, ecolors[i].name); + if (ee) + ee += strlen(ecolors[i].name); + + if (ee && *ee == '=') { + d = strdup(ee + 1); + if (d) { + end = strchr(d, ':'); + if (end) + *end = '\0'; + c = atoi(d); + end = strchr(d, ';'); + if (end && *++end == '0') + bold = _me; + free(d); + } + } + } + strlcpy(tmp, tgoto(bold, 0, 0), sizeof(tmp)); + strlcat(tmp, tgoto(_af, 0, c & 0x07), sizeof(tmp)); + } else + _GET_CAP(tmp, _me); + + if (tmp[0]) + _ASSIGN_CAP(ecolors_str[i]); + else + ecolors_str[i] = &nullstr; + } + + _GET_CAP(tmp, _ce); + _ASSIGN_CAP(flush); + _GET_CAP(tmp, _up); + _ASSIGN_CAP(up); + strlcpy(tmp, _ch, sizeof(tmp)); + _ASSIGN_CAP(goto_column); + + in_colour = 1; + return true; +} + +static int +get_term_columns(FILE * EINFO_RESTRICT stream) +{ + struct winsize ws; + char *env = getenv("COLUMNS"); + char *p; + int i; + + if (env) { + i = strtoimax(env, &p, 10); + if (!*p) + return i; + } + + if (ioctl(fileno(stream), TIOCGWINSZ, &ws) == 0) + return ws.ws_col; + + return DEFAULT_COLS; +} + +void +eprefix(const char *EINFO_RESTRICT prefix) +{ + _eprefix = prefix; +} +hidden_def(eprefix) + +static void EINFO_PRINTF(2, 0) +elogv(int level, const char *EINFO_RESTRICT fmt, va_list ap) +{ + char *e = getenv("EINFO_LOG"); + va_list apc; + + if (fmt && e) { + closelog(); + openlog(e, LOG_PID, LOG_DAEMON); + va_copy(apc, ap); + vsyslog(level, fmt, apc); + va_end(apc); + closelog(); + } +} + +void +elog(int level, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + elogv(level, fmt, ap); + va_end(ap); +} +hidden_def(elog) + +static int +_eindent(FILE * EINFO_RESTRICT stream) +{ + char *env = getenv("EINFO_INDENT"); + int amount = 0; + char indent[INDENT_MAX]; + + if (env) { + errno = 0; + amount = strtoimax(env, NULL, 0); + if (errno != 0 || amount < 0) + amount = 0; + else if (amount > INDENT_MAX) + amount = INDENT_MAX; + + if (amount > 0) + memset(indent, ' ', (size_t)amount); + } + + /* Terminate it */ + memset(indent + amount, 0, 1); + return fprintf(stream, "%s", indent); +} + +static const char * +_ecolor(FILE * EINFO_RESTRICT f, ECOLOR color) +{ + unsigned int i; + + if (!colour_terminal(f)) + return ""; + + for (i = 0; i < ARRAY_SIZE(ecolors); ++i) + if (ecolors[i].color == color) + return ecolors_str[i]; + return ""; +} +hidden_def(ecolor) + +const char * +ecolor(ECOLOR color) +{ + FILE *f = stdout; + + /* Try and guess a valid tty */ + if (!isatty(fileno(f))) { + f = stderr; + if (!isatty(fileno(f))) { + f = stdin; + if (!isatty(fileno(f))) + f = NULL; + } + } + + return _ecolor(f, color); +} + +#define LASTCMD(_cmd) { \ + unsetenv("EINFO_LASTCMD"); \ + setenv("EINFO_LASTCMD", _cmd, 1); \ + } + +static int EINFO_PRINTF(3, 0) + _einfo(FILE *f, ECOLOR color, const char *EINFO_RESTRICT fmt, va_list va) +{ + int retval = 0; + char *last = getenv("EINFO_LASTCMD"); + va_list ap; + + if (last && + !colour_terminal(f) && + strcmp(last, "ewarn") != 0 && + last[strlen(last) - 1] == 'n') + fprintf(f, "\n"); + if (_eprefix) + fprintf(f, "%s%s%s|", _ecolor(f, color), _eprefix, _ecolor(f, ECOLOR_NORMAL)); + fprintf(f, " %s*%s ", _ecolor(f, color), _ecolor(f, ECOLOR_NORMAL)); + retval += _eindent(f); + va_copy(ap, va); + retval += vfprintf(f, fmt, ap) + 3; + va_end(ap); \ + if (colour_terminal(f)) + fprintf(f, "%s", flush); + return retval; +} + +#define _einfovn(fmt, ap) _einfo(stdout, ECOLOR_GOOD, fmt, ap) +#define _ewarnvn(fmt, ap) _einfo(stderr, ECOLOR_WARN, fmt, ap) +#define _eerrorvn(fmt, ap) _einfo(stderr, ECOLOR_BAD, fmt, ap) + +int +einfon(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + va_end(ap); + LASTCMD("einfon"); + return retval; +} +hidden_def(einfon) + +int +ewarnn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _ewarnvn(fmt, ap); + va_end(ap); + LASTCMD("ewarnn"); + return retval; +} +hidden_def(ewarnn) + +int +eerrorn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_really_quiet()) + return 0; + va_start(ap, fmt); + retval = _eerrorvn(fmt, ap); + va_end(ap); + LASTCMD("errorn"); + return retval; +} +hidden_def(eerrorn) + +int +einfo(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + retval += printf("\n"); + va_end(ap); + LASTCMD("einfo"); + return retval; +} +hidden_def(einfo) + +int +ewarn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + elogv(LOG_WARNING, fmt, ap); + retval = _ewarnvn(fmt, ap); + retval += fprintf(stderr, "\n"); + va_end(ap); + LASTCMD("ewarn"); + return retval; +} +hidden_def(ewarn) + +void +ewarnx(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (fmt && !is_quiet()) { + va_start(ap, fmt); + elogv(LOG_WARNING, fmt, ap); + retval = _ewarnvn(fmt, ap); + va_end(ap); + retval += fprintf(stderr, "\n"); + } + exit(EXIT_FAILURE); +} +hidden_def(ewarnx) + +int +eerror(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_really_quiet()) + return 0; + va_start(ap, fmt); + elogv(LOG_ERR, fmt, ap); + retval = _eerrorvn(fmt, ap); + va_end(ap); + retval += fprintf(stderr, "\n"); + LASTCMD("eerror"); + return retval; +} +hidden_def(eerror) + +void +eerrorx(const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (fmt && !is_really_quiet()) { + va_start(ap, fmt); + elogv(LOG_ERR, fmt, ap); + _eerrorvn(fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } + exit(EXIT_FAILURE); +} +hidden_def(eerrorx) + +int +ebegin(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + va_end(ap); + retval += printf(" ..."); + if (colour_terminal(stdout)) + retval += printf("\n"); + LASTCMD("ebegin"); + return retval; +} +hidden_def(ebegin) + +static void +_eend(FILE * EINFO_RESTRICT fp, int col, ECOLOR color, const char *msg) +{ + int i; + int cols; + + if (!msg) + return; + + cols = get_term_columns(fp) - (strlen(msg) + 5); + + /* cons25 is special - we need to remove one char, otherwise things + * do not align properly at all. */ + if (!term) { + term = getenv("TERM"); + if (term && strcmp(term, "cons25") == 0) + term_is_cons25 = true; + else + term_is_cons25 = false; + } + if (term_is_cons25) + cols--; + + if (cols > 0 && colour_terminal(fp)) { + fprintf(fp, "%s%s %s[%s %s %s]%s\n", up, tgoto(goto_column, 0, cols), + ecolor(ECOLOR_BRACKET), ecolor(color), msg, + ecolor(ECOLOR_BRACKET), ecolor(ECOLOR_NORMAL)); + } else { + if (col > 0) + for (i = 0; i < cols - col; i++) + fprintf(fp, " "); + fprintf(fp, " [ %s ]\n", msg); + } +} + +static int EINFO_PRINTF(3, 0) +_do_eend(const char *cmd, int retval, + const char *EINFO_RESTRICT fmt, va_list ap) +{ + int col = 0; + FILE *fp = stdout; + va_list apc; + + if (fmt && *fmt != '\0' && retval != 0) { + fp = stderr; + va_copy(apc, ap); + if (strcmp(cmd, "ewend") == 0) + col = _ewarnvn(fmt, apc); + else + col = _eerrorvn(fmt, apc); + col += fprintf(fp, "\n"); + va_end(apc); + } + _eend(fp, col, + retval == 0 ? ECOLOR_GOOD : ECOLOR_BAD, + retval == 0 ? OK : NOT_OK); + return retval; +} + +int +eend(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (is_quiet()) + return retval; + va_start(ap, fmt); + _do_eend("eend", retval, fmt, ap); + va_end(ap); + LASTCMD("eend"); + return retval; +} +hidden_def(eend) + +int +ewend(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (is_quiet()) + return retval; + va_start(ap, fmt); + _do_eend("ewend", retval, fmt, ap); + va_end(ap); + LASTCMD("ewend"); + return retval; +} +hidden_def(ewend) + +void +ebracket(int col, ECOLOR color, const char *msg) +{ + _eend(stdout, col, color, msg); +} +hidden_def(ebracket) + +void +eindent(void) +{ + char *env = getenv("EINFO_INDENT"); + int amount = 0; + char num[10]; + + if (env) { + errno = 0; + amount = strtoimax(env, NULL, 0); + if (errno != 0) + amount = 0; + } + amount += INDENT_WIDTH; + if (amount > INDENT_MAX) + amount = INDENT_MAX; + snprintf(num, 10, "%08d", amount); + setenv("EINFO_INDENT", num, 1); +} +hidden_def(eindent) + +void eoutdent(void) +{ + char *env = getenv("EINFO_INDENT"); + int amount = 0; + char num[10]; + int serrno = errno; + + if (!env) + return; + errno = 0; + amount = strtoimax(env, NULL, 0); + if (errno != 0) + amount = 0; + else + amount -= INDENT_WIDTH; + if (amount <= 0) + unsetenv("EINFO_INDENT"); + else { + snprintf(num, 10, "%08d", amount); + setenv("EINFO_INDENT", num, 1); + } + errno = serrno; +} +hidden_def(eoutdent) + +int +einfovn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + va_end(ap); + LASTCMD("einfovn"); + return retval; +} +hidden_def(einfovn) + +int +ewarnvn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _ewarnvn(fmt, ap); + va_end(ap); + LASTCMD("ewarnvn"); + return retval; +} +hidden_def(ewarnvn) + +int +einfov(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + retval += printf("\n"); + va_end(ap); + LASTCMD("einfov"); + return retval; +} +hidden_def(einfov) + +int +ewarnv(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _ewarnvn(fmt, ap); + retval += printf("\n"); + va_end(ap); + LASTCMD("ewarnv"); + return retval; +} +hidden_def(ewarnv) + +int +ebeginv(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + retval += printf(" ..."); + if (colour_terminal(stdout)) + retval += printf("\n"); + va_end(ap); + LASTCMD("ebeginv"); + return retval; +} +hidden_def(ebeginv) + +int +eendv(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (!is_verbose()) + return 0; + va_start(ap, fmt); + _do_eend("eendv", retval, fmt, ap); + va_end(ap); + LASTCMD("eendv"); + return retval; +} +hidden_def(eendv) + +int +ewendv(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (!is_verbose()) + return 0; + va_start(ap, fmt); + _do_eend("ewendv", retval, fmt, ap); + va_end(ap); + LASTCMD("ewendv"); + return retval; +} +hidden_def(ewendv) + +void +eindentv(void) +{ + if (is_verbose()) + eindent(); +} +hidden_def(eindentv) + +void +eoutdentv(void) +{ + if (is_verbose()) + eoutdent(); +} +hidden_def(eoutdentv) |