Logo Search packages:      
Sourcecode: tardy version File versions  Download package

error.cc

/*
 *    tardy - a tar post-processor
 *    Copyright (C) 1991-1995, 1998, 1999, 2001-2004 Peter Miller;
 *    All rights reserved.
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to report errors
 */

#include <ac/assert.h>
#include <ac/ctype.h>
#include <ac/errno.h>
#include <ac/grp.h>
#include <ac/pwd.h>
#include <ac/stdarg.h>
#include <ac/stddef.h>
#include <ac/stdio.h>
#include <ac/stdlib.h>
#include <ac/string.h>
#include <ac/unistd.h>

#include <arglex.h>
#include <error.h>
#include <mprintf.h>
#include <progname.h>


static void
error_get_id(int *uid, int *gid)
{
      *uid = geteuid();
      *gid = getegid();
}


static      error_id_ty errid = error_get_id;


void
error_set_id_func(error_id_ty f)
{
      if (f)
            errid = f;
      else
            errid = error_get_id;
}


/*
 * NAME
 *    wrap - wrap s string over lines
 *
 * SYNOPSIS
 *    void wrap(char *);
 *
 * DESCRIPTION
 *    The wrap function is used to print error messages onto stderr
 *    wrapping ling lines.
 *
 * CAVEATS
 *    Line length is assumed to be 80 characters.
 */

static void
wrap(char *s)
{
      static char escapes[] = "\rr\nn\ff\bb\tt";
      int   page_width;
      char  tmp[200];
      int   first_line;
      char  *tp;

      if (fflush(stdout) || ferror(stdout))
            nfatal("(stdout)");
      /* don't use last column, many terminals are dumb */
      page_width = 79;
      const char *progname = progname_get();
      first_line = 1;
      while (*s)
      {
            char  *ep;
            int   ocol;

            /*
             * Work out how many characters fit on the line.
             */
            if (first_line)
                  ocol = strlen(progname) + 2;
            else
                  ocol = 8;
            for (ep = s; *ep; ++ep)
            {
                  int   cw;
                  int   c;

                  c = (unsigned char)*ep;
                  if (isprint(c))
                        cw = 1 + (c == '\\');
                  else
                        cw = (strchr(escapes, c) ? 2 : 4);
                  if (ocol + cw > page_width)
                        break;
                  ocol += cw;
            }

            /*
             * see if there is a better place to break the line
             */
            if (*ep && *ep != ' ')
            {
                  char  *mp;

                  for (mp = ep; mp > s; --mp)
                  {
                        if (strchr(" /", mp[-1]))
                        {
                              ep = mp;
                              break;
                        }
                  }
            }

            /*
             * ignore trailing blanks
             */
            while (ep > s && ep[-1] == ' ')
                  ep--;

            /*
             * print the line
             */
            if (first_line)
                  snprintf(tmp, sizeof(tmp), "%s: ", progname);
            else
                  strcpy(tmp, "\t");
            tp = tmp + strlen(tmp);
            while (s < ep)
            {
                int c = (unsigned char)*s++;
                if (isprint(c))
                {
                  if (c == '\\')
                      *tp++ = '\\';
                  *tp++ = c;
                }
                else
                {
                  char *esc = strchr(escapes, c);
                  if (esc)
                  {
                      *tp++ = '\\';
                      *tp++ = esc[1];
                  }
                  else
                  {
                      snprintf(tp, tmp + sizeof(tmp) - tp, "\\%3.3o", c);
                      tp += strlen(tp);
                  }
                }
            }
            *tp++ = '\n';
            *tp = 0;
            fputs(tmp, stderr);
            if (ferror(stderr))
                  break;

            /*
             * skip leading spaces for subsequent lines
             */
            while (*s == ' ')
                  s++;
            first_line = 0;
      }
      if (fflush(stderr) || ferror(stderr))
            nfatal("(stderr)");
}


static void
double_jeopardy(void)
{
      char buffer[200];
      snprintf
      (
            buffer,
            sizeof(buffer),
            "while attempting to construct an error message: %s (fatal)",
            strerror(errno)
      );
      wrap(buffer);
      quit(1);
}


static char *
copy_string(const char *s)
{
      char        *cp;

      errno = 0;
      cp = (char *)malloc(strlen(s) + 1);
      if (!cp)
      {
            if (!errno)
                  errno = ENOMEM;
            double_jeopardy();
      }
      strcpy(cp, s);
      return cp;
}


