/**
 * @internal Netcdf-4 files might have more than one unlimited
 * dimension, but return the first one anyway.
 *
 * @note that this code is inconsistent with nc_inq
 *
 * @param ncid File and group ID.
 * @param unlimdimidp Pointer that gets ID of first unlimited
 * dimension, or -1.
 *
 * @return ::NC_NOERR No error.
 * @return ::NC_EBADID Bad ncid.
 * @author Ed Hartnett
 */


/**
 * @internal Given dim name, find its id.
 *
 * @param ncid File and group ID.
 * @param name Name of the dimension to find.
 * @param idp Pointer that gets dimension ID.
 *
 * @return ::NC_NOERR No error.
 * @return ::NC_EBADID Bad ncid.
 * @return ::NC_EBADDIM Dimension not found.
 * @return ::NC_EINVAL Invalid input. Name must be provided.
 * @author Ed Hartnett
 */


/**
 * @internal Returns an array of unlimited dimension ids.The user can
 * get the number of unlimited dimensions by first calling this with
 * NULL for the second pointer.
 *
 * @param ncid File and group ID.
 * @param nunlimdimsp Pointer that gets the number of unlimited
 * dimensions. Ignored if NULL.
 * @param unlimdimidsp Pointer that gets arrray of unlimited dimension
 * ID. Ignored if NULL.
 *
 * @return ::NC_NOERR No error.
 * @return ::NC_EBADID Bad ncid.
 * @author Ed Hartnett, Dennis Heimbigner
 */


/**
 * @internal Get the format (i.e. NC_FORMAT_NETCDF4 pr
 * NC_FORMAT_NETCDF4_CLASSIC) of an open netCDF-4 file.
 *
 * @param ncid File ID (ignored).
 * @param formatp Pointer that gets the constant indicating format.

 * @return ::NC_NOERR No error.
 * @return ::NC_EBADID Bad ncid.
 * @author Ed Hartnett
 */


/**
 * @internal Return the extended format (i.e. the dispatch model),
 * plus the mode associated with an open file.
 *
 * @param ncid File ID (ignored).
 * @param formatp a pointer that gets the extended format. Note that
 * this is not the same as the format provided by nc_inq_format(). The
 * extended foramt indicates the dispatch layer model. NetCDF-4 files
 * will always get NC_FORMATX_NC4.
 * @param modep a pointer that gets the open/create mode associated with
 * this file. Ignored if NULL.

 * @return ::NC_NOERR No error.
 * @return ::NC_EBADID Bad ncid.
 * @author Dennis Heimbigner
 */


/**
 * @internal Initialize netCDF-4. If user-defined format(s) have been
 * specified in configure, load their dispatch table(s).
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */


/**
 * @internal Finalize netCDF-4.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */


/*
 *  check all vars in file which are (text/numeric) compatible with TYPE
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type text
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type uchar
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type schar
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type short
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type int
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type long
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type float
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type double
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type ushort
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type uint
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type longlong
 */


/*
 *  for _text tests, check all attributes in file which are of text type
 *  Note no NC_ERANGE check for text attributes as text is not convertible to
 *  any other numerical data types (i.e. NC_ECHAR)
 *
 *  for other tests, check all numerical attributes in file against values
 *  outside range of type ulonglong
 */


/* wrapper for inRange to handle special NC_BYTE/uchar adjustment */
/* this function checks whether "value" to be casted to type "itype" is
 * within the range of external "xtype".
 */


/*
 *  Does x == y, where one is internal and other external (netCDF)?
 *  Use tolerant comparison based on IEEE FLT_EPSILON or DBL_EPSILON.
 */


/* this function is for the APIs without itype, i.e. xtype == itype */


/*
   define global variables:
   dim_name, dim_len,
   var_name, var_type, var_rank, var_shape, var_natts, var_dimid, var_nels
   att_name, gatt_name, att_type, gatt_type, att_len, gatt_len
 */


/* Remove a file; do not care if it does not exist */


/* TODO:
 * This diagnostic doesn't fit very well with the diagnostic message
 * "architecture" of this program.
 */


/* Write a file with 2 dims and 6 vars, including some sample data. */


/* Add some attributes to the vars of an open file. */


/* Read the file and check the data. */


/* static void */
/* parray(const char *label, size_t count, const size_t array[]) */
/* { */
/* 	(void) fprintf(stdout, "%s", label); */
/* 	(void) fputc('\t',stdout);	 */
/* 	for(; count != 0; count--, array++) */
/* 		(void) fprintf(stdout," %lu", (unsigned long) *array); */
/* } */


/*
 * Fill typed array element with specified value, that is
 *
 * 	v[ii] = val;
 */


/*
 * Compare typed array element with specified value, that is return
 *
 * 	(v[ii] != val)
 *
 * returns 0 if equal, 1 if not equal
 */


/* Duplicate an NC_memio instance; needed to avoid
   attempting to use memory that might have been realloc'd
   Allow the new memory to be larger than the src memory
*/


/*
Given an ncid of a created file, fill in the meta-data
and data as described by the above CDL. Do not close
the file.
*/


/*
Create our reference file as a real on-disk file
and read it back into memory
*/


/* Use this to force significant file size increase */


/* Verify the content of a file */


/* External Discovery Functions */


/* Make this explicit */
/*
 * The "can_apply" callback returns positive a valid combination, zero for an
 * invalid combination and negative for an error.
 */


/**************************************************/
/* Topo sort in postfix order */


/*
Do depth first search
*/


/**************************************************/
/* Mark the offset and length of each var/field
   inside the raw dapdata.
   Assumes it is called before byte swapping, so we
   need to do swapping of counts and the final remote checksum.
*/


/* Includes opaque and enum */


/**************************************************/
/*
Walk the (toplevel) var's data to get to the count'th instance.
For efficiency, it can be supplied with a previous case.
Assumes it is called after byte swapping and offsetting.
Assumes that var is not fixed size.
*/


/*
Force dap4 access to be read-only
*/


/**************************************************/
/*
Following functions basically return the netcdf-4 value WRT to the nc4id.
However, it is necessary to modify the grpid(ncid) to point to the substrate.
*/


/* Force specific format */


/*
Override nc_inq_dim to handle the fact
that unlimited dimensions will not have
a proper size because the substrate has
never (not yet) been written.
*/


/* Return 1 if this machine is little endian */


/* Compute the size of an atomic type, except opaque */


/* Caller must free return value */


/*
create the last part of the fqn
(post groups)
*/


/* Parse an fqn into a sequence of names;
   using '/', and then (conditionally) '.' */


/**
Wrap mktmp and return the generated name
*/


/**
Given a raw response, attempt to infer the mode: DMR, DAP, DSR.
Since DSR is not standardizes, it becomes the default.
*/


/**
 * Print an arbitrary file and its subnodes in xml
 * Handling newlines is a bit tricky because they may be
 * embedded for e.g. groups, enums,
 * etc.	 So the rule is that the
 * last newline is elided and left
 * for the caller to print.
 * Exceptions: printMetadata
 * printDimrefs.
 *
 * @param out - the output buffer
 * @param node - the tree to print
 * @param depth - the depth of our code
 */


/*************************************************/
/* Misc. Static Utilities */


/*
Future optimization. It is sometimes the case that
some suffix of the index set cover a contiguous chunk
of the index space. This happens when the index positions
starting at p upto rank have a start of zero, a count
the same as the max index size, and a stride of one.
Then we can effectively tread indices 0 thru p-1 as
a short odometer that steps through the chunks.
In practice, it is usually only ncdump that does this.
A simpler optimization that is to see if the (start,count)
covers the whole index space and has stride one.
We can detect and use this optimization elsewhere to
circumvent use of the odometer altogether.
See d4odometer#NCD4_odomWhole().
*/


/* Convert current d4odometer settings to a single integer offset*/


/**************************************************/
/*
Given a d4odometer, compute the total
number of elements in its space.
*/


/* Scalar Odometer support */


/*
For debugging purposes, it is desirable to fake an nccopy
bv inserting the data into the substrate and then writing it out.
*/


/* Provide a string printer that can be called from gdb */


/* Dump the variables in a group */


/*
Build a single instance of a type. The blobs
argument accumulates any malloc'd data so we can
reclaim it in case of an error.

Activity is to walk the variable's data to
produce a copy that is compatible with the
netcdf4 memory format.

Assumes that NCD4_processdata has been called.
*/


/*
Extract and oob a single string instance
*/


/*
Move a dap4 variable length opaque out of band.
We treat as if it was (in cdl) ubyte(*).
*/


/*
Assumes that compiler->swap is true; does necessary
byte swapping.
*/


/*
Remember that the base type of var is a vlen.
*/


/* Reclaim an NCD4INFO instance */


/* Recursively walk the DOM tree to create the metadata */


/*
Specialized version of parseFields that is used
to attach a singleton field to a vlentype
*/


/* Determine is a name is reserved */


/* If a node has the UCARTAGORIGTYPE attribute,
   then capture that annotation. */


/* Locate an attribute.
   If not found, then *attrp will be null
*/


/*
Parse a simple string of digits into an signed long long
Return the value.
*/


/*
Convert a sequence of fqn names into a specific node.
WARNING: This is highly specialized in that it assumes
that the final object is one of: dimension, type, or var.
This means that e.g. groups, attributes, econsts, cannot
be found by this procedure.
*/


/* Binary search the set of set of atomictypes */


/*
Classify inserts the node into the proper container list
based on the node's sort.
*/


/*
Classify inserts the node into the proper parser global list
based on the node's sort.
*/


/* Try to remove excess text from a value set */


/* Create an empty NCD4meta object for
   use in subsequent calls
   (is the the right src file to hold this?)
*/


/* Recursively walk the tree to create the metadata */


/* Insert a group into the groupbyid for a group */


/* Insert a var into the varbyid for a group */


/**
Convert a list of attribute value strings
into a memory chunk capable of being passed
to nc_put_att().
*/


/*
Given an enum type, and a string representing an econst,
convert to integer.
Note: this will work if the econst string is a number or a econst name
*/


/* Tag each compound type as fixed size or not
   Assumes:
	- atomic types defined and marked
	- topo sorted
*/


/*
Compute the in-memory size of an instance of a type.
Note that nc_inq_type is used, so that C struct field
alignment is taken into account for compound types.
The variables total size will be this * dimproduct.
*/


/* Compute the dap data size for each type; note that this
   is unlikely to be the same as the meta.memsize unless
   the type is atomic and is <= NC_UINT64.
*/


/* Determine if this version of curl supports
       "file://..." &/or "https://..." urls.
*/


/*
"Inverse" of set_curlflag;
Given a flag and value, it updates state.
Update a specific flag from state->curlflags.
*/


/* called when the parser finds an internal doctype subset*/


/* parse the given xml string and return an ezxml structure*/


/* returns a new empty ezxml structure with the given root tag name*/


/* removes a tag along with its subtags without freeing its memory*/


/** \ingroup user_types
Create an opaque type. Provide a size and a name.

\param ncid \ref ncid
\param size The size of each opaque object in bytes.
\param name \ref object_name of the new type.
\param xtypep Pointer where the new typeid for this type is returned.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad \ref ncid.
\returns ::NC_EBADTYPE Bad type id.
\returns ::NC_ENOTNC4 Not an netCDF-4 file, or classic model enabled.
\returns ::NC_EHDFERR An error was reported by the HDF5 layer.
\returns ::NC_ENAMEINUSE That name is in use.
\returns ::NC_EMAXNAME Name exceeds max length NC_MAX_NAME.
\returns ::NC_EBADNAME Name contains illegal characters.
\returns ::NC_EPERM Attempt to write to a read-only file.
\returns ::NC_ENOTINDEFINE Not in define mode.
 */


/** \ingroup user_types
Learn about an opaque type.

\param ncid \ref ncid

\param xtype Typeid to inquire about.

\param name The \ref object_name of this type will be
copied here. \ref ignored_if_null.

\param sizep The size of the type will be copied here. \ref
ignored_if_null.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad \ref ncid.
\returns ::NC_EBADTYPE Bad type id.
\returns ::NC_ENOTNC4 Not an netCDF-4 file, or classic model enabled.
\returns ::NC_EHDFERR An error was reported by the HDF5 layer.
 */


/*
 * Verify that a name string is valid syntax.  The allowed name
 * syntax (in RE form) is:
 *
 * ([a-zA-Z0-9_]|{UTF8})([^\x00-\x1F\x7F/]|{UTF8})*
 *
 * where UTF8 represents a multibyte UTF-8 encoding.  Also, no
 * trailing spaces are permitted in names.  This definition
 * must be consistent with the one in ncgen.l.  We do not allow '/'
 * because HDF5 does not permit slashes in names as slash is used as a
 * group separator.  If UTF-8 is supported, then a multi-byte UTF-8
 * character can occur anywhere within an identifier.  We later
 * normalize UTF-8 strings to NFC to facilitate matching and queries.
 */


/*!
\ingroup attributes
Get an attribute of any type.

The nc_get_att() functions works for any type of attribute, and must
be used to get attributes of user-defined type. We recommend that they
type safe versions of this function be used for atomic data types.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL
for a global attribute.

\param name Attribute name.

\param value Pointer to location for returned attribute value(s). All
elements of the vector of attribute values are returned, so you must
allocate enough space to hold them. Before using the value as a C
string, make sure it is null-terminated. Call nc_inq_attlen() first to
find out the length of the attribute.

\note See documentation for nc_get_att_string() regarding a special
case where memory must be explicitly released.

\returns ::NC_NOERR for success.
\returns ::NC_EBADID Bad ncid.
\returns ::NC_ENOTVAR Bad varid.
\returns ::NC_EBADNAME Bad name. See \ref object_name.
\returns ::NC_EINVAL Invalid parameters.
\returns ::NC_ENOTATT Can't find attribute.
\returns ::NC_ECHAR Can't convert to or from NC_CHAR.
\returns ::NC_ENOMEM Out of memory.
\returns ::NC_ERANGE Data conversion went out of range.

Example

Here is an example using nc_get_att() from nc_test4/tst_vl.c creates a
VLEN attribute, then uses nc_get_att() to read it.

@code
#define FILE_NAME "tst_vl.nc"
#define VLEN_NAME "vlen_name"
#define ATT_NAME "att_name"

      int ncid, typeid;
      nc_vlen_t data[DIM_LEN], data_in[DIM_LEN];
      ...

      if (nc_create(FILE_NAME, NC_NETCDF4, &ncid)) ERR;
      if (nc_def_vlen(ncid, VLEN_NAME, NC_INT, &typeid)) ERR;
      ...
      if (nc_put_att(ncid, NC_GLOBAL, ATT_NAME, typeid, DIM_LEN, data)) ERR;
      if (nc_close(ncid)) ERR;

      ...
      if (nc_open(FILE_NAME, NC_NOWRITE, &ncid)) ERR;
      if (nc_get_att(ncid, NC_GLOBAL, ATT_NAME, data_in)) ERR;
      ...
      if (nc_close(ncid)) ERR;
@endcode

\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/*!
\ingroup attributes
Get a variable-length string attribute.

This function gets an attribute from netCDF file. The nc_get_att()
function works with any type of data including user defined types, but
this function will retrieve attributes which are of type
variable-length string.

\note Note that unlike most other nc_get_att functions,
nc_get_att_string() allocates a chunk of memory which is returned to
the calling function.  This chunk of memory must be specifically
deallocated with nc_free_string() to avoid any memory leaks.  Also
note that you must still preallocate the memory needed for the array
of pointers passed to nc_get_att_string().

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL
for a global attribute.

\param name Attribute name.

\param value Pointer to location for returned attribute value(s). All
elements of the vector of attribute values are returned, so you must
allocate enough space to hold them. If you don't know how much
space to reserve, call nc_inq_attlen() first to find out the length of
the attribute.

\returns ::NC_NOERR for success.
\returns ::NC_EBADID Bad ncid.
\returns ::NC_ENOTVAR Bad varid.
\returns ::NC_EBADNAME Bad name. See \ref object_name.
\returns ::NC_EINVAL Invalid parameters.
\returns ::NC_ENOTATT Can't find attribute.
\returns ::NC_ECHAR Can't convert to or from NC_CHAR.
\returns ::NC_ENOMEM Out of memory.
\returns ::NC_ERANGE Data conversion went out of range.

\section nc_get_att_string_example Example

\code{.c}
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <netcdf.h>

void check(int stat) {
  if (stat != NC_NOERR) {
    printf("NetCDF error: %s\n", nc_strerror(stat));
    exit(1);
  }
}

int main(int argc, char ** argv) {
  int stat = 0;

  int ncid = 0;
  stat = nc_open("test.nc", NC_NOWRITE, &ncid); check(stat);

  int varid = 0;
  stat = nc_inq_varid(ncid, "variable", &varid); check(stat);

  size_t attlen = 0;
  stat = nc_inq_attlen(ncid, varid, "attribute", &attlen); check(stat);

  char **string_attr = (char**)malloc(attlen * sizeof(char*));
  memset(string_attr, 0, attlen * sizeof(char*));

  stat = nc_get_att_string(ncid, varid, "attribute", string_attr); check(stat);

  for (size_t k = 0; k < attlen; ++k) {
    printf("variable:attribute[%d] = %s\n", k, string_attr[k]);
  }

  stat = nc_free_string(attlen, string_attr); check(stat);

  free(string_attr);

  stat = nc_close(ncid); check(stat);

  return 0;
}
\endcode
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
\ingroup attributes
Return information about a netCDF attribute.

The function nc_inq_att returns the attribute's type and length.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL
for a global attribute.

\param name Pointer to the location for the returned attribute \ref
object_name. \ref ignored_if_null.

\param xtypep Pointer to location for returned attribute \ref
data_type. \ref ignored_if_null.

\param lenp Pointer to location for returned number of values
currently stored in the attribute. For attributes of type ::NC_CHAR,
you should not assume that this includes a trailing zero byte; it
doesn't if the attribute was stored without a trailing zero byte, for
example from a FORTRAN program. Before using the value as a C string,
make sure it is null-terminated. \ref ignored_if_null.

\section nc_inq_att_example Example

Here is an example using nc_inq_att() to find out the type and length of
a variable attribute named valid_range for a netCDF variable named rh
and a global attribute named title in an existing netCDF dataset named
foo.nc:

\code
     #include <netcdf.h>
        ...
     int  status;
     int  ncid;
     int  rh_id;
     nc_type vr_type, t_type;
     size_t  vr_len, t_len;

        ...
     status = nc_open("foo.nc", NC_NOWRITE, &ncid);
     if (status != NC_NOERR) handle_error(status);
        ...
     status = nc_inq_varid (ncid, "rh", &rh_id);
     if (status != NC_NOERR) handle_error(status);
        ...
     status = nc_inq_att (ncid, rh_id, "valid_range", &vr_type, &vr_len);
     if (status != NC_NOERR) handle_error(status);
     status = nc_inq_att (ncid, NC_GLOBAL, "title", &t_type, &t_len);
     if (status != NC_NOERR) handle_error(status);
\endcode

\returns ::NC_NOERR no error.
\returns ::NC_EBADID bad ncid.
\returns ::NC_ENOTVAR bad varid.
\returns ::NC_EBADGRPID bad group ID.
\returns ::NC_EBADNAME bad name.
\returns ::NC_ENOTATT attribute not found.
\returns ::NC_ECHAR illegal conversion to or from NC_CHAR.
\returns ::NC_ENOMEM out of memory.
\returns ::NC_ERANGE range error when converting data.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
\ingroup attributes
Find an attribute ID.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL for
a global attribute.

\param name Attribute \ref object_name.

\param idp Pointer to location for returned attribute number that
specifies which attribute this is for this variable (or which global
attribute). If you already know the attribute name, knowing its number
is not very useful, because accessing information about an attribute
requires its name.

\section nc_inq_attid_example Example

Here is an example using nc_inq_attid() from nc_test4/tst_vars2.c. In
this example three attributes are created in a file. Then it is
re-opened, and their IDs are checked. They will be 0, 1, and 2, in
the order that the attributes were written to the file.

\code
     #include <netcdf.h>
     ...
     printf("**** testing fill value with three other attributes...");
     {
#define NUM_LEADERS 3
         char leader[NUM_LEADERS][NC_MAX_NAME + 1] = {"hair_length_of_strategoi",
                                                      "hair_length_of_Miltiades",
                                                      "hair_length_of_Darius_I"};
         short hair_length[NUM_LEADERS] = {3, 11, 4};
         short short_in;
         int a;

         if (nc_create(FILE_NAME, cmode, &ncid)) ERR;
         if (nc_def_dim(ncid, DIM1_NAME, DIM1_LEN, &dimids[0])) ERR;
         if (nc_def_var(ncid, VAR_NAME, NC_BYTE, NUM_DIMS, dimids, &varid)) ERR;
         for (a = 0; a < NUM_LEADERS; a++)
            if (nc_put_att_short(ncid, varid, leader[a], NC_SHORT, 1, &hair_length[a])) ERR;
         if (nc_put_att_schar(ncid, varid, _FillValue, NC_BYTE, 1, &fill_value)) ERR;
         if (nc_close(ncid)) ERR;

         if (nc_open(FILE_NAME, NC_NOWRITE, &ncid)) ERR;
         for (a = 0; a < NUM_LEADERS; a++)
         {
            ...
            if (nc_inq_attid(ncid, 0, leader[a], &attnum_in)) ERR;
            if (attnum_in != a) ERR;
         }

         if (nc_close(ncid)) ERR;
\endcode

\returns ::NC_NOERR no error.
\returns ::NC_EBADID bad ncid.
\returns ::NC_ENOTVAR bad varid.
\returns ::NC_EBADGRPID bad group ID.
\returns ::NC_EBADNAME bad name.
\returns ::NC_ENOTATT attribute not found.
\returns ::NC_ENOMEM out of memory.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
\ingroup attributes
Find the name of an attribute.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL
for a global attribute.

\param attnum Attribute number. The attributes for each variable are
numbered from 0 (the first attribute) to natts-1, where natts is the
number of attributes for the variable, as returned from a call to
nc_inq_varnatts().

\param name Pointer to the location for the returned attribute \ref
object_name.

\section nc_inq_attname_example Example

Here is an example from nc_test4/tst_atts3.c a variable of every type
is added to a file, with names from the 'names' array. Then the file
is re-opened, and the names of the attributes are checked in a for
loop.

\code
     #include <netcdf.h>
     ...
#define NUM_ATTS 8
#define ATT_MAX_NAME 25
int
tst_att_ordering(int cmode)
{
   int ncid;
   char name[NUM_ATTS][ATT_MAX_NAME + 1] = {"Gc", "Gb", "Gs", "Gi", "Gf",
					    "Gd", "Gatt-name-dashes", "Gatt.name.dots"};
   int len[NUM_ATTS] = {0, 2, 3, 3, 3, 3, 1, 1};
   signed char b[2] = {-128, 127};
   short s[3] = {-32768, 0, 32767};
   int i[3] = {42, 0, -42};
   float f[3] = {42.0, -42.0, 42.0};
   double d[3] = {420.0, -420.0, 420.0};
   int att_name_dashes = -1, att_name_dots = -2;
   char name_in[NC_MAX_NAME];
   int j;

   if (nc_create(FILE_NAME, cmode, &ncid)) ERR;
   ...
   if (nc_put_att_text(ncid, NC_GLOBAL, name[0], len[0], NULL)) ERR;
   if (nc_put_att_schar(ncid, NC_GLOBAL, name[1], NC_BYTE, len[1], b)) ERR;
   if (nc_put_att_short(ncid, NC_GLOBAL, name[2], NC_SHORT, len[2], s)) ERR;
   if (nc_put_att_int(ncid, NC_GLOBAL, name[3], NC_INT, len[3], i)) ERR;
   if (nc_put_att_float(ncid, NC_GLOBAL, name[4], NC_FLOAT, len[4], f)) ERR;
   if (nc_put_att_double(ncid, NC_GLOBAL, name[5], NC_DOUBLE, len[5], d)) ERR;
   if (nc_put_att_int(ncid, NC_GLOBAL, name[6], NC_INT, len[6], &att_name_dashes)) ERR;
   if (nc_put_att_int(ncid, NC_GLOBAL, name[7], NC_INT, len[7], &att_name_dots)) ERR;
   if (nc_close(ncid)) ERR;

   ...
   if (nc_open(FILE_NAME, 0, &ncid)) ERR;
   for (j = 0; j < NUM_ATTS; j++)
   {
      if (nc_inq_attname(ncid, NC_GLOBAL, j, name_in)) ERR;
      if (strcmp(name_in, name[j])) ERR;
   }

   if (nc_close(ncid)) ERR;

\endcode

\returns ::NC_NOERR no error.
\returns ::NC_EBADID bad ncid.
\returns ::NC_ENOTVAR bad varid.
\returns ::NC_EBADGRPID bad group ID.
\returns ::NC_EBADNAME bad name.
\returns ::NC_ENOTATT attribute not found.
\returns ::NC_ECHAR illegal conversion to or from NC_CHAR.
\returns ::NC_ENOMEM out of memory.
\returns ::NC_ERANGE range error when converting data.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
\ingroup attributes
Find number of global or group attributes.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param nattsp Pointer where number of global or group attributes will be
written. \ref ignored_if_null.

\section nc_inq_natts_example Example

Here is an example from

\code
     #include <netcdf.h>
     ...
int
check_4D_example(char *file_name, int expected_format)
{
   int ncid;
   int format, ndims_in, nvars_in, natts_in;
   ...

   if (nc_open(file_name, 0, &ncid)) ERR;
   ...
   if (nc_inq_natts(ncid, &natts_in)) ERR;
   if (natts_in != 0) ERR;

\endcode

\returns ::NC_NOERR no error.
\returns ::NC_EBADID bad ncid.
\returns ::NC_EBADGRPID bad group ID.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
\ingroup attributes
Find the type of an attribute.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL
for a global or group attribute.

\param name Attribute \ref object_name.

\param xtypep Pointer to location for returned attribute \ref data_type.

\section nc_inq_atttype_example Example

Here is an example from nc_test4/tst_h_refs.c. In this example, a file
with an integer attribute is open. It's type is confirmed to be
NC_INT.

\code
     #include <netcdf.h>
     ...
    printf("*** Checking accessing file through netCDF-4 API...");
    {
	int ncid, varid, attid;
	nc_type type;

	if (nc_open(FILE_NAME, NC_NOWRITE, &ncid)) ERR;
        ...
	if (nc_inq_atttype(ncid, NC_GLOBAL, INT_ATT_NAME, &type)) ERR;
	if (type != NC_INT) ERR;

\endcode

\returns ::NC_NOERR no error.
\returns ::NC_EBADID bad ncid.
\returns ::NC_ENOTVAR bad varid.
\returns ::NC_EBADGRPID bad group ID.
\returns ::NC_EBADNAME bad name.
\returns ::NC_ENOTATT attribute not found.
\returns ::NC_ECHAR illegal conversion to or from NC_CHAR.
\returns ::NC_ENOMEM out of memory.
\returns ::NC_ERANGE range error when converting data.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
\ingroup attributes
Find the length of an attribute.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL
for a global or group attribute.

\param name Attribute \ref object_name.

\param lenp Pointer to location for returned number of values
currently stored in the attribute. Before using the value as a C
string, make sure it is null-terminated. \ref ignored_if_null.

\section nc_inq_attlen_example Example

Here is an example from nc_test4/tst_h_scalar.c which checks the
attributes of an already-open netCDF file. In this code, the length of
two attributes are checked, and found to be 1.

\code
     #include <netcdf.h>
     ...
int
check_attrs(int ncid, int obj)
{
    int attid;
    int natts = 0;
    size_t len;
    nc_type type;
    char *vlstr;
    char fixstr[10];
    int x;

    ...
    if (nc_inq_attlen(ncid, obj, VSTR_ATT1_NAME, &len)) ERR_GOTO;
    if (len != 1) ERR_GOTO;
    ...
    if (nc_inq_attlen(ncid, obj, VSTR_ATT2_NAME, &len)) ERR_GOTO;
    if (len != 1) ERR_GOTO;

\endcode

\returns ::NC_NOERR no error.
\returns ::NC_EBADID bad ncid.
\returns ::NC_ENOTVAR bad varid.
\returns ::NC_EBADGRPID bad group ID.
\returns ::NC_EBADNAME bad name.
\returns ::NC_ENOTATT attribute not found.
\returns ::NC_ECHAR illegal conversion to or from NC_CHAR.
\returns ::NC_ENOMEM out of memory.
\returns ::NC_ERANGE range error when converting data.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
\ingroup user_types
Learn if two types are equal.

\note User-defined types in netCDF-4/HDF5 files must be committed to
the file before nc_inq_type_equal() will work on the type. For
uncommitted user-defined types, nc_inq_type_equal() will return
::NC_EHDFERR. Commit types to the file with a call to nc_enddef().

\param ncid1 \ref ncid of first typeid.
\param typeid1 First typeid.
\param ncid2 \ref ncid of second typeid.
\param typeid2 Second typeid.
\param equal Pointer to int. A non-zero value will be copied here if
the two types are equal, a zero if they are not equal.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad \ref ncid.
\returns ::NC_EBADTYPE Bad type id.
\returns ::NC_ENOTNC4 Not an netCDF-4 file, or classic model enabled.
\returns ::NC_EHDFERR An error was reported by the HDF5 layer. This
will occur if either of the types have not been committed to the file
(with an nc_enddef()).

\author Dennis Heimbigner, Ward Fisher, Ed Hartnett
 */


/** \ingroup user_types

Find a type by name. Given a group ID and a type name, find the ID of
the type. If the type is not found in the group, then the parents are
searched. If still not found, the entire file is searched.

\param ncid \ref ncid
\param name \ref object_name of type to search for.
\param typeidp Typeid of named type will be copied here, if it is
found.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad \ref ncid.
\returns ::NC_EBADTYPE Bad type id.
\returns ::NC_ENOTNC4 Not an netCDF-4 file, or classic model enabled.
\returns ::NC_EHDFERR An error was reported by the HDF5 layer.
\author Ed Hartnett, Dennis Heimbigner
 */


/** \ingroup user_types
Learn about a user defined type.

Given an ncid and a typeid, get the information about a user defined
type. This function will work on any user defined type, whether
compound, opaque, enumeration, or variable length array.

\param ncid \ref ncid

\param xtype The typeid

\param name The \ref object_name will be copied here. \ref
ignored_if_null.

\param size the (in-memory) size of the type in bytes will be copied
here. VLEN type size is the size of nc_vlen_t. String size is returned
as the size of a character pointer. The size may be used to malloc
space for the data, no matter what the type. \ref ignored_if_null.

\param base_nc_typep The base type will be copied here for enum and
VLEN types. \ref ignored_if_null.

\param nfieldsp The number of fields will be copied here for enum and
compound types. \ref ignored_if_null.

\param classp Return the class of the user defined type, ::NC_VLEN,
::NC_OPAQUE, ::NC_ENUM, or ::NC_COMPOUND. \ref ignored_if_null.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad \ref ncid.
\returns ::NC_EBADTYPE Bad type id.
\returns ::NC_ENOTNC4 Not an netCDF-4 file, or classic model enabled.
\returns ::NC_EHDFERR An error was reported by the HDF5 layer.
\author Ed Hartnett, Dennis Heimbigner
 */


/**
 * @internal Initialize odometer.
 *
 * @param odom Pointer to odometer.
 * @param rank
 * @param start Start indices.
 * @param edges Counts.
 * @param stride Strides.
 *
 */


/**
 * @internal Return true if there is more.
 *
 * @param odom Pointer to odometer.
 *
 * @return True if there is more, 0 otherwise.
 */


/** \internal
\ingroup variables
*/


/* Null terminate the byte string without extending its length */


/* Remove char at position i */


/* Trim trailing whitespace, up to n characters. */
/* If no whitespace up to the last character, set */
/* the last character to null, else set the first */
/* whitespace character to null. */


/* Compute month and day from year and day-of-year.
 *
 *	Input:
 *		doy	     (int)  (day-of-year)
 *		date->year   (long)  (year since 0 BC)
 *              date->timeType (CdTimetype) (time type)
 *              date->baseYear   base year for relative times
 *	Output:
 *		date->month  (short)  (month in year)
 *		date->day    (short)  (day in month)
 *
 *
 * Derived from NRL NEONS V3.6.
 */


/* Compute day-of-year from year, month and day
 *
 *	Input:
 *		date->year  (long)  (year since 0 BC)
 *		date->month (short)  (month in year)
 *		date->day   (short)  (day in month)
 *              date->baseYear   base year for relative times
 *	Output: doy         (int)  (day-of-year)
 *
 * Derived from NRL NEONS V3.6
 */


/* Convert epochal time (hours since 00 jan 1, 1970)
 *   to human time (structured)
 *
 * Input:
 *   etime = epochal time representation
 *   timeType = time type (e.g., CdChron, CdClim, etc.) as defined in cdms.h
 *   baseYear = base real, used for relative time types only
 *
 * Output: htime = human (structured) time representation
 *
 * Derived from NRL Neons V3.6
 */


/* Add 'nDel' times 'delTime' to epochal time 'begEtm',
 * return the result in epochal time 'endEtm'.
 */


/* Parse relative units, returning the unit and base component time. */
/* Function returns 1 if error, 0 on success */


/* ca - cb in Gregorian calendar */
/* Result is in hours. */


/* Return -1, 0, 1 as ca is less than, equal to, */
/* or greater than cb, respectively. */


/* ca - cb in Julian calendar.  Result is in hours. */


/* ca - cb in mixed Julian/Gregorian calendar. */
/* Result is in hours. */


/* Divide ('endEtm' - 'begEtm') by 'delTime',
 * return the integer portion of the result in 'nDel'.
 */


/* Value is in hours. Translate to units. */


/* Map to old timetypes */


