Index: /trunk/psLib/src/astronomy/psAstronomyErrors.dat
===================================================================
--- /trunk/psLib/src/astronomy/psAstronomyErrors.dat	(revision 3944)
+++ /trunk/psLib/src/astronomy/psAstronomyErrors.dat	(revision 3945)
@@ -66,3 +66,4 @@
 psMetadataIO_TAG_MISMATCH              Start tag, %s and end tag, %s do not agree.
 psMetadataIO_TAG_UNKNOWN               Invalid end tag name, %s.
-
+psMetadataIO_TYPE_DUPLICATE            Specified type, %s, on line %u of %s is already defined.
+psMetadataIO_DUPLICATE_MULTI           Duplicate MULTI specifier on line %u of %s.
Index: /trunk/psLib/src/astronomy/psAstronomyErrors.h
===================================================================
--- /trunk/psLib/src/astronomy/psAstronomyErrors.h	(revision 3944)
+++ /trunk/psLib/src/astronomy/psAstronomyErrors.h	(revision 3945)
@@ -7,6 +7,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-03-17 19:01:01 $
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-16 19:43:06 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -83,4 +83,6 @@
 #define PS_ERRORTEXT_psMetadataIO_TAG_MISMATCH "Start tag, %s and end tag, %s do not agree."
 #define PS_ERRORTEXT_psMetadataIO_TAG_UNKNOWN "Invalid end tag name, %s."
+#define PS_ERRORTEXT_psMetadataIO_TYPE_DUPLICATE "Specified type, %s, on line %u of %s is already defined."
+#define PS_ERRORTEXT_psMetadataIO_DUPLICATE_MULTI "Duplicate MULTI specifier on line %u of %s."
 //~End
 
Index: /trunk/psLib/src/collections/psMetadata.c
===================================================================
--- /trunk/psLib/src/collections/psMetadata.c	(revision 3944)
+++ /trunk/psLib/src/collections/psMetadata.c	(revision 3945)
@@ -12,6 +12,6 @@
 *  @author Ross Harman, MHPCC
 *
-*  @version $Revision: 1.61 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-04-28 19:52:48 $
+*  @version $Revision: 1.62 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-16 19:43:53 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -38,6 +38,34 @@
 #include "psAstronomyErrors.h"
 #include "psConstants.h"
+#include "psLogMsg.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 1024
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
 
 static psS32 metadataId = 0;
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
 
 static psMetadataItem* makeMetaMulti(psHash* table, const char* key, psMetadataItem* existing)
@@ -48,11 +76,11 @@
     }
 
+    // move any existing entry into a psList
+    psList* newList = psListAlloc(existing);
 
     psMetadataItem* item = psMetadataItemAlloc(key,
                            PS_META_MULTI,
-                           "List of Metadata Items",
-                           NULL);
-
-    psListAdd(item->data.list,PS_LIST_TAIL,existing);
+                           "",
+                           newList);
 
     if (existing != NULL) {
@@ -60,7 +88,8 @@
     }
 
-    psHashAdd(table, key, item); // put in the new MULTI list entry
-
-    // free local references of newly allocated item.
+    psHashAdd(table, key, item); // put in the new entry
+
+    // free local references of newly allocated items.
+    psFree(newList);
     psFree(item);
 
@@ -80,5 +109,6 @@
     psFree(metadataItem->name);
     psFree(metadataItem->comment);
-    if (! PS_META_IS_PRIMITIVE(type)) {
+
+    if(!PS_META_IS_PRIMITIVE(type)) {
         psFree(metadataItem->data.V);
     }
@@ -104,4 +134,5 @@
     psFree(metadata->list);
     psFree(metadata->table);
+
 }
 
@@ -146,6 +177,5 @@
 {
     psMetadataItem* metadataItem = NULL;
-    char tmp;
-    int nBytes;
+
 
     PS_PTR_CHECK_NULL(name,NULL);
@@ -158,10 +188,11 @@
     psMemSetDeallocator(metadataItem, (psFreeFcn) metadataItemFree);
 
-    // set metadata item comment
+    // Allocate and set metadata item comment
+    metadataItem->comment = (char *)psAlloc(sizeof(char) * MAX_STRING_LENGTH);
     if (comment == NULL) {
         // Per SDRS, null isn't allowed, must use "" instead
-        metadataItem->comment = psStringCopy("");
+        strncpy(metadataItem->comment, "", MAX_STRING_LENGTH);
     } else {
-        metadataItem->comment = psStringCopy(comment);
+        strncpy(metadataItem->comment, comment, MAX_STRING_LENGTH);
     }
 
@@ -173,6 +204,5 @@
 
     // Allocate and set metadata item name
-    nBytes = vsnprintf(&tmp, 0, name, argPtr) + 1;
-    metadataItem->name = (char *)psAlloc(sizeof(char) * nBytes);
+    metadataItem->name = (char *)psAlloc(sizeof(char) * MAX_STRING_LENGTH);
     vsprintf(metadataItem->name, name, argPtr);
 
@@ -193,9 +223,5 @@
     case PS_META_STR:
         // Perform copy of input strings
-        metadataItem->data.V = psStringCopy(va_arg(argPtr, char *));
-        break;
-    case PS_META_MULTI:
-        // MULTI needs to create a psList entry, value must be NULL
-        metadataItem->data.list = psListAlloc(NULL);
+        metadataItem->data.V = psStringNCopy(va_arg(argPtr, char *), MAX_STRING_LENGTH);
         break;
     case PS_META_LIST:
@@ -207,4 +233,6 @@
     case PS_META_ASTROM:
     case PS_META_UNKNOWN:
+    case PS_META_META:
+    case PS_META_MULTI:
         // Copy of input data not performed due to variability of data types
         metadataItem->data.V = psMemIncrRefCounter(va_arg(argPtr, psPtr));
@@ -258,55 +286,70 @@
 
     // See if key is already in table
-    existingEntry = psMetadataLookup(md, key);
-
-    // if replace is set, remove any existing items of the same key
-    if (existingEntry != NULL && (flags & PS_META_REPLACE) != 0) {
-        psMetadataRemove(md,0,key);
-        existingEntry = NULL;
-    }
-
-    // if the metadataItem is MULTI, just create a MULTI node.
+    existingEntry = (psMetadataItem*)psHashLookup(mdTable, key);
+
     if (metadataItem->type == PS_META_MULTI) {
-        if (metadataItem->data.list == NULL || metadataItem->data.list->size > 0) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE,true,
-                    "Specified PS_META_MULTI item is not defined properly "
-                    "(list allocated with zero size).");
-            return false;
-        }
-
-        // make sure the existing entry is PS_META_MULTI
+        // the incoming entry is PS_META_MULTI
+
+        // force the hash entry to be PS_META_MULTI
         existingEntry = makeMetaMulti(mdTable,key,existingEntry);
 
+        // add all the items in the incoming entry to metadata
+        psList* list = metadataItem->data.list;
+        if (list != NULL) {
+            psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+            psMetadataItem* listItem;
+            while ((listItem=(psMetadataItem*)psListGetAndIncrement(iter)) != NULL) {
+                psMetadataAddItem(md,listItem,location,flags);
+            }
+            psFree(iter);
+        }
+
         return true; // all done.
     }
 
     // how the item is added to the hash depends on prior existence, flags, etc.
-    if(existingEntry == NULL) { // no prior existence
-        // Node doesn't already exist - Add new metadata item to metadata collection's hash
-        if(!psHashAdd(mdTable, key, metadataItem)) {
-            psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
-            return false;
-        }
-    } else {
+    if(existingEntry != NULL) { // prior existence
         if (existingEntry->type == PS_META_MULTI || (flags & PS_META_DUPLICATE_OK) != 0) {
             // duplicate entries allowed - add another entry.
 
-            // make sure the existing hash entry is PS_META_MULTI
+            // make sure the existing entry is PS_META_MULTI
             existingEntry = makeMetaMulti(mdTable,key,existingEntry);
 
-            // add to the hash key's list of entries
+            // add to the hash's list of duplicate entries
             if (! psListAdd(existingEntry->data.list, PS_LIST_TAIL, metadataItem) ) {
                 psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
                 return false;
             }
+        } else if ((flags & PS_META_REPLACE) != 0) {
+            // replace entry instead of creating a duplicate entry.
+
+            // remove the existing entry from metadata
+            psMetadataRemove(md,0,key);
+
+            // treat as if new (added to list below)
+            if(!psHashAdd(mdTable, key, metadataItem)) {
+                psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
+                return false;
+            }
+
+            // Generate warning that item has been replaced
+            psLogMsg(__func__,PS_LOG_WARN,"Metadata item %s has been replaced",
+                     existingEntry->name);
         } else {
-            // error on duplicate entry.
+            // default is to error on duplicate entry.
             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
                     PS_ERRORTEXT_psMetadata_DUPLICATE_NOT_ALLOWED);
             return false;
         }
-    }
-
-    // finally, add the metadataItem to the metadata's list.
+    } else {
+        // OK, this is a new item.
+
+        // Node doesn't exist - Add new metadata item to metadata collection's hash
+        if(!psHashAdd(mdTable, key, metadataItem)) {
+            psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
+            return false;
+        }
+    }
+
     if(!psListAdd(mdList, location, metadataItem)) {
         psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
@@ -363,4 +406,5 @@
 METADATA_ADD_TYPE(LookupTable,psLookupTable*,PS_META_LOOKUPTABLE)
 METADATA_ADD_TYPE(Unknown,void*,PS_META_UNKNOWN)
+METADATA_ADD_TYPE(Metadata,psMetadata*,PS_META_META)
 
 psBool psMetadataRemove(psMetadata *md, psS32 where, const char *key)
Index: /trunk/psLib/src/collections/psMetadata.h
===================================================================
--- /trunk/psLib/src/collections/psMetadata.h	(revision 3944)
+++ /trunk/psLib/src/collections/psMetadata.h	(revision 3945)
@@ -11,6 +11,6 @@
 *  @author Ross Harman, MHPCC
 *
-*  @version $Revision: 1.44 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-04-08 17:58:57 $
+*  @version $Revision: 1.45 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-16 19:43:53 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -51,5 +51,6 @@
     PS_META_ASTROM,                    ///< Astrometric coefficients (Stored as item.data.V).
     PS_META_UNKNOWN,                   ///< Other data (Stored as item.data.V).
-    PS_META_MULTI                      ///< Used internally, do not create an metadata item of this type.
+    PS_META_MULTI,                     ///< Used internally, do not create an metadata item of this type.
+    PS_META_META                       ///< Metadata data (Stored as item.data.md).
 } psMetadataType;
 
@@ -105,5 +106,4 @@
 }
 psMetadataIterator;
-
 
 /** Metadata item data structure.
@@ -316,4 +316,6 @@
 psBool psMetadataAddUnknown(psMetadata* md, psS32 location, const char* name,
                             const char* comment, psPtr value);
+psBool psMetadataAddMetadata(psMetadata* md, psS32 location, const char* name,
+                             const char* comment, psMetadata* value);
 
 /** Remove an item from metadata collection.
Index: /trunk/psLib/src/collections/psMetadataIO.c
===================================================================
--- /trunk/psLib/src/collections/psMetadataIO.c	(revision 3944)
+++ /trunk/psLib/src/collections/psMetadataIO.c	(revision 3945)
@@ -8,7 +8,8 @@
 *
 *  @author Ross Harman, MHPCC
+*  @author Eric Van Alst, MHPCC
 *
-*  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-04-26 19:53:30 $
+*  @version $Revision: 1.26 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-16 19:43:53 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -33,5 +34,4 @@
 #include "psConstants.h"
 #include "psAstronomyErrors.h"
-
 
 /******************************************************************************/
@@ -53,23 +53,4 @@
 return NULL;
 
-/** Free and null temporary variables used by config file parser */
-#define CLEAR_TEMPS()                                                                                        \
-if(strName) {                                                                                                \
-    psFree(strName);                                                                                         \
-    strName = NULL;                                                                                          \
-}                                                                                                            \
-if(strType) {                                                                                                \
-    psFree(strType);                                                                                         \
-    strType = NULL;                                                                                          \
-}                                                                                                            \
-if(strValue) {                                                                                               \
-    psFree(strValue);                                                                                        \
-    strValue = NULL;                                                                                         \
-}                                                                                                            \
-if(strComment) {                                                                                             \
-    psFree(strComment);                                                                                      \
-    strComment = NULL;                                                                                       \
-}
-
 /** Maximum size of a FITS line */
 #define FITS_LINE_SIZE 80
@@ -105,7 +86,24 @@
 static void initMetadataItemXml(void *ctx, char *tagName);
 static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts);
-
-/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
- *  must be null terminated. */
+static psMetadata* getMetadataType(char *linePtr);
+static psMetadata* setMetadataItem(psMetadata* template, char* linePtr)
+;
+static void parseLevelInfoFree(p_psParseLevelInfo* info);
+static psBool parseLine(psS32* level,   psArray* levelArray,
+                        char*  linePtr, psS32 lineCount,     char* fileName, psBool overwrite);
+static psBool parseMetadataItem(char* keyName, psS32* level,     psArray* levelArray,
+                                char* linePtr, psS32  lineCount, char*    fileName,    psMetadataFlags flags);
+
+static void parseLevelInfoFree(p_psParseLevelInfo* info)
+{
+    psFree(info->nonUniqueKeyArray);
+    psFree(info->typeArray);
+    psFree(info->templateArray);
+    psFree(info->name);
+    psFree(info->metadata);
+}
+
+// Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+// must be null terminated.
 psBool ignoreLine(char *inString)
 {
@@ -121,7 +119,7 @@
 
 
-/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
- *  terminated copy of the original input string. */
-char *cleanString(char *inString, psS32 sLen)
+//  Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+//  terminated copy of the original input string.
+char *cleanString(char *inString, psS32 sLen, psBool ignoreComment)
 {
     char *ptrB = NULL;
@@ -129,16 +127,28 @@
     char *cleaned = NULL;
 
-
+    // Initialize begining of string pointer
     ptrB = inString;
 
-    /* Skip over leading # or whitespace */
-    while (isspace(*ptrB) || *ptrB=='#') {
-        ptrB++;
-    }
-
-    /* Skip over trailing whitespace, null terminators, and # characters */
+    // Skip over leading # or whitespace
+    if(ignoreComment) {
+        while (isspace(*ptrB) || *ptrB=='#') {
+            ptrB++;
+        }
+    } else {
+        while (isspace(*ptrB)) {
+            ptrB++;
+        }
+    }
+
+    // Skip over trailing whitespace, null terminators, and # characters
     ptrE = inString + sLen;
-    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
-        ptrE--;
+    if(ignoreComment) {
+        while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+            ptrE--;
+        }
+    } else {
+        while(isspace(*ptrE) || *ptrE=='\0') {
+            ptrE--;
+        }
     }
 
@@ -152,9 +162,8 @@
 }
 
