Index: /trunk/doc/pslib/psLibSDRS.tex
===================================================================
--- /trunk/doc/pslib/psLibSDRS.tex	(revision 515)
+++ /trunk/doc/pslib/psLibSDRS.tex	(revision 516)
@@ -1,3 +1,3 @@
-%%% $Id: psLibSDRS.tex,v 1.36 2004-04-09 05:16:54 price Exp $
+%%% $Id: psLibSDRS.tex,v 1.37 2004-04-26 22:19:55 price Exp $
 \documentclass[panstarrs]{panstarrs}
 
@@ -27,7 +27,7 @@
 \RevisionsStart
 % version  Date            Description
-DR-1       & 2003 Mar 11 & Draft \\
-\hline
-DR-2       & 2003 Mar 29 & Reorganized, spell-checked \\
+DR & 2004 Mar 29 & Draft \\ \hline
+00 & 2004 Apr 1  & First version, sent to MHPCC \\ \hline
+01 & 2004 Apr 23 & Added section on error handling \\ \hline
 \RevisionsEnd
 
@@ -35,5 +35,5 @@
 
 \DocumentsInternal
-PSCD-430-xxx  &   PS-1 Design Reference Mission \\ \hline
+PSCD-130-001  &   PS-1 Design Reference Mission \\ \hline
 PSCD-430-004  &   Pan-STARRS IPP C Code Conventions \\ \hline
 PSCD-430-005  &   Pan-STARRS IPP SRS \\ \hline
@@ -801,37 +801,246 @@
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
-\subsection{Miscellaneous Utilities}
-
-We require several very low-level functions.  Two functions provide
-conveniences tied to the logging facilities:
-%
+\subsection{Error Handling}
+\hlabel{errorStack}
+
+\PS{} errors shall be raised using a function, \code{psError}, with
+the caller supplying a component name and error message.  It is
+desireable to be able to trace an error through the program so that
+the events that led to the error may be quickly and clearly
+identified.
+
+\begin{verbatim}
+/// 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
+    );
+
+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;
+\end{verbatim}
+
+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 an error onto
+a stack; this stack is cleared if \code{psErrorStatus} is true, or by a call
+to \code{psErrorClear}.  The errors are defined as the following:
+
+\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;
+\end{verbatim}
+
+
+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}).
+
+\begin{verbatim}
+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)
+\end{verbatim}
+
+The error stack may be completely cleared:
+
+\begin{verbatim}
+void psErrorClear(void);                ///< Clear the error stack
+\end{verbatim}
+
+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}.
+
+\begin{verbatim}
+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
+\end{verbatim}
+
+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.
+
+The routine \code{psErrorCodeString} returns the string associated
+with an error code:
+
+\begin{verbatim}
+const char *psErrorCodeString(psErrorCode code);        ///< return the string associated with an error code.
+\end{verbatim}
+
+
+\subsubsection{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}
+
+\subsubsection{Example}
+
+The following example 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}
+
+
+Produces 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}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\subsection{Abort}
+
+\code{psAbort}, shall call \code{psMsgLog} with a
+level of \code{PS_LOG_ABORT}, and then call \code{abort}.
+
 \begin{verbatim}
 void psAbort(char *name, char *fmt,...);
-void psError(char *name, char *fmt,...);
-\end{verbatim}
-%
-The first function, \code{psAbort}, shall call \code{psMsgLog} with a
-level of \code{PS_LOG_ABORT}, and then call \code{abort}.  The second
-function, \code{psError}, shall call \code{psMsgLog} with a level of
-\code{PS_LOG_ERROR}, and then return.  In cases of doubt, a good
-choice for \code{name} is \code{__func__}.  
-
-A few useful string functions are also necessary:
-%
-\begin{verbatim}
-PS_STRING(S);
-char *psStringCopy(char *string);
-char *psStringNCopy(char *string, int nChar);
-\end{verbatim}
-%
-The first function simply converts the argument to a string
-\tbd{explanation of usage and rationale?}.  The second function,
-\code{psStringCopy}, shall allocate a sufficient memory block and
-return a copy of the input string.  Similarly, \code{psStringNCopy}
-shall allocate exactly \code{nChar} bytes and copy as much of the
-input string as possible into the resulting block without overrunning
-the available space.  The last byte of the output string is required
-to be \code{NULL}, as well as any additional bytes if \code{string} is
-not long enough to fill the output string.
+\end{verbatim}
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2580,10 +2789,4 @@
 second representation employs a hash table which allows fast look-up
 given a specific metadata keyword.
-
-When we refer to ``metadata'' (especially in the case of images), we
-generally mean a \code{psMetadata}:
-\begin{verbatim}
-typedef psMetadata psMetadata;
-\end{verbatim}
 
 An example of the usage of the metadata APIs is as follows:
@@ -2739,4 +2942,8 @@
 and the \tbd{first} item is returned.
 
+Care should be taken not to leak memory when appending an item for
+which the key already exists in the metadata (and is not
+\code{PS_META_NON_UNIQUE}).
+
 \begin{verbatim}
 /// delete entry from the metadata
