source: XIOS/dev/dev_olga/extern/src_netcdf4/nc4hdf.c @ 1620

Last change on this file since 1620 was 409, checked in by ymipsl, 11 years ago

Add improved nectdf internal library src

YM

  • Property svn:eol-style set to native
File size: 128.6 KB
Line 
1/*
2  This file is part of netcdf-4, a netCDF-like interface for HDF5, or a
3  HDF5 backend for netCDF, depending on your point of view.
4
5  This file contains functions internal to the netcdf4 library. None of
6  the functions in this file are exposed in the exetnal API. These
7  functions handle the HDF interface.
8
9  Copyright 2003, University Corporation for Atmospheric
10  Research. See the COPYRIGHT file for copying and redistribution
11  conditions.
12
13  $Id: nc4hdf.c,v 1.273 2010/05/27 21:34:14 dmh Exp $
14*/
15
16#include "config.h"
17#include "nc4internal.h"
18#include <H5DSpublic.h>
19#include <math.h>
20
21#ifdef IGNORE
22extern NC_FILE_INFO_T *nc_file;
23#endif
24
25#define NC3_STRICT_ATT_NAME "_nc3_strict"
26
27/* This is to track opened HDF5 objects to make sure they are
28 * closed. */
29#ifdef EXTRA_TESTS
30int num_plists;
31int num_spaces;
32#endif /* EXTRA_TESTS */
33
34/* This function is needed to handle one special case: what if the
35 * user defines a dim, writes metadata, then goes back into define
36 * mode and adds a coordinate var for the already existing dim. In
37 * that case, I need to recreate the dim's dimension scale dataset,
38 * and then I need to go to every var in the file which uses that
39 * dimension, and attach the new dimension scale. */
40static int
41rec_reattach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
42{
43   NC_VAR_INFO_T *var;
44   NC_GRP_INFO_T *child_grp;
45   int d;
46   int retval;
47
48   assert(grp && grp->name && dimid >= 0 && dimscaleid >= 0);
49   LOG((3, "rec_reattach_scales: grp->name %s", grp->name));
50
51   /* If there are any child groups, attach dimscale there, if needed. */
52   for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
53      if ((retval = rec_reattach_scales(child_grp, dimid, dimscaleid)))
54         return retval;
55
56   /* Find any vars that use this dimension id. */
57   for (var = grp->var; var; var = var->next)
58      for (d = 0; d < var->ndims; d++)
59         if (var->dimids[d] == dimid && !var->dimscale)
60         {
61            LOG((2, "rec_reattach_scaled: attaching scale for dimid %d to var %s", 
62                 var->dimids[d], var->name));
63            if (var->created)
64            {
65               if (H5DSattach_scale(var->hdf_datasetid, dimscaleid, d) < 0)
66                  return NC_EHDFERR;
67               var->dimscale_attached[d]++;
68            }
69         }
70
71   return NC_NOERR;
72}
73
74/* This function is needed to handle one special case: what if the
75 * user defines a dim, writes metadata, then goes back into define
76 * mode and adds a coordinate var for the already existing dim. In
77 * that case, I need to recreate the dim's dimension scale dataset,
78 * and then I need to go to every var in the file which uses that
79 * dimension, and attach the new dimension scale. */
80static int
81rec_detach_scales(NC_GRP_INFO_T *grp, int dimid, hid_t dimscaleid)
82{
83   NC_VAR_INFO_T *var;
84   NC_GRP_INFO_T *child_grp;
85   int d;
86   int retval;
87
88   assert(grp && grp->name && dimid >= 0 && dimscaleid >= 0);
89   LOG((3, "rec_detach_scales: grp->name %s", grp->name));
90
91   /* If there are any child groups, attach dimscale there, if needed. */
92   for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
93      if ((retval = rec_detach_scales(child_grp, dimid, dimscaleid)))
94         return retval;
95
96   /* If there are no vars, we are done. */
97   if (!grp->var)
98      return NC_NOERR;
99
100   /* Find any (already created) vars that use this dimension id. Go
101    * through the list backwards to accomdate a HDF5 bug. */
102   for (var = grp->var; var->next; var = var->next)
103      ;
104
105   for ( ; var; var = var->prev)
106      for (d = 0; d < var->ndims; d++)
107         if (var->dimids[d] == dimid && !var->dimscale)
108         {
109            LOG((2, "rec_detach_scales: detaching scale for dimid %d to var %s", 
110                 var->dimids[d], var->name));
111            if (var->created)
112            {
113               if (var->dimscale_attached[d])
114               {
115                  if (H5DSdetach_scale(var->hdf_datasetid, dimscaleid, d) < 0)
116                     return NC_EHDFERR;
117                  var->dimscale_attached[d] = 0;
118               }
119            }
120         }
121
122   return NC_NOERR;
123}
124
125/* Open the dataset and leave it open. */
126int 
127nc4_open_var_grp2(NC_GRP_INFO_T *grp, int varid, hid_t *dataset)
128{
129   NC_VAR_INFO_T *var;
130   
131   /* Find the requested varid. */
132   for (var = grp->var; var; var = var->next)
133      if (var->varid == varid)
134         break;
135   if (!var)
136      return NC_ENOTVAR;
137   
138   /* Open this dataset if necessary. */
139   if (!var->hdf_datasetid)
140      if ((var->hdf_datasetid = H5Dopen2(grp->hdf_grpid, var->name, 
141                                         H5P_DEFAULT)) < 0)
142         return NC_ENOTVAR;
143
144   *dataset = var->hdf_datasetid;
145   
146   return NC_NOERR;
147}
148
149/* Write or read one element of data.
150
151   Oh, better far to live and die
152   Under the brave black flag I fly,
153   Than play a sanctimonious part,
154   With a pirate head and a pirate heart.
155
156   Away to the cheating world go you,
157   Where pirates all are well-to-do.
158   But I'll be true to the song I sing,
159   And live and die a Pirate king.
160*/
161int
162nc4_pg_var1(NC_PG_T pg, NC_FILE_INFO_T *nc, int ncid, int varid, 
163            const size_t *indexp, nc_type xtype, int is_long, void *ip)
164{
165   NC_GRP_INFO_T *grp;
166   NC_VAR_INFO_T *var;
167   int i;
168   size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
169   int retval;
170
171   /* Find file and var, cause I need the number of dims. */
172   assert(nc);
173   if ((retval = nc4_find_g_var_nc(nc, ncid, varid, &grp, &var)))
174      return retval;
175   assert(grp && var && var->name);
176
177   /* Set up the count and start arrays. */
178   for (i=0; i<var->ndims; i++)
179   {
180      start[i] = indexp[i];
181      count[i] = 1;
182   }
183
184   /* Get or put this data. */
185   if (pg == GET)
186      return nc4_get_vara(nc, ncid, varid, start, count, xtype, 
187                          is_long, ip);
188   else
189      return nc4_put_vara(nc, ncid, varid, start, count, xtype, 
190                          is_long, ip);
191}
192
193/* Get the default fill value for an atomic type. Memory for
194 * fill_value must already be allocated, or you are DOOMED!!!*/
195int
196nc4_get_default_fill_value(NC_TYPE_INFO_T *type_info, void *fill_value)
197{
198   switch (type_info->nc_typeid)
199   {
200      case NC_BYTE:
201         *(signed char *)fill_value = NC_FILL_BYTE;
202         break;
203      case NC_CHAR:
204         *(char *)fill_value = NC_FILL_CHAR;
205         break;
206      case NC_SHORT:
207         *(short *)fill_value = NC_FILL_SHORT;
208         break;
209      case NC_INT:
210         *(int *)fill_value = NC_FILL_INT;
211         break;
212      case NC_FLOAT:
213         *(float *)fill_value = NC_FILL_FLOAT;
214         break;
215      case NC_DOUBLE:
216         *(double *)fill_value = NC_FILL_DOUBLE;
217         break;
218      case NC_UBYTE:
219         *(unsigned char *)fill_value = NC_FILL_UBYTE;
220         break;
221      case NC_USHORT:
222         *(unsigned short *)fill_value = NC_FILL_USHORT;
223         break;
224      case NC_UINT:
225         *(unsigned int *)fill_value = NC_FILL_UINT;
226         break;
227      case NC_INT64:
228         *(long long *)fill_value = NC_FILL_INT64;
229         break;
230      case NC_UINT64:
231         *(unsigned long long *)fill_value = NC_FILL_UINT64;
232         break;
233      case NC_STRING:
234         strcpy((char *)fill_value, "");
235         break;
236      default:
237         return NC_EINVAL;
238   }
239
240   return NC_NOERR;
241}
242
243/* What fill value should be used for a variable? */
244static int
245get_fill_value(NC_HDF5_FILE_INFO_T *h5, NC_VAR_INFO_T *var, void **fillp)
246{   
247   size_t size;
248   int retval;
249
250   /* Find out how much space we need for this type's fill value. */
251   if ((retval = nc4_get_typelen_mem(h5, var->xtype, 0, &size)))
252      return retval;
253
254   /* Strings have a size of one for the empty sting (to hold the
255    * null), otherwise the length of the users fill_value string, plus
256    * one. */
257   if (var->xtype == NC_STRING)
258   {
259         size = 1;
260   }
261   
262   /* Allocate the space. VLENS are different, of course. */
263   if (var->type_info->class == NC_VLEN)
264   {
265      if (!((*fillp) = malloc(sizeof(nc_vlen_t))))
266         return NC_ENOMEM;
267   }
268   else
269   {
270      if (!((*fillp) = malloc(size)))
271         return NC_ENOMEM;
272   }
273
274   /* If the user has set a fill_value for this var, use, otherwise
275    * find the default fill value. */
276   if (var->fill_value)
277   {
278      LOG((4, "Found a fill value for var %s", var->name));
279      if (var->type_info->class == NC_VLEN)   
280      {
281         nc_vlen_t *in_vlen = (nc_vlen_t *)(var->fill_value), *fv_vlen = (nc_vlen_t *)(*fillp);
282         fv_vlen->len = in_vlen->len;
283         if (!(fv_vlen->p = malloc(size * in_vlen->len)))
284            return NC_ENOMEM;
285         memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * size);
286      }
287      else if (var->xtype == NC_STRING)
288      {
289         if (!(*(char **)fillp = malloc((strlen((char *)var->fill_value) + 1) * 
290                                        sizeof(char))))
291            return NC_ENOMEM;
292         strcpy(*(char **)fillp, (char *)var->fill_value);
293      }
294      else
295         memcpy((*fillp), var->fill_value, size);
296   }
297   else
298   {
299      if ((nc4_get_default_fill_value(var->type_info, *fillp)))
300      {
301         free(*fillp);
302         *fillp = NULL;
303      }
304   }
305
306   return NC_NOERR;
307}
308
309/* Given a netcdf type, return appropriate HDF typeid. */
310int
311nc4_get_hdf_typeid(NC_HDF5_FILE_INFO_T *h5, nc_type xtype, 
312                   hid_t *hdf_typeid, int endianness)
313{
314   NC_TYPE_INFO_T *type;
315   hid_t typeid = 0;
316   int retval = NC_NOERR;
317
318   assert(hdf_typeid && h5);
319
320   *hdf_typeid = -1;
321   switch (xtype)
322   {
323      case NC_NAT: /* NAT = 'Not A Type' (c.f. NaN) */
324         return NC_EBADTYPE;
325      case NC_BYTE: /* signed 1 byte integer */
326         if (endianness == NC_ENDIAN_LITTLE)
327            *hdf_typeid = H5T_STD_I8LE;
328         else if (endianness == NC_ENDIAN_BIG)
329            *hdf_typeid = H5T_STD_I8BE;
330         else
331            *hdf_typeid = H5T_NATIVE_SCHAR;
332         break;
333      case NC_CHAR: /* ISO/ASCII character */
334         if ((typeid = H5Tcopy(H5T_C_S1)) < 0) 
335            return NC_EHDFERR;
336         if (H5Tset_strpad(typeid, H5T_STR_NULLTERM) < 0)
337            BAIL(NC_EVARMETA);
338         *hdf_typeid = typeid;
339         break;
340      case NC_SHORT: /* signed 2 byte integer */
341         if (endianness == NC_ENDIAN_LITTLE)
342            *hdf_typeid = H5T_STD_I16LE;
343         else if (endianness == NC_ENDIAN_BIG)
344            *hdf_typeid = H5T_STD_I16BE;
345         else
346            *hdf_typeid = H5T_NATIVE_SHORT;
347         break;
348      case NC_INT:
349         if (endianness == NC_ENDIAN_LITTLE)
350            *hdf_typeid = H5T_STD_I32LE;
351         else if (endianness == NC_ENDIAN_BIG)
352            *hdf_typeid = H5T_STD_I32BE;
353         else
354            *hdf_typeid = H5T_NATIVE_INT;
355         break;
356      case NC_FLOAT:
357         if (endianness == NC_ENDIAN_LITTLE)
358            *hdf_typeid = H5T_IEEE_F32LE;
359         else if (endianness == NC_ENDIAN_BIG)
360            *hdf_typeid = H5T_IEEE_F32BE;
361         else
362            *hdf_typeid = H5T_NATIVE_FLOAT;
363         break;
364      case NC_DOUBLE:
365         if (endianness == NC_ENDIAN_LITTLE)
366            *hdf_typeid = H5T_IEEE_F64LE;
367         else if (endianness == NC_ENDIAN_BIG)
368            *hdf_typeid = H5T_IEEE_F64BE;
369         else
370            *hdf_typeid = H5T_NATIVE_DOUBLE;
371         break;
372      case NC_UBYTE:
373         if (endianness == NC_ENDIAN_LITTLE)
374            *hdf_typeid = H5T_STD_U8LE;
375         else if (endianness == NC_ENDIAN_BIG)
376            *hdf_typeid = H5T_STD_U8BE;
377         else
378            *hdf_typeid = H5T_NATIVE_UCHAR;
379         break;
380      case NC_USHORT:
381         if (endianness == NC_ENDIAN_LITTLE)
382            *hdf_typeid = H5T_STD_U16LE;
383         else if (endianness == NC_ENDIAN_BIG)
384            *hdf_typeid = H5T_STD_U16BE;
385         else
386            *hdf_typeid = H5T_NATIVE_USHORT;
387         break;
388      case NC_UINT:
389         if (endianness == NC_ENDIAN_LITTLE)
390            *hdf_typeid = H5T_STD_U32LE;
391         else if (endianness == NC_ENDIAN_BIG)
392            *hdf_typeid = H5T_STD_U32BE;
393         else
394            *hdf_typeid = H5T_NATIVE_UINT;
395         break;
396      case NC_INT64:
397         if (endianness == NC_ENDIAN_LITTLE)
398            *hdf_typeid = H5T_STD_I64LE;
399         else if (endianness == NC_ENDIAN_BIG)
400            *hdf_typeid = H5T_STD_I64BE;
401         else
402            *hdf_typeid = H5T_NATIVE_LLONG;
403         break;
404      case NC_UINT64:
405         if (endianness == NC_ENDIAN_LITTLE)
406            *hdf_typeid = H5T_STD_U64LE;
407         else if (endianness == NC_ENDIAN_BIG)
408            *hdf_typeid = H5T_STD_U64BE;
409         else
410            *hdf_typeid = H5T_NATIVE_ULLONG;
411         break;
412      case NC_STRING:
413         if ((typeid = H5Tcopy(H5T_C_S1)) < 0) 
414            return NC_EHDFERR;
415         if (H5Tset_size(typeid, H5T_VARIABLE) < 0)
416            return NC_EHDFERR;
417         *hdf_typeid = typeid;
418         break;
419      default:
420         /* Maybe this is a user defined type? */
421         if (!nc4_find_type(h5, xtype, &type))
422         {
423            if (!type)
424               return NC_EBADTYPE;
425            *hdf_typeid = type->hdf_typeid;
426         }
427   }
428
429   if (*hdf_typeid == -1)
430      return NC_EBADTYPE;
431   
432   return NC_NOERR;
433
434  exit:
435   if (xtype == NC_CHAR && typeid > 0 && H5Tclose(typeid) < 0)
436      BAIL2(NC_EHDFERR);     
437   return retval;
438}
439
440/* Do some common check for nc4_put_vara and nc4_get_vara. These
441 * checks have to be done when both reading and writing data. */
442static int 
443check_for_vara(nc_type *mem_nc_type, NC_VAR_INFO_T *var, NC_HDF5_FILE_INFO_T *h5)
444{
445   int retval;
446
447   /* If mem_nc_type is NC_NAT, it means we want to use the file type
448    * as the mem type as well. */
449   assert(mem_nc_type);
450   if (*mem_nc_type == NC_NAT)
451      *mem_nc_type = var->xtype;
452   assert(*mem_nc_type);
453
454   /* No NC_CHAR conversions, you pervert! */
455   if (var->xtype != *mem_nc_type && 
456       (var->xtype == NC_CHAR || *mem_nc_type == NC_CHAR))
457      return NC_ECHAR;
458
459   /* If we're in define mode, we can't read or write data. */
460   if (h5->flags & NC_INDEF)
461   {
462      if (h5->cmode & NC_CLASSIC_MODEL)
463         return NC_EINDEFINE;
464      if ((retval = nc4_enddef_netcdf4_file(h5)))
465         return retval;
466   }
467
468   return NC_NOERR;
469}
470
471#ifdef LOGGING
472/* Print some debug info about dimensions to the log. */   
473static void
474log_dim_info(NC_VAR_INFO_T *var, hsize_t *fdims, hsize_t *fmaxdims, 
475             hsize_t *start, hsize_t *count)
476{
477   int d2;
478
479   /* Print some debugging info... */
480   LOG((4, "nc4_put_vara: var name %s ndims %d", var->name, var->ndims)); 
481   LOG((4, "File space, and requested:"));
482   for (d2 = 0; d2 < var->ndims; d2++)
483   {
484      LOG((4, "fdims[%d]=%Ld fmaxdims[%d]=%Ld", d2, fdims[d2], d2, 
485           fmaxdims[d2]));
486      LOG((4, "start[%d]=%Ld  count[%d]=%Ld", d2, start[d2], d2, count[d2]));
487   }
488}
489#endif /* LOGGING */
490
491#ifdef USE_PARALLEL
492static int
493set_par_access(NC_HDF5_FILE_INFO_T *h5, NC_VAR_INFO_T *var, hid_t xfer_plistid)
494{
495   H5FD_mpio_xfer_t hdf5_xfer_mode;
496
497   /* If netcdf is built with parallel I/O, then parallel access can
498    * be used, and, if this file was opened or created for parallel
499    * access, we need to set the transfer mode. */
500   if (h5->parallel)
501   {
502      /* Decide on collective or independent. */
503      hdf5_xfer_mode = (var->parallel_access != NC_INDEPENDENT) ?
504         H5FD_MPIO_COLLECTIVE : H5FD_MPIO_INDEPENDENT;
505
506      /* Set the mode in the transfer property list. */
507      if (H5Pset_dxpl_mpio(xfer_plistid, hdf5_xfer_mode) < 0)
508         return NC_EPARINIT;
509
510      LOG((4, "hdf5_xfer_mode: %d H5FD_MPIO_COLLECTIVE: %d H5FD_MPIO_INDEPENDENT: %d", 
511           (int)hdf5_xfer_mode, H5FD_MPIO_COLLECTIVE, H5FD_MPIO_INDEPENDENT));
512   }
513   return NC_NOERR;
514}
515#endif
516
517/* Write an array of data to a variable. When it comes right down to
518 * it, this is what netCDF-4 is all about, this is *the* function, the
519 * big enchilda, the grand poo-bah, the alpha dog, the head honcho,
520 * the big cheese, the mighty kahuna, the top bananna, the high
521 * muckity-muck, numero uno. Well, you get the idea.  */
522int 
523nc4_put_vara(NC_FILE_INFO_T *nc, int ncid, int varid, const size_t *startp, 
524                      const size_t *countp, nc_type mem_nc_type, int is_long, void *data)
525{
526   NC_GRP_INFO_T *grp;
527   NC_HDF5_FILE_INFO_T *h5;
528   NC_VAR_INFO_T *var;
529   NC_DIM_INFO_T *dim;
530
531   hid_t file_spaceid = 0, mem_spaceid = 0, xfer_plistid = 0;
532   size_t file_type_size;
533
534   hsize_t *xtend_size = NULL, count[NC_MAX_VAR_DIMS];
535   hsize_t fdims[NC_MAX_VAR_DIMS], fmaxdims[NC_MAX_VAR_DIMS];
536   hsize_t start[NC_MAX_VAR_DIMS];
537   int need_to_extend = 0;
538   int scalar = 0, retval = NC_NOERR, range_error = 0, i, d2;
539   void *bufr = NULL;
540#ifndef HDF5_CONVERT   
541   int need_to_convert = 0;
542   size_t len = 1;
543#endif
544#ifdef HDF5_CONVERT
545   hid_t mem_typeid = 0;
546#endif   
547   int no_data=0 ;
548   
549   /* Find our metadata for this file, group, and var. */
550   assert(nc);
551   if ((retval = nc4_find_g_var_nc(nc, ncid, varid, &grp, &var)))
552      return retval;
553   h5 = nc->nc4_info;
554   assert(grp && h5 && var && var->name);
555
556   LOG((3, "nc4_put_vara: var->name %s mem_nc_type %d is_long %d", 
557        var->name, mem_nc_type, is_long));
558
559   /* Check some stuff about the type and the file. If the file must
560    * be switched from define mode, it happens here. */
561   if ((retval = check_for_vara(&mem_nc_type, var, h5)))
562      return retval;
563
564   /* Convert from size_t and ptrdiff_t to hssize_t, and hsize_t. */
565   for (i = 0; i < var->ndims; i++)
566   {
567      start[i] = startp[i];
568      count[i] = countp[i];
569   }
570   
571   /* Open this dataset if necessary, also checking for a weird case:
572    * a non-coordinate (and non-scalar) variable that has the same
573    * name as a dimension. */
574   if (var->hdf5_name && strlen(var->hdf5_name) >= strlen(NON_COORD_PREPEND) && 
575       strncmp(var->hdf5_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND)) == 0 &&
576       var->ndims)
577       if ((var->hdf_datasetid = H5Dopen2(grp->hdf_grpid, var->hdf5_name,
578                                          H5P_DEFAULT)) < 0)
579           return NC_ENOTVAR;
580   if (!var->hdf_datasetid)
581       if ((var->hdf_datasetid = H5Dopen2(grp->hdf_grpid, var->name,
582                                          H5P_DEFAULT)) < 0)
583           return NC_ENOTVAR;
584
585   /* Get file space of data. */
586   if ((file_spaceid = H5Dget_space(var->hdf_datasetid)) < 0) 
587      BAIL(NC_EHDFERR);
588#ifdef EXTRA_TESTS
589   num_spaces++;
590#endif
591
592   /* Check to ensure the user selection is
593    * valid. H5Sget_simple_extent_dims gets the sizes of all the dims
594    * and put them in fdims. */
595   if (H5Sget_simple_extent_dims(file_spaceid, fdims, fmaxdims) < 0)
596      BAIL(NC_EHDFERR);
597     
598#ifdef LOGGING
599   log_dim_info(var, fdims, fmaxdims, start, count);
600#endif
601
602   /* Check dimension bounds. Remember that unlimited dimnsions can
603    * put data beyond their current length. */
604   for (d2 = 0; d2 < var->ndims; d2++)
605   {
606      for (dim = grp->dim; dim; dim = dim->next)
607      {
608         if (dim->dimid == var->dimids[d2])
609         {
610            if (!dim->unlimited)
611            {
612               if (start[d2] >= (hssize_t)fdims[d2])
613                  BAIL_QUIET(NC_EINVALCOORDS);
614               if (start[d2] + count[d2] > fdims[d2])
615                  BAIL_QUIET(NC_EEDGE);
616            }
617         }
618      }
619   }
620
621
622   /* A little quirk: if any of the count values are zero, then
623      return success and forget about it. */
624
625   for (d2 = 0; d2 < var->ndims; d2++)
626      if (count[d2] != 0) no_data++ ;
627/*         goto exit; */
628   
629   /* Now you would think that no one would be crazy enough to write
630      a scalar dataspace with one of the array function calls, but you
631      would be wrong. So let's check to see if the dataset is
632      scalar. If it is, we won't try to set up a hyperslab. */
633   if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR)
634   {
635      if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0) 
636         BAIL(NC_EHDFERR);
637#ifdef EXTRA_TESTS
638      num_spaces++;
639#endif
640      scalar++;
641   }
642   else
643   {
644      if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, start, NULL, 
645                              count, NULL) < 0)
646         BAIL(NC_EHDFERR);
647
648      /* Create a space for the memory, just big enough to hold the slab
649         we want. */
650      if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0) 
651         BAIL(NC_EHDFERR);
652#ifdef EXTRA_TESTS
653      num_spaces++;
654#endif
655   }
656   
657   /* Later on, we will need to know the size of this type in the
658    * file. */
659   if ((retval = nc4_get_typelen_mem(h5, var->xtype, 0, &file_type_size)))
660      return retval;
661
662#ifndef HDF5_CONVERT   
663   /* Are we going to convert any data? (No converting of compound or
664    * opaque types.) */
665   if ((mem_nc_type != var->xtype || (var->xtype == NC_INT && is_long)) && 
666       mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE)
667   {
668      /* We must convert - allocate a buffer. */
669      need_to_convert++;
670      if (var->ndims)
671         for (d2=0; d2<var->ndims; d2++)
672            len *= countp[d2];
673      LOG((4, "converting data for var %s type=%d len=%d", var->name, 
674           var->xtype, len));
675
676      /* If we're reading, we need bufr to have enough memory to store
677       * the data in the file. If we're writing, we need bufr to be
678       * big enough to hold all the data in the file's type. */
679      if (!(bufr = malloc(len * file_type_size)))
680         BAIL(NC_ENOMEM);
681   }
682   else
683#endif /* ifndef HDF5_CONVERT */
684      bufr = data;
685
686#ifdef HDF5_CONVERT
687   /* Get the HDF type of the data in memory. */
688   if ((retval = nc4_get_hdf_typeid(h5, mem_nc_type, &mem_typeid, 
689                                    var->type_info->endianness)))
690      BAIL(retval);
691#endif
692   
693   /* Create the data transfer property list. */
694   if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0)
695      BAIL(NC_EHDFERR);
696#ifdef EXTRA_TESTS
697      num_plists++;
698#endif
699
700   /* Apply the callback function which will detect range
701    * errors. Which one to call depends on the length of the
702    * destination buffer type. */
703#ifdef HDF5_CONVERT
704   if (H5Pset_type_conv_cb(xfer_plistid, except_func, &range_error) < 0) 
705      BAIL(NC_EHDFERR);
706#endif
707
708#ifdef USE_PARALLEL
709   /* Set up parallel I/O, if needed. */
710   if ((retval = set_par_access(h5, var, xfer_plistid)))
711      BAIL(retval);
712#endif
713
714   /* Read/write this hyperslab into memory. */
715   /* Does the dataset have to be extended? If it's already
716      extended to the required size, it will do no harm to reextend
717      it to that size. */
718   if (var->ndims)
719   {
720      if (!(xtend_size = malloc(var->ndims * sizeof(hsize_t))))
721         BAIL(NC_ENOMEM);
722      for (d2 = 0; d2 < var->ndims; d2++)
723      {
724         if ((retval = nc4_find_dim(grp, var->dimids[d2], &dim, NULL)))
725            BAIL(retval);
726         if (dim->unlimited)
727         {
728            if (start[d2] + count[d2] > fdims[d2])
729            {
730               xtend_size[d2] = start[d2] + count[d2];
731               need_to_extend++;
732            }
733            else
734               xtend_size[d2] = fdims[d2];
735
736            if (start[d2] + count[d2] > dim->len)
737            {
738               dim->len = start[d2] + count[d2];
739               dim->extended++;
740            }
741         }
742         else
743         {
744            xtend_size[d2] = dim->len;
745         }
746      }
747
748      /* If we need to extend it, we also need a new file_spaceid
749         to reflect the new size of the space. */
750      if (need_to_extend)
751      {
752         LOG((4, "extending dataset"));
753         if (H5Dextend(var->hdf_datasetid, xtend_size) < 0)
754            BAIL(NC_EHDFERR);
755         if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0)
756            BAIL2(NC_EHDFERR);
757         if ((file_spaceid = H5Dget_space(var->hdf_datasetid)) < 0) 
758            BAIL(NC_EHDFERR);
759#ifdef EXTRA_TESTS
760         num_spaces++;
761#endif
762         if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, 
763                                 start, NULL, count, NULL) < 0)
764            BAIL(NC_EHDFERR);
765      }
766   }
767
768#ifndef HDF5_CONVERT
769   /* Do we need to convert the data? */
770   if (need_to_convert)
771   {
772      if ((retval = nc4_convert_type(data, bufr, mem_nc_type, var->xtype, 
773                                     len, &range_error, var->fill_value,
774                                     (h5->cmode & NC_CLASSIC_MODEL), is_long, 0)))
775         BAIL(retval);
776   }
777#endif
778
779   /* Write the data. At last! */
780   LOG((4, "about to H5Dwrite datasetid 0x%x mem_spaceid 0x%x "
781        "file_spaceid 0x%x", var->hdf_datasetid, mem_spaceid, file_spaceid));
782/*
783   if (no_data==0)
784   {
785     H5Sselect_none(mem_spaceid);
786     H5Sselect_none(file_spaceid);
787     bufr=NULL ;
788   }
789*/ 
790/*   if (no_data!=0) */
791        if (H5Dwrite(var->hdf_datasetid, var->type_info->hdf_typeid, 
792                  mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0)
793        BAIL(NC_EHDFERR);
794     
795
796   /* Remember that we have written to this var so that Fill Value
797    * can't be set for it. */
798   if (!var->written_to)
799      var->written_to++;
800
801   /* For strict netcdf-3 rules, ignore erange errors between UBYTE
802    * and BYTE types. */
803   if ((h5->cmode & NC_CLASSIC_MODEL) &&
804       (var->xtype == NC_UBYTE || var->xtype == NC_BYTE) &&
805       (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) &&
806       range_error)
807      range_error = 0;
808
809  exit:
810   if (file_spaceid > 0 && H5Sclose(file_spaceid) < 0)
811      BAIL2(NC_EHDFERR);
812#ifdef EXTRA_TESTS
813   num_spaces--;
814#endif
815   if (mem_spaceid > 0 && H5Sclose(mem_spaceid) < 0)
816      BAIL2(NC_EHDFERR);
817#ifdef EXTRA_TESTS
818   num_spaces--;
819#endif
820   if (xfer_plistid && (H5Pclose(xfer_plistid) < 0))
821      BAIL2(NC_EPARINIT);
822#ifdef EXTRA_TESTS
823      num_plists--;
824#endif
825#ifndef HDF5_CONVERT
826   if (need_to_convert) free(bufr);
827#endif
828   if (xtend_size) free(xtend_size);
829
830   /* If there was an error return it, otherwise return any potential
831      range error value. If none, return NC_NOERR as usual.*/
832   if (retval)     
833      return retval;
834   if (range_error)
835      return NC_ERANGE;
836   return NC_NOERR;
837}
838
839int 
840nc4_get_vara(NC_FILE_INFO_T *nc, int ncid, int varid, const size_t *startp, 
841                      const size_t *countp, nc_type mem_nc_type, int is_long, void *data)
842{
843   NC_GRP_INFO_T *grp, *g;
844   NC_HDF5_FILE_INFO_T *h5;
845   NC_VAR_INFO_T *var;
846   NC_DIM_INFO_T *dim;
847
848   hid_t file_spaceid = 0, mem_spaceid = 0;
849   hid_t xfer_plistid = 0;
850   hid_t hdf_datasetid;
851   size_t file_type_size;
852
853   hsize_t *xtend_size = NULL, count[NC_MAX_VAR_DIMS];
854   hsize_t fdims[NC_MAX_VAR_DIMS], fmaxdims[NC_MAX_VAR_DIMS];
855   hsize_t start[NC_MAX_VAR_DIMS];
856   void *fillvalue = NULL;
857   int no_read = 0, provide_fill = 0;
858   int fill_value_size[NC_MAX_VAR_DIMS];
859   int scalar = 0, retval = NC_NOERR, range_error = 0, i, d2;
860   void *bufr = NULL;
861   int break_it;
862#ifndef HDF5_CONVERT   
863   int need_to_convert = 0;
864   size_t len = 1;
865#endif
866
867   /* Find our metadata for this file, group, and var. */
868   assert(nc);
869   if ((retval = nc4_find_g_var_nc(nc, ncid, varid, &grp, &var)))
870      return retval;
871   h5 = nc->nc4_info;
872   assert(grp && h5 && var && var->name);
873
874   LOG((3, "nc4_get_vara: var->name %s mem_nc_type %d is_long %d", 
875        var->name, mem_nc_type, is_long));
876
877   /* Check some stuff about the type and the file. */
878   if ((retval = check_for_vara(&mem_nc_type, var, h5)))
879      return retval;
880
881   /* Convert from size_t and ptrdiff_t to hssize_t, and hsize_t. */
882   for (i = 0; i < var->ndims; i++)
883   {
884      start[i] = startp[i];
885      count[i] = countp[i];
886   }
887
888   /* Open this dataset if necessary, also checking for a weird case:
889    * a non-coordinate (and non-scalar) variable that has the same
890    * name as a dimension. */
891   if (var->hdf5_name && strlen(var->hdf5_name) >= strlen(NON_COORD_PREPEND) && 
892       strncmp(var->hdf5_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND)) == 0 &&
893       var->ndims)
894       if ((var->hdf_datasetid = H5Dopen2(grp->hdf_grpid, var->hdf5_name,
895                                          H5P_DEFAULT)) < 0)
896           return NC_ENOTVAR;
897   if (!var->hdf_datasetid)
898       if ((var->hdf_datasetid = H5Dopen2(grp->hdf_grpid, var->name,
899                                          H5P_DEFAULT)) < 0)
900           return NC_ENOTVAR;
901
902   /* Get file space of data. */
903   if ((file_spaceid = H5Dget_space(var->hdf_datasetid)) < 0) 
904      BAIL(NC_EHDFERR);
905#ifdef EXTRA_TESTS
906   num_spaces++;
907#endif
908
909   /* Check to ensure the user selection is
910    * valid. H5Sget_simple_extent_dims gets the sizes of all the dims
911    * and put them in fdims. */
912   if (H5Sget_simple_extent_dims(file_spaceid, fdims, fmaxdims) < 0)
913      BAIL(NC_EHDFERR);
914
915#ifdef LOGGING
916   log_dim_info(var, fdims, fmaxdims, start, count);
917#endif
918
919   /* Check dimension bounds. Remember that unlimited dimnsions can
920    * put data beyond their current length. */
921   for (d2 = 0, break_it = 0; d2 < var->ndims; d2++)
922   {
923      for (g = grp; g && !break_it; g = g->parent)
924         for (dim = g->dim; dim; dim = dim->next)
925         {
926            if (dim->dimid == var->dimids[d2])
927            {
928               if (!dim->unlimited)
929               {
930                  if (start[d2] >= (hssize_t)fdims[d2])
931                     BAIL_QUIET(NC_EINVALCOORDS);
932                  if (start[d2] + count[d2] > fdims[d2])
933                     BAIL_QUIET(NC_EEDGE);
934               }
935               if (dim->unlimited)
936               {
937                  size_t ulen;
938                 
939                  /* We can't go beyond the latgest current extent of
940                     the unlimited dim. */
941                  if ((retval = nc_inq_dimlen(ncid, dim->dimid, &ulen)))
942                     BAIL(retval);
943                 
944                  /* Check for out of bound requests. */
945                  if (start[d2] >= (hssize_t)ulen && count[d2])
946                     BAIL_QUIET(NC_EINVALCOORDS);
947                  if (start[d2] + count[d2] > ulen)
948                     BAIL_QUIET(NC_EEDGE);
949                 
950                  /* THings get a little tricky here. If we're getting
951                     a GET request beyond the end of this var's
952                     current length in an unlimited dimension, we'll
953                     later need to return the fill value for the
954                     variable. */
955                  if (start[d2] >= (hssize_t)fdims[d2])
956                     fill_value_size[d2] = count[d2];
957                  else if (start[d2] + count[d2] > fdims[d2])
958                     fill_value_size[d2] = count[d2] - (fdims[d2] - start[d2]);
959                  else
960                     fill_value_size[d2] = 0;
961                  count[d2] -= fill_value_size[d2];
962                  if (fill_value_size[d2])
963                     provide_fill++;
964               }
965               else
966                  fill_value_size[d2] = count[d2];
967            }
968         }
969   }
970   
971   /* A little quirk: if any of the count values are zero, don't
972    * read. */
973   for (d2 = 0; d2 < var->ndims; d2++)
974      if (count[d2] == 0)
975         no_read++;
976   
977   /* Later on, we will need to know the size of this type in the
978    * file. */
979   if ((retval = nc4_get_typelen_mem(h5, var->xtype, 0, &file_type_size)))
980      return retval;
981
982   if (!no_read)
983   {
984      /* Now you would think that no one would be crazy enough to write
985         a scalar dataspace with one of the array function calls, but you
986         would be wrong. So let's check to see if the dataset is
987         scalar. If it is, we won't try to set up a hyperslab. */
988      if (H5Sget_simple_extent_type(file_spaceid) == H5S_SCALAR)
989      {
990         if ((mem_spaceid = H5Screate(H5S_SCALAR)) < 0) 
991            BAIL(NC_EHDFERR);
992         scalar++;
993#ifdef EXTRA_TESTS
994         num_spaces++;
995#endif
996      }
997      else
998      {
999         if (H5Sselect_hyperslab(file_spaceid, H5S_SELECT_SET, 
1000                                 start, NULL, count, NULL) < 0)
1001            BAIL(NC_EHDFERR);
1002         /* Create a space for the memory, just big enough to hold the slab
1003            we want. */
1004         if ((mem_spaceid = H5Screate_simple(var->ndims, count, NULL)) < 0) 
1005            BAIL(NC_EHDFERR);
1006#ifdef EXTRA_TESTS
1007         num_spaces++;
1008#endif
1009      }
1010
1011      /* Fix bug when reading HDF5 files with variable of type
1012       * fixed-length string.  We need to make it look like a
1013       * variable-length string, because that's all netCDF-4 data
1014       * model supports, lacking anonymous dimensions.  So
1015       * variable-length strings are in allocated memory that user has
1016       * to free, which we allocate here. */
1017      if(var->type_info->class == H5T_STRING && 
1018         H5Tget_size(var->type_info->hdf_typeid) > 1 &&
1019         !H5Tis_variable_str(var->type_info->hdf_typeid)) {
1020          hsize_t fstring_len;
1021          if ((fstring_len = H5Tget_size(var->type_info->hdf_typeid)) < 0)
1022              BAIL(NC_EHDFERR);
1023          if (!(*(char **)data = malloc(1 + fstring_len)))
1024              BAIL(NC_ENOMEM);
1025          bufr = *(char **)data;
1026      }
1027   
1028#ifndef HDF5_CONVERT   
1029      /* Are we going to convert any data? (No converting of compound or
1030       * opaque types.) */
1031      if ((mem_nc_type != var->xtype || (var->xtype == NC_INT && is_long)) && 
1032          mem_nc_type != NC_COMPOUND && mem_nc_type != NC_OPAQUE)
1033      {
1034         /* We must convert - allocate a buffer. */
1035         need_to_convert++;
1036         if (var->ndims)
1037            for (d2 = 0; d2 < var->ndims; d2++)
1038               len *= countp[d2];
1039         LOG((4, "converting data for var %s type=%d len=%d", var->name, 
1040              var->xtype, len));
1041
1042         /* If we're reading, we need bufr to have enough memory to store
1043          * the data in the file. If we're writing, we need bufr to be
1044          * big enough to hold all the data in the file's type. */
1045         if (!(bufr = malloc(len * file_type_size)))
1046            BAIL(NC_ENOMEM);
1047      }
1048      else
1049#endif /* ifndef HDF5_CONVERT */
1050          if(!bufr)
1051              bufr = data;
1052
1053      /* Get the HDF type of the data in memory. */
1054#ifdef HDF5_CONVERT
1055      if ((retval = nc4_get_hdf_typeid(h5, mem_nc_type, &mem_typeid, 
1056                                       var->type_info->endianness)))
1057         BAIL(retval);
1058#endif
1059   
1060      /* Create the data transfer property list. */
1061      if ((xfer_plistid = H5Pcreate(H5P_DATASET_XFER)) < 0)
1062         BAIL(NC_EHDFERR);
1063#ifdef EXTRA_TESTS
1064      num_plists++;
1065#endif
1066
1067#ifdef HDF5_CONVERT
1068      /* Apply the callback function which will detect range
1069       * errors. Which one to call depends on the length of the
1070       * destination buffer type. */
1071      if (H5Pset_type_conv_cb(xfer_plistid, except_func, &range_error) < 0) 
1072         BAIL(NC_EHDFERR);
1073#endif
1074
1075#ifdef USE_PARALLEL
1076      /* Set up parallel I/O, if needed. */
1077      if ((retval = set_par_access(h5, var, xfer_plistid)))
1078         BAIL(retval);
1079#endif
1080
1081      /* Read this hyperslab into memory. */
1082      LOG((5, "About to H5Dread some data..."));
1083      if (H5Dread(var->hdf_datasetid, var->type_info->native_typeid, 
1084                  mem_spaceid, file_spaceid, xfer_plistid, bufr) < 0)
1085         BAIL(NC_EHDFERR);
1086
1087#ifndef HDF5_CONVERT
1088      /* Eventually the block below will go away. Right now it's
1089         needed to support conversions between int/float, and range
1090         checking converted data in the netcdf way. These features are
1091         being added to HDF5 at the HDF5 World Hall of Coding right
1092         now, by a staff of thousands of programming gnomes. */
1093      if (need_to_convert)
1094      {
1095         if ((retval = nc4_convert_type(bufr, data, var->xtype, mem_nc_type, 
1096                                        len, &range_error, var->fill_value, 
1097                                        (h5->cmode & NC_CLASSIC_MODEL), 0, is_long)))
1098            BAIL(retval);
1099
1100         /* For strict netcdf-3 rules, ignore erange errors between UBYTE
1101          * and BYTE types. */
1102         if ((h5->cmode & NC_CLASSIC_MODEL) &&
1103             (var->xtype == NC_UBYTE || var->xtype == NC_BYTE) &&
1104             (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) &&
1105             range_error)
1106            range_error = 0;
1107      }
1108#endif
1109
1110      /* For strict netcdf-3 rules, ignore erange errors between UBYTE
1111       * and BYTE types. */
1112      if ((h5->cmode & NC_CLASSIC_MODEL) &&
1113          (var->xtype == NC_UBYTE || var->xtype == NC_BYTE) &&
1114          (mem_nc_type == NC_UBYTE || mem_nc_type == NC_BYTE) &&
1115          range_error)
1116         range_error = 0;
1117
1118   } /* endif ! no_read */
1119
1120   /* Now we need to fake up any further data that was asked for,
1121      using the fill values instead. First skip past the data we
1122      just read, if any. */
1123   if (!scalar && provide_fill)
1124   {
1125      void *filldata;
1126      size_t real_data_size = 0;
1127      size_t fill_len;
1128
1129      /* Skip past the real data we've already read. */
1130      if (!no_read)
1131         for (real_data_size = file_type_size, d2 = 0; d2 < var->ndims; d2++)
1132            real_data_size *= (count[d2] - start[d2]);
1133
1134      /* Get the fill value from the HDF5 variable. Memory will be
1135       * allocated. */
1136      if (get_fill_value(h5, var, &fillvalue) < 0)
1137         BAIL(NC_EHDFERR);
1138
1139      /* How many fill values do we need? */
1140      for (fill_len = 1, d2 = 0; d2 < var->ndims; d2++)     
1141         fill_len *= (fill_value_size[d2] ? fill_value_size[d2] : 1);
1142
1143      /* Copy the fill value into the rest of the data buffer. */
1144      filldata = (char *)data + real_data_size;
1145      for (i = 0; i < fill_len; i++)
1146      {
1147         if (var->xtype == NC_STRING)
1148         {
1149            if (!(*(char **)filldata = malloc(sizeof(*(char **)fillvalue))))
1150               return NC_ENOMEM;
1151            strcpy(*(char **)filldata, *(char **)fillvalue);
1152         }
1153         else
1154         {
1155            memcpy(filldata, fillvalue, file_type_size);
1156            filldata = (char *)filldata + file_type_size;
1157         }
1158      }
1159   }
1160
1161  exit:
1162/**   if (var->xtype == NC_CHAR && mem_typeid > 0 && H5Tclose(mem_typeid) < 0)
1163      BAIL2(NC_EHDFERR);*/
1164   if (file_spaceid > 0)
1165   {
1166      if (H5Sclose(file_spaceid) < 0)
1167         BAIL2(NC_EHDFERR);
1168#ifdef EXTRA_TESTS
1169      num_spaces--;
1170#endif
1171   }
1172   if (mem_spaceid > 0)
1173   {
1174      if (H5Sclose(mem_spaceid) < 0)
1175         BAIL2(NC_EHDFERR);
1176#ifdef EXTRA_TESTS
1177      num_spaces--;
1178#endif
1179   }
1180   if (xfer_plistid > 0)
1181   {
1182      if (H5Pclose(xfer_plistid) < 0)
1183         BAIL2(NC_EHDFERR);
1184#ifdef EXTRA_TESTS
1185      num_plists--;
1186#endif
1187   }
1188#ifndef HDF5_CONVERT
1189   if (need_to_convert) 
1190      free(bufr);
1191#endif
1192   if (xtend_size) 
1193      free(xtend_size);
1194   if (fillvalue) 
1195   {
1196      if (var->xtype == NC_STRING)
1197      {
1198         if (nc_free_string(1, (char **)fillvalue)) 
1199            return NC_ENOMEM;
1200      }
1201      else
1202         free(fillvalue);
1203   }
1204
1205   /* If there was an error return it, otherwise return any potential
1206      range error value. If none, return NC_NOERR as usual.*/
1207   if (retval)     
1208      return retval;
1209   if (range_error)
1210      return NC_ERANGE;
1211   return NC_NOERR;
1212}
1213
1214/* This function is a bit of a hack. Turns out that HDF5 dimension
1215 * scales cannot themselves have scales attached. This leaves
1216 * multidimensional coordinate variables hosed. So this function
1217 * writes a special attribute for such a variable, which has the ids
1218 * of all the dimensions for that coordinate variable. This sucks,
1219 * really. But that's the way the cookie crumbles. Better luck next
1220 * time. This function also contains a new way of dealing with HDF5
1221 * error handling, abandoning the BAIL macros for a more organic and
1222 * natural approach, made with whole grains, and locally-grown
1223 * vegetables. */
1224static int
1225write_coord_dimids(NC_VAR_INFO_T *var)     
1226{
1227   hsize_t coords_len[1];
1228   hid_t c_spaceid = -1, c_attid = -1;
1229   int ret = 0;
1230
1231   /* Write our attribute. */
1232   coords_len[0] = var->ndims;
1233   if ((c_spaceid = H5Screate_simple(1, coords_len, coords_len)) < 0) ret++;
1234#ifdef EXTRA_TESTS
1235      num_spaces++;
1236#endif
1237   if (!ret && (c_attid = H5Acreate(var->hdf_datasetid, COORDINATES, H5T_NATIVE_INT, 
1238                                    c_spaceid, H5P_DEFAULT)) < 0) ret++;
1239   if (!ret && H5Awrite(c_attid, H5T_NATIVE_INT, var->dimids) < 0) ret++;
1240
1241   /* Close up shop. */
1242   if (c_spaceid > 0 && H5Sclose(c_spaceid) < 0) ret++;
1243#ifdef EXTRA_TESTS
1244   num_spaces--;
1245#endif
1246   if (c_attid > 0 && H5Aclose(c_attid) < 0) ret++;
1247   return ret ? NC_EHDFERR : 0;
1248}
1249
1250/* Write a special attribute for the netCDF-4 dimension ID. */
1251static int
1252write_netcdf4_dimid(hid_t datasetid, int dimid)
1253{
1254   hid_t dimid_spaceid, dimid_attid;
1255
1256   /* Create the space. */
1257   if ((dimid_spaceid = H5Screate(H5S_SCALAR)) < 0) 
1258      return NC_EHDFERR;
1259#ifdef EXTRA_TESTS
1260   num_spaces++;
1261#endif
1262
1263   /* Does the attribute already exist? If so, don't try to create it. */
1264   H5E_BEGIN_TRY { 
1265      dimid_attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME, 
1266                                    H5P_DEFAULT, H5P_DEFAULT);
1267   } H5E_END_TRY;
1268 
1269   /* Create the attribute if needed. */
1270   if (dimid_attid < 0)
1271      if ((dimid_attid = H5Acreate(datasetid, NC_DIMID_ATT_NAME, 
1272                                   H5T_NATIVE_INT, dimid_spaceid, H5P_DEFAULT)) < 0)
1273         return NC_EHDFERR;
1274
1275   /* Write it. */
1276   LOG((4, "write_netcdf4_dimid: writting secret dimid %d", dimid));
1277   if (H5Awrite(dimid_attid, H5T_NATIVE_INT, &dimid) < 0)
1278      return NC_EHDFERR;
1279
1280   /* Close stuff*/
1281   if (H5Sclose(dimid_spaceid) < 0) 
1282      return NC_EHDFERR;
1283#ifdef EXTRA_TESTS
1284   num_spaces--;
1285#endif
1286   if (H5Aclose(dimid_attid) < 0)
1287      return NC_EHDFERR;
1288
1289   return NC_NOERR;
1290}
1291
1292/* This function creates the HDF5 dataset for a variabale. */
1293static int
1294var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, int write_dimid)
1295{
1296   NC_GRP_INFO_T *g;
1297   hid_t plistid = 0, access_plistid = 0, typeid = 0, spaceid = 0;
1298   hsize_t *chunksize = NULL, *dimsize = NULL, *maxdimsize = NULL;
1299   int d;
1300   NC_DIM_INFO_T *dim = NULL;
1301   void *fillp = NULL;
1302   int dims_found = 0;
1303   int set_chunksizes = 0;
1304   char *name_to_use;
1305   int retval = NC_NOERR;
1306   
1307   LOG((3, "var_create_dataset: name %s", var->name));
1308
1309   /* Scalar or not, we need a creation property list. */
1310   if ((plistid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
1311      BAIL(NC_EHDFERR);
1312#ifdef EXTRA_TESTS
1313      num_plists++;
1314#endif
1315   if ((access_plistid = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
1316      BAIL(NC_EHDFERR);
1317#ifdef EXTRA_TESTS
1318      num_plists++;
1319#endif
1320
1321   /* Find the HDF5 type of the dataset. */
1322   if ((retval = nc4_get_hdf_typeid(grp->file->nc4_info, var->xtype, &typeid, 
1323                                    var->type_info->endianness)))
1324      BAIL(retval);
1325
1326   /* Figure out what fill value to set, if any. */
1327   if (!var->no_fill)
1328   {
1329      if ((retval = get_fill_value(grp->file->nc4_info, var, &fillp)))
1330         BAIL(retval);
1331
1332      /* If there is a fill value, set it. */
1333      if (fillp)
1334      {
1335         if (var->xtype == NC_STRING)
1336         {
1337            if (H5Pset_fill_value(plistid, typeid, &fillp) < 0)
1338               BAIL(NC_EHDFERR);
1339            free((char *)fillp);
1340         }
1341         else 
1342         {
1343            /* The fill value set in HDF5 must always be presented as
1344             * a native type, even if the endianness for this dataset
1345             * is non-native. HDF5 will translate the fill value to
1346             * the target endiannesss. */
1347            hid_t fill_typeid;
1348            if ((retval = nc4_get_hdf_typeid(grp->file->nc4_info, var->xtype, &fill_typeid, 
1349                                             NC_ENDIAN_NATIVE)))
1350               BAIL(retval);
1351            if (H5Pset_fill_value(plistid, fill_typeid, fillp) < 0)
1352               BAIL(NC_EHDFERR);
1353            if (var->type_info->class == NC_VLEN)
1354               nc_free_vlen((nc_vlen_t *)fillp);
1355            else
1356               free(fillp);
1357            if (var->type_info->nc_typeid == NC_STRING || var->type_info->nc_typeid == NC_CHAR)
1358               if (H5Tclose(fill_typeid) < 0)
1359                  BAIL(NC_EHDFERR);
1360         }
1361      }
1362   } else {
1363       /* Required to truly turn HDF5 fill values off */
1364       if(H5Pset_fill_time(plistid,H5D_FILL_TIME_NEVER) < 0)
1365           BAIL(NC_EHDFERR);
1366   }
1367
1368   /* If the user wants to shuffle the data, set that up now. */
1369   if (var->shuffle)
1370      if (H5Pset_shuffle(plistid) < 0)
1371         BAIL(NC_EHDFERR);
1372
1373   /* If the user wants to deflate the data, set that up now. */
1374   if (var->deflate)
1375      if (H5Pset_deflate(plistid, var->deflate_level) < 0)
1376         BAIL(NC_EHDFERR);
1377
1378   /* Szip? NO! We don't want anyone to produce szipped netCDF files! */
1379/* #ifdef USE_SZIP */
1380/*    if (var->options_mask) */
1381/*       if (H5Pset_szip(plistid, var->options_mask, var->bits_per_pixel) < 0) */
1382/*          BAIL(NC_EHDFERR); */
1383/* #endif */
1384 
1385   /* If the user wants to fletcher error correcton, set that up now. */
1386   if (var->fletcher32)
1387      if (H5Pset_fletcher32(plistid) < 0)
1388         BAIL(NC_EHDFERR);
1389
1390   /* If ndims non-zero, get info for all dimensions. We look up the
1391      dimids and get the len of each dimension. We need this to create
1392      the space for the dataset. In netCDF a dimension length of zero
1393      means an unlimited dimension. */
1394   if (var->ndims)
1395   {
1396      int unlimdim = 0;
1397
1398      /* Check to see if any unlimited dimensions are used in this var. */
1399      for (d = 0; d < var->ndims; d++)
1400         if (var->dim[d]->unlimited)
1401            unlimdim++;
1402
1403      /* If there are no unlimited dims, and no filters, and the user
1404       * has not specified chunksizes, use contiguous variable for
1405       * better performance. */
1406      if (!unlimdim && !var->shuffle && !var->deflate && !var->options_mask &&
1407          !var->fletcher32 && !var->chunksizes[0])
1408         var->contiguous = 1;
1409
1410      if (!(dimsize = malloc(var->ndims * sizeof(hsize_t))))
1411         BAIL(NC_ENOMEM);
1412      if (!(maxdimsize = malloc(var->ndims * sizeof(hsize_t))))
1413         BAIL(NC_ENOMEM);
1414      if (!(chunksize = malloc(var->ndims * sizeof(hsize_t))))
1415         BAIL(NC_ENOMEM);
1416/*      for (d = 0; d < var->ndims; d++)
1417         dimsize[d] = var->dim[d]->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : var->dim[d]->len;
1418      maxdimsize[d] = var->dim[d]->unlimited ? H5S_UNLIMITED : (hsize_t)var->dim[d]->len;
1419      chunksize[d] = var->chunksizes[d];*/
1420   
1421         for (d = 0; d < var->ndims; d++)
1422         for (g = grp; g && (dims_found < var->ndims); g = g->parent)
1423            for (dim = g->dim; dim; dim = dim->next)
1424               if (dim->dimid == var->dimids[d])
1425               {
1426                  dimsize[d] = dim->unlimited ? NC_HDF5_UNLIMITED_DIMSIZE : dim->len;
1427                  maxdimsize[d] = dim->unlimited ? H5S_UNLIMITED : (hsize_t)dim->len;
1428                  if (var->chunksizes[d])
1429                     chunksize[d] = var->chunksizes[d];
1430                  else
1431                  {
1432                     size_t type_size;
1433                     if (var->type_info->nc_typeid == NC_STRING)
1434                        type_size = sizeof(char *);
1435                     else
1436                        type_size = var->type_info->size;
1437                     
1438                     /* Unlimited dim always gets chunksize of 1. */
1439                     if (dim->unlimited)
1440                        chunksize[d] = 1;
1441                     else
1442                        chunksize[d] = pow((double)DEFAULT_CHUNK_SIZE/type_size, 
1443                                           1/(double)(var->ndims - unlimdim));
1444                     
1445                     /* If the chunksize is greater than the dim
1446                      * length, make it the dim length. */
1447                     if (!dim->unlimited && chunksize[d] > dim->len)
1448                        chunksize[d] = dim->len;
1449                     set_chunksizes++;
1450                  }
1451
1452                  if (!var->contiguous && !var->chunksizes[d])
1453                     var->chunksizes[d] = chunksize[d];
1454                  dims_found++;
1455                  break;
1456               }
1457     
1458      if (var->contiguous)
1459      {
1460         if (H5Pset_layout(plistid, H5D_CONTIGUOUS) < 0)
1461            BAIL(NC_EHDFERR);
1462      }
1463      else
1464      {
1465         if (H5Pset_chunk(plistid, var->ndims, chunksize) < 0)
1466            BAIL(NC_EHDFERR);
1467      }
1468   
1469      /* Create the dataspace. */
1470      if ((spaceid = H5Screate_simple(var->ndims, dimsize, maxdimsize)) < 0)
1471         BAIL(NC_EHDFERR);
1472#ifdef EXTRA_TESTS
1473      num_spaces++;
1474#endif
1475   }
1476   else
1477   {
1478      if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1479         BAIL(NC_EHDFERR);
1480#ifdef EXTRA_TESTS
1481      num_spaces++;
1482#endif
1483   }
1484
1485   /* Turn on creation order tracking. */
1486   if (H5Pset_attr_creation_order(plistid, H5P_CRT_ORDER_TRACKED|
1487                                  H5P_CRT_ORDER_INDEXED) < 0)
1488      BAIL(NC_EHDFERR);
1489
1490   /* Set per-var chunk cache. */
1491   if (var->chunk_cache_size)
1492      if (H5Pset_chunk_cache(access_plistid, var->chunk_cache_nelems,
1493                             var->chunk_cache_size, var->chunk_cache_preemption) < 0)
1494         BAIL(NC_EHDFERR);
1495
1496   /* At long last, create the dataset. */
1497   name_to_use = var->hdf5_name ? var->hdf5_name : var->name;
1498   LOG((4, "var_create_dataset: about to H5Dcreate dataset %s of type 0x%x", 
1499        name_to_use, typeid));
1500   if ((var->hdf_datasetid = H5Dcreate2(grp->hdf_grpid, name_to_use, typeid, 
1501                                        spaceid, H5P_DEFAULT, plistid, access_plistid)) < 0)
1502      BAIL(NC_EHDFERR);
1503   var->created++;
1504   var->dirty = 0;
1505
1506   /* If this is a dimscale, mark it as such in the HDF5 file. Also
1507    * find the dimension info and store the dataset id of the dimscale
1508    * dataset. */
1509   if (var->dimscale)
1510   {
1511      if (H5DSset_scale(var->hdf_datasetid, var->name) < 0)
1512         BAIL(NC_EHDFERR);
1513      for (dim = grp->dim; dim; dim = dim->next)
1514         if (strcmp(dim->name, var->name) == 0)
1515         {
1516            dim->hdf_dimscaleid = var->hdf_datasetid;
1517            break;
1518         }
1519
1520      /* Make sure we found a dimension, and gave it a dimscale id. */
1521      if (!dim || !dim->hdf_dimscaleid)
1522         BAIL(NC_EDIMMETA);
1523
1524      /* If this is a multidimensional coordinate variable, write a
1525       * coordinates attribute. */
1526      if (var->ndims > 1)
1527         if ((retval = write_coord_dimids(var)))
1528            BAIL(retval);
1529
1530      /* If desired, write the netCDF dimid. */
1531      if (write_dimid)
1532         if ((retval = write_netcdf4_dimid(var->hdf_datasetid, 
1533                                           var->dimids[0])))
1534            BAIL(retval);
1535
1536   }
1537
1538  exit:
1539   if (var->type_info->nc_typeid == NC_STRING || var->type_info->nc_typeid == NC_CHAR)
1540      if (typeid > 0 && H5Tclose(typeid) < 0)
1541          BAIL2(NC_EHDFERR);
1542   if (plistid > 0 && H5Pclose(plistid) < 0)
1543      BAIL2(NC_EHDFERR);
1544#ifdef EXTRA_TESTS
1545      num_plists--;
1546#endif
1547   if (access_plistid > 0 && H5Pclose(access_plistid) < 0)
1548      BAIL2(NC_EHDFERR);
1549#ifdef EXTRA_TESTS
1550      num_plists--;
1551#endif
1552   if (spaceid > 0 && H5Sclose(spaceid) < 0)
1553      BAIL2(NC_EHDFERR);
1554#ifdef EXTRA_TESTS
1555   num_spaces--;
1556#endif
1557   if (maxdimsize) free(maxdimsize);
1558   if (dimsize) free(dimsize);
1559   if (chunksize) free(chunksize);
1560   return retval;
1561}
1562
1563/* Adjust the chunk cache of a var for better performance. */
1564int
1565nc4_adjust_var_cache(NC_GRP_INFO_T *grp, NC_VAR_INFO_T * var)
1566{
1567   size_t chunk_size_bytes = 1;
1568   int d;
1569   int retval;
1570
1571   /* Nothing to be done. */
1572   if (var->contiguous)
1573      return NC_NOERR;
1574#ifdef USE_PARALLEL
1575   return NC_NOERR;
1576#endif
1577
1578   /* How many bytes in the chunk? */
1579   for (d = 0; d < var->ndims; d++)
1580      chunk_size_bytes *= var->chunksizes[d];
1581   if (var->type_info->size)
1582      chunk_size_bytes *= var->type_info->size;
1583   else
1584      chunk_size_bytes *= sizeof(char *);
1585
1586   /* If the chunk cache is too small, and the user has not changed
1587    * the default value of the chunk cache size, then increase the
1588    * size of the cache. */
1589   if (var->chunk_cache_size == CHUNK_CACHE_SIZE)
1590      if (chunk_size_bytes > var->chunk_cache_size)
1591      {
1592         var->chunk_cache_size = chunk_size_bytes * DEFAULT_CHUNKS_IN_CACHE;
1593         if (var->chunk_cache_size > MAX_DEFAULT_CACHE_SIZE)
1594            var->chunk_cache_size = MAX_DEFAULT_CACHE_SIZE;
1595         if ((retval = nc4_reopen_dataset(grp, var)))
1596            return retval;
1597      }
1598
1599   return NC_NOERR;
1600}
1601
1602/* Read or write an attribute. */
1603static int 
1604put_att_grpa(NC_GRP_INFO_T *grp, int varid, NC_ATT_INFO_T *att)
1605{
1606   hid_t datasetid = 0, locid;
1607   hid_t attid = 0, spaceid = 0, file_typeid = 0;
1608   hsize_t dims[1]; /* netcdf attributes always 1-D. */
1609   int retval = NC_NOERR;
1610   void *data;
1611   int phoney_data = 99;
1612
1613   assert(att->name);
1614   LOG((3, "put_att_grpa: varid %d att->attnum %d att->name %s "
1615        "att->xtype %d att->len %d", varid, att->attnum, att->name,
1616        att->xtype, att->len));
1617
1618   /* If the file is read-only, return an error. */
1619   if (grp->file->nc4_info->no_write)
1620      return NC_EPERM;
1621   
1622   /* Get the hid to attach the attribute to, or read it from. */
1623   if (varid == NC_GLOBAL)
1624      locid = grp->hdf_grpid;
1625   else 
1626   {
1627      if ((retval = nc4_open_var_grp2(grp, varid, &datasetid)))
1628         BAIL(retval);
1629      locid = datasetid;
1630   }
1631
1632   /* Delete the att if it exists already. */
1633   H5E_BEGIN_TRY {
1634      H5Adelete(locid, att->name);
1635   } H5E_END_TRY;
1636     
1637   /* Get the length ready, and find the HDF type we'll be
1638    * writing. */
1639   dims[0] = att->len;
1640   if ((retval = nc4_get_hdf_typeid(grp->file->nc4_info, att->xtype, 
1641                                    &file_typeid, 0)))
1642      BAIL(retval);
1643
1644   /* Even if the length is zero, HDF5 won't let me write with a
1645    * NULL pointer. So if the length of the att is zero, point to
1646    * some phoney data (which won't be written anyway.)*/
1647   if (!dims[0])
1648      data = &phoney_data;
1649   else if (att->data)
1650      data = att->data;
1651   else if (att->stdata)
1652      data = att->stdata;
1653   else 
1654      data = att->vldata;
1655
1656   /* NC_CHAR types require some extra work. The space ID is set to
1657    * scalar, and the type is told how long the string is. If it's
1658    * really zero length, set the size to 1. (The fact that it's
1659    * really zero will be marked by the NULL dataspace, but HDF5
1660    * doesn't allow me to set the size of the type to zero.)*/
1661   if (att->xtype == NC_CHAR)
1662   {
1663      size_t string_size = dims[0];
1664      if (!string_size)
1665      {
1666         string_size = 1;
1667         if ((spaceid = H5Screate(H5S_NULL)) < 0) 
1668            BAIL(NC_EATTMETA);
1669#ifdef EXTRA_TESTS
1670         num_spaces++;
1671#endif
1672      }
1673      else
1674      {
1675         if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1676            BAIL(NC_EATTMETA);
1677#ifdef EXTRA_TESTS
1678         num_spaces++;
1679#endif
1680      }
1681      if (H5Tset_size(file_typeid, string_size) < 0)
1682         BAIL(NC_EATTMETA);
1683      if (H5Tset_strpad(file_typeid, H5T_STR_NULLTERM) < 0)
1684         BAIL(NC_EATTMETA);
1685   }
1686   else
1687   {
1688      if (!att->len)
1689      {
1690         if ((spaceid = H5Screate(H5S_NULL)) < 0) 
1691            BAIL(NC_EATTMETA);
1692#ifdef EXTRA_TESTS
1693         num_spaces++;
1694#endif
1695      }
1696      else
1697      {
1698         if ((spaceid = H5Screate_simple(1, dims, NULL)) < 0)
1699            BAIL(NC_EATTMETA);
1700#ifdef EXTRA_TESTS
1701         num_spaces++;
1702#endif
1703      }
1704   }
1705   if ((attid = H5Acreate(locid, att->name, file_typeid, spaceid, 
1706                          H5P_DEFAULT)) < 0)
1707      BAIL(NC_EATTMETA);
1708
1709   /* Write the values, (even if length is zero). */
1710   if (H5Awrite(attid, file_typeid, data) < 0)
1711      BAIL(NC_EATTMETA);
1712
1713  exit:
1714   if (att->xtype == NC_CHAR || att->xtype == NC_STRING)
1715      if (file_typeid && H5Tclose(file_typeid))
1716         BAIL2(NC_EHDFERR);
1717   if (attid > 0 && H5Aclose(attid) < 0)
1718      BAIL2(NC_EHDFERR);
1719   if (spaceid > 0 && H5Sclose(spaceid) < 0)
1720      BAIL2(NC_EHDFERR);
1721#ifdef EXTRA_TESTS
1722   num_spaces--;
1723#endif
1724   return retval;
1725}
1726
1727/* Create a HDF5 defined type from a NC_TYPE_INFO_T struct, and commit
1728 * it to the file. */
1729static int
1730commit_type(NC_GRP_INFO_T *grp, NC_TYPE_INFO_T *type)
1731{
1732   NC_FIELD_INFO_T *field;
1733   NC_ENUM_MEMBER_INFO_T *enum_m;
1734   hid_t hdf_base_typeid, hdf_typeid;
1735   int retval;
1736
1737   assert(grp && type);
1738
1739   /* Did we already record this type? */
1740   if (type->committed) 
1741      return NC_NOERR;
1742   
1743   /* Is this a compound type? */
1744   if (type->class == NC_COMPOUND)
1745   {
1746      if ((type->hdf_typeid = H5Tcreate(H5T_COMPOUND, type->size)) < 0)
1747         return NC_EHDFERR;
1748      LOG((4, "creating compound type %s hdf_typeid 0x%x", type->name, 
1749           type->hdf_typeid));
1750      for (field = type->field; field; field = field->next)
1751      {
1752         if ((retval = nc4_get_hdf_typeid(grp->file->nc4_info, field->nctype, 
1753                                          &hdf_base_typeid, type->endianness)))
1754            return retval;
1755         /* If this is an array, create a special array type. */
1756         if (field->ndims)
1757         {
1758            int d;
1759            hsize_t dims[NC_MAX_DIMS];
1760            for (d = 0; d < field->ndims; d++)
1761               dims[d] = field->dim_size[d];
1762            if ((hdf_typeid = H5Tarray_create(hdf_base_typeid, field->ndims, 
1763                                              dims, NULL)) < 0)
1764               return NC_EHDFERR;
1765         } 
1766         else
1767            hdf_typeid = hdf_base_typeid;
1768         LOG((4, "inserting field %s offset %d hdf_typeid 0x%x", field->name, 
1769              field->offset, hdf_typeid));
1770         if (H5Tinsert(type->hdf_typeid, field->name, field->offset, 
1771                       hdf_typeid) < 0)
1772            return NC_EHDFERR;
1773         if (field->ndims && H5Tclose(hdf_typeid) < 0)
1774            return NC_EHDFERR;
1775      }
1776   } 
1777   else if (type->class == NC_VLEN)
1778   {
1779      /* Find the HDF typeid of the base type of this vlen. */
1780      if ((retval = nc4_get_hdf_typeid(grp->file->nc4_info, type->base_nc_type, 
1781                                       &type->base_hdf_typeid, type->endianness)))
1782         return retval;
1783
1784      /* Create a vlen type. */
1785      if ((type->hdf_typeid = H5Tvlen_create(type->base_hdf_typeid)) < 0)
1786         return NC_EHDFERR;
1787   } 
1788   else if (type->class == NC_OPAQUE)
1789   {
1790      /* Create the opaque type. */
1791      if ((type->hdf_typeid = H5Tcreate(H5T_OPAQUE, type->size)) < 0)
1792         return NC_EHDFERR;
1793   } 
1794   else if (type->class == NC_ENUM)
1795   {
1796      if (!type->enum_member)
1797         return NC_EINVAL;
1798
1799      /* Find the HDF typeid of the base type of this enum. */
1800      if ((retval = nc4_get_hdf_typeid(grp->file->nc4_info, type->base_nc_type, 
1801                                       &type->base_hdf_typeid, type->endianness)))
1802         return retval;
1803     
1804      /* Create an enum type. */
1805      if ((type->hdf_typeid =  H5Tenum_create(type->base_hdf_typeid)) < 0) 
1806         return NC_EHDFERR;
1807     
1808      /* Add all the members to the HDF5 type. */
1809      for (enum_m = type->enum_member; enum_m; enum_m = enum_m->next)   
1810         if (H5Tenum_insert(type->hdf_typeid, enum_m->name, 
1811                            enum_m->value) < 0) 
1812            return NC_EHDFERR;
1813   } 
1814   else
1815   {
1816      LOG((0, "Unknown class: %d", type->class));
1817      return NC_EBADTYPE;
1818   }
1819     
1820   /* Commit the type. */
1821   if (H5Tcommit(grp->hdf_grpid, type->name, type->hdf_typeid) < 0)
1822      return NC_EHDFERR;
1823   type->committed++;
1824   LOG((4, "just committed type %s, HDF typeid: 0x%x", type->name, 
1825        type->hdf_typeid));
1826   
1827   /* Later we will always use the native typeid. In this case, it is
1828    * a copy of the same type pointed to by hdf_typeid, but it's
1829    * easier to maintain a copy. */
1830   if ((type->native_typeid = H5Tget_native_type(type->hdf_typeid, 
1831                                                 H5T_DIR_DEFAULT)) < 0)
1832      return NC_EHDFERR;
1833
1834   return NC_NOERR;
1835}
1836
1837/* Write an attribute, with value 1, to indicate that strict NC3 rules
1838 * apply to this file. */
1839static int
1840write_nc3_strict_att(hid_t hdf_grpid)
1841{
1842   hid_t attid = 0, spaceid = 0;
1843   int one = 1, num, a;
1844   char att_name[NC_MAX_NAME + 1];
1845   int retval = NC_NOERR;
1846
1847   /* If the attribute already exists, call that a success and return
1848    * NC_NOERR. */
1849   if ((num = H5Aget_num_attrs(hdf_grpid)) < 0)
1850      return NC_EHDFERR;
1851   for (a = 0; a < num; a++) 
1852   {
1853      if ((attid = H5Aopen_idx(hdf_grpid, (unsigned int)a)) < 0)
1854         BAIL(NC_EHDFERR);
1855      if (H5Aget_name(attid, NC_MAX_HDF5_NAME, att_name) < 0)
1856         BAIL(NC_EHDFERR);
1857      if (!strcmp(att_name, NC3_STRICT_ATT_NAME))
1858      {
1859         if (H5Aclose(attid) < 0)
1860            return NC_EFILEMETA;
1861         return NC_NOERR;
1862      }
1863   }
1864
1865   /* Create the attribute to mark this as a file that needs to obey
1866    * strict netcdf-3 rules. */
1867   if ((spaceid = H5Screate(H5S_SCALAR)) < 0)
1868      BAIL(NC_EFILEMETA);
1869#ifdef EXTRA_TESTS
1870      num_spaces++;
1871#endif
1872   if ((attid = H5Acreate(hdf_grpid, NC3_STRICT_ATT_NAME, 
1873                          H5T_NATIVE_INT, spaceid, H5P_DEFAULT)) < 0)
1874      BAIL(NC_EFILEMETA);
1875   if (H5Awrite(attid, H5T_NATIVE_INT, &one) < 0)
1876      BAIL(NC_EFILEMETA);
1877
1878  exit:
1879   if (spaceid && (H5Sclose(spaceid) < 0))
1880      BAIL2(NC_EFILEMETA);
1881#ifdef EXTRA_TESTS
1882   num_spaces--;
1883#endif
1884   if (attid && (H5Aclose(attid) < 0))
1885      BAIL2(NC_EFILEMETA);
1886   return retval;
1887}
1888
1889static int
1890create_group(NC_GRP_INFO_T *grp)
1891{
1892   hid_t gcpl_id = 0;
1893   int retval = NC_NOERR;;
1894
1895   assert(grp);
1896
1897   /* If this is not the root group, create it in the HDF5 file. */
1898   if (grp->parent)
1899   {
1900      /* Create group, with link_creation_order set in the group
1901       * creation property list. */
1902      if ((gcpl_id = H5Pcreate(H5P_GROUP_CREATE)) < 0)
1903         return NC_EHDFERR;
1904#ifdef EXTRA_TESTS
1905      num_plists++;
1906#endif
1907      if (H5Pset_link_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1908         BAIL(NC_EHDFERR);
1909      if (H5Pset_attr_creation_order(gcpl_id, H5P_CRT_ORDER_TRACKED|H5P_CRT_ORDER_INDEXED) < 0)
1910         BAIL(NC_EHDFERR);
1911      if ((grp->hdf_grpid = H5Gcreate2(grp->parent->hdf_grpid, grp->name, 
1912                                       H5P_DEFAULT, gcpl_id, H5P_DEFAULT)) < 0)
1913         BAIL(NC_EHDFERR);
1914      if (H5Pclose(gcpl_id) < 0)
1915         BAIL(NC_EHDFERR);
1916#ifdef EXTRA_TESTS
1917      num_plists--;
1918#endif
1919   }
1920   else
1921   {
1922      /* Since this is the root group, we have to open it. */
1923      if ((grp->hdf_grpid = H5Gopen2(grp->file->nc4_info->hdfid, "/", H5P_DEFAULT)) < 0)
1924         BAIL(NC_EFILEMETA);
1925   }
1926   return NC_NOERR;
1927
1928  exit:
1929   if (gcpl_id > 0 && H5Pclose(gcpl_id) < 0)
1930      BAIL2(NC_EHDFERR);
1931#ifdef EXTRA_TESTS
1932      num_plists--;
1933#endif
1934   if (grp->hdf_grpid > 0 && H5Gclose(grp->hdf_grpid) < 0)
1935      BAIL2(NC_EHDFERR);
1936   return retval;
1937}
1938
1939/* After all the datasets of the file have been read, it's time to
1940 * sort the wheat from the chaff. Which of the datasets are netCDF
1941 * dimensions, and which are coordinate variables, and which are
1942 * non-coordinate variables. */
1943static int
1944attach_dimscales(NC_GRP_INFO_T *grp)
1945{
1946   NC_VAR_INFO_T *var;
1947   NC_DIM_INFO_T *dim1;
1948   NC_GRP_INFO_T *g;   
1949   int d;
1950   int retval = NC_NOERR;
1951
1952   /* Attach dimension scales. */
1953   for (var = grp->var; var; var = var->next)
1954   {
1955      /* Scales themselves do not attach. But I really wish they
1956       * would. */
1957      if (var->dimscale) 
1958      {
1959         /* If this is a multidimensional coordinate variable, it will
1960          * have a special coords attribute (read earlier) with a list
1961          * of the dimensions for this variable. */
1962      }
1963      else /* not a dimscale... */ 
1964      {
1965         /* Find the scale for each dimension and attach it. */
1966         for (d = 0; d < var->ndims; d++)
1967         {
1968            /* Is there a dimscale for this dimension? */
1969            if (var->dimscale_attached) 
1970            {
1971               if (!var->dimscale_attached[d])
1972               {
1973                  for (g = grp; g && !var->dimscale_attached[d]; g = g->parent)
1974                     for (dim1 = g->dim; dim1; dim1 = dim1->next)
1975                        if (var->dimids[d] == dim1->dimid)
1976                        {
1977                           LOG((2, "attach_dimscales: attaching scale for dimid %d to var %s", 
1978                                var->dimids[d], var->name));
1979                           if (H5DSattach_scale(var->hdf_datasetid, dim1->hdf_dimscaleid, d) < 0)
1980                              BAIL(NC_EHDFERR);
1981                           var->dimscale_attached[d]++;
1982                           break;
1983                        }
1984               }
1985           
1986               /* If we didn't find a dimscale to attach, that's a problem! */
1987               if (!var->dimscale_attached[d])
1988               {
1989                  LOG((0, "no dimscale found!"));
1990                  return NC_EDIMSCALE;
1991               }
1992            }
1993            else
1994            {
1995               /* Create a phoney dimension! */
1996               
1997            }
1998         }
1999      } /* next d */
2000   }
2001  exit:
2002   return retval;
2003}
2004
2005/* Write all the dirty atts in an attlist. */
2006static int
2007write_attlist(NC_ATT_INFO_T *attlist, int varid, NC_GRP_INFO_T *grp)
2008{
2009   NC_ATT_INFO_T *att;
2010   int retval;
2011
2012   for (att = attlist; att; att = att->next)
2013   {
2014      if (att->dirty)
2015      {
2016         LOG((4, "write_attlist: writing att %s to varid %d", att->name, varid));
2017         if ((retval = put_att_grpa(grp, varid, att)))
2018            return retval;
2019         att->dirty = 0;
2020         att->created++;
2021      }
2022   }
2023   return NC_NOERR;
2024}
2025
2026/* Using the HDF5 group iterator is more efficient than the original
2027 * code (O(n) vs O(n**2) for n variables in the group) */
2028#define USE_ITERATE_CODE
2029#ifdef  USE_ITERATE_CODE
2030typedef struct {
2031    char *name;          /* The name of the object to searched*/
2032    int  *exists;         /* 1 if the object exists, 0 otherswise */
2033} var_exists_iter_info;
2034
2035/*-------------------------------------------------------------------------
2036 * Function:    var_exists_cb
2037 *
2038 * Purpose:     Callback routine for checking an object by its name
2039 *
2040 * Return:      Exist:      1
2041 *              Not exist:  0
2042 *              Failure:   -1
2043 *
2044 * Programmer:  Peter Cao
2045 *              1/25/2012
2046 *
2047 *-------------------------------------------------------------------------
2048 */
2049static int
2050var_exists_cb(hid_t grpid, const char *name, const H5L_info_t *info,
2051    void *_op_data)
2052{
2053    var_exists_iter_info *iter_info = (var_exists_iter_info *)_op_data;
2054    H5I_type_t otype;
2055        hid_t oid;
2056
2057    if ((oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0) 
2058        return H5_ITER_STOP;
2059         
2060    if ((otype = H5Iget_type( oid ))<0) {
2061                H5Oclose(oid);
2062                return H5_ITER_STOP;
2063        }
2064    H5Oclose(oid);
2065       
2066    if (otype == H5I_DATASET) {
2067                if (!strcmp(iter_info->name, name)) {
2068                        *(iter_info->exists) = 1;
2069                        return (H5_ITER_STOP);
2070                }
2071        }
2072
2073    return (H5_ITER_CONT);
2074} /* end var_exists_cb() */
2075
2076static int
2077var_exists(hid_t grpid, char *name, int *exists)
2078{
2079    hsize_t num_obj;
2080    var_exists_iter_info iter_info;
2081    iter_info.name = name;
2082    iter_info.exists = exists;
2083
2084        if (H5Gget_num_objs(grpid, &num_obj) < 0)
2085      return NC_EVARMETA;
2086
2087    if (!name)
2088       return NC_NOERR;
2089           
2090    *exists = 0;
2091    if (H5Literate(grpid, H5_INDEX_CRT_ORDER, H5_ITER_INC, NULL, var_exists_cb, &iter_info) < 0)
2092       return NC_EHDFERR;
2093
2094    return NC_NOERR;
2095}
2096#else
2097static int
2098var_exists(hid_t grpid, char *name, int *exists)
2099{
2100   hsize_t num_obj, i;
2101   H5O_info_t obj_info;
2102   int obj_class;
2103   char obj_name[NC_MAX_NAME + 1];
2104   ssize_t size;
2105   int retval = NC_NOERR;
2106   
2107   *exists = 0;
2108   if (H5Gget_num_objs(grpid, &num_obj) < 0)
2109      return NC_EVARMETA;
2110   for (i = 0; i < num_obj; i++)
2111   {
2112      if (H5Oget_info_by_idx(grpid, ".", H5_INDEX_CRT_ORDER, H5_ITER_INC, i, &obj_info, H5P_DEFAULT) < 0) 
2113         return NC_EHDFERR;
2114      obj_class = obj_info.type;
2115      if ((size = H5Lget_name_by_idx(grpid, ".", H5_INDEX_CRT_ORDER, H5_ITER_INC, i, NULL, 0, H5P_DEFAULT)) < 0) 
2116         return NC_EHDFERR;
2117      if (size > NC_MAX_NAME)
2118         return NC_EMAXNAME;
2119      if (H5Lget_name_by_idx(grpid, ".", H5_INDEX_CRT_ORDER, H5_ITER_INC, i, obj_name, size+1, H5P_DEFAULT) < 0) 
2120         return NC_EHDFERR;
2121      if (obj_class == H5O_TYPE_DATASET)
2122      {
2123         if (!strncmp(name, obj_name, NC_MAX_NAME))
2124         {
2125            *exists = 1;
2126            return NC_NOERR;
2127         }
2128      }
2129   }
2130
2131   return retval;
2132}
2133#endif  /* USE_ITERATE_CODE */
2134
2135/* This function writes a variable. The principle difficulty comes
2136 * from the possibility that this is a coordinate variable, and was
2137 * already written to the file as a dimension-only dimscale. If this
2138 * occurs, then it must be deleted and recreated. */
2139static int
2140write_var(NC_VAR_INFO_T *var, NC_GRP_INFO_T *grp, int write_dimid)
2141{
2142   NC_DIM_INFO_T *d1 = NULL;
2143   int replace_existing_var = 0;
2144   int exists;
2145   int retval;
2146
2147   if (!var->dirty)
2148   {
2149      if (write_dimid && var->ndims)
2150         if ((retval = write_netcdf4_dimid(var->hdf_datasetid, 
2151                                           var->dimids[0])))
2152            BAIL(retval);
2153   }
2154   else
2155   {
2156      LOG((4, "write_var: writing var %s", var->name));
2157
2158      if (var->created)
2159         replace_existing_var = 1;
2160
2161      /* If this is a coordinate var, and a dataset has already
2162       * been created for it, then delete that dataset and recreate
2163       * it (because its type may be wrong anyway.) But then we
2164       * have to reattach dimension scales for all vars! Oh well,
2165       * this all only happens when the user defines a var, writes
2166       * metadata, reenters define mode, and adds a coordinate
2167       * var. Presumably this will happen rarely. */
2168
2169      /* Is this a coordinate var that has already been created in
2170       * the HDF5 as a dimscale dataset? Check for dims with the
2171       * same name in this group. If there is one, check to see if
2172       * this object exists in the HDF group. */
2173      if (var->dimscale)
2174         for (d1 = grp->dim; d1; d1 = d1->next)
2175            if (!strcmp(d1->name, var->name))
2176            {
2177               if ((retval = var_exists(grp->hdf_grpid, var->name, &exists)))
2178                  return retval;
2179               if (exists)
2180               {
2181                  replace_existing_var++;
2182
2183                  /* If we're replacing an existing dimscale dataset, go to
2184                   * every var in the file and detatch this dimension scale,
2185                   * because we have to delete it. */
2186                  if ((retval = rec_detach_scales(grp->file->nc4_info->root_grp, 
2187                                                  var->dimids[0], d1->hdf_dimscaleid)))
2188                     return retval;
2189                  break;
2190               }
2191            }
2192         
2193      /* Delete the HDF5 dataset that is to be replaced. */
2194      if (replace_existing_var)
2195      {
2196         /* If this is a dimension scale, do this stuff. */
2197         if (d1)
2198         {
2199            assert(d1 && d1->hdf_dimscaleid);
2200            if (H5Dclose(d1->hdf_dimscaleid) < 0) 
2201               return NC_EDIMMETA;
2202         }
2203         else
2204         {
2205            int dims_detached = 0;
2206            int finished = 0;
2207            int d;
2208            NC_DIM_INFO_T *dim1;
2209            NC_GRP_INFO_T *g;
2210
2211            /* If this is a regular var, detach all its dim scales. */
2212            for (d = 0; d < var->ndims; d++)
2213               for (g = grp; g && !finished; g = g->parent)
2214                  for (dim1 = g->dim; dim1; dim1 = dim1->next)
2215                     if (var->dimids[d] == dim1->dimid)
2216                     {
2217                        if (H5DSdetach_scale(var->hdf_datasetid, dim1->hdf_dimscaleid, d) < 0)
2218                           BAIL(NC_EHDFERR);
2219                        var->dimscale_attached[d] = 0;
2220                        if (dims_detached++ == var->ndims)
2221                           finished++;
2222                     }
2223         }
2224
2225         /* Free the HDF5 dataset id. */
2226         if (var->hdf_datasetid && H5Dclose(var->hdf_datasetid)) 
2227            BAIL(NC_EHDFERR);
2228               
2229         /* Now delete the variable. */
2230         if (H5Gunlink(grp->hdf_grpid, var->name) < 0)
2231            return NC_EDIMMETA;
2232      }
2233
2234      /* Create the dataset. */
2235      if ((retval = var_create_dataset(grp, var, write_dimid)))
2236         return retval;
2237         
2238      /* Reattach this scale everywhere it is used. (Recall that
2239       * netCDF dimscales are always 1-D). */
2240      if (d1 && replace_existing_var)
2241      {
2242         d1->hdf_dimscaleid = var->hdf_datasetid;
2243         if ((retval = rec_reattach_scales(grp->file->nc4_info->root_grp, 
2244                                           var->dimids[0], d1->hdf_dimscaleid)))
2245            return retval;
2246      }
2247   }
2248         
2249   /* Now check the atributes for this var. */
2250   /* Write attributes for this var. */
2251   if ((retval = write_attlist(var->att, var->varid, grp)))
2252      BAIL(retval);
2253   
2254   return NC_NOERR;
2255  exit:
2256   return retval;
2257}
2258
2259static int
2260write_dim(NC_DIM_INFO_T *dim, NC_GRP_INFO_T *grp, int write_dimid)
2261{
2262   hid_t spaceid, create_propid;
2263   hsize_t dims[1], max_dims[1], chunk_dims[1] = {1}; 
2264   int dimscale_exists = 0;
2265   char dimscale_wo_var[NC_MAX_NAME];
2266   int retval;
2267
2268   if (dim->dirty)
2269   {
2270      /* If there's no dimscale dataset for this dim, create one,
2271       * and mark that it should be hidden from netCDF as a
2272       * variable. (That is, it should appear as a dimension
2273       * without an associated variable.) */
2274      if (!dimscale_exists)
2275      {
2276         LOG((4, "write_dim: creating dim %s", dim->name));
2277
2278         /* Create a property list. If this dimension scale is
2279          * unlimited (i.e. it's an unlimited dimension), then set
2280          * up chunking, with a chunksize of 1. */
2281         if ((create_propid = H5Pcreate(H5P_DATASET_CREATE)) < 0)
2282            BAIL(NC_EHDFERR);
2283#ifdef EXTRA_TESTS
2284         num_plists++;
2285#endif
2286         dims[0] = dim->len;
2287         max_dims[0] = dim->len;
2288         if (dim->unlimited) 
2289         {
2290            max_dims[0] = H5S_UNLIMITED;
2291            if (H5Pset_chunk(create_propid, 1, chunk_dims) < 0)
2292               BAIL(NC_EHDFERR);
2293         }
2294
2295         /* Set up space. */
2296         if ((spaceid = H5Screate_simple(1, dims, max_dims)) < 0) 
2297            BAIL(NC_EHDFERR);
2298#ifdef EXTRA_TESTS
2299         num_spaces++;
2300#endif
2301
2302         /* If we define, and then rename this dimension before
2303          * creation of the dimscale dataset, then we can throw
2304          * away the old_name of the dimension. */
2305         if (dim->old_name && strlen(dim->old_name))
2306            strcpy(dim->old_name, "");
2307
2308         if (H5Pset_attr_creation_order(create_propid, H5P_CRT_ORDER_TRACKED|
2309                                        H5P_CRT_ORDER_INDEXED) < 0)
2310            BAIL(NC_EHDFERR);
2311
2312         /* Create the dataset that will be the dimension scale. */
2313         LOG((4, "write_dim: about to H5Dcreate a dimscale dataset %s", dim->name));
2314         if ((dim->hdf_dimscaleid = H5Dcreate1(grp->hdf_grpid, dim->name, H5T_IEEE_F32BE, 
2315                                               spaceid, create_propid)) < 0)
2316            BAIL(NC_EHDFERR);
2317
2318         /* Close the spaceid and create_propid. */
2319         if (H5Sclose(spaceid) < 0)
2320            BAIL(NC_EHDFERR);
2321#ifdef EXTRA_TESTS
2322         num_spaces--;
2323#endif
2324         if (H5Pclose(create_propid) < 0)
2325            BAIL(NC_EHDFERR);
2326#ifdef EXTRA_TESTS
2327         num_plists--;
2328#endif
2329
2330         /* Indicate that this is a scale. Also indicate that not
2331          * be shown to the user as a variable. It is hidden. It is
2332          * a DIM WITHOUT A VARIABLE! */
2333         sprintf(dimscale_wo_var, "%s%10d", DIM_WITHOUT_VARIABLE, (int)dim->len);
2334         if (H5DSset_scale(dim->hdf_dimscaleid, dimscale_wo_var) < 0)
2335            BAIL(NC_EHDFERR);
2336
2337      }
2338      dim->dirty = 0;
2339   }
2340   
2341   /* Did we extend an unlimited dimension? */
2342   if (dim->extended)
2343   {
2344      NC_VAR_INFO_T *v1;
2345
2346      assert(dim->unlimited);
2347      /* If this is a dimension without a variable, then update
2348       * the secret length information at the end of the NAME
2349       * attribute. */
2350      for (v1 = grp->var; v1; v1 = v1->next)
2351         if (!strcmp(v1->name, dim->name))
2352            break;
2353         
2354      if (v1)
2355      {
2356         hsize_t *new_size;
2357         NC_GRP_INFO_T *g;
2358         NC_DIM_INFO_T *dim1;
2359         int d1;
2360
2361         /* Extend the dimension scale dataset to reflect the new
2362          * length of the dimension. */
2363         if (!(new_size = malloc(v1->ndims * sizeof(hsize_t))))
2364            BAIL(NC_ENOMEM);
2365         for (d1 = 0; d1 < v1->ndims; d1++)
2366         {
2367            if (v1->dimids[d1] == dim->dimid)
2368               new_size[d1] = dim->len;
2369            else
2370            {
2371               int break_it = 0;
2372               for (g = grp; g && !break_it; g = g->parent)
2373                  for (dim1 = g->dim; dim1; dim1 = dim1->next)
2374                     if (dim1->dimid == v1->dimids[d1])
2375                     {
2376                        new_size[d1] = dim1->len;
2377                        break_it++;
2378                        break;
2379                     }
2380            }
2381         }
2382         if (H5Dextend(v1->hdf_datasetid, new_size) < 0)
2383            BAIL(NC_EHDFERR);
2384         free(new_size);
2385      }
2386   }
2387
2388   /* Did we rename this dimension? */
2389   if (dim->old_name && strlen(dim->old_name))
2390   {
2391      /* Rename the dimension's dataset in the HDF5 file. */
2392      if (H5Gmove2(grp->hdf_grpid, dim->old_name, grp->hdf_grpid, dim->name) < 0)
2393         return NC_EHDFERR;
2394         
2395      /* Reset old_name. */
2396      strcpy(dim->old_name, "");
2397   }
2398
2399   /* If desired, write the secret dimid. This will be used instead of
2400    * the dimid that the dimension would otherwise receive based on
2401    * creation order. This can be necessary when dims and their
2402    * coordinate variables were created in different order. */
2403   if (write_dimid && dim->hdf_dimscaleid)
2404      if ((retval = write_netcdf4_dimid(dim->hdf_dimscaleid, 
2405                                        dim->dimid)))
2406         BAIL(retval);
2407
2408   return NC_NOERR;
2409  exit:
2410   return retval;
2411}
2412
2413/* Recursively write all the metadata in a group. Groups and types
2414 * have all already been written. */
2415int
2416nc4_rec_write_metadata(NC_GRP_INFO_T *grp)
2417{
2418   NC_DIM_INFO_T *dim;
2419   NC_VAR_INFO_T *var;
2420   NC_GRP_INFO_T *child_grp;
2421   int found_coord, coord_varid = -1, wrote_coord;
2422   int bad_coord_order = 0;
2423   int last_dimid = -1;
2424   int retval;
2425
2426   assert(grp && grp->name && grp->hdf_grpid);
2427   LOG((3, "nc4_rec_write_metadata: grp->name %s", grp->name));
2428
2429   /* Write global attributes for this group. */
2430   if ((retval = write_attlist(grp->att, NC_GLOBAL, grp)))
2431      return retval;
2432
2433   /* If the user writes coord vars in a different order then he
2434    * defined their dimensions, then, when the file is reopened, the
2435    * order of the dimids will change to match the order of the coord
2436    * vars. Detect if this is about to happen. */
2437   for (var = grp->var; var; var = var->next)
2438   {
2439      LOG((5, "checking %s for out of order coord var", var->name));
2440      if (var->ndims && var->dimscale)
2441      {
2442         if (var->dimids[0] < last_dimid)
2443         {
2444            bad_coord_order++;
2445            break;
2446         }
2447         last_dimid = var->dimids[0];
2448      }
2449   }
2450
2451   /* Did the user define a dimension, end define mode, reenter define
2452    * mode, and then define a coordinate variable for that dimension?
2453    * If so, dimensions will be out of order. */
2454   for (var = grp->var; var; var = var->next)
2455      if (var->dirty && !var->created && var->ndims)
2456         for (dim = grp->dim; dim && dim->next; dim = dim->next)
2457            if (strcmp(dim->name, var->name) && !dim->dirty)
2458            {
2459               LOG((5, "coord var defined after enddef/redef"));
2460               bad_coord_order++;
2461            }
2462   
2463     
2464   /* For some stupid reason, the dim list is stored backwards! Get to
2465    * the back of the list. */
2466   for (dim = grp->dim; dim && dim->next; dim = dim->next)
2467      ;
2468
2469   /* Set the pointer to the beginning of the list of vars in this
2470    * group. */
2471   var = grp->var;
2472
2473   /* Because of HDF5 ordering the dims and vars have to be stored in
2474    * this way to ensure that the dims and coordinate vars come out in
2475    * the correct order. */
2476   while (dim || var)
2477   {
2478      /* Write non-coord dims in order, stopping at the first one that
2479       * has an associated coord var. */
2480      for (found_coord = 0; dim && !found_coord; dim = dim->prev)
2481      {
2482         if (!dim->coord_var_in_grp)
2483         {
2484            if ((retval = write_dim(dim, grp, bad_coord_order)))
2485               return retval;
2486         }
2487         else
2488         {
2489            found_coord++;
2490            coord_varid = dim->coord_var->varid;
2491         }
2492      }
2493
2494      /* Write each var. When we get to the coord var we are waiting
2495       * for (if any), then we break after writing it. */
2496      for (wrote_coord = 0; var && !wrote_coord; var = var->next)
2497      {
2498         if ((retval = write_var(var, grp, bad_coord_order)))
2499            return retval;
2500         if (found_coord && var->varid == coord_varid)
2501            wrote_coord++;
2502      }
2503   } /* end while */
2504
2505   if ((retval = attach_dimscales(grp)))
2506      return retval;
2507   
2508   /* If there are any child groups, write their metadata. */
2509   for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
2510      if ((retval = nc4_rec_write_metadata(child_grp)))
2511         return retval;
2512     
2513   return NC_NOERR;
2514}
2515
2516/* Recursively write all groups and types. */
2517int
2518nc4_rec_write_types(NC_GRP_INFO_T *grp)
2519{
2520   NC_GRP_INFO_T *child_grp;
2521   NC_TYPE_INFO_T *type;
2522   int retval;
2523
2524   assert(grp && grp->name);
2525   LOG((3, "nc4_rec_write_types: grp->name %s", grp->name));
2526
2527   /* Create the group in the HDF5 file if it doesn't exist. */
2528   if (!grp->hdf_grpid)
2529      if ((retval = create_group(grp)))
2530         return retval;
2531
2532   /* If this is the root group of a file with strict NC3 rules, write
2533    * an attribute. But don't leave the attribute open. */
2534   if (!grp->parent && (grp->file->nc4_info->cmode & NC_CLASSIC_MODEL))
2535      if ((retval = write_nc3_strict_att(grp->hdf_grpid)))
2536         return retval;
2537
2538   /* If there are any user-defined types, write them now. */
2539   for (type = grp->type; type; type = type->next)
2540      if ((retval = commit_type(grp, type)))
2541         return retval;
2542
2543   /* If there are any child groups, write their groups and types. */
2544   for (child_grp = grp->children; child_grp; child_grp = child_grp->next)
2545      if ((retval = nc4_rec_write_types(child_grp)))
2546         return retval;
2547     
2548   return NC_NOERR;
2549}
2550
2551/* This reads/writes a whole var at a time. If the file has an
2552   unlimited dimension, then we will look at the number of records
2553   currently existing for this var, and read/write that many. This
2554   this is not what the user intended, particularly with writing, then
2555   that is there look-out! So we will not be extending datasets
2556   here. */
2557int
2558pg_var(NC_PG_T pg, NC_FILE_INFO_T *nc, int ncid, int varid, nc_type xtype, 
2559       int is_long, void *ip)
2560{
2561   NC_GRP_INFO_T *grp;
2562   NC_VAR_INFO_T *var;
2563   size_t start[NC_MAX_VAR_DIMS], count[NC_MAX_VAR_DIMS];
2564   int i;
2565   int retval;
2566
2567   assert(nc);
2568   if ((retval = nc4_find_g_var_nc(nc, ncid, varid, &grp, &var)))
2569      return retval;
2570   assert(grp && var && var->name);
2571
2572   /* For each dimension, the start will be 0, and the count will be
2573    * the length of the dimension. */
2574   for (i = 0; i < var->ndims; i++)
2575   {
2576      start[i] = 0;
2577      if ((retval = nc_inq_dimlen(ncid, var->dimids[i], &(count[i]))))
2578         return retval;
2579   }
2580
2581   /* Read or write the data. */
2582   if (pg == GET)
2583      return nc4_get_vara(nc, ncid, varid, start, count, xtype, 
2584                          is_long, ip);
2585   else
2586      return nc4_put_vara(nc, ncid, varid, start, count, xtype, 
2587                          is_long, ip);
2588}
2589
2590/* Write or read some mapped data. Yea, like I even understand what it
2591   is! 
2592
2593   I stole this code, lock, stock, and semicolons, from the netcdf
2594   3.5.1 beta release. It walks through the stride and map arrays, and
2595   converts them to a series of calles to the varm function.
2596
2597   I had to modify the code a little to fit it in, and generalize it
2598   for all data types, and for both puts and gets.
2599
2600   Ed Hartnett, 9/43/03
2601*/
2602int 
2603nc4_pg_varm(NC_PG_T pg, NC_FILE_INFO_T *nc, int ncid, int varid, const size_t *start, 
2604            const size_t *edges, const ptrdiff_t *stride,
2605            const ptrdiff_t *map, nc_type xtype, int is_long, void *data)
2606{
2607   NC_GRP_INFO_T *grp;
2608   NC_HDF5_FILE_INFO_T *h5;
2609   NC_VAR_INFO_T *var;
2610   int maxidim;    /* maximum dimensional index */
2611   size_t mem_type_size;
2612   int convert_map = 0;
2613   ptrdiff_t new_map[NC_MAX_VAR_DIMS];
2614   int i;
2615   int retval = NC_NOERR;
2616
2617   LOG((3, "nc4_pg_varm: ncid 0x%x varid %d xtype %d", ncid, varid, 
2618        xtype));
2619
2620   /* Find metadata for this file and var. */
2621   assert(nc && nc->nc4_info);
2622   h5 = nc->nc4_info;
2623   if ((retval = nc4_find_g_var_nc(nc, ncid, varid, &grp, &var)))
2624      return retval;
2625   assert(grp && var && var->name);
2626
2627   /* If mem_nc_type is NC_NAT, it means we were called by
2628    * nc_get|put_varm, the old V2 API call! In this case we want to
2629    * use the file type as the mem type as well. Also, for these two
2630    * functions only, we interpret the map array as referring to
2631    * numbers of bytes rather than number of elements. (This is
2632    * something that changed between V2 and V3.) Also we do not allow
2633    * mapped access to user-defined vars in nc4. */
2634   if (xtype == NC_NAT)
2635   {
2636      if (var->xtype > NC_STRING)
2637         return NC_EMAPTYPE;
2638      xtype = var->xtype;
2639      convert_map++;
2640   }
2641   assert(xtype);
2642
2643   /* What is the size of this type? */
2644   if ((retval = nc4_get_typelen_mem(h5, xtype, is_long, &mem_type_size)))
2645      return retval;
2646
2647   if(map != NULL && var->ndims && convert_map)
2648   {
2649      /* convert map units from bytes to units of sizeof(type) */
2650      for(i = 0; i < var->ndims; i++)
2651      {
2652         if(map[i] % mem_type_size != 0)       
2653            return NC_EINVAL;
2654         new_map[i] = map[i] / mem_type_size;
2655      }
2656      map = new_map;
2657   }
2658
2659   /* No text to number hanky-panky is allowed for those observing
2660    * strict netcdf-3 rules! It's sick. */
2661   if ((h5->cmode & NC_CLASSIC_MODEL) && (xtype == NC_CHAR || var->xtype == NC_CHAR) &&
2662       (xtype != var->xtype))
2663      return NC_ECHAR;
2664
2665   /* If the file is read-only, return an error. */
2666   if (pg == PUT && h5->no_write)
2667      return NC_EPERM;
2668
2669   /* If we're in define mode, we can't read or write data. If strict
2670    * nc3 rules are in effect, return an error, otherwise leave define
2671    * mode. */
2672   if (h5->flags & NC_INDEF)
2673   {
2674      if (h5->cmode & NC_CLASSIC_MODEL)
2675         return NC_EINDEFINE;
2676      if ((retval = nc_enddef(ncid)))
2677         BAIL(retval);
2678   }
2679
2680#ifdef LOGGING
2681   {
2682      int i;
2683      if (start)
2684         for (i=0; i<var->ndims; i++)
2685            LOG((4, "start[%d] %d", i, start[i]));
2686      if (edges)
2687         for (i=0; i<var->ndims; i++)
2688            LOG((4, "edges[%d] %d", i, edges[i]));
2689      if (stride)
2690         for (i=0; i<var->ndims; i++)
2691            LOG((4, "stride[%d] %d", i, stride[i]));
2692      if (map)
2693         for (i=0; i<var->ndims; i++)
2694            LOG((4, "map[%d] %d", i, map[i]));
2695   }
2696#endif /* LOGGING */
2697
2698   /* The code below was stolen from netcdf-3. Some comments by Ed. */
2699   maxidim = (int) var->ndims - 1;
2700   if (maxidim < 0)
2701   {
2702      /* The variable is a scalar; consequently, there is only one
2703         thing to get and only one place to put it.  (Why was I
2704         called?) */
2705      return pg_var(pg, nc, ncid, varid, xtype, is_long, data);
2706   }
2707       
2708   /* The variable is an array.  */
2709   {
2710      int idim;
2711      size_t *mystart = NULL;
2712      size_t *myedges;
2713      size_t *iocount;        /* count vector */
2714      size_t *stop;   /* stop indexes */
2715      size_t *length; /* edge lengths in bytes */
2716      ptrdiff_t *mystride;
2717      ptrdiff_t *mymap;
2718
2719      /* Verify stride argument. */
2720      for (idim = 0; idim <= maxidim; ++idim)
2721      {
2722         if (stride != NULL
2723             && (stride[idim] == 0
2724                 /* cast needed for braindead systems with signed size_t */
2725                 || (unsigned long) stride[idim] >= X_INT_MAX))
2726         {
2727            return NC_ESTRIDE;
2728         }
2729      }
2730
2731      /* The mystart array of pointer info is needed to walk our way
2732         through the dimensions as specified by the start, edges,
2733         stride and (gulp!) map parameters. */
2734      if (!(mystart = (size_t *)calloc((size_t)var->ndims * 7, sizeof(ptrdiff_t))))
2735         return NC_ENOMEM;
2736      myedges = mystart + var->ndims;
2737      iocount = myedges + var->ndims;
2738      stop = iocount + var->ndims;
2739      length = stop + var->ndims;
2740      mystride = (ptrdiff_t *)(length + var->ndims);
2741      mymap = mystride + var->ndims;
2742
2743      /* Initialize I/O parameters. */
2744      for (idim = maxidim; idim >= 0; --idim)
2745      {
2746         /* Get start value, use 0 if non provided. */
2747         mystart[idim] = start != NULL ? start[idim] : 0;
2748
2749         /* If any edges are 0, return NC_NOERR and forget it. */
2750         if (edges[idim] == 0)
2751         {
2752            retval = NC_NOERR;
2753            goto done;
2754         }
2755
2756         /* If edges not provided, use the current dimlen. */
2757         if (edges)
2758            myedges[idim] = edges[idim];
2759         else 
2760         {
2761            size_t len;
2762            if ((retval = nc_inq_dimlen(ncid, var->dimids[idim], &len)))
2763               goto done;
2764            myedges[idim] = len - mystart[idim];
2765         }
2766         
2767         /* If stride not provided, use 1. */
2768         mystride[idim] = stride != NULL ? stride[idim] : 1;
2769
2770         /* If map is not provided, do something dark and
2771            mysterious. */
2772         if (map)
2773            mymap[idim] = map[idim];
2774         else
2775            mymap[idim] = idim == maxidim ? 1 : 
2776               mymap[idim + 1] * (ptrdiff_t) myedges[idim + 1];
2777
2778         iocount[idim] = 1;
2779         length[idim] = mymap[idim] * myedges[idim];
2780         stop[idim] = mystart[idim] + myedges[idim] * mystride[idim];
2781      }
2782
2783      /* Check start, edges */
2784      for (idim = maxidim; idim >= 0; --idim)
2785      {
2786         size_t dimlen;
2787         if ((retval = nc_inq_dimlen(ncid, var->dimids[idim], &dimlen)))
2788            goto done;
2789         /* Don't check unlimited dimension on PUTs. */
2790         if (pg == PUT)
2791         {
2792            int stop = 0, d, num_unlim_dim, unlim_dimids[NC_MAX_DIMS];
2793            if ((retval = nc_inq_unlimdims(ncid, &num_unlim_dim, unlim_dimids)))
2794               goto done;
2795            for (d = 0; d < num_unlim_dim; d++)
2796               if (var->dimids[idim] == unlim_dimids[d])
2797                  stop++;
2798            if (stop)
2799               break;
2800         }
2801         LOG((4, "idim=%d mystart[idim]=%d myedge[idim]=%d dimlen=%d", 
2802              idim, mystart[idim], myedges[idim], dimlen));
2803         if (mystart[idim] >= dimlen)
2804         {
2805            retval = NC_EINVALCOORDS;
2806            goto done;
2807         }
2808         
2809         if (mystart[idim] + myedges[idim] > dimlen)
2810         {
2811            retval = NC_EEDGE;
2812            goto done;
2813         }
2814      }
2815
2816      /* OK, now we're just getting too fancy... As an optimization,
2817         adjust I/O parameters when the fastest dimension has unity
2818         stride both externally and internally. In this case, the user
2819         could have called a simpler routine
2820         (i.e. ncvarnc_get_vara_text).*/
2821      if (mystride[maxidim] == 1
2822          && mymap[maxidim] == 1)
2823      {
2824         iocount[maxidim] = myedges[maxidim];
2825         mystride[maxidim] = (ptrdiff_t) myedges[maxidim];
2826         mymap[maxidim] = (ptrdiff_t) length[maxidim];
2827      }
2828
2829      /* Perform I/O.  Exit when done. */
2830      for (;;)
2831      {
2832         int lretval;
2833         if (pg == GET)
2834            lretval = nc4_get_vara(nc, ncid, varid, mystart, iocount, xtype, 
2835                                   is_long, data);
2836         else
2837            lretval =  nc4_put_vara(nc, ncid, varid, mystart, iocount, xtype, 
2838                                    is_long, data);
2839         if (lretval != NC_NOERR
2840             && (retval == NC_NOERR || lretval != NC_ERANGE))
2841            retval = lretval;
2842
2843         /*
2844          * The following code permutes through the variable s
2845          * external start-index space and it s internal address
2846          * space.  At the UPC, this algorithm is commonly
2847          * called "odometer code".
2848          */
2849         idim = maxidim;
2850        carry:
2851         data = (char *)data + (mymap[idim] * mem_type_size);
2852         LOG((4, "data=0x%x mymap[%d]=%d", data, idim, (int)mymap[idim]));
2853         mystart[idim] += mystride[idim];
2854         LOG((4, "mystart[%d]=%d length[%d]=%d", idim, (int)mystart[idim], 
2855              idim, (int)length[idim]));
2856         if (mystart[idim] == stop[idim])
2857         {
2858            mystart[idim] = start[idim];
2859            data = (char *)data - (length[idim] * mem_type_size);
2860            if (--idim < 0)
2861               break; /* normal return */
2862            goto carry;
2863         }
2864      } /* I/O loop */
2865     done:
2866      free(mystart);
2867   } /* variable is array */
2868
2869  exit:   
2870   return retval;
2871}
2872
2873/* This function will copy data from one buffer to another, in
2874   accordance with the types. Range errors will be noted, and the fill
2875   value used (or the default fill value if none is supplied) for
2876   values that overflow the type.
2877
2878   I should be able to take this out when HDF5 does the right thing
2879   with data type conversion.
2880
2881   Ed Hartnett, 11/15/3
2882*/
2883
2884int
2885nc4_convert_type(const void *src, void *dest, 
2886                 const nc_type src_type, const nc_type dest_type, 
2887                 const size_t len, int *range_error, 
2888                 const void *fill_value, int strict_nc3, int src_long, 
2889                 int dest_long)
2890{
2891   char *cp, *cp1;
2892   float *fp, *fp1;
2893   double *dp, *dp1;
2894   int *ip, *ip1;
2895   signed long *lp, *lp1;
2896   short *sp, *sp1;
2897   signed char *bp, *bp1;
2898   unsigned char *ubp, *ubp1;
2899   unsigned short *usp, *usp1;
2900   unsigned int *uip, *uip1;
2901   long long *lip, *lip1;
2902   unsigned long long *ulip, *ulip1;
2903   size_t count = 0;
2904
2905   *range_error = 0;
2906   LOG((3, "nc4_convert_type: len %d src_type %d dest_type %d src_long %d"
2907        " dest_long %d", len, src_type, dest_type, src_long, dest_long));
2908
2909   /* OK, this is ugly. If you can think of anything better, I'm open
2910      to suggestions!
2911
2912      Note that we don't use a default fill value for type
2913      NC_BYTE. This is because Lord Voldemort cast a nofilleramous spell
2914      at Harry Potter, but it bounced off his scar and hit the netcdf-4
2915      code.
2916   */
2917   switch (src_type)
2918   {
2919      case NC_CHAR:
2920         switch (dest_type)
2921         {
2922            case NC_CHAR:
2923               for (cp = (char *)src, cp1 = dest; count < len; count++)
2924                  *cp1++ = *cp++;
2925               break;
2926            default:
2927               LOG((0, "nc4_convert_type: Uknown destination type."));
2928         }
2929         break;
2930      case NC_BYTE:
2931         switch (dest_type)
2932         {
2933            case NC_BYTE:
2934               for (bp = (signed char *)src, bp1 = dest; count < len; count++)
2935                  *bp1++ = *bp++;
2936               break;
2937            case NC_UBYTE:
2938               for (bp = (signed char *)src, ubp = dest; count < len; count++)
2939               {
2940                  if (*bp < 0)
2941                     (*range_error)++;
2942                  *ubp++ = *bp++;
2943               }
2944               break;
2945            case NC_SHORT:
2946               for (bp = (signed char *)src, sp = dest; count < len; count++)
2947                  *sp++ = *bp++;
2948               break;
2949            case NC_USHORT:
2950               for (bp = (signed char *)src, usp = dest; count < len; count++)
2951               {
2952                  if (*bp < 0)
2953                     (*range_error)++;
2954                  *usp++ = *bp++;
2955               }
2956               break;
2957            case NC_INT:
2958               if (dest_long)
2959               {
2960                  for (bp = (signed char *)src, lp = dest; count < len; count++)
2961                     *lp++ = *bp++;
2962                  break;
2963               }
2964               else
2965               {
2966                  for (bp = (signed char *)src, ip = dest; count < len; count++)
2967                     *ip++ = *bp++;
2968                  break;
2969               }
2970            case NC_UINT:
2971               for (bp = (signed char *)src, uip = dest; count < len; count++)
2972               {
2973                  if (*bp < 0)
2974                     (*range_error)++;
2975                  *uip++ = *bp++;
2976               }
2977               break;
2978            case NC_INT64:
2979               for (bp = (signed char *)src, lip = dest; count < len; count++)
2980                  *lip++ = *bp++;
2981               break;
2982            case NC_UINT64:
2983               for (bp = (signed char *)src, ulip = dest; count < len; count++)
2984               {
2985                  if (*bp < 0)
2986                     (*range_error)++;
2987                  *ulip++ = *bp++;
2988               }
2989               break;
2990            case NC_FLOAT:
2991               for (bp = (signed char *)src, fp = dest; count < len; count++)
2992                  *fp++ = *bp++;
2993               break;
2994            case NC_DOUBLE:
2995               for (bp = (signed char *)src, dp = dest; count < len; count++)
2996                  *dp++ = *bp++;
2997               break;
2998            default:
2999               LOG((0, "nc4_convert_type: unexpected dest type. "
3000                    "src_type %d, dest_type %d", src_type, dest_type));
3001               return NC_EBADTYPE;
3002         }
3003         break;
3004      case NC_UBYTE:
3005         switch (dest_type)
3006         {
3007            case NC_BYTE:
3008               for (ubp = (unsigned char *)src, bp = dest; count < len; count++)
3009               {
3010                  if (!strict_nc3 && *ubp > X_SCHAR_MAX)
3011                     (*range_error)++;
3012                  *bp++ = *ubp++;
3013               }
3014               break;
3015            case NC_SHORT:
3016               for (ubp = (unsigned char *)src, sp = dest; count < len; count++)
3017                  *sp++ = *ubp++;
3018               break;
3019            case NC_UBYTE:
3020               for (ubp = (unsigned char *)src, ubp1 = dest; count < len; count++)
3021                  *ubp1++ = *ubp++;
3022               break;
3023            case NC_USHORT:
3024               for (ubp = (unsigned char *)src, usp = dest; count < len; count++)
3025                  *usp++ = *ubp++;
3026               break;
3027            case NC_INT:
3028               if (dest_long)
3029               {
3030                  for (ubp = (unsigned char *)src, lp = dest; count < len; count++)
3031                     *lp++ = *ubp++;
3032                  break;
3033               }
3034               else
3035               {
3036                  for (ubp = (unsigned char *)src, ip = dest; count < len; count++)
3037                     *ip++ = *ubp++;
3038                  break;
3039               }
3040            case NC_UINT:
3041               for (ubp = (unsigned char *)src, uip = dest; count < len; count++)
3042                  *uip++ = *ubp++;
3043               break;
3044            case NC_INT64:
3045               for (ubp = (unsigned char *)src, lip = dest; count < len; count++)
3046                  *lip++ = *ubp++;
3047               break;
3048            case NC_UINT64:
3049               for (ubp = (unsigned char *)src, ulip = dest; count < len; count++)
3050                  *ulip++ = *ubp++;
3051               break;
3052            case NC_FLOAT:
3053               for (ubp = (unsigned char *)src, fp = dest; count < len; count++)
3054                  *fp++ = *ubp++;
3055               break;
3056            case NC_DOUBLE:
3057               for (ubp = (unsigned char *)src, dp = dest; count < len; count++)
3058                  *dp++ = *ubp++;
3059               break;
3060            default:
3061               LOG((0, "nc4_convert_type: unexpected dest type. "
3062                    "src_type %d, dest_type %d", src_type, dest_type));
3063               return NC_EBADTYPE;
3064         }
3065         break;
3066      case NC_SHORT:
3067         switch (dest_type)
3068         {
3069            case NC_UBYTE:
3070               for (sp = (short *)src, ubp = dest; count < len; count++)
3071               {
3072                  if (*sp > X_UCHAR_MAX || *sp < 0)
3073                     (*range_error)++;
3074                  *ubp++ = *sp++;
3075               }
3076               break;
3077            case NC_BYTE:
3078               for (sp = (short *)src, bp = dest; count < len; count++)
3079               {
3080                  if (*sp > X_SCHAR_MAX || *sp < X_SCHAR_MIN)
3081                     (*range_error)++;
3082                  *bp++ = *sp++;
3083               }
3084               break;
3085            case NC_SHORT:
3086               for (sp = (short *)src, sp1 = dest; count < len; count++)
3087                  *sp1++ = *sp++;
3088               break;
3089            case NC_USHORT:
3090               for (sp = (short *)src, usp = dest; count < len; count++)
3091               {
3092                  if (*sp < 0)
3093                     (*range_error)++;
3094                  *usp++ = *sp++;
3095               }
3096               break;
3097            case NC_INT:
3098               if (dest_long)
3099                  for (sp = (short *)src, lp = dest; count < len; count++)
3100                     *lp++ = *sp++;
3101               else
3102                  for (sp = (short *)src, ip = dest; count < len; count++)
3103                     *ip++ = *sp++;
3104               break;
3105            case NC_UINT:
3106               for (sp = (short *)src, uip = dest; count < len; count++)
3107               { 
3108                   if (*sp < 0)
3109                       (*range_error)++;
3110                  *uip++ = *sp++;
3111               }
3112               break;
3113            case NC_INT64:
3114               for (sp = (short *)src, lip = dest; count < len; count++)
3115                  *lip++ = *sp++;
3116               break;
3117            case NC_UINT64:
3118               for (sp = (short *)src, ulip = dest; count < len; count++)
3119               { 
3120                   if (*sp < 0)
3121                       (*range_error)++;
3122                  *ulip++ = *sp++;
3123               }
3124               break;
3125            case NC_FLOAT:
3126               for (sp = (short *)src, fp = dest; count < len; count++)
3127                  *fp++ = *sp++;
3128               break;
3129            case NC_DOUBLE:
3130               for (sp = (short *)src, dp = dest; count < len; count++)
3131                  *dp++ = *sp++;
3132               break;
3133            default:
3134               LOG((0, "nc4_convert_type: unexpected dest type. "
3135                    "src_type %d, dest_type %d", src_type, dest_type));
3136               return NC_EBADTYPE;
3137         }
3138         break;
3139      case NC_USHORT:
3140         switch (dest_type)
3141         {
3142            case NC_UBYTE:
3143               for (usp = (unsigned short *)src, ubp = dest; count < len; count++)
3144               {
3145                  if (*usp > X_UCHAR_MAX)
3146                     (*range_error)++;
3147                  *ubp++ = *usp++;
3148               }
3149               break;
3150            case NC_BYTE:
3151               for (usp = (unsigned short *)src, bp = dest; count < len; count++)
3152               {
3153                  if (*usp > X_SCHAR_MAX)
3154                     (*range_error)++;
3155                  *bp++ = *usp++;
3156               }
3157               break;
3158            case NC_SHORT:
3159               for (usp = (unsigned short *)src, sp = dest; count < len; count++)
3160               {
3161                  if (*usp > X_SHORT_MAX)
3162                     (*range_error)++;
3163                  *sp++ = *usp++;
3164               }
3165               break;
3166            case NC_USHORT:
3167               for (usp = (unsigned short *)src, usp1 = dest; count < len; count++)
3168                  *usp1++ = *usp++;
3169               break;
3170            case NC_INT:
3171               if (dest_long)
3172                  for (usp = (unsigned short *)src, lp = dest; count < len; count++)
3173                     *lp++ = *usp++;
3174               else
3175                  for (usp = (unsigned short *)src, ip = dest; count < len; count++)
3176                     *ip++ = *usp++;
3177               break;
3178            case NC_UINT:
3179               for (usp = (unsigned short *)src, uip = dest; count < len; count++)
3180                  *uip++ = *usp++;
3181               break;
3182            case NC_INT64:
3183               for (usp = (unsigned short *)src, lip = dest; count < len; count++)
3184                  *lip++ = *usp++;
3185               break;
3186            case NC_UINT64:
3187               for (usp = (unsigned short *)src, ulip = dest; count < len; count++)
3188                  *ulip++ = *usp++;
3189               break;
3190            case NC_FLOAT:
3191               for (usp = (unsigned short *)src, fp = dest; count < len; count++)
3192                  *fp++ = *usp++;
3193               break;
3194            case NC_DOUBLE:
3195               for (usp = (unsigned short *)src, dp = dest; count < len; count++)
3196                  *dp++ = *usp++;
3197               break;
3198            default:
3199               LOG((0, "nc4_convert_type: unexpected dest type. "
3200                    "src_type %d, dest_type %d", src_type, dest_type));
3201               return NC_EBADTYPE;
3202         }
3203         break;
3204      case NC_INT:
3205         if (src_long)
3206         {
3207            switch (dest_type)
3208            {
3209               case NC_UBYTE:
3210                  for (lp = (long *)src, ubp = dest; count < len; count++)
3211                  {
3212                     if (*lp > X_UCHAR_MAX || *lp < 0)
3213                        (*range_error)++;
3214                     *ubp++ = *lp++;
3215                  }
3216                  break;
3217               case NC_BYTE:
3218                  for (lp = (long *)src, bp = dest; count < len; count++)
3219                  {
3220                     if (*lp > X_SCHAR_MAX || *lp < X_SCHAR_MIN)
3221                        (*range_error)++;
3222                     *bp++ = *lp++;
3223                  }
3224                  break;
3225               case NC_SHORT:
3226                  for (lp = (long *)src, sp = dest; count < len; count++)
3227                  {
3228                     if (*lp > X_SHORT_MAX || *lp < X_SHORT_MIN)
3229                        (*range_error)++;
3230                     *sp++ = *lp++;
3231                  }
3232                  break;
3233               case NC_USHORT:
3234                  for (lp = (long *)src, usp = dest; count < len; count++)
3235                  {
3236                     if (*lp > X_USHORT_MAX || *lp < 0)
3237                        (*range_error)++;
3238                     *usp++ = *lp++;
3239                  }
3240                  break;
3241               case NC_INT: /* src is long */
3242                  if (dest_long)
3243                  {
3244                     for (lp = (long *)src, lp1 = dest; count < len; count++)
3245                     {
3246                        if (*lp > X_LONG_MAX || *lp < X_LONG_MIN)
3247                           (*range_error)++;
3248                        *lp1++ = *lp++;
3249                     }
3250                  }
3251                  else /* dest is int */
3252                  {
3253                     for (lp = (long *)src, ip = dest; count < len; count++)
3254                     {
3255                        if (*lp > X_INT_MAX || *lp < X_INT_MIN)
3256                           (*range_error)++;
3257                        *ip++ = *lp++;
3258                     }
3259                  }
3260                  break;
3261               case NC_UINT:
3262                  for (lp = (long *)src, uip = dest; count < len; count++)
3263                  {
3264                     if (*lp > X_UINT_MAX || *lp < 0)
3265                        (*range_error)++;
3266                     *uip++ = *lp++;
3267                  }
3268                  break;
3269               case NC_INT64:
3270                  for (lp = (long *)src, lip = dest; count < len; count++)
3271                     *lip++ = *lp++;
3272                  break;
3273               case NC_UINT64:
3274                  for (lp = (long *)src, ulip = dest; count < len; count++)
3275                  {
3276                     if (*lp < 0)
3277                        (*range_error)++;
3278                     *ulip++ = *lp++;
3279                  }
3280                  break;
3281               case NC_FLOAT:
3282                  for (lp = (long *)src, fp = dest; count < len; count++)
3283                     *fp++ = *lp++;
3284                  break;
3285               case NC_DOUBLE:
3286                  for (lp = (long *)src, dp = dest; count < len; count++)
3287                     *dp++ = *lp++;
3288                  break;
3289               default:
3290                  LOG((0, "nc4_convert_type: unexpected dest type. "
3291                       "src_type %d, dest_type %d", src_type, dest_type));
3292                  return NC_EBADTYPE;
3293            }
3294         }
3295         else
3296         {
3297            switch (dest_type)
3298            {
3299               case NC_UBYTE:
3300                  for (ip = (int *)src, ubp = dest; count < len; count++)
3301                  {
3302                     if (*ip > X_UCHAR_MAX || *ip < 0)
3303                        (*range_error)++;
3304                     *ubp++ = *ip++;
3305                  }
3306                  break;
3307               case NC_BYTE:
3308                  for (ip = (int *)src, bp = dest; count < len; count++)
3309                  {
3310                     if (*ip > X_SCHAR_MAX || *ip < X_SCHAR_MIN)
3311                        (*range_error)++;
3312                     *bp++ = *ip++;
3313                  }
3314                  break;
3315               case NC_SHORT:
3316                  for (ip = (int *)src, sp = dest; count < len; count++)
3317                  {
3318                     if (*ip > X_SHORT_MAX || *ip < X_SHORT_MIN)
3319                        (*range_error)++;
3320                     *sp++ = *ip++;
3321                  }
3322                  break;
3323               case NC_USHORT:
3324                  for (ip = (int *)src, usp = dest; count < len; count++)
3325                  {
3326                     if (*ip > X_USHORT_MAX || *ip < 0)
3327                        (*range_error)++;
3328                     *usp++ = *ip++;
3329                  }
3330                  break;
3331               case NC_INT: /* src is int */
3332                  if (dest_long)
3333                  {
3334                     for (ip = (int *)src, lp1 = dest; count < len; count++)
3335                     {
3336                        if (*ip > X_LONG_MAX || *ip < X_LONG_MIN)
3337                           (*range_error)++;
3338                        *lp1++ = *ip++;
3339                     }
3340                  }
3341                  else /* dest is int */
3342                  {
3343                     for (ip = (int *)src, ip1 = dest; count < len; count++)
3344                     {
3345                        if (*ip > X_INT_MAX || *ip < X_INT_MIN)
3346                           (*range_error)++;
3347                        *ip1++ = *ip++;
3348                     }
3349                  }
3350                  break;
3351               case NC_UINT:
3352                  for (ip = (int *)src, uip = dest; count < len; count++)
3353                  {
3354                     if (*ip > X_UINT_MAX || *ip < 0)
3355                        (*range_error)++;
3356                     *uip++ = *ip++;
3357                  }
3358                  break;
3359               case NC_INT64:
3360                  for (ip = (int *)src, lip = dest; count < len; count++)
3361                     *lip++ = *ip++;
3362                  break;
3363               case NC_UINT64:
3364                  for (ip = (int *)src, ulip = dest; count < len; count++)
3365                  {
3366                      if (*ip < 0) 
3367                          (*range_error)++;
3368                     *ulip++ = *ip++;
3369                  }
3370                  break;
3371               case NC_FLOAT:
3372                  for (ip = (int *)src, fp = dest; count < len; count++)
3373                     *fp++ = *ip++;
3374                  break;
3375               case NC_DOUBLE:
3376                  for (ip = (int *)src, dp = dest; count < len; count++)
3377                     *dp++ = *ip++;
3378                  break;
3379               default:
3380                  LOG((0, "nc4_convert_type: unexpected dest type. "
3381                       "src_type %d, dest_type %d", src_type, dest_type));
3382                  return NC_EBADTYPE;
3383            }
3384         }
3385         break;
3386      case NC_UINT:
3387         switch (dest_type)
3388         {
3389            case NC_UBYTE:
3390               for (uip = (unsigned int *)src, ubp = dest; count < len; count++)
3391               {
3392                  if (*uip > X_UCHAR_MAX)
3393                     (*range_error)++;
3394                  *ubp++ = *uip++;
3395               }
3396               break;
3397            case NC_BYTE:
3398               for (uip = (unsigned int *)src, bp = dest; count < len; count++)
3399               {
3400                  if (*uip > X_SCHAR_MAX)
3401                     (*range_error)++;
3402                  *bp++ = *uip++;
3403               }
3404               break;
3405            case NC_SHORT:
3406               for (uip = (unsigned int *)src, sp = dest; count < len; count++)
3407               {
3408                  if (*uip > X_SHORT_MAX)
3409                     (*range_error)++;
3410                  *sp++ = *uip++;
3411               }
3412               break;
3413            case NC_USHORT:
3414               for (uip = (unsigned int *)src, usp = dest; count < len; count++)
3415               {
3416                  if (*uip > X_USHORT_MAX)
3417                     (*range_error)++;
3418                  *usp++ = *uip++;
3419               }
3420               break;
3421            case NC_INT:
3422               if (dest_long)
3423                  for (uip = (unsigned int *)src, lp = dest; count < len; count++)
3424                  {
3425                     if (*uip > X_LONG_MAX)
3426                        (*range_error)++;
3427                     *lp++ = *uip++;
3428                  }
3429               else
3430                  for (uip = (unsigned int *)src, ip = dest; count < len; count++)
3431                  {
3432                     if (*uip > X_INT_MAX)
3433                        (*range_error)++;
3434                     *ip++ = *uip++;
3435                  }
3436               break;
3437            case NC_UINT:
3438               for (uip = (unsigned int *)src, uip1 = dest; count < len; count++)
3439               {
3440                  if (*uip > X_UINT_MAX)
3441                     (*range_error)++;
3442                  *uip1++ = *uip++;
3443               }
3444               break;
3445            case NC_INT64:
3446               for (uip = (unsigned int *)src, lip = dest; count < len; count++)
3447                  *lip++ = *uip++;
3448               break;
3449            case NC_UINT64:
3450               for (uip = (unsigned int *)src, ulip = dest; count < len; count++)
3451                  *ulip++ = *uip++;
3452               break;
3453            case NC_FLOAT:
3454               for (uip = (unsigned int *)src, fp = dest; count < len; count++)
3455                  *fp++ = *uip++;
3456               break;
3457            case NC_DOUBLE:
3458               for (uip = (unsigned int *)src, dp = dest; count < len; count++)
3459                  *dp++ = *uip++;
3460               break;
3461            default:
3462               LOG((0, "nc4_convert_type: unexpected dest type. "
3463                    "src_type %d, dest_type %d", src_type, dest_type));
3464               return NC_EBADTYPE;
3465         }
3466         break;
3467      case NC_INT64:
3468         switch (dest_type)
3469         {
3470            case NC_UBYTE:
3471               for (lip = (long long *)src, ubp = dest; count < len; count++)
3472               {
3473                  if (*lip > X_UCHAR_MAX || *lip < 0)
3474                     (*range_error)++;
3475                  *ubp++ = *lip++;
3476               }
3477               break;
3478            case NC_BYTE:
3479               for (lip = (long long *)src, bp = dest; count < len; count++)
3480               {
3481                  if (*lip > X_SCHAR_MAX || *lip < X_SCHAR_MIN)
3482                     (*range_error)++;
3483                  *bp++ = *lip++;
3484               }
3485               break;
3486            case NC_SHORT:
3487               for (lip = (long long *)src, sp = dest; count < len; count++)
3488               {
3489                  if (*lip > X_SHORT_MAX || *lip < X_SHORT_MIN)
3490                     (*range_error)++;
3491                  *sp++ = *lip++;
3492               }
3493               break;
3494            case NC_USHORT:
3495               for (lip = (long long *)src, usp = dest; count < len; count++)
3496               {
3497                  if (*lip > X_USHORT_MAX || *lip < 0)
3498                     (*range_error)++;
3499                  *usp++ = *lip++;
3500               }
3501               break;
3502            case NC_UINT:
3503               for (lip = (long long *)src, uip = dest; count < len; count++)
3504               {
3505                  if (*lip > X_UINT_MAX || *lip < 0)
3506                     (*range_error)++;
3507                  *uip++ = *lip++;
3508               }
3509               break;
3510            case NC_INT:
3511               if (dest_long)
3512                  for (lip = (long long *)src, lp = dest; count < len; count++)
3513                  {
3514                     if (*lip > X_LONG_MAX || *lip < X_LONG_MIN)
3515                        (*range_error)++;
3516                     *lp++ = *lip++;
3517                  }
3518               else
3519                  for (lip = (long long *)src, ip = dest; count < len; count++)
3520                  {
3521                     if (*lip > X_INT_MAX || *lip < X_INT_MIN)
3522                        (*range_error)++;
3523                     *ip++ = *lip++;
3524                  }
3525               break;
3526            case NC_INT64:
3527               for (lip = (long long *)src, lip1 = dest; count < len; count++)
3528                  *lip1++ = *lip++;
3529               break;
3530            case NC_UINT64:
3531               for (lip = (long long *)src, ulip = dest; count < len; count++)
3532               {
3533                  if (*lip < 0)
3534                     (*range_error)++;
3535                  *ulip++ = *lip++;
3536               }
3537               break;
3538            case NC_FLOAT:
3539               for (lip = (long long *)src, fp = dest; count < len; count++)
3540                  *fp++ = *lip++;
3541               break;
3542            case NC_DOUBLE:
3543               for (lip = (long long *)src, dp = dest; count < len; count++)
3544                  *dp++ = *lip++;
3545               break;
3546            default:
3547               LOG((0, "nc4_convert_type: unexpected dest type. "
3548                    "src_type %d, dest_type %d", src_type, dest_type));
3549               return NC_EBADTYPE;
3550         }
3551         break;
3552      case NC_UINT64:
3553         switch (dest_type)
3554         {
3555            case NC_UBYTE:
3556               for (ulip = (unsigned long long *)src, ubp = dest; count < len; count++)
3557               {
3558                  if (*ulip > X_UCHAR_MAX)
3559                     (*range_error)++;
3560                  *ubp++ = *ulip++;
3561               }
3562               break;
3563            case NC_BYTE:
3564               for (ulip = (unsigned long long *)src, bp = dest; count < len; count++)
3565               {
3566                  if (*ulip > X_SCHAR_MAX)
3567                     (*range_error)++;
3568                  *bp++ = *ulip++;
3569               }
3570               break;
3571            case NC_SHORT:
3572               for (ulip = (unsigned long long *)src, sp = dest; count < len; count++)
3573               {
3574                  if (*ulip > X_SHORT_MAX)
3575                     (*range_error)++;
3576                  *sp++ = *ulip++;
3577               }
3578               break;
3579            case NC_USHORT:
3580               for (ulip = (unsigned long long *)src, usp = dest; count < len; count++)
3581               {
3582                  if (*ulip > X_USHORT_MAX)
3583                     (*range_error)++;
3584                  *usp++ = *ulip++;
3585               }
3586               break;
3587            case NC_UINT:
3588               for (ulip = (unsigned long long *)src, uip = dest; count < len; count++)
3589               {
3590                  if (*ulip > X_UINT_MAX)
3591                     (*range_error)++;
3592                  *uip++ = *ulip++;
3593               }
3594               break;
3595            case NC_INT:
3596               if (dest_long)
3597                  for (ulip = (unsigned long long *)src, lp = dest; count < len; count++)
3598                  {
3599                     if (*ulip > X_LONG_MAX)
3600                        (*range_error)++;
3601                     *lp++ = *ulip++;
3602                  }
3603               else
3604                  for (ulip = (unsigned long long *)src, ip = dest; count < len; count++)
3605                  {
3606                     if (*ulip > X_INT_MAX)
3607                        (*range_error)++;
3608                     *ip++ = *ulip++;
3609                  }
3610               break;
3611            case NC_INT64:
3612               for (ulip = (unsigned long long *)src, lip = dest; count < len; count++)
3613               {
3614                  if (*ulip > X_INT64_MAX)
3615                    (*range_error)++;
3616                  *lip++ = *ulip++;
3617               }
3618               break;
3619            case NC_UINT64:
3620               for (ulip = (unsigned long long *)src, ulip1 = dest; count < len; count++)
3621                  *ulip1++ = *ulip++;
3622               break;
3623            case NC_FLOAT:
3624               for (ulip = (unsigned long long *)src, fp = dest; count < len; count++)
3625                  *fp++ = *ulip++;
3626               break;
3627            case NC_DOUBLE:
3628               for (ulip = (unsigned long long *)src, dp = dest; count < len; count++)
3629                  *dp++ = *ulip++;
3630               break;
3631            default:
3632               LOG((0, "nc4_convert_type: unexpected dest type. "
3633                    "src_type %d, dest_type %d", src_type, dest_type));
3634               return NC_EBADTYPE;
3635         }
3636         break;
3637      case NC_FLOAT:
3638         switch (dest_type)
3639         {
3640            case NC_UBYTE:
3641               for (fp = (float *)src, ubp = dest; count < len; count++)
3642               {
3643                  if (*fp > X_UCHAR_MAX || *fp < 0)
3644                     (*range_error)++;
3645                  *ubp++ = *fp++;
3646               }
3647               break;
3648            case NC_BYTE:
3649               for (fp = (float *)src, bp = dest; count < len; count++)
3650               {
3651                  if (*fp > (double)X_SCHAR_MAX || *fp < (double)X_SCHAR_MIN)
3652                     (*range_error)++;
3653                  *bp++ = *fp++;
3654               }
3655               break;
3656            case NC_SHORT:
3657               for (fp = (float *)src, sp = dest; count < len; count++)
3658               {
3659                  if (*fp > (double)X_SHORT_MAX || *fp < (double)X_SHORT_MIN)
3660                     (*range_error)++;
3661                  *sp++ = *fp++;
3662               }
3663               break;
3664            case NC_USHORT:
3665               for (fp = (float *)src, usp = dest; count < len; count++)
3666               {
3667                  if (*fp > X_USHORT_MAX || *fp < 0)
3668                     (*range_error)++;
3669                  *usp++ = *fp++;
3670               }
3671               break;
3672            case NC_UINT:
3673               for (fp = (float *)src, uip = dest; count < len; count++)
3674               {
3675                  if (*fp > X_UINT_MAX || *fp < 0)
3676                     (*range_error)++;
3677                  *uip++ = *fp++;
3678               }
3679               break;
3680            case NC_INT:
3681               if (dest_long)
3682                  for (fp = (float *)src, lp = dest; count < len; count++)
3683                  {
3684                     if (*fp > (double)X_LONG_MAX || *fp < (double)X_LONG_MIN)
3685                        (*range_error)++;
3686                     *lp++ = *fp++;
3687                  }
3688               else
3689                  for (fp = (float *)src, ip = dest; count < len; count++)
3690                  {
3691                     if (*fp > (double)X_INT_MAX || *fp < (double)X_INT_MIN)
3692                        (*range_error)++;
3693                     *ip++ = *fp++;
3694                  }
3695               break;
3696            case NC_INT64:
3697               for (fp = (float *)src, lip = dest; count < len; count++)
3698               {
3699                  if (*fp > X_INT64_MAX || *fp <X_INT64_MIN)
3700                    (*range_error)++;
3701                  *lip++ = *fp++;
3702               }
3703               break;
3704            case NC_UINT64:
3705               for (fp = (float *)src, lip = dest; count < len; count++)
3706               {
3707                  if (*fp > X_UINT64_MAX || *fp < 0)
3708                    (*range_error)++;
3709                  *lip++ = *fp++;
3710               }
3711               break;
3712            case NC_FLOAT:
3713               for (fp = (float *)src, fp1 = dest; count < len; count++)
3714               {
3715/*                if (*fp > X_FLOAT_MAX || *fp < X_FLOAT_MIN)
3716                  (*range_error)++;*/
3717                  *fp1++ = *fp++;
3718               }
3719               break;
3720            case NC_DOUBLE:
3721               for (fp = (float *)src, dp = dest; count < len; count++)
3722                  *dp++ = *fp++;
3723               break;
3724            default:
3725               LOG((0, "nc4_convert_type: unexpected dest type. "
3726                    "src_type %d, dest_type %d", src_type, dest_type));
3727               return NC_EBADTYPE;
3728         }
3729         break;
3730      case NC_DOUBLE:
3731         switch (dest_type)
3732         {
3733            case NC_UBYTE:
3734               for (dp = (double *)src, ubp = dest; count < len; count++)
3735               {
3736                  if (*dp > X_UCHAR_MAX || *dp < 0)
3737                     (*range_error)++;
3738                  *ubp++ = *dp++;
3739               }
3740               break;
3741            case NC_BYTE:
3742               for (dp = (double *)src, bp = dest; count < len; count++)
3743               {
3744                  if (*dp > X_SCHAR_MAX || *dp < X_SCHAR_MIN)
3745                     (*range_error)++;
3746                  *bp++ = *dp++;
3747               }
3748               break;
3749            case NC_SHORT:
3750               for (dp = (double *)src, sp = dest; count < len; count++)
3751               {
3752                  if (*dp > X_SHORT_MAX || *dp < X_SHORT_MIN)
3753                     (*range_error)++;
3754                  *sp++ = *dp++;
3755               }
3756               break;
3757            case NC_USHORT:
3758               for (dp = (double *)src, usp = dest; count < len; count++)
3759               {
3760                  if (*dp > X_USHORT_MAX || *dp < 0)
3761                     (*range_error)++;
3762                  *usp++ = *dp++;
3763               }
3764               break;
3765            case NC_UINT:
3766               for (dp = (double *)src, uip = dest; count < len; count++)
3767               {
3768                  if (*dp > X_UINT_MAX || *dp < 0)
3769                     (*range_error)++;
3770                  *uip++ = *dp++;
3771               }
3772               break;
3773            case NC_INT:
3774               if (dest_long)
3775                  for (dp = (double *)src, lp = dest; count < len; count++)
3776                  {
3777                     if (*dp > X_LONG_MAX || *dp < X_LONG_MIN)
3778                        (*range_error)++;
3779                     *lp++ = *dp++;
3780                  }
3781               else
3782                  for (dp = (double *)src, ip = dest; count < len; count++)
3783                  {
3784                     if (*dp > X_INT_MAX || *dp < X_INT_MIN)
3785                        (*range_error)++;
3786                     *ip++ = *dp++;
3787                  }
3788               break;
3789            case NC_INT64:
3790               for (dp = (double *)src, lip = dest; count < len; count++)
3791               {
3792                  if (*dp > X_INT64_MAX || *dp < X_INT64_MIN)
3793                    (*range_error)++;
3794                  *lip++ = *dp++;
3795               }
3796               break;
3797            case NC_UINT64:
3798               for (dp = (double *)src, lip = dest; count < len; count++)
3799               {
3800                  if (*dp > X_UINT64_MAX || *dp < 0)
3801                    (*range_error)++;
3802                  *lip++ = *dp++;
3803               }
3804               break;
3805            case NC_FLOAT:
3806               for (dp = (double *)src, fp = dest; count < len; count++)
3807               {
3808                  if (*dp > X_FLOAT_MAX || *dp < X_FLOAT_MIN)
3809                     (*range_error)++;
3810                  *fp++ = *dp++;
3811               }
3812               break;
3813            case NC_DOUBLE:
3814               for (dp = (double *)src, dp1 = dest; count < len; count++)
3815               {
3816                   /* if (*dp > X_DOUBLE_MAX || *dp < X_DOUBLE_MIN) */
3817                   /*    (*range_error)++; */
3818                  *dp1++ = *dp++;
3819               }
3820               break;
3821            default:
3822               LOG((0, "nc4_convert_type: unexpected dest type. "
3823                    "src_type %d, dest_type %d", src_type, dest_type));
3824               return NC_EBADTYPE;
3825         }
3826         break;
3827      default:
3828         LOG((0, "nc4_convert_type: unexpected src type. "
3829              "src_type %d, dest_type %d", src_type, dest_type));
3830         return NC_EBADTYPE;
3831   }
3832   return NC_NOERR;
3833}
3834
3835/* In our first pass through the data, we may have encountered
3836 * variables before encountering their dimscales, so go through the
3837 * vars in this file and make sure we've got a dimid for each. */
3838int
3839nc4_rec_match_dimscales(NC_GRP_INFO_T *grp)
3840{
3841   NC_GRP_INFO_T *g;
3842   NC_VAR_INFO_T *var;
3843   NC_DIM_INFO_T *dim;
3844   H5G_stat_t statbuf;
3845   int d, finished;
3846   int retval = NC_NOERR;
3847
3848   assert(grp && grp->name);
3849   LOG((4, "nc4_rec_match_dimscales: grp->name %s", grp->name));
3850
3851   /* Perform var dimscale match for child groups. */
3852   for (g = grp->children; g; g = g->next)
3853      if ((retval = nc4_rec_match_dimscales(g)))
3854         return retval;
3855   
3856   /* Check all the vars in this group. If they have dimscale info,
3857    * try and find a dimension for them. */
3858   for (var = grp->var; var; var = var->next)
3859   {
3860      /* Are there dimscales for this variable? */
3861      if (var->dimscale_hdf5_objids)
3862      {
3863         for (d = 0; d < var->ndims; d++)
3864         {
3865            LOG((5, "nc4_rec_match_dimscales: var %s has dimscale info...", var->name));
3866            /* Look at all the dims in this group to see if they
3867             * match. */
3868            finished = 0;
3869            for (g = grp; g && !finished; g = g->parent)
3870            {
3871               for (dim = g->dim; dim; dim = dim->next)
3872               {
3873                  if (!dim->hdf_dimscaleid)
3874                     return NC_EDIMMETA;
3875                  if (H5Gget_objinfo(dim->hdf_dimscaleid, ".", 1, &statbuf) < 0)
3876                     return NC_EHDFERR;
3877                  if (var->dimscale_hdf5_objids[d].fileno[0] == statbuf.fileno[0] && 
3878                      var->dimscale_hdf5_objids[d].objno[0] == statbuf.objno[0] &&
3879                      var->dimscale_hdf5_objids[d].fileno[1] == statbuf.fileno[1] && 
3880                      var->dimscale_hdf5_objids[d].objno[1] == statbuf.objno[1])
3881                  {
3882                     LOG((4, "nc4_rec_match_dimscales: for dimension %d, found dim %s", 
3883                          d, dim->name));
3884                     var->dimids[d] = dim->dimid;
3885                     finished++;
3886                     break;
3887                  }
3888               } /* next dim */
3889            } /* next grp */
3890            LOG((5, "nc4_rec_match_dimscales: dimid for this dimscale is %d", var->xtype));
3891         } /* next var->dim */
3892      }
3893      else
3894      {
3895         if (!var->dimscale)
3896         {
3897            hid_t spaceid = 0;
3898            hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL;
3899            int dataset_ndims;
3900
3901            /* No dimscales for this var! Invent phony dimensions. */
3902
3903            /* Find the space information for this dimension. */
3904            if ((spaceid = H5Dget_space(var->hdf_datasetid)) < 0)
3905               return NC_EHDFERR;
3906#ifdef EXTRA_TESTS
3907            num_spaces++;
3908#endif
3909
3910            /* Get the len of each dim in the space. */
3911            if (var->ndims)
3912            {
3913               if (!(h5dimlen = malloc(var->ndims * sizeof(hsize_t))))
3914                  return NC_ENOMEM;
3915               if (!(h5dimlenmax = malloc(var->ndims * sizeof(hsize_t))))
3916                  return NC_ENOMEM;
3917               if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, h5dimlen, 
3918                                                              h5dimlenmax)) < 0)
3919                  return NC_EHDFERR;             
3920               if (dataset_ndims != var->ndims)
3921                  return NC_EHDFERR;             
3922            } 
3923            else
3924            {
3925               /* Make sure it's scalar. */
3926               if (H5Sget_simple_extent_type(spaceid) != H5S_SCALAR)
3927                  return NC_EHDFERR;
3928            }
3929
3930            /* Release the space object. */
3931            if (H5Sclose(spaceid) < 0)
3932               return NC_EHDFERR;                             
3933#ifdef EXTRA_TESTS
3934            num_spaces--;
3935#endif
3936           
3937            /* Create a phony dimension for each dimension in the
3938             * dataset, unless there already is one the correct
3939             * size. */
3940            for (d = 0; d < var->ndims; d++)
3941            {
3942               NC_DIM_INFO_T *dim = NULL;
3943               char phony_dim_name[NC_MAX_NAME + 1];
3944
3945               /* Is there already a phony dimension of the correct size? */
3946               for (dim = grp->dim; dim; dim = dim->next)
3947                  if ((dim->len == h5dimlen[d]) &&
3948                      ((h5dimlenmax[d] == H5S_UNLIMITED && dim->unlimited) ||
3949                       (h5dimlenmax[d] != H5S_UNLIMITED && !dim->unlimited)))
3950                     break;
3951               
3952               /* Didn't find a phony dim? Then create one. */
3953               if (!dim)
3954               {
3955                  LOG((3, "nc4_rec_match_dimscales: creating phony dim for var %s", var->name));
3956                  if ((retval = nc4_dim_list_add(&grp->dim)))
3957                     return retval;
3958                  grp->ndims++;
3959                  dim = grp->dim;
3960                  dim->dimid = grp->file->nc4_info->next_dimid++;
3961                  sprintf(phony_dim_name, "phony_dim_%d", dim->dimid);
3962                  if (!(dim->name = malloc((strlen(phony_dim_name) + 1) * sizeof(char))))
3963                     return NC_ENOMEM;
3964                  strcpy(dim->name, phony_dim_name);
3965                  dim->len = h5dimlen[d];
3966                  if (h5dimlenmax[d] == H5S_UNLIMITED)
3967                     dim->unlimited = 1;
3968               }
3969               
3970               /* The variable must remember the dimid. */
3971               var->dimids[d] = dim->dimid;
3972            } /* next dim */
3973
3974            /* Free the memory we malloced. */
3975            free(h5dimlen);
3976            free(h5dimlenmax);
3977         }
3978      }
3979   }
3980
3981   return retval;
3982}
3983
3984/* Get the length, in bytes, of one element of a type in memory. */
3985int 
3986nc4_get_typelen_mem(NC_HDF5_FILE_INFO_T *h5, nc_type xtype, int is_long, 
3987                    size_t *len)
3988{
3989   NC_TYPE_INFO_T *type;
3990   int retval;
3991
3992   LOG((4, "nc4_get_typelen_mem xtype: %d", xtype));
3993   assert(len);
3994
3995   /* If this is an atomic type, the answer is easy. */
3996   switch (xtype)
3997   {
3998      case NC_BYTE:
3999      case NC_CHAR:
4000      case NC_UBYTE:
4001         *len = sizeof(char);
4002         return NC_NOERR;
4003      case NC_SHORT:
4004      case NC_USHORT:
4005         *len = sizeof(short);
4006         return NC_NOERR;
4007      case NC_INT:
4008      case NC_UINT:
4009         if (is_long)
4010            *len = sizeof(long);
4011         else
4012            *len = sizeof(int);
4013         return NC_NOERR;
4014      case NC_FLOAT:
4015         *len = sizeof(float);
4016         return NC_NOERR;
4017      case NC_DOUBLE:
4018         *len = sizeof(double);
4019         return NC_NOERR;
4020      case NC_INT64:
4021      case NC_UINT64:
4022         *len = sizeof(long long);
4023         return NC_NOERR;
4024      case NC_STRING:
4025         *len = 0; /* can't even guess! */
4026         return NC_NOERR;
4027   }
4028
4029   /* See if var is compound type. */
4030   if ((retval = nc4_find_type(h5, xtype, &type)))
4031      return retval;
4032
4033   if (!type)
4034      return NC_EBADTYPE;
4035
4036   *len = type->size;
4037
4038   LOG((5, "type->size ", type->size));
4039
4040   return NC_NOERR;
4041}
Note: See TracBrowser for help on using the repository browser.