diff --git a/ThirdParty/netcdf/update.sh b/ThirdParty/netcdf/update.sh
index 18e4a36524cdeb8becfc65fcc4f22d298e888816..d87536605626896bfbe8a028aa40e40b73a0c432 100755
--- a/ThirdParty/netcdf/update.sh
+++ b/ThirdParty/netcdf/update.sh
@@ -8,7 +8,7 @@ readonly name="netcdf"
 readonly ownership="netcdf Upstream <kwrobot@kitware.com>"
 readonly subtree="ThirdParty/$name/vtk$name"
 readonly repo="https://gitlab.kitware.com/third-party/netcdf.git"
-readonly tag="for/vtk-20190607.1-4.7.0"
+readonly tag="for/vtk-20190618-4.7.0"
 readonly paths="
 include/*.h
 include/netcdf_meta.h.in
@@ -18,6 +18,7 @@ liblib/nc_initialize.c
 libsrc/*.h
 libsrc/*.c
 libsrc4/*.c
+libhdf5/*.c
 
 .gitattributes
 CMakeLists.vtk.txt
diff --git a/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt b/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt
index 9f3d6911cbf445ff8ad692bbd5669b1a6459e999..07a71bb723af2ca23136e431951df35301b4c2fd 100644
--- a/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt
+++ b/ThirdParty/netcdf/vtknetcdf/CMakeLists.txt
@@ -13,6 +13,7 @@ set(sources
   libdispatch/dattinq.c
   libdispatch/dattput.c
   libdispatch/dauth.c
+  libdispatch/daux.c
   libdispatch/dcompound.c
   libdispatch/dcopy.c
   libdispatch/ddim.c
@@ -23,10 +24,13 @@ set(sources
   libdispatch/dgroup.c
   libdispatch/dinfermodel.c
   libdispatch/dinternal.c
+  libdispatch/dnotnc3.c
+  libdispatch/dnotnc4.c
   libdispatch/doffsets.c
   libdispatch/dopaque.c
   libdispatch/dparallel.c
   libdispatch/drc.c
+  libdispatch/dreadonly.c
   libdispatch/dstring.c
   libdispatch/dtype.c
   libdispatch/dutf8.c
@@ -37,8 +41,8 @@ set(sources
   libdispatch/dvarput.c
   libdispatch/dvlen.c
   libdispatch/dwinpath.c
-  libdispatch/nc.c
   libdispatch/ncbytes.c
+  libdispatch/nc.c
   libdispatch/nchashmap.c
   libdispatch/nclist.c
   libdispatch/nclistmgr.c
@@ -72,7 +76,24 @@ set(sources
   libsrc4/nc4type.c
   libsrc4/nc4var.c
   libsrc4/ncfunc.c
-  libsrc4/ncindex.c)
+  libsrc4/ncindex.c
+
+  libhdf5/nc4hdf.c
+  libhdf5/nc4info.c
+  libhdf5/hdf5file.c
+  libhdf5/hdf5attr.c
+  libhdf5/hdf5dim.c
+  libhdf5/hdf5grp.c
+  libhdf5/hdf5type.c
+  libhdf5/hdf5internal.c
+  libhdf5/hdf5create.c
+  libhdf5/hdf5open.c
+  libhdf5/hdf5var.c
+  libhdf5/nc4mem.c
+  libhdf5/nc4memcb.c
+  libhdf5/hdf5cache.c
+  libhdf5/hdf5dispatch.c
+  )
 
 set(CHUNK_CACHE_NELEMS 1009)
 set(CHUNK_CACHE_PREEMPTION 0.75)
@@ -91,6 +112,7 @@ set(USE_DISKLESS 1)
 set(USE_FFIO 0)
 set(USE_FSYNC 0)
 set(USE_HDF4 0)
+set(USE_HDF5 1)
 set(USE_MMAP 0)
 set(USE_NETCDF4 1)
 set(USE_NETCDF_2 0)
@@ -148,27 +170,28 @@ check_symbol_exists("strlcat" "string.h" HAVE_STRLCAT)
 check_symbol_exists("st_blocksize" "sys/stat.h" HAVE_STRUCT_STAT_ST_BLKSIZE)
 check_symbol_exists("sysconf" "unistd.h" HAVE_SYSCONF)
 
-set(CMAKE_REQUIRED_INCLUDES_save "${CMAKE_REQUIRED_INCLUDES}")
-list(APPEND CMAKE_REQUIRED_INCLUDES
-  "${CMAKE_CURRENT_BINARY_DIR}/../../hdf5")
+set(HAVE_HDF5_H 1)
+set(H5_USE_16_API 1) # Enable HDF5 1.6.x Compatibility(Required)
 if (VTK_MODULE_USE_EXTERNAL_hdf5)
+  set(CMAKE_REQUIRED_INCLUDES_save "${CMAKE_REQUIRED_INCLUDES}")
+  list(APPEND CMAKE_REQUIRED_INCLUDES
+    "${CMAKE_CURRENT_BINARY_DIR}/../../hdf5")
   # The symbol detections can still fail for an external HDF5 that isn't in a
   # standard location. We need generate-time knowledge to get the real include
   # directories. These checks should really be replaced at some point with some
   # compile-time logic if at all possible (version checks, preprocessor
   # detections, etc.).
+  check_symbol_exists("H5free_memory" "vtk_hdf5.h" HDF5_HAS_H5FREE)
+  check_symbol_exists("H5Pset_all_coll_metadata_ops" "vtk_hdf5.h" H5PSET_ALL_COLL_METADATA_OPS)
+  check_symbol_exists("H5Pset_libver_bounds" "vtk_hdf5.h" HDF5_HAS_LIBVER_BOUNDS)
+  check_symbol_exists("HDF5_PARALLEL" "vtk_hdf5.h" HDF5_PARALLEL)
+  set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES_save}")
 else ()
-  list(APPEND CMAKE_REQUIRED_INCLUDES
-    "${CMAKE_CURRENT_BINARY_DIR}/../../hdf5/vtkhdf5"
-    "${CMAKE_CURRENT_SOURCE_DIR}/../../hdf5"
-    "${CMAKE_CURRENT_SOURCE_DIR}/../../hdf5/vtkhdf5/src")
+  set(HDF5_HAS_H5FREE 1)
+  set(H5PSET_ALL_COLL_METADATA_OPS 1)
+  set(HDF5_HAS_LIBVER_BOUNDS 1)
+  set(HDF5_PARALLEL 0)
 endif ()
-set(HAVE_HDF5_H 1)
-check_symbol_exists("H5free_memory" "vtk_hdf5.h" HDF5_HAS_H5FREE)
-check_symbol_exists("H5Pset_all_coll_metadata_ops" "vtk_hdf5.h" H5PSET_ALL_COLL_METADATA_OPS)
-check_symbol_exists("H5Pset_libver_bounds" "vtk_hdf5.h" HDF5_HAS_LIBVER_BOUNDS)
-check_symbol_exists("HDF5_PARALLEL" "vtk_hdf5.h" HDF5_PARALLEL)
-set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES_save}")
 
 include(CheckIncludeFiles)
 check_include_file("alloca.h" HAVE_ALLOCA_H)
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/H5FDhttp.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/H5FDhttp.c
new file mode 100644
index 0000000000000000000000000000000000000000..ef0a45ed4ef9264bcf453dce08a8564459699b13
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/H5FDhttp.c
@@ -0,0 +1,890 @@
+/*********************************************************************
+*    Copyright 2018, UCAR/Unidata
+*    See netcdf/COPYRIGHT file for copying and redistribution conditions.
+* ********************************************************************/
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group.                                               *
+ * Copyright by the Board of Trustees of the University of Illinois.         *
+ * All rights reserved.                                                      *
+ *                                                                           *
+ * This file is part of HDF5.  The full HDF5 copyright notice, including     *
+ * terms governing use, modification, and redistribution, is contained in    *
+ * the COPYING file, which can be found at the root of the source code       *
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
+ * If you do not have access to either file, you may request a copy from     *
+ * help@hdfgroup.org.                                                        *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/* Programmer:  Dennis Heimbigner dmh@ucar.edu
+ *
+ * Purpose:  Access remote datasets using byte range requests.
+ * Derived from the HDF5 H5FDstdio.c file.
+ *
+ * NOTE:    This driver is not as well tested as the standard SEC2 driver
+ *          and is not intended for production use!
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <hdf5.h>
+#include <curl/curl.h>
+
+#ifdef H5_HAVE_FLOCK
+/* Needed for lock type definitions (e.g., LOCK_EX) */
+#include <sys/file.h>
+#endif /* H5_HAVE_FLOCK */
+
+#ifdef H5_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/*
+Define a simple #ifdef test for the version of H5FD_class_t we are using 
+*/
+#if H5_VERS_MAJOR == 1
+#if H5_VERS_MINOR < 10
+#define H5FDCLASS1 1
+#endif
+#else
+#error "Cannot determine version of H5FD_class_t"
+#endif
+
+#ifdef H5_HAVE_WIN32_API
+/* The following two defines must be before any windows headers are included */
+#define WIN32_LEAN_AND_MEAN    /* Exclude rarely-used stuff from Windows headers */
+#define NOGDI                  /* Exclude Graphic Display Interface macros */
+
+#include <windows.h>
+#include <io.h>
+
+#endif /* H5_HAVE_WIN32_API */
+
+#include "netcdf.h"
+#include "ncbytes.h"
+#include "nchttp.h"
+
+#include "H5FDhttp.h"
+
+typedef off_t file_offset_t;
+
+/* The driver identification number, initialized at runtime */
+static hid_t H5FD_HTTP_g = 0;
+
+/* File operations */
+typedef enum {
+    H5FD_HTTP_OP_UNKNOWN=0,
+    H5FD_HTTP_OP_READ=1,
+    H5FD_HTTP_OP_WRITE=2,
+    H5FD_HTTP_OP_SEEK=3
+} H5FD_http_file_op;
+
+/* The description of a file belonging to this driver. The 'eoa' and 'eof'
+ * determine the amount of hdf5 address space in use and the high-water mark
+ * of the file (the current size of the underlying Unix file). The 'pos'
+ * value is used to eliminate file position updates when they would be a
+ * no-op. Unfortunately we've found systems that use separate file position
+ * indicators for reading and writing so the lseek can only be eliminated if
+ * the current operation is the same as the previous operation.  When opening
+ * a file the 'eof' will be set to the current file size, 'eoa' will be set
+ * to zero, 'pos' will be set to H5F_ADDR_UNDEF (as it is when an error
+ * occurs), and 'op' will be set to H5F_OP_UNKNOWN.
+ */
+typedef struct H5FD_http_t {
+    H5FD_t      pub;            /* public stuff, must be first      */
+    haddr_t     eoa;            /* end of allocated region          */
+    haddr_t     eof;            /* end of file; current file size   */
+    haddr_t     pos;            /* current file I/O position        */
+    unsigned    write_access;   /* Flag to indicate the file was opened with write access */
+    H5FD_http_file_op op;		/* last operation */
+    CURL*           curl;       /* Curl handle */
+    char*           url;        /* The URL (minus any fragment) for the dataset */ 
+} H5FD_http_t;
+
+
+/* These macros check for overflow of various quantities.  These macros
+ * assume that file_offset_t is signed and haddr_t and size_t are unsigned.
+ *
+ * ADDR_OVERFLOW:  Checks whether a file address of type `haddr_t'
+ *      is too large to be represented by the second argument
+ *      of the file seek function.
+ *
+ * SIZE_OVERFLOW:  Checks whether a buffer size of type `hsize_t' is too
+ *      large to be represented by the `size_t' type.
+ *
+ * REGION_OVERFLOW:  Checks whether an address and size pair describe data
+ *      which can be addressed entirely by the second
+ *      argument of the file seek function.
+ */
+/* adding for windows NT filesystem support. */
+#define MAXADDR (((haddr_t)1<<(8*sizeof(file_offset_t)-1))-1)
+#define ADDR_OVERFLOW(A)  (HADDR_UNDEF==(A) || ((A) & ~(haddr_t)MAXADDR))
+#define SIZE_OVERFLOW(Z)  ((Z) & ~(hsize_t)MAXADDR)
+#define REGION_OVERFLOW(A,Z)  (ADDR_OVERFLOW(A) || SIZE_OVERFLOW(Z) || \
+    HADDR_UNDEF==(A)+(Z) || (file_offset_t)((A)+(Z))<(file_offset_t)(A))
+
+/* Prototypes */
+static H5FD_t *H5FD_http_open(const char *name, unsigned flags,
+                 hid_t fapl_id, haddr_t maxaddr);
+static herr_t H5FD_http_close(H5FD_t *lf);
+static int H5FD_http_cmp(const H5FD_t *_f1, const H5FD_t *_f2);
+static herr_t H5FD_http_query(const H5FD_t *_f1, unsigned long *flags);
+static haddr_t H5FD_http_alloc(H5FD_t *_file, H5FD_mem_t type, hid_t dxpl_id, hsize_t size);
+static haddr_t H5FD_http_get_eoa(const H5FD_t *_file, H5FD_mem_t type);
+static herr_t H5FD_http_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr);
+static herr_t  H5FD_http_get_handle(H5FD_t *_file, hid_t fapl, void** file_handle);
+static herr_t H5FD_http_read(H5FD_t *lf, H5FD_mem_t type, hid_t fapl_id, haddr_t addr,
+                size_t size, void *buf);
+static herr_t H5FD_http_write(H5FD_t *lf, H5FD_mem_t type, hid_t fapl_id, haddr_t addr,
+                size_t size, const void *buf);
+
+/* The H5FD_class_t structure has different versions */
+#ifdef H5FDCLASS1
+static haddr_t H5FD_http_get_eof(const H5FD_t *_file);
+static herr_t H5FD_http_flush(H5FD_t *_file, hid_t dxpl_id, unsigned closing);
+static herr_t H5FD_http_lock(H5FD_t *_file, unsigned char* old, unsigned lock_type, hbool_t last);
+static herr_t H5FD_http_unlock(H5FD_t *file, unsigned char *oid, hbool_t last);
+#else
+static herr_t H5FD_http_term(void);
+static haddr_t H5FD_http_get_eof(const H5FD_t *_file, H5FD_mem_t type);
+static herr_t H5FD_http_flush(H5FD_t *_file, hid_t dxpl_id, hbool_t closing);
+static herr_t H5FD_http_lock(H5FD_t *_file, hbool_t rw);
+static herr_t H5FD_http_unlock(H5FD_t *_file);
+#endif
+
+/* Beware, not same as H5FD_HTTP_g */
+static const H5FD_class_t H5FD_http_g = {
+    "http",                     /* name         */
+    MAXADDR,                    /* maxaddr      */
+    H5F_CLOSE_WEAK,             /* fc_degree    */
+#ifndef H5FDCLASS1
+    H5FD_http_term,             /* terminate    */
+#endif
+    NULL,                       /* sb_size      */
+    NULL,                       /* sb_encode    */
+    NULL,                       /* sb_decode    */
+    0,                          /* fapl_size    */
+    NULL,                       /* fapl_get     */
+    NULL,                       /* fapl_copy    */
+    NULL,                       /* fapl_free    */
+    0,                          /* dxpl_size    */
+    NULL,                       /* dxpl_copy    */
+    NULL,                       /* dxpl_free    */
+    H5FD_http_open,            /* open         */
+    H5FD_http_close,           /* close        */
+    H5FD_http_cmp,             /* cmp          */
+    H5FD_http_query,           /* query        */
+    NULL,                       /* get_type_map */
+    H5FD_http_alloc,           /* alloc        */
+    NULL,                       /* free         */
+    H5FD_http_get_eoa,         /* get_eoa      */
+    H5FD_http_set_eoa,         /* set_eoa      */
+    H5FD_http_get_eof,         /* get_eof      */
+    H5FD_http_get_handle,      /* get_handle   */
+    H5FD_http_read,            /* read         */
+    H5FD_http_write,           /* write        */
+    H5FD_http_flush,           /* flush        */
+    NULL, 		       /* truncate     */
+    H5FD_http_lock,            /* lock         */
+    H5FD_http_unlock,          /* unlock       */
+    H5FD_FLMAP_DICHOTOMY       /* fl_map       */
+};
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_init
+ *
+ * Purpose:  Initialize this driver by registering the driver with the
+ *    library.
+ *
+ * Return:  Success:  The driver ID for the driver.
+ *
+ *    Failure:  Negative.
+ *
+ * Programmer:  Robb Matzke
+ *              Thursday, July 29, 1999
+ *
+ *-------------------------------------------------------------------------
+ */
+EXTERNL hid_t
+H5FD_http_init(void)
+{
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    if (H5I_VFL!=H5Iget_type(H5FD_HTTP_g))
+        H5FD_HTTP_g = H5FDregister(&H5FD_http_g);
+    return H5FD_HTTP_g;
+} /* end H5FD_http_init() */
+
+
+/*---------------------------------------------------------------------------
+ * Function:  H5FD_http_term
+ *
+ * Purpose:  Shut down the VFD
+ *
+ * Returns:     Non-negative on success or negative on failure
+ *
+ * Programmer:  Quincey Koziol
+ *              Friday, Jan 30, 2004
+ *
+ *---------------------------------------------------------------------------
+ */
+#ifndef H5FDCLASS1
+static herr_t
+H5FD_http_term(void)
+{
+    /* Reset VFL ID */
+    H5FD_HTTP_g = 0;
+
+    return 0;
+} /* end H5FD_http_term() */
+#endif
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5Pset_fapl_http
+ *
+ * Purpose:  Modify the file access property list to use the H5FD_HTTP
+ *    driver defined in this source file.  There are no driver
+ *    specific properties.
+ *
+ * Return:  Non-negative on success/Negative on failure
+ *
+ * Programmer:  Robb Matzke
+ *    Thursday, February 19, 1998
+ *
+ *-------------------------------------------------------------------------
+ */
+EXTERNL herr_t
+H5Pset_fapl_http(hid_t fapl_id)
+{
+    static const char *func = "H5FDset_fapl_http";  /*for error reporting*/
+
+    /*NO TRACE*/
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    if(0 == H5Pisa_class(fapl_id, H5P_FILE_ACCESS))
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_PLIST, H5E_BADTYPE, "not a file access property list", -1)
+
+    return H5Pset_driver(fapl_id, H5FD_HTTP, NULL);
+} /* end H5Pset_fapl_http() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_open
+ *
+ * Purpose:  Opens a remote Object as an HDF5 file.
+ *
+ * Errors:
+ *  IO  CANTOPENFILE    File doesn't exist and CREAT wasn't
+ *                      specified.
+ *
+ * Return:
+ *      Success:    A pointer to a new file data structure. The
+ *                  public fields will be initialized by the
+ *                  caller, which is always H5FD_open().
+ *
+ *      Failure:    NULL
+ *
+ * Programmer:  Dennis Heimbigner
+ *
+ *-------------------------------------------------------------------------
+ */
+static H5FD_t *
+H5FD_http_open( const char *name, unsigned flags, hid_t /*UNUSED*/ fapl_id,
+    haddr_t maxaddr)
+{
+    unsigned            write_access = 0;           /* File opened with write access? */
+    H5FD_http_t        *file = NULL;
+    static const char   *func = "H5FD_http_open";  /* Function Name for error reporting */
+    CURL* curl = NULL;
+    long long len = -1;
+    int ncstat = NC_NOERR;
+
+    /* Sanity check on file offsets */
+    assert(sizeof(file_offset_t) >= sizeof(size_t));
+
+    /* Quiet compiler */
+    fapl_id = fapl_id;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Check arguments */
+    if (!name || !*name)
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_ARGS, H5E_BADVALUE, "invalid URL", NULL)
+    if (0 == maxaddr || HADDR_UNDEF == maxaddr)
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_ARGS, H5E_BADRANGE, "bogus maxaddr", NULL)
+    if (ADDR_OVERFLOW(maxaddr))
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_ARGS, H5E_OVERFLOW, "maxaddr too large", NULL)
+
+    /* Always read-only */
+    write_access = 0;
+
+    /* Open file in read-only mode, to check for existence  and get length */
+    if((ncstat = nc_http_open(name,&curl,&len))) {
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_CANTOPENFILE, "cannot access object", NULL)
+    }
+
+    /* Build the return value */
+    if(NULL == (file = (H5FD_http_t *)H5allocate_memory(sizeof(H5FD_http_t),0))) {
+	nc_http_close(curl);
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed", NULL)
+    } /* end if */
+    memset(file,0,sizeof(H5FD_http_t));
+
+    file->op = H5FD_HTTP_OP_SEEK;
+    file->pos = HADDR_UNDEF;
+    file->write_access = write_access;    /* Note the write_access for later */
+    file->eof = (haddr_t)len;
+    file->curl = curl; curl = NULL;
+    file->url = H5allocate_memory(strlen(name+1),0);
+    if(file->url == NULL) {
+	nc_http_close(curl);
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed", NULL)
+    }
+    memcpy(file->url,name,strlen(name)+1);
+
+    return((H5FD_t*)file);
+} /* end H5FD_HTTP_OPen() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5F_http_close
+ *
+ * Purpose:  Closes a file.
+ *
+ * Errors:
+ *    IO    CLOSEERROR  Fclose failed.
+ *
+ * Return:  Non-negative on success/Negative on failure
+ *
+ * Programmer:  Dennis Heimbigner
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_http_close(H5FD_t *_file)
+{
+    H5FD_http_t  *file = (H5FD_http_t*)_file;
+#if 0
+    static const char *func = "H5FD_http_close";  /* Function Name for error reporting */
+#endif
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Close the underlying curl handle*/
+    if(file->curl) nc_http_close(file->curl);
+    if(file->url) H5free_memory(file->url);
+
+    H5free_memory(file);
+
+    return 0;
+} /* end H5FD_http_close() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_cmp
+ *
+ * Purpose:  Compares two files belonging to this driver using an
+ *    arbitrary (but consistent) ordering.
+ *
+ * Return:
+ *      Success:    A value like strcmp()
+ *
+ *      Failure:    never fails (arguments were checked by the caller).
+ *
+ * Programmer:  Robb Matzke
+ *              Thursday, July 29, 1999
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+H5FD_http_cmp(const H5FD_t *_f1, const H5FD_t *_f2)
+{
+    const H5FD_http_t  *f1 = (const H5FD_http_t*)_f1;
+    const H5FD_http_t  *f2 = (const H5FD_http_t*)_f2;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    if(strcmp(f1->url,f2->url) < 0) return -1;
+    if(strcmp(f1->url,f2->url) > 0) return 1;
+    return 0;
+} /* H5FD_http_cmp() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_query
+ *
+ * Purpose:  Set the flags that this VFL driver is capable of supporting.
+ *              (listed in H5FDpublic.h)
+ *
+ * Return:  Success:  non-negative
+ *
+ *    Failure:  negative
+ *
+ * Programmer:  Quincey Koziol
+ *              Friday, August 25, 2000
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_http_query(const H5FD_t *_f, unsigned long /*OUT*/ *flags)
+{
+    /* Quiet the compiler */
+    _f=_f;
+
+    /* Set the VFL feature flags that this driver supports.
+     *
+     * Note that this VFD does not support SWMR due to the unpredictable
+     * nature of the buffering layer.
+     */
+    if(flags) {
+        *flags = 0;
+        *flags |= H5FD_FEAT_AGGREGATE_METADATA;     /* OK to aggregate metadata allocations                             */
+        *flags |= H5FD_FEAT_ACCUMULATE_METADATA;    /* OK to accumulate metadata for faster writes                      */
+        *flags |= H5FD_FEAT_DATA_SIEVE;             /* OK to perform data sieving for faster raw data reads & writes    */
+        *flags |= H5FD_FEAT_AGGREGATE_SMALLDATA;    /* OK to aggregate "small" raw data allocations                     */
+#ifndef H5FDCLASS1
+        *flags |= H5FD_FEAT_DEFAULT_VFD_COMPATIBLE; /* VFD creates a file which can be opened with the default VFD      */
+#endif
+    }
+
+    return 0;
+} /* end H5FD_http_query() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_alloc
+ *
+ * Purpose:     Allocates file memory. If fseeko isn't available, makes
+ *              sure the file size isn't bigger than 2GB because the
+ *              parameter OFFSET of fseek is of the type LONG INT, limiting
+ *              the file size to 2GB.
+ *
+ * Return:
+ *      Success:    Address of new memory
+ *
+ *      Failure:    HADDR_UNDEF
+ *
+ * Programmer:  Raymond Lu
+ *              30 March 2007
+ *
+ *-------------------------------------------------------------------------
+ */
+static haddr_t
+H5FD_http_alloc(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, hid_t /*UNUSED*/ dxpl_id, hsize_t size)
+{
+    H5FD_http_t    *file = (H5FD_http_t*)_file;
+    haddr_t         addr;
+
+    /* Quiet compiler */
+    type = type;
+    dxpl_id = dxpl_id;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Compute the address for the block to allocate */
+    addr = file->eoa;
+
+    file->eoa = addr + size;
+
+    return addr;
+} /* end H5FD_http_alloc() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_get_eoa
+ *
+ * Purpose:  Gets the end-of-address marker for the file. The EOA marker
+ *           is the first address past the last byte allocated in the
+ *           format address space.
+ *
+ * Return:  Success:  The end-of-address marker.
+ *
+ *    Failure:  HADDR_UNDEF
+ *
+ * Programmer:  Robb Matzke
+ *              Monday, August  2, 1999
+ *
+ *-------------------------------------------------------------------------
+ */
+static haddr_t
+H5FD_http_get_eoa(const H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type)
+{
+    const H5FD_http_t *file = (const H5FD_http_t *)_file;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Quiet compiler */
+    type = type;
+
+    return file->eoa;
+} /* end H5FD_http_get_eoa() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_set_eoa
+ *
+ * Purpose:  Set the end-of-address marker for the file. This function is
+ *    called shortly after an existing HDF5 file is opened in order
+ *    to tell the driver where the end of the HDF5 data is located.
+ *
+ * Return:  Success:  0
+ *
+ *    Failure:  Does not fail
+ *
+ * Programmer:  Robb Matzke
+ *              Thursday, July 29, 1999
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_http_set_eoa(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, haddr_t addr)
+{
+    H5FD_http_t  *file = (H5FD_http_t*)_file;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Quiet the compiler */
+    type = type;
+
+    file->eoa = addr;
+
+    return 0;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_get_eof
+ *
+ * Purpose:  Returns the end-of-file marker, which is the greater of
+ *    either the Unix end-of-file or the HDF5 end-of-address
+ *    markers.
+ *
+ * Return:  Success:  End of file address, the first address past
+ *        the end of the "file", either the Unix file
+ *        or the HDF5 file.
+ *
+ *    Failure:  HADDR_UNDEF
+ *
+ * Programmer:  Robb Matzke
+ *              Thursday, July 29, 1999
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static haddr_t
+#ifdef H5FDCLASS1
+H5FD_http_get_eof(const H5FD_t *_file)
+#else
+H5FD_http_get_eof(const H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type)
+#endif
+{
+    const H5FD_http_t  *file = (const H5FD_http_t *)_file;
+
+#ifndef H5FDCLASS1
+    /* Quiet the compiler */
+    type = type;
+#endif
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    return(file->eof);
+} /* end H5FD_http_get_eof() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:       H5FD_http_get_handle
+ *
+ * Purpose:        Returns the file handle of file driver.
+ *
+ * Returns:        Non-negative if succeed or negative if fails.
+ *
+ * Programmer:     Raymond Lu
+ *                 Sept. 16, 2002
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_http_get_handle(H5FD_t *_file, hid_t /*UNUSED*/ fapl, void **file_handle)
+{
+    H5FD_http_t       *file = (H5FD_http_t *)_file;
+    static const char  *func = "H5FD_http_get_handle";  /* Function Name for error reporting */
+
+    /* Quiet the compiler */
+    fapl = fapl;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    *file_handle = file->curl;
+    if(*file_handle == NULL)
+        H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_WRITEERROR, "get handle failed", -1)
+
+    return 0;
+} /* end H5FD_http_get_handle() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_read
+ *
+ * Purpose:  Reads SIZE bytes beginning at address ADDR in file LF and
+ *    places them in buffer BUF.  Reading past the logical or
+ *    physical end of file returns zeros instead of failing.
+ *
+ * Errors:
+ *    IO    READERROR  fread failed.
+ *    IO    SEEKERROR  fseek failed.
+ *
+ * Return:  Non-negative on success/Negative on failure
+ *
+ * Programmer:  Robb Matzke
+ *    Wednesday, October 22, 1997
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_http_read(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, hid_t /*UNUSED*/ dxpl_id,
+    haddr_t addr, size_t size, void /*OUT*/ *buf)
+{
+    H5FD_http_t    *file = (H5FD_http_t*)_file;
+    static const char *func = "H5FD_http_read";  /* Function Name for error reporting */
+    int ncstat = NC_NOERR;
+
+    /* Quiet the compiler */
+    type = type;
+    dxpl_id = dxpl_id;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Check for overflow */
+    if (HADDR_UNDEF==addr)
+        H5Epush_ret (func, H5E_ERR_CLS, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1)
+    if (REGION_OVERFLOW(addr, size))
+        H5Epush_ret (func, H5E_ERR_CLS, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1)
+
+    /* Check easy cases */
+    if (0 == size)
+        return 0;
+    if ((haddr_t)addr >= file->eof) {
+        memset(buf, 0, size);
+        return 0;
+    }
+
+    /* Seek to the correct file position. */
+    if (!(file->op == H5FD_HTTP_OP_READ || file->op == H5FD_HTTP_OP_SEEK) ||
+            file->pos != addr) {
+#if 0
+        if (file_fseek(file->fp, (file_offset_t)addr, SEEK_SET) < 0) {
+            file->op = H5FD_HTTP_OP_UNKNOWN;
+            file->pos = HADDR_UNDEF;
+            H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_SEEKERROR, "fseek failed", -1)
+        }
+#endif
+        file->pos = addr;
+    }
+
+    /* Read zeros past the logical end of file (physical is handled below) */
+    if (addr + size > file->eof) {
+        size_t nbytes = (size_t) (addr + size - file->eof);
+        memset((unsigned char *)buf + size - nbytes, 0, nbytes);
+        size -= nbytes;
+    }
+
+    {
+	NCbytes* bbuf = ncbytesnew();
+        if((ncstat = nc_http_read(file->curl,file->url,addr,size,bbuf))) {
+            file->op = H5FD_HTTP_OP_UNKNOWN;
+            file->pos = HADDR_UNDEF;
+	    ncbytesfree(bbuf); bbuf = NULL;
+            H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_READERROR, "HTTP byte-range read failed", -1)
+        } /* end if */
+
+	/* Check that proper number of bytes was read */
+	if(ncbyteslength(bbuf) != size) {
+	    ncbytesfree(bbuf); bbuf = NULL;
+            H5Epush_ret(func, H5E_ERR_CLS, H5E_IO, H5E_READERROR, "HTTP byte-range read mismatch ", -1)
+	}	
+
+	/* Extract the data from buf */
+	memcpy(buf,ncbytescontents(bbuf),size);	        
+	ncbytesfree(bbuf);
+    }
+
+    /* Update the file position data. */
+    file->op = H5FD_HTTP_OP_READ;
+    file->pos = addr;
+
+    return 0;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_write
+ *
+ * Purpose:  Writes SIZE bytes from the beginning of BUF into file LF at
+ *    file address ADDR.
+ *
+ * Errors:
+ *    IO    SEEKERROR   fseek failed.
+ *    IO    WRITEERROR  fwrite failed.
+ *
+ * Return:  Non-negative on success/Negative on failure
+ *
+ * Programmer:  Dennis Heimbigner
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_http_write(H5FD_t *_file, H5FD_mem_t /*UNUSED*/ type, hid_t /*UNUSED*/ dxpl_id,
+    haddr_t addr, size_t size, const void *buf)
+{
+    static const char *func = "H5FD_http_write";  /* Function Name for error reporting */
+
+    /* Quiet the compiler */
+    dxpl_id = dxpl_id;
+    type = type;
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Always Fails */
+    H5Epush_ret (func, H5E_ERR_CLS, H5E_IO, H5E_WRITEERROR, "file is read-only", -1)
+
+    return 0;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function:  H5FD_http_flush
+ *
+ * Purpose:  Makes sure that all data is on disk.
+ *
+ * Errors:
+ *    IO    SEEKERROR     fseek failed.
+ *    IO    WRITEERROR    fflush or fwrite failed.
+ *
+ * Return:  Non-negative on success/Negative on failure
+ *
+ * Programmer:  Robb Matzke
+ *    Wednesday, October 22, 1997
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+#ifdef H5FDCLASS1
+H5FD_http_flush(H5FD_t *_file, hid_t dxpl_id, unsigned closing)
+#else
+H5FD_http_flush(H5FD_t *_file, hid_t /*UNUSED*/ dxpl_id, hbool_t closing)
+#endif
+{
+
+#ifndef H5FDCLASS1
+    /* Quiet the compiler */
+    dxpl_id = dxpl_id;
+#endif
+
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    return 0;
+} /* end H5FD_http_flush() */
+
+
+/*-------------------------------------------------------------------------
+ * Function:    H5FD_http_lock
+ *
+ * Purpose:     Lock a file via flock
+ *              NOTE: This function is a no-op if flock() is not present.
+ *
+ * Errors:
+ *    IO    FCNTL    flock failed.
+ *
+ * Return:      Non-negative on success/Negative on failure
+ *
+ * Programmer:  Vailin Choi; March 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+#ifdef H5FDCLASS1
+H5FD_http_lock(H5FD_t *_file, unsigned char* old, unsigned lock_type, hbool_t last)
+#else
+H5FD_http_lock(H5FD_t *_file, hbool_t rw)
+#endif
+{
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+#ifdef H5FDCLASS1
+    /* Quiet the compiler */
+    lock_type = lock_type;
+    last = last;
+#else
+    rw = rw;
+#endif
+
+    return 0;
+} /* end H5FD_http_lock() */
+
+/*-------------------------------------------------------------------------
+ * Function:    H5F_http_unlock
+ *
+ * Purpose:     Unlock a file via flock
+ *              NOTE: This function is a no-op if flock() is not present.
+ *
+ * Errors:
+ *    IO    FCNTL    flock failed.
+ *
+ * Return:      Non-negative on success/Negative on failure
+ *
+ * Programmer:  Vailin Choi; March 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+#ifdef H5FDCLASS1
+H5FD_http_unlock(H5FD_t *file, /*UNUSED*/unsigned char *oid, /*UNUSED*/ hbool_t last)
+#else
+H5FD_http_unlock(H5FD_t *_file)
+#endif
+{
+    /* Clear the error stack */
+    H5Eclear2(H5E_DEFAULT);
+
+    /* Quiet the compiler */
+#ifdef H5FDCLASS1
+    oid = oid;
+    last = last;
+#endif
+
+    return 0;
+} /* end H5FD_http_unlock() */
+
+
+#ifdef _H5private_H
+/*
+ * This is not related to the functionality of the driver code.
+ * It is added here to trigger warning if HDF5 private definitions are included
+ * by mistake.  The code should use only HDF5 public API and definitions.
+ */
+#error "Do not use HDF5 private definitions"
+#endif
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5attr.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5attr.c
new file mode 100644
index 0000000000000000000000000000000000000000..70356a7234c214fd1c26a35c2afdf7a47b77802e
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5attr.c
@@ -0,0 +1,940 @@
+/* Copyright 2003-2018, University Corporation for Atmospheric
+ * Research. See COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file
+ * @internal This file handles HDF5 attributes.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+/**
+ * @internal Get the attribute list for either a varid or NC_GLOBAL
+ *
+ * @param grp Group
+ * @param varid Variable ID | NC_BLOGAL
+ * @param varp Pointer that gets pointer to NC_VAR_INFO_T
+ * instance. Ignored if NULL.
+ * @param attlist Pointer that gets pointer to attribute list.
+ *
+ * @return NC_NOERR No error.
+ * @author Dennis Heimbigner, Ed Hartnett
+ */
+static int
+getattlist(NC_GRP_INFO_T *grp, int varid, NC_VAR_INFO_T **varp,
+           NCindex **attlist)
+{
+    int retval;
+
+    assert(grp && attlist);
+
+    if (varid == NC_GLOBAL)
+    {
+        /* Do we need to read the atts? */
+        if (!grp->atts_read)
+            if ((retval = nc4_read_atts(grp, NULL)))
+                return retval;
+
+        if (varp)
+            *varp = NULL;
+        *attlist = grp->att;
+    }
+    else
+    {
+        NC_VAR_INFO_T *var;
+
+        if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
+            return NC_ENOTVAR;
+        assert(var->hdr.id == varid);
+
+        /* Do we need to read the atts? */
+        if (!var->atts_read)
+            if ((retval = nc4_read_atts(grp, var)))
+                return retval;
+
+        if (varp)
+            *varp = var;
+        *attlist = var->att;
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal Get one of three special attributes, NCPROPS,
+ * ISNETCDF4ATT, and SUPERBLOCKATT. These atts are not all really in
+ * the file, they are constructed on the fly.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param name Name of attribute.
+ * @param filetypep Pointer that gets type of the attribute data in
+ * file.
+ * @param mem_type Type of attribute data in memory.
+ * @param lenp Pointer that gets length of attribute array.
+ * @param attnump Pointer that gets the attribute number.
+ * @param data Attribute data.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ERANGE Data conversion out of range.
+ * @author Dennis Heimbigner
+ */
+int
+nc4_get_att_special(NC_FILE_INFO_T* h5, const char* name,
+                    nc_type* filetypep, nc_type mem_type, size_t* lenp,
+                    int* attnump, void* data)
+{
+    /* Fail if asking for att id */
+    if(attnump)
+        return NC_EATTMETA;
+
+    if(strcmp(name,NCPROPS)==0) {
+        int len;
+        if(h5->provenance.ncproperties == NULL)
+            return NC_ENOTATT;
+        if(mem_type == NC_NAT) mem_type = NC_CHAR;
+        if(mem_type != NC_CHAR)
+            return NC_ECHAR;
+        if(filetypep) *filetypep = NC_CHAR;
+	len = strlen(h5->provenance.ncproperties);
+        if(lenp) *lenp = len;
+        if(data) strncpy((char*)data,h5->provenance.ncproperties,len+1);
+    } else if(strcmp(name,ISNETCDF4ATT)==0
+              || strcmp(name,SUPERBLOCKATT)==0) {
+        unsigned long long iv = 0;
+        if(filetypep) *filetypep = NC_INT;
+        if(lenp) *lenp = 1;
+        if(strcmp(name,SUPERBLOCKATT)==0)
+            iv = (unsigned long long)h5->provenance.superblockversion;
+        else /* strcmp(name,ISNETCDF4ATT)==0 */
+            iv = NC4_isnetcdf4(h5);
+        if(mem_type == NC_NAT) mem_type = NC_INT;
+        if(data)
+            switch (mem_type) {
+            case NC_BYTE: *((char*)data) = (char)iv; break;
+            case NC_SHORT: *((short*)data) = (short)iv; break;
+            case NC_INT: *((int*)data) = (int)iv; break;
+            case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break;
+            case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break;
+            case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break;
+            case NC_INT64: *((long long*)data) = (long long)iv; break;
+            case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break;
+            default:
+                return NC_ERANGE;
+            }
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal I think all atts should be named the exact same thing, to
+ * avoid confusion!
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param name Name of attribute.
+ * @param newname New name for attribute.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EMAXNAME New name too long.
+ * @return ::NC_EPERM File is read-only.
+ * @return ::NC_ENAMEINUSE New name already in use.
+ * @return ::NC_ENOTINDEFINE Classic model file not in define mode.
+ * @return ::NC_EHDFERR HDF error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EINTERNAL Could not rebuild list.
+ * @author Ed Hartnett
+ */
+int
+NC4_HDF5_rename_att(int ncid, int varid, const char *name, const char *newname)
+{
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var = NULL;
+    NC_ATT_INFO_T *att;
+    NCindex *list;
+    char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1];
+    hid_t datasetid = 0;
+    int retval = NC_NOERR;
+
+    if (!name || !newname)
+        return NC_EINVAL;
+
+    LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s",
+         ncid, varid, name, newname));
+
+    /* If the new name is too long, that's an error. */
+    if (strlen(newname) > NC_MAX_NAME)
+        return NC_EMAXNAME;
+
+    /* Find info for this file, group, and h5 info. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        return retval;
+    assert(h5 && grp);
+
+    /* If the file is read-only, return an error. */
+    if (h5->no_write)
+        return NC_EPERM;
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(newname, norm_newname)))
+        return retval;
+
+    /* Get the list of attributes. */
+    if ((retval = getattlist(grp, varid, &var, &list)))
+        return retval;
+
+    /* Is new name in use? */
+    att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_newname);
+    if(att != NULL)
+        return NC_ENAMEINUSE;
+
+    /* Normalize name and find the attribute. */
+    if ((retval = nc4_normalize_name(name, norm_name)))
+        return retval;
+
+    att = (NC_ATT_INFO_T*)ncindexlookup(list,norm_name);
+    if (!att)
+        return NC_ENOTATT;
+
+    /* If we're not in define mode, new name must be of equal or
+       less size, if complying with strict NC3 rules. */
+    if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->hdr.name) &&
+        (h5->cmode & NC_CLASSIC_MODEL))
+        return NC_ENOTINDEFINE;
+
+    /* Delete the original attribute, if it exists in the HDF5 file. */
+    if (att->created)
+    {
+        if (varid == NC_GLOBAL)
+        {
+            if (H5Adelete(((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid,
+                          att->hdr.name) < 0)
+                return NC_EHDFERR;
+        }
+        else
+        {
+            if ((retval = nc4_open_var_grp2(grp, varid, &datasetid)))
+                return retval;
+            if (H5Adelete(datasetid, att->hdr.name) < 0)
+                return NC_EHDFERR;
+        }
+        att->created = NC_FALSE;
+    }
+
+    /* Copy the new name into our metadata. */
+    if(att->hdr.name) free(att->hdr.name);
+    if (!(att->hdr.name = strdup(norm_newname)))
+        return NC_ENOMEM;
+    att->hdr.hashkey = NC_hashmapkey(att->hdr.name,strlen(att->hdr.name)); /* Fix hash key */
+
+    att->dirty = NC_TRUE;
+
+    /* Rehash the attribute list so that the new name is used */
+    if(!ncindexrebuild(list))
+        return NC_EINTERNAL;
+
+    /* Mark attributes on variable dirty, so they get written */
+    if(var)
+        var->attr_dirty = NC_TRUE;
+
+    return retval;
+}
+
+/**
+ * @internal Delete an att. Rub it out. Push the button on
+ * it. Liquidate it. Bump it off. Take it for a one-way
+ * ride. Terminate it.
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param name Name of attribute to delete.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTATT Attribute not found.
+ * @return ::NC_EINVAL No name provided.
+ * @return ::NC_EPERM File is read only.
+ * @return ::NC_ENOTINDEFINE Classic model not in define mode.
+ * @return ::NC_EINTERNAL Could not rebuild list.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_HDF5_del_att(int ncid, int varid, const char *name)
+{
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var;
+    NC_FILE_INFO_T *h5;
+    NC_ATT_INFO_T *att;
+    NCindex* attlist = NULL;
+    hid_t locid = 0;
+    int i;
+    size_t deletedid;
+    int retval;
+
+    /* Name must be provided. */
+    if (!name)
+        return NC_EINVAL;
+
+    LOG((2, "nc_del_att: ncid 0x%x varid %d name %s", ncid, varid, name));
+
+    /* Find info for this file, group, and h5 info. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        return retval;
+    assert(h5 && grp);
+
+    /* If the file is read-only, return an error. */
+    if (h5->no_write)
+        return NC_EPERM;
+
+    /* If file is not in define mode, return error for classic model
+     * files, otherwise switch to define mode. */
+    if (!(h5->flags & NC_INDEF))
+    {
+        if (h5->cmode & NC_CLASSIC_MODEL)
+            return NC_ENOTINDEFINE;
+        if ((retval = NC4_redef(ncid)))
+            return retval;
+    }
+
+    /* Get either the global or a variable attribute list. */
+    if ((retval = getattlist(grp, varid, &var, &attlist)))
+        return retval;
+
+    /* Determine the location id in the HDF5 file. */
+    if (varid == NC_GLOBAL)
+        locid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid;
+    else if (var->created)
+        locid = ((NC_HDF5_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid;
+
+    /* Now find the attribute by name. */
+    if (!(att = (NC_ATT_INFO_T*)ncindexlookup(attlist, name)))
+        return NC_ENOTATT;
+
+    /* Delete it from the HDF5 file, if it's been created. */
+    if (att->created)
+    {
+        assert(locid);
+        if (H5Adelete(locid, att->hdr.name) < 0)
+            return NC_EATTMETA;
+    }
+
+    deletedid = att->hdr.id;
+
+    /* Remove this attribute in this list */
+    if ((retval = nc4_att_list_del(attlist, att)))
+        return retval;
+
+    /* Renumber all attributes with higher indices. */
+    for (i = 0; i < ncindexsize(attlist); i++)
+    {
+        NC_ATT_INFO_T *a;
+        if (!(a = (NC_ATT_INFO_T *)ncindexith(attlist, i)))
+            continue;
+        if (a->hdr.id > deletedid)
+            a->hdr.id--;
+    }
+
+    /* Rebuild the index. */
+    if (!ncindexrebuild(attlist))
+        return NC_EINTERNAL;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This will return the length of a netcdf atomic data type
+ * in bytes.
+ *
+ * @param type A netcdf atomic type.
+ *
+ * @return Type size in bytes, or -1 if type not found.
+ * @author Ed Hartnett
+ */
+static int
+nc4typelen(nc_type type)
+{
+    switch(type){
+    case NC_BYTE:
+    case NC_CHAR:
+    case NC_UBYTE:
+        return 1;
+    case NC_USHORT:
+    case NC_SHORT:
+        return 2;
+    case NC_FLOAT:
+    case NC_INT:
+    case NC_UINT:
+        return 4;
+    case NC_DOUBLE:
+    case NC_INT64:
+    case NC_UINT64:
+        return 8;
+    }
+    return -1;
+}
+
+/**
+ * @internal
+ * Write an attribute to a netCDF-4/HDF5 file, converting
+ * data type if necessary.
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param name Name of attribute.
+ * @param file_type Type of the attribute data in file.
+ * @param len Number of elements in attribute array.
+ * @param data Attribute data.
+ * @param mem_type Type of data in memory.
+ * @param force write even if the attribute is special
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINVAL Invalid parameters.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTVAR Variable not found.
+ * @return ::NC_EBADNAME Name contains illegal characters.
+ * @return ::NC_ENAMEINUSE Name already in use.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+nc4_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type,
+            size_t len, const void *data, nc_type mem_type, int force)
+{
+    NC* nc;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var = NULL;
+    NCindex* attlist = NULL;
+    NC_ATT_INFO_T* att;
+    char norm_name[NC_MAX_NAME + 1];
+    nc_bool_t new_att = NC_FALSE;
+    int retval = NC_NOERR, range_error = 0;
+    size_t type_size;
+    int i;
+    int ret;
+    int ncid;
+
+    h5 = grp->nc4_info;
+    nc = h5->controller;
+    assert(nc && grp && h5);
+
+    ncid = nc->ext_ncid | grp->hdr.id;
+
+    /* Find att, if it exists. (Must check varid first or nc_test will
+     * break.) This also does lazy att reads if needed. */
+    if ((ret = getattlist(grp, varid, &var, &attlist)))
+        return ret;
+
+    /* The length needs to be positive (cast needed for braindead
+       systems with signed size_t). */
+    if((unsigned long) len > X_INT_MAX)
+        return NC_EINVAL;
+
+    /* Check name before LOG statement. */
+    if (!name || strlen(name) > NC_MAX_NAME)
+        return NC_EBADNAME;
+
+    LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d",
+         __func__,ncid, varid, name, file_type, mem_type, len));
+
+    /* If len is not zero, then there must be some data. */
+    if (len && !data)
+        return NC_EINVAL;
+
+    /* If the file is read-only, return an error. */
+    if (h5->no_write)
+        return NC_EPERM;
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        return retval;
+
+    /* Check that a reserved att name is not being used improperly */
+    const NC_reservedatt* ra = NC_findreserved(name);
+    if(ra != NULL && !force) {
+        /* case 1: grp=root, varid==NC_GLOBAL, flags & READONLYFLAG */
+        if (nc->ext_ncid == ncid && varid == NC_GLOBAL && grp->parent == NULL
+            && (ra->flags & READONLYFLAG))
+            return NC_ENAMEINUSE;
+        /* case 2: grp=NA, varid!=NC_GLOBAL, flags & DIMSCALEFLAG */
+        if (varid != NC_GLOBAL && (ra->flags & DIMSCALEFLAG))
+            return NC_ENAMEINUSE;
+    }
+
+    /* See if there is already an attribute with this name. */
+    att = (NC_ATT_INFO_T*)ncindexlookup(attlist,norm_name);
+
+    if (!att)
+    {
+        /* If this is a new att, require define mode. */
+        if (!(h5->flags & NC_INDEF))
+        {
+            if (h5->cmode & NC_CLASSIC_MODEL)
+                return NC_ENOTINDEFINE;
+            if ((retval = NC4_redef(ncid)))
+                BAIL(retval);
+        }
+        new_att = NC_TRUE;
+    }
+    else
+    {
+        /* For an existing att, if we're not in define mode, the len
+           must not be greater than the existing len for classic model. */
+        if (!(h5->flags & NC_INDEF) &&
+            len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid))
+        {
+            if (h5->cmode & NC_CLASSIC_MODEL)
+                return NC_ENOTINDEFINE;
+            if ((retval = NC4_redef(ncid)))
+                BAIL(retval);
+        }
+    }
+
+    /* We must have two valid types to continue. */
+    if (file_type == NC_NAT || mem_type == NC_NAT)
+        return NC_EBADTYPE;
+
+    /* Get information about this type. */
+    if ((retval = nc4_get_typelen_mem(h5, file_type, &type_size)))
+        return retval;
+
+    /* No character conversions are allowed. */
+    if (file_type != mem_type &&
+        (file_type == NC_CHAR || mem_type == NC_CHAR ||
+         file_type == NC_STRING || mem_type == NC_STRING))
+        return NC_ECHAR;
+
+    /* For classic mode file, only allow atts with classic types to be
+     * created. */
+    if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE)
+        return NC_ESTRICTNC3;
+
+    /* Add to the end of the attribute list, if this att doesn't
+       already exist. */
+    if (new_att)
+    {
+        LOG((3, "adding attribute %s to the list...", norm_name));
+        if ((ret = nc4_att_list_add(attlist, norm_name, &att)))
+            BAIL(ret);
+
+        /* Allocate storage for the HDF5 specific att info. */
+        if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T))))
+            BAIL(NC_ENOMEM);
+    }
+
+    /* Now fill in the metadata. */
+    att->dirty = NC_TRUE;
+    att->nc_typeid = file_type;
+
+    /* If this att has vlen or string data, release it before we lose the length value. */
+    if (att->stdata)
+    {
+        for (i = 0; i < att->len; i++)
+            if(att->stdata[i])
+                free(att->stdata[i]);
+        free(att->stdata);
+        att->stdata = NULL;
+    }
+    if (att->vldata)
+    {
+        for (i = 0; i < att->len; i++)
+            nc_free_vlen(&att->vldata[i]); /* FIX: see warning of nc_free_vlen */
+        free(att->vldata);
+        att->vldata = NULL;
+    }
+
+    att->len = len;
+
+    /* If this is the _FillValue attribute, then we will also have to
+     * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T
+     * struct for this var. (But ignore a global _FillValue
+     * attribute). */
+    if (!strcmp(att->hdr.name, _FillValue) && varid != NC_GLOBAL)
+    {
+        int size;
+
+        /* Fill value must be same type and have exactly one value */
+        if (att->nc_typeid != var->type_info->hdr.id)
+            return NC_EBADTYPE;
+        if (att->len != 1)
+            return NC_EINVAL;
+
+        /* If we already wrote to the dataset, then return an error. */
+        if (var->written_to)
+            return NC_ELATEFILL;
+
+        /* Get the length of the veriable data type. */
+        if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id,
+                                          &type_size)))
+            return retval;
+
+        /* Already set a fill value? Now I'll have to free the old
+         * one. Make up your damn mind, would you? */
+        if (var->fill_value)
+        {
+            if (var->type_info->nc_type_class == NC_VLEN)
+            {
+                if ((retval = nc_free_vlen(var->fill_value)))
+                    return retval;
+            }
+            else if (var->type_info->nc_type_class == NC_STRING)
+            {
+                if (*(char **)var->fill_value)
+                    free(*(char **)var->fill_value);
+            }
+            free(var->fill_value);
+        }
+
+        /* Determine the size of the fill value in bytes. */
+        if (var->type_info->nc_type_class == NC_VLEN)
+            size = sizeof(hvl_t);
+        else if (var->type_info->nc_type_class == NC_STRING)
+            size = sizeof(char *);
+        else
+            size = type_size;
+
+        /* Allocate space for the fill value. */
+        if (!(var->fill_value = calloc(1, size)))
+            return NC_ENOMEM;
+
+        /* Copy the fill_value. */
+        LOG((4, "Copying fill value into metadata for variable %s", var->hdr.name));
+        if (var->type_info->nc_type_class == NC_VLEN)
+        {
+            nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value);
+            NC_TYPE_INFO_T* basetype;
+            size_t basetypesize = 0;
+
+            /* get the basetype and its size */
+            basetype = var->type_info;
+            if ((retval = nc4_get_typelen_mem(grp->nc4_info, basetype->hdr.id, &basetypesize)))
+                return retval;
+            /* shallow clone the content of the vlen; shallow because it has only a temporary existence */
+            fv_vlen->len = in_vlen->len;
+            if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len)))
+                return NC_ENOMEM;
+            memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize);
+        }
+        else if (var->type_info->nc_type_class == NC_STRING)
+        {
+            if (*(char **)data)
+            {
+                if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1)))
+                    return NC_ENOMEM;
+                strcpy(*(char **)var->fill_value, *(char **)data);
+            }
+            else
+                *(char **)var->fill_value = NULL;
+        }
+        else
+            memcpy(var->fill_value, data, type_size);
+
+        /* Indicate that the fill value was changed, if the variable has already
+         * been created in the file, so the dataset gets deleted and re-created. */
+        if (var->created)
+            var->fill_val_changed = NC_TRUE;
+    }
+
+    /* Copy the attribute data, if there is any. VLENs and string
+     * arrays have to be handled specially. */
+    if (att->len)
+    {
+        nc_type type_class;    /* Class of attribute's type */
+
+        /* Get class for this type. */
+        if ((retval = nc4_get_typeclass(h5, file_type, &type_class)))
+            return retval;
+
+        assert(data);
+        if (type_class == NC_VLEN)
+        {
+            const hvl_t *vldata1;
+            NC_TYPE_INFO_T *vltype;
+            size_t base_typelen;
+
+            /* Get the type object for the attribute's type */
+            if ((retval = nc4_find_type(h5, file_type, &vltype)))
+                BAIL(retval);
+
+            /* Retrieve the size of the base type */
+            if ((retval = nc4_get_typelen_mem(h5, vltype->u.v.base_nc_typeid, &base_typelen)))
+                BAIL(retval);
+
+            vldata1 = data;
+            if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t))))
+                BAIL(NC_ENOMEM);
+            for (i = 0; i < att->len; i++)
+            {
+                att->vldata[i].len = vldata1[i].len;
+                /* Warning, this only works for cases described for nc_free_vlen() */
+                if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len)))
+                    BAIL(NC_ENOMEM);
+                memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len);
+            }
+        }
+        else if (type_class == NC_STRING)
+        {
+            LOG((4, "copying array of NC_STRING"));
+            if (!(att->stdata = malloc(sizeof(char *) * att->len))) {
+                BAIL(NC_ENOMEM);
+            }
+
+            /* If we are overwriting an existing attribute,
+               specifically an NC_CHAR, we need to clean up
+               the pre-existing att->data. */
+            if (!new_att && att->data) {
+                free(att->data);
+                att->data = NULL;
+            }
+
+            for (i = 0; i < att->len; i++)
+            {
+                if(NULL != ((char **)data)[i]) {
+                    LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1));
+                    if (!(att->stdata[i] = strdup(((char **)data)[i])))
+                        BAIL(NC_ENOMEM);
+                }
+                else
+                    att->stdata[i] = ((char **)data)[i];
+            }
+        }
+        else
+        {
+            /* [Re]allocate memory for the attribute data */
+            if (!new_att)
+                free (att->data);
+            if (!(att->data = malloc(att->len * type_size)))
+                BAIL(NC_ENOMEM);
+
+            /* Just copy the data, for non-atomic types */
+            if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM)
+                memcpy(att->data, data, len * type_size);
+            else
+            {
+                /* Data types are like religions, in that one can convert.  */
+                if ((retval = nc4_convert_type(data, att->data, mem_type, file_type,
+                                               len, &range_error, NULL,
+                                               (h5->cmode & NC_CLASSIC_MODEL))))
+                    BAIL(retval);
+            }
+        }
+    }
+    att->dirty = NC_TRUE;
+    att->created = NC_FALSE;
+
+    /* Mark attributes on variable dirty, so they get written */
+    if(var)
+        var->attr_dirty = NC_TRUE;
+
+exit:
+    /* If there was an error return it, otherwise return any potential
+       range error value. If none, return NC_NOERR as usual.*/
+    if (retval)
+        return retval;
+    if (range_error)
+        return NC_ERANGE;
+    return NC_NOERR;
+}
+
+/**
+ * @internal Write an attribute to a netCDF-4/HDF5 file, converting
+ * data type if necessary.
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param name Name of attribute.
+ * @param file_type Type of the attribute data in file.
+ * @param len Number of elements in attribute array.
+ * @param data Attribute data.
+ * @param mem_type Type of data in memory.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINVAL Invalid parameters.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTVAR Variable not found.
+ * @return ::NC_EBADNAME Name contains illegal characters.
+ * @return ::NC_ENAMEINUSE Name already in use.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_HDF5_put_att(int ncid, int varid, const char *name, nc_type file_type,
+                 size_t len, const void *data, nc_type mem_type)
+{
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp;
+    int ret;
+
+    /* Find info for this file, group, and h5 info. */
+    if ((ret = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5)))
+        return ret;
+    assert(grp && h5);
+
+    return nc4_put_att(grp, varid, name, file_type, len, data, mem_type, 0);
+}
+
+/**
+ * @internal Learn about an att. All the nc4 nc_inq_ functions just
+ * call nc4_get_att to get the metadata on an attribute.
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param name Name of attribute.
+ * @param xtypep Pointer that gets type of attribute.
+ * @param lenp Pointer that gets length of attribute data array.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @author Ed Hartnett
+ */
+int
+NC4_HDF5_inq_att(int ncid, int varid, const char *name, nc_type *xtypep,
+                 size_t *lenp)
+{
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var = NULL;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
+
+    /* Find the file, group, and var info, and do lazy att read if
+     * needed. */
+    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
+                                            &h5, &grp, &var, NULL)))
+        return retval;
+
+    /* If this is one of the reserved atts, use nc_get_att_special. */
+    if (!var)
+    {
+        const NC_reservedatt *ra = NC_findreserved(norm_name);
+        if (ra  && ra->flags & NAMEONLYFLAG)
+            return nc4_get_att_special(h5, norm_name, xtypep, NC_NAT, lenp, NULL,
+                                       NULL);
+    }
+
+    return nc4_get_att_ptrs(h5, grp, var, norm_name, xtypep, NC_NAT,
+                            lenp, NULL, NULL);
+}
+
+/**
+ * @internal Learn an attnum, given a name.
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param name Name of attribute.
+ * @param attnump Pointer that gets the attribute index number.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_HDF5_inq_attid(int ncid, int varid, const char *name, int *attnump)
+{
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var = NULL;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
+
+    /* Find the file, group, and var info, and do lazy att read if
+     * needed. */
+    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
+                                            &h5, &grp, &var, NULL)))
+        return retval;
+
+    /* If this is one of the reserved atts, use nc_get_att_special. */
+    if (!var)
+    {
+        const NC_reservedatt *ra = NC_findreserved(norm_name);
+        if (ra  && ra->flags & NAMEONLYFLAG)
+            return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, attnump,
+                                       NULL);
+    }
+
+    return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, NC_NAT,
+                            NULL, attnump, NULL);
+}
+
+/**
+ * @internal Given an attnum, find the att's name.
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param attnum The index number of the attribute.
+ * @param name Pointer that gets name of attrribute.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @author Ed Hartnett
+ */
+int
+NC4_HDF5_inq_attname(int ncid, int varid, int attnum, char *name)
+{
+    NC_ATT_INFO_T *att;
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
+
+    /* Find the file, group, and var info, and do lazy att read if
+     * needed. */
+    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, NULL, attnum, 0, NULL,
+                                            NULL, NULL, NULL, &att)))
+        return retval;
+    assert(att);
+
+    /* Get the name. */
+    if (name)
+        strcpy(name, att->hdr.name);
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Get an attribute.
+ *
+ * @param ncid File and group ID.
+ * @param varid Variable ID.
+ * @param name Name of attribute.
+ * @param value Pointer that gets attribute data.
+ * @param memtype The type the data should be converted to as it is
+ * read.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @author Ed Hartnett
+ */
+int
+NC4_HDF5_get_att(int ncid, int varid, const char *name, void *value,
+                 nc_type memtype)
+{
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var = NULL;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
+
+    /* Find the file, group, and var info, and do lazy att read if
+     * needed. */
+    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, name, 0, 1, norm_name,
+                                            &h5, &grp, &var, NULL)))
+        return retval;
+
+    /* If this is one of the reserved atts, use nc_get_att_special. */
+    if (!var)
+    {
+        const NC_reservedatt *ra = NC_findreserved(norm_name);
+        if (ra  && ra->flags & NAMEONLYFLAG)
+            return nc4_get_att_special(h5, norm_name, NULL, NC_NAT, NULL, NULL,
+                                       value);
+    }
+
+    return nc4_get_att_ptrs(h5, grp, var, norm_name, NULL, memtype,
+                            NULL, NULL, value);
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5cache.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5cache.c
new file mode 100644
index 0000000000000000000000000000000000000000..5eca998a88bc1fabbeeb7ae724ddc8d3d2a792cf
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5cache.c
@@ -0,0 +1,113 @@
+/* Copyright 2018, University Corporation for Atmospheric
+ * Research. See COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file @internal The netCDF-4 functions which control HDF5
+ * caching. These caching controls allow the user to change the cache
+ * sizes of HDF5 before opening files.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+/* These are the default chunk cache sizes for HDF5 files created or
+ * opened with netCDF-4. */
+extern size_t nc4_chunk_cache_size;
+extern size_t nc4_chunk_cache_nelems;
+extern float nc4_chunk_cache_preemption;
+
+/**
+ * Set chunk cache size. Only affects files opened/created *after* it
+ * is called.
+ *
+ * @param size Size in bytes to set cache.
+ * @param nelems Number of elements to hold in cache.
+ * @param preemption Premption stragety (between 0 and 1).
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINVAL Bad preemption.
+ * @author Ed Hartnett
+ */
+int
+nc_set_chunk_cache(size_t size, size_t nelems, float preemption)
+{
+    if (preemption < 0 || preemption > 1)
+        return NC_EINVAL;
+    nc4_chunk_cache_size = size;
+    nc4_chunk_cache_nelems = nelems;
+    nc4_chunk_cache_preemption = preemption;
+    return NC_NOERR;
+}
+
+/**
+ * Get chunk cache size. Only affects files opened/created *after* it
+ * is called.
+ *
+ * @param sizep Pointer that gets size in bytes to set cache.
+ * @param nelemsp Pointer that gets number of elements to hold in cache.
+ * @param preemptionp Pointer that gets premption stragety (between 0 and 1).
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+nc_get_chunk_cache(size_t *sizep, size_t *nelemsp, float *preemptionp)
+{
+    if (sizep)
+        *sizep = nc4_chunk_cache_size;
+
+    if (nelemsp)
+        *nelemsp = nc4_chunk_cache_nelems;
+
+    if (preemptionp)
+        *preemptionp = nc4_chunk_cache_preemption;
+    return NC_NOERR;
+}
+
+/**
+ * @internal Set the chunk cache. Required for fortran to avoid size_t
+ * issues.
+ *
+ * @param size Cache size.
+ * @param nelems Number of elements.
+ * @param preemption Preemption * 100.
+ *
+ * @return NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+nc_set_chunk_cache_ints(int size, int nelems, int preemption)
+{
+    if (size <= 0 || nelems <= 0 || preemption < 0 || preemption > 100)
+        return NC_EINVAL;
+    nc4_chunk_cache_size = size;
+    nc4_chunk_cache_nelems = nelems;
+    nc4_chunk_cache_preemption = (float)preemption / 100;
+    return NC_NOERR;
+}
+
+/**
+ * @internal Get the chunk cache settings. Required for fortran to
+ * avoid size_t issues.
+ *
+ * @param sizep Pointer that gets cache size.
+ * @param nelemsp Pointer that gets number of elements.
+ * @param preemptionp Pointer that gets preemption * 100.
+ *
+ * @return NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+nc_get_chunk_cache_ints(int *sizep, int *nelemsp, int *preemptionp)
+{
+    if (sizep)
+        *sizep = (int)nc4_chunk_cache_size;
+    if (nelemsp)
+        *nelemsp = (int)nc4_chunk_cache_nelems;
+    if (preemptionp)
+        *preemptionp = (int)(nc4_chunk_cache_preemption * 100);
+
+    return NC_NOERR;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5create.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5create.c
new file mode 100644
index 0000000000000000000000000000000000000000..268c8a321bd6e529ad884ddb5ea6978973849762
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5create.c
@@ -0,0 +1,305 @@
+/* Copyright 2003-2018, University Corporation for Atmospheric
+ * Research. See COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file
+ * @internal The netCDF-4 file functions relating to file creation.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+/* From hdf5file.c. */
+extern size_t nc4_chunk_cache_size;
+extern size_t nc4_chunk_cache_nelems;
+extern float nc4_chunk_cache_preemption;
+
+/** @internal These flags may not be set for create. */
+static const int ILLEGAL_CREATE_FLAGS = (NC_NOWRITE|NC_MMAP|NC_64BIT_OFFSET|NC_CDF5);
+
+/* From nc4mem.c */
+extern int NC4_create_image_file(NC_FILE_INFO_T* h5, size_t);
+
+/**
+ * @internal Create a netCDF-4/HDF5 file.
+ *
+ * @param path The file name of the new file.
+ * @param cmode The creation mode flag.
+ * @param initialsz The proposed initial file size (advisory)
+ * @param parameters extra parameter info (like  MPI communicator)
+ * @param nc Pointer to an instance of NC.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINVAL Invalid input (check cmode).
+ * @return ::NC_EEXIST File exists and NC_NOCLOBBER used.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @ingroup netcdf4
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+nc4_create_file(const char *path, int cmode, size_t initialsz,
+                void* parameters, NC *nc)
+{
+    hid_t fcpl_id, fapl_id = -1;
+    unsigned flags;
+    FILE *fp;
+    int retval = NC_NOERR;
+    NC_FILE_INFO_T* nc4_info = NULL;
+    NC_HDF5_FILE_INFO_T *hdf5_info;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+
+#ifdef USE_PARALLEL4
+    NC_MPI_INFO *mpiinfo = NULL;
+    MPI_Comm comm;
+    MPI_Info info;
+    int comm_duped = 0; /* Whether the MPI Communicator was duplicated */
+    int info_duped = 0; /* Whether the MPI Info object was duplicated */
+#endif /* !USE_PARALLEL4 */
+
+    assert(nc && path);
+    LOG((3, "%s: path %s mode 0x%x", __func__, path, cmode));
+
+    /* Add necessary structs to hold netcdf-4 file data. */
+    if ((retval = nc4_nc4f_list_add(nc, path, (NC_WRITE | cmode))))
+        BAIL(retval);
+    nc4_info = NC4_DATA(nc);
+    assert(nc4_info && nc4_info->root_grp);
+    nc4_info->root_grp->atts_read = 1;
+
+    /* Add struct to hold HDF5-specific file metadata. */
+    if (!(nc4_info->format_file_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T))))
+        BAIL(NC_ENOMEM);
+    hdf5_info = (NC_HDF5_FILE_INFO_T *)nc4_info->format_file_info;
+
+    /* Add struct to hold HDF5-specific group info. */
+    if (!(nc4_info->root_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T))))
+        return NC_ENOMEM;
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)nc4_info->root_grp->format_grp_info;
+
+    nc4_info->mem.inmemory = (cmode & NC_INMEMORY) == NC_INMEMORY;
+    nc4_info->mem.diskless = (cmode & NC_DISKLESS) == NC_DISKLESS;
+    nc4_info->mem.persist =  (cmode & NC_PERSIST) == NC_PERSIST;
+    nc4_info->mem.created = 1;
+    nc4_info->mem.initialsize = initialsz;
+
+    /* diskless => !inmemory */
+    if(nc4_info->mem.inmemory && nc4_info->mem.diskless)
+        BAIL(NC_EINTERNAL);
+
+    if(nc4_info->mem.inmemory && parameters)
+        nc4_info->mem.memio = *(NC_memio*)parameters;
+#ifdef USE_PARALLEL4
+    else if(parameters) {
+        mpiinfo = (NC_MPI_INFO *)parameters;
+        comm = mpiinfo->comm;
+        info = mpiinfo->info;
+    }
+#endif
+    if(nc4_info->mem.diskless)
+        flags = H5F_ACC_TRUNC;
+    else if(cmode & NC_NOCLOBBER)
+        flags = H5F_ACC_EXCL;
+    else
+        flags = H5F_ACC_TRUNC;
+
+    /* If this file already exists, and NC_NOCLOBBER is specified,
+       return an error (unless diskless|inmemory) */
+    if (!nc4_info->mem.diskless && !nc4_info->mem.inmemory) {
+        if ((cmode & NC_NOCLOBBER) && (fp = fopen(path, "r"))) {
+            fclose(fp);
+            BAIL(NC_EEXIST);
+        }
+    }
+
+    /* Need this access plist to control how HDF5 handles open objects
+     * on file close. Setting H5F_CLOSE_SEMI will cause H5Fclose to
+     * fail if there are any open objects in the file. */
+    if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+        BAIL(NC_EHDFERR);
+    if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI))
+        BAIL(NC_EHDFERR);
+
+#ifdef USE_PARALLEL4
+    /* If this is a parallel file create, set up the file creation
+       property list. */
+    if (mpiinfo != NULL) {
+        nc4_info->parallel = NC_TRUE;
+        LOG((4, "creating parallel file with MPI/IO"));
+        if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0)
+            BAIL(NC_EPARINIT);
+
+        /* Keep copies of the MPI Comm & Info objects */
+        if (MPI_SUCCESS != MPI_Comm_dup(comm, &nc4_info->comm))
+            BAIL(NC_EMPI);
+        comm_duped++;
+        if (MPI_INFO_NULL != info)
+        {
+            if (MPI_SUCCESS != MPI_Info_dup(info, &nc4_info->info))
+                BAIL(NC_EMPI);
+            info_duped++;
+        }
+        else
+        {
+            /* No dup, just copy it. */
+            nc4_info->info = info;
+        }
+    }
+#else /* only set cache for non-parallel... */
+    if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
+                     nc4_chunk_cache_preemption) < 0)
+        BAIL(NC_EHDFERR);
+    LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f",
+         __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems,
+         nc4_chunk_cache_preemption));
+#endif /* USE_PARALLEL4 */
+
+#ifdef HDF5_HAS_LIBVER_BOUNDS
+#if H5_VERSION_GE(1,10,2)
+    if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_EARLIEST, H5F_LIBVER_V18) < 0)
+#else
+        if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_EARLIEST,
+                                 H5F_LIBVER_LATEST) < 0)
+#endif
+            BAIL(NC_EHDFERR);
+#endif
+
+    /* Create the property list. */
+    if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* RJ: this suppose to be FALSE that is defined in H5 private.h as 0 */
+    if (H5Pset_obj_track_times(fcpl_id,0)<0)
+        BAIL(NC_EHDFERR);
+
+    /* Set latest_format in access propertly list and
+     * H5P_CRT_ORDER_TRACKED in the creation property list. This turns
+     * on HDF5 creation ordering. */
+    if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
+                                             H5P_CRT_ORDER_INDEXED)) < 0)
+        BAIL(NC_EHDFERR);
+    if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
+                                             H5P_CRT_ORDER_INDEXED)) < 0)
+        BAIL(NC_EHDFERR);
+
+#ifdef HDF5_HAS_COLL_METADATA_OPS
+    /* If HDF5 supports collective metadata operations, turn them
+     * on. This is only relevant for parallel I/O builds of HDF5. */
+    if (H5Pset_all_coll_metadata_ops(fapl_id, 1) < 0)
+        BAIL(NC_EHDFERR);
+    if (H5Pset_coll_metadata_write(fapl_id, 1) < 0)
+        BAIL(NC_EHDFERR);
+#endif
+
+    if(nc4_info->mem.inmemory) {
+        retval = NC4_create_image_file(nc4_info,initialsz);
+        if(retval)
+            BAIL(retval);
+    }
+    else
+        if(nc4_info->mem.diskless) {
+            size_t alloc_incr;     /* Buffer allocation increment */
+            size_t min_incr = 65536; /* Minimum buffer increment */
+            double buf_prcnt = 0.1f;  /* Percentage of buffer size to set as increment */
+            /* set allocation increment to a percentage of the supplied buffer size, or
+             * a pre-defined minimum increment value, whichever is larger
+             */
+            if ((buf_prcnt * initialsz) > min_incr)
+                alloc_incr = (size_t)(buf_prcnt * initialsz);
+            else
+                alloc_incr = min_incr;
+            /* Configure FAPL to use the core file driver */
+            if (H5Pset_fapl_core(fapl_id, alloc_incr, (nc4_info->mem.persist?1:0)) < 0)
+                BAIL(NC_EHDFERR);
+            if ((hdf5_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0)
+                BAIL(EACCES);
+        }
+        else /* Normal file */
+        {
+            /* Create the HDF5 file. */
+            if ((hdf5_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0)
+                BAIL(EACCES);
+        }
+
+    /* Open the root group. */
+    if ((hdf5_grp->hdf_grpid = H5Gopen2(hdf5_info->hdfid, "/", H5P_DEFAULT)) < 0)
+        BAIL(NC_EFILEMETA);
+
+    /* Release the property lists. */
+    if (H5Pclose(fapl_id) < 0 || H5Pclose(fcpl_id) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Define mode gets turned on automatically on create. */
+    nc4_info->flags |= NC_INDEF;
+
+    /* Save the HDF5 superblock number and set the _NCProperties attribute. */
+    if ((retval = NC4_new_provenance(nc4_info)))
+        BAIL(retval);
+
+    return NC_NOERR;
+
+exit: /*failure exit*/
+#ifdef USE_PARALLEL4
+    if (comm_duped) MPI_Comm_free(&nc4_info->comm);
+    if (info_duped) MPI_Info_free(&nc4_info->info);
+#endif
+    if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
+    if(!nc4_info) return retval;
+    nc4_close_hdf5_file(nc4_info, 1, NULL); /* treat like abort */
+    return retval;
+}
+
+/**
+ * @internal Create a netCDF-4/HDF5 file.
+ *
+ * @param path The file name of the new file.
+ * @param cmode The creation mode flag.
+ * @param initialsz Ignored by this function.
+ * @param basepe Ignored by this function.
+ * @param chunksizehintp Ignored by this function.
+ * @param parameters pointer to struct holding extra data (e.g. for
+ * parallel I/O) layer. Ignored if NULL.
+ * @param dispatch Pointer to the dispatch table for this file.
+ * @param nc_file Pointer to an instance of NC.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINVAL Invalid input (check cmode).
+ * @ingroup netcdf4
+ * @author Ed Hartnett
+ */
+int
+NC4_create(const char* path, int cmode, size_t initialsz, int basepe,
+           size_t *chunksizehintp, void *parameters,
+           NC_Dispatch *dispatch, NC *nc_file)
+{
+    int res;
+
+    assert(nc_file && path);
+
+    LOG((1, "%s: path %s cmode 0x%x parameters %p",
+         __func__, path, cmode, parameters));
+
+    /* If this is our first file, turn off HDF5 error messages. */
+    if (!nc4_hdf5_initialized)
+        nc4_hdf5_initialize();
+
+#ifdef LOGGING
+    /* If nc logging level has changed, see if we need to turn on
+     * HDF5's error messages. */
+    hdf5_set_log_level();
+#endif /* LOGGING */
+
+    /* Check the cmode for validity. */
+    if((cmode & ILLEGAL_CREATE_FLAGS) != 0)
+    {res = NC_EINVAL; goto done;}
+
+    /* check parallel against NC_DISKLESS already done in NC_create() */
+
+    nc_file->int_ncid = nc_file->ext_ncid;
+
+    res = nc4_create_file(path, cmode, initialsz, parameters, nc_file);
+
+done:
+    return res;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dim.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dim.c
new file mode 100644
index 0000000000000000000000000000000000000000..a184f99251aa8b9c8704f62a4ae59f22c238824b
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dim.c
@@ -0,0 +1,308 @@
+/* Copyright 2003-2019, University Corporation for Atmospheric
+ * Research. See the COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file @internal This file is part of netcdf-4, a netCDF-like
+ * interface for HDF5, or a HDF5 backend for netCDF, depending on your
+ * point of view.
+ *
+ * This file includes the HDF5 code to deal with dimensions.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+/**
+ * @internal Dimensions are defined in attributes attached to the
+ * appropriate group in the data file.
+ *
+ * @param ncid File and group ID.
+ * @param name Name of the new dimension.
+ * @param len Length of the new dimension.
+ * @param idp Pointer that gets the ID of the new dimension. Ignored
+ * if NULL.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @return ::NC_EINVAL Invalid input.
+ * @return ::NC_EPERM Read-only file.
+ * @return ::NC_EUNLIMIT Only one unlimited dim for classic model.
+ * @return ::NC_ENOTINDEFINE Not in define mode.
+ * @return ::NC_EDIMSIZE Dim length too large.
+ * @return ::NC_ENAMEINUSE Name already in use in group.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+int
+NC4_def_dim(int ncid, const char *name, size_t len, int *idp)
+{
+    NC *nc;
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    NC_DIM_INFO_T *dim;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval = NC_NOERR;
+    int i;
+
+    LOG((2, "%s: ncid 0x%x name %s len %d", __func__, ncid, name,
+         (int)len));
+
+    /* Find our global metadata structure. */
+    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
+        return retval;
+    assert(h5 && nc && grp);
+
+    /* If the file is read-only, return an error. */
+    if (h5->no_write)
+        return NC_EPERM;
+
+    /* Check some stuff if strict nc3 rules are in effect. */
+    if (h5->cmode & NC_CLASSIC_MODEL)
+    {
+        /* Only one limited dimenson for strict nc3. */
+        if (len == NC_UNLIMITED) {
+            for(i=0;i<ncindexsize(grp->dim);i++) {
+                dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
+                if(dim == NULL) continue;
+                if (dim->unlimited)
+                    return NC_EUNLIMIT;
+            }
+        }
+        /* Must be in define mode for stict nc3. */
+        if (!(h5->flags & NC_INDEF))
+            return NC_ENOTINDEFINE;
+    }
+
+    /* Make sure this is a valid netcdf name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        return retval;
+
+    /* For classic model: dim length has to fit in a 32-bit unsigned
+     * int, as permitted for 64-bit offset format. */
+    if (h5->cmode & NC_CLASSIC_MODEL)
+        if(len > X_UINT_MAX) /* Backward compat */
+            return NC_EDIMSIZE;
+
+    /* Make sure the name is not already in use. */
+    dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name);
+    if(dim != NULL)
+        return NC_ENAMEINUSE;
+
+    /* If it's not in define mode, enter define mode. Do this only
+     * after checking all input data, so we only enter define mode if
+     * input is good. */
+    if (!(h5->flags & NC_INDEF))
+        if ((retval = NC4_redef(ncid)))
+            return retval;
+
+    /* Add a dimension to the list. The ID must come from the file
+     * information, since dimids are visible in more than one group. */
+    if ((retval = nc4_dim_list_add(grp, norm_name, len, -1, &dim)))
+        return retval;
+
+    /* Create struct for HDF5-specific dim info. */
+    if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
+        return NC_ENOMEM;
+
+    /* Pass back the dimid. */
+    if (idp)
+        *idp = dim->hdr.id;
+
+    return retval;
+}
+
+/**
+ * @internal Find out name and len of a dim. For an unlimited
+ * dimension, the length is the largest length so far written. If the
+ * name of lenp pointers are NULL, they will be ignored.
+ *
+ * @param ncid File and group ID.
+ * @param dimid Dimension ID.
+ * @param name Pointer that gets name of the dimension.
+ * @param lenp Pointer that gets length of dimension.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EDIMSIZE Dimension length too large.
+ * @return ::NC_EBADDIM Dimension not found.
+ * @author Ed Hartnett
+ */
+int
+NC4_inq_dim(int ncid, int dimid, char *name, size_t *lenp)
+{
+    NC *nc;
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp, *dim_grp;
+    NC_DIM_INFO_T *dim;
+    int ret = NC_NOERR;
+
+    LOG((2, "%s: ncid 0x%x dimid %d", __func__, ncid, dimid));
+
+    /* Find our global metadata structure. */
+    if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
+        return ret;
+    assert(h5 && nc && grp);
+
+    /* Find the dimension and its home group. */
+    if ((ret = nc4_find_dim(grp, dimid, &dim, &dim_grp)))
+        return ret;
+    assert(dim);
+
+    /* Return the dimension name, if the caller wants it. */
+    if (name && dim->hdr.name)
+        strcpy(name, dim->hdr.name);
+
+    /* Return the dimension length, if the caller wants it. */
+    if (lenp)
+    {
+        if (dim->unlimited)
+        {
+            /* Since this is an unlimited dimension, go to the file
+               and see how many records there are. Take the max number
+               of records from all the vars that share this
+               dimension. */
+            *lenp = 0;
+            if ((ret = nc4_find_dim_len(dim_grp, dimid, &lenp)))
+                return ret;
+        }
+        else
+        {
+            if (dim->too_long)
+            {
+                ret = NC_EDIMSIZE;
+                *lenp = NC_MAX_UINT;
+            }
+            else
+                *lenp = dim->len;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @internal Rename a dimension, for those who like to prevaricate.
+ *
+ * @note If we're not in define mode, new name must be of equal or
+ * less size, if strict nc3 rules are in effect for this file. But we
+ * don't check this because reproducing the exact classic behavior
+ * would be too difficult. See github issue #1340.
+ *
+ * @param ncid File and group ID.
+ * @param dimid Dimension ID.
+ * @param name New dimension name.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EINVAL Name must be provided.
+ * @return ::NC_ENAMEINUSE Name is already in use in group.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADDIM Dimension not found.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @return ::NC_EDIMMETA Unable to delete HDF5 dataset.
+ * @author Ed Hartnett
+ */
+int
+NC4_rename_dim(int ncid, int dimid, const char *name)
+{
+    NC_GRP_INFO_T *grp;
+    NC_DIM_INFO_T *dim;
+    NC_HDF5_DIM_INFO_T *hdf5_dim;
+    NC_FILE_INFO_T *h5;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    /* Note: name is new name */
+    if (!name)
+        return NC_EINVAL;
+
+    LOG((2, "%s: ncid 0x%x dimid %d name %s", __func__, ncid,
+         dimid, name));
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        return retval;
+    assert(h5 && grp);
+
+    /* Trying to write to a read-only file? No way, Jose! */
+    if (h5->no_write)
+        return NC_EPERM;
+
+    /* Make sure this is a valid netcdf name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        return retval;
+
+    /* Get the original dim. */
+    if ((retval = nc4_find_dim(grp, dimid, &dim, NULL)))
+        return retval;
+    assert(dim && dim->format_dim_info);
+    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+    /* Check if new name is in use. */
+    if (ncindexlookup(grp->dim, norm_name))
+        return NC_ENAMEINUSE;
+
+    /* Check for renaming dimension w/o variable. */
+    if (hdf5_dim->hdf_dimscaleid)
+    {
+        assert(!dim->coord_var);
+        LOG((3, "dim %s is a dim without variable", dim->hdr.name));
+
+        /* Delete the dimscale-only dataset. */
+        if ((retval = delete_dimscale_dataset(grp, dimid, dim)))
+            return retval;
+    }
+
+    /* Give the dimension its new name in metadata. UTF8 normalization
+     * has been done. */
+    assert(dim->hdr.name);
+    free(dim->hdr.name);
+    if (!(dim->hdr.name = strdup(norm_name)))
+        return NC_ENOMEM;
+    LOG((3, "dim is now named %s", dim->hdr.name));
+
+    /* Fix hash key and rebuild index. */
+    dim->hdr.hashkey = NC_hashmapkey(dim->hdr.name,strlen(dim->hdr.name));
+    if (!ncindexrebuild(grp->dim))
+        return NC_EINTERNAL;
+
+    /* Check if dimension was a coordinate variable, but names are
+     * different now. */
+    if (dim->coord_var && strcmp(dim->hdr.name, dim->coord_var->hdr.name))
+    {
+        /* Break up the coordinate variable. */
+        if ((retval = nc4_break_coord_var(grp, dim->coord_var, dim)))
+            return retval;
+    }
+
+    /* Check if dimension should become a coordinate variable. */
+    if (!dim->coord_var)
+    {
+        NC_VAR_INFO_T *var;
+
+        /* Attempt to find a variable with the same name as the
+         * dimension in the current group. */
+        if ((retval = nc4_find_var(grp, dim->hdr.name, &var)))
+            return retval;
+
+        /* Check if we found a variable and the variable has the
+         * dimension in index 0. */
+        if (var && var->dim[0] == dim)
+        {
+            /* Sanity check */
+            assert(var->dimids[0] == dim->hdr.id);
+
+            /* Reform the coordinate variable. */
+            if ((retval = nc4_reform_coord_var(grp, var, dim)))
+                return retval;
+        }
+    }
+
+    return NC_NOERR;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dispatch.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dispatch.c
new file mode 100644
index 0000000000000000000000000000000000000000..eec6fdb9e4b8d61ad2bf8be383df5f8ad4827433
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5dispatch.c
@@ -0,0 +1,144 @@
+/* Copyright 2005-2018 University Corporation for Atmospheric
+   Research/Unidata. */
+/**
+ * @file
+ * @internal This header file contains prototypes and initialization
+ * for the HDF5 dispatch layer.
+ *
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+#ifdef ENABLE_BYTERANGE
+#include "H5FDhttp.h"
+#endif
+
+static NC_Dispatch NC4_dispatcher = {
+
+    NC_FORMATX_NC4,
+
+    NC4_create,
+    NC4_open,
+
+    NC4_redef,
+    NC4__enddef,
+    NC4_sync,
+    NC4_abort,
+    NC4_close,
+    NC4_set_fill,
+    NC_NOTNC3_inq_base_pe,
+    NC_NOTNC3_set_base_pe,
+    NC4_inq_format,
+    NC4_inq_format_extended,
+
+    NC4_inq,
+    NC4_inq_type,
+
+    NC4_def_dim,
+    NC4_inq_dimid,
+    NC4_inq_dim,
+    NC4_inq_unlimdim,
+    NC4_rename_dim,
+
+    NC4_HDF5_inq_att,
+    NC4_HDF5_inq_attid,
+    NC4_HDF5_inq_attname,
+    NC4_HDF5_rename_att,
+    NC4_HDF5_del_att,
+    NC4_HDF5_get_att,
+    NC4_HDF5_put_att,
+
+    NC4_def_var,
+    NC4_inq_varid,
+    NC4_rename_var,
+    NC4_get_vara,
+    NC4_put_vara,
+    NC4_get_vars,
+    NC4_put_vars,
+    NCDEFAULT_get_varm,
+    NCDEFAULT_put_varm,
+
+    NC4_HDF5_inq_var_all,
+
+    NC4_var_par_access,
+    NC4_def_var_fill,
+
+#ifdef USE_NETCDF4
+    NC4_show_metadata,
+    NC4_inq_unlimdims,
+
+    NC4_inq_ncid,
+    NC4_inq_grps,
+    NC4_inq_grpname,
+    NC4_inq_grpname_full,
+    NC4_inq_grp_parent,
+    NC4_inq_grp_full_ncid,
+    NC4_inq_varids,
+    NC4_inq_dimids,
+    NC4_inq_typeids,
+    NC4_inq_type_equal,
+    NC4_def_grp,
+    NC4_rename_grp,
+    NC4_inq_user_type,
+    NC4_inq_typeid,
+
+    NC4_def_compound,
+    NC4_insert_compound,
+    NC4_insert_array_compound,
+    NC4_inq_compound_field,
+    NC4_inq_compound_fieldindex,
+    NC4_def_vlen,
+    NC4_put_vlen_element,
+    NC4_get_vlen_element,
+    NC4_def_enum,
+    NC4_insert_enum,
+    NC4_inq_enum_member,
+    NC4_inq_enum_ident,
+    NC4_def_opaque,
+    NC4_def_var_deflate,
+    NC4_def_var_fletcher32,
+    NC4_def_var_chunking,
+    NC4_def_var_endian,
+    NC4_def_var_filter,
+    NC4_HDF5_set_var_chunk_cache,
+    NC4_get_var_chunk_cache,
+#endif
+
+};
+
+NC_Dispatch* HDF5_dispatch_table = NULL; /* moved here from ddispatch.c */
+
+/**
+ * @internal Initialize the HDF5 dispatch layer.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC_HDF5_initialize(void)
+{
+    HDF5_dispatch_table = &NC4_dispatcher;
+
+    if (!nc4_hdf5_initialized)
+        nc4_hdf5_initialize();
+
+#ifdef ENABLE_BYTERANGE
+    (void)H5FD_http_init();
+#endif  
+    return NC4_provenance_init();
+}
+
+/**
+ * @internal Finalize the HDF5 dispatch layer.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+int
+NC_HDF5_finalize(void)
+{
+    (void)nc4_hdf5_finalize();
+    return NC_NOERR;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5file.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5file.c
new file mode 100644
index 0000000000000000000000000000000000000000..1b110a2812f3340c5af8e8c8beb9b6fb2d864bfc
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5file.c
@@ -0,0 +1,783 @@
+/* Copyright 2003-2018, University Corporation for Atmospheric
+ * Research. See COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file
+ * @internal The netCDF-4 file functions.
+ *
+ * This file is part of netcdf-4, a netCDF-like interface for HDF5, or
+ * a HDF5 backend for netCDF, depending on your point of view.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+#include "ncrc.h"
+
+extern int NC4_extract_file_image(NC_FILE_INFO_T* h5); /* In nc4memcb.c */
+
+static void dumpopenobjects(NC_FILE_INFO_T* h5);
+
+/** @internal When we have open objects at file close, should
+    we log them or print to stdout. Default is to log. */
+#define LOGOPEN 1
+
+/** @internal Number of reserved attributes. These attributes are
+ * hidden from the netcdf user, but exist in the HDF5 file to help
+ * netcdf read the file. */
+#define NRESERVED 11 /*|NC_reservedatt|*/
+
+/** @internal List of reserved attributes. This list must be in sorted
+ * order for binary search. */
+static const NC_reservedatt NC_reserved[NRESERVED] = {
+    {NC_ATT_CLASS, READONLYFLAG|DIMSCALEFLAG},            /*CLASS*/
+    {NC_ATT_DIMENSION_LIST, READONLYFLAG|DIMSCALEFLAG},   /*DIMENSION_LIST*/
+    {NC_ATT_NAME, READONLYFLAG|DIMSCALEFLAG},             /*NAME*/
+    {NC_ATT_REFERENCE_LIST, READONLYFLAG|DIMSCALEFLAG},   /*REFERENCE_LIST*/
+    {NC_ATT_FORMAT, READONLYFLAG},                        /*_Format*/
+    {ISNETCDF4ATT, READONLYFLAG|NAMEONLYFLAG},            /*_IsNetcdf4*/
+    {NCPROPS, READONLYFLAG|NAMEONLYFLAG|MATERIALIZEDFLAG},/*_NCProperties*/
+    {NC_ATT_COORDINATES, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Coordinates*/
+    {NC_DIMID_ATT_NAME, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Dimid*/
+    {SUPERBLOCKATT, READONLYFLAG|NAMEONLYFLAG},/*_SuperblockVersion*/
+    {NC3_STRICT_ATT_NAME, READONLYFLAG|MATERIALIZEDFLAG},  /*_nc3_strict*/
+};
+
+/* Forward */
+static int NC4_enddef(int ncid);
+static void dumpopenobjects(NC_FILE_INFO_T* h5);
+
+/**
+ * @internal Define a binary searcher for reserved attributes
+ * @param name for which to search
+ * @return pointer to the matchig NC_reservedatt structure.
+ * @return NULL if not found.
+ * @author Dennis Heimbigner
+ */
+const NC_reservedatt*
+NC_findreserved(const char* name)
+{
+    int n = NRESERVED;
+    int L = 0;
+    int R = (n - 1);
+    for(;;) {
+        if(L > R) break;
+        int m = (L + R) / 2;
+        const NC_reservedatt* p = &NC_reserved[m];
+        int cmp = strcmp(p->name,name);
+        if(cmp == 0) return p;
+        if(cmp < 0)
+            L = (m + 1);
+        else /*cmp > 0*/
+            R = (m - 1);
+    }
+    return NULL;
+}
+
+/**
+ * @internal Recursively determine if there is a mismatch between
+ * order of coordinate creation and associated dimensions in this
+ * group or any subgroups, to find out if we have to handle that
+ * situation.  Also check if there are any multidimensional coordinate
+ * variables defined, which require the same treatment to fix a
+ * potential bug when such variables occur in subgroups.
+ *
+ * @param grp Pointer to group info struct.
+ * @param bad_coord_orderp Pointer that gets 1 if there is a bad
+ * coordinate order.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+static int
+detect_preserve_dimids(NC_GRP_INFO_T *grp, nc_bool_t *bad_coord_orderp)
+{
+    NC_VAR_INFO_T *var;
+    NC_GRP_INFO_T *child_grp;
+    int last_dimid = -1;
+    int retval;
+    int i;
+
+    /* Iterate over variables in this group */
+    for (i=0; i < ncindexsize(grp->vars); i++)
+    {
+        var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
+        if (var == NULL) continue;
+        /* Only matters for dimension scale variables, with non-scalar dimensionality */
+        if (var->dimscale && var->ndims)
+        {
+            /* If the user writes coord vars in a different order then he
+             * defined their dimensions, then, when the file is reopened, the
+             * order of the dimids will change to match the order of the coord
+             * vars. Detect if this is about to happen. */
+            if (var->dimids[0] < last_dimid)
+            {
+                LOG((5, "%s: %s is out of order coord var", __func__, var->hdr.name));
+                *bad_coord_orderp = NC_TRUE;
+                return NC_NOERR;
+            }
+            last_dimid = var->dimids[0];
+
+            /* If there are multidimensional coordinate variables defined, then
+             * it's also necessary to preserve dimension IDs when the file is
+             * reopened ... */
+            if (var->ndims > 1)
+            {
+                LOG((5, "%s: %s is multidimensional coord var", __func__, var->hdr.name));
+                *bad_coord_orderp = NC_TRUE;
+                return NC_NOERR;
+            }
+
+            /* Did the user define a dimension, end define mode, reenter define
+             * mode, and then define a coordinate variable for that dimension?
+             * If so, dimensions will be out of order. */
+            if (var->is_new_var || var->became_coord_var)
+            {
+                LOG((5, "%s: coord var defined after enddef/redef", __func__));
+                *bad_coord_orderp = NC_TRUE;
+                return NC_NOERR;
+            }
+        }
+    }
+
+    /* If there are any child groups, check them also for this condition. */
+    for (i = 0; i < ncindexsize(grp->children); i++)
+    {
+        if (!(child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i)))
+            continue;
+        if ((retval = detect_preserve_dimids(child_grp, bad_coord_orderp)))
+            return retval;
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function will write all changed metadata and flush
+ * HDF5 file to disk.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINDEFINE Classic model file in define mode.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+static int
+sync_netcdf4_file(NC_FILE_INFO_T *h5)
+{
+    NC_HDF5_FILE_INFO_T *hdf5_info;
+    int retval;
+
+    assert(h5 && h5->format_file_info);
+    LOG((3, "%s", __func__));
+
+    /* If we're in define mode, that's an error, for strict nc3 rules,
+     * otherwise, end define mode. */
+    if (h5->flags & NC_INDEF)
+    {
+        if (h5->cmode & NC_CLASSIC_MODEL)
+            return NC_EINDEFINE;
+
+        /* Turn define mode off. */
+        h5->flags ^= NC_INDEF;
+
+        /* Redef mode needs to be tracked separately for nc_abort. */
+        h5->redef = NC_FALSE;
+    }
+
+#ifdef LOGGING
+    /* This will print out the names, types, lens, etc of the vars and
+       atts in the file, if the logging level is 2 or greater. */
+    log_metadata_nc(h5);
+#endif
+
+    /* Write any metadata that has changed. */
+    if (!h5->no_write)
+    {
+        nc_bool_t bad_coord_order = NC_FALSE;
+
+        /* Write any user-defined types. */
+        if ((retval = nc4_rec_write_groups_types(h5->root_grp)))
+            return retval;
+
+        /* Check to see if the coordinate order is messed up. If
+         * detected, propagate to all groups to consistently store
+         * dimids. */
+        if ((retval = detect_preserve_dimids(h5->root_grp, &bad_coord_order)))
+            return retval;
+
+        /* Write all the metadata. */
+        if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order)))
+            return retval;
+
+        /* Write out provenance*/
+        if((retval = NC4_write_provenance(h5)))
+            return retval;
+    }
+
+    /* Tell HDF5 to flush all changes to the file. */
+    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
+    if (H5Fflush(hdf5_info->hdfid, H5F_SCOPE_GLOBAL) < 0)
+        return NC_EHDFERR;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function will free all allocated metadata memory,
+ * and close the HDF5 file. The group that is passed in must be the
+ * root group of the file. If inmemory is used, then save
+ * the final memory in mem.memio.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param abort True if this is an abort.
+ * @param memio the place to return a core image if not NULL
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 could not close the file.
+ * @return ::NC_EINDEFINE Classic model file is in define mode.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+nc4_close_netcdf4_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio)
+{
+    NC_HDF5_FILE_INFO_T *hdf5_info;
+    int retval;
+
+    assert(h5 && h5->root_grp && h5->format_file_info);
+    LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort));
+
+    /* Get HDF5 specific info. */
+    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
+
+    /* Delete all the list contents for vars, dims, and atts, in each
+     * group. */
+    if ((retval = nc4_rec_grp_del(h5->root_grp)))
+        return retval;
+
+    /* Free lists of dims, groups, and types in the root group. */
+    nclistfree(h5->alldims);
+    nclistfree(h5->allgroups);
+    nclistfree(h5->alltypes);
+
+#ifdef USE_PARALLEL4
+    /* Free the MPI Comm & Info objects, if we opened the file in
+     * parallel. */
+    if (h5->parallel)
+    {
+        if (h5->comm != MPI_COMM_NULL)
+            MPI_Comm_free(&h5->comm);
+        if (h5->info != MPI_INFO_NULL)
+            MPI_Info_free(&h5->info);
+    }
+#endif
+
+    /* Free the fileinfo struct, which holds info from the fileinfo
+     * hidden attribute. */
+    NC4_clear_provenance(&h5->provenance);
+
+    /* Close hdf file. It may not be open, since this function is also
+     * called by NC_create() when a file opening is aborted. */
+    if (hdf5_info->hdfid > 0 && H5Fclose(hdf5_info->hdfid) < 0)
+    {
+        dumpopenobjects(h5);
+        return NC_EHDFERR;
+    }
+
+    /* If inmemory is used and user wants the final memory block,
+       then capture and return the final memory block else free it */
+    if(h5->mem.inmemory) {
+        /* Pull out the final memory */
+        (void)NC4_extract_file_image(h5);
+        if(!abort && memio != NULL) {
+            *memio = h5->mem.memio; /* capture it */
+            h5->mem.memio.memory = NULL; /* avoid duplicate free */
+        }
+        /* If needed, reclaim extraneous memory */
+        if(h5->mem.memio.memory != NULL) {
+            /* If the original block of memory is not resizeable, then
+               it belongs to the caller and we should not free it. */
+            if(!h5->mem.locked)
+                free(h5->mem.memio.memory);
+        }
+        h5->mem.memio.memory = NULL;
+        h5->mem.memio.size = 0;
+        NC4_image_finalize(h5->mem.udata);
+    }
+
+    /* Free the HDF5-specific info. */
+    if (h5->format_file_info)
+        free(h5->format_file_info);
+
+    /* Free the nc4_info struct; above code should have reclaimed
+       everything else */
+    free(h5);
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function will recurse through an open HDF5 file and
+ * release resources. All open HDF5 objects in the file will be
+ * closed.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param abort True if this is an abort.
+ * @param memio the place to return a core image if not NULL
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 could not close the file.
+ * @author Ed Hartnett
+ */
+int
+nc4_close_hdf5_file(NC_FILE_INFO_T *h5, int abort,  NC_memio *memio)
+{
+    int retval;
+
+    assert(h5 && h5->root_grp && h5->format_file_info);
+    LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort));
+
+    /* According to the docs, always end define mode on close. */
+    if (h5->flags & NC_INDEF)
+        h5->flags ^= NC_INDEF;
+
+    /* Sync the file, unless we're aborting, or this is a read-only
+     * file. */
+    if (!h5->no_write && !abort)
+        if ((retval = sync_netcdf4_file(h5)))
+            return retval;
+
+    /* Close all open HDF5 objects within the file. */
+    if ((retval = nc4_rec_grp_HDF5_del(h5->root_grp)))
+        return retval;
+
+    /* Release all intarnal lists and metadata associated with this
+     * file. All HDF5 objects have already been released. */
+    if ((retval = nc4_close_netcdf4_file(h5, abort, memio)))
+        return retval;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Output a list of still-open objects in the HDF5
+ * file. This is only called if the file fails to close cleanly.
+ *
+ * @param h5 Pointer to file info.
+ *
+ * @author Dennis Heimbigner
+ */
+static void
+dumpopenobjects(NC_FILE_INFO_T* h5)
+{
+    NC_HDF5_FILE_INFO_T *hdf5_info;
+    int nobjs;
+
+    assert(h5 && h5->format_file_info);
+    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
+
+    if(hdf5_info->hdfid <= 0)
+        return; /* File was never opened */
+
+    nobjs = H5Fget_obj_count(hdf5_info->hdfid, H5F_OBJ_ALL);
+
+    /* Apparently we can get an error even when nobjs == 0 */
+    if(nobjs < 0) {
+        return;
+    } else if(nobjs > 0) {
+        char msg[1024];
+        int logit = 0;
+        /* If the close doesn't work, probably there are still some HDF5
+         * objects open, which means there's a bug in the library. So
+         * print out some info on to help the poor programmer figure it
+         * out. */
+        snprintf(msg,sizeof(msg),"There are %d HDF5 objects open!", nobjs);
+#ifdef LOGGING
+#ifdef LOGOPEN
+        LOG((0, msg));
+        logit = 1;
+#endif
+#else
+        fprintf(stdout,"%s\n",msg);
+        logit = 0;
+#endif
+        reportopenobjects(logit,hdf5_info->hdfid);
+        fflush(stderr);
+    }
+
+    return;
+}
+
+/**
+ * @internal Unfortunately HDF only allows specification of fill value
+ * only when a dataset is created. Whereas in netcdf, you first create
+ * the variable and then (optionally) specify the fill value. To
+ * accomplish this in HDF5 I have to delete the dataset, and recreate
+ * it, with the fill value specified.
+ *
+ * @param ncid File and group ID.
+ * @param fillmode File mode.
+ * @param old_modep Pointer that gets old mode. Ignored if NULL.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_set_fill(int ncid, int fillmode, int *old_modep)
+{
+    NC_FILE_INFO_T *nc4_info;
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x fillmode %d", __func__, ncid, fillmode));
+
+    /* Get pointer to file info. */
+    if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
+        return retval;
+    assert(nc4_info);
+
+    /* Trying to set fill on a read-only file? You sicken me! */
+    if (nc4_info->no_write)
+        return NC_EPERM;
+
+    /* Did you pass me some weird fillmode? */
+    if (fillmode != NC_FILL && fillmode != NC_NOFILL)
+        return NC_EINVAL;
+
+    /* If the user wants to know, tell him what the old mode was. */
+    if (old_modep)
+        *old_modep = nc4_info->fill_mode;
+
+    nc4_info->fill_mode = fillmode;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Put the file back in redef mode. This is done
+ * automatically for netcdf-4 files, if the user forgets.
+ *
+ * @param ncid File and group ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_redef(int ncid)
+{
+    NC_FILE_INFO_T *nc4_info;
+    int retval;
+
+    LOG((1, "%s: ncid 0x%x", __func__, ncid));
+
+    /* Find this file's metadata. */
+    if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
+        return retval;
+    assert(nc4_info);
+
+    /* If we're already in define mode, return an error. */
+    if (nc4_info->flags & NC_INDEF)
+        return NC_EINDEFINE;
+
+    /* If the file is read-only, return an error. */
+    if (nc4_info->no_write)
+        return NC_EPERM;
+
+    /* Set define mode. */
+    nc4_info->flags |= NC_INDEF;
+
+    /* For nc_abort, we need to remember if we're in define mode as a
+       redef. */
+    nc4_info->redef = NC_TRUE;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal For netcdf-4 files, this just calls nc_enddef, ignoring
+ * the extra parameters.
+ *
+ * @param ncid File and group ID.
+ * @param h_minfree Ignored for netCDF-4 files.
+ * @param v_align Ignored for netCDF-4 files.
+ * @param v_minfree Ignored for netCDF-4 files.
+ * @param r_align Ignored for netCDF-4 files.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4__enddef(int ncid, size_t h_minfree, size_t v_align,
+            size_t v_minfree, size_t r_align)
+{
+    return NC4_enddef(ncid);
+}
+
+/**
+ * @internal Take the file out of define mode. This is called
+ * automatically for netcdf-4 files, if the user forgets.
+ *
+ * @param ncid File and group ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EBADGRPID Bad group ID.
+ * @author Ed Hartnett
+ */
+static int
+NC4_enddef(int ncid)
+{
+    NC_FILE_INFO_T *nc4_info;
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var;
+    int i;
+    int retval;
+
+    LOG((1, "%s: ncid 0x%x", __func__, ncid));
+
+    /* Find pointer to group and nc4_info. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &nc4_info)))
+        return retval;
+
+    /* When exiting define mode, mark all variable written. */
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+    {
+        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
+        assert(var);
+        var->written_to = NC_TRUE;
+    }
+
+    return nc4_enddef_netcdf4_file(nc4_info);
+}
+
+/**
+ * @internal Flushes all buffers associated with the file, after
+ * writing all changed metadata. This may only be called in data mode.
+ *
+ * @param ncid File and group ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EINDEFINE Classic model file is in define mode.
+ * @author Ed Hartnett
+ */
+int
+NC4_sync(int ncid)
+{
+    NC_FILE_INFO_T *nc4_info;
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x", __func__, ncid));
+
+    if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
+        return retval;
+    assert(nc4_info);
+
+    /* If we're in define mode, we can't sync. */
+    if (nc4_info->flags & NC_INDEF)
+    {
+        if (nc4_info->cmode & NC_CLASSIC_MODEL)
+            return NC_EINDEFINE;
+        if ((retval = NC4_enddef(ncid)))
+            return retval;
+    }
+
+    return sync_netcdf4_file(nc4_info);
+}
+
+/**
+ * @internal From the netcdf-3 docs: The function nc_abort just closes
+ * the netCDF dataset, if not in define mode. If the dataset is being
+ * created and is still in define mode, the dataset is deleted. If
+ * define mode was entered by a call to nc_redef, the netCDF dataset
+ * is restored to its state before definition mode was entered and the
+ * dataset is closed.
+ *
+ * @param ncid File and group ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_abort(int ncid)
+{
+    NC *nc;
+    NC_FILE_INFO_T *nc4_info;
+    int delete_file = 0;
+    char path[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x", __func__, ncid));
+
+    /* Find metadata for this file. */
+    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, NULL, &nc4_info)))
+        return retval;
+    assert(nc4_info);
+
+    /* If we're in define mode, but not redefing the file, delete it. */
+    if (nc4_info->flags & NC_INDEF && !nc4_info->redef)
+    {
+        delete_file++;
+        strncpy(path, nc->path, NC_MAX_NAME);
+    }
+
+    /* Free any resources the netcdf-4 library has for this file's
+     * metadata. */
+    if ((retval = nc4_close_hdf5_file(nc4_info, 1, NULL)))
+        return retval;
+
+    /* Delete the file, if we should. */
+    if (delete_file)
+        if (remove(path) < 0)
+            return NC_ECANTREMOVE;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Close the netcdf file, writing any changes first.
+ *
+ * @param ncid File and group ID.
+ * @param params any extra parameters in/out of close
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_close(int ncid, void* params)
+{
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    int retval;
+    int inmemory;
+    NC_memio* memio = NULL;
+
+    LOG((1, "%s: ncid 0x%x", __func__, ncid));
+
+    /* Find our metadata for this file. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        return retval;
+
+    assert(h5 && grp);
+
+    /* This must be the root group. */
+    if (grp->parent)
+        return NC_EBADGRPID;
+
+    inmemory = ((h5->cmode & NC_INMEMORY) == NC_INMEMORY);
+
+    if(inmemory && params != NULL) {
+        memio = (NC_memio*)params;
+    }
+
+    /* Call the nc4 close. */
+    if ((retval = nc4_close_hdf5_file(grp->nc4_info, 0, memio)))
+        return retval;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Learn number of dimensions, variables, global attributes,
+ * and the ID of the first unlimited dimension (if any).
+ *
+ * @note It's possible for any of these pointers to be NULL, in which
+ * case don't try to figure out that value.
+ *
+ * @param ncid File and group ID.
+ * @param ndimsp Pointer that gets number of dimensions.
+ * @param nvarsp Pointer that gets number of variables.
+ * @param nattsp Pointer that gets number of global attributes.
+ * @param unlimdimidp Pointer that gets first unlimited dimension ID,
+ * or -1 if there are no unlimied dimensions.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
+{
+    NC *nc;
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp;
+    int retval;
+    int i;
+
+    LOG((2, "%s: ncid 0x%x", __func__, ncid));
+
+    /* Find file metadata. */
+    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
+        return retval;
+
+    assert(h5 && grp && nc);
+
+    /* Count the number of dims, vars, and global atts; need to iterate
+     * because of possible nulls. */
+    if (ndimsp)
+    {
+        *ndimsp = ncindexcount(grp->dim);
+    }
+    if (nvarsp)
+    {
+        *nvarsp = ncindexcount(grp->vars);
+    }
+    if (nattsp)
+    {
+        /* Do we need to read the atts? */
+        if (!grp->atts_read)
+            if ((retval = nc4_read_atts(grp, NULL)))
+                return retval;
+
+        *nattsp = ncindexcount(grp->att);
+    }
+
+    if (unlimdimidp)
+    {
+        /* Default, no unlimited dimension */
+        *unlimdimidp = -1;
+
+        /* If there's more than one unlimited dim, which was not possible
+           with netcdf-3, then only the last unlimited one will be reported
+           back in xtendimp. */
+        /* Note that this code is inconsistent with nc_inq_unlimid() */
+        for(i=0;i<ncindexsize(grp->dim);i++) {
+            NC_DIM_INFO_T* d = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
+            if(d == NULL) continue;
+            if(d->unlimited) {
+                *unlimdimidp = d->hdr.id;
+                break;
+            }
+        }
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function will do the enddef stuff for a netcdf-4 file.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOTINDEFINE Not in define mode.
+ * @author Ed Hartnett
+ */
+int
+nc4_enddef_netcdf4_file(NC_FILE_INFO_T *h5)
+{
+    assert(h5);
+    LOG((3, "%s", __func__));
+
+    /* If we're not in define mode, return an error. */
+    if (!(h5->flags & NC_INDEF))
+        return NC_ENOTINDEFINE;
+
+    /* Turn define mode off. */
+    h5->flags ^= NC_INDEF;
+
+    /* Redef mode needs to be tracked separately for nc_abort. */
+    h5->redef = NC_FALSE;
+
+    return sync_netcdf4_file(h5);
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5grp.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5grp.c
new file mode 100644
index 0000000000000000000000000000000000000000..880538f01cd98ab6bf34dfbf9b4c93325ece29cd
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5grp.c
@@ -0,0 +1,175 @@
+/* Copyright 2005-2018, University Corporation for Atmospheric
+ * Research. See COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file @internal This file is part of netcdf-4, a netCDF-like
+ * interface for HDF5, or a HDF5 backend for netCDF, depending on your
+ * point of view.
+ *
+ * This file handles HDF5 groups.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+/**
+ * @internal Create a group. Its ncid is returned in the new_ncid
+ * pointer.
+ *
+ * @param parent_ncid Parent group.
+ * @param name Name of new group.
+ * @param new_ncid Pointer that gets ncid for new group.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ESTRICTNC3 Classic model in use for this file.
+ * @return ::NC_ENOTNC4 Not a netCDF-4 file.
+ * @author Ed Hartnett
+ */
+int
+NC4_def_grp(int parent_ncid, const char *name, int *new_ncid)
+{
+    NC_GRP_INFO_T *grp, *g;
+    NC_FILE_INFO_T *h5;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "%s: parent_ncid 0x%x name %s", __func__, parent_ncid, name));
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_grp_h5(parent_ncid, &grp, &h5)))
+        return retval;
+    assert(h5);
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        return retval;
+
+    /* Check that this name is not in use as a var, grp, or type. */
+    if ((retval = nc4_check_dup_name(grp, norm_name)))
+        return retval;
+
+    /* No groups in netcdf-3! */
+    if (h5->cmode & NC_CLASSIC_MODEL)
+        return NC_ESTRICTNC3;
+
+    /* If it's not in define mode, switch to define mode. */
+    if (!(h5->flags & NC_INDEF))
+        if ((retval = NC4_redef(parent_ncid)))
+            return retval;
+
+    /* Update internal lists to reflect new group. The actual HDF5
+     * group creation will be done when metadata is written by a
+     * sync. */
+    if ((retval = nc4_grp_list_add(h5, grp, norm_name, &g)))
+        return retval;
+    if (!(g->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T))))
+        return NC_ENOMEM;
+
+    /* For new groups, there are no atts to read from file. */
+    g->atts_read = 1;
+
+    /* Return the ncid to the user. */
+    if (new_ncid)
+        *new_ncid = grp->nc4_info->controller->ext_ncid | g->hdr.id;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Rename a group.
+ *
+ * @param grpid Group ID.
+ * @param name New name for group.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTNC4 Not a netCDF-4 file.
+ * @return ::NC_EPERM File opened read-only.
+ * @return ::NC_EBADGRPID Renaming root forbidden.
+ * @return ::NC_EHDFERR HDF5 function returned error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+int
+NC4_rename_grp(int grpid, const char *name)
+{
+    NC_GRP_INFO_T *grp;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    NC_FILE_INFO_T *h5;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "nc_rename_grp: grpid 0x%x name %s", grpid, name));
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_grp_h5(grpid, &grp, &h5)))
+        return retval;
+    assert(h5 && grp && grp->format_grp_info);
+
+    /* Get HDF5-specific group info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    if (h5->no_write)
+        return NC_EPERM; /* attempt to write to a read-only file */
+
+    /* Do not allow renaming the root group */
+    if (grp->parent == NULL)
+        return NC_EBADGRPID;
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        return retval;
+
+    /* Check that this name is not in use as a var, grp, or type in the
+     * parent group (i.e. the group that grp is in). */
+    if ((retval = nc4_check_dup_name(grp->parent, norm_name)))
+        return retval;
+
+    /* If it's not in define mode, switch to define mode. */
+    if (!(h5->flags & NC_INDEF))
+        if ((retval = NC4_redef(grpid)))
+            return retval;
+
+    /* Rename the group, if it exists in the file */
+    if (hdf5_grp->hdf_grpid)
+    {
+        NC_HDF5_GRP_INFO_T *parent_hdf5_grp;
+        parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
+
+        /* Close the group */
+        if (H5Gclose(hdf5_grp->hdf_grpid) < 0)
+            return NC_EHDFERR;
+        hdf5_grp->hdf_grpid = 0;
+
+        /* Attempt to rename & re-open the group, if the parent group is open */
+        if (parent_hdf5_grp->hdf_grpid)
+        {
+            /* Rename the group. */
+            if (H5Lmove(parent_hdf5_grp->hdf_grpid, grp->hdr.name,
+                        parent_hdf5_grp->hdf_grpid, name, H5P_DEFAULT,
+                        H5P_DEFAULT) < 0)
+                return NC_EHDFERR;
+
+            /* Reopen the group, with the new name. */
+            if ((hdf5_grp->hdf_grpid = H5Gopen2(parent_hdf5_grp->hdf_grpid, name,
+                                                H5P_DEFAULT)) < 0)
+                return NC_EHDFERR;
+        }
+    }
+
+    /* Give the group its new name in metadata. UTF8 normalization
+     * has been done. */
+    free(grp->hdr.name);
+    if (!(grp->hdr.name = strdup(norm_name)))
+        return NC_ENOMEM;
+
+    /* Update the hash and rebuild index. */
+    grp->hdr.hashkey = NC_hashmapkey(grp->hdr.name,strlen(grp->hdr.name));
+    if(!ncindexrebuild(grp->parent->children))
+        return NC_EINTERNAL;
+
+    return NC_NOERR;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5internal.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5internal.c
new file mode 100644
index 0000000000000000000000000000000000000000..ad843553d6659bb2e2d0e1907c5451adf15c2eba
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5internal.c
@@ -0,0 +1,934 @@
+/* Copyright 2003-2018, University Corporation for Atmospheric
+ * Research. See the COPYRIGHT file for copying and redistribution
+ * conditions.
+ */
+/**
+ * @file @internal Internal netcdf-4 functions.
+ *
+ * This file contains functions internal to the netcdf4 library. None of
+ * the functions in this file are exposed in the exetnal API. These
+ * functions all relate to the manipulation of netcdf-4's in-memory
+ * buffer of metadata information, i.e. the linked list of NC
+ * structs.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+#undef DEBUGH5
+
+#ifdef DEBUGH5
+/**
+ * @internal Provide a catchable error reporting function
+ *
+ * @param ignored Ignored.
+ *
+ * @return 0 for success.
+ */
+static herr_t
+h5catch(void* ignored)
+{
+    H5Eprint(NULL);
+    return 0;
+}
+#endif
+
+/* These are the default chunk cache sizes for HDF5 files created or
+ * opened with netCDF-4. */
+extern size_t nc4_chunk_cache_size;
+extern size_t nc4_chunk_cache_nelems;
+extern float nc4_chunk_cache_preemption;
+
+#ifdef LOGGING
+/* This is the severity level of messages which will be logged. Use
+   severity 0 for errors, 1 for important log messages, 2 for less
+   important, etc. */
+extern int nc_log_level;
+
+#endif /* LOGGING */
+
+int nc4_hdf5_initialized = 0; /**< True if initialization has happened. */
+
+/**
+ * @internal Provide a wrapper for H5Eset_auto
+ * @param func Pointer to func.
+ * @param client_data Client data.
+ *
+ * @return 0 for success
+ */
+static herr_t
+set_auto(void* func, void *client_data)
+{
+#ifdef DEBUGH5
+    return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)h5catch,client_data);
+#else
+    return H5Eset_auto2(H5E_DEFAULT,(H5E_auto2_t)func,client_data);
+#endif
+}
+
+/**
+ * @internal Provide a function to do any necessary initialization of
+ * the HDF5 library.
+ */
+void
+nc4_hdf5_initialize(void)
+{
+    if (set_auto(NULL, NULL) < 0)
+        LOG((0, "Couldn't turn off HDF5 error messages!"));
+    LOG((1, "HDF5 error messages have been turned off."));
+    nc4_hdf5_initialized = 1;
+}
+
+/**
+ * @internal Provide a function to do any necessary finalization of
+ * the HDF5 library.
+ */
+void
+nc4_hdf5_finalize(void)
+{
+    /* Reclaim global resources */
+    NC4_provenance_finalize();
+    nc4_hdf5_initialized = 0;
+}
+
+/**
+ * @internal Given a varid, return the maximum length of a dimension
+ * using dimid.
+ *
+ * @param grp Pointer to group info struct.
+ * @param varid Variable ID.
+ * @param dimid Dimension ID.
+ * @param maxlen Pointer that gets the max length.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+static int
+find_var_dim_max_length(NC_GRP_INFO_T *grp, int varid, int dimid,
+                        size_t *maxlen)
+{
+    hid_t datasetid = 0, spaceid = 0;
+    NC_VAR_INFO_T *var;
+    hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
+    int d, dataset_ndims = 0;
+    int retval = NC_NOERR;
+
+    *maxlen = 0;
+
+    /* Find this var. */
+    var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid);
+    if (!var) return NC_ENOTVAR;
+    assert(var->hdr.id == varid);
+
+    /* If the var hasn't been created yet, its size is 0. */
+    if (!var->created)
+    {
+        *maxlen = 0;
+    }
+    else
+    {
+        /* Get the number of records in the dataset. */
+        if ((retval = nc4_open_var_grp2(grp, var->hdr.id, &datasetid)))
+            BAIL(retval);
+        if ((spaceid = H5Dget_space(datasetid)) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* If it's a scalar dataset, it has length one. */
+        if (H5Sget_simple_extent_type(spaceid) == H5S_SCALAR)
+        {
+            *maxlen = (var->dimids && var->dimids[0] == dimid) ? 1 : 0;
+        }
+        else
+        {
+            /* Check to make sure ndims is right, then get the len of each
+               dim in the space. */
+            if ((dataset_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
+                BAIL(NC_EHDFERR);
+            if (dataset_ndims != var->ndims)
+                BAIL(NC_EHDFERR);
+            if (!(h5dimlen = malloc(dataset_ndims * sizeof(hsize_t))))
+                BAIL(NC_ENOMEM);
+            if (!(h5dimlenmax = malloc(dataset_ndims * sizeof(hsize_t))))
+                BAIL(NC_ENOMEM);
+            if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid,
+                                                           h5dimlen, h5dimlenmax)) < 0)
+                BAIL(NC_EHDFERR);
+            LOG((5, "find_var_dim_max_length: varid %d len %d max: %d",
+                 varid, (int)h5dimlen[0], (int)h5dimlenmax[0]));
+            for (d=0; d<dataset_ndims; d++) {
+                if (var->dimids[d] == dimid) {
+                    *maxlen = *maxlen > h5dimlen[d] ? *maxlen : h5dimlen[d];
+                }
+            }
+        }
+    }
+
+exit:
+    if (spaceid > 0 && H5Sclose(spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (h5dimlen) free(h5dimlen);
+    if (h5dimlenmax) free(h5dimlenmax);
+    return retval;
+}
+
+/**
+ * @internal Search for type with a given HDF type id.
+ *
+ * @param h5 File
+ * @param target_hdf_typeid HDF5 type ID to find.
+ *
+ * @return Pointer to type info struct, or NULL if not found.
+ * @author Ed Hartnett
+ */
+NC_TYPE_INFO_T *
+nc4_rec_find_hdf_type(NC_FILE_INFO_T *h5, hid_t target_hdf_typeid)
+{
+    NC_TYPE_INFO_T *type;
+    htri_t equal;
+    int i;
+
+    assert(h5);
+
+    for (i = 0; i < nclistlength(h5->alltypes); i++)
+    {
+        NC_HDF5_TYPE_INFO_T *hdf5_type;
+        hid_t hdf_typeid;
+
+        type = (NC_TYPE_INFO_T*)nclistget(h5->alltypes, i);
+        if(type == NULL) continue;
+
+        /* Get HDF5-specific type info. */
+        hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info;
+
+        /* Select the HDF5 typeid to use. */
+        hdf_typeid = hdf5_type->native_hdf_typeid ?
+            hdf5_type->native_hdf_typeid : hdf5_type->hdf_typeid;
+
+        /* Is this the type we are searching for? */
+        if ((equal = H5Tequal(hdf_typeid, target_hdf_typeid)) < 0)
+            return NULL;
+        if (equal)
+            return type;
+    }
+    /* Can't find it. Fate, why do you mock me? */
+    return NULL;
+}
+
+/**
+ * @internal Find the actual length of a dim by checking the length of
+ * that dim in all variables that use it, in grp or children. **len
+ * must be initialized to zero before this function is called.
+ *
+ * @param grp Pointer to group info struct.
+ * @param dimid Dimension ID.
+ * @param len Pointer to pointer that gets length.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+nc4_find_dim_len(NC_GRP_INFO_T *grp, int dimid, size_t **len)
+{
+    NC_VAR_INFO_T *var;
+    int retval;
+    int i;
+
+    assert(grp && len);
+    LOG((3, "%s: grp->name %s dimid %d", __func__, grp->hdr.name, dimid));
+
+    /* If there are any groups, call this function recursively on
+     * them. */
+    for (i = 0; i < ncindexsize(grp->children); i++)
+        if ((retval = nc4_find_dim_len((NC_GRP_INFO_T*)ncindexith(grp->children, i),
+                                       dimid, len)))
+            return retval;
+
+    /* For all variables in this group, find the ones that use this
+     * dimension, and remember the max length. */
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+    {
+        size_t mylen;
+        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
+        assert(var);
+
+        /* Find max length of dim in this variable... */
+        if ((retval = find_var_dim_max_length(grp, var->hdr.id, dimid, &mylen)))
+            return retval;
+
+        **len = **len > mylen ? **len : mylen;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Break a coordinate variable to separate the dimension and
+ * the variable.
+ *
+ * This is called from nc_rename_dim() and nc_rename_var(). In some
+ * renames, the coord variable must stay, but it is no longer a coord
+ * variable. This function changes a coord var into an ordinary
+ * variable.
+ *
+ * @param grp Pointer to group info struct.
+ * @param coord_var Pointer to variable info struct.
+ * @param dim Pointer to dimension info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Quincey Koziol, Ed Hartnett
+ */
+int
+nc4_break_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *coord_var,
+                    NC_DIM_INFO_T *dim)
+{
+    int retval;
+
+    /* Sanity checks */
+    assert(grp && coord_var && dim && dim->coord_var == coord_var &&
+           coord_var->dim[0] == dim && coord_var->dimids[0] == dim->hdr.id &&
+           !((NC_HDF5_DIM_INFO_T *)(dim->format_dim_info))->hdf_dimscaleid);
+    LOG((3, "%s dim %s was associated with var %s, but now has different name",
+         __func__, dim->hdr.name, coord_var->hdr.name));
+
+    /* If we're replacing an existing dimscale dataset, go to
+     * every var in the file and detach this dimension scale. */
+    if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
+                                    dim->hdr.id,
+                                    ((NC_HDF5_VAR_INFO_T *)(coord_var->format_var_info))->hdf_datasetid)))
+        return retval;
+
+    /* Allow attached dimscales to be tracked on the [former]
+     * coordinate variable */
+    if (coord_var->ndims)
+    {
+        /* Coordinate variables shouldn't have dimscales attached. */
+        assert(!coord_var->dimscale_attached);
+
+        /* Allocate space for tracking them */
+        if (!(coord_var->dimscale_attached = calloc(coord_var->ndims,
+                                                    sizeof(nc_bool_t))))
+            return NC_ENOMEM;
+    }
+
+    /* Detach dimension from variable */
+    coord_var->dimscale = NC_FALSE;
+    dim->coord_var = NULL;
+
+    /* Set state transition indicators */
+    coord_var->was_coord_var = NC_TRUE;
+    coord_var->became_coord_var = NC_FALSE;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Delete an existing dimscale-only dataset.
+ *
+ * A dimscale-only HDF5 dataset is created when a dim is defined
+ * without an accompanying coordinate variable.
+ *
+ * Sometimes, during renames, or late creation of variables, an
+ * existing, dimscale-only dataset must be removed. This means
+ * detaching all variables that use the dataset, then closing and
+ * unlinking it.
+ *
+ * @param grp The grp of the dimscale-only dataset to be deleted, or a
+ * higher group in the hierarchy (ex. root group).
+ * @param dimid id of the dimension
+ * @param dim Pointer to the dim with the dimscale-only dataset to be
+ * deleted.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+int
+delete_dimscale_dataset(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T *dim)
+{
+    NC_HDF5_DIM_INFO_T *hdf5_dim;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    int retval;
+
+    assert(grp && grp->format_grp_info && dim && dim->format_dim_info);
+    LOG((2, "%s: deleting dimscale dataset %s dimid %d", __func__, dim->hdr.name,
+         dimid));
+
+    /* Get HDF5 specific grp and dim info. */
+    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    /* Detach dimscale from any variables using it */
+    if ((retval = rec_detach_scales(grp, dimid, hdf5_dim->hdf_dimscaleid)) < 0)
+        return retval;
+
+    /* Close the HDF5 dataset */
+    if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0)
+        return NC_EHDFERR;
+    hdf5_dim->hdf_dimscaleid = 0;
+
+    /* Now delete the dataset. */
+    if (H5Gunlink(hdf5_grp->hdf_grpid, dim->hdr.name) < 0)
+        return NC_EHDFERR;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Reform a coordinate variable from a dimension and a
+ * variable.
+ *
+ * @param grp Pointer to group info struct.
+ * @param var Pointer to variable info struct.
+ * @param dim Pointer to dimension info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Quincey Koziol, Ed Hartnett
+ */
+int
+nc4_reform_coord_var(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, NC_DIM_INFO_T *dim)
+{
+    NC_HDF5_DIM_INFO_T *hdf5_dim;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    int need_to_reattach_scales = 0;
+    int retval = NC_NOERR;
+
+    assert(grp && grp->format_grp_info && var && var->format_var_info &&
+           dim && dim->format_dim_info);
+    LOG((3, "%s: dim->hdr.name %s var->hdr.name %s", __func__, dim->hdr.name,
+         var->hdr.name));
+
+    /* Get HDF5-specific dim, group, and var info. */
+    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Detach dimscales from the [new] coordinate variable. */
+    if (var->dimscale_attached)
+    {
+        int dims_detached = 0;
+        int finished = 0;
+        int d;
+
+        /* Loop over all dimensions for variable. */
+        for (d = 0; d < var->ndims && !finished; d++)
+        {
+            /* Is there a dimscale attached to this axis? */
+            if (var->dimscale_attached[d])
+            {
+                NC_GRP_INFO_T *g;
+                int k;
+
+                for (g = grp; g && !finished; g = g->parent)
+                {
+                    NC_DIM_INFO_T *dim1;
+                    NC_HDF5_DIM_INFO_T *hdf5_dim1;
+
+                    for (k = 0; k < ncindexsize(g->dim); k++)
+                    {
+                        dim1 = (NC_DIM_INFO_T *)ncindexith(g->dim, k);
+                        assert(dim1 && dim1->format_dim_info);
+                        hdf5_dim1 = (NC_HDF5_DIM_INFO_T *)dim1->format_dim_info;
+
+                        if (var->dimids[d] == dim1->hdr.id)
+                        {
+                            hid_t dim_datasetid;  /* Dataset ID for dimension */
+
+                            /* Find dataset ID for dimension */
+                            if (dim1->coord_var)
+                                dim_datasetid = ((NC_HDF5_VAR_INFO_T *)
+                                                 (dim1->coord_var->format_var_info))->hdf_datasetid;
+                            else
+                                dim_datasetid = hdf5_dim1->hdf_dimscaleid;
+
+                            /* dim_datasetid may be 0 in some cases when
+                             * renames of dims and vars are happening. In
+                             * this case, the scale has already been
+                             * detached. */
+                            if (dim_datasetid > 0)
+                            {
+                                LOG((3, "detaching scale from %s", var->hdr.name));
+                                if (H5DSdetach_scale(hdf5_var->hdf_datasetid,
+                                                     dim_datasetid, d) < 0)
+                                    BAIL(NC_EHDFERR);
+                            }
+                            var->dimscale_attached[d] = NC_FALSE;
+                            if (dims_detached++ == var->ndims)
+                                finished++;
+                        }
+                    }
+                }
+            }
+        } /* next variable dimension */
+
+        /* Release & reset the array tracking attached dimscales. */
+        free(var->dimscale_attached);
+        var->dimscale_attached = NULL;
+        need_to_reattach_scales++;
+    }
+
+    /* Use variable's dataset ID for the dimscale ID. */
+    if (hdf5_dim->hdf_dimscaleid && grp != NULL)
+    {
+        LOG((3, "closing and unlinking dimscale dataset %s", dim->hdr.name));
+        if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0)
+            BAIL(NC_EHDFERR);
+        hdf5_dim->hdf_dimscaleid = 0;
+
+        /* Now delete the dimscale's dataset (it will be recreated
+           later, if necessary). */
+        if (H5Gunlink(hdf5_grp->hdf_grpid, dim->hdr.name) < 0)
+            return NC_EDIMMETA;
+    }
+
+    /* Attach variable to dimension. */
+    var->dimscale = NC_TRUE;
+    dim->coord_var = var;
+
+    /* Check if this variable used to be a coord. var */
+    if (need_to_reattach_scales || (var->was_coord_var && grp != NULL))
+    {
+        /* Reattach the scale everywhere it is used. (Recall that netCDF
+         * dimscales are always 1-D) */
+        if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
+                                          var->dimids[0], hdf5_var->hdf_datasetid)))
+            return retval;
+
+        /* Set state transition indicator (cancels earlier
+         * transition). */
+        var->was_coord_var = NC_FALSE;
+    }
+
+    /* Set state transition indicator */
+    var->became_coord_var = NC_TRUE;
+
+exit:
+    return retval;
+}
+
+/**
+ * @internal Close HDF5 resources for global atts in a group.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+static int
+close_gatts(NC_GRP_INFO_T *grp)
+{
+    NC_ATT_INFO_T *att;
+    int a;
+
+    for (a = 0; a < ncindexsize(grp->att); a++)
+    {
+        NC_HDF5_ATT_INFO_T *hdf5_att;
+
+        att = (NC_ATT_INFO_T *)ncindexith(grp->att, a);
+        assert(att && att->format_att_info);
+        hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info;
+
+        /* Close the HDF5 typeid. */
+        if (hdf5_att->native_hdf_typeid &&
+            H5Tclose(hdf5_att->native_hdf_typeid) < 0)
+            return NC_EHDFERR;
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal Close HDF5 resources for vars in a group.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+static int
+close_vars(NC_GRP_INFO_T *grp)
+{
+    NC_VAR_INFO_T *var;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    NC_ATT_INFO_T *att;
+    int a, i;
+
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+    {
+        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
+        assert(var && var->format_var_info);
+        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+        /* Close the HDF5 dataset associated with this var. */
+        if (hdf5_var->hdf_datasetid)
+        {
+            LOG((3, "closing HDF5 dataset %lld", hdf5_var->hdf_datasetid));
+            if (H5Dclose(hdf5_var->hdf_datasetid) < 0)
+                return NC_EHDFERR;
+
+            if (var->fill_value)
+            {
+                if (var->type_info)
+                {
+                    if (var->type_info->nc_type_class == NC_VLEN)
+                        nc_free_vlen((nc_vlen_t *)var->fill_value);
+                    else if (var->type_info->nc_type_class == NC_STRING && *(char **)var->fill_value)
+                        free(*(char **)var->fill_value);
+                }
+            }
+        }
+
+        /* Delete any HDF5 dimscale objid information. */
+        if (hdf5_var->dimscale_hdf5_objids)
+            free(hdf5_var->dimscale_hdf5_objids);
+
+        for (a = 0; a < ncindexsize(var->att); a++)
+        {
+            NC_HDF5_ATT_INFO_T *hdf5_att;
+            att = (NC_ATT_INFO_T *)ncindexith(var->att, a);
+            assert(att && att->format_att_info);
+            hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info;
+
+            /* Close the HDF5 typeid if one is open. */
+            if (hdf5_att->native_hdf_typeid &&
+                H5Tclose(hdf5_att->native_hdf_typeid) < 0)
+                return NC_EHDFERR;
+        }
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Close HDF5 resources for dims in a group.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+static int
+close_dims(NC_GRP_INFO_T *grp)
+{
+    NC_DIM_INFO_T *dim;
+    int i;
+
+    for (i = 0; i < ncindexsize(grp->dim); i++)
+    {
+        NC_HDF5_DIM_INFO_T *hdf5_dim;
+
+        dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, i);
+        assert(dim && dim->format_dim_info);
+        hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+        /* If this is a dim without a coordinate variable, then close
+         * the HDF5 DIM_WITHOUT_VARIABLE dataset associated with this
+         * dim. */
+        if (hdf5_dim->hdf_dimscaleid && H5Dclose(hdf5_dim->hdf_dimscaleid) < 0)
+            return NC_EHDFERR;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Close HDF5 resources for types in a group.  Set values to
+ * 0 after closing types. Because of type reference counters, these
+ * closes can be called multiple times.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+close_types(NC_GRP_INFO_T *grp)
+{
+    int i;
+
+    for (i = 0; i < ncindexsize(grp->type); i++)
+    {
+        NC_TYPE_INFO_T *type;
+        NC_HDF5_TYPE_INFO_T *hdf5_type;
+
+        type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i);
+        assert(type && type->format_type_info);
+
+        /* Get HDF5-specific type info. */
+        hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info;
+
+        /* Close any open user-defined HDF5 typeids. */
+        if (hdf5_type->hdf_typeid && H5Tclose(hdf5_type->hdf_typeid) < 0)
+            return NC_EHDFERR;
+        hdf5_type->hdf_typeid = 0;
+        if (hdf5_type->native_hdf_typeid &&
+            H5Tclose(hdf5_type->native_hdf_typeid) < 0)
+            return NC_EHDFERR;
+        hdf5_type->native_hdf_typeid = 0;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Recursively free HDF5 objects for a group (and everything
+ * it contains).
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+int
+nc4_rec_grp_HDF5_del(NC_GRP_INFO_T *grp)
+{
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    int i;
+    int retval;
+
+    assert(grp && grp->format_grp_info);
+    LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));
+
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    /* Recursively call this function for each child, if any, stopping
+     * if there is an error. */
+    for (i = 0; i < ncindexsize(grp->children); i++)
+        if ((retval = nc4_rec_grp_HDF5_del((NC_GRP_INFO_T *)ncindexith(grp->children,
+                                                                       i))))
+            return retval;
+
+    /* Close HDF5 resources associated with global attributes. */
+    if ((retval = close_gatts(grp)))
+        return retval;
+
+    /* Close HDF5 resources associated with vars. */
+    if ((retval = close_vars(grp)))
+        return retval;
+
+    /* Close HDF5 resources associated with dims. */
+    if ((retval = close_dims(grp)))
+        return retval;
+
+    /* Close HDF5 resources associated with types. */
+    if ((retval = close_types(grp)))
+        return retval;
+
+    /* Close the HDF5 group. */
+    LOG((4, "%s: closing group %s", __func__, grp->hdr.name));
+    if (hdf5_grp->hdf_grpid && H5Gclose(hdf5_grp->hdf_grpid) < 0)
+        return NC_EHDFERR;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Given an ncid and varid, get pointers to the group and var
+ * metadata. Lazy var metadata reads are done as needed.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param h5 Pointer that gets pointer to the NC_FILE_INFO_T struct
+ * for this file. Ignored if NULL.
+ * @param grp Pointer that gets pointer to group info. Ignored if
+ * NULL.
+ * @param var Pointer that gets pointer to var info. Ignored if NULL.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOTVAR Variable not found.
+ * @author Ed Hartnett
+ */
+int
+nc4_hdf5_find_grp_h5_var(int ncid, int varid, NC_FILE_INFO_T **h5,
+                         NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var)
+{
+    NC_FILE_INFO_T *my_h5;
+    NC_GRP_INFO_T *my_grp;
+    NC_VAR_INFO_T *my_var;
+    int retval;
+
+    /* Look up file and group metadata. */
+    if ((retval = nc4_find_grp_h5(ncid, &my_grp, &my_h5)))
+        return retval;
+    assert(my_grp && my_h5);
+
+    /* Find the var. */
+    if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, varid)))
+        return NC_ENOTVAR;
+    assert(my_var && my_var->hdr.id == varid);
+
+    /* Do we need to read var metadata? */
+    if (!my_var->meta_read && my_var->created)
+        if ((retval = nc4_get_var_meta(my_var)))
+            return retval;
+
+    /* Return pointers that caller wants. */
+    if (h5)
+        *h5 = my_h5;
+    if (grp)
+        *grp = my_grp;
+    if (var)
+        *var = my_var;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Given an ncid, varid, and attribute name, return
+ * normalized name and pointers to the file, group, var, and att info
+ * structs. Lazy reads of attributes and variable metadata are done as
+ * needed.
+ *
+ * @param ncid File/group ID.
+ * @param varid Variable ID.
+ * @param name Name to of attribute.
+ * @param attnum Number of attribute.
+ * @param use_name If true, use the name to get the
+ * attribute. Otherwise use the attnum.
+ * @param norm_name Pointer to storage of size NC_MAX_NAME + 1,
+ * which will get the normalized name, if use_name is true. Ignored if
+ * NULL.
+ * @param h5 Pointer to pointer that gets file info struct. Ignored if
+ * NULL.
+ * @param grp Pointer to pointer that gets group info struct. Ignored
+ * if NULL.
+ * @param h5 Pointer to pointer that gets variable info
+ * struct. Ignored if NULL.
+ * @param att Pointer to pointer that gets attribute info
+ * struct. Ignored if NULL.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTVAR Variable not found.
+ * @return ::NC_ENOTATT Attribute not found.
+ * @author Ed Hartnett
+ */
+int
+nc4_hdf5_find_grp_var_att(int ncid, int varid, const char *name, int attnum,
+                          int use_name, char *norm_name, NC_FILE_INFO_T **h5,
+                          NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var,
+                          NC_ATT_INFO_T **att)
+{
+    NC_FILE_INFO_T *my_h5;
+    NC_GRP_INFO_T *my_grp;
+    NC_VAR_INFO_T *my_var = NULL;
+    NC_ATT_INFO_T *my_att;
+    char my_norm_name[NC_MAX_NAME + 1] = "";
+    NCindex *attlist = NULL;
+    int retval;
+
+    LOG((4, "%s: ncid %d varid %d attnum %d use_name %d", __func__, ncid, varid,
+         attnum, use_name));
+
+    /* Don't need to provide name unless getting att pointer and using
+     * use_name. */
+    assert(!att || ((use_name && name) || !use_name));
+
+    /* Find info for this file, group, and h5 info. */
+    if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &my_grp, &my_h5)))
+        return retval;
+    assert(my_grp && my_h5);
+
+    /* Get either the global or a variable attribute list. */
+    if (varid == NC_GLOBAL)
+    {
+        /* Do we need to read the atts? */
+        if (!my_grp->atts_read)
+            if ((retval = nc4_read_atts(my_grp, NULL)))
+                return retval;
+
+        attlist = my_grp->att;
+    }
+    else
+    {
+        if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, varid)))
+            return NC_ENOTVAR;
+
+        /* Do we need to read the var attributes? */
+        if (!my_var->atts_read)
+            if ((retval = nc4_read_atts(my_grp, my_var)))
+                return retval;
+
+        /* Do we need to read var metadata? */
+        if (!my_var->meta_read && my_var->created)
+            if ((retval = nc4_get_var_meta(my_var)))
+                return retval;
+
+        attlist = my_var->att;
+    }
+    assert(attlist);
+
+    /* Need a name if use_name is true. */
+    if (use_name && !name)
+        return NC_EBADNAME;
+
+    /* Normalize the name. */
+    if (use_name)
+        if ((retval = nc4_normalize_name(name, my_norm_name)))
+            return retval;
+
+    /* Now find the attribute by name or number. */
+    if (att)
+    {
+        my_att = use_name ? (NC_ATT_INFO_T *)ncindexlookup(attlist, my_norm_name) :
+            (NC_ATT_INFO_T *)ncindexith(attlist, attnum);
+        if (!my_att)
+            return NC_ENOTATT;
+    }
+
+    /* Give the people what they want. */
+    if (norm_name)
+        strncpy(norm_name, my_norm_name, NC_MAX_NAME);
+    if (h5)
+        *h5 = my_h5;
+    if (grp)
+        *grp = my_grp;
+    if (var)
+        *var = my_var;
+    if (att)
+        *att = my_att;
+
+    return NC_NOERR;
+}
+
+#ifdef LOGGING
+/* We will need to check against nc log level from nc4internal.c. */
+extern int nc_log_level;
+
+/**
+ * @internal This is like nc_set_log_level(), but will also turn on
+ * HDF5 internal logging, in addition to netCDF logging. This should
+ * never be called by the user. It is called in open/create when the
+ * nc logging level has changed.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+hdf5_set_log_level()
+{
+    /* If the user wants to completely turn off logging, turn off HDF5
+       logging too. Now I truely can't think of what to do if this
+       fails, so just ignore the return code. */
+    if (nc_log_level == NC_TURN_OFF_LOGGING)
+    {
+        set_auto(NULL, NULL);
+        LOG((1, "HDF5 error messages turned off!"));
+    }
+    else
+    {
+        if (set_auto((H5E_auto_t)&H5Eprint, stderr) < 0)
+            LOG((0, "H5Eset_auto failed!"));
+        LOG((1, "HDF5 error messages turned on."));
+    }
+
+    return NC_NOERR;
+}
+#endif /* LOGGING */
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5open.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5open.c
new file mode 100644
index 0000000000000000000000000000000000000000..42ef8cd2fa93ffc9b6fcf11a2d038b0c6d1742a7
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5open.c
@@ -0,0 +1,2617 @@
+/* Copyright 2003-2018, University Corporation for Atmospheric
+ * Research. See COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file
+ * @internal This file contains functions that are used in file
+ * opens.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+#include "ncrc.h"
+#include "ncmodel.h"
+
+#ifdef ENABLE_BYTERANGE
+#include "H5FDhttp.h"
+#endif
+
+#define NUM_TYPES 12 /**< Number of netCDF atomic types. */
+#define CD_NELEMS_ZLIB 1 /**< Number of parameters needed for ZLIB filter. */
+
+/** @internal Native HDF5 constants for atomic types. For performance,
+ * fill this array only the first time, and keep it in global memory
+ * for each further use. */
+static hid_t h5_native_type_constant_g[NUM_TYPES];
+
+/** @internal NetCDF atomic type names. */
+static const char nc_type_name_g[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short",
+                                                                "int", "float", "double", "ubyte",
+                                                                "ushort", "uint", "int64",
+                                                                "uint64", "string"};
+
+/** @internal NetCDF atomic types. */
+static const nc_type nc_type_constant_g[NUM_TYPES] = {NC_CHAR, NC_BYTE, NC_SHORT,
+                                                      NC_INT, NC_FLOAT, NC_DOUBLE, NC_UBYTE,
+                                                      NC_USHORT, NC_UINT, NC_INT64,
+                                                      NC_UINT64, NC_STRING};
+
+/** @internal NetCDF atomic type sizes. */
+static const int nc_type_size_g[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short),
+                                              sizeof(int), sizeof(float), sizeof(double), sizeof(unsigned char),
+                                              sizeof(unsigned short), sizeof(unsigned int), sizeof(long long),
+                                              sizeof(unsigned long long), sizeof(char *)};
+
+/** @internal These flags may not be set for open mode. */
+static const int ILLEGAL_OPEN_FLAGS = (NC_MMAP);
+
+/* From libsrc4, these are the netcdf-4 cache sizes. */
+extern size_t nc4_chunk_cache_size;
+extern size_t nc4_chunk_cache_nelems;
+extern float nc4_chunk_cache_preemption;
+
+/* From nc4mem.c */
+extern int NC4_open_image_file(NC_FILE_INFO_T* h5);
+
+/* Defined later in this file. */
+static int rec_read_metadata(NC_GRP_INFO_T *grp);
+
+/**
+ * @internal Struct to track HDF5 object info, for
+ * rec_read_metadata(). We get this info for every object in the
+ * HDF5 file when we H5Literate() over the file. */
+typedef struct hdf5_obj_info
+{
+    hid_t oid;                          /* HDF5 object ID */
+    char oname[NC_MAX_NAME + 1];        /* Name of object */
+    H5G_stat_t statbuf;                 /* Information about the object */
+    struct hdf5_obj_info *next; /* Pointer to next node in list */
+} hdf5_obj_info_t;
+
+/**
+ * @internal User data struct for call to H5Literate() in
+ * rec_read_metadata(). When iterating through the objects in a
+ * group, if we find child groups, we save their hdf5_obj_info_t
+ * object in a list. Then we processes them after completely
+ * processing the parent group. */
+typedef struct user_data
+{
+    NClist *grps; /* NClist<hdf5_obj_info_t*> */
+    NC_GRP_INFO_T *grp; /* Pointer to parent group */
+} user_data_t;
+
+/* Custom iteration callback data */
+typedef struct {
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var;
+} att_iter_info;
+
+/**
+ * @internal Given an HDF5 type, set a pointer to netcdf type_info
+ * struct, either an existing one (for user-defined types) or a newly
+ * created one.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param datasetid HDF5 dataset ID.
+ * @param type_info Pointer to pointer that gets type info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EBADTYPID Type not found.
+ * @author Ed Hartnett
+ */
+static int
+get_type_info2(NC_FILE_INFO_T *h5, hid_t datasetid, NC_TYPE_INFO_T **type_info)
+{
+    NC_HDF5_TYPE_INFO_T *hdf5_type;
+    htri_t is_str, equal = 0;
+    H5T_class_t class;
+    hid_t native_typeid, hdf_typeid;
+    H5T_order_t order;
+    int t;
+
+    assert(h5 && type_info);
+
+    /* Because these N5T_NATIVE_* constants are actually function calls
+     * (!) in H5Tpublic.h, I can't initialize this array in the usual
+     * way, because at least some C compilers (like Irix) complain
+     * about calling functions when defining constants. So I have to do
+     * it like this. Note that there's no native types for char or
+     * string. Those are handled later. */
+    if (!h5_native_type_constant_g[1])
+    {
+        h5_native_type_constant_g[1] = H5T_NATIVE_SCHAR;
+        h5_native_type_constant_g[2] = H5T_NATIVE_SHORT;
+        h5_native_type_constant_g[3] = H5T_NATIVE_INT;
+        h5_native_type_constant_g[4] = H5T_NATIVE_FLOAT;
+        h5_native_type_constant_g[5] = H5T_NATIVE_DOUBLE;
+        h5_native_type_constant_g[6] = H5T_NATIVE_UCHAR;
+        h5_native_type_constant_g[7] = H5T_NATIVE_USHORT;
+        h5_native_type_constant_g[8] = H5T_NATIVE_UINT;
+        h5_native_type_constant_g[9] = H5T_NATIVE_LLONG;
+        h5_native_type_constant_g[10] = H5T_NATIVE_ULLONG;
+    }
+
+    /* Get the HDF5 typeid - we'll need it later. */
+    if ((hdf_typeid = H5Dget_type(datasetid)) < 0)
+        return NC_EHDFERR;
+
+    /* Get the native typeid. Will be equivalent to hdf_typeid when
+     * creating but not necessarily when reading, a variable. */
+    if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
+        return NC_EHDFERR;
+
+    /* Is this type an integer, string, compound, or what? */
+    if ((class = H5Tget_class(native_typeid)) < 0)
+        return NC_EHDFERR;
+
+    /* Is this an atomic type? */
+    if (class == H5T_STRING || class == H5T_INTEGER || class == H5T_FLOAT)
+    {
+        /* Allocate a phony NC_TYPE_INFO_T struct to hold type info. */
+        if (!(*type_info = calloc(1, sizeof(NC_TYPE_INFO_T))))
+            return NC_ENOMEM;
+
+        /* Allocate storage for HDF5-specific type info. */
+        if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T))))
+            return NC_ENOMEM;
+        (*type_info)->format_type_info = hdf5_type;
+
+        /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
+         * H5Tget_class will return H5T_STRING if this is a string. */
+        if (class == H5T_STRING)
+        {
+            if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
+                return NC_EHDFERR;
+            /* Make sure fixed-len strings will work like variable-len
+             * strings */
+            if (is_str || H5Tget_size(hdf_typeid) > 1)
+            {
+                /* Set a class for the type */
+                t = NUM_TYPES - 1;
+                (*type_info)->nc_type_class = NC_STRING;
+            }
+            else
+            {
+                /* Set a class for the type */
+                t = 0;
+                (*type_info)->nc_type_class = NC_CHAR;
+            }
+        }
+        else
+        {
+            for (t = 1; t < NUM_TYPES - 1; t++)
+            {
+                if ((equal = H5Tequal(native_typeid,
+                                      h5_native_type_constant_g[t])) < 0)
+                    return NC_EHDFERR;
+                if (equal)
+                    break;
+            }
+
+            /* Find out about endianness. As of HDF 1.8.6, this works
+             * with all data types Not just H5T_INTEGER. See
+             * https://www.hdfgroup.org/HDF5/doc/RM/RM_H5T.html#Datatype-GetOrder */
+            if ((order = H5Tget_order(hdf_typeid)) < 0)
+                return NC_EHDFERR;
+
+            if (order == H5T_ORDER_LE)
+                (*type_info)->endianness = NC_ENDIAN_LITTLE;
+            else if (order == H5T_ORDER_BE)
+                (*type_info)->endianness = NC_ENDIAN_BIG;
+            else
+                return NC_EBADTYPE;
+
+            if (class == H5T_INTEGER)
+                (*type_info)->nc_type_class = NC_INT;
+            else
+                (*type_info)->nc_type_class = NC_FLOAT;
+        }
+        (*type_info)->hdr.id = nc_type_constant_g[t];
+        (*type_info)->size = nc_type_size_g[t];
+        if (!((*type_info)->hdr.name = strdup(nc_type_name_g[t])))
+            return NC_ENOMEM;
+        hdf5_type->hdf_typeid = hdf_typeid;
+        hdf5_type->native_hdf_typeid = native_typeid;
+        return NC_NOERR;
+    }
+    else
+    {
+        NC_TYPE_INFO_T *type;
+
+        /* This is a user-defined type. */
+        if((type = nc4_rec_find_hdf_type(h5, native_typeid)))
+            *type_info = type;
+
+        /* The type entry in the array of user-defined types already has
+         * an open data typeid (and native typeid), so close the ones we
+         * opened above. */
+        if (H5Tclose(native_typeid) < 0)
+            return NC_EHDFERR;
+        if (H5Tclose(hdf_typeid) < 0)
+            return NC_EHDFERR;
+
+        if (type)
+            return NC_NOERR;
+    }
+
+    return NC_EBADTYPID;
+}
+
+/**
+ * @internal This function reads the coordinates attribute used for
+ * multi-dimensional coordinates. It then sets var->dimids[], and
+ * attempts to find a pointer to the dims and sets var->dim[] as well.
+ *
+ * @param grp Group info pointer.
+ * @param var Var info pointer.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOTATT Attribute does not exist.
+ * @return ::NC_EATTMETA Attribute metadata error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+static int
+read_coord_dimids(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
+{
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    hid_t coord_att_typeid = -1, coord_attid = -1, spaceid = -1;
+    hssize_t npoints;
+    htri_t attr_exists;
+    int d;
+    int retval = NC_NOERR;
+
+    assert(grp && var && var->format_var_info);
+    LOG((3, "%s: var->hdr.name %s", __func__, var->hdr.name));
+
+    /* Have we already read the coordinates hidden att for this var? */
+    if (var->coords_read)
+        return NC_NOERR;
+
+    /* Get HDF5-sepecific var info. */
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Does the COORDINATES att exist? */
+    if ((attr_exists = H5Aexists(hdf5_var->hdf_datasetid, COORDINATES)) < 0)
+        return NC_EHDFERR;
+    if (!attr_exists)
+        return NC_ENOTATT;
+
+    /* There is a hidden attribute telling us the ids of the
+     * dimensions that apply to this multi-dimensional coordinate
+     * variable. Read it. */
+    if ((coord_attid = H5Aopen_name(hdf5_var->hdf_datasetid, COORDINATES)) < 0)
+        BAIL(NC_EATTMETA);
+
+    if ((coord_att_typeid = H5Aget_type(coord_attid)) < 0)
+        BAIL(NC_EATTMETA);
+
+    /* How many dimensions are there? */
+    if ((spaceid = H5Aget_space(coord_attid)) < 0)
+        BAIL(NC_EATTMETA);
+    if ((npoints = H5Sget_simple_extent_npoints(spaceid)) < 0)
+        BAIL(NC_EATTMETA);
+
+    /* Check that the number of points is the same as the number of
+     * dimensions for the variable. */
+    if (npoints != var->ndims)
+        BAIL(NC_EATTMETA);
+
+    /* Read the dimids for this var. */
+    if (H5Aread(coord_attid, coord_att_typeid, var->dimids) < 0)
+        BAIL(NC_EATTMETA);
+    LOG((4, "read dimids for this var"));
+
+    /* Update var->dim field based on the var->dimids. Ok if does not
+     * find a dim at this time, but if found set it. */
+    for (d = 0; d < var->ndims; d++)
+        nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
+
+    /* Remember that we have read the coordinates hidden attribute. */
+    var->coords_read = NC_TRUE;
+
+exit:
+    if (spaceid >= 0 && H5Sclose(spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (coord_att_typeid >= 0 && H5Tclose(coord_att_typeid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (coord_attid >= 0 && H5Aclose(coord_attid) < 0)
+        BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal This function is called when reading a file's metadata
+ * for each dimension scale attached to a variable.
+ *
+ * @param did HDF5 ID for dimscale.
+ * @param dim
+ * @param dsid
+ * @param dimscale_hdf5_objids
+ *
+ * @return 0 for success, -1 for error.
+ * @author Ed Hartnett
+ */
+static herr_t
+dimscale_visitor(hid_t did, unsigned dim, hid_t dsid,
+                 void *dimscale_hdf5_objids)
+{
+    H5G_stat_t statbuf;
+
+    LOG((4, "%s", __func__));
+
+    /* Get more info on the dimscale object.*/
+    if (H5Gget_objinfo(dsid, ".", 1, &statbuf) < 0)
+        return -1;
+
+    /* Pass this information back to caller. */
+    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[0] = statbuf.fileno[0];
+    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[1] = statbuf.fileno[1];
+    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[0] = statbuf.objno[0];
+    (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[1] = statbuf.objno[1];
+    return 0;
+}
+
+/**
+ * @internal For files without any netCDF-4 dimensions defined, create phony
+ * dimension to match the available datasets.
+ *
+ * @param grp Pointer to the group info.
+ * @param hdf_datasetid HDF5 datsetid for the var's dataset.
+ * @param var Pointer to the var info.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @returns NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+static int
+create_phony_dims(NC_GRP_INFO_T *grp, hid_t hdf_datasetid, NC_VAR_INFO_T *var)
+{
+    NC_DIM_INFO_T *dim;
+    hid_t spaceid = 0;
+    hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
+    int dataset_ndims;
+    int d;
+    int retval = NC_NOERR;
+
+    /* Find the space information for this dimension. */
+    if ((spaceid = H5Dget_space(hdf_datasetid)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Get the len of each dim in the space. */
+    if (var->ndims)
+    {
+        /* Allocate storage for dim lens and max lens for this var. */
+        if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
+            return NC_ENOMEM;
+        if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
+            BAIL(NC_ENOMEM);
+
+        /* Get ndims, also len and mac len of all dims. */
+        if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen,
+                                                       h5dimlenmax)) < 0)
+            BAIL(NC_EHDFERR);
+        assert(dataset_ndims == var->ndims);
+    }
+    else
+    {
+        /* Make sure it's scalar. */
+        assert(H5Sget_simple_extent_type(spaceid) == H5S_SCALAR);
+    }
+
+    /* Create a phony dimension for each dimension in the dataset,
+     * unless there already is one the correct size. */
+    for (d = 0; d < var->ndims; d++)
+    {
+        int k;
+        int match;
+
+        /* Is there already a phony dimension of the correct size? */
+        for (match=-1, k = 0; k < ncindexsize(grp->dim); k++)
+        {
+            dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, k);
+            assert(dim);
+            if ((dim->len == h5dimlen[d]) &&
+                ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
+                 (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
+            {match = k; break;}
+        }
+
+        /* Didn't find a phony dim? Then create one. */
+        if (match < 0)
+        {
+            char phony_dim_name[NC_MAX_NAME + 1];
+            sprintf(phony_dim_name, "phony_dim_%d", grp->nc4_info->next_dimid);
+            LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name));
+
+            /* Add phony dim to metadata list. */
+            if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim)))
+                BAIL(retval);
+
+            /* Create struct for HDF5-specific dim info. */
+            if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
+                BAIL(NC_ENOMEM);
+            if (h5dimlenmax[d] == H5S_UNLIMITED)
+                dim->unlimited = NC_TRUE;
+        }
+
+        /* The variable must remember the dimid. */
+        var->dimids[d] = dim->hdr.id;
+        var->dim[d] = dim;
+    } /* next dim */
+
+exit:
+    /* Free resources. */
+    if (spaceid > 0 && H5Sclose(spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (h5dimlenmax)
+        free(h5dimlenmax);
+    if (h5dimlen)
+        free(h5dimlen);
+
+    return retval;
+}
+
+/**
+ * @internal Iterate through the vars in this file and make sure we've
+ * got a dimid and a pointer to a dim for each dimension. This may
+ * already have been done using the COORDINATES hidden attribute, in
+ * which case this function will not have to do anything. This is
+ * desirable because recurdively matching the dimscales (when
+ * necessary) is very much the slowest part of opening a file.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @returns NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+static int
+rec_match_dimscales(NC_GRP_INFO_T *grp)
+{
+    NC_VAR_INFO_T *var;
+    NC_DIM_INFO_T *dim;
+    int retval = NC_NOERR;
+    int i;
+
+    assert(grp && grp->hdr.name);
+    LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
+
+    /* Perform var dimscale match for child groups. */
+    for (i = 0; i < ncindexsize(grp->children); i++)
+        if ((retval = rec_match_dimscales((NC_GRP_INFO_T *)ncindexith(grp->children, i))))
+            return retval;
+
+    /* Check all the vars in this group. If they have dimscale info,
+     * try and find a dimension for them. */
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+    {
+        NC_HDF5_VAR_INFO_T *hdf5_var;
+        int d;
+
+        /* Get pointer to var and to the HDF5-specific var info. */
+        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
+        assert(var && var->format_var_info);
+        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+        /* Check all vars and see if dim[i] != NULL if dimids[i]
+         * valid. Recall that dimids were initialized to -1. */
+        for (d = 0; d < var->ndims; d++)
+        {
+            if (!var->dim[d])
+                nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
+        }
+
+        /* Skip dimension scale variables */
+        if (var->dimscale)
+            continue;
+
+        /* If we have already read hidden coordinates att, then we don't
+         * have to match dimscales for this var. */
+        if (var->coords_read)
+            continue;
+
+        /* Skip dimension scale variables */
+        if (!var->dimscale)
+        {
+            int d;
+            int j;
+
+            /* Are there dimscales for this variable? */
+            if (hdf5_var->dimscale_hdf5_objids)
+            {
+                for (d = 0; d < var->ndims; d++)
+                {
+                    NC_GRP_INFO_T *g;
+                    nc_bool_t finished = NC_FALSE;
+                    LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name));
+
+                    /* If we already have the dimension, we don't need to
+                     * match the dimscales. This is better because matching
+                     * the dimscales is slow. */
+                    if (var->dim[d])
+                        continue;
+
+                    /* Now we have to try to match dimscales. Check this
+                     * and parent groups. */
+                    for (g = grp; g && !finished; g = g->parent)
+                    {
+                        /* Check all dims in this group. */
+                        for (j = 0; j < ncindexsize(g->dim); j++)
+                        {
+                            /* Get the HDF5 specific dim info. */
+                            NC_HDF5_DIM_INFO_T *hdf5_dim;
+                            dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j);
+                            assert(dim && dim->format_dim_info);
+                            hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+                            /* Check for exact match of fileno/objid arrays
+                             * to find identical objects in HDF5 file. */
+                            if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] &&
+                                hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] &&
+                                hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] &&
+                                hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1])
+                            {
+                                LOG((4, "%s: for dimension %d, found dim %s", __func__,
+                                     d, dim->hdr.name));
+                                var->dimids[d] = dim->hdr.id;
+                                var->dim[d] = dim;
+                                finished = NC_TRUE;
+                                break;
+                            }
+                        } /* next dim */
+                    } /* next grp */
+                } /* next var->dim */
+            }
+            else
+            {
+                /* No dimscales for this var! Invent phony dimensions. */
+                if ((retval = create_phony_dims(grp, hdf5_var->hdf_datasetid, var)))
+                    return retval;
+            }
+        }
+    }
+
+    return retval;
+}
+
+/**
+ * @internal Check for the attribute that indicates that netcdf
+ * classic model is in use.
+ *
+ * @param root_grp pointer to the group info for the root group of the
+ * @param is_classic store 1 if this is a classic file.
+ * file.
+ *
+ * @return NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+static int
+check_for_classic_model(NC_GRP_INFO_T *root_grp, int *is_classic)
+{
+    htri_t attr_exists;
+    hid_t grpid;
+
+    /* Check inputs. */
+    assert(root_grp && root_grp->format_grp_info && !root_grp->parent
+           && is_classic);
+
+    /* Get the HDF5 group id. */
+    grpid = ((NC_HDF5_GRP_INFO_T *)(root_grp->format_grp_info))->hdf_grpid;
+
+    /* If this attribute exists in the root group, then classic model
+     * is in effect. */
+    if ((attr_exists = H5Aexists(grpid, NC3_STRICT_ATT_NAME)) < 0)
+        return NC_EHDFERR;
+    *is_classic = attr_exists ? 1 : 0;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Open a netcdf-4 file. Things have already been kicked off
+ * in ncfunc.c in nc_open, but here the netCDF-4 part of opening a
+ * file is handled.
+ *
+ * @param path The file name of the new file.
+ * @param mode The open mode flag.
+ * @param parameters File parameters.
+ * @param nc Pointer to NC file info.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EINTERNAL Internal list error.
+ * @return ::NC_EHDFERR HDF error.
+ * @return ::NC_EMPI MPI error for parallel.
+ * @return ::NC_EPARINIT Parallel I/O initialization error.
+ * @return ::NC_EINMEMMORY Memory file error.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+nc4_open_file(const char *path, int mode, void* parameters, NC *nc)
+{
+    hid_t fapl_id = H5P_DEFAULT;
+    int retval;
+    unsigned flags;
+    NC_FILE_INFO_T *nc4_info = NULL;
+    int is_classic;
+    NC_HDF5_FILE_INFO_T *h5 = NULL;
+
+#ifdef USE_PARALLEL4
+    NC_MPI_INFO* mpiinfo = NULL;
+    int comm_duped = 0; /* Whether the MPI Communicator was duplicated */
+    int info_duped = 0; /* Whether the MPI Info object was duplicated */
+#endif
+
+    LOG((3, "%s: path %s mode %d", __func__, path, mode));
+    assert(path && nc);
+
+    flags = (mode & NC_WRITE) ? H5F_ACC_RDWR : H5F_ACC_RDONLY;
+
+    /* Add necessary structs to hold netcdf-4 file data. */
+    if ((retval = nc4_nc4f_list_add(nc, path, mode)))
+        BAIL(retval);
+    nc4_info = NC4_DATA(nc);
+    assert(nc4_info && nc4_info->root_grp);
+
+    /* Add struct to hold HDF5-specific file metadata. */
+    if (!(nc4_info->format_file_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T))))
+        BAIL(NC_ENOMEM);
+
+    /* Add struct to hold HDF5-specific group info. */
+    if (!(nc4_info->root_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T))))
+        BAIL(NC_ENOMEM);
+
+    h5 = (NC_HDF5_FILE_INFO_T*)nc4_info->format_file_info;
+
+#ifdef ENABLE_BYTERANGE
+    /* See if we want the byte range protocol */
+    if(nc->model->iosp == NC_IOSP_HTTP) {
+	h5->http.iosp = 1;
+	/* Kill off any conflicting modes flags */
+	mode &= ~(NC_WRITE|NC_DISKLESS|NC_PERSIST|NC_INMEMORY);
+	parameters = NULL; /* kill off parallel */
+    } else
+	h5->http.iosp = 0;
+#endif /*ENABLE_BYTERANGE*/
+
+    nc4_info->mem.inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY);
+    nc4_info->mem.diskless = ((mode & NC_DISKLESS) == NC_DISKLESS);
+    nc4_info->mem.persist = ((mode & NC_PERSIST) == NC_PERSIST);
+
+    /* Does the mode specify that this file is read-only? */
+    if ((mode & NC_WRITE) == 0)
+	nc4_info->no_write = NC_TRUE;
+
+    if(nc4_info->mem.inmemory && nc4_info->mem.diskless)
+        BAIL(NC_EINTERNAL);
+
+#ifdef USE_PARALLEL4
+    mpiinfo = (NC_MPI_INFO*)parameters; /* assume, may be changed if inmemory is true */
+#endif /* !USE_PARALLEL4 */
+
+    /* Need this access plist to control how HDF5 handles open objects
+     * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
+     * fail if there are any open objects in the file). */
+    if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+        BAIL(NC_EHDFERR);
+
+    if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI) < 0)
+        BAIL(NC_EHDFERR);
+
+#ifdef USE_PARALLEL4
+    if (!(mode & (NC_INMEMORY | NC_DISKLESS)) && mpiinfo != NULL) {
+        /* If this is a parallel file create, set up the file creation
+         * property list.
+         */
+        nc4_info->parallel = NC_TRUE;
+        LOG((4, "opening parallel file with MPI/IO"));
+        if (H5Pset_fapl_mpio(fapl_id, mpiinfo->comm, mpiinfo->info) < 0)
+            BAIL(NC_EPARINIT);
+
+        /* Keep copies of the MPI Comm & Info objects */
+        if (MPI_SUCCESS != MPI_Comm_dup(mpiinfo->comm, &nc4_info->comm))
+            BAIL(NC_EMPI);
+        comm_duped++;
+        if (MPI_INFO_NULL != mpiinfo->info)
+        {
+            if (MPI_SUCCESS != MPI_Info_dup(mpiinfo->info, &nc4_info->info))
+                BAIL(NC_EMPI);
+            info_duped++;
+        }
+        else
+        {
+            /* No dup, just copy it. */
+            nc4_info->info = mpiinfo->info;
+        }
+    }
+
+#ifdef HDF5_HAS_COLL_METADATA_OPS
+    if (H5Pset_all_coll_metadata_ops(fapl_id, 1) < 0)
+        BAIL(NC_EPARINIT);
+#endif
+
+#else /* only set cache for non-parallel. */
+    if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
+                     nc4_chunk_cache_preemption) < 0)
+        BAIL(NC_EHDFERR);
+    LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f",
+         __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems,
+         nc4_chunk_cache_preemption));
+#endif /* USE_PARALLEL4 */
+
+    /* Process  NC_INMEMORY */
+    if(nc4_info->mem.inmemory) {
+	NC_memio* memio;
+	/* validate */
+	if(parameters == NULL)
+	    BAIL(NC_EINMEMORY);
+	memio = (NC_memio*)parameters;
+	if(memio->memory == NULL || memio->size == 0)
+            BAIL(NC_EINMEMORY);
+	/* initialize h5->mem */
+	nc4_info->mem.memio = *memio;
+	/* Is the incoming memory locked? */
+	nc4_info->mem.locked = (nc4_info->mem.memio.flags & NC_MEMIO_LOCKED) == NC_MEMIO_LOCKED;
+	/* As a safeguard, if not locked and not read-only,
+	   then we must take control of the incoming memory */
+	if(!nc4_info->mem.locked && !nc4_info->no_write) {
+            memio->memory = NULL; /* take control */
+            memio->size = 0;
+	}
+	retval = NC4_open_image_file(nc4_info);
+	if(retval)
+            BAIL(NC_EHDFERR);
+    }
+    else
+    if(nc4_info->mem.diskless) {   /* Process  NC_DISKLESS */
+	size_t min_incr = 65536; /* Minimum buffer increment */
+	/* Configure FAPL to use the core file driver */
+	if (H5Pset_fapl_core(fapl_id, min_incr, (nc4_info->mem.persist?1:0)) < 0)
+	BAIL(NC_EHDFERR);
+	/* Open the HDF5 file. */
+	if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
+            BAIL(NC_EHDFERR);
+    }
+#ifdef ENABLE_BYTERANGE
+    else
+    if(h5->http.iosp) {   /* Arrange to use the byte-range driver */
+	/* Configure FAPL to use the byte-range file driver */
+	if (H5Pset_fapl_http(fapl_id) < 0)
+	    BAIL(NC_EHDFERR);
+	/* Open the HDF5 file. */
+	if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
+	    BAIL(NC_EHDFERR);
+    }
+#endif
+    else
+    {
+       /* Open the HDF5 file. */
+       if ((h5->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
+          BAIL(NC_EHDFERR);
+    }
+
+    /* Now read in all the metadata. Some types and dimscale
+     * information may be difficult to resolve here, if, for example, a
+     * dataset of user-defined type is encountered before the
+     * definition of that type. */
+    if ((retval = rec_read_metadata(nc4_info->root_grp)))
+       BAIL(retval);
+
+    /* Check for classic model attribute. */
+    if ((retval = check_for_classic_model(nc4_info->root_grp, &is_classic)))
+       BAIL(retval);
+    if (is_classic)
+       nc4_info->cmode |= NC_CLASSIC_MODEL;
+
+    /* Set the provenance info for this file */
+    if ((retval = NC4_read_provenance(nc4_info)))
+       BAIL(retval);
+
+    /* Now figure out which netCDF dims are indicated by the dimscale
+     * information. */
+    if ((retval = rec_match_dimscales(nc4_info->root_grp)))
+       BAIL(retval);
+
+#ifdef LOGGING
+    /* This will print out the names, types, lens, etc of the vars and
+       atts in the file, if the logging level is 2 or greater. */
+    log_metadata_nc(nc4_info);
+#endif
+
+    /* Close the property list. */
+    if (H5Pclose(fapl_id) < 0)
+        BAIL(NC_EHDFERR);
+
+    return NC_NOERR;
+
+exit:
+#ifdef USE_PARALLEL4
+    if (comm_duped) MPI_Comm_free(&nc4_info->comm);
+    if (info_duped) MPI_Info_free(&nc4_info->info);
+#endif
+
+    if (fapl_id > 0 && fapl_id != H5P_DEFAULT)
+        H5Pclose(fapl_id);
+    if (nc4_info)
+        nc4_close_hdf5_file(nc4_info, 1, 0); /*  treat like abort*/
+    return retval;
+}
+
+/**
+ * @internal Open a netCDF-4 file.
+ *
+ * @param path The file name of the new file.
+ * @param mode The open mode flag.
+ * @param basepe Ignored by this function.
+ * @param chunksizehintp Ignored by this function.
+ * @param parameters pointer to struct holding extra data (e.g. for parallel I/O)
+ * layer. Ignored if NULL.
+ * @param dispatch Pointer to the dispatch table for this file.
+ * @param nc_file Pointer to an instance of NC.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINVAL Invalid inputs.
+ * @author Ed Hartnett
+ */
+int
+NC4_open(const char *path, int mode, int basepe, size_t *chunksizehintp,
+         void *parameters, NC_Dispatch *dispatch, NC *nc_file)
+{
+    assert(nc_file && path && dispatch && nc_file &&
+           nc_file->model->impl == NC_FORMATX_NC4);
+
+    LOG((1, "%s: path %s mode %d params %x",
+         __func__, path, mode, parameters));
+
+    /* Check the mode for validity */
+    if (mode & ILLEGAL_OPEN_FLAGS)
+        return NC_EINVAL;
+
+    if((mode & NC_DISKLESS) && (mode & NC_INMEMORY))
+        return NC_EINVAL;
+
+    /* If this is our first file, initialize HDF5. */
+    if (!nc4_hdf5_initialized)
+        nc4_hdf5_initialize();
+
+#ifdef LOGGING
+    /* If nc logging level has changed, see if we need to turn on
+     * HDF5's error messages. */
+    hdf5_set_log_level();
+#endif /* LOGGING */
+
+    nc_file->int_ncid = nc_file->ext_ncid;
+
+    /* Open the file. */
+    return nc4_open_file(path, mode, parameters, nc_file);
+}
+
+/**
+ * @internal Find out what filters are applied to this HDF5 dataset,
+ * fletcher32, deflate, and/or shuffle. All other filters are just
+ * dumped The possible values of
+ *
+ * @param propid ID of HDF5 var creation properties list.
+ * @param var Pointer to NC_VAR_INFO_T for this variable.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @author Dennis Heimbigner, Ed Hartnett
+ */
+static int get_filter_info(hid_t propid, NC_VAR_INFO_T *var)
+{
+    H5Z_filter_t filter;
+    int num_filters;
+    unsigned int cd_values_zip[CD_NELEMS_ZLIB];
+    size_t cd_nelems = CD_NELEMS_ZLIB;
+    int f;
+
+    assert(var);
+
+    if ((num_filters = H5Pget_nfilters(propid)) < 0)
+        return NC_EHDFERR;
+
+    for (f = 0; f < num_filters; f++)
+    {
+        if ((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems, cd_values_zip,
+                                     0, NULL, NULL)) < 0)
+            return NC_EHDFERR;
+        switch (filter)
+        {
+        case H5Z_FILTER_SHUFFLE:
+            var->shuffle = NC_TRUE;
+            break;
+
+        case H5Z_FILTER_FLETCHER32:
+            var->fletcher32 = NC_TRUE;
+            break;
+
+        case H5Z_FILTER_DEFLATE:
+            var->deflate = NC_TRUE;
+            if (cd_nelems != CD_NELEMS_ZLIB ||
+                cd_values_zip[0] > NC_MAX_DEFLATE_LEVEL)
+                return NC_EHDFERR;
+            var->deflate_level = cd_values_zip[0];
+            break;
+
+        case H5Z_FILTER_SZIP:
+            /* Szip is tricky because the filter code expands the set of parameters from 2 to 4
+               and changes some of the parameter values */
+            var->filterid = filter;
+            if(cd_nelems == 0)
+                var->params = NULL;
+            else {
+                /* We have to re-read the parameters based on actual nparams,
+                   which in the case of szip, differs from users original nparams */
+                var->params = (unsigned int*)calloc(1,sizeof(unsigned int)*cd_nelems);
+                if(var->params == NULL)
+                    return NC_ENOMEM;
+                if((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems,
+                                            var->params, 0, NULL, NULL)) < 0)
+                    return NC_EHDFERR;
+                /* fix up the parameters and the #params */
+                var->nparams = cd_nelems;
+            }
+            break;
+
+        default:
+            var->filterid = filter;
+            var->nparams = cd_nelems;
+            if(cd_nelems == 0)
+                var->params = NULL;
+            else {
+                /* We have to re-read the parameters based on actual nparams */
+                var->params = (unsigned int*)calloc(1,sizeof(unsigned int)*var->nparams);
+                if(var->params == NULL)
+                    return NC_ENOMEM;
+                if((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems,
+                                            var->params, 0, NULL, NULL)) < 0)
+                    return NC_EHDFERR;
+            }
+            break;
+        }
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal Learn if there is a fill value defined for a variable,
+ * and, if so, its value.
+ *
+ * @param propid ID of HDF5 var creation properties list.
+ * @param var Pointer to NC_VAR_INFO_T for this variable.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @author Dennis Heimbigner, Ed Hartnett
+ */
+static int get_fill_info(hid_t propid, NC_VAR_INFO_T *var)
+{
+    H5D_fill_value_t fill_status;
+
+    /* Is there a fill value associated with this dataset? */
+    if (H5Pfill_value_defined(propid, &fill_status) < 0)
+        return NC_EHDFERR;
+
+    /* Get the fill value, if there is one defined. */
+    if (fill_status == H5D_FILL_VALUE_USER_DEFINED)
+    {
+        /* Allocate space to hold the fill value. */
+        if (!var->fill_value)
+        {
+            if (var->type_info->nc_type_class == NC_VLEN)
+            {
+                if (!(var->fill_value = malloc(sizeof(nc_vlen_t))))
+                    return NC_ENOMEM;
+            }
+            else if (var->type_info->nc_type_class == NC_STRING)
+            {
+                if (!(var->fill_value = malloc(sizeof(char *))))
+                    return NC_ENOMEM;
+            }
+            else
+            {
+                assert(var->type_info->size);
+                if (!(var->fill_value = malloc(var->type_info->size)))
+                    return NC_ENOMEM;
+            }
+        }
+
+        /* Get the fill value from the HDF5 property lust. */
+        if (H5Pget_fill_value(propid, ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid,
+                              var->fill_value) < 0)
+            return NC_EHDFERR;
+    }
+    else
+        var->no_fill = NC_TRUE;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Learn the chunking settings of a var.
+ *
+ * @param propid ID of HDF5 var creation properties list.
+ * @param var Pointer to NC_VAR_INFO_T for this variable.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @author Dennis Heimbigner, Ed Hartnett
+ */
+static int get_chunking_info(hid_t propid, NC_VAR_INFO_T *var)
+{
+    H5D_layout_t layout;
+    hsize_t chunksize[H5S_MAX_RANK] = {0};
+    int d;
+
+    /* Get the chunking info the var. */
+    if ((layout = H5Pget_layout(propid)) < -1)
+        return NC_EHDFERR;
+
+    /* Remember the layout and, if chunked, the chunksizes. */
+    if (layout == H5D_CHUNKED)
+    {
+        if (H5Pget_chunk(propid, H5S_MAX_RANK, chunksize) < 0)
+            return NC_EHDFERR;
+        if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t))))
+            return NC_ENOMEM;
+        for (d = 0; d < var->ndims; d++)
+            var->chunksizes[d] = chunksize[d];
+    }
+    else if (layout == H5D_CONTIGUOUS || layout == H5D_COMPACT)
+        var->contiguous = NC_TRUE;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function gets info about the dimscales attached to a
+ * dataset. The info is used later for dimscale matching.
+ *
+ * @param var Pointer to var info struct.
+ * @param hdf5_var Pointer to HDF5 var info struct.
+ * @param ndims Number of dims for this var.
+ * @param datasetid HDF5 datasetid.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EVARMETA Error with var metadata.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+get_attached_info(NC_VAR_INFO_T *var, NC_HDF5_VAR_INFO_T *hdf5_var, int ndims,
+                  hid_t datasetid)
+{
+    int d;
+    int num_scales = 0;
+
+    LOG((4, "%s ndims %d datasetid %ld", __func__, ndims, datasetid));
+
+    /* Find out how many scales are attached to this
+     * dataset. H5DSget_num_scales returns an error if there are no
+     * scales, so convert a negative return value to zero. */
+    num_scales = H5DSget_num_scales(datasetid, 0);
+    if (num_scales < 0)
+        num_scales = 0;
+    LOG((4, "num_scales %d", num_scales));
+
+    /* If an enddef has already been called, the dimscales will already
+     * be taken care of. */
+    if (num_scales && ndims && !var->dimscale_attached)
+    {
+        /* Allocate space to remember whether the dimscale has been
+         * attached for each dimension, and the HDF5 object IDs of the
+         * scale(s). */
+        assert(!hdf5_var->dimscale_hdf5_objids);
+        if (!(var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t))))
+            return NC_ENOMEM;
+        if (!(hdf5_var->dimscale_hdf5_objids = malloc(ndims *
+                                                      sizeof(struct hdf5_objid))))
+            return NC_ENOMEM;
+
+        /* Store id information allowing us to match hdf5 dimscales to
+         * netcdf dimensions. */
+        for (d = 0; d < var->ndims; d++)
+        {
+            LOG((4, "about to iterate scales for dim %d", d));
+            if (H5DSiterate_scales(hdf5_var->hdf_datasetid, d, NULL, dimscale_visitor,
+                                   &(hdf5_var->dimscale_hdf5_objids[d])) < 0)
+                return NC_EHDFERR;
+            var->dimscale_attached[d] = NC_TRUE;
+            LOG((4, "dimscale attached"));
+        }
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function reads scale info for vars, whether they
+ * are scales or not.
+ *
+ * @param grp Pointer to group info struct.
+ * @param dim Pointer to dim info struct if this is a scale, NULL
+ * otherwise.
+ * @param var Pointer to var info struct.
+ * @param hdf5_var Pointer to HDF5 var info struct.
+ * @param ndims Number of dims for this var.
+ * @param datasetid HDF5 datasetid.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EVARMETA Error with var metadata.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+get_scale_info(NC_GRP_INFO_T *grp, NC_DIM_INFO_T *dim, NC_VAR_INFO_T *var,
+               NC_HDF5_VAR_INFO_T *hdf5_var, int ndims, hid_t datasetid)
+{
+    int retval;
+
+    /* If it's a scale, mark it as such. */
+    if (dim)
+    {
+        assert(ndims);
+        var->dimscale = NC_TRUE;
+
+        /* If this is a multi-dimensional coordinate var, then the
+         * dimids must be stored in the hidden coordinates attribute. */
+        if (var->ndims > 1)
+        {
+            if ((retval = read_coord_dimids(grp, var)))
+                return retval;
+        }
+        else
+        {
+            /* This is a 1-dimensional coordinate var. */
+            assert(!strcmp(var->hdr.name, dim->hdr.name));
+            var->dimids[0] = dim->hdr.id;
+            var->dim[0] = dim;
+        }
+        dim->coord_var = var;
+    }
+    else /* Not a scale. */
+    {
+        if (!var->coords_read)
+            if ((retval = get_attached_info(var, hdf5_var, ndims, datasetid)))
+                return retval;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Get the metadata for a variable.
+ *
+ * @param var Pointer to var info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EVARMETA Error with var metadata.
+ * @author Ed Hartnett
+ */
+int
+nc4_get_var_meta(NC_VAR_INFO_T *var)
+{
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    hid_t access_pid = 0;
+    hid_t propid = 0;
+    double rdcc_w0;
+    int retval = NC_NOERR;
+
+    assert(var && var->format_var_info);
+    LOG((3, "%s: var %s", __func__, var->hdr.name));
+
+    /* Have we already read the var metadata? */
+    if (var->meta_read)
+        return NC_NOERR;
+
+    /* Get pointer to the HDF5-specific var info struct. */
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Get the current chunk cache settings. */
+    if ((access_pid = H5Dget_access_plist(hdf5_var->hdf_datasetid)) < 0)
+        BAIL(NC_EVARMETA);
+
+    /* Learn about current chunk cache settings. */
+    if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems),
+                            &(var->chunk_cache_size), &rdcc_w0)) < 0)
+        BAIL(NC_EHDFERR);
+    var->chunk_cache_preemption = rdcc_w0;
+
+    /* Get the dataset creation properties. */
+    if ((propid = H5Dget_create_plist(hdf5_var->hdf_datasetid)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Get var chunking info. */
+    if ((retval = get_chunking_info(propid, var)))
+        BAIL(retval);
+
+    /* Get filter info for a var. */
+    if ((retval = get_filter_info(propid, var)))
+        BAIL(retval);
+
+    /* Get fill value, if defined. */
+    if ((retval = get_fill_info(propid, var)))
+        BAIL(retval);
+
+    /* Is this a deflated variable with a chunksize greater than the
+     * current cache size? */
+    if ((retval = nc4_adjust_var_cache(var->container, var)))
+        BAIL(retval);
+
+    if (var->coords_read && !var->dimscale)
+        if ((retval = get_attached_info(var, hdf5_var, var->ndims, hdf5_var->hdf_datasetid)))
+            return retval;
+
+    /* Remember that we have read the metadata for this var. */
+    var->meta_read = NC_TRUE;
+
+exit:
+    if (access_pid && H5Pclose(access_pid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (propid > 0 && H5Pclose(propid) < 0)
+        BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal This function is called by read_dataset(), (which is
+ * called by rec_read_metadata()) when a netCDF variable is found in
+ * the file. This function reads in all the metadata about the
+ * var. Attributes are not read until the user asks for information
+ * about one of them.
+ *
+ * @param grp Pointer to group info struct.
+ * @param datasetid HDF5 dataset ID.
+ * @param obj_name Name of the HDF5 object to read.
+ * @param ndims Number of dimensions.
+ * @param dim If non-NULL, then this var is a coordinate var for a
+ * dimension, and this points to the info for that dimension.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EVARMETA Error with var metadata.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+read_var(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
+         size_t ndims, NC_DIM_INFO_T *dim)
+{
+    NC_VAR_INFO_T *var = NULL;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    int incr_id_rc = 0; /* Whether dataset ID's ref count has been incremented */
+    char *finalname = NULL;
+    int retval = NC_NOERR;
+
+    assert(obj_name && grp);
+    LOG((4, "%s: obj_name %s", __func__, obj_name));
+
+    /* Check for a weird case: a non-coordinate variable that has the
+     * same name as a dimension. It's legal in netcdf, and requires
+     * that the HDF5 dataset name be changed. */
+    if (strlen(obj_name) > strlen(NON_COORD_PREPEND) &&
+        !strncmp(obj_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND)))
+    {
+        /* Allocate space for the name. */
+        if (!(finalname = malloc(((strlen(obj_name) -
+                                   strlen(NON_COORD_PREPEND))+ 1) * sizeof(char))))
+            BAIL(NC_ENOMEM);
+        strcpy(finalname, &obj_name[strlen(NON_COORD_PREPEND)]);
+    } else
+        finalname = strdup(obj_name);
+
+    /* Add a variable to the end of the group's var list. */
+    if ((retval = nc4_var_list_add(grp, finalname, ndims, &var)))
+        BAIL(retval);
+
+    /* Add storage for HDF5-specific var info. */
+    if (!(var->format_var_info = calloc(1, sizeof(NC_HDF5_VAR_INFO_T))))
+        BAIL(NC_ENOMEM);
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Fill in what we already know. */
+    hdf5_var->hdf_datasetid = datasetid;
+    H5Iinc_ref(hdf5_var->hdf_datasetid); /* Increment number of objects using ID */
+    incr_id_rc++; /* Indicate that we've incremented the ref. count (for errors) */
+    var->created = NC_TRUE;
+    var->atts_read = 0;
+
+    /* Try and read the dimids from the COORDINATES attribute. If it's
+     * not present, we will have to do dimsscale matching to locate the
+     * dims for this var. */
+    retval = read_coord_dimids(grp, var);
+    if (retval && retval != NC_ENOTATT)
+        BAIL(retval);
+    retval = NC_NOERR;
+
+    /* Handle scale info. */
+    if ((retval = get_scale_info(grp, dim, var, hdf5_var, ndims, datasetid)))
+        BAIL(retval);
+
+    /* Learn all about the type of this variable. This will fail for
+     * HDF5 reference types, and then the var we just created will be
+     * deleted, thus ignoring HDF5 reference type objects. */
+    if ((retval = get_type_info2(var->container->nc4_info, hdf5_var->hdf_datasetid,
+                                 &var->type_info)))
+        BAIL(retval);
+
+    /* Indicate that the variable has a pointer to the type */
+    var->type_info->rc++;
+
+exit:
+    if (finalname)
+        free(finalname);
+    if (retval)
+    {
+        /* If there was an error, decrement the dataset ref counter, and
+         * delete the var info struct we just created. */
+        if (incr_id_rc && H5Idec_ref(datasetid) < 0)
+            BAIL2(NC_EHDFERR);
+        if (var)
+            nc4_var_list_del(grp, var);
+    }
+
+    return retval;
+}
+
+/**
+ * @internal Given an HDF5 type, set a pointer to netcdf type.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param native_typeid HDF5 type ID.
+ * @param xtype Pointer that gets netCDF type.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EBADTYPID Type not found.
+ * @author Ed Hartnett
+ */
+static int
+get_netcdf_type(NC_FILE_INFO_T *h5, hid_t native_typeid,
+                nc_type *xtype)
+{
+    NC_TYPE_INFO_T *type;
+    H5T_class_t class;
+    htri_t is_str, equal = 0;
+
+    assert(h5 && xtype);
+
+    if ((class = H5Tget_class(native_typeid)) < 0)
+        return NC_EHDFERR;
+
+    /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
+     * H5Tget_class will return H5T_STRING if this is a string. */
+    if (class == H5T_STRING)
+    {
+        if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
+            return NC_EHDFERR;
+        if (is_str)
+            *xtype = NC_STRING;
+        else
+            *xtype = NC_CHAR;
+        return NC_NOERR;
+    }
+    else if (class == H5T_INTEGER || class == H5T_FLOAT)
+    {
+        /* For integers and floats, we don't have to worry about
+         * endianness if we compare native types. */
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SCHAR)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_BYTE;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SHORT)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_SHORT;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_INT)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_INT;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_FLOAT)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_FLOAT;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_DOUBLE)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_DOUBLE;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UCHAR)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_UBYTE;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_USHORT)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_USHORT;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UINT)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_UINT;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_LLONG)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_INT64;
+            return NC_NOERR;
+        }
+        if ((equal = H5Tequal(native_typeid, H5T_NATIVE_ULLONG)) < 0)
+            return NC_EHDFERR;
+        if (equal)
+        {
+            *xtype = NC_UINT64;
+            return NC_NOERR;
+        }
+    }
+
+    /* Maybe we already know about this type. */
+    if (!equal)
+        if((type = nc4_rec_find_hdf_type(h5, native_typeid)))
+        {
+            *xtype = type->hdr.id;
+            return NC_NOERR;
+        }
+
+    *xtype = NC_NAT;
+    return NC_EBADTYPID;
+}
+
+/**
+ * @internal Read an attribute. This is called by
+ * att_read_callbk().
+ *
+ * @param grp Pointer to group info struct.
+ * @param attid Attribute ID.
+ * @param att Pointer that gets att info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EATTMETA Att metadata error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+static int
+read_hdf5_att(NC_GRP_INFO_T *grp, hid_t attid, NC_ATT_INFO_T *att)
+{
+    NC_HDF5_ATT_INFO_T *hdf5_att;
+    hid_t spaceid = 0, file_typeid = 0;
+    hsize_t dims[1] = {0}; /* netcdf attributes always 1-D. */
+    size_t type_size;
+    int att_ndims;
+    hssize_t att_npoints;
+    H5T_class_t att_class;
+    int fixed_len_string = 0;
+    size_t fixed_size = 0;
+    int retval = NC_NOERR;
+
+    assert(att && att->hdr.name && att->format_att_info);
+    LOG((5, "%s: att->hdr.id %d att->hdr.name %s att->nc_typeid %d att->len %d",
+         __func__, att->hdr.id, att->hdr.name, (int)att->nc_typeid, att->len));
+
+    /* Get HDF5-sepecific info stuct for this attribute. */
+    hdf5_att = (NC_HDF5_ATT_INFO_T *)att->format_att_info;
+
+    /* Get type of attribute in file. */
+    if ((file_typeid = H5Aget_type(attid)) < 0)
+        return NC_EATTMETA;
+    if ((hdf5_att->native_hdf_typeid = H5Tget_native_type(file_typeid,
+                                                          H5T_DIR_DEFAULT)) < 0)
+        BAIL(NC_EHDFERR);
+    if ((att_class = H5Tget_class(hdf5_att->native_hdf_typeid)) < 0)
+        BAIL(NC_EATTMETA);
+    if (att_class == H5T_STRING &&
+        !H5Tis_variable_str(hdf5_att->native_hdf_typeid))
+    {
+        fixed_len_string++;
+        if (!(fixed_size = H5Tget_size(hdf5_att->native_hdf_typeid)))
+            BAIL(NC_EATTMETA);
+    }
+    if ((retval = get_netcdf_type(grp->nc4_info, hdf5_att->native_hdf_typeid,
+                                  &(att->nc_typeid))))
+        BAIL(retval);
+
+
+    /* Get len. */
+    if ((spaceid = H5Aget_space(attid)) < 0)
+        BAIL(NC_EATTMETA);
+    if ((att_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
+        BAIL(NC_EATTMETA);
+    if ((att_npoints = H5Sget_simple_extent_npoints(spaceid)) < 0)
+        BAIL(NC_EATTMETA);
+
+    /* If both att_ndims and att_npoints are zero, then this is a
+     * zero length att. */
+    if (att_ndims == 0 && att_npoints == 0)
+        dims[0] = 0;
+    else if (att->nc_typeid == NC_STRING)
+        dims[0] = att_npoints;
+    else if (att->nc_typeid == NC_CHAR)
+    {
+        /* NC_CHAR attributes are written as a scalar in HDF5, of type
+         * H5T_C_S1, of variable length. */
+        if (att_ndims == 0)
+        {
+            if (!(dims[0] = H5Tget_size(file_typeid)))
+                BAIL(NC_EATTMETA);
+        }
+        else
+        {
+            /* This is really a string type! */
+            att->nc_typeid = NC_STRING;
+            dims[0] = att_npoints;
+        }
+    }
+    else
+    {
+        H5S_class_t space_class;
+
+        /* All netcdf attributes are scalar or 1-D only. */
+        if (att_ndims > 1)
+            BAIL(NC_EATTMETA);
+
+        /* Check class of HDF5 dataspace */
+        if ((space_class = H5Sget_simple_extent_type(spaceid)) < 0)
+            BAIL(NC_EATTMETA);
+
+        /* Check for NULL HDF5 dataspace class (should be weeded out
+         * earlier) */
+        if (H5S_NULL == space_class)
+            BAIL(NC_EATTMETA);
+
+        /* check for SCALAR HDF5 dataspace class */
+        if (H5S_SCALAR == space_class)
+            dims[0] = 1;
+        else /* Must be "simple" dataspace */
+        {
+            /* Read the size of this attribute. */
+            if (H5Sget_simple_extent_dims(spaceid, dims, NULL) < 0)
+                BAIL(NC_EATTMETA);
+        }
+    }
+
+    /* Tell the user what the length if this attribute is. */
+    att->len = dims[0];
+
+    /* Allocate some memory if the len is not zero, and read the
+       attribute. */
+    if (dims[0])
+    {
+        if ((retval = nc4_get_typelen_mem(grp->nc4_info, att->nc_typeid,
+                                          &type_size)))
+            return retval;
+        if (att_class == H5T_VLEN)
+        {
+            if (!(att->vldata = malloc((unsigned int)(att->len * sizeof(hvl_t)))))
+                BAIL(NC_ENOMEM);
+            if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->vldata) < 0)
+                BAIL(NC_EATTMETA);
+        }
+        else if (att->nc_typeid == NC_STRING)
+        {
+            if (!(att->stdata = calloc(att->len, sizeof(char *))))
+                BAIL(NC_ENOMEM);
+            /* For a fixed length HDF5 string, the read requires
+             * contiguous memory. Meanwhile, the netCDF API requires that
+             * nc_free_string be called on string arrays, which would not
+             * work if one contiguous memory block were used. So here I
+             * convert the contiguous block of strings into an array of
+             * malloced strings (each string with its own malloc). Then I
+             * copy the data and free the contiguous memory. This
+             * involves copying the data, which is bad, but this only
+             * occurs for fixed length string attributes, and presumably
+             * these are small. (And netCDF-4 does not create them - it
+             * always uses variable length strings. */
+            if (fixed_len_string)
+            {
+                int i;
+                char *contig_buf, *cur;
+
+                /* Alloc space for the contiguous memory read. */
+                if (!(contig_buf = malloc(att->len * fixed_size * sizeof(char))))
+                    BAIL(NC_ENOMEM);
+
+                /* Read the fixed-len strings as one big block. */
+                if (H5Aread(attid, hdf5_att->native_hdf_typeid, contig_buf) < 0) {
+                    free(contig_buf);
+                    BAIL(NC_EATTMETA);
+                }
+
+                /* Copy strings, one at a time, into their new home. Alloc
+                   space for each string. The user will later free this
+                   space with nc_free_string. */
+                cur = contig_buf;
+                for (i = 0; i < att->len; i++)
+                {
+                    if (!(att->stdata[i] = malloc(fixed_size))) {
+                        free(contig_buf);
+                        BAIL(NC_ENOMEM);
+                    }
+                    strncpy(att->stdata[i], cur, fixed_size);
+                    cur += fixed_size;
+                }
+
+                /* Free contiguous memory buffer. */
+                free(contig_buf);
+            }
+            else
+            {
+                /* Read variable-length string atts. */
+                if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->stdata) < 0)
+                    BAIL(NC_EATTMETA);
+            }
+        }
+        else
+        {
+            if (!(att->data = malloc((unsigned int)(att->len * type_size))))
+                BAIL(NC_ENOMEM);
+            if (H5Aread(attid, hdf5_att->native_hdf_typeid, att->data) < 0)
+                BAIL(NC_EATTMETA);
+        }
+    }
+
+    if (H5Tclose(file_typeid) < 0)
+        BAIL(NC_EHDFERR);
+    if (H5Sclose(spaceid) < 0)
+        return NC_EHDFERR;
+
+    return NC_NOERR;
+
+exit:
+    if (H5Tclose(file_typeid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (spaceid > 0 && H5Sclose(spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal Wrap HDF5 allocated memory free operations
+ *
+ * @param memory Pointer to memory to be freed.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+static void
+hdf5free(void* memory)
+{
+#ifndef JNA
+    /* On Windows using the microsoft runtime, it is an error
+       for one library to free memory allocated by a different library.*/
+#ifdef HDF5_HAS_H5FREE
+    if(memory != NULL) H5free_memory(memory);
+#else
+#ifndef _MSC_VER
+    if(memory != NULL) free(memory);
+#endif
+#endif
+#endif
+}
+
+/**
+ * @internal Read information about a user defined type from the HDF5
+ * file, and stash it in the group's list of types.
+ *
+ * @param grp Pointer to group info struct.
+ * @param hdf_typeid HDF5 type ID.
+ * @param type_name Pointer that gets the type name.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @return ::NC_EBADTYPID Type not found.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+static int
+read_type(NC_GRP_INFO_T *grp, hid_t hdf_typeid, char *type_name)
+{
+    NC_TYPE_INFO_T *type;
+    NC_HDF5_TYPE_INFO_T *hdf5_type;
+    H5T_class_t class;
+    hid_t native_typeid;
+    size_t type_size;
+    int nmembers;
+    int retval;
+
+    assert(grp && type_name);
+
+    LOG((4, "%s: type_name %s grp->hdr.name %s", __func__, type_name,
+         grp->hdr.name));
+
+    /* What is the native type for this platform? */
+    if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
+        return NC_EHDFERR;
+
+    /* What is the size of this type on this platform. */
+    if (!(type_size = H5Tget_size(native_typeid)))
+        return NC_EHDFERR;
+    LOG((5, "type_size %d", type_size));
+
+    /* Add to the list for this new type, and get a local pointer to it. */
+    if ((retval = nc4_type_list_add(grp, type_size, type_name, &type)))
+        return retval;
+
+    /* Allocate storage for HDF5-specific type info. */
+    if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T))))
+        return NC_ENOMEM;
+    type->format_type_info = hdf5_type;
+
+    /* Remember HDF5-specific type info. */
+    hdf5_type->hdf_typeid = hdf_typeid;
+    hdf5_type->native_hdf_typeid = native_typeid;
+
+    /* Remember we have committed this type. */
+    type->committed = NC_TRUE;
+
+    /* Increment number of objects using ID. */
+    if (H5Iinc_ref(hdf5_type->hdf_typeid) < 0)
+        return NC_EHDFERR;
+
+    /* What is the class of this type, compound, vlen, etc. */
+    if ((class = H5Tget_class(hdf_typeid)) < 0)
+        return NC_EHDFERR;
+    switch (class)
+    {
+    case H5T_STRING:
+        type->nc_type_class = NC_STRING;
+        break;
+
+    case H5T_COMPOUND:
+    {
+        int nmembers;
+        unsigned int m;
+        char* member_name = NULL;
+#ifdef JNA
+        char jna[1001];
+#endif
+
+        type->nc_type_class = NC_COMPOUND;
+
+        if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0)
+            return NC_EHDFERR;
+        LOG((5, "compound type has %d members", nmembers));
+        type->u.c.field = nclistnew();
+        nclistsetalloc(type->u.c.field,nmembers);
+
+        for (m = 0; m < nmembers; m++)
+        {
+            hid_t member_hdf_typeid;
+            hid_t member_native_typeid;
+            size_t member_offset;
+            H5T_class_t mem_class;
+            nc_type member_xtype;
+
+            /* Get the typeid and native typeid of this member of the
+             * compound type. */
+            if ((member_hdf_typeid = H5Tget_member_type(native_typeid, m)) < 0)
+                return NC_EHDFERR;
+
+            if ((member_native_typeid = H5Tget_native_type(member_hdf_typeid,
+                                                           H5T_DIR_DEFAULT)) < 0)
+                return NC_EHDFERR;
+
+            /* Get the name of the member.*/
+            member_name = H5Tget_member_name(native_typeid, m);
+            if (!member_name || strlen(member_name) > NC_MAX_NAME) {
+                retval = NC_EBADNAME;
+                break;
+            }
+#ifdef JNA
+            else {
+                strncpy(jna,member_name,1000);
+                member_name = jna;
+            }
+#endif
+
+            /* Offset in bytes on *this* platform. */
+            member_offset = H5Tget_member_offset(native_typeid, m);
+
+            /* Get dimensional data if this member is an array of something. */
+            if ((mem_class = H5Tget_class(member_hdf_typeid)) < 0)
+                return NC_EHDFERR;
+            if (mem_class == H5T_ARRAY)
+            {
+                int ndims, dim_size[NC_MAX_VAR_DIMS];
+                hsize_t dims[NC_MAX_VAR_DIMS];
+                int d;
+
+                if ((ndims = H5Tget_array_ndims(member_hdf_typeid)) < 0)
+                    return NC_EHDFERR;
+
+                if (H5Tget_array_dims(member_hdf_typeid, dims, NULL) != ndims)
+                    return NC_EHDFERR;
+
+                for (d = 0; d < ndims; d++)
+                    dim_size[d] = dims[d];
+
+                /* What is the netCDF typeid of this member? */
+                if ((retval = get_netcdf_type(grp->nc4_info, H5Tget_super(member_hdf_typeid),
+                                              &member_xtype)))
+                    return retval;
+
+                /* Add this member to our list of fields in this compound type. */
+                if ((retval = nc4_field_list_add(type, member_name, member_offset,
+                                                 member_xtype, ndims, dim_size)))
+                    return retval;
+            }
+            else
+            {
+                /* What is the netCDF typeid of this member? */
+                if ((retval = get_netcdf_type(grp->nc4_info, member_native_typeid,
+                                              &member_xtype)))
+                    return retval;
+
+                /* Add this member to our list of fields in this compound type. */
+                if ((retval = nc4_field_list_add(type, member_name, member_offset,
+                                                 member_xtype, 0, NULL)))
+                    return retval;
+            }
+
+            hdf5free(member_name);
+        }
+    }
+    break;
+
+    case H5T_VLEN:
+    {
+        htri_t ret;
+
+        /* For conveninence we allow user to pass vlens of strings
+         * with null terminated strings. This means strings are
+         * treated slightly differently by the API, although they are
+         * really just VLENs of characters. */
+        if ((ret = H5Tis_variable_str(hdf_typeid)) < 0)
+            return NC_EHDFERR;
+        if (ret)
+            type->nc_type_class = NC_STRING;
+        else
+        {
+            hid_t base_hdf_typeid;
+            nc_type base_nc_type = NC_NAT;
+
+            type->nc_type_class = NC_VLEN;
+
+            /* Find the base type of this vlen (i.e. what is this a
+             * vlen of?) */
+            if (!(base_hdf_typeid = H5Tget_super(native_typeid)))
+                return NC_EHDFERR;
+
+            /* What size is this type? */
+            if (!(type_size = H5Tget_size(base_hdf_typeid)))
+                return NC_EHDFERR;
+
+            /* What is the netcdf corresponding type. */
+            if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
+                                          &base_nc_type)))
+                return retval;
+            LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
+                 base_hdf_typeid, type_size, base_nc_type));
+
+            /* Remember the base type for this vlen. */
+            type->u.v.base_nc_typeid = base_nc_type;
+        }
+    }
+    break;
+
+    case H5T_OPAQUE:
+        type->nc_type_class = NC_OPAQUE;
+        break;
+
+    case H5T_ENUM:
+    {
+        hid_t base_hdf_typeid;
+        nc_type base_nc_type = NC_NAT;
+        void *value;
+        int i;
+        char *member_name = NULL;
+#ifdef JNA
+        char jna[1001];
+#endif
+
+        type->nc_type_class = NC_ENUM;
+
+        /* Find the base type of this enum (i.e. what is this a
+         * enum of?) */
+        if (!(base_hdf_typeid = H5Tget_super(hdf_typeid)))
+            return NC_EHDFERR;
+        /* What size is this type? */
+        if (!(type_size = H5Tget_size(base_hdf_typeid)))
+            return NC_EHDFERR;
+        /* What is the netcdf corresponding type. */
+        if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
+                                      &base_nc_type)))
+            return retval;
+        LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
+             base_hdf_typeid, type_size, base_nc_type));
+
+        /* Remember the base type for this enum. */
+        type->u.e.base_nc_typeid = base_nc_type;
+
+        /* Find out how many member are in the enum. */
+        if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0)
+            return NC_EHDFERR;
+        type->u.e.enum_member = nclistnew();
+        nclistsetalloc(type->u.e.enum_member,nmembers);
+
+        /* Allocate space for one value. */
+        if (!(value = calloc(1, type_size)))
+            return NC_ENOMEM;
+
+        /* Read each name and value defined in the enum. */
+        for (i = 0; i < nmembers; i++)
+        {
+            /* Get the name and value from HDF5. */
+            if (!(member_name = H5Tget_member_name(hdf_typeid, i)))
+                return NC_EHDFERR;
+
+#ifdef JNA
+            strncpy(jna,member_name,1000);
+            member_name = jna;
+#endif
+
+            if (strlen(member_name) > NC_MAX_NAME)
+                return NC_EBADNAME;
+
+            if (H5Tget_member_value(hdf_typeid, i, value) < 0)
+                return NC_EHDFERR;
+
+            /* Insert new field into this type's list of fields. */
+            if ((retval = nc4_enum_member_add(type, type->size,
+                                              member_name, value)))
+                return retval;
+
+            hdf5free(member_name);
+        }
+        free(value);
+    }
+    break;
+
+    default:
+        LOG((0, "unknown class"));
+        return NC_EBADCLASS;
+    }
+    return retval;
+}
+
+/**
+ * @internal Callback function for reading attributes. This is used
+ * for both global and variable attributes.
+ *
+ * @param loc_id HDF5 attribute ID.
+ * @param att_name Name of the attrigute.
+ * @param ainfo HDF5 info struct for attribute.
+ * @param att_data Pointer to an att_iter_info struct, which contains
+ * pointers to the NC_GRP_INFO_T and (for variable attributes) the
+ * NC_VAR_INFO_T. For global atts the var pointer is NULL.
+ *
+ * @return ::NC_NOERR No error. Iteration continues.
+ * @return ::-1 Error. Stop iteration.
+ * @author Ed Hartnett
+ */
+static herr_t
+att_read_callbk(hid_t loc_id, const char *att_name, const H5A_info_t *ainfo,
+                void *att_data)
+{
+
+    hid_t attid = 0;
+    NC_ATT_INFO_T *att;
+    NCindex *list;
+    att_iter_info *att_info = (att_iter_info *)att_data;
+    int retval = NC_NOERR;
+
+    /* Determin what list is being added to. */
+    list = att_info->var ? att_info->var->att : att_info->grp->att;
+
+    /* This may be an attribute telling us that strict netcdf-3 rules
+     * are in effect. If so, we will make note of the fact, but not add
+     * this attribute to the metadata. It's not a user attribute, but
+     * an internal netcdf-4 one. */
+    if (!strcmp(att_name, NC3_STRICT_ATT_NAME))
+    {
+        /* Only relevant for groups, not vars. */
+        if (!att_info->var)
+            att_info->grp->nc4_info->cmode |= NC_CLASSIC_MODEL;
+        return NC_NOERR;
+    }
+
+    /* Should we ignore this attribute? */
+    if (NC_findreserved(att_name))
+        return NC_NOERR;
+
+    /* Add to the end of the list of atts for this var. */
+    if ((retval = nc4_att_list_add(list, att_name, &att)))
+        BAIL(-1);
+
+    /* Allocate storage for the HDF5 specific att info. */
+    if (!(att->format_att_info = calloc(1, sizeof(NC_HDF5_ATT_INFO_T))))
+        BAIL(-1);
+
+    /* Open the att by name. */
+    if ((attid = H5Aopen(loc_id, att_name, H5P_DEFAULT)) < 0)
+        BAIL(-1);
+    LOG((4, "%s::  att_name %s", __func__, att_name));
+
+    /* Read the rest of the info about the att,
+     * including its values. */
+    if ((retval = read_hdf5_att(att_info->grp, attid, att)))
+        BAIL(retval);
+
+    if (att)
+        att->created = NC_TRUE;
+
+exit:
+    if (retval == NC_EBADTYPID)
+    {
+        /* NC_EBADTYPID will be normally converted to NC_NOERR so that
+           the parent iterator does not fail. */
+        retval = nc4_att_list_del(list, att);
+        att = NULL;
+    }
+    if (attid > 0 && H5Aclose(attid) < 0)
+        retval = -1;
+
+    /* Since this is a HDF5 iterator callback, return -1 for any error
+     * to stop iteration. */
+    if (retval)
+        retval = -1;
+    return retval;
+}
+
+/**
+ * @internal This function reads all the attributes of a variable or
+ * the global attributes of a group.
+ *
+ * @param grp Pointer to the group info.
+ * @param var Pointer to the var info. NULL for global att reads.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EATTMETA Some error occured reading attributes.
+ * @author Ed Hartnett
+ */
+int
+nc4_read_atts(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
+{
+    att_iter_info att_info;         /* Custom iteration information */
+    hid_t locid; /* HDF5 location to read atts from. */
+
+    /* Check inputs. */
+    assert(grp);
+
+    /* Assign var and grp in struct. (var may be NULL). */
+    att_info.var = var;
+    att_info.grp = grp;
+
+    /* Determine where to read from in the HDF5 file. */
+    locid = var ? ((NC_HDF5_VAR_INFO_T *)(var->format_var_info))->hdf_datasetid :
+        ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid;
+
+    /* Now read all the attributes at this location, ignoring special
+     * netCDF hidden attributes. */
+    if ((H5Aiterate2(locid, H5_INDEX_CRT_ORDER, H5_ITER_INC, NULL,
+                     att_read_callbk, &att_info)) < 0)
+        return NC_EATTMETA;
+
+    /* Remember that we have read the atts for this var or group. */
+    if (var)
+        var->atts_read = 1;
+    else
+        grp->atts_read = 1;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function is called by read_dataset() when a
+ * dimension scale dataset is encountered. It reads in the dimension
+ * data (creating a new NC_DIM_INFO_T object), and also checks to see
+ * if this is a dimension without a variable - that is, a coordinate
+ * dimension which does not have any coordinate data.
+ *
+ * @param grp Pointer to group info struct.
+ * @param datasetid The HDF5 dataset ID.
+ * @param obj_name The HDF5 object name.
+ * @param statbuf HDF5 status buffer.
+ * @param scale_size Size of dimension scale.
+ * @param max_scale_size Maximum size of dim scale.
+ * @param dim Pointer to pointer that gets new dim info struct.
+ *
+ * @returns ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @author Ed Hartnett
+ */
+static int
+read_scale(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
+           const H5G_stat_t *statbuf, hsize_t scale_size,
+           hsize_t max_scale_size, NC_DIM_INFO_T **dim)
+{
+    NC_DIM_INFO_T *new_dim; /* Dimension added to group */
+    NC_HDF5_DIM_INFO_T *new_hdf5_dim; /* HDF5-specific dim info. */
+    char dimscale_name_att[NC_MAX_NAME + 1] = "";    /* Dimscale name, for checking if dim without var */
+    htri_t attr_exists = -1; /* Flag indicating hidden attribute exists */
+    hid_t attid = -1; /* ID of hidden attribute (to store dim ID) */
+    int dimscale_created = 0; /* Remember if a dimension was created (for error recovery) */
+    short initial_next_dimid = grp->nc4_info->next_dimid;/* Retain for error recovery */
+    size_t len = 0;
+    int too_long = NC_FALSE;
+    int assigned_id = -1;
+    int retval = NC_NOERR;
+
+    assert(grp && dim);
+
+    /* Does this dataset have a hidden attribute that tells us its
+     * dimid? If so, read it. */
+    if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0)
+        BAIL(NC_EHDFERR);
+    if (attr_exists)
+    {
+        if ((attid = H5Aopen_name(datasetid, NC_DIMID_ATT_NAME)) < 0)
+            BAIL(NC_EHDFERR);
+
+        if (H5Aread(attid, H5T_NATIVE_INT, &assigned_id) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* Check if scale's dimid should impact the group's next dimid */
+        if (assigned_id >= grp->nc4_info->next_dimid)
+            grp->nc4_info->next_dimid = assigned_id + 1;
+    }
+
+    /* Get dim size. On machines with a size_t of less than 8 bytes, it
+     * is possible for a dimension to be too long. */
+    if (SIZEOF_SIZE_T < 8 && scale_size > NC_MAX_UINT)
+    {
+        len = NC_MAX_UINT;
+        too_long = NC_TRUE;
+    }
+    else
+        len = scale_size;
+
+    /* Create the dimension for this scale. */
+    if ((retval = nc4_dim_list_add(grp, obj_name, len, assigned_id, &new_dim)))
+        BAIL(retval);
+    new_dim->too_long = too_long;
+
+    /* Create struct for HDF5-specific dim info. */
+    if (!(new_dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
+        BAIL(NC_ENOMEM);
+    new_hdf5_dim = (NC_HDF5_DIM_INFO_T *)new_dim->format_dim_info;
+
+    dimscale_created++;
+
+    /* Remember these 4 values to uniquely identify this dataset in the
+     * HDF5 file. */
+    new_hdf5_dim->hdf5_objid.fileno[0] = statbuf->fileno[0];
+    new_hdf5_dim->hdf5_objid.fileno[1] = statbuf->fileno[1];
+    new_hdf5_dim->hdf5_objid.objno[0] = statbuf->objno[0];
+    new_hdf5_dim->hdf5_objid.objno[1] = statbuf->objno[1];
+
+    /* If the dimscale has an unlimited dimension, then this dimension
+     * is unlimited. */
+    if (max_scale_size == H5S_UNLIMITED)
+        new_dim->unlimited = NC_TRUE;
+
+    /* If the scale name is set to DIM_WITHOUT_VARIABLE, then this is a
+     * dimension, but not a variable. (If get_scale_name returns an
+     * error, just move on, there's no NAME.) */
+    if (H5DSget_scale_name(datasetid, dimscale_name_att, NC_MAX_NAME) >= 0)
+    {
+        if (!strncmp(dimscale_name_att, DIM_WITHOUT_VARIABLE,
+                     strlen(DIM_WITHOUT_VARIABLE)))
+        {
+            if (new_dim->unlimited)
+            {
+                size_t len = 0, *lenp = &len;
+
+                /* Find actual length by checking all datasets that use
+                 * this dim. */
+                if ((retval = nc4_find_dim_len(grp, new_dim->hdr.id, &lenp)))
+                    BAIL(retval);
+                new_dim->len = *lenp;
+            }
+
+            /* Hold open the dataset, since the dimension doesn't have a
+             * coordinate variable */
+            new_hdf5_dim->hdf_dimscaleid = datasetid;
+            H5Iinc_ref(new_hdf5_dim->hdf_dimscaleid);        /* Increment number of objects using ID */
+        }
+    }
+
+    /* Set the dimension created. */
+    *dim = new_dim;
+
+exit:
+    /* Close the hidden attribute, if it was opened. */
+    if (attid > 0 && H5Aclose(attid) < 0)
+        BAIL2(NC_EHDFERR);
+
+    /* On error, undo any dimscale creation */
+    if (retval && dimscale_created)
+    {
+        /* free the dimension */
+        if ((retval = nc4_dim_list_del(grp, new_dim)))
+            BAIL2(retval);
+
+        /* Reset the group's information */
+        grp->nc4_info->next_dimid = initial_next_dimid;
+    }
+
+    return retval;
+}
+
+/**
+ * @internal Read a HDF5 dataset. This function is called when
+ * read_hdf5_obj() encounters an HDF5 dataset when opening a file.
+ *
+ * @param grp Pointer to group info struct.
+ * @param datasetid HDF5 dataset ID.
+ * @param obj_name Object name.
+ * @param statbuf HDF5 status buffer.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EHDFERR HDF5 returned error.
+ * @author Ed Hartnett
+ */
+static int
+read_dataset(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
+             const H5G_stat_t *statbuf)
+{
+    NC_DIM_INFO_T *dim = NULL;   /* Dimension created for scales */
+    NC_HDF5_DIM_INFO_T *hdf5_dim;
+    hid_t spaceid = 0;
+    int ndims;
+    htri_t is_scale;
+    int retval = NC_NOERR;
+
+    /* Get the dimension information for this dataset. */
+    if ((spaceid = H5Dget_space(datasetid)) < 0)
+        BAIL(NC_EHDFERR);
+    if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Is this a dimscale? */
+    if ((is_scale = H5DSis_scale(datasetid)) < 0)
+        BAIL(NC_EHDFERR);
+    if (is_scale)
+    {
+        hsize_t dims[H5S_MAX_RANK];
+        hsize_t max_dims[H5S_MAX_RANK];
+
+        /* Query the scale's size & max. size */
+        if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* Read the scale information. */
+        if ((retval = read_scale(grp, datasetid, obj_name, statbuf, dims[0],
+                                 max_dims[0], &dim)))
+            BAIL(retval);
+        hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+    }
+
+    /* Add a var to the linked list, and get its metadata,
+     * unless this is one of those funny dimscales that are a
+     * dimension in netCDF but not a variable. (Spooky!) */
+    if (!dim || (dim && !hdf5_dim->hdf_dimscaleid))
+        if ((retval = read_var(grp, datasetid, obj_name, ndims, dim)))
+            BAIL(retval);
+
+exit:
+    if (spaceid && H5Sclose(spaceid) <0)
+        BAIL2(retval);
+
+    return retval;
+}
+
+/**
+ * @internal Add HDF5 object info for a group to a list for later
+ * processing. We do this when we encounter groups, so that the parent
+ * group can be fully processed before the child groups.
+ *
+ * @param udata Pointer to the user data, in this case a
+ * user_data_t.
+ * @param oinfo The HDF5 object info.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+oinfo_list_add(user_data_t *udata, const hdf5_obj_info_t *oinfo)
+{
+    hdf5_obj_info_t *new_oinfo;    /* Pointer to info for object */
+
+    /* Allocate memory for the object's info. */
+    if (!(new_oinfo = calloc(1, sizeof(hdf5_obj_info_t))))
+        return NC_ENOMEM;
+
+    /* Make a copy of the object's info. */
+    memcpy(new_oinfo, oinfo, sizeof(hdf5_obj_info_t));
+
+    /* Add it to the list for future processing. */
+    nclistpush(udata->grps, new_oinfo);
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Callback function called by H5Literate() for every HDF5
+ * object in the file.
+ *
+ * @note This function is called by HDF5 so does not return a netCDF
+ * error code.
+ *
+ * @param grpid HDF5 group ID.
+ * @param name Name of object.
+ * @param info Info struct for object.
+ * @param _op_data Pointer to user data, a user_data_t. It will
+ * contain a pointer to the current group and a list of
+ * hdf5_obj_info_t. Any child groups will get their hdf5_obj_info
+ * added to this list.
+ *
+ * @return H5_ITER_CONT No error, continue iteration.
+ * @return H5_ITER_ERROR HDF5 error, stop iteration.
+ * @author Ed Hartnett
+ */
+static int
+read_hdf5_obj(hid_t grpid, const char *name, const H5L_info_t *info,
+              void *_op_data)
+{
+    /* Pointer to user data for callback */
+    user_data_t *udata = (user_data_t *)_op_data;
+    hdf5_obj_info_t oinfo;    /* Pointer to info for object */
+    int retval = H5_ITER_CONT;
+
+    /* Open this critter. */
+    if ((oinfo.oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0)
+        BAIL(H5_ITER_ERROR);
+
+    /* Get info about the object.*/
+    if (H5Gget_objinfo(oinfo.oid, ".", 1, &oinfo.statbuf) < 0)
+        BAIL(H5_ITER_ERROR);
+
+    strncpy(oinfo.oname, name, NC_MAX_NAME);
+
+    /* Add object to list, for later */
+    switch(oinfo.statbuf.type)
+    {
+    case H5G_GROUP:
+        LOG((3, "found group %s", oinfo.oname));
+
+        /* Defer descending into child group immediately, so that the
+         * types in the current group can be processed and be ready for
+         * use by vars in the child group(s). */
+        if (oinfo_list_add(udata, &oinfo))
+            BAIL(H5_ITER_ERROR);
+        break;
+
+    case H5G_DATASET:
+        LOG((3, "found dataset %s", oinfo.oname));
+
+        /* Learn all about this dataset, which may be a dimscale
+         * (i.e. dimension metadata), or real data. */
+        if ((retval = read_dataset(udata->grp, oinfo.oid, oinfo.oname,
+                                   &oinfo.statbuf)))
+        {
+            /* Allow NC_EBADTYPID to transparently skip over datasets
+             * which have a datatype that netCDF-4 doesn't undertand
+             * (currently), but break out of iteration for other
+             * errors. */
+            if (retval != NC_EBADTYPID)
+                BAIL(H5_ITER_ERROR);
+            else
+                retval = H5_ITER_CONT;
+        }
+
+        /* Close the object */
+        if (H5Oclose(oinfo.oid) < 0)
+            BAIL(H5_ITER_ERROR);
+        break;
+
+    case H5G_TYPE:
+        LOG((3, "found datatype %s", oinfo.oname));
+
+        /* Process the named datatype */
+        if (read_type(udata->grp, oinfo.oid, oinfo.oname))
+            BAIL(H5_ITER_ERROR);
+
+        /* Close the object */
+        if (H5Oclose(oinfo.oid) < 0)
+            BAIL(H5_ITER_ERROR);
+        break;
+
+    default:
+        LOG((0, "Unknown object class %d in %s!", oinfo.statbuf.type, __func__));
+        BAIL(H5_ITER_ERROR);
+    }
+
+exit:
+    if (retval)
+    {
+        if (oinfo.oid > 0 && H5Oclose(oinfo.oid) < 0)
+            BAIL2(H5_ITER_ERROR);
+    }
+
+    return (retval);
+}
+
+/**
+ * @internal This is the main function to recursively read all the
+ * metadata for the file. The links in the 'grp' are iterated over and
+ * added to the file's metadata information. Note that child groups
+ * are not immediately processed, but are deferred until all the other
+ * links in the group are handled (so that vars in the child groups
+ * are guaranteed to have types that they use in a parent group in
+ * place).
+ *
+ * @param grp Pointer to a group.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_ECANTWRITE File must be opened read-only.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+rec_read_metadata(NC_GRP_INFO_T *grp)
+{
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    user_data_t udata;         /* User data for iteration */
+    hdf5_obj_info_t *oinfo;    /* Pointer to info for object */
+    hsize_t idx = 0;
+    hid_t pid = -1;
+    unsigned crt_order_flags = 0;
+    H5_index_t iter_index;
+    int i, retval = NC_NOERR;
+
+    assert(grp && grp->hdr.name && grp->format_grp_info);
+    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
+
+    /* Get HDF5-specific group info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    /* Open this HDF5 group and retain its grpid. It will remain open
+     * with HDF5 until this file is nc_closed. */
+    if (!hdf5_grp->hdf_grpid)
+    {
+        if (grp->parent)
+        {
+            /* This is a child group. */
+            NC_HDF5_GRP_INFO_T *parent_hdf5_grp;
+            parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
+
+            if ((hdf5_grp->hdf_grpid = H5Gopen2(parent_hdf5_grp->hdf_grpid,
+                                                grp->hdr.name, H5P_DEFAULT)) < 0)
+                BAIL(NC_EHDFERR);
+        }
+        else
+        {
+            /* This is the root group. */
+            NC_HDF5_FILE_INFO_T *hdf5_info;
+            hdf5_info = (NC_HDF5_FILE_INFO_T *)grp->nc4_info->format_file_info;
+
+            if ((hdf5_grp->hdf_grpid = H5Gopen2(hdf5_info->hdfid, "/",
+                                                H5P_DEFAULT)) < 0)
+                BAIL(NC_EHDFERR);
+        }
+    }
+    assert(hdf5_grp->hdf_grpid > 0);
+
+    /* Get the group creation flags, to check for creation ordering. */
+    if ((pid = H5Gget_create_plist(hdf5_grp->hdf_grpid)) < 0)
+        BAIL(NC_EHDFERR);
+    if (H5Pget_link_creation_order(pid, &crt_order_flags) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Set the iteration index to use. */
+    if (crt_order_flags & H5P_CRT_ORDER_TRACKED)
+        iter_index = H5_INDEX_CRT_ORDER;
+    else
+    {
+        NC_FILE_INFO_T *h5 = grp->nc4_info;
+
+        /* Without creation ordering, file must be read-only. */
+        if (!h5->no_write)
+            BAIL(NC_ECANTWRITE);
+
+        iter_index = H5_INDEX_NAME;
+    }
+
+    /* Set user data for iteration over any child groups. */
+    udata.grp = grp;
+    udata.grps = nclistnew();
+
+    /* Iterate over links in this group, building lists for the types,
+     * datasets and groups encountered. A pointer to udata will be
+     * passed as a parameter to the callback function
+     * read_hdf5_obj(). (I have also tried H5Oiterate(), but it is much
+     * slower iterating over the same file - Ed.) */
+    if (H5Literate(hdf5_grp->hdf_grpid, iter_index, H5_ITER_INC, &idx,
+                   read_hdf5_obj, (void *)&udata) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Process the child groups found. (Deferred until now, so that the
+     * types in the current group get processed and are available for
+     * vars in the child group(s).) */
+    for (i = 0; i < nclistlength(udata.grps); i++)
+    {
+        NC_GRP_INFO_T *child_grp;
+        oinfo = (hdf5_obj_info_t*)nclistget(udata.grps, i);
+
+        /* Add group to file's hierarchy. */
+        if ((retval = nc4_grp_list_add(grp->nc4_info, grp, oinfo->oname,
+                                       &child_grp)))
+            BAIL(retval);
+
+        /* Allocate storage for HDF5-specific group info. */
+        if (!(child_grp->format_grp_info = calloc(1, sizeof(NC_HDF5_GRP_INFO_T))))
+            return NC_ENOMEM;
+
+        /* Recursively read the child group's metadata. */
+        if ((retval = rec_read_metadata(child_grp)))
+            BAIL(retval);
+    }
+
+    /* When reading existing file, mark all variables as written. */
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+        ((NC_VAR_INFO_T *)ncindexith(grp->vars, i))->written_to = NC_TRUE;
+
+exit:
+    if (pid > 0 && H5Pclose(pid) < 0)
+        BAIL2(NC_EHDFERR);
+
+    /* Clean up list of child groups. */
+    for (i = 0; i < nclistlength(udata.grps); i++)
+    {
+        oinfo = (hdf5_obj_info_t *)nclistget(udata.grps, i);
+        /* Close the open HDF5 object. */
+        if (H5Oclose(oinfo->oid) < 0)
+            BAIL2(NC_EHDFERR);
+        free(oinfo);
+    }
+    nclistfree(udata.grps);
+
+    return retval;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5type.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5type.c
new file mode 100644
index 0000000000000000000000000000000000000000..37a3dfd7a077471bfb9e50b38a84f427c6b94781
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5type.c
@@ -0,0 +1,549 @@
+/* Copyright 2005-2018, University Corporation for Atmospheric
+ * Research. See the COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file @internal This file is part of netcdf-4, a netCDF-like
+ * interface for HDF5, or a HDF5 backend for netCDF, depending on your
+ * point of view.
+ *
+ * This file handles the nc4 user-defined type functions
+ * (i.e. compound and opaque types).
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+
+/**
+ * @internal Determine if two types are equal.
+ *
+ * @param ncid1 First file/group ID.
+ * @param typeid1 First type ID.
+ * @param ncid2 Second file/group ID.
+ * @param typeid2 Second type ID.
+ * @param equalp Pointer that will get 1 if the two types are equal.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EBADTYPE Type not found.
+ * @return ::NC_EINVAL Invalid type.
+ * @author Ed Hartnett
+ */
+extern int
+NC4_inq_type_equal(int ncid1, nc_type typeid1, int ncid2,
+                   nc_type typeid2, int *equalp)
+{
+    NC_GRP_INFO_T *grpone, *grptwo;
+    NC_TYPE_INFO_T *type1, *type2;
+    int retval;
+
+    LOG((2, "nc_inq_type_equal: ncid1 0x%x typeid1 %d ncid2 0x%x typeid2 %d",
+         ncid1, typeid1, ncid2, typeid2));
+
+    /* Check input. */
+    if(equalp == NULL) return NC_NOERR;
+
+    if (typeid1 <= NC_NAT || typeid2 <= NC_NAT)
+        return NC_EINVAL;
+
+    /* If one is atomic, and the other user-defined, the types are not
+     * equal. */
+    if ((typeid1 <= NC_STRING && typeid2 > NC_STRING) ||
+        (typeid2 <= NC_STRING && typeid1 > NC_STRING))
+    {
+        *equalp = 0;
+        return NC_NOERR;
+    }
+
+    /* If both are atomic types, the answer is easy. */
+    if (typeid1 <= NUM_ATOMIC_TYPES)
+    {
+        if (typeid1 == typeid2)
+            *equalp = 1;
+        else
+            *equalp = 0;
+        return NC_NOERR;
+    }
+
+    /* Not atomic types - so find type1 and type2 information. */
+    if ((retval = nc4_find_nc4_grp(ncid1, &grpone)))
+        return retval;
+    if (!(type1 = nclistget(grpone->nc4_info->alltypes, typeid1)))
+        return NC_EBADTYPE;
+    if ((retval = nc4_find_nc4_grp(ncid2, &grptwo)))
+        return retval;
+    if (!(type2 = nclistget(grptwo->nc4_info->alltypes, typeid2)))
+        return NC_EBADTYPE;
+
+    /* Are the two types equal? */
+    {
+        hid_t hid1, hid2;
+
+        /* Get the HDF5 types from the HDF5-specific type info. */
+        assert(type1->format_type_info && type2->format_type_info);
+        hid1 = ((NC_HDF5_TYPE_INFO_T *)type1->format_type_info)->native_hdf_typeid;
+        hid2 = ((NC_HDF5_TYPE_INFO_T *)type2->format_type_info)->native_hdf_typeid;
+
+        /* Ask HDF5 if the types are equal. */
+        if ((retval = H5Tequal(hid1, hid2)) < 0)
+            return NC_EHDFERR;
+        *equalp = 1 ? retval : 0;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Get the id of a type from the name.
+ *
+ * @param ncid File and group ID.
+ * @param name Name of type.
+ * @param typeidp Pointer that will get the type ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM Out of memory.
+ * @return ::NC_EINVAL Bad size.
+ * @return ::NC_ENOTNC4 User types in netCDF-4 files only.
+ * @return ::NC_EBADTYPE Type not found.
+ * @author Ed Hartnett
+ */
+extern int
+NC4_inq_typeid(int ncid, const char *name, nc_type *typeidp)
+{
+    NC_GRP_INFO_T *grp;
+    NC_GRP_INFO_T *grptwo;
+    NC_FILE_INFO_T *h5;
+    NC_TYPE_INFO_T *type = NULL;
+    char *norm_name;
+    int i, retval;
+
+    /* Handle atomic types. */
+    for (i = 0; i < NUM_ATOMIC_TYPES; i++)
+        if (!strcmp(name, nc4_atomic_name[i]))
+        {
+            if (typeidp)
+                *typeidp = i;
+            return NC_NOERR;
+        }
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        return retval;
+    assert(h5 && grp);
+
+    /* If the first char is a /, this is a fully-qualified
+     * name. Otherwise, this had better be a local name (i.e. no / in
+     * the middle). */
+    if (name[0] != '/' && strstr(name, "/"))
+        return NC_EINVAL;
+
+    /* Normalize name. */
+    if (!(norm_name = (char*)malloc(strlen(name) + 1)))
+        return NC_ENOMEM;
+    if ((retval = nc4_normalize_name(name, norm_name))) {
+        free(norm_name);
+        return retval;
+    }
+    /* Is the type in this group? If not, search parents. */
+    for (grptwo = grp; grptwo; grptwo = grptwo->parent) {
+        type = (NC_TYPE_INFO_T*)ncindexlookup(grptwo->type,norm_name);
+        if(type)
+        {
+            if (typeidp)
+                *typeidp = type->hdr.id;
+            break;
+        }
+    }
+
+    /* Still didn't find type? Search file recursively, starting at the
+     * root group. */
+    if (!type)
+        if ((type = nc4_rec_find_named_type(grp->nc4_info->root_grp, norm_name)))
+            if (typeidp)
+                *typeidp = type->hdr.id;
+
+    free(norm_name);
+
+    /* OK, I give up already! */
+    if (!type)
+        return NC_EBADTYPE;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This internal function adds a new user defined type to
+ * the metadata of a group of an open file.
+ *
+ * @param ncid File and group ID.
+ * @param size Size in bytes of new type.
+ * @param name Name of new type.
+ * @param base_typeid Base type ID.
+ * @param type_class NC_VLEN, NC_ENUM, or NC_STRING
+ * @param typeidp Pointer that gets new type ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTNC4 User types in netCDF-4 files only.
+ * @return ::NC_EINVAL Bad size.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @return ::NC_ESTRICTNC3 Cannot define user types in classic model.
+ * @author Ed Hartnett
+ */
+static int
+add_user_type(int ncid, size_t size, const char *name, nc_type base_typeid,
+              nc_type type_class, nc_type *typeidp)
+{
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp;
+    NC_TYPE_INFO_T *type;
+    NC_HDF5_TYPE_INFO_T *hdf5_type;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        return retval;
+
+    LOG((2, "%s: ncid 0x%x size %d name %s base_typeid %d ",
+         __FUNCTION__, ncid, size, norm_name, base_typeid));
+
+    /* Find group metadata. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        return retval;
+    assert(h5 && grp);
+
+    /* User types cannot be defined with classic model flag. */
+    if (h5->cmode & NC_CLASSIC_MODEL)
+        return NC_ESTRICTNC3;
+
+    /* Turn on define mode if it is not on. */
+    if (!(h5->cmode & NC_INDEF))
+        if ((retval = NC4_redef(ncid)))
+            return retval;
+
+    /* No size is provided for vlens or enums, get it from the base type. */
+    if (type_class == NC_VLEN || type_class == NC_ENUM)
+    {
+        if ((retval = nc4_get_typelen_mem(grp->nc4_info, base_typeid, &size)))
+            return retval;
+    }
+    else if (size <= 0)
+        return NC_EINVAL;
+
+    /* Check that this name is not in use as a var, grp, or type. */
+    if ((retval = nc4_check_dup_name(grp, norm_name)))
+        return retval;
+
+    /* Add to our list of types. */
+    if ((retval = nc4_type_list_add(grp, size, norm_name, &type)))
+        return retval;
+
+    /* Allocate storage for HDF5-specific type info. */
+    if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T))))
+        return NC_ENOMEM;
+    type->format_type_info = hdf5_type;
+
+    /* Remember info about this type. */
+    type->nc_type_class = type_class;
+    if (type_class == NC_VLEN)
+        type->u.v.base_nc_typeid = base_typeid;
+    else if (type_class == NC_ENUM) {
+        type->u.e.base_nc_typeid = base_typeid;
+        type->u.e.enum_member = nclistnew();
+    } else if (type_class == NC_COMPOUND)
+        type->u.c.field = nclistnew();
+
+    /* Return the typeid to the user. */
+    if (typeidp)
+        *typeidp = type->hdr.id;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Create a compound type.
+ *
+ * @param ncid File and group ID.
+ * @param size Gets size in bytes of one element of type.
+ * @param name Name of the type.
+ * @param typeidp Gets the type ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTNC4 User types in netCDF-4 files only.
+ * @return ::NC_EINVAL Bad size.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @return ::NC_ESTRICTNC3 Cannot define user types in classic model.
+ * @author Ed Hartnett
+ */
+int
+NC4_def_compound(int ncid, size_t size, const char *name, nc_type *typeidp)
+{
+    return add_user_type(ncid, size, name, 0, NC_COMPOUND, typeidp);
+}
+
+/**
+ * @internal Insert a named field into a compound type.
+ *
+ * @param ncid File and group ID.
+ * @param typeid1 Type ID.
+ * @param name Name of the type.
+ * @param offset Offset of field.
+ * @param field_typeid Field type ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @author Ed Hartnett
+ */
+int
+NC4_insert_compound(int ncid, nc_type typeid1, const char *name, size_t offset,
+                    nc_type field_typeid)
+{
+    return nc_insert_array_compound(ncid, typeid1, name, offset,
+                                    field_typeid, 0, NULL);
+}
+
+/**
+ * @internal Insert a named array into a compound type.
+ *
+ * @param ncid File and group ID.
+ * @param typeid1 Type ID.
+ * @param name Name of the array field.
+ * @param offset Offset in bytes.
+ * @param field_typeid Type of field.
+ * @param ndims Number of dims for field.
+ * @param dim_sizesp Array of dim sizes.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @author Ed Hartnett
+ */
+extern int
+NC4_insert_array_compound(int ncid, int typeid1, const char *name,
+                          size_t offset, nc_type field_typeid,
+                          int ndims, const int *dim_sizesp)
+{
+    NC_GRP_INFO_T *grp;
+    NC_TYPE_INFO_T *type;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "nc_insert_array_compound: ncid 0x%x, typeid %d name %s "
+         "offset %d field_typeid %d ndims %d", ncid, typeid1,
+         name, offset, field_typeid, ndims));
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        return retval;
+
+    /* Find file metadata. */
+    if ((retval = nc4_find_nc4_grp(ncid, &grp)))
+        return retval;
+
+    /* Find type metadata. */
+    if ((retval = nc4_find_type(grp->nc4_info, typeid1, &type)))
+        return retval;
+
+    /* Did the user give us a good compound type typeid? */
+    if (!type || type->nc_type_class != NC_COMPOUND)
+        return NC_EBADTYPE;
+
+    /* If this type has already been written to the file, you can't
+     * change it. */
+    if (type->committed)
+        return NC_ETYPDEFINED;
+
+    /* Insert new field into this type's list of fields. */
+    if ((retval = nc4_field_list_add(type, norm_name, offset, field_typeid,
+                                     ndims, dim_sizesp)))
+        return retval;
+
+    return NC_NOERR;
+}
+
+/* Opaque type. */
+
+/**
+ * @internal Create an opaque type. Provide a size and a name.
+ *
+ * @param ncid File and group ID.
+ * @param datum_size Size in bytes of a datum.
+ * @param name Name of new vlen type.
+ * @param typeidp Pointer that gets new type ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTNC4 User types in netCDF-4 files only.
+ * @return ::NC_EINVAL Bad size.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @return ::NC_ESTRICTNC3 Cannot define user types in classic model.
+ * @author Ed Hartnett
+ */
+int
+NC4_def_opaque(int ncid, size_t datum_size, const char *name,
+               nc_type *typeidp)
+{
+    return add_user_type(ncid, datum_size, name, 0, NC_OPAQUE, typeidp);
+}
+
+
+/**
+ * @internal Define a variable length type.
+ *
+ * @param ncid File and group ID.
+ * @param name Name of new vlen type.
+ * @param base_typeid Base type of vlen.
+ * @param typeidp Pointer that gets new type ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTNC4 User types in netCDF-4 files only.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @return ::NC_ESTRICTNC3 Cannot define user types in classic model.
+ * @author Ed Hartnett
+ */
+int
+NC4_def_vlen(int ncid, const char *name, nc_type base_typeid,
+             nc_type *typeidp)
+{
+    return add_user_type(ncid, 0, name, base_typeid, NC_VLEN, typeidp);
+}
+
+/**
+ * @internal Create an enum type. Provide a base type and a name. At
+ * the moment only ints are accepted as base types.
+ *
+ * @param ncid File and group ID.
+ * @param base_typeid Base type of vlen.
+ * @param name Name of new vlen type.
+ * @param typeidp Pointer that gets new type ID.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_ENOTNC4 User types in netCDF-4 files only.
+ * @return ::NC_EMAXNAME Name is too long.
+ * @return ::NC_EBADNAME Name breaks netCDF name rules.
+ * @return ::NC_ESTRICTNC3 Cannot define user types in classic model.
+ * @author Ed Hartnett
+ */
+int
+NC4_def_enum(int ncid, nc_type base_typeid, const char *name,
+             nc_type *typeidp)
+{
+    return add_user_type(ncid, 0, name, base_typeid, NC_ENUM, typeidp);
+}
+
+/**
+ * @internal Insert a identifier value into an enum type. The value
+ * must fit within the size of the enum type, the identifier size must
+ * be <= NC_MAX_NAME.
+ *
+ * @param ncid File and group ID.
+ * @param typeid1 Type ID.
+ * @param identifier Name of this enum value.
+ * @param value Value of enum.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EBADID Bad ncid.
+ * @return ::NC_EBADTYPE Type not found.
+ * @return ::NC_ETYPDEFINED Type already defined.
+ * @author Ed Hartnett
+ */
+int
+NC4_insert_enum(int ncid, nc_type typeid1, const char *identifier,
+                const void *value)
+{
+    NC_GRP_INFO_T *grp;
+    NC_TYPE_INFO_T *type;
+    char norm_name[NC_MAX_NAME + 1];
+    int retval;
+
+    LOG((2, "nc_insert_enum: ncid 0x%x, typeid %d identifier %s value %d", ncid,
+         typeid1, identifier, value));
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(identifier, norm_name)))
+        return retval;
+
+    /* Find file metadata. */
+    if ((retval = nc4_find_nc4_grp(ncid, &grp)))
+        return retval;
+
+    /* Find type metadata. */
+    if ((retval = nc4_find_type(grp->nc4_info, typeid1, &type)))
+        return retval;
+
+    /* Did the user give us a good enum typeid? */
+    if (!type || type->nc_type_class != NC_ENUM)
+        return NC_EBADTYPE;
+
+    /* If this type has already been written to the file, you can't
+     * change it. */
+    if (type->committed)
+        return NC_ETYPDEFINED;
+
+    /* Insert new field into this type's list of fields. */
+    if ((retval = nc4_enum_member_add(type, type->size,
+                                      norm_name, value)))
+        return retval;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Insert one element into an already allocated vlen array
+ * element.
+ *
+ * @param ncid File and group ID.
+ * @param typeid1 Type ID.
+ * @param vlen_element The VLEN element to insert.
+ * @param len Length of element in bytes.
+ * @param data Element data.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_put_vlen_element(int ncid, int typeid1, void *vlen_element,
+                     size_t len, const void *data)
+{
+    nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
+    tmp->len = len;
+    tmp->p = (void *)data;
+    return NC_NOERR;
+}
+
+/**
+ * @internal Insert one element into an already allocated vlen array
+ * element.
+ *
+ * @param ncid File and group ID.
+ * @param typeid1 Type ID.
+ * @param vlen_element The VLEN element to insert.
+ * @param len Length of element in bytes.
+ * @param data Element data.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+NC4_get_vlen_element(int ncid, int typeid1, const void *vlen_element,
+                     size_t *len, void *data)
+{
+    const nc_vlen_t *tmp = (nc_vlen_t*)vlen_element;
+    int type_size = 4;
+
+    *len = tmp->len;
+    memcpy(data, tmp->p, tmp->len * type_size);
+    return NC_NOERR;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5var.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5var.c
new file mode 100644
index 0000000000000000000000000000000000000000..3888526422c23bd928861b6aa86357ea135f8929
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/hdf5var.c
@@ -0,0 +1,2265 @@
+/* Copyright 2003-2019, University Corporation for Atmospheric
+ * Research. See COPYRIGHT file for copying and redistribution
+ * conditions.*/
+/**
+ * @file
+ * @internal This file handles the HDF5 variable functions.
+ *
+ * @author Ed Hartnett
+ */
+
+#include "config.h"
+#include <hdf5internal.h>
+#include <math.h> /* For pow() used below. */
+
+/** @internal Default size for unlimited dim chunksize. */
+#define DEFAULT_1D_UNLIM_SIZE (4096)
+
+/** @internal Temp name used when renaming vars to preserve varid
+ * order. */
+#define NC_TEMP_NAME "_netcdf4_temporary_variable_name_for_rename"
+
+/**
+ * @internal If the HDF5 dataset for this variable is open, then close
+ * it and reopen it, with the perhaps new settings for chunk caching.
+ *
+ * @param grp Pointer to the group info.
+ * @param var Pointer to the var info.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+int
+nc4_reopen_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
+{
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    hid_t access_pid;
+    hid_t grpid;
+
+    assert(var && var->format_var_info && grp && grp->format_grp_info);
+
+    /* Get the HDF5-specific var info. */
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    if (hdf5_var->hdf_datasetid)
+    {
+        /* Get the HDF5 group id. */
+        grpid = ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid;
+
+
+        if ((access_pid = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+            return NC_EHDFERR;
+        if (H5Pset_chunk_cache(access_pid, var->chunk_cache_nelems,
+                               var->chunk_cache_size,
+                               var->chunk_cache_preemption) < 0)
+            return NC_EHDFERR;
+        if (H5Dclose(hdf5_var->hdf_datasetid) < 0)
+            return NC_EHDFERR;
+        if ((hdf5_var->hdf_datasetid = H5Dopen2(grpid, var->hdr.name, access_pid)) < 0)
+            return NC_EHDFERR;
+        if (H5Pclose(access_pid) < 0)
+            return NC_EHDFERR;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Check a set of chunksizes to see if they specify a chunk
+ * that is too big.
+ *
+ * @param grp Pointer to the group info.
+ * @param var Pointer to the var info.
+ * @param chunksizes Array of chunksizes to check.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_EBADCHUNK Bad chunksize.
+ */
+static int
+check_chunksizes(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, const size_t *chunksizes)
+{
+    double dprod;
+    size_t type_len;
+    int d;
+    int retval;
+
+    if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->hdr.id, &type_len)))
+        return retval;
+    if (var->type_info->nc_type_class == NC_VLEN)
+        dprod = (double)sizeof(hvl_t);
+    else
+        dprod = (double)type_len;
+    for (d = 0; d < var->ndims; d++)
+        dprod *= (double)chunksizes[d];
+
+    if (dprod > (double) NC_MAX_UINT)
+        return NC_EBADCHUNK;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Determine some default chunksizes for a variable.
+ *
+ * @param grp Pointer to the group info.
+ * @param var Pointer to the var info.
+ *
+ * @returns ::NC_NOERR for success
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+static int
+nc4_find_default_chunksizes2(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
+{
+    int d;
+    size_t type_size;
+    float num_values = 1, num_unlim = 0;
+    int retval;
+    size_t suggested_size;
+#ifdef LOGGING
+    double total_chunk_size;
+#endif
+
+    if (var->type_info->nc_type_class == NC_STRING)
+        type_size = sizeof(char *);
+    else
+        type_size = var->type_info->size;
+
+#ifdef LOGGING
+    /* Later this will become the total number of bytes in the default
+     * chunk. */
+    total_chunk_size = (double) type_size;
+#endif
+
+    /* How many values in the variable (or one record, if there are
+     * unlimited dimensions). */
+    for (d = 0; d < var->ndims; d++)
+    {
+        assert(var->dim[d]);
+        if (! var->dim[d]->unlimited)
+            num_values *= (float)var->dim[d]->len;
+        else {
+            num_unlim++;
+            var->chunksizes[d] = 1; /* overwritten below, if all dims are unlimited */
+        }
+    }
+    /* Special case to avoid 1D vars with unlim dim taking huge amount
+       of space (DEFAULT_CHUNK_SIZE bytes). Instead we limit to about
+       4KB */
+    if (var->ndims == 1 && num_unlim == 1) {
+        if (DEFAULT_CHUNK_SIZE / type_size <= 0)
+            suggested_size = 1;
+        else if (DEFAULT_CHUNK_SIZE / type_size > DEFAULT_1D_UNLIM_SIZE)
+            suggested_size = DEFAULT_1D_UNLIM_SIZE;
+        else
+            suggested_size = DEFAULT_CHUNK_SIZE / type_size;
+        var->chunksizes[0] = suggested_size / type_size;
+        LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d "
+             "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[0]));
+    }
+    if (var->ndims > 1 && var->ndims == num_unlim) { /* all dims unlimited */
+        suggested_size = pow((double)DEFAULT_CHUNK_SIZE/type_size, 1.0/(double)(var->ndims));
+        for (d = 0; d < var->ndims; d++)
+        {
+            var->chunksizes[d] = suggested_size ? suggested_size : 1;
+            LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d "
+                 "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[d]));
+        }
+    }
+
+    /* Pick a chunk length for each dimension, if one has not already
+     * been picked above. */
+    for (d = 0; d < var->ndims; d++)
+        if (!var->chunksizes[d])
+        {
+            suggested_size = (pow((double)DEFAULT_CHUNK_SIZE/(num_values * type_size),
+                                  1.0/(double)(var->ndims - num_unlim)) * var->dim[d]->len - .5);
+            if (suggested_size > var->dim[d]->len)
+                suggested_size = var->dim[d]->len;
+            var->chunksizes[d] = suggested_size ? suggested_size : 1;
+            LOG((4, "%s: name %s dim %d DEFAULT_CHUNK_SIZE %d num_values %f type_size %d "
+                 "chunksize %ld", __func__, var->hdr.name, d, DEFAULT_CHUNK_SIZE, num_values, type_size, var->chunksizes[d]));
+        }
+
+#ifdef LOGGING
+    /* Find total chunk size. */
+    for (d = 0; d < var->ndims; d++)
+        total_chunk_size *= (double) var->chunksizes[d];
+    LOG((4, "total_chunk_size %f", total_chunk_size));
+#endif
+
+    /* But did this result in a chunk that is too big? */
+    retval = check_chunksizes(grp, var, var->chunksizes);
+    if (retval)
+    {
+        /* Other error? */
+        if (retval != NC_EBADCHUNK)
+            return retval;
+
+        /* Chunk is too big! Reduce each dimension by half and try again. */
+        for ( ; retval == NC_EBADCHUNK; retval = check_chunksizes(grp, var, var->chunksizes))
+            for (d = 0; d < var->ndims; d++)
+                var->chunksizes[d] = var->chunksizes[d]/2 ? var->chunksizes[d]/2 : 1;
+    }
+
+    /* Do we have any big data overhangs? They can be dangerous to
+     * babies, the elderly, or confused campers who have had too much
+     * beer. */
+    for (d = 0; d < var->ndims; d++)
+    {
+        size_t num_chunks;
+        size_t overhang;
+        assert(var->chunksizes[d] > 0);
+        num_chunks = (var->dim[d]->len + var->chunksizes[d] - 1) / var->chunksizes[d];
+        if(num_chunks > 0) {
+            overhang = (num_chunks * var->chunksizes[d]) - var->dim[d]->len;
+            var->chunksizes[d] -= overhang / num_chunks;
+        }
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Give a var a secret HDF5 name. This is needed when a var
+ * is defined with the same name as a dim, but it is not a coord var
+ * of that dim. In that case, the var uses a secret name inside the
+ * HDF5 file.
+ *
+ * @param var Pointer to var info.
+ * @param name Name to use for base of secret name.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EMAXNAME Name too long to fit secret prefix.
+ * @returns ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+static int
+give_var_secret_name(NC_VAR_INFO_T *var, const char *name)
+{
+    /* Set a different hdf5 name for this variable to avoid name
+     * clash. */
+    if (strlen(name) + strlen(NON_COORD_PREPEND) > NC_MAX_NAME)
+        return NC_EMAXNAME;
+    if (!(var->hdf5_name = malloc((strlen(NON_COORD_PREPEND) +
+                                   strlen(name) + 1) * sizeof(char))))
+        return NC_ENOMEM;
+
+    sprintf(var->hdf5_name, "%s%s", NON_COORD_PREPEND, name);
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This is called when a new netCDF-4 variable is defined
+ * with nc_def_var().
+ *
+ * @param ncid File ID.
+ * @param name Name.
+ * @param xtype Type.
+ * @param ndims Number of dims. HDF5 has maximim of 32.
+ * @param dimidsp Array of dim IDs.
+ * @param varidp Gets the var ID.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
+ * netcdf-4 file.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EPERM File is read only.
+ * @returns ::NC_EMAXDIMS Classic model file exceeds ::NC_MAX_VAR_DIMS.
+ * @returns ::NC_ESTRICTNC3 Attempting to create netCDF-4 type var in
+ * classic model file
+ * @returns ::NC_EBADNAME Bad name.
+ * @returns ::NC_EBADTYPE Bad type.
+ * @returns ::NC_ENOMEM Out of memory.
+ * @returns ::NC_EHDFERR Error returned by HDF5 layer.
+ * @returns ::NC_EINVAL Invalid input
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_def_var(int ncid, const char *name, nc_type xtype, int ndims,
+            const int *dimidsp, int *varidp)
+{
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var;
+    NC_DIM_INFO_T *dim;
+    NC_FILE_INFO_T *h5;
+    NC_TYPE_INFO_T *type = NULL;
+    NC_HDF5_TYPE_INFO_T *hdf5_type;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    char norm_name[NC_MAX_NAME + 1];
+    int d;
+    int retval;
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        BAIL(retval);
+    assert(grp && grp->format_grp_info && h5);
+
+    /* Get HDF5-specific group info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    /* HDF5 allows maximum of 32 dimensions. */
+    if (ndims > H5S_MAX_RANK)
+        BAIL(NC_EMAXDIMS);
+
+    /* If it's not in define mode, strict nc3 files error out,
+     * otherwise switch to define mode. This will also check that the
+     * file is writable. */
+    if (!(h5->flags & NC_INDEF))
+    {
+        if (h5->cmode & NC_CLASSIC_MODEL)
+            BAIL(NC_ENOTINDEFINE);
+        if ((retval = NC4_redef(ncid)))
+            BAIL(retval);
+    }
+    assert(!h5->no_write);
+
+    /* Check and normalize the name. */
+    if ((retval = nc4_check_name(name, norm_name)))
+        BAIL(retval);
+
+    /* Not a Type is, well, not a type.*/
+    if (xtype == NC_NAT)
+        BAIL(NC_EBADTYPE);
+
+    /* For classic files, only classic types are allowed. */
+    if (h5->cmode & NC_CLASSIC_MODEL && xtype > NC_DOUBLE)
+        BAIL(NC_ESTRICTNC3);
+
+    /* For classic files limit number of dims. */
+    if (h5->cmode & NC_CLASSIC_MODEL && ndims > NC_MAX_VAR_DIMS)
+        BAIL(NC_EMAXDIMS);
+
+    /* cast needed for braindead systems with signed size_t */
+    if ((unsigned long) ndims > X_INT_MAX) /* Backward compat */
+        BAIL(NC_EINVAL);
+
+    /* Check that this name is not in use as a var, grp, or type. */
+    if ((retval = nc4_check_dup_name(grp, norm_name)))
+        BAIL(retval);
+
+    /* For non-scalar vars, dim IDs must be provided. */
+    if (ndims && !dimidsp)
+        BAIL(NC_EINVAL);
+
+    /* Check all the dimids to make sure they exist. */
+    for (d = 0; d < ndims; d++)
+        if ((retval = nc4_find_dim(grp, dimidsp[d], &dim, NULL)))
+            BAIL(retval);
+
+    /* These degrubbing messages sure are handy! */
+    LOG((2, "%s: name %s type %d ndims %d", __func__, norm_name, xtype, ndims));
+#ifdef LOGGING
+    {
+        int dd;
+        for (dd = 0; dd < ndims; dd++)
+            LOG((4, "dimid[%d] %d", dd, dimidsp[dd]));
+    }
+#endif
+
+    /* If this is a user-defined type, there is a type struct with
+     * all the type information. For atomic types, fake up a type
+     * struct. */
+    if (xtype <= NC_STRING)
+    {
+        size_t len;
+
+        /* Get type length. */
+        if ((retval = nc4_get_typelen_mem(h5, xtype, &len)))
+            BAIL(retval);
+
+        /* Create new NC_TYPE_INFO_T struct for this atomic type. */
+        if ((retval = nc4_type_new(len, nc4_atomic_name[xtype], xtype, &type)))
+            BAIL(retval);
+        type->endianness = NC_ENDIAN_NATIVE;
+        type->size = len;
+
+        /* Allocate storage for HDF5-specific type info. */
+        if (!(hdf5_type = calloc(1, sizeof(NC_HDF5_TYPE_INFO_T))))
+            BAIL(NC_ENOMEM);
+        type->format_type_info = hdf5_type;
+
+        /* Get HDF5 typeids. */
+        if ((retval = nc4_get_hdf_typeid(h5, xtype, &hdf5_type->hdf_typeid,
+                                         type->endianness)))
+            BAIL(retval);
+
+        /* Get the native HDF5 typeid. */
+        if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid,
+                                                               H5T_DIR_DEFAULT)) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* Set the "class" of the type */
+        if (xtype == NC_CHAR)
+            type->nc_type_class = NC_CHAR;
+        else
+        {
+            H5T_class_t class;
+
+            if ((class = H5Tget_class(hdf5_type->hdf_typeid)) < 0)
+                BAIL(NC_EHDFERR);
+            switch(class)
+            {
+            case H5T_STRING:
+                type->nc_type_class = NC_STRING;
+                break;
+
+            case H5T_INTEGER:
+                type->nc_type_class = NC_INT;
+                break;
+
+            case H5T_FLOAT:
+                type->nc_type_class = NC_FLOAT;
+                break;
+
+            default:
+                BAIL(NC_EBADTYPID);
+            }
+        }
+    }
+    else
+    {
+        /* If this is a user defined type, find it. */
+        if (nc4_find_type(grp->nc4_info, xtype, &type))
+            BAIL(NC_EBADTYPE);
+    }
+
+    /* Create a new var and fill in some HDF5 cache setting values. */
+    if ((retval = nc4_var_list_add(grp, norm_name, ndims, &var)))
+        BAIL(retval);
+
+    /* Add storage for HDF5-specific var info. */
+    if (!(var->format_var_info = calloc(1, sizeof(NC_HDF5_VAR_INFO_T))))
+        BAIL(NC_ENOMEM);
+
+    /* Set these state flags for the var. */
+    var->is_new_var = NC_TRUE;
+    var->meta_read = NC_TRUE;
+    var->atts_read = NC_TRUE;
+
+    /* Point to the type, and increment its ref. count */
+    var->type_info = type;
+    var->type_info->rc++;
+    type = NULL;
+
+    /* Set variables no_fill to match the database default unless the
+     * variable type is variable length (NC_STRING or NC_VLEN) or is
+     * user-defined type. */
+    if (var->type_info->nc_type_class < NC_STRING)
+        var->no_fill = h5->fill_mode;
+
+    /* Assign dimensions to the variable. At the same time, check to
+     * see if this is a coordinate variable. If so, it will have the
+     * same name as one of its dimensions. If it is a coordinate var,
+     * is it a coordinate var in the same group as the dim? Also, check
+     * whether we should use contiguous or chunked storage. */
+    var->contiguous = NC_TRUE;
+    for (d = 0; d < ndims; d++)
+    {
+        NC_GRP_INFO_T *dim_grp;
+        NC_HDF5_DIM_INFO_T *hdf5_dim;
+
+        /* Look up each dimension */
+        if ((retval = nc4_find_dim(grp, dimidsp[d], &dim, &dim_grp)))
+            BAIL(retval);
+        assert(dim && dim->format_dim_info);
+        hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+        /* Check for dim index 0 having the same name, in the same group */
+        if (d == 0 && dim_grp == grp && strcmp(dim->hdr.name, norm_name) == 0)
+        {
+            var->dimscale = NC_TRUE;
+            dim->coord_var = var;
+
+            /* Use variable's dataset ID for the dimscale ID. So delete
+             * the HDF5 DIM_WITHOUT_VARIABLE dataset that was created for
+             * this dim. */
+            if (hdf5_dim->hdf_dimscaleid)
+            {
+                /* Detach dimscale from any variables using it */
+                if ((retval = rec_detach_scales(grp, dimidsp[d],
+                                                hdf5_dim->hdf_dimscaleid)) < 0)
+                    BAIL(retval);
+
+                /* Close the HDF5 DIM_WITHOUT_VARIABLE dataset. */
+                if (H5Dclose(hdf5_dim->hdf_dimscaleid) < 0)
+                    BAIL(NC_EHDFERR);
+                hdf5_dim->hdf_dimscaleid = 0;
+
+                /* Now delete the DIM_WITHOUT_VARIABLE dataset (it will be
+                 * recreated later, if necessary). */
+                if (H5Gunlink(hdf5_grp->hdf_grpid, dim->hdr.name) < 0)
+                    BAIL(NC_EDIMMETA);
+            }
+        }
+
+        /* Check for unlimited dimension and turn off contiguous storage. */
+        if (dim->unlimited)
+            var->contiguous = NC_FALSE;
+
+        /* Track dimensions for variable */
+        var->dimids[d] = dimidsp[d];
+        var->dim[d] = dim;
+    }
+
+    /* Determine default chunksizes for this variable. (Even for
+     * variables which may be contiguous.) */
+    LOG((4, "allocating array of %d size_t to hold chunksizes for var %s",
+         var->ndims, var->hdr.name));
+    if (var->ndims)
+        if (!(var->chunksizes = calloc(var->ndims, sizeof(size_t))))
+            BAIL(NC_ENOMEM);
+
+    if ((retval = nc4_find_default_chunksizes2(grp, var)))
+        BAIL(retval);
+
+    /* Is this a variable with a chunksize greater than the current
+     * cache size? */
+    if ((retval = nc4_adjust_var_cache(grp, var)))
+        BAIL(retval);
+
+    /* If the user names this variable the same as a dimension, but
+     * doesn't use that dimension first in its list of dimension ids,
+     * is not a coordinate variable. I need to change its HDF5 name,
+     * because the dimension will cause a HDF5 dataset to be created,
+     * and this var has the same name. */
+    dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name);
+    if (dim && (!var->ndims || dimidsp[0] != dim->hdr.id))
+        if ((retval = give_var_secret_name(var, var->hdr.name)))
+            BAIL(retval);
+
+    /* If this is a coordinate var, it is marked as a HDF5 dimension
+     * scale. (We found dim above.) Otherwise, allocate space to
+     * remember whether dimension scales have been attached to each
+     * dimension. */
+    if (!var->dimscale && ndims)
+        if (!(var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t))))
+            BAIL(NC_ENOMEM);
+
+    /* Return the varid. */
+    if (varidp)
+        *varidp = var->hdr.id;
+    LOG((4, "new varid %d", var->hdr.id));
+
+exit:
+    if (type)
+        if ((retval = nc4_type_free(type)))
+            BAIL2(retval);
+
+    return retval;
+}
+
+/**
+ * @internal This functions sets extra stuff about a netCDF-4 variable which
+ * must be set before the enddef but after the def_var.
+ *
+ * @note All pointer parameters may be NULL, in which case they are ignored.
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param shuffle Pointer to shuffle setting.
+ * @param deflate Pointer to deflate setting.
+ * @param deflate_level Pointer to deflate level.
+ * @param fletcher32 Pointer to fletcher32 setting.
+ * @param contiguous Pointer to contiguous setting.
+ * @param chunksizes Array of chunksizes.
+ * @param no_fill Pointer to no_fill setting.
+ * @param fill_value Pointer to fill value.
+ * @param endianness Pointer to endianness setting.
+ *
+ * @returns ::NC_NOERR for success
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
+ * netcdf-4 file.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EPERM File is read only.
+ * @returns ::NC_EINVAL Invalid input
+ * @returns ::NC_EBADCHUNK Bad chunksize.
+ * @author Ed Hartnett
+ */
+static int
+nc_def_var_extra(int ncid, int varid, int *shuffle, int *deflate,
+                 int *deflate_level, int *fletcher32, int *contiguous,
+                 const size_t *chunksizes, int *no_fill,
+                 const void *fill_value, int *endianness)
+{
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var;
+    int d;
+    int retval;
+
+    /* All or none of these will be provided. */
+    assert((deflate && deflate_level && shuffle) ||
+           (!deflate && !deflate_level && !shuffle));
+
+    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5)))
+        return retval;
+    assert(grp && h5);
+
+    /* Trying to write to a read-only file? No way, Jose! */
+    if (h5->no_write)
+        return NC_EPERM;
+
+    /* Find the var. */
+    if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
+        return NC_ENOTVAR;
+    assert(var && var->hdr.id == varid);
+
+    /* Can't turn on parallel and deflate/fletcher32/szip/shuffle. */
+    if (h5->parallel == NC_TRUE)
+        if (deflate || fletcher32 || shuffle)
+            return NC_EINVAL;
+
+    /* If the HDF5 dataset has already been created, then it is too
+     * late to set all the extra stuff. */
+    if (var->created)
+        return NC_ELATEDEF;
+
+    /* Check compression options. */
+    if (deflate && !deflate_level)
+        return NC_EINVAL;
+
+    /* Valid deflate level? */
+    if (deflate)
+    {
+        if (*deflate)
+            if (*deflate_level < NC_MIN_DEFLATE_LEVEL ||
+                *deflate_level > NC_MAX_DEFLATE_LEVEL)
+                return NC_EINVAL;
+
+        /* For scalars, just ignore attempt to deflate. */
+        if (!var->ndims)
+            return NC_NOERR;
+
+        /* Well, if we couldn't find any errors, I guess we have to take
+         * the users settings. Darn! */
+        var->contiguous = NC_FALSE;
+        var->deflate = *deflate;
+        if (*deflate)
+            var->deflate_level = *deflate_level;
+        LOG((3, "%s: *deflate_level %d", __func__, *deflate_level));
+    }
+
+    /* Shuffle filter? */
+    if (shuffle)
+    {
+        var->shuffle = *shuffle;
+        var->contiguous = NC_FALSE;
+    }
+
+    /* Fletcher32 checksum error protection? */
+    if (fletcher32)
+    {
+        var->fletcher32 = *fletcher32;
+        var->contiguous = NC_FALSE;
+    }
+
+    /* Does the user want a contiguous dataset? Not so fast! Make sure
+     * that there are no unlimited dimensions, and no filters in use
+     * for this data. */
+    if (contiguous && *contiguous)
+    {
+        if (var->deflate || var->fletcher32 || var->shuffle)
+            return NC_EINVAL;
+
+        for (d = 0; d < var->ndims; d++)
+            if (var->dim[d]->unlimited)
+                return NC_EINVAL;
+        var->contiguous = NC_TRUE;
+    }
+
+    /* Chunksizes anyone? */
+    if (contiguous && *contiguous == NC_CHUNKED)
+    {
+        var->contiguous = NC_FALSE;
+
+        /* If the user provided chunksizes, check that they are not too
+         * big, and that their total size of chunk is less than 4 GB. */
+        if (chunksizes)
+        {
+
+            if ((retval = check_chunksizes(grp, var, chunksizes)))
+                return retval;
+
+            /* Ensure chunksize is smaller than dimension size */
+            for (d = 0; d < var->ndims; d++)
+                if(!var->dim[d]->unlimited && var->dim[d]->len > 0 && chunksizes[d] > var->dim[d]->len)
+                    return NC_EBADCHUNK;
+
+            /* Set the chunksizes for this variable. */
+            for (d = 0; d < var->ndims; d++)
+                var->chunksizes[d] = chunksizes[d];
+        }
+    }
+
+    /* Is this a variable with a chunksize greater than the current
+     * cache size? */
+    if (!var->contiguous && (deflate || contiguous))
+    {
+        /* Determine default chunksizes for this variable (do nothing
+         * for scalar vars). */
+        if (var->chunksizes && !var->chunksizes[0])
+            if ((retval = nc4_find_default_chunksizes2(grp, var)))
+                return retval;
+
+        /* Adjust the cache. */
+        if ((retval = nc4_adjust_var_cache(grp, var)))
+            return retval;
+    }
+
+    /* Are we setting a fill modes? */
+    if (no_fill)
+    {
+        if (*no_fill)
+        {
+            /* NC_STRING types may not turn off fill mode. It's disallowed
+             * by HDF5 and will cause a HDF5 error later. */
+            if (*no_fill)
+                if (var->type_info->hdr.id == NC_STRING)
+                    return NC_EINVAL;
+
+            /* Set the no-fill mode. */
+            var->no_fill = NC_TRUE;
+        }
+        else
+            var->no_fill = NC_FALSE;
+    }
+
+    /* Are we setting a fill value? */
+    if (fill_value && !var->no_fill)
+    {
+        /* Copy the fill_value. */
+        LOG((4, "Copying fill value into metadata for variable %s",
+             var->hdr.name));
+
+        /* If there's a _FillValue attribute, delete it. */
+        retval = NC4_HDF5_del_att(ncid, varid, _FillValue);
+        if (retval && retval != NC_ENOTATT)
+            return retval;
+
+        /* Create a _FillValue attribute. */
+        if ((retval = nc_put_att(ncid, varid, _FillValue, var->type_info->hdr.id,
+                                 1, fill_value)))
+            return retval;
+    }
+
+    /* Is the user setting the endianness? */
+    if (endianness)
+        var->type_info->endianness = *endianness;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Set compression settings on a variable. This is called by
+ * nc_def_var_deflate().
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param shuffle True to turn on the shuffle filter.
+ * @param deflate True to turn on deflation.
+ * @param deflate_level A number between 0 (no compression) and 9
+ * (maximum compression).
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EINVAL Invalid input
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_def_var_deflate(int ncid, int varid, int shuffle, int deflate,
+                    int deflate_level)
+{
+    return nc_def_var_extra(ncid, varid, &shuffle, &deflate,
+                            &deflate_level, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+/**
+ * @internal Set checksum on a variable. This is called by
+ * nc_def_var_fletcher32().
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param fletcher32 Pointer to fletcher32 setting.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EINVAL Invalid input
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_def_var_fletcher32(int ncid, int varid, int fletcher32)
+{
+    return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, &fletcher32,
+                            NULL, NULL, NULL, NULL, NULL);
+}
+
+/**
+ * @internal Define chunking stuff for a var. This is called by
+ * nc_def_var_chunking(). Chunking is required in any dataset with one
+ * or more unlimited dimensions in HDF5, or any dataset using a
+ * filter.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param contiguous Pointer to contiguous setting.
+ * @param chunksizesp Array of chunksizes.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EINVAL Invalid input
+ * @returns ::NC_EBADCHUNK Bad chunksize.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_def_var_chunking(int ncid, int varid, int contiguous, const size_t *chunksizesp)
+{
+    return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL,
+                            &contiguous, chunksizesp, NULL, NULL, NULL);
+}
+
+/**
+ * @internal Define chunking stuff for a var. This is called by
+ * the fortran API.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param contiguous Pointer to contiguous setting.
+ * @param chunksizesp Array of chunksizes.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EINVAL Invalid input
+ * @returns ::NC_EBADCHUNK Bad chunksize.
+ * @author Ed Hartnett
+ */
+int
+nc_def_var_chunking_ints(int ncid, int varid, int contiguous, int *chunksizesp)
+{
+    NC_VAR_INFO_T *var;
+    size_t *cs;
+    int i, retval;
+
+    /* Get pointer to the var. */
+    if ((retval = nc4_hdf5_find_grp_h5_var(ncid, varid, NULL, NULL, &var)))
+        return retval;
+    assert(var);
+
+    /* Allocate space for the size_t copy of the chunksizes array. */
+    if (var->ndims)
+        if (!(cs = malloc(var->ndims * sizeof(size_t))))
+            return NC_ENOMEM;
+
+    /* Copy to size_t array. */
+    for (i = 0; i < var->ndims; i++)
+        cs[i] = chunksizesp[i];
+
+    retval = nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL,
+                              &contiguous, cs, NULL, NULL, NULL);
+
+    if (var->ndims)
+        free(cs);
+    return retval;
+}
+
+/**
+ * @internal This functions sets fill value and no_fill mode for a
+ * netCDF-4 variable. It is called by nc_def_var_fill().
+ *
+ * @note All pointer parameters may be NULL, in which case they are ignored.
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param no_fill No_fill setting.
+ * @param fill_value Pointer to fill value.
+ *
+ * @returns ::NC_NOERR for success
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
+ * netcdf-4 file.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EPERM File is read only.
+ * @returns ::NC_EINVAL Invalid input
+ * @author Ed Hartnett
+ */
+int
+NC4_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value)
+{
+    return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL,
+                            NULL, &no_fill, fill_value, NULL);
+}
+
+/**
+ * @internal This functions sets endianness for a netCDF-4
+ * variable. Called by nc_def_var_endian().
+ *
+ * @note All pointer parameters may be NULL, in which case they are ignored.
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param endianness Endianness setting.
+ *
+ * @returns ::NC_NOERR for success
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
+ * netcdf-4 file.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_ENOTINDEFINE Not in define mode.
+ * @returns ::NC_EPERM File is read only.
+ * @returns ::NC_EINVAL Invalid input
+ * @author Ed Hartnett
+ */
+int
+NC4_def_var_endian(int ncid, int varid, int endianness)
+{
+    return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL,
+                            NULL, NULL, NULL, &endianness);
+}
+
+/**
+ * @internal Define filter settings. Called by nc_def_var_filter().
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param id Filter ID
+ * @param nparams Number of parameters for filter.
+ * @param parms Filter parameters.
+ *
+ * @returns ::NC_NOERR for success
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
+ * not netCDF-4/HDF5.
+ * @returns ::NC_ELATEDEF Too late to change settings for this variable.
+ * @returns ::NC_EFILTER Filter error.
+ * @returns ::NC_EINVAL Invalid input
+ * @author Dennis Heimbigner
+ */
+int
+NC4_def_var_filter(int ncid, int varid, unsigned int id, size_t nparams,
+                   const unsigned int* parms)
+{
+    int retval = NC_NOERR;
+    NC *nc;
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var;
+
+    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
+        return retval;
+
+    assert(nc && grp && h5);
+
+    /* Find the var. */
+    var = (NC_VAR_INFO_T*)ncindexith(grp->vars,varid);
+    if(!var)
+        return NC_ENOTVAR;
+    assert(var->hdr.id == varid);
+
+    /* If the HDF5 dataset has already been created, then it is too
+     * late to set all the extra stuff. */
+    if (var->created)
+        return NC_ELATEDEF;
+
+#ifdef HAVE_H5Z_SZIP
+    if(id == H5Z_FILTER_SZIP) {
+        if(nparams != 2)
+            return NC_EFILTER; /* incorrect no. of parameters */
+    }
+#else /*!HAVE_H5Z_SZIP*/
+    if(id == H5Z_FILTER_SZIP)
+        return NC_EFILTER; /* Not allowed */
+#endif
+
+#if 0
+    {
+        unsigned int fcfg = 0;
+        herr_t herr = H5Zget_filter_info(id,&fcfg);
+        if(herr < 0)
+            return NC_EFILTER;
+        if((H5Z_FILTER_CONFIG_ENCODE_ENABLED & fcfg) == 0
+           || (H5Z_FILTER_CONFIG_DECODE_ENABLED & fcfg) == 0)
+            return NC_EFILTER;
+    }
+#endif /*0*/
+
+    var->filterid = id;
+    var->nparams = nparams;
+    var->params = NULL;
+    if(parms != NULL) {
+        var->params = (unsigned int*)calloc(nparams,sizeof(unsigned int));
+        if(var->params == NULL) return NC_ENOMEM;
+        memcpy(var->params,parms,sizeof(unsigned int)*var->nparams);
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal Rename a var to "bubba," for example. This is called by
+ * nc_rename_var() for netCDF-4 files. This results in complexities
+ * when coordinate variables are involved.
+
+ * Whenever a var has the same name as a dim, and also uses that dim
+ * as its first dimension, then that var is aid to be a coordinate
+ * variable for that dimensions. Coordinate variables are represented
+ * in the HDF5 by making them dimscales. Dimensions without coordinate
+ * vars are represented by datasets which are dimscales, but have a
+ * special attribute marking them as dimscales without associated
+ * coordinate variables.
+ *
+ * When a var is renamed, we must detect whether it has become a
+ * coordinate var (by being renamed to the same name as a dim that is
+ * also its first dimension), or whether it is no longer a coordinate
+ * var. These cause flags to be set in NC_VAR_INFO_T which are used at
+ * enddef time to make changes in the HDF5 file.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID
+ * @param name New name of the variable.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_EBADNAME Bad name.
+ * @returns ::NC_EMAXNAME Name is too long.
+ * @returns ::NC_ENAMEINUSE Name in use.
+ * @returns ::NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+int
+NC4_rename_var(int ncid, int varid, const char *name)
+{
+    NC_GRP_INFO_T *grp;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var;
+    NC_DIM_INFO_T *other_dim;
+    int use_secret_name = 0;
+    int retval = NC_NOERR;
+
+    if (!name)
+        return NC_EINVAL;
+
+    LOG((2, "%s: ncid 0x%x varid %d name %s", __func__, ncid, varid, name));
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
+        return retval;
+    assert(h5 && grp && grp->format_grp_info);
+
+    /* Get HDF5-specific group info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    /* Is the new name too long? */
+    if (strlen(name) > NC_MAX_NAME)
+        return NC_EMAXNAME;
+
+    /* Trying to write to a read-only file? No way, Jose! */
+    if (h5->no_write)
+        return NC_EPERM;
+
+    /* Check name validity, if strict nc3 rules are in effect for this
+     * file. */
+    if ((retval = NC_check_name(name)))
+        return retval;
+
+    /* Get the variable wrt varid */
+    if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
+        return NC_ENOTVAR;
+
+    /* Check if new name is in use; note that renaming to same name is
+       still an error according to the nc_test/test_write.c
+       code. Why?*/
+    if (ncindexlookup(grp->vars, name))
+        return NC_ENAMEINUSE;
+
+    /* If we're not in define mode, new name must be of equal or
+       less size, if strict nc3 rules are in effect for this . */
+    if (!(h5->flags & NC_INDEF) && strlen(name) > strlen(var->hdr.name) &&
+        (h5->cmode & NC_CLASSIC_MODEL))
+        return NC_ENOTINDEFINE;
+
+    /* Is there another dim with this name, for which this var will not
+     * be a coord var? If so, we have to create a dim without a
+     * variable for the old name. */
+    if ((other_dim = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, name)) &&
+        strcmp(name, var->dim[0]->hdr.name))
+    {
+        /* Create a dim without var dataset for old dim. */
+        if ((retval = nc4_create_dim_wo_var(other_dim)))
+            return retval;
+
+        /* Give this var a secret HDF5 name so it can co-exist in file
+         * with dim wp var dataset. Base the secret name on the new var
+         * name. */
+        if ((retval = give_var_secret_name(var, name)))
+            return retval;
+        use_secret_name++;
+    }
+
+    /* Change the HDF5 file, if this var has already been created
+       there. */
+    if (var->created)
+    {
+        int v;
+        char *hdf5_name; /* Dataset will be renamed to this. */
+        hdf5_name = use_secret_name ? var->hdf5_name: (char *)name;
+
+        /* Do we need to read var metadata? */
+        if (!var->meta_read)
+            if ((retval = nc4_get_var_meta(var)))
+                return retval;
+
+        if (var->ndims)
+        {
+            NC_HDF5_DIM_INFO_T *hdf5_d0;
+            hdf5_d0 = (NC_HDF5_DIM_INFO_T *)var->dim[0]->format_dim_info;
+
+            /* Is there an existing dimscale-only dataset of this name? If
+             * so, it must be deleted. */
+            if (hdf5_d0->hdf_dimscaleid)
+            {
+                if ((retval = delete_dimscale_dataset(grp, var->dim[0]->hdr.id,
+                                                      var->dim[0])))
+                    return retval;
+            }
+        }
+
+        LOG((3, "Moving dataset %s to %s", var->hdr.name, name));
+        if (H5Lmove(hdf5_grp->hdf_grpid, var->hdr.name, hdf5_grp->hdf_grpid,
+                    hdf5_name, H5P_DEFAULT, H5P_DEFAULT) < 0)
+            return NC_EHDFERR;
+
+        /* Rename all the vars in this file with a varid greater than
+         * this var. Varids are assigned based on dataset creation time,
+         * and we have just changed that for this var. We must do the
+         * same for all vars with a > varid, so that the creation order
+         * will continue to be correct. */
+        for (v = var->hdr.id + 1; v < ncindexsize(grp->vars); v++)
+        {
+            NC_VAR_INFO_T *my_var;
+            my_var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v);
+            assert(my_var);
+
+            LOG((3, "mandatory rename of %s to same name", my_var->hdr.name));
+
+            /* Rename to temp name. */
+            if (H5Lmove(hdf5_grp->hdf_grpid, my_var->hdr.name, hdf5_grp->hdf_grpid,
+                        NC_TEMP_NAME, H5P_DEFAULT, H5P_DEFAULT) < 0)
+                return NC_EHDFERR;
+
+            /* Rename to real name. */
+            if (H5Lmove(hdf5_grp->hdf_grpid, NC_TEMP_NAME, hdf5_grp->hdf_grpid,
+                        my_var->hdr.name, H5P_DEFAULT, H5P_DEFAULT) < 0)
+                return NC_EHDFERR;
+        }
+    }
+
+    /* Now change the name in our metadata. */
+    free(var->hdr.name);
+    if (!(var->hdr.name = strdup(name)))
+        return NC_ENOMEM;
+    LOG((3, "var is now %s", var->hdr.name));
+
+    /* Fix hash key and rebuild index. */
+    var->hdr.hashkey = NC_hashmapkey(var->hdr.name, strlen(var->hdr.name));
+    if (!ncindexrebuild(grp->vars))
+        return NC_EINTERNAL;
+
+    /* Check if this was a coordinate variable previously, but names
+     * are different now */
+    if (var->dimscale && strcmp(var->hdr.name, var->dim[0]->hdr.name))
+    {
+        /* Break up the coordinate variable */
+        if ((retval = nc4_break_coord_var(grp, var, var->dim[0])))
+            return retval;
+    }
+
+    /* Check if this should become a coordinate variable. */
+    if (!var->dimscale)
+    {
+        /* Only variables with >0 dimensions can become coordinate
+         * variables. */
+        if (var->ndims)
+        {
+            NC_GRP_INFO_T *dim_grp;
+            NC_DIM_INFO_T *dim;
+
+            /* Check to see if this is became a coordinate variable.  If
+             * so, it will have the same name as dimension index 0. If it
+             * is a coordinate var, is it a coordinate var in the same
+             * group as the dim? */
+            if ((retval = nc4_find_dim(grp, var->dimids[0], &dim, &dim_grp)))
+                return retval;
+            if (!strcmp(dim->hdr.name, name) && dim_grp == grp)
+            {
+                /* Reform the coordinate variable. */
+                if ((retval = nc4_reform_coord_var(grp, var, dim)))
+                    return retval;
+                var->became_coord_var = NC_TRUE;
+            }
+        }
+    }
+
+    return retval;
+}
+
+/**
+ * @internal Write an array of data to a variable. This is called by
+ * nc_put_vara() and other nc_put_vara_* functions, for netCDF-4
+ * files.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param startp Array of start indices.
+ * @param countp Array of counts.
+ * @param op pointer that gets the data.
+ * @param memtype The type of these data in memory.
+ *
+ * @returns ::NC_NOERR for success
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_put_vara(int ncid, int varid, const size_t *startp,
+             const size_t *countp, const void *op, int memtype)
+{
+    return NC4_put_vars(ncid, varid, startp, countp, NULL, op, memtype);
+}
+
+/**
+ * @internal Read an array of values. This is called by nc_get_vara()
+ * for netCDF-4 files, as well as all the other nc_get_vara_*
+ * functions.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param startp Array of start indices.
+ * @param countp Array of counts.
+ * @param ip pointer that gets the data.
+ * @param memtype The type of these data after it is read into memory.
+
+ * @returns ::NC_NOERR for success
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_get_vara(int ncid, int varid, const size_t *startp,
+             const size_t *countp, void *ip, int memtype)
+{
+    return NC4_get_vars(ncid, varid, startp, countp, NULL, ip, memtype);
+}
+
+/**
+ * @internal Do some common check for NC4_put_vars and
+ * NC4_get_vars. These checks have to be done when both reading and
+ * writing data.
+ *
+ * @param mem_nc_type Pointer to type of data in memory.
+ * @param var Pointer to var info struct.
+ * @param h5 Pointer to HDF5 file info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+static int
+check_for_vara(nc_type *mem_nc_type, NC_VAR_INFO_T *var, NC_FILE_INFO_T *h5)
+{
+    int retval;
+
+    /* If mem_nc_type is NC_NAT, it means we want to use the file type
+     * as the mem type as well. */
+    assert(mem_nc_type);
+    if (*mem_nc_type == NC_NAT)
+        *mem_nc_type = var->type_info->hdr.id;
+    assert(*mem_nc_type);
+
+    /* No NC_CHAR conversions, you pervert! */
+    if (var->type_info->hdr.id != *mem_nc_type &&
+        (var->type_info->hdr.id == NC_CHAR || *mem_nc_type == NC_CHAR))
+        return NC_ECHAR;
+
+    /* If we're in define mode, we can't read or write data. */
+    if (h5->flags & NC_INDEF)
+    {
+        if (h5->cmode & NC_CLASSIC_MODEL)
+            return NC_EINDEFINE;
+        if ((retval = nc4_enddef_netcdf4_file(h5)))
+            return retval;
+    }
+
+    return NC_NOERR;
+}
+
+#ifdef LOGGING
+/**
+ * @intarnal Print some debug info about dimensions to the log.
+ */
+static void
+log_dim_info(NC_VAR_INFO_T *var, hsize_t *fdims, hsize_t *fmaxdims,
+             hsize_t *start, hsize_t *count)
+{
+    int d2;
+
+    /* Print some debugging info... */
+    LOG((4, "%s: var name %s ndims %d", __func__, var->hdr.name, var->ndims));
+    LOG((4, "File space, and requested:"));
+    for (d2 = 0; d2 < var->ndims; d2++)
+    {
+        LOG((4, "fdims[%d]=%Ld fmaxdims[%d]=%Ld", d2, fdims[d2], d2,
+             fmaxdims[d2]));
+        LOG((4, "start[%d]=%Ld  count[%d]=%Ld", d2, start[d2], d2, count[d2]));
+    }
+}
+#endif /* LOGGING */
+
+#ifdef USE_PARALLEL4
+/**
+ * @internal Set the parallel access for a var (collective
+ * vs. independent).
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param var Pointer to var info struct.
+ * @param xfer_plistid H5FD_MPIO_COLLECTIVE or H5FD_MPIO_INDEPENDENT.
+ *
+ * @returns NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+static int
+set_par_access(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, hid_t xfer_plistid)
+{
+    /* If netcdf is built with parallel I/O, then parallel access can
+     * be used, and, if this file was opened or created for parallel
+     * access, we need to set the transfer mode. */
+    if (h5->parallel)
+    {
+        H5FD_mpio_xfer_t hdf5_xfer_mode;
+
+        /* Decide on collective or independent. */
+        hdf5_xfer_mode = (var->parallel_access != NC_INDEPENDENT) ?
+            H5FD_MPIO_COLLECTIVE : H5FD_MPIO_INDEPENDENT;
+
+        /* Set the mode in the transfer property list. */
+        if (H5Pset_dxpl_mpio(xfer_plistid, hdf5_xfer_mode) < 0)
+            return NC_EPARINIT;
+
+        LOG((4, "%s: %d H5FD_MPIO_COLLECTIVE: %d H5FD_MPIO_INDEPENDENT: %d",
+             __func__, (int)hdf5_xfer_mode, H5FD_MPIO_COLLECTIVE,
+             H5FD_MPIO_INDEPENDENT));
+    }
+    return NC_NOERR;
+}
+#endif /* USE_PARALLEL4 */
+
+/**
+ * @internal Write a strided array of data to a variable. This is
+ * called by nc_put_vars() and other nc_put_vars_* functions, for
+ * netCDF-4 files. Also the nc_put_vara() calls end up calling this
+ * with a NULL stride parameter.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param startp Array of start indices. Must always be provided by
+ * caller for non-scalar vars.
+ * @param countp Array of counts. Will default to counts of full
+ * dimension size if NULL.
+ * @param stridep Array of strides. Will default to strides of 1 if
+ * NULL.
+ * @param data The data to be written.
+ * @param mem_nc_type The type of the data in memory.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Var not found.
+ * @returns ::NC_EHDFERR HDF5 function returned error.
+ * @returns ::NC_EINVALCOORDS Incorrect start.
+ * @returns ::NC_EEDGE Incorrect start/count.
+ * @returns ::NC_ENOMEM Out of memory.
+ * @returns ::NC_EMPI MPI library error (parallel only)
+ * @returns ::NC_ECANTEXTEND Can't extend dimension for write.
+ * @returns ::NC_ERANGE Data conversion error.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_put_vars(int ncid, int varid, const size_t *startp, const size_t *countp,
+             const ptrdiff_t *stridep, const void *data, nc_type mem_nc_type)
+{
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var;
+    NC_DIM_INFO_T *dim;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    hid_t file_spaceid = 0, mem_spaceid = 0, xfer_plistid = 0;
+    long long unsigned xtend_size[NC_MAX_VAR_DIMS];
+    hsize_t fdims[NC_MAX_VAR_DIMS], fmaxdims[NC_MAX_VAR_DIMS];
+    hsize_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
+    hsize_t stride[NC_MAX_VAR_DIMS];
+    int need_to_extend = 0;
+#ifdef USE_PARALLEL4
+    int extend_possible = 0;
+#endif
+    int retval, range_error = 0, i, d2;
+    void *bufr = NULL;
+    int need_to_convert = 0;
+    int zero_count = 0; /* true if a count is zero */
+    size_t len = 1;
+
+    /* Find info for this file, group, and var. */
+    if ((retval = nc4_hdf5_find_grp_h5_var(ncid, varid, &h5, &grp, &var)))
+        return retval;
+    assert(h5 && grp && var && var->hdr.id == varid && var->format_var_info);
+
+    /* Get the HDF5-specific var info. */
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Cannot convert to user-defined types. */
+    if (mem_nc_type >= NC_FIRSTUSERTYPEID)
+        mem_nc_type = NC_NAT;
+
+    LOG((3, "%s: var->hdr.name %s mem_nc_type %d", __func__,
+         var->hdr.name, mem_nc_type));
+
+    /* Check some stuff about the type and the file. If the file must
+     * be switched from define mode, it happens here. */
+    if ((retval = check_for_vara(&mem_nc_type, var, h5)))
+        return retval;
+    assert(hdf5_var->hdf_datasetid && (!var->ndims || (startp && countp)));
+
+    /* Convert from size_t and ptrdiff_t to hssize_t, and hsize_t. */
+    /* Also do sanity checks */
+    for (i = 0; i < var->ndims; i++)
+    {
+        /* Check for non-positive stride. */
+        if (stridep && stridep[i] <= 0)
+            return NC_ESTRIDE;
+
+        start[i] = startp[i];
+        count[i] = countp ? countp[i] : var->dim[i]->len;
+        stride[i] = stridep ? stridep[i] : 1;
+
+        /* Check to see if any counts are zero. */
+        if (!count[i])
+            zero_count++;
+    }
+
+    /* Get file space of data. */
+    if ((file_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Get the sizes of all the dims and put them in fdims. */
+    if (H5Sget_simple_extent_dims(file_spaceid, fdims, fmaxdims) < 0)
+        BAIL(NC_EHDFERR);
+
+#ifdef LOGGING
+    log_dim_info(var, fdims, fmaxdims, start, count);
+#endif
+
+    /* Check dimension bounds. Remember that unlimited dimensions can
+     * put data beyond their current length. */
+    for (d2 = 0; d2 < var->ndims; d2++)
+    {
+        hsize_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index written */
+        dim = var->dim[d2];
+        assert(dim && dim->hdr.id == var->dimids[d2]);
+        if (count[d2] == 0)
+            endindex = start[d2]; /* fixup for zero read count */
+        if (!dim->unlimited)
+        {
+#ifdef RELAX_COORD_BOUND
+            /* Allow start to equal dim size if count is zero. */
+            if (start[d2] > (hssize_t)fdims[d2] ||
+                (start[d2] == (hssize_t)fdims[d2] && count[d2] > 0))
+                BAIL_QUIET(NC_EINVALCOORDS);
+            if (!zero_count && endindex >= fdims[d2])
+                BAIL_QUIET(NC_EEDGE);
+#else
+            if (start[d2] >= (hssize_t)fdims[d2])
+                BAIL_QUIET(NC_EINVALCOORDS);
+            if (endindex >= fdims[d2])
+                BAIL_QUIET(NC_EEDGE);
+#endif
+        }
+    }
+
+    /* Now you would think that no one would be crazy enough to write
+       a scalar dataspace with one of the array function calls, but you
+       would be wrong. So let's check to see if the dataset is
+       scalar. If it is, we won't try to set up a hyperslab. */
+    if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR)
+    {
+        if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0)
+            BAIL(NC_EHDFERR);
+    }
+    else
+    {
+        if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, start, stride,
+                                count, NULL) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* Create a space for the memory, just big enough to hold the slab
+           we want. */
+        if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0)
+            BAIL(NC_EHDFERR);
+    }
+
+    /* Are we going to convert any data? (No converting of compound or
+     * opaque types.) */
+    if (mem_nc_type != var->type_info->hdr.id &&
+        mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE)
+    {
+        size_t file_type_size;
+
+        /* We must convert - allocate a buffer. */
+        need_to_convert++;
+        if (var->ndims)
+            for (d2=0; d2<var->ndims; d2++)
+                len *= countp[d2];
+        LOG((4, "converting data for var %s type=%d len=%d", var->hdr.name,
+             var->type_info->hdr.id, len));
+
+        /* Later on, we will need to know the size of this type in the
+         * file. */
+        assert(var->type_info->size);
+        file_type_size = var->type_info->size;
+
+        /* If we're reading, we need bufr to have enough memory to store
+         * the data in the file. If we're writing, we need bufr to be
+         * big enough to hold all the data in the file's type. */
+        if (len > 0)
+            if (!(bufr = malloc(len * file_type_size)))
+                BAIL(NC_ENOMEM);
+    }
+    else
+        bufr = (void *)data;
+
+    /* Create the data transfer property list. */
+    if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0)
+        BAIL(NC_EHDFERR);
+
+#ifdef USE_PARALLEL4
+    /* Set up parallel I/O, if needed. */
+    if ((retval = set_par_access(h5, var, xfer_plistid)))
+        BAIL(retval);
+#endif
+
+    /* Read this hyperslab from memory. Does the dataset have to be
+       extended? If it's already extended to the required size, it will
+       do no harm to reextend it to that size. */
+    if (var->ndims)
+    {
+        for (d2 = 0; d2 < var->ndims; d2++)
+        {
+            hsize_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index written */
+            if (count[d2] == 0)
+                endindex = start[d2];
+            dim = var->dim[d2];
+            assert(dim && dim->hdr.id == var->dimids[d2]);
+            if (dim->unlimited)
+            {
+#ifdef USE_PARALLEL4
+                extend_possible = 1;
+#endif
+                if (!zero_count && endindex >= fdims[d2])
+                {
+                    xtend_size[d2] = (long long unsigned)(endindex+1);
+                    need_to_extend++;
+                }
+                else
+                    xtend_size[d2] = (long long unsigned)fdims[d2];
+
+                if (!zero_count && endindex >= dim->len)
+                {
+                    dim->len = endindex+1;
+                    dim->extended = NC_TRUE;
+                }
+            }
+            else
+            {
+                xtend_size[d2] = (long long unsigned)dim->len;
+            }
+        }
+
+#ifdef USE_PARALLEL4
+        /* Check if anyone wants to extend. */
+        if (extend_possible && h5->parallel &&
+            NC_COLLECTIVE == var->parallel_access)
+        {
+            /* Form consensus opinion among all processes about whether
+             * to perform collective I/O.  */
+            if (MPI_SUCCESS != MPI_Allreduce(MPI_IN_PLACE, &need_to_extend, 1,
+                                             MPI_INT, MPI_BOR, h5->comm))
+                BAIL(NC_EMPI);
+        }
+#endif /* USE_PARALLEL4 */
+
+        /* If we need to extend it, we also need a new file_spaceid
+           to reflect the new size of the space. */
+        if (need_to_extend)
+        {
+            LOG((4, "extending dataset"));
+#ifdef USE_PARALLEL4
+            if (h5->parallel)
+            {
+                if (NC_COLLECTIVE != var->parallel_access)
+                    BAIL(NC_ECANTEXTEND);
+
+                /* Reach consensus about dimension sizes to extend to */
+                if (MPI_SUCCESS != MPI_Allreduce(MPI_IN_PLACE, xtend_size, var->ndims,
+                                                 MPI_UNSIGNED_LONG_LONG, MPI_MAX,
+                                                 h5->comm))
+                    BAIL(NC_EMPI);
+            }
+#endif /* USE_PARALLEL4 */
+            /* Convert xtend_size back to hsize_t for use with
+             * H5Dset_extent. */
+            for (d2 = 0; d2 < var->ndims; d2++)
+                fdims[d2] = (hsize_t)xtend_size[d2];
+
+            if (H5Dset_extent(hdf5_var->hdf_datasetid, fdims) < 0)
+                BAIL(NC_EHDFERR);
+            if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0)
+                BAIL2(NC_EHDFERR);
+            if ((file_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
+                BAIL(NC_EHDFERR);
+            if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET,
+                                    start, stride, count, NULL) < 0)
+                BAIL(NC_EHDFERR);
+        }
+    }
+
+    /* Do we need to convert the data? */
+    if (need_to_convert)
+    {
+        if ((retval = nc4_convert_type(data, bufr, mem_nc_type, var->type_info->hdr.id,
+                                       len, &range_error, var->fill_value,
+                                       (h5->cmode & NC_CLASSIC_MODEL))))
+            BAIL(retval);
+    }
+
+    /* Write the data. At last! */
+    LOG((4, "about to H5Dwrite datasetid 0x%x mem_spaceid 0x%x "
+         "file_spaceid 0x%x", hdf5_var->hdf_datasetid, mem_spaceid, file_spaceid));
+    if (H5Dwrite(hdf5_var->hdf_datasetid,
+                 ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->hdf_typeid,
+                 mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Remember that we have written to this var so that Fill Value
+     * can't be set for it. */
+    if (!var->written_to)
+        var->written_to = NC_TRUE;
+
+    /* For strict netcdf-3 rules, ignore erange errors between UBYTE
+     * and BYTE types. */
+    if ((h5->cmode & NC_CLASSIC_MODEL) &&
+        (var->type_info->hdr.id == NC_UBYTE || var->type_info->hdr.id == NC_BYTE) &&
+        (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) &&
+        range_error)
+        range_error = 0;
+
+exit:
+    if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (mem_spaceid > 0 && H5Sclose(mem_spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (xfer_plistid && (H5Pclose(xfer_plistid) < 0))
+        BAIL2(NC_EPARINIT);
+    if (need_to_convert && bufr) free(bufr);
+
+    /* If there was an error return it, otherwise return any potential
+       range error value. If none, return NC_NOERR as usual.*/
+    if (retval)
+        return retval;
+    if (range_error)
+        return NC_ERANGE;
+    return NC_NOERR;
+}
+
+/**
+ * @internal Read a strided array of data from a variable. This is
+ * called by nc_get_vars() for netCDF-4 files, as well as all the
+ * other nc_get_vars_* functions.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param startp Array of start indices. Must be provided for
+ * non-scalar vars.
+ * @param countp Array of counts. Will default to counts of extent of
+ * dimension if NULL.
+ * @param stridep Array of strides. Will default to strides of 1 if
+ * NULL.
+ * @param data The data to be written.
+ * @param mem_nc_type The type of the data in memory. (Convert to this
+ * type from file type.)
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Var not found.
+ * @returns ::NC_EHDFERR HDF5 function returned error.
+ * @returns ::NC_EINVALCOORDS Incorrect start.
+ * @returns ::NC_EEDGE Incorrect start/count.
+ * @returns ::NC_ENOMEM Out of memory.
+ * @returns ::NC_EMPI MPI library error (parallel only)
+ * @returns ::NC_ECANTEXTEND Can't extend dimension for write.
+ * @returns ::NC_ERANGE Data conversion error.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_get_vars(int ncid, int varid, const size_t *startp, const size_t *countp,
+             const ptrdiff_t *stridep, void *data, nc_type mem_nc_type)
+{
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    NC_DIM_INFO_T *dim;
+    NC_HDF5_TYPE_INFO_T *hdf5_type;
+    hid_t file_spaceid = 0, mem_spaceid = 0;
+    hid_t xfer_plistid = 0;
+    size_t file_type_size;
+    hsize_t count[NC_MAX_VAR_DIMS];
+    hsize_t fdims[NC_MAX_VAR_DIMS], fmaxdims[NC_MAX_VAR_DIMS];
+    hsize_t start[NC_MAX_VAR_DIMS];
+    hsize_t stride[NC_MAX_VAR_DIMS];
+    void *fillvalue = NULL;
+    int no_read = 0, provide_fill = 0;
+    int fill_value_size[NC_MAX_VAR_DIMS];
+    int scalar = 0, retval, range_error = 0, i, d2;
+    void *bufr = NULL;
+    int need_to_convert = 0;
+    size_t len = 1;
+
+    /* Find info for this file, group, and var. */
+    if ((retval = nc4_hdf5_find_grp_h5_var(ncid, varid, &h5, &grp, &var)))
+        return retval;
+    assert(h5 && grp && var && var->hdr.id == varid && var->format_var_info &&
+           var->type_info && var->type_info->size &&
+           var->type_info->format_type_info);
+
+    /* Get the HDF5-specific var and type info. */
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+    hdf5_type = (NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info;
+
+    LOG((3, "%s: var->hdr.name %s mem_nc_type %d", __func__,
+         var->hdr.name, mem_nc_type));
+
+    /* Check some stuff about the type and the file. Also end define
+     * mode, if needed. */
+    if ((retval = check_for_vara(&mem_nc_type, var, h5)))
+        return retval;
+    assert(hdf5_var->hdf_datasetid && (!var->ndims || (startp && countp)));
+
+    /* Convert from size_t and ptrdiff_t to hsize_t. Also do sanity
+     * checks. */
+    for (i = 0; i < var->ndims; i++)
+    {
+        /* If any of the stride values are non-positive, fail. */
+        if (stridep && stridep[i] <= 0)
+            return NC_ESTRIDE;
+
+        start[i] = startp[i];
+        count[i] = countp[i];
+        stride[i] = stridep ? stridep[i] : 1;
+
+        /* if any of the count values are zero don't actually read. */
+        if (count[i] == 0)
+            no_read++;
+    }
+
+    /* Get file space of data. */
+    if ((file_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Check to ensure the user selection is
+     * valid. H5Sget_simple_extent_dims gets the sizes of all the dims
+     * and put them in fdims. */
+    if (H5Sget_simple_extent_dims(file_spaceid, fdims, fmaxdims) < 0)
+        BAIL(NC_EHDFERR);
+
+#ifdef LOGGING
+    log_dim_info(var, fdims, fmaxdims, start, count);
+#endif
+
+    /* Check dimension bounds. Remember that unlimited dimensions can
+     * put data beyond their current length. */
+    for (d2 = 0; d2 < var->ndims; d2++)
+    {
+        hsize_t endindex = start[d2] + stride[d2] * (count[d2] - 1); /* last index read */
+        dim = var->dim[d2];
+        assert(dim && dim->hdr.id == var->dimids[d2]);
+        if (count[d2] == 0)
+            endindex = start[d2]; /* fixup for zero read count */
+        if (dim->unlimited)
+        {
+            size_t ulen;
+
+            /* We can't go beyond the largest current extent of
+               the unlimited dim. */
+            if ((retval = NC4_inq_dim(ncid, dim->hdr.id, NULL, &ulen)))
+                BAIL(retval);
+
+            /* Check for out of bound requests. */
+#ifdef RELAX_COORD_BOUND
+            /* Allow start to equal dim size if count is zero. */
+            if (start[d2] > (hssize_t)ulen ||
+                (start[d2] == (hssize_t)ulen && count[d2] > 0))
+                BAIL_QUIET(NC_EINVALCOORDS);
+#else
+            if (start[d2] >= (hssize_t)ulen && ulen > 0)
+                BAIL_QUIET(NC_EINVALCOORDS);
+#endif
+            if (count[d2] && endindex >= ulen)
+                BAIL_QUIET(NC_EEDGE);
+
+            /* Things get a little tricky here. If we're getting
+               a GET request beyond the end of this var's
+               current length in an unlimited dimension, we'll
+               later need to return the fill value for the
+               variable. */
+            if (start[d2] >= (hssize_t)fdims[d2])
+                fill_value_size[d2] = count[d2];
+            else if (endindex >= fdims[d2])
+                fill_value_size[d2] = count[d2] - ((fdims[d2] - start[d2])/stride[d2]);
+            else
+                fill_value_size[d2] = 0;
+            count[d2] -= fill_value_size[d2];
+            if (fill_value_size[d2])
+                provide_fill++;
+        }
+        else /* Dim is not unlimited. */
+        {
+            /* Check for out of bound requests. */
+#ifdef RELAX_COORD_BOUND
+            /* Allow start to equal dim size if count is zero. */
+            if (start[d2] > (hssize_t)fdims[d2] ||
+                (start[d2] == (hssize_t)fdims[d2] && count[d2] > 0))
+                BAIL_QUIET(NC_EINVALCOORDS);
+#else
+            if (start[d2] >= (hssize_t)fdims[d2])
+                BAIL_QUIET(NC_EINVALCOORDS);
+#endif
+            if (count[d2] && endindex >= fdims[d2])
+                BAIL_QUIET(NC_EEDGE);
+
+            /* Set the fill value boundary */
+            fill_value_size[d2] = count[d2];
+        }
+    }
+
+    /* Check the type_info fields. */
+    assert(var->type_info && var->type_info->size &&
+           var->type_info->format_type_info);
+
+    /* Later on, we will need to know the size of this type in the
+     * file. */
+    file_type_size = var->type_info->size;
+
+    if (!no_read)
+    {
+        /* Now you would think that no one would be crazy enough to write
+           a scalar dataspace with one of the array function calls, but you
+           would be wrong. So let's check to see if the dataset is
+           scalar. If it is, we won't try to set up a hyperslab. */
+        if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR)
+        {
+            if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0)
+                BAIL(NC_EHDFERR);
+            scalar++;
+        }
+        else
+        {
+            if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET,
+                                    start, stride, count, NULL) < 0)
+                BAIL(NC_EHDFERR);
+            /* Create a space for the memory, just big enough to hold the slab
+               we want. */
+            if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0)
+                BAIL(NC_EHDFERR);
+        }
+
+        /* Fix bug when reading HDF5 files with variable of type
+         * fixed-length string.  We need to make it look like a
+         * variable-length string, because that's all netCDF-4 data
+         * model supports, lacking anonymous dimensions.  So
+         * variable-length strings are in allocated memory that user has
+         * to free, which we allocate here. */
+        if (var->type_info->nc_type_class == NC_STRING &&
+            H5Tget_size(hdf5_type->hdf_typeid) > 1 &&
+            !H5Tis_variable_str(hdf5_type->hdf_typeid))
+        {
+            hsize_t fstring_len;
+
+            if ((fstring_len = H5Tget_size(hdf5_type->hdf_typeid)) == 0)
+                BAIL(NC_EHDFERR);
+            if (!(*(char **)data = malloc(1 + fstring_len)))
+                BAIL(NC_ENOMEM);
+            bufr = *(char **)data;
+        }
+
+        /* Are we going to convert any data? (No converting of compound or
+         * opaque types.) */
+        if (mem_nc_type != var->type_info->hdr.id &&
+            mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE)
+        {
+            /* We must convert - allocate a buffer. */
+            need_to_convert++;
+            if (var->ndims)
+                for (d2 = 0; d2 < var->ndims; d2++)
+                    len *= countp[d2];
+            LOG((4, "converting data for var %s type=%d len=%d", var->hdr.name,
+                 var->type_info->hdr.id, len));
+
+            /* If we're reading, we need bufr to have enough memory to store
+             * the data in the file. If we're writing, we need bufr to be
+             * big enough to hold all the data in the file's type. */
+            if (len > 0)
+                if (!(bufr = malloc(len * file_type_size)))
+                    BAIL(NC_ENOMEM);
+        }
+        else
+            if (!bufr)
+                bufr = data;
+
+        /* Create the data transfer property list. */
+        if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0)
+            BAIL(NC_EHDFERR);
+
+#ifdef USE_PARALLEL4
+        /* Set up parallel I/O, if needed. */
+        if ((retval = set_par_access(h5, var, xfer_plistid)))
+            BAIL(retval);
+#endif
+
+        /* Read this hyperslab into memory. */
+        LOG((5, "About to H5Dread some data..."));
+        if (H5Dread(hdf5_var->hdf_datasetid,
+                    ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid,
+                    mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* Convert data type if needed. */
+        if (need_to_convert)
+        {
+            if ((retval = nc4_convert_type(bufr, data, var->type_info->hdr.id, mem_nc_type,
+                                           len, &range_error, var->fill_value,
+                                           (h5->cmode & NC_CLASSIC_MODEL))))
+                BAIL(retval);
+
+            /* For strict netcdf-3 rules, ignore erange errors between UBYTE
+             * and BYTE types. */
+            if ((h5->cmode & NC_CLASSIC_MODEL) &&
+                (var->type_info->hdr.id == NC_UBYTE || var->type_info->hdr.id == NC_BYTE) &&
+                (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) &&
+                range_error)
+                range_error = 0;
+        }
+    } /* endif ! no_read */
+    else
+    {
+#ifdef USE_PARALLEL4 /* Start block contributed by HDF group. */
+        /* For collective IO read, some processes may not have any element for reading.
+           Collective requires all processes to participate, so we use H5Sselect_none
+           for these processes. */
+        if (var->parallel_access == NC_COLLECTIVE)
+        {
+            /* Create the data transfer property list. */
+            if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0)
+                BAIL(NC_EHDFERR);
+
+            if ((retval = set_par_access(h5, var, xfer_plistid)))
+                BAIL(retval);
+
+            if (H5Sselect_none(file_spaceid) < 0)
+                BAIL(NC_EHDFERR);
+
+            /* Since no element will be selected, we just get the memory
+             * space the same as the file space. */
+            if ((mem_spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
+                BAIL(NC_EHDFERR);
+            if (H5Sselect_none(mem_spaceid) < 0)
+                BAIL(NC_EHDFERR);
+
+            /* Read this hyperslab into memory. */
+            LOG((5, "About to H5Dread some data..."));
+            if (H5Dread(hdf5_var->hdf_datasetid,
+                        ((NC_HDF5_TYPE_INFO_T *)var->type_info->format_type_info)->native_hdf_typeid,
+                        mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0)
+                BAIL(NC_EHDFERR);
+        }
+#endif /* USE_PARALLEL4 */
+    }
+    /* Now we need to fake up any further data that was asked for,
+       using the fill values instead. First skip past the data we
+       just read, if any. */
+    if (!scalar && provide_fill)
+    {
+        void *filldata;
+        size_t real_data_size = 0;
+        size_t fill_len;
+
+        /* Skip past the real data we've already read. */
+        if (!no_read)
+            for (real_data_size = file_type_size, d2 = 0; d2 < var->ndims; d2++)
+                real_data_size *= (count[d2] - start[d2]);
+
+        /* Get the fill value from the HDF5 variable. Memory will be
+         * allocated. */
+        if (nc4_get_fill_value(h5, var, &fillvalue) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* How many fill values do we need? */
+        for (fill_len = 1, d2 = 0; d2 < var->ndims; d2++)
+            fill_len *= (fill_value_size[d2] ? fill_value_size[d2] : 1);
+
+        /* Copy the fill value into the rest of the data buffer. */
+        filldata = (char *)data + real_data_size;
+        for (i = 0; i < fill_len; i++)
+        {
+
+            if (var->type_info->nc_type_class == NC_STRING)
+            {
+                if (*(char **)fillvalue)
+                {
+                    if (!(*(char **)filldata = strdup(*(char **)fillvalue)))
+                        BAIL(NC_ENOMEM);
+                }
+                else
+                    *(char **)filldata = NULL;
+            }
+            else if (var->type_info->nc_type_class == NC_VLEN)
+            {
+                if (fillvalue)
+                {
+                    memcpy(filldata,fillvalue,file_type_size);
+                } else {
+                    *(char **)filldata = NULL;
+                }
+            }
+            else
+                memcpy(filldata, fillvalue, file_type_size);
+            filldata = (char *)filldata + file_type_size;
+        }
+    }
+
+exit:
+    if (file_spaceid > 0)
+        if (H5Sclose(file_spaceid) < 0)
+            BAIL2(NC_EHDFERR);
+    if (mem_spaceid > 0)
+        if (H5Sclose(mem_spaceid) < 0)
+            BAIL2(NC_EHDFERR);
+    if (xfer_plistid > 0)
+        if (H5Pclose(xfer_plistid) < 0)
+            BAIL2(NC_EHDFERR);
+    if (need_to_convert && bufr)
+        free(bufr);
+    if (fillvalue)
+    {
+        if (var->type_info->nc_type_class == NC_VLEN)
+            nc_free_vlen((nc_vlen_t *)fillvalue);
+        else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillvalue)
+            free(*(char **)fillvalue);
+        free(fillvalue);
+    }
+
+    /* If there was an error return it, otherwise return any potential
+       range error value. If none, return NC_NOERR as usual.*/
+    if (retval)
+        return retval;
+    if (range_error)
+        return NC_ERANGE;
+    return NC_NOERR;
+}
+
+/**
+ * @internal Get all the information about a variable. Pass NULL for
+ * whatever you don't care about.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param name Gets name.
+ * @param xtypep Gets type.
+ * @param ndimsp Gets number of dims.
+ * @param dimidsp Gets array of dim IDs.
+ * @param nattsp Gets number of attributes.
+ * @param shufflep Gets shuffle setting.
+ * @param deflatep Gets deflate setting.
+ * @param deflate_levelp Gets deflate level.
+ * @param fletcher32p Gets fletcher32 setting.
+ * @param contiguousp Gets contiguous setting.
+ * @param chunksizesp Gets chunksizes.
+ * @param no_fill Gets fill mode.
+ * @param fill_valuep Gets fill value.
+ * @param endiannessp Gets one of ::NC_ENDIAN_BIG ::NC_ENDIAN_LITTLE
+ * ::NC_ENDIAN_NATIVE
+ * @param idp Pointer to memory to store filter id.
+ * @param nparamsp Pointer to memory to store filter parameter count.
+ * @param params Pointer to vector of unsigned integers into which
+ * to store filter parameters.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Bad varid.
+ * @returns ::NC_ENOMEM Out of memory.
+ * @returns ::NC_EINVAL Invalid input.
+ * @author Ed Hartnett, Dennis Heimbigner
+ */
+int
+NC4_HDF5_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep,
+                     int *ndimsp, int *dimidsp, int *nattsp,
+                     int *shufflep, int *deflatep, int *deflate_levelp,
+                     int *fletcher32p, int *contiguousp, size_t *chunksizesp,
+                     int *no_fill, void *fill_valuep, int *endiannessp,
+                     unsigned int *idp, size_t *nparamsp, unsigned int *params)
+{
+    NC_FILE_INFO_T *h5;
+    NC_GRP_INFO_T *grp;
+    NC_VAR_INFO_T *var = NULL;
+    int retval;
+
+    LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid));
+
+    /* Find the file, group, and var info, and do lazy att read if
+     * needed. */
+    if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, NULL, 0, 0, NULL,
+                                            &h5, &grp, &var, NULL)))
+        return retval;
+    assert(grp && h5);
+
+    /* Now that lazy atts have been read, use the libsrc4 function to
+     * get the answers. */
+    return NC4_inq_var_all(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp,
+                           shufflep, deflatep, deflate_levelp, fletcher32p,
+                           contiguousp, chunksizesp, no_fill, fill_valuep,
+                           endiannessp, idp, nparamsp, params);
+}
+
+/**
+ * @internal Set chunk cache size for a variable. This is the internal
+ * function called by nc_set_var_chunk_cache().
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param size Size in bytes to set cache.
+ * @param nelems Number of elements in cache.
+ * @param preemption Controls cache swapping.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EBADID Bad ncid.
+ * @returns ::NC_ENOTVAR Invalid variable ID.
+ * @returns ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict
+ * nc3 netcdf-4 file.
+ * @returns ::NC_EINVAL Invalid input.
+ * @returns ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+int
+NC4_HDF5_set_var_chunk_cache(int ncid, int varid, size_t size, size_t nelems,
+                             float preemption)
+{
+    NC_GRP_INFO_T *grp;
+    NC_FILE_INFO_T *h5;
+    NC_VAR_INFO_T *var;
+    int retval;
+
+    /* Check input for validity. */
+    if (preemption < 0 || preemption > 1)
+        return NC_EINVAL;
+
+    /* Find info for this file and group, and set pointer to each. */
+    if ((retval = nc4_find_nc_grp_h5(ncid, NULL, &grp, &h5)))
+        return retval;
+    assert(grp && h5);
+
+    /* Find the var. */
+    if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
+        return NC_ENOTVAR;
+    assert(var && var->hdr.id == varid);
+
+    /* Set the values. */
+    var->chunk_cache_size = size;
+    var->chunk_cache_nelems = nelems;
+    var->chunk_cache_preemption = preemption;
+
+    /* Reopen the dataset to bring new settings into effect. */
+    if ((retval = nc4_reopen_dataset(grp, var)))
+        return retval;
+    return NC_NOERR;
+}
+
+/**
+ * @internal A wrapper for NC4_set_var_chunk_cache(), we need this
+ * version for fortran. Negative values leave settings as they are.
+ *
+ * @param ncid File ID.
+ * @param varid Variable ID.
+ * @param size Size in bytes to set cache.
+ * @param nelems Number of elements in cache.
+ * @param preemption Controls cache swapping.
+ *
+ * @returns ::NC_NOERR for success
+ * @author Ed Hartnett
+ */
+int
+nc_set_var_chunk_cache_ints(int ncid, int varid, int size, int nelems,
+                            int preemption)
+{
+    size_t real_size = H5D_CHUNK_CACHE_NBYTES_DEFAULT;
+    size_t real_nelems = H5D_CHUNK_CACHE_NSLOTS_DEFAULT;
+    float real_preemption = CHUNK_CACHE_PREEMPTION;
+
+    if (size >= 0)
+        real_size = ((size_t) size) * MEGABYTE;
+
+    if (nelems >= 0)
+        real_nelems = nelems;
+
+    if (preemption >= 0)
+        real_preemption = preemption / 100.;
+
+    return NC4_HDF5_set_var_chunk_cache(ncid, varid, real_size, real_nelems,
+                                        real_preemption);
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4hdf.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4hdf.c
new file mode 100644
index 0000000000000000000000000000000000000000..6b496094c61890f41758034533367ef8c647be53
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4hdf.c
@@ -0,0 +1,2640 @@
+/* Copyright 2018, University Corporation for Atmospheric
+ * Research. See the COPYRIGHT file for copying and redistribution
+ * conditions. */
+/**
+ * @file
+ * @internal This file is part of netcdf-4, a netCDF-like interface
+ * for HDF5, or a HDF5 backend for netCDF, depending on your point of
+ * view.
+ *
+ * This file contains functions internal to the netcdf4 library. None of
+ * the functions in this file are exposed in the exetnal API. These
+ * functions handle the HDF interface.
+ *
+ * @author Ed Hartnett, Dennis Heimbigner, Ward Fisher
+ */
+
+#include "config.h"
+#include "hdf5internal.h"
+#include <math.h>
+
+#ifdef HAVE_INTTYPES_H
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#endif
+
+#define NC_HDF5_MAX_NAME 1024 /**< @internal Max size of HDF5 name. */
+
+/**
+ * @internal Flag attributes in a linked list as dirty.
+ *
+ * @param attlist List of attributes, may be NULL.
+ *
+ * @return NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+static int
+flag_atts_dirty(NCindex *attlist) {
+
+    NC_ATT_INFO_T *att = NULL;
+    int i;
+
+    if(attlist == NULL) {
+        return NC_NOERR;
+    }
+
+    for(i=0;i<ncindexsize(attlist);i++) {
+        att = (NC_ATT_INFO_T*)ncindexith(attlist,i);
+        if(att == NULL) continue;
+        att->dirty = NC_TRUE;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function is needed to handle one special case: what
+ * if the user defines a dim, writes metadata, then goes back into
+ * define mode and adds a coordinate var for the already existing
+ * dim. In that case, I need to recreate the dim's dimension scale
+ * dataset, and then I need to go to every var in the file which uses
+ * that dimension, and attach the new dimension scale.
+ *
+ * @param grp Pointer to group info struct.
+ * @param dimid Dimension ID.
+ * @param dimscaleid HDF5 dimension scale ID.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+int
+rec_reattach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
+{
+    NC_VAR_INFO_T *var;
+    NC_GRP_INFO_T *child_grp;
+    int d, i;
+    int retval;
+
+    assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0);
+    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
+
+    /* If there are any child groups, attach dimscale there, if needed. */
+    for (i = 0; i < ncindexsize(grp->children); i++)
+    {
+        child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
+        assert(child_grp);
+        if ((retval = rec_reattach_scales(child_grp, dimid, dimscaleid)))
+            return retval;
+    }
+
+    /* Find any vars that use this dimension id. */
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+    {
+        NC_HDF5_VAR_INFO_T *hdf5_var;
+
+        var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
+        assert(var && var->format_var_info);
+        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+        for (d = 0; d < var->ndims; d++)
+        {
+            if (var->dimids[d] == dimid && !var->dimscale)
+            {
+                LOG((2, "%s: attaching scale for dimid %d to var %s",
+                     __func__, var->dimids[d], var->hdr.name));
+                if (var->created)
+                {
+                    if (H5DSattach_scale(hdf5_var->hdf_datasetid,
+                                         dimscaleid, d) < 0)
+                        return NC_EHDFERR;
+                    var->dimscale_attached[d] = NC_TRUE;
+                }
+            }
+        }
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function is needed to handle one special case: what
+ * if the user defines a dim, writes metadata, then goes back into
+ * define mode and adds a coordinate var for the already existing
+ * dim. In that case, I need to recreate the dim's dimension scale
+ * dataset, and then I need to go to every var in the file which uses
+ * that dimension, and attach the new dimension scale.
+ *
+ * @param grp Pointer to group info struct.
+ * @param dimid Dimension ID.
+ * @param dimscaleid HDF5 dimension scale ID.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+int
+rec_detach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
+{
+    NC_VAR_INFO_T *var;
+    NC_GRP_INFO_T *child_grp;
+    int d, i;
+    int retval;
+
+    assert(grp && grp->hdr.name && dimid >= 0 && dimscaleid >= 0);
+    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
+
+    /* If there are any child groups, detach dimscale there, if needed. */
+    for(i=0;i<ncindexsize(grp->children);i++) {
+        child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
+        if(child_grp == NULL) continue;
+        if ((retval = rec_detach_scales(child_grp, dimid, dimscaleid)))
+            return retval;
+    }
+
+    /* Find any vars that use this dimension id. */
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+    {
+        NC_HDF5_VAR_INFO_T *hdf5_var;
+        var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
+        assert(var && var->format_var_info);
+        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+        for (d = 0; d < var->ndims; d++)
+        {
+            if (var->dimids[d] == dimid && !var->dimscale)
+            {
+                LOG((2, "%s: detaching scale for dimid %d to var %s",
+                     __func__, var->dimids[d], var->hdr.name));
+                if (var->created)
+                {
+                    if (var->dimscale_attached && var->dimscale_attached[d])
+                    {
+                        if (H5DSdetach_scale(hdf5_var->hdf_datasetid,
+                                             dimscaleid, d) < 0)
+                            return NC_EHDFERR;
+                        var->dimscale_attached[d] = NC_FALSE;
+                    }
+                }
+            }
+        }
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal Open a HDF5 dataset and leave it open.
+ *
+ * @param grp Pointer to group info struct.
+ * @param varid Variable ID.
+ * @param dataset Pointer that gets the HDF5 dataset ID.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+int
+nc4_open_var_grp2(NC_GRP_INFO_T *grp, int varid, hid_t *dataset)
+{
+    NC_VAR_INFO_T *var;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+
+    assert(grp && grp->format_grp_info && dataset);
+
+    /* Find the requested varid. */
+    if (!(var = (NC_VAR_INFO_T *)ncindexith(grp->vars, varid)))
+        return NC_ENOTVAR;
+    assert(var && var->hdr.id == varid && var->format_var_info);
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Open this dataset if necessary. */
+    if (!hdf5_var->hdf_datasetid)
+    {
+        NC_HDF5_GRP_INFO_T *hdf5_grp;
+        hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+        if ((hdf5_var->hdf_datasetid = H5Dopen2(hdf5_grp->hdf_grpid,
+                                                var->hdr.name, H5P_DEFAULT)) < 0)
+            return NC_ENOTVAR;
+    }
+
+    *dataset = hdf5_var->hdf_datasetid;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal What fill value should be used for a variable?
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param var Pointer to variable info struct.
+ * @param fillp Pointer that gets pointer to fill value.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+int
+nc4_get_fill_value(NC_FILE_INFO_T *h5, NC_VAR_INFO_T *var, void **fillp)
+{
+    size_t size;
+    int retval;
+
+    /* Find out how much space we need for this type's fill value. */
+    if (var->type_info->nc_type_class == NC_VLEN)
+        size = sizeof(nc_vlen_t);
+    else if (var->type_info->nc_type_class == NC_STRING)
+        size = sizeof(char *);
+    else
+    {
+        if ((retval = nc4_get_typelen_mem(h5, var->type_info->hdr.id, &size)))
+            return retval;
+    }
+    assert(size);
+
+    /* Allocate the space. */
+    if (!((*fillp) = calloc(1, size)))
+        return NC_ENOMEM;
+
+    /* If the user has set a fill_value for this var, use, otherwise
+     * find the default fill value. */
+    if (var->fill_value)
+    {
+        LOG((4, "Found a fill value for var %s", var->hdr.name));
+        if (var->type_info->nc_type_class == NC_VLEN)
+        {
+            nc_vlen_t *in_vlen = (nc_vlen_t *)(var->fill_value), *fv_vlen = (nc_vlen_t *)(*fillp);
+            size_t basetypesize = 0;
+
+            if((retval=nc4_get_typelen_mem(h5, var->type_info->u.v.base_nc_typeid, &basetypesize)))
+                return retval;
+
+            fv_vlen->len = in_vlen->len;
+            if (!(fv_vlen->p = malloc(basetypesize * in_vlen->len)))
+            {
+                free(*fillp);
+                *fillp = NULL;
+                return NC_ENOMEM;
+            }
+            memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * basetypesize);
+        }
+        else if (var->type_info->nc_type_class == NC_STRING)
+        {
+            if (*(char **)var->fill_value)
+                if (!(**(char ***)fillp = strdup(*(char **)var->fill_value)))
+                {
+                    free(*fillp);
+                    *fillp = NULL;
+                    return NC_ENOMEM;
+                }
+        }
+        else
+            memcpy((*fillp), var->fill_value, size);
+    }
+    else
+    {
+        if (nc4_get_default_fill_value(var->type_info, *fillp))
+        {
+            /* Note: release memory, but don't return error on failure */
+            free(*fillp);
+            *fillp = NULL;
+        }
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Given a netcdf type, return appropriate HDF typeid.  (All
+ * hdf_typeid's returned from this routine must be H5Tclosed by the
+ * caller).
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param xtype NetCDF type ID.
+ * @param hdf_typeid Pointer that gets the HDF5 type ID.
+ * @param endianness Desired endianness in HDF5 type.
+ *
+ * @return NC_NOERR No error.
+ * @return NC_ECHAR Conversions of NC_CHAR forbidden.
+ * @return NC_EVARMETA HDF5 returning error creating datatype.
+ * @return NC_EHDFERR HDF5 returning error.
+ * @return NC_EBADTYE Type not found.
+ * @author Ed Hartnett
+ */
+int
+nc4_get_hdf_typeid(NC_FILE_INFO_T *h5, nc_type xtype,
+                   hid_t *hdf_typeid, int endianness)
+{
+    NC_TYPE_INFO_T *type;
+    hid_t typeid = 0;
+    int retval = NC_NOERR;
+
+    assert(hdf_typeid && h5);
+
+    *hdf_typeid = -1;
+
+    /* Determine an appropriate HDF5 datatype */
+    if (xtype == NC_NAT)
+        return NC_EBADTYPE;
+    else if (xtype == NC_CHAR || xtype == NC_STRING)
+    {
+        /* NC_CHAR & NC_STRING types create a new HDF5 datatype */
+        if (xtype == NC_CHAR)
+        {
+            if ((typeid = H5Tcopy(H5T_C_S1)) < 0)
+                return NC_EHDFERR;
+            if (H5Tset_strpad(typeid, H5T_STR_NULLTERM) < 0)
+                BAIL(NC_EVARMETA);
+            if(H5Tset_cset(typeid, H5T_CSET_ASCII) < 0)
+                BAIL(NC_EVARMETA);
+
+            /* Take ownership of the newly created HDF5 datatype */
+            *hdf_typeid = typeid;
+            typeid = 0;
+        }
+        else
+        {
+            if ((typeid = H5Tcopy(H5T_C_S1)) < 0)
+                return NC_EHDFERR;
+            if (H5Tset_size(typeid, H5T_VARIABLE) < 0)
+                BAIL(NC_EVARMETA);
+            if(H5Tset_cset(typeid, H5T_CSET_UTF8) < 0)
+                BAIL(NC_EVARMETA);
+
+            /* Take ownership of the newly created HDF5 datatype */
+            *hdf_typeid = typeid;
+            typeid = 0;
+        }
+    }
+    else
+    {
+        /* All other types use an existing HDF5 datatype */
+        switch (xtype)
+        {
+        case NC_BYTE: /* signed 1 byte integer */
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_I8LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_I8BE;
+            else
+                typeid = H5T_NATIVE_SCHAR;
+            break;
+
+        case NC_SHORT: /* signed 2 byte integer */
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_I16LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_I16BE;
+            else
+                typeid = H5T_NATIVE_SHORT;
+            break;
+
+        case NC_INT:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_I32LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_I32BE;
+            else
+                typeid = H5T_NATIVE_INT;
+            break;
+
+        case NC_UBYTE:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_U8LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_U8BE;
+            else
+                typeid = H5T_NATIVE_UCHAR;
+            break;
+
+        case NC_USHORT:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_U16LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_U16BE;
+            else
+                typeid = H5T_NATIVE_USHORT;
+            break;
+
+        case NC_UINT:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_U32LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_U32BE;
+            else
+                typeid = H5T_NATIVE_UINT;
+            break;
+
+        case NC_INT64:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_I64LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_I64BE;
+            else
+                typeid = H5T_NATIVE_LLONG;
+            break;
+
+        case NC_UINT64:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_STD_U64LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_STD_U64BE;
+            else
+                typeid = H5T_NATIVE_ULLONG;
+            break;
+
+        case NC_FLOAT:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_IEEE_F32LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_IEEE_F32BE;
+            else
+                typeid = H5T_NATIVE_FLOAT;
+            break;
+
+        case NC_DOUBLE:
+            if (endianness == NC_ENDIAN_LITTLE)
+                typeid = H5T_IEEE_F64LE;
+            else if (endianness == NC_ENDIAN_BIG)
+                typeid = H5T_IEEE_F64BE;
+            else
+                typeid = H5T_NATIVE_DOUBLE;
+            break;
+
+        default:
+            /* Maybe this is a user defined type? */
+            if (nc4_find_type(h5, xtype, &type))
+                return NC_EBADTYPE;
+            if (!type)
+                return NC_EBADTYPE;
+            typeid = ((NC_HDF5_TYPE_INFO_T *)type->format_type_info)->hdf_typeid;
+            break;
+        }
+        assert(typeid);
+
+        /* Copy the HDF5 datatype, so the function operates uniformly */
+        if ((*hdf_typeid = H5Tcopy(typeid)) < 0)
+            return NC_EHDFERR;
+        typeid = 0;
+    }
+    assert(*hdf_typeid != -1);
+
+exit:
+    if (typeid > 0 && H5Tclose(typeid) < 0)
+        BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal Write an attribute.
+ *
+ * @param grp Pointer to group info struct.
+ * @param varid Variable ID or NC_GLOBAL.
+ * @param att Pointer to attribute info struct.
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_ENOTVAR Variable not found.
+ * @returns ::NC_EPERM Read-only file.
+ * @returns ::NC_EHDFERR HDF5 returned error.
+ * @returns ::NC_EATTMETA HDF5 returned error with attribute calls.
+ * @author Ed Hartnett
+ */
+static int
+put_att_grpa(NC_GRP_INFO_T *grp, int varid, NC_ATT_INFO_T *att)
+{
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    hid_t datasetid = 0, locid;
+    hid_t attid = 0, spaceid = 0, file_typeid = 0;
+    hid_t existing_att_typeid = 0, existing_attid = 0, existing_spaceid = 0;
+    hsize_t dims[1]; /* netcdf attributes always 1-D. */
+    htri_t attr_exists;
+    int reuse_att = 0; /* Will be true if we can re-use an existing att. */
+    void *data;
+    int phoney_data = 99;
+    int retval = NC_NOERR;
+
+    assert(att->hdr.name && grp && grp->format_grp_info);
+    LOG((3, "%s: varid %d att->hdr.id %d att->hdr.name %s att->nc_typeid %d "
+         "att->len %d", __func__, varid, att->hdr.id, att->hdr.name,
+         att->nc_typeid, att->len));
+
+    /* Get HDF5-specific group info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    /* If the file is read-only, return an error. */
+    if (grp->nc4_info->no_write)
+        BAIL(NC_EPERM);
+
+    /* Get the hid to attach the attribute to, or read it from. */
+    if (varid == NC_GLOBAL)
+        locid = hdf5_grp->hdf_grpid;
+    else
+    {
+        if ((retval = nc4_open_var_grp2(grp, varid, &datasetid)))
+            BAIL(retval);
+        locid = datasetid;
+    }
+
+    /* Get the length ready, and find the HDF type we'll be
+     * writing. */
+    dims[0] = att->len;
+    if ((retval = nc4_get_hdf_typeid(grp->nc4_info, att->nc_typeid,
+                                     &file_typeid, 0)))
+        BAIL(retval);
+
+    /* Even if the length is zero, HDF5 won't let me write with a
+     * NULL pointer. So if the length of the att is zero, point to
+     * some phoney data (which won't be written anyway.)*/
+    if (!dims[0])
+        data = &phoney_data;
+    else if (att->data)
+        data = att->data;
+    else if (att->stdata)
+        data = att->stdata;
+    else
+        data = att->vldata;
+
+    /* NC_CHAR types require some extra work. The space ID is set to
+     * scalar, and the type is told how long the string is. If it's
+     * really zero length, set the size to 1. (The fact that it's
+     * really zero will be marked by the NULL dataspace, but HDF5
+     * doesn't allow me to set the size of the type to zero.)*/
+    if (att->nc_typeid == NC_CHAR)
+    {
+        size_t string_size = dims[0];
+        if (!string_size)
+        {
+            string_size = 1;
+            if ((spaceid = H5Screate(H5S_NULL)) < 0)
+                BAIL(NC_EATTMETA);
+        }
+        else
+        {
+            if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
+                BAIL(NC_EATTMETA);
+        }
+        if (H5Tset_size(file_typeid, string_size) < 0)
+            BAIL(NC_EATTMETA);
+        if (H5Tset_strpad(file_typeid, H5T_STR_NULLTERM) < 0)
+            BAIL(NC_EATTMETA);
+    }
+    else
+    {
+        if (!att->len)
+        {
+            if ((spaceid = H5Screate(H5S_NULL)) < 0)
+                BAIL(NC_EATTMETA);
+        }
+        else
+        {
+            if ((spaceid = H5Screate_simple(1, dims, NULL)) < 0)
+                BAIL(NC_EATTMETA);
+        }
+    }
+
+    /* Does the att exists already? */
+    if ((attr_exists = H5Aexists(locid, att->hdr.name)) < 0)
+        BAIL(NC_EHDFERR);
+    if (attr_exists)
+    {
+        hssize_t npoints;
+
+        /* Open the attribute. */
+        if ((existing_attid = H5Aopen(locid, att->hdr.name, H5P_DEFAULT)) < 0)
+            BAIL(NC_EATTMETA);
+
+        /* Find the type of the existing attribute. */
+        if ((existing_att_typeid = H5Aget_type(existing_attid)) < 0)
+            BAIL(NC_EATTMETA);
+
+        /* How big is the attribute? */
+        if ((existing_spaceid = H5Aget_space(existing_attid)) < 0)
+            BAIL(NC_EATTMETA);
+        if ((npoints = H5Sget_simple_extent_npoints(existing_spaceid)) < 0)
+            BAIL(NC_EATTMETA);
+
+        /* Delete the attribute. */
+        if (file_typeid != existing_att_typeid || npoints != att->len)
+        {
+            if (H5Adelete(locid, att->hdr.name) < 0)
+                BAIL(NC_EHDFERR);
+        }
+        else
+        {
+            reuse_att++;
+        }
+    }
+
+    /* Create the attribute. */
+    if ((attid = H5Acreate(locid, att->hdr.name, file_typeid, spaceid,
+                           H5P_DEFAULT)) < 0)
+        BAIL(NC_EATTMETA);
+
+    /* Write the values, (even if length is zero). */
+    if (H5Awrite(attid, file_typeid, data) < 0)
+        BAIL(NC_EATTMETA);
+
+exit:
+    if (file_typeid && H5Tclose(file_typeid))
+        BAIL2(NC_EHDFERR);
+    if (attid > 0 && H5Aclose(attid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (existing_att_typeid && H5Tclose(existing_att_typeid))
+        BAIL2(NC_EHDFERR);
+    if (existing_attid > 0 && H5Aclose(existing_attid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (spaceid > 0 && H5Sclose(spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (existing_spaceid > 0 && H5Sclose(existing_spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal Write all the dirty atts in an attlist.
+ *
+ * @param attlist Pointer to the list if attributes.
+ * @param varid Variable ID.
+ * @param grp Pointer to group info struct.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+static int
+write_attlist(NCindex *attlist, int varid, NC_GRP_INFO_T *grp)
+{
+    NC_ATT_INFO_T *att;
+    int retval;
+    int i;
+
+    for(i = 0; i < ncindexsize(attlist); i++)
+    {
+        att = (NC_ATT_INFO_T *)ncindexith(attlist, i);
+        assert(att);
+        if (att->dirty)
+        {
+            LOG((4, "%s: writing att %s to varid %d", __func__, att->hdr.name, varid));
+            if ((retval = put_att_grpa(grp, varid, att)))
+                return retval;
+            att->dirty = NC_FALSE;
+            att->created = NC_TRUE;
+        }
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal HDF5 dimension scales cannot themselves have scales
+ * attached. This is a problem for multidimensional coordinate
+ * variables. So this function writes a special attribute for such a
+ * variable, which has the ids of all the dimensions for that
+ * coordinate variable.
+ *
+ * @param var Pointer to var info struct.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+static int
+write_coord_dimids(NC_VAR_INFO_T *var)
+{
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    hsize_t coords_len[1];
+    hid_t c_spaceid = -1, c_attid = -1;
+    int retval = NC_NOERR;
+
+    assert(var && var->format_var_info);
+
+    /* Get HDF5-specific var info. */
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Set up space for attribute. */
+    coords_len[0] = var->ndims;
+    if ((c_spaceid = H5Screate_simple(1, coords_len, coords_len)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Create the attribute. */
+    if ((c_attid = H5Acreate(hdf5_var->hdf_datasetid, COORDINATES,
+                             H5T_NATIVE_INT, c_spaceid, H5P_DEFAULT)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Write our attribute. */
+    if (H5Awrite(c_attid, H5T_NATIVE_INT, var->dimids) < 0)
+        BAIL(NC_EHDFERR);
+
+exit:
+    if (c_spaceid >= 0 && H5Sclose(c_spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (c_attid >= 0 && H5Aclose(c_attid) < 0)
+        BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal Write a special attribute for the netCDF-4 dimension ID.
+ *
+ * @param datasetid HDF5 datasset ID.
+ * @param dimid NetCDF dimension ID.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+static int
+write_netcdf4_dimid(hid_t datasetid, int dimid)
+{
+    hid_t dimid_spaceid = -1, dimid_attid = -1;
+    htri_t attr_exists;
+    int retval = NC_NOERR;
+
+    /* Create the space. */
+    if ((dimid_spaceid = H5Screate(H5S_SCALAR)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Does the attribute already exist? If so, don't try to create it. */
+    if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0)
+        BAIL(NC_EHDFERR);
+    if (attr_exists)
+        dimid_attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME,
+                                      H5P_DEFAULT, H5P_DEFAULT);
+    else
+        /* Create the attribute if needed. */
+        dimid_attid = H5Acreate(datasetid, NC_DIMID_ATT_NAME,
+                                H5T_NATIVE_INT, dimid_spaceid, H5P_DEFAULT);
+    if (dimid_attid  < 0)
+        BAIL(NC_EHDFERR);
+
+
+    /* Write it. */
+    LOG((4, "%s: writing secret dimid %d", __func__, dimid));
+    if (H5Awrite(dimid_attid, H5T_NATIVE_INT, &dimid) < 0)
+        BAIL(NC_EHDFERR);
+
+exit:
+    /* Close stuff*/
+    if (dimid_spaceid >= 0 && H5Sclose(dimid_spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (dimid_attid >= 0 && H5Aclose(dimid_attid) < 0)
+        BAIL2(NC_EHDFERR);
+
+    return retval;
+}
+
+/**
+ * @internal This function creates the HDF5 dataset for a variable.
+ *
+ * @param grp Pointer to group info struct.
+ * @param var Pointer to variable info struct.
+ * @param write_dimid True to write dimid.
+ *
+ * @return ::NC_NOERR
+ * @returns NC_ECHAR Conversions of NC_CHAR forbidden.
+ * @returns NC_EVARMETA HDF5 returning error creating datatype.
+ * @returns NC_EHDFERR HDF5 returning error.
+ * @returns NC_EBADTYE Type not found.
+ * @author Ed Hartnett
+ */
+static int
+var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid)
+{
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    hid_t plistid = 0, access_plistid = 0, typeid = 0, spaceid = 0;
+    hsize_t chunksize[H5S_MAX_RANK], dimsize[H5S_MAX_RANK], maxdimsize[H5S_MAX_RANK];
+    int d;
+    void *fillp = NULL;
+    NC_DIM_INFO_T *dim = NULL;
+    char *name_to_use;
+    int retval;
+
+    assert(grp && grp->format_grp_info && var && var->format_var_info);
+
+    LOG((3, "%s:: name %s", __func__, var->hdr.name));
+
+    /* Get HDF5-specific group and var info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* Scalar or not, we need a creation property list. */
+    if ((plistid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+        BAIL(NC_EHDFERR);
+    if ((access_plistid = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Turn off object tracking times in HDF5. */
+    if (H5Pset_obj_track_times(plistid, 0) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Find the HDF5 type of the dataset. */
+    if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &typeid,
+                                     var->type_info->endianness)))
+        BAIL(retval);
+
+    /* Figure out what fill value to set, if any. */
+    if (var->no_fill)
+    {
+        /* Required to truly turn HDF5 fill values off */
+        if (H5Pset_fill_time(plistid, H5D_FILL_TIME_NEVER) < 0)
+            BAIL(NC_EHDFERR);
+    }
+    else
+    {
+        if ((retval = nc4_get_fill_value(grp->nc4_info, var, &fillp)))
+            BAIL(retval);
+
+        /* If there is a fill value, set it. */
+        if (fillp)
+        {
+            if (var->type_info->nc_type_class == NC_STRING)
+            {
+                if (H5Pset_fill_value(plistid, typeid, fillp) < 0)
+                    BAIL(NC_EHDFERR);
+            }
+            else
+            {
+                /* The fill value set in HDF5 must always be presented as
+                 * a native type, even if the endianness for this dataset
+                 * is non-native. HDF5 will translate the fill value to
+                 * the target endiannesss. */
+                hid_t fill_typeid = 0;
+
+                if ((retval = nc4_get_hdf_typeid(grp->nc4_info, var->type_info->hdr.id, &fill_typeid,
+                                                 NC_ENDIAN_NATIVE)))
+                    BAIL(retval);
+                if (H5Pset_fill_value(plistid, fill_typeid, fillp) < 0)
+                {
+                    if (H5Tclose(fill_typeid) < 0)
+                        BAIL(NC_EHDFERR);
+                    BAIL(NC_EHDFERR);
+                }
+                if (H5Tclose(fill_typeid) < 0)
+                    BAIL(NC_EHDFERR);
+            }
+        }
+    }
+
+    /* If the user wants to shuffle the data, set that up now. */
+    if (var->shuffle) {
+        if (H5Pset_shuffle(plistid) < 0)
+            BAIL(NC_EHDFERR);
+    }
+
+    /* If the user wants to deflate the data, set that up now. */
+    if (var->deflate) {
+        if (H5Pset_deflate(plistid, var->deflate_level) < 0)
+            BAIL(NC_EHDFERR);
+    } else if(var->filterid) {
+        /* Handle szip case here */
+        if(var->filterid == H5Z_FILTER_SZIP) {
+            int options_mask;
+            int bits_per_pixel;
+            if(var->nparams != 2)
+                BAIL(NC_EFILTER);
+            options_mask = (int)var->params[0];
+            bits_per_pixel = (int)var->params[1];
+            if(H5Pset_szip(plistid, options_mask, bits_per_pixel) < 0)
+                BAIL(NC_EFILTER);
+        } else {
+            herr_t code = H5Pset_filter(plistid, var->filterid, H5Z_FLAG_MANDATORY, var->nparams, var->params);
+            if(code < 0) {
+                BAIL(NC_EFILTER);
+            }
+        }
+    }
+
+    /* If the user wants to fletcher error correcton, set that up now. */
+    if (var->fletcher32)
+        if (H5Pset_fletcher32(plistid) < 0)
+            BAIL(NC_EHDFERR);
+
+    /* If ndims non-zero, get info for all dimensions. We look up the
+       dimids and get the len of each dimension. We need this to create
+       the space for the dataset. In netCDF a dimension length of zero
+       means an unlimited dimension. */
+    if (var->ndims)
+    {
+        int unlimdim = 0;
+
+        /* Check to see if any unlimited dimensions are used in this var. */
+        for (d = 0; d < var->ndims; d++) {
+            dim = var->dim[d];
+            assert(dim && dim->hdr.id == var->dimids[d]);
+            if (dim->unlimited)
+                unlimdim++;
+        }
+
+        /* If there are no unlimited dims, and no filters, and the user
+         * has not specified chunksizes, use contiguous variable for
+         * better performance. */
+        if (!var->shuffle && !var->deflate && !var->fletcher32 &&
+            (var->chunksizes == NULL || !var->chunksizes[0]) && !unlimdim)
+            var->contiguous = NC_TRUE;
+
+        /* Gather current & maximum dimension sizes, along with chunk sizes */
+        for (d = 0; d < var->ndims; d++)
+        {
+            dim = var->dim[d];
+            assert(dim && dim->hdr.id == var->dimids[d]);
+            dimsize[d] = dim->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : dim->len;
+            maxdimsize[d] = dim->unlimited ? H5S_UNLIMITED : (hsize_t)dim->len;
+            if (!var->contiguous) {
+                if (var->chunksizes[d])
+                    chunksize[d] = var->chunksizes[d];
+                else
+                {
+                    size_t type_size;
+                    if (var->type_info->nc_type_class == NC_STRING)
+                        type_size = sizeof(char *);
+                    else
+                        type_size = var->type_info->size;
+
+                    /* Unlimited dim always gets chunksize of 1. */
+                    if (dim->unlimited)
+                        chunksize[d] = 1;
+                    else
+                        chunksize[d] = pow((double)DEFAULT_CHUNK_SIZE/type_size,
+                                           1/(double)(var->ndims - unlimdim));
+
+                    /* If the chunksize is greater than the dim
+                     * length, make it the dim length. */
+                    if (!dim->unlimited && chunksize[d] > dim->len)
+                        chunksize[d] = dim->len;
+
+                    /* Remember the computed chunksize */
+                    var->chunksizes[d] = chunksize[d];
+                }
+            }
+        }
+
+        if (var->contiguous)
+        {
+            if (H5Pset_layout(plistid, H5D_CONTIGUOUS) < 0)
+                BAIL(NC_EHDFERR);
+        }
+        else
+        {
+            if (H5Pset_chunk(plistid, var->ndims, chunksize) < 0)
+                BAIL(NC_EHDFERR);
+        }
+
+        /* Create the dataspace. */
+        if ((spaceid = H5Screate_simple(var->ndims, dimsize, maxdimsize)) < 0)
+            BAIL(NC_EHDFERR);
+    }
+    else
+    {
+        if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
+            BAIL(NC_EHDFERR);
+    }
+
+    /* Turn on creation order tracking. */
+    if (H5Pset_attr_creation_order(plistid, H5P_CRT_ORDER_TRACKED|
+                                   H5P_CRT_ORDER_INDEXED) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Set per-var chunk cache, for chunked datasets. */
+    if (!var->contiguous && var->chunk_cache_size)
+        if (H5Pset_chunk_cache(access_plistid, var->chunk_cache_nelems,
+                               var->chunk_cache_size, var->chunk_cache_preemption) < 0)
+            BAIL(NC_EHDFERR);
+
+    /* At long last, create the dataset. */
+    name_to_use = var->hdf5_name ? var->hdf5_name : var->hdr.name;
+    LOG((4, "%s: about to H5Dcreate2 dataset %s of type 0x%x", __func__,
+         name_to_use, typeid));
+    if ((hdf5_var->hdf_datasetid = H5Dcreate2(hdf5_grp->hdf_grpid, name_to_use, typeid,
+                                              spaceid, H5P_DEFAULT, plistid, access_plistid)) < 0)
+        BAIL(NC_EHDFERR);
+    var->created = NC_TRUE;
+    var->is_new_var = NC_FALSE;
+
+    /* Always write the hidden coordinates attribute, which lists the
+     * dimids of this var. When present, this speeds opens. When no
+     * present, dimscale matching is used. */
+    if (var->ndims > 1)
+        if ((retval = write_coord_dimids(var)))
+            BAIL(retval);
+
+    /* If this is a dimscale, mark it as such in the HDF5 file. Also
+     * find the dimension info and store the dataset id of the dimscale
+     * dataset. */
+    if (var->dimscale)
+    {
+        if (H5DSset_scale(hdf5_var->hdf_datasetid, var->hdr.name) < 0)
+            BAIL(NC_EHDFERR);
+
+        /* If this is a multidimensional coordinate variable, write a
+         * coordinates attribute. */
+        /* if (var->ndims > 1) */
+        /*    if ((retval = write_coord_dimids(var))) */
+        /*       BAIL(retval); */
+
+        /* If desired, write the netCDF dimid. */
+        if (write_dimid)
+            if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid, var->dimids[0])))
+                BAIL(retval);
+    }
+
+    /* Write attributes for this var. */
+    if ((retval = write_attlist(var->att, var->hdr.id, grp)))
+        BAIL(retval);
+    var->attr_dirty = NC_FALSE;
+
+exit:
+    if (typeid > 0 && H5Tclose(typeid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (plistid > 0 && H5Pclose(plistid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (access_plistid > 0 && H5Pclose(access_plistid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (spaceid > 0 && H5Sclose(spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (fillp)
+    {
+        if (var->type_info->nc_type_class == NC_VLEN)
+            nc_free_vlen((nc_vlen_t *)fillp);
+        else if (var->type_info->nc_type_class == NC_STRING && *(char **)fillp)
+            free(*(char **)fillp);
+        free(fillp);
+    }
+
+    return retval;
+}
+
+/**
+ * @internal Adjust the chunk cache of a var for better
+ * performance.
+ *
+ * @param grp Pointer to group info struct.
+ * @param var Pointer to var info struct.
+ *
+ * @return NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+nc4_adjust_var_cache(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
+{
+    size_t chunk_size_bytes = 1;
+    int d;
+    int retval;
+
+    /* Nothing to be done. */
+    if (var->contiguous)
+        return NC_NOERR;
+#ifdef USE_PARALLEL4
+    return NC_NOERR;
+#endif
+
+    /* How many bytes in the chunk? */
+    for (d = 0; d < var->ndims; d++)
+        chunk_size_bytes *= var->chunksizes[d];
+    if (var->type_info->size)
+        chunk_size_bytes *= var->type_info->size;
+    else
+        chunk_size_bytes *= sizeof(char *);
+
+    /* If the chunk cache is too small, and the user has not changed
+     * the default value of the chunk cache size, then increase the
+     * size of the cache. */
+    if (var->chunk_cache_size == CHUNK_CACHE_SIZE)
+        if (chunk_size_bytes > var->chunk_cache_size)
+        {
+            var->chunk_cache_size = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE;
+            if (var->chunk_cache_size > MAX_DEFAULT_CACHE_SIZE)
+                var->chunk_cache_size = MAX_DEFAULT_CACHE_SIZE;
+            if ((retval = nc4_reopen_dataset(grp, var)))
+                return retval;
+        }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Create a HDF5 defined type from a NC_TYPE_INFO_T struct,
+ * and commit it to the file.
+ *
+ * @param grp Pointer to group info struct.
+ * @param type Pointer to type info struct.
+ *
+ * @return NC_NOERR No error.
+ * @return NC_EHDFERR HDF5 error.
+ * @return NC_ECHAR Conversions of NC_CHAR forbidden.
+ * @return NC_EVARMETA HDF5 returning error creating datatype.
+ * @return NC_EHDFERR HDF5 returning error.
+ * @return NC_EBADTYE Type not found.
+ * @author Ed Hartnett
+ */
+static int
+commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type)
+{
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    NC_HDF5_TYPE_INFO_T *hdf5_type;
+    hid_t base_hdf_typeid;
+    int retval;
+
+    assert(grp && grp->format_grp_info && type && type->format_type_info);
+
+    /* Get HDF5-specific group and type info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+    hdf5_type = (NC_HDF5_TYPE_INFO_T *)type->format_type_info;
+
+    /* Did we already record this type? */
+    if (type->committed)
+        return NC_NOERR;
+
+    /* Is this a compound type? */
+    if (type->nc_type_class == NC_COMPOUND)
+    {
+        NC_FIELD_INFO_T *field;
+        hid_t hdf_base_typeid, hdf_typeid;
+        int i;
+
+        if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0)
+            return NC_EHDFERR;
+        LOG((4, "creating compound type %s hdf_typeid 0x%x", type->hdr.name,
+             hdf5_type->hdf_typeid));
+
+        for(i=0;i<nclistlength(type->u.c.field);i++)
+        {
+            field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, i);
+            assert(field);
+            if ((retval = nc4_get_hdf_typeid(grp->nc4_info, field->nc_typeid,
+                                             &hdf_base_typeid, type->endianness)))
+                return retval;
+
+            /* If this is an array, create a special array type. */
+            if (field->ndims)
+            {
+                int d;
+                hsize_t dims[NC_MAX_VAR_DIMS];
+
+                for (d = 0; d < field->ndims; d++)
+                    dims[d] = field->dim_size[d];
+                if ((hdf_typeid = H5Tarray_create(hdf_base_typeid, field->ndims,
+                                                  dims, NULL)) < 0)
+                {
+                    if (H5Tclose(hdf_base_typeid) < 0)
+                        return NC_EHDFERR;
+                    return NC_EHDFERR;
+                }
+                if (H5Tclose(hdf_base_typeid) < 0)
+                    return NC_EHDFERR;
+            }
+            else
+                hdf_typeid = hdf_base_typeid;
+            LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->hdr.name,
+                 field->offset, hdf_typeid));
+            if (H5Tinsert(hdf5_type->hdf_typeid, field->hdr.name, field->offset,
+                          hdf_typeid) < 0)
+                return NC_EHDFERR;
+            if (H5Tclose(hdf_typeid) < 0)
+                return NC_EHDFERR;
+        }
+    }
+    else if (type->nc_type_class == NC_VLEN)
+    {
+        /* Find the HDF typeid of the base type of this vlen. */
+        if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.v.base_nc_typeid,
+                                         &base_hdf_typeid, type->endianness)))
+            return retval;
+
+        /* Create a vlen type. */
+        if ((hdf5_type->hdf_typeid = H5Tvlen_create(base_hdf_typeid)) < 0)
+            return NC_EHDFERR;
+    }
+    else if (type->nc_type_class == NC_OPAQUE)
+    {
+        /* Create the opaque type. */
+        if ((hdf5_type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0)
+            return NC_EHDFERR;
+    }
+    else if (type->nc_type_class == NC_ENUM)
+    {
+        NC_ENUM_MEMBER_INFO_T *enum_m;
+        int i;
+
+        if (nclistlength(type->u.e.enum_member) == 0)
+            return NC_EINVAL;
+
+        /* Find the HDF typeid of the base type of this enum. */
+        if ((retval = nc4_get_hdf_typeid(grp->nc4_info, type->u.e.base_nc_typeid,
+                                         &base_hdf_typeid, type->endianness)))
+            return retval;
+
+        /* Create an enum type. */
+        if ((hdf5_type->hdf_typeid =  H5Tenum_create(base_hdf_typeid)) < 0)
+            return NC_EHDFERR;
+
+        /* Add all the members to the HDF5 type. */
+        for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
+            enum_m = (NC_ENUM_MEMBER_INFO_T*)nclistget(type->u.e.enum_member,i);
+            if (H5Tenum_insert(hdf5_type->hdf_typeid, enum_m->name, enum_m->value) < 0)
+                return NC_EHDFERR;
+        }
+    }
+    else
+    {
+        LOG((0, "Unknown class: %d", type->nc_type_class));
+        return NC_EBADTYPE;
+    }
+
+    /* Commit the type. */
+    if (H5Tcommit(hdf5_grp->hdf_grpid, type->hdr.name, hdf5_type->hdf_typeid) < 0)
+        return NC_EHDFERR;
+    type->committed = NC_TRUE;
+    LOG((4, "just committed type %s, HDF typeid: 0x%x", type->hdr.name,
+         hdf5_type->hdf_typeid));
+
+    /* Later we will always use the native typeid. In this case, it is
+     * a copy of the same type pointed to by hdf_typeid, but it's
+     * easier to maintain a copy. */
+    if ((hdf5_type->native_hdf_typeid = H5Tget_native_type(hdf5_type->hdf_typeid,
+                                                           H5T_DIR_DEFAULT)) < 0)
+        return NC_EHDFERR;
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Write an attribute, with value 1, to indicate that strict
+ * NC3 rules apply to this file.
+ *
+ * @param hdf_grpid HDF5 group ID.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+static int
+write_nc3_strict_att(hid_t hdf_grpid)
+{
+    hid_t attid = 0, spaceid = 0;
+    int one = 1;
+    int retval = NC_NOERR;
+    htri_t attr_exists;
+
+    /* If the attribute already exists, call that a success and return
+     * NC_NOERR. */
+    if ((attr_exists = H5Aexists(hdf_grpid, NC3_STRICT_ATT_NAME)) < 0)
+        return NC_EHDFERR;
+    if (attr_exists)
+        return NC_NOERR;
+
+    /* Create the attribute to mark this as a file that needs to obey
+     * strict netcdf-3 rules. */
+    if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
+        BAIL(NC_EFILEMETA);
+    if ((attid = H5Acreate(hdf_grpid, NC3_STRICT_ATT_NAME,
+                           H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0)
+        BAIL(NC_EFILEMETA);
+    if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0)
+        BAIL(NC_EFILEMETA);
+
+exit:
+    if (spaceid > 0 && (H5Sclose(spaceid) < 0))
+        BAIL2(NC_EFILEMETA);
+    if (attid > 0 && (H5Aclose(attid) < 0))
+        BAIL2(NC_EFILEMETA);
+    return retval;
+}
+
+/**
+ * @internal Create a HDF5 group that is not the root group. HDF5
+ * properties are set in the group to ensure that objects and
+ * attributes are kept in creation order, instead of alphebetical
+ * order (the HDF5 default).
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @return NC_NOERR No error.
+ * @return NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+static int
+create_group(NC_GRP_INFO_T *grp)
+{
+    NC_HDF5_GRP_INFO_T *hdf5_grp, *parent_hdf5_grp;
+    hid_t gcpl_id = -1;
+    int retval = NC_NOERR;;
+
+    assert(grp && grp->format_grp_info && grp->parent &&
+           grp->parent->format_grp_info);
+
+    /* Get HDF5 specific group info for group and parent. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+    parent_hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->parent->format_grp_info;
+    assert(parent_hdf5_grp->hdf_grpid);
+
+    /* Create group, with link_creation_order set in the group
+     * creation property list. */
+    if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Set track_times to be FALSE. */
+    if (H5Pset_obj_track_times(gcpl_id, 0) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Tell HDF5 to keep track of objects in creation order. */
+    if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Tell HDF5 to keep track of attributes in creation order. */
+    if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Create the group. */
+    if ((hdf5_grp->hdf_grpid = H5Gcreate2(parent_hdf5_grp->hdf_grpid, grp->hdr.name,
+                                          H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0)
+        BAIL(NC_EHDFERR);
+
+exit:
+    if (gcpl_id > -1 && H5Pclose(gcpl_id) < 0)
+        BAIL2(NC_EHDFERR);
+    if (retval)
+        if (hdf5_grp->hdf_grpid > 0 && H5Gclose(hdf5_grp->hdf_grpid) < 0)
+            BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal After all the datasets of the file have been read, it's
+ * time to sort the wheat from the chaff. Which of the datasets are
+ * netCDF dimensions, and which are coordinate variables, and which
+ * are non-coordinate variables.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+static int
+attach_dimscales(NC_GRP_INFO_T *grp)
+{
+    NC_VAR_INFO_T *var;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    int d, v;
+
+    /* Attach dimension scales. */
+    for (v = 0; v < ncindexsize(grp->vars); v++)
+    {
+        /* Get pointer to var and HDF5-specific var info. */
+        var = (NC_VAR_INFO_T *)ncindexith(grp->vars, v);
+        assert(var && var->format_var_info);
+        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+        /* Scales themselves do not attach. But I really wish they
+         * would. */
+        if (var->dimscale)
+            continue;
+
+        /* Find the scale for each dimension, if any, and attach it. */
+        for (d = 0; d < var->ndims; d++)
+        {
+            /* Is there a dimscale for this dimension? */
+            if (var->dimscale_attached)
+            {
+                if (!var->dimscale_attached[d])
+                {
+                    hid_t dsid;  /* Dataset ID for dimension */
+                    assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
+                           var->dim[d]->format_dim_info);
+
+                    LOG((2, "%s: attaching scale for dimid %d to var %s",
+                         __func__, var->dimids[d], var->hdr.name));
+
+                    /* Find dataset ID for dimension */
+                    if (var->dim[d]->coord_var)
+                        dsid = ((NC_HDF5_VAR_INFO_T *)(var->dim[d]->coord_var->format_var_info))->hdf_datasetid;
+                    else
+                        dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
+                    assert(dsid > 0);
+
+                    /* Attach the scale. */
+                    if (H5DSattach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
+                        return NC_EHDFERR;
+                    var->dimscale_attached[d] = NC_TRUE;
+                }
+            }
+        }
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Does a variable exist?
+ *
+ * @param grpid HDF5 group ID.
+ * @param name Name of variable.
+ * @param exists Pointer that gets 1 of the variable exists, 0 otherwise.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+static int
+var_exists(hid_t grpid, char *name, nc_bool_t *exists)
+{
+    htri_t link_exists;
+
+    /* Reset the boolean */
+    *exists = NC_FALSE;
+
+    /* Check if the object name exists in the group */
+    if ((link_exists = H5Lexists(grpid, name, H5P_DEFAULT)) < 0)
+        return NC_EHDFERR;
+    if (link_exists)
+    {
+        H5G_stat_t statbuf;
+
+        /* Get info about the object */
+        if (H5Gget_objinfo(grpid, name, 1, &statbuf) < 0)
+            return NC_EHDFERR;
+
+        if (H5G_DATASET == statbuf.type)
+            *exists = NC_TRUE;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Convert a coordinate variable HDF5 dataset into one that
+ * is not a coordinate variable. This happens during renaming of vars
+ * and dims. This function removes the HDF5 NAME and CLASS attributes
+ * associated with dimension scales, and also the NC_DIMID_ATT_NAME
+ * attribute which may be present, and, if it does, holds the dimid of
+ * the coordinate variable.
+ *
+ * @param hdf_datasetid The HDF5 dataset ID of the coordinate variable
+ * dataset.
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EHDFERR HDF5 error.
+ * @author Ed Hartnett
+ */
+static int
+remove_coord_atts(hid_t hdf_datasetid)
+{
+    htri_t attr_exists;
+
+    /* If the variable dataset has an optional NC_DIMID_ATT_NAME
+     * attribute, delete it. */
+    if ((attr_exists = H5Aexists(hdf_datasetid, NC_DIMID_ATT_NAME)) < 0)
+        return NC_EHDFERR;
+    if (attr_exists)
+    {
+        if (H5Adelete(hdf_datasetid, NC_DIMID_ATT_NAME) < 0)
+            return NC_EHDFERR;
+    }
+
+    /* Remove the dimension scale 'CLASS' & 'NAME' attributes. */
+    if ((attr_exists = H5Aexists(hdf_datasetid,
+                                 HDF5_DIMSCALE_CLASS_ATT_NAME)) < 0)
+        return NC_EHDFERR;
+    if (attr_exists)
+    {
+        if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_CLASS_ATT_NAME) < 0)
+            return NC_EHDFERR;
+    }
+    if ((attr_exists = H5Aexists(hdf_datasetid,
+                                 HDF5_DIMSCALE_NAME_ATT_NAME)) < 0)
+        return NC_EHDFERR;
+    if (attr_exists)
+    {
+        if (H5Adelete(hdf_datasetid, HDF5_DIMSCALE_NAME_ATT_NAME) < 0)
+            return NC_EHDFERR;
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal This function writes a variable. The principle difficulty
+ * comes from the possibility that this is a coordinate variable, and
+ * was already written to the file as a dimension-only dimscale. If
+ * this occurs, then it must be deleted and recreated.
+ *
+ * @param var Pointer to variable info struct.
+ * @param grp Pointer to group info struct.
+ * @param write_dimid
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett, Quincey Koziol
+ */
+static int
+write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
+{
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    NC_HDF5_VAR_INFO_T *hdf5_var;
+    nc_bool_t replace_existing_var = NC_FALSE;
+    int retval;
+
+    assert(var && var->format_var_info && grp && grp->format_grp_info);
+
+    LOG((4, "%s: writing var %s", __func__, var->hdr.name));
+
+    /* Get HDF5-specific group and var info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+    hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+    /* If the variable has already been created & the fill value changed,
+     * indicate that the existing variable should be replaced. */
+    if (var->created && var->fill_val_changed)
+    {
+        replace_existing_var = NC_TRUE;
+        var->fill_val_changed = NC_FALSE;
+        /* If the variable is going to be replaced, we need to flag any
+           other attributes associated with the variable as 'dirty', or
+           else *only* the fill value attribute will be copied over and
+           the rest will be lost.  See
+           https://github.com/Unidata/netcdf-c/issues/239 */
+        flag_atts_dirty(var->att);
+    }
+
+    /* Is this a coordinate var that has already been created in
+     * the HDF5 file as a dimscale dataset? Check for dims with the
+     * same name in this group. If there is one, check to see if
+     * this object exists in the HDF group. */
+    if (var->became_coord_var)
+    {
+        if ((NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name))
+        {
+            nc_bool_t exists;
+
+            if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
+                return retval;
+            if (exists)
+            {
+                /* Indicate that the variable already exists, and should
+                 * be replaced. */
+                replace_existing_var = NC_TRUE;
+                flag_atts_dirty(var->att);
+            }
+        }
+    }
+
+    /* Check dims if the variable will be replaced, so that the
+     * dimensions will be de-attached and re-attached correctly. */
+    if (replace_existing_var)
+    {
+        NC_DIM_INFO_T *d1;
+
+        /* Is there a dim with this var's name? */
+        if ((d1 = (NC_DIM_INFO_T *)ncindexlookup(grp->dim, var->hdr.name)))
+        {
+            nc_bool_t exists;
+            assert(d1->format_dim_info && d1->hdr.name);
+
+            if ((retval = var_exists(hdf5_grp->hdf_grpid, var->hdr.name, &exists)))
+                return retval;
+            if (exists)
+            {
+                hid_t dsid;
+
+                /* Find dataset ID for dimension */
+                if (d1->coord_var)
+                    dsid = ((NC_HDF5_VAR_INFO_T *)d1->coord_var->format_var_info)->hdf_datasetid;
+                else
+                    dsid = ((NC_HDF5_DIM_INFO_T *)d1->format_dim_info)->hdf_dimscaleid;
+                assert(dsid > 0);
+
+                /* If we're replacing an existing dimscale dataset, go to
+                 * every var in the file and detach this dimension scale,
+                 * because we have to delete it. */
+                if ((retval = rec_detach_scales(grp->nc4_info->root_grp,
+                                                var->dimids[0], dsid)))
+                    return retval;
+            }
+        }
+    }
+
+    /* If this is not a dimension scale, remove any attached scales,
+     * and delete dimscale attributes from the var. */
+    if (var->was_coord_var && var->dimscale_attached)
+    {
+        int d;
+
+        /* If the variable already exists in the file, Remove any dimension scale
+         * attributes from it, if they exist. */
+        if (var->created)
+            if ((retval = remove_coord_atts(hdf5_var->hdf_datasetid)))
+                return retval;
+
+        /* If this is a regular var, detach all its dim scales. */
+        for (d = 0; d < var->ndims; d++)
+        {
+            if (var->dimscale_attached[d])
+            {
+                hid_t dsid;  /* Dataset ID for dimension */
+                assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d] &&
+                       var->dim[d]->format_dim_info);
+
+                /* Find dataset ID for dimension */
+                if (var->dim[d]->coord_var)
+                    dsid = ((NC_HDF5_VAR_INFO_T *)var->dim[d]->coord_var->format_var_info)->hdf_datasetid;
+                else
+                    dsid = ((NC_HDF5_DIM_INFO_T *)var->dim[d]->format_dim_info)->hdf_dimscaleid;
+                assert(dsid > 0);
+
+                /* Detach this dim scale. */
+                if (H5DSdetach_scale(hdf5_var->hdf_datasetid, dsid, d) < 0)
+                    return NC_EHDFERR;
+                var->dimscale_attached[d] = NC_FALSE;
+            }
+        }
+    }
+
+    /* Delete the HDF5 dataset that is to be replaced. */
+    if (replace_existing_var)
+    {
+        /* Free the HDF5 dataset id. */
+        if (hdf5_var->hdf_datasetid && H5Dclose(hdf5_var->hdf_datasetid) < 0)
+            return NC_EHDFERR;
+        hdf5_var->hdf_datasetid = 0;
+
+        /* Now delete the variable. */
+        if (H5Gunlink(hdf5_grp->hdf_grpid, var->hdr.name) < 0)
+            return NC_EDIMMETA;
+    }
+
+    /* Create the dataset. */
+    if (var->is_new_var || replace_existing_var)
+    {
+        if ((retval = var_create_dataset(grp, var, write_dimid)))
+            return retval;
+    }
+    else
+    {
+        if (write_dimid && var->ndims)
+            if ((retval = write_netcdf4_dimid(hdf5_var->hdf_datasetid,
+                                              var->dimids[0])))
+                return retval;
+    }
+
+    if (replace_existing_var)
+    {
+        /* If this is a dimension scale, reattach the scale everywhere it
+         * is used. (Recall that netCDF dimscales are always 1-D). */
+        if(var->dimscale)
+        {
+            if ((retval = rec_reattach_scales(grp->nc4_info->root_grp,
+                                              var->dimids[0], hdf5_var->hdf_datasetid)))
+                return retval;
+        }
+        /* If it's not a dimension scale, clear the dimscale attached flags,
+         * so the dimensions are re-attached. */
+        else
+        {
+            if (var->dimscale_attached)
+                memset(var->dimscale_attached, 0, sizeof(nc_bool_t) * var->ndims);
+        }
+    }
+
+    /* Clear coord. var state transition flags */
+    var->was_coord_var = NC_FALSE;
+    var->became_coord_var = NC_FALSE;
+
+    /* Now check the attributes for this var. */
+    if (var->attr_dirty)
+    {
+        /* Write attributes for this var. */
+        if ((retval = write_attlist(var->att, var->hdr.id, grp)))
+            return retval;
+        var->attr_dirty = NC_FALSE;
+    }
+
+    return NC_NOERR;
+}
+
+/**
+ * @internal Write a HDF5 dataset which is a dimension without a
+ * coordinate variable. This is a special 1-D dataset.
+ *
+ * @param dim Pointer to dim info struct.
+ * @param grp Pointer to group info struct.
+ * @param write_dimid
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EPERM Read-only file.
+ * @returns ::NC_EHDFERR HDF5 returned error.
+ * @author Ed Hartnett
+ */
+int
+nc4_create_dim_wo_var(NC_DIM_INFO_T *dim)
+{
+    NC_HDF5_DIM_INFO_T *hdf5_dim;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    hid_t spaceid = -1, create_propid = -1;
+    hsize_t dims[1], max_dims[1], chunk_dims[1] = {1};
+    char dimscale_wo_var[NC_MAX_NAME];
+    int retval = NC_NOERR;
+
+    LOG((4, "%s: creating dim %s", __func__, dim->hdr.name));
+
+    /* Sanity check */
+    assert(!dim->coord_var);
+
+    /* Get HDF5-specific dim and group info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)dim->container->format_grp_info;
+    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+    /* Create a property list. */
+    if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Turn off recording of times associated with this object. */
+    if (H5Pset_obj_track_times(create_propid, 0) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Set size of dataset to size of dimension. */
+    dims[0] = dim->len;
+    max_dims[0] = dim->len;
+
+    /* If this dimension scale is unlimited (i.e. it's an unlimited
+     * dimension), then set up chunking, with a chunksize of 1. */
+    if (dim->unlimited)
+    {
+        max_dims[0] = H5S_UNLIMITED;
+        if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
+            BAIL(NC_EHDFERR);
+    }
+
+    /* Set up space. */
+    if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Turn on creation-order tracking. */
+    if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
+                                   H5P_CRT_ORDER_INDEXED) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Create the dataset that will be the dimension scale. */
+    LOG((4, "%s: about to H5Dcreate1 a dimscale dataset %s", __func__,
+         dim->hdr.name));
+    if ((hdf5_dim->hdf_dimscaleid = H5Dcreate2(hdf5_grp->hdf_grpid, dim->hdr.name,
+                                               H5T_IEEE_F32BE, spaceid,
+                                               H5P_DEFAULT, create_propid,
+                                               H5P_DEFAULT)) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Indicate that this is a scale. Also indicate that not
+     * be shown to the user as a variable. It is hidden. It is
+     * a DIM WITHOUT A VARIABLE! */
+    sprintf(dimscale_wo_var, "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
+    if (H5DSset_scale(hdf5_dim->hdf_dimscaleid, dimscale_wo_var) < 0)
+        BAIL(NC_EHDFERR);
+
+    /* Since this dimension was created out of order, we cannot rely on
+     * it getting the correct dimid on file open. We must assign it
+     * explicitly. */
+    if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
+        BAIL(retval);
+
+exit:
+    if (spaceid > 0 && H5Sclose(spaceid) < 0)
+        BAIL2(NC_EHDFERR);
+    if (create_propid > 0 && H5Pclose(create_propid) < 0)
+        BAIL2(NC_EHDFERR);
+    return retval;
+}
+
+/**
+ * @internal Write a dimension.
+ *
+ * @param dim Pointer to dim info struct.
+ * @param grp Pointer to group info struct.
+ * @param write_dimid
+ *
+ * @returns ::NC_NOERR No error.
+ * @returns ::NC_EPERM Read-only file.
+ * @returns ::NC_EHDFERR HDF5 returned error.
+ * @author Ed Hartnett
+ */
+static int
+write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, nc_bool_t write_dimid)
+{
+    NC_HDF5_DIM_INFO_T *hdf5_dim;
+    int retval = NC_NOERR;
+
+    assert(dim && dim->format_dim_info && grp && grp->format_grp_info);
+
+    /* Get HDF5-specific dim and group info. */
+    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+    /* If there's no dimscale dataset for this dim, create one,
+     * and mark that it should be hidden from netCDF as a
+     * variable. (That is, it should appear as a dimension
+     * without an associated variable.) */
+    if (!hdf5_dim->hdf_dimscaleid)
+        if ((retval = nc4_create_dim_wo_var(dim)))
+            BAIL(retval);
+
+    /* Did we extend an unlimited dimension? */
+    if (dim->extended)
+    {
+        NC_VAR_INFO_T *v1 = NULL;
+
+        assert(dim->unlimited);
+        /* If this is a dimension without a variable, then update
+         * the secret length information at the end of the NAME
+         * attribute. */
+        v1 = (NC_VAR_INFO_T *)ncindexlookup(grp->vars, dim->hdr.name);
+        if (v1)
+        {
+            NC_HDF5_VAR_INFO_T *hdf5_v1;
+            hsize_t *new_size;
+            int d1;
+
+            hdf5_v1 = (NC_HDF5_VAR_INFO_T *)v1->format_var_info;
+
+            /* Extend the dimension scale dataset to reflect the new
+             * length of the dimension. */
+            if (!(new_size = malloc(v1->ndims * sizeof(hsize_t))))
+                BAIL(NC_ENOMEM);
+            for (d1 = 0; d1 < v1->ndims; d1++)
+            {
+                assert(v1->dim[d1] && v1->dim[d1]->hdr.id == v1->dimids[d1]);
+                new_size[d1] = v1->dim[d1]->len;
+            }
+            if (H5Dset_extent(hdf5_v1->hdf_datasetid, new_size) < 0)
+                BAIL(NC_EHDFERR);
+            free(new_size);
+        }
+    }
+
+    /* If desired, write the secret dimid. This will be used instead of
+     * the dimid that the dimension would otherwise receive based on
+     * creation order. This can be necessary when dims and their
+     * coordinate variables were created in different order. */
+    if (write_dimid && hdf5_dim->hdf_dimscaleid)
+        if ((retval = write_netcdf4_dimid(hdf5_dim->hdf_dimscaleid, dim->hdr.id)))
+            BAIL(retval);
+
+exit:
+
+    return retval;
+}
+
+/**
+ * @internal Recursively write all the metadata in a group. Groups and
+ * types have all already been written. Propagate bad cooordinate
+ * order to subgroups, if detected.
+ *
+ * @param grp Pointer to group info struct.
+ * @param bad_coord_order 1 if there is a bad coordinate order.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+int
+nc4_rec_write_metadata(NC_GRP_INFO_T *grp, nc_bool_t bad_coord_order)
+{
+    NC_DIM_INFO_T *dim = NULL;
+    NC_VAR_INFO_T *var = NULL;
+    NC_GRP_INFO_T *child_grp = NULL;
+    int coord_varid = -1;
+    int var_index = 0;
+    int dim_index = 0;
+    int retval;
+    int i;
+
+    assert(grp && grp->hdr.name &&
+           ((NC_HDF5_GRP_INFO_T *)(grp->format_grp_info))->hdf_grpid);
+    LOG((3, "%s: grp->hdr.name %s, bad_coord_order %d", __func__, grp->hdr.name,
+         bad_coord_order));
+
+    /* Write global attributes for this group. */
+    if ((retval = write_attlist(grp->att, NC_GLOBAL, grp)))
+        return retval;
+
+    /* Set the pointers to the beginning of the list of dims & vars in this
+     * group. */
+    dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, dim_index);
+    var = (NC_VAR_INFO_T *)ncindexith(grp->vars, var_index);
+
+    /* Because of HDF5 ordering the dims and vars have to be stored in
+     * this way to ensure that the dims and coordinate vars come out in
+     * the correct order. */
+    while (dim || var)
+    {
+        nc_bool_t found_coord, wrote_coord;
+
+        /* Write non-coord dims in order, stopping at the first one that
+         * has an associated coord var. */
+        for (found_coord = NC_FALSE; dim && !found_coord; )
+        {
+            if (!dim->coord_var)
+            {
+                if ((retval = write_dim(dim, grp, bad_coord_order)))
+                    return retval;
+            }
+            else
+            {
+                coord_varid = dim->coord_var->hdr.id;
+                found_coord = NC_TRUE;
+            }
+            dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, ++dim_index);
+        }
+
+        /* Write each var. When we get to the coord var we are waiting
+         * for (if any), then we break after writing it. */
+        for (wrote_coord = NC_FALSE; var && !wrote_coord; )
+        {
+            if ((retval = write_var(var, grp, bad_coord_order)))
+                return retval;
+            if (found_coord && var->hdr.id == coord_varid)
+                wrote_coord = NC_TRUE;
+            var = (NC_VAR_INFO_T *)ncindexith(grp->vars, ++var_index);
+        }
+    } /* end while */
+
+    /* Attach dimscales to vars in this group. */
+    if ((retval = attach_dimscales(grp)))
+        return retval;
+
+    /* If there are any child groups, write their metadata. */
+    for (i = 0; i < ncindexsize(grp->children); i++)
+    {
+        child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i);
+        assert(child_grp);
+        if ((retval = nc4_rec_write_metadata(child_grp, bad_coord_order)))
+            return retval;
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal Recursively write all groups and types.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @author Ed Hartnett
+ */
+int
+nc4_rec_write_groups_types(NC_GRP_INFO_T *grp)
+{
+    NC_GRP_INFO_T *child_grp;
+    NC_HDF5_GRP_INFO_T *hdf5_grp;
+    NC_TYPE_INFO_T *type;
+    int retval;
+    int i;
+
+    assert(grp && grp->hdr.name && grp->format_grp_info);
+    LOG((3, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
+
+    /* Get HDF5-specific group info. */
+    hdf5_grp = (NC_HDF5_GRP_INFO_T *)grp->format_grp_info;
+
+    /* Create the group in the HDF5 file if it doesn't exist. */
+    if (!hdf5_grp->hdf_grpid)
+        if ((retval = create_group(grp)))
+            return retval;
+
+    /* If this is the root group of a file with strict NC3 rules, write
+     * an attribute. But don't leave the attribute open. */
+    if (!grp->parent && (grp->nc4_info->cmode & NC_CLASSIC_MODEL))
+        if ((retval = write_nc3_strict_att(hdf5_grp->hdf_grpid)))
+            return retval;
+
+    /* If there are any user-defined types, write them now. */
+    for(i=0;i<ncindexsize(grp->type);i++) {
+        type = (NC_TYPE_INFO_T *)ncindexith(grp->type, i);
+        assert(type);
+        if ((retval = commit_type(grp, type)))
+            return retval;
+    }
+
+    /* If there are any child groups, write their groups and types. */
+    for(i=0;i<ncindexsize(grp->children);i++) {
+        if((child_grp = (NC_GRP_INFO_T*)ncindexith(grp->children,i)) == NULL) continue;
+        if ((retval = nc4_rec_write_groups_types(child_grp)))
+            return retval;
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal In our first pass through the data, we may have
+ * encountered variables before encountering their dimscales, so go
+ * through the vars in this file and make sure we've got a dimid for
+ * each.
+ *
+ * @param grp Pointer to group info struct.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned an error.
+ * @returns NC_ENOMEM Out of memory.
+ * @author Ed Hartnett
+ */
+int
+nc4_rec_match_dimscales(NC_GRP_INFO_T *grp)
+{
+    NC_GRP_INFO_T *g;
+    NC_VAR_INFO_T *var;
+    NC_DIM_INFO_T *dim;
+    int retval = NC_NOERR;
+    int i;
+
+    assert(grp && grp->hdr.name);
+    LOG((4, "%s: grp->hdr.name %s", __func__, grp->hdr.name));
+
+    /* Perform var dimscale match for child groups. */
+    for (i = 0; i < ncindexsize(grp->children); i++)
+    {
+        g = (NC_GRP_INFO_T*)ncindexith(grp->children, i);
+        assert(g);
+        if ((retval = nc4_rec_match_dimscales(g)))
+            return retval;
+    }
+
+    /* Check all the vars in this group. If they have dimscale info,
+     * try and find a dimension for them. */
+    for (i = 0; i < ncindexsize(grp->vars); i++)
+    {
+        NC_HDF5_VAR_INFO_T *hdf5_var;
+        int ndims;
+        int d;
+
+        /* Get pointer to var and to the HDF5-specific var info. */
+        var = (NC_VAR_INFO_T*)ncindexith(grp->vars, i);
+        assert(var && var->format_var_info);
+        hdf5_var = (NC_HDF5_VAR_INFO_T *)var->format_var_info;
+
+        /* Check all vars and see if dim[i] != NULL if dimids[i] valid. */
+        /* This loop is very odd. Under normal circumstances, var->dimid[d] is zero
+           (from the initial calloc) which is a legitimate dimid. The code does not
+           distinquish this case from the dimscale case where the id might actually
+           be defined.
+           The original nc4_find_dim searched up the group tree looking for the given
+           dimid in one of the dim lists associated with each ancestor group.
+           I changed nc4_fnd_dim to use the dimid directly using h5->alldims.
+           However, here that is incorrect because it will find the dimid 0 always
+           (if any dimensions were defined). Except that when dimscale dimids have
+           been defined, one or more of the values in var->dimids will have a
+           legitimate value.
+           The solution I choose is to modify nc4_var_list_add to initialize dimids to
+           illegal values (-1). This is another example of the problems with dimscales.
+        */
+        ndims = var->ndims;
+        for (d = 0; d < ndims; d++)
+        {
+            if (var->dim[d] == NULL) {
+                nc4_find_dim(grp, var->dimids[d], &var->dim[d], NULL);
+            }
+            /*       assert(var->dim[d] && var->dim[d]->hdr.id == var->dimids[d]); */
+        }
+
+        /* Skip dimension scale variables */
+        if (!var->dimscale)
+        {
+            int d;
+            int j;
+
+            /* Are there dimscales for this variable? */
+            if (hdf5_var->dimscale_hdf5_objids)
+            {
+                for (d = 0; d < var->ndims; d++)
+                {
+                    nc_bool_t finished = NC_FALSE;
+                    LOG((5, "%s: var %s has dimscale info...", __func__, var->hdr.name));
+
+                    /* Check this and parent groups. */
+                    for (g = grp; g && !finished; g = g->parent)
+                    {
+                        /* Check all dims in this group. */
+                        for (j = 0; j < ncindexsize(g->dim); j++)
+                        {
+                            /* Get the HDF5 specific dim info. */
+                            NC_HDF5_DIM_INFO_T *hdf5_dim;
+                            dim = (NC_DIM_INFO_T *)ncindexith(g->dim, j);
+                            assert(dim && dim->format_dim_info);
+                            hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
+
+                            /* Check for exact match of fileno/objid arrays
+                             * to find identical objects in HDF5 file. */
+                            if (hdf5_var->dimscale_hdf5_objids[d].fileno[0] == hdf5_dim->hdf5_objid.fileno[0] &&
+                                hdf5_var->dimscale_hdf5_objids[d].objno[0] == hdf5_dim->hdf5_objid.objno[0] &&
+                                hdf5_var->dimscale_hdf5_objids[d].fileno[1] == hdf5_dim->hdf5_objid.fileno[1] &&
+                                hdf5_var->dimscale_hdf5_objids[d].objno[1] == hdf5_dim->hdf5_objid.objno[1])
+                            {
+                                LOG((4, "%s: for dimension %d, found dim %s", __func__,
+                                     d, dim->hdr.name));
+                                var->dimids[d] = dim->hdr.id;
+                                var->dim[d] = dim;
+                                finished = NC_TRUE;
+                                break;
+                            }
+                        } /* next dim */
+                    } /* next grp */
+                    LOG((5, "%s: dimid for this dimscale is %d", __func__,
+                         var->type_info->hdr.id));
+                } /* next var->dim */
+            }
+            /* No dimscales for this var! Invent phony dimensions. */
+            else
+            {
+                hid_t spaceid = 0;
+                hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
+                int dataset_ndims;
+
+                /* Find the space information for this dimension. */
+                if ((spaceid = H5Dget_space(hdf5_var->hdf_datasetid)) < 0)
+                    return NC_EHDFERR;
+
+                /* Get the len of each dim in the space. */
+                if (var->ndims)
+                {
+                    if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
+                        return NC_ENOMEM;
+                    if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
+                    {
+                        free(h5dimlen);
+                        return NC_ENOMEM;
+                    }
+                    if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen,
+                                                                   h5dimlenmax)) < 0) {
+                        free(h5dimlenmax);
+                        free(h5dimlen);
+                        return NC_EHDFERR;
+                    }
+                    if (dataset_ndims != var->ndims) {
+                        free(h5dimlenmax);
+                        free(h5dimlen);
+                        return NC_EHDFERR;
+                    }
+                }
+                else
+                {
+                    /* Make sure it's scalar. */
+                    if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR)
+                        return NC_EHDFERR;
+                }
+
+                /* Release the space object. */
+                if (H5Sclose(spaceid) < 0) {
+                    free(h5dimlen);
+                    free(h5dimlenmax);
+                    return NC_EHDFERR;
+                }
+
+                /* Create a phony dimension for each dimension in the
+                 * dataset, unless there already is one the correct
+                 * size. */
+                for (d = 0; d < var->ndims; d++)
+                {
+                    int k;
+                    int match;
+                    /* Is there already a phony dimension of the correct size? */
+                    for(match=-1,k=0;k<ncindexsize(grp->dim);k++) {
+                        if((dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,k)) == NULL) continue;
+                        if ((dim->len == h5dimlen[d]) &&
+                            ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
+                             (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
+                        {match = k; break;}
+                    }
+
+                    /* Didn't find a phony dim? Then create one. */
+                    if (match < 0)
+                    {
+                        char phony_dim_name[NC_MAX_NAME + 1];
+                        sprintf(phony_dim_name, "phony_dim_%d", grp->nc4_info->next_dimid);
+                        LOG((3, "%s: creating phony dim for var %s", __func__, var->hdr.name));
+                        if ((retval = nc4_dim_list_add(grp, phony_dim_name, h5dimlen[d], -1, &dim)))
+                        {
+                            free(h5dimlenmax);
+                            free(h5dimlen);
+                            return retval;
+                        }
+                        /* Create struct for HDF5-specific dim info. */
+                        if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
+                            return NC_ENOMEM;
+                        if (h5dimlenmax[d] == H5S_UNLIMITED)
+                            dim->unlimited = NC_TRUE;
+                    }
+
+                    /* The variable must remember the dimid. */
+                    var->dimids[d] = dim->hdr.id;
+                    var->dim[d] = dim;
+                } /* next dim */
+
+                /* Free the memory we malloced. */
+                free(h5dimlen);
+                free(h5dimlenmax);
+            }
+        }
+    }
+
+    return retval;
+}
+
+/**
+ * @internal Get the class of a type
+ *
+ * @param h5 Pointer to the HDF5 file info struct.
+ * @param xtype NetCDF type ID.
+ * @param type_class Pointer that gets class of type, NC_INT,
+ * NC_FLOAT, NC_CHAR, or NC_STRING, NC_ENUM, NC_VLEN, NC_COMPOUND, or
+ * NC_OPAQUE.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Ed Hartnett
+ */
+int
+nc4_get_typeclass(const NC_FILE_INFO_T *h5, nc_type xtype, int *type_class)
+{
+    int retval = NC_NOERR;
+
+    LOG((4, "%s xtype: %d", __func__, xtype));
+    assert(type_class);
+
+    /* If this is an atomic type, the answer is easy. */
+    if (xtype <= NC_STRING)
+    {
+        switch (xtype)
+        {
+        case NC_BYTE:
+        case NC_UBYTE:
+        case NC_SHORT:
+        case NC_USHORT:
+        case NC_INT:
+        case NC_UINT:
+        case NC_INT64:
+        case NC_UINT64:
+            /* NC_INT is class used for all integral types */
+            *type_class = NC_INT;
+            break;
+
+        case NC_FLOAT:
+        case NC_DOUBLE:
+            /* NC_FLOAT is class used for all floating-point types */
+            *type_class = NC_FLOAT;
+            break;
+
+        case NC_CHAR:
+            *type_class = NC_CHAR;
+            break;
+
+        case NC_STRING:
+            *type_class = NC_STRING;
+            break;
+
+        default:
+            BAIL(NC_EBADTYPE);
+        }
+    }
+    else
+    {
+        NC_TYPE_INFO_T *type;
+
+        /* See if it's a used-defined type */
+        if ((retval = nc4_find_type(h5, xtype, &type)))
+            BAIL(retval);
+        if (!type)
+            BAIL(NC_EBADTYPE);
+
+        *type_class = type->nc_type_class;
+    }
+
+exit:
+    return retval;
+}
+
+/**
+ * @internal Report information about an open HDF5 object. This is
+ * called on any still-open objects when a HDF5 file close is
+ * attempted.
+ *
+ * @param uselog If true, send output to LOG not stderr.
+ * @param id HDF5 ID of open object.
+ * @param type Type of HDF5 object, file, dataset, etc.
+ *
+ * @author Dennis Heimbigner
+ */
+void
+reportobject(int uselog, hid_t id, unsigned int type)
+{
+    char name[NC_HDF5_MAX_NAME];
+    ssize_t len;
+    const char* typename = NULL;
+    long long printid = (long long)id;
+
+    len = H5Iget_name(id, name, NC_HDF5_MAX_NAME);
+    if(len < 0) return;
+    name[len] = '\0';
+
+    switch (type) {
+    case H5F_OBJ_FILE: typename = "File"; break;
+    case H5F_OBJ_DATASET: typename = "Dataset"; break;
+    case H5F_OBJ_GROUP: typename = "Group"; break;
+    case H5F_OBJ_DATATYPE: typename = "Datatype"; break;
+    case H5F_OBJ_ATTR:
+        typename = "Attribute";
+        len = H5Aget_name(id, NC_HDF5_MAX_NAME, name);
+        if(len < 0) len = 0;
+        name[len] = '\0';
+        break;
+    default: typename = "<unknown>"; break;
+    }
+#ifdef LOGGING
+    if(uselog) {
+        LOG((0,"Type = %s(%lld) name='%s'",typename,printid,name));
+    } else
+#endif
+    {
+        fprintf(stderr,"Type = %s(%lld) name='%s'",typename,printid,name);
+    }
+
+}
+
+/**
+ * @internal
+ *
+ * @param uselog
+ * @param fid HDF5 ID.
+ * @param ntypes Number of types.
+ * @param otypes Pointer that gets number of open types.
+ *
+ * @author Dennis Heimbigner
+ */
+static void
+reportopenobjectsT(int uselog, hid_t fid, int ntypes, unsigned int* otypes)
+{
+    int t,i;
+    ssize_t ocount;
+    size_t maxobjs = -1;
+    hid_t* idlist = NULL;
+
+    /* Always report somehow */
+#ifdef LOGGING
+    if(uselog)
+        LOG((0,"\nReport: open objects on %lld",(long long)fid));
+    else
+#endif
+        fprintf(stdout,"\nReport: open objects on %lld\n",(long long)fid);
+    maxobjs = H5Fget_obj_count(fid,H5F_OBJ_ALL);
+    if(idlist != NULL) free(idlist);
+    idlist = (hid_t*)malloc(sizeof(hid_t)*maxobjs);
+    for(t=0;t<ntypes;t++) {
+        unsigned int ot = otypes[t];
+        ocount = H5Fget_obj_ids(fid,ot,maxobjs,idlist);
+        for(i=0;i<ocount;i++) {
+            hid_t o = idlist[i];
+            reportobject(uselog,o,ot);
+        }
+    }
+    if(idlist != NULL) free(idlist);
+}
+
+/**
+ * @internal Report open objects.
+ *
+ * @param uselog
+ * @param fid HDF5 file ID.
+ *
+ * @author Dennit Heimbigner
+ */
+void
+reportopenobjects(int uselog, hid_t fid)
+{
+    unsigned int OTYPES[5] = {H5F_OBJ_FILE, H5F_OBJ_DATASET, H5F_OBJ_GROUP,
+                              H5F_OBJ_DATATYPE, H5F_OBJ_ATTR};
+
+    reportopenobjectsT(uselog, fid ,5, OTYPES);
+}
+
+/**
+ * @internal Report open objects given a pointer to NC_FILE_INFO_T object
+ *
+ * @param h5 file object
+ *
+ * @author Dennis Heimbigner
+ */
+void
+showopenobjects5(NC_FILE_INFO_T* h5)
+{
+    NC_HDF5_FILE_INFO_T *hdf5_info;
+
+    assert(h5 && h5->format_file_info);
+    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
+
+    fprintf(stderr,"===== begin showopenobjects =====\n");
+    reportopenobjects(0,hdf5_info->hdfid);
+    fprintf(stderr,"===== end showopenobjects =====\n");
+    fflush(stderr);
+}
+
+/**
+ * @internal Report open objects given an ncid
+ * Defined to support user or gdb level call.
+ *
+ * @param ncid file id
+ *
+ * @author Dennis Heimbigner
+ */
+void
+showopenobjects(int ncid)
+{
+    NC_FILE_INFO_T* h5 = NULL;
+
+    /* Find our metadata for this file. */
+    if (nc4_find_nc_grp_h5(ncid, NULL, NULL, &h5) != NC_NOERR)
+        fprintf(stderr,"failed\n");
+    else
+        showopenobjects5(h5);
+    fflush(stderr);
+}
+
+/**
+ * @internal Get HDF5 library version.
+ *
+ * @param major Pointer that gets major version number.
+ * @param minor Pointer that gets minor version number.
+ * @param release Pointer that gets release version number.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned error.
+ * @author Dennis Heimbigner
+ */
+int
+NC4_hdf5get_libversion(unsigned* major,unsigned* minor,unsigned* release)
+{
+    if(H5get_libversion(major,minor,release) < 0)
+        return NC_EHDFERR;
+    return NC_NOERR;
+}
+
+/**
+ * @internal Get HDF5 superblock version.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ * @param idp Pointer that gets superblock version.
+ *
+ * @returns NC_NOERR No error.
+ * @returns NC_EHDFERR HDF5 returned error.
+ * @author Dennis Heimbigner
+ */
+int
+NC4_hdf5get_superblock(struct NC_FILE_INFO* h5, int* idp)
+{
+    NC_HDF5_FILE_INFO_T *hdf5_info;
+    int stat = NC_NOERR;
+    unsigned super;
+    hid_t plist = -1;
+
+    assert(h5 && h5->format_file_info);
+    hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
+
+    if((plist = H5Fget_create_plist(hdf5_info->hdfid)) < 0)
+    {stat = NC_EHDFERR; goto done;}
+    if(H5Pget_version(plist, &super, NULL, NULL, NULL) < 0)
+    {stat = NC_EHDFERR; goto done;}
+    if(idp) *idp = (int)super;
+done:
+    if(plist >= 0) H5Pclose(plist);
+    return stat;
+}
+
+static int NC4_get_strict_att(NC_FILE_INFO_T*);
+static int NC4_walk(hid_t, int*);
+
+/**
+ * @internal Determine whether file is netCDF-4.
+ *
+ * We define a file as being from netcdf-4 if any of the following
+ * are true:
+ * 1. NCPROPS attribute exists in root group
+ * 2. NC3_STRICT_ATT_NAME exists in root group
+ * 3. any of NC_ATT_REFERENCE_LIST, NC_ATT_CLASS,
+ * NC_ATT_DIMENSION_LIST, NC_ATT_NAME,
+ * NC_ATT_COORDINATES, NC_DIMID_ATT_NAME
+ * exist anywhere in the file; note that this
+ * requires walking the file.
+
+ * @note WARNINGS:
+ *   1. False negatives are possible for a small subset of netcdf-4
+ *   created files.
+ *   2. Deliberate falsification in the file can be used to cause
+ *   a false positive.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ *
+ * @returns NC_NOERR No error.
+ * @author Dennis Heimbigner.
+ */
+int
+NC4_isnetcdf4(struct NC_FILE_INFO* h5)
+{
+    int stat;
+    int isnc4 = 0;
+    int count;
+
+    /* Look for NC3_STRICT_ATT_NAME */
+    isnc4 = NC4_get_strict_att(h5);
+    if(isnc4 > 0)
+        goto done;
+    /* attribute did not exist */
+    /* => last resort: walk the HDF5 file looking for markers */
+    count = 0;
+    stat = NC4_walk(((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid,
+                    &count);
+    if(stat != NC_NOERR)
+        isnc4 = 0;
+    else /* Threshold is at least two matches */
+        isnc4 = (count >= 2);
+
+done:
+    return isnc4;
+}
+
+/**
+ * @internal Get the NC3 strict attribute.
+ *
+ * @param h5 Pointer to HDF5 file info struct.
+ *
+ * @returns NC_NOERR No error.
+ * @author Dennis Heimbigner.
+ */
+static int
+NC4_get_strict_att(NC_FILE_INFO_T *h5)
+{
+    hid_t grpid = -1;
+    hid_t attid = -1;
+
+    /* Get root group ID. */
+    grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
+
+    /* Try to extract the NC3_STRICT_ATT_NAME attribute */
+    attid = H5Aopen_name(grpid, NC3_STRICT_ATT_NAME);
+    H5Aclose(attid);
+    return attid;
+}
+
+/**
+ * @internal Walk group struct.
+ *
+ * @param gid HDF5 ID of starting group.
+ * @param countp Pointer that gets count.
+ *
+ * @returns NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+static int
+NC4_walk(hid_t gid, int* countp)
+{
+    int ncstat = NC_NOERR;
+    int i,j,na;
+    ssize_t len;
+    hsize_t nobj;
+    herr_t err;
+    int otype;
+    hid_t grpid, dsid;
+    char name[NC_HDF5_MAX_NAME];
+
+    /* walk group members of interest */
+    err = H5Gget_num_objs(gid, &nobj);
+    if(err < 0) return err;
+
+    for(i = 0; i < nobj; i++) {
+        /* Get name & kind of object in the group */
+        len = H5Gget_objname_by_idx(gid,(hsize_t)i,name,(size_t)NC_HDF5_MAX_NAME);
+        if(len < 0) return len;
+
+        otype =  H5Gget_objtype_by_idx(gid,(size_t)i);
+        switch(otype) {
+        case H5G_GROUP:
+            grpid = H5Gopen(gid,name);
+            NC4_walk(grpid,countp);
+            H5Gclose(grpid);
+            break;
+        case H5G_DATASET: /* variables */
+            /* Check for phony_dim */
+            if(strcmp(name,"phony_dim")==0)
+                *countp = *countp + 1;
+            dsid = H5Dopen(gid,name);
+            na = H5Aget_num_attrs(dsid);
+            for(j = 0; j < na; j++) {
+                hid_t aid =  H5Aopen_idx(dsid,(unsigned int)    j);
+                if(aid >= 0) {
+                    const NC_reservedatt* ra;
+                    ssize_t len = H5Aget_name(aid, NC_HDF5_MAX_NAME, name);
+                    if(len < 0) return len;
+                    /* Is this a netcdf-4 marker attribute */
+                    /* Is this a netcdf-4 marker attribute */
+                    ra = NC_findreserved(name);
+                    if(ra != NULL)
+                        *countp = *countp + 1;
+                }
+                H5Aclose(aid);
+            }
+            H5Dclose(dsid);
+            break;
+        default:/* ignore */
+            break;
+        }
+    }
+    return ncstat;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4info.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4info.c
new file mode 100644
index 0000000000000000000000000000000000000000..5b79f68b4cf79a1846f008a9d5687fed9096cdc3
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4info.c
@@ -0,0 +1,691 @@
+/**
+ * @file
+ * @internal Add provenance info for netcdf-4 files.
+ *
+ * Copyright 2018, UCAR/Unidata See netcdf/COPYRIGHT file for copying
+ * and redistribution conditions.
+ * @author Dennis Heimbigner
+ */
+
+#include "config.h"
+#include "nc4internal.h"
+#include "hdf5internal.h"
+#include "nc_provenance.h"
+#include "nclist.h"
+#include "ncbytes.h"
+
+/* Provide a hack to suppress the writing of _NCProperties attribute.
+   This is for creating a file without _NCProperties for testing purposes.
+*/
+#undef SUPPRESSNCPROPS
+
+/* Various Constants */
+#define NCPROPS_MAX_NAME 1024 /* max key name size */
+#define NCPROPS_MAX_VALUE 1024 /* max value size */
+#define HDF5_MAX_NAME 1024 /**< HDF5 max name. */
+
+#define ESCAPECHARS "\\=|,"
+
+/** @internal Check NetCDF return code. */
+#define NCHECK(expr) {if((expr)!=NC_NOERR) {goto done;}}
+
+/** @internal Check HDF5 return code. */
+#define HCHECK(expr) {if((expr)<0) {ncstat = NC_EHDFERR; goto done;}}
+
+static int NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring);
+static int NC4_write_ncproperties(NC_FILE_INFO_T* h5);
+
+static int globalpropinitialized = 0;
+static NC4_Provenance globalprovenance;
+
+/**
+ * @internal Initialize default provenance info
+ * This will only be used for newly created files
+ * or for opened files that do not contain an _NCProperties
+ * attribute.
+ *
+ * @return ::NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+int
+NC4_provenance_init(void)
+{
+    int stat = NC_NOERR;
+    char* name = NULL;
+    char* value = NULL;
+    unsigned major,minor,release;
+    NCbytes* buffer = NULL; /* for constructing the global _NCProperties */
+    char printbuf[1024];
+    const char* p = NULL;
+
+    if(globalpropinitialized)
+        return stat;
+
+    /* Build _NCProperties info */
+
+    /* Initialize globalpropinfo */
+    memset((void*)&globalprovenance,0,sizeof(NC4_Provenance));
+    globalprovenance.version = NCPROPS_VERSION;
+
+    buffer = ncbytesnew();
+
+    /* Insert version as first entry */
+    ncbytescat(buffer,NCPVERSION);
+    ncbytescat(buffer,"=");
+
+    snprintf(printbuf,sizeof(printbuf),"%d",globalprovenance.version);
+    ncbytescat(buffer,printbuf);
+
+    /* Insert the netcdf version */
+    ncbytesappend(buffer,NCPROPSSEP2);
+    ncbytescat(buffer,NCPNCLIB2);
+    ncbytescat(buffer,"=");
+    ncbytescat(buffer,PACKAGE_VERSION);
+
+    /* Insert the HDF5 as underlying storage format library */
+    ncbytesappend(buffer,NCPROPSSEP2);
+    ncbytescat(buffer,NCPHDF5LIB2);
+    ncbytescat(buffer,"=");
+    if((stat = NC4_hdf5get_libversion(&major,&minor,&release))) goto done;
+    snprintf(printbuf,sizeof(printbuf),"%1u.%1u.%1u",major,minor,release);
+    ncbytescat(buffer,printbuf);
+
+#ifdef NCPROPERTIES_EXTRA
+    /* Add any extra fields */
+    p = NCPROPERTIES_EXTRA;
+    if(p[0] == NCPROPSSEP2) p++; /* If leading separator */
+    ncbytesappend(buffer,NCPROPSSEP2);
+    ncbytescat(buffer,p);
+#endif
+    ncbytesnull(buffer);
+    globalprovenance.ncproperties = ncbytesextract(buffer);
+
+done:
+    ncbytesfree(buffer);
+    if(name != NULL) free(name);
+    if(value != NULL) free(value);
+    if(stat == NC_NOERR)
+        globalpropinitialized = 1; /* avoid repeating it */
+    return stat;
+}
+
+/**
+ * @internal finalize default provenance info
+ *
+ * @return ::NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+int
+NC4_provenance_finalize(void)
+{
+    return NC4_clear_provenance(&globalprovenance);
+}
+
+/**
+ * @internal
+ *
+ * Construct the provenance information for a newly created file.
+ * Note that creation of the _NCProperties attribute is deferred
+ * to the sync_netcdf4_file function.
+ *
+ * @param file Pointer to file object.
+ *
+ * @return ::NC_NOERR No error.
+ * [Note: other errors are reported via LOG()]
+ * @author Dennis Heimbigner
+ */
+int
+NC4_new_provenance(NC_FILE_INFO_T* file)
+{
+    int ncstat = NC_NOERR;
+    NC4_Provenance* provenance = NULL;
+    int superblock = -1;
+
+    LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id));
+
+    assert(file->provenance.ncproperties == NULL); /* not yet defined */
+
+    provenance = &file->provenance;
+    memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */
+
+    /* Set the version */
+    provenance->version = globalprovenance.version;
+
+    /* Set the superblock number */
+    if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done;
+    provenance->superblockversion = superblock;
+
+    if(globalprovenance.ncproperties != NULL) {
+        if((provenance->ncproperties = strdup(globalprovenance.ncproperties)) == NULL)
+	    {ncstat = NC_ENOMEM; goto done;}
+    }
+
+done:
+    if(ncstat) {
+        LOG((0,"Could not create _NCProperties attribute"));
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal
+ *
+ * Construct the provenance information for an existing file.
+ *
+ * @param file Pointer to file object.
+ *
+ * @return ::NC_NOERR No error.
+ * [Note: other errors are reported via LOG()]
+ * @author Dennis Heimbigner
+ */
+int
+NC4_read_provenance(NC_FILE_INFO_T* file)
+{
+    int ncstat = NC_NOERR;
+    NC4_Provenance* provenance = NULL;
+    int superblock = -1;
+    char* propstring = NULL;
+
+    LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id));
+
+    assert(file->provenance.version == 0); /* not yet defined */
+
+    provenance = &file->provenance;
+    memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */
+
+    /* Set the superblock number */
+    if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done;
+    provenance->superblockversion = superblock;
+
+    /* Read the _NCProperties value from the file */
+    if((ncstat = NC4_read_ncproperties(file,&propstring))) goto done;
+    provenance->ncproperties = propstring;
+    propstring = NULL;
+
+done:
+    nullfree(propstring);
+    if(ncstat) {
+        LOG((0,"Could not create _NCProperties attribute"));
+    }
+    return NC_NOERR;
+}
+
+/**
+ * @internal
+ *
+ * Add the provenance information to a newly created file.
+ *
+ * @param file Pointer to file object.
+ *
+ * @return ::NC_NOERR No error.
+ * [Note: other errors are reported via LOG()]
+ * @author Dennis Heimbigner
+ */
+int
+NC4_write_provenance(NC_FILE_INFO_T* file)
+{
+    int ncstat = NC_NOERR;
+    if((ncstat = NC4_write_ncproperties(file)))
+	goto done;
+done:
+    return ncstat;
+}
+
+/* HDF5 Specific attribute read/write of _NCProperties */
+static int
+NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring)
+{
+    int retval = NC_NOERR;
+    hid_t hdf5grpid = -1;
+    hid_t attid = -1;
+    hid_t aspace = -1;
+    hid_t atype = -1;
+    hid_t ntype = -1;
+    char* text = NULL;
+    H5T_class_t t_class;
+    hsize_t size;
+
+    LOG((5, "%s", __func__));
+
+    hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
+
+    if(H5Aexists(hdf5grpid,NCPROPS) <= 0) { /* Does not exist */
+        /* File did not contain a _NCProperties attribute; leave empty */
+        goto done;
+    }
+
+    /* NCPROPS Attribute exists, make sure it is legitimate */
+    attid = H5Aopen_name(hdf5grpid, NCPROPS);
+    assert(attid > 0);
+    aspace = H5Aget_space(attid);
+    atype = H5Aget_type(attid);
+    /* Verify atype and size */
+    t_class = H5Tget_class(atype);
+    if(t_class != H5T_STRING)
+    {retval = NC_EINVAL; goto done;}
+    size = H5Tget_size(atype);
+    if(size == 0)
+    {retval = NC_EINVAL; goto done;}
+    text = (char*)malloc(1+(size_t)size);
+    if(text == NULL)
+    {retval = NC_ENOMEM; goto done;}
+    if((ntype = H5Tget_native_type(atype, H5T_DIR_DEFAULT)) < 0)
+    {retval = NC_EHDFERR; goto done;}
+    if((H5Aread(attid, ntype, text)) < 0)
+    {retval = NC_EHDFERR; goto done;}
+    /* Make sure its null terminated */
+    text[(size_t)size] = '\0';
+    if(propstring) {*propstring = text; text = NULL;}
+
+done:
+    if(text != NULL) free(text);
+    /* Close out the HDF5 objects */
+    if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR;
+    if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR;
+    if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR;
+    if(ntype > 0 && H5Tclose(ntype) < 0) retval = NC_EHDFERR;
+
+    /* For certain errors, actually fail, else log that attribute was invalid and ignore */
+    if(retval != NC_NOERR) {
+        if(retval != NC_ENOMEM && retval != NC_EHDFERR) {
+            LOG((0,"Invalid _NCProperties attribute: ignored"));
+            retval = NC_NOERR;
+        }
+    }
+    return retval;
+}
+
+static int
+NC4_write_ncproperties(NC_FILE_INFO_T* h5)
+{
+#ifdef SUPPRESSNCPROPERTY
+    return NC_NOERR;
+#else /*!SUPPRESSNCPROPERTY*/
+    int retval = NC_NOERR;
+    hid_t hdf5grpid = -1;
+    hid_t attid = -1;
+    hid_t aspace = -1;
+    hid_t atype = -1;
+    size_t len = 0;
+    NC4_Provenance* prov = &h5->provenance;
+
+    LOG((5, "%s", __func__));
+
+    /* If the file is read-only, return an error. */
+    if (h5->no_write)
+    {retval = NC_EPERM; goto done;}
+
+    hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
+
+    if(H5Aexists(hdf5grpid,NCPROPS) > 0) /* Already exists, no overwrite */
+        goto done;
+
+    /* Build the property if we have legit value */
+    if(prov->ncproperties != NULL) {
+	/* Build the HDF5 string type */
+	if ((atype = H5Tcopy(H5T_C_S1)) < 0)
+	    {retval = NC_EHDFERR; goto done;}
+	if (H5Tset_strpad(atype, H5T_STR_NULLTERM) < 0)
+	    {retval = NC_EHDFERR; goto done;}
+	if(H5Tset_cset(atype, H5T_CSET_ASCII) < 0)
+	    {retval = NC_EHDFERR; goto done;}
+	len = strlen(prov->ncproperties);
+	if(H5Tset_size(atype, len) < 0)
+	    {retval = NC_EFILEMETA; goto done;}
+	/* Create NCPROPS attribute */
+	if((aspace = H5Screate(H5S_SCALAR)) < 0)
+	    {retval = NC_EFILEMETA; goto done;}
+	if ((attid = H5Acreate(hdf5grpid, NCPROPS, atype, aspace, H5P_DEFAULT)) < 0)
+	    {retval = NC_EFILEMETA; goto done;}
+	if (H5Awrite(attid, atype, prov->ncproperties) < 0)
+	    {retval = NC_EFILEMETA; goto done;}
+/* Verify */
+#if 0
+    {
+        hid_t spacev, typev;
+        hsize_t dsize, tsize;
+        typev = H5Aget_type(attid);
+        spacev = H5Aget_space(attid);
+        dsize = H5Aget_storage_size(attid);
+        tsize = H5Tget_size(typev);
+        fprintf(stderr,"dsize=%lu tsize=%lu\n",(unsigned long)dsize,(unsigned long)tsize);
+    }
+#endif
+    }
+
+done:
+    /* Close out the HDF5 objects */
+    if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR;
+    if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR;
+    if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR;
+
+    /* For certain errors, actually fail, else log that attribute was invalid and ignore */
+    switch (retval) {
+    case NC_ENOMEM:
+    case NC_EHDFERR:
+    case NC_EPERM:
+    case NC_EFILEMETA:
+    case NC_NOERR:
+        break;
+    default:
+        LOG((0,"Invalid _NCProperties attribute"));
+        retval = NC_NOERR;
+        break;
+    }
+    return retval;
+#endif /*!SUPPRESSNCPROPERTY*/
+}
+
+/**************************************************/
+/* Utilities */
+
+/* Debugging */
+
+void
+ncprintprovenance(NC4_Provenance* info)
+{
+    fprintf(stderr,"[%p] version=%d superblockversion=%d ncproperties=|%s|\n",
+	info,
+	info->version,
+	info->superblockversion,
+	(info->ncproperties==NULL?"":info->ncproperties));
+}
+
+/**
+ * @internal
+ *
+ * Clear the NCPROVENANCE object; do not free it
+ * @param prov Pointer to provenance object
+ *
+ * @return ::NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+int
+NC4_clear_provenance(NC4_Provenance* prov)
+{
+    LOG((5, "%s", __func__));
+
+    if(prov == NULL) return NC_NOERR;
+    nullfree(prov->ncproperties);
+    memset(prov,0,sizeof(NC4_Provenance));
+    return NC_NOERR;
+}
+
+#if 0
+/* Unused functions */
+
+/**
+ * @internal Parse file properties.
+ *
+ * @param text0 Text properties.
+ * @param pairs list of parsed (key,value) pairs
+ *
+ * @return ::NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+static int
+properties_parse(const char* text0, NClist* pairs)
+{
+    int ret = NC_NOERR;
+    char* p;
+    char* q;
+    char* text = NULL;
+
+    if(text0 == NULL || strlen(text0) == 0)
+        goto done;
+
+    text = strdup(text0);
+    if(text == NULL) return NC_ENOMEM;
+
+    /* For back compatibility with version 1, translate '|' -> ',' */
+    for(p=text;*p;p++) {
+        if(*p == NCPROPSSEP1)
+            *p = NCPROPSSEP2;
+    }
+
+    /* Walk and fill in ncinfo */
+    p = text;
+    while(*p) {
+        char* name = p;
+        char* value = NULL;
+        char* next = NULL;
+
+        /* Delimit whole (key,value) pair */
+        q = locate(p,NCPROPSSEP2);
+        if(*q != '\0') /* Never go beyond the final nul term */
+            *q++ = '\0';
+        next = q;
+        /* split key and value */
+        q = locate(p,'=');
+        name = p;
+        *q++ = '\0';
+        value = q;
+        /* Set up p for next iteration */
+        p = next;
+        nclistpush(pairs,strdup(name));
+        nclistpush(pairs,strdup(value));
+    }
+done:
+    if(text) free(text);
+    return ret;
+}
+
+/* Locate a specific character and return its pointer
+   or EOS if not found
+   take \ escapes into account */
+static char*
+locate(char* p, char tag)
+{
+    char* next;
+    int c;
+    assert(p != NULL);
+    for(next = p;(c = *next);next++) {
+        if(c == tag)
+            return next;
+        else if(c == '\\' && next[1] != '\0')
+            next++; /* skip escaped char */
+    }
+    return next; /* not found */
+}
+
+/* Utility to transfer a string to a buffer with escaping */
+static void
+escapify(NCbytes* buffer, const char* s)
+{
+    const char* p;
+    for(p=s;*p;p++) {
+        if(strchr(ESCAPECHARS,*p) != NULL)
+            ncbytesappend(buffer,'\\');
+        ncbytesappend(buffer,*p);
+    }
+}
+
+/**
+ * @internal Build _NCProperties attribute value.
+ *
+ * Convert a NCPROPINFO instance to a single string.
+ * Will always convert to current format
+ *
+ * @param version
+ * @param list Properties list
+ * @param spropp Pointer that gets properties string.
+ * @return ::NC_NOERR No error.
+ * @return ::NC_EINVAL failed.
+ * @author Dennis Heimbigner
+ */
+static int
+build_propstring(int version, NClist* list, char** spropp)
+{
+    int stat = NC_NOERR;
+    int i;
+    NCbytes* buffer = NULL;
+    char sversion[64];
+
+    LOG((5, "%s version=%d", __func__, version));
+
+    if(spropp != NULL) *spropp = NULL;
+
+    if(version == 0 || version > NCPROPS_VERSION) /* unknown case */
+	goto done;
+     if(list == NULL)
+        {stat = NC_EINVAL; goto done;}
+
+    if((buffer = ncbytesnew()) ==  NULL)
+        {stat = NC_ENOMEM; goto done;}
+
+    /* start with version */
+    ncbytescat(buffer,NCPVERSION);
+    ncbytesappend(buffer,'=');
+    /* Use current version */
+    snprintf(sversion,sizeof(sversion),"%d",NCPROPS_VERSION);
+    ncbytescat(buffer,sversion);
+
+    for(i=0;i<nclistlength(list);i+=2) {
+        char* value, *name;
+        name = nclistget(list,i);
+        if(name == NULL) continue;
+        value = nclistget(list,i+1);
+        ncbytesappend(buffer,NCPROPSSEP2); /* terminate last entry */
+        escapify(buffer,name);
+        ncbytesappend(buffer,'=');
+        escapify(buffer,value);
+    }
+    /* Force null termination */
+    ncbytesnull(buffer);
+    if(spropp) *spropp = ncbytesextract(buffer);
+
+done:
+    if(buffer != NULL) ncbytesfree(buffer);
+    return stat;
+}
+
+static int
+properties_getversion(const char* propstring, int* versionp)
+{
+    int ncstat = NC_NOERR;
+    int version = 0;
+    /* propstring should begin with "version=dddd" */
+    if(propstring == NULL || strlen(propstring) < strlen("version=") + strlen("1"))
+        {ncstat = NC_EINVAL; goto done;} /* illegal version */
+    if(memcmp(propstring,"version=",strlen("version=")) != 0)
+        {ncstat = NC_EINVAL; goto done;} /* illegal version */
+    propstring += strlen("version=");
+    /* get version */
+    version = atoi(propstring);
+    if(version < 0)
+        {ncstat = NC_EINVAL; goto done;} /* illegal version */
+    if(versionp) *versionp = version;
+done:
+    return ncstat;
+}
+
+/**
+ * @internal
+ *
+ * Construct the parsed provenance information
+ *
+ * @param prov Pointer to provenance object
+ *
+ * @return ::NC_NOERR No error.
+ * @return ::NC_ENOMEM
+ * @return ::NC_EINVAL
+ * @author Dennis Heimbigner
+ */
+static int
+parse_provenance(NC4_Provenance* prov)
+{
+    int ncstat = NC_NOERR;
+    char *name = NULL;
+    char *value = NULL;
+    int version = 0;
+    NClist* list = NULL;
+
+    LOG((5, "%s: prov 0x%x", __func__, prov));
+
+    if(prov->ncproperty == NULL || strlen(prov->ncproperty) < strlen("version="))
+        {ncstat = NC_EINVAL; goto done;}
+    if((list = nclistnew()) == NULL)
+        {ncstat = NC_ENOMEM; goto done;}
+
+    /* Do we understand the version? */
+    if(prov->version > 0 && prov->version <= NCPROPS_VERSION) {/* recognized version */
+        if((ncstat=properties_parse(prov->ncproperty,list)))
+	    goto done;
+        /* Remove version pair from properties list*/
+        if(nclistlength(list) < 2)
+            {ncstat = NC_EINVAL; goto done;} /* bad _NCProperties attribute */
+        /* Throw away the purported version=... */
+        nclistremove(list,0); /* version key */
+        nclistremove(list,0); /* version value */
+
+        /* Now, rebuild to the latest version */
+	switch (version) {
+	default: break; /* do nothing */
+	case 1: {
+            int i;
+            for(i=0;i<nclistlength(list);i+=2) {
+                char* newname = NULL;
+                name = nclistget(list,i);
+                if(name == NULL) continue; /* ignore */
+                if(strcmp(name,NCPNCLIB1) == 0)
+                    newname = NCPNCLIB2; /* change name */
+                else if(strcmp(name,NCPHDF5LIB1) == 0)
+                    newname = NCPHDF5LIB2;
+                else continue; /* ignore */
+                /* Do any rename */
+                nclistset(list,i,strdup(newname));
+                if(name) {free(name); name = NULL;}
+            }
+        } break;
+	} /*switch*/
+    }
+    prov->properties = list;
+    list = NULL;
+
+done:
+    nclistfreeall(list);
+    if(name != NULL) free(name);
+    if(value != NULL) free(value);
+    return ncstat;
+}
+
+/**
+ * @internal
+ *
+ * Clear and Free the NC4_Provenance object
+ * @param prov Pointer to provenance object
+ *
+ * @return ::NC_NOERR No error.
+ * @author Dennis Heimbigner
+ */
+static int
+NC4_free_provenance(NC4_Provenance* prov)
+{
+    LOG((5, "%s", __func__));
+
+    if(prov == NULL) return NC_NOERR;
+    NC4_clear_provenance(prov);
+    free(prov);
+    return NC_NOERR;
+}
+
+/* Utility to copy contents of the dfalt into an NCPROPINFO object */
+static int
+propinfo_default(NC4_Properties* dst, const NC4_Properties* dfalt)
+{
+    int i;
+    if(dst->properties == NULL) {
+        dst->properties = nclistnew();
+        if(dst->properties == NULL) return NC_ENOMEM;
+    }
+    dst->version = dfalt->version;
+    for(i=0;i<nclistlength(dfalt->properties);i++) {
+        char* s = nclistget(dfalt->properties,i);
+        s = strdup(s);
+        if(s == NULL) return NC_ENOMEM;
+        nclistpush(dst->properties,s);
+    }
+    return NC_NOERR;
+}
+
+#endif /*0*/
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4mem.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4mem.c
new file mode 100644
index 0000000000000000000000000000000000000000..41a2934bd8261bb031141f23f14b556d4de9ef30
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4mem.c
@@ -0,0 +1,81 @@
+/*********************************************************************
+*    Copyright 2018, UCAR/Unidata
+*    See netcdf/COPYRIGHT file for copying and redistribution conditions.
+* ********************************************************************/
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+* Copyright by The HDF Group.                                               *
+* Copyright by the Board of Trustees of the University of Illinois.         *
+* All rights reserved.                                                      *
+*                                                                           *
+* This file is part of HDF5.  The full HDF5 copyright notice, including     *
+* terms governing use, modification, and redistribution, is contained in    *
+ * the COPYING file, which can be found at the root of the source code       *
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
+ * If you do not have access to either file, you may request a copy from     *
+ * help@hdfgroup.org.                                                        *
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "config.h"
+#include "hdf5internal.h"
+#include <H5DSpublic.h> /* must be after nc4internal.h */
+#include <H5Fpublic.h>
+#include <H5LTpublic.h>
+
+#include "netcdf.h"
+#include "nc4internal.h"
+
+#undef DEBUG
+
+#ifndef HDrealloc
+    #define HDrealloc(M,Z)    realloc(M,Z)
+#endif /* HDrealloc */
+
+int
+NC4_open_image_file(NC_FILE_INFO_T* h5)
+{
+    int stat = NC_NOERR;
+    hid_t hdfid;
+
+    /* check arguments */
+    if(h5->mem.memio.memory == NULL || h5->mem.memio.size == 0)
+	{stat = NC_EINVAL; goto done;}
+
+    /* Figure out the image flags */
+    h5->mem.imageflags = 0;
+    if(h5->mem.locked) {
+	h5->mem.imageflags |= (H5LT_FILE_IMAGE_DONT_COPY | H5LT_FILE_IMAGE_DONT_RELEASE);
+    }
+    if(!h5->no_write)
+	h5->mem.imageflags |= H5LT_FILE_IMAGE_OPEN_RW;
+
+    /* Create the file but using our version of H5LTopen_file_image */
+    hdfid = NC4_image_init(h5);
+    if(hdfid < 0)
+	{stat = NC_EHDFERR; goto done;}
+
+    /* Remember HDF5 file identifier. */
+    ((NC_HDF5_FILE_INFO_T *)h5->format_file_info)->hdfid = hdfid;
+
+done:
+    return stat;
+}
+
+int
+NC4_create_image_file(NC_FILE_INFO_T* h5, size_t initialsz)
+{
+    int stat = NC_NOERR;
+    hid_t hdfid;
+
+    /* Create the file but using our version of H5LTopen_file_image */
+    h5->mem.created = 1;
+    h5->mem.initialsize = initialsz;
+    h5->mem.imageflags |= H5LT_FILE_IMAGE_OPEN_RW;
+    hdfid = NC4_image_init(h5);
+    if(hdfid < 0)
+	{stat = NC_EHDFERR; goto done;}
+    /* Remember HDF5 file identifier. */
+    ((NC_HDF5_FILE_INFO_T *)h5->format_file_info)->hdfid = hdfid;
+done:
+    return stat;
+}
diff --git a/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4memcb.c b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4memcb.c
new file mode 100644
index 0000000000000000000000000000000000000000..472f6cac7a915a582565a6ef037239b5503d0f94
--- /dev/null
+++ b/ThirdParty/netcdf/vtknetcdf/libhdf5/nc4memcb.c
@@ -0,0 +1,1042 @@
+/*********************************************************************
+*    Copyright 2018, UCAR/Unidata
+*    See netcdf/COPYRIGHT file for copying and redistribution conditions.
+* ********************************************************************/
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+* Copyright by The HDF Group.                                               *
+* Copyright by the Board of Trustees of the University of Illinois.         *
+* All rights reserved.                                                      *
+*                                                                           *
+* This file is part of HDF5.  The full HDF5 copyright notice, including     *
+* terms governing use, modification, and redistribution, is contained in    *
+ * the COPYING file, which can be found at the root of the source code       *
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
+ * If you do not have access to either file, you may request a copy from     *
+ * help@hdfgroup.org.                                                        *
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * @internal Inmemory support
+ *
+ * This code is derived from H5LT.c#H5LTopen_file_image.
+ * In order to make the netcdf inmemory code work, it is necessary
+ * to modify some of the callback functions; specifically
+ * image_malloc, image_realloc, and image_memcpy.
+ * <p>
+ * The changes are directed at allowing the caller to
+ * specify two things.
+ * <ol>
+ * <li> To specify (indirectly) the H5LT_FILE_IMAGE_DONT_COPY flag.
+ * This means that no attempt to realloc the caller provided memory
+ * should be made. This also means that the memory block pointer
+ * provided by the caller will be thesame returned by <em>nc_close_memio()</em>.
+ * <li> The caller overallocates the memory so that there is space
+ * to allow the file to be modified in place.
+ * </ol>
+ * <p>
+ * The existing implementation of H5LTopen_file_image has two flaws
+ * with respect to these properties.
+ * <ol>
+ * <li> The image_realloc callback fails if
+ * H5LT_FILE_IMAGE_DONT_COPY flag is set even if there is room
+ * to allow the memory block to pretend to expand (because
+ * of overallocation).
+ * <li> When the caller attempts to get the final memory block,
+ * the HDF5 library makes a copy, unless theH5LT_FILE_IMAGE_DONT_COPY
+ * flag is set. This is unnecessary. Note that in this situation,
+ * the HDF5 library will use
+ * <em>image_malloc()</em>
+ * followed by
+ * <em>image_memcpy()</em>
+ * </ol>
+ * <p>
+ * So, the callback changes to support this properly are as follows.
+ * <dl>
+ * <dt>image_realloc</dt>
+ * <dd>If there is sufficient space (because of overallocation),
+ * the pretend to realloc and return the incoming memory block
+ * instead of taking the chance of doing a real realloc.</dd>
+ * <dt>image_malloc</dt>
+ * <dd>If the operation being performed is to obtain
+ * the space to copy the final memory, then just return
+ * the original memory block. Note that this case is detectable
+ * because the callback is given the value
+ * H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET.</dd>
+ * <dt>image_memcpy</dt>
+ * <dd>Similar to the image_malloc change.
+ * Namely, if the operation being performed is to copy
+ * out the final memory contents, and the final memory block
+ * is the same as that provided by the caller originally, then
+ * just do nothing. Again, this case can be detected
+ * by the occurrence of H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET.</dd>
+ * </dl>
+ * @author Dennis Heimbigner
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include <hdf5.h>
+#include <hdf5_hl.h>
+
+#include "nc4internal.h"
+
+#ifndef HDrealloc
+#define HDrealloc(x,y) realloc(x,y)
+#endif
+#ifndef SUCCEED
+#define SUCCEED 0
+#define FAIL -1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#undef TRACE
+#undef CATCH
+#undef TRACE_UDATA
+
+#ifdef TRACE
+#define CATCH
+#endif
+
+#ifdef TRACE
+#include <stdarg.h>
+static void trace(const char* fcn, H5FD_file_image_op_t op, void* _udata, ...);
+static void traceend(const char* fcn, void* _udata, uintptr_t retval);
+static const char* traceop(H5FD_file_image_op_t op);
+static char* traceflags(int flags);
+
+/* In case we do not have variadic macros */
+#define TRACE0(fcn,op,udata)  trace(fcn,op,udata)
+#define TRACE1(fcn,op,udata,x1)  trace(fcn,op,udata,x1)
+#define TRACE2(fcn,op,udata,x1,x2)  trace(fcn,op,udata,x1,x2)
+#define TRACE3(fcn,op,udata,x1,x2,x3)  trace(fcn,op,udata,x1,x2,x3)
+#define TRACEEND(fcn,udata,retval) traceend(fcn,udata,(uintptr_t)retval);
+#else /*!TRACE*/
+#define TRACE0(fcn,op,udata)
+#define TRACE1(fcn,op,udata,x1)
+#define TRACE2(fcn,op,udata,x1,x2)
+#define TRACE3(fcn,op,udata,x1,x2,x3)
+#define TRACEEND(fcn,udata,retval)
+#endif
+
+#ifdef CATCH
+static void tracefail(const char* fcn);
+#define TRACEFAIL(fcn) tracefail(fcn)
+#else
+#define TRACEFAIL(fcn)
+#endif
+
+#define DEFAULT_CREATE_MEMSIZE ((size_t)1<<16)
+
+#ifndef H5LT_FILE_IMAGE_DONT_COPY
+
+/* Flag definitions for H5LTopen_file_image() */
+#define H5LT_FILE_IMAGE_OPEN_RW      0x0001 /* Open image for read-write */
+#define H5LT_FILE_IMAGE_DONT_COPY    0x0002 /* The HDF5 lib won't copy   */
+/* user supplied image buffer. The same image is open with the core driver.  */
+#define H5LT_FILE_IMAGE_DONT_RELEASE 0x0004 /* The HDF5 lib won't        */
+/* deallocate user supplied image buffer. The user application is reponsible */
+/* for doing so.                                                             */
+#define H5LT_FILE_IMAGE_ALL          0x0007
+
+#endif /*H5LT_FILE_IMAGE_DONT_COPY*/
+
+
+#if 0
+/* For Lex and Yacc */
+#define         COL             3
+#define         LIMIT           512
+#define         INCREMENT       1024
+#define         TMP_LEN         256
+#define         MAX(a,b)        (((a)>(b)) ? (a) : (b))
+size_t  input_len;
+char *myinput;
+size_t  indent = 0;
+#endif /*0*/
+
+/* File Image operations
+
+   A file image is a representation of an HDF5 file in a memory
+   buffer. In order to perform operations on an image in a similar way
+   to a  file, the application buffer is copied to a FAPL buffer, which
+   in turn is copied to a VFD buffer. Buffer copying can decrease
+   performance, especially when using large file images. A solution to
+   this issue is to simulate the copying of the application buffer,
+   when actually the same buffer is used for the FAPL and the VFD.
+   This is implemented by using callbacks that simulate the standard
+   functions for memory management (additional callbacks are used for
+   the management of associated data structures). From the application
+   standpoint, a file handle can be obtained from a file image by using
+   the API routine H5LTopen_file_image(). This function takes a flag
+   argument that indicates the HDF5 library how to handle the given image;
+   several flag values can be combined by using the bitwise OR operator.
+   Valid flag values include:
+
+   H5LT_FILE_IMAGE_OPEN_RW indicates the HDF5 library to open the file
+   image in read/write mode. Default is read-only mode.
+
+   H5LT_FILE_IMAGE_DONT_COPY indicates the HDF5 library to not copy the
+   supplied user buffer; the same buffer will be handled by the FAPL and
+   the VFD driver. Default operation copies the user buffer to the FAPL and
+   VFD driver.
+
+   H5LT_FILE_IMAGE_DONT_RELEASE indicates the HDF5 library to not release
+   the buffer handled by the FAPL and the VFD upon closing. This flag value
+   is only applicable when the flag value H5LT_FILE_IMAGE_DONT_COPY is set as
+   well. The application is responsible to release the image buffer.
+*/
+
+/* Data structure to pass application data to callbacks. */
+/* Modified to add NC_FILE_INFO_T ptr */
+typedef struct {
+    void *app_image_ptr;	/* Pointer to application buffer */
+    size_t app_image_size;	/* Size of application buffer */
+    void *fapl_image_ptr;	/* Pointer to FAPL buffer */
+    size_t fapl_image_size;	/* Size of FAPL buffer */
+    int fapl_ref_count;		/* Reference counter for FAPL buffer */
+    void *vfd_image_ptr;	/* Pointer to VFD buffer */
+    size_t vfd_image_size;	/* Size of VFD buffer */
+    int vfd_ref_count;		/* Reference counter for VFD buffer */
+    unsigned flags;		/* Flags indicate how the file image will */
+                                /* be open */
+    int ref_count;		/* Reference counter on udata struct */
+    NC_FILE_INFO_T* h5;
+} H5LT_file_image_ud_t;
+
+/* callbacks prototypes for file image ops */
+static void *local_image_malloc(size_t size, H5FD_file_image_op_t file_image_op, void *udata);
+static void *local_image_memcpy(void *dest, const void *src, size_t size, H5FD_file_image_op_t file_image_op, void *udata);
+static herr_t local_image_free(void *ptr, H5FD_file_image_op_t file_image_op, void *udata);
+static void *local_udata_copy(void *udata);
+static herr_t local_udata_free(void *udata);
+
+/* Definition of callbacks for file image operations. */
+
+
+/*-------------------------------------------------------------------------
+* Function: image_malloc
+*
+* Purpose: Simulates malloc() function to avoid copying file images.
+*          The application buffer is set to the buffer on only one FAPL.
+*          Then the FAPL buffer can be copied to other FAPL buffers or
+*          to only one VFD buffer.
+*
+* Return: Address of "allocated" buffer, if successful. Otherwise, it returns
+*         NULL.
+*
+* Programmer: Christian Chilan
+*
+* Date: October 3, 2011
+*
+*-------------------------------------------------------------------------
+*/
+static void *
+local_image_malloc(size_t size, H5FD_file_image_op_t file_image_op, void *_udata)
+{
+    H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata;
+    void * return_value = NULL;
+
+    TRACE1("malloc", file_image_op, _udata, size);
+
+#if 0
+    /* callback is only used if the application buffer is not actually copied */
+    if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
+        goto out;
+#endif
+
+    switch ( file_image_op ) {
+        /* the app buffer is "copied" to only one FAPL. Afterwards, FAPLs can be "copied" */
+        case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET:
+	    /* It appears that the fapl memory is never created as we use it, so
+               we expect the udata ptr to be either null or same as the app buffer.*/
+	    assert(udata->fapl_image_ptr == NULL || udata->fapl_image_ptr == udata->app_image_ptr);
+
+            if (udata->app_image_ptr == NULL)
+                goto out;
+            if (udata->app_image_size != size)
+                goto out;
+	    if (udata->fapl_image_ptr != NULL)
+                goto out;
+            if (udata->fapl_image_size != 0)
+                goto out;
+            if (udata->fapl_ref_count != 0)
+                goto out;
+
+            udata->fapl_image_ptr = udata->app_image_ptr;
+            udata->fapl_image_size = udata->app_image_size;
+            return_value = udata->fapl_image_ptr;
+            udata->fapl_ref_count++;
+	    return_value = udata->fapl_image_ptr;
+	    break;
+
+	case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY:
+            if (udata->fapl_image_ptr == NULL)
+                goto out;
+            if (udata->fapl_image_size != size)
+                goto out;
+            if (udata->fapl_ref_count == 0)
+                goto out;
+
+            return_value = udata->fapl_image_ptr;
+            udata->fapl_ref_count++;
+	    break;
+
+       case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET:
+            if (udata->fapl_image_ptr == NULL)
+                goto out;
+	    /* fake the malloc by returning the current memory */
+            return_value = udata->fapl_image_ptr;
+	    break;
+
+        case H5FD_FILE_IMAGE_OP_FILE_OPEN:
+            /* FAPL buffer is "copied" to only one VFD buffer */
+            if (udata->vfd_image_ptr != NULL)
+                goto out;
+            if (udata->vfd_image_size != 0)
+                goto out;
+            if (udata->vfd_ref_count != 0)
+                goto out;
+            if (udata->fapl_image_ptr == NULL)
+                goto out;
+            if (udata->fapl_image_size != size)
+                goto out;
+            if (udata->fapl_ref_count == 0)
+                goto out;
+
+            udata->vfd_image_ptr = udata->fapl_image_ptr;
+ 	    udata->vfd_image_size = size;
+            udata->vfd_ref_count++;
+            return_value = udata->vfd_image_ptr;
+            break;
+
+	/* added unused labels to shut the compiler up */
+	case H5FD_FILE_IMAGE_OP_NO_OP:
+	case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE:
+	case H5FD_FILE_IMAGE_OP_FILE_RESIZE:
+	case H5FD_FILE_IMAGE_OP_FILE_CLOSE:
+        default:
+            goto out;
+    } /* end switch */
+
+    TRACEEND("malloc",_udata,return_value);
+
+    return(return_value);
+
+out:
+    TRACEFAIL("malloc");
+    return NULL;
+} /* end image_malloc() */
+
+
+/*-------------------------------------------------------------------------
+* Function: image_memcpy
+*
+* Purpose:  Simulates memcpy() function to avoid copying file images.
+*           The image buffer can be set to only one FAPL buffer, and
+*           "copied" to only one VFD buffer. The FAPL buffer can be
+*           "copied" to other FAPLs buffers.
+*
+* Return: The address of the destination buffer, if successful. Otherwise, it
+*         returns NULL.
+*
+* Programmer: Christian Chilan
+*
+* Date: October 3, 2011
+*
+*-------------------------------------------------------------------------
+*/
+static void *
+local_image_memcpy(void *dest, const void *src, size_t size, H5FD_file_image_op_t file_image_op,
+    void *_udata)
+{
+    H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata;
+
+    TRACE3("memcpy", file_image_op, _udata,dest,src,size);
+
+#if 0
+    /* callback is only used if the application buffer is not actually copied */
+    if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
+        goto out;
+#endif
+
+    switch(file_image_op) {
+        case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET:
+            if (dest != udata->fapl_image_ptr)
+                goto out;
+            if (src != udata->app_image_ptr)
+                goto out;
+            if (size != udata->fapl_image_size)
+                goto out;
+            if (size != udata->app_image_size)
+                goto out;
+            if (udata->fapl_ref_count == 0)
+                goto out;
+            if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) {
+		if(src != dest) {
+		    memcpy(dest,src,size);
+#ifdef TRACE
+		    fprintf(stderr,"\t>>>> memcpy(%p,%p,%ld)\n",
+				dest,src,(unsigned long)size);
+#endif
+		}
+	    }
+            break;
+
+        case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY:
+            if (dest != udata->fapl_image_ptr)
+                goto out;
+            if (src != udata->fapl_image_ptr)
+                goto out;
+            if (size != udata->fapl_image_size)
+                goto out;
+            if (udata->fapl_ref_count < 2)
+                goto out;
+            break;
+
+        case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET:
+	    if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
+                goto out;
+	    /* test: src == dest == original */
+	    if(src != dest || src != udata->fapl_image_ptr)
+		goto out;
+	    break;
+
+        case H5FD_FILE_IMAGE_OP_FILE_OPEN:
+            if (dest != udata->vfd_image_ptr)
+                goto out;
+            if (src != udata->fapl_image_ptr)
+                goto out;
+            if (size != udata->vfd_image_size)
+                goto out;
+            if (size != udata->fapl_image_size)
+                goto out;
+            if (udata->fapl_ref_count == 0)
+                goto out;
+            if (udata->vfd_ref_count != 1)
+                goto out;
+            break;
+
+	/* added unused labels to shut the compiler up */
+	case H5FD_FILE_IMAGE_OP_NO_OP:
+	case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE:
+	case H5FD_FILE_IMAGE_OP_FILE_RESIZE:
+	case H5FD_FILE_IMAGE_OP_FILE_CLOSE:
+        default:
+            goto out;
+    } /* end switch */
+
+    TRACEEND("memcpy",_udata,dest);
+    return(dest);
+
+out:
+    TRACEFAIL("memcpy");
+    return NULL;
+} /* end image_memcpy() */
+
+
+/*-------------------------------------------------------------------------
+* Function: image_realloc
+*
+* Purpose: Reallocates the shared application image buffer and updates data
+*          structures that manage buffer "copying".
+*
+* Return: Address of reallocated buffer, if successful. Otherwise, it returns
+*         NULL.
+*
+* Programmer: Christian Chilan
+*
+* Date: October 3, 2011
+*
+*-------------------------------------------------------------------------
+*/
+
+/* This warning is from H5FDcore.c"
+    Be careful of non-Posix realloc() that doesn't understand
+    what to do when the first argument is null.
+*/
+
+/* Modified:
+1. If the realloc new size is <= existing size,
+   then pretend we did a realloc and return success.
+   This avoids unneccessary heap operations.
+2. If the H5LT_FILE_IMAGE_DONT_COPY or
+   H5LT_FILE_IMAGE_DONT_RELEASE flag is set and the
+   realloc new size is > existing size, then fail
+   because the realloc() call may change the address
+   of the buffer. The new address cannot be
+   communicated to the application to release it.
+3. Otherwise, use realloc(). Note that this may have the
+   side effect of freeing the previous memory chunk.
+*/
+static void *
+local_image_realloc(void *ptr, size_t size, H5FD_file_image_op_t file_image_op, void *_udata)
+{
+    H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata;
+    void * return_value = NULL;
+
+    TRACE2("realloc", file_image_op, _udata, ptr, size);
+
+#if 0
+    /* callback is only used if the application buffer is not actually copied */
+    if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
+        goto out;
+#endif
+
+    /* realloc() is not allowed if the image is open in read-only mode */
+    if (!(udata->flags & H5LT_FILE_IMAGE_OPEN_RW))
+        goto out;
+
+    /* DONT_COPY => DONT_RELEASE */
+    assert(((udata->flags & H5LT_FILE_IMAGE_DONT_COPY)?(udata->flags & H5LT_FILE_IMAGE_DONT_RELEASE):1));
+
+    /* Note that the fapl pointer is never realloc'd */
+    if (file_image_op == H5FD_FILE_IMAGE_OP_FILE_RESIZE) {
+
+        if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY)) { /* buffer modification is allowed */
+	    /* Divide code based on whether ptr == NULL or not */
+ 	    if(ptr == NULL) {
+		/* From realloc man page: If ptr is NULL, then the call is equivalent to malloc(size),
+		   for all values of size; if size is equal to zero, and ptr is not NULL, then the call
+		   is equivalent to free(ptr). */
+		/* if the app_image != NULL then free it to simulate effect of realloc */
+	        if(udata->app_image_ptr != NULL)
+		    free(udata->app_image_ptr);
+		udata->vfd_image_ptr = malloc(size);
+	        udata->vfd_ref_count++;
+	    } else { /* ptr != NULL */
+		if(udata->vfd_image_ptr != ptr)
+		    goto out;
+		if (udata->vfd_ref_count != 1)
+		    goto out;
+		udata->vfd_image_ptr = realloc(ptr, size);
+		if(NULL == udata->vfd_image_ptr) {
+		    LOG((0,"image_realloc: unable to allocate memory block of size: %lu bytes",(unsigned long)size));
+		    goto out;
+	        }
+#ifdef TRACE
+		fprintf(stderr,"\t>>>> realloc(%p,%ld)=>%p\n",ptr,(unsigned long)size,udata->vfd_image_ptr);
+#endif
+	    }
+            udata->vfd_image_size = size;
+
+	    /* Make sure other pointers are consistent */
+	    udata->app_image_ptr = udata->vfd_image_ptr;
+	    udata->fapl_image_ptr = udata->vfd_image_ptr;
+
+	} else { /* Cannot realloc, so fake it */
+	   if(size <= udata->vfd_image_size) {
+	       /* Ok, pretend we did a realloc but just change size*/
+               udata->vfd_image_size = size;
+           } else
+		goto out;
+        }
+        return_value = udata->vfd_image_ptr;
+    } /* end if */
+    else
+        goto out;
+
+    TRACEEND("realloc",_udata,return_value);
+    return(return_value);
+
+out:
+    TRACEFAIL("realloc");
+    return NULL;
+} /* end local_image_realloc() */
+
+/*-------------------------------------------------------------------------
+* Function: image_free
+*
+* Purpose: Simulates deallocation of FAPL and VFD buffers by decreasing
+*          reference counters. Shared application buffer is actually
+*          deallocated if there are no outstanding references.
+*
+* Return: SUCCEED or FAIL
+*
+* Programmer: Christian Chilan
+*
+* Date: October 3, 2011
+*
+*-------------------------------------------------------------------------
+*/
+static herr_t
+local_image_free(void *ptr, H5FD_file_image_op_t file_image_op, void *_udata)
+{
+    H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata;
+
+    TRACE1("free", file_image_op, _udata, ptr);
+
+#if 0
+    /* callback is only used if the application buffer is not actually copied */
+    if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
+        goto out;
+#endif
+
+    switch(file_image_op) {
+        case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE:
+	    if (udata->fapl_image_ptr != ptr)
+                goto out;
+            if (udata->fapl_ref_count == 0)
+                goto out;
+
+            udata->fapl_ref_count--;
+
+	    /* For the way we use it, it should still be the case that
+               the fapl pointer is same as image_ptr, so we do not need
+               to do anything */
+	    assert(udata->fapl_image_ptr == udata->app_image_ptr);
+	    /* clean up */
+#if 0
+	    udata->app_image_ptr = NULL;
+	    udata->fapl_image_ptr = NULL;
+#endif
+            break;
+
+        case H5FD_FILE_IMAGE_OP_FILE_CLOSE:
+            if (udata->vfd_image_ptr != ptr)
+                goto out;
+            if (udata->vfd_ref_count != 1)
+                goto out;
+
+            udata->vfd_ref_count--;
+
+            break;
+
+	/* added unused labels to keep the compiler quiet */
+	case H5FD_FILE_IMAGE_OP_NO_OP:
+	case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET:
+	case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY:
+	case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET:
+	case H5FD_FILE_IMAGE_OP_FILE_OPEN:
+	case H5FD_FILE_IMAGE_OP_FILE_RESIZE:
+	default:
+            goto out;
+    } /* end switch */
+
+    TRACEEND("free",_udata,1);
+    return(SUCCEED);
+
+out:
+    TRACEFAIL("free");
+    return(FAIL);
+} /* end image_free() */
+
+
+/*-------------------------------------------------------------------------
+* Function: udata_copy
+*
+* Purpose: Simulates the copying of the user data structure utilized in the
+*          management of the "copying" of file images.
+*
+* Return: Address of "newly allocated" structure, if successful. Otherwise, it
+*         returns NULL.
+*
+* Programmer: Christian Chilan
+*
+* Date: October 3, 2011
+*
+*-------------------------------------------------------------------------
+*/
+static void *
+local_udata_copy(void *_udata)
+{
+    H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata;
+
+    TRACE0("udata_copy", 0,  _udata);
+
+#if 0
+    /* callback is only used if the application buffer is not actually copied */
+    if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
+        goto out;
+#endif
+
+    /* never copy so we only have one instance */
+    if (udata->ref_count == 0)
+	goto out;
+    udata->ref_count++;
+
+    TRACEEND("udata_copy",udata,1);
+    return(udata);
+
+out:
+    TRACEFAIL("udata_copy");
+    return NULL;
+} /* end udata_copy */
+
+
+/*-------------------------------------------------------------------------
+* Function: udata_free
+*
+* Purpose: Simulates deallocation of the user data structure utilized in the
+*          management of the "copying" of file images. The data structure is
+*          actually deallocated when there are no outstanding references.
+*
+* Return: SUCCEED or FAIL
+*
+* Programmer: Christian Chilan
+*
+* Date: October 3, 2011
+*
+*-------------------------------------------------------------------------
+*/
+static herr_t
+local_udata_free(void *_udata)
+{
+    H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata;
+
+    TRACE0("udata_free", 0, _udata);
+
+#if 0
+    /* callback is only used if the application buffer is not actually copied */
+    if (!(udata->flags & H5LT_FILE_IMAGE_DONT_COPY))
+        goto out;
+#endif
+
+    if (udata->ref_count == 0)
+        goto out;
+    udata->ref_count--;
+
+    TRACEEND("udata_free",udata,1);
+    return(SUCCEED);
+
+out:
+    TRACEFAIL("udata_free");
+    return(FAIL);
+} /* end udata_free */
+
+
+/* End of callbacks definitions for file image operations */
+
+hid_t
+NC4_image_init(NC_FILE_INFO_T* h5)
+{
+    hid_t		fapl = -1, file_id = -1; /* HDF5 identifiers */
+    unsigned            file_open_flags = 0;/* Flags for hdf5 open */
+    char                file_name[64];	/* Filename buffer */
+    size_t              alloc_incr;     /* Buffer allocation increment */
+    size_t              min_incr = 65536; /* Minimum buffer increment */
+    double              buf_prcnt = 0.1f;  /* Percentage of buffer size to set
+                                             as increment */
+    unsigned imageflags;
+    int create = 0;
+    H5LT_file_image_ud_t *udata = NULL;	/* Pointer to udata structure */
+
+    H5FD_file_image_callbacks_t callbacks = {&local_image_malloc, &local_image_memcpy,
+                                           &local_image_realloc, &local_image_free,
+                                           &local_udata_copy, &local_udata_free,
+                                           (void *)NULL};
+    static long         file_name_counter;
+
+    imageflags = h5->mem.imageflags;
+    create = h5->mem.created;
+
+    /* check arguments */
+    if (h5->mem.memio.memory == NULL) {
+	if(create) {
+	    if(h5->mem.memio.size == 0) h5->mem.memio.size = DEFAULT_CREATE_MEMSIZE;
+	    h5->mem.memio.memory = malloc(h5->mem.memio.size);
+	}  else
+	    goto out; /* open requires an input buffer */
+    } else if(h5->mem.memio.size == 0)
+	goto out;
+
+    /* Create FAPL to transmit file image */
+    if ((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+        goto out;
+
+    /* set allocation increment to a percentage of the supplied buffer size, or
+     * a pre-defined minimum increment value, whichever is larger
+     */
+    if ((buf_prcnt * h5->mem.memio.size) > min_incr)
+        alloc_incr = (size_t)(buf_prcnt * h5->mem.memio.size);
+    else
+        alloc_incr = min_incr;
+
+    /* Configure FAPL to use the core file driver */
+    if (H5Pset_fapl_core(fapl, alloc_incr, FALSE) < 0)
+        goto out;
+
+    /* Set callbacks for file image ops always */
+    {
+        /* Allocate buffer to communicate user data to callbacks */
+        if (NULL == (udata = (H5LT_file_image_ud_t *)calloc(1,sizeof(H5LT_file_image_ud_t))))
+            goto out;
+
+        /* Initialize udata with info about app buffer containing file image  and flags */
+        udata->app_image_ptr = h5->mem.memio.memory;
+        udata->app_image_size = h5->mem.memio.size;
+	h5->mem.memio.memory = NULL; /* move control */
+	h5->mem.memio.size = 0;
+        udata->fapl_image_ptr = NULL;
+        udata->fapl_image_size = 0;
+        udata->fapl_ref_count = 0;
+        udata->vfd_image_ptr = NULL;
+        udata->vfd_image_size = 0;
+        udata->vfd_ref_count = 0;
+        udata->flags = imageflags;
+        udata->ref_count = 1; /* corresponding to the first FAPL */
+	udata->h5 = h5;
+
+        /* copy address of udata into callbacks */
+        callbacks.udata = (void *)udata;
+        /* Set file image callbacks */
+        if (H5Pset_file_image_callbacks(fapl, &callbacks) < 0)
+            goto out;
+    }
+
+    /* Assign file image in user buffer to FAPL */
+    if (H5Pset_file_image(fapl, udata->app_image_ptr, udata->app_image_size) < 0)
+        goto out;
+
+    /* define a unique file name */
+    snprintf(file_name, (sizeof(file_name) - 1), "file_image_%ld", file_name_counter++);
+
+    /* set file open/create flags */
+    if(create)
+        file_open_flags = H5F_ACC_TRUNC; /* H5Fcreate does not like H5F_ACC_RDWR */
+    else if (imageflags & H5LT_FILE_IMAGE_OPEN_RW)
+        file_open_flags = H5F_ACC_RDWR;
+    else
+        file_open_flags = H5F_ACC_RDONLY;
+
+    /* Assign file image in FAPL to the core file driver */
+    if(create) {
+        if ((file_id = H5Fcreate(file_name, file_open_flags, H5P_DEFAULT, fapl)) < 0)
+            goto out;
+    } else {
+        if ((file_id = H5Fopen(file_name, file_open_flags, fapl)) < 0)
+            goto out;
+    }
+
+    /* Maintain a backward link */
+    h5->mem.udata = (void*)udata;
+    udata = NULL;
+
+done:
+    /* Reclaim the fapl object */
+    H5E_BEGIN_TRY {
+	if(fapl >= 0)
+            H5Pclose(fapl);
+    } H5E_END_TRY;
+    /* Return file identifier */
+    return file_id;
+
+out:
+    /* free up udata only on error */
+    if(udata != NULL) free(udata);
+    file_id = -1;
+    goto done;
+} /* end H5LTopen_file_image() */
+
+void
+NC4_image_finalize(void* _udata)
+{
+    if(_udata != NULL) {
+	H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t*)_udata;
+        /* checks reference counts before deallocating udata */
+#if 0
+        assert(udata->ref_count == 1 && udata->fapl_ref_count == 0 && udata->vfd_ref_count == 0);
+	if(udata->app_image_ptr != NULL) free(udata->app_image_ptr);
+	if(udata->fapl_image_ptr != NULL) free(udata->fapl_image_ptr);
+	if(udata->vfd_image_ptr != NULL) free(udata->vfd_image_ptr);
+#endif
+        free(udata);
+#ifdef TRACE_UDATA
+	fprintf(stderr,"\t>>>> freed: udata=%p\n",udata);
+#endif
+    }
+}
+
+int
+NC4_extract_file_image(NC_FILE_INFO_T* h5)
+{
+    int stat = NC_NOERR;
+    H5LT_file_image_ud_t *udata;
+
+    udata = (H5LT_file_image_ud_t *)h5->mem.udata;
+    assert(udata != NULL);
+
+    /* Fill in h5->mem.memio from udata */
+    h5->mem.memio.memory = udata->vfd_image_ptr;
+    h5->mem.memio.size = udata->vfd_image_size;
+
+    /* Move control */
+    udata->vfd_image_ptr = NULL;
+    udata->vfd_image_size = 0;
+    
+    return stat;
+}
+
+#ifdef TRACE
+
+static char*
+printudata(H5LT_file_image_ud_t* udata)
+{
+    char buf[8192];
+    char tmp[8192];
+    char* flags = "";
+
+    buf[0] = '\0';
+    if(udata == NULL) return strdup("");
+    strlcat(buf,"flags=",sizeof(buf));
+    flags = traceflags(udata->flags);
+    strlcat(buf,flags,sizeof(buf));
+    if(flags != NULL) free(flags);
+    snprintf(tmp,sizeof(tmp)," ref_count=%d",udata->ref_count);
+    strlcat(buf,tmp,sizeof(tmp));
+    snprintf(tmp,sizeof(tmp)," app=(%p,%lld)",udata->app_image_ptr,(long long)udata->app_image_size);
+    strlcat(buf,tmp,sizeof(tmp));
+    snprintf(tmp,sizeof(tmp)," fapl=(%p,%lld)[%d]",udata->fapl_image_ptr,(long long)udata->fapl_image_size,udata->fapl_ref_count);
+    strlcat(buf,tmp,sizeof(tmp));
+    snprintf(tmp,sizeof(tmp)," vfd=(%p,%lld)[%d]",udata->vfd_image_ptr,(long long)udata->vfd_image_size,udata->vfd_ref_count);
+    strlcat(buf,tmp,sizeof(tmp));
+    return strdup(buf);
+}
+
+static void
+trace(const char* fcn, H5FD_file_image_op_t op, void* _udata, ...)
+{
+    H5LT_file_image_ud_t *udata = NULL;
+    va_list ap;
+    char buf[16000];
+    char tmp[8192];
+    char* ud;
+
+    va_start(ap, _udata); /* Requires the last fixed parameter (to get the address) */
+    udata = (H5LT_file_image_ud_t *)_udata;
+    buf[0] = '\0';
+    snprintf(tmp,sizeof(tmp),"trace [ %s: op=%s: ",fcn,traceop(op));
+    strlcat(buf,tmp,sizeof(tmp));
+    if(strcmp("malloc",fcn)==0) {
+	size_t size = va_arg(ap,size_t);
+        snprintf(tmp,sizeof(tmp),"size=%lld",(long long)size);
+    } else if(strcmp("realloc",fcn)==0) {
+	void* ptr = va_arg(ap,void*);
+	size_t size = va_arg(ap,size_t);
+	snprintf(tmp,sizeof(tmp),"ptr=%p, size=%lld",ptr,(long long)size);
+    } else if(strcmp("free",fcn)==0) {
+	void* ptr = va_arg(ap,void*);
+	snprintf(tmp,sizeof(tmp),"ptr=%p",ptr);
+    } else if(strcmp("memcpy",fcn)==0) {
+	void* dest = va_arg(ap,void*);
+	void* src = va_arg(ap,void*);
+	size_t size = va_arg(ap,size_t);
+	snprintf(tmp,sizeof(tmp),"dest=%p, src=%p, size=%lld",dest,src,(long long)size);
+    } else if(strcmp("udata_copy",fcn)==0) {
+#ifdef TRACE_UDATA
+	snprintf(tmp,sizeof(tmp),"udata=%p",udata);
+#endif
+    } else if(strcmp("udata_free",fcn)==0) {
+#ifdef TRACE_UDATA
+	snprintf(tmp,sizeof(tmp),"udata=%p",udata);
+#endif
+    } else {
+	snprintf(tmp,sizeof(tmp),"unknown fcn: %s",fcn);
+    }
+    strlcat(buf,tmp,sizeof(buf));
+    ud = printudata(udata);
+    strlcat(buf,"\n\tudata=",sizeof(buf));
+    strlcat(buf,ud,sizeof(tmp));
+    free(ud);	
+    strlcat(buf,"\n",sizeof(buf));
+    va_end(ap);
+    fprintf(stderr,"%s",buf);
+    fflush(stderr);
+}
+
+static void
+traceend(const char* fcn, void* _udata, uintptr_t retval)
+{
+    char buf[16000];
+    char tmp[8192];
+    char* ud;
+    const char* tab = "    ";
+
+    buf[0] = '\0';
+    H5LT_file_image_ud_t *udata = (H5LT_file_image_ud_t *)_udata;
+    snprintf(tmp,sizeof(tmp),"%s]: retval=%p",tab,(void*)retval);
+    strlcat(buf,tmp,sizeof(buf));
+    strlcat(buf," udata=",sizeof(buf));
+    ud = printudata(udata);
+    strlcat(buf,ud,sizeof(tmp));
+    free(ud);	
+    strlcat(buf,"\n",sizeof(buf));
+    fprintf(stderr,"%s",buf);
+    fflush(stderr);
+}
+
+#endif /*TRACE*/
+
+#ifdef CATCH
+static void
+tracefail(const char* fcn)
+{
+    fprintf(stderr,"fail: %s\n",fcn);
+    fflush(stderr);
+}
+#endif /*CATCH*/
+
+#ifdef TRACE
+static char*
+traceflags(int flags)
+{
+    int i;
+    char buf[8192];
+    char tmp[8192];
+    buf[0] = '\0';
+    for(i=0;i<16;i++) {
+	tmp[0] = '\0';
+	if((flags & 1<<i) == 0) continue;
+        if(i > 0) strlcat(tmp,"|",sizeof(tmp));
+	switch(1<<i) {
+	case H5LT_FILE_IMAGE_OPEN_RW: /* 0x0001 Open image for read-write */
+	    strlcat(tmp,"OPEN_RW",sizeof(tmp));
+	    break;
+	case H5LT_FILE_IMAGE_DONT_COPY: /*0x0002 the HDF5 lib won't copy   */
+	    strlcat(tmp,"DONT_COPY",sizeof(tmp));
+	    break;
+	case H5LT_FILE_IMAGE_DONT_RELEASE: /* 0x0004 The HDF5 lib won't
+                                              deallocate user supplied image
+                                              buffer. The user application
+                                              is reponsible for doing so. */
+	    strlcat(tmp,"DONT_RELEASE",sizeof(tmp));
+	    break;
+	default: break;
+	}
+	strlcat(buf,tmp,sizeof(buf));
+    }
+    return strdup(buf);
+}
+
+static const char*
+traceop(H5FD_file_image_op_t op)
+{
+    const char* sop = NULL;
+    switch ( op ) {
+    case H5FD_FILE_IMAGE_OP_NO_OP:
+	sop = "NO_OP"; break;
+    case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_SET:
+	sop = "PROPERTY_LIST_SET"; break;
+    case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_COPY:
+	sop = "PROPERTY_LIST_COPY"; break;
+    case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_GET:
+	sop = "PROPERTY_LIST_GET"; break;
+    case H5FD_FILE_IMAGE_OP_PROPERTY_LIST_CLOSE:
+	sop = "PROPERTY_LIST_CLOSE"; break;
+    case H5FD_FILE_IMAGE_OP_FILE_OPEN:
+	sop = "FILE_OPEN"; break;
+    case H5FD_FILE_IMAGE_OP_FILE_RESIZE:
+	sop = "FILE_RESIZE"; break;
+    case H5FD_FILE_IMAGE_OP_FILE_CLOSE:
+	sop = "FILE_CLOSE"; break;
+    default: break;
+    }
+    return sop;
+}
+
+#endif
+