/* Convert human time to epochal time (hours since 00 jan 1, 1970)
 *
 * Input: htime = human time representation
 *
 * Output: etime = epochal time representation
 *
 * Derived from NRL Neons V3.6
 */


/* Validate the component time, return 0 if valid, 1 if not */


/* Convert ct to relunits (unit, basetime) */
/* in the mixed Julian/Gregorian calendar. */
/* unit is anything but year, season, month. unit and basetime are */
/* from the parsed relunits. Return result in reltime. */


/* Add (value,unit) to comptime. */
/* value is in hours. */
/* calendar is anything but cdMixed. */


/* Add value in hours to ct, in the mixed Julian/Gregorian
 * calendar. */


/* Return value expressed in hours. */


/* Convert relative time (reltime, unit, basetime) to comptime in the
 * mixed Julian/Gregorian calendar. unit is anything but year, season,
 * month. unit and basetime are from the parsed relunits. Return
 * result in comptime. */


/* rkr: output as ISO 8601 strings */


/* rkr: added for output closer to ISO 8601 */


/**
Create a netCDF file for parallel I/O.

This function creates a new netCDF file for parallel I/O access.

Parallel I/O access is only available in library build which support
parallel I/O. To support parallel I/O, netCDF must be built with
netCDF-4 enabled (configure options --enable-netcdf-4 and --enable-parallel4)
and with a HDF5 library that supports parallel I/O, or with support for the
PnetCDF library via the --enable-pnetcdf option.
This function is collective, i.e. must be called by all MPI processes defined
in the MPI communicator, argument comm. In addition,
values of arguments of this function must be the same among all MPI processes.

See nc_create() for a fuller discussion of file creation.

\note When opening a netCDF-4 file HDF5 error reporting is turned off,
if it is on. This doesn't stop the HDF5 error stack from recording the
errors, it simply stops their display to the user through stderr.

\param path The file name of the new netCDF dataset.

\param cmode The creation mode flag. The following flags are available:
  NC_CLOBBER (overwrite existing file),
  NC_NOCLOBBER (do not overwrite existing file),
  NC_NETCDF4 (create netCDF-4/HDF5 file),
  NC_CLASSIC_MODEL (enforce netCDF classic mode on netCDF-4/HDF5 files),
  NC_64BIT_OFFSET (create CDF-2 file),
  NC_64BIT_DATA (create CDF-5 file).

\param comm the MPI communicator specifying the processes participating the
parallel I/O to this file.

\param info MPI info object containing I/O hints or MPI_INFO_NULL.

\param ncidp Pointer to location where returned netCDF ID is to be
stored.

\returns ::NC_NOERR No error.
\returns ::NC_ENOPAR Library was not built with parallel I/O features.
\returns ::NC_EPERM: Attempting to create a netCDF file in a directory where you do not have permission to create files.
\returns ::NC_ENOTBUILT Library was not built with NETCDF4 or PnetCDF.
\returns ::NC_EEXIST Specifying a file name of a file that exists and also specifying NC_NOCLOBBER.
\returns ::NC_EINVAL Invalid input parameters.
\returns ::NC_ENOMEM System out of memory.
\returns ::NC_EHDFERR HDF5 error (netCDF-4 files only).
\returns ::NC_EFILEMETA Error writing netCDF-4 file-level metadata in
HDF5 file. (netCDF-4 files only).

Example

In this example from nc_test4/tst_parallel.c, a file is created for
parallel I/O.

\code
    int mpi_size, mpi_rank;
    MPI_Comm comm = MPI_COMM_WORLD;
    MPI_Info info = MPI_INFO_NULL;
    int ncid, v1id, dimids[NDIMS];
    char file_name[NC_MAX_NAME + 1];

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
    MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);

    sprintf(file_name, "%s/%s", TEMP_LARGE, FILE);
    if ((res = nc_create_par(file_name, NC_NETCDF4, comm, info, &ncid))) ERR;

    if (nc_def_dim(ncid, "d1", DIMSIZE, dimids)) ERR;
    if (nc_def_dim(ncid, "d2", DIMSIZE, &dimids[1])) ERR;
    if (nc_def_dim(ncid, "d3", NUM_SLABS, &dimids[2])) ERR;

    if ((res = nc_def_var(ncid, "v1", NC_INT, NDIMS, dimids, &v1id))) ERR;

        ... use collective I/O
    err = nc_var_par_access(ncid, v1id, NC_COLLECTIVE); ERR

    if ((res = nc_enddef(ncid))) ERR;

    ...

    if ((res = nc_close(ncid))) ERR;
\endcode
\author Ed Hartnett, Dennis Heimbigner
\ingroup datasets
*/


/**
Open an existing netCDF file for parallel I/O.

This function opens an existing netCDF dataset for parallel I/O access. It
determines the underlying file format automatically. Use the same call to open
a netCDF classic, 64-bit offset, CDF-5, or netCDF-4 file.

Parallel I/O access is only available in library build which support
parallel I/O. To support parallel I/O, netCDF must be built with
netCDF-4 enabled (configure options --enable-netcdf-4 and --enable-parallel4)
and with a HDF5 library that supports parallel I/O, or with support for the
PnetCDF library via the --enable-pnetcdf option.
This function is collective, i.e. must be called by all MPI processes defined
in the MPI communicator, argument comm. In addition,
values of arguments of this function must be the same among all MPI processes.

It is not necessary to pass any information about the format of the
file being opened. The file type will be detected automatically by the
netCDF library.

As of version 4.3.1.2, multiple calls to nc_open_par() with the same
path will return the same ncid value.

\note When opening a netCDF-4 file HDF5 error reporting is turned off,
if it is on. This doesn't stop the HDF5 error stack from recording the
errors, it simply stops their display to the user through stderr.

\param path File name for netCDF dataset to be opened.

\param omode The open mode flag may be NC_WRITE (for read/write
access) or NC_NOWRITE (for read-only access).

\param comm the MPI communicator specifying the processes participating the
parallel I/O to this file.

\param info MPI info object containing I/O hints or MPI_INFO_NULL.

\param ncidp Pointer to location where returned netCDF ID is to be
stored.

nc_open_par() returns the value NC_NOERR if no errors
occurred. Otherwise, the returned status indicates an error. Possible
causes of errors include:

\returns ::NC_NOERR No error.
\returns ::NC_ENOPAR Library was not built with parallel I/O features.
\returns ::NC_EPERM: Attempting to open a netCDF file where you do not have permission to write the file.
\returns ::NC_ENOTBUILT Library was not built with NETCDF4 or PnetCDF.
\returns ::NC_EINVAL Invalid parameters.
\returns ::NC_ENOTNC Not a netCDF file.
\returns ::NC_ENOMEM Out of memory.
\returns ::NC_EHDFERR HDF5 error. (NetCDF-4 files only.)
\returns ::NC_EDIMMETA Error in netCDF-4 dimension metadata (NetCDF-4 files only.)

Examples

Here is an example using nc_open_par() from examples/C/parallel_vara.c.

\code
#include <mpi.h>
#include <netcdf.h>
#include <netcdf_par.h>
   ...
    int ncid, omode;
    char filename[128];
   ...
    omode = NC_NOWRITE;
    err = nc_open_par(filename, omode, MPI_COMM_WORLD, MPI_INFO_NULL, &ncid); FATAL_ERR
\endcode
\author Ed Hartnett, Dennis Heimbigner
\ingroup datasets
*/


/**
This is the same as nc_open_par(), but accepts the MPI comm/info as
integers.

\param path File name for netCDF dataset to be opened.

\param omode The open mode flag may be NC_WRITE (for read/write
access) or NC_NOWRITE (for read-only access).

\param comm the MPI communicator specifying the processes participating the
parallel I/O to this file.

\param info MPI info object containing I/O hints or MPI_INFO_NULL.

\param ncidp Pointer to location where returned netCDF ID is to be
stored.

nc_open_par() returns the value NC_NOERR if no errors occurred. Otherwise, the
returned status indicates an error. Possible causes of errors include:

\returns ::NC_NOERR No error.
\returns ::NC_EINVAL Invalid parameters.
\returns ::NC_ENOTNC Not a netCDF file.
\returns ::NC_ENOMEM Out of memory.
\returns ::NC_EHDFERR HDF5 error. (NetCDF-4 files only.)
\returns ::NC_EDIMMETA Error in netCDF-4 dimension metadata. (NetCDF-4
files only.)

\author Ed Hartnett
\ingroup datasets
\internal
*/


/**\ingroup datasets

This function will change the parallel access of a variable from independent to
collective and vice versa. Note when file is opened/created to use PnetCDF
library to perform parallel I/O underneath, argument varid is ignored and the
mode changed by this function applies to all variables. This is because PnetCDF
does not support access mode change for individual variables. In this case,
users may use NC_GLOBAL in varid argument for better program readability. This
function is collective, i.e. must be called by all MPI processes defined in the
MPI communicator used in nc_create_par() or nc_open_par(). In addition, values
of arguments of this function must be the same among all MPI processes.

To obtain a good I/O performance, users are recommended to use collective mode.
In addition, switching between collective and independent I/O mode can be
expensive.

\param ncid NetCDF or group ID, from a previous call to nc_open_par(),
nc_create_par(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID

\param par_access NC_COLLECTIVE or NC_INDEPENDENT.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Invalid ncid passed.
\returns ::NC_ENOTVAR Invalid varid passed.
\returns ::NC_ENOPAR File was not opened with nc_open_par/nc_create_var.
\returns ::NC_EINVAL Invalid par_access specified.

Example

Here is an example from examples/C/parallel_vara.c which changes the
parallel access of a variable and then writes to it.

\code
#define NY 10
#define NX 4

    #include <mpi.h>
    #include <netcdf.h>
    #include <netcdf_par.h>
        ...
    char filename[128];
    int err, ncid, cmode, varid, dimid, dimid[2], buf[NY][NX];
    size_t global_ny, global_nx, start[2], count[2];
        ...
    global_ny = NY;
    global_nx = NX * nprocs;
        ...
    cmode = NC_CLOBBER;
    err = nc_create_par(filename, cmode, MPI_COMM_WORLD, MPI_INFO_NULL, &ncid); FATAL_ERR
        ...
    err = nc_def_dim(ncid, "Y", global_ny, &dimid[0]); ERR
    err = nc_def_dim(ncid, "X", global_nx, &dimid[1]); ERR
    err = nc_def_var(ncid, "var", NC_INT, 2, dimid, &varid); ERR
        ...
        ... set collective I/O globally (for all variables)
    err = nc_var_par_access(ncid, NC_GLOBAL, NC_COLLECTIVE); ERR

    err = nc_enddef(ncid); ERR
        ...
    start[0] = 0;
    start[1] = NX * rank;
    count[0] = NY;
    count[1] = NX;

    err = nc_put_vara_int(ncid, varid, start, count, &buf[0][0]); ERR

\endcode
\author Ed Hartnett, Dennis Heimbigner
\ingroup datasets
 */


