1/*
2This file is part of netcdf-4, a netCDF-like interface for HDF5, or a
3HDF5 backend for netCDF, depending on your point of view.
4
5This file handles the nc4 attribute functions.
6
7Remember that with atts, type conversion can take place when writing
8them, and when reading them.
9
10Copyright 2003-2011, University Corporation for Atmospheric
11Research. See COPYRIGHT file for copying and redistribution
12conditions.
13*/
14
15#include "nc4internal.h"
16#include "nc.h"
17#include "nc4dispatch.h"
18#include "ncdispatch.h"
19
20static int nc4_get_att_special(NC_HDF5_FILE_INFO_T*, const char*,
21                               nc_type*, nc_type, size_t*, int*, int, void*);
22
23int nc4typelen(nc_type type);
24
25/* Get or put attribute metadata from our linked list of file
26   info. Always locate the attribute by name, never by attnum.
27   The mem_type is ignored if data=NULL. */
28int
29nc4_get_att(int ncidNC *nc, int varid, const char *name,
30     nc_type *xtypenc_type mem_type, size_t *lenp,
31     int *attnum, int is_long, void *data)
32{
33   NC_GRP_INFO_T *grp;
34   NC_HDF5_FILE_INFO_T *h5;
35   NC_ATT_INFO_T *att = NULL;
36   int my_attnum = -1;
37
38   int need_to_convert = 0;
39   int range_error = NC_NOERR;
40   void *bufr = NULL;
41   size_t type_size;
42   char norm_name[NC_MAX_NAME + 1];
43   int i;
44   int retval = NC_NOERR;
45   const char** sp;
46
47   if (attnum) {
48      my_attnum = *attnum;
49   }
50
51   LOG((3, "%s: ncid 0x%x varid %d name %s attnum %d mem_type %d",
52 __func__ncidvaridnamemy_attnummem_type));
53
54   /* Find info for this file and group, and set pointer to each. */
55   h5 = NC4_DATA(nc);
56   if (!(grp = nc4_rec_find_grp(h5->root_grp, (ncid & GRP_ID_MASK))))
57      BAIL(NC_EBADGRPID);
58
59   /* Normalize name. */
60   if ((retval = nc4_normalize_name(namenorm_name)))
61      BAIL(retval);
62
63   if(nc->ext_ncid == ncid && varid == NC_GLOBAL) {
64 const char** sp;
65 for(sp = NC_RESERVED_SPECIAL_LIST;*sp;sp++) {
66     if(strcmp(name,*sp)==0) {
67 return nc4_get_att_special(h5norm_namextypemem_typelenpattnumis_longdata);
68     }
69 }
70    }
71
72   /* Find the attribute, if it exists.
73      <strike>If we don't find it, we are major failures.</strike>
74   */
75   if ((retval = nc4_find_grp_att(grpvaridnorm_namemy_attnum, &att))) {
76     if(retval == NC_ENOTATT)
77 return retval;
78     else
79      BAIL(retval);
80   }
81
82   /* If mem_type is NC_NAT, it means we want to use the attribute's
83    * file type as the mem type as well. */
84   if (mem_type == NC_NAT)
85      mem_type = att->nc_typeid;
86
87   /* If the attribute is NC_CHAR, and the mem_type isn't, or vice
88    * versa, that's a freakish attempt to convert text to
89    * numbers. Some pervert out there is trying to pull a fast one!
90    * Send him an NC_ECHAR error...*/
91   if (data && att->len &&
92       ((att->nc_typeid == NC_CHAR && mem_type != NC_CHAR) ||
93 (att->nc_typeid != NC_CHAR && mem_type == NC_CHAR)))
94      BAIL(NC_ECHAR); /* take that, you freak! */
95
96   /* Copy the info. */
97   if (lenp)
98      *lenp = att->len;
99   if (xtype)
100      *xtype = att->nc_typeid;
101   if (attnum) {
102      *attnum = att->attnum;
103   }
104
105   /* Zero len attributes are easy to read! */
106   if (!att->len)
107      BAIL(NC_NOERR);
108
109   /* Later on, we will need to know the size of this type. */
110   if ((retval = nc4_get_typelen_mem(h5mem_typeis_long, &type_size)))
111      BAIL(retval);
112
113   /* We may have to convert data. Treat NC_CHAR the same as
114    * NC_UBYTE. If the mem_type is NAT, don't try any conversion - use
115    * the attribute's type. */
116   if (data && att->len && mem_type != att->nc_typeid &&
117       mem_type != NC_NAT &&
118       !(mem_type == NC_CHAR &&
119  (att->nc_typeid == NC_UBYTE || att->nc_typeid == NC_BYTE)))
120   {
121      if (!(bufr = malloc((size_t)(att->len * type_size))))
122  BAIL(NC_ENOMEM);
123      need_to_convert++;
124      if ((retval = nc4_convert_type(att->databufratt->nc_typeid,
125      mem_type, (size_t)att->len, &range_error,
126      NULL, (h5->cmode & NC_CLASSIC_MODEL), 0, is_long)))
127  BAIL(retval);
128
129      /* For strict netcdf-3 rules, ignore erange errors between UBYTE
130       * and BYTE types. */
131      if ((h5->cmode & NC_CLASSIC_MODEL) &&
132   (att->nc_typeid == NC_UBYTE || att->nc_typeid == NC_BYTE) &&
133   (mem_type == NC_UBYTE || mem_type == NC_BYTE) &&
134   range_error)
135  range_error = 0;
136   }
137   else
138   {
139      bufr = att->data;
140   }
141
142   /* If the caller wants data, copy it for him. If he hasn't
143      allocated enough memory for it, he will burn in segmentation
144      fault hell, writhing with the agony of undiscovered memory
145      bugs! */
146   if (data)
147   {
148      if (att->vldata)
149      {
150  size_t base_typelen;
151  hvl_t *vldest = data;
152  NC_TYPE_INFO_T *type;
153
154         /* Get the type object for the attribute's type */
155  if ((retval = nc4_find_type(h5att->nc_typeid, &type)))
156    BAIL(retval);
157
158         /* Retrieve the size of the base type */
159         if ((retval = nc4_get_typelen_mem(h5type->u.v.base_nc_typeid, 0, &base_typelen)))
160            BAIL(retval);
161
162  for (i = 0; i < att->leni++)
163  {
164     vldest[i].len = att->vldata[i].len;
165     if (!(vldest[i].p = malloc(vldest[i].len * base_typelen)))
166        BAIL(NC_ENOMEM);
167     memcpy(vldest[i].patt->vldata[i].pvldest[i].len * base_typelen);
168  }
169      }
170      else if (att->stdata)
171      {
172  for (i = 0; i < att->leni++)
173  {
174            /* Check for NULL pointer for string (valid in HDF5) */
175            if(att->stdata[i])
176            {
177                if (!(((char **)data)[i] = strdup(att->stdata[i])))
178                   BAIL(NC_ENOMEM);
179            }
180            else
181                ((char **)data)[i] = att->stdata[i];
182  }
183      }
184      else
185      {
186  /* For long types, we need to handle this special... */
187  if (is_long && att->nc_typeid == NC_INT)
188  {
189     long *lp = data;
190     int *ip = bufr;
191
192     for (i = 0; i < att->leni++)
193        *lp++ = *ip++;
194  }
195  else
196     memcpy(databufr, (size_t)(att->len * type_size));
197      }
198   }
199
200 exit:
201   if (need_to_convert)
202      free(bufr);
203   if (range_error)
204      retval = NC_ERANGE;
205   return retval;
206}
207
208/* Put attribute metadata into our global metadata. */
209static int
210nc4_put_att(int ncidNC *nc, int varid, const char *name,
211     nc_type file_typenc_type mem_type, size_t len, int is_long,
212     const void *data)
213{
214   NC_GRP_INFO_T *grp;
215   NC_HDF5_FILE_INFO_T *h5;
216   NC_VAR_INFO_T *var = NULL;
217   NC_ATT_INFO_T *att, **attlist = NULL;
218   char norm_name[NC_MAX_NAME + 1];
219   nc_bool_t new_att = NC_FALSE;
220   int retval = NC_NOERRrange_error = 0;
221   size_t type_size;
222   int i;
223   int res;
224
225   if (!name)
226      return NC_EBADNAME;
227   assert(nc && NC4_DATA(nc));
228
229   LOG((1, "nc4_put_att: ncid 0x%x varid %d name %s "
230 "file_type %d mem_type %d len %d", ncidvarid,
231 namefile_typemem_typelen));
232
233   /* If len is not zero, then there must be some data. */
234   if (len && !data)
235      return NC_EINVAL;
236
237   /* Find info for this file and group, and set pointer to each. */
238   h5 = NC4_DATA(nc);
239   if (!(grp = nc4_rec_find_grp(h5->root_grp, (ncid & GRP_ID_MASK))))
240      return NC_EBADGRPID;
241
242   /* If the file is read-only, return an error. */
243   if (h5->no_write)
244     return NC_EPERM;
245
246   /* Check and normalize the name. */
247   if ((retval = nc4_check_name(namenorm_name)))
248      return retval;
249
250   if(nc->ext_ncid == ncid && varid == NC_GLOBAL) {
251 const char** sp;
252 for(sp = NC_RESERVED_SPECIAL_LIST;*sp;sp++) {
253     if(strcmp(name,*sp)==0) {
254 return NC_ENOTATT; /* Not settable */
255     }
256 }
257    }
258
259   /* Find att, if it exists. */
260   if (varid == NC_GLOBAL)
261      attlist = &grp->att;
262   else
263   {
264     for (var = grp->varvarvar = var->l.next)
265       if (var->varid == varid)
266         {
267           attlist = &var->att;
268           break;
269         }
270     if (!var)
271       return NC_ENOTVAR;
272   }
273
274   for (att = *attlistattatt = att->l.next)
275     if (!strcmp(att->namenorm_name))
276       break;
277
278   if (!att)
279   {
280      /* If this is a new att, require define mode. */
281      if (!(h5->flags & NC_INDEF))
282      {
283  if (h5->cmode & NC_CLASSIC_MODEL)
284     return NC_EINDEFINE;
285  if ((retval = NC4_redef(ncid)))
286     BAIL(retval);
287      }
288      new_att = NC_TRUE;
289   }
290   else
291   {
292      /* For an existing att, if we're not in define mode, the len
293  must not be greater than the existing len for classic model. */
294     if (!(h5->flags & NC_INDEF) &&
295         len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid))
296       {
297         if (h5->cmode & NC_CLASSIC_MODEL)
298           return NC_EINDEFINE;
299         if ((retval = NC4_redef(ncid)))
300           BAIL(retval);
301       }
302   }
303
304   /* We must have two valid types to continue. */
305   if (file_type == NC_NAT || mem_type == NC_NAT)
306      return NC_EBADTYPE;
307
308   /* Get information about this type. */
309   if ((retval = nc4_get_typelen_mem(h5file_typeis_long, &type_size)))
310      return retval;
311
312   /* No character conversions are allowed. */
313   if (file_type != mem_type &&
314       (file_type == NC_CHAR || mem_type == NC_CHAR ||
315 file_type == NC_STRING || mem_type == NC_STRING))
316      return NC_ECHAR;
317
318   /* For classic mode file, only allow atts with classic types to be
319    * created. */
320   if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE)
321      return NC_ESTRICTNC3;
322
323   /* Add to the end of the attribute list, if this att doesn't
324      already exist. */
325   if (new_att)
326   {
327      LOG((3, "adding attribute %s to the list...", norm_name));
328      if ((res = nc4_att_list_add(attlist, &att)))
329        BAIL (res);
330      if (!(att->name = strdup(norm_name)))
331        return NC_ENOMEM;
332   }
333
334   /* Now fill in the metadata. */
335   att->dirty = NC_TRUE;
336   att->nc_typeid = file_type;
337
338   /* If this att has vlen or string data, release it before we lose the length value. */
339   if (att->stdata)
340   {
341      for (i = 0; i < att->leni++)
342         if(att->stdata[i])
343     free(att->stdata[i]);
344      free(att->stdata);
345      att->stdata = NULL;
346   }
347   if (att->vldata)
348   {
349      for (i = 0; i < att->leni++)
350  nc_free_vlen(&att->vldata[i]);
351      free(att->vldata);
352      att->vldata = NULL;
353   }
354
355   att->len = len;
356   if (att->l.prev)
357      att->attnum = ((NC_ATT_INFO_T *)att->l.prev)->attnum + 1;
358   else
359      att->attnum = 0;
360
361   /* If this is the _FillValue attribute, then we will also have to
362    * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T
363    * struct for this var. (But ignore a global _FillValue
364    * attribute). */
365   if (!strcmp(att->name_FillValue) && varid != NC_GLOBAL)
366   {
367      NC_ATT_INFO_T *varatt;
368      int size;
369
370      /* Fill value must be same type and have exactly one value */
371      if (att->nc_typeid != var->type_info->nc_typeid)
372        return NC_EBADTYPE;
373      if (att->len != 1)
374        return NC_EINVAL;
375
376      /* If we already wrote to the dataset, then return an error. */
377      if (var->written_to)
378        return NC_ELATEFILL;
379
380      /* If fill value hasn't been set, allocate space. Of course,
381       * vlens have to be different... */
382      if ((retval = nc4_get_typelen_mem(grp->nc4_infovar->type_info->nc_typeid, 0,
383                                        &type_size)))
384        return retval;
385
386      /* Already set a fill value? Now I'll have to free the old
387       * one. Make up your damn mind, would you? */
388      if (var->fill_value)
389        {
390          if (var->type_info->nc_type_class == NC_VLEN)
391            {
392              if ((retval = nc_free_vlen(var->fill_value)))
393                return retval;
394            }
395          else if (var->type_info->nc_type_class == NC_STRING)
396            {
397              if (*(char **)var->fill_value)
398                free(*(char **)var->fill_value);
399            }
400          free(var->fill_value);
401        }
402
403      /* Allocate space for the fill value. */
404      if (var->type_info->nc_type_class == NC_VLEN)
405        size = sizeof(hvl_t);
406      else if (var->type_info->nc_type_class == NC_STRING)
407        size = sizeof(char *);
408      else
409        size = type_size;
410
411      if (!(var->fill_value = calloc(1, size)))
412        return NC_ENOMEM;
413
414      /* Copy the fill_value. */
415      LOG((4, "Copying fill value into metadata for variable %s", var->name));
416      if (var->type_info->nc_type_class == NC_VLEN)
417        {
418          nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value);
419
420          fv_vlen->len = in_vlen->len;
421          if (!(fv_vlen->p = malloc(size * in_vlen->len)))
422            return NC_ENOMEM;
423          memcpy(fv_vlen->pin_vlen->pin_vlen->len * size);
424        }
425      else if (var->type_info->nc_type_class == NC_STRING)
426        {
427          if(NULL != (*(char **)data))
428            {
429              if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1)))
430                return NC_ENOMEM;
431              strcpy(*(char **)var->fill_value, *(char **)data);
432            }
433          else
434            *(char **)var->fill_value = NULL;
435        }
436      else
437        memcpy(var->fill_valuedatatype_size);
438
439      /* Indicate that the fill value was changed, if the variable has already
440       * been created in the file, so the dataset gets deleted and re-created. */
441      if (var->created)
442         var->fill_val_changed = NC_TRUE;
443   }
444
445   /* Copy the attribute data, if there is any. VLENs and string
446    * arrays have to be handled specially. */
447   if(att->len)
448   {
449      nc_type type_class;    /* Class of attribute's type */
450
451      /* Get class for this type. */
452      if ((retval = nc4_get_typeclass(h5file_type, &type_class)))
453         return retval;
454
455      assert(data);
456      if (type_class == NC_VLEN)
457      {
458         const hvl_t *vldata1;
459  NC_TYPE_INFO_T *type;
460  size_t base_typelen;
461
462         /* Get the type object for the attribute's type */
463  if ((retval = nc4_find_type(h5file_type, &type)))
464    BAIL(retval);
465
466         /* Retrieve the size of the base type */
467         if ((retval = nc4_get_typelen_mem(h5type->u.v.base_nc_typeid, 0, &base_typelen)))
468            BAIL(retval);
469
470         vldata1 = data;
471         if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t))))
472            BAIL(NC_ENOMEM);
473         for (i = 0; i < att->leni++)
474         {
475            att->vldata[i].len = vldata1[i].len;
476            if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len)))
477               BAIL(NC_ENOMEM);
478            memcpy(att->vldata[i].pvldata1[i].pbase_typelen * att->vldata[i].len);
479         }
480      }
481      else if (type_class == NC_STRING)
482      {
483        LOG((4, "copying array of NC_STRING"));
484        if (!(att->stdata = malloc(sizeof(char *) * att->len))) {
485          BAIL(NC_ENOMEM);
486        }
487
488        /* If we are overwriting an existing attribute,
489           specifically an NC_CHAR, we need to clean up
490           the pre-existing att->data. */
491        if (!new_att && att->data) {
492          free(att->data);
493          att->data = NULL;
494        }
495
496        for (i = 0; i < att->leni++)
497          {
498            if(NULL != ((char **)data)[i]) {
499              LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1));
500              if (!(att->stdata[i] = strdup(((char **)data)[i])))
501                BAIL(NC_ENOMEM);
502            }
503            else
504              att->stdata[i] = ((char **)data)[i];
505          }
506      }
507      else
508      {
509         /* [Re]allocate memory for the attribute data */
510         if (!new_att)
511            free (att->data);
512         if (!(att->data = malloc(att->len * type_size)))
513            BAIL(NC_ENOMEM);
514
515         /* Just copy the data, for non-atomic types */
516         if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM)
517            memcpy(att->datadatalen * type_size);
518         else
519         {
520            /* Data types are like religions, in that one can convert.  */
521            if ((retval = nc4_convert_type(dataatt->datamem_typefile_type,
522                                           len, &range_errorNULL,
523                                           (h5->cmode & NC_CLASSIC_MODEL), is_long, 0)))
524               BAIL(retval);
525         }
526      }
527   }
528   att->dirty = NC_TRUE;
529   att->created = NC_FALSE;
530
531   /* Mark attributes on variable dirty, so they get written */
532   if(var)
533       var->attr_dirty = NC_TRUE;
534
535 exit:
536   /* If there was an error return it, otherwise return any potential
537      range error value. If none, return NC_NOERR as usual.*/
538   if (retval)
539      return retval;
540   if (range_error)
541      return NC_ERANGE;
542   return NC_NOERR;
543}
544
545/* Learn about an att. All the nc4 nc_inq_ functions just call
546 * nc4_get_att to get the metadata on an attribute. */
547int
548NC4_inq_att(int ncid, int varid, const char *namenc_type *xtypep, size_t *lenp)
549{
550   NC *nc;
551   NC_HDF5_FILE_INFO_T *h5;
552
553   LOG((2, "nc_inq_att: ncid 0x%x varid %d name %s", ncidvaridname));
554
555   /* Find metadata. */
556   if (!(nc = nc4_find_nc_file(ncid,NULL)))
557      return NC_EBADID;
558
559   /* get netcdf-4 metadata */
560   h5 = NC4_DATA(nc);
561   assert(h5);
562
563   /* Handle netcdf-4 files. */
564   return nc4_get_att(ncidncvaridnamextypepNC_NATlenpNULL, 0, NULL);
565}
566
567/* Learn an attnum, given a name. */
568int
569NC4_inq_attid(int ncid, int varid, const char *name, int *attnump)
570{
571   NC *nc;
572   NC_HDF5_FILE_INFO_T *h5;
573   int stat;
574
575   LOG((2, "nc_inq_attid: ncid 0x%x varid %d name %s", ncidvaridname));
576
577   /* Find metadata. */
578   if (!(nc = nc4_find_nc_file(ncid,NULL)))
579      return NC_EBADID;
580
581   /* get netcdf-4 metadata */
582   h5 = NC4_DATA(nc);
583   assert(h5);
584
585   /* Handle netcdf-4 files. */
586   stat = nc4_get_att(ncidncvaridnameNULLNC_NAT,
587       NULLattnump, 0, NULL);
588   return stat;
589}
590
591
592/* Given an attnum, find the att's name. */
593int
594NC4_inq_attname(int ncid, int varid, int attnum, char *name)
595{
596   NC *nc;
597   NC_ATT_INFO_T *att;
598   NC_HDF5_FILE_INFO_T *h5;
599   int retval = NC_NOERR;
600
601   LOG((2, "nc_inq_attname: ncid 0x%x varid %d attnum %d",
602 ncidvaridattnum));
603
604   /* Find metadata. */
605   if (!(nc = nc4_find_nc_file(ncid,NULL)))
606      return NC_EBADID;
607
608   /* get netcdf-4 metadata */
609   h5 = NC4_DATA(nc);
610   assert(h5);
611
612   /* Handle netcdf-4 files. */
613   if ((retval = nc4_find_nc_att(ncidvaridNULLattnum, &att)))
614      return retval;
615
616   /* Get the name. */
617   if (name)
618      strcpy(nameatt->name);
619
620   return NC_NOERR;
621}
622
623/* I think all atts should be named the exact same thing, to avoid
624   confusion! */
625int
626NC4_rename_att(int ncid, int varid, const char *name,
627       const char *newname)
628{
629   NC *nc;
630   NC_GRP_INFO_T *grp;
631   NC_HDF5_FILE_INFO_T *h5;
632   NC_VAR_INFO_T *var = NULL;
633   NC_ATT_INFO_T *att, *list;
634   char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1];
635   hid_t datasetid = 0;
636   int retval = NC_NOERR;
637
638   if (!name || !newname)
639      return NC_EINVAL;
640
641   LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s",
642 ncidvaridnamenewname));
643
644   /* If the new name is too long, that's an error. */
645   if (strlen(newname) > NC_MAX_NAME)
646      return NC_EMAXNAME;
647
648   /* Find metadata for this file. */
649   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
650      return retval;
651
652   assert(h5 && grp);
653
654   /* If the file is read-only, return an error. */
655   if (h5->no_write)
656     return NC_EPERM;
657
658   /* Check and normalize the name. */
659   if ((retval = nc4_check_name(newnamenorm_newname)))
660      return retval;
661
662   /* Is norm_newname in use? */
663   if (varid == NC_GLOBAL)
664   {
665      list = grp->att;
666   }
667   else
668   {
669      for (var = grp->varvarvar = var->l.next)
670  if (var->varid == varid)
671  {
672     list = var->att;
673     break;
674  }
675      if (!var)
676  return NC_ENOTVAR;
677   }
678   for (att = listattatt = att->l.next)
679      if (!strncmp(att->namenorm_newnameNC_MAX_NAME))
680  return NC_ENAMEINUSE;
681
682   /* Normalize name and find the attribute. */
683   if ((retval = nc4_normalize_name(namenorm_name)))
684      return retval;
685   for (att = listattatt = att->l.next)
686      if (!strncmp(att->namenorm_nameNC_MAX_NAME))
687  break;
688   if (!att)
689      return NC_ENOTATT;
690
691   /* If we're not in define mode, new name must be of equal or
692      less size, if complying with strict NC3 rules. */
693   if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->name) &&
694       (h5->cmode & NC_CLASSIC_MODEL))
695      return NC_ENOTINDEFINE;
696
697   /* Delete the original attribute, if it exists in the HDF5 file. */
698   if (att->created)
699   {
700      if (varid == NC_GLOBAL)
701      {
702         if (H5Adelete(grp->hdf_grpidatt->name) < 0)
703     return NC_EHDFERR;
704      }
705      else
706      {
707  if ((retval = nc4_open_var_grp2(grpvarid, &datasetid)))
708     return retval;
709         if (H5Adelete(datasetidatt->name) < 0)
710     return NC_EHDFERR;
711      }
712      att->created = NC_FALSE;
713   }
714
715   /* Copy the new name into our metadata. */
716   free(att->name);
717   if (!(att->name = malloc((strlen(norm_newname) + 1) * sizeof(char))))
718      return NC_ENOMEM;
719   strcpy(att->namenorm_newname);
720   att->dirty = NC_TRUE;
721
722   /* Mark attributes on variable dirty, so they get written */
723   if(var)
724       var->attr_dirty = NC_TRUE;
725
726   return retval;
727}
728
729/* Delete an att. Rub it out. Push the button on it. Liquidate
730   it. Bump it off. Take it for a one-way ride. Terminate it. Drop the
731   bomb on it. You get the idea.
732   Ed Hartnett, 10/1/3
733*/
734int
735NC4_del_att(int ncid, int varid, const char *name)
736{
737   NC *nc;
738   NC_GRP_INFO_T *grp;
739   NC_HDF5_FILE_INFO_T *h5;
740   NC_ATT_INFO_T *att, *natt;
741   NC_VAR_INFO_T *var;
742   NC_ATT_INFO_T **attlist = NULL;
743   hid_t locid = 0, datasetid = 0;
744   int retval = NC_NOERR;
745
746   if (!name)
747      return NC_EINVAL;
748
749   LOG((2, "nc_del_att: ncid 0x%x varid %d name %s",
750 ncidvaridname));
751
752   /* Find metadata for this file. */
753   if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
754      return retval;
755
756   assert(h5 && grp);
757
758   /* If the file is read-only, return an error. */
759   if (h5->no_write)
760      return NC_EPERM;
761
762   /* If it's not in define mode, forget it. */
763   if (!(h5->flags & NC_INDEF))
764   {
765      if (h5->cmode & NC_CLASSIC_MODEL)
766  return NC_ENOTINDEFINE;
767      if ((retval = NC4_redef(ncid)))
768  BAIL(retval);
769   }
770
771   /* Get either the global or a variable attribute list. Also figure
772      out the HDF5 location it's attached to. */
773   if (varid == NC_GLOBAL)
774   {
775      attlist = &grp->att;
776      locid = grp->hdf_grpid;
777   }
778   else
779   {
780      for(var = grp->varvarvar = var->l.next)
781      {
782  if (var->varid == varid)
783  {
784     attlist = &var->att;
785     break;
786  }
787      }
788      if (!var)
789  return NC_ENOTVAR;
790      if (var->created)
791  locid = var->hdf_datasetid;
792   }
793
794   /* Now find the attribute by name or number. */
795   for (att = *attlistattatt = att->l.next)
796      if (!strcmp(att->namename))
797  break;
798
799   /* If att is NULL, we couldn't find the attribute. */
800   if (!att)
801      BAIL_QUIET(NC_ENOTATT);
802
803   /* Delete it from the HDF5 file, if it's been created. */
804   if (att->created)
805   {
806      assert(locid);
807
808      if(H5Adelete(locidatt->name) < 0)
809  BAIL(NC_EATTMETA);
810   }
811
812   /* Renumber all following attributes. */
813   for (natt = att->l.nextnattnatt = natt->l.next)
814      natt->attnum--;
815
816   /* Delete this attribute from this list. */
817   if ((retval = nc4_att_list_del(attlistatt)))
818      BAIL(retval);
819
820 exit:
821   if (datasetid > 0) H5Dclose(datasetid);
822   return retval;
823}
824
825/* Write an attribute with type conversion. */
826static int
827nc4_put_att_tc(int ncid, int varid, const char *namenc_type file_type,
828        nc_type mem_type, int mem_type_is_long, size_t len,
829        const void *op)
830{
831   NC *nc;
832   NC_HDF5_FILE_INFO_T *h5;
833
834   if (!name || strlen(name) > NC_MAX_NAME)
835      return NC_EBADNAME;
836
837   LOG((3, "nc4_put_att_tc: ncid 0x%x varid %d name %s file_type %d "
838 "mem_type %d len %d", ncidvaridnamefile_typemem_typelen));
839
840   /* The length needs to be positive (cast needed for braindead
841      systems with signed size_t). */
842   if((unsigned long) len > X_INT_MAX)
843      return NC_EINVAL;
844
845   /* Find metadata. */
846   if (!(nc = nc4_find_nc_file(ncid,NULL)))
847      return NC_EBADID;
848
849   /* get netcdf-4 metadata */
850   h5 = NC4_DATA(nc);
851   assert(h5);
852
853   if(nc->ext_ncid == ncid && varid == NC_GLOBAL) {
854      const char** reserved = NC_RESERVED_ATT_LIST;
855      for(;*reserved;reserved++) {
856 if(strcmp(name,*reserved)==0)
857     return NC_ENAMEINUSE;
858      }
859   }
860
861   if(varid != NC_GLOBAL) {
862      const char** reserved = NC_RESERVED_VARATT_LIST;
863      for(;*reserved;reserved++) {
864 if(strcmp(name,*reserved)==0)
865     return NC_ENAMEINUSE;
866      }
867   }
868
869   /* Otherwise, handle things the netcdf-4 way. */
870   return nc4_put_att(ncidncvaridnamefile_typemem_typelen,
871       mem_type_is_longop);
872}
873
874static int
875nc4_get_att_special(NC_HDF5_FILE_INFO_Th5, const char* name,
876                    nc_typefiletypepnc_type mem_type, size_t* lenp,
877                    int* attnump, int is_long, void* data)
878{
879    /* Fail if asking for att id */
880    if(attnump)
881 return NC_EATTMETA;
882
883    if(strcmp(name,NCPROPS)==0) {
884 char* propdata = NULL;
885 int stat = NC_NOERR;
886 int len;
887 if(h5->fileinfo->propattr.version == 0)
888     return NC_ENOTATT;
889 if(mem_type == NC_NATmem_type = NC_CHAR;
890 if(mem_type != NC_CHAR)
891     return NC_ECHAR;
892 if(filetypep) *filetypep = NC_CHAR;
893 stat = NC4_buildpropinfo(&h5->fileinfo->propattr, &propdata);
894 if(stat != NC_NOERR) return stat;
895 len = strlen(propdata);
896 if(lenp) *lenp = len;
897 if(datastrncpy((char*)data,propdata,len+1);
898 free(propdata);
899    } else if(strcmp(name,ISNETCDF4ATT)==0
900              || strcmp(name,SUPERBLOCKATT)==0) {
901 unsigned long long iv = 0;
902 if(filetypep) *filetypep = NC_INT;
903 if(lenp) *lenp = 1;
904 if(strcmp(name,SUPERBLOCKATT)==0)
905     iv = (unsigned long long)h5->fileinfo->superblockversion;
906 else /* strcmp(name,ISNETCDF4ATT)==0 */
907     iv = NC4_isnetcdf4(h5);
908 if(mem_type == NC_NATmem_type = NC_INT;
909 if(data)
910 switch (mem_type) {
911 case NC_BYTE: *((char*)data) = (char)iv; break;
912 case NC_SHORT: *((short*)data) = (short)iv; break;
913 case NC_INT: *((int*)data) = (int)iv; break;
914 case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break;
915 case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break;
916 case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break;
917 case NC_INT64: *((long long*)data) = (long long)iv; break;
918 case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break;
919 default:
920     return NC_ERANGE;
921 }
922    }
923    return NC_NOERR;
924}
925
926/* Read an attribute of any type, with type conversion. This may be
927 * called by any of the nc_get_att_* functions. */
928int
929nc4_get_att_tc(int ncid, int varid, const char *namenc_type mem_type,
930        int mem_type_is_long, void *ip)
931{
932   NC *nc;
933   NC_HDF5_FILE_INFO_T *h5;
934
935   LOG((3, "nc4_get_att_tc: ncid 0x%x varid %d name %s mem_type %d",
936 ncidvaridnamemem_type));
937
938   /* Find metadata. */
939   if (!(nc = nc4_find_nc_file(ncid,NULL)))
940      return NC_EBADID;
941
942   /* get netcdf-4 metadata */
943   h5 = NC4_DATA(nc);
944   assert(h5);
945
946   return nc4_get_att(ncidncvaridnameNULLmem_type,
947       NULLNULLmem_type_is_longip);
948}
949
950int
951NC4_put_att(int ncid, int varid, const char *namenc_type xtype,
952            size_t nelems, const void *valuenc_type memtype)
953{
954   return nc4_put_att_tc(ncidvaridnamextypememtype, 0, nelemsvalue);
955}
956
957int
958NC4_get_att(int ncid, int varid, const char *name, void *valuenc_type memtype)
959{
960   return nc4_get_att_tc(ncidvaridnamememtype, 0, value);
961}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed