Index: trunk/psLib/src/xml/psXML.c
===================================================================
--- trunk/psLib/src/xml/psXML.c	(revision 3769)
+++ 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
