/* Phoney free for debugging. */


/* This function prints out a message, if the severity of the message
   is lower than the global nc_log_level. To use it, do something like
   this:

   nc_log(0, "this computer will explode in %d seconds", i);

   After the first arg (the severity), use the rest like a normal
   printf statement. Output will appear on stdout.

   This function is heavily based on the function in section 15.5 of
   the C FAQ. */


/* For debugging purposes, check memory stuff to make sure everything
   that was malloced was freed. This function only exists if the LOGGING
   option is used at compile. */


/* Create a group. It's ncid is returned in the new_ncid pointer. */


/* Given an ncid and group name (NULL gets root group), return
 * the ncid of that group. */


/* Given a location id, return the number of groups it contains, and
 * an array of their locids. */


/* Given locid, find name of group. (Root group is named "/".) */


/* Find the parent ncid of a group. For the root group, return
 * NC_ENOGRP error. */


/* Given parent ncid and name, find ncid. (Root group is named "/".) */


/* Given a full name and ncid, find group ncid. */


/* Get a list of ids for all the variables in a group. */


/* Find all dimids for a location. This finds all dimensions in a
 * group, with or without any of it's parents, depending on last
 * parameter. */


/* This is called when a new netCDF-4 variable is defined. */


/* Create a new variable to hold user data. This is what it's all
 * about baby! */


/* Get all the information about a variable. Pass NULL for whatever
 * you don't care about. This is an internal funtion, not exposed to
 * the user. */


/* Find out about a var. The function ignores any of the pointer
   parameters if a NULL is passed. */


/* This functions sets extra stuff about a netCDF-4 variable which
   must be set before the enddef but after the def_var. This is an
   internal function, deliberately hidden from the user so that we can
   change the prototype of this functions without changing the API. */


/* Set the deflate level for a var, lower is faster, higher is
 * better. Must be called after nc_def_var and before nc_enddef or any
 * functions which writes data to the file. */


/* Find out compression settings of a var. */


/* Set fletcher32 checksum for a var. This must be called after the
 * nc_def_var but before the nc_enddef. */


/* Find out if fletcher32 checksum is in use for a var. */


/* Define chunking stuff for a var. This must be done after nc_def_var
   and before nc_enddef.

   Chunking is required in any dataset with one or more unlimited
   dimension in HDF5.

   Where chunksize is a pointer to an array of size ndims, with the
   chunksize in each dimension.

   When the (netcdf-3) function nc_def_var is used, a sequential
   chunking algorithm will be used.

   The sequential chunking algorithm sets a chunksize of 1 all
   unlimited dimensions, and all other chunksizes to the size of that
   dimension, unless the resulting chunksize is greater than 250 KB,
   in which case subsequent dimensions will be set to 1 until the
   chunksize is less than 250 KB (one quarter of the default chunk
   cache size).
*/


/* Inquire about chunking stuff for a var. */


/* Define fill value behavior for a variable. This must be done after
   nc_def_var and before nc_enddef. */


/* Inq fill value setting for a var. */


/* Define the endianness of a variable. */


/* Learn about the endianness of a variable. */


/* Get var id from name. */


/* Get name from varid. */


/* Return a var's type. */


/* Return number of dims for a var. */


/* Return the dimids of a var. */


/* Return the number of atts of a var. */


/* Rename a var to "bubba," for example.

   According to the netcdf-3.5 docs: If the new name is longer than
   the old name, the netCDF dataset must be in define mode.  */


/* This will copy a variable from one file to another.

   This function must work even if the files are different formats,
   (i.e. one old netcdf, the other hdf5-netcdf.)

   But if you're copying into a netcdf-3 file, from a netcdf-4 file,
   you must be copying a var of one of the six netcdf-3
   types. Similarly for the attributes.

*/


/* Get one little itsy-bitsy datum, with type conversion. */


/* Get one little itsy-bitsy text byte. */


/* Put one little itsy-bitsy text byte. */


/* Get an array of text. */


/* Put an array of text. */


/* Write an array of values. */


/* Read an array of values. */


/* Given an ncid, check the file to make sure it has all the objects I
 * expect. */


/* typedef struct { */
/*       unsigned long 	fileno;		/\*file number			*\/ */
/*       haddr_t 		objno;		/\*object number			*\/ */
/* } HDF5_OBJID_T;  */


/* Find all user-defined types for a location. This finds all
 * user-defined types in a group. */


/* This internal function adds a new user defined type to the metadata
 * of a group of an open file. */


/* Get the name and size of a type. For strings, 0 is returned. For
 * VLEN the base type len is returned. */


/* Create a compound type. */


/* Insert a named field into a compound type. */


/* Get the name of a compound type. */


/* Get the size of a compound type. */


/* Get the number of fields in this compound type. */


/* Get the info about a compound type. */


/* Find info about any user defined type. */


/* Given the ncid, typeid and fieldid, get info about the field. */


/* Given the ncid, typeid and fieldid, get the name. */


/* Find a netcdf-4 file. THis will return an error if it finds a
 * netcdf-3 file, or a netcdf-4 file with strict nc3 rules. */


/* Given the typeid and the name, get the fieldid. */


/* Given the typeid and fieldid, get the offset. */


/* Given the typeid and the fieldid, get the type of that field. */


/* Given the typeid and the fieldid, get the number of dimensions for
 * that field (scalars are 0). */


/* Given the typeid and the fieldid, get the sizes of dimensions for
 * that field. User must have allocated storage for the dim_sizes. */


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


/* Get the name and size of an opaque type. */


/* Write an attribute of opaque type. */


/* Read an attribute of opaque type. */


/* Define a variable length type. */


/* Find out about a vlen. */


/* Create an enum type. Provide a base type and a name. At the moment
 * only ints are accepted as base types. */


/* Get information about an enum type: it's name, base type and the
 * number of members defined. */


/* Get enum name from enum value. Name size will be <= NC_MAX_NAME. */


/* Get information about an enum member: an identifier and
 * value. Identifier size will be <= NC_MAX_NAME. */


/* Insert a identifierd value into an enum type. The value must fit within
 * the size of the enum type, the identifier size must be <= NC_MAX_NAME. */


/* Insert one element into an already allocated vlen array element. */


/* provide a strerror function for older unix systems */


/*
 * Return the number of bytes until the next "word" boundary
 * N.B. This is based on the very wierd YMP address structure,
 * which puts the address within a word in the leftmost 3 bits
 * of the address.
 */


/*
 * Release the stream, invalidate buffer
 */


/*
 * Release the current chunk and get the next one.
 * Also used for initialization when gsp->base == NULL.
 */


/*
 * Ensure that 'nextread' bytes are available.
 */


/* Write a size_t to the header */


/* Read a size_t from the header */


/* Write a nc_type to the header */


/* Read a nc_type from the header */


/* Write a NCtype to the header */


/* Read a NCtype from the header */


/* Write a NC_string to the header */


/* Read a NC_string from the header */


/* Write a NC_dim to the header */


/* Read a NC_dim from the header */


/* Read a NC_dimarray from the header */


/*
 * Put the values of an attribute
 * The loop is necessary since attrp->nelems
 * could potentially be quite large.
 */


/* Write a NC_attr to the header */


/*
 * Get the values of an attribute
 * The loop is necessary since attrp->nelems
 * could potentially be quite large.
 */


/* Read a NC_attr from the header */


/* Read a NC_attrarray from the header */


/* Write a NC_var to the header */


/* Read a NC_var from the header */


/* Read a NC_vararray from the header */


/*
 * Recompute the shapes of all variables
 * Sets ncp->begin_var to start of first variable.
 * Sets ncp->begin_rec to start of first record variable.
 * Returns -1 on error. The only possible error is a reference
 * to a non existent dimension, which could occur for a corrupted
 * netcdf file.
 */


/* Write the file header */


/* Make the in-memory NC structure from reading the file header */


/*
 * Free dim
 * Formerly
NC_free_dim(dim)
 */


/*
 * Formerly
NC_new_dim(const char *uname, long size)
 */


/*
 * Step thru NC_DIMENSION array, seeking the UNLIMITED dimension.
 * Return dimid or -1 on not found.
 * *dimpp is set to the appropriate NC_dim.
 * The loop structure is odd. In order to parallelize,
 * we moved a clearer 'break' inside the loop body to the loop test.
 */


/*
 * Step thru NC_DIMENSION array, seeking match on uname.
 * Return dimid or -1 on not found.
 * *dimpp is set to the appropriate NC_dim.
 * The loop structure is odd. In order to parallelize,
 * we moved a clearer 'break' inside the loop body to the loop test.
 */


/*
 * Free the stuff "in" (referred to by) an NC_dimarray.
 * Leaves the array itself allocated.
 */


/*
 * Free NC_dimarray values.
 * formerly
NC_free_array()
 */


/*
 * Add a new handle on the end of an array of handles
 * Formerly
NC_incr_array(array, tail)
 */


/*
 * Free attr
 * Formerly
NC_free_attr()
 */


/*
 * Free the stuff "in" (referred to by) an NC_attrarray.
 * Leaves the array itself allocated.
 */


/*
 * Free NC_attrarray values.
 * formerly
NC_free_array()
 */


/*
 * Given ncp and varid, return ptr to array of attributes
 *  else NULL on error
 */


/*
 * Look up by ncid, varid and name, return NULL if not found
 */


/*
 * Sortof like ftruncate, except won't make the
 * file shorter.
 */


/*
 * Sortof like ftruncate, except won't make the file shorter.  Differs
 * from fgrow by only writing one byte at designated seek position, if
 * needed.
 */


/* End OS */
/* Begin ffio */


/* ncio_ffio_sync_noffflush is only needed if the FFIO global layer is
 * used, because it currently has a bug that causes the PEs to hang
 * RKO 06/26/98
 */


/* sanity check, about X_SIZE_T_MAX/8 */


/*
 * Get file size in bytes.
 * Is use of ffseek() really necessary, or could we use standard fstat() call
 * and get st_size member?
 */


/*
 * Sync any changes to disk, then extend file so its size is length.
 * This is only intended to be called before close, if the file is
 * open for writing and the actual size does not match the calculated
 * size, perhaps as the result of having been previously written in
 * NOFILL mode.
 */


/*
 * Free string, and, if needed, its values.
 * Formerly
NC_free_string()
 */


/*
 * Verify that a name string is valid syntax.  The allowed name
 * syntax (in RE form) is:
 *
 * ([a-zA-Z_]|{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.
 */


/*
 * Allocate a NC_string structure large enough
 * to hold slen characters.
 * Formerly
NC_new_string(count, str)
 */


/*
 * Free var
 * Formerly
NC_free_var(var)
 */


/*
 * Common code for new_NC_var()
 * and ncx_get_NC_var()
 */


/*
 * Free the stuff "in" (referred to by) an NC_vararray.
 * Leaves the array itself allocated.
 */


/*
 * Free NC_vararray values.
 * formerly
NC_free_array()
 */