-/** Count repeat occurances of a single character within a line. The input string must be null terminated. */
+// Count repeat occurances of a single character within a line. The input string must be null terminated.
 psS32 repeatedChars(char *inString, char ch)
 {
     psS32 count = 0;
-
 
     while(*inString!='\0') {
@@ -168,11 +177,16 @@
 }
 
-/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
- * the beginning of the string. Tokens are newly allocated null terminated strings. */
-char* getToken(char **inString, char *delimiter, psS32 *status)
+// Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+// the beginning of the string. Tokens are newly allocated null terminated strings.
+char* getToken(char **inString, char *delimiter, psS32 *status, psBool ignoreComment)
 {
     char *cleanToken = NULL;
+    char *convertChar = NULL;
     psS32 sLen = 0;
 
+    // Convert tab characters to white space
+    while((convertChar=strchr(*inString,'\t')) != NULL ) {
+        *convertChar = ' ';
+    }
 
     // Skip over leading whitespace
@@ -186,5 +200,5 @@
 
         // Create new, cleaned, and null terminated token
-        cleanToken = cleanString(*inString, sLen);
+        cleanToken = cleanString(*inString, sLen,ignoreComment);
 
         // Move to end of token
@@ -197,11 +211,10 @@
 }
 
-/** Returns single parsed value as a double precision number. The input string must be cleaned and null
- * terminated. */
+// Returns single parsed value as a double precision number. The input string must be cleaned and null
+// terminated.
 double parseValue(char *inString, psS32 *status)
 {
     char *end = NULL;
     double value = 0.0;
-
 
     value = strtod(inString, &end);
@@ -235,11 +248,10 @@
 psVector* parseVector(char *inString, psElemType elemType, psS32 *status)
 {
-    char *end = NULL;
-    char *saveValue = NULL;
-    psS32 i = 0;
-    psS32 numValues = 0;
-    double value = 0.0;
-    psVector *vec = NULL;
-
+    char*      end       = NULL;
+    char*      saveValue = NULL;
+    psS32      i         = 0;
+    psS32      numValues = 0;
+    double     value     = 0.0;
+    psVector*  vec       = NULL;
 
     // Cycle through string and count entries
@@ -274,6 +286,24 @@
                 vec->data.U8[i++] = (psU8)value;
                 break;
+            case PS_TYPE_U16:
+                vec->data.U16[i++] = (psU16)value;
+                break;
+            case PS_TYPE_U32:
+                vec->data.U32[i++] = (psU32)value;
+                break;
+            case PS_TYPE_U64:
+                vec->data.U64[i++] = (psU64)value;
+                break;
+            case PS_TYPE_S8:
+                vec->data.S8[i++] = (psS8)value;
+                break;
+            case PS_TYPE_S16:
+                vec->data.S16[i++] = (psS16)value;
+                break;
             case PS_TYPE_S32:
                 vec->data.S32[i++] = (psS32)value;
+                break;
+            case PS_TYPE_S64:
+                vec->data.S64[i++] = (psS64)value;
                 break;
             case PS_TYPE_F32:
@@ -303,4 +333,33 @@
 /* FUNCTION IMPLEMENTATION - PUBLIC                                          */
 /*****************************************************************************/
+
+p_psParseLevelInfo* p_psParseLevelInfoAlloc(void)
+{
+
+    p_psParseLevelInfo* info = NULL;
+
+    // Allocate memory for parse level info
+    info = (p_psParseLevelInfo*)psAlloc(sizeof(p_psParseLevelInfo));
+
+    // If allocation successful then initialize members
+    if(info != NULL) {
+
+        info->nonUniqueKeyArray = psArrayAlloc(10);
+        info->nonUniqueKeyArray->n = 0;
+
+        info->typeArray = psArrayAlloc(10);
+        info->typeArray->n = 0;
+
+        info->templateArray = psArrayAlloc(10);
+        info->templateArray->n = 0;
+
+        info->metadata = NULL;
+        info->name = NULL;
+
+        // Set memory deallocator
+        psMemSetDeallocator(info,(psFreeFcn)parseLevelInfoFree);
+    }
+    return info;
+}
 
 bool psMetadataItemPrint(FILE * fd, const char *format, const psMetadataItem* metadataItem)
@@ -500,26 +559,537 @@
 }
 
+static psMetadata* getMetadataType(char* linePtr)
+{
+    psMetadata*     metadataTemplate = NULL;
+    psS32           status           = 0;
+    char*           token            = NULL;
+    psMetadataItem* tempItem         = NULL;
+
+    // Loop through line and generate metadata items for each token found
+    while((token = getToken(&linePtr," ",&status,false)) != NULL ) {
+
+        // If not allocated then allocate new metadata
+        if(metadataTemplate == NULL) {
+            metadataTemplate = psMetadataAlloc();
+        }
+
+        // Look for comment indicator #
+        if(strstr(token,"#") != 0) {
+            psFree(token);
+            break;
+        }
+
+        // Create metadata item to represent token read
+        tempItem = psMetadataItemAllocStr(token,"","");
+        if(tempItem == NULL) {
+            psFree(metadataTemplate);
+            psFree(token);
+            metadataTemplate = NULL;
+            break;
+        }
+
+        // Add item to template
+        if(!psMetadataAddItem(metadataTemplate, tempItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+            psFree(metadataTemplate);
+            psFree(tempItem);
+            psFree(token);
+            metadataTemplate = NULL;
+            break;
+        }
+        psFree(tempItem);
+        psFree(token);
+    }
+
+    return metadataTemplate;
+}
+
+static psMetadata* setMetadataItem(psMetadata* template, char* linePtr)
+{
+    psMetadata*      md           = NULL;
+    psS32            items        = 0;
+    char*            token        = NULL;
+    psS32            status       = 0;
+    psMetadataItem*  mdItem       = NULL;
+    psMetadataItem*  templateItem = NULL;
+    psListIterator*  iter         = NULL;
+
+    // Determine the number of items in template
+    items = template->list->size;
+    if(items > 0 ) {
+
+        // Allocate metadata
+        md = psMetadataAlloc()
+             ;
+
+        // Point to first item in template
+        iter = psListIteratorAlloc(template->
+                                   list,PS_LIST_HEAD,true);
+
+        // For each item in template parse line string for values
+        for(psS32 i = 0;
+                i < items;
+                i++) {
+
+            // Get template item
+            templateItem = psListGetAndIncrement(iter)
+                           ;
+            if(templateItem == NULL) {
+                psFree(md);
+                md = NULL;
+                break;
+            }
+
+            // Get the next token on the line
+            token = getToken(&linePtr," ",&status,false);
+            if(token != NULL) {
+                // Allocate metadata item
+                mdItem = psMetadataItemAllocStr(templateItem->name,templateItem->comment,token);
+                if(mdItem == NULL) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(token);
+                    break;
+                }
+                // Add item to metadata
+                if(!psMetadataAddItem(md, mdItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(mdItem);
+                    psFree(token);
+                    break;
+                }
+                psFree(mdItem);
+            } else {
+                // Missing items
+                psFree(md);
+                md = NULL;
+                break;
+            }
+
+            psFree(token);
+        }
+        psFree(iter);
+    }
+    return md;
+}
+
+psBool parseMetadataItem(char* keyName, psS32* level,     psArray* levelArray,
+                         char* linePtr, psS32  lineCount, char*    fileName,   psMetadataFlags flags)
+{
+    psBool               returnValue   = true;
+    psBool               addStatus     = false;
+    psMetadataType       mdType        = PS_META_UNKNOWN;
+    psElemType           vectorType    = PS_TYPE_S8;
+    char*                strType       = NULL;
+    char*                strValue      = NULL;
+    char*                strComment    = NULL;
+    psS32                status        = 0;
+    psMetadata*          md            = NULL;
+    psF64                tempDbl       = 0.0;
+    psBool               tempBool      = false;
+    psS32                tempInt       = 0;
+    psVector*            tempVec       = NULL;
+    char*                tempStr       = NULL;
+    psArray*             nonUniqueKeys = NULL;
+    psBool               typeFound     = false;
+    psArray*             typeArray     = NULL;
+    psArray*             templateArray = NULL;
+    psMetadata*          tempMeta      = NULL;
+    p_psParseLevelInfo*  nextLevelInfo = NULL;
+
+    // Get the metadata item type
+    strType = getToken(&linePtr, " ", &status,true);
+
+    // Check for no type
+    if(strType==NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
+                fileName);
+        returnValue = false;
+    } else {
+
+        // Set metadata type based on type token
+
+        // Check if the keyName specifies a vector and if so use strType token to find vector type
+        if(*keyName == '@') {
+            mdType = PS_META_VEC;
+            // Get the type of vector
+            if(!strncmp(strType, "U8", 2)) {
+                vectorType = PS_TYPE_U8;
+            } else if (!strncmp(strType,"U16",3)) {
+                vectorType = PS_TYPE_U16;
+            } else if (!strncmp(strType,"U32",3)) {
+                vectorType = PS_TYPE_U32;
+            } else if (!strncmp(strType,"U64",3)) {
+                vectorType = PS_TYPE_U64;
+            } else if (!strncmp(strType,"S8",2)) {
+                vectorType = PS_TYPE_S8;
+            } else if (!strncmp(strType,"S16",3)) {
+                vectorType = PS_TYPE_S16;
+            } else if (!strncmp(strType,"S32",3)) {
+                vectorType = PS_TYPE_S32;
+            } else if (!strncmp(strType,"S64",3)) {
+                vectorType = PS_TYPE_S64;
+            } else if (!strncmp(strType,"F32",3)) {
+                vectorType = PS_TYPE_F32;
+            } else if (!strncmp(strType,"F64",3)) {
+                vectorType = PS_TYPE_F64;
+            } else {
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, "", keyName,
+                        strType, lineCount, fileName);
+                psFree(strType);
+                return false;
+            }
+        } else if(!strncmp(strType, "STR", 3)) {
+            mdType = PS_META_STR;
+        } else if(!strncmp(strType, "BOOL", 4)) {
+            mdType = PS_META_BOOL;
+        } else if(!strncmp(strType, "S32", 3)) {
+            mdType = PS_META_S32;
+        } else if(!strncmp(strType, "F32", 3)) {
+            mdType = PS_META_F32;
+        } else if(!strncmp(strType, "F64", 3)) {
+            mdType = PS_META_F64;
+        } else if(!strncmp(strType, "MULTI", 5)) {
+            mdType = PS_META_MULTI;
+        } else if(!strncmp(strType, "METADATA", 8)) {
+            mdType = PS_META_META;
+        } else {
+            // Search through user types
+            typeArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->typeArray;
+            templateArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->templateArray;
+            for(psS32 k = 0; k < typeArray->n; k++) {
+                if(strcmp(strType,(char*)typeArray->data[k]) == 0) {
+                    tempMeta = setMetadataItem((psMetadata*)templateArray->data[k],linePtr);
+                    if(tempMeta != NULL) {
+                        // Add metadata item
+                        md = ((p_psParseLevelInfo*)(levelArray->data[*level]))->metadata;
+                        addStatus = psMetadataAdd(md,PS_LIST_TAIL,keyName,PS_META_META | flags,"",tempMeta);
+                        // Check for add failure
+                        if (! addStatus) {
+                            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM,
+                                    keyName, lineCount, fileName);
+                            psFree(strType);
+                            psFree(tempMeta);
+                            return false;
+                        }
+                        psFree(tempMeta);
+                    } else {
+                        // Metadata type read error
+                        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL,
+                                keyName,lineCount,fileName);
+                        psFree(strType);
+                        return false;
+                    }
+                    typeFound = true;
+                    break;
+                }
+            }
+            if(!typeFound) {
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                        strType, lineCount,fileName);
+                psFree(strType);
+                return false;
+            } else {
+                psFree(strType);
+                return true;
+            }
+        }
+    }
+
+    // If type is not MULTI or META then get the value and comment
+    if((mdType != PS_META_MULTI) && (mdType != PS_META_META)) {
+        // Get the metadata item value if there is one.
+        status = 0;
+        strValue = getToken(&linePtr, "#", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+        if(strValue==NULL) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+        // Not all lines will have comments, so NULL is ok.
+        status = 0;
+        strComment = getToken(&linePtr,"~", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            psFree(strComment);
+        }
+    }
+
+    // Need to add item to metadata so get pointer to metadata
+    status = 0;
+    md = ((p_psParseLevelInfo*)(levelArray->data[*level]))->metadata;
+    nonUniqueKeys = ((p_psParseLevelInfo*)(levelArray->data[*level]))->nonUniqueKeyArray;
+    switch(mdType) {
+    case PS_META_STR:
+        addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                  mdType | flags,
+                                  strComment, strValue);
+        break;
+    case PS_META_BOOL:
+        tempBool = parseBool(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempBool);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_F32:
+    case PS_META_F64:
+        tempDbl = parseValue(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempDbl);
+        } else {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName, strType, lineCount,
+                    fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_S32:
+        tempInt = (psS32)parseValue(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempInt);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_VEC:
+        tempVec = parseVector(strValue, vectorType, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName+1,
+                                      mdType | flags,
+                                      strComment, tempVec);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        psFree(tempVec);
+        break;
+    case PS_META_MULTI:
+        // Add key to non-unique array of keys
+        // Check for duplicate MULTI lines
+        addStatus = true;
+        for(psS32 k=0; k < nonUniqueKeys->n; k++) {
+            if(strcmp(keyName,(char*)nonUniqueKeys->data[k]) == 0) {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_DUPLICATE_MULTI,
+                        lineCount,fileName);
+                psFree(strType);
+                return false;
+            }
+        }
+        tempStr = psStringCopy(keyName);
+        nonUniqueKeys = psArrayAdd(nonUniqueKeys,0,tempStr);
+        addStatus = true;
+        psFree(tempStr);
+        break;
+    case PS_META_META:
+        // Create next level info
+        nextLevelInfo = p_psParseLevelInfoAlloc();
+        // Create new metadata
+        nextLevelInfo->metadata = psMetadataAlloc();
+        // Save name of metadata
+        nextLevelInfo->name = psStringCopy(keyName);
+        // Add next level to levelArray
+        levelArray = psArrayAdd(levelArray,1,nextLevelInfo);
+        psFree(nextLevelInfo);
+        // Increment level counter
+        (*level)++;
+        addStatus = true;
+        break;
+    default:
+        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                lineCount,fileName);
+        break;
+    }
+
+    // Check if the add status was successful
+    if (! addStatus) {
+        //        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, keyName, lineCount,
+        //                fileName);
+        returnValue = false;
+    }
+
+    psFree(strComment);
+    psFree(strValue);
+    psFree(strType);
+
+    return returnValue;
+}
+
+psBool parseLine(psS32* level,     psArray* levelArray, char*  linePtr,
+                 psS32  lineCount, char*    fileName,   psBool overwrite)
+{
+    psBool              returnValue    = true;
+    psMetadataFlags     flags          = PS_META_DEFAULT;
+    char*               keyName        = NULL;
+    psS32               status         = 0;
+    psS32               limit          = 0;
+    psMetadata*         tempTemplate   = NULL;
+    char*               strType        = NULL;
+    psArray*            typeArray      = NULL;
+    psArray*            templateArray  = NULL;
+    p_psParseLevelInfo* upperLevelInfo = NULL;
+    p_psParseLevelInfo* lowerLevelInfo = NULL;
+    psBool              addStatus      = 0;
+
+    // Set flags if overwrite specified
+    if(overwrite) {
+        flags = PS_META_REPLACE;
+    }
+
+    // If line is not a comment or blank, then extract data
+    if(!ignoreLine(linePtr)) {
+
+        // Check for more than one '@' in a line
+        if(repeatedChars(linePtr, '@') > 1) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
+            return false;
+        }
+
+        // Get metadata item name
+        keyName = getToken(&linePtr, " ", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "keyName", lineCount, fileName);
+            psFree(keyName);
+            return false;
+        }
+
+        // Check for special keyName values "TYPE", "END"
+        if(strcmp(keyName,"TYPE") == 0 ) {
+            // Get the type name
+            strType = getToken(&linePtr," ",&status,true);
+            if(strType == NULL) {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL,"type",lineCount,
+                        fileName);
+                psFree(keyName);
+                return false;
+            }
+            tempTemplate = getMetadataType(linePtr);
+            // Check if type was parsed succesfully
+            if(tempTemplate != NULL) {
+                // Access type array
+                typeArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->typeArray;
+                // Check if type already exists in array
+                for(psS32 k=0; k < typeArray->n; k++) {
+                    // Compare type name with the list of current types
+                    if(strcmp(strType,(char*)typeArray->data[k]) == 0) {
+                        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_TYPE_DUPLICATE,
+                                strType,lineCount,fileName);
+                        psFree(tempTemplate);
+                        psFree(keyName);
+                        psFree(strType);
+                        return false;
+                    }
+                }
+                // Add key name to array of type
+                typeArray = psArrayAdd(typeArray,1,strType);
+                // Add template to array of templates
+                templateArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->templateArray;
+                templateArray = psArrayAdd(templateArray,1,tempTemplate);
+                psFree(tempTemplate);
+            } else {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                        strType,lineCount,fileName);
+                psFree(keyName);
+                psFree(strType);
+                return false;
+            }
+            psFree(strType);
+        } else if (strcmp(keyName,"END") == 0 ) {
+            if(*level > 0) {
+                upperLevelInfo = ((p_psParseLevelInfo*)(levelArray->data[*level-1]));
+                lowerLevelInfo = ((p_psParseLevelInfo*)(levelArray->data[*level]));
+                // Check name in nonunique list
+                for(psS32 k=0; k < upperLevelInfo->nonUniqueKeyArray->n; k++) {
+                    if(strcmp(upperLevelInfo->nonUniqueKeyArray->data[k],lowerLevelInfo->name) == 0) {
+                        flags = PS_META_DUPLICATE_OK;
+                        break;
+                    }
+                }
+                // Add metadata to upper level metadata
+                addStatus = psMetadataAdd(upperLevelInfo->metadata,
+                                          PS_LIST_TAIL,lowerLevelInfo->name,
+                                          PS_META_META | flags,
+                                          "",
+                                          lowerLevelInfo->metadata);
+                if(!addStatus) {
+                    psFree(keyName);
+                    return false;
+                } else {
+                    // Remove lower info level
+                    if(!psArrayRemove(levelArray,levelArray->data[*level])) {
+                        psFree(keyName);
+                        return false;
+                    }
+                    psFree(lowerLevelInfo);
+                    (*level)--;
+                }
+            } else {
+                psFree(keyName);
+                return false;
+            }
+        } else {
+            // Check if key name present in array of non-unique key names
+            limit = ((p_psParseLevelInfo*)(levelArray->data[*level]))->nonUniqueKeyArray->n;
+            for(psS32 k=0; k < limit; k++) {
+                char* name = (char*)((p_psParseLevelInfo*)
+                                     (levelArray->data[*level]))->nonUniqueKeyArray->data[k];
+                if(strcmp(name,keyName) == 0) {
+                    flags = PS_META_DUPLICATE_OK;
+                }
+            }
+            // Parse metadataItem
+            if(!parseMetadataItem(keyName,level, levelArray, linePtr, lineCount, fileName, flags)) {
+                psFree(keyName);
+                return false;
+            }
+        }
+        psFree(keyName);
+    }
+    return returnValue;
+}
+
 psMetadata* psMetadataParseConfig(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
 {
-    psBool tempBool;
-    char *line = NULL;
-    char *strName = NULL;
-    char *strType = NULL;
-    char *strValue = NULL;
-    char *strComment = NULL;
-    char *linePtr = NULL;
-    psElemType vecType = 0;
-    psS32 status = 0;
-    psU32 lineCount = 0;
-    psF64 tempDbl = 0.0;
-    psS32 tempInt = 0.0;
-    psVector *tempVec = NULL;
-    FILE *fp = NULL;
-    psMetadataType mdType;
-    psMetadataFlags flags;
-    psBool addStatus;
-
-    // Check for nulls
+    FILE*               fp                   = NULL;
+    char*               line                 = NULL;
+    char*               linePtr              = NULL;
+    psArray*            parseLevelInfoArray  = NULL;
+    psS32               lineCount            = 0;
+    psS32               nestingLevel         = 0;
+    p_psParseLevelInfo* topLevelInfo         = NULL;
+
+    // Check for NULL file name
     PS_PTR_CHECK_NULL(fileName,NULL);
+
+    // Check for NULL nFail
+    PS_PTR_CHECK_NULL(nFail,NULL);
+
+    // Attempt to open specified file
     if((fp=fopen(fileName, "r")) == NULL) {
         psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
@@ -532,4 +1102,14 @@
     }
 
+    // Allocate array to store parse level information
+    parseLevelInfoArray = psArrayAlloc(10);
+    parseLevelInfoArray->n = 0;
+
+    // Set parse level info for the top level
+    topLevelInfo = p_psParseLevelInfoAlloc();
+    topLevelInfo->metadata = psMemIncrRefCounter(md);
+    parseLevelInfoArray = psArrayAdd(parseLevelInfoArray,1,topLevelInfo);
+    psFree(topLevelInfo);
+
     // Create reusable line for continuous read
     line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
@@ -541,189 +1121,22 @@
         linePtr = line;
         lineCount++;
-        CLEAR_TEMPS();
-
-        // If line is not a comment or blank, then extract data
-        if(!ignoreLine(linePtr)) {
-
-            // Check for more than one '*' or '@' in a line
-            if(repeatedChars(linePtr, '@') > 1) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
-                continue;
-            } else if(repeatedChars(linePtr, '*') > 1) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '*', lineCount, fileName);
-                continue;
-            } else if(repeatedChars(linePtr, '~') > 0) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '~', lineCount, fileName);
-                continue;
-            }
-
-            // Get metadata item name
-            strName = getToken(&linePtr, " ", &status);
-            if(strName==NULL || status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "name", lineCount, fileName);
-                continue;
-            }
-
-            flags = (overwrite) ? PS_META_REPLACE : PS_META_DEFAULT;
-
-            // Get the metadata item type
-            strType = getToken(&linePtr, " ", &status);
-            if(strType==NULL) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
-                        fileName);
-                continue;
-            } else {
-                char* tempStrType = strType;
-                if(*strType == '*') {
-                    flags = PS_META_DUPLICATE_OK;
-                    tempStrType = strType+1;
-                }
-
-                if(!strncmp(tempStrType, "STR", 3)) {
-                    mdType = PS_META_STR;
-                } else if(!strncmp(tempStrType, "BOOL", 4)) {
-                    mdType = PS_META_BOOL;
-                } else if(!strncmp(tempStrType, "S32", 3)) {
-                    mdType = PS_META_S32;
-                } else if(!strncmp(tempStrType, "F32", 3)) {
-                    mdType = PS_META_F32;
-                } else if(!strncmp(tempStrType, "F64", 3)) {
-                    mdType = PS_META_F64;
-                } else {
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineCount,
-                            fileName);
-                    continue;
-                }
-            }
-
-            if(*strName == '@') {
-                vecType = PS_META_PRIMITIVE_TYPE(mdType);
-                mdType = PS_META_VEC;
-            }
-
-            // Get the metadata item value if there is one.
-            strValue = getToken(&linePtr, "#", &status);
-            if(status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
-                        fileName);
-                continue;
-            }
-            if(strValue==NULL) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
-                        fileName);
-                continue;
-            }
-
-            // Not all lines will have comments, so NULL is ok.
-            strComment = getToken(&linePtr,"~", &status);
-            if(status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
-                        fileName);
-                continue;
-            }
-
-            // Create and add metadata item to metadata and parse values
-            switch (mdType) {
-            case PS_META_STR:
-                addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                          mdType | flags,
-                                          strComment, strValue);
-                break;
-            case PS_META_VEC:
-                tempVec = parseVector(strValue, vecType, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName+1,
-                                              mdType | flags,
-                                              strComment, tempVec);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                psFree(tempVec);
-                break;
-            case PS_META_BOOL:
-                tempBool = parseBool(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempBool);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                break;
-            case PS_META_S32:
-                tempInt = (psS32)parseValue(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempInt);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                break;
-            case PS_META_F32:
-            case PS_META_F64:
-                tempDbl = parseValue(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempDbl);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true,
-                            PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType, lineCount,
-                            fileName);
-                    continue;
-                }
-                break;
-            default:
-                (*nFail)++;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, mdType, lineCount,
-                        fileName);
-                continue;
-            } // switch
-            if (! addStatus) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineCount,
-                        fileName);
-            }
-
-        } // if ignoreLine
-    } // while loop
-
+
+        if(!parseLine(&nestingLevel,parseLevelInfoArray,linePtr,lineCount,(char*)fileName,overwrite)) {
+            (*nFail)++;
+        }
+    }
+
+    // Free parse array and line buffer
+    psFree(parseLevelInfoArray);
     psFree(line);
 
+    // Check if any failed lined detected
+    if( (*nFail) != 0) {
+        psFree(md);
+        md = NULL;
+    }
     return md;
 }
+
 
 static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts)
@@ -750,5 +1163,5 @@
 
     // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-    psTagName = psStringCopy((const char*)tagName);
+    psTagName = psStringCopy(tagName);
 
     // Metadata containter for housing element attributes used by other SAX events
@@ -774,6 +1187,6 @@
 
                 // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-                psAttName = psStringCopy((const char*)attName);
-                psAttValue = psStringCopy((const char*)attValue);
+                psAttName = psStringCopy(attName);
+                psAttValue = psStringCopy(attValue);
                 psHashAdd(htAtts, psAttName, psAttValue);
                 psFree(psAttName);
@@ -1082,5 +1495,5 @@
 
     // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-    psEndTagName = psStringCopy((const char*)tagName);
+    psEndTagName = psStringCopy(tagName);
 
     // Compare start and end tag names
Index: /trunk/psLib/src/collections/psMetadataIO.h
===================================================================
--- /trunk/psLib/src/collections/psMetadataIO.h	(revision 3944)
+++ /trunk/psLib/src/collections/psMetadataIO.h	(revision 3945)
@@ -10,6 +10,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-03-07 20:58:50 $
+ *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-16 19:43:53 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -21,4 +21,15 @@
 /// @{
 
+typedef struct
+{
+    psArray*    nonUniqueKeyArray;
+    psArray*    typeArray;
+    psArray*    templateArray;
+    psMetadata* metadata;
+    char*       name;
+}
+p_psParseLevelInfo;
+
+p_psParseLevelInfo* p_psParseLevelInfoAlloc(void);
 
 /** Print metadata item to file.
Index: /trunk/psLib/src/types/psMetadata.c
===================================================================
--- /trunk/psLib/src/types/psMetadata.c	(revision 3944)
+++ /trunk/psLib/src/types/psMetadata.c	(revision 3945)
@@ -12,6 +12,6 @@
 *  @author Ross Harman, MHPCC
 *
-*  @version $Revision: 1.61 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-04-28 19:52:48 $
+*  @version $Revision: 1.62 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-16 19:43:53 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -38,6 +38,34 @@
 #include "psAstronomyErrors.h"
 #include "psConstants.h"
+#include "psLogMsg.h"
+
+/******************************************************************************/
+/*  DEFINE STATEMENTS                                                         */
+/******************************************************************************/
+
+/** Maximum size of a string */
+#define MAX_STRING_LENGTH 1024
+
+/******************************************************************************/
+/*  TYPE DEFINITIONS                                                          */
+/******************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  GLOBAL VARIABLES                                                         */
+/*****************************************************************************/
+
+// None
+
+/*****************************************************************************/
+/*  FILE STATIC VARIABLES                                                    */
+/*****************************************************************************/
 
 static psS32 metadataId = 0;
+
+/*****************************************************************************/
+/*  FUNCTION IMPLEMENTATION - LOCAL                                          */
+/*****************************************************************************/
 
 static psMetadataItem* makeMetaMulti(psHash* table, const char* key, psMetadataItem* existing)
@@ -48,11 +76,11 @@
     }
 
+    // move any existing entry into a psList
+    psList* newList = psListAlloc(existing);
 
     psMetadataItem* item = psMetadataItemAlloc(key,
                            PS_META_MULTI,
-                           "List of Metadata Items",
-                           NULL);
-
-    psListAdd(item->data.list,PS_LIST_TAIL,existing);
+                           "",
+                           newList);
 
     if (existing != NULL) {
@@ -60,7 +88,8 @@
     }
 
-    psHashAdd(table, key, item); // put in the new MULTI list entry
-
-    // free local references of newly allocated item.
+    psHashAdd(table, key, item); // put in the new entry
+
+    // free local references of newly allocated items.
+    psFree(newList);
     psFree(item);
 
@@ -80,5 +109,6 @@
     psFree(metadataItem->name);
     psFree(metadataItem->comment);
-    if (! PS_META_IS_PRIMITIVE(type)) {
+
+    if(!PS_META_IS_PRIMITIVE(type)) {
         psFree(metadataItem->data.V);
     }
@@ -104,4 +134,5 @@
     psFree(metadata->list);
     psFree(metadata->table);
+
 }
 
