/* $Id: c,v.xperror 1.1 1998/07/31 14:59:10 stoklund Exp stoklund $ */
/* xperror.c: Extended perror and strerror functions.

   Copyright (C) 1998 Jakob Stoklund Olesen

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "kernel.h"
#define _XPERROR_C
#include "xperror.h"

/* A pointer to the last OS error we know about */
static _kernel_oserror *last_error = NULL;
/* Or just a message when errno == MASK */
static const char *last_string = NULL;

/* We set bit 30 in errno when it comes from last_error. */
#define MASK (1<<30)

/* Same as strerror() from string.h */
const char *
xstrerror (int err)
{
  err &= ~MASK;			/* clear our special bit */
  /* If err was errno set by errno_user then use that one */
  if (last_error && err == last_error->errnum)
    return last_error->errmess;
  else if (last_string && err == 0)
    return last_string;
  else
    return strerror (err);
}

/* Same as perror() from stdio.h */
extern void
xperror (const char *msg)
{
  /* First update errno if it is clear or previous OS error */
  if (errno == 0 || (errno & MASK))
    errno_swi (NULL);

  if (msg)
    fprintf (stderr, "%s: %s\n", msg, xstrerror (errno));
  else
    fprintf (stderr, "%s\n", xstrerror (errno));
}

/* Set errno to reflect the returned error from a SWI.
   If ERP is NULL, errno is set from _kernel_last_oserror */
extern void
errno_swi (void *erp)
{
  _kernel_oserror *kernel = _kernel_last_oserror ();
  _kernel_oserror *thiserr = erp;

  if (!thiserr)
    thiserr = kernel;

  if (thiserr)
    {
      last_string = NULL;
      last_error = thiserr;
      errno = thiserr->errnum | MASK;
    }
}

extern void
errno_string (const char *msg)
{
  /* Forget any kernel error */
  _kernel_last_oserror ();
  last_error = NULL;
  last_string = msg;
  errno = MASK;
}

#ifdef TEST
int
main ()
{
  FILE *f;

  f = fopen ("dummy", "w");
  if (!f)
    xperror ("fopen");

  errno = 25;
  xperror ("25");
}
#endif
