Index: /trunk/psLib/src/types/psMetadataConfig.c
===================================================================
--- /trunk/psLib/src/types/psMetadataConfig.c	(revision 7831)
+++ /trunk/psLib/src/types/psMetadataConfig.c	(revision 7832)
@@ -10,6 +10,6 @@
 *  @author Eric Van Alst, MHPCC
 *
-*  @version $Revision: 1.62 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-06-30 02:20:06 $
+*  @version $Revision: 1.63 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-07-07 02:25:54 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -90,4 +90,6 @@
 static psBool parseMetadataItem(char* keyName, psS32* level, psArray* levelArray, char* linePtr,
                                 psS32 lineCount, char* fileName, psMetadataFlags flags);
+static psString formatMetadataItem(psMetadataItem *item);
+static psArray *p_psMetadataKeyArray(psMetadata *md);
 static void parseLevelInfoFree(p_psParseLevelInfo* info)
 {
@@ -1486,261 +1488,324 @@
     PS_ASSERT_PTR_NON_NULL(md, NULL);
 
-    psString mdString = NULL;
-    psString content = NULL;
-
-    psMetadataItem *item = NULL;
-    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
-
-    while ( (item = psMetadataGetAndIncrement(iter)) ) {
-        if (item == NULL) {
+    psString format = NULL;
+
+    psArray *keys = p_psMetadataKeyArray(md);
+    for (long i = 0; i < psArrayLength(keys); i++) {
+        psMetadataItem *item = psMetadataLookup(md, keys->data[i]);
+        if (!item) {
             // XXX : this is probably not the right error value
             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
                     PS_ERRORTEXT_psMetadata_METATYPE_INVALID, PS_DATA_UNKNOWN);
-            psFree(iter);
+            psFree(keys);
+            psFree(format);
             return NULL;
         }
-
-        // In this block, the single item is used to build 'content'
-        switch (item->type) {
-        case PS_DATA_BOOL:
-            psStringAppend (&content, "%s BOOL  ", item->name);
-            if ( item->data.B ) {
-                psStringAppend(&content, "TRUE");
+        psString str = formatMetadataItem(item);
+        if (!str) {
+            psError(PS_ERR_UNKNOWN, false, "failed to format psMetadataItem");
+            psFree(keys);
+            psFree(format);
+            return NULL;
+        }
+        psStringAppend(&format, "%s", str);
+        psFree(str);
+    }
+
+    psFree(keys);
+
+    return format;
+}
+
+
+static psString formatMetadataItem(psMetadataItem *item)
+{
+    PS_ASSERT_PTR_NON_NULL(item, NULL);
+
+    psString content = NULL;
+
+    // In this block, the single item is used to build 'content'
+    switch (item->type) {
+    case PS_DATA_METADATA_MULTI: {
+            if (item->comment) {
+                psStringAppend(&content, "%s MULTI # %s\n", item->name, item->comment);
             } else {
-                psStringAppend(&content, "FALSE");
-            }
-            if ( item->comment != NULL ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n", item->comment);
+                psStringAppend(&content, "%s MULTI\n", item->name);
+            }
+
+            // a MULTI is a list of items so we need to recurse through the
+            // list
+            psListIterator *iter = psListIteratorAlloc(item->data.list, 0, false);
+            psMetadataItem *multiItem = NULL;
+            while ((multiItem = psListGetAndIncrement(iter))) {
+                psString str = formatMetadataItem(multiItem);
+                psStringAppend(&content, str);
+                psFree(str);
+            }
+        }
+        break;
+    case PS_DATA_BOOL:
+        psStringAppend (&content, "%s BOOL  ", item->name);
+        if ( item->data.B ) {
+            psStringAppend(&content, "TRUE");
+        } else {
+            psStringAppend(&content, "FALSE");
+        }
+        if ( item->comment != NULL ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n", item->comment);
+        break;
+    case PS_DATA_S8:
+        psStringAppend(&content, "%s S8  ", item->name);
+        psStringAppend(&content, "%d ", item->data.S8);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_S16:
+        psStringAppend(&content, "%s S16  ", item->name);
+        psStringAppend(&content, "%d ", item->data.S16);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_S32:
+        psStringAppend(&content, "%s S32  ", item->name);
+        psStringAppend(&content, "%d ", item->data.S32);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_S64:
+        psStringAppend(&content, "%s S64  ", item->name);
+        psStringAppend(&content, "%ld ", item->data.S64);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_U8:
+        psStringAppend(&content, "%s U8  ", item->name);
+        psStringAppend(&content, "%u ", item->data.U8);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_U16:
+        psStringAppend(&content, "%s U16  ", item->name);
+        psStringAppend(&content, "%u ", item->data.U16);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_U32:
+        psStringAppend(&content, "%s U32  ", item->name);
+        psStringAppend(&content, "%u ", item->data.U32);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_U64:
+        psStringAppend(&content, "%s U64  ", item->name);
+        psStringAppend(&content, "%lu ", item->data.U64);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_F32:
+        psStringAppend(&content, "%s F32  ", item->name);
+        psStringAppend(&content, "%.7g ", item->data.F32);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_F64:
+        psStringAppend(&content, "%s F64  ", item->name);
+        psStringAppend(&content, "%.15g ", item->data.F64);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_STRING:
+        psStringAppend(&content, "%s STR  ", item->name);
+        psStringAppend(&content, "%s ", ((char *)(item->data.V)));
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_METADATA:
+        psStringAppend(&content, "\n%s  METADATA  ", item->name);
+        psString newStr = psMetadataConfigFormat(item->data.md);
+        // XXX need to check for NULL return value, implying error
+        //     from a lower level
+
+        // add 3 extra spaces to each metadata folder item
+        char *buffer2 = strtok(newStr, "\n");
+        while( buffer2 != NULL ) {
+            psStringAppend(&content, "\n   %s", buffer2);
+            buffer2 = strtok(NULL, "\n");
+        }
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, "\nEND   #%s\n\n", item->comment);
+        } else {
+            psStringAppend(&content, "\nEND\n\n");
+        }
+        psFree(newStr);
+        break;
+    case PS_DATA_TIME:
+        psStringAppend(&content, "%s ", item->name);
+        psTime *time = item->data.V;
+        if ( time->type == PS_TIME_UTC )
+            psStringAppend(&content, "PS_TIME_UTC  ");
+        else if ( time->type == PS_TIME_TAI )
+            psStringAppend(&content, "PS_TIME_TAI  ");
+        else if ( time->type == PS_TIME_UT1 )
+            psStringAppend(&content, "PS_TIME_UT1  ");
+        else if ( time->type == PS_TIME_TT )
+            psStringAppend(&content, "PS_TIME_TT  ");
+        else {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_METATYPE_INVALID, time->type);
+            psFree(content);
+            return NULL;
+        }
+
+        psStringAppend(&content, "%ld, ", (long)time->sec);
+        psStringAppend(&content, "%u, ", time->nsec);
+        if ( time->leapsecond )
+            psStringAppend(&content, "T ");
+        else
+            psStringAppend(&content, "F ");
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_VECTOR:
+        psStringAppend(&content, "@%s ", item->name);
+        psVector *vector = item->data.V;
+
+        switch (vector->type.type) {
+        case PS_DATA_U8:
+            psStringAppend(&content, "U8 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%u ", vector->data.U8[i]);
+            }
+            break;
+        case PS_DATA_U16:
+            psStringAppend(&content, "U16 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%u ", vector->data.U16[i]);
+            }
+            break;
+        case PS_DATA_U32:
+            psStringAppend(&content, "U32 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%u ", vector->data.U32[i]);
+            }
+            break;
+        case PS_DATA_U64:
+            psStringAppend(&content, "U64 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%lu ", vector->data.U64[i]);
+            }
             break;
         case PS_DATA_S8:
