\documentclass[panstarrs]{panstarrs}

\begin{document}
  
\section{Error Handling}
\hlabel{errorStack}

\begin{table}
\begin{verbatim}
typedef struct {
    char *name;                         // category of code that caused the error
    psErrorCode code;                   // class of error (equivalent to errno)
    char *msg;                          // the message associated with the error
} psErr;

typedef enum {
    PS_OLD_ERROR = 0,                   ///< This is an old error, and should append to the error stack
    PS_NEW_ERROR = 1,                   ///< This is a new error and should clear the error stack
} psErrorStatus;

/// Prints an error message and doesn't abort; returns code
int psError(const char *name,           ///< Category of code that caused the error
            psErrorCode code,           ///< class of error (equivalent to errno)
            psErrorStatus status,       ///< is this a new error?
            const char *fmt,            ///< Format
            ...                         ///< Extra arguments to use format
    );

const psErr *psGetError(int which);     // return specified error (or an "error" with code PS_ERR_NONE)
const psErr *psLastError(void);         // return last error (or an "error" with code PS_ERR_NONE)

void psErrorClear(void);                ///< Clear the error stack

void psErrorStackPrint(FILE *fd, const char *fmt, ...); ///< print the errorstack to this file descriptor
void psVErrorStackPrint(FILE *fd, const char *fmt, va_list va); ///< print the errorstack to this file descriptor
const char *psErrorCodeString(psErrorCode code);        ///< return the string associated with an error code.

\end{verbatim}
\end{table}

\PS{} errors shall be raised using the function \code{psError}. The \code{name} is of the
form \code{aaa.bbb.ccc} and identifies the component raising the error.  The \code{psErrorCode} is
an enumerated type which lists the possible textit{classes} of errors (e.g. \code{PS_ERR_IO})
that \PS/ code can generate (see section \ref{psErrorCodes}). \code{status} specifies whether this is a
new error, or whether this call to \code{psError} is in response to an error that has
already resulted in a call to \code{psError}.
The final required argument, \code{fmt}, is a \code{printf}-style
format that is passed to \code{psLogMsg} with code \code{PS_LOG_ERROR}.

The result of a call to \code{psError} shall be to push a \code{psErr} struct onto
a stack; this stack is cleared if \code{psErrorStatus} is true, or by a call
to \code{psErrorClear}.

The last error reported is available from \code{psLastError}; if no errors are
current, a non-\code{NULL} \code{psErr} shall be returned with code \code{PS_ERR_NONE}.
Previous errors on the stack shall be returned by \code{psGetError} (a value of \code{0}
passed to \code{psGetError} is equivalent to a call to \code{psLastError}).

The routine \code{psErrorCodeString} returns the string associated with an error code.

The entire error stack may be printed to an open file descriptor by calling \code{psErrorStackPrint}
(or \code{psVErrorStackPrint});
if and only if there are current errors, the printf-style string \code{fmt} is first printed
to the file descriptor \code{fd}. In this printout, error codes shall be replaced by their
string equivalents as defined in the next section.  Note that these are also available in
the \code{psErr} structure. The successive lines of the traceback should be indented by
an additional space (see example). \code{psVErrorStackPrint} shall not invoke \code{va_end}.

Any \code{errorCode}s less then or equal to \code{PS_ERR_BASE} (see next section) shall be taken
to be valid values of \code{errno}, and \code{psErrorStackPrint} shall print the value returned
by \code{strerror} if such error codes are encountered.

\subsection{Error Codes}
\hlabel{psErrorCodes}

