Index: trunk/archive/pslib/src/Utils/error.c
===================================================================
--- trunk/archive/pslib/src/Utils/error.c	(revision 332)
+++ trunk/archive/pslib/src/Utils/error.c	(revision 332)
@@ -0,0 +1,193 @@
+/*
+ * Support a Pan-STARRS error stack
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "psLogMsg.h"
+#include "psMemory.h"
+#include "psMisc.h"
+#include "psError.h"
+#include "psLib.h"
+
+/************************************************************************************************************/
+/*
+ * Description strings for errors
+ */
+#include "psErrorCodes.h"
+#include "psErrorCodes.c"
+
+static char *errorStringList[PS_ERR_N_ERR_CLASSES]; // error strings indexed by errorCode
+
+/************************************************************************************************************/
+/*
+ * Here's the error stack
+ */
+typedef struct errStack {
+    psErr error;
+    struct errStack *next;		// pointer to next message on the stack
+} errStack;
+
+static errStack *errorStack = NULL;	// the error stack itself, last message pushed is FIRST
+int nerror = 0;				// number of errors
+
+/************************************************************************************************************/
+/*
+ * Push an element onto the error stack
+ */
+void errStackPush(const char *name,		// name of error
+		  psErrorCode code,		// class of error
+		  const char *fmt,		// format for error message
+		  va_list ap)			// optional arguments for error
+{
+    errStack *err = psAlloc(sizeof(errStack));
+
+    err->next = errorStack;
+    errorStack = err;
+    nerror++;
+
+    err->error.name = psStringCopy(name);
+    err->error.code = code;
+
+    char buff[256];
+    int n = vsnprintf(buff, sizeof(buff), fmt, ap);
+    err->error.msg = strcpy(psAlloc(n + 1), buff);
+}
+/*
+ * Free an errStack element; don't worry about fixing the ->next pointer (that's why it's Del not Free)
+ */
+void errStackDel(errStack *err)
+{
+    if (err == NULL) {
+	return;
+    }
+
+    psFree(err->error.name);
+    psFree(err->error.msg);
+    psFree(err);
+}
+
+/************************************************************************************************************/
+/*
+ * Push an error onto the errorstack and print an error message
+ */
+int psError(const char *name,		// category of code that caused the error
+	    psErrorCode code,		// code of error (equivalent to errno)
+	    psErrorStatus status,	// is this a new error?
+	    const char *fmt, ...)	// format and possible extra arguments
+{
+    if (code == PS_ERR_NONE || status == PS_NEW_ERROR) { // free old error stack
+	errStack *eptr = errorStack;
+
+	while (eptr != NULL) {
+	    errStack *next = eptr->next;
+	    errStackDel(eptr);
+	    eptr = next;
+	}
+
+	errorStack = NULL;
+	nerror = 0;
+    }
+
+    if (code != PS_ERR_NONE) {
+	va_list ap;
+	va_start(ap, fmt);
+	errStackPush(name, code, fmt, ap);
+	va_end(ap);
+	
+	va_start(ap, fmt);
+	p_psVLogMsg(name, PS_LOG_ERROR, fmt, ap);
+	va_end(ap);
+    }
+
+    return code;
+}
+
+/************************************************************************************************************/
+/*
+ * Clear the error stack
+ */
+void psErrorClear(void)
+{
+    psError("", PS_ERR_NONE, 1, "");
+}
+
+/************************************************************************************************************/
+/*
+ * Return the last error; if there is none, return one with code "PS_ERR_NONE"
+ */
+const psErr *psLastError(void)
+{
+    return psGetError(0);
+}
+
+/*
+ * Return specified error (0: last error, 1: previous error, and so on)
+ */
+const psErr *psGetError(int which)
+{
+    static psErr noError = { "", PS_ERR_NONE, "" };
+
+    errStack *eptr = errorStack;
+    for (int i = 0; i < which && eptr != NULL; i++) {
+	eptr = eptr->next;
+    }
+
+    return (eptr == NULL) ? &noError : &eptr->error;
+}
+
+/************************************************************************************************************/
+/*
+ * Print the header then error stack to the provided file descriptor
+ */
+void psErrorStackPrint(FILE *fd,	// write to this file descriptor
+		       const char *fmt,	// format for any header information; may be NULL
+		       ...)		// arguments for format
+{
+    static int first = 1;
+    
+    if (first) {			// need to build the errorStringList
+	first = 0;
+
+	for (int i = 0; i < PS_ERR_N_ERR_CLASSES; i++) {
+	    errorStringList[i] = NULL;
+	}
+
+	for (int i = 0; i < PS_ERR_N_ERR_CLASSES; i++) {
+	    if (errorStrings[i].descrip == NULL) {
+		break;
+	    }
+	    
+	    errorStringList[errorStrings[i].code] = errorStrings[i].descrip;
+	}
+    }
+
+    if (errorStack == NULL) {
+	return;
+    }
+    /*
+     * Print header?
+     */
+    if (fmt != NULL) {
+	va_list ap;
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	va_end(ap);
+    }
+    /*
+     * Print error stack
+     */
+    const errStack *stack[nerror];	// we need to reverse the list
+    errStack *eptr = errorStack;
+    for (int i = 0; i < nerror; i++) {
+	stack[nerror - i - 1] = eptr;
+	eptr = eptr->next;
+    }
+    
+    for (int i = 0; i < nerror; i++) {
+	fprintf(fd, "%*s%-*s %-30s %s\n",
+		i, "", 30 - i, stack[i]->error.name,
+		errorStringList[stack[i]->error.code], stack[i]->error.msg);
+    }
+}
Index: trunk/archive/pslib/src/Utils/psErrorCodes.dat
===================================================================
--- trunk/archive/pslib/src/Utils/psErrorCodes.dat	(revision 332)
+++ trunk/archive/pslib/src/Utils/psErrorCodes.dat	(revision 332)
@@ -0,0 +1,9 @@
+#
+# 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
Index: trunk/archive/pslib/src/Utils/tst_error.c
===================================================================
--- trunk/archive/pslib/src/Utils/tst_error.c	(revision 332)
+++ trunk/archive/pslib/src/Utils/tst_error.c	(revision 332)
@@ -0,0 +1,54 @@
+#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)
+{
+    psSetTraceLevel("", 10);		// turn on all tracing
+    psSetLogDestination(PS_LOG_NONE);	// turn off error logging
+
+    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;
+}