-            psStringAppend(&content, "%s S8  ", item->name);
-            psStringAppend(&content, "%d ", item->data.S8);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
+            psStringAppend(&content, "S8 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%d ", vector->data.S8[i]);
+            }
             break;
         case PS_DATA_S16:
-            psStringAppend(&content, "%s S16  ", item->name);
-            psStringAppend(&content, "%d ", item->data.S16);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
+            psStringAppend(&content, "S16 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%d ", vector->data.S16[i]);
+            }
             break;
         case PS_DATA_S32:
-            psStringAppend(&content, "%s S32  ", item->name);
-            psStringAppend(&content, "%d ", item->data.S32);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
+            psStringAppend(&content, "S32 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%d ", vector->data.S32[i]);
+            }
             break;
         case PS_DATA_S64:
-            psStringAppend(&content, "%s S64  ", item->name);
-            psStringAppend(&content, "%ld ", item->data.S64);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
-            break;
-        case PS_DATA_U8:
-            psStringAppend(&content, "%s U8  ", item->name);
-            psStringAppend(&content, "%u ", item->data.U8);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
-            break;
-        case PS_DATA_U16:
-            psStringAppend(&content, "%s U16  ", item->name);
-            psStringAppend(&content, "%u ", item->data.U16);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
-            break;
-        case PS_DATA_U32:
-            psStringAppend(&content, "%s U32  ", item->name);
-            psStringAppend(&content, "%u ", item->data.U32);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
-            break;
-        case PS_DATA_U64:
-            psStringAppend(&content, "%s U64  ", item->name);
-            psStringAppend(&content, "%lu ", item->data.U64);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
+            psStringAppend(&content, "S64 ");
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%ld ", vector->data.S64[i]);
+            }
             break;
         case PS_DATA_F32:
-            psStringAppend(&content, "%s F32  ", item->name);
-            psStringAppend(&content, "%.7g ", item->data.F32);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
+            psStringAppend(&content, "F32 ", MAXSTR);
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%.7g ", vector->data.F32[i]);
+            }
             break;
         case PS_DATA_F64:
-            psStringAppend(&content, "%s F64  ", item->name);
-            psStringAppend(&content, "%.15g ", item->data.F64);
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
-            break;
-        case PS_DATA_STRING:
-            psStringAppend(&content, "%s STR  ", item->name);
-            psStringAppend(&content, "%s ", ((char *)(item->data.V)));
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
-            break;
-        case PS_DATA_METADATA:
-            psStringAppend(&content, "\n%s  METADATA  ", item->name);
-            psString newStr = psMetadataConfigFormat(item->data.md);
-            // XXX need to check for NULL return value, implying error
-            //     from a lower level
-
-            // add 3 extra spaces to each metadata folder item
-            char *buffer2 = strtok(newStr, "\n");
-            while( buffer2 != NULL ) {
-                psStringAppend(&content, "\n   %s", buffer2);
-                buffer2 = strtok(NULL, "\n");
-            }
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, "\nEND   #%s\n\n", item->comment);
-            } else {
-                psStringAppend(&content, "\nEND\n\n");
-            }
-            psFree(newStr);
-            break;
-        case PS_DATA_TIME:
-            psStringAppend(&content, "%s ", item->name);
-            psTime *time = item->data.V;
-            if ( time->type == PS_TIME_UTC )
-                psStringAppend(&content, "PS_TIME_UTC  ");
-            else if ( time->type == PS_TIME_TAI )
-                psStringAppend(&content, "PS_TIME_TAI  ");
-            else if ( time->type == PS_TIME_UT1 )
-                psStringAppend(&content, "PS_TIME_UT1  ");
-            else if ( time->type == PS_TIME_TT )
-                psStringAppend(&content, "PS_TIME_TT  ");
-            else {
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true, PS_ERRORTEXT_psMetadata_METATYPE_INVALID, time->type);
-                // XXX memory leak: should free content and mdString before returning
-                psFree(iter);
-                return NULL;
-            }
-
-            psStringAppend(&content, "%ld, ", (long)time->sec);
-            psStringAppend(&content, "%u, ", time->nsec);
-            if ( time->leapsecond )
-                psStringAppend(&content, "T ");
-            else
-                psStringAppend(&content, "F ");
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
-            break;
-        case PS_DATA_VECTOR:
-            psStringAppend(&content, "@%s ", item->name);
-            psVector *vector = item->data.V;
-
-            switch (vector->type.type) {
-            case PS_DATA_U8:
-                psStringAppend(&content, "U8 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%u ", vector->data.U8[i]);
-                }
-                break;
-            case PS_DATA_U16:
-                psStringAppend(&content, "U16 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%u ", vector->data.U16[i]);
-                }
-                break;
-            case PS_DATA_U32:
-                psStringAppend(&content, "U32 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%u ", vector->data.U32[i]);
-                }
-                break;
-            case PS_DATA_U64:
-                psStringAppend(&content, "U64 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%lu ", vector->data.U64[i]);
-                }
-                break;
-            case PS_DATA_S8:
-                psStringAppend(&content, "S8 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%d ", vector->data.S8[i]);
-                }
-                break;
-            case PS_DATA_S16:
-                psStringAppend(&content, "S16 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%d ", vector->data.S16[i]);
-                }
-                break;
-            case PS_DATA_S32:
-                psStringAppend(&content, "S32 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%d ", vector->data.S32[i]);
-                }
-                break;
-            case PS_DATA_S64:
-                psStringAppend(&content, "S64 ");
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%ld ", vector->data.S64[i]);
-                }
-                break;
-            case PS_DATA_F32:
-                psStringAppend(&content, "F32 ", MAXSTR);
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%.7g ", vector->data.F32[i]);
-                }
-                break;
-            case PS_DATA_F64:
-                psStringAppend(&content, "F64 ", MAXSTR);
-                for (int i = 0; i < vector->n; i++) {
-                    psStringAppend(&content, "%.15g", vector->data.F64[i]);
-                }
-                break;
-            case PS_DATA_UNKNOWN:
-            default:
-                psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                        PS_ERRORTEXT_psMetadata_METATYPE_INVALID, vector->type.type);
-                psFree(iter);
-                // XXX memory leak: should free content and mdString before returning
-                return NULL;
-            }
-            if ( strncmp(item->comment,"",2) ) {
-                psStringAppend(&content, " #%s", item->comment);
-            }
-            psStringAppend(&content, " \n");
+            psStringAppend(&content, "F64 ", MAXSTR);
+            for (int i = 0; i < vector->n; i++) {
+                psStringAppend(&content, "%.15g", vector->data.F64[i]);
+            }
             break;
         case PS_DATA_UNKNOWN:
         default:
             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