/*
 * Step thru NC_VARIABLE array, seeking match on name.
 * Return varid or -1 on not found.
 * *varpp is set to the appropriate NC_var.
 * Formerly (sort of)
NC_hvarid
 */


/*
 * For a netcdf type
 *  return the size of one element in the external representation.
 * Note that arrays get rounded up to X_ALIGN boundaries.
 * Formerly
NC_xtypelen
 * See also ncx_len()
 */


/*
 * 'compile' the shape and len of a variable
 *  Formerly
NC_var_shape(var, dims)
 */


/*
 * Check whether variable size is less than or equal to vlen_max,
 * without overflowing in arithmetic calculations.  If OK, return 1,
 * else, return 0.  For CDF1 format or for CDF2 format on non-LFS
 * platforms, vlen_max should be 2^31 - 4, but for CDF2 format on
 * systems with LFS it should be 2^32 - 4.
 */


/*
 * Given valid ncp and varid, return var
 *  else NULL on error
 * Formerly
NC_hlookupvar()
 */


/*
 *  Verify that this is a user nc_type
 * Formerly
NCcktype()
 * Sense of the return is changed.
 */


/*
 * Compute each variable's 'begin' offset,
 * update 'begin_rec' as well.
 */


/*
 * Read just the numrecs member.
 * (A relatively expensive way to do things.)
 */


/*
 * Write out just the numrecs member.
 * (A relatively expensive way to do things.)
 */


/*
 * Read in the header
 * It is expensive.
 */


/*
 * Write out the header
 */


/*
 * Write the header or the numrecs if necessary.
 */


/*
 * Initialize the 'non-record' variables.
 */


/*
 * Move the records "out".
 * Fill as needed.
 */


/*
 * Move the "non record" variables "out".
 * Fill as needed.
 */


/*
 * Given a valid ncp, return NC_EVARSIZE if any variable has a bad len
 * (product of non-rec dim sizes too large), else return NC_NOERR.
 */


/*
 *  End define mode.
 *  Common code for ncendef, ncclose(endef)
 *  Flushes I/O buffers.
 */


/*
 * Compute the expected size of the file.
 */


/* This function sets a default create flag that will be logically
   or'd to whatever flags are passed into nc_create for all future
   calls to nc_create.
   Valid default create flags are NC_64BIT_OFFSET, NC_CLOBBER,
   NC_LOCK, NC_SHARE. */


/*
 * In data mode, same as ncclose.
 * In define mode, restore previous definition.
 * In create, remove the file.
 */


/* create function versions of the NC_*_numrecs macros */


/* everyone in communicator group will be executing this */
/*ARGSUSED*/


/* Write out a "page" of data to the file. The size of the page
   (i.e. the extent) varies.

   nciop - pointer to the file metadata.
   offset - where in the file should this page be written.
   extent - how many bytes should be written.
   vp - pointer to the data to write.
   posp - pointer to current position in file, updated after write.
*/


/* Read in a page of data.

   nciop - a pointer to the ncio struct for this file.
   offset - byte offset in file where read starts.
   extent - the size of the page that will be read.
   vp - a pointer to where the data will end up.
   nreadp - returned number of bytes actually read (may be less than extent).
   posp - pointer to current position in file, updated after read.
*/


/*ARGSUSED*/
/* This function indicates the file region starting at offset may be
   released.

   This is for POSIX, without NC_SHARE.  If called with RGN_MODIFIED
   flag, sets the modified flag in pxp->bf_rflags and decrements the
   reference count.

   pxp - pointer to posix non-share ncio_px struct.

   offset - file offset for beginning of to region to be
   released.

   rflags - only RGN_MODIFIED is relevent to this function, others ignored
*/


/* This function indicates the file region starting at offset may be
   released.  Each read or write to the file is bracketed by a call to
   the "get" region function and a call to the "rel" region function.
   If you only read from the memory region, release it with a flag of
   0, if you modify the region, release it with a flag of
   RGN_MODIFIED.

   For POSIX system, without NC_SHARE, this becomes the rel function
   pointed to by the ncio rel function pointer. It mearly checks for
   file write permission, then calls px_rel to do everything.

   nciop - pointer to ncio struct.
   offset - num bytes from beginning of buffer to region to be
   released.
   rflags - only RGN_MODIFIED is relevent to this function, others ignored
*/


/* POSIX get. This will "make a region available." Since we're using
   buffered IO, this means that if needed, we'll fetch a new page from
   the file, otherwise, just return a pointer to what's in memory
   already.

   nciop - pointer to ncio struct, containing file info.
   pxp - pointer to ncio_px struct, which contains special metadate
   for posix files without NC_SHARE.
   offset - start byte of region to get.
   extent - how many bytes to read.
   rflags - One of the RGN_* flags defined in ncio.h.
   vpp - pointer to pointer that will recieve data.

   NOTES:

   * For blkoffset round offset down to the nearest pxp->blksz. This
   provides the offset (in bytes) to the beginning of the block that
   holds the current offset.

   * diff tells how far into the current block we are.

   * For blkextent round up to the number of bytes at the beginning of
   the next block, after the one that holds our current position, plus
   whatever extra (i.e. the extent) that we are about to grab.

   * The blkextent can't be more than twice the pxp->blksz. That's
   because the pxp->blksize is the sizehint, and in ncio_px_init2 the
   buffer (pointed to by pxp->bf-base) is allocated with 2 *
   *sizehintp. This is checked (unneccesarily) more than once in
   asserts.

   * If this is called on a newly opened file, pxp->bf_offset will be
   OFF_NONE and we'll jump to label pgin to immediately read in a
   page.
*/


/* Request that the region (offset, extent) be made available through
   *vpp.

   This function converts a file region specified by an offset and
   extent to a memory pointer. The region may be locked until the
   corresponding call to rel().

   For POSIX systems, without NC_SHARE. This function gets a page of
   size extent?

   This is a wrapper for the function px_get, which does all the heavy
   lifting.

   nciop - pointer to ncio struct for this file.
   offset - offset (from beginning of file?) to the data we want to
   read.
   extent - the number of bytes to read from the file.
   rflags - One of the RGN_* flags defined in ncio.h.
   vpp - handle to point at data when it's been read.
*/


/* Like memmove(), safely move possibly overlapping data.

   Copy one region to another without making anything available to
   higher layers. May be just implemented in terms of get() and rel(),
   or may be tricky to be efficient. Only used in by nc_enddef()
   after redefinition.

   nciop - pointer to ncio struct with file info.
   to - src for move?
   from - dest for move?
   nbytes - number of bytes to move.
   rflags - One of the RGN_* flags defined in ncio.h. The only
   reasonable flag value is RGN_NOLOCK.
*/


/* Flush any buffers to disk. May be a no-op on if I/O is unbuffered.
   This function is used when NC_SHARE is NOT used.
*/


/* Internal function called at close to
   free up anything hanging off pvt.
*/


/* This is the second half of the ncio initialization. This is called
   after the file has actually been opened.

   The most important thing that happens is the allocation of a block
   of memory at pxp->bf_base. This is going to be twice the size of
   the chunksizehint (rounded up to the nearest sizeof(double)) passed
   in from nc__create or nc__open. The rounded chunksizehint (passed
   in here in sizehintp) is going to be stored as pxp->blksize.

   According to our "contract" we are not allowed to ask for an extent
   larger than this chunksize/sizehint/blksize from the ncio get
   function.

   nciop - pointer to the ncio struct
   sizehintp - pointer to a size hint that will be rounded up and
   passed back to the caller.
   isNew - true if this is being called from ncio_create for a new
   file.
*/


/* This is the first of a two-part initialization of the ncio struct.
   Here the rel, get, move, sync, and free function pointers are set
   to their POSIX non-NC_SHARE functions (ncio_px_*).

   The ncio_px struct is also partially initialized.
*/


/*ARGSUSED*/
/* This function releases the region specified by offset.

   For POSIX system, with NC_SHARE, this becomes the rel function
   pointed to by the ncio rel function pointer. It mearly checks for
   file write permission, then calls px_rel to do everything.

   nciop - pointer to ncio struct.

   offset - beginning of region.

   rflags - One of the RGN_* flags defined in ncio.h. If set to
   RGN_MODIFIED it means that the data in this region were modified,
   and it needs to be written out to the disk immediately (since we
   are not buffering with NC_SHARE on).

*/


/* Request that the region (offset, extent) be made available through
   *vpp.

   This function converts a file region specified by an offset and
   extent to a memory pointer. The region may be locked until the
   corresponding call to rel().

   For POSIX systems, with NC_SHARE.

   nciop - pointer to ncio struct for this file.
   offset - offset (from beginning of file?) to the data we want to
   read.
   extent - the number of bytes we want.
   rflags - One of the RGN_* flags defined in ncio.h. May be RGN_NOLOCK.
   vpp - handle to point at data when it's been read.
*/


/* Copy one region to another without making anything available to
   higher layers. May be just implemented in terms of get() and rel(),
   or may be tricky to be efficient.  Only used in by nc_enddef()
   after redefinition.

   nciop - pointer to ncio struct for this file.
   to - dest for move?
   from - src for move?
   nbytes - number of bytes to move.
   rflags - One of the RGN_* flags defined in ncio.h.
*/


/*ARGSUSED*/
/* Flush any buffers to disk. May be a no-op on if I/O is unbuffered.
*/


/* This does the second half of the ncio_spx struct initialization for
   POSIX systems, with NC_SHARE on.

   nciop - pointer to ncio struct for this file. File has been opened.
   sizehintp - pointer to a size which will be rounded up to the
   nearest 8-byt boundary and then used as the max size "chunk" (or
   page) to read from the file.
*/


/* First half of init for ncio_spx struct, setting the rel, get, move,
   snyc, and free function pointers to the NC_SHARE versions of these
   functions (i.e. the ncio_spx_* functions).
*/


/* This will call whatever free function is attached to the free
   function pointer in ncio. It's called from ncio_close, and from
   ncio_open and ncio_create when an error occurs that the file
   metadata must be freed.
*/


/* Create a new ncio struct to hold info about the file. This will
   create and init the ncio_px or ncio_spx struct (the latter if
   NC_SHARE is used.)
*/


/* Create a file, and the ncio struct to go with it. This funtion is
   only called from nc__create_mp.

   path - path of file to create.
   ioflags - flags from nc_create
   initialsz - From the netcdf man page: "The argument
   Iinitialsize sets the initial size of the file at creation time."
   igeto -
   igetsz -
   sizehintp - this eventually goes into pxp->blksz and is the size of
   a page of data for buffered reads and writes.
   nciopp - pointer to a pointer that will get location of newly
   created and inited ncio struct.
   igetvpp - pointer to pointer which will get the location of ?
*/