@@ -146,6 +177,5 @@
 {
     psMetadataItem* metadataItem = NULL;
-    char tmp;
-    int nBytes;
+
 
     PS_PTR_CHECK_NULL(name,NULL);
@@ -158,10 +188,11 @@
     psMemSetDeallocator(metadataItem, (psFreeFcn) metadataItemFree);
 
-    // set metadata item comment
+    // Allocate and set metadata item comment
+    metadataItem->comment = (char *)psAlloc(sizeof(char) * MAX_STRING_LENGTH);
     if (comment == NULL) {
         // Per SDRS, null isn't allowed, must use "" instead
-        metadataItem->comment = psStringCopy("");
+        strncpy(metadataItem->comment, "", MAX_STRING_LENGTH);
     } else {
-        metadataItem->comment = psStringCopy(comment);
+        strncpy(metadataItem->comment, comment, MAX_STRING_LENGTH);
     }
 
@@ -173,6 +204,5 @@
 
     // Allocate and set metadata item name
-    nBytes = vsnprintf(&tmp, 0, name, argPtr) + 1;
-    metadataItem->name = (char *)psAlloc(sizeof(char) * nBytes);
+    metadataItem->name = (char *)psAlloc(sizeof(char) * MAX_STRING_LENGTH);
     vsprintf(metadataItem->name, name, argPtr);
 
@@ -193,9 +223,5 @@
     case PS_META_STR:
         // Perform copy of input strings
-        metadataItem->data.V = psStringCopy(va_arg(argPtr, char *));
-        break;
-    case PS_META_MULTI:
-        // MULTI needs to create a psList entry, value must be NULL
-        metadataItem->data.list = psListAlloc(NULL);
+        metadataItem->data.V = psStringNCopy(va_arg(argPtr, char *), MAX_STRING_LENGTH);
         break;
     case PS_META_LIST:
@@ -207,4 +233,6 @@
     case PS_META_ASTROM:
     case PS_META_UNKNOWN:
+    case PS_META_META:
+    case PS_META_MULTI:
         // Copy of input data not performed due to variability of data types
         metadataItem->data.V = psMemIncrRefCounter(va_arg(argPtr, psPtr));
@@ -258,55 +286,70 @@
 
     // See if key is already in table
-    existingEntry = psMetadataLookup(md, key);
-
-    // if replace is set, remove any existing items of the same key
-    if (existingEntry != NULL && (flags & PS_META_REPLACE) != 0) {
-        psMetadataRemove(md,0,key);
-        existingEntry = NULL;
-    }
-
-    // if the metadataItem is MULTI, just create a MULTI node.
+    existingEntry = (psMetadataItem*)psHashLookup(mdTable, key);
+
     if (metadataItem->type == PS_META_MULTI) {
-        if (metadataItem->data.list == NULL || metadataItem->data.list->size > 0) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE,true,
-                    "Specified PS_META_MULTI item is not defined properly "
-                    "(list allocated with zero size).");
-            return false;
-        }
-
-        // make sure the existing entry is PS_META_MULTI
+        // the incoming entry is PS_META_MULTI
+
+        // force the hash entry to be PS_META_MULTI
         existingEntry = makeMetaMulti(mdTable,key,existingEntry);
 
+        // add all the items in the incoming entry to metadata
+        psList* list = metadataItem->data.list;
+        if (list != NULL) {
+            psListIterator* iter = psListIteratorAlloc(list,PS_LIST_HEAD,true);
+            psMetadataItem* listItem;
+            while ((listItem=(psMetadataItem*)psListGetAndIncrement(iter)) != NULL) {
+                psMetadataAddItem(md,listItem,location,flags);
+            }
+            psFree(iter);
+        }
+
         return true; // all done.
     }
 
     // how the item is added to the hash depends on prior existence, flags, etc.
-    if(existingEntry == NULL) { // no prior existence
-        // Node doesn't already exist - Add new metadata item to metadata collection's hash
-        if(!psHashAdd(mdTable, key, metadataItem)) {
-            psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
-            return false;
-        }
-    } else {
+    if(existingEntry != NULL) { // prior existence
         if (existingEntry->type == PS_META_MULTI || (flags & PS_META_DUPLICATE_OK) != 0) {
             // duplicate entries allowed - add another entry.
 
-            // make sure the existing hash entry is PS_META_MULTI
+            // make sure the existing entry is PS_META_MULTI
             existingEntry = makeMetaMulti(mdTable,key,existingEntry);
 
-            // add to the hash key's list of entries
+            // add to the hash's list of duplicate entries
             if (! psListAdd(existingEntry->data.list, PS_LIST_TAIL, metadataItem) ) {
                 psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
                 return false;
             }
+        } else if ((flags & PS_META_REPLACE) != 0) {
+            // replace entry instead of creating a duplicate entry.
+
+            // remove the existing entry from metadata
+            psMetadataRemove(md,0,key);
+
+            // treat as if new (added to list below)
+            if(!psHashAdd(mdTable, key, metadataItem)) {
+                psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
+                return false;
+            }
+
+            // Generate warning that item has been replaced
+            psLogMsg(__func__,PS_LOG_WARN,"Metadata item %s has been replaced",
+                     existingEntry->name);
         } else {
-            // error on duplicate entry.
+            // default is to error on duplicate entry.
             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
                     PS_ERRORTEXT_psMetadata_DUPLICATE_NOT_ALLOWED);
             return false;
         }
-    }
-
-    // finally, add the metadataItem to the metadata's list.
+    } else {
+        // OK, this is a new item.
+
+        // Node doesn't exist - Add new metadata item to metadata collection's hash
+        if(!psHashAdd(mdTable, key, metadataItem)) {
+            psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_TABLE_FAILED,key);
+            return false;
+        }
+    }
+
     if(!psListAdd(mdList, location, metadataItem)) {
         psError(PS_ERR_UNKNOWN,false,PS_ERRORTEXT_psMetadata_ADD_COLLECTION_FAILED,key);
@@ -363,4 +406,5 @@
 METADATA_ADD_TYPE(LookupTable,psLookupTable*,PS_META_LOOKUPTABLE)
 METADATA_ADD_TYPE(Unknown,void*,PS_META_UNKNOWN)
+METADATA_ADD_TYPE(Metadata,psMetadata*,PS_META_META)
 
 psBool psMetadataRemove(psMetadata *md, psS32 where, const char *key)
Index: /trunk/psLib/src/types/psMetadata.h
===================================================================
--- /trunk/psLib/src/types/psMetadata.h	(revision 3944)
+++ /trunk/psLib/src/types/psMetadata.h	(revision 3945)
@@ -11,6 +11,6 @@
 *  @author Ross Harman, MHPCC
 *
-*  @version $Revision: 1.44 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-04-08 17:58:57 $
+*  @version $Revision: 1.45 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-16 19:43:53 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -51,5 +51,6 @@
     PS_META_ASTROM,                    ///< Astrometric coefficients (Stored as item.data.V).
     PS_META_UNKNOWN,                   ///< Other data (Stored as item.data.V).
-    PS_META_MULTI                      ///< Used internally, do not create an metadata item of this type.
+    PS_META_MULTI,                     ///< Used internally, do not create an metadata item of this type.
+    PS_META_META                       ///< Metadata data (Stored as item.data.md).
 } psMetadataType;
 
@@ -105,5 +106,4 @@
 }
 psMetadataIterator;
-
 
 /** Metadata item data structure.
@@ -316,4 +316,6 @@
 psBool psMetadataAddUnknown(psMetadata* md, psS32 location, const char* name,
                             const char* comment, psPtr value);
+psBool psMetadataAddMetadata(psMetadata* md, psS32 location, const char* name,
+                             const char* comment, psMetadata* value);
 
 /** Remove an item from metadata collection.
Index: /trunk/psLib/src/types/psMetadataConfig.c
===================================================================
--- /trunk/psLib/src/types/psMetadataConfig.c	(revision 3944)
+++ /trunk/psLib/src/types/psMetadataConfig.c	(revision 3945)
@@ -8,7 +8,8 @@
 *
 *  @author Ross Harman, MHPCC
+*  @author Eric Van Alst, MHPCC
 *
-*  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-04-26 19:53:30 $
+*  @version $Revision: 1.26 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-16 19:43:53 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -33,5 +34,4 @@
 #include "psConstants.h"
 #include "psAstronomyErrors.h"
-
 
 /******************************************************************************/
@@ -53,23 +53,4 @@
 return NULL;
 
-/** Free and null temporary variables used by config file parser */
-#define CLEAR_TEMPS()                                                                                        \
-if(strName) {                                                                                                \
-    psFree(strName);                                                                                         \
-    strName = NULL;                                                                                          \
-}                                                                                                            \
-if(strType) {                                                                                                \
-    psFree(strType);                                                                                         \
-    strType = NULL;                                                                                          \
-}                                                                                                            \
-if(strValue) {                                                                                               \
-    psFree(strValue);                                                                                        \
-    strValue = NULL;                                                                                         \
-}                                                                                                            \
-if(strComment) {                                                                                             \
-    psFree(strComment);                                                                                      \
-    strComment = NULL;                                                                                       \
-}
-
 /** Maximum size of a FITS line */
 #define FITS_LINE_SIZE 80
@@ -105,7 +86,24 @@
 static void initMetadataItemXml(void *ctx, char *tagName);
 static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts);
-
-/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
- *  must be null terminated. */
+static psMetadata* getMetadataType(char *linePtr);
+static psMetadata* setMetadataItem(psMetadata* template, char* linePtr)
+;
+static void parseLevelInfoFree(p_psParseLevelInfo* info);
+static psBool parseLine(psS32* level,   psArray* levelArray,
+                        char*  linePtr, psS32 lineCount,     char* fileName, psBool overwrite);
+static psBool parseMetadataItem(char* keyName, psS32* level,     psArray* levelArray,
+                                char* linePtr, psS32  lineCount, char*    fileName,    psMetadataFlags flags);
+
+static void parseLevelInfoFree(p_psParseLevelInfo* info)
+{
+    psFree(info->nonUniqueKeyArray);
+    psFree(info->typeArray);
+    psFree(info->templateArray);
+    psFree(info->name);
+    psFree(info->metadata);
+}
+
+// Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+// must be null terminated.
 psBool ignoreLine(char *inString)
 {
@@ -121,7 +119,7 @@
 
 
-/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
- *  terminated copy of the original input string. */
-char *cleanString(char *inString, psS32 sLen)
+//  Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+//  terminated copy of the original input string.
+char *cleanString(char *inString, psS32 sLen, psBool ignoreComment)
 {
     char *ptrB = NULL;
@@ -129,16 +127,28 @@
     char *cleaned = NULL;
 
-
+    // Initialize begining of string pointer
     ptrB = inString;
 
-    /* Skip over leading # or whitespace */
-    while (isspace(*ptrB) || *ptrB=='#') {
-        ptrB++;
-    }
-
-    /* Skip over trailing whitespace, null terminators, and # characters */
+    // Skip over leading # or whitespace
+    if(ignoreComment) {
+        while (isspace(*ptrB) || *ptrB=='#') {
+            ptrB++;
+        }
+    } else {
+        while (isspace(*ptrB)) {
+            ptrB++;
+        }
+    }
+
+    // Skip over trailing whitespace, null terminators, and # characters
     ptrE = inString + sLen;
-    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
-        ptrE--;
+    if(ignoreComment) {
+        while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+            ptrE--;
+        }
+    } else {
+        while(isspace(*ptrE) || *ptrE=='\0') {
+            ptrE--;
+        }
     }
 
@@ -152,9 +162,8 @@
 }
 
