Index: /trunk/psLib/src/types/psMetadataConfig.c
===================================================================
--- /trunk/psLib/src/types/psMetadataConfig.c	(revision 8773)
+++ /trunk/psLib/src/types/psMetadataConfig.c	(revision 8774)
@@ -10,6 +10,6 @@
 *  @author Eric Van Alst, MHPCC
 *
-*  @version $Revision: 1.79 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-09-07 22:02:47 $
+*  @version $Revision: 1.80 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-09-08 02:13:20 $
 *
 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -22,4 +22,9 @@
 #include <inttypes.h>
 #include <strings.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #include "psAbort.h"
@@ -69,8 +74,6 @@
 ;
 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 psBool parseLine(psS32* level,   psArray* levelArray, char*  linePtr, psS32 lineCount, psBool overwrite);
+static psBool parseMetadataItem(char* keyName, psS32* level, psArray* levelArray, char* linePtr, psS32 lineCount, psMetadataFlags flags);
 static psString formatMetadataItem(psMetadataItem *item);
 static psArray *p_psMetadataKeyArray(psMetadata *md);
@@ -301,12 +304,15 @@
     }
 
-    psTime *out = psTimeFromISO(inString, tt);
-
+    // like psTimeFromISO() but the ending Z is required
+    psTime *out = psTimeStrptime(inString, "%Y-%m-%dT%H:%M:%SZ");
     if (!out) {
-        *status = 0;
+        *status = 1;
         return NULL;
     }
 
-    *status = 1;
+    // psTimeStrptime() leaves the type at the default
+    out->type = tt;
+
+    *status = 0;
 
     return out;
@@ -662,5 +668,4 @@
                          char* linePtr,
                          psS32 lineCount,
-                         char* fileName,
                          psMetadataFlags flags)
 {
@@ -694,6 +699,5 @@
     // Check for no type
     if(strType==NULL) {
-        psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u of %s."), "type",lineCount,
-                fileName);
+        psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u."), "type",lineCount);
         returnValue = false;
     } else {
@@ -726,6 +730,6 @@
                 vectorType = PS_TYPE_F64;
             } else {
-                psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), "", keyName,
-                        strType, lineCount, fileName);
+                psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), "", keyName,
+                        strType, lineCount);
                 psFree(strType);
                 return false;
@@ -755,6 +759,6 @@
         } else if(!strncmp(strType, "METADATA", 8)) {
             mdType = PS_DATA_METADATA;
-        } else if( !strncmp(strType, "PS_TIME_UTC", 12) || !strncmp(strType, "PS_TIME_TAI", 12)
-                   || !strncmp(strType, "PS_TIME_UT1", 12) || !strncmp(strType, "PS_TIME_TT", 12)) {
+        } else if( !strncmp(strType, "UTC", 3) || !strncmp(strType, "TAI", 3)
+                   || !strncmp(strType, "UT1", 3) || !strncmp(strType, "TT", 3)) {
             mdType = PS_DATA_TIME;
         } else {
@@ -771,6 +775,6 @@
                         // Check for add failure
                         if (! addStatus) {
-                            psError(PS_ERR_IO, true, _("Duplicate Metadata item, %s, found on line %u of %s.  Overwrite not allowed."),
-                                    keyName, lineCount, fileName);
+                            psError(PS_ERR_IO, true, _("Duplicate Metadata item, %s, found on line %u.  Overwrite not allowed."),
+                                    keyName, lineCount);
                             psFree(strType);
                             psFree(tempMeta);
@@ -780,6 +784,6 @@
                     } else {
                         // Metadata type read error
-                        psError(PS_ERR_IO,true,_("Failed to read a metadata %s on line %u of %s."),
-                                keyName,lineCount,fileName);
+                        psError(PS_ERR_IO,true,_("Failed to read a metadata %s on line %u."),
+                                keyName, lineCount);
                         psFree(strType);
                         return false;
@@ -790,6 +794,6 @@
             }
             if(!typeFound) {
-                psError(PS_ERR_IO, true, _("Metadata type '%s', found on line %u of %s, is invalid."),
-                        strType, lineCount,fileName);
+                psError(PS_ERR_IO, true, _("Metadata type '%s', found on line %u, is invalid."),
+                        strType, lineCount);
                 psFree(strType);
                 return false;
@@ -807,6 +811,5 @@
         strValue = getToken(&linePtr, "#", &status,true);
         if(status) {
-            psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u of %s."), "value", lineCount,
-                    fileName);
+            psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u."), "value", lineCount);
             psFree(strType);
             psFree(strValue);
@@ -814,6 +817,5 @@
         }
         if(strValue==NULL) {
-            psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u of %s."), "value", lineCount,
-                    fileName);
+            psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u."), "value", lineCount);
             psFree(strType);
             psFree(strValue);
@@ -824,6 +826,5 @@
         strComment = getToken(&linePtr,"~", &status,true);
         if(status) {
-            psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u of %s."), "comment", lineCount,
-                    fileName);
+            psError(PS_ERR_IO, true, _("Failed to read a metadata %s on line %u."), "comment", lineCount);
             psFree(strType);
             psFree(strValue);
@@ -849,6 +850,6 @@
                                       strComment, tempBool);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -863,6 +864,5 @@
         } else {
             psError(PS_ERR_IO, true,
-                    _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName, strType, lineCount,
-                    fileName);
+                    _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName, strType, lineCount);
             returnValue = false;
         }