/* This function opens the data file. It is only called from nc.c,
   from nc__open_mp and nc_delete_mp.

   path - path of data file.

   ioflags - flags passed into nc_open.

   igeto - looks like this function can do an initial page get, and
   igeto is going to be the offset for that. But it appears to be
   unused

   igetsz - the size in bytes of initial page get (a.k.a. extent). Not
   ever used in the library.

   sizehintp - pointer to sizehint parameter from nc__open or
   nc__create. This is used to set pxp->blksz.

   Here's what the man page has to say:

   "The argument referenced by chunksize controls a space versus time
   tradeoff, memory allocated in the netcdf library versus number of
   system calls.

   Because of internal requirements, the value may not be set to
   exactly the value requested. The actual value chosen is returned by reference.

   Using the value NC_SIZEHINT_DEFAULT causes the library to choose a
   default. How the system choses the default depends on the
   system. On many systems, the "preferred I/O block size" is
   available from the stat() system call, struct stat member
   st_blksize. If this is available it is used. Lacking that, twice
   the system pagesize is used. Lacking a call to discover the system
   pagesize, we just set default chunksize to 8192.

   The chunksize is a property of a given open netcdf descriptor ncid,
   it is not a persistent property of the netcdf dataset."

   nciopp - pointer to pointer that will get address of newly created
   and inited ncio struct.

   igetvpp - handle to pass back pointer to data from inital page
   read, if this were ever used, which it isn't.
*/


/*
 * Get file size in bytes.
 */


/*
 * Sync any changes to disk, then truncate or extend file so its size
 * is length.  This is only intended to be called before close, if the
 * file is open for writing and the actual size does not match the
 * calculated size, perhaps as the result of having been previously
 * written in NOFILL mode.
 */


/* Write out any dirty buffers to disk and
   ensure that next read will get data from disk.

   Sync any changes, then close the open file associated with the ncio
   struct, and free its memory.

   nciop - pointer to ncio to close.

   doUnlink - if true, unlink file
*/


/*
 * Print the values of an array of size_t
 */


/*
 *  This is how much space is required by the user, as in
 *
 *   vals = malloc(nel * nctypelen(var.type));
 *   ncvarget(cdfid, varid, cor, edg, vals);
 */


/*
 * Next 6 type specific functions
 * Fill a some memory with the default special value.
 * Formerly
NC_arrayfill()
 */


/*
 * Fill the external space for variable 'varp' values at 'recno' with
 * the appropriate value. If 'varp' is not a record variable, fill the
 * whole thing.  For the special case when 'varp' is the only record
 * variable and it is of type byte, char, or short, varsize should be
 * ncp->recsize, otherwise it should be varp->len.
 * Formerly
xdr_NC_fill()
 */


/*
 * Add a record containing the fill values.
 */


/*
 * Add a record containing the fill values in the special case when
 * there is exactly one record variable, where we don't require each
 * record to be four-byte aligned (no record padding).
 */


/*
 * Grow the file to a size which can contain recno
 */


/*
 * Ensure that the netcdf file has 'numrecs' records,
 * add records and fill as neccessary.
 */


/*
 * Check whether 'coord' values are valid for the variable.
 */


/*
 * Translate the (variable, coord) pair into a seek index
 */


/*
 * Copy 'nbytes' contiguous external values
 * from ('inncp', invp', inncoord')
 * to   ('outncp', 'outvp', 'outcoord')
 * 'inncp' shouldn't be the same as 'outncp'.
 * Used only by ncvarcopy()
 */


/*
 *  For ncvar{put,get},
 *  find the largest contiguous block from within 'edges'.
 *  returns the index to the left of this (which may be -1).
 *  Compute the number of contiguous elements and return
 *  that in *iocountp.
 *  The presence of "record" variables makes this routine
 *  overly subtle.
 */


/*
 * The infamous and oft-discussed odometer code.
 *
 * 'start[]' is the starting coordinate.
 * 'upper[]' is the upper bound s.t. start[ii] < upper[ii].
 * 'coord[]' is the register, the current coordinate value.
 * For some ii,
 * upp == &upper[ii]
 * cdp == &coord[ii]
 *
 * Running this routine increments *cdp.
 *
 * If after the increment, *cdp is equal to *upp
 * (and cdp is not the leftmost dimension),
 * *cdp is "zeroed" to the starting value and
 * we need to "carry", eg, increment one place to
 * the left.
 *
 * TODO: Some architectures hate recursion?
 * 	Reimplement non-recursively.
 */


/*
 * Generalized hyperslab input.
 */


/*
 * Generalized hyperslab output.
 */


/*
 * Copy the values of a variable from an input netCDF to an output netCDF.
 * Input and output var assummed to have the same shape.
 * return -1 on error.
 */


/* no longer deprecated, used to support the 2.x interface  and also the netcdf-4 api. */


/*
 * 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.
 */


/*
 * Computes record size (in bytes) of the record variable with a specified
 * variable id.  Returns size as 0 if not a record variable.
 */


/*
 * Retrieves the dimension sizes of a variable with a specified variable id in
 * an open netCDF file.  Returns -1 on error.
 */


/*
 * 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.
 */


/*
 * 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.
 */


/*
 * 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.
 */


/* End error handling */


/*
 * Returns number of record variables in an open netCDF file, and an array of
 * the record variable ids, if the array parameter is non-null.  Returns -1 on
 * error.
 */


/*
 * Returns record size (in bytes) of the record variable with a specified
 * variable id.  Returns 0 if not a record variable.  Returns -1 on error.
 */


/*
 * 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 the same as the ncrecinq() in the library, except that can handle
 * errors better.
 */


/* fill typed value block with values of specified type */


/* fill typed value block with zeros of specified type */


/*
 * compare two typed value blocks, return 0 if equal, 1+n otherwise,
 * where n is the index of the first differing element.
 */


/* print typed value block with values of specified type */


/*
 * 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
 */


/*
 * Turn off netCDF library handling of errors.  Caller must check all error
 * returns after calling this, until on_errs() is called.
 */


/*
 * Let netCDF library handle subsequent errors.  Callers don't need to check
 * error returns after this.  (This is the initial default.)
 */


/*
				 * in-memory netcdf structure, kept in sync
				 * with disk netcdf
				 */


/**
Given a partial suffix path and a specified
protocol, test if a request to any of the test
servers + path returns some kind of result.
This indicates that the server is up and running.
Return the complete url for the server plus the path.
*/


/*
See if a server is responding.
Return NC_ECURL if the ping fails, NC_NOERR otherwise
*/


/* get a (unsigned) char from underlying stream*/


/* get an unsigned short from underlying stream*/


/* get a unsigned int from underlying stream*/


/* get a long long  from underlying stream*/


/* get some bytes from underlying stream;
   will move xdrs pointer to next XDRUNIT boundary*/


/* get counted string from underlying stream*/


/* returns bytes off from beginning*/


/* reposition the stream*/


/* returns total available starting at current position */


/* free up XXDR  structure */


/* Skip exactly "len" bytes in the input; any rounding must be done by the caller*/


/* skip "n" string/bytestring instances in the input*/


/**************************************************/
/* File based xdr */


/*
 * Initialize a stdio xdr stream.
 * Sets the xdr stream handle xdrs for use on the stream file.
 * Operation flag is set to op.
 */


/**************************************************/
/* memory based xdr */


/*
 * Initialize a stdio xdr stream.
 * Sets the xdr stream handle xdrs for use on the
 * given memory starting at base offset.
 */


/* get a float from underlying stream*/


/* Get a double from underlying stream */


/* Double needs special handling */


/* Assume we are operating on the datadds tree */


/* XDR representation size depends on if this is scalar or not */


/* Not all systems have strndup, so provide one*/


/* Do not trust strncmp semantics; this one
   compares upto len chars or to null terminators */


/* Compute total # of elements if dimensioned*/


/* if we get OC_EDATADDS error, then try to capture any
   error message and log it; assumes that in this case,
   the datadds is not big.
*/


/* Given some set of indices [i0][i1]...[in] (where n == rank-1)
   and the maximum sizes, compute the linear offset
   for set of dimension indices.
*/


/* Inverse of ocarrayoffset: convert linear index to a set of indices */


/* Given some set of edge counts [i0][i1]...[in] (where n == rank-1)
   and the maximum sizes, compute the linear offset
   for the last edge position
*/


/* Use the initial keyword to indicate what we are parsing */


/* Create an ocnode and capture in the state->ocnode list */


/* Wrapper for dapparse */


/*
Set a specific curl flag; primary wrapper for curl_easy_setopt
*/


/*
Update a specific flag from state
*/


/* Set various general curl flags per fetch  */


/* Set various general curl flags per link */


/* See if http: protocol is supported */


/* Process ocnodes to fix various semantic issues*/


/* Convert path to a string */


/* Collect the set of nodes ending in "node"*/


/*
In order to be as compatible as possible with libdap,
we try to use the same algorithm for DAS->DDS matching.
As described there, the algorithm is as follows.
    If the [attribute] name contains one or
    more field separators then look for a [DDS]variable whose
    name matches exactly. If the name contains no field separators then
    the look first in the top level [of the DDS] and then in all subsequent
    levels and return the first occurrence found. In general, this
    searches constructor types in the order in which they appear
    in the DDS, but there is no requirement that it do so.

    Note: If a dataset contains two constructor types which have field names
    that are the same (say point.x and pair.x) one should use fully qualified
    names to get each of those variables.

*/


/*
Mark cacheable those atomic String/URL typed nodes
that are contained only in structures with rank > 0.
*/


/* Merge das attributes into the dds node*/


/*----------------------------------------.
| Print this symbol's value on YYOUTPUT.  |
`----------------------------------------*/


/*--------------------------------.
| Print this symbol on YYOUTPUT.  |
`--------------------------------*/


/* Use this to attach to a data tree for a DATADDS */


/* Move to the ith sequence record. */


/**************************************************/
/*
In order to actually extract data, one must move to the
specific primitive typed field containing the data of
interest by using ocdata_fieldith().  Then this procedure
is invoked to extract some subsequence of items from the
field.  For scalars, the start and count are ignored.
Note that start and count are linearized from the oc_data_read
arguments.
*/


/**************************************************/
/*
Extract data from a leaf into memory.
*/


/*!
This procedure opens a link to some OPeNDAP
data server to request a specific url, possibly with constraints.
It returns an <i>OClink</i> object.
\param[in] url The url for the OPeNDAP server to which a connection
is created and the request is made.
\param[out] linkp A pointer to a location into which the link
object is to be returned.

\retval OC_NOERR The link was successfully created.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
*/


/*!
Alias for oc_dds_ithfield.

\param[in] link The link through which the server is accessed.
\param[in] ddsnode The container node of interest.
\param[in] index The index (starting at zero) of the field to return.
\param[out] fieldnodep  A pointer into which the i'th field node is stored.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINDEX The index was greater than the number of fields.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
\retval OC_EBADTYPE The dds node is not a container node.
*/


/*!
Obtain the DDS node corresponding to the array of a Grid container.
Equivalent to oc_dds_ithfield(link,grid-container,0,arraynode).

\param[in] link The link through which the server is accessed.
\param[in] grid The grid container node of interest.
\param[out] arraynodep  A pointer into which the grid array node is stored.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
*/