-/** Count repeat occurances of a single character within a line. The input string must be null terminated. */
+// Count repeat occurances of a single character within a line. The input string must be null terminated.
 psS32 repeatedChars(char *inString, char ch)
 {
     psS32 count = 0;
-
 
     while(*inString!='\0') {
@@ -168,11 +177,16 @@
 }
 
-/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
- * the beginning of the string. Tokens are newly allocated null terminated strings. */
-char* getToken(char **inString, char *delimiter, psS32 *status)
+// Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+// the beginning of the string. Tokens are newly allocated null terminated strings.
+char* getToken(char **inString, char *delimiter, psS32 *status, psBool ignoreComment)
 {
     char *cleanToken = NULL;
+    char *convertChar = NULL;
     psS32 sLen = 0;
 
+    // Convert tab characters to white space
+    while((convertChar=strchr(*inString,'\t')) != NULL ) {
+        *convertChar = ' ';
+    }
 
     // Skip over leading whitespace
@@ -186,5 +200,5 @@
 
         // Create new, cleaned, and null terminated token
-        cleanToken = cleanString(*inString, sLen);
+        cleanToken = cleanString(*inString, sLen,ignoreComment);
 
         // Move to end of token
@@ -197,11 +211,10 @@
 }
 
-/** Returns single parsed value as a double precision number. The input string must be cleaned and null
- * terminated. */
+// Returns single parsed value as a double precision number. The input string must be cleaned and null
+// terminated.
 double parseValue(char *inString, psS32 *status)
 {
     char *end = NULL;
     double value = 0.0;
-
 
     value = strtod(inString, &end);
@@ -235,11 +248,10 @@
 psVector* parseVector(char *inString, psElemType elemType, psS32 *status)
 {
-    char *end = NULL;
-    char *saveValue = NULL;
-    psS32 i = 0;
-    psS32 numValues = 0;
-    double value = 0.0;
-    psVector *vec = NULL;
-
+    char*      end       = NULL;
+    char*      saveValue = NULL;
+    psS32      i         = 0;
+    psS32      numValues = 0;
+    double     value     = 0.0;
+    psVector*  vec       = NULL;
 
     // Cycle through string and count entries
@@ -274,6 +286,24 @@
                 vec->data.U8[i++] = (psU8)value;
                 break;
+            case PS_TYPE_U16:
+                vec->data.U16[i++] = (psU16)value;
+                break;
+            case PS_TYPE_U32:
+                vec->data.U32[i++] = (psU32)value;
+                break;
+            case PS_TYPE_U64:
+                vec->data.U64[i++] = (psU64)value;
+                break;
+            case PS_TYPE_S8:
+                vec->data.S8[i++] = (psS8)value;
+                break;
+            case PS_TYPE_S16:
+                vec->data.S16[i++] = (psS16)value;
+                break;
             case PS_TYPE_S32:
                 vec->data.S32[i++] = (psS32)value;
+                break;
+            case PS_TYPE_S64:
+                vec->data.S64[i++] = (psS64)value;
                 break;
             case PS_TYPE_F32:
@@ -303,4 +333,33 @@
 /* FUNCTION IMPLEMENTATION - PUBLIC                                          */
 /*****************************************************************************/
+
+p_psParseLevelInfo* p_psParseLevelInfoAlloc(void)
+{
+
+    p_psParseLevelInfo* info = NULL;
+
+    // Allocate memory for parse level info
+    info = (p_psParseLevelInfo*)psAlloc(sizeof(p_psParseLevelInfo));
+
+    // If allocation successful then initialize members
+    if(info != NULL) {
+
+        info->nonUniqueKeyArray = psArrayAlloc(10);
+        info->nonUniqueKeyArray->n = 0;
+
+        info->typeArray = psArrayAlloc(10);
+        info->typeArray->n = 0;
+
+        info->templateArray = psArrayAlloc(10);
+        info->templateArray->n = 0;
+
+        info->metadata = NULL;
+        info->name = NULL;
+
+        // Set memory deallocator
+        psMemSetDeallocator(info,(psFreeFcn)parseLevelInfoFree);
+    }
+    return info;
+}
 
 bool psMetadataItemPrint(FILE * fd, const char *format, const psMetadataItem* metadataItem)
@@ -500,26 +559,537 @@
 }
 
+static psMetadata* getMetadataType(char* linePtr)
+{
+    psMetadata*     metadataTemplate = NULL;
+    psS32           status           = 0;
+    char*           token            = NULL;
+    psMetadataItem* tempItem         = NULL;
+
+    // Loop through line and generate metadata items for each token found
+    while((token = getToken(&linePtr," ",&status,false)) != NULL ) {
+
+        // If not allocated then allocate new metadata
+        if(metadataTemplate == NULL) {
+            metadataTemplate = psMetadataAlloc();
+        }
+
+        // Look for comment indicator #
+        if(strstr(token,"#") != 0) {
+            psFree(token);
+            break;
+        }
+
+        // Create metadata item to represent token read
+        tempItem = psMetadataItemAllocStr(token,"","");
+        if(tempItem == NULL) {
+            psFree(metadataTemplate);
+            psFree(token);
+            metadataTemplate = NULL;
+            break;
+        }
+
+        // Add item to template
+        if(!psMetadataAddItem(metadataTemplate, tempItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+            psFree(metadataTemplate);
+            psFree(tempItem);
+            psFree(token);
+            metadataTemplate = NULL;
+            break;
+        }
+        psFree(tempItem);
+        psFree(token);
+    }
+
+    return metadataTemplate;
+}
+
+static psMetadata* setMetadataItem(psMetadata* template, char* linePtr)
+{
+    psMetadata*      md           = NULL;
+    psS32            items        = 0;
+    char*            token        = NULL;
+    psS32            status       = 0;
+    psMetadataItem*  mdItem       = NULL;
+    psMetadataItem*  templateItem = NULL;
+    psListIterator*  iter         = NULL;
+
+    // Determine the number of items in template
+    items = template->list->size;
+    if(items > 0 ) {
+
+        // Allocate metadata
+        md = psMetadataAlloc()
+             ;
+
+        // Point to first item in template
+        iter = psListIteratorAlloc(template->
+                                   list,PS_LIST_HEAD,true);
+
+        // For each item in template parse line string for values
+        for(psS32 i = 0;
+                i < items;
+                i++) {
+
+            // Get template item
+            templateItem = psListGetAndIncrement(iter)
+                           ;
+            if(templateItem == NULL) {
+                psFree(md);
+                md = NULL;
+                break;
+            }
+
+            // Get the next token on the line
+            token = getToken(&linePtr," ",&status,false);
+            if(token != NULL) {
+                // Allocate metadata item
+                mdItem = psMetadataItemAllocStr(templateItem->name,templateItem->comment,token);
+                if(mdItem == NULL) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(token);
+                    break;
+                }
+                // Add item to metadata
+                if(!psMetadataAddItem(md, mdItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(mdItem);
+                    psFree(token);
+                    break;
+                }
+                psFree(mdItem);
+            } else {
+                // Missing items
+                psFree(md);
+                md = NULL;
+                break;
+            }
+
+            psFree(token);
+        }
+        psFree(iter);
+    }
+    return md;
+}
+
+psBool parseMetadataItem(char* keyName, psS32* level,     psArray* levelArray,
+                         char* linePtr, psS32  lineCount, char*    fileName,   psMetadataFlags flags)
+{
+    psBool               returnValue   = true;
+    psBool               addStatus     = false;
+    psMetadataType       mdType        = PS_META_UNKNOWN;
+    psElemType           vectorType    = PS_TYPE_S8;
+    char*                strType       = NULL;
+    char*                strValue      = NULL;
+    char*                strComment    = NULL;
+    psS32                status        = 0;
+    psMetadata*          md            = NULL;
+    psF64                tempDbl       = 0.0;
+    psBool               tempBool      = false;
+    psS32                tempInt       = 0;
+    psVector*            tempVec       = NULL;
+    char*                tempStr       = NULL;
+    psArray*             nonUniqueKeys = NULL;
+    psBool               typeFound     = false;
+    psArray*             typeArray     = NULL;
+    psArray*             templateArray = NULL;
+    psMetadata*          tempMeta      = NULL;
+    p_psParseLevelInfo*  nextLevelInfo = NULL;
+
+    // Get the metadata item type
+    strType = getToken(&linePtr, " ", &status,true);
+
+    // Check for no type
+    if(strType==NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
+                fileName);
+        returnValue = false;
+    } else {
+
+        // Set metadata type based on type token
+
+        // Check if the keyName specifies a vector and if so use strType token to find vector type
+        if(*keyName == '@') {
+            mdType = PS_META_VEC;
+            // Get the type of vector
+            if(!strncmp(strType, "U8", 2)) {
+                vectorType = PS_TYPE_U8;
+            } else if (!strncmp(strType,"U16",3)) {
+                vectorType = PS_TYPE_U16;
+            } else if (!strncmp(strType,"U32",3)) {
+                vectorType = PS_TYPE_U32;
+            } else if (!strncmp(strType,"U64",3)) {
+                vectorType = PS_TYPE_U64;
+            } else if (!strncmp(strType,"S8",2)) {
+                vectorType = PS_TYPE_S8;
+            } else if (!strncmp(strType,"S16",3)) {
+                vectorType = PS_TYPE_S16;
+            } else if (!strncmp(strType,"S32",3)) {
+                vectorType = PS_TYPE_S32;
+            } else if (!strncmp(strType,"S64",3)) {
+                vectorType = PS_TYPE_S64;
+            } else if (!strncmp(strType,"F32",3)) {
+                vectorType = PS_TYPE_F32;
+            } else if (!strncmp(strType,"F64",3)) {
+                vectorType = PS_TYPE_F64;
+            } else {
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, "", keyName,
+                        strType, lineCount, fileName);
+                psFree(strType);
+                return false;
+            }
+        } else if(!strncmp(strType, "STR", 3)) {
+            mdType = PS_META_STR;
+        } else if(!strncmp(strType, "BOOL", 4)) {
+            mdType = PS_META_BOOL;
+        } else if(!strncmp(strType, "S32", 3)) {
+            mdType = PS_META_S32;
+        } else if(!strncmp(strType, "F32", 3)) {
+            mdType = PS_META_F32;
+        } else if(!strncmp(strType, "F64", 3)) {
+            mdType = PS_META_F64;
+        } else if(!strncmp(strType, "MULTI", 5)) {
+            mdType = PS_META_MULTI;
+        } else if(!strncmp(strType, "METADATA", 8)) {
+            mdType = PS_META_META;
+        } else {
+            // Search through user types
+            typeArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->typeArray;
+            templateArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->templateArray;
+            for(psS32 k = 0; k < typeArray->n; k++) {
+                if(strcmp(strType,(char*)typeArray->data[k]) == 0) {
+                    tempMeta = setMetadataItem((psMetadata*)templateArray->data[k],linePtr);
+                    if(tempMeta != NULL) {
+                        // Add metadata item
+                        md = ((p_psParseLevelInfo*)(levelArray->data[*level]))->metadata;
+                        addStatus = psMetadataAdd(md,PS_LIST_TAIL,keyName,PS_META_META | flags,"",tempMeta);
+                        // Check for add failure
+                        if (! addStatus) {
+                            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM,
+                                    keyName, lineCount, fileName);
+                            psFree(strType);
+                            psFree(tempMeta);
+                            return false;
+                        }
+                        psFree(tempMeta);
+                    } else {
+                        // Metadata type read error
+                        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL,
+                                keyName,lineCount,fileName);
+                        psFree(strType);
+                        return false;
+                    }
+                    typeFound = true;
+                    break;
+                }
+            }
+            if(!typeFound) {
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                        strType, lineCount,fileName);
+                psFree(strType);
+                return false;
+            } else {
+                psFree(strType);
+                return true;
+            }
+        }
+    }
+
+    // If type is not MULTI or META then get the value and comment
+    if((mdType != PS_META_MULTI) && (mdType != PS_META_META)) {
+        // Get the metadata item value if there is one.
+        status = 0;
+        strValue = getToken(&linePtr, "#", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+        if(strValue==NULL) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+        // Not all lines will have comments, so NULL is ok.
+        status = 0;
+        strComment = getToken(&linePtr,"~", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            psFree(strComment);
+        }
+    }
+
+    // Need to add item to metadata so get pointer to metadata
+    status = 0;
+    md = ((p_psParseLevelInfo*)(levelArray->data[*level]))->metadata;
+    nonUniqueKeys = ((p_psParseLevelInfo*)(levelArray->data[*level]))->nonUniqueKeyArray;
+    switch(mdType) {
+    case PS_META_STR:
+        addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                  mdType | flags,
+                                  strComment, strValue);
+        break;
+    case PS_META_BOOL:
+        tempBool = parseBool(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempBool);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_F32:
+    case PS_META_F64:
+        tempDbl = parseValue(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempDbl);
+        } else {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName, strType, lineCount,
+                    fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_S32:
+        tempInt = (psS32)parseValue(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempInt);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_VEC:
+        tempVec = parseVector(strValue, vectorType, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName+1,
+                                      mdType | flags,
+                                      strComment, tempVec);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        psFree(tempVec);
+        break;
+    case PS_META_MULTI:
+        // Add key to non-unique array of keys
+        // Check for duplicate MULTI lines
+        addStatus = true;
+        for(psS32 k=0; k < nonUniqueKeys->n; k++) {
+            if(strcmp(keyName,(char*)nonUniqueKeys->data[k]) == 0) {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_DUPLICATE_MULTI,
+                        lineCount,fileName);
+                psFree(strType);
+                return false;
+            }
+        }
+        tempStr = psStringCopy(keyName);
+        nonUniqueKeys = psArrayAdd(nonUniqueKeys,0,tempStr);
+        addStatus = true;
+        psFree(tempStr);
+        break;
+    case PS_META_META:
+        // Create next level info
+        nextLevelInfo = p_psParseLevelInfoAlloc();
+        // Create new metadata
+        nextLevelInfo->metadata = psMetadataAlloc();
+        // Save name of metadata
+        nextLevelInfo->name = psStringCopy(keyName);
+        // Add next level to levelArray
+        levelArray = psArrayAdd(levelArray,1,nextLevelInfo);
+        psFree(nextLevelInfo);
+        // Increment level counter
+        (*level)++;
+        addStatus = true;
+        break;
+    default:
+        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                lineCount,fileName);
+        break;
+    }
+
+    // Check if the add status was successful
+    if (! addStatus) {
+        //        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, keyName, lineCount,
+        //                fileName);
+        returnValue = false;
+    }
+
+    psFree(strComment);
+    psFree(strValue);
+    psFree(strType);
+
+    return returnValue;
+}
+
+psBool parseLine(psS32* level,     psArray* levelArray, char*  linePtr,
+                 psS32  lineCount, char*    fileName,   psBool overwrite)
+{
+    psBool              returnValue    = true;
+    psMetadataFlags     flags          = PS_META_DEFAULT;
+    char*               keyName        = NULL;
+    psS32               status         = 0;
+    psS32               limit          = 0;
+    psMetadata*         tempTemplate   = NULL;
+    char*               strType        = NULL;
+    psArray*            typeArray      = NULL;
+    psArray*            templateArray  = NULL;
+    p_psParseLevelInfo* upperLevelInfo = NULL;
+    p_psParseLevelInfo* lowerLevelInfo = NULL;
+    psBool              addStatus      = 0;
+
+    // Set flags if overwrite specified
+    if(overwrite) {
+        flags = PS_META_REPLACE;
+    }
+
+    // If line is not a comment or blank, then extract data
+    if(!ignoreLine(linePtr)) {
+
+        // Check for more than one '@' in a line
+        if(repeatedChars(linePtr, '@') > 1) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
+            return false;
+        }
+
+        // Get metadata item name
+        keyName = getToken(&linePtr, " ", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "keyName", lineCount, fileName);
+            psFree(keyName);
+            return false;
+        }
+
+        // Check for special keyName values "TYPE", "END"
+        if(strcmp(keyName,"TYPE") == 0 ) {
+            // Get the type name
+            strType = getToken(&linePtr," ",&status,true);
+            if(strType == NULL) {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL,"type",lineCount,
+                        fileName);
+                psFree(keyName);
+                return false;
+            }
+            tempTemplate = getMetadataType(linePtr);
+            // Check if type was parsed succesfully
+            if(tempTemplate != NULL) {
+                // Access type array
+                typeArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->typeArray;
+                // Check if type already exists in array
+                for(psS32 k=0; k < typeArray->n; k++) {
+                    // Compare type name with the list of current types
+                    if(strcmp(strType,(char*)typeArray->data[k]) == 0) {
+                        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_TYPE_DUPLICATE,
+                                strType,lineCount,fileName);
+                        psFree(tempTemplate);
+                        psFree(keyName);
+                        psFree(strType);
+                        return false;
+                    }
+                }
+                // Add key name to array of type
+                typeArray = psArrayAdd(typeArray,1,strType);
+                // Add template to array of templates
+                templateArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->templateArray;
+                templateArray = psArrayAdd(templateArray,1,tempTemplate);
+                psFree(tempTemplate);
+            } else {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                        strType,lineCount,fileName);
+                psFree(keyName);
+                psFree(strType);
+                return false;
+            }
+            psFree(strType);
+        } else if (strcmp(keyName,"END") == 0 ) {
+            if(*level > 0) {
+                upperLevelInfo = ((p_psParseLevelInfo*)(levelArray->data[*level-1]));
+                lowerLevelInfo = ((p_psParseLevelInfo*)(levelArray->data[*level]));
+                // Check name in nonunique list
+                for(psS32 k=0; k < upperLevelInfo->nonUniqueKeyArray->n; k++) {
+                    if(strcmp(upperLevelInfo->nonUniqueKeyArray->data[k],lowerLevelInfo->name) == 0) {
+                        flags = PS_META_DUPLICATE_OK;
+                        break;
+                    }
+                }
+                // Add metadata to upper level metadata
+                addStatus = psMetadataAdd(upperLevelInfo->metadata,
+                                          PS_LIST_TAIL,lowerLevelInfo->name,
+                                          PS_META_META | flags,
+                                          "",
+                                          lowerLevelInfo->metadata);
+                if(!addStatus) {
+                    psFree(keyName);
+                    return false;
+                } else {
+                    // Remove lower info level
+                    if(!psArrayRemove(levelArray,levelArray->data[*level])) {
+                        psFree(keyName);
+                        return false;
+                    }
+                    psFree(lowerLevelInfo);
+                    (*level)--;
+                }
+            } else {
+                psFree(keyName);
+                return false;
+            }
+        } else {
+            // Check if key name present in array of non-unique key names
+            limit = ((p_psParseLevelInfo*)(levelArray->data[*level]))->nonUniqueKeyArray->n;
+            for(psS32 k=0; k < limit; k++) {
+                char* name = (char*)((p_psParseLevelInfo*)
+                                     (levelArray->data[*level]))->nonUniqueKeyArray->data[k];
+                if(strcmp(name,keyName) == 0) {
+                    flags = PS_META_DUPLICATE_OK;
+                }
+            }
+            // Parse metadataItem
+            if(!parseMetadataItem(keyName,level, levelArray, linePtr, lineCount, fileName, flags)) {
+                psFree(keyName);
+                return false;
+            }
+        }
+        psFree(keyName);
+    }
+    return returnValue;
+}
+
 psMetadata* psMetadataParseConfig(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
 {
-    psBool tempBool;
-    char *line = NULL;
-    char *strName = NULL;
-    char *strType = NULL;
-    char *strValue = NULL;
-    char *strComment = NULL;
-    char *linePtr = NULL;
-    psElemType vecType = 0;
-    psS32 status = 0;
-    psU32 lineCount = 0;
-    psF64 tempDbl = 0.0;
-    psS32 tempInt = 0.0;
-    psVector *tempVec = NULL;
-    FILE *fp = NULL;
-    psMetadataType mdType;
-    psMetadataFlags flags;
-    psBool addStatus;
-
-    // Check for nulls
+    FILE*               fp                   = NULL;
+    char*               line                 = NULL;
+    char*               linePtr              = NULL;
+    psArray*            parseLevelInfoArray  = NULL;
+    psS32               lineCount            = 0;
+    psS32               nestingLevel         = 0;
+    p_psParseLevelInfo* topLevelInfo         = NULL;
+
+    // Check for NULL file name
     PS_PTR_CHECK_NULL(fileName,NULL);
+
+    // Check for NULL nFail
+    PS_PTR_CHECK_NULL(nFail,NULL);
+
+    // Attempt to open specified file
     if((fp=fopen(fileName, "r")) == NULL) {
         psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
@@ -532,4 +1102,14 @@
     }
 
+    // Allocate array to store parse level information
+    parseLevelInfoArray = psArrayAlloc(10);
+    parseLevelInfoArray->n = 0;
+
+    // Set parse level info for the top level
+    topLevelInfo = p_psParseLevelInfoAlloc();
+    topLevelInfo->metadata = psMemIncrRefCounter(md);
+    parseLevelInfoArray = psArrayAdd(parseLevelInfoArray,1,topLevelInfo);
+    psFree(topLevelInfo);
+
     // Create reusable line for continuous read
     line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
@@ -541,189 +1121,22 @@
         linePtr = line;
         lineCount++;
-        CLEAR_TEMPS();
-
-        // If line is not a comment or blank, then extract data
-        if(!ignoreLine(linePtr)) {
-
-            // Check for more than one '*' or '@' in a line
-            if(repeatedChars(linePtr, '@') > 1) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
-                continue;
-            } else if(repeatedChars(linePtr, '*') > 1) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '*', lineCount, fileName);
-                continue;
-            } else if(repeatedChars(linePtr, '~') > 0) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '~', lineCount, fileName);
-                continue;
-            }
-
-            // Get metadata item name
-            strName = getToken(&linePtr, " ", &status);
-            if(strName==NULL || status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "name", lineCount, fileName);
-                continue;
-            }
-
-            flags = (overwrite) ? PS_META_REPLACE : PS_META_DEFAULT;
-
-            // Get the metadata item type
-            strType = getToken(&linePtr, " ", &status);
-            if(strType==NULL) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
-                        fileName);
-                continue;
-            } else {
-                char* tempStrType = strType;
-                if(*strType == '*') {
-                    flags = PS_META_DUPLICATE_OK;
-                    tempStrType = strType+1;
-                }
-
-                if(!strncmp(tempStrType, "STR", 3)) {
-                    mdType = PS_META_STR;
-                } else if(!strncmp(tempStrType, "BOOL", 4)) {
-                    mdType = PS_META_BOOL;
-                } else if(!strncmp(tempStrType, "S32", 3)) {
-                    mdType = PS_META_S32;
-                } else if(!strncmp(tempStrType, "F32", 3)) {
-                    mdType = PS_META_F32;
-                } else if(!strncmp(tempStrType, "F64", 3)) {
-                    mdType = PS_META_F64;
-                } else {
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineCount,
-                            fileName);
-                    continue;
-                }
-            }
-
-            if(*strName == '@') {
-                vecType = PS_META_PRIMITIVE_TYPE(mdType);
-                mdType = PS_META_VEC;
-            }
-
-            // Get the metadata item value if there is one.
-            strValue = getToken(&linePtr, "#", &status);
-            if(status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
-                        fileName);
-                continue;
-            }
-            if(strValue==NULL) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
-                        fileName);
-                continue;
-            }
-
-            // Not all lines will have comments, so NULL is ok.
-            strComment = getToken(&linePtr,"~", &status);
-            if(status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
-                        fileName);
-                continue;
-            }
-
-            // Create and add metadata item to metadata and parse values
-            switch (mdType) {
-            case PS_META_STR:
-                addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                          mdType | flags,
-                                          strComment, strValue);
-                break;
-            case PS_META_VEC:
-                tempVec = parseVector(strValue, vecType, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName+1,
-                                              mdType | flags,
-                                              strComment, tempVec);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                psFree(tempVec);
-                break;
-            case PS_META_BOOL:
-                tempBool = parseBool(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempBool);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                break;
-            case PS_META_S32:
-                tempInt = (psS32)parseValue(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempInt);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                break;
-            case PS_META_F32:
-            case PS_META_F64:
-                tempDbl = parseValue(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempDbl);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true,
-                            PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType, lineCount,
-                            fileName);
-                    continue;
-                }
-                break;
-            default:
-                (*nFail)++;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, mdType, lineCount,
-                        fileName);
-                continue;
-            } // switch
-            if (! addStatus) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineCount,
-                        fileName);
-            }
-
-        } // if ignoreLine
-    } // while loop
-
+
+        if(!parseLine(&nestingLevel,parseLevelInfoArray,linePtr,lineCount,(char*)fileName,overwrite)) {
+            (*nFail)++;
+        }
+    }
+
+    // Free parse array and line buffer
+    psFree(parseLevelInfoArray);
     psFree(line);
 
+    // Check if any failed lined detected
+    if( (*nFail) != 0) {
+        psFree(md);
+        md = NULL;
+    }
     return md;
 }
+
 
 static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts)
@@ -750,5 +1163,5 @@
 
     // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-    psTagName = psStringCopy((const char*)tagName);
+    psTagName = psStringCopy(tagName);
 
     // Metadata containter for housing element attributes used by other SAX events
@@ -774,6 +1187,6 @@
 
                 // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-                psAttName = psStringCopy((const char*)attName);
-                psAttValue = psStringCopy((const char*)attValue);
+                psAttName = psStringCopy(attName);
+                psAttValue = psStringCopy(attValue);
                 psHashAdd(htAtts, psAttName, psAttValue);
                 psFree(psAttName);
@@ -1082,5 +1495,5 @@
 
     // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-    psEndTagName = psStringCopy((const char*)tagName);
+    psEndTagName = psStringCopy(tagName);
 
     // Compare start and end tag names
Index: /trunk/psLib/src/types/psMetadataConfig.h
===================================================================
--- /trunk/psLib/src/types/psMetadataConfig.h	(revision 3944)
+++ /trunk/psLib/src/types/psMetadataConfig.h	(revision 3945)
@@ -10,6 +10,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-03-07 20:58:50 $
+ *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-16 19:43:53 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -21,4 +21,15 @@
 /// @{
 
+typedef struct
+{
+    psArray*    nonUniqueKeyArray;
+    psArray*    typeArray;
+    psArray*    templateArray;
+    psMetadata* metadata;
+    char*       name;
+}
+p_psParseLevelInfo;
+
+p_psParseLevelInfo* p_psParseLevelInfoAlloc(void);
 
 /** Print metadata item to file.
Index: /trunk/psLib/src/xml/psXML.c
===================================================================
--- /trunk/psLib/src/xml/psXML.c	(revision 3944)
+++ /trunk/psLib/src/xml/psXML.c	(revision 3945)
@@ -8,7 +8,8 @@
 *
 *  @author Ross Harman, MHPCC
+*  @author Eric Van Alst, MHPCC
 *
-*  @version $Revision: 1.25 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-04-26 19:53:30 $
+*  @version $Revision: 1.26 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-05-16 19:43:53 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -33,5 +34,4 @@
 #include "psConstants.h"
 #include "psAstronomyErrors.h"
-
 
 /******************************************************************************/
@@ -53,23 +53,4 @@
 return NULL;
 
-/** Free and null temporary variables used by config file parser */
-#define CLEAR_TEMPS()                                                                                        \
-if(strName) {                                                                                                \
-    psFree(strName);                                                                                         \
-    strName = NULL;                                                                                          \
-}                                                                                                            \
-if(strType) {                                                                                                \
-    psFree(strType);                                                                                         \
-    strType = NULL;                                                                                          \
-}                                                                                                            \
-if(strValue) {                                                                                               \
-    psFree(strValue);                                                                                        \
-    strValue = NULL;                                                                                         \
-}                                                                                                            \
-if(strComment) {                                                                                             \
-    psFree(strComment);                                                                                      \
-    strComment = NULL;                                                                                       \
-}
-
 /** Maximum size of a FITS line */
 #define FITS_LINE_SIZE 80
@@ -105,7 +86,24 @@
 static void initMetadataItemXml(void *ctx, char *tagName);
 static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts);
