Index: /trunk/psLib/src/types/psArguments.c
===================================================================
--- /trunk/psLib/src/types/psArguments.c	(revision 7200)
+++ /trunk/psLib/src/types/psArguments.c	(revision 7201)
@@ -7,6 +7,6 @@
  *  @author David Robbins, MHPCC
  *
- *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-05-23 23:41:42 $
+ *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-05-25 03:51:33 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -104,48 +104,43 @@
 
 
-static psMetadataItem *argumentRead(psMetadataItem *item, // Item to read into
-                                    int argnum, // Argument number
-                                    int *argc, // Number of arguments in total
-                                    char **argv) // The arguments
-
-{
-    psMetadataItem *newItem = NULL;
+#define ARG_READ_CASE(TYPE,FUNC) \
+case PS_TYPE_##TYPE: \
+item->data.TYPE = FUNC(argv[argnum]); \
+return psArgumentRemove(argnum, argc, argv);
+
+static bool argumentRead(psMetadataItem *item, // Item to read into
+                         int argnum, // Argument number
+                         int *argc, // Number of arguments in total
+                         char **argv) // The arguments
+
+{
     switch(item->type)
     {
-        // Only doing a representative set of types
-    case PS_DATA_S32:
-        newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atoi(argv[argnum]));
-        psArgumentRemove(argnum, argc, argv);
-        break;
-    case PS_DATA_F32:
-        newItem = psMetadataItemAlloc(item->name, item->type, item->comment, atof(argv[argnum]));
-        psArgumentRemove(argnum, argc, argv);
-        break;
+        ARG_READ_CASE(U8,atoi);
+        ARG_READ_CASE(U16,atoi);
+        ARG_READ_CASE(U32,atoi);
+        ARG_READ_CASE(S8,atoi);
+        ARG_READ_CASE(S16,atoi);
+        ARG_READ_CASE(S32,atoi);
+        ARG_READ_CASE(F32,atof);
+        ARG_READ_CASE(F64,atof);
     case PS_DATA_BOOL:
         // Turn option on; no optional argument to remove
-        newItem = psMetadataItemAlloc(item->name, item->type, item->comment, true);
-        break;
-        // XXX: Include the other numerical types
-    case PS_DATA_STRING: {
-            //psString string = psStringCopy(argv[argnum]); // Get the argument into PS memory management
-            //psFree(string);
-            newItem = psMetadataItemAlloc(item->name, item->type, item->comment, argv[argnum]);
-            psArgumentRemove(argnum, argc, argv);
-        }
-        break;
+        item->data.B = true;
+        return true;
+    case PS_DATA_STRING:
+        psFree(item->data.V);
+        item->data.V = psStringCopy(argv[argnum]);
+        return psArgumentRemove(argnum, argc, argv);
     default:
-        psError(PS_ERR_IO, true, "Argument type (%x) is not supported --- argument %s ignored\n",
-                item->type, item->name);
-        psFree(newItem);
-        return NULL;
-    }
-
-    return newItem;
-}
-
-
-// XXX: There is a memory leak in the MULTI section.  I think it might have something to do with reference
-// counting between lists and MD, in the second section of the code (copy newArgs into arguments), but I'm not
-// entirely sure.
+        psError(PS_ERR_IO, true, "Argument type (%x) is not supported --- argument %s (%s) ignored\n",
+                item->type, item->name, item->comment);
+        return false;
+    }
+
+    return true;
+}
+
+
 bool psArgumentParse(psMetadata *arguments,
                      int *argc,
@@ -156,7 +151,6 @@
     // the default values, which we probably want to output in a "help" message (we don't want to print
     // the changed values and have the user think that they are default values).
-
-    psMetadata *newArgs = psMetadataAlloc(); // Place to read arguments into
-    psList *changed = psListAlloc(NULL);// List of keys that have changed
+    psMetadata *oldArgs = psMetadataCopy(NULL, arguments); // Copy of old arguments, in event of an error
+    psMetadata *multiFlag = psMetadataAlloc(); // Flag for which MULTI values have been read
 
     for (int i = 1; i < *argc; i++) {
@@ -165,133 +159,86 @@
         if (argItem) {
             psArgumentRemove(i, argc, argv); // Remove the switch
-            if (argItem->type != PS_DATA_METADATA_MULTI) {
+
+            switch (argItem->type) {
+            case PS_DATA_METADATA: {
+                    // -arg 1 2 3
+                    psMetadata *params = argItem->data.V; // The list of parameters
+                    if (*argc < i + params->list->n) {
+                        psError(PS_ERR_IO, true, "Not enough arguments for %s.\n", argItem->name);
+                        // Remove the arguments --- they will be ignored
+                        for (int j = i; j < *argc; j++) {
+                            psArgumentRemove(i, argc, argv);
+                        }
+                        goto failed;
+                    }
+
+                    psMetadataIterator *paramsIter = psMetadataIteratorAlloc(params, PS_LIST_HEAD, NULL);// Iter
+                    psMetadataItem *param = NULL; // Parameter from iteration
+                    while ((param = psMetadataGetAndIncrement(paramsIter))) {
+                        argumentRead(param, i, argc, argv);
+                    }
+                    psFree(paramsIter);
+                    break;
+                }
+            case PS_DATA_METADATA_MULTI: {
+                    // -arg 1 -arg 2 -arg 3 ....
+                    psList *multi = argItem->data.V; // The MULTI list
+                    psMetadataItem *template = psListGet(multi, PS_LIST_HEAD)
+                                               ; // The template item
+                    psMetadataItem *newItem = psMetadataItemCopy(template)
+                                              ; // New item to add
+                    argumentRead(newItem, i, argc, argv);
+                    psMetadataAddItem(arguments, newItem, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
+                    psFree(newItem);      // Drop reference
+
+                    // Remove the template
+                    bool search = false;  // Result of search
+                    if (!psMetadataLookupBool(&search, multiFlag, argItem->name) || !search) {
+                        // Chop the template off
+                        psListRemove(multi, PS_LIST_HEAD); // Remove from MULTI (hash side)
+                        psListRemoveData(arguments->list, template)
+                        ; // Remove from list side
+                        psMetadataAddBool(multiFlag, PS_LIST_HEAD, argItem->name, PS_META_REPLACE,
+                                          NULL, true);
+                    }
+                    break;
+                }
+            default:
+                // -arg 1
                 if (argItem->type != PS_DATA_BOOL && *argc < i + 1) {
                     psError(PS_ERR_IO, true, "Required argument for %s is missing.\n", argItem->name);
-                    // XXX: Cleanup before returning
-                    psFree(newArgs);
-                    return false;
+                    goto failed;
                 }
-                psMetadataItem *newItem = argumentRead(argItem, i, argc, argv);
-                psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_REPLACE);
-                psFree(newItem);
-            } else {
-                // Go through the MULTI
-                psList *multi = argItem->data.V; // The list of MULTI psMetadataItems
-                if (*argc < i + multi->n) {
-                    psError(PS_ERR_IO, true, "Not enough arguments for %s.\n", argItem->name);
-                    // Remove the arguments --- they will be ignored
-                    for (int j = i; j < *argc; j++) {
-                        psArgumentRemove(i, argc, argv);
-                    }
-                    // XXX: Cleanup before returning
-                    psFree(newArgs);
-                    return false;
-                }
-
-                // Remove any prior existence in the newArgs --- this is important because we specify
-                // adding the new items as DUPLICATE_OK, so if some idiot specifies it twice, we'd end
-                // up with two copies of everything.
-                psMetadataItem *checkItem = psMetadataLookup(newArgs, argItem->name);
-                if (checkItem) {
-                    (void)psMetadataRemove(newArgs, 0, argItem->name);
-                    (void)psListRemoveData(changed, argItem->name);
-                }
-
-                psListIterator *multiIter = psListIteratorAlloc(multi, PS_LIST_HEAD, true);
-                psMetadataItem *nextItem = NULL; // Item from list
-                while ( (nextItem = psListGetAndIncrement(multiIter)) ) {
-                    psMetadataItem *newItem = argumentRead(nextItem, i, argc, argv);
-                    psMetadataAddItem(newArgs, newItem, PS_LIST_TAIL, PS_META_DUPLICATE_OK);
-                    //psFree(newItem);
-                }
-                psFree(multiIter);
-            }
-
-            // Some book-keeping
-            //     psString name = psStringCopy(argItem->name);
-            psListAdd(changed, PS_LIST_TAIL, argItem->name);
-            i--;
-
-        } else if ( (strncmp(argv[i], "-", 1) == 0 ) || (strncmp(argv[i], "+", 1) == 0) ) {
+                argumentRead(argItem, i, argc, argv);
+                break;
+            }
+            i--;                        // We removed stuff
+        } else if (strncmp(argv[i], "-", 1) == 0 || strncmp(argv[i], "+", 1) == 0) {
             // Someone's specified a bad option
             psError(PS_ERR_IO, true, "Unknown option: %s\n", argv[i]);
-            psFree(newArgs);
-            return false;
-        }
-    }
-
-    // All the arguments are good, so now we can copy the newArgs over
-    psListIterator *changedIter = psListIteratorAlloc(changed, PS_LIST_HEAD, false); // Iterator
-    psString name = NULL;  // Item from iteration
-    while ( (name = psListGetAndIncrement(changedIter)) ) {
-        psTrace(__func__, 7, "Updating %s\n", name);
-        psMetadataItem *oldItem = psMetadataLookup(arguments, name);
-        psMetadataItem *newItem = psMetadataLookup(newArgs, name);
-        if ( (oldItem->type != newItem->type) ) {
-            psAbort(__func__, "Shouldn't reach here!\n");
-        }
-        switch (oldItem->type) {
-            // Only doing a representative set of types
-        case PS_DATA_S32:
-            oldItem->data.S32 = newItem->data.S32;
-            break;
-        case PS_DATA_F32:
-            oldItem->data.F32 = newItem->data.F32;
-            break;
-        case PS_DATA_BOOL:
-            oldItem->data.B = newItem->data.B;
-            break;
-            // XXX: Include the other numerical types
-        case PS_DATA_STRING:
-            psFree(oldItem->data.V);
-            oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
-            break;
-        case PS_DATA_METADATA_MULTI: {
-                psList *newMulti = psMemIncrRefCounter(newItem->data.V); // The new list of MULTI
-                psList *oldMulti = oldItem->data.V; // The old list of MULTI
-                psListIterator *newMultiIter = psListIteratorAlloc(newMulti, PS_LIST_HEAD, false);
-                psListIterator *oldMultiIter = psListIteratorAlloc(oldMulti, PS_LIST_HEAD, true);
-                psMetadataItem *newMultiItem = NULL; // Item from iterator
-                while ( (newMultiItem = psListGetAndIncrement(newMultiIter)) ) {
-                    psMetadataItem *oldMultiItem = psListGetAndIncrement(oldMultiIter);
-                    if (!oldMultiItem) {
-                        psAbort(__func__,
-                                "Something went very wrong here!  The lists SHOULD be of the same length!\n");
-                    }
-                    switch (oldMultiItem->type) {
-                        // Only doing a representative set of types
-                    case PS_DATA_S32:
-                        oldItem->data.S32 = newItem->data.S32;
-                        break;
-                    case PS_DATA_F32:
-                        oldItem->data.F32 = newItem->data.F32;
-                        break;
-                        // XXX: Include the other numerical types
-                    case PS_DATA_STRING:
-                        psFree(oldItem->data.V);
-                        oldItem->data.V = psMemIncrRefCounter(newItem->data.V);
-                        break;
-                    default:
-                        psAbort(__func__, "Should never ever get here, ever.\n");
-                    }
-                    psFree(oldMultiItem);
-                    psFree(newMultiItem);
-                }
-                psFree(newMultiIter);
-                psFree(oldMultiIter);
-            }
-            break;
-        default:
-            psAbort(__func__, "Should never ever ever get here.\n");
-        }
-    }
-    psFree(changedIter);
-    psFree(changed);
-
-    // Now, blow away the newArgs and we're done.
-    psFree(newArgs);
+            goto failed;
+        }
+    }
+
+    psFree(multiFlag);
+    psFree(oldArgs);                    // Didn't need these
     return true;