/*!
Obtain the DDS node corresponding to the i'th map of a Grid container.
Equivalent to oc_dds_ithfield(link,grid-container,index+1,arraynode).
Note the map index starts at zero.

\param[in] link The link through which the server is accessed.
\param[in] grid The grid container node of interest.
\param[in] index The (zero-based) index of the map node to return.
\param[out] mapnodep  A pointer into which the grid map node is stored.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINDEX The map index is illegal.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
*/


/*!
Given a counted vector of strings, free up all of the strings,
BUT NOT THE VECTOR since that was allocated by the caller.

\param[in] n The link through which the server is accessed.
\param[in] svec The node of interest.
*/


/*!
Obtain a data node by name from a container data node.

\param[in] link The link through which the server is accessed.
\param[in] datanode The container data node instance of interest.
\param[in] name The name of the field instance to return.
\param[out] fieldp  A pointer into which the i'th field instance is stored.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINDEX No field with the given name was found.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
\retval OC_EBADTYPE The data node is not a container node.
*/


/*!
Obtain the data instance corresponding to the array field
of a Grid container instance.
Equivalent to oc_data_ithfield(link,grid,0,arraydata).

\param[in] link The link through which the server is accessed.
\param[in] grid The grid container instance of interest.
\param[out] arraydatap  A pointer into which the grid array instance is stored.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
*/


/*!
Obtain the data instance corresponding to the ith map field
of a Grid container instance.
Equivalent to oc_data_ithfield(link,grid-container,index+1,mapdata).
Note that Map indices start at zero.

\param[in] link The link through which the server is accessed.
\param[in] grid The grid container instance of interest.
\param[in] index The map index of the map to return.
\param[out] mapdatap A pointer into which the grid map instance is stored.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
*/


/*!
This procedure is a variant of oc_data_read for reading a
single scalar from a leaf instance of a data tree and
storing it into memory for use by the calling code.  The
data instance must be referencing a scalar primitive value.
That is, its oc_data_octype() value must be OCatomic.

\param[in] link The link through which the server is accessed.
\param[in] datanode The data node instance of interest.
\param[in] memsize The size (in bytes) of the memory argument.
\param[out] memory User allocated memory into which the extracted
data is to be stored. The caller is responsible for allocating and free'ing
this argument.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINVAL The memsize argument is too small to hold
the specified data.
\retval OC_ESCALAR The data instance is not a scalar.
\retval OC_EDATADDS The data retrieved from the server was malformed
and the read request cannot be completed.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
*/


/*!
This procedure is a variant of oc_data_read for reading a single scalar.
This procedure has the same semantics as oc_data_readscalar.
However, it takes an OCddsnode as argument.
The limitation is that the DDS node must be a top-level, atomic variable.
Top-level means that it is not nested in a Sequence or a
dimensioned Structure; being in a Grid is ok as is being in
a scalar structure.

\param[in] link The link through which the server is accessed.
\param[in] ddsnode The dds node instance of interest.
\param[in] memsize The size (in bytes) of the memory argument.
\param[out] memory User allocated memory into which the extracted
data is to be stored. The caller is responsible for allocating and free'ing
this argument.

\retval OC_NOERR The procedure executed normally.
\retval OC_EINVAL The memsize argument is too small to hold
the specified data.
\retval OC_ESCALAR The data instance is not a scalar.
\retval OC_EDATADDS The data retrieved from the server was malformed
and the read request cannot be completed.
\retval OC_EINVAL  One of the arguments (link, etc.) was invalid.
*/


/*!
Return the size of the C data structure corresponding
to a given atomic type.
For example,
oc_typesize(OC_Int32) == sizeof(int), and
oc_typesize(OC_String) == sizeof(char*).
Non-atomic types (e.g. OC_Structure) return zero.

\param[in] etype The atomic type.

\return The C size of the atomic type.
*/


/*!
Return a string corresponding to the
to a given OCtype.
For example,
oc_typetostring(OC_Int32) == "Int32" and
oc_typesize(OC_Structure) == "Structure".
The caller MUST NOT free the returned string.

\param[in] octype The OCtype value.

\return The name, as a string, of that OCtype value.
*/


/*!
Print a value of an atomic type instance.
This is primarily for debugging and provides
a simple way to convert a value to a printable string.

\param[in] etype The OCtype atomic type.
\param[in] value A pointer to the value to be printed.
\param[in] bufsize The size of the buffer argument
\param[in] buffer The buffer into which to store the printable
value as a NULL terminated string.

\retval OC_NOERR if the procedure succeeded
\retval OC_EINVAL  if one of the arguments is illegal.
*/


/*!
Return a user-readable error message corresponding
to a given OCerror value.

\param[in] err The OCerror value.

\return The error message
*/


/*!
Attempt to retrieve a dataset using a specified URL
and using the DAP protocol.

\param[in] url The url to use for the request.

\retval OC_NOERR if the request succeeded.
\retval OC_EINVAL if the request failed.
*/


/* Free up a datanode that is no longer being used;
   Currently does nothing
*/


/* Free up a ddsnode that is no longer being used;
   Currently does nothing
*/


/* Modify constraint to use %XX escapes */


/*
    Extract state values from .rc file
*/


/*
    Set curl properties for link based on fields in the state.
*/


/**************************************************/
/* Curl option functions */
/*
Note that if we set the option in curlflags,
then we need to also invoke the ocset_curlopt
to update the curl flags in libcurl.
*/


/*
 * "Expands" valid escape sequences in yystring (read by lex) into the
 * appropriate characters in termstring.  For example, the two character
 * sequence "\t" in yystring would be converted into a single tab character
 * in termstring.  On return, termstring is properly terminated.
 */


/*
 * From a long line FORTRAN statement, generates the necessary FORTRAN
 * lines with continuation characters in column 6.  If stmnt starts with "*",
 * it is treated as a one-line comment.  Statement labels are *not* handled,
 * but since we don't generate any labels, we don't care.
 */


/*
 * Replace special chars in name so it can be used in C and Fortran
 * variable names without causing syntax errors.  Here we just replace
 * each "-" in a name with "_MINUS_", each "." with "_PERIOD_", etc.
 * For bytes with high bit set, from UTF-8 encoding of Unicode, just
 * replace with "_xHH", where each H is the appropriate hex digit.  If
 * a name begins with a number N, such as "4LFTX", replace with
 * "DIGIT_N_", such as "DIGIT_4_LFTX".
 *
 * Returned name is malloc'ed, so caller is responsible for freeing it.
 */


/* Find CDL name in symbol table (linear search).  Note, this has a
 * side-effect: it handles escape characters in the name, deleting
 * single escape characters from the CDL name, before looking it up.
 */


/* yy_get_next_buffer - try to read in a new buffer
 *
 * Returns a code representing an action:
 *	EOB_ACT_LAST_MATCH -
 *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
 *	EOB_ACT_END_OF_FILE - end of file
 */


/* yy_try_NUL_trans - try to make a transition on the NUL character
 *
 * synopsis
 *	next_state = yy_try_NUL_trans( current_state );
 */


/** Immediately switch to a different input stream.
 * @param input_file A readable stream.
 *
 * @note This function does not reset the start condition to @c INITIAL .
 */


/** Switch to a different input buffer.
 * @param new_buffer The new input buffer.
 *
 */


/** Allocate and initialize an input buffer state.
 * @param file A readable stream.
 * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
 *
 * @return the allocated buffer state.
 */


/** Destroy the buffer.
 * @param b a buffer created with ncg_create_buffer()
 *
 */


/* Initializes or reinitializes a buffer.
 * This function is sometimes called more than once on the same buffer,
 * such as during a ncgrestart() or at EOF.
 */


/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
 * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
 *
 */


/** Pushes the new state onto the stack. The new state becomes
 *  the current state. This function will allocate the stack
 *  if necessary.
 *  @param new_buffer The new state.
 *
 */


/** Removes and deletes the top of the stack, if present.
 *  The next element becomes the new top.
 *
 */


/* Allocates the stack if it does not exist.
 *  Guarantees space for at least one push.
 */


/** Setup the input buffer state to scan directly from a user-specified character buffer.
 * @param base the character buffer
 * @param size the size in bytes of the character buffer
 *
 * @return the newly allocated buffer state object.
 */


/** Get the current line number.
 *
 */


/** Set the current line number.
 * @param line_number
 *
 */


/* ncglex_destroy is for both reentrant and non-reentrant scanners. */


/* Hack to keep compile quiet */


/* Convert current dapodometer settings to a single integer count*/


/* Make trackable ERR macro replacement */


/* initialize a dimmap for n dimension ids */


/* store association between an input dimid and an output dimid, which
 * should be ints between 0 and ndims-1, inclusive */


/* return whether idimid dimension is unlimited */


/* Return size in bytes of a variable value */


/* Get parent id needed to define a new group from its full name in an
 * open file identified by ncid.  Assumes all intermediate groups are
 * already defined.  */


/* Compute the fully qualified name of a  (grpid,varid) pair; caller must free */


/* Return size of chunk in bytes for a variable varid in a group igrp, or 0 if
 * layout is contiguous */


/* Return estimated number of elems required in chunk cache and
 * estimated size of chunk cache adequate to efficiently copy input
 * variable ivarid to output variable ovarid, which may have different
 * chunk size and shape */


/*
 * copy a user-defined variable length type in the group igrp to the
 * group ogrp
 */


/*
 * copy a user-defined opaque type in the group igrp to the group ogrp
 */


/*
 * copy a user-defined enum type in the group igrp to the group ogrp
 */


/*
 * copy a user-defined compound type in the group igrp to the group ogrp
 */


/*
 * copy a user-defined type in the group igrp to the group ogrp
 */


/* Copy a group and all its subgroups, recursively, from iroot to
 * oroot, the ncids of input file and output file.  This just creates
 * all the groups in the destination, but doesn't copy anything that's
 * in the groups yet. */


/*
 * Copy the user-defined types in this group (igrp) and all its
 * subgroups, recursively, to corresponding group in output (ogrp)
 */


/* Copy netCDF-4 specific variable filter properties */
/* Watch out if input is netcdf-3 */


/* Propagate chunking from input to output taking -c flags into account. */
/* Subsumes old set_var_chunked */


/* Copy all netCDF-4 specific variable properties such as chunking,
 * endianness, deflation, checksumming, fill, etc. */


/* Copy dimensions from group igrp to group ogrp, also associate input
 * dimids with output dimids (they need not match, because the input
 * dimensions may have been defined in a different order than we define
 * the output dimensions here. */


/* Copy the attributes for variable ivar in group igrp to variable
 * ovar in group ogrp.  Global (group) attributes are specified by
 * using the varid NC_GLOBAL */


/* copy the schema for a single variable in group igrp to group ogrp */


/* copy the schema for all the variables in group igrp to group ogrp */


/* Copy the schema in a group and all its subgroups, recursively, from
 * group igrp in input to parent group ogrp in destination.  Use
 * dimmap array to map input dimids to output dimids. */


/* Return number of values for a variable varid in a group igrp */