/**
Create a netCDF file for parallel access from the Fortran API.

This function calls nc_create_par() after converting the MPI comm and
info from Fortran to C, if necessary.

\param path The file name of the new netCDF dataset.

\param cmode The creation mode flag. The following flags are available:
  NC_CLOBBER (overwrite existing file),
  NC_NOCLOBBER (do not overwrite existing file),
  NC_NETCDF4 (create netCDF-4/HDF5 file),
  NC_CLASSIC_MODEL (enforce netCDF classic mode on netCDF-4/HDF5 files),
  NC_64BIT_OFFSET (create CDF-2 file)
  NC_64BIT_DATA (create CDF-5 file)

\param comm the MPI communicator specifying the processes participating the
parallel I/O to this file.

\param info MPI info object containing I/O hints or MPI_INFO_NULL.

\param ncidp Pointer to location where returned netCDF ID is to be
stored.

\returns ::NC_NOERR No error.
\returns ::NC_ENOPAR Library was not built with parallel I/O features.
\returns ::NC_EPERM: Attempting to create a netCDF file in a directory where you do not have permission to create files.
\returns ::NC_EINVAL Invalid input parameters.
\returns ::NC_ENOMEM System out of memory.
\returns ::NC_ENOTNC Binary format could not be determined.
\returns ::NC_EHDFERR HDF5 error (netCDF-4 files only).
\returns ::NC_EFILEMETA Error writing netCDF-4 file-level metadata in
HDF5 file. (netCDF-4 files only).

\author Ed Hartnett, Dennis Heimbigner
\ingroup datasets
\internal
*/


/**
Certain functions are in the dispatch table,
but not in the netcdf.h API. These need to
be exposed for use in delegation such as
in libdap2.
*/


/* Do a simple uri parse: return NCU_OK if success, NCU_XXX if failed */


/* Replace the protocol */


/* Replace the query */


/* Replace the constraints */


/* Construct a complete NC URI.
   Optionally with the constraints.
   Optionally with the user parameters.
   Caller frees returned string.
   Optionally encode the pieces.
*/


/* Internal version of lookup; returns the paired index of the key;
   case insensitive
 */


/* Return the ptr to the first occurrence of
   any char in the list. Return NULL if no
   occurrences
*/


/* Shift every char starting at p 1 place to the left */


/* Shift every char starting at p 1 place to the right */


/*
Support encode of user and password fields
*/


/* Return a string representing encoding of input; caller must free;
   watch out: will encode whole string, so watch what you give it.
   Allowable argument specifies characters that do not need escaping.
 */


/* Return a string representing decoding of input; caller must free;*/


/*
Partially decode a string. Only characters in 'decodeset'
are decoded. Return decoded string; caller must free.
*/


/*! Return the group ID for a group given the name.

  @param[in] ncid      A valid file or group ncid.
  @param[in] name      The name of the group you are querying.
  @param[out] grp_ncid Pointer to memory to hold the group ncid.

  @returns Error code or ::NC_NOERR or no error.

 */


/*! Get a list of groups or subgroups from a file or groupID.

  @param[in]  ncid    The ncid of the file or parent group.
  @param[out] numgrps Pointer to memory to hold the number of groups.
  @param[out] ncids   Pointer to memory to hold the ncid for each group.

  @returns Error code or ::NC_NOERR for no error.

 */


/*! Get the name of a group given an ID.

  @param[in]  ncid The ncid of the file or parent group.
  @param[out] name The name of the group associated with the id.

  @returns Error code or ::NC_NOERR for no error.
*/


/*! Get the full path/groupname of a group/subgroup given an ID.

  @param[in]  ncid      The ncid of the file or parent group.
  @param[out] lenp      Pointer to memory to hold the length of the full name.
  @param[out] full_name Pointer to memory to hold the full name of the group including root/parent.

  @returns Error code or ::NC_NOERR for no error.

*/


/*! Get the length of a group name given an ID.

  @param[in] ncid  The ncid of the group in question.
  @param[out] lenp Pointer to memory to hold the length of the name of the group in question.

  @returns Error code or ::NC_NOERR for no error.

*/


/*! Get the ID of the parent based on a group ID.

  @param[in] ncid         The ncid of the group in question.
  @param[out] parent_ncid Pointer to memory to hold the identifier of the parent of the group in question.

  @returns Error code or ::NC_NOERR for no error.

 */


/*! Get the full ncid given a group name.

  @param[in] ncid      The ncid of the file.
  @param[in] full_name The full name of the group in question.
  @param[out] grp_ncid Pointer to memory to hold the identifier of the full group in question.

  @returns Error code or ::NC_NOERR for no error.

 */


/*! Get a list of varids associated with a group given a group ID.

  @param[in] ncid    The ncid of the group in question.
  @param[out] nvars  Pointer to memory to hold the number of variables in the group in question.
  @param[out] varids Pointer to memory to hold the variable ids contained by the group in question.

  @returns Error code or ::NC_NOERR for no error.

*/


/*! Retrieve a list of dimension ids associated with a group.

  @param[in] ncid    The ncid of the group in question.
  @param[out] ndims  Pointer to memory to contain the number of dimids associated with the group.
  @param[out] dimids Pointer to memory to contain the number of dimensions associated with the group.
  @param[in] include_parents If non-zero, parent groups are also traversed.

  @returns Error code or ::NC_NOERR for no error.

 */


/*! Retrieve a list of types associated with a group

  @param[in] ncid     The ncid for the group in question.
  @param[out] ntypes  Pointer to memory to hold the number of typeids contained by the group in question.
  @param[out] typeids Pointer to memory to hold the typeids contained by the group in question.

  @returns Error code or ::NC_NOERR for no error.

*/


/*! Define a new group.

  The function nc_def_grp() adds a new
  group to an open netCDF dataset in define mode.  It returns (as an
  argument) a group id, given the parent ncid and the name of the group.

  A group may be a top-level group if it is passed the ncid of the file,
  or a sub-group if passed the ncid of an existing group.

  @param[in]  parent_ncid The ncid of the parent for the group.
  @param[in]  name        Name of the new group.
  @param[out] new_ncid    Pointer to memory to hold the new ncid.

  @returns Error code or ::NC_NOERR for no error.

  @retval ::NC_NOERR No error.
  @retval ::NC_ENOTNC4 Not an nc4 file.
  @retval ::NC_ENOTINDEFINE Not in define mode.
  @retval ::NC_ESTRICTNC3 Not permissible in nc4 classic mode.
  @retval ::NC_EPERM Write to read only.
  @retval ::NC_ENOMEM Memory allocation (malloc) failure.
  @retval ::NC_ENAMEINUSE String match to name in use.

  \section nc_def_grp_example Example

  Here is an example using nc_def_grp() to create a new group.

  \code{.c}

  #include <netcdf.h>
  ...
  int status, ncid, grpid, latid, recid;
  ...

  \endcode

*/


/*! Rename a group.

  @param[in] grpid The ID for the group in question.
  @param[in] name  The new name for the group.

  @returns Error code or ::NC_NOERR for no error.

*/


/*! Print the metadata for a file.

  @param[in] ncid The ncid of an open file.

  @returns Error code or ::NC_NOERR for no error.

 */


/**
 * @internal Compare two netcdf types for equality. Must have the
 * ncids as well, to find user-defined types.
 *
 * @param ncid1 File ID.
 * @param typeid1 Type ID.
 * @param ncid2 File ID.
 * @param typeid2 Type ID.
 * @param equalp Pointer that gets 1 of the types are equal, 0
 * otherwise.
 *
 * @return ::NC_NOERR No error.
 * @author Ed Hartnett
*/


/**
 * @internal Recursively hunt for a netCDF type id. (Code from
 * nc4internal.c); Return matching typeid or 0 if not found.
 *
 * @param ncid1 File ID.
 * @param tid1 Type ID.
 * @param ncid2 File ID.
 * @param tid2 Pointer that gets type ID of equal type.
 *
 * @return ::NC_NOERR No error.
 * @author Ed Hartnett
*/


/**
 * @internal Given a type in one file, find its equal (if any) in
 * another file. It sounds so simple, but it's a real pain!
 *
 * @param ncid1 File ID.
 * @param xtype1 Type ID.
 * @param ncid2 File ID.
 * @param xtype2 Pointer that gets type ID of equal type.
 *
 * @return ::NC_NOERR No error.
 * @author Ed Hartnett
*/


/**
 * This will copy a variable that is an array of primitive type and
 * its attributes from one file to another, assuming dimensions in the
 * output file are already defined and have same dimension IDs and
 * length.  However it doesn't work for copying netCDF-4 variables of
 * type string or a user-defined type.
 *
 * This function works even if the files are different formats,
 * (for example, one netcdf classic, the other netcdf-4).
 *
 * If you're copying into a classic-model file, from a netcdf-4 file,
 * you must be copying a variable of one of the six classic-model
 * types, and similarly for the attributes.
 *
 * For large netCDF-3 files, this can be a very inefficient way to
 * copy data from one file to another, because adding a new variable
 * to the target file may require more space in the header and thus
 * result in moving data for other variables in the target file. This
 * is not a problem for netCDF-4 files, which support efficient
 * addition of variables without moving data for other variables.
 *
 * @param ncid_in File ID to copy from.
 * @param varid_in Variable ID to copy.
 * @param ncid_out File ID to copy to.
 *
 * @return ::NC_NOERR No error.
 * @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
 * Copy an attribute from one open file to another.
 *
 * Special programming challenge: this function must work even if one
 * of the other of the files is a netcdf version 1.0 file (i.e. not
 * HDF5). So only use top level netcdf api functions.
 *
 * From the netcdf-3 docs: The output netCDF dataset should be in
 * define mode if the attribute to be copied does not already exist
 * for the target variable, or if it would cause an existing target
 * attribute to grow.
 *
 * @param ncid_in File ID to copy from.
 * @param varid_in Variable ID to copy from.
 * @param name Name of attribute to copy.
 * @param ncid_out File ID to copy to.
 * @param varid_out Variable ID to copy to.
 *
 * @return ::NC_NOERR No error.
 * @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/* Locate where given object is or should be placed in indexp.
   if fail to find spot return 0 else 1.
   If deletok then a deleted slot is ok to return;
   return invariant: return == 0 || *indexp is defined
 */


/* Return the hash key for specified key; takes key+size*/
/* Wrapper for crc32 */


/** Change the data for the specified key
    Return 1 if found, 0 otherwise
*/


/**
Friend: ncindex.c
Only used by ncindex.c.
Allows a hack by ncindex.
*/


/*
Binary search prime table for first prime just greater than or
equal to val
*/


/*!
Rename an attribute.

The function nc_rename_att() changes the name of an attribute. If the
new name is longer than the original name, the netCDF dataset must be
in define mode. You cannot rename an attribute to have the same name
as another attribute of the same variable.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL for
a global attribute.

\param name Attribute \ref object_name.

\param newname The new attribute \ref object_name.

Example

Here is an example using nc_rename_att() to rename the variable
attribute units to Units for a variable rh in an existing netCDF
dataset named foo.nc:

\code
     #include <netcdf.h>
        ...
     int  status;
     int  ncid;
     int  rh_id;
        ...
     status = nc_open("foo.nc", NC_NOWRITE, &ncid);
     if (status != NC_NOERR) handle_error(status);
        ...
     status = nc_inq_varid (ncid, "rh", &rh_id);
     if (status != NC_NOERR) handle_error(status);
        ...
     status = nc_rename_att(ncid, rh_id, "units", "Units");
     if (status != NC_NOERR) handle_error(status);
\endcode

\returns NC_NOERR No error.
\returns ::NC_EBADID Bad ncid.
\returns ::NC_ENOTVAR Bad varid.
\returns ::NC_EBADNAME Bad name.
\returns ::NC_EMAXNAME New name too long.
\returns ::NC_EINVAL Name or new name not provided.
\returns ::NC_ENAMEINUSE Name already in use.
\returns ::NC_EPERM File was opened read only.
\returns ::NC_ENOTINDEFINE File is not in define mode.
\returns ::NC_ENOTATT Attribute not found.
\returns ::NC_EHDFERR Failure at HDF5 layer.
\returns ::NC_ENOMEM Out of memory.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
 */


