/* vi: set tabstop=4 shiftwidth=4 */

/*
 * $Id: error.c,v 2.1 2005/06/17 20:48:15 schweikh Exp $
 */

/*@ignore@*/
#ifndef _POSIX_SOURCE
#  define _POSIX_SOURCE 1
#endif
/*@end@*/

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "shutup.h"
#include "error.h"

/*@-ptrarith@*/
/*@-redecl@*/
/*@-predboolint -statictrans -globstate@*/

/*
 * progname is a short string prepended to error messages.
 */

/*@i1@*/ static char *progname = "(program name not assigned)";

#if MALLOC_DEBUG
static FILE *debug;
#endif
long alloc_free = 0;            /* # alloc minus # free */

	static void
err_doit (int, const char *, va_list ap)
	/*@globals progname,stderr,fileSystem,errno@*/
	/*@modifies *stderr,fileSystem,ap,errno@*/;

	char *
program_name (void)
{
	return progname;
}

/*
 * Checked fprintf: do fprintf and exit if it failed
 * (probably due to an I/O or "no space on device" error).
 */
	void
cfprintf (FILE *fp, const char *fmt, ...)
{
	va_list ap;

	va_start (ap, fmt);
	if (vfprintf (fp, fmt, ap) < 0) {
		err_sys ("fprintf failed for format `%s'", fmt);
	}
	va_end (ap);
}

/*
 * write program name to a stream and return
 * a pointer to static memory with the name.
 */

	char *
err_progname (FILE * fp)
{
    if (fp != NULL) {
        check_sys (fflush (fp) == 0);
        cfprintf (fp, "%s", progname);
        check_sys (fflush (fp) == 0);
    }
    return progname;
}

	void
set_progname (char *argv0, char *def)
{
    if (argv0 == NULL || argv0[0] == '\0') {
        progname = def;
    } else {
        char *p = strrchr (argv0, '/');

        /* p + 1 um '/' übergehen */
        progname = p == NULL ? argv0 : p + 1;
    }
#if MALLOC_DEBUG
    debug = fopen (DEBUG_FILE, "w");
    check_sys (debug != NULL);
#endif
}

/*
 * Nonfatal error related to a system call. Print a message and return.
 */

	void
err_ret (const char *fmt, ...)
{
	va_list ap;
	va_start (ap, fmt);
	err_doit (1, fmt, ap);
	va_end (ap);
}

/*
 * Fatal error related to a system call. Print a message and exit().
 */

	void
err_sys (const char *fmt, ...)
{
	va_list ap;
	va_start (ap, fmt);
	err_doit (1, fmt, ap);
	va_end (ap);
	exit (EXIT_FAILURE);
}

/*
 * Fatal error related to a system call. Print a message and dump core.
 */

	void
err_dump (const char *fmt, ...)
{
	va_list ap;
	va_start (ap, fmt);
	err_doit (1, fmt, ap);
	va_end (ap);
	abort ();
	/*@NOTREACHED@*/
	exit (EXIT_FAILURE); /* should never get here */
}

/*
 * Nonfatal error unrelated to a system call. Print a message and return.
 */

	void
err_msg (const char *fmt, ...)
{
	va_list ap;
	va_start (ap, fmt);
	err_doit (0, fmt, ap);
	va_end (ap);
}

/*
 * Fatal error unrelated to a system call. Print a message and exit().
 */

	void
err_quit (const char *fmt, ...)
{
	va_list ap;
	va_start (ap, fmt);
	err_doit (0, fmt, ap);
	va_end (ap);
	exit (EXIT_FAILURE);
}

	static void
err_doit (int errnoflag, const char *fmt, va_list ap)
{
	int errno_save = errno;

	Fflush (stdout);
	Fprintf (stderr, "%s: ", progname);
	Vfprintf (stderr, fmt, ap);
	if (errnoflag != 0) {
		Fputc (':', stderr);
		Fputc (' ', stderr);
		errno = errno_save;
		perror (NULL);
	} else {
		Fputc ('\n', stderr);
	}
	Fflush (NULL);
}


void
/*@unused@*//*@printflike@*/
warn (int flags, const char *fmt,...)
{
    int errno_save = errno;
    va_list ap;
    va_start (ap, fmt);

    check_sys (fflush (NULL) == 0);
    cfprintf (stderr, "%s: ", progname);
    check_sys (vfprintf (stderr, fmt, ap) >= 0);
    if (flags & E_PERROR) {
        errno = errno_save;
        perror (" ");
    } else {
        check_sys (fputc ('\n', stderr) != EOF);
    }
    check_sys (fflush (stderr) == 0);
}