/* Copy data from variable varid in group igrp to corresponding group
 * ogrp. */


/* Copy data from variables in group igrp to variables in
 * corresponding group with parent ogrp, and all subgroups
 * recursively  */


/* Count total number of dimensions in ncid and all its descendant subgroups */


/* Test if special case: netCDF-3 file with more than one record
 * variable.  Performance can be very slow for this case when the disk
 * block size is large, there are many record variables, and a
 * record's worth of data for some variables is smaller than the disk
 * block size.  In this case, copying the record variables a variable
 * at a time causes much rereading of record data, so instead we want
 * to copy data a record at a time. */


/* Classify variables in ncid as either fixed-size variables (with no
 * unlimited dimension) or as record variables (with an unlimited
 * dimension) */


/* Only called for classic format or 64-bit offset format files, to speed up special case */


/* copy a record's worth of data for a variable from input to output */


/* copy infile to outfile using netCDF API
 */


/*
 * For non-negative numeric string with multiplier suffix K, M, G, T,
 * or P (or lower-case equivalent), return corresponding value
 * incorporating multiplier 1000, 1000000, 1.0d9, ... 1.0d15, or -1.0
 * for error.
 */


/*
 * convert pathname of netcdf file into name for cdl unit, by taking
 * last component of path and stripping off any extension.
 * DMH: add code to handle OPeNDAP url.
 * DMH: I think this also works for UTF8.
 */


/* Return file type string */


/* Return extended format string */


/*
 * Emit initial line of output for NcML
 */


/*
 * Print list of attribute values, for attributes of primitive types.
 * Attribute values must be printed with explicit type tags for
 * netCDF-3 primitive types, because CDL doesn't require explicit
 * syntax to declare such attribute types.
 */


/* Common code for printing attribute name */


/*
 * Print special _Format global attribute, a virtual attribute not
 * actually stored in the file.
 */


/*
 * Print special reserved variable attributes, such as _Chunking,
 * _DeflateLevel, ...  These are virtual, not real, attributes
 * generated from the result of inquire calls.  They are of primitive
 * type to fit into the classic model.  Currently, these only exist
 * for netCDF-4 data.
 */


/* Recursively dump the contents of a group. (Only netcdf-4 format
 * files can have groups, so recursion will not take place for classic
 * format files.)
 *
 * ncid: id of open file (first call) or group (subsequent recursive calls)
 * path: file path name (first call)
 */


/* replace path string with same string prefixed by
 * DAP_CLIENT_NCDUMP_DIRECTIVE */


/* set from command-line options */


/* This routine really is intended to return the max atomic typeid */


/*
 * Return ( *(float* )v1p == *(float* )v2p);
 * except use floating epsilon to compare very close vals as equal
 * and handle IEEE NaNs and infinities.
 */


/*
 * Return ( *(double* )v1p == *(double* )v2p);
 * except use floating epsilon to compare very close vals as equal
 * and handle IEEE NaNs and infinities.
 */


/*
 * Print a number of char values as a text string.
 */


/* Convert value of any numeric type to a double.  Beware, this may
 * lose precision for values of type NC_INT64 or NC_UINT64 */


/*
 * Print type name with CDL escapes for special characters.  locid is
 * the id of the group in which the type is referenced, which is
 * needed to determine whether an absolute type name must be printed.
 * If the type is defined in the referenced group or in some ancestor
 * group, only the simple type name is printed.  If the type is
 * defined in some other non-ancestor group, an absolute path for the
 * typename is printed instead.
 */


/*
 * Parse chunkspec string of either kind.
 * Returns NC_NOERR if no error, NC_EINVAL if spec was malformed.
 */


/*
 * Parse chunkspec string and convert into dimchunkspec structure.
 *   ncid: location ID of open netCDF file or group in an open file
 *   spec: string of form
 *           dim1/n1,dim2/n2,...,dimk/nk
 *   specifying chunk size (ni) to be used for dimension named
 *   dimi.  Dimension names may be absolute,
 *   e.g. "/grp_a/grp_a1/dim".  The "ni" part of the spec may be
 *   omitted, in which case it is assumed to be the entire
 *   dimension size.  That is also the default for dimensions
 *   not mentioned in the string. However, for unlimited dimensions,
 *   the default is a default size: 4 megabytes or the
 *   existing unlimited size if smaller.
 *   If the chunkspec string is "/", specifying no dimensions or
 *   chunk sizes, it indicates chunking to be turned off on output.
 *
 * Returns NC_NOERR if no error, NC_EINVAL if spec has consecutive
 * unescaped commas or no chunksize specified for dimension.
 */


/* Return size in chunkspec string specified for dimension corresponding to dimid, 0 if not found */


/*
 * Parse per-variable chunkspec string and convert into varchunkspec structure.
 *   ncid: location ID of open netCDF file or group in an open file
 *   spec: string of form
 *           var:n1,n2,...nk
 *
 *         specifying chunk size (ni) to be used for ith dimension of
 *         variable named var. Variable names may be absolute.
 *         e.g. "/grp_a/grp_a1/var".
 *         If no chunk sizes are specified, then the variable is not chunked at all.
 *
 * Returns NC_NOERR if no error, NC_EINVAL if spec has consecutive
 * unescaped commas or no chunksize specified for dimension.
 */


/* rkr: added bounds functions to detect bounds variables of time variables */


/* Check for optional "calendar" attribute and return specified
 * calendar type, if present. */


/* Test if attribute is of form required by cdtime:
 *     <time_unit> since <base_time>
 * where
 *     <time_unit>:
 */


/* Return true only if this is a "bounds" attribute */


/* Insert info about a bounds attribute into bounds list, so we can
 * later determine which variables are bounds variables for which
 * other variables.  att must be a variable "bounds" attribute.  */


/* print_att_times
 * by Dave Allured, NOAA/PSD/CIRES.
 * This version supports only primitive attribute types; do not call
 * for user defined types.  Print interpreted, human readable (ISO)
 * time strings for an attribute of a CF-like time variable.
 *
 * Print strings as CDL comments, following the normal non-decoded
 * numeric values, which were already printed by the calling function.
 * In the following example, this function prints only the right hand
 * side, starting at the two slashes:
 *
 *    time:actual_range = 51133., 76670. ; // "1940-01-01", "2009-12-01"
 *
 * This function may be called for ALL primitive attributes.
 * This function qualifies the attribute for numeric type and
 * inheriting valid time attributes (has_time).  If the attribute
 * does not qualify, this function prints nothing and safely
 * returns.
 *
 * This function interprets and formats time values with the SAME
 * methods already used in ncdump -t for data variables.
 *
 * This version has special line wrapping rules:
 *
 * (1) If the attribute has one or two values, the time strings are
 *     always printed on the same line.
 *
 * (2) If the attribute has three or more values, the time strings
 *     are always printed on successive lines, with line wrapping
 *     as needed.
 *
 * Assume: Preceding call to pr_att_valgs has already screened
 * this attribute for valid primitive types for the current netcdf
 * model (netcdf 3 or 4).
 */


/* Convert a full path name to a group to the specific groupid. */


/* Convert a full path name to a varid to the specific varid + grpid */


/*
 * return true if id is member of list that idlist points to.
 */


/*
 * Release a variable list.
 */


/*
 * Return true if group identified by grpid is member of grpids, a list of groups.
 * nlgrps is number of groups in the list.
 */


/* Determine whether a group named formatting_specs.lgrps[igrp] exists
 * in a netCDF file or group with id ncid.  If so, return the count of
 * how many matching groups were found, else return a count of 0.  If
 * the name begins with "/", it is interpreted as an absolute group
 * name, in which case only 0 or 1 is returned.  Otherwise, interpret
 * it as a relative name, and the total number of occurrences within
 * the file/group identified by ncid is returned.
 *
 * Also has side effect of updating the ngrpids and the associate
 * grpids array that represent the group list specified by the -g
 * option.  TODO: put this in its own function instead.
 */


/* Check if any group names specified with "-g grp1,...,grpn" are
 * missing.  Returns total number of matching groups if no missing
 * groups detected, otherwise exits. */


/* Returns 1 if string s1 ends with string s2, 0 otherwise. */


/* Get varid of variable with name using nested group syntax
 * "gp1/gp2/var" or "/gp1/gp2/var".  In the former case, grpname of
 * grp corresponding to grpid must end in "gp1/gp2".  In the latter
 * case, grpname for grpid must be exactly "/gp1/gp2".  If variable
 * named "var" is not in group grpid, returns NC_ENOTVAR, else sets
 * varid and returns NC_NOERR.  */


/* Determine whether a variable named varname exists in any group in
   an open netCDF file with id ncid.  If so, return the count of how
   many matching variables were found, else return a count of 0.  The
   variable name can be absolute such as "/foo" or "/GRP1/GRP1A/foo",
   in which case there is only one group to look in, given by the path
   from the root group.  Alternatively, the variable name can be
   relative, such as "foo" or "GRPA/GRPB/foo", in which case every
   group is examined for a variable with that relative name.  */


/* Check if any variable names specified with "-v var1,...,varn" are
 * missing.  Returns 0 if no missing variables detected, otherwise
 * exits. */


/* initialize and return a new empty stack of grpids */


/* free a stack and all its nodes */


/* test if a stack is empty */


/* push a grpid on stack */


/* pop value off stack and return */


/* Return top value on stack without popping stack.  Defined for
 * completeness but not used (here). */


/* Like netCDF-4 function nc_inq_grps(), but can be called from
 * netCDF-3 only code as well.  Maybe this is what nc_inq_grps()
 * should do if built without netCDF-4 data model support. */


/* Initialize group iterator for start group and all its descendant
 * groups. */


/*
 * Get group id of next group.  On first call gets start group id,
 * subsequently returns other subgroup ids in preorder.  Returns zero
 * when no more groups left.
 */


/*
 * Release group iter.
 */


/*
 * Get total number of groups (including the top-level group and all
 * descendant groups, recursively) and all descendant subgroup ids
 * (including the input rootid of the start group) for a group and
 * all its descendants, in preorder.
 *
 * If grpids or numgrps is NULL, it will be ignored.  So typical use
 * is to call with grpids NULL to get numgrps, allocate enough space
 * for the group ids, then call again to get them.
 */


/* Insert at position i of l; will push up elements i..|seq|. */


/* Remove element by value; only removes first encountered */


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


/*
 * Output a string that should not be split across lines.  If it would
 * make current line too long, first output a newline and current
 * (nested group) indentation, then continuation indentation, then
 * output string.  If string ends with a newline to force short line,
 * reset indentation after output.
 */


/*
 * Prints brief annotation for a row of data values
 */


/*  Print data values for variable varid.
 *
 * Recursive to handle possibility of variables with multiple
 * unlimited dimensions, for which the CDL syntax requires use of "{"
 * and "}" in data section to disambiguate the size of nested records
 * in a simple linear list of values.
 */


/* Initialize block iteration for variables, including those that
 * won't fit in the copy buffer all at once.  Returns error if
 * variable is chunked but size of chunks is too big to fit in bufsize
 * bytes. */