-}
-
+
+failed:
+    // We need to copy everything back
+    while (psMetadataRemoveIndex(arguments, PS_LIST_TAIL))
+        ;
+    psMetadataCopy(arguments, oldArgs);
+    psFree(multiFlag);
+    psFree(oldArgs);
+    return false;
+}
+
+
+#define LENGTH_CASE(TYPE) \
+case PS_TYPE_##TYPE: \
+return arg->data.TYPE == 0 ? 1 : \
+       (arg->data.TYPE > 0 ? (int)log10f((float)arg->data.TYPE) + 1 : \
+        (int)log10f(-(float)arg->data.TYPE) + 2);
 
 static int argLength(psMetadataItem *arg)
@@ -299,9 +246,10 @@
     switch (arg->type) {
         // Only doing a representative set of types
-    case PS_DATA_S32:
-        return arg->data.S32 == 0 ? 1 :
-               (arg->data.S32 > 0 ? (int)log10f((float)arg->data.S32) + 1 :
-                (int)log10f(-(float)arg->data.S32) + 2);
-        // XXX: Other numerical types
+        LENGTH_CASE(U8);
+        LENGTH_CASE(U16);
+        LENGTH_CASE(U32);
+        LENGTH_CASE(S8);
+        LENGTH_CASE(S16);
+        LENGTH_CASE(S32);
     case PS_DATA_F32:
         return arg->data.F32 >= 0 ? 12 : 13; // -d.dddddde?dd
@@ -319,27 +267,102 @@
 }
 
