/*
 * nbexit.c  -  Handle program exit and cleanup
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  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
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: nbexit.c,v 1.9 2007/01/06 18:31:38 gkminix Exp $
 */

#include <common.h>
#include <nblib.h>
#include "privlib.h"



/* Type of function to call on exit */
typedef void (*exithandler) __P((void));



/* Maximum number of exit functions allowed */
#define MAX_ATEXIT	32



/* Global variables */
char *nberrmsg = NULL;				/* pointer to error message */
int nberrnum = 0;				/* last error number */



/* Local variables */
static int isinit = FALSE;			/* initialization flag */
static int handlernum = 0;			/* current no. of functions */
static exithandler handlers[MAX_ATEXIT];	/* atexit handler list */



/*
 * Execute all exit routines, but do not actually exit the program
 */
void nblib_exit_cleanup __F_NOARGS
{
  while (handlernum > 0)
	(handlers[--handlernum])();
}



/*
 * Function to be called upon program termination
 */
#if defined(HAVE_ON_EXIT) && !defined(HAVE_ATEXIT)
static void termall __F((exitcode, exitarg), int exitcode AND voidstar exitarg)
{
  nblib_exit_cleanup();
}
#endif



/*
 * Exit the current program
 */
void nbexit __F((code), int code)
{
  if (code < 0) {
	if (nberrmsg != NULL)
		prnerr(nberrmsg);
	code = nberrnum;
  }
#if !defined(HAVE_ATEXIT) && !defined(HAVE_ON_EXIT)
  nblib_exit_cleanup();
#endif
  exit(code);
}



/*
 * Add a routine to the list of functions to execute at exit
 */
int nbatexit __F((func), void (*func)__F_NOARGS)
{
  /* Check if termination handler already initialized */
  if (!isinit) {
#ifdef HAVE_ATEXIT
	if (atexit(&nblib_exit_cleanup) != 0)
		return(-1);
#else
# ifdef HAVE_ON_EXIT
	if (on_exit(&termall, NULL) != 0)
		return(-1);
# endif
#endif
	isinit = TRUE;
  }

  /* Save function to execute at exit */
  if (handlernum >= MAX_ATEXIT)
	return(-1);
  handlers[handlernum++] = (exithandler)func;
  return(0);
}



/*
 * Save new error message and error number
 */
#if defined(HAVE_ANSI_CC)
void nberror(int errnum, const char *errmsg, ...)
#else
void nberror(va_list) va_decl
#endif
{
  va_list args;
#if !defined(HAVE_ANSI_CC)
  char *errmsg;
  int errnum;
#endif

  /* Startup variadic argument handling */
#if defined(HAVE_ANSI_CC)
  va_start(args, errmsg);
#else
  va_start(args);
  errnum = va_arg(args, int);
  errmsg = va_arg(args, char *);
#endif

  /* Generate error message */
  free(nberrmsg);
  nberrmsg = NULL;
  nberrnum = 0;
  if (errnum > 0) {
	nberrnum = errnum;
	if (errmsg != NULL && *errmsg != '\0')
		vasprintf(&nberrmsg, errmsg, args);
  }

  /* Terminate variadic argument handling */
  va_end(args);
}