/* From netCDF type in group igrp, get size in memory needed for each
 * value.  Wouldn't be needed if nc_inq_type() was a netCDF-3 function
 * too. */


/*
 * Updates a vector of size_t, odometer style.  Returns 0 if odometer
 * overflowed, else 1.
 */


/*
 * Updates a vector of size_t, odometer style, for chunk access.
 * Returns 0 if odometer overflowed, else 1.
 */


/* Initialize iteration for a variable.  Just a wrapper for
 * nc_blkio_init() that makes the netCDF calls needed to initialize
 * lower-level iterator. */


/* Iterate on blocks for variables, by updating start and count vector
 * for next vara call.  Assumes nc_get_iter called first.  Returns
 * number of variable values to get, 0 if done, negative number if
 * error, so use like this:
   size_t to_get;
   while((to_get = nc_next_iter(&iter, start, count)) > 0) {
      ... iteration ...
   }
   if(to_get < 0) { ... handle error ... }
 */


/* Free iterator and its internally allocated memory */


/**
This is the main procedure for
printing a data tree.
*/


/* Print a single container datanode */


/* Add escape characters to a string */


/* Add escape characters to an identifier */


/**
Return 1 if the given string, used as a value, should be escaped.
*/


/* Return 0 if we have exhausted the indices, 1 otherwise */


/* Compute total # of elements if dimensioned */


/*
Read the chunking information about the variable
and verify that it is as expected.
*/


/*
Compare the data we wrote against the data we read.
*/


/* Parse incoming url constraints, if any,
   to check for syntactic correctness
*/


/*
Given two projection lists, merge
src into dst taking
overlapping projections into acct.
Dst will be modified.
*/


/* Modify merged projection to include "addition" projection */


/* Convert a DCEprojection instance into a string
   that can be used with the url
*/


/* Mostly for debugging */


/* Collect all nodes within a specified constraint tree */
/* Caller frees result */


/* Remove slicing from terminal segment of p */


/* Compute segment size for subset of slices */


/* Return the index of the leftmost slice
   starting at start and up to, but not including
   stop, such that it and all slices to the right
   are "safe". Safe means dceiswholeslice() is true.
   In effect, we can read the safe index set as a
   single chunk. Return stop if there is no safe index.
*/


/**************************************************/
/**
1. We build the projection to be sent to the server aka
the fetch constraint.  We want the server do do as much work
as possible, so we send it a url with a fetch constraint
that is the merge of the url constraint with the vara
constraint.

The url constraint, if any, is the one that was provided
in the url specified in nc_open().

The vara constraint is the one formed from the arguments
(start, count, stride) provided to the call to nc_get_vara().

There are some exceptions to the formation of the fetch constraint.
In all cases, the fetch constraint will use any URL selections,
but will use different fetch projections.
a. URL is unconstrainable (e.g. file://...):
	   fetchprojection = null => fetch whole dataset
b. The target variable (as specified in nc_get_vara())
   is already in the cache and is whole variable, but note
   that it might be part of the prefetch mixed in with other prefetched
   variables.
	   fetchprojection = N.A. since variable is in the cache
c. Vara is requesting part of a variable but NCF_WHOLEVAR flag is set.
	   fetchprojection = unsliced vara variable => fetch whole variable
d. Vara is requesting part of a variable and NCF_WHOLEVAR flag is not set.
	   fetchprojection = sliced vara variable => fetch part variable

2. At this point, all or part of the target variable is available in the cache.

3. We build a projection to walk (guide) the use of the oc
   data procedures to extract the required data from the cache.
   For cases a,b,c:
       walkprojection = merge(urlprojection,varaprojection)
   For case d:
       walkprojection =  varaprojection without slicing.
       This means we need only extract the complete contents of the cache.
       Notice that this will not necessarily be a direct memory to
       memory copy because the dap encoding still needs to be
       interpreted. For this case, we derive a walk projection
       from the vara projection that will properly access the cached data.
       This walk projection shifts the merged projection so all slices
       start at 0 and have a stride of 1.
*/


/* Remove any pseudodimensions (sequence and string)*/


/* Determine the index in the odometer at which
   the odometer will be walking the whole subslice
   This will allow us to optimize.
*/


/* We are at a primitive variable or scalar that has no string dimensions.
   Extract the data. (This is way too complicated)
*/


/*
Extract data for a netcdf variable that has a string dimension.
*/


/*
Given DDS node, locate the node
in a DATADDS that matches the DDS node.
Return NULL if no node found
*/


/*
Match a DATADDS node to a DDS node.
It is assumed that both trees have been re-struct'ed if necessary.
*/


/*
Match nodes in pattern tree to nodes in target tree;
pattern tree is typically a structural superset of target tree.
WARNING: Dimensions are not attached
*/


/**
Convert an attribute value string to a specific type.
If the conversion fails, then return NC_EBADTYPE.
If we need an int and the string value is out of range, return NC_ERANGE.
@param etype the target type
@param dst the memory into which to put the converted constants
@param src list of constants as strings
*/


/**
Convert a struct Value.dval field to a long in struct Value.llval field.
Report if the result is out of range wrt NC_MAX/MIN_INT.
 @param val store original and converted value
@return NC_NOERR | NC_ERANGE
*/


/**
Convert a struct Value.llval field to double in struct Value.dval field.
@return NC_NOERR
*/


/**
Convert long long bit pattern to conform to a given
integer type.
@param dsttype target integer type
@param valp pointer to long long value to convert
@return NC_NOERR | NC_EBADTYPE
*/


/* Place breakpoint here to catch errors close to where they occur*/


/*
Provide a way to print the oc full name for
an ocnode
*/


/* Wrapper for ceparse */


/*
Invoke oc_merge_das and then extract special
attributes such as "strlen" and "dimname"
and stuff from DODS_EXTRA.
*/


/*
Build an NCattribute
from a DAP attribute.
As of Jun 27, 2017, we modify
to suppress nul characters and terminate
the name at the first nul.
*/


/* moved here from ddispatch.c */


/* See ncd2dispatch.c for other version */


/* Simultaneously build any associated attributes*/
/* and any necessary pseudo-dimensions for string types*/


/*
   Provide short and/or unified names for dimensions.
   This must mimic lib-ncdap, which is difficult.
*/


/* Lookup a parameter key; case insensitive */


/* Note: this routine only applies some common
   client parameters, other routines may apply
   specific ones.
*/


/**
 *  Given an anonymous dimension, compute the
 *  effective 0-based index wrt to the specified var.
 *  The result should mimic the libnc-dap indices.
 */


/* Replace dims in a list with their corresponding basedim */


/**
 Two dimensions are equivalent if
 1. they have the same size
 2. neither are anonymous
 3. they ave the same names.
 */


/*
This is more complex than one might think. We want to find
a path to a variable inside the given node so that we can
ask for a single instance of that variable to minimize the
amount of data we retrieve. However, we want to avoid passing
through any nested sequence. This is possible because of the way
that sequencecheck() works.
TODO: some servers will not accept an unconstrained fetch, so
make sure we always have a constraint.
*/


/* Given an existing candidate, see if we prefer newchoice */


/* computeseqcountconstraints recursive helper function */


/* Estimate variables sizes and then resort the variable list
   by that size
*/


/* Suppress variables not in usable sequences*/


/*
For variables which have a zero size dimension,
make them invisible.
*/


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


/*
Following functions basically return the netcdf-3 value WRT to the nc3id.
*/


/* Build an odometer covering slices startslice upto, but not including, stopslice */


/* Return 1 if we can reuse cached data to address
   the current get_vara request; return 0 otherwise.
   Target is in the constrained tree space.
   Currently, if the target matches a cache that is not
   a whole variable, then match is false.
*/


/* Compute the set of prefetched data.
   Notes:
   1. All prefetches are whole variable fetches.
   2. If the data set is unconstrainable, we
      will prefetch the whole thing
*/


/*
A variable is prefetchable if
1. it is atomic
2. it's size is sufficiently small
3. it is not contained in sequence or a dimensioned structure.
*/


/* Accumulate useful node sets  */


/*
Figure out the names for variables.
*/


/* locate and connect usable sequences and vars.
A sequence is usable iff:
1. it has a path from one of its subnodes to a leaf and that
   path does not contain a sequence.
2. No parent container has dimensions.
*/


/*
Originally, if one did a constraint on a Grid such that only
one array or map in the grid was returned, that element was
returned as a top level variable.  This is incorrect because
it loses the Grid scope information.

Eventually, this behavior was changed so that such partial
grids are converted to structures where the structure name
is the grid name. This preserves the proper scoping.
However, it is still the case that some servers do the old
behavior.

The rules that most old-style servers appear to adhere to are these.
1. Asking for just a grid array or a single grid map
   returns just the array not wrapped in a structure.
2. Asking for a subset of the fields (array plus map) of a grid
   returns those fields wrapped in a structure.
3. However, there is an odd situation: asking for a grid array
   plus any subset of maps that includes the last map in the grid
   returns a malformed grid. This is clearly a bug.

For case 1, we insert a structure node so that case 1 is consistent
with case 2. Case 3 should cause an error with a malformed grid.

[Note: for some reason, this code has been difficult to get right;
I have rewritten 6 times and it probably is still not right.]
[2/25/2013 Sigh! Previous fixes have introducted another bug,
so now we fix the fix.]

Input is
(1) the root of the dds that needs to be re-gridded
(2) the full datadds tree that defines where the grids are.
(3) the projections that were used to produce (1) from (2).

*/


/*
Locate nodes in the tree rooted at node
that correspond to a single grid field in the pattern
when the pattern is a grid.
Wrap that grid field in a synthesized structure.

The key thing to look for is the case where
we have an atomic variable that appear where
we expected a grid.

*/


/* Wrap the node wrt the pattern grid or pattern struct */


/* Create a structure to surround projected grid array or map;
   this occurs because some servers (that means you ferret and you thredds!)
   do not adhere to the DAP2 protocol spec.
*/


/**
Make the constrained dds nodes (root)
point to the corresponding unconstrained
dds nodes (fullroot).
 */


/* The specific actions of a map are defined
   by this function.
*/


/*
Move dimension data from basenodes to nodes
*/


/* Define the dimsetplus list for a node = dimset0+pseudo dims */


/* Define the dimsetall list for a node =  */


/* Define the dimsettrans list for a single node */


/*
Recursively define the transitive closure of dimensions
(dimsettrans) based on the original dimension set (dimset0):
*/


/*
Recursive helper for definedimsettrans3
*/


/*
Recursively define two dimension sets for each structural node
based on the original dimension set (dimset0):
1. dimsetplus = dimset0+pseudo-dimensions (string,sequence).
2. dimsetall = parent-dimsetall + dimsetplus
*/


/* Given an OCnode tree, mimic it as a CDFnode tree;
   Add DAS attributes if DAS is available. Accumulate set
   of all nodes in preorder.
*/