-
-/** Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
- *  must be null terminated. */
+static psMetadata* getMetadataType(char *linePtr);
+static psMetadata* setMetadataItem(psMetadata* template, char* linePtr)
+;
+static void parseLevelInfoFree(p_psParseLevelInfo* info);
+static psBool parseLine(psS32* level,   psArray* levelArray,
+                        char*  linePtr, psS32 lineCount,     char* fileName, psBool overwrite);
+static psBool parseMetadataItem(char* keyName, psS32* level,     psArray* levelArray,
+                                char* linePtr, psS32  lineCount, char*    fileName,    psMetadataFlags flags);
+
+static void parseLevelInfoFree(p_psParseLevelInfo* info)
+{
+    psFree(info->nonUniqueKeyArray);
+    psFree(info->typeArray);
+    psFree(info->templateArray);
+    psFree(info->name);
+    psFree(info->metadata);
+}
+
+// Determines if a line is blank (whitespace only) or a commentline. It returns true if so. The input string
+// must be null terminated.
 psBool ignoreLine(char *inString)
 {
@@ -121,7 +119,7 @@
 
 
-/** Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
- *  terminated copy of the original input string. */
-char *cleanString(char *inString, psS32 sLen)
+//  Removes leading and trailing whitespace and # characters from a string. The cleaned string is a new null
+//  terminated copy of the original input string.
+char *cleanString(char *inString, psS32 sLen, psBool ignoreComment)
 {
     char *ptrB = NULL;
@@ -129,16 +127,28 @@
     char *cleaned = NULL;
 
-
+    // Initialize begining of string pointer
     ptrB = inString;
 
-    /* Skip over leading # or whitespace */
-    while (isspace(*ptrB) || *ptrB=='#') {
-        ptrB++;
-    }
-
-    /* Skip over trailing whitespace, null terminators, and # characters */
+    // Skip over leading # or whitespace
+    if(ignoreComment) {
+        while (isspace(*ptrB) || *ptrB=='#') {
+            ptrB++;
+        }
+    } else {
+        while (isspace(*ptrB)) {
+            ptrB++;
+        }
+    }
+
+    // Skip over trailing whitespace, null terminators, and # characters
     ptrE = inString + sLen;
-    while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
-        ptrE--;
+    if(ignoreComment) {
+        while(isspace(*ptrE) || *ptrE=='\0' || *ptrE=='#') {
+            ptrE--;
+        }
+    } else {
+        while(isspace(*ptrE) || *ptrE=='\0') {
+            ptrE--;
+        }
     }
 
@@ -152,9 +162,8 @@
 }
 
-/** Count repeat occurances of a single character within a line. The input string must be null terminated. */
+// Count repeat occurances of a single character within a line. The input string must be null terminated.
 psS32 repeatedChars(char *inString, char ch)
 {
     psS32 count = 0;
-
 
     while(*inString!='\0') {
@@ -168,11 +177,16 @@
 }
 
-/** Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
- * the beginning of the string. Tokens are newly allocated null terminated strings. */
-char* getToken(char **inString, char *delimiter, psS32 *status)
+// Returns cleaned token based on delimiter, but not including delimiter. Also changes the pointer location
+// the beginning of the string. Tokens are newly allocated null terminated strings.
+char* getToken(char **inString, char *delimiter, psS32 *status, psBool ignoreComment)
 {
     char *cleanToken = NULL;
+    char *convertChar = NULL;
     psS32 sLen = 0;
 
+    // Convert tab characters to white space
+    while((convertChar=strchr(*inString,'\t')) != NULL ) {
+        *convertChar = ' ';
+    }
 
     // Skip over leading whitespace
@@ -186,5 +200,5 @@
 
         // Create new, cleaned, and null terminated token
-        cleanToken = cleanString(*inString, sLen);
+        cleanToken = cleanString(*inString, sLen,ignoreComment);
 
         // Move to end of token
@@ -197,11 +211,10 @@
 }
 
-/** Returns single parsed value as a double precision number. The input string must be cleaned and null
- * terminated. */
+// Returns single parsed value as a double precision number. The input string must be cleaned and null
+// terminated.
 double parseValue(char *inString, psS32 *status)
 {
     char *end = NULL;
     double value = 0.0;
-
 
     value = strtod(inString, &end);
@@ -235,11 +248,10 @@
 psVector* parseVector(char *inString, psElemType elemType, psS32 *status)
 {
-    char *end = NULL;
-    char *saveValue = NULL;
-    psS32 i = 0;
-    psS32 numValues = 0;
-    double value = 0.0;
-    psVector *vec = NULL;
-
+    char*      end       = NULL;
+    char*      saveValue = NULL;
+    psS32      i         = 0;
+    psS32      numValues = 0;
+    double     value     = 0.0;
+    psVector*  vec       = NULL;
 
     // Cycle through string and count entries
@@ -274,6 +286,24 @@
                 vec->data.U8[i++] = (psU8)value;
                 break;
+            case PS_TYPE_U16:
+                vec->data.U16[i++] = (psU16)value;
+                break;
+            case PS_TYPE_U32:
+                vec->data.U32[i++] = (psU32)value;
+                break;
+            case PS_TYPE_U64:
+                vec->data.U64[i++] = (psU64)value;
+                break;
+            case PS_TYPE_S8:
+                vec->data.S8[i++] = (psS8)value;
+                break;
+            case PS_TYPE_S16:
+                vec->data.S16[i++] = (psS16)value;
+                break;
             case PS_TYPE_S32:
                 vec->data.S32[i++] = (psS32)value;
+                break;
+            case PS_TYPE_S64:
+                vec->data.S64[i++] = (psS64)value;
                 break;
             case PS_TYPE_F32:
@@ -303,4 +333,33 @@
 /* FUNCTION IMPLEMENTATION - PUBLIC                                          */
 /*****************************************************************************/
+
+p_psParseLevelInfo* p_psParseLevelInfoAlloc(void)
+{
+
+    p_psParseLevelInfo* info = NULL;
+
+    // Allocate memory for parse level info
+    info = (p_psParseLevelInfo*)psAlloc(sizeof(p_psParseLevelInfo));
+
+    // If allocation successful then initialize members
+    if(info != NULL) {
+
+        info->nonUniqueKeyArray = psArrayAlloc(10);
+        info->nonUniqueKeyArray->n = 0;
+
+        info->typeArray = psArrayAlloc(10);
+        info->typeArray->n = 0;
+
+        info->templateArray = psArrayAlloc(10);
+        info->templateArray->n = 0;
+
+        info->metadata = NULL;
+        info->name = NULL;
+
+        // Set memory deallocator
+        psMemSetDeallocator(info,(psFreeFcn)parseLevelInfoFree);
+    }
+    return info;
+}
 
 bool psMetadataItemPrint(FILE * fd, const char *format, const psMetadataItem* metadataItem)
@@ -500,26 +559,537 @@
 }
 
+static psMetadata* getMetadataType(char* linePtr)
+{
+    psMetadata*     metadataTemplate = NULL;
+    psS32           status           = 0;
+    char*           token            = NULL;
+    psMetadataItem* tempItem         = NULL;
+
+    // Loop through line and generate metadata items for each token found
+    while((token = getToken(&linePtr," ",&status,false)) != NULL ) {
+
+        // If not allocated then allocate new metadata
+        if(metadataTemplate == NULL) {
+            metadataTemplate = psMetadataAlloc();
+        }
+
+        // Look for comment indicator #
+        if(strstr(token,"#") != 0) {
+            psFree(token);
+            break;
+        }
+
+        // Create metadata item to represent token read
+        tempItem = psMetadataItemAllocStr(token,"","");
+        if(tempItem == NULL) {
+            psFree(metadataTemplate);
+            psFree(token);
+            metadataTemplate = NULL;
+            break;
+        }
+
+        // Add item to template
+        if(!psMetadataAddItem(metadataTemplate, tempItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+            psFree(metadataTemplate);
+            psFree(tempItem);
+            psFree(token);
+            metadataTemplate = NULL;
+            break;
+        }
+        psFree(tempItem);
+        psFree(token);
+    }
+
+    return metadataTemplate;
+}
+
+static psMetadata* setMetadataItem(psMetadata* template, char* linePtr)
+{
+    psMetadata*      md           = NULL;
+    psS32            items        = 0;
+    char*            token        = NULL;
+    psS32            status       = 0;
+    psMetadataItem*  mdItem       = NULL;
+    psMetadataItem*  templateItem = NULL;
+    psListIterator*  iter         = NULL;
+
+    // Determine the number of items in template
+    items = template->list->size;
+    if(items > 0 ) {
+
+        // Allocate metadata
+        md = psMetadataAlloc()
+             ;
+
+        // Point to first item in template
+        iter = psListIteratorAlloc(template->
+                                   list,PS_LIST_HEAD,true);
+
+        // For each item in template parse line string for values
+        for(psS32 i = 0;
+                i < items;
+                i++) {
+
+            // Get template item
+            templateItem = psListGetAndIncrement(iter)
+                           ;
+            if(templateItem == NULL) {
+                psFree(md);
+                md = NULL;
+                break;
+            }
+
+            // Get the next token on the line
+            token = getToken(&linePtr," ",&status,false);
+            if(token != NULL) {
+                // Allocate metadata item
+                mdItem = psMetadataItemAllocStr(templateItem->name,templateItem->comment,token);
+                if(mdItem == NULL) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(token);
+                    break;
+                }
+                // Add item to metadata
+                if(!psMetadataAddItem(md, mdItem, PS_LIST_TAIL, PS_META_DEFAULT)) {
+                    psFree(md);
+                    md = NULL;
+                    psFree(mdItem);
+                    psFree(token);
+                    break;
+                }
+                psFree(mdItem);
+            } else {
+                // Missing items
+                psFree(md);
+                md = NULL;
+                break;
+            }
+
+            psFree(token);
+        }
+        psFree(iter);
+    }
+    return md;
+}
+
+psBool parseMetadataItem(char* keyName, psS32* level,     psArray* levelArray,
+                         char* linePtr, psS32  lineCount, char*    fileName,   psMetadataFlags flags)
+{
+    psBool               returnValue   = true;
+    psBool               addStatus     = false;
+    psMetadataType       mdType        = PS_META_UNKNOWN;
+    psElemType           vectorType    = PS_TYPE_S8;
+    char*                strType       = NULL;
+    char*                strValue      = NULL;
+    char*                strComment    = NULL;
+    psS32                status        = 0;
+    psMetadata*          md            = NULL;
+    psF64                tempDbl       = 0.0;
+    psBool               tempBool      = false;
+    psS32                tempInt       = 0;
+    psVector*            tempVec       = NULL;
+    char*                tempStr       = NULL;
+    psArray*             nonUniqueKeys = NULL;
+    psBool               typeFound     = false;
+    psArray*             typeArray     = NULL;
+    psArray*             templateArray = NULL;
+    psMetadata*          tempMeta      = NULL;
+    p_psParseLevelInfo*  nextLevelInfo = NULL;
+
+    // Get the metadata item type
+    strType = getToken(&linePtr, " ", &status,true);
+
+    // Check for no type
+    if(strType==NULL) {
+        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
+                fileName);
+        returnValue = false;
+    } else {
+
+        // Set metadata type based on type token
+
+        // Check if the keyName specifies a vector and if so use strType token to find vector type
+        if(*keyName == '@') {
+            mdType = PS_META_VEC;
+            // Get the type of vector
+            if(!strncmp(strType, "U8", 2)) {
+                vectorType = PS_TYPE_U8;
+            } else if (!strncmp(strType,"U16",3)) {
+                vectorType = PS_TYPE_U16;
+            } else if (!strncmp(strType,"U32",3)) {
+                vectorType = PS_TYPE_U32;
+            } else if (!strncmp(strType,"U64",3)) {
+                vectorType = PS_TYPE_U64;
+            } else if (!strncmp(strType,"S8",2)) {
+                vectorType = PS_TYPE_S8;
+            } else if (!strncmp(strType,"S16",3)) {
+                vectorType = PS_TYPE_S16;
+            } else if (!strncmp(strType,"S32",3)) {
+                vectorType = PS_TYPE_S32;
+            } else if (!strncmp(strType,"S64",3)) {
+                vectorType = PS_TYPE_S64;
+            } else if (!strncmp(strType,"F32",3)) {
+                vectorType = PS_TYPE_F32;
+            } else if (!strncmp(strType,"F64",3)) {
+                vectorType = PS_TYPE_F64;
+            } else {
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, "", keyName,
+                        strType, lineCount, fileName);
+                psFree(strType);
+                return false;
+            }
+        } else if(!strncmp(strType, "STR", 3)) {
+            mdType = PS_META_STR;
+        } else if(!strncmp(strType, "BOOL", 4)) {
+            mdType = PS_META_BOOL;
+        } else if(!strncmp(strType, "S32", 3)) {
+            mdType = PS_META_S32;
+        } else if(!strncmp(strType, "F32", 3)) {
+            mdType = PS_META_F32;
+        } else if(!strncmp(strType, "F64", 3)) {
+            mdType = PS_META_F64;
+        } else if(!strncmp(strType, "MULTI", 5)) {
+            mdType = PS_META_MULTI;
+        } else if(!strncmp(strType, "METADATA", 8)) {
+            mdType = PS_META_META;
+        } else {
+            // Search through user types
+            typeArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->typeArray;
+            templateArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->templateArray;
+            for(psS32 k = 0; k < typeArray->n; k++) {
+                if(strcmp(strType,(char*)typeArray->data[k]) == 0) {
+                    tempMeta = setMetadataItem((psMetadata*)templateArray->data[k],linePtr);
+                    if(tempMeta != NULL) {
+                        // Add metadata item
+                        md = ((p_psParseLevelInfo*)(levelArray->data[*level]))->metadata;
+                        addStatus = psMetadataAdd(md,PS_LIST_TAIL,keyName,PS_META_META | flags,"",tempMeta);
+                        // Check for add failure
+                        if (! addStatus) {
+                            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM,
+                                    keyName, lineCount, fileName);
+                            psFree(strType);
+                            psFree(tempMeta);
+                            return false;
+                        }
+                        psFree(tempMeta);
+                    } else {
+                        // Metadata type read error
+                        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL,
+                                keyName,lineCount,fileName);
+                        psFree(strType);
+                        return false;
+                    }
+                    typeFound = true;
+                    break;
+                }
+            }
+            if(!typeFound) {
+                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                        strType, lineCount,fileName);
+                psFree(strType);
+                return false;
+            } else {
+                psFree(strType);
+                return true;
+            }
+        }
+    }
+
+    // If type is not MULTI or META then get the value and comment
+    if((mdType != PS_META_MULTI) && (mdType != PS_META_META)) {
+        // Get the metadata item value if there is one.
+        status = 0;
+        strValue = getToken(&linePtr, "#", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+        if(strValue==NULL) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            return false;
+        }
+        // Not all lines will have comments, so NULL is ok.
+        status = 0;
+        strComment = getToken(&linePtr,"~", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
+                    fileName);
+            psFree(strType);
+            psFree(strValue);
+            psFree(strComment);
+        }
+    }
+
+    // Need to add item to metadata so get pointer to metadata
+    status = 0;
+    md = ((p_psParseLevelInfo*)(levelArray->data[*level]))->metadata;
+    nonUniqueKeys = ((p_psParseLevelInfo*)(levelArray->data[*level]))->nonUniqueKeyArray;
+    switch(mdType) {
+    case PS_META_STR:
+        addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                  mdType | flags,
+                                  strComment, strValue);
+        break;
+    case PS_META_BOOL:
+        tempBool = parseBool(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempBool);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_F32:
+    case PS_META_F64:
+        tempDbl = parseValue(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempDbl);
+        } else {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName, strType, lineCount,
+                    fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_S32:
+        tempInt = (psS32)parseValue(strValue, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName,
+                                      mdType | flags,
+                                      strComment, tempInt);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        break;
+    case PS_META_VEC:
+        tempVec = parseVector(strValue, vectorType, &status);
+        if(!status) {
+            addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName+1,
+                                      mdType | flags,
+                                      strComment, tempVec);
+        } else {
+            psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, keyName,
+                    strType, lineCount, fileName);
+            returnValue = false;
+        }
+        psFree(tempVec);
+        break;
+    case PS_META_MULTI:
+        // Add key to non-unique array of keys
+        // Check for duplicate MULTI lines
+        addStatus = true;
+        for(psS32 k=0; k < nonUniqueKeys->n; k++) {
+            if(strcmp(keyName,(char*)nonUniqueKeys->data[k]) == 0) {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_DUPLICATE_MULTI,
+                        lineCount,fileName);
+                psFree(strType);
+                return false;
+            }
+        }
+        tempStr = psStringCopy(keyName);
+        nonUniqueKeys = psArrayAdd(nonUniqueKeys,0,tempStr);
+        addStatus = true;
+        psFree(tempStr);
+        break;
+    case PS_META_META:
+        // Create next level info
+        nextLevelInfo = p_psParseLevelInfoAlloc();
+        // Create new metadata
+        nextLevelInfo->metadata = psMetadataAlloc();
+        // Save name of metadata
+        nextLevelInfo->name = psStringCopy(keyName);
+        // Add next level to levelArray
+        levelArray = psArrayAdd(levelArray,1,nextLevelInfo);
+        psFree(nextLevelInfo);
+        // Increment level counter
+        (*level)++;
+        addStatus = true;
+        break;
+    default:
+        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                lineCount,fileName);
+        break;
+    }
+
+    // Check if the add status was successful
+    if (! addStatus) {
+        //        psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, keyName, lineCount,
+        //                fileName);
+        returnValue = false;
+    }
+
+    psFree(strComment);
+    psFree(strValue);
+    psFree(strType);
+
+    return returnValue;
+}
+
+psBool parseLine(psS32* level,     psArray* levelArray, char*  linePtr,
+                 psS32  lineCount, char*    fileName,   psBool overwrite)
+{
+    psBool              returnValue    = true;
+    psMetadataFlags     flags          = PS_META_DEFAULT;
+    char*               keyName        = NULL;
+    psS32               status         = 0;
+    psS32               limit          = 0;
+    psMetadata*         tempTemplate   = NULL;
+    char*               strType        = NULL;
+    psArray*            typeArray      = NULL;
+    psArray*            templateArray  = NULL;
+    p_psParseLevelInfo* upperLevelInfo = NULL;
+    p_psParseLevelInfo* lowerLevelInfo = NULL;
+    psBool              addStatus      = 0;
+
+    // Set flags if overwrite specified
+    if(overwrite) {
+        flags = PS_META_REPLACE;
+    }
+
+    // If line is not a comment or blank, then extract data
+    if(!ignoreLine(linePtr)) {
+
+        // Check for more than one '@' in a line
+        if(repeatedChars(linePtr, '@') > 1) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
+            return false;
+        }
+
+        // Get metadata item name
+        keyName = getToken(&linePtr, " ", &status,true);
+        if(status) {
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "keyName", lineCount, fileName);
+            psFree(keyName);
+            return false;
+        }
+
+        // Check for special keyName values "TYPE", "END"
+        if(strcmp(keyName,"TYPE") == 0 ) {
+            // Get the type name
+            strType = getToken(&linePtr," ",&status,true);
+            if(strType == NULL) {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL,"type",lineCount,
+                        fileName);
+                psFree(keyName);
+                return false;
+            }
+            tempTemplate = getMetadataType(linePtr);
+            // Check if type was parsed succesfully
+            if(tempTemplate != NULL) {
+                // Access type array
+                typeArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->typeArray;
+                // Check if type already exists in array
+                for(psS32 k=0; k < typeArray->n; k++) {
+                    // Compare type name with the list of current types
+                    if(strcmp(strType,(char*)typeArray->data[k]) == 0) {
+                        psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_TYPE_DUPLICATE,
+                                strType,lineCount,fileName);
+                        psFree(tempTemplate);
+                        psFree(keyName);
+                        psFree(strType);
+                        return false;
+                    }
+                }
+                // Add key name to array of type
+                typeArray = psArrayAdd(typeArray,1,strType);
+                // Add template to array of templates
+                templateArray = ((p_psParseLevelInfo*)(levelArray->data[*level]))->templateArray;
+                templateArray = psArrayAdd(templateArray,1,tempTemplate);
+                psFree(tempTemplate);
+            } else {
+                psError(PS_ERR_IO,true,PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID,
+                        strType,lineCount,fileName);
+                psFree(keyName);
+                psFree(strType);
+                return false;
+            }
+            psFree(strType);
+        } else if (strcmp(keyName,"END") == 0 ) {
+            if(*level > 0) {
+                upperLevelInfo = ((p_psParseLevelInfo*)(levelArray->data[*level-1]));
+                lowerLevelInfo = ((p_psParseLevelInfo*)(levelArray->data[*level]));
+                // Check name in nonunique list
+                for(psS32 k=0; k < upperLevelInfo->nonUniqueKeyArray->n; k++) {
+                    if(strcmp(upperLevelInfo->nonUniqueKeyArray->data[k],lowerLevelInfo->name) == 0) {
+                        flags = PS_META_DUPLICATE_OK;
+                        break;
+                    }
+                }
+                // Add metadata to upper level metadata
+                addStatus = psMetadataAdd(upperLevelInfo->metadata,
+                                          PS_LIST_TAIL,lowerLevelInfo->name,
+                                          PS_META_META | flags,
+                                          "",
+                                          lowerLevelInfo->metadata);
+                if(!addStatus) {
+                    psFree(keyName);
+                    return false;
+                } else {
+                    // Remove lower info level
+                    if(!psArrayRemove(levelArray,levelArray->data[*level])) {
+                        psFree(keyName);
+                        return false;
+                    }
+                    psFree(lowerLevelInfo);
+                    (*level)--;
+                }
+            } else {
+                psFree(keyName);
+                return false;
+            }
+        } else {
+            // Check if key name present in array of non-unique key names
+            limit = ((p_psParseLevelInfo*)(levelArray->data[*level]))->nonUniqueKeyArray->n;
+            for(psS32 k=0; k < limit; k++) {
+                char* name = (char*)((p_psParseLevelInfo*)
+                                     (levelArray->data[*level]))->nonUniqueKeyArray->data[k];
+                if(strcmp(name,keyName) == 0) {
+                    flags = PS_META_DUPLICATE_OK;
+                }
+            }
+            // Parse metadataItem
+            if(!parseMetadataItem(keyName,level, levelArray, linePtr, lineCount, fileName, flags)) {
+                psFree(keyName);
+                return false;
+            }
+        }
+        psFree(keyName);
+    }
+    return returnValue;
+}
+
 psMetadata* psMetadataParseConfig(psMetadata* md, psU32 *nFail, const char *fileName, psBool overwrite)
 {
-    psBool tempBool;
-    char *line = NULL;
-    char *strName = NULL;
-    char *strType = NULL;
-    char *strValue = NULL;
-    char *strComment = NULL;
-    char *linePtr = NULL;
-    psElemType vecType = 0;
-    psS32 status = 0;
-    psU32 lineCount = 0;
-    psF64 tempDbl = 0.0;
-    psS32 tempInt = 0.0;
-    psVector *tempVec = NULL;
-    FILE *fp = NULL;
-    psMetadataType mdType;
-    psMetadataFlags flags;
-    psBool addStatus;
-
-    // Check for nulls
+    FILE*               fp                   = NULL;
+    char*               line                 = NULL;
+    char*               linePtr              = NULL;
+    psArray*            parseLevelInfoArray  = NULL;
+    psS32               lineCount            = 0;
+    psS32               nestingLevel         = 0;
+    p_psParseLevelInfo* topLevelInfo         = NULL;
+
+    // Check for NULL file name
     PS_PTR_CHECK_NULL(fileName,NULL);
+
+    // Check for NULL nFail
+    PS_PTR_CHECK_NULL(nFail,NULL);
+
+    // Attempt to open specified file
     if((fp=fopen(fileName, "r")) == NULL) {
         psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_OPEN_FAILED, fileName);
@@ -532,4 +1102,14 @@
     }
 
+    // Allocate array to store parse level information
+    parseLevelInfoArray = psArrayAlloc(10);
+    parseLevelInfoArray->n = 0;
+
+    // Set parse level info for the top level
+    topLevelInfo = p_psParseLevelInfoAlloc();
+    topLevelInfo->metadata = psMemIncrRefCounter(md);
+    parseLevelInfoArray = psArrayAdd(parseLevelInfoArray,1,topLevelInfo);
+    psFree(topLevelInfo);
+
     // Create reusable line for continuous read
     line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
@@ -541,189 +1121,22 @@
         linePtr = line;
         lineCount++;
