summaryrefslogtreecommitdiff
path: root/src/libeinfo/libeinfo.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2008-01-05 19:25:55 +0000
committerRoy Marples <roy@marples.name>2008-01-05 19:25:55 +0000
commitac21d75300dabe83578e4373fcfd09d67c3a083b (patch)
treed5f8e2a16920add2277c79ff8a1b7e99ec2976df /src/libeinfo/libeinfo.c
parent112fbde453d55c49b7999d2e35496a8758aaa7b5 (diff)
downloadopenrc-ac21d75300dabe83578e4373fcfd09d67c3a083b.tar.gz
openrc-ac21d75300dabe83578e4373fcfd09d67c3a083b.tar.bz2
openrc-ac21d75300dabe83578e4373fcfd09d67c3a083b.tar.xz
Add some .mk stubs to impersonate bsk .mk files to make writing our Makefiles easier. libeinfo, librc and rc now have their own seperate directories. More work is needed to tidy this up though.
Diffstat (limited to 'src/libeinfo/libeinfo.c')
-rw-r--r--src/libeinfo/libeinfo.c1060
1 files changed, 1060 insertions, 0 deletions
diff --git a/src/libeinfo/libeinfo.c b/src/libeinfo/libeinfo.c
new file mode 100644
index 0000000..d80af9c
--- /dev/null
+++ b/src/libeinfo/libeinfo.c
@@ -0,0 +1,1060 @@
+/*
+ einfo.c
+ Informational functions
+ */
+
+/*
+ * Copyright 2007-2008 Roy Marples
+ * All rights reserved
+
+ * 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 <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 "rc.h"
+#include "rc-misc.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"
+
+/* 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 {
+ einfo_color_t color;
+ int def;
+ const char *name;
+ char *str;
+};
+static char nullstr = '\0';
+
+static struct ecolor ecolors[] = {
+ { ECOLOR_GOOD, GOOD, "good", NULL },
+ { ECOLOR_WARN, WARN, "warn", NULL },
+ { ECOLOR_BAD, BAD, "bad", NULL },
+ { ECOLOR_HILITE, HILITE, "hilite", NULL },
+ { ECOLOR_BRACKET, BRACKET, "bracket", NULL },
+ { ECOLOR_NORMAL, 0, NULL, NULL },
+};
+
+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 */
+static const char *const color_terms[] = {
+ "Eterm",
+ "ansi",
+ "color-xterm",
+ "con132x25",
+ "con132x30",
+ "con132x43",
+ "con132x60",
+ "con80x25",
+ "con80x28",
+ "con80x30",
+ "con80x43",
+ "con80x50",
+ "con80x60",
+ "cons25",
+ "console",
+ "cygwin",
+ "dtterm",
+ "gnome",
+ "konsole",
+ "kterm",
+ "linux",
+ "linux-c",
+ "mach-color",
+ "mlterm",
+ "putty",
+ "rxvt",
+ "rxvt-cygwin",
+ "rxvt-cygwin-native",
+ "rxvt-unicode",
+ "screen",
+ "screen-bce",
+ "screen-w",
+ "screen.linux",
+ "vt100",
+ "xterm",
+ "xterm-256color",
+ "xterm-color",
+ "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));
+}
+
+static size_t strlcpy (char *dst, const char *src, size_t size)
+{
+ const char *s = src;
+ size_t n = size;
+
+ if (n && --n)
+ do {
+ if (! (*dst++ = *src++))
+ break;
+ } while (--n);
+
+ if (! n) {
+ if (size)
+ *dst = '\0';
+ while (*src++);
+ }
+
+ return (src - s - 1);
+}
+# 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()
+{
+ return (yesno (getenv ("EINFO_QUIET")));
+}
+
+static bool is_verbose()
+{
+ 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 a, int b)
+{
+ static char buf[20];
+
+ snprintf (buf, sizeof (buf), cap, b, a);
+ return (buf);
+}
+#endif
+
+static bool colour_terminal (FILE * __EINFO_RESTRICT f)
+{
+ static int in_colour = -1;
+ char *e;
+ int c;
+ const char *_af = NULL;
+ const char *_ce = NULL;
+ const char *_ch = NULL;
+ const char *_md = NULL;
+ const char *_me = NULL;
+ const char *_up = NULL;
+ char tmp[100];
+ char *p;
+ unsigned int i = 0;
+
+ 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) {
+ char *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) {
+#else
+ while (color_terms[i]) {
+ 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 || ! _ch || ! _me || !_md || ! _up) {
+ in_colour = 0;
+ return (false);
+ }
+#endif
+
+#define _GET_CAP(_d, _c) strlcpy (_d, tgoto (_c, 0, 0), sizeof (_d));
+#define _ASSIGN_CAP(_v) { \
+ _v = p; \
+ p += strlcpy (p, tmp, sizeof (ebuffer) - (p - ebuffer)) + 1; \
+}
+
+ /* Now setup our colours */
+ p = ebuffer;
+ for (i = 0; i < sizeof (ecolors) / sizeof (ecolors[0]); i++) {
+ tmp[0] = '\0';
+
+ if (ecolors[i].name) {
+ const char *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"))) {
+ char *ee = strstr (e, ecolors[i].name);
+
+ if (ee)
+ ee += strlen (ecolors[i].name);
+
+ if (ee && *ee == '=') {
+
+ char *d = xstrdup (ee + 1);
+ char *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[i].str)
+ else
+ ecolors[i].str = &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 = strtol (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 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 = strtol (env, NULL, 0);
+ if (errno != 0 || amount < 0)
+ amount = 0;
+ else if (amount > INDENT_MAX)
+ amount = INDENT_MAX;
+
+ if (amount > 0)
+ memset (indent, ' ', amount);
+ }
+
+ /* Terminate it */
+ memset (indent + amount, 0, 1);
+ return (fprintf (stream, "%s", indent));
+}
+
+static const char *_ecolor (FILE * __EINFO_RESTRICT f, einfo_color_t color)
+{
+ unsigned int i;
+
+ if (! colour_terminal (f))
+ return ("");
+
+ for (i = 0; i < sizeof (ecolors) / sizeof (ecolors[0]); i++) {
+ if (ecolors[i].color == color)
+ return (ecolors[i].str);
+ }
+
+ return ("");
+}
+hidden_def(ecolor)
+
+const char *ecolor (einfo_color_t 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);
+
+#define EINFOVN(_file, _color) \
+{ \
+ char *_e = getenv ("EINFO_LASTCMD"); \
+ if (_e && ! colour_terminal (_file) && strcmp (_e, "ewarn") != 0 && \
+ _e[strlen (_e) - 1] == 'n') \
+ fprintf (_file, "\n"); \
+ if (_eprefix) \
+ fprintf (_file, "%s%s%s|", _ecolor (_file, _color), _eprefix, _ecolor (_file, ECOLOR_NORMAL)); \
+ fprintf (_file, " %s*%s ", _ecolor (_file, _color), _ecolor (_file, ECOLOR_NORMAL)); \
+ retval += _eindent (_file); \
+{ \
+ va_list _ap; \
+ va_copy (_ap, ap); \
+ retval += vfprintf (_file, fmt, _ap) + 3; \
+ va_end (_ap); \
+} \
+ if (colour_terminal (_file)) \
+ fprintf (_file, "%s", flush); \
+}
+
+static int _einfovn (const char *__EINFO_RESTRICT fmt, va_list ap)
+{
+ int retval = 0;
+
+ EINFOVN (stdout, ECOLOR_GOOD);
+ return (retval);
+}
+
+static int _ewarnvn (const char *__EINFO_RESTRICT fmt, va_list ap)
+{
+ int retval = 0;
+
+ EINFOVN (stdout, ECOLOR_WARN);
+ return (retval);
+}
+
+static int _eerrorvn (const char *__EINFO_RESTRICT fmt, va_list ap)
+{
+ int retval = 0;
+
+ EINFOVN (stderr, ECOLOR_BAD);
+ return (retval);
+}
+
+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;
+
+ 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 += printf ("\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 += printf ("\n");
+ }
+ exit (EXIT_FAILURE);
+}
+hidden_def(ewarnx)
+
+int eerror (const char *__EINFO_RESTRICT fmt, ...)
+{
+ int retval;
+ va_list ap;
+
+ if (! fmt)
+ 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) {
+ 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, einfo_color_t color,
+ const char *msg)
+{
+ int i;
+ int cols;
+
+ if (! msg)
+ return;
+
+ cols = get_term_columns (fp) - (strlen (msg) + 3);
+
+ /* 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 _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 && retval != 0) {
+ va_copy (apc, ap);
+ if (strcmp (cmd, "ewend") == 0) {
+ col = _ewarnvn (fmt, apc);
+ fp = stdout;
+ } else {
+ col = _eerrorvn (fmt, apc);
+ fp = stderr;
+ }
+ 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, einfo_color_t 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 = strtol (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];
+
+ if (! env)
+ return;
+
+ errno = 0;
+ amount = strtol (env, NULL, 0);
+ if (errno != 0)
+ amount = 0;
+ else
+ amount -= INDENT_WIDTH;
+
+ if (amount <= 0)
+ unsetenv ("EINFO_EINDENT");
+ else {
+ snprintf (num, 10, "%08d", amount);
+ setenv ("EINFO_EINDENT", num, 1);
+ }
+}
+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)