/* $Id: err.C,v 1.23 2002/03/11 01:47:10 dm Exp $ */

/*
 *
 * Copyright (C) 1998 David Mazieres (dm@uun.org)
 *
 * 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, 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-1307
 * USA
 *
 */

#include "err.h"

#undef warn
#undef warnx
#undef vwarn
#undef vwarnx
#undef fatal
#undef panic

bssstr progname;
str progdir;
void (*fatalhook) ();

int errfd = 2;
bool fatal_no_destruct;
void (*_err_output) (suio *uio, int flags) = _err_output_sync;
void (*_err_reset_hook) ();

/* Newer versions of dmalloc changed dmalloc_logpath to
 * _dmalloc_logpath. */
#if 1
#define dmalloc_logpath _dmalloc_logpath
extern "C" char *dmalloc_logpath;
#endif

void
setprogname (char *argv0)
{
  char *cp;
  if ((cp = strrchr (argv0, '/')))
    cp++;
  else
    cp = argv0;
  /* Libtool shell wrappers leave lt- in argv[0] */
  if (cp[0] == 'l' && cp[1] == 't' && cp[2] == '-')
    progname = cp + 3;
  else
    progname = cp;
  if (cp > argv0)
    progdir.setbuf (argv0, cp - argv0);
  else
    progdir = NULL;
#ifdef DMALLOC
  if (dmalloc_logpath) {
    str logname;
    const char *p;
    if (!(p = strrchr (dmalloc_logpath, '/')) || !(p = strrchr (p, '.')))
      p = dmalloc_logpath + strlen (dmalloc_logpath);
    logname = strbuf ("%.*s.%s", p - dmalloc_logpath, dmalloc_logpath,
		      progname.cstr ());
    static char *lp;
    if (lp)
      xfree (lp);
    lp = xstrdup (logname);
    dmalloc_logpath = lp;
  }
#endif /* DMALLOC */
}

void
err_reset ()
{
  if (_err_reset_hook)
    (*_err_reset_hook) ();
  _err_reset_hook = NULL;
  _err_output = _err_output_sync;
}

void
_err_output_sync (suio *uio, int flags)
{
  int saved_errno = errno;
  uio->output (errfd);
  if (flags & warnobj::panicflag)
    abort ();
  if (flags & warnobj::fatalflag) {
    if (fatalhook)
      (*fatalhook) ();
    if (fatal_no_destruct)
      _exit (1);
    exit (1);
  }
  errno = saved_errno;
}

/*
 *  warnobj
 */

warnobj::warnobj (int f)
  : flags (f)
{
  if (!(flags & xflag) && progname)
    cat (progname).cat (": ");
  if (flags & panicflag)
    cat ("PANIC: ");
  else if (flags & fatalflag)
    cat ("fatal: ");
}

const warnobj &
warnobj::operator() (const char *fmt, ...) const
{
  va_list ap;
  va_start (ap, fmt);
  vfmt (fmt, ap);
  va_end (ap);
  return *this;
}

warnobj::~warnobj ()
{
  _err_output (uio, flags);
}

#ifndef fatalobj
fatalobj::~fatalobj ()
{
  /* Of course, gcc won't let this function return, so we have to jump
   * through a few hoops rather than simply implement ~fatalobj as
   * {}. */
  static_cast<warnobj *> (this)->~warnobj ();
  abort ();
}
#endif /* !fatalobj */


/*
 *  Traditional functions
 */

void
sfs_vwarn (const char *fmt, va_list ap)
{
  suio uio;
  if (progname)
    uio.print (progname.cstr (), progname.len ());
  suio_vuprintf (&uio, fmt, ap);
  _err_output (&uio, 0);
}

void
sfs_vwarnx (const char *fmt, va_list ap)
{
  suio uio;
  suio_vuprintf (&uio, fmt, ap);
  _err_output (&uio, warnobj::xflag);
}

void
sfs_warn (const char *fmt, ...)
{
  va_list ap;
  va_start (ap, fmt);
  sfs_vwarn (fmt, ap);
  va_end (ap);
}

void
sfs_warnx (const char *fmt, ...)
{
  va_list ap;
  va_start (ap, fmt);
  sfs_vwarnx (fmt, ap);
  va_end (ap);
}

void
fatal (const char *fmt, ...)
{
  va_list ap;
  strbuf b;
  if (progname)
    b << progname << ": ";
  b << "fatal: ";

  va_start (ap, fmt);
  b.vfmt (fmt, ap);
  va_end (ap);

  _err_output (b.tosuio (), warnobj::fatalflag);
  exit (1);
}

void
panic (const char *fmt, ...)
{
  va_list ap;
  strbuf b;
  if (progname)
    b << progname << ": ";
  b << "PANIC: " << __BACKTRACE__ << "\n";

  va_start (ap, fmt);
  b.vfmt (fmt, ap);
  va_end (ap);

  _err_output (b.tosuio (), warnobj::panicflag);
  abort ();
}

GLOBALDESTRUCT;