/*!
Delete an attribute.

The function nc_del_att() deletes a netCDF attribute from an open
netCDF dataset. The netCDF dataset must be in define mode.

\param ncid NetCDF or group ID, from a previous call to nc_open(),
nc_create(), nc_def_grp(), or associated inquiry functions such as
nc_inq_ncid().

\param varid Variable ID of the attribute's variable, or ::NC_GLOBAL
for a global attribute.

\param name Attribute name.

Example

Here is an example using nc_del_att() to delete the variable attribute
Units for a variable rh in an existing netCDF dataset named foo.nc:

\code
     #include <netcdf.h>
        ...
     int  status;
     int  ncid;
     int  rh_id;
        ...
     status = nc_open("foo.nc", NC_WRITE, &ncid);
     if (status != NC_NOERR) handle_error(status);
        ...
     status = nc_inq_varid (ncid, "rh", &rh_id);
     if (status != NC_NOERR) handle_error(status);
        ...
     status = nc_redef(ncid);
     if (status != NC_NOERR) handle_error(status);
     status = nc_del_att(ncid, rh_id, "Units");
     if (status != NC_NOERR) handle_error(status);
     status = nc_enddef(ncid);
     if (status != NC_NOERR) handle_error(status);
\endcode

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad ncid.
\returns ::NC_ENOTVAR Bad varid.
\returns ::NC_EBADNAME Bad name.
\returns ::NC_EINVAL Name not provided.
\returns ::NC_EPERM File was opened read only.
\returns ::NC_ENOTINDEFINE File is not in define mode.
\returns ::NC_ENOTATT Attribute not found.
\returns ::NC_EATTMETA Failure at HDF5 layer.
\author Glenn Davis, Ed Hartnett, Dennis Heimbigner
 */


/**
   Define a new variable.

   This function adds a new variable to an open netCDF dataset or group.
   It returns (as an argument) a variable ID, given the netCDF ID,
   the variable name, the variable type, the number of dimensions, and a
   list of the dimension IDs.

   @param ncid NetCDF or group ID, from a previous call to nc_open(),
   nc_create(), nc_def_grp(), or associated inquiry functions such as
   nc_inq_ncid().
   @param name Variable @ref object_name.
   @param xtype @ref data_type of the variable.
   @param ndims Number of dimensions for the variable. For example, 2
   specifies a matrix, 1 specifies a vector, and 0 means the variable is
   a scalar with no dimensions. Must not be negative or greater than the
   predefined constant ::NC_MAX_VAR_DIMS. In netCDF-4/HDF5 files, may not
   exceed the HDF5 maximum number of dimensions (32).
   @param dimidsp Vector of ndims dimension IDs corresponding to the
   variable dimensions. For classic model netCDF files, if the ID of the
   unlimited dimension is included, it must be first. This argument is
   ignored if ndims is 0. For expanded model netCDF4/HDF5 files, there
   may be any number of unlimited dimensions, and they may be used in any
   element of the dimids array.
   @param varidp Pointer to location for the returned variable ID.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTINDEFINE Not in define mode.
   @return ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 netcdf-4 file.
   @return ::NC_EMAXVARS NC_MAX_VARS exceeded [Not enforced after 4.5.0]
   @return ::NC_EBADTYPE Bad type.
   @return ::NC_EINVAL Invalid input.
   @return ::NC_ENAMEINUSE Name already in use.
   @return ::NC_EPERM Attempt to create object in read-only file.

   @section nc_def_var_example Example

   Here is an example using nc_def_var to create a variable named rh of
   type double with three dimensions, time, lat, and lon in a new netCDF
   dataset named foo.nc:

   @code
   #include <netcdf.h>
   ...
   int  status;
   int  ncid;
   int  lat_dim, lon_dim, time_dim;
   int  rh_id;
   int  rh_dimids[3];
   ...
   status = nc_create("foo.nc", NC_NOCLOBBER, &ncid);
   if (status != NC_NOERR) handle_error(status);
   ...

   status = nc_def_dim(ncid, "lat", 5L, &lat_dim);
   if (status != NC_NOERR) handle_error(status);
   status = nc_def_dim(ncid, "lon", 10L, &lon_dim);
   if (status != NC_NOERR) handle_error(status);
   status = nc_def_dim(ncid, "time", NC_UNLIMITED, &time_dim);
   if (status != NC_NOERR) handle_error(status);
   ...

   rh_dimids[0] = time_dim;
   rh_dimids[1] = lat_dim;
   rh_dimids[2] = lon_dim;
   status = nc_def_var (ncid, "rh", NC_DOUBLE, 3, rh_dimids, &rh_id);
   if (status != NC_NOERR) handle_error(status);
   @endcode

   @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
   Set the fill value for a variable.

   @note For netCDF classic, 64-bit offset, and CDF5 formats, it is
   allowed (but not good practice) to set the fill value after data
   have been written to the variable. In this case, unless the
   variable has been completely specified (without gaps in the data),
   any existing filled values will not be recognized as fill values by
   applications reading the data. Best practice is to set the fill
   value after the variable has been defined, but before any data have
   been written to that varibale. In NetCDF-4 files, this is enforced
   by the HDF5 library. For netCDF-4 files, an error is returned if
   the user attempts to set the fill value after writing data to the
   variable.

   @param ncid NetCDF ID, from a previous call to nc_open() or
   nc_create().
   @param varid Variable ID.
   @param no_fill Set to ::NC_NOFILL to turn off fill mode for this
   variable. Set to ::NC_FILL (the default) to turn on fill mode for
   the variable.
   @param fill_value the fill value to be used for this variable. Must
   be the same type as the variable. This must point to enough free
   memory to hold one element of the data type of the variable. (For
   example, an ::NC_INT will require 4 bytes for it's fill value,
   which is also an ::NC_INT.)

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ID.
   @return ::NC_ENOTINDEFINE Not in define mode.  This is returned for
   netCDF classic, 64-bit offset, or 64-bit data files, or for
   netCDF-4 files, when they were created with ::NC_CLASSIC_MODEL flag by
   nc_creae().
   @return ::NC_EPERM Attempt to create object in read-only file.
   @return ::NC_ELATEDEF (NetCDF-4 only). Returned when user attempts
   to set fill value after data are written.
   @return ::NC_EGLOBAL Attempt to set fill value on NC_GLOBAL.

   @section nc_def_var_fill_example Example

   In this example from libsrc4/tst_vars.c, a variable is defined, and
   the fill mode turned off. Then nc_inq_fill() is used to check that
   the setting is correct. Then some data are written to the
   variable. Since the data that are written do not cover the full
   extent of the variable, the missing values will just be random. If
   fill value mode was turned on, the missing values would get the
   fill value.

   @code
   #define DIM7_LEN 2
   #define DIM7_NAME "dim_7_from_Indiana"
   #define VAR7_NAME "var_7_from_Idaho"
   #define NDIMS 1
   int dimids[NDIMS];
   size_t index[NDIMS];
   int varid;
   int no_fill;
   unsigned short ushort_data = 42, ushort_data_in, fill_value_in;

   if (nc_create(FILE_NAME, NC_NETCDF4, &ncid)) ERR;
   if (nc_def_dim(ncid, DIM7_NAME, DIM7_LEN, &dimids[0])) ERR;
   if (nc_def_var(ncid, VAR7_NAME, NC_USHORT, NDIMS, dimids,
   &varid)) ERR;
   if (nc_def_var_fill(ncid, varid, 1, NULL)) ERR;

   if (nc_inq_var_fill(ncid, varid, &no_fill, &fill_value_in)) ERR;
   if (!no_fill) ERR;

   index[0] = 1;
   if (nc_put_var1_ushort(ncid, varid, index, &ushort_data)) ERR;

   index[0] = 0;
   if (nc_get_var1_ushort(ncid, varid, index, &ushort_data_in)) ERR;

   if (nc_close(ncid)) ERR;
   @endcode
   @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
   Set the compression settings for a netCDF-4/HDF5 variable.

   This function must be called after nc_def_var and before nc_enddef
   or any functions which writes data to the file.

   Deflation and shuffline require chunked data. If this function is
   called on a variable with contiguous data, then the data is changed
   to chunked data, with default chunksizes. Use nc_def_var_chunking()
   to tune performance with user-defined chunksizes.

   If this function is called on a scalar variable, it is ignored.

   @param ncid NetCDF or group ID, from a previous call to nc_open(),
   nc_create(), nc_def_grp(), or associated inquiry functions such as
   nc_inq_ncid().
   @param varid Variable ID
   @param shuffle True to turn on the shuffle filter. The shuffle
   filter can assist with the compression of integer data by changing
   the byte order in the data stream. It makes no sense to use the
   shuffle filter without setting a deflate level, or to use shuffle
   on non-integer data.
   @param deflate True to turn on deflation for this variable.
   @param deflate_level A number between 0 (no compression) and 9
   (maximum compression).

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTVAR Invalid variable ID.
   @return ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
   not netCDF-4/HDF5.
   @return ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
   netcdf-4 file.
   @return ::NC_ELATEDEF Too late to change settings for this variable.
   @return ::NC_ENOTINDEFINE Not in define mode.
   @return ::NC_EPERM File is read only.
   @return ::NC_EMAXDIMS Classic model file exceeds ::NC_MAX_VAR_DIMS.
   @return ::NC_ESTRICTNC3 Attempting to create netCDF-4 type var in
   classic model file
   @return ::NC_EBADTYPE Bad type.
   @return ::NC_ENOMEM Out of memory.
   @return ::NC_EHDFERR Error returned by HDF5 layer.
   @return ::NC_EINVAL Invalid input. Deflate can't be set unless
   variable storage is NC_CHUNK.

   @section nc_def_var_deflate_example Example

   Here is an example from /examples/C/simple_xy_nc4_wr.c using
   nc_def_var_deflate to create a variable and then turn on the shuffle
   filter and compression.

   @code
   #include <netcdf.h>
   #define NDIMS 2
   #define NX 6
   #define NY 12

   int ncid, x_dimid, y_dimid, varid;
   int dimids[NDIMS];
   int shuffle, deflate, deflate_level;
   int data_out[NX][NY];
   int x, y, retval;

   shuffle = NC_SHUFFLE;
   deflate = 1;
   deflate_level = 1;
   ...
   if ((retval = nc_create(FILE_NAME, NC_NETCDF4, &ncid)))
   ERR(retval);

   if ((retval = nc_def_dim(ncid, "x", NX, &x_dimid)))
   ERR(retval);
   if ((retval = nc_def_dim(ncid, "y", NY, &y_dimid)))
   ERR(retval);

   dimids[0] = x_dimid;
   dimids[1] = y_dimid;

   if ((retval = nc_def_var(ncid, "data", NC_INT, NDIMS,
   dimids, &varid)))
   ERR(retval);

   ...

   if ((retval = nc_def_var_deflate(ncid, varid, shuffle, deflate,
   deflate_level)))
   ERR(retval);
   ...
   @endcode
   @author Ed Hartnett, Dennis Heimbigner
*/


