aboutsummaryrefslogtreecommitdiff
path: root/src/libeinfo
diff options
context:
space:
mode:
Diffstat (limited to 'src/libeinfo')
-rw-r--r--src/libeinfo/Makefile14
-rw-r--r--src/libeinfo/einfo.h144
-rw-r--r--src/libeinfo/einfo.map35
-rw-r--r--src/libeinfo/libeinfo.c1060
4 files changed, 1253 insertions, 0 deletions
diff --git a/src/libeinfo/Makefile b/src/libeinfo/Makefile
new file mode 100644
index 00000000..b6826340
--- /dev/null
+++ b/src/libeinfo/Makefile
@@ -0,0 +1,14 @@
+TOPDIR= ..
+include $(TOPDIR)/os.mk
+
+LIB= einfo
+SHLIB_MAJOR= 1
+SRCS= libeinfo.c
+INCS= einfo.h
+
+SHLIBDIR= /${LIBNAME}
+
+include $(TOPDIR)/cc.mk
+include $(TOPDIR)/lib.mk
+include $(TOPDIR)/$(TERMCAP).mk
+
diff --git a/src/libeinfo/einfo.h b/src/libeinfo/einfo.h
new file mode 100644
index 00000000..2720e458
--- /dev/null
+++ b/src/libeinfo/einfo.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2007 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.
+ */
+
+#ifndef __EINFO_H__
+#define __EINFO_H__
+
+#ifdef __GNUC__
+# define __EINFO_PRINTF __attribute__ ((__format__ (__printf__, 1, 2)))
+# define __EINFO_XPRINTF __attribute__ ((__noreturn__, __format__ (__printf__, 1, 2)))
+# define __EEND_PRINTF __attribute__ ((__format__ (__printf__, 2, 3)))
+#else
+# define __EINFO_PRINTF
+# define __EINFO_XPRINTF
+# define __EEND_PRINTF
+#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
+
+/*! @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
+} einfo_color_t;
+
+/*! @brief Returns the ASCII code for the color */
+const char *ecolor (einfo_color_t);
+
+/*! @brief Writes to syslog. */
+void elog (int __level, const char * __EINFO_RESTRICT __fmt, ...) __EEND_PRINTF;
+
+/*!
+ * @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 __fmt, ...) __EINFO_PRINTF;
+int ewarnn (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+int eerrorn (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+int einfo (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+int ewarn (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+void ewarnx (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_XPRINTF;
+int eerror (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+void eerrorx (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_XPRINTF;
+
+int einfovn (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+int ewarnvn (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+int ebeginvn (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+int eendvn (int __retval, const char * __EINFO_RESTRICT __fmt, ...) __EEND_PRINTF;
+int ewendvn (int __retval, const char * __EINFO_RESTRICT __fmt, ...) __EEND_PRINTF;
+int einfov (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+int ewarnv (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+/*@}*/
+
+/*! @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 __fmt, ...) __EINFO_PRINTF;
+int ebegin (const char * __EINFO_RESTRICT __fmt, ...) __EINFO_PRINTF;
+/*@}*/
+
+/*! @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 __retval, const char * __EINFO_RESTRICT __fmt, ...) __EEND_PRINTF;
+int ewend (int __retval, const char * __EINFO_RESTRICT __fmt, ...) __EEND_PRINTF;
+void ebracket (int __col, einfo_color_t __color, const char * __EINFO_RESTRICT __msg);
+
+int eendv (int __retval, const char * __EINFO_RESTRICT __fmt, ...) __EEND_PRINTF;
+int ewendv (int __retval, const char * __EINFO_RESTRICT __fmt, ...) __EEND_PRINTF;
+/*@}*/
+
+/*! @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 __prefix);
+
+#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..d80af9c0
--- /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)