-        CLEAR_TEMPS();
-
-        // If line is not a comment or blank, then extract data
-        if(!ignoreLine(linePtr)) {
-
-            // Check for more than one '*' or '@' in a line
-            if(repeatedChars(linePtr, '@') > 1) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '@', lineCount, fileName);
-                continue;
-            } else if(repeatedChars(linePtr, '*') > 1) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '*', lineCount, fileName);
-                continue;
-            } else if(repeatedChars(linePtr, '~') > 0) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_MULTIPLE_CHAR, '~', lineCount, fileName);
-                continue;
-            }
-
-            // Get metadata item name
-            strName = getToken(&linePtr, " ", &status);
-            if(strName==NULL || status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true,
-                        PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "name", lineCount, fileName);
-                continue;
-            }
-
-            flags = (overwrite) ? PS_META_REPLACE : PS_META_DEFAULT;
-
-            // Get the metadata item type
-            strType = getToken(&linePtr, " ", &status);
-            if(strType==NULL) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "type",lineCount,
-                        fileName);
-                continue;
-            } else {
-                char* tempStrType = strType;
-                if(*strType == '*') {
-                    flags = PS_META_DUPLICATE_OK;
-                    tempStrType = strType+1;
-                }
-
-                if(!strncmp(tempStrType, "STR", 3)) {
-                    mdType = PS_META_STR;
-                } else if(!strncmp(tempStrType, "BOOL", 4)) {
-                    mdType = PS_META_BOOL;
-                } else if(!strncmp(tempStrType, "S32", 3)) {
-                    mdType = PS_META_S32;
-                } else if(!strncmp(tempStrType, "F32", 3)) {
-                    mdType = PS_META_F32;
-                } else if(!strncmp(tempStrType, "F64", 3)) {
-                    mdType = PS_META_F64;
-                } else {
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, strType, lineCount,
-                            fileName);
-                    continue;
-                }
-            }
-
-            if(*strName == '@') {
-                vecType = PS_META_PRIMITIVE_TYPE(mdType);
-                mdType = PS_META_VEC;
-            }
-
-            // Get the metadata item value if there is one.
-            strValue = getToken(&linePtr, "#", &status);
-            if(status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
-                        fileName);
-                continue;
-            }
-            if(strValue==NULL) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "value", lineCount,
-                        fileName);
-                continue;
-            }
-
-            // Not all lines will have comments, so NULL is ok.
-            strComment = getToken(&linePtr,"~", &status);
-            if(status) {
-                (*nFail)++;
-                status = 0;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_ELEMENT_NULL, "comment", lineCount,
-                        fileName);
-                continue;
-            }
-
-            // Create and add metadata item to metadata and parse values
-            switch (mdType) {
-            case PS_META_STR:
-                addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                          mdType | flags,
-                                          strComment, strValue);
-                break;
-            case PS_META_VEC:
-                tempVec = parseVector(strValue, vecType, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName+1,
-                                              mdType | flags,
-                                              strComment, tempVec);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                psFree(tempVec);
-                break;
-            case PS_META_BOOL:
-                tempBool = parseBool(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempBool);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                break;
-            case PS_META_S32:
-                tempInt = (psS32)parseValue(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempInt);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName,
-                            strType, lineCount, fileName);
-                    continue;
-                }
-                break;
-            case PS_META_F32:
-            case PS_META_F64:
-                tempDbl = parseValue(strValue, &status);
-                if(!status) {
-                    addStatus = psMetadataAdd(md, PS_LIST_TAIL, strName,
-                                              mdType | flags,
-                                              strComment, tempDbl);
-                } else {
-                    status = 0;
-                    (*nFail)++;
-                    psError(PS_ERR_IO, true,
-                            PS_ERRORTEXT_psMetadataIO_PARSE_FAILED, strValue, strName, strType, lineCount,
-                            fileName);
-                    continue;
-                }
-                break;
-            default:
-                (*nFail)++;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_FILE_TYPE_INVALID, mdType, lineCount,
-                        fileName);
-                continue;
-            } // switch
-            if (! addStatus) {
-                (*nFail)++;
-                psError(PS_ERR_IO, true, PS_ERRORTEXT_psMetadataIO_OVERWRITE_ITEM, strName, lineCount,
-                        fileName);
-            }
-
-        } // if ignoreLine
-    } // while loop
-
+
+        if(!parseLine(&nestingLevel,parseLevelInfoArray,linePtr,lineCount,(char*)fileName,overwrite)) {
+            (*nFail)++;
+        }
+    }
+
+    // Free parse array and line buffer
+    psFree(parseLevelInfoArray);
     psFree(line);
 
+    // Check if any failed lined detected
+    if( (*nFail) != 0) {
+        psFree(md);
+        md = NULL;
+    }
     return md;
 }
+
 
 static void saxStartElement(void *ctx, const xmlChar *tagName, const xmlChar **atts)
@@ -750,5 +1163,5 @@
 
     // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-    psTagName = psStringCopy((const char*)tagName);
+    psTagName = psStringCopy(tagName);
 
     // Metadata containter for housing element attributes used by other SAX events
@@ -774,6 +1187,6 @@
 
                 // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-                psAttName = psStringCopy((const char*)attName);
-                psAttValue = psStringCopy((const char*)attValue);
+                psAttName = psStringCopy(attName);
+                psAttValue = psStringCopy(attValue);
                 psHashAdd(htAtts, psAttName, psAttValue);
                 psFree(psAttName);
@@ -1082,5 +1495,5 @@
 
     // Copy XML strings to psStrings to avoid libxml2/psLib memory corruption problems
-    psEndTagName = psStringCopy((const char*)tagName);
+    psEndTagName = psStringCopy(tagName);
 
     // Compare start and end tag names
Index: /trunk/psLib/src/xml/psXML.h
===================================================================
--- /trunk/psLib/src/xml/psXML.h	(revision 3944)
+++ /trunk/psLib/src/xml/psXML.h	(revision 3945)
@@ -10,6 +10,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-03-07 20:58:50 $
+ *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-05-16 19:43:53 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -21,4 +21,15 @@
 /// @{
 