@@ -873,6 +873,6 @@
             addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName, mdType | flags, strComment, tempInt);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -883,6 +883,6 @@
             addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName, mdType | flags, strComment, tempInt);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -893,6 +893,6 @@
             addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName, mdType | flags, strComment, tempInt);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -903,6 +903,6 @@
             addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName, mdType | flags, strComment, tempUint);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -913,6 +913,6 @@
             addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName, mdType | flags, strComment, tempUint);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -923,18 +923,20 @@
             addStatus = psMetadataAdd(md, PS_LIST_TAIL, keyName, mdType | flags, strComment, tempUint);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
         break;
     case PS_DATA_TIME:
-        if( !strncmp(strType, "PS_TIME_UTC", 12) )
+        if(!strncmp(strType, "UTC", 3)) {
             timeType = PS_TIME_UTC;
-        else if( !strncmp(strType, "PS_TIME_TAI", 12) )
+        } else if(!strncmp(strType, "TAI", 3)) {
             timeType = PS_TIME_TAI;
-        else if( !strncmp(strType, "PS_TIME_UT1", 12) )
+        } else if(!strncmp(strType, "UT1", 3)) {
             timeType = PS_TIME_UT1;
-        else if( !strncmp(strType, "PS_TIME_TT", 12) )
+        } else if(!strncmp(strType, "TT", 3)) {
             timeType = PS_TIME_TT;
+        }
+
         mTime = parseTime(strValue, timeType, &status);
         if(!status) {
@@ -943,6 +945,6 @@
                                       strComment, mTime);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -956,6 +958,6 @@
                                       strComment, tempVec);
         } else {
-            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u of %s."), strValue, keyName,
-                    strType, lineCount, fileName);
+            psError(PS_ERR_IO, true, _("Failed to parse the value '%s' of metadata item %s, type %s, on line %u."), strValue, keyName,
+                    strType, lineCount);
             returnValue = false;
         }
@@ -968,6 +970,6 @@
         for(psS32 k=0; k < nonUniqueKeys->n; k++) {
             if(strcmp(keyName,(char*)nonUniqueKeys->data[k]) == 0) {
-                psError(PS_ERR_IO,true,_("Duplicate MULTI specifier on line %u of %s."),
-                        lineCount,fileName);
+                psError(PS_ERR_IO,true,_("Duplicate MULTI specifier on line %u."),
+                        lineCount);
                 psFree(strType);
                 return false;
@@ -994,6 +996,6 @@
         break;
     default:
-        psError(PS_ERR_IO,true,_("Metadata of unknown type found on line %u of %s."),
-                lineCount,fileName);
+        psError(PS_ERR_IO,true,_("Metadata of unknown type found on line %u."),
+                lineCount);
         break;
     }
@@ -1015,5 +1017,4 @@
                  char* linePtr,
                  psS32 lineCount,
-                 char* fileName,
                  psBool overwrite)
 {
@@ -1042,5 +1043,5 @@
         if(repeatedChars(linePtr, '@') > 1) {
             psError(PS_ERR_IO, true,
-                    _("More than one '%c' character not allowed.  Found on line %u of %s."), '@', lineCount, fileName);
+                    _("More than one '%c' character not allowed.  Found on line %u."), '@', lineCount);
             return false;
         }