void
/*@unused@*//*@printflike@*/
error (int flags, const char *fmt,...)
{
    int errno_save = errno;
    va_list ap;
    va_start (ap, fmt);

    check_sys (fflush (NULL) == 0);
    cfprintf (stderr, "%s: ", progname);
    check_sys (vfprintf (stderr, fmt, ap) >= 0);
    if (flags & E_PERROR) {
        errno = errno_save;
        perror (" ");
    } else {
        check_sys (fputc ('\n', stderr) != EOF);
    }
    check_sys (fflush (stderr) == 0);
    if (flags & E_ABORT)
        abort ();
    else
        exit (EXIT_FAILURE);
}

/*@-compdef@*/
void *
#if MALLOC_DEBUG
xxmalloc (size_t s, char *file, int line)
{
    void *p = malloc (s);

    if (p == NULL)
        error (0, "malloc(%lu)", (unsigned long) s);
    cfprintf (debug, "a %p %s:%d %lu\n",
              (void *) p, file, line, (unsigned long) s);
    ++alloc_free;
    return p;
}
#else                           /* !MALLOC_DEBUG */
xxmalloc (size_t s)
{
    void *p = malloc (s);

    if (p == NULL)
        error (0, "malloc(%lu)", (unsigned long) s);
    ++alloc_free;
    return p;
}
#endif

/*@only@*/ void *
#if MALLOC_DEBUG
xxcalloc (size_t n, size_t s, char *file, int line)
{
    void *p = calloc (n, s);

    if (p == NULL)
        error (0, "calloc(%lu, %lu)",
               (unsigned long) n, (unsigned long) s);
    cfprintf (debug, "a %p %s:%d %lu\n",
              (void *) p, file, line, (unsigned long) n * s);
    ++alloc_free;
    return p;
}
#else                           /* !MALLOC_DEBUG */
xxcalloc (size_t n, size_t s)
{
    void *p = calloc (n, s);

    if (p == NULL)
        error (0, "calloc(%lu, %lu)",
               (unsigned long) n, (unsigned long) s);
    ++alloc_free;
    return p;
}
#endif

/*
 * realloc hat 3 Arbeitsweisen:
 * 1.  realloc (0, s)     wie malloc (s)
 * 2.  realloc (oldp, 0)  wie free (oldp)
 * 3.  realloc (oldp, s)  resize
 */
void *
#if MALLOC_DEBUG
xxrealloc (void *oldp, size_t newsize, char *file, int line)
{
    void *newp;

    if (oldp != NULL) {
        --alloc_free;
        cfprintf (debug, "f %p %s:%d\n",
                  (void *) oldp, file, line);
    }
    newp = realloc (oldp, newsize);
    if (newsize == 0)           /* free */
        return (void *) 1;      /* dummy; nicht NULL wg lclint */
    if (newp == NULL)
        error (0, "realloc(%lu)", (unsigned long) newsize);
    cfprintf (debug, "a %p %s:%d %lu\n",
              (void *) newp, file, line,
              (unsigned long) newsize);
    ++alloc_free;
    return newp;
}
#else                           /* !MALLOC_DEBUG */
xxrealloc (void *oldp, size_t newsize)
{
    void *newp;

    if (oldp != NULL)
        --alloc_free;
    newp = realloc (oldp, newsize);
    if (newsize == 0)           /* free */
        return (void *) 1;      /* dummy; nicht NULL wg lclint */
    if (newp == NULL)
        error (0, "realloc(%lu)", (unsigned long) newsize);
    ++alloc_free;
    return newp;
}
#endif

/*@-nullpass@*/

void
#if MALLOC_DEBUG
xxfree (void *p, char *file, int line)
{
    if (p == NULL) {
        cfprintf (debug, "f NULL %s:%d\n", file, line);
        return;
    }
    cfprintf (debug, "f %p %s:%d\n", (void *) p, file, line);
    --alloc_free;
    free (p);
}

#else                           /* !MALLOC_DEBUG */
xxfree (void *p)
{
    if (p != NULL) {
        --alloc_free;
        free (p);
    }
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1