static char *
id(void)
{
      int uid;
      int gid;
      errid(&uid, &gid);
      struct passwd *pw = getpwuid(uid);
      char uidn[20];
      if (pw)
            snprintf(uidn, sizeof(uidn), "user \"%.8s\"", pw->pw_name);
      else
            snprintf(uidn, sizeof(uidn), "uid %d", uid);

      struct group *gr = getgrgid(gid);
      char gidn[20];
      if (gr)
            snprintf(gidn, sizeof(gidn), "group \"%.8s\"", gr->gr_name);
      else
            snprintf(gidn, sizeof(gidn), "gid %d", gid);

      char buffer[100];
      snprintf(buffer, sizeof(buffer), " [%s, %s]", uidn, gidn);
      return copy_string(buffer);
}


/*
 *  NAME
 *    error - place a message on the error stream
 *
 *  SYNOPSIS
 *    void error(char *fmt, ...);
 *
 *  DESCRIPTION
 *    Error places a message on the error output stream.
 *    The first argument is a printf-like format string,
 *    optionally followed by other arguments.
 *    The message will be prefixed by the program name and a colon,
 *    and will be terminated with a newline, automatically.
 *
 *  CAVEAT
 *    Things like "error(filename)" blow up if the filename
 *    contains a '%' character.
 */

/*VARARGS1*/
void
error(const char *fmt, ...)
{
      va_list ap;
      va_start(ap, fmt);
      char *buffer = vmprintf_errok(fmt, ap);
      va_end(ap);
      if (!buffer)
            double_jeopardy();
      wrap(buffer);
}


/*
 *  NAME
 *    nerror - place a system fault message on the error stream
 *
 *  SYNOPSIS
 *    void nerror(char *fmt, ...);
 *
 *  DESCRIPTION
 *    Nerror places a message on the error output stream.
 *    The first argument is a printf-like format string,
 *    optionally followed by other arguments.
 *    The message will be prefixed by the program name and a colon,
 *    and will be terminated with a text description of the error
 *    indicated by the 'errno' global variable, automatically.
 *
 *  CAVEAT
 *    Things like "nerror(filename)" blow up if the filename
 *    contains a '%' character.
 */

/*VARARGS1*/
void
nerror(const char *fmt, ...)
{
      int n = errno;
      va_list ap;
      va_start(ap, fmt);
      char *s1 = vmprintf_errok(fmt, ap);
      va_end(ap);
      if (!s1)
            double_jeopardy();
      s1 = copy_string(s1);
      char *s2;
      if (n == EPERM || n == EACCES)
            s2 = id();
      else
            s2 = copy_string("");
      error("%s: %s%s", s1, strerror(n), s2);
      free(s1);
      free(s2);
}


/*
 *  NAME
 *    nfatal - place a system fault message on the error stream and exit
 *
 *  SYNOPSIS
 *    void nfatal(char *fmt, ...);
 *
 *  DESCRIPTION
 *    Nfatal places a message on the error output stream and exits.
 *    The first argument is a printf-like format string,
 *    optionally followed by other arguments.
 *    The message will be prefixed by the program name and a colon,
 *    and will be terminated with a text description of the error
 *    indicated by the 'errno' global variable, automatically.
 *
 *  CAVEAT
 *    Things like "nfatal(filename)" blow up if the filename
 *    contains a '%' character.
 *
 *    This function does NOT return.
 */

/*VARARGS1*/
void
nfatal(const char *fmt, ...)
{
      int n = errno;
      va_list ap;
      va_start(ap, fmt);
      char *s1 = vmprintf_errok(fmt, ap);
      va_end(ap);
      if (!s1)
            double_jeopardy();
      s1 = copy_string(s1);

      char *s2;
      if (n == EPERM || n == EACCES)
            s2 = id();
      else
            s2 = "";

      fatal("%s: %s%s", s1, strerror(n), s2);
}


/*
 *  NAME
 *    fatal - place a message on the error stream and exit
 *
 *  SYNOPSIS
 *    void fatal(char *fmt, ...);
 *
 *  DESCRIPTION
 *    Fatal places a message on the error output stream and exits.
 *    The first argument is a printf-like format string,
 *    optionally followed by other arguments.
 *    The message will be prefixed by the program name and a colon,
 *    and will be terminated with a newline, automatically.
 *
 *  CAVEAT
 *    Things like "error(filename)" blow up if the filename
 *    contains a '%' character.
 *
 *    This function does NOT return.
 */