@@ -1050,5 +1051,5 @@
         if(status) {
             psError(PS_ERR_IO, true,
-                    _("Failed to read a metadata %s on line %u of %s."), "keyName", lineCount, fileName);
+                    _("Failed to read a metadata %s on line %u."), "keyName", lineCount);
             psFree(keyName);
             return false;
@@ -1060,6 +1061,5 @@
             strType = getToken(&linePtr," ",&status,true);
             if(strType == NULL) {
-                psError(PS_ERR_IO,true,_("Failed to read a metadata %s on line %u of %s."),"type",lineCount,
-                        fileName);
+                psError(PS_ERR_IO,true,_("Failed to read a metadata %s on line %u."),"type",lineCount);
                 psFree(keyName);
                 return false;
@@ -1074,6 +1074,6 @@
                     // Compare type name with the list of current types
                     if(strcmp(strType,(char*)typeArray->data[k]) == 0) {
-                        psError(PS_ERR_IO,true,_("Specified type, %s, on line %u of %s is already defined."),
-                                strType,lineCount,fileName);
+                        psError(PS_ERR_IO,true,_("Specified type, %s, on line %u is already defined."),
+                                strType,lineCount);
                         psFree(tempTemplate);
                         psFree(keyName);
@@ -1089,6 +1089,6 @@
                 psFree(tempTemplate);
             } else {
-                psError(PS_ERR_IO,true,_("Metadata type '%s', found on line %u of %s, is invalid."),
-                        strType,lineCount,fileName);
+                psError(PS_ERR_IO,true,_("Metadata type '%s', found on line %u, is invalid."),
+                        strType,lineCount);
                 psFree(keyName);
                 psFree(strType);
@@ -1139,5 +1139,5 @@
             }
             // Parse metadataItem
-            if(!parseMetadataItem(keyName,level, levelArray, linePtr, lineCount, fileName, flags)) {
+            if(!parseMetadataItem(keyName,level, levelArray, linePtr, lineCount, flags)) {
                 psFree(keyName);
                 return false;
@@ -1149,19 +1149,49 @@
 }
 
+psMetadata* psMetadataConfigRead(psMetadata* md,
+                                 unsigned int *nFail,
+                                 const char *filename,
+                                 bool overwrite)
+{
+    // Check for NULL file name
+    PS_ASSERT_PTR_NON_NULL(filename,NULL);
+
+    struct stat buf;
+
+    // Attempt to open specified file
+    int fd = 0;
+    if (!(fd = open(filename, O_RDONLY))) {
+        // XXX really should return strerror() here
+        psError(PS_ERR_IO, true, _("Failed to open file '%s'. Check if it exists and it has the proper permissions."), filename);
+        return NULL;
+    }
+
+    // psMetadataConfigParse() is going to read the entire file into memory so
+    // we're trying to be nice by allowing the VM to flush the file out of
+    // memory if need be.
+    // XXX yes, mmap is evil
+    void *file = mmap(0, (size_t)buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+    md = psMetadataConfigParse(md, nFail, (char *)file, overwrite);
+
+    munmap(file, (size_t)buf.st_size);
+
+    if (close(fd) != 0) {
+        // XXX really should return strerror() here
+        psError(PS_ERR_IO, true, _("Failed to close file '%s'."), filename);
+        return NULL;
+    }
+
+    return md;
+}
+
 psMetadata* psMetadataConfigParse(psMetadata* md,
                                   unsigned int *nFail,
-                                  const char *filename,
+                                  const char *str,
                                   bool overwrite)
 {
-    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_ASSERT_PTR_NON_NULL(filename,NULL);
 
     // Initialise nFail, if provided
@@ -1170,10 +1200,4 @@
     }
 
-    // Attempt to open specified file
-    if((fp=fopen(filename, "r")) == NULL) {
-        psError(PS_ERR_IO, true, _("Failed to open file '%s'. Check if it exists and it has the proper permissions."), filename);
-        return NULL;
-    }
-
     // Allocate metadata if necessary
     if (md == NULL) {
@@ -1187,35 +1211,41 @@
     topLevelInfo = p_psParseLevelInfoAlloc();
     topLevelInfo->metadata = psMemIncrRefCounter(md);
-    parseLevelInfoArray = psArrayAdd(parseLevelInfoArray,1,topLevelInfo);
+    parseLevelInfoArray = psArrayAdd(parseLevelInfoArray, 1, topLevelInfo);
     psFree(topLevelInfo);
-
-    // Create reusable line for continuous read
-    line = (char*)psAlloc(MAX_STRING_LENGTH*sizeof(char));
 
     // While loop to parse the file
     psErrorClear();   // so we can call psError(..., false, ...)
-    while(fgets(line, MAX_STRING_LENGTH, fp) != NULL) {
-
-        // Initialize variables for new line
-        linePtr = line;
-        lineCount++;
-
-        if(!parseLine(&nestingLevel,parseLevelInfoArray,linePtr,lineCount,(char*)filename,overwrite)) {
+
+    psList *doc = psStringSplit(str, "\n", false);
+    if (!doc) {
+        psFree(parseLevelInfoArray);
+        psFree(md);
+        return NULL;
+    }
+
+    long lineCount = 0;
+    char *line = NULL;
+    psListIterator *iter = psListIteratorAlloc(doc, 0, false);
+    while((line = psListGetAndIncrement(iter))) {
+        lineCount++; // indexed from 1
+
+        if(!parseLine(&nestingLevel, parseLevelInfoArray, line, lineCount,
+                      overwrite)) {
             if (nFail != NULL) {
                 (*nFail)++;
             } else {
-                psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Error parsing line: %s", linePtr);
-            }
-        }
-    }
-    fclose(fp);
+                psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Error parsing line: %s", line);
+            }
+        }
+    }
+
+    psFree(iter);
+    psFree(doc);
 
     // Free parse array and line buffer
     psFree(parseLevelInfoArray);
-    psFree(line);
 
     return md;
 }
-
 
 psString psMetadataConfigFormat(psMetadata *md)
@@ -1358,11 +1388,11 @@
         psTime *time = item->data.V;
         if ( time->type == PS_TIME_UTC )
-            psStringAppend(&content, "PS_TIME_UTC  ");
+            psStringAppend(&content, "UTC  ");
         else if ( time->type == PS_TIME_TAI )
-            psStringAppend(&content, "PS_TIME_TAI  ");
+            psStringAppend(&content, "TAI  ");
         else if ( time->type == PS_TIME_UT1 )
-            psStringAppend(&content, "PS_TIME_UT1  ");
+            psStringAppend(&content, "UT1  ");
         else if ( time->type == PS_TIME_TT )
-            psStringAppend(&content, "PS_TIME_TT  ");
+            psStringAppend(&content, "TT  ");
         else {
             psError(PS_ERR_BAD_PARAMETER_VALUE, true, _("Specified psDataType, %d, is not supported."), time->type);
Index: /trunk/psLib/src/types/psMetadataConfig.h
===================================================================
--- /trunk/psLib/src/types/psMetadataConfig.h	(revision 8773)
+++ /trunk/psLib/src/types/psMetadataConfig.h	(revision 8774)
@@ -10,6 +10,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-08-14 20:05:05 $
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-09-08 02:13:20 $
  *
  *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
@@ -65,8 +65,21 @@
  *  @return psMetadata* : Resulting metadata from read.
  */
+psMetadata* psMetadataConfigRead(
+    psMetadata* md,                    ///< Resulting metadata from read.
+    unsigned int *nFail,               ///< Number of failed lines.
+    const char *filename,              ///< Name of file to read.
+    bool overwrite                     ///< Allow overwrite of duplicate specifications.
+);
+
+/** Parse metadata configuration string.
+ *
+ *  Loads pre-defined settings by parsing a string into a psMetadata structure.
+ *
+ *  @return psMetadata* : Resulting metadata from parse.
+ */
 psMetadata* psMetadataConfigParse(
     psMetadata* md,                    ///< Resulting metadata from read.
     unsigned int *nFail,               ///< Number of failed lines.
-    const char *filename,              ///< Name of file to read.
+    const char *str,                   ///< String to process.
     bool overwrite                     ///< Allow overwrite of duplicate specifications.
 );
Index: /trunk/psLib/test/types/Makefile.am
===================================================================
--- /trunk/psLib/test/types/Makefile.am	(revision 8773)
+++ /trunk/psLib/test/types/Makefile.am	(revision 8774)
@@ -16,4 +16,5 @@
 	tap_psListIterator \
 	tap_psMetadataConfigParse \
+	tap_psMetadataConfigParse_time \
 	tap_psMetadataItemCompare \
 	tap_psMetadataItemParse \
Index: /trunk/psLib/test/types/tap_psMetadataConfigParse.c
===================================================================
--- /trunk/psLib/test/types/tap_psMetadataConfigParse.c	(revision 8773)
+++ /trunk/psLib/test/types/tap_psMetadataConfigParse.c	(revision 8774)
@@ -148,14 +148,14 @@
     plan_tests(52);
 
-    diag("psMetadataParseConfig tests");
+    diag("psMetadataConfigRead tests");
 
     generateMDConfig();
 
     unsigned int numBadLines = 0;            // Number of bad lines
-    psMetadata *md = psMetadataConfigParse(NULL, &numBadLines, FILENAME, false);
+    psMetadata *md = psMetadataConfigRead(NULL, &numBadLines, FILENAME, false);
     ok(md, "md = %x", md);
     ok(numBadLines == 1, "number of bad lines = %d", numBadLines); // One bad line from boolean
     skip_start(!md || numBadLines != 1, 0,
-               "Skipping 0 tests because psMetadataConfigParse failed.");
+               "Skipping 0 tests because psMetadataConfigRead failed.");
     ok(psListLength(md->list) == 6, "size = %d", psListLength(md->list));
 
Index: /trunk/psLib/test/types/tap_psMetadataConfigParse_time.c
===================================================================
--- /trunk/psLib/test/types/tap_psMetadataConfigParse_time.c	(revision 8774)
+++ /trunk/psLib/test/types/tap_psMetadataConfigParse_time.c	(revision 8774)
@@ -0,0 +1,182 @@
+#include <stdio.h>
+#include <pslib.h>
+
+#include "tap.h"
+#include "pstap.h"
+
+int main (void)
+{
+    plan_tests(40);
+
+    diag("psMetadataParseConfig time format tests");
+
+    {
+        psMemId id = psMemGetId();
+
+        char *testStr = "time    UTC     2005-03-18T16:05:00Z\n";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_UTC);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad); // One bad line from boolean
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_UTC);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+        char *testStr = "time    UT1     2005-03-18T16:05:00Z\n";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_UT1);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad);
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_UT1);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+        char *testStr = "time    TAI     2005-03-18T16:05:00Z\n";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_TAI);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad);
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_TAI);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+        char *testStr = "time    TT      2005-03-18T16:05:00Z";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+        psTime *time1 = psMetadataLookupTime(NULL, md, "time");
+        psTime *time2 = psTimeFromISO("2005-03-18T16:05:00Z", PS_TIME_TT);
+        double delta = psTimeDelta(time1, time2);
+        psFree(time2);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 0, "number of bad lines = %d", nBad);
+        ok(md->list->n == 1, "number of items in metadata = %ld", md->list->n);
+        ok1(time1->type == PS_TIME_TT);
+        ok(delta == 0.0, "reported time was off by %fs", delta);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+        // missing Z
+        char *testStr = "broken      UTC     2005-03-18T16:05:00";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 1, "number of bad lines = %d", nBad);
+        // XXX should a md even be returned instead of NULL?
+        ok(md->list->n == 0, "number of items in metadata = %ld", md->list->n);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+        // missing Z
+        char *testStr = "broken      UT1     2005-03-18T16:05:00";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 1, "number of bad lines = %d", nBad);
+        // XXX should a md even be returned instead of NULL?
+        ok(md->list->n == 0, "number of items in metadata = %ld", md->list->n);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+        // missing Z
+        char *testStr = "broken      TAI     2005-03-18T16:05:00";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 1, "number of bad lines = %d", nBad);
+        // XXX should a md even be returned instead of NULL?
+        ok(md->list->n == 0, "number of items in metadata = %ld", md->list->n);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    {
+        psMemId id = psMemGetId();
+
+        // missing Z
+        char *testStr = "broken      TT      2005-03-18T16:05:00";
+
+        psU32 nBad = 0;
+        psMetadata *md = psMetadataConfigParse(NULL, &nBad, testStr, false);
+
+        ok(md, "md = %x", md);
+        ok(nBad == 1, "number of bad lines = %d", nBad);
+        // XXX should a md even be returned instead of NULL?
+        ok(md->list->n == 0, "number of items in metadata = %ld", md->list->n);
+
+        psFree(md);
+
+        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    }
+
+    return exit_status();
+}