+
+#define PRINT_CASE(TYPE,FORMAT) \
+case PS_TYPE_##TYPE: \
+printf(FORMAT, item->data.TYPE); \
+break;
+
+// Print the value and comment for an argument item
+static void printValueComment(psMetadataItem *item, // Argument item for which to print
+                              const char *comment, // Comment to print
+                              unsigned int numSpaces // Number of spaces
+                             )
+{
+    int valueLength = 0;                // Length of the value portion
+    printf("(");
+    if (item) {
+        // Print the value
+        switch (item->type) {
+            PRINT_CASE(U8,"%d");
+            PRINT_CASE(U16,"%d");
+            PRINT_CASE(U32,"%d");
+            PRINT_CASE(S8,"%d");
+            PRINT_CASE(S16,"%d");
+            PRINT_CASE(S32,"%d");
+            PRINT_CASE(F32,"%.6e");
+            PRINT_CASE(F64,"%.6e");
+        case PS_DATA_BOOL:
+            if (item->data.B) {
+                printf("TRUE");
+            } else {
+                printf("FALSE");
+            }
+            break;
+        case PS_DATA_STRING:
+            if (item->data.V) {
+                printf("""%s""", (char*)(item->data.V));
+            }
+            break;
+        default:
+            psAbort(__func__, "Argument type (%x) for %s is not supported.\n", item->type, item->name);
+        }
+        valueLength = argLength(item);
+    }
+    printf(")");
+
+    // Spaces for formatting
+    for (int i = valueLength; i < numSpaces; i++) {
+        printf(" ");
+    }
+
+    // Print the comment
+    if (comment) {
+        printf("%s", comment);
+    }
+    printf("\n");
+
+    return;
+}
+
+// Return the maximum length of the values
+static void maxLength(int *maxName,     // Maximum length of the name
+                      int *maxValue,    // Maximum length of the value
+                      psMetadata *md    // Metadata for which to check the length
+                     )
+{
+    psMetadataIterator *iter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *item = NULL;        // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        if (item->type == PS_DATA_METADATA) {
+            maxLength(maxName, maxValue, item->data.V);
+        } else {
+            int nameLength = strlen(item->name); // Length of the name
+            if (nameLength > *maxName) {
+                *maxName = nameLength;
+            }
+            int valueLength = argLength(item); // Length of the value
+            if (valueLength > *maxValue) {
+                *maxValue = valueLength;
+            }
+        }
+    }
+    psFree(iter);
+    return;
+}
+
+
 void psArgumentHelp(psMetadata *arguments)
 {
     printf("Optional arguments, with default values:\n");
+    int maxName = 4;   // Maximum length of a name
+    int maxValue = 4;   // Maximum length of a value
+
+    // First pass to get the sizes
+    maxLength(&maxName, &maxValue, arguments);
+
+    // Second pass to print
     psMetadataIterator *argIter = psMetadataIteratorAlloc(arguments, PS_LIST_HEAD, NULL);
     psMetadataItem *argItem = NULL; // Item from iterator
-    int maxName = 4;   // Maximum length of a name
-    int maxValue = 4;   // Maximum length of a value
-
-    // First pass to get the sizes
-    while ( (argItem = psMetadataGetAndIncrement(argIter)) ) {
-        if (strlen(argItem->name) > maxName) {
-            maxName = strlen(argItem->name);
-        }
-        int valLength = argLength(argItem);
-        if (valLength > maxValue) {
-            maxValue = valLength;
-        }
-    }
-
-    // Second pass to print
-    psMetadataIteratorSet(argIter, PS_LIST_HEAD);
-    psString lastName = NULL;  // Last name we printed
-    while ( (argItem = psMetadataGetAndIncrement(argIter)) ) {
+    while ((argItem = psMetadataGetAndIncrement(argIter))) {
         // Initial indent
         for (int i = 0; i < NUM_SPACES; i++) {
@@ -348,54 +371,39 @@
 
         // Print the name if required
-        int position = 0; // Number of spaces in
-        if (! lastName || strcmp(lastName, argItem->name) != 0) {
-            // A new name
-            printf("%s", argItem->name);
-            position += strlen(argItem->name);
-            lastName = argItem->name;
-        }
+        printf("%s", argItem->name);
+        int position = strlen(argItem->name); // Number of spaces in
         for (int i = position; i < maxName + NUM_SPACES; i++) {
             printf(" ");
         }
 
-        // Print the value
-        printf("(");
-        switch (argItem->type) {
-            // Only doing a representative set of types
-        case PS_DATA_S32:
-            printf("%d", argItem->data.S32);
-            break;
-            // XXX: Other numerical types
-        case PS_DATA_F32:
-            printf("%.6e", argItem->data.F32);
-            break;
-        case PS_DATA_F64:
-            printf("%.6e", argItem->data.F64);
-            break;
-        case PS_DATA_BOOL:
-            if (argItem->data.B) {
-                printf("TRUE");
-            } else {
-                printf("FALSE");
-            }
-            break;
-        case PS_DATA_STRING:
-            if (argItem->data.V) {
-                printf("""%s""", (char*)(argItem->data.V));
-            }
-            break;
-        default:
-            psAbort(__func__, "Argument type (%x) is not supported.\n", argItem->type);
-        }
-        printf(")");
-        for (int i = argLength(argItem); i < maxValue + NUM_SPACES; i++) {
-            printf(" ");
-        }
-
-        // Print the comment
-        if (argItem->comment) {
-            printf("%s", argItem->comment);
-        }
-        printf("\n");
+        // Check to see if it's a MULTI --- there are no default values for a MULTI
+        // -arg 1 -arg 2 -arg 3 ...
+        psMetadataItem *multiCheck = psMetadataLookup(arguments, argItem->name); // Item to check for MULTI
+        if (multiCheck->type == PS_DATA_METADATA_MULTI) {
+            printValueComment(NULL, argItem->comment, maxValue + NUM_SPACES);
+            continue;
+        }
+
+        // -arg 1 2 3
+        if (argItem->type == PS_DATA_METADATA) {
+            psMetadata *params = argItem->data.V; // The list of parameters
+            psMetadataIterator *paramsIter = psMetadataIteratorAlloc(params, PS_LIST_HEAD, NULL); // Iterator
+            psMetadataItem *paramItem = NULL; // Parameter, from iteration
+            bool first = true;          // Is this the first one?
+            while ((paramItem = psMetadataGetAndIncrement(paramsIter))) {
+                if (!first) {
+                    for (int i = 0; i < maxName + 2*NUM_SPACES; i++) {
+                        printf(" ");
+                    }
+                }
+                printValueComment(paramItem, paramItem->comment, maxValue + NUM_SPACES);
+                first = false;
+            }
+            psFree(paramsIter);
+            continue;
+        }
+
+        // -arg 1
+        printValueComment(argItem, argItem->comment, maxValue + NUM_SPACES);
     }
 