/* Free up a single node, but not any
   nodes it points to.
*/


/* Return true if node and node1 appear to refer to the same thing;
   takes grid->structure changes into account.
*/


/*
Try to figure out if two nodes
are the "related" =>
    same name && same nc_type and same arity
but: Allow Grid == Structure
*/


/* Ensure every node has an initial base name defined and fullname */
/* Exceptions: anonymous dimensions. */


/* Convert an NCprojection instance into a string
   that can be used with the url
*/


/* Provide detailed data on a CDFnode */


/* This should be consistent with makeslicestring3 in constraints3.c */


/* Parse incoming url constraints, if any,
   to check for syntactic correctness */


/* Map constraint paths to CDFnode paths in specified tree and fill
   in the declsizes.

   Two things to watch out for:
   1. suffix paths are legal (i.e. incomplete paths)
   2. nc_virtual nodes (via restruct)

*/


/* Fill in:
    1. projection segments
    2. projection segment slices declsize
    3. selection path
*/


/* convert all names in projections in paths to be fully qualified
   by adding prefix segment objects.
*/


/* Make sure that the slice declsizes are all defined for this projection */


/**
 * Given a path as segments,
 * try to locate the CDFnode
 * instance (from a given set)
 * that corresponds to the path.
 * The key difficulty is that the
 * path may only be a suffix of the
 * complete path.
 */


/* Given the arguments to vara
   construct a corresponding projection
   with any pseudo dimensions removed
*/


/*
Given a set of projections, we need to produce
an expanded, correct, and equivalent set of projections.
The term "correct" means we must fix the following cases:
1. Multiple occurrences of the same leaf variable
   with differing projection slices. Fix is to complain.
2. Occurrences of container and one or more of its fields.
   Fix is to suppress the container.
The term "expanded" means
1. Expand all occurrences of only a container by
   replacing it with all of its fields.
*/


/* Convert a CDFnode var to a projection; include
   pseudodimensions; always whole variable.
*/


/*
Given a set of projections and a projection
representing a variable (from, say vara or prefetch)
construct a single projection for fetching that variable
with the proper constraints.
*/


/* Shift the slice so it runs from 0..count by step 1 */


/* Compute the set of variables referenced in the projections
   of the input constraint.
*/


/*
Given a legal dap name with arbitrary characters,
convert to equivalent legal cdf name.
Currently, the only change is to convert '/'
names to %2f.
*/


/* Define the type conversion of the DAP variables
   to the external netCDF variable type.
   The proper way is to, for example, convert unsigned short
   to an int to maintain the values.
   Unfortunately, libnc-dap does not do this:
   it translates the types directly. For example
   libnc-dap upgrades the DAP byte type, which is unsigned char,
   to NC_BYTE, which signed char.
   Oh well. So we do the same.
*/


/* Pad a buffer */


/* Return value of param or NULL if not found */


/* Search for substring in value of param. If substring == NULL; then just
   check if param is defined.
*/


/* This is NOT UNION */


/* Collect the set of container nodes ending in "container"*/


/* Like collectnodepath3, but in ocspace */


/* convert path to string using the ncname field */


/* Collect the set names of container nodes ending in "container"*/


/* Provide a wrapper for oc_fetch so we can log what it does */


/* Repair a dap name */


/* Heavy duty test file...*/
/*#define FILE_NAME "/machine/downloads/T159_1978110112.nc4"*/


/* This is a callback function for H5Literate().

The parameters of this callback function have the following values or
meanings:

g_id Group that serves as root of the iteration; same value as the
H5Lvisit group_id parameter

name Name of link, relative to g_id, being examined at current step of
the iteration

info H5L_info_t struct containing information regarding that link

op_data User-defined pointer to data required by the application in
processing the link; a pass-through of the op_data pointer provided
with the H5Lvisit function call

*/


/**
Alternate binary data generator.
Inputs:
	Datalist* data - to use to generate the binary data
	Symbol* tsym - the top-level type for which instances
	               are to be generated
	Datalist* fillvalue - the fillvalue for the toplevel type
	Bytebuffer* databuf - the buffer into which instances are to be stored
*/


/* Recursive type walker: reclaim a single instance */


/* Return the group name for the specified group*/


/* Compute the C name for a given type's id*/
/* Watch out: the result is a static*/


/* Compute the C name for a given var's id*/
/* Watch out: the result is a static*/


/* Compute the C name for a given dim's id*/
/* Watch out: the result is pooled*/


/* Compute the C name for a given type*/


/*
Generate the C code for defining a given type
*/


/* Compute the C name for a given symbol;
modified to use the fqn
*/


/* Iterate on blocks for variables, by updating start and count vector
 * for next vara call.  Assumes nc_get_iter called first.  Returns
 * number of variable values to get, 0 if done, negative number if
 * error, so use like this:
   size_t to_get;
   while((to_get = nc_next_iter(&iter, start, count)) > 0) {
      ... iteration ...
   }
   if(to_get < 0) { ... handle error ... }
 */


/* Initialize block iteration for variables, including those that
 * won't fit in the copy buffer all at once.
 */


/* For ncgen, we want the iterator to return
   dimension indices, not offsets, so force size
   to be one.
*/


/*
 * Generate code for creating netCDF from in-memory structure.
 */


/* Compute the name for a given var's id*/
/* Watch out: the result is a static*/


/* Compute the name for a given dim's id*/
/* Watch out: the result is a static*/


/* Compute the name for a given type*/


/* Compute the name for a given symbol*/


/* Result is pool alloc'd*/


/* return FORTRAN declaration type for given type code */


/*
 * Return proper _put_var_ suffix for given nc_type
 */


/*
 * Return FORTRAN type name for netCDF attribute type
 */


/* For debugging purposes*/


/* This assumes s is a null terminated string*/


/*! Pop head off of a byte buffer.
 *
 * @param Bytebuffer bb Pointer to Bytebuffer.
 * @param char* pelem pointer to location for head element.
 *
 * @return Returns TRUE on success.
 */


/* Add invisible NULL terminator */


/* Extract the content and leave content null */


/**

The basic idea is to split the set of dimensions into
groups and iterate over each group by recursion.

A group is defined as the range of indices starting at an
unlimited dimension upto (but not including) the next
unlimited.

The first group starts at index 0, even if dimension 0 is not
unlimited.  The last group is everything from the last
unlimited dimension thru the last dimension (index rank-1).

*/


/**
The basic idea is to split the set of dimensions into groups
and iterate over each group.  A group is defined as the
range of indices starting at an unlimited dimension upto
(but not including) the next unlimited.  The first group
starts at index 0, even if dimension 0 is not unlimited.
The last group is everything from the last unlimited
dimension thru the last dimension (index rank-1).
*/


/* Generate an instance of the basetype using the value of con*/


/* Used only for structure field arrays*/


/* An opaque string value might not conform
   to the size of the opaque to which it is being
   assigned. Normalize it to match the required
   opaque length (in bytes).
   Note that the string is a sequence of nibbles (4 bits).
*/


/*
Generate type definitions
*/


/* Following is patterned after the walk functions in semantics.c */


/* Compute the name for a given symbol*/
/* Cache in symbol->lname*/


/*
 * Return java type name for netCDF type, given type code.
 */


/*
 * Return a type name and dimensions for constant arrays
 * for netCDF type, given type code.
 */


/*
 * Return netcdf interface type name for netCDF type suffix, given type code.
 */


/* Create an array of fill values of basetype given the dimset. This
is recursive over the dimensions as specified by the index argumen
*/


/*
 * Given primitive netCDF type, return a default fill_value appropriate for
 * that type.
 */


/* index of match */


/* Deep constant cloning; return struct not pointer to struct*/


/* Deep constant clear*/


/* Requires that the string be balanced
   WRT to braces
*/


/*
Following routines are in support of language-oriented output
*/


/* Make an empty string constant*/


/* Convert a datalist to a compound constant */


/* return 1 if the next element in the datasrc is a fill value*/


/* return 1 if the next element in the datasrc is nc_type*/


/** Destroy the buffer.
 * @param b a buffer created with yy_create_buffer()
 *
 */


/* Initializes or reinitializes a buffer.
 * This function is sometimes called more than once on the same buffer,
 * such as during a yyrestart() or at EOF.
 */


/** Setup the input buffer state to scan directly from a user-specified character buffer.
 * @param base the character buffer
 * @param size the size in bytes of the character buffer
 *
 * @return the newly allocated buffer state object.
 */


/** Setup the input buffer state to scan a string. The next call to yylex() will
 * scan from a @e copy of @a str.
 * @param yystr a NUL-terminated string to scan
 *
 * @return the newly allocated buffer state object.
 * @note If you want to scan bytes that may contain NUL values, then use
 *       yy_scan_bytes() instead.
 */


/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
 * scan from a @e copy of @a bytes.
 * @param yybytes the byte buffer to scan
 * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
 *
 * @return the newly allocated buffer state object.
 */


/** Set the current line number.
 * @param _line_number line number
 *
 */


/* yylex_destroy is for both reentrant and non-reentrant scanners. */


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


/**
Given the raw bits, the sign char, the tag, and hasU
fill in the appropriate *_val field
and return the type.
Note that we cannot return unsigned types if running pure netcdf classic.
The rule is to pick the smallest enclosing type.

The rule used here is that the tag (the suffix, if any)
always takes precedence and the value is modified to conform
if possible, otherwise out-of-range is signalled.
For historical reasons (ncgen3), values that fit as unsigned
are acceptable for the signed tag and conversion is attempted;
e.g. 65535s; is legal and is return as a negative short.
*/


/* capture the tag string */


/* Really should init our data within this file */


/* Symbol table operations for ncgen tool */
/* install sname in symbol table even if it is already there */


/* Check for duplicate name of given type within current group*/


/* Convert an nc_type code to the corresponding Symbol*/


/* Since this may be affected by the _Format attribute, which
   may come last, capture all the special info and sort it out
   in semantics.
*/


/*
Try to infer the file type from the
kinds of constructs used in the cdl file.
*/


/*
Parse a filter spec string and store it in special
*/


/*
Since the arguments are all simple constants,
we can evaluate the function immediately
and return its value.
Note that currently, only a single value can
be returned.
*/


/* Support debugging of memory*/


/**************************************************/
/* Define methods for a dimension odometer*/


/*
Suppose we have the declaration int F[2][5][3];
There are obviously a total of 2 X 5 X 3 = 30 integers in F.
Thus, these three dimensions will be reduced to a single
dimension of size 30 (0..29).
A particular point in the three dimensions, say [x][y][z], is reduced to
a number in the range 0..29 by computing ((x*5)+y)*3+z
So, given an odometer in some state, it computes the offset
for that odometer's state.
*/


/*
Get the start vector
from the odometer
*/


/*
Get the count vector
from the odometer
*/


/*
 * For logging error conditions.
 * Designed to be called by other vararg procedures
 */


/* Report version errors */


/**************************************************/
/* Provide a version of snprintf that panics if*/
/* the buffer is overrun*/