/**
   Set checksum for a var.

   This function must be called after nc_def_var and before nc_enddef
   or any functions which writes data to the file.

   Checksums require chunked data. If this function is called on a
   variable with contiguous data, then the data is changed to chunked
   data, with default chunksizes. Use nc_def_var_chunking() to tune
   performance with user-defined chunksizes.

   @param ncid NetCDF or group ID, from a previous call to nc_open(),
   nc_create(), nc_def_grp(), or associated inquiry functions such as
   nc_inq_ncid().
   @param varid Variable ID
   @param fletcher32 True to turn on Fletcher32 checksums for this
   variable.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTVAR Invalid variable ID.
   @return ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
   not netCDF-4/HDF5.
   @return ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
   netcdf-4 file.
   @return ::NC_ELATEDEF Too late to change settings for this variable.
   @return ::NC_EINVAL Invalid input
   @author Ed Hartnett, Dennis Heimbigner
*/


/**
   Define chunking parameters for a variable

   The function nc_def_var_chunking sets the chunking parameters for a
   variable in a netCDF-4 file. It can set the chunk sizes to get chunked
   storage, or it can set the contiguous flag to get contiguous storage.

   The total size of a chunk must be less than 4 GiB. That is, the
   product of all chunksizes and the size of the data (or the size of
   nc_vlen_t for VLEN types) must be less than 4 GiB.

   This function may only be called after the variable is defined, but
   before nc_enddef is called. Once the chunking parameters are set for a
   variable, they cannot be changed.

   Note that this does not work for scalar variables. Only non-scalar
   variables can have chunking.

   @param[in] ncid NetCDF ID, from a previous call to nc_open or
   nc_create.

   @param[in] varid Variable ID.

   @param[in] storage If ::NC_CONTIGUOUS, then contiguous storage is used
   for this variable. Variables with one or more unlimited dimensions
   cannot use contiguous storage. If contiguous storage is turned on, the
   chunksizes parameter is ignored. If ::NC_CHUNKED, then chunked storage
   is used for this variable. Chunk sizes may be specified with the
   chunksizes parameter or default sizes will be used if that parameter
   is NULL.

   @param[in] chunksizesp A pointer to an array list of chunk sizes. The
   array must have one chunksize for each dimension of the variable. If
   ::NC_CONTIGUOUS storage is set, then the chunksizes parameter is
   ignored.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ID.
   @return ::NC_ENOTNC4 Not a netCDF-4 file.
   @return ::NC_ELATEDEF This variable has already been the subject of a
   nc_enddef call.  In netCDF-4 files nc_enddef will be called
   automatically for any data read or write. Once nc_enddef has been
   called after the nc_def_var call for a variable, it is impossible to
   set the chunking for that variable.
   @return ::NC_ENOTINDEFINE Not in define mode.  This is returned for
   netCDF classic or 64-bit offset files, or for netCDF-4 files, when
   they wwere created with ::NC_CLASSIC_MODEL flag by nc_create().
   @return ::NC_EPERM Attempt to create object in read-only file.
   @return ::NC_EBADCHUNK Retunrs if the chunk size specified for a
   variable is larger than the length of the dimensions associated with
   variable.

   @section nc_def_var_chunking_example Example

   In this example from libsrc4/tst_vars2.c, chunksizes are set with
   nc_var_def_chunking, and checked with nc_var_inq_chunking.

   @code
   printf("**** testing chunking...");
   {
   #define NDIMS5 1
   #define DIM5_NAME "D5"
   #define VAR_NAME5 "V5"
   #define DIM5_LEN 1000

   int dimids[NDIMS5], dimids_in[NDIMS5];
   int varid;
   int ndims, nvars, natts, unlimdimid;
   nc_type xtype_in;
   char name_in[NC_MAX_NAME + 1];
   int data[DIM5_LEN], data_in[DIM5_LEN];
   size_t chunksize[NDIMS5] = {5};
   size_t chunksize_in[NDIMS5];
   int storage_in;
   int i, d;

   for (i = 0; i < DIM5_LEN; i++)
   data[i] = i;

   if (nc_create(FILE_NAME, NC_NETCDF4, &ncid)) ERR;
   if (nc_def_dim(ncid, DIM5_NAME, DIM5_LEN, &dimids[0])) ERR;
   if (nc_def_var(ncid, VAR_NAME5, NC_INT, NDIMS5, dimids, &varid)) ERR;
   if (nc_def_var_chunking(ncid, varid, NC_CHUNKED, chunksize)) ERR;
   if (nc_put_var_int(ncid, varid, data)) ERR;

   if (nc_inq_var_chunking(ncid, varid, &storage_in, chunksize_in)) ERR;
   for (d = 0; d < NDIMS5; d++)
   if (chunksize[d] != chunksize_in[d]) ERR;
   if (storage_in != NC_CHUNKED) ERR;
   @endcode
*/


/**
   Define endianness of a variable.

   With this function the endianness (i.e. order of bits in integers) can
   be changed on a per-variable basis. By default, the endianness is the
   same as the default endianness of the platform. But with
   nc_def_var_endianness the endianness can be explicitly set for a
   variable.

   Warning: this function is only defined if the type of the variable
   is an atomic integer or float type.

   This function may only be called after the variable is defined, but
   before nc_enddef is called.

   @param ncid NetCDF ID, from a previous call to nc_open() or
   nc_create().

   @param varid Variable ID.

   @param endian ::NC_ENDIAN_NATIVE to select the native endianness of
   the platform (the default), ::NC_ENDIAN_LITTLE to use
   little-endian, ::NC_ENDIAN_BIG to use big-endian.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ID.
   @return ::NC_ENOTNC4 Not a netCDF-4 file.
   @return ::NC_ELATEDEF This variable has already been the subject of a
   nc_enddef call. In netCDF-4 files nc_enddef will be called
   automatically for any data read or write. Once nc_enddef has been
   called after the nc_def_var call for a variable, it is impossible to
   set the chunking for that variable.
   @return ::NC_ENOTINDEFINE Not in define mode. This is returned for
   netCDF classic or 64-bit offset files, or for netCDF-4 files, when
   they wwere created with ::NC_CLASSIC_MODEL flag by nc_create().
   @return ::NC_EPERM Attempt to create object in read-only file.

   @section nc_def_var_endian_example Example

   In this example from libsrc4/tst_vars2.c, a variable is created, and
   the endianness set to ::NC_ENDIAN_BIG.

   @code
   #define NDIMS4 1
   #define DIM4_NAME "Joe"
   #define VAR_NAME4 "Ed"
   #define DIM4_LEN 10
   {
   int dimids[NDIMS4], dimids_in[NDIMS4];
   int varid;
   int ndims, nvars, natts, unlimdimid;
   nc_type xtype_in;
   char name_in[NC_MAX_NAME + 1];
   int data[DIM4_LEN], data_in[DIM4_LEN];
   int endian_in;
   int i;

   for (i = 0; i < DIM4_LEN; i++)
   data[i] = i;

   if (nc_create(FILE_NAME, NC_NETCDF4, &ncid)) ERR;
   if (nc_def_dim(ncid, DIM4_NAME, DIM4_LEN, &dimids[0])) ERR;
   if (dimids[0] != 0) ERR;
   if (nc_def_var(ncid, VAR_NAME4, NC_INT, NDIMS4, dimids, &varid)) ERR;
   if (nc_def_var_endian(ncid, varid, NC_ENDIAN_BIG)) ERR;
   @endcode
   @author Ed Hartnett
*/


/**
   Define a new variable filter.

   @param ncid File and group ID.
   @param varid Variable ID.
   @param id
   @param nparams Number of filter parameters.
   @param parms Filter parameters.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ID.
   @author Dennis Heimbigner
*/


/**
   Rename a variable.

   This function changes the name of a netCDF variable in an open netCDF
   file or group. You cannot rename a variable to have the name of any existing
   variable.

   For classic format, 64-bit offset format, and netCDF-4/HDF5 with
   classic mode, if the new name is longer than the old name, the netCDF
   dataset must be in define mode.

   For netCDF-4/HDF5 files, renaming the variable changes the order of
   the variables in the file. The renamed variable becomes the last
   variable in the file.

   @param ncid NetCDF or group ID, from a previous call to nc_open(),
   nc_create(), nc_def_grp(), or associated inquiry functions such as
   nc_inq_ncid().

   @param varid Variable ID

   @param name New name of the variable.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTVAR Invalid variable ID.
   @return ::NC_EBADNAME Bad name.
   @return ::NC_EMAXNAME Name is too long.
   @return ::NC_ENAMEINUSE Name in use.
   @return ::NC_ENOMEM Out of memory.

   @section nc_rename_var_example Example

   Here is an example using nc_rename_var to rename the variable rh to
   rel_hum in an existing netCDF dataset named foo.nc:

   @code
   #include <netcdf.h>
   ...
   int  status;
   int  ncid;
   int  rh_id;
   ...
   status = nc_open("foo.nc", NC_WRITE, &ncid);
   if (status != NC_NOERR) handle_error(status);
   ...
   status = nc_redef(ncid);
   if (status != NC_NOERR) handle_error(status);
   status = nc_inq_varid (ncid, "rh", &rh_id);
   if (status != NC_NOERR) handle_error(status);
   status = nc_rename_var (ncid, rh_id, "rel_hum");
   if (status != NC_NOERR) handle_error(status);
   status = nc_enddef(ncid);
   if (status != NC_NOERR) handle_error(status);
   @endcode
   @author Glenn Davis, Ed Hartnett, Dennis Heimbigner
*/


/**
   @internal Does a variable have a record dimension?

   @param ncid File ID.
   @param varid Variable ID.
   @param nrecs Pointer that gets number of records.

   @return 0 if not a record var, 1 if it is.
*/


/**
   @internal Get the number of record dimensions for a variable and an
   array that identifies which of a variable's dimensions are record
   dimensions. Intended to be used instead of NC_is_recvar(), which
   doesn't work for netCDF-4 variables which have multiple unlimited
   dimensions or an unlimited dimension that is not the first of a
   variable's dimensions.

   @param ncid File ID.
   @param varid Variable ID.
   @param nrecdimsp Pointer that gets number of record dims.
   @param is_recdim Pointer that gets 1 if there is one or more record
   dimensions, 0 if not.

   @return 0 if not a record var, 1 if it is.

   Example use:
   @code
   int nrecdims;
   int is_recdim[NC_MAX_VAR_DIMS];
   ...
   status = NC_inq_recvar(ncid,varid,&nrecdims,is_recdim);
   isrecvar = (nrecdims > 0);
   @endcode
*/


/**
   @internal
   Find the length of a type. This is how much space is required by
   the in memory to hold one element of this type.

   @param type A netCDF atomic type.

   @return Length of the type in bytes, or -1 if type not found.
   @author Ed Hartnett
*/


/**
    @internal
    Find the length of a type. Redundant over nctypelen() above.

    @param xtype an nc_type.

    @author Dennis Heimbigner
*/


/**
    @internal
    Get the type name.

    @param xtype an nc_type.

    @author Dennis Heimbigner
*/


/**
   @internal
   Get the shape of a variable.

   @param ncid NetCDF ID, from a previous call to nc_open() or
   nc_create().
   @param varid Variable ID.
   @param ndims Number of dimensions for this var.
   @param shape Pointer to pre-allocated array that gets the size of
   each dimension.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTVAR Bad varid.

   @author Dennis Heimbigner
*/