The type \code{psErrorCode} is defined by an auxiliary file, conventionally named
\file{psErrorCodes.dat}. This file shall consist of a number of lines, each
of the form:
\begin{verbatim}
NAME [ = value ] , STRING
\end{verbatim}
where \code{[ = value]} is optional, and no spaces are significant except in the
STRING.  Comments extend from \code{#} to the end of the line (except that a
\code{\#} shall be replaced by \code{#} and not taken to start a comment). For example,
\begin{verbatim}
#
# This file is used to generate psErrorClasses.h
#
NONE = 0,               not an error; must be 0
BASE = 256,             first value we use; should avoid errno conflicts
UNKNOWN,                unknown error
IO,                     I/O error
BADFREE,                bad argument to psFree()
MEMORY_CORRUPTION,      memory corruption detected
\end{verbatim}
The values \code{NONE = 0} and {UNKNOWN} must be present.

The \PS{} Makefiles shall
generate two files, \file{psErrorCodes.h} and
\file{psErrorCodes.c} from the input file \file{psErrorCodes.dat}.
\file{psErrorCodes.h} shall define an enumerated type
\code{psErrorCode} with elements \code{PS_ERR_NAME} and values as specified
in \file{psErrorCodes.dat}, e.g.
\begin{verbatim}
#if !defined(PS_ERROR_CODES_H)
#define PS_ERROR_CODES_H

typedef enum {
    PS_ERR_NONE = 0,
    PS_ERR_BASE = 256,
    PS_ERR_UNKNOWN,
    PS_ERR_IO,
    PS_ERR_BADFREE,
    PS_ERR_MEMORY_CORRUPTION,
    PS_ERR_N_ERR_CLASSES,
} psErrorCode;

#endif
\end{verbatim}
Any \code{errorCode}s less then or equal to \code{PS_ERR_BASE} shall be taken
to be valid values of \code{errno}.

The implementation may add extra fields (e.g. \code{PS_ERR_N_ERR_CLASSES}).

The latter shall be of the form
\begin{verbatim}
static struct {
    psErrorCode code;
    char *descrip;
} errorStrings[] = {
    { PS_ERR_NONE, "not an error; must be 0"},
    { PS_ERR_BASE, "first value we use; should avoid errno conflicts"},
    { PS_ERR_UNKNOWN, "unknown error"},
    { PS_ERR_IO, "I/O error"},
    { PS_ERR_BADFREE, "bad argument to psFree()"},
    { PS_ERR_MEMORY_CORRUPTION, "memory corruption detected"},
    { PS_ERR_N_ERR_CLASSES, NULL},
};
\end{verbatim}

\subsection{Example}

The following output:
\begin{verbatim}
Traceback:
tst.error.primary              I/O error                      Primary error
 tst.error.middle              unknown error                  Secondary error
  tst.error                    unknown error                  Toplevel error
Last error is of unknown type
Third oldest error is of type IO
No errors. Hurrah
\end{verbatim}

produced by running this code:

\begin{verbatim}
#include "psLib.h"

static int primary(int i)
{
    if (i != 0) {                       // let's pretend it's an I/O error
        return psError("tst.error.primary", PS_ERR_IO, 1, "Primary error");
    }

    return 0;
}

static int middle(void)
{
    if (primary(1) != 0) {
        return psError("tst.error.middle", PS_ERR_UNKNOWN, 0, "Secondary error");
    }

    return 0;
}

static int toplevel(void)
{
    if (middle() != 0) {
        return psError("tst.error", PS_ERR_UNKNOWN, 0, "Toplevel error");
    }

    return 0;
}

int main(void)
{
    if (toplevel() != 0) {
        psErrorStackPrint(stdout, "Traceback:\n");

        if (psLastError()->code == PS_ERR_UNKNOWN) {
            fprintf(stderr, "Last error is of unknown type\n");
        }
        if (psGetError(2)->code == PS_ERR_IO) {
            fprintf(stderr, "Third oldest error is of type IO\n");
        }
    }

    psErrorClear();
    psErrorStackPrint(stdout, "Traceback:\n");

    if (psLastError()->code == PS_ERR_NONE) {
        fprintf(stderr, "No errors. Hurrah\n");
    }

    return 0;
}
\end{verbatim}

%------------------------------------------------------------------------------

\end{document}