+typedef struct
+{
+    psArray*    nonUniqueKeyArray;
+    psArray*    typeArray;
+    psArray*    templateArray;
+    psMetadata* metadata;
+    char*       name;
+}
+p_psParseLevelInfo;
+
+p_psParseLevelInfo* p_psParseLevelInfoAlloc(void);
 
 /** Print metadata item to file.
Index: /trunk/psLib/test/collections/test1.config
===================================================================
--- /trunk/psLib/test/collections/test1.config	(revision 3945)
+++ /trunk/psLib/test/collections/test1.config	(revision 3945)
@@ -0,0 +1,57 @@
+Double	F64     1.23456789      # This is a comment
+Float     F32 0.98765#This is a comment too
+String   STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of 'boolean' is 'true'
+
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but 'comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if 'overwrite' is 'false', is ignored
+
+boolean1  BOOL   F # The value of 'boolean' is 'false'
+
+@negprimes S8 -2, -3, -5, -7,-11,-13,-17,-19
+
+@vector1 U16  0, 1, 2, 4, 8
+@vector2 U32  0, 8, 16, 32, 64, 128
+@vector3 U64  0, 64, 256,
+
+@vector4 S16  -2 -1 0 1 2
+@vector5 S32  -4, -2, 0, 2, 4, 6
+@vector6 S64  -16, -4, 0, 4 16 36 64
+
+@vector7 F32  -1.03, 1.04, -1.05, 1.06
+@vector8 F64  -2.22, 2.21 -2.20, 2.19, -2.18
+
+TYPE	CELL	EXTNAME		BIASSEC		CHIP       #
+CELL.00	CELL	CCD00		BSEC-00		CHIP.00
+CELL.01	CELL	CCD01		BSEC-01		CHIP.00
+
+MYCELL  MULTI
+MYCELL    METADATA
+  EXTNAME   STR   CCD00
+  BIASSEC   STR   BSEC-00
+  CHIP      STR   CHIP.00
+  NCELL     S32   24
+END
+MYCELL S32 123 # A number
+
+cell  METADATA
+   foo   METADATA
+       bar     STR   BAZ
+       ping    STR   PONG
+   END
+
+   EXTNAME   STR CCD00
+   BIASSEC   STR BSEC-00
+   CHIP      STR CHIP.00
+   NCELL     S32 12
+END
+
Index: /trunk/psLib/test/collections/test2.config
===================================================================
--- /trunk/psLib/test/collections/test2.config	(revision 3945)
+++ /trunk/psLib/test/collections/test2.config	(revision 3945)
@@ -0,0 +1,44 @@
+ # This should generate error message
+boolean     BOOL    X # The value of 'boolean' is 'true'
+
+value1    S32  # No value
+
+# Invalid vector entries
+@vector  U8
+@vector1 U8  x,y,z
+@vector2
+@vector3 F8 ,,,
+
+# Multiple MULTI definitions
+comment MULTI
+comment MULTI
+
+# Type CELL not defined
+MYCELL CELL CCD00 BSEC-00 CHIP.00
+
+# Invalid value entries
+value1   F64   aabb
+value2   S32   ccdd
+
+# Repeated vector character
+@@vector4 U8 1 2 3 4 #
+
+# Invalid TYPE line
+TYPE
+
+# Duplicate TYPE
+TYPE CELL EXTNAME BIASSEC CHIP
+TYPE CELL EXTNAME BIASSEC CHIP
+
+# END with no matching METADATA
+END
+
+# Duplicate item names within metadata type specifier
+TYPE NEWCELL VALUE1 VALUE1 #
+
+# Not enough values in specified type
+OURCELL  CELL CCD00
+
+# Type with no item
+TYPE NEWCELL1
+
Index: /trunk/psLib/test/collections/test3.config
===================================================================
--- /trunk/psLib/test/collections/test3.config	(revision 3945)
+++ /trunk/psLib/test/collections/test3.config	(revision 3945)
@@ -0,0 +1,16 @@
+Double	F64     1.23456789      # This is a comment
+Float     F32 0.98765#This is a comment too
+String   STR This is the string that forms the value #comment
+
+ # This is a comment line and is to be ignored
+boolean     BOOL    T # The value of 'boolean' is 'true'
+
+@primes U8  2,3 5 7,11,13 17 #   These are prime numbers
+
+comment MULTI # The rest of this line is ignored, but 'comment' is set to be non-unique
+comment STR This
+comment STR     is
+comment STR       a
+comment STR        non-unique
+comment STR                  key
+Float F64 1.23456 # This generates a warning, and, if 'overwrite' is 'false', is ignored
Index: /trunk/psLib/test/collections/tst_psMetadataIO.c
===================================================================
--- /trunk/psLib/test/collections/tst_psMetadataIO.c	(revision 3944)
+++ /trunk/psLib/test/collections/tst_psMetadataIO.c	(revision 3945)
@@ -4,16 +4,15 @@
  *
  *  This test driver contains the following tests for psMetadata:
- *    Test A - Read config file with overwrite set true
- *    Test B - Read config file with overwrite set false
- *    Test C - Read config file without auto-allocation of metadata
- *    Test D - Attempt to use null fileName argument
- *    Test E - Attempt to open nonexistant file
- *    Test F - Free psMetadata
+ *    Read config file with overwrite set true
+ *    Read config file with overwrite set false
+ *    Attempt to use null fileName argument
+ *    Attempt to open nonexistant file
  *
  *  @author  Ross Harman, MHPCC
  *  @author  Robert DeSonia, MHPCC
+ *  @author  Eric Van Alst, MHPCC
  *
- *  @version $Revision: 1.14 $  $Name: not supported by cvs2svn $
- *  @date  $Date: 2005-04-28 02:45:12 $
+ *  @version $Revision: 1.15 $  $Name: not supported by cvs2svn $
+ *  @date  $Date: 2005-05-16 19:41:40 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -25,148 +24,949 @@
 #include "psTest.h"
 
-static void printMetadataItem(psMetadataItem *metadataItem, char *spaces);
-static void printMetadata(psMetadata *metadata);
-static void printMetadataList(psList *metadataItemList, char* spaces);
-static void printMetadataTable(psHash *mdTable);
-
-static void printMetadata(psMetadata *metadata)
-{
-    printf("Contents of metadata list:\n");
-    printMetadataList(metadata->list, " ");
-    printf("\nContents of metadata table:\n");
-    printMetadataTable(metadata->table);
-}
-
-static void printMetadataList(psList *metadataItemList, char* spaces)
-{
-    psMetadataItem *entryChild = NULL;
-
+static psS32 testMetadataParseConfig(void);
+static psS32 testMetadataParseConfig1(void);
+static psS32 testMetadataParseConfig2(void);
+
+testDescription tests[] = {
+                              {testMetadataParseConfig,000,"psMetadataParseConfig",0,false},
+                              {testMetadataParseConfig1,000,"psMetadataParseConfig",0,false},
+                              {testMetadataParseConfig2,000,"psMetadataParseConfig",0,false},
+                              {NULL}
+                          };
+
+static void writeMetadataItem(psMetadataItem* metadataIem, char* indentStr);
+static void writeMetadata(psMetadata* metadata, char* indentStr);
+static void writeMetadataList(psList *metadataItemList, char* indentStr);
+
+static psBool checkFailedLines(psS32 failedLines, psS32 expectedFailedLines, char* configFile);
+static psBool checkNumberOfItems(psMetadata* md, psS32 expectedItems, char* configFile);
+static psBool checkItemName(psMetadataItem* mdItem, char* expectedName, char* configFile);
+static psBool checkItemType(psMetadataItem* mdItem, psS32 expectedType, char* configFile);
+static psBool checkItemComment(psMetadataItem* mdItem, char* expectedComment, char* configFile);
+
+#define ERROR_TOL  0.0001
+
+const char testConfig3[] = "test3.config";
+const psS32 testConfig3Fails = 1;
+
+const char testConfig2[] = "test2.config";
+const psS32 testConfig2Fails = 17;
+
+// SDR-14 test config file #1
+const char  testConfig1[] = "test1.config";
+const psS32 testConfig1FailsOverwrite = 0;
+const psS32 testConfig1Fails = 1;
+const psS32 testConfig1Items = 25;
+const char* testConfig1KeyOverwrite[] =
+    {
+        "Double","String","boolean","primes","comment",
+        "comment","comment","comment","comment","Float",
+        "boolean1","negprimes","vector1","vector2","vector3",
+        "vector4","vector5","vector6","vector7","vector8",
+        "CELL.00", "CELL.01","MYCELL","MYCELL","cell"
+    };
+const char* testConfig1CommentOverwrite[] =
+    {
+        "This is a comment","comment","The value of 'boolean' is 'true'","These are prime numbers",
+        "","","","","",
+        "This generates a warning, and, if 'overwrite' is 'false', is ignored","The value of 'boolean' is 'false'",
+        "","","","","",
+        "","","","",
+        "","","","A number",""
+    };
+const psMetadataType testConfig1TypeOverwrite[] =
+    {
+        PS_META_F64, PS_META_STR, PS_META_BOOL, PS_META_VEC, PS_META_STR,
+        PS_META_STR, PS_META_STR, PS_META_STR, PS_META_STR,  PS_META_F64,
+        PS_META_BOOL, PS_META_VEC, PS_META_VEC, PS_META_VEC, PS_META_VEC,
+        PS_META_VEC, PS_META_VEC, PS_META_VEC, PS_META_VEC, PS_META_VEC,
+        PS_META_META, PS_META_META, PS_META_META, PS_META_S32, PS_META_META
+    };
+const psS32 testConfig1ValueS32Overwrite[] =
+    {
+        0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0,
+        0, 0, 0, 123, 0
+    };
+const psF32 testConfig1ValueF32Overwrite[] =
+    {
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0
+    };
+const psF64 testConfig1ValueF64Overwrite[] =
+    {
+        1.23456789, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 1.23456,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0,
+        0.0, 0.0, 0.0, 0.0, 0.0
+    };
+const psBool testConfig1ValueBoolOverwrite[] =
+    {
+        false, false, true, false, false,
+        false, false, false, false, false,
+        false, false, false, false, false,
+        false, false, false, false, false,
+        false, false, false, false, false
+    };
+const char* testConfig1ValueStrOverwrite[] =
+    {
+        "", "This is the string that forms the value","","","This",
+        "is","a","non-unique","key","",
+        "","","","","",
+        "","","","","",
+        "","","","",""
+    };
+const psS32 testConfig1ValueVecItemsU8 = 7;
+const psElemType testConfig1ValueVecTypeU8 = PS_TYPE_U8;
+const psU8 testConfig1ValueVecValueU8[] =
+    {
+        2, 3, 5, 7, 11, 13, 17
+    };
+const psS32 testConfig1ValueVecItemsS8 = 8;
+const psElemType testConfig1ValueVecTypeS8 = PS_TYPE_S8;
+const psS8 testConfig1ValueVecValueS8[] =
+    {
+        -2, -3, -5, -7, -11, -13, -17, -19
+    };
+const psS32 testConfig1ValueVecItemsU16 = 5;
+const psElemType testConfig1ValueVecTypeU16 = PS_TYPE_U16;
+const psU16 testConfig1ValueVecValueU16[] =
+    {
+        0, 1, 2, 4, 8
+    };
+const psS32 testConfig1ValueVecItemsU32 = 6;
+const psElemType testConfig1ValueVecTypeU32 = PS_TYPE_U32;
+const psU32 testConfig1ValueVecValueU32[] =
+    {
+        0, 8, 16, 32, 64, 128
+    };
+const psS32 testConfig1ValueVecItemsU64 = 3;
+const psElemType testConfig1ValueVecTypeU64 = PS_TYPE_U64;
+const psU64 testConfig1ValueVecValueU64[] =
+    {
+        0, 64, 256
+    };
+const psS32 testConfig1ValueVecItemsS16 = 5;
+const psElemType testConfig1ValueVecTypeS16 = PS_TYPE_S16;
+const psS16 testConfig1ValueVecValueS16[] =
+    {
+        -2, -1, 0, 1, 2
+    };
+const psS32 testConfig1ValueVecItemsS32 = 6;
+const psElemType testConfig1ValueVecTypeS32 = PS_TYPE_S32;
+const psS32 testConfig1ValueVecValueS32[] =
+    {
+        -4, -2, 0, 2, 4, 6
+    };
+const psS32 testConfig1ValueVecItemsS64 = 7;
+const psElemType testConfig1ValueVecTypeS64 = PS_TYPE_S64;
+const psS64 testConfig1ValueVecValueS64[] =
+    {
+        -16, -4, 0, 4, 16, 36, 64
+    };
+const psS32 testConfig1ValueVecItemsF32 = 4;
+const psElemType testConfig1ValueVecTypeF32 = PS_TYPE_F32;
+const psF32 testConfig1ValueVecValueF32[] =
+    {
+        -1.03, 1.04, -1.05, 1.06
+    };
+const psS32 testConfig1ValueVecItemsF64 = 5;
+const psElemType testConfig1ValueVecTypeF64 = PS_TYPE_F64;
+const psF64 testConfig1ValueVecValueF64[] =
+    {
+        -2.22, 2.21, -2.20, 2.19, -2.18
+    };
+const psS32 testConfig1ValueMetaItems1 = 3;
+const char* testConfig1ValueMetaNames1[] =
+    {
+        "EXTNAME","BIASSEC","CHIP"
+    };
+const psMetadataType testConfig1ValueMetaTypes1[] =
+    {
+        PS_META_STR, PS_META_STR, PS_META_STR
+    };
+const char* testConfig1ValueMetaValue1[] =
+    {
+        "CCD00","BSEC-00","CHIP.00"
+    };
+const psS32 testConfig1ValueMetaItems2 = 3;
+const char* testConfig1ValueMetaNames2[] =
+    {
+        "EXTNAME","BIASSEC","CHIP"
+    };
+const psMetadataType testConfig1ValueMetaTypes2[] =
+    {
+        PS_META_STR, PS_META_STR, PS_META_STR
+    };
+const char* testConfig1ValueMetaValue2[] =
+    {
+        "CCD01","BSEC-01","CHIP.00"
+    };
+const psS32 testConfig1ValueMetaItems3 = 4;
+const char* testConfig1ValueMetaNames3[] =
+    {
+        "EXTNAME","BIASSEC","CHIP","NCELL"
+    };
+const psMetadataType testConfig1ValueMetaTypes3[] =
+    {
+        PS_META_STR, PS_META_STR, PS_META_STR, PS_META_S32
+    };
+const char* testConfig1ValueMetaValueStr3[] =
+    {
+        "CCD00","BSEC-00","CHIP.00",""
+    };
+const psS32 testConfig1ValueMetaValueS323[] =
+    {
+        0, 0, 0, 24
+    };
+
+static void writeMetadata(psMetadata* metadata, char* indentStr)
+{
+    writeMetadataList(metadata->list, indentStr);
+}
+
+static void writeMetadataList(psList* metadataItemList, char* indentStr)
+{
+    psMetadataItem* entryChild        = NULL;
+    psMetadataItem* searchChild       = NULL;
+    psArray*        nonUniqueKeyArray = NULL;
+    psBool          keyFound          = false;
+    char*           tempKey           = NULL;
+
+    // Allocate iterator for moving through metadata list
     psListIterator* iter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);
-
-    while ( (entryChild=psListGetAndIncrement(iter)) != NULL) {
-        printMetadataItem(entryChild, spaces);
+    psListIterator* searchIter = psListIteratorAlloc(metadataItemList, PS_LIST_HEAD, true);
+
+    // Allocate array for nonUnique key names
+    nonUniqueKeyArray = psArrayAlloc(10);
+    nonUniqueKeyArray->n = 0;
+
+
+    // Loop through all items in the list
+    while ( (entryChild = psListGetAndIncrement(iter)) != NULL) {
+        // Search list for another entry with same key name
+        // Check if last entry
+        if(iter->index != metadataItemList->size) {
+            // Set search iterator to index
+            if(!psListIteratorSet(searchIter,iter->index) ) {
+                psError(PS_ERR_UNKNOWN,true,"Error searching list for multiple keys");
+                break;
+            }
+            keyFound = false;
+            while ( (searchChild = psListGetAndIncrement(searchIter)) != NULL) {
+                if(strcmp(entryChild->name,searchChild->name) == 0) {
+                    // Search nonUnique key array
+                    for(psS32 i = 0; i < nonUniqueKeyArray->n; i++) {
+                        if(strcmp(entryChild->name,nonUniqueKeyArray->data[i]) == 0) {
+                            keyFound = true;
+                        }
+                    }
+                    if(!keyFound) {
+                        // Add key to non-unique array
+                        tempKey = psStringCopy(entryChild->name);
+                        nonUniqueKeyArray = psArrayAdd(nonUniqueKeyArray,1,tempKey);
+                        psFree(tempKey);
+
+                        // Print MULTI line
+                        printf("%-25s MULTI\n",entryChild->name);
+
+                        // Break out of loop
+                        break;
+                    }
+                }
+            }
+        }
+        writeMetadataItem(entryChild,indentStr);
     }
 
     psFree(iter);
-}
-
-static void printMetadataTable(psHash *mdTable)
-{
-    psS32 i;
-    psHashBucket* ptr = NULL;
-    for(i=0; i<mdTable->nbucket; i++) {
-        ptr = mdTable->buckets[i];
-        while (ptr != NULL) {
-            printMetadataItem(ptr->data, " ");
-            ptr = ptr->next;
-        }
-    }
-}
-
-static void printMetadataItem(psMetadataItem *metadataItem, char *spaces)
-{
-    char* vecStr;
-
-    printf("%sKey Name: %8s  ", spaces, metadataItem->name);
-    printf("Key mdType: 0x%08x ", metadataItem->type);
-
-    switch (metadataItem->type) {
-    case PS_META_MULTI:
-        printf("Key Value: %17c", ' ');
+    psFree(searchIter);
+    psFree(nonUniqueKeyArray);
+}
+
+char* vectorToConfigString(psVector* vector)
+{
+    psS32  maxLength = 256;
+
+    char* str = psAlloc(sizeof(char)*maxLength+1);
+
+    if (vector == NULL) {
+        snprintf(str,maxLength, "NULL");
+        return str;
+    }
+
+    int size = vector->n;
+
+    if (size == 0) {
+        snprintf(str,maxLength, " ");
+        return str;
+    }
+
+    char* tempStr = psAlloc(sizeof(char)*maxLength+1);
+    *str = '\0';
+    bool full = false;
+
+    #define APPEND_ELEMENTS_CASE(TYPE, NATIVE_TYPE, FORMAT) \
+case PS_TYPE_##TYPE: \
+    strcat(str,#TYPE); \
+    for(psS32 i = 0; i < (strlen(str)-6); i++) {  \
+        strcat(str," "); \
+    } \
+    for (lcv=0; lcv < size && ! full; lcv++) { \
+        snprintf(tempStr, maxLength, "%s" FORMAT, prefix, (NATIVE_TYPE) (vector->data.TYPE[lcv])); \
+        strncat(str,tempStr,maxLength); \
+        full = (strlen(str) > maxLength-2); \
+        prefix = ","; \
+    } \
+    break;
+
+    int lcv;
+    char* prefix = " ";
+    switch(vector->type.type) {
+        APPEND_ELEMENTS_CASE(S8,char,"%hd")
+        APPEND_ELEMENTS_CASE(S16,short int,"%hd")
+        APPEND_ELEMENTS_CASE(S32,int,"%d")
+        APPEND_ELEMENTS_CASE(S64,long,"%ld")
+        APPEND_ELEMENTS_CASE(U8,unsigned char,"%hu")
+        APPEND_ELEMENTS_CASE(U16,unsigned short,"%hu")
+        APPEND_ELEMENTS_CASE(U32,unsigned int, "%u")
+        APPEND_ELEMENTS_CASE(U64,unsigned long,"%lu")
+        APPEND_ELEMENTS_CASE(F32,double,"%g")
+        APPEND_ELEMENTS_CASE(F64,double,"%g")
+    default:
+        snprintf(str,maxLength,"INVALID TYPE");
         break;
+    }
+
+    psFree(tempStr);
+
+    return str;
+}
+
+
+static void writeMetadataItem(psMetadataItem *metadataItem, char* indentStr)
+{
+    char*  vecStr;
+
+    switch(metadataItem->type) {
     case PS_META_BOOL:
-        printf("Key Value: %15d  ", metadataItem->data.B);
+        printf("%s%-25s BOOL  %40d",indentStr,metadataItem->name,metadataItem->data.B);
         break;
     case PS_META_S32:
-        printf("Key Value: %15d  ", metadataItem->data.S32);
+        printf("%s%-25s S32   %40d",indentStr,metadataItem->name,metadataItem->data.S32);
         break;
     case PS_META_F32:
-        printf("Key Value: %15.3f  ", metadataItem->data.F32);
+        printf("%s%-25s F32   %40f",indentStr,metadataItem->name,metadataItem->data.F32);
         break;
     case PS_META_F64:
-        printf("Key Value: %15.3f  ", metadataItem->data.F64);
+        printf("%s%-25s F64   %40lf",indentStr,metadataItem->name,metadataItem->data.F64);
         break;
     case PS_META_VEC:
-        vecStr = (char*)psVectorToString((psVector*)metadataItem->data.V, 15);
-        printf("Key Value: %15s  ", vecStr);
+        vecStr = (char*)vectorToConfigString((psVector*)metadataItem->data.V);
+        printf("%s@%-24s %s",indentStr,metadataItem->name,vecStr);
         psFree(vecStr);
         break;
     case PS_META_STR:
-        printf("Key Value: %15s  ", (char*)metadataItem->data.V);
+        printf("%s%-25s STR   %40s",indentStr,metadataItem->name,(char*)metadataItem->data.V);
+        break;
+    case PS_META_META:
+        printf("%s%-25s METADATA\n",indentStr,metadataItem->name);
+        writeMetadata((psMetadata*)metadataItem->data.md,"    ");
         break;
     default:
-        printf("Bad type: 0x%08x ", metadataItem->type);
-    }
-    printf("Key Comment: %s\n", metadataItem->comment);
-
-    if(metadataItem->data.V && metadataItem->type==PS_META_MULTI) {
-        printMetadataList(metadataItem->data.V, "    ");
-    }
-}
-
+        printf("#%s%-24s BAD TYPE=%d",indentStr,metadataItem->name,metadataItem->type);
+        break;
+    }
+    if(strlen(metadataItem->comment) > 0) {
+        printf("  # %-20s\n",metadataItem->comment);
+    } else {
+        printf("\n");
+    }
+}
+
+static psBool checkFailedLines(psS32 failedLines, psS32 expectedFailedLines, char* configFile)
+{
+    psBool     returnValue = true;
+
+    // Verify the expected number of failed lines
+    if(failedLines != expectedFailedLines) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Number of failed lines = %d not as expected %d",
+                configFile,failedLines,expectedFailedLines);
+        returnValue = false;
+    }
+    return returnValue;
+}
+
+static psBool checkNumberOfItems(psMetadata* md, psS32 expectedItems, char* configFile)
+{
+    psBool     returnValue = true;
+
+    // Verify the number of items in metadata
+    if(md->list->size != expectedItems) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Number of items = %d not as expected %d",
+                configFile,md->list->size, expectedItems);
+        returnValue = false;
+    }
+    return returnValue;
+}
+
+static psBool checkItemName(psMetadataItem* mdItem, char* expectedName, char* configFile)
+{
+    psBool    returnValue = true;
+
+    // Compare key names
+    if(strcmp(mdItem->name,expectedName) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Key name %s not as expected %s",
+                configFile,mdItem->name,expectedName);
+        returnValue = false;
+    }
+
+    return returnValue;
+}
+
+static psBool checkItemType(psMetadataItem* mdItem, psS32 expectedType, char* configFile)
+{
+    psBool     returnValue = true;
+
+    // Compare types
+    if(mdItem->type != expectedType) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Type %d not as expected %d",
+                configFile,mdItem->type,expectedType);
+        returnValue = false;
+    }
+    return returnValue;
+}
+
+static psBool checkItemComment(psMetadataItem* mdItem, char* expectedComment, char* configFile)
+{
+    psBool    returnValue = true;
+
+    // Compare comments
+    if(strcmp(mdItem->comment,expectedComment) != 0) {
+        psError(PS_ERR_UNKNOWN,true,"File: %s : Comment %s not as expected %s",
+                configFile,mdItem->comment,expectedComment);
+        returnValue = false;
+    }
+    return returnValue;
+}
 
 psS32 main(psS32 argc, char* argv[])
 {
-    // Test A - Read config file with overwrite set true
-    printPositiveTestHeader(stdout, "psMetadata", "Test A - Read config file with overwrite set true");
-    psMetadata *metadata1 = NULL;
-    psU32 failedLines1 = 0;
-    metadata1 = psMetadataParseConfig(metadata1, &failedLines1, "test.config", true);
-    printf("Failed lines: %d Expected: 6\n", failedLines1);
-    printMetadata(metadata1);
-    printFooter(stdout, "psMetadata", "Test A - Read config file with overwrite set true", true);
-
-
-    // Test B - Read config file with overwrite set false
-    printPositiveTestHeader(stdout, "psMetadata", "Test B - Read config file with overwrite set false");
-    psMetadata *metadata2 = NULL;
-    psU32 failedLines2 = 0;
-    metadata2 = psMetadataParseConfig(metadata2, &failedLines2, "test.config", false);
-    printf("Failed lines: %d Expected: 7 ", failedLines2);
-    printMetadata(metadata2);
-    printFooter(stdout, "psMetadata", "Test B - Read config file with overwrite set false", true);
-
-
-    // Test C - Read config file without auto-allocation of metadata
-    printPositiveTestHeader(stdout, "psMetadata", "Test C - Read config file without auto-allocation of metadata");
-    psMetadata *metadata3 = psMetadataAlloc();
-    psU32 failedLines3 = 0;
-    metadata3 = psMetadataParseConfig(metadata3, &failedLines3, "test.config", true);
-    printf("Failed lines: %d Expected: 6 ", failedLines3);
-    printMetadata(metadata3);
-    printFooter(stdout, "psMetadata", "Test C - Read config file without auto-allocation of metadata", true);
-
-
-    // Test D - Attempt to use null fileName argument
-    printNegativeTestHeader(stdout,"psMetadataIO", "Test D - Attempt to use null fileName argument",
-                            "Null failedLines not allowed", 0);
-    psMetadataParseConfig(metadata1, &failedLines1, NULL, true);
-    printFooter(stdout, "psMetadata", "Test D - Attempt to use null fileName argument", true);
-
-
-    // Test E - Attempt to open nonexistant file
-    printNegativeTestHeader(stdout,"psMetadataIO", "Test E - Attempt to open nonexistant file",
-                            "Error opening file", 0);
-    psMetadataParseConfig(metadata1, &failedLines1, "abcedfg", true);
-    printFooter(stdout, "psMetadata", "Test E - Attempt to open nonexistant file", true);
-
-
-    // Test F - Free psMetadata
-    printPositiveTestHeader(stdout, "psMetadata", "Test F - Free psMetadata");
+    psLogSetLevel(PS_LOG_INFO);
+
+    if( !runTestSuite(stderr,"psMetadataParseConfig",tests,argc,argv)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+psS32 testMetadataParseConfig(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psMetadataItem*   entryChild     = NULL;
+    psS32             failedLines    = 0;
+    psListIterator*   iter           = NULL;
+    psListIterator*   mdIter         = NULL;
+    psMetadataItem*   mdChild        = NULL;
+    psS32             metaCounter    = 0;
+    psS32             mdCounter      = 0;
+
+    // Read config file test1.config with overwrite set true
+    metadata1 = psMetadataParseConfig(metadata1,&failedLines,testConfig1,true);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig1FailsOverwrite,(char*)testConfig1)) {
+        return 1;
+    }
+    // Verify the number of items in metadata
+    if(!checkNumberOfItems(metadata1,testConfig1Items,(char*)testConfig1)) {
+        return 2;
+    }
+    // Verify metadata item quads
+    iter = psListIteratorAlloc(metadata1->list, PS_LIST_HEAD, true);
+    for(psS32 i = 0; i < testConfig1Items; i++) {
+        // Get list item
+        entryChild = psListGetAndIncrement(iter);
+
+        // Verify end of list not reached prematurely
+        if(entryChild == NULL) {
+            psError(PS_ERR_UNKNOWN,true,"End of list encountered at %d not as expected %d",
+                    i,testConfig1Items);
+            return i*10+1;
+        }
+        // Verify name
+        if(!checkItemName(entryChild,(char*)testConfig1KeyOverwrite[i],(char*)testConfig1)) {
+            return i*10+2;
+        }
+        // Verify type
+        if(!checkItemType(entryChild,testConfig1TypeOverwrite[i],(char*)testConfig1)) {
+            return i*10+3;
+        }
+        // Verify comment
+        if(!checkItemComment(entryChild,(char*)testConfig1CommentOverwrite[i],(char*)testConfig1)) {
+            return i*10+4;
+        }
+        // Compare values
+        switch(entryChild->type) {
+        case PS_META_S32:
+            if(entryChild->data.S32 != testConfig1ValueS32Overwrite[i]) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %d not as expected %d",
+                        testConfig1,entryChild->data.S32, testConfig1ValueS32Overwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_META_F32:
+            if(fabs(entryChild->data.F32 - testConfig1ValueF32Overwrite[i]) > ERROR_TOL) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %f not as expected %f",
+                        testConfig1,entryChild->data.F32, testConfig1ValueF32Overwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_META_F64:
+            if(fabs(entryChild->data.F64 - testConfig1ValueF64Overwrite[i]) > ERROR_TOL) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %lf not as expected %lf",
+                        testConfig1,entryChild->data.F64, testConfig1ValueF64Overwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_META_BOOL:
+            if(entryChild->data.B != testConfig1ValueBoolOverwrite[i]) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %d not as expected %d",
+                        testConfig1,entryChild->data.B,testConfig1ValueBoolOverwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_META_STR:
+            if(strcmp(entryChild->data.V,testConfig1ValueStrOverwrite[i]) != 0) {
+                psError(PS_ERR_UNKNOWN,true,"File: %s : Value %s not as expected %s",
+                        testConfig1,entryChild->data.V,testConfig1ValueStrOverwrite[i]);
+                return i*10+5;
+            }
+            break;
+        case PS_META_VEC:
+            // Verify the correct number of entries
+            switch(((psVector*)(entryChild->data.V))->type.type) {
+            case PS_TYPE_U8:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU8);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU8);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU8; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U8[j] != testConfig1ValueVecValueU8[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U8[j],
+                                testConfig1ValueVecValueU8[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_U16:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU16);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU16);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU16; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U16[j] != testConfig1ValueVecValueU16[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U16[j],
+                                testConfig1ValueVecValueU16[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_U32:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU32);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU32);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU32; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U32[j] != testConfig1ValueVecValueU32[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U32[j],
+                                testConfig1ValueVecValueU32[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_U64:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsU64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsU64);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeU64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeU64);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsU64; j++) {
+                    if(((psVector*)entryChild->data.V)->data.U64[j] != testConfig1ValueVecValueU64[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %ld not as expected %ld",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.U64[j],
+                                testConfig1ValueVecValueU64[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S8:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS8);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS8) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS8);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS8; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S8[j] != testConfig1ValueVecValueS8[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S8[j],
+                                testConfig1ValueVecValueS8[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S16:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS16);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS16) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS16);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS16; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S16[j] != testConfig1ValueVecValueS16[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S16[j],
+                                testConfig1ValueVecValueS16[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S32:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS32);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS32);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS32; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S32[j] != testConfig1ValueVecValueS32[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %d not as expected %d",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S32[j],
+                                testConfig1ValueVecValueS32[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_S64:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsS64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsS64);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeS64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeS64);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsS64; j++) {
+                    if(((psVector*)entryChild->data.V)->data.S64[j] != testConfig1ValueVecValueS64[j]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %ld not as expected %ld",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.S64[j],
+                                testConfig1ValueVecValueS64[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_F32:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsF32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsF32);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeF32) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeF32);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsF32; j++) {
+                    if(fabs(((psVector*)entryChild->data.V)->data.F32[j]-testConfig1ValueVecValueF32[j])
+                            > ERROR_TOL) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %f not as expected %f",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.F32[j],
+                                testConfig1ValueVecValueF32[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            case PS_TYPE_F64:
+                if(((psVector*)(entryChild->data.V))->n != testConfig1ValueVecItemsF64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector entries %d not as expected %d",
+                            testConfig1,((psVector*)(entryChild->data.V))->n,testConfig1ValueVecItemsF64);
+                    return i*10+5;
+                }
+                // Verify the correct type
+                if(((psVector*)(entryChild->data.V))->type.type != testConfig1ValueVecTypeF64) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Vector type %d not as expected %s",
+                            testConfig1,((psVector*)entryChild->data.V)->type.type,testConfig1ValueVecTypeF64);
+                    return i*10+5;
+                }
+                // Verify the values in vector
+                for(psS32 j = 0; j < testConfig1ValueVecItemsF64; j++) {
+                    if(fabs(((psVector*)entryChild->data.V)->data.F64[j]-testConfig1ValueVecValueF64[j])
+                            > ERROR_TOL) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Vector data[%d] = %lf not as expected %lf",
+                                testConfig1,j,((psVector*)entryChild->data.V)->data.F64[j],
+                                testConfig1ValueVecValueF64[j]);
+                        return i*10+5*j;
+                    }
+                }
+                break;
+            default:
+                break;
+            }
+            break;
+        case PS_META_META:
+            if(metaCounter == 0) {
+                // Check if number of items is as expected
+                if(entryChild->data.md->list->size != testConfig1ValueMetaItems1) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 items %d not as expected %d",
+                            testConfig1,entryChild->data.md->list->size,testConfig1ValueMetaItems1);
+                    return i*10+6;
+                }
+                // Loop through metadata items
+                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
+                mdCounter = 0;
+                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
+                    if(strcmp(mdChild->name,testConfig1ValueMetaNames1[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 name[%d] %s not as expected %s",
+                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames1[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(mdChild->type != testConfig1ValueMetaTypes1[mdCounter]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 type[%d] %d not as expected %d",
+                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes1[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValue1[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 1 value[%d] %s not as expected %s",
+                                testConfig1,mdCounter,(char*)mdChild->data.V,testConfig1ValueMetaValue1[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    mdCounter++;
+                }
+                psFree(mdIter);
+            } else if(metaCounter == 1) {
+                // Check if number of items is as expected
+                if(entryChild->data.md->list->size != testConfig1ValueMetaItems2) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 items %d not as expected %d",
+                            testConfig1,entryChild->data.md->list->size,testConfig1ValueMetaItems2);
+                    return i*10+6;
+                }
+                // Loop through metadata items
+                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
+                mdCounter = 0;
+                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
+                    if(strcmp(mdChild->name,testConfig1ValueMetaNames2[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 name[%d] %s not as expected %s",
+                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames2[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(mdChild->type != testConfig1ValueMetaTypes2[mdCounter]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 type[%d] %d not as expected %d",
+                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes2[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValue2[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 2 value[%d] %s not as expected %s",
+                                testConfig1,mdCounter,(char*)mdChild->data.V,testConfig1ValueMetaValue2[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    mdCounter++;
+                }
+                psFree(mdIter);
+            } else if(metaCounter == 2) {
+                // Check if number of items is as expected
+                if(entryChild->data.md->list->size != testConfig1ValueMetaItems3) {
+                    psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 items %d not as expected %d",
+                            testConfig1,entryChild->data.md->list->size,testConfig1ValueMetaItems3);
+                    return i*10+6;
+                }
+                // Loop through metadata items
+                mdIter = psListIteratorAlloc(entryChild->data.md->list, PS_LIST_HEAD, true);
+                mdCounter = 0;
+                while ( (mdChild = psListGetAndIncrement(mdIter)) != NULL) {
+                    if(strcmp(mdChild->name,testConfig1ValueMetaNames3[mdCounter]) != 0) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 name[%d] %s not as expected %s",
+                                testConfig1,mdCounter,mdChild->name,testConfig1ValueMetaNames3[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    if(mdChild->type != testConfig1ValueMetaTypes3[mdCounter]) {
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 type[%d] %d not as expected %d",
+                                testConfig1,mdCounter,mdChild->type,testConfig1ValueMetaTypes3[mdCounter]);
+                        return i*10+6*mdCounter;
+                    }
+                    switch(mdChild->type) {
+                    case PS_META_STR:
+                        if(strcmp((char*)mdChild->data.V,testConfig1ValueMetaValueStr3[mdCounter]) != 0) {
+                            psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] %s not as expected %s",
+                                    testConfig1,mdCounter,(char*)mdChild->data.V,
+                                    testConfig1ValueMetaValueStr3[mdCounter]);
+                            return i*10+6*mdCounter;
+                        }
+                        break;
+                    case PS_META_S32:
+                        if(mdChild->data.S32 != testConfig1ValueMetaValueS323[mdCounter]) {
+                            psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] %d not as expected %d",
+                                    testConfig1,mdCounter,mdChild->data.S32,
+                                    testConfig1ValueMetaValueS323[mdCounter]);
+                            return i*10+6*mdCounter;
+                        }
+                        break;
+                    default:
+                        psError(PS_ERR_UNKNOWN,true,"File: %s : Metadata 3 value[%d] unknown type",
+                                testConfig1,mdCounter);
+                        return i*10+6*mdCounter;
+                        break;
+                    }
+                    mdCounter++;
+                }
+                psFree(mdIter);
+            }
+            metaCounter++;
+            break;
+        default:
+            psError(PS_ERR_UNKNOWN,true,"Unexpected type %d encountered",entryChild->type);
+            return i*10+5;
+            break;
+        }
+    }
+
+    writeMetadata(metadata1,"");
+
     psFree(metadata1);
-    psFree(metadata2);
-    psFree(metadata3);
-    if(psMemCheckLeaks(0, NULL, stdout,false)) {
-        psError(PS_ERR_UNKNOWN,true,"Memory leak detected");
-        return 10;
-    }
-    psMemCheckCorruption(0);
-    psS32 nBad = psMemCheckCorruption(0);
-    if(nBad) {
-        printf("ERROR: Found %d bad memory blocks\n", nBad);
-    }
-    printFooter(stdout, "psMetadata", "Test F - Free psMetadata", true);
-
 
     return 0;
 }
+
+psS32 testMetadataParseConfig1(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psS32             failedLines    = 0;
+
+    // Read config file test2.config with overwrite set true
+    // This file contains parse errors
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate parse error message");
+    metadata1 = psMetadataParseConfig(metadata1,&failedLines,testConfig2,true);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig2Fails,(char*)testConfig2)) {
+        return 1;
+    }
+    // Verify return value is null
+    if(metadata1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for failed parse");
+        return 2;
+    }
+
+    // Attempt parse a non-existant file
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message");
+    metadata1 = psMetadataParseConfig(metadata1,&failedLines,"ab.config",true);
+    if(metadata1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for non-existant file");
+        return 3;
+    }
+
+    // Attempt parse with NULL failed lines ptr
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL failed lines arg");
+    metadata1 = psMetadataParseConfig(metadata1,NULL,testConfig2,true);
+    if(metadata1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for NULL failed lines argument");
+        return 4;
+    }
+
+    // Attempt parse with NULL file name
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for NULL file name arg");
+    metadata1 = psMetadataParseConfig(metadata1,&failedLines,NULL,true);
+    if(metadata1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for NULL filename argument");
+        return 5;
+    }
+
+    psFree(metadata1);
+
+    return 0;
+}
+
+psS32 testMetadataParseConfig2(void)
+{
+    psMetadata*       metadata1      = NULL;
+    psS32             failedLines    = 0;
+
+    // Read config file test1.config with overwrite set false
+    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error of duplicate key names");
+    metadata1 = psMetadataParseConfig(metadata1,&failedLines,testConfig3,false);
+    // Verify the expected number of failed lines
+    if(!checkFailedLines(failedLines,testConfig3Fails,(char*)testConfig3)) {
+        return 1;
+    }
+    if(metadata1 != NULL) {
+        psError(PS_ERR_UNKNOWN,true,"Expected a NULL return for failed due to overwrite false");
+        return 2;
+    }
+    psFree(metadata1);
+
+    return 0;
+}
+
Index: /trunk/psLib/test/collections/verified/tst_psMetadataIO.stderr
===================================================================
--- /trunk/psLib/test/collections/verified/tst_psMetadataIO.stderr	(revision 3944)
+++ /trunk/psLib/test/collections/verified/tst_psMetadataIO.stderr	(revision 3945)
@@ -1,46 +1,85 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_psMetadataIO.c                                         *
+*            TestPoint: psMetadataParseConfig{psMetadataParseConfig}               *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<DATE><TIME>|<HOST>|W|psMetadataAddItem
+    Metadata item Float has been replaced
+
+---> TESTPOINT PASSED (psMetadataParseConfig{psMetadataParseConfig} | tst_psMetadataIO.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_psMetadataIO.c                                         *
+*            TestPoint: psMetadataParseConfig{psMetadataParseConfig}               *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<DATE><TIME>|<HOST>|I|testMetadataParseConfig1
+    Following should generate parse error message
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to parse the value 'X' of metadata item boolean, type BOOL, on line 2 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to read a metadata value on line 4 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to read a metadata value on line 7 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to parse the value 'x,y,z' of metadata item @vector1, type U8, on line 8 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to read a metadata type on line 9 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to read a metadata value on line 9 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to parse the value '' of metadata item @vector3, type F8, on line 10 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Duplicate MULTI specifier on line 14 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Metadata type 'CELL', found on line 17 of test2.config, is not invalid.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to parse the value 'aabb' of metadata item value1, type F64, on line 20 of test2.config.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to parse the value 'ccdd' of metadata item value2, type S32, on line 21 of test2.config.
+<DATE><TIME>|<HOST>|E|parseLine (FILE:LINENO)
+    More than one '@' character not allowed.  Found on line 24 of test2.config.
+<DATE><TIME>|<HOST>|E|parseLine (FILE:LINENO)
+    Failed to read a metadata type on line 27 of test2.config.
+<DATE><TIME>|<HOST>|E|parseLine (FILE:LINENO)
+    Specified type, CELL, on line 31 of test2.config is already defined.
+<DATE><TIME>|<HOST>|E|psMetadataAddItem (FILE:LINENO)
+    Duplicate metadata item name is not allowed.  Use a psMetadataFlags option to allow such action.
+<DATE><TIME>|<HOST>|E|parseLine (FILE:LINENO)
+    Metadata type 'NEWCELL', found on line 37 of test2.config, is not invalid.
+<DATE><TIME>|<HOST>|E|parseMetadataItem (FILE:LINENO)
+    Failed to read a metadata OURCELL on line 40 of test2.config.
+<DATE><TIME>|<HOST>|E|parseLine (FILE:LINENO)
+    Metadata type 'NEWCELL1', found on line 43 of test2.config, is not invalid.
+<DATE><TIME>|<HOST>|I|testMetadataParseConfig1
+    Following should generate error message
 <DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Failed to parse the value '9876.54qqq32' of metadata item xPosition, type F64, on line 89 of test.config.
+    Failed to open file 'ab.config'. Check if it exists and it has the proper permissions.
+<DATE><TIME>|<HOST>|I|testMetadataParseConfig1
+    Following should generate an error message for NULL failed lines arg
 <DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Failed to read a metadata value on line 95 of test.config.
+    Unallowable operation: nFail is NULL.
+<DATE><TIME>|<HOST>|I|testMetadataParseConfig1
+    Following should generate an error message for NULL file name arg
 <DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Metadata type '99.999', found on line 101 of test.config, is not invalid.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '*' character not allowed.  Found on line 107 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '@' character not allowed.  Found on line 113 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '~' character not allowed.  Found on line 119 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Failed to parse the value '9876.54qqq32' of metadata item xPosition, type F64, on line 89 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Failed to read a metadata value on line 95 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Metadata type '99.999', found on line 101 of test.config, is not invalid.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '*' character not allowed.  Found on line 107 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '@' character not allowed.  Found on line 113 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '~' character not allowed.  Found on line 119 of test.config.
+    Unallowable operation: fileName is NULL.
+
+---> TESTPOINT PASSED (psMetadataParseConfig{psMetadataParseConfig} | tst_psMetadataIO.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_psMetadataIO.c                                         *
+*            TestPoint: psMetadataParseConfig{psMetadataParseConfig}               *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<DATE><TIME>|<HOST>|I|testMetadataParseConfig2
+    Following should generate an error of duplicate key names
 <DATE><TIME>|<HOST>|E|psMetadataAddItem (FILE:LINENO)
     Duplicate metadata item name is not allowed.  Use a psMetadataFlags option to allow such action.
 <DATE><TIME>|<HOST>|E|psMetadataAddV (FILE:LINENO)
     Failed to add metadata item to metadata collection list.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Duplicate Metadata item, speed, found on line 127 of test.config.  Overwrite not allowed.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Failed to parse the value '9876.54qqq32' of metadata item xPosition, type F64, on line 89 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Failed to read a metadata value on line 95 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Metadata type '99.999', found on line 101 of test.config, is not invalid.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '*' character not allowed.  Found on line 107 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '@' character not allowed.  Found on line 113 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    More than one '~' character not allowed.  Found on line 119 of test.config.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Unallowable operation: fileName is NULL.
-<DATE><TIME>|<HOST>|E|psMetadataParseConfig (FILE:LINENO)
-    Failed to open file 'abcedfg'. Check if it exists and it has the proper permissions.
+
+---> TESTPOINT PASSED (psMetadataParseConfig{psMetadataParseConfig} | tst_psMetadataIO.c)
+
Index: /trunk/psLib/test/collections/verified/tst_psMetadataIO.stdout
===================================================================
--- /trunk/psLib/test/collections/verified/tst_psMetadataIO.stdout	(revision 3944)
+++ /trunk/psLib/test/collections/verified/tst_psMetadataIO.stdout	(revision 3945)
@@ -1,173 +1,49 @@
-/***************************** TESTPOINT ******************************************\
-*             TestFile: tst_psMetadataIO.c                                         *
-*            TestPoint: psMetadata{Test A - Read config file with overwrite set true} *
-*             TestType: Positive                                                   *
-\**********************************************************************************/
+Double                    F64                                   1.234568  # This is a comment   
+String                    STR    This is the string that forms the value  # comment             
+boolean                   BOOL                                         1  # The value of 'boolean' is 'true'
+@primes                   U8     2,3,5,7,11,13,17  # These are prime numbers
+comment                   MULTI
+comment                   STR                                       This
+comment                   STR                                         is
+comment                   STR                                          a
+comment                   STR                                 non-unique
+comment                   STR                                        key
+Float                     F64                                   1.234560  # This generates a warning, and, if 'overwrite' is 'false', is ignored
+boolean1                  BOOL                                         0  # The value of 'boolean' is 'false'
+@negprimes                S8     -2,-3,-5,-7,-11,-13,-17,-19
+@vector1                  U16    0,1,2,4,8
+@vector2                  U32    0,8,16,32,64,128
+@vector3                  U64    0,64,256
+@vector4                  S16    -2,-1,0,1,2
+@vector5                  S32    -4,-2,0,2,4,6
+@vector6                  S64    -16,-4,0,4,16,36,64
+@vector7                  F32    -1.03,1.04,-1.05,1.06
+@vector8                  F64    -2.22,2.21,-2.2,2.19,-2.18
+CELL.00                   METADATA
+    EXTNAME                   STR                                      CCD00
+    BIASSEC                   STR                                    BSEC-00
+    CHIP                      STR                                    CHIP.00
 