/*
Matching strings to char variables, attributes, and vlen
constants is challenging because it is desirable to mimic
the original ncgen(3). The "algorithms" used there have no
simple characterization (such as "abc" == {'a','b','c'}).
So, this rather ugly code is kept in this file
and a variety of heuristics are used to mimic ncgen3.

The core algorithm is as follows.
1. Assume we have a set of dimensions D1..Dn.
   Any of the Di may be unlimited,
   but it is assumed that the sizes of the Di are all known.
2. Given a sequence of string or character constants
   C1..Cm, our goal is to construct a single string
   whose length is the cross product of D1 thru Dn.
3. For purposes of this algorithm, character constants
   are treated as strings of size 1.
4. Construct Dx = cross product of D1 thru D(n-1).
5. For each constant Ci, add fill characters, if necessary,
   so that its length is a multiple of Dn.
6. Concatenate the modified C1..Cm to produce string S.
7. Add fill characters to S to make its size be a multiple of
   Dn.
8. If S is longer than the Dx * Dn, then truncate
   and generate a warning.

Two special cases:
1. character vlen: char(*) vlen_t.
    For this case, we simply concat all the elements.
2. character attribute.
    For this case, we simply concat all the elements.
*/


/* I think there is a flaw in the ncgen manual
   about handling something like this:
    dimensions:
      n = 8 ;
    variables:
      char cdata2(n) ;
    data:
      cdata2 = '\000','\001','\002','\177','\200','\201','\376','\377';
    The rules would say that each of the 8 char constants must
    be padded to length 8. I think this is only true if the dimension
    is unlimited, and even then, I am not sure.
 */


/* Create a new string constant */


/* Assume bytebuffer contains pointers to char**/


/* return 1 if first dimension is unlimited*/


/* True only if dim[0] is unlimited all rest are bounded*/
/* or all are bounded*/


/* True only iff no dimension is unlimited*/


/* Result is pool'd*/


/* Result is malloc'd*/


/* compute the total n-dimensional size as 1 long array;
   if stop == 0, then stop = dimset->ndims.
*/


/* Do the "complement" of crossproduct;
   compute the total n-dimensional size of an array
   starting at 0 thru the 'last' array index.
   stop if we encounter an unlimited dimension
*/


/**
Find the index of the first unlimited
dimension at or after 'start'.
If no unlimited exists, return |dimset|
*/


/**
Find the index of the last unlimited
dimension.
If no unlimited exists, return |dimset|
*/


/**
Count the number of unlimited dimensions.
*/


/* Return standard format string */


/* strip off leading path */
/* result is malloc'd */


/* Post-parse semantic checks and actions*/


/*
Given a reference symbol, produce the corresponding
definition symbol; return NULL if there is no definition
Note that this is somewhat complicated to conform to
various scoping rules, namely:
1. look into parent hierarchy for un-prefixed dimension names.
2. look in whole group tree for un-prefixed type names;
   search is depth first. MODIFIED 5/26/2009: Search is as follows:
   a. search parent hierarchy for matching type names.
   b. search whole tree for unique matching type name
   c. complain and require prefixed name.
3. look in the same group as ref for un-prefixed variable names.
4. ditto for group references
5. look in whole group tree for un-prefixed enum constants;
   result must be unique
*/


/*
Search for an object in all groups using preorder depth-first traversal.
Return NULL if symbol is not unique or not found at all.
*/


/*
Compute the fqn for every top-level definition symbol
*/


/**
Process the root group.
Currently mean:
1. Compute and store the filename
*/


/* 1. Do a topological sort of the types based on dependency*/
/*    so that the least dependent are first in the typdefs list*/
/* 2. fill in type typecodes*/
/* 3. mark types that use vlen*/


/* Recursively check for vlens*/


/* Make sure all typecodes are set if basetype is set*/


/* Walk all data lists looking for econst refs
   and convert to point to actual definition
*/


/* Recursive helper for processeconstrefs */


/* If we have an enum-valued group attribute, then we need to do
extra work to find the containing enum type
*/


/*
Locate enums whose name is a prefix of ident
and contains the suffix as an enum const
and capture that enum constant.
*/


/* Compute type sizes and compound offsets*/


/*
Given two types, attempt to upgrade to the "bigger type"
Rules:
- type size has precedence over signed/unsigned:
   e.g. NC_INT over NC_UBYTE
*/


/*
Collect info by repeated walking of the attribute value list.
*/


/* recursive helper for validataNIL */


/* Find name within group structure*/


/* Find name within given group*/


/* Find symbol within group structure*/


/* Rules for specifying the dataset name:
	1. use -o name
	2. use the datasetname from the .cdl file
	3. use input cdl file name (with .cdl removed)
	It would be better if there was some way
	to specify the datasetname independently of the
	file name, but oh well.
*/


/*
Given a character c, fill s with the character suitably escaped.
E.g. c = '\t' => s="\t"
Caller must ensure enough space.
Watch out for embedded NULs.
Currently passes unicode thru unchanged.
Returns s as its result.
*/


/* Return a pool string that is s0 with all characters*/
/* ecaped that require it.  The resulting string is not*/
/* surrounded by quotes.*/
/* Since the string might actually contain nulls, specify the length.*/


/*
Convert a name to a
form suitable for use in a
language file.
Conversion depends on l_flag.
*/


/* Return a pool string that is s0 with all characters
   ecaped that require it.  The resulting string is not
   surrounded by quotes.
   Since the string might actually contain nulls, specify the length.
*/


/*
Given a utf16 character c,
fill s with the characters needed
to suitably escape c for use with Java.
*/


/*
Convert a java name that might possibly
contain utf8 characters to one that is
acceptable to the Java compiler.
Basically this means convert the printables
using ccodify (above) equivalent and then escape
all the utf chars.
*/


/**************************************************/
/* FORTRAN does escapes differently than e.g. C */


/**************************************************/
/* Escape Fqn segment names by replacing
   '/' and '.' by alternate representation.
*/


/*
 * Given a pointer to a string of the form
 * 'xdd', return the corresponding hex byte
 */


/*
 * Given a pointer to a string of the form
 * 'ddd', return the corresponding
 * unsigned octal byte
 */


/*
 * "Un-escapes" valid escape sequences in yystring (read by lex) into the
 * appropriate unescaped characters.  For example, the two character
 * sequence "\t" in yystring would be converted into a single tab character.
 * On return, termstring is nul terminated.
 * Watch out for embedded nuls and utf-8 characters.
 * Return # of characters written.
 * Note that the escape handling for identifiers is different
 * than for string constants
 */


/**
Do equivalent of strchr, but taking escapes into account
Set octhex to true if the string might contain \ddd or \xdd.
Note that escaped versions of the stop character are not
considered to match.
WARNING: if char is not found, then return pointer to the
trailing nul char
*/


/**
Return a string representing
the fully qualified name of a nested symbol
(i.e. field or econst).
Caller must free.
*/


/**
Return a string representing
the fully qualified name of an attribute.
Caller must free.
*/


/* Function to test nc_inq_format(). */


/* Read the file. Return -1 if endianness is not little. */


/* Create a HDF4 file with one dataset of type dtype. */


/**
 * @internal Initialize the HDF5 dispatch layer.
 *
 * @return ::NC_NOERR No error.
 * @author Ed Hartnett
 */


/**
 * @internal Finalize the HDF5 dispatch layer.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */


/*-------------------------------------------------------------------------
* Function: image_malloc
*
* Purpose: Simulates malloc() function to avoid copying file images.
*          The application buffer is set to the buffer on only one FAPL.
*          Then the FAPL buffer can be copied to other FAPL buffers or
*          to only one VFD buffer.
*
* Return: Address of "allocated" buffer, if successful. Otherwise, it returns
*         NULL.
*
* Programmer: Christian Chilan
*
* Date: October 3, 2011
*
*-------------------------------------------------------------------------
*/


/*-------------------------------------------------------------------------
* Function: image_memcpy
*
* Purpose:  Simulates memcpy() function to avoid copying file images.
*           The image buffer can be set to only one FAPL buffer, and
*           "copied" to only one VFD buffer. The FAPL buffer can be
*           "copied" to other FAPLs buffers.
*
* Return: The address of the destination buffer, if successful. Otherwise, it
*         returns NULL.
*
* Programmer: Christian Chilan
*
* Date: October 3, 2011
*
*-------------------------------------------------------------------------
*/


/* Modified:
1. If the realloc new size is <= existing size,
   then pretend we did a realloc and return success.
   This avoids unneccessary heap operations.
2. If the H5LT_FILE_IMAGE_DONT_COPY or
   H5LT_FILE_IMAGE_DONT_RELEASE flag is set and the
   realloc new size is > existing size, then fail
   because the realloc() call may change the address
   of the buffer. The new address cannot be
   communicated to the application to release it.
3. Otherwise, use realloc(). Note that this may have the
   side effect of freeing the previous memory chunk.
*/


/*-------------------------------------------------------------------------
* Function: image_free
*
* Purpose: Simulates deallocation of FAPL and VFD buffers by decreasing
*          reference counters. Shared application buffer is actually
*          deallocated if there are no outstanding references.
*
* Return: SUCCEED or FAIL
*
* Programmer: Christian Chilan
*
* Date: October 3, 2011
*
*-------------------------------------------------------------------------
*/


/*-------------------------------------------------------------------------
* Function: udata_copy
*
* Purpose: Simulates the copying of the user data structure utilized in the
*          management of the "copying" of file images.
*
* Return: Address of "newly allocated" structure, if successful. Otherwise, it
*         returns NULL.
*
* Programmer: Christian Chilan
*
* Date: October 3, 2011
*
*-------------------------------------------------------------------------
*/


/*-------------------------------------------------------------------------
* Function: udata_free
*
* Purpose: Simulates deallocation of the user data structure utilized in the
*          management of the "copying" of file images. The data structure is
*          actually deallocated when there are no outstanding references.
*
* Return: SUCCEED or FAIL
*
* Programmer: Christian Chilan
*
* Date: October 3, 2011
*
*-------------------------------------------------------------------------
*/


/* End of callbacks definitions for file image operations */


/**
 * @internal Initialize default provenance info
 * This will only be used for newly created files
 * or for opened files that do not contain an _NCProperties
 * attribute.
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */


/**
 * @internal finalize default provenance info
 *
 * @return ::NC_NOERR No error.
 * @author Dennis Heimbigner
 */


/**
 * @internal
 *
 * Construct the provenance information for a newly created file.
 * Note that creation of the _NCProperties attribute is deferred
 * to the sync_netcdf4_file function.
 *
 * @param file Pointer to file object.
 *
 * @return ::NC_NOERR No error.
 * [Note: other errors are reported via LOG()]
 * @author Dennis Heimbigner
 */


/**
 * @internal
 *
 * Construct the provenance information for an existing file.
 *
 * @param file Pointer to file object.
 *
 * @return ::NC_NOERR No error.
 * [Note: other errors are reported via LOG()]
 * @author Dennis Heimbigner
 */