/*VARARGS1*/
void
fatal(const char *fmt, ...)
{
      va_list ap;
      va_start(ap, fmt);
      char *buffer = vmprintf_errok(fmt, ap);
      va_end(ap);
      if (!buffer)
            double_jeopardy();
      wrap(buffer);
      quit(1);
}


/*
 *  NAME
 *      signal_name - find it
 *
 *  SYNOPSIS
 *      char *signal_name(int n);
 *
 *  DESCRIPTION
 *      The signal_name function is used to find the name of a signal from its
 *      number.
 *
 *  RETURNS
 *      char *: pointer to the signal name.
 *
 *  CAVEAT
 *      The signal name may not be written on.  Subsequent calls may alter the
 *      area pointed to.
 */

const char *
signal_name(int n)
{
      static char buffer[16];

      switch (n)
      {
#ifdef SIGHUP
      case SIGHUP:
            return "hang up [SIGHUP]";
#endif /* SIGHUP */

#ifdef SIGINT
      case SIGINT:
            return "user interrupt [SIGINT]";
#endif /* SIGINT */

#ifdef SIGQUIT
      case SIGQUIT:
            return "user quit [SIGQUIT]";
#endif /* SIGQUIT */

#ifdef SIGILL
      case SIGILL:
            return "illegal instruction [SIGILL]";
#endif /* SIGILL */

#ifdef SIGTRAP
      case SIGTRAP:
            return "trace trap [SIGTRAP]";
#endif /* SIGTRAP */

#ifdef SIGIOT
      case SIGIOT:
            return "abort [SIGIOT]";
#endif /* SIGIOT */

#ifdef SIGEMT
      case SIGEMT:
            return "EMT instruction [SIGEMT]";
#endif /* SIGEMT */

#ifdef SIGFPE
      case SIGFPE:
            return "floating point exception [SIGFPE]";
#endif /* SIGFPE */

#ifdef SIGKILL
      case SIGKILL:
            return "kill [SIGKILL]";
#endif /* SIGKILL */

#ifdef SIGBUS
      case SIGBUS:
            return "bus error [SIGBUS]";
#endif /* SIGBUS */

#ifdef SIGSEGV
      case SIGSEGV:
            return "segmentation violation [SIGSEGV]";
#endif /* SIGSEGV */

#ifdef SIGSYS
      case SIGSYS:
            return "bad argument to system call [SIGSYS]";
#endif /* SIGSYS */

#ifdef SIGPIPE
      case SIGPIPE:
            return "write on a pipe with no one to read it [SIGPIPE]";
#endif /* SIGPIPE */

#ifdef SIGALRM
      case SIGALRM:
            return "alarm clock [SIGALRM]";
#endif /* SIGALRM */

#ifdef SIGTERM
      case SIGTERM:
            return "software termination [SIGTERM]";
#endif /* SIGTERM */

#ifdef SIGUSR1
      case SIGUSR1:
            return "user defined signal one [SIGUSR1]";
#endif /* SIGUSR1 */

#ifdef SIGUSR2
      case SIGUSR2:
            return "user defined signal two [SIGUSR2]";
#endif /* SIGUSR2 */

#ifdef SIGCLD
      case SIGCLD:
            return "death of child [SIGCLD]";
#endif /* SIGCLD */

#ifdef SIGPWR
      case SIGPWR:
            return "power failure [SIGPWR]";
#endif /* SIGPWR */

      default:
            snprintf(buffer, sizeof(buffer), "signal %d", n);
            return buffer;
      }
}


static      quit_ty     quit_list[10];
static      int   quit_list_len;


void
quit_register(quit_ty func)
{
      assert(quit_list_len < SIZEOF(quit_list));
      assert(func);
      quit_list[quit_list_len++] = func;
}


void
quit(int n)
{
      int         j;
      static int  quitting;

      if (quitting > 4)
      {
            fprintf
            (
                  stderr,
                  "%s: incorrectly handled error while quitting (bug)\n",
                  progname_get()
            );
            exit(1);
      }
      ++quitting;
      for (j = quit_list_len - 1; j >= 0; --j)
            quit_list[j](n);
      exit(n);
}

Generated by  Doxygen 1.6.0   Back to index