-Failed lines: 6 Expected: 6
-Contents of metadata list:
- Key Name:       pi  Key mdType: 0x00000408 Key Value:           3.142  Key Comment: Definition of pi
- Key Name: altitude  Key mdType: 0x00000104 Key Value:           10000  Key Comment: 
- Key Name:     time  Key mdType: 0x00000404 Key Value:        1234.568  Key Comment: 
- Key Name:   myBool  Key mdType: 0x00001301 Key Value:               0  Key Comment: F, f, 0, T, t, 1 are also acceptable
- Key Name:   title1  Key mdType: 0x00010001 Key Value:     Hello world  Key Comment: This is a comment for the string value
- Key Name:   title2  Key mdType: 0x00010001 Key Value:  Good bye world  Key Comment: STRING or STR may be used as the string type
- Key Name:   primes  Key mdType: 0x00010002 Key Value:   [2,3,5,7,...]  Key Comment: These are prime numbers
- Key Name: negatives  Key mdType: 0x00010002 Key Value:  [-1,-2,-3,...]  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:            This  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:              is  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:              an  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:            ugly  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:         comment  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:             but  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:           still  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:           valid  Key Comment: 
- Key Name:    speed  Key mdType: 0x00000404 Key Value:          66.660  Key Comment: 
+CELL.01                   METADATA
+    EXTNAME                   STR                                      CCD01
+    BIASSEC                   STR                                    BSEC-01
+    CHIP                      STR                                    CHIP.00
 
-Contents of metadata table:
- Key Name:  comment  Key mdType: 0x0001000a Key Value:                  Key Comment: List of Metadata Items
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:            This  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:              is  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:              an  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:            ugly  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:         comment  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:             but  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:           still  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:           valid  Key Comment: 
- Key Name:   title2  Key mdType: 0x00010001 Key Value:  Good bye world  Key Comment: STRING or STR may be used as the string type
- Key Name:     time  Key mdType: 0x00000404 Key Value:        1234.568  Key Comment: 
- Key Name: negatives  Key mdType: 0x00010002 Key Value:  [-1,-2,-3,...]  Key Comment: 
- Key Name:       pi  Key mdType: 0x00000408 Key Value:           3.142  Key Comment: Definition of pi
- Key Name:   myBool  Key mdType: 0x00001301 Key Value:               0  Key Comment: F, f, 0, T, t, 1 are also acceptable
- Key Name:   primes  Key mdType: 0x00010002 Key Value:   [2,3,5,7,...]  Key Comment: These are prime numbers
- Key Name: altitude  Key mdType: 0x00000104 Key Value:           10000  Key Comment: 
- Key Name:    speed  Key mdType: 0x00000404 Key Value:          66.660  Key Comment: 
- Key Name:   title1  Key mdType: 0x00010001 Key Value:     Hello world  Key Comment: This is a comment for the string value
+MYCELL                    MULTI
+MYCELL                    METADATA
+    EXTNAME                   STR                                      CCD00
+    BIASSEC                   STR                                    BSEC-00
+    CHIP                      STR                                    CHIP.00
+    NCELL                     S32                                         24
 
----> TESTPOINT PASSED (psMetadata{Test A - Read config file with overwrite set true} | tst_psMetadataIO.c)
+MYCELL                    S32                                        123  # A number            
+cell                      METADATA
+    foo                       METADATA
+    bar                       STR                                        BAZ
+    ping                      STR                                       PONG
 
-/***************************** TESTPOINT ******************************************\
-*             TestFile: tst_psMetadataIO.c                                         *
-*            TestPoint: psMetadata{Test B - Read config file with overwrite set false} *
-*             TestType: Positive                                                   *
-\**********************************************************************************/
+    EXTNAME                   STR                                      CCD00
+    BIASSEC                   STR                                    BSEC-00
+    CHIP                      STR                                    CHIP.00
+    NCELL                     S32                                         12
 
-Failed lines: 7 Expected: 7 Contents of metadata list:
- Key Name:       pi  Key mdType: 0x00000408 Key Value:           3.142  Key Comment: Definition of pi
- Key Name: altitude  Key mdType: 0x00000104 Key Value:           10000  Key Comment: 
- Key Name:     time  Key mdType: 0x00000404 Key Value:        1234.568  Key Comment: 
- Key Name:   myBool  Key mdType: 0x00001301 Key Value:               0  Key Comment: F, f, 0, T, t, 1 are also acceptable
- Key Name:   title1  Key mdType: 0x00010001 Key Value:     Hello world  Key Comment: This is a comment for the string value
- Key Name:   title2  Key mdType: 0x00010001 Key Value:  Good bye world  Key Comment: STRING or STR may be used as the string type
- Key Name:   primes  Key mdType: 0x00010002 Key Value:   [2,3,5,7,...]  Key Comment: These are prime numbers
- Key Name: negatives  Key mdType: 0x00010002 Key Value:  [-1,-2,-3,...]  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:            This  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:              is  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:              an  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:            ugly  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:         comment  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:             but  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:           still  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:           valid  Key Comment: 
- Key Name:    speed  Key mdType: 0x00000404 Key Value:          55.550  Key Comment: 
-
-Contents of metadata table:
- Key Name:  comment  Key mdType: 0x0001000a Key Value:                  Key Comment: List of Metadata Items
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:            This  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:              is  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:              an  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:            ugly  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:         comment  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:             but  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:           still  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:           valid  Key Comment: 
- Key Name:   title2  Key mdType: 0x00010001 Key Value:  Good bye world  Key Comment: STRING or STR may be used as the string type
- Key Name:     time  Key mdType: 0x00000404 Key Value:        1234.568  Key Comment: 
- Key Name: negatives  Key mdType: 0x00010002 Key Value:  [-1,-2,-3,...]  Key Comment: 
- Key Name:       pi  Key mdType: 0x00000408 Key Value:           3.142  Key Comment: Definition of pi
- Key Name:   myBool  Key mdType: 0x00001301 Key Value:               0  Key Comment: F, f, 0, T, t, 1 are also acceptable
- Key Name:   primes  Key mdType: 0x00010002 Key Value:   [2,3,5,7,...]  Key Comment: These are prime numbers
- Key Name: altitude  Key mdType: 0x00000104 Key Value:           10000  Key Comment: 
- Key Name:    speed  Key mdType: 0x00000404 Key Value:          55.550  Key Comment: 
- Key Name:   title1  Key mdType: 0x00010001 Key Value:     Hello world  Key Comment: This is a comment for the string value
-
----> TESTPOINT PASSED (psMetadata{Test B - Read config file with overwrite set false} | tst_psMetadataIO.c)
-
-/***************************** TESTPOINT ******************************************\
-*             TestFile: tst_psMetadataIO.c                                         *
-*            TestPoint: psMetadata{Test C - Read config file without auto-allocation of metadata} *
-*             TestType: Positive                                                   *
-\**********************************************************************************/
-
-Failed lines: 6 Expected: 6 Contents of metadata list:
- Key Name:       pi  Key mdType: 0x00000408 Key Value:           3.142  Key Comment: Definition of pi
- Key Name: altitude  Key mdType: 0x00000104 Key Value:           10000  Key Comment: 
- Key Name:     time  Key mdType: 0x00000404 Key Value:        1234.568  Key Comment: 
- Key Name:   myBool  Key mdType: 0x00001301 Key Value:               0  Key Comment: F, f, 0, T, t, 1 are also acceptable
- Key Name:   title1  Key mdType: 0x00010001 Key Value:     Hello world  Key Comment: This is a comment for the string value
- Key Name:   title2  Key mdType: 0x00010001 Key Value:  Good bye world  Key Comment: STRING or STR may be used as the string type
- Key Name:   primes  Key mdType: 0x00010002 Key Value:   [2,3,5,7,...]  Key Comment: These are prime numbers
- Key Name: negatives  Key mdType: 0x00010002 Key Value:  [-1,-2,-3,...]  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:            This  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:              is  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:              an  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:            ugly  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:         comment  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:             but  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:           still  Key Comment: 
- Key Name:  comment  Key mdType: 0x00010001 Key Value:           valid  Key Comment: 
- Key Name:    speed  Key mdType: 0x00000404 Key Value:          66.660  Key Comment: 
-
-Contents of metadata table:
- Key Name:  comment  Key mdType: 0x0001000a Key Value:                  Key Comment: List of Metadata Items
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:            This  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:              is  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:              an  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:            ugly  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:         comment  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:             but  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:           still  Key Comment: 
-    Key Name:  comment  Key mdType: 0x00010001 Key Value:           valid  Key Comment: 
- Key Name:   title2  Key mdType: 0x00010001 Key Value:  Good bye world  Key Comment: STRING or STR may be used as the string type
- Key Name:     time  Key mdType: 0x00000404 Key Value:        1234.568  Key Comment: 
- Key Name: negatives  Key mdType: 0x00010002 Key Value:  [-1,-2,-3,...]  Key Comment: 
- Key Name:       pi  Key mdType: 0x00000408 Key Value:           3.142  Key Comment: Definition of pi
- Key Name:   myBool  Key mdType: 0x00001301 Key Value:               0  Key Comment: F, f, 0, T, t, 1 are also acceptable
- Key Name:   primes  Key mdType: 0x00010002 Key Value:   [2,3,5,7,...]  Key Comment: These are prime numbers
- Key Name: altitude  Key mdType: 0x00000104 Key Value:           10000  Key Comment: 
- Key Name:    speed  Key mdType: 0x00000404 Key Value:          66.660  Key Comment: 
- Key Name:   title1  Key mdType: 0x00010001 Key Value:     Hello world  Key Comment: This is a comment for the string value
-
----> TESTPOINT PASSED (psMetadata{Test C - Read config file without auto-allocation of metadata} | tst_psMetadataIO.c)
-
-/***************************** TESTPOINT ******************************************\
-*             TestFile: tst_psMetadataIO.c                                         *
-*            TestPoint: psMetadataIO{Test D - Attempt to use null fileName argument} *
-*             TestType: Negative                                                   *
-*    ExpectedErrorText: Null failedLines not allowed                               *
-*  ExpectedStatusValue: 0                                                          *
-\**********************************************************************************/
-
-
----> TESTPOINT PASSED (psMetadata{Test D - Attempt to use null fileName argument} | tst_psMetadataIO.c)
-
-/***************************** TESTPOINT ******************************************\
-*             TestFile: tst_psMetadataIO.c                                         *
-*            TestPoint: psMetadataIO{Test E - Attempt to open nonexistant file}    *
-*             TestType: Negative                                                   *
-*    ExpectedErrorText: Error opening file                                         *
-*  ExpectedStatusValue: 0                                                          *
-\**********************************************************************************/
-
-
----> TESTPOINT PASSED (psMetadata{Test E - Attempt to open nonexistant file} | tst_psMetadataIO.c)
-
-/***************************** TESTPOINT ******************************************\
-*             TestFile: tst_psMetadataIO.c                                         *
-*            TestPoint: psMetadata{Test F - Free psMetadata}                       *
-*             TestType: Positive                                                   *
-\**********************************************************************************/
-
-
----> TESTPOINT PASSED (psMetadata{Test F - Free psMetadata} | tst_psMetadataIO.c)
-
Index: /trunk/psLib/test/collections/verified/tst_psMetadata_01.stdout
===================================================================
--- /trunk/psLib/test/collections/verified/tst_psMetadata_01.stdout	(revision 3944)
+++ /trunk/psLib/test/collections/verified/tst_psMetadata_01.stdout	(revision 3945)
@@ -44,9 +44,9 @@
  Key Name:   PCOUNT  Key mdType: 0x00000104  Key Value:               0  Key Comment: required keyword; must = 0
  Key Name: XTENSION  Key mdType: 0x00010001  Key Value:      'IMAGE   '  Key Comment: IMAGE extension
- Key Name:   BITPIX  Key mdType: 0x0001000a  Key Value:                  Key Comment: List of Metadata Items
+ Key Name:   BITPIX  Key mdType: 0x0001000a  Key Value:                  Key Comment: 
     Key Name:   BITPIX  Key mdType: 0x00000104  Key Value:             -64  Key Comment: number of bits per data pixel
     Key Name:   BITPIX  Key mdType: 0x00000104  Key Value:             -64  Key Comment: number of bits per data pixel
  Key Name:   GCOUNT  Key mdType: 0x00000104  Key Value:               1  Key Comment: required keyword; must = 1
- Key Name:  HISTORY  Key mdType: 0x0001000a  Key Value:                  Key Comment: List of Metadata Items
+ Key Name:  HISTORY  Key mdType: 0x0001000a  Key Value:                  Key Comment: 
     Key Name:  HISTORY  Key mdType: 0x00010001  Key Value:                  Key Comment: File modified by user 'harman' with fv  on 2004-08-04T<DATE>
     Key Name:  HISTORY  Key mdType: 0x00010001  Key Value:                  Key Comment: File modified by user 'harman' with fv  on 2004-08-04T<DATE>
@@ -81,5 +81,5 @@
  Key Name:   GCOUNT  Key mdType: 0x00000104  Key Value:               1  Key Comment: required keyword; must = 1
  Key Name:   BITPIX  Key mdType: 0x00000104  Key Value:             -64  Key Comment: number of bits per data pixel
- Key Name:  HISTORY  Key mdType: 0x0001000a  Key Value:                  Key Comment: List of Metadata Items
+ Key Name:  HISTORY  Key mdType: 0x0001000a  Key Value:                  Key Comment: 
     Key Name:  HISTORY  Key mdType: 0x00010001  Key Value:                  Key Comment: File modified by user 'harman' with fv  on 2004-08-04T<DATE>
     Key Name:  HISTORY  Key mdType: 0x00010001  Key Value:                  Key Comment: File modified by user 'harman' with fv  on 2004-08-04T<DATE>
