diff options
Diffstat (limited to 'src/libeinfo.c')
-rw-r--r-- | src/libeinfo.c | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/src/libeinfo.c b/src/libeinfo.c new file mode 100644 index 0000000..e0faf98 --- /dev/null +++ b/src/libeinfo.c @@ -0,0 +1,877 @@ +/* + einfo.c + Gentoo informational functions + Copyright 2007 Gentoo Foundation + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" + +/* Incase we cannot work out how many columns from ioctl, supply a default */ +#define DEFAULT_COLS 80 + +#define OK "ok" +#define NOT_OK "!!" + +#define CHECK_VERBOSE if (! is_env ("RC_VERBOSE", "yes")) return 0 + +/* Number of spaces for an indent */ +#define INDENT_WIDTH 2 + +/* How wide can the indent go? */ +#define INDENT_MAX 40 + +#define EBUFFER_LOCK RC_SVCDIR "ebuffer/.lock" + +/* A cheat sheet of colour capable terminals + This is taken from DIR_COLORS from GNU coreutils + We embed it here as we shouldn't depend on coreutils */ +static const char *colour_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 +}; + +static bool is_env (const char *var, const char *val) +{ + char *v; + + if (! var) + return (false); + + v = getenv (var); + if (! v) + return (val == NULL ? true : false); + + return (strcasecmp (v, val) == 0 ? true : false); +} + +bool colour_terminal (void) +{ + static int in_colour = -1; + int i = 0; + char *term; + + if (in_colour == 0) + return (false); + if (in_colour == 1) + return (true); + + term = getenv ("TERM"); + /* If $TERM isn't set then the chances are we're in single user mode */ + if (! term) + return (true); + + while (colour_terms[i]) + { + if (strcmp (colour_terms[i], term) == 0) + { + in_colour = 1; + return (true); + } + i++; + } + + in_colour = 0; + return (false); +} + +static int get_term_columns (void) +{ +#ifdef TIOCGSIZE /* BSD */ + struct ttysize ts; + + if (ioctl(0, TIOCGSIZE, &ts) == 0) + return (ts.ts_cols); +#elif TIOCGWINSZ /* Linux */ + struct winsize ws; + + if (ioctl(0, TIOCGWINSZ, &ws) == 0) + return (ws.ws_col); +#endif + + return (DEFAULT_COLS); +} + +static int ebuffer (const char *cmd, int retval, const char *fmt, va_list ap) +{ + char *file = getenv ("RC_EBUFFER"); + FILE *fp; + char buffer[RC_LINEBUFFER]; + int l = 1; + + if (! file || ! cmd || strlen (cmd) < 4) + return (0); + + if (! (fp = fopen (file, "a"))) + { + fprintf (stderr, "fopen `%s': %s\n", file, strerror (errno)); + return (0); + } + + fprintf (fp, "%s %d ", cmd, retval); + + if (fmt) + { + l = vsnprintf (buffer, sizeof (buffer), fmt, ap); + fprintf (fp, "%d %s\n", l, buffer); + } + else + fprintf (fp, "0\n"); + + fclose (fp); + return (l); +} + +typedef struct func +{ + const char *name; + int (*efunc) (const char *fmt, ...); + int (*eefunc) (int retval, const char *fmt, ...); + void (*eind) (void); +} func_t; + +static const func_t funcmap[] = { + { "einfon", &einfon, NULL, NULL }, + { "ewarnn", &ewarnn, NULL, NULL}, + { "eerrorn", &eerrorn, NULL, NULL}, + { "einfo", &einfo, NULL, NULL }, + { "ewarn", &ewarn, NULL, NULL }, + { "eerror", &eerror, NULL, NULL }, + { "ebegin", &ebegin, NULL, NULL }, + { "eend", NULL, &eend, NULL }, + { "ewend", NULL, &ewend, NULL }, + { "eindent", NULL, NULL, &eindent }, + { "eoutdent", NULL, NULL, &eoutdent }, + { "veinfon", &veinfon, NULL, NULL }, + { "vewarnn", &vewarnn, NULL, NULL }, + { "veinfo", &veinfo, NULL, NULL }, + { "vewarn", &vewarn, NULL, NULL }, + { "vebegin", &vebegin, NULL, NULL }, + { "veend", NULL, &veend, NULL }, + { "vewend", NULL, &vewend, NULL }, + { "veindent" ,NULL, NULL, &veindent }, + { "veoutdent", NULL, NULL, &veoutdent }, + { NULL, NULL, NULL, NULL }, +}; + +void eflush (void) +{ + FILE *fp; + char *file = getenv ("RC_EBUFFER"); + char buffer[RC_LINEBUFFER]; + char *cmd; + int retval = 0; + int length = 0; + char *token; + char *p; + struct stat buf; + pid_t pid; + char newfile[PATH_MAX]; + int i = 1; + + if (! file|| (stat (file, &buf) != 0)) + { + errno = 0; + return; + } + + /* Find a unique name for our file */ + while (true) + { + snprintf (newfile, sizeof (newfile), "%s.%d", file, i); + if (stat (newfile, &buf) != 0) + { + if (rename (file, newfile)) + fprintf (stderr, "rename `%s' `%s': %s\n", file, newfile, + strerror (errno)); + break; + } + i++; + } + + /* We fork a child process here so we don't hold anything up */ + if ((pid = fork ()) == -1) + { + fprintf (stderr, "fork: %s", strerror (errno)); + return; + } + + if (pid != 0) + return; + + /* Spin until we can lock the ebuffer */ + while (true) + { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 20000; + select (0, NULL, NULL, NULL, &tv); + errno = 0; + if (link (newfile, EBUFFER_LOCK) == 0) + break; + if (errno != EEXIST) + fprintf (stderr, "link `%s' `%s': %s\n", newfile, EBUFFER_LOCK, + strerror (errno)); + } + + if (! (fp = fopen (newfile, "r"))) + { + fprintf (stderr, "fopen `%s': %s\n", newfile, strerror (errno)); + return; + } + + unsetenv ("RC_EBUFFER"); + + memset (buffer, 0, RC_LINEBUFFER); + while (fgets (buffer, RC_LINEBUFFER, fp)) + { + i = strlen (buffer) - 1; + if (i < 1) + continue; + + if (buffer[i] == '\n') + buffer[i] = 0; + + p = buffer; + cmd = strsep (&p, " "); + token = strsep (&p, " "); + if (sscanf (token, "%d", &retval) != 1) + { + fprintf (stderr, "eflush `%s': not a number", token); + continue; + } + token = strsep (&p, " "); + if (sscanf (token, "%d", &length) != 1) + { + fprintf (stderr, "eflush `%s': not a number", token); + continue; + } + + i = 0; + while (funcmap[i].name) + { + if (strcmp (funcmap[i].name, cmd) == 0) + { + if (funcmap[i].efunc) + { + if (p) + funcmap[i].efunc ("%s", p); + else + funcmap[i].efunc (NULL, NULL); + } + else if (funcmap[i].eefunc) + { + if (p) + funcmap[i].eefunc (retval, "%s", p); + else + funcmap[i].eefunc (retval, NULL, NULL); + } + else if (funcmap[i].eind) + funcmap[i].eind (); + else + fprintf (stderr, "eflush `%s': no function defined\n", cmd); + break; + } + i++; + } + + if (! funcmap[i].name) + fprintf (stderr, "eflush `%s': invalid function\n", cmd); + } + fclose (fp); + + if (unlink (EBUFFER_LOCK)) + fprintf (stderr, "unlink `%s': %s", EBUFFER_LOCK, strerror (errno)); + + if (unlink (newfile)) + fprintf (stderr, "unlink `%s': %s", newfile, strerror (errno)); + + _exit (EXIT_SUCCESS); +} + +#define EBUFFER(_cmd, _retval, _fmt, _ap) \ +{ \ + int _i = ebuffer (_cmd, _retval, _fmt, _ap); \ + if (_i) \ + return (_i); \ +} + +static void elog (int level, const char *fmt, va_list ap) +{ + char *e = getenv ("RC_ELOG"); + + if (e) + { + closelog (); + openlog (e, LOG_PID, LOG_DAEMON); + vsyslog (level, fmt, ap); + } +} +static int _eindent (FILE *stream) +{ + char *env = getenv ("RC_EINDENT"); + 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)); +} + +#define VEINFON(_file, _colour) \ + if (colour_terminal ()) \ + fprintf (_file, " " _colour "*" EINFO_NORMAL " "); \ + else \ + fprintf (_file, " * "); \ + retval += _eindent (_file); \ + retval += vfprintf (_file, fmt, ap) + 3; \ + if (colour_terminal ()) \ + fprintf (_file, "\033[K"); + +static int _veinfon (const char *fmt, va_list ap) +{ + int retval = 0; + + VEINFON (stdout, EINFO_GOOD); + return (retval); +} + +static int _vewarnn (const char *fmt, va_list ap) +{ + int retval = 0; + + VEINFON (stdout, EINFO_WARN); + return (retval); +} + +static int _veerrorn (const char *fmt, va_list ap) +{ + int retval = 0; + + VEINFON (stderr, EINFO_BAD); + return (retval); +} + +int einfon (const char *fmt, ...) +{ + int retval; + va_list ap; + + if (! fmt || is_env ("RC_QUIET", "yes")) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("einfon", 0, fmt, ap))) + retval = _veinfon (fmt, ap); + va_end (ap); + + return (retval); +} + +int ewarnn (const char *fmt, ...) +{ + int retval; + va_list ap; + + if (! fmt || is_env ("RC_QUIET", "yes")) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("ewarnn", 0, fmt, ap))) + retval = _vewarnn (fmt, ap); + va_end (ap); + + return (retval); +} + +int eerrorn (const char *fmt, ...) +{ + int retval; + va_list ap; + + va_start (ap, fmt); + if (! (retval = ebuffer ("eerrorn", 0, fmt, ap))) + retval = _veerrorn (fmt, ap); + va_end (ap); + + return (retval); +} + +int einfo (const char *fmt, ...) +{ + int retval; + va_list ap; + + if (! fmt || is_env ("RC_QUIET", "yes")) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("einfo", 0, fmt, ap))) + { + retval = _veinfon (fmt, ap); + retval += printf ("\n"); + } + va_end (ap); + + return (retval); +} + +int ewarn (const char *fmt, ...) +{ + int retval; + va_list ap; + + if (! fmt || is_env ("RC_QUIET", "yes")) + return (0); + + va_start (ap, fmt); + elog (LOG_WARNING, fmt, ap); + if (! (retval = ebuffer ("ewarn", 0, fmt, ap))) + { + retval = _vewarnn (fmt, ap); + retval += printf ("\n"); + } + va_end (ap); + + return (retval); +} + +void ewarnx (const char *fmt, ...) +{ + int retval; + va_list ap; + + if (fmt && ! is_env ("RC_QUIET", "yes")) + { + va_start (ap, fmt); + elog (LOG_WARNING, fmt, ap); + retval = _vewarnn (fmt, ap); + va_end (ap); + retval += printf ("\n"); + } + exit (EXIT_FAILURE); +} + +int eerror (const char *fmt, ...) +{ + int retval; + va_list ap; + + if (! fmt) + return (0); + + va_start (ap, fmt); + elog (LOG_ERR, fmt, ap); + retval = _veerrorn (fmt, ap); + va_end (ap); + retval += fprintf (stderr, "\n"); + + return (retval); +} + +void eerrorx (const char *fmt, ...) +{ + va_list ap; + + if (fmt) + { + va_start (ap, fmt); + elog (LOG_ERR, fmt, ap); + _veerrorn (fmt, ap); + va_end (ap); + printf ("\n"); + } + exit (EXIT_FAILURE); +} + +int ebegin (const char *fmt, ...) +{ + int retval; + va_list ap; + + if (! fmt || is_env ("RC_QUIET", "yes")) + return (0); + + va_start (ap, fmt); + if ((retval = ebuffer ("ebegin", 0, fmt, ap))) + { + va_end (ap); + return (retval); + } + + retval = _veinfon (fmt, ap); + va_end (ap); + retval += printf (" ..."); + if (colour_terminal ()) + retval += printf ("\n"); + + return (retval); +} + +static void _eend (int col, einfo_color_t color, const char *msg) +{ + FILE *fp = stdout; + int i; + int cols; + + if (! msg) + return; + + if (color == einfo_bad) + fp = stderr; + + cols = get_term_columns () - (strlen (msg) + 6); + + if (cols > 0 && colour_terminal ()) + { + fprintf (fp, "\033[A\033[%dC %s[ ", cols, EINFO_BRACKET); + switch (color) + { + case einfo_good: + fprintf (fp, EINFO_GOOD); + break; + case einfo_warn: + fprintf (fp, EINFO_WARN); + break; + case einfo_bad: + fprintf (fp, EINFO_BAD); + break; + case einfo_hilite: + fprintf (fp, EINFO_HILITE); + break; + case einfo_bracket: + fprintf (fp, EINFO_BRACKET); + break; + case einfo_normal: + fprintf (fp, EINFO_NORMAL); + break; + } + fprintf (fp, "%s%s ]%s\n", msg, EINFO_BRACKET, EINFO_NORMAL); + } + else + { + for (i = -1; i < cols - col; i++) + fprintf (fp, " "); + fprintf (fp, "[ %s ]\n", msg); + } +} + +static int _do_eend (const char *cmd, int retval, const char *fmt, va_list ap) +{ + int col = 0; + FILE *fp; + + if (ebuffer (cmd, retval, fmt, ap)) + return (retval); + + if (fmt && retval != 0) + { + if (strcmp (cmd, "ewend") == 0) + { + col = _vewarnn (fmt, ap); + fp = stdout; + } + else + { + col = _veerrorn (fmt, ap); + fp = stderr; + } + if (colour_terminal ()) + fprintf (fp, "\n"); + } + + _eend (col, retval == 0 ? einfo_good : einfo_bad, retval == 0 ? OK : NOT_OK); + return (retval); +} + +int eend (int retval, const char *fmt, ...) +{ + va_list ap; + + if (is_env ("RC_QUIET", "yes")) + return (retval); + + va_start (ap, fmt); + _do_eend ("eend", retval, fmt, ap); + va_end (ap); + + return (retval); +} + +int ewend (int retval, const char *fmt, ...) +{ + va_list ap; + + if (is_env ("RC_QUIET", "yes")) + return (retval); + + va_start (ap, fmt); + _do_eend ("ewend", retval, fmt, ap); + va_end (ap); + + return (retval); +} + +void ebracket (int col, einfo_color_t color, const char *msg) +{ + _eend (col, color, msg); +} + +void eindent (void) +{ + char *env = getenv ("RC_EINDENT"); + int amount = 0; + char num[10]; + + if (ebuffer ("eindent", 0, NULL, NULL)) + return; + + 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 ("RC_EINDENT", num, 1); +} + +void eoutdent (void) +{ + char *env = getenv ("RC_EINDENT"); + int amount = 0; + char num[10]; + + if (ebuffer ("eoutdent", 0, NULL, NULL)) + return; + + if (! env) + return; + + errno = 0; + amount = strtol (env, NULL, 0); + if (errno != 0) + amount = 0; + else + amount -= INDENT_WIDTH; + + if (amount <= 0) + unsetenv ("RC_EINDENT"); + else + { + snprintf (num, 10, "%08d", amount); + setenv ("RC_EINDENT", num, 1); + } +} + +int veinfon (const char *fmt, ...) +{ + int retval; + va_list ap; + + CHECK_VERBOSE; + + if (! fmt) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("veinfon", 0, fmt, ap))) + retval = _veinfon (fmt, ap); + va_end (ap); + + return (retval); +} + +int vewarnn (const char *fmt, ...) +{ + int retval; + va_list ap; + + CHECK_VERBOSE; + + if (! fmt) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("vewarnn", 0, fmt, ap))) + retval = _vewarnn (fmt, ap); + va_end (ap); + + return (retval); +} + +int veinfo (const char *fmt, ...) +{ + int retval; + va_list ap; + + CHECK_VERBOSE; + + if (! fmt) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("veinfo", 0, fmt, ap))) + { + retval = _veinfon (fmt, ap); + retval += printf ("\n"); + } + va_end (ap); + + return (retval); +} + +int vewarn (const char *fmt, ...) +{ + int retval; + va_list ap; + + CHECK_VERBOSE; + + if (! fmt) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("vewarn", 0, fmt, ap))) + { + retval = _vewarnn (fmt, ap); + retval += printf ("\n"); + } + va_end (ap); + retval += printf ("\n"); + + return (retval); +} + +int vebegin (const char *fmt, ...) +{ + int retval; + va_list ap; + + CHECK_VERBOSE; + + if (! fmt) + return (0); + + va_start (ap, fmt); + if (! (retval = ebuffer ("vewarn", 0, fmt, ap))) + { + retval = _veinfon (fmt, ap); + retval += printf (" ..."); + if (colour_terminal ()) + retval += printf ("\n"); + } + va_end (ap); + + return (retval); +} + +int veend (int retval, const char *fmt, ...) +{ + va_list ap; + + CHECK_VERBOSE; + + va_start (ap, fmt); + _do_eend ("veend", retval, fmt, ap); + va_end (ap); + + return (retval); +} + +int vewend (int retval, const char *fmt, ...) +{ + va_list ap; + + CHECK_VERBOSE; + + va_start (ap, fmt); + _do_eend ("vewend", retval, fmt, ap); + va_end (ap); + + return (retval); +} + +void veindent (void) +{ + if (is_env ("RC_VERBOSE", "yes")) + eindent (); +} + +void veoutdent (void) +{ + if (is_env ("RC_VERBOSE", "yes")) + eoutdent (); +} |