/**
   @internal Check the start, count, and stride parameters for gets
   and puts, and handle NULLs.

   @param ncid The file ID.
   @param varid The variable ID.
   @param start Pointer to start array. If NULL ::NC_EINVALCOORDS will
   be returned for non-scalar variable.
   @param count Pointer to pointer to count array. If *count is NULL,
   an array of the correct size will be allocated, and filled with
   counts that represent the full extent of the variable. In this
   case, the memory must be freed by the caller.
   @param stride Pointer to pointer to stride array. If NULL, stide is
   ignored. If *stride is NULL an array of the correct size will be
   allocated, and filled with ones. In this case, the memory must be
   freed by the caller.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTVAR Variable not found.
   @return ::NC_ENOMEM Out of memory.
   @return ::NC_EINVALCOORDS Missing start array.
   @author Ed Hartnett
*/


/*! @{ */
/**
   Free string space allocated by the library.

   When you read string type the library will allocate the storage
   space for the data. This storage space must be freed, so pass the
   pointer back to this function, when you're done with the data, and
   it will free the string memory.

   @param len The number of character arrays in the array.
   @param data The pointer to the data array.

   @return ::NC_NOERR No error.
   @author Ed Hartnett
*/


/*! @{ */
/**
   Change the cache settings for a chunked variable. This function allows
   users to control the amount of memory used in the per-variable chunk
   cache at the HDF5 level. Changing the chunk cache only has effect
   until the file is closed. Once re-opened, the variable chunk cache
   returns to its default value.

   @param ncid NetCDF or group ID, from a previous call to nc_open(),
   nc_create(), nc_def_grp(), or associated inquiry functions such as
   nc_inq_ncid().
   @param varid Variable ID
   @param size The total size of the raw data chunk cache, in bytes.
   @param nelems The number of chunk slots in the raw data chunk cache.
   @param preemption The preemption, a value between 0 and 1 inclusive
   that indicates how much chunks that have been fully read are favored
   for preemption. A value of zero means fully read chunks are treated no
   differently than other chunks (the preemption is strictly LRU) while a
   value of one means fully read chunks are always preempted before other
   chunks.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTVAR Invalid variable ID.
   @return ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
   netcdf-4 file.
   @return ::NC_EINVAL Invalid input

   @section nc_def_var_chunk_cache_example Example

   In this example from nc_test4/tst_coords.c, a variable is defined, and
   the chunk cache settings are changed for that variable.

   @code
   printf("**** testing setting cache values for coordinate variables...");
   {
   #define RANK_1 1
   #define DIM0_NAME "d0"
   #define CACHE_SIZE 1000000
   #define CACHE_NELEMS 1009
   #define CACHE_PREEMPTION .90

   int ncid, dimid, varid;
   char name_in[NC_MAX_NAME + 1];

   if (nc_create(FILE_NAME, NC_CLASSIC_MODEL|NC_NETCDF4, &ncid)) ERR;
   if (nc_def_dim(ncid, DIM0_NAME, NC_UNLIMITED, &dimid)) ERR;
   if (nc_def_var(ncid, DIM0_NAME, NC_DOUBLE, 1, &dimid, &varid)) ERR;
   if (nc_set_var_chunk_cache(ncid, varid, CACHE_SIZE, CACHE_NELEMS, CACHE_PREEMPTION)) ERR;
   if (nc_close(ncid)) ERR;

   ...
   }
   SUMMARIZE_ERR;
   @endcode
   @author Ed Hartnett
*/


/**
   Get the per-variable chunk cache settings from the HDF5 layer.

   @param ncid NetCDF or group ID, from a previous call to nc_open(),
   nc_create(), nc_def_grp(), or associated inquiry functions such as
   nc_inq_ncid().
   @param varid Variable ID
   @param sizep The total size of the raw data chunk cache, in bytes,
   will be put here. @ref ignored_if_null.
   @param nelemsp The number of chunk slots in the raw data chunk
   cache hash table will be put here. @ref ignored_if_null.
   @param preemptionp The preemption will be put here. The preemtion
   value is between 0 and 1 inclusive and indicates how much chunks
   that have been fully read are favored for preemption. A value of
   zero means fully read chunks are treated no differently than other
   chunks (the preemption is strictly LRU) while a value of one means
   fully read chunks are always preempted before other chunks. @ref
   ignored_if_null.

   @return ::NC_NOERR No error.
   @return ::NC_EBADID Bad ncid.
   @return ::NC_ENOTVAR Invalid variable ID.
   @return ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3
   netcdf-4 file.
   @return ::NC_EINVAL Invalid input
   @author Ed Hartnett
*/


/*
Free a list and its contents
*/


/* Insert at position i of l; will overwrite previous value;
   guarantees alloc and length
*/


/* Extends nclist to include a unique operator
   which remove duplicate values; NULL values removed
   return value is always 1.
*/


/* internal "unsafe" version that does not check whether uc is in range */


/* return whether there is a grapheme break between boundclasses lbc and tbc
   (according to the definition of extended grapheme clusters)

  Rule numbering refers to TR29 Version 29 (Unicode 9.0.0):
  http://www.unicode.org/reports/tr29/tr29-29.html

  CAVEATS:
   Please note that evaluation of GB10 (grapheme breaks between emoji zwj sequences)
   and GB 12/13 (regional indicator code points) require knowledge of previous characters
   and are thus not handled by this function. This may result in an incorrect break before
   an E_Modifier class codepoint and an incorrectly missing break between two
   REGIONAL_INDICATOR class code points if such support does not exist in the caller.

   See the special support in grapheme_break_extended, for required bookkeeping by the caller.
*/


/**************************************************/
/* External Entry Points */


/*
Given form user:pwd, parse into user and pwd
and do %xx unescaping
*/


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param id Ignored.
 * @param nparams Ignored.
 * @param parms Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param parent_ncid Ignored.
 * @param name Ignored.
 * @param new_ncid Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param grpid Ignored.
 * @param name Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param size Ignored.
 * @param name Ignored.
 * @param typeidp Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param name Ignored.
 * @param offset Ignored.
 * @param field Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param name Ignored.
 * @param offset Ignored.
 * @param field Ignored.
 * @param ndims Ignored.
 * @param dim Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param fieldid Ignored.
 * @param name Ignored.
 * @param offsetp Ignored.
 * @param field Ignored.
 * @param ndimsp Ignored.
 * @param dim Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param name Ignored.
 * @param fieldidp Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param datum Ignored.
 * @param name Ignored.
 * @param typeidp Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param name Ignored.
 * @param base_typeid Ignored.
 * @param typeidp Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param base_typeid Ignored.
 * @param name Ignored.
 * @param typeidp Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param xtype Ignored.
 * @param value Ignored.
 * @param identifier Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param idx Ignored.
 * @param identifier Ignored.
 * @param value Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param identifier Ignored.
 * @param value Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param vlen_element Ignored.
 * @param len Ignored.
 * @param data Ignored.
 *
 * @return ::NC_NOERR No error.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param typeid1 Ignored.
 * @param vlen_element Ignored.
 * @param len Ignored.
 * @param data Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param size Ignored.
 * @param nelems Ignored.
 * @param preemption Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param sizep Ignored.
 * @param nelemsp Ignored.
 * @param preemptionp Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param shuffle Ignored.
 * @param deflate Ignored.
 * @param deflate_level Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett, Dennis Heimbigner
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param fletcher32 Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett, Dennis Heimbigner
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param contiguous Ignored.
 * @param chunksizesp Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett, Dennis Heimbigner
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param endianness Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett
 */


/**
 * @internal Not allowed for classic model.
 *
 * @param ncid Ignored.
 * @param varid Ignored.
 * @param par_access Ignored.
 *
 * @return ::NC_ENOTNC4 Not allowed for classic model.
 * @author Ed Hartnett, Dennis Heimbigner
 */


/**
Wrap mktmp and return the generated path,
or null if failed.
Base is the base file path. XXXXX is appended
to allow mktmp add its unique id.
Return the generated path.
*/


/* Used to avoid errors on 64-bit windows related to
   c89 macros and flow control/conditionals. */


/**
 * Computes number of record variables in an open netCDF file, and an array of
 * the record variable ids, if the array parameter is non-null.
 *
 * @param ncid File ID.
 * @param nrecvarsp Pointer that gets number of record variables.
 * @param recvarids Pointer that gets array of record variable IDs.
 *
 * @return ::NC_NOERR No error.
 * @return -1 on error.
 * @author Russ Rew
 */


/**
 * Computes record size (in bytes) of the record variable with a specified
 * variable id.  Returns size as 0 if not a record variable.
 *
 * @param ncid File ID.
 * @param varid Variable ID.
 * @param recsizep Pointer that gets record size.
 *
 * @return size, or 0 if not a record variable
 */


/**
 * Retrieves the dimension sizes of a variable with a specified variable id in
 * an open netCDF file.
 *
 * @param ncid File ID.
 * @param varid Variable ID.
 * @param sizes Pointer that gets sizes.
 *
 * @return ::NC_NOERR No error.
 * @return -1 on error.
 * @author Russ Rew
 */


/** \ingroup v2_api

Retrieves the number of record variables, the record variable ids, and the
record size of each record variable.  If any pointer to info to be returned
is null, the associated information is not returned.  Returns -1 on error.

This is part of the legacy V2 API of netCDF. New code should be
written with the V3 API. See V3 functions nc_inq_nvars(),
nc_inq_unlimdim(), nc_inq_dim().

\param ncid file ID
\param nrecvarsp pointer that will get the number of record variables
in the file.
\param recvarids pointer to array that will get the variable IDs of
all variables that use the record dimension.
\param recsizes pointer to array that will dimension size of the
record dimension for each variable.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad ncid.
\returns ::NC_ENOTVAR Invalid variable ID.
\returns ::NC_EINVAL Invalid input
*/


/** \ingroup v2_api

Write one record's worth of data, except don't write to variables for which
the address of the data to be written is NULL.  Return -1 on error.  This is
the same as the ncrecput() in the library, except that can handle errors
better.

This is part of the legacy V2 API of netCDF. New code should be
written with the V3 API. See V3 function nc_put_vara().

\param ncid file ID
\param recnum the record number to write.
\param datap pointer to one record's worth of data for all variables.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad ncid.
\returns ::NC_ENOTVAR Invalid variable ID.
\returns ::NC_EINVAL Invalid input
*/


/** \ingroup v2_api

Read one record's worth of data, except don't read from variables for which
the address of the data to be read is null.  Return -1 on error.  This is
the same as the ncrecget() in the library, except that can handle errors
better.

This is part of the legacy V2 API of netCDF. New code should be
written with the V3 API. See V3 function nc_get_vara().

\param ncid file ID
\param recnum the record number to read.
\param datap pointer memory to hold one record's worth of data for all
variables.

\returns ::NC_NOERR No error.
\returns ::NC_EBADID Bad ncid.
\returns ::NC_ENOTVAR Invalid variable ID.
\returns ::NC_EINVAL Invalid input

*/


/** \ingroup v2_api

Show an error message and exit (based on ncopts).

This is part of the legacy V2 API of netCDF. New code should be
written with the V3 API. See V3 function nc_strerror()

\param routine_name
\param err error code
\param fmt pointer to a char array containing string format

*/


/** \ingroup v2_api

Create a netCDF file.

This is part of the legacy V2 API of netCDF. New code should be
written with the V3 API. See V3 function nc_create().

\param path path and filename of the file to be created.
\param cmode see nc_create() for full discussion of the create mode.

\returns the ncid of the created file.
*/


/** \ingroup v2_api

Open a netCDF file.

This is part of the legacy V2 API of netCDF. New code should be
written with the V3 API. See V3 function nc_open().

\param path path and filename of the file to be created.
\param mode see nc_open() for full discussion of the open mode.

\returns the ncid of the created file.
*/