Index: /tags/v1_0_0/archive/pslib/src/Makefile
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Makefile	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Makefile	(revision 144)
@@ -0,0 +1,30 @@
+CC = cc -std=c99
+CFLAGS = -Wall -g $(INCLUDES)
+
+#
+# N.b. Utils must come first:
+#
+DIRS = Utils Metadata
+
+.PHONY : init
+init :
+	@for d in $(DIRS); do \
+	    echo "cd $$d; $(MAKE)"; \
+	    (cd $$d; $(MAKE)); \
+	done
+#
+_tags :;
+tags : _tags
+	etags */*.[ch]
+#
+clean :
+	$(RM) $(PROGS) *.o *.a *.log *.dvi *.aux *.toc *.log *.out *~ core TAGS
+	@for d in $(DIRS); do \
+	    echo "cd $$d; $(MAKE) clean"; \
+	    (cd $$d; $(MAKE) clean); \
+	done
+empty : clean
+	@for d in $(DIRS); do \
+	    echo "cd $$d; $(MAKE) empty"; \
+	    (cd $$d; $(MAKE) empty); \
+	done
Index: /tags/v1_0_0/archive/pslib/src/Metadata/Makefile
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Metadata/Makefile	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Metadata/Makefile	(revision 144)
@@ -0,0 +1,23 @@
+CC = cc -std=c99
+CFLAGS = -Wall -g $(INCLUDES)
+
+INCLUDES = -I$(UTILS_DIR)
+LIBS = -L$(UTILS_DIR) -lUtils
+
+UTILS_DIR = ../Utils
+
+PROGS = metadata
+
+.PHONY: init
+init : $(PROGS)
+
+metadata : metadata.o 
+	$(CC) -o metadata metadata.o $(LIBS)
+#
+_tags :;
+tags : _tags
+	etags *.[ch]
+#
+clean :
+	$(RM) $(PROGS) *.o *.a *.log *.dvi *.aux *.toc *.log *.out *~ core TAGS
+empty : clean
Index: /tags/v1_0_0/archive/pslib/src/Metadata/metadata.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Metadata/metadata.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Metadata/metadata.c	(revision 144)
@@ -0,0 +1,209 @@
+/*
+ * A strawman implementation of the Pan-STARRS metadata system
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include "psUtils.h"
+#include "psMetaData.h"
+/*
+ * Return a properly filled-out psMetaData
+ *
+ * N.b. For types PS_META_FLOAT, PS_META_INT, and PS_META_STR, a COPY
+ * of the data will be kept. In all other cases, only a POINTER
+ * to the data will be kept.
+ *
+ * An unused argument, copy, is provided to handle this better
+ * once we have copy constructors available.
+ */
+psMetaData *psMetaDataNew(
+    const char *name,			// name of new item of metadata
+    psMetaDataType type,		// type of this piece of metadata
+    const void *data,			// value of new item
+    const char *comment,		// comment associated with item;
+					// may be NULL
+    int copy)				// always copy data; NOTUSED
+{
+    static int id = 0;			// unique ID for an item of metadata
+    psMetaData *ms = psAlloc(sizeof(psMetaData));
+
+    *(int *)&ms->id = id++;		// need to cast away const
+    ms->type = type;
+
+    assert (name != NULL);
+    ms->name = psAlloc(strlen(name) + 1);
+    strcpy(ms->name, name);
+
+    if(comment == NULL) {
+	comment = "";
+    }
+    ms->comment = psAlloc(strlen(comment) + 1);
+    strcpy(ms->comment, comment);
+
+    assert (type >= 0 && type < PS_META_NTYPE);
+    switch (type) {
+      case PS_META_FLOAT:
+	*(float *)&ms->val.f = *(float *)data;
+	break;
+	
+      case PS_META_INT:
+	*(int *)&ms->val.i = *(int *)data;
+	break;
+	
+      case PS_META_STR:
+	*(void **)&ms->val.v = psAlloc(strlen(data) + 1);
+	strcpy(ms->val.v, data);
+	break;
+	
+      case PS_META_IMG:
+      case PS_META_JPEG:
+      case PS_META_PNG:
+      case PS_META_ASTROM:
+      case PS_META_UNKNOWN:
+	*(void **)ms->val.v = (void *)data;
+	break;
+      case PS_META_NTYPE:
+	psAbort(__FILE__, "You cannot get here (%d)\n", __LINE__);
+	break;				// NOTREACHED
+    }
+
+    return ms;
+}
+
+void psMetaDataDel(psMetaData *ms)	// piece of metadata to destroy
+{
+    if(ms == NULL) {
+	return;
+    }
+
+    psFree(ms->name);
+    psFree(ms->comment);
+
+    assert (ms->type >= 0 && ms->type < PS_META_NTYPE);
+    switch (ms->type) {
+      case PS_META_FLOAT:
+      case PS_META_INT:
+	break;				// nothing to do
+	
+      case PS_META_STR:
+	psFree(ms->val.v);
+	break;
+	
+      case PS_META_IMG:
+      case PS_META_JPEG:
+      case PS_META_PNG:
+      case PS_META_ASTROM:
+      case PS_META_UNKNOWN:
+	break;				// we don't own the memory
+      case PS_META_NTYPE:
+	psAbort(__FILE__, "You cannot get here (%d)\n", __LINE__);
+	break;				// NOTREACHED
+    }
+
+    psFree(ms);
+    
+}
+
+/*****************************************************************************/
+
+void psMetaDataPrint(FILE *fd,		// file descriptor to write to
+		     const psMetaData *ms) // item of metadata to print
+{
+    if(ms == NULL) {
+	return;
+    }
+
+    fprintf(fd, "Name:     %-40s (ID: %d)\n", ms->name, ms->id);
+    if (*ms->comment != '\0') {
+	fprintf(fd, "Comment:  %s\n", ms->comment);
+    }
+    fprintf(fd, "Value:    ");
+
+    assert (ms->type >= 0 && ms->type < PS_META_NTYPE);
+    switch (ms->type) {
+      case PS_META_FLOAT:
+	fprintf(fd, "%g\n", ms->val.f);
+	break;
+
+      case PS_META_INT:
+	fprintf(fd, "%d\n", ms->val.i);
+	break;
+	
+      case PS_META_STR:
+	fprintf(fd, "%s\n", (char *)ms->val.v);
+	break;
+	
+      case PS_META_IMG:
+	fprintf(fd, "(IMG)\n");
+	break;
+	
+      case PS_META_JPEG:
+	fprintf(fd, "(JPG)\n");
+	break;
+	
+      case PS_META_PNG:
+	fprintf(fd, "(PNG)\n");
+	break;
+	
+      case PS_META_ASTROM:
+	fprintf(fd, "(ASTROM)\n");
+	break;
+	
+      case PS_META_UNKNOWN:
+	fprintf(fd, "(UNKNOWN)\n");
+	break;
+
+      case PS_META_NTYPE:
+	psAbort(__FILE__, "You cannot get here (%d)\n", __LINE__);
+	break;				// NOTREACHED
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Test code
+ */
+#if 1
+#include <math.h>
+
+int main(void)
+{
+    psDlist *list = psDlistNew(NULL);	// list of metadata
+
+    const int ten = 10;
+    psDlistAppend(list, psMetaDataNew("const.ten", PS_META_INT, &ten, NULL,0));
+    const float sqrt2 = sqrt(2);
+    psDlistAppend(list, psMetaDataNew("const.sqrt2", PS_META_FLOAT, &sqrt2,
+				      "square root of two", 0));
+    psDlistAppend(list, psMetaDataNew("lang.hello", PS_META_STR, "Bonjour",
+				      "French", 0));
+    /*
+     * Print that data
+     */
+    for (psDlistElem *ptr = list->head; ptr != NULL; ptr = ptr->next) {
+	psMetaDataPrint(stdout, ptr->data);
+    }
+
+    fprintf(stdout, "------\n");
+    /*
+     * Print it again
+     */
+    (void)psDlistGet(list, PS_DLIST_TAIL); // initialise iterator
+    psMetaData *ms = NULL;
+    while ((ms = psDlistGet(list, PS_DLIST_PREV)) != NULL) {
+	psMetaDataPrint(stdout, ms);
+    }
+    /*
+     * Clean up
+     */
+    psDlistDel(list, (void (*)(void *))psMetaDataDel);
+    /*
+     * Check for memory leaks
+     */
+    fprintf(stderr,"Checking for memory leaks:\n");
+    psMemCheckLeaks(0, NULL, stderr);
+    
+    return 0;
+}
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Metadata/psAstrom.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Metadata/psAstrom.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Metadata/psAstrom.h	(revision 144)
@@ -0,0 +1,10 @@
+#if !defined(PS_ASTROM_H)
+#define PS_ASTROM_H
+/*
+ * This is not a full implementation...
+ */
+typedef struct {
+   const int id;
+} psAstrom;
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Metadata/psImage.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Metadata/psImage.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Metadata/psImage.h	(revision 144)
@@ -0,0 +1,10 @@
+#if !defined(PS_IMAGE_H)
+#define PS_IMAGE_H
+/*
+ * This is not a full implementation...
+ */
+typedef struct {
+   const int id;
+} psImage;
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Metadata/psMetaData.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Metadata/psMetaData.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Metadata/psMetaData.h	(revision 144)
@@ -0,0 +1,60 @@
+#if !defined(PS_META_DATA_H)
+#define PS_META_DATA_H 1
+
+#include "psImage.h"			// for psImage
+#include "psAstrom.h"			// for psAstrom
+/*
+ * Possible types of metadata.  
+ */
+typedef enum {				// type of val is:
+    PS_META_FLOAT = 0,			// float (.f)
+    PS_META_INT,			// int (.i)
+    PS_META_STR,			// string (.v)
+    PS_META_IMG,			// image (.v)
+    PS_META_JPEG,			// JPEG (.v)
+    PS_META_PNG,			// PNG (.v)
+    PS_META_ASTROM,			// astrometric coefficients (.v)
+    PS_META_UNKNOWN,			// other (.v)
+    PS_META_NTYPE			// Number of types; must be last
+} psMetaDataType;
+
+/*
+ * A struct to define a single item of metadata
+ */
+typedef struct {
+    const int id;			// unique ID for this item
+    
+    char *name;				// Name of item
+    psMetaDataType type;		// type of this item
+    const union {
+	float f;			// floating value
+	int i;				// integer value
+	void *v;			// other type
+    } val;				// value of metadata
+    char *comment;			// optional comment ("", not NULL)
+} psMetaData;
+
+/*****************************************************************************/
+/*
+ * Constructors and destructors
+ */
+psMetaData *psMetaDataNew(
+    const char *name,			// name of new item of metadata
+    psMetaDataType type,		// type of this piece of metadata
+    const void *val,			// value of new item
+    					// N.b. a pointer even if the item
+    					// is of type e.g. int
+    const char *comment,		// comment associated with item
+    int copy);				// always copy data; NOTUSED
+
+void psMetaDataDel(psMetaData *ms);	// piece of metadata to destroy
+
+/*****************************************************************************/
+/*
+ * Utilities
+ */
+void psMetaDataPrint(FILE *fd,		// file descriptor to write to
+		     const psMetaData *ms); // item of metadata to print
+
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/.gdb_history
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/.gdb_history	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/.gdb_history	(revision 144)
@@ -0,0 +1,477 @@
+rr
+up
+rr
+b 180
+rr
+p froot
+p *froot
+p *froot->subfacil[0]
+p *froot->subfacil[0]->subfacil[0]
+p *froot->subfacil[0]->subfacil[0]->subfacil[0]
+rr
+rr
+dis
+c
+rr
+d
+b 101
+rr
+c
+p2 facil->name, cpt0
+p2 facil->name cpt0
+rr
+dis
+c
+rr
+rr
+rr
+rr
+rr
+rr
+rr
+b 258
+rr
+call psGetTraceLevel("aaa")
+call psGetTraceLevel("aaa.bbb")
+call psGetTraceLevel("aaa.bbb.ccc")
+call psGetTraceLevel("aaa.BBB")
+call psGetTraceLevel("aaa.BBB.aa")
+call psGetTraceLevel("")
+rr
+q
+b 254
+rr
+call psGetTraceLevel("aaa.bbb.ddd")
+p highest_level 
+q
+rr
+rr
+rr
+ww
+rr
+ww
+up
+p fmt
+q
+rr
+ww
+up
+p ap
+whatis ap
+list
+p va_list
+whatis va_list
+rr
+b 216
+rr
+p name
+p cachedName
+b 212
+rr
+c
+p name
+p cachedName
+n
+p name
+p level
+c
+rr
+cond 2 name[0] == 'u'
+c
+p2 name cachedName
+n
+b 108
+rr
+p cpt0
+p rest
+p *facil
+rr
+dis
+c
+c
+rr
+ena
+rr
+p *facil
+n
+rr
+p *facil
+rr
+dis
+c
+c
+rr
+rr
+up
+l
+p facil
+p level
+l
+where
+call psGetTraceLevel("aaa.bbb.ddd")
+b 311
+rr
+call psGetTraceLevel("aaa.bbb.ddd")
+call psGetTraceLevel("aaa.bbb.ccc")
+call psGetTraceLevel("aaa.bbb")
+call psGetTraceLevel("")
+call psGetTraceLevel("aaa.bbb.ccc")
+b 207
+call psGetTraceLevel("aaa.bbb.ccc")
+rr
+dis 2
+c
+ena
+c
+up
+p *facil
+down
+p *facil
+p level
+rr
+dis
+c
+ena 1
+rr
+n
+call psGetTraceLevel("aaa.bbb.ccc")
+call psGetTraceLevel("")
+p froot->level
+rr
+rr
+rr
+rr
+rr
+q
+b 533
+b memory.c:533
+rr
+rr
+b memAllocateCB0
+b main
+call psMemSetAllocateID(1)
+r
+call psMemSetAllocateID(1)
+c
+up
+b psDlistDel
+f0
+p *ptr
+c
+p *list
+p *(psMemBlock *)list
+p *((psMemBlock *)list - 1)
+n
+rr
+c
+q
+p/x 512
+p/x 511
+4b
+rr
+b psHashDel
+rr
+n
+p *table
+p *table->buckets
+p table->buckets[0]@16
+b 172
+rr
+s
+n
+p hash
+n
+p ptr
+n
+rr
+c
+p table->buckets[0]@16
+c
+rr
+dis
+c
+rr
+q
+rr
+b psHashDel
+rr
+n
+l
+b 93
+c
+p *(psMemBlock *)(bucket - 1)
+p *(psMemBlock *)(ptr - 1)
+p *((psMemBlock *)ptr - 1)
+p *ptr
+c
+b psFree
+rr
+c
+ww
+up
+l
+c
+q
+rr
+rr
+rr
+ww
+p *((psMemBlock *)ptr - 1)
+p *((psMemBlock *)id - 1)
+up
+p table->buckets[0]@16
+up
+p table->buckets[0]@16
+p i
+b psHashDel 
+rr
+p table->buckets[0]@16
+p *table->buckets[5]
+p *table->buckets[4]
+p *table->buckets[8]
+p *table->buckets[4]->data
+p table->buckets[4]->data
+p *((psMemBlock*)table->buckets[4]->data - 1)
+s
+l
+p i
+n
+p i
+l
+b 92
+c
+n
+p tmp
+s
+n
+p *((psMemBlock*)table->buckets[4]->data - 1)
+up
+p *((psMemBlock*)table->buckets[4]->data - 1)
+down
+n
+dis
+rr
+q
+b psHashLookup
+r
+s
+l
+b 130
+c
+p hash
+p ptr
+n
+p ptr->key
+p key
+n
+p ptr->data
+q
+q
+rr
+q
+rr
+b 217
+rr
+n
+p *id
+p name
+n
+rr
+n
+p *name
+p id
+p id->name
+c
+dis
+c
+ww
+q
+rr
+rr
+q
+rr
+up
+p ptr
+p table->buckets[0]@16
+b 149
+rr
+p *((psMemBlock *)ptr - 1)
+n
+p *((psMemBlock *)data - 1)
+rr
+c
+q
+rr
+up
+p fd
+q
+q
+rr
+q
+rr
+rrrr
+r
+ww
+p2 i j
+rr
+q
+rr
+rr
+b psLogMsg
+rr
+n
+p hostname
+p name
+n
+p name
+n
+p head
+rr
+dis
+c
+b p_psRealloc
+rr
+n
+p *ptr
+n
+p *ptr
+rr
+n
+p2 ptr *ptr
+n
+p2 ptr *ptr
+c
+rr
+n
+p *ptr
+whatis ptr
+p2 ptr *ptr
+n
+p2 ptr *ptr
+n
+n
+p t
+p *((psMemBlock *)t - 1)
+p *((psMemBlock *)t->arr - 1)
+dis
+rr
+q
+rr
+p *m
+up
+rr
+ww
+up
+down
+rr
+q
+rr
+p ptrt
+p ptr
+p *ptr
+p i
+up
+ww
+b 287]
+b 287
+rr
+p list
+p *list
+p *list->head
+p *list->head->next
+p *list->head->next->next
+p *list->head->next->next->data
+p *(X *)(list->head->next->next->data)
+n
+p *ptr
+rr
+dis
+c
+q
+rr
+ww
+b psDlistToArray
+rr
+p *dlist
+n
+p *arr
+p *arr->arr[0]
+p arr->arr[0]
+n
+p i
+p n
+p *ptr
+n
+p *ptr
+n
+p arr->arr[0]@3
+rr
+c
+ww
+f 5
+p elem
+p *elem
+rr
+dis
+c
+rr
+rr
+rr
+dis
+d
+b 307
+rr
+p arr
+whatis arr
+ptype *arr
+ptype *arr->arr
+ptype *arr->arr[0]
+ptype psVoidPtr
+whatis arr->arr
+whatis arr->arr[0]
+p (X *)(arr->arr[0])->i
+p (X *)(arr->arr[0])
+rr
+c
+d
+rr
+rr
+rr
+rr
+rr
+up
+ww
+b psVoidPtrArrayDel
+rr
+p *arr
+n
+rr
+dis
+c
+p arr
+rr
+up
+down
+p *vptr
+p *ptr
+ww
+where
+rr
+q
+b psLogMsg
+rr
+up
+l
+p file
+p *ptr
+up
+l
+rr
+up
+down
+p *ptr
+p sizeof(ptr)
+p sizeof(*ptr)
+q
+rr
+q
Index: /tags/v1_0_0/archive/pslib/src/Utils/.log
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/.log	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/.log	(revision 144)
@@ -0,0 +1,34 @@
+This is pdfTeX, Version 3.14159-1.10a-devel (Web2C 7.3.8) (format=pdflatex 2003.1.31)  2 MAR 2004 11:42
+**utils.tex
+(/usr/local/tetex/share/texmf/tex/latex/tools/.tex{/usr/local/tetex/share/texmf
+.local/pdftex/config/pdftex.cfg
+Warning: pdftex (file /usr/local/tetex/share/texmf.local/pdftex/config/pdftex.c
+fg): invalid line in config file: `pdf_minorversion 4'
+}
+LaTeX2e <2001/06/01>
+Babel <v3.7h> and hyphenation patterns for american, british, nohyphenation, lo
+aded.
+File ignored)
+*
+! Interruption.
+<*> 
+    
+? 
+! Emergency stop.
+<*> 
+    
+End of file on the terminal!
+
+ 
+Here is how much of TeX's memory you used:
+ 6 strings out of 60983
+ 162 string characters out of 1189764
+ 44491 words of memory out of 2500001
+ 3092 multiletter control sequences out of 10000+50000
+ 3640 words of font info for 14 fonts, out of 500000 for 1000
+ 22 hyphenation exceptions out of 1000
+ 5i,0n,1p,89b,7s stack positions out of 1500i,500n,5000p,200000b,5000s
+ 0 PDF objects out of 300000
+ 0 named destinations out of 131072
+ 0 words of extra memory for PDF output out of 65536
+No pages of output.
Index: /tags/v1_0_0/archive/pslib/src/Utils/Makefile
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/Makefile	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/Makefile	(revision 144)
@@ -0,0 +1,26 @@
+CC = cc -std=c99
+CFLAGS = -Wall -g
+
+libUtils.a : array.o dlist.o hash.o logmsg.o memory.o misc.o trace.o
+	ar r libUtils.a $?
+	ranlib libUtils.a
+#
+# Test code (change #if 0 to #if 1 in ONE source file to build)
+#
+array : libUtils.a array.o
+	$(CC) -o array array.o libUtils.a
+dlist : libUtils.a dlist.o
+	$(CC) -o dlist dlist.o libUtils.a
+logmsg : libUtils.a logmsg.o
+	$(CC) -o logmsg logmsg.o libUtils.a
+memory : libUtils.a
+	$(CC) -o memory memory.o libUtils.a
+#
+#
+_tags :;
+tags : _tags
+	etags *.[ch]
+#
+clean :
+	$(RM) $(PROGS) *.o *.a *.log *.dvi *.aux *.toc *.log *.out *~ core TAGS
+empty : clean
Index: /tags/v1_0_0/archive/pslib/src/Utils/Private/p_psMemory.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/Private/p_psMemory.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/Private/p_psMemory.h	(revision 144)
@@ -0,0 +1,17 @@
+#if !defined(P_PS_MEMORY_H)
+#define P_PS_MEMORY_H
+
+#define P_PS_MEMMAGIC (void *)0xdeadbeef // Magic number in psMemBlock header
+
+/*****************************************************************************/
+/*
+ * When to call the allocate/free callbacks.  Set using
+ * psMemSetAllocateID/psMemSetFreeID, but it can be
+ * convenient to see these directly from the debugger
+ */
+extern long p_psMemAllocateID;		// notify user this block is allocated
+extern long p_psMemFreeID;		// notify user this block is freed
+
+/*****************************************************************************/
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/array.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/array.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/array.c	(revision 144)
@@ -0,0 +1,92 @@
+/*
+ * Arrays.  Almost all the array support is in fact in psArray.h,
+ * but we directly support arrays of (void *) here, so as to be
+ * able to free each element properly
+ */
+#include "psUtils.h"
+
+typedef void *psVoidPtr;
+
+P_PS_CREATE_ARRAY_TYPE(static, my_, psVoidPtr);
+
+psVoidPtrArray *psVoidPtrArrayNew(int n, int s)
+{
+    return my_psVoidPtrArrayNew(n, s);
+}
+
+psVoidPtrArray *psVoidPtrArrayRealloc(psVoidPtrArray *arr, int n)
+{
+    return my_psVoidPtrArrayRealloc(arr, n);
+}
+
+void psVoidPtrArrayDel(psVoidPtrArray *arr,
+		       void (*elemDel)(void *)) // destructor for data on array
+{
+    if (arr == NULL) {
+	return;
+    }
+	
+    for (int i = 0; i < arr->n; i++) {
+	if (elemDel == NULL) {
+	    psMemDecrRefCounter(arr->arr[i]);
+	} else {
+	    elemDel(psMemDecrRefCounter(arr->arr[i]));
+	}
+    }
+    my_psVoidPtrArrayDel(arr);
+}
+
+/*****************************************************************************/
+#if 0					// testing
+
+typedef struct {
+    int x, y;
+} psXY;
+
+psXY *psXYNew(void)
+{
+    return psAlloc(sizeof(psXY));
+}
+
+void psXYDel(psXY *xy)
+{
+    psFree(xy);
+}
+
+PS_DECLARE_ARRAY_TYPE(psXY);
+PS_CREATE_ARRAY_TYPE(psXY);
+
+PS_DECLARE_ARRAY_PTR_TYPE(psXY);
+PS_CREATE_ARRAY_PTR_TYPE(psXY);
+
+int main(void)
+{
+    psXYArray *t = psXYArrayNew(10, 15);
+    psXYPtrArray *pt = psXYPtrArrayNew(10, 10);
+
+    for (int i = 0; i < t->n; i++) {
+	t->arr[i].x = i;
+	pt->arr[i]->y = 10*i;
+    }
+
+    t = psXYArrayRealloc(t, 5);
+    t = psXYArrayRealloc(t, 8);
+
+    for (int i = 0; i < t->n; i++) {
+	printf("%d %d  ", t->arr[i].x, pt->arr[i]->y);
+    }
+    printf("\n");
+    
+    psXYArrayDel(t);
+
+    psXY *xy = psMemDecrRefCounter(pt->arr[0]);
+    pt->arr[0] = NULL;
+    psXYDel(xy);    
+    
+    psXYPtrArrayDel(pt);
+
+    psMemCheckLeaks(0, NULL, stderr);
+
+    return 0;
+}
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/dlist.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/dlist.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/dlist.c	(revision 144)
@@ -0,0 +1,360 @@
+/*
+ * Minimal support for doubly linked lists
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "psUtils.h"
+
+static psDlistElem *dlistElemNew(void)
+{
+    return(psAlloc(sizeof(psDlistElem)));
+}
+
+static void dlistElemDel(psDlistElem *elem)
+{
+    psFree(elem);
+}
+
+/*****************************************************************************/
+
+psDlist *psDlistNew(void *data)		// initial data item; may be NULL
+{
+    psDlist *list = psAlloc(sizeof(psDlist));
+
+    list->n = 0;
+    list->head = list->tail = NULL;
+    list->iter = NULL;
+    if (data != NULL) {
+	psDlistAdd(list, data, PS_DLIST_TAIL);
+    }
+
+    return list;
+}
+
+void psDlistDel(psDlist *list,		// list to destroy
+		void (*elemDel)(void *)) // destructor for data on list
+{
+    if (list == NULL) {
+	return;
+    }
+
+    psDlistElem *ptr = list->head;
+    while (ptr != NULL) {
+	psDlistElem *next = ptr->next;
+
+	if (elemDel == NULL) {
+	    psMemDecrRefCounter(ptr->data);
+	} else {
+	    elemDel(psMemDecrRefCounter(ptr->data));
+	}
+	dlistElemDel(ptr);
+	
+	ptr = next;
+    }
+
+    psFree(list);
+}
+
+/*****************************************************************************/
+/*
+ * Add an element to a list
+ */
+psDlist *psDlistAdd(psDlist *list,	// list to add to; may be NULL
+		    void *data,		// data to add
+		    int where)		// where to add data. PS_DLIST_HEAD,
+					// PS_DLIST_TAIL, or an integer
+{
+    psDlistElem *elem = dlistElemNew();
+
+    if (list == NULL) {
+	list = psDlistNew(NULL);
+    }
+
+    if (where > list->n) {		// XXX need better diagnostic here
+	fprintf(stderr, "Invalid index %d (only %d elements)\n",
+		where, list->n);
+	dlistElemDel(elem);		// cleanup
+	
+	return list;
+    }
+
+    if (where == PS_DLIST_HEAD) {	// easy
+	elem->prev = NULL;
+	elem->next = list->head;
+	elem->data = psMemIncrRefCounter(data);
+
+	if (list->head != NULL) {
+	    list->head->prev = elem;
+	}
+
+	list->head = elem;
+	if (list->tail == NULL) {
+	    list->tail = elem;
+	}
+	list->n++;
+    } else if (where == PS_DLIST_TAIL) { // also easy
+	elem->prev = list->tail;
+	elem->next = NULL;
+	elem->data = psMemIncrRefCounter(data);
+
+	if (list->tail != NULL) {
+	    list->tail->next = elem;
+	}
+
+	list->tail = elem;
+	if (list->head == NULL) {
+	    list->head = elem;
+	}
+	list->n++;
+    } else {				// XXX
+	fprintf(stderr, "Insertion into psDlists is not yet supported\n");
+	return psDlistAdd(list, data, PS_DLIST_TAIL);
+    }
+
+    return list;
+}
+
+/*****************************************************************************/
+
+psDlist *psDlistAppend(psDlist *list,	// list to add to; may be NULL
+		       void *data)	// data to add at end
+{
+    return psDlistAdd(list, data, PS_DLIST_TAIL);
+}
+
+/*****************************************************************************/
+/*
+ * Remove an element from a list
+ */
+void *psDlistRemove(psDlist *list,	// list to remove element from
+		    void *data,		// data item to remove (or NULL)
+		    int which)		// index of item, if known.
+					// PS_DLIST_UNKNOWN, or PS_DLIST_HEAD,
+					// or PS_DLIST_TAIL
+{
+    assert (list != NULL);
+
+    if (data != NULL) {
+	assert (which == PS_DLIST_UNKNOWN);
+
+	fprintf(stderr,"psDlistRemove does not yet support removal by data");
+	return NULL;
+    }
+
+    if (which == PS_DLIST_HEAD) {
+	if (list->head == NULL) {
+	    return NULL;
+	} else {
+	    data = list->head->data;
+	    if (list->head->next != NULL) {
+		list->head->next->prev = NULL;
+	    }
+	    list->head = list->head->next;
+	    list->n--;
+
+	    if (list->head == NULL) {
+		list->tail = NULL;
+	    }
+	}
+    } else if (which == PS_DLIST_TAIL) {
+	if (list->tail == NULL) {
+	    return NULL;
+	} else {
+	    data = list->tail->data;
+	    if (list->tail->prev != NULL) {
+		list->tail->prev->next = NULL;
+	    }
+	    list->tail = list->tail->prev;
+	    list->n--;
+
+	    if (list->tail == NULL) {
+		list->head = NULL;
+	    }
+	}
+    } else if (which != PS_DLIST_UNKNOWN) {
+	fprintf(stderr,"psDlistRemove does not yet support removal by index");
+	return NULL;
+    }
+
+    return psMemDecrRefCounter(data);
+}
+
+/*****************************************************************************/
+/*
+ * Retrieve an element from the list, or iterate over list.
+ *
+ * If which is psDlist{Next,Prev} return the next/previous
+ * data and move the iteration cursor 
+ *
+ * If which is psDlist{Head,Tail} initialise the iteration pointer
+ * and return the head/tail
+ *
+ * Otherwise, return the desired element
+ */
+#define ITER_INIT_HEAD ((void *)1)	// next iteration should return head
+#define ITER_INIT_TAIL ((void *)2)	// next iteration should return tail
+
+void *psDlistGet(
+    const psDlist *list,		// list to retrieve element from
+    int which)				// index of item, or PS_DLIST_NEXT,
+					// or PS_DLIST_PREV
+{
+    assert (list != NULL);
+
+    switch (which) {
+      case PS_DLIST_HEAD:		// easy
+	((psDlist *)list)->iter = ITER_INIT_HEAD;
+	return (list->head == NULL) ? NULL : list->head->data;
+      case PS_DLIST_TAIL:		// also easy
+	((psDlist *)list)->iter = ITER_INIT_TAIL;
+	return (list->tail == NULL) ? NULL : list->tail->data;
+      case PS_DLIST_PREV:		// use iterator
+      case PS_DLIST_NEXT:
+	if (list->iter == NULL) {
+	    return NULL;
+	} else if (list->iter == ITER_INIT_HEAD) {
+	    ((psDlist *)list)->iter = list->head;
+	} else if (list->iter == ITER_INIT_TAIL) {
+	    ((psDlist *)list)->iter = list->tail;
+	} else if (which == PS_DLIST_PREV) {
+	    ((psDlist *)list)->iter = list->iter->prev;
+	} else if (which == PS_DLIST_NEXT) {
+	    ((psDlist *)list)->iter = list->iter->next;
+	} else {
+	    fprintf(stderr,"You cannot get here\n");
+	    abort();
+	}
+
+	if (list->iter == NULL) {
+	    return NULL;
+	}
+	return list->iter->data;	// XXX what if data is NULL?
+
+	break;
+      default:				// XXX
+	fprintf(stderr,
+		"Retrieval from psDlists by index is not yet supported\n");
+	return NULL;
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Convert a psDlist to/from a psVoidPtrArray
+ */
+psVoidPtrArray *psDlistToArray(psDlist *restrict dlist)
+{
+    if (dlist == NULL) {
+	return NULL;
+    }
+    
+    psVoidPtrArray *restrict arr = psVoidPtrArrayNew(dlist->n, dlist->n);
+
+    psDlistElem *ptr = dlist->head;
+    for (int i = 0, n = dlist->n; i < n; i++) {
+	arr->arr[i] = ptr->data;
+
+	ptr->data = NULL;
+	ptr = ptr->next;
+    }
+
+    psDlistDel(dlist, NULL);
+
+    return arr;    
+}
+
+psDlist *psArrayToDlist(psVoidPtrArray *arr)
+{
+    psDlist *list = psDlistNew(NULL);	// list of elements
+
+    for (int i = 0, n = arr->n; i < n; i++) {
+	psDlistAppend(list,
+		      psMemDecrRefCounter(arr->arr[i])); // it's already Incr
+	arr->arr[i] = NULL;
+    }
+
+    psVoidPtrArrayDel(arr, NULL);
+
+    return list;
+}
+
+/*****************************************************************************/
+#if 0					// testing
+
+typedef struct { int i; } X;
+static X *xNew(int i) { X *x = psAlloc(sizeof(X)); x->i = i; return x; }
+static void xDel(X *x) { psFree(x); }
+
+int main(void)
+{
+    psDlist *list = psDlistNew(NULL);	// list of metadata
+
+    psDlistAppend(list, xNew(1));
+    psDlistAppend(list, xNew(2));
+    psDlistAppend(list, xNew(3));
+    /*
+     * Print that data
+     */
+    for (psDlistElem *ptr = list->head; ptr != NULL; ptr = ptr->next) {
+	printf("%d ", ((X *)(ptr->data))->i);
+    }
+    printf("\n");
+    /*
+     * Print it again
+     */
+    (void)psDlistGet(list, PS_DLIST_TAIL); // initialise iterator
+    X *x = NULL;
+    while ((x = psDlistGet(list, PS_DLIST_PREV)) != NULL) {
+	printf("%d ", x->i);
+    }
+    printf("\n");
+#if 1
+    /*
+     * Convert to an array
+     */
+    psVoidPtrArray *arr = psDlistToArray(list);
+    list = NULL;			// it's been freed
+
+    for (int i = 0; i < arr->n; i++) {
+	printf("%d ", ((X *)(arr->arr[i]))->i);
+    }
+    printf("\n");
+#if 1
+    /*
+     * Convert back to a list
+     */
+    list = psArrayToDlist(arr);
+    arr = NULL;				// it's been freed
+
+    (void)psDlistGet(list, PS_DLIST_TAIL); // initialise iterator
+    while ((x = psDlistGet(list, PS_DLIST_PREV)) != NULL) {
+	printf("%d ", x->i);
+    }
+    printf("\n");
+#endif
+    psVoidPtrArrayDel(arr, (void (*)(void *))xDel);
+#endif
+
+#if 1
+    list = psArrayToDlist(psDlistToArray(list));
+
+    (void)psDlistGet(list, PS_DLIST_TAIL); // initialise iterator
+    while ((x = psDlistGet(list, PS_DLIST_PREV)) != NULL) {
+	printf("%d ", x->i);
+    }
+    printf("\n");
+#endif
+    /*
+     * Clean up
+     */
+    psDlistDel(list, (void (*)(void *))xDel);
+    /*
+     * Check for memory leaks
+     */
+    fprintf(stderr,"Checking for memory leaks:\n");
+    psMemCheckLeaks(0, NULL, stderr);
+    
+    return 0;
+}
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/hash.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/hash.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/hash.c	(revision 144)
@@ -0,0 +1,245 @@
+/*
+ * An interface to hash tables for Pan-STARRS
+ *
+ * Collisions are handled by simple linked lists
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "psUtils.h"
+
+/*****************************************************************************/
+/*
+ * A bucket that holds an item of data
+ */
+typedef struct HashBucket {
+    char *key;				// key for this item of data
+    void *data;				// the data itself
+    struct HashBucket *next;		// list of other possible keys
+} HashBucket;
+
+/*
+ * An entire hash table
+ */
+struct HashTable {
+    int nbucket;			// number of buckets
+    HashBucket **buckets;		// the buckets themselves
+};
+
+/*****************************************************************************/
+/*
+ * Con/Destruct buckets
+ */
+static HashBucket *hashBucketNew(const char *key,
+				 void *data,
+				 HashBucket *next)
+{
+    HashBucket *bucket = psAlloc(sizeof(HashBucket *));
+
+    bucket->key = psStringCopy(key);
+    bucket->data = psMemIncrRefCounter(data);
+    bucket->next = next;
+
+    return bucket;
+}
+
+static void hashBucketDel(
+    HashBucket *bucket,			// bucket to free
+    void (*itemDel)(void *item))	// how to free hashed data;
+					// or NULL
+{
+    if (bucket == NULL) {
+	return;
+    }
+
+    psFree(bucket->key);
+    psMemDecrRefCounter(bucket->data);
+    
+    if (itemDel != NULL) {
+	itemDel(bucket->data);
+    }
+    
+    psFree(bucket);
+}
+
+/*****************************************************************************/
+/*
+ * Con/Destruct psHash tables
+ */
+psHash *psHashNew(int nbucket)		// initial number of buckets
+{
+    psHash *table = psAlloc(sizeof(psHash));
+    table->buckets = psAlloc(nbucket*sizeof(HashBucket *));
+    table->nbucket = nbucket;
+
+    psTrace("utils.hash", 1, "Creating %d-element hash table\n", nbucket);
+    
+    for (int i = 0; i < nbucket; i++) {
+	table->buckets[i] = NULL;
+    }
+
+    return table;
+}
+
+void psHashDel(psHash *table,		// hash table to be freed
+	       void (*itemDel)(void *item)) // how to free hashed data; or NULL
+{
+    if (table == NULL) {
+	return;
+    }
+/*
+ * Release data interned on the list, and delete the buckets
+ */
+    for (int i = 0; i < table->nbucket; i++) {
+	if (table->buckets[i] != NULL) {
+	    HashBucket *ptr = table->buckets[i];
+	    while (ptr != NULL) {
+		HashBucket *tmp = ptr->next;
+		hashBucketDel(ptr, itemDel);
+		ptr = tmp;
+	    }
+	}
+    }
+
+    psFree(table->buckets);
+    psFree(table);
+}
+
+/*****************************************************************************/
+/*
+ * Here's the routine to do the work of insertion/retrieval.
+ *
+ * We need itemDel if the key is already in use and we're inserting
+ *
+ * N.b. this is NOT a good hash function! See Knuth for some better ones
+ */
+static void *doHashWork(
+    psHash *table,			// table to insert in
+    const char *key,			// key to use
+    void *data,				// data to insert,
+					// or (if NULL) retrieve
+    void (*itemDel)(void *item))	// how to free hashed data
+					// or NULL
+{
+    long int hash = 1;
+
+    for (int i = 0, len = strlen(key); i < len; i++) {
+	hash = (hash << 1) ^ key[i];
+    }
+
+    hash &= (table->nbucket - 1);
+
+    HashBucket *ptr = table->buckets[hash];
+    /*
+     * We've found the correct hash bucket, now we need to know what to do
+     */
+    if (data == NULL) {			// retrieve
+	while (ptr != NULL) {
+	    if (strcmp(key, ptr->key) == 0) { // found it!
+		return ptr->data;
+	    }
+
+	    ptr = ptr->next;
+	}
+
+	return NULL;			// not in hash
+    } else {				// insert
+	while (ptr != NULL) {
+	    if (strcmp(key, ptr->key) == 0) { // found it!
+		if (itemDel == NULL) {
+		    return NULL;	// can't insert
+		}			
+
+		psTrace("utils.hash.insert", 3, "Replacing data for %s\n",
+			key);
+
+		itemDel(psMemDecrRefCounter(ptr->data));
+		ptr->data = psMemIncrRefCounter(data);
+
+		return data;
+	    }
+	}
+	/*
+	 * Not found, so insert at the front of the list
+	 */
+	table->buckets[hash] = hashBucketNew(key, data, table->buckets[hash]);
+
+	return data;
+    }
+}    
+
+/*****************************************************************************/
+/*
+ * Insert a value into a hash table
+ */
+void *psHashInsert(psHash *table,	// table to insert in
+		   const char *key,	// key to use
+		   void *data,		// data to insert
+		   void (*itemDel)(void *item))	// how to free hashed data;
+					// or NULL
+{
+    return doHashWork(table, key, data, itemDel);
+}
+
+/*****************************************************************************/
+/*
+ * Lookup a value in a hash table
+ */
+void *psHashLookup(psHash *table,	// table to lookup key in
+		   const char *key)	// key to lookup
+{
+    return doHashWork(table, key, NULL, NULL);
+}
+
+/*****************************************************************************/
+#if 0					// testing
+
+typedef struct { char *name; } ID;
+static ID *IdNew(const char *name)
+{
+    ID *id = psAlloc(sizeof(ID));
+    id->name = psStringCopy(name);
+
+    return id;
+}
+
+static void IdDel(ID *id) {
+    if (psMemGetRefCounter(id) > 1) {
+	psMemDecrRefCounter(id);
+	return;
+    }
+
+    psFree(id->name);
+    psFree(id);
+}
+
+int main(void)
+{
+    psSetTraceLevel("utils.hash", 3);
+    long memId0 = psMemGetId();
+
+    psHash *hash = psHashNew(16);
+
+    psHashInsert(hash, "Lynda", IdNew("Lee"), (void (*)(void *))IdDel);
+    psHashInsert(hash, "Lynda", IdNew("Lee"), (void (*)(void *))IdDel);
+    psHashInsert(hash, "Robert", IdNew("Lupton"), (void (*)(void *))IdDel);
+    psHashInsert(hash, "Robert", IdNew("the Good"), (void (*)(void *))IdDel);
+
+    char *names[] = {"Robert", "Lynda", "Rowan", NULL};
+    for (char **name = names; *name != NULL; name++) {
+	ID *id = psHashLookup(hash, *name);
+	printf("%s's surname is:\t%s\n", *name,
+	       ((id == NULL) ? "(unknown)" : id->name));
+    }
+
+    psHashDel(hash, (void (*)(void *))IdDel);
+    /*
+     * Check for memory leaks
+     */
+    fprintf(stderr,"Checking for memory leaks:\n");
+    psMemCheckLeaks(memId0, NULL, stderr);
+
+    return 0;
+}
+    
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/logmsg.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/logmsg.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/logmsg.c	(revision 144)
@@ -0,0 +1,168 @@
+/*
+ * A simple implementation of a logging facility for Pan-STARRS
+ */
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+#include "psUtils.h"
+
+/*****************************************************************************/
+
+static int logDest = PS_LOG_TO_STDERR;	// where to log messages
+static int logLevel = PS_LOG_INFO;	// log all messages at this or above
+
+/*****************************************************************************/
+/*
+ * Set the current log level and return old level
+ */
+int psSetLogLevel(int level)		// new level
+{
+    int oldLevel = logLevel;
+
+    if (level < 0 || level > 9) {
+	psLogMsg("logmsg", PS_LOG_WARN,
+		 "Attempt to set invalid logMsg level: %d", level);
+	level = (level < 0) ? 0 : 9;
+    }
+
+    logLevel = level;
+
+    return oldLevel;
+}
+
+/*****************************************************************************/
+
+int psSetLogDestination(int dest)	// new destination
+{
+    int old = logDest;
+
+    switch (dest) {
+      case PS_LOG_TO_STDOUT:
+      case PS_LOG_TO_STDERR:
+	logDest = dest;
+	break;
+
+      default:
+	fprintf(stderr,"Unknown logDestination: %d (ignored)\n", dest);
+	break;
+    }
+
+    return old;
+}
+
+/*****************************************************************************/
+#if !defined(HOST_NAME_MAX)		// should be in limits.h
+#  define HOST_NAME_MAX 256
+#endif
+
+void p_psVLogMsg(const char *name, int level, const char *fmt, va_list ap)
+{
+    static int first = 1;
+    static char hostname[HOST_NAME_MAX + 1];
+     
+    if (level > logLevel) {
+	return;
+    }
+
+    if (first) {
+	first = 0;
+	gethostname(hostname, HOST_NAME_MAX);
+    }
+    
+    char clevel;			// letter-name for level
+    switch (level) {
+      case PS_LOG_ABORT:
+	clevel = 'A';
+	break;
+	
+      case PS_LOG_ERROR:
+	clevel = 'E';
+	break;
+	
+      case PS_LOG_WARN:
+	clevel = 'W';
+	break;
+	
+      case PS_LOG_INFO:
+	clevel = 'I';
+	break;
+
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+      case 8:
+      case 9:
+	clevel = level + '0';
+	break;
+
+      default:
+	psTrace("utils.logMsg", 2, "Invalid logMsg level: %d (%s)\n",
+		level, fmt);
+	level = (level < 0) ? 0 : 9;
+	break;
+    }
+
+    char head[HOST_NAME_MAX + 40];	// yes, this is long enough
+    time_t clock = time(NULL);
+    struct tm *utc = gmtime(&clock);
+
+    sprintf(head, "%4d:%02d:%02d %02d:%02d:%02dZ|%-20s|%c|%-15s|",
+	    utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
+	    utc->tm_hour, utc->tm_min, utc->tm_sec,
+	    hostname, clevel, name);
+
+    switch (logDest) {
+      case PS_LOG_TO_STDOUT:
+	puts(head);
+	vprintf(fmt, ap);
+	if (fmt[strlen(fmt) - 1] != '\n') {
+	    putc('\n', stdout);
+	}
+	break;
+
+      case PS_LOG_TO_STDERR:
+	fputs(head, stderr);
+	vfprintf(stderr, fmt, ap);
+	if (fmt[strlen(fmt) - 1] != '\n') {
+	    putc('\n', stderr);
+	}
+	break;
+
+      default:
+	fprintf(stderr, "psLogMsg: You cannot get here (%s:%d)\n",
+		__FILE__, __LINE__);
+	abort();
+    }
+    
+    va_end(ap);
+}
+
+void psLogMsg(const char *name, int level, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+
+    p_psVLogMsg(name, level, fmt, ap);
+}
+
+/*****************************************************************************/
+#if 0					// testing
+
+int main(void)
+{
+    psSetTraceLevel("utils.logMsg", 10);
+    
+    psSetLogDestination(PS_LOG_TO_STDERR);
+    psSetLogLevel(PS_LOG_INFO);
+    
+    psLogMsg("aaa", PS_LOG_INFO, "Hello World\n");
+    psAbort("bbb", "I'm in Hawai`i");
+
+    return 0;
+}
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/memory.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/memory.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/memory.c	(revision 144)
@@ -0,0 +1,572 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "psUtils.h"
+#include "Private/p_psMemory.h"
+
+#undef psAlloc				// don't get the __FILE__ versions
+#undef psRealloc
+#undef psFree
+
+static int bad_memblock(const psMemBlock *m, int quiet);
+/*
+ * This strawman allocation package only handles up to NMEMBLOCK allocations
+ */
+#define NMEMBLOCK 10000			// max. number of calls to psAlloc
+static psMemBlock *memBlocks[NMEMBLOCK];
+
+/*****************************************************************************/
+/*
+ * Unique ID for allocated blocks
+ */
+static unsigned long memid = 0;
+
+/*****************************************************************************/
+
+void *psAlloc(size_t size)
+{
+    return p_psAlloc(size, "(unknown)", 0);
+}
+
+void *psRealloc(void *ptr, size_t size)
+{
+    return p_psRealloc(ptr, size, "(unknown)", 0);
+}
+
+void psFree(void *ptr)
+{
+    p_psFree(ptr, "(unknown)", 0);
+}
+
+/*****************************************************************************/
+/*
+ * Callbacks
+ */
+/*
+ * First the I-can't-get-the-memory callback
+ */
+static void *memExhaustedCB0(size_t size)
+{
+    return NULL;
+}
+static psMemExhaustedCallback memExhaustedCB = memExhaustedCB0;
+
+psMemExhaustedCallback psMemExhaustedSetCB(psMemExhaustedCallback func)
+{
+    psMemExhaustedCallback old = memExhaustedCB;
+
+    memExhaustedCB = (func != NULL) ? func : memExhaustedCB0;
+
+    return old;
+}
+/*
+ * then the I-have-detected-a-problem callback
+ */
+static void memProblemCB0(const psMemBlock *ptr,
+			  const char *file, int lineno)
+{
+    if (bad_memblock(ptr, 0)) {
+	fprintf(stderr, "Memory corruption or an attempt to manipulate memory "
+		"not allocated with psAlloc at %s", file);
+	if (lineno > 0) {
+	    fprintf(stderr, ":%d", lineno);
+	}
+	fprintf(stderr, "\n");
+    } else if(ptr->refCounter <= 0) {
+	psError(__func__, "Block %ld allocated at %s:%d freed more than once at %s:%d\n",
+		ptr->id, ptr->file, ptr->lineno, file, lineno);
+    } else if(ptr->refCounter > 1) {
+	psError(__func__, "Block %ld allocated at %s:%d freed while still referenced at %s:%d\n",
+		ptr->id, ptr->file, ptr->lineno, file, lineno);
+    }
+
+    if (lineno > 0) {
+	psAbort(__func__, "Detected memory corruption");
+    }
+}
+static psMemProblemCallback memProblemCB = memProblemCB0;
+
+psMemProblemCallback psMemProblemSetCB(psMemProblemCallback func)
+{
+    psMemProblemCallback old = memProblemCB;
+
+    memProblemCB = (func != NULL) ? func : memProblemCB0;
+
+    return old;
+}
+/*
+ * And now the I-want-to-be-informed callbacks
+ *
+ * Call the callbacks when these IDs are allocated/freed
+ */
+long p_psMemAllocateID = 0;		// notify user this block is allocated
+long p_psMemFreeID = 0;			// notify user this block is freed
+
+long psMemSetAllocateID(long id)	// set p_psMemAllocateID to id
+{
+    long old = p_psMemAllocateID;
+    p_psMemAllocateID = id;
+
+    return old;
+}
+
+long psMemSetFreeID(long id)		// set p_psMemFreeID to id
+{
+    long old = p_psMemFreeID;
+    p_psMemFreeID = id;
+
+    return old;
+}
+
+/*
+ * Default callback for both allocate and free. Note that the
+ * value of p_psMemAllocateID/p_psMemFreeID is incremented
+ * by the return value (so returning 0 means that the callback
+ * isn't resignalled)
+ */
+static int memAllocateCB0(const psMemBlock *ptr)
+{
+    static int incr = 0;		// "p_psMemAllocateID += incr"
+
+    assert (ptr != NULL);		// prevent compiler warnings
+    
+    return incr;
+}
+
+/*
+ * The default callbacks, and the routines to change them
+ */
+static psMemCallback memAllocateCB = memAllocateCB0;
+static psMemCallback memFreeCB = memAllocateCB0;
+
+psMemCallback psMemAllocateSetCB(psMemCallback func)
+{
+    psMemCallback old = memAllocateCB;
+
+    memAllocateCB = (func != NULL) ? func : memAllocateCB0;
+
+    return old;
+}
+
+psMemCallback psMemFreeSetCB(psMemCallback func)
+{
+    psMemCallback old = memFreeCB;
+
+    memFreeCB = (func != NULL) ? func : memAllocateCB0;
+
+    return old;
+}
+
+/*****************************************************************************/
+/*
+ * Return memory ID counter for next block to be allocated
+ */
+int psMemGetId(void)
+{
+    return memid + 1;
+}
+
+/*****************************************************************************/
+/*
+ * Routines to check the consistency of the allocated and/or free memory arena
+ *
+ * N.b. If the block wasn't allocated by psAlloc, it will appear corrupted
+ */
+#define ALIGNED(P) ((void *)((long)(P) & ~03) == (P))
+
+static int
+bad_memblock(const psMemBlock *m,	// block to check
+	     int quiet)			// be quiet?
+{
+    if(m == NULL) {
+	if (!quiet) {
+	    psError(__func__, "psMemCheckCorruption: NULL memory block\n");
+	}
+	return(1);
+    }
+    
+    if(!ALIGNED(m)) {
+	if (!quiet) {
+	    psError(__func__, "psMemCheckCorruption: non-aligned memory block\n");
+	}
+	return(1);
+    }
+    
+    if(m->magic0 != P_PS_MEMMAGIC || m->magic != P_PS_MEMMAGIC) {
+	if (!quiet) {
+	    psError(__func__,
+		    "psMemCheckCorruption: memory block %ld is corrupted\n", m->id);
+	}
+	return(1);
+    }
+
+    return(0);
+}
+
+int psMemCheckCorruption(int abort_on_error)
+{
+    int nbad = 0;			// number of bad blocks
+
+    for (int i = 1; i <= memid; i++) {
+	if (memBlocks[i] != NULL) {
+	    if (bad_memblock(memBlocks[i], 1)) {
+		nbad++;
+
+		memProblemCB(memBlocks[i], "(psMemCheckCorruption)", 0);
+		
+		if (abort_on_error) {
+		    psAbort(__func__, "Detected memory corruption");
+		}
+	    }
+	}
+    }
+
+    return nbad;
+}
+
+/*****************************************************************************/
+
+void *p_psAlloc(size_t size, const char *file, int lineno)
+{
+    psMemBlock *ptr = malloc(sizeof(psMemBlock) + size);
+
+    if (ptr == NULL) {
+	ptr = memExhaustedCB(size);
+	if (ptr == NULL) {
+	    psAbort(__func__, "Failed to allocate %ld bytes at %s:%d",
+		    size, file, lineno);
+	}
+    }
+
+    *(unsigned long *)&ptr->id = ++memid;
+    ptr->file = file;
+    *(int *)&ptr->lineno = lineno;
+    *(void **)&ptr->magic0 = *(void **)&ptr->magic = P_PS_MEMMAGIC;
+
+    ptr->refCounter = 1;		// one user so far
+    /*
+     * Did the user ask to be informed about this allocation?
+     */
+    if(ptr->id == p_psMemAllocateID) {
+	p_psMemAllocateID += memAllocateCB(ptr);	
+    }
+    /*
+     * Register the allocation.  This is not production quality!
+     */
+    if (memid == NMEMBLOCK) {
+	psAbort(__func__, "I'm sorry, but I can only handle %d allocations",
+		NMEMBLOCK);
+    }
+    memBlocks[ptr->id] = ptr;
+    /*
+     * And return the user the memory that they allocated
+     */
+    return ptr + 1;			// usr memory
+}
+
+void *p_psRealloc(void *vptr, size_t size, const char *file, int lineno)
+{
+    if (vptr == NULL) {
+	return p_psAlloc(size, file, lineno);
+    } else {
+	psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+	ptr = (psMemBlock *)realloc(ptr, sizeof(psMemBlock) + size);
+	memBlocks[ptr->id] = ptr;
+	
+	return ptr + 1;			// usr memory
+    }
+}
+
+void p_psFree(void *vptr, const char *file, int lineno)
+{
+    if (vptr == NULL) {
+	return;
+    }
+
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+
+    if (bad_memblock(ptr, 0) || ptr->refCounter != 1) {
+	memProblemCB(ptr, file, lineno);
+    }
+    /*
+     * Did the user ask to be informed about this allocation?
+     */
+    if(ptr->id == p_psMemFreeID) {
+	p_psMemFreeID += memFreeCB(ptr);	
+    }
+
+    ptr->refCounter--;
+    memBlocks[ptr->id] = NULL;		// XXX sub-optimal! Can't check
+    					// freed blocks for corruption
+    free(ptr);
+}
+
+/*****************************************************************************/
+/*
+ * Check for memory leaks. Not production quality code
+ */
+int psMemCheckLeaks(
+    int id0,				// don't list blocks with id < id0
+    psMemBlock ***arr,			// pointer to array of pointers to
+					// leaked blocks, or NULL
+    FILE *fd)				// print list of leaks to fd (or NULL)
+{
+    int nleak = 0;
+    
+    for (int i = id0; i <= memid; i++) {
+	if (memBlocks[i] != NULL) {
+	    if (memBlocks[i]->refCounter > 0) {
+		nleak++;
+
+		if (fd != NULL) {
+		    if (nleak == 1) {
+			fprintf(fd, "   %20s:line ID\n", "file");
+		    }
+
+		    fprintf(fd, "   %20s:%-4d %ld\n",
+			    memBlocks[i]->file, memBlocks[i]->lineno,
+			    memBlocks[i]->id);
+		}
+	    }
+	}
+    }
+
+    if (nleak == 0 || arr == NULL) {
+	return nleak;
+    }
+
+    assert (*arr == NULL);		// Don't generate a memory leak!
+
+    *arr = p_psAlloc(nleak*sizeof(psMemBlock), __FILE__, __LINE__);
+    for (int i = id0, j = 0; i < memid; i++) {
+	if (memBlocks[i] != NULL) {
+	    if (memBlocks[i]->refCounter > 0) {
+		(*arr)[j++] = memBlocks[i];
+		if (j == nleak) {	// found them all
+		    break;
+		}
+	    }
+	}
+    }
+
+    return nleak;
+}
+
+/*****************************************************************************/
+/*
+ * Reference counting APIs
+ */
+int psMemGetRefCounter(void *vptr)	// return refCounter
+{
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+
+    if (bad_memblock(ptr, 0)) {
+	memProblemCB(ptr, "(psMemGetRefCounter)", -1);
+    }
+
+    return ptr->refCounter;
+}
+
+void *psMemIncrRefCounter(void *vptr)	// increment and return refCounter
+{
+    if (vptr == NULL) {
+	return vptr;
+    }
+		 
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+
+    if (bad_memblock(ptr, 0)) {
+	memProblemCB(ptr, "(psMemIncrRefCounter)", -1);
+    }
+
+    ptr->refCounter++;
+
+    return vptr;
+}
+
+void *psMemDecrRefCounter(void *vptr)	// decrement and return refCounter
+{
+    if (vptr == NULL) {
+	return vptr;
+    }
+
+    psMemBlock *ptr = ((psMemBlock *)vptr) - 1;
+
+    if (bad_memblock(ptr, 0)) {
+	memProblemCB(ptr, "(psMemDecrRefCounter)", -1);
+    }
+
+    ptr->refCounter--;
+
+    return vptr;
+}
+
+/*****************************************************************************/
+#if 0					// Testing
+#include <signal.h>
+/*
+ * These are usually defined in psMemory.h, but in this file (only!)
+ * they are undefined explicitly, so let's get them back
+ */
+#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)
+#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)
+#define psFree(size) p_psFree(size, __FILE__, __LINE__)
+
+/*****************************************************************************/
+/*
+ * My callbacks
+ */
+static int my_MemAllocateCB(const psMemBlock *ptr)
+{
+    static int incr = 0;		// "p_psMemAllocateID += incr"
+
+    assert (ptr != NULL);		// prevent compiler warnings
+    
+    fprintf(stderr, "Allocating block %ld (%s:%d)\n",
+	    ptr->id, ptr->file, ptr->lineno);
+    
+    incr = 2;				// See every other allocation
+
+    return incr;
+}
+
+static void *my_MemExhaustedCB(size_t size)
+{
+    psError(__func__, "Bye bye sweet world\n");
+    
+    return NULL;
+}
+
+static psMemProblemCallback default_MemProblemCB = NULL;
+
+static void my_MemProblemCB(const psMemBlock *ptr,
+			    const char *file, int lineno)
+{
+    fprintf(stderr, "I foresee trouble:\n");
+
+    default_MemProblemCB(ptr, file, lineno);
+}
+    
+/*****************************************************************************/
+
+int main(void)
+{
+    /*
+     * Survive any aborts that are generated
+     * This doesn't seem to work on mac os/x 10.2.8
+     */
+    if (signal(SIGABRT, SIG_IGN) == SIG_ERR) {
+	perror("Installing a SIGABRT handler");
+    }
+    /*
+     * Install my callbacks
+     */
+    (void)psMemAllocateSetCB(my_MemAllocateCB);
+    default_MemProblemCB = psMemProblemSetCB(my_MemProblemCB);
+    (void)psMemExhaustedSetCB(my_MemExhaustedCB);
+    /*
+     * Request callback on first allocation
+     */
+    (void)psMemSetAllocateID(1);
+    /*
+     * Start allocating
+     */
+    char *str = psAlloc(10);
+    double *x = psAlloc(1);
+    int *i = psAlloc(1);
+    *i = 1;
+    *x = 1.234;
+
+    psFree(i);
+    
+    str = psRealloc(str, 20);
+    psFree(str);
+#if 1
+    /*
+     * An illegal double-free
+     */
+    psFree(str);
+#endif
+
+#if 1
+    /*
+     * Free memory not allocated with psAlloc
+     */
+    psFree(malloc(1));
+#endif
+
+#if 0
+    /*
+     * Ask for more memory than we can get
+     */
+    psAlloc((size_t)-1 - sizeof(psMemBlock));
+#endif
+
+#if 0
+    /*
+     * Increment refCounter on non-malloced pointer
+     */
+    psMemGetRefCounter(malloc(1));
+#endif
+
+#if 0
+    /*
+     * Mess with the refCounter
+     */
+    (void)psMemIncrRefCounter(x);
+    (void)psMemIncrRefCounter(x);
+    (void)psMemDecrRefCounter(x);	// now == 2
+#endif
+
+#if 0
+    /*
+     * A memory leak
+     */
+    {
+	char *str2 = psAlloc(20);
+	
+	sprintf(str2, "XXX");
+    }
+#else
+    psFree(x);
+#endif
+
+#if 1
+    /*
+     * Corrupt some memory
+     */
+    char *c = psAlloc(1);
+    c[-4] = 0;
+#endif
+
+    /*************************************************************************/
+    /*
+     * Check for memory corruption
+     */
+    if(psMemCheckCorruption(1)) {
+	psError(__func__, "Detected corrupted memory\n\n");
+    }
+    /*
+     * Check for memory leaks
+     */
+    psMemBlock **leaks = NULL;
+    int nleak = psMemCheckLeaks(0, &leaks, NULL);
+
+    if (nleak > 0) {
+	fprintf(stderr, "Memory is leaking (%d blocks):\n", nleak);
+	fprintf(stderr, "   %20s:line ID\n", "file");
+	for (int i = 0; i < nleak; i++) {
+	    fprintf(stderr, "   %20s:%-4d %ld\n",
+		    leaks[i]->file, leaks[i]->lineno, leaks[i]->id);
+	}
+	psFree(leaks);
+    }
+
+    if ((nleak = psMemCheckLeaks(0, NULL, NULL)) > 0) {
+	fprintf(stderr, "Memory is still leaking (%d blocks):\n", nleak);
+	psMemCheckLeaks(0, NULL, stderr);
+    }
+
+    return 0;
+}
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/misc.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/misc.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/misc.c	(revision 144)
@@ -0,0 +1,41 @@
+/*
+ * A set of miscellaneous utilities for Pan-STARRS
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "psUtils.h"
+
+/*****************************************************************************/
+/*
+ * Return a copy of a string
+ */
+char *psStringCopy(const char *str)
+{
+    return strcpy(psAlloc(strlen(str) + 1), str);
+}
+
+/*****************************************************************************/
+/*
+ * Send a psLogFatal message and abort
+ */
+void psAbort(const char *name, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+
+    p_psVLogMsg(name, PS_LOG_ABORT, fmt, ap);
+    abort();
+}
+
+/*****************************************************************************/
+/*
+ * Print an error message
+ */
+void psError(const char *name, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+
+    p_psVLogMsg(name, PS_LOG_ERROR, fmt, ap);
+}
Index: /tags/v1_0_0/archive/pslib/src/Utils/psArray.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psArray.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psArray.h	(revision 144)
@@ -0,0 +1,137 @@
+#if !defined(PS_ARRAY_H)
+#define PS_ARRAY_H
+
+#include <stdlib.h>
+/*
+ * Declare TYPEArray
+ */
+#define PS_DECLARE_ARRAY_TYPE(TYPE) \
+    typedef struct { \
+	int size;			/* number of allocated elements */ \
+	int n;				/* number of elements in use */ \
+	\
+	TYPE *arr;			/* the actual data */ \
+    } PS_CONCAT(TYPE, Array); \
+    \
+PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,New)(int n, int s); \
+PS_CONCAT(TYPE, Array) *PS_CONCAT3(TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n); \
+void PS_CONCAT3(TYPE,Array,Del)(PS_CONCAT(TYPE, Array) *arr)
+
+/*****************************************************************************/
+/*
+ * Generate the code TYPEArray's constructors/destructors
+ */
+#define PS_CREATE_ARRAY_TYPE(TYPE) \
+    P_PS_CREATE_ARRAY_TYPE(,,TYPE)
+
+#define P_PS_CREATE_ARRAY_TYPE(QUAL,PREFIX,TYPE) \
+\
+QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,New)(int n, /* initial dimension */\
+                                                   int s) /* initial number of elements */ \
+{ \
+    PS_CONCAT(TYPE, Array) *arr = psAlloc(sizeof(PS_CONCAT(TYPE, Array))); \
+    \
+    if (n > s) { \
+	psLogMsg(PS_STRING(TYPE) "ArrayNew", PS_LOG_ERROR, "Too few elements requested in array (%d < %d)\n", s, n); \
+	s = n; \
+    } \
+    \
+    arr->size = s; \
+    arr->n = n; \
+    \
+    if (s == 0) { \
+	arr->arr = NULL; \
+    } else { \
+	arr->arr = psAlloc(s*sizeof(TYPE)); \
+    } \
+    \
+    return arr; \
+} \
+\
+QUAL PS_CONCAT(TYPE, Array) *PS_CONCAT4(PREFIX,TYPE,Array,Realloc)(PS_CONCAT(TYPE, Array) *arr, int n) \
+{ \
+    if (arr == NULL) { \
+	return PS_CONCAT4(PREFIX,TYPE,Array,New)(n, n); \
+    } \
+    \
+    if (n <= arr->size) { \
+	if (arr->n < n) { \
+	    arr->n = n; \
+	} \
+    } else { \
+        arr->arr = psRealloc(arr->arr, n*sizeof(TYPE)); \
+	arr->size = n; \
+    } \
+    \
+    return arr; \
+} \
+\
+QUAL void PS_CONCAT4(PREFIX,TYPE,Array,Del)(PS_CONCAT(TYPE,Array) *arr) \
+{ \
+    if (arr == NULL) { \
+	return; \
+    } \
+    \
+    psFree(arr->arr); \
+    psFree(arr); \
+}
+
+/*****************************************************************************/
+/*
+ * Support for pointer types
+ */
+#define PS_DECLARE_ARRAY_PTR_TYPE(TYPE) \
+    typedef TYPE *P_PS_CONCAT(TYPE, Ptr); \
+    PS_DECLARE_ARRAY_TYPE(PS_CONCAT(TYPE, Ptr))
+
+#define PS_CREATE_ARRAY_PTR_TYPE(TYPE); \
+    P_PS_CREATE_ARRAY_TYPE(static, my_, P_PS_CONCAT(TYPE, Ptr)) \
+    \
+    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,New)(int n, int s) \
+    { \
+	PS_CONCAT(TYPE, PtrArray) *arr = PS_CONCAT4(my_,TYPE,PtrArray,New)(n, s); \
+	for (int i = 0; i < arr->n; i++) { \
+	    arr->arr[i] = psMemIncrRefCounter,(PS_CONCAT(TYPE, New)());\
+	} \
+        \
+	return arr; \
+    } \
+    \
+    PS_CONCAT(TYPE, PtrArray) *PS_CONCAT3(TYPE,PtrArray,Realloc)(PS_CONCAT(TYPE, PtrArray) *arr, int n) \
+    { \
+	for (int i = n; i < arr->n; i++) { \
+	    PS_CONCAT(TYPE, Del)(psMemDecrRefCounter(arr->arr[i]));\
+	} \
+        \
+	return PS_CONCAT4(my_,TYPE,PtrArray,Realloc)(arr, n); \
+    } \
+    \
+    void PS_CONCAT3(TYPE,PtrArray,Del)(PS_CONCAT2(TYPE,PtrArray) *arr) \
+    { \
+	if (arr == NULL) { return; } \
+	for (int i = 0; i < arr->size; i++) { \
+	    PS_CONCAT(TYPE, Del)(psMemDecrRefCounter(arr->arr[i])); \
+	} \
+	PS_CONCAT4(my_, TYPE, PtrArray, Del)(arr); \
+    }
+
+/*****************************************************************************/
+/*
+ * Declare some common types of arrays
+ *
+ * psVoidPtrArray is special, as it needs to have a destructor that
+ * accepts a destructor for the elements, so we cannot simply use
+ *       PS_DECLARE_ARRAY_TYPE(psVoidPtr);
+ */
+typedef struct {
+   int n, size;
+   void **arr;
+} psVoidPtrArray;
+
+psVoidPtrArray *psVoidPtrArrayNew(int n, int s);
+psVoidPtrArray *psVoidPtrArrayRealloc(psVoidPtrArray *arr, int n);
+void psVoidPtrArrayDel(psVoidPtrArray *arr,
+		       void (*elemDel)(void *)); // destructor for array data
+
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/psDlist.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psDlist.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psDlist.h	(revision 144)
@@ -0,0 +1,56 @@
+#if !defined(PS_DLIST_H)
+#define PS_DLIST_H
+/*
+ * Support for doubly linked lists
+ */
+typedef struct psDlistElem {
+   struct psDlistElem *prev;		// previous link in list
+   struct psDlistElem *next;		// next link in list
+   void *data;				// real data item
+} psDlistElem;
+
+typedef struct {
+   int n;				// number of elements on list
+   psDlistElem *head;			// first element on list (may be NULL)
+   psDlistElem *tail;			// last element on list (may be NULL)
+   psDlistElem *iter;			// iteration cursor
+} psDlist;
+
+enum {					// Special values of index into list
+   PS_DLIST_HEAD = 0,			// at head
+   PS_DLIST_TAIL = -1,			// at tail
+   PS_DLIST_UNKNOWN = -2,		// unknown position
+   PS_DLIST_PREV = -3,			// previous element
+   PS_DLIST_NEXT = -4			// next element
+};
+
+/*****************************************************************************/
+/*
+ * Constructors and Destructors
+ */
+psDlist *psDlistNew(void *data);	// initial data item; may be NULL
+
+void psDlistDel(psDlist *list,		// list to destroy
+		void (*elemDel)(void *)); // destructor for data on list
+
+/*
+ * List maintainence functions
+ */
+psDlist *psDlistAdd(psDlist *list,	// list to add to (may be NULL)
+		    void *data,		// data item to add
+		    int where);		// index, PS_DLIST_HEAD, or PS_DLIST_TAIL
+psDlist *psDlistAppend(psDlist *list,	// list to append to (may be NULL)
+		       void *data);	// data item to add
+void *psDlistRemove(psDlist *list,	// list to remove element from
+		    void *data,		// data item to remove
+		    int which);		// index of item, or PS_DLIST_UNKNOWN,
+					// or PS_DLIST_NEXT, or PS_DLIST_PREV
+void *psDlistGet(const psDlist *list,	// list to retrieve element from
+		 int which);		// index of item, or PS_DLIST_NEXT,
+					// or PS_DLIST_PREV
+/*
+ * Conversions to/from arrays
+ */
+psVoidPtrArray *psDlistToArray(psDlist *dlist);
+psDlist *psArrayToDlist(psVoidPtrArray *arr);
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/psHash.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psHash.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psHash.h	(revision 144)
@@ -0,0 +1,19 @@
+#if !defined(PS_HASH_H)
+#define PS_HASH_H
+
+typedef struct HashTable psHash;
+
+psHash *psHashNew(int nbucket);		// initial number of buckets
+void psHashDel(psHash *table,		// hash table to be freed
+	       void (*itemDel)(void *item)); // how to free hashed data;
+					// or NULL
+
+void *psHashInsert(psHash *table,	// table to insert in
+		   const char *key,	// key to use
+		   void *data,		// data to insert
+		   void (*itemDel)(void *item)); // how to free hashed data;
+					// or NULL
+void *psHashLookup(psHash *table,	// table to lookup key in
+		   const char *key);	// key to lookup
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/psLogMsg.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psLogMsg.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psLogMsg.h	(revision 144)
@@ -0,0 +1,14 @@
+#if !defined(PS_LOG_MSG_H)
+#define PS_LOG_MSG_H
+#include <stdarg.h>
+
+enum { PS_LOG_ABORT = 0, PS_LOG_ERROR, PS_LOG_WARN, PS_LOG_INFO };
+
+enum { PS_LOG_TO_STDERR, PS_LOG_TO_STDOUT };
+
+int psSetLogDestination(int dest);
+int psSetLogLevel(int level);
+void psLogMsg(const char *name, int level, const char *fmt, ...);
+void p_psVLogMsg(const char *name, int level, const char *fmt, va_list ap);
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/psMemory.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psMemory.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psMemory.h	(revision 144)
@@ -0,0 +1,71 @@
+#if !defined(PS_MEMORY_H)
+#define PS_MEMORY_H
+#include <stdio.h>
+/*
+ * Book-keeping data for storage allocator
+ *
+ * N.b. sizeof(psMemBlock) must be chosen such that if ptr is a pointer
+ * returned by malloc, then ((char *)ptr + sizeof(psMemBlock)) is properly
+ * aligned for all storage types.
+ */
+typedef struct {
+    const void *magic0;			// initialised to p_psMEMMAGIC
+    const unsigned long id;		// a unique ID for this allocation
+    const char *file;			// set from __FILE__ in e.g. p_psAlloc
+    const int lineno;			// set from __LINE__ in e.g. p_psAlloc
+    int refCounter;			// how many times pointer is referenced
+    const void *magic;			// initialised to p_psMEMMAGIC
+} psMemBlock;
+
+typedef int (*psMemCallback)(const psMemBlock *ptr);
+typedef void (*psMemProblemCallback)(const psMemBlock *ptr,
+				     const char *file, int lineno);
+typedef void *(*psMemExhaustedCallback)(size_t size);
+
+/*****************************************************************************/
+
+void *psAlloc(size_t size);
+void *psRealloc(void *ptr, size_t size);
+void psFree(void *ptr);
+
+void *p_psAlloc(size_t size, const char *file, int lineno);
+void *p_psRealloc(void *ptr, size_t size, const char *file, int lineno);
+void p_psFree(void *ptr, const char *file, int lineno);
+
+#define psAlloc(size) p_psAlloc(size, __FILE__, __LINE__)
+#define psRealloc(ptr, size) p_psRealloc(ptr, size, __FILE__, __LINE__)
+#define psFree(size) p_psFree(size, __FILE__, __LINE__)
+
+/*****************************************************************************/
+/*
+ * Checks of memory system 
+ */
+int psMemCheckLeaks(
+    int id0,				// don't list blocks with id < id0
+    psMemBlock ***arr,			// pointer to array of pointers to
+					// leaked blocks, or NULL
+    FILE *fd);				// print list of leaks to fd (or NULL)
+int psMemCheckCorruption(int abort_on_error);
+
+/*****************************************************************************/
+/*
+ * Manipulate reference counter
+ */
+int psMemGetRefCounter(void *vptr);	// return refCounter
+void *psMemIncrRefCounter(void *vptr);	// increment refCounter and return vptr
+void *psMemDecrRefCounter(void *vptr);	// decrement refCounter and return vptr
+
+/*****************************************************************************/
+/*
+ * Functions to set and control callbacks
+ */
+psMemProblemCallback psMemProblemSetCB(psMemProblemCallback func);
+psMemExhaustedCallback psMemExhaustedSetCB(psMemExhaustedCallback func);
+
+psMemCallback psMemAllocateSetCB(psMemCallback func);
+psMemCallback psMemFreeSetCB(psMemCallback func);
+
+int psMemGetId(void);			// get next memory ID
+long psMemSetAllocateID(long id);	// set p_psMemAllocateID to id
+long psMemSetFreeID(long id);		// set p_psMemFreeID to id
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/psMisc.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psMisc.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psMisc.h	(revision 144)
@@ -0,0 +1,19 @@
+#if !defined(PS_MISC_H)
+#define PS_MISC_H
+/*
+ * Concatenate two macro arguments
+ */
+#define P_PS_CONCAT(A, B) A ## B	// Expands to AB
+#define PS_CONCAT(A, B) A ## B		// Also Expands to AB
+#define PS_CONCAT2(A, B) PS_CONCAT(A, B) // Also expands to AB
+#define PS_CONCAT3(A, B, C) A ## B ## C // Expands to ABC
+#define PS_CONCAT4(A, B, C, D) A ## B ## C ## D // Expands to ABCD
+
+#define PS_STRING(S) #S			// converts argument to string
+
+void psAbort(const char *name, const char *fmt, ...);
+void psError(const char *name, const char *fmt, ...);
+
+char *psStringCopy(const char *str);
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/psTrace.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psTrace.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psTrace.h	(revision 144)
@@ -0,0 +1,20 @@
+#if !defined(PS_TRACE_H)
+#define PS_TRACE_H 1
+
+//#define PS_NTRACE 1			/* to turn off all tracing */
+#if defined(PS_NTRACE)
+#  define psTrace(facil, level, ...) /* do nothing */
+#else
+#  define psTrace(facil, level, ...) \
+          p_psTrace(facil, level, __VA_ARGS__)
+#endif
+
+void p_psTrace(const char *facil, int level, ...);
+
+int psSetTraceLevel(const char *facil,	// facilty of interest
+		    int level);		// desired trace level
+int psGetTraceLevel(const char *name);	// facilty of interest
+
+void psPrintTraceLevels(void);		// print trace levels
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/psUtils.h
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/psUtils.h	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/psUtils.h	(revision 144)
@@ -0,0 +1,13 @@
+#if !defined(PS_UTILS)
+#define PS_UTILS
+
+#include "psArray.h"
+#include "psDlist.h"
+#include "psHash.h"
+#include "psLogMsg.h"
+#include "psMemory.h"
+#include "psTrace.h"
+
+#include "psMisc.h"			// must go last
+
+#endif
Index: /tags/v1_0_0/archive/pslib/src/Utils/trace.c
===================================================================
--- /tags/v1_0_0/archive/pslib/src/Utils/trace.c	(revision 144)
+++ /tags/v1_0_0/archive/pslib/src/Utils/trace.c	(revision 144)
@@ -0,0 +1,326 @@
+/*
+ * A simple implementation of a tracing facility for Pan-STARRS
+ *
+ * Tracing is controlled on a per "component" basis, where a "component" is a
+ * name of the form aaa.bbb.ccc where aaa is the Most significant part; for
+ * example, the utilities library might be called "utils", the doubly-linked
+ * list "utils.dlist", and the code to destroy a list "utils.dlist.del" 
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "psUtils.h"
+
+/*****************************************************************************/
+/*
+ * A component is a string of the form aaa.bbb.ccc, and may itself
+ * contain further subcomponents. The Component structure doesn't
+ * in fact contain it's full name, but only the last part.
+ */
+typedef struct Component {
+    const char *name;			// last part of name of this component
+    int level;				// trace level for this component
+    int dimen;				// dimension of subcomp
+    int n;				// number of subcomponents
+    struct Component **subcomp;		// next level of subcomponents
+} Component;
+
+static int highest_level = 0;		// highest level requested
+/*
+ * tracelevel cache
+ */
+#define CACHE_NAME_LEN 50
+#define NO_CACHE "\a"			// no name is cached
+
+#define UNKNOWN_LEVEL -9999		// we don't know this name's level
+
+static char cachedName[CACHE_NAME_LEN + 1] = NO_CACHE; // last name looked up
+static int cachedLevel;			// level of last looked up name
+
+/*****************************************************************************/
+
+static Component *componentNew(const char *name,
+			       int level)
+{
+    Component *comp = psAlloc(sizeof(Component));
+
+    comp->name = psStringCopy(name);
+    comp->level = level;
+    comp->dimen = comp->n = 0;
+    comp->subcomp = NULL;
+
+    return comp;
+}
+
+static void componentDel(Component *comp)
+{
+    if (comp == NULL) { return; }
+
+    if (comp->subcomp != NULL) {
+	for (int i = 0; comp->subcomp[i] != NULL; i++) {
+	    componentDel(comp->subcomp[i]);
+	}
+	psFree(comp->subcomp);
+    }
+
+    psFree(comp);
+}
+
+/*****************************************************************************/
+/*
+ * The root of the trace component tree
+ */
+static Component *croot = NULL;
+
+static void initTrace(void) {
+    if (croot == NULL) {
+	croot = componentNew("", UNKNOWN_LEVEL);
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Add a new component to the tree
+ */
+static void componentAdd(Component *comp,
+			 const char *aname,
+			 int level)
+{
+    char name[strlen(aname) + 1];	// need a writeable copy
+    strcpy(name, aname);
+
+    char *cpt0;				// first component of name
+    char *rest = name;			// rest of name
+    cpt0 = strsep(&rest, ".");
+    /*
+     * Does cpt0 match this level?
+     */
+    if (strcmp(comp->name, cpt0) == 0) { // a match
+	if (rest == NULL) {
+	    comp->level = level;
+	} else {
+	    componentAdd(comp, rest, level);
+	}
+	
+	return;
+    }
+    /*
+     * Look for a match for cpt0 in this level's subcomps
+     */
+    for (int i = 0; i < comp->n; i++) {
+	if (strcmp(comp->subcomp[i]->name, cpt0) == 0) { // a match
+	    if (rest == NULL) {
+		comp->subcomp[i]->level = level;
+	    } else {
+		componentAdd(comp->subcomp[i], rest, level);
+	    }
+
+	    return;
+	}
+    }
+    /*
+     * No match; add cpt0 to this level. Ensure that subcomp is sorted
+     */
+    if (comp->n == comp->dimen) {
+	comp->dimen += 5;
+	comp->subcomp = psRealloc(comp->subcomp, comp->dimen);
+    }
+
+    Component *fcpt0 = componentNew(cpt0, UNKNOWN_LEVEL);
+    int i;
+    for (i = 0; i < comp->n; i++) {
+	if (strcmp(cpt0, comp->subcomp[i]->name) > 0) {
+	    for (int j = comp->n; j > i; j--) {
+		comp->subcomp[j] = comp->subcomp[j - 1];
+	    }
+	}
+    }
+    comp->subcomp[i] = fcpt0;
+    comp->n++;
+
+    if (rest == NULL) {
+	fcpt0->level = level;
+    } else {
+	componentAdd(fcpt0, rest, level);
+    }
+}
+
+/*****************************************************************************/
+/*
+ * Find the highest level present in the tree
+ */
+static void setHighestLevel(const Component *comp)
+{
+    if (comp->level > highest_level) {
+	highest_level = comp->level;
+    }
+    
+    for (int i = 0; i < comp->n; i++) {
+	setHighestLevel(comp->subcomp[i]);
+    }
+}
+
+/*****************************************************************************/
+
+int psSetTraceLevel(const char *comp,	// component of interest
+		    int level)		// desired trace level
+{
+    if (croot == NULL) {
+	initTrace();
+    }
+
+    strncpy(cachedName, NO_CACHE, CACHE_NAME_LEN); // invalidate cache
+    
+    componentAdd(croot, comp, level);
+
+    if (level > highest_level) {
+	highest_level = level;
+    } else {
+	highest_level = UNKNOWN_LEVEL;
+	setHighestLevel(croot);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+/*
+ * Return a trace level given a name
+ */
+static int doGetTraceLevel(const Component *comp, // end of component to search
+			   const char *aname) // name to find
+{
+    char name[strlen(aname) + 1];	// need a writeable copy
+    strcpy(name, aname);
+
+    char *cpt0;				// first component of name
+    char *rest = name;			// rest of name
+    cpt0 = strsep(&rest, ".");
+    /*
+     * Look for a match for cpt0 in this level's subcomps
+     */
+    for (int i = 0; i < comp->n; i++) {
+	if (strcmp(comp->subcomp[i]->name, cpt0) == 0) { // a match
+	    if (rest == NULL) {		// we're there
+		int level = comp->subcomp[i]->level;
+
+		return (level == UNKNOWN_LEVEL) ? comp->level : level;
+	    } else {
+		return doGetTraceLevel(comp->subcomp[i], rest);
+	    }
+	}
+    }
+    /*
+     * No match. This is as far as she goes
+     */
+    return comp->level;
+}
+
+int psGetTraceLevel(const char *name)	// component of interest
+{
+    if (croot == NULL) {
+	initTrace();
+    }
+
+    if (strcmp(name, cachedName) == 0) {
+	return cachedLevel;
+    }
+
+    int level = doGetTraceLevel(croot, name);
+
+    strncpy(cachedName, name, CACHE_NAME_LEN);
+    cachedLevel = level;
+
+    return level;
+}
+
+/*****************************************************************************/
+/*
+ * Print a tree of trace levels
+ */
+static void doPrintTraceLevels(const Component *comp, int depth)
+{
+    if (comp->name[0] == '\0') {
+	printf("%*s%-*s %d\n", depth, "", 20 - depth,
+	       "(root)", (comp->level == UNKNOWN_LEVEL) ? 0 : comp->level);
+    } else {
+	if (comp->level == UNKNOWN_LEVEL) {
+	    printf("%*s%-*s %s\n", depth, "", 20 - depth,
+		   comp->name, ".");
+	} else {
+	    printf("%*s%-*s %d\n", depth, "", 20 - depth,
+		   comp->name, comp->level);
+	}
+    }
+    
+    for (int i = 0; i < comp->n; i++) {
+	doPrintTraceLevels(comp->subcomp[i], depth + 1);
+    }
+}
+
+void psPrintTraceLevels(void)
+{
+    if (croot == NULL) {
+	initTrace();
+    }
+
+    doPrintTraceLevels(croot, 0);
+}
+
+/*****************************************************************************/
+/*
+ * Actually generate the trace message. Note that psTrace dealt with
+ * prepending appropriate indentation
+ */
+void p_psTrace(const char *comp,	// component being traced
+	       int level,		// desired trace level
+	       ...)			// arguments
+{
+    if (level <= highest_level && psGetTraceLevel(comp) >= level) {
+	va_list ap;
+	va_start(ap, level);
+	for (int i = 0; i < level; i++) {
+	    putchar(' ');
+	}
+	char *fmt = va_arg(ap, char *);
+	vprintf(fmt, ap);
+	va_end(ap);
+    }
+}
+
+/*****************************************************************************/
+#if 0					// testing
+
+int main(void)
+{
+#if 0
+    psSetTraceLevel("", 10);
+#else
+    psSetTraceLevel("aaa.bbb.ccc.ddd", 9);
+    psSetTraceLevel("aaa.bbb.ccc", 3);
+    psSetTraceLevel("aaa.bbb.ddd", 4);
+    psSetTraceLevel("aaa.BBB", 2);
+    psSetTraceLevel("BBB", 2);
+    psSetTraceLevel("BBB.XXX.YYY.ZZZ", 5);
+    psSetTraceLevel("aaa.bbb", 2);
+    psSetTraceLevel("aaa.bbb.ccc", 9);
+    psSetTraceLevel("aaa", 1);
+#endif
+    
+    psPrintTraceLevels();
+    printf("\n");
+
+    psTrace("aaa.bbb.ddd", 2, "(aaa.bbb.ddd) Hello %s\n", "world");
+    psTrace("aaa", 2, "aaa\n");
+    psTrace("", 2, "\"\"\n");
+    psTrace("aaa.bbb", 2, "aaa.bbb\n");
+    psTrace("aaa.bbb.ddd", 2, "aaa.bbb.ddd\n");
+    psTrace("aaa.bbb.ddd", 4, "aaa.bbb.ddd\n");
+    psTrace("aaa.bbb.ddd", 2, "aaa.bbb.ddd\n");
+    psTrace("aaa.bbb.ccc", 1, "aaa.bbb.ccc\n");
+    psTrace("aaa.bbb.eee", 2, "aaa.bbb.eee\n");
+
+    return 0;
+}
+#endif
Index: /tags/v1_0_0/archive/utils/check-namespace
===================================================================
--- /tags/v1_0_0/archive/utils/check-namespace	(revision 144)
+++ /tags/v1_0_0/archive/utils/check-namespace	(revision 144)
@@ -0,0 +1,301 @@
+#!/usr/bin/perl -w
+#
+# Check that a set of .h files and .o and .a files satisfy the Pan-STARRS
+# rules about namespaces.
+#
+# Robert Lupton (rhl@astro.princeton.edu)  February 2004
+#
+# Parse arguments
+#
+$nm_opts = "";
+$prefix = "ps";
+$pprefix = "p_$prefix";
+$verbose = 0;
+
+if (!@ARGV) {
+   warn "Please specify some files to check! E.g. *.h *.o\n";
+   push @ARGV, "-h";
+}
+
+while ($ARGV[0] =~ /^-/) {
+   if ($ARGV[0] eq "-h" || $ARGV[0] eq "-?") {
+      warn "\
+Usage: check-namespace [options] files.h files.o
+
+Check that a set of .h and .o (and/or .a) files obey the pan-STARRS
+naming convention, namely that iff a symbol is externally visible, it
+should have a name starting \"ps\" or \"p_ps\", and be declared in a
+.h file.
+
+E.g. check-namespace *.o *.h
+
+The treatment of comments and typedefs is sleazy and non-robust.
+
+Options:
+	-h, -?		Print this message
+	-p prefix	Change prefix from ps to \"prefix\"
+	-v		Be chatty (repeat for even more output)
+";
+      exit(0);
+   } elsif($ARGV[0] eq "-p") {
+      shift(@ARGV);
+      $arg = $ARGV[0];
+      if(!$arg) {
+	 warn "-p expects an argument\n";
+      } else {
+	 $prefix = "$arg";
+	 $pprefix = "p_$prefix";
+      }
+   } elsif($ARGV[0] eq "-v") {
+      $verbose++;
+   } else {
+      warn "Unknown argument $ARGV[0]\n";
+   }
+
+   shift(@ARGV);
+}
+#
+$uprefix = uc($prefix);
+$upprefix = uc($pprefix);
+#
+# Get list of files to process
+#
+foreach (@ARGV) {
+   if(/\.h$/) {
+      push(@hfiles, $_);
+   } elsif(/\.[ao]$/) {
+      push(@ofiles, $_);
+   } else {
+      warn "I don't know what to do with $_; ignoring\n";
+   }
+}
+#
+# Read all the .h files looking for suitable symbols
+#
+foreach $file (@hfiles) {
+   $in_comment = 0;		# in a multiline comment?
+   $in_typedef = 0;		# parsing a typedef?
+   
+   if ($verbose > 1) {
+      warn "   $file\n";
+   }
+
+   open(FD, $file) or die "Failed to open $file:$! \n";
+   while (<FD>) {
+      chomp;
+      # Deal with comments, at least approximately
+      if($in_comment) {
+	 while (!m|\*/|) {
+	    $_ = <FD>;
+	 }
+	 $in_comment = 0;
+      }
+
+      s|//.*||;
+      if(m|(.*)/\*(.*)|) {
+	 $first = $1; $rest = $2;
+	 if ($rest =~ m|\*/(.*)|) {
+	    $_ = "$first /**/ $1";
+	 } else {
+	    $_ = $first;
+	    $in_comment = 1;
+	 }
+      }
+      #
+      # OK, how about typedefs?
+      #
+      if (/typedef.*\W\(\*(($pprefix|$prefix)[a-zA-Z_0-9]+)\)/o) {
+	 $name = $1;
+ 
+	 $typedefs{$name} = $file;
+	 $in_typedef--;
+      } elsif (/typedef\s+struct/) {
+	 if(/([a-zA-Z_0-9]+)\s*;/) {
+	    $name = $1;
+	    $typedefs{$name} = $file;
+	    next;
+	 }
+	 $in_typedef = 1;
+      } elsif ($in_typedef) {
+	 if (/\{/) {
+	    $in_typedef++;
+	 } elsif ($in_typedef > 1 && /\}/) {
+	    $in_typedef--;
+	 }
+      }
+
+      if ($in_typedef) {
+	 if (/\}\s*(($prefix|$pprefix)[a-zA-Z_0-9]+)\s*;/) {
+	    $in_typedef--;
+	    if ($in_typedef == 0) {
+	       $name = $1;
+	       $typedefs{$name} = $file;
+	    }
+	 }
+      }
+      #
+      # And #defines?
+      #
+      if (/^\s*\#\s*define\s+([a-zA-Z_0-9]+)(\()?/) {
+	 $name = $1;
+	 $func_macro = (defined($2)) ? 1 : 0;
+
+	 $defines{$name} = $file;
+
+	 if((!$func_macro && $name !~ /^($uprefix|$upprefix)/o) ||
+	    ($func_macro && $name !~ /^($prefix|$pprefix)/io)) {
+	    printf STDERR
+		"\#define %s (%s) does not have a permitted prefix\n",
+		$name, $file;
+	 }
+      }
+      #
+      # OK, how about enums? Their members look like external names,
+      # and should obey the same rules
+      #
+      if (s/((typedef\s+)?enum)//) {
+	 $typedef = $1;
+	 $is_enum_typedef = ($typedef =~ /typedef/) ? 1 : 0;
+
+	 while (!/\}/) {
+	    $line = <FD>;
+	    chomp;
+	    s|//.*||;
+	    s|/\*[^\*]*\*/||;
+
+	    $_ .= " $line";
+	 }
+
+	 if ($is_enum_typedef) {
+	    if ($line =~ /\}\s*([a-zA-Z_0-9]+)/) {
+	       $name = $1;
+	       $typedefs{$name} = $file;
+	    }
+	 }
+
+	 s/^\s*\{\s*//;
+	 while (s/([a-zA-Z_0-9]+)(\s*=\s*-?[a-zA-Z_0-9]+)?(\s*,\s*)?\s*//) {
+	    if(/^\}/) {
+	       last;
+	    }
+	    
+	    $name = $1;
+
+	    if ($name eq "enum") {
+	       next;
+	    }
+
+	    #$enums{$1} = $file;
+	    
+	    if($name !~ /^($uprefix|$upprefix)/o) {
+	       printf STDERR
+		   "enum element %s (%s) does not have a permitted prefix\n",
+		   $name, $file;
+	    }
+	 }
+      }
+      #
+      # Include filenames can look like external names too
+      #
+      if (/^\s*\#\s*include\s/) {
+	 next;
+      }
+      # Look for possible external symbols
+      #
+      while(s/(^|\W)(($pprefix|$prefix)[a-zA-Z_0-9]+)//o) {
+	 $name = $2;
+
+	 $declarations{$name} = $file;
+      }
+   }
+   close(FD);
+}
+
+if($verbose) {
+   warn "Typedefs found in header files:\n";
+   foreach (sort keys %typedefs) {
+      printf STDERR "%-30s %s\n", $_, $typedefs{$_};
+   }
+
+   warn "External symbols found in header files:\n";
+   foreach (sort keys %declarations) {
+      printf STDERR "%-30s %s\n", $_, $declarations{$_};
+   }
+}
+#
+# OK, now run nm on the .o and .a files and see if there are problems
+# 
+foreach $file (@ofiles) {
+   if ($verbose) {
+      warn "   $file\n";
+   }
+
+   open(FD, "nm $nm_opts $file|") or die "Failed to open $file:$! \n";
+   while (<FD>) {
+      if (/^\s*$/) {
+	 next;
+      }
+
+      if (/^(lib[A-Za-z0-9]+\.a\(\w+\.o\))/) {
+	 $file = $1;
+	 next;
+      }
+      
+      if (/^\s+U /) {
+	 next;			# an undefined symbol
+      }
+
+      my($addr, $type, $name) = split;
+
+      if (!defined($name)) {
+	 next;
+      }
+      $name =~ s/^_//;
+
+      if ($name eq "main") {
+	 next;
+      }
+      if ($name =~ /\./) {
+	 next;			# an internal name to the compiler
+      }
+
+      if ($type eq "b") { $type = "d"; } # don't distinguish BSS and Data
+      if ($type eq "B") { $type = "D"; }
+
+      if ($type !~ /[DT]/i) {	# Data or Text section
+	 die "Unknown symbol type in $file: $_\n";
+      }
+
+      if ($verbose > 1) {
+	 printf "%-20s %-40s %s\n", $file, $name, $type;
+      }
+
+      if ($type =~ /[dt]/) {
+	 if($name =~ /^($prefix|$pprefix)/o) {
+	    warn "Non-global symbol $name in $file is in reserved namespace ($prefix|$pprefix)\n";
+	 }
+      } elsif ($type =~ /[DT]/) {
+	 if ($declarations{$name}) {
+	    $used_declarations{$name}++;
+	 } elsif ($name !~ /^($pprefix|$prefix)/) {
+	    if($name !~ /^($prefix|$pprefix)/io) {
+	       printf STDERR
+		   "global symbol %s (%s) does not have a permitted prefix\n",
+		   $name, $file;
+	    }
+	 } else {
+	    printf STDERR "External symbol %s (%s) appears in no .h file\n",
+	    $name, $file;
+	 }
+      }
+   }
+   close(FD);
+}
+#
+# Now check for unused declarations
+#
+foreach $name (sort keys %declarations) {
+   if (!$used_declarations{$name} && !$typedefs{$name} && !$defines{$name}) {
+      warn "$name was declared in $declarations{$name} but not defined\n";
+   }
+}