-                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, item->type);
-            psFree(iter);
-            // XXX memory leak: should free content and mdString before returning
+                    PS_ERRORTEXT_psMetadata_METATYPE_INVALID, vector->type.type);
+            psFree(content);
             return NULL;
         }
-        psStringAppend(&mdString, content);
+        if ( strncmp(item->comment,"",2) ) {
+            psStringAppend(&content, " #%s", item->comment);
+        }
+        psStringAppend(&content, " \n");
+        break;
+    case PS_DATA_UNKNOWN:
+    default:
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psMetadata_METATYPE_INVALID, item->type);
         psFree(content);
-        content = NULL;
-    }
-    psFree(iter);
-    return mdString;
+        return NULL;
+    }
+
+    return content;
+}
+
+
+static psArray *p_psMetadataKeyArray(psMetadata *md)
+{
+    PS_ASSERT_PTR_NON_NULL(md, NULL);
+
+    psArray *keys = psArrayAlloc(psListLength(md->list));
+
+    // since we want to preserve the order of the keys in the metadata we can't
+    // get a list of key names from the metadata's hash table.  Instead we have
+    // to iterate through the metadata's list of items and reject duplicate key
+    // names.
+
+    psMetadataItem *item = NULL;
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL);
+OUTSIDE:
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        for (long i = 0; i < psArrayLength(keys); i++) {
+            // does this element have a value
+            psString elem = keys->data[i];
+            if (elem) {
+                if(strcmp(elem, item->name) == 0) {
+                    goto OUTSIDE;
+                }
+            }
+        }
+        psArrayAdd(keys, 0, psStringCopy(item->name));
+    }
+
+    return keys;
 }
 
