NetCDF  4.6.1
 All Data Structures Files Functions Variables Typedefs Macros Modules Pages
nc4attr.c
Go to the documentation of this file.
1 /* Copyright 2003-2018, University Corporation for Atmospheric
2  * Research. See COPYRIGHT file for copying and redistribution
3  * conditions. */
18 #include "nc4internal.h"
19 #include "nc.h"
20 #include "nc4dispatch.h"
21 #include "ncdispatch.h"
22 
23 int nc4typelen(nc_type type);
24 
43 static int
44 nc4_get_att_special(NC_HDF5_FILE_INFO_T* h5, const char* name,
45  nc_type* filetypep, nc_type mem_type, size_t* lenp,
46  int* attnump, int is_long, void* data)
47 {
48  /* Fail if asking for att id */
49  if(attnump)
50  return NC_EATTMETA;
51 
52  if(strcmp(name,NCPROPS)==0) {
53  char* propdata = NULL;
54  int stat = NC_NOERR;
55  int len;
56  if(h5->fileinfo->propattr.version == 0)
57  return NC_ENOTATT;
58  if(mem_type == NC_NAT) mem_type = NC_CHAR;
59  if(mem_type != NC_CHAR)
60  return NC_ECHAR;
61  if(filetypep) *filetypep = NC_CHAR;
62  stat = NC4_buildpropinfo(&h5->fileinfo->propattr, &propdata);
63  if(stat != NC_NOERR) return stat;
64  len = strlen(propdata);
65  if(lenp) *lenp = len;
66  if(data) strncpy((char*)data,propdata,len+1);
67  free(propdata);
68  } else if(strcmp(name,ISNETCDF4ATT)==0
69  || strcmp(name,SUPERBLOCKATT)==0) {
70  unsigned long long iv = 0;
71  if(filetypep) *filetypep = NC_INT;
72  if(lenp) *lenp = 1;
73  if(strcmp(name,SUPERBLOCKATT)==0)
74  iv = (unsigned long long)h5->fileinfo->superblockversion;
75  else /* strcmp(name,ISNETCDF4ATT)==0 */
76  iv = NC4_isnetcdf4(h5);
77  if(mem_type == NC_NAT) mem_type = NC_INT;
78  if(data)
79  switch (mem_type) {
80  case NC_BYTE: *((char*)data) = (char)iv; break;
81  case NC_SHORT: *((short*)data) = (short)iv; break;
82  case NC_INT: *((int*)data) = (int)iv; break;
83  case NC_UBYTE: *((unsigned char*)data) = (unsigned char)iv; break;
84  case NC_USHORT: *((unsigned short*)data) = (unsigned short)iv; break;
85  case NC_UINT: *((unsigned int*)data) = (unsigned int)iv; break;
86  case NC_INT64: *((long long*)data) = (long long)iv; break;
87  case NC_UINT64: *((unsigned long long*)data) = (unsigned long long)iv; break;
88  default:
89  return NC_ERANGE;
90  }
91  }
92  return NC_NOERR;
93 }
94 
113 static int
114 nc4_get_att(int ncid, int varid, const char *name, nc_type *xtype,
115  nc_type mem_type, size_t *lenp, int *attnum, void *data)
116 {
117  NC *nc;
118  NC_GRP_INFO_T *grp;
119  NC_HDF5_FILE_INFO_T *h5;
120  NC_ATT_INFO_T *att = NULL;
121  int my_attnum = -1;
122  int need_to_convert = 0;
123  int range_error = NC_NOERR;
124  void *bufr = NULL;
125  size_t type_size;
126  char norm_name[NC_MAX_NAME + 1];
127  int i;
128  int retval;
129 
130  if (attnum)
131  my_attnum = *attnum;
132 
133  LOG((3, "%s: ncid 0x%x varid %d name %s attnum %d mem_type %d",
134  __func__, ncid, varid, name, my_attnum, mem_type));
135 
136  /* Find info for this file, group, and h5 info. */
137  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
138  return retval;
139 
140  /* Check varid */
141  if (varid != NC_GLOBAL) {
142  if (varid < 0 || varid >= grp->vars.nelems)
143  return NC_ENOTVAR;
144  if (grp->vars.value[varid] == NULL)
145  return NC_ENOTVAR;
146  assert(grp->vars.value[varid]->varid == varid);
147  }
148 
149  if (name == NULL)
150  BAIL(NC_EBADNAME);
151 
152  /* Normalize name. */
153  if ((retval = nc4_normalize_name(name, norm_name)))
154  BAIL(retval);
155 
156  /* If this is one of the reserved atts, use nc_get_att_special. */
157  if (nc->ext_ncid == ncid && varid == NC_GLOBAL) {
158  const char** sp;
159  for (sp = NC_RESERVED_SPECIAL_LIST; *sp; sp++) {
160  if (strcmp(name,*sp)==0) {
161  return nc4_get_att_special(h5, norm_name, xtype, mem_type, lenp,
162  attnum, 0, data);
163  }
164  }
165  }
166 
167  /* Find the attribute, if it exists. */
168  if ((retval = nc4_find_grp_att(grp, varid, norm_name, my_attnum, &att)))
169  return retval;
170 
171  /* If mem_type is NC_NAT, it means we want to use the attribute's
172  * file type as the mem type as well. */
173  if (mem_type == NC_NAT)
174  mem_type = att->nc_typeid;
175 
176  /* If the attribute is NC_CHAR, and the mem_type isn't, or vice
177  * versa, that's a freakish attempt to convert text to
178  * numbers. Some pervert out there is trying to pull a fast one!
179  * Send him an NC_ECHAR error. */
180  if (data && att->len)
181  if ((att->nc_typeid == NC_CHAR && mem_type != NC_CHAR) ||
182  (att->nc_typeid != NC_CHAR && mem_type == NC_CHAR))
183  BAIL(NC_ECHAR); /* take that, you freak! */
184 
185  /* Copy the info. */
186  if (lenp)
187  *lenp = att->len;
188  if (xtype)
189  *xtype = att->nc_typeid;
190  if (attnum) {
191  *attnum = att->attnum;
192  }
193 
194  /* Zero len attributes are easy to read! */
195  if (!att->len)
196  BAIL(NC_NOERR);
197 
198  /* Later on, we will need to know the size of this type. */
199  if ((retval = nc4_get_typelen_mem(h5, mem_type, 0, &type_size)))
200  BAIL(retval);
201 
202  /* We may have to convert data. Treat NC_CHAR the same as
203  * NC_UBYTE. If the mem_type is NAT, don't try any conversion - use
204  * the attribute's type. */
205  if (data && att->len && mem_type != att->nc_typeid &&
206  mem_type != NC_NAT &&
207  !(mem_type == NC_CHAR &&
208  (att->nc_typeid == NC_UBYTE || att->nc_typeid == NC_BYTE)))
209  {
210  if (!(bufr = malloc((size_t)(att->len * type_size))))
211  BAIL(NC_ENOMEM);
212  need_to_convert++;
213  if ((retval = nc4_convert_type(att->data, bufr, att->nc_typeid,
214  mem_type, (size_t)att->len, &range_error,
215  NULL, (h5->cmode & NC_CLASSIC_MODEL), 0, 0)))
216  BAIL(retval);
217 
218  /* For strict netcdf-3 rules, ignore erange errors between UBYTE
219  * and BYTE types. */
220  if ((h5->cmode & NC_CLASSIC_MODEL) &&
221  (att->nc_typeid == NC_UBYTE || att->nc_typeid == NC_BYTE) &&
222  (mem_type == NC_UBYTE || mem_type == NC_BYTE) &&
223  range_error)
224  range_error = 0;
225  }
226  else
227  {
228  bufr = att->data;
229  }
230 
231  /* If the caller wants data, copy it for him. If he hasn't
232  allocated enough memory for it, he will burn in segmentation
233  fault hell, writhing with the agony of undiscovered memory
234  bugs! */
235  if (data)
236  {
237  if (att->vldata)
238  {
239  size_t base_typelen;
240  hvl_t *vldest = data;
241  NC_TYPE_INFO_T *type;
242 
243  /* Get the type object for the attribute's type */
244  if ((retval = nc4_find_type(h5, att->nc_typeid, &type)))
245  BAIL(retval);
246 
247  /* Retrieve the size of the base type */
248  if ((retval = nc4_get_typelen_mem(h5, type->u.v.base_nc_typeid, 0, &base_typelen)))
249  BAIL(retval);
250 
251  for (i = 0; i < att->len; i++)
252  {
253  vldest[i].len = att->vldata[i].len;
254  if (!(vldest[i].p = malloc(vldest[i].len * base_typelen)))
255  BAIL(NC_ENOMEM);
256  memcpy(vldest[i].p, att->vldata[i].p, vldest[i].len * base_typelen);
257  }
258  }
259  else if (att->stdata)
260  {
261  for (i = 0; i < att->len; i++)
262  {
263  /* Check for NULL pointer for string (valid in HDF5) */
264  if(att->stdata[i])
265  {
266  if (!(((char **)data)[i] = strdup(att->stdata[i])))
267  BAIL(NC_ENOMEM);
268  }
269  else
270  ((char **)data)[i] = att->stdata[i];
271  }
272  }
273  else
274  {
275  memcpy(data, bufr, (size_t)(att->len * type_size));
276  }
277  }
278 
279 exit:
280  if (need_to_convert)
281  free(bufr);
282  if (range_error)
283  retval = NC_ERANGE;
284  return retval;
285 }
286 
301 int
302 NC4_inq_att(int ncid, int varid, const char *name, nc_type *xtypep,
303  size_t *lenp)
304 {
305  LOG((2, "%s: ncid 0x%x varid %d name %s", __func__, ncid, varid, name));
306  return nc4_get_att(ncid, varid, name, xtypep, NC_NAT, lenp, NULL, NULL);
307 }
308 
320 int
321 NC4_inq_attid(int ncid, int varid, const char *name, int *attnump)
322 {
323  LOG((2, "%s: ncid 0x%x varid %d name %s", __func__, ncid, varid, name));
324  return nc4_get_att(ncid, varid, name, NULL, NC_NAT, NULL, attnump, NULL);
325 }
326 
327 
340 int
341 NC4_inq_attname(int ncid, int varid, int attnum, char *name)
342 {
343  NC *nc;
344  NC_ATT_INFO_T *att;
345  NC_HDF5_FILE_INFO_T *h5;
346  int retval = NC_NOERR;
347 
348  LOG((2, "nc_inq_attname: ncid 0x%x varid %d attnum %d",
349  ncid, varid, attnum));
350 
351  /* Find metadata. */
352  if (!(nc = nc4_find_nc_file(ncid,NULL)))
353  return NC_EBADID;
354 
355  /* get netcdf-4 metadata */
356  h5 = NC4_DATA(nc);
357  assert(h5);
358 
359  /* Handle netcdf-4 files. */
360  if ((retval = nc4_find_nc_att(ncid, varid, NULL, attnum, &att)))
361  return retval;
362 
363  /* Get the name. */
364  if (name)
365  strcpy(name, att->name);
366 
367  return NC_NOERR;
368 }
369 
383 int
384 NC4_rename_att(int ncid, int varid, const char *name, const char *newname)
385 {
386  NC *nc;
387  NC_GRP_INFO_T *grp;
388  NC_HDF5_FILE_INFO_T *h5;
389  NC_VAR_INFO_T *var = NULL;
390  NC_ATT_INFO_T *att, *list;
391  char norm_newname[NC_MAX_NAME + 1], norm_name[NC_MAX_NAME + 1];
392  hid_t datasetid = 0;
393  int retval = NC_NOERR;
394 
395  if (!name || !newname)
396  return NC_EINVAL;
397 
398  LOG((2, "nc_rename_att: ncid 0x%x varid %d name %s newname %s",
399  ncid, varid, name, newname));
400 
401  /* If the new name is too long, that's an error. */
402  if (strlen(newname) > NC_MAX_NAME)
403  return NC_EMAXNAME;
404 
405  /* Find info for this file, group, and h5 info. */
406  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
407  return retval;
408  assert(h5 && grp && h5);
409 
410  /* If the file is read-only, return an error. */
411  if (h5->no_write)
412  return NC_EPERM;
413 
414  /* Check and normalize the name. */
415  if ((retval = nc4_check_name(newname, norm_newname)))
416  return retval;
417 
418  /* Is norm_newname in use? */
419  if (varid == NC_GLOBAL)
420  {
421  list = grp->att;
422  }
423  else
424  {
425  if (varid < 0 || varid >= grp->vars.nelems)
426  return NC_ENOTVAR;
427  var = grp->vars.value[varid];
428  if (!var) return NC_ENOTVAR;
429  assert(var->varid == varid);
430  list = var->att;
431  }
432  for (att = list; att; att = att->l.next)
433  if (!strncmp(att->name, norm_newname, NC_MAX_NAME))
434  return NC_ENAMEINUSE;
435 
436  /* Normalize name and find the attribute. */
437  if ((retval = nc4_normalize_name(name, norm_name)))
438  return retval;
439  for (att = list; att; att = att->l.next)
440  if (!strncmp(att->name, norm_name, NC_MAX_NAME))
441  break;
442  if (!att)
443  return NC_ENOTATT;
444 
445  /* If we're not in define mode, new name must be of equal or
446  less size, if complying with strict NC3 rules. */
447  if (!(h5->flags & NC_INDEF) && strlen(norm_newname) > strlen(att->name) &&
448  (h5->cmode & NC_CLASSIC_MODEL))
449  return NC_ENOTINDEFINE;
450 
451  /* Delete the original attribute, if it exists in the HDF5 file. */
452  if (att->created)
453  {
454  if (varid == NC_GLOBAL)
455  {
456  if (H5Adelete(grp->hdf_grpid, att->name) < 0)
457  return NC_EHDFERR;
458  }
459  else
460  {
461  if ((retval = nc4_open_var_grp2(grp, varid, &datasetid)))
462  return retval;
463  if (H5Adelete(datasetid, att->name) < 0)
464  return NC_EHDFERR;
465  }
466  att->created = NC_FALSE;
467  }
468 
469  /* Copy the new name into our metadata. */
470  free(att->name);
471  if (!(att->name = malloc((strlen(norm_newname) + 1) * sizeof(char))))
472  return NC_ENOMEM;
473  strcpy(att->name, norm_newname);
474  att->dirty = NC_TRUE;
475 
476  /* Mark attributes on variable dirty, so they get written */
477  if(var)
478  var->attr_dirty = NC_TRUE;
479 
480  return retval;
481 }
482 
496 int
497 NC4_del_att(int ncid, int varid, const char *name)
498 {
499  NC *nc;
500  NC_GRP_INFO_T *grp;
501  NC_HDF5_FILE_INFO_T *h5;
502  NC_ATT_INFO_T *att, *natt;
503  NC_VAR_INFO_T *var;
504  NC_ATT_INFO_T **attlist = NULL;
505  hid_t locid = 0, datasetid = 0;
506  int retval = NC_NOERR;
507 
508  if (!name)
509  return NC_EINVAL;
510 
511  LOG((2, "nc_del_att: ncid 0x%x varid %d name %s",
512  ncid, varid, name));
513 
514  /* Find info for this file, group, and h5 info. */
515  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
516  return retval;
517 
518  assert(h5 && grp);
519 
520  /* If the file is read-only, return an error. */
521  if (h5->no_write)
522  return NC_EPERM;
523 
524  /* If it's not in define mode, forget it. */
525  if (!(h5->flags & NC_INDEF))
526  {
527  if (h5->cmode & NC_CLASSIC_MODEL)
528  return NC_ENOTINDEFINE;
529  if ((retval = NC4_redef(ncid)))
530  BAIL(retval);
531  }
532 
533  /* Get either the global or a variable attribute list. Also figure
534  out the HDF5 location it's attached to. */
535  if (varid == NC_GLOBAL)
536  {
537  attlist = &grp->att;
538  locid = grp->hdf_grpid;
539  }
540  else
541  {
542  if (varid < 0 || varid >= grp->vars.nelems)
543  return NC_ENOTVAR;
544  var = grp->vars.value[varid];
545  if (!var) return NC_ENOTVAR;
546  attlist = &var->att;
547  assert(var->varid == varid);
548  if (var->created)
549  locid = var->hdf_datasetid;
550  }
551 
552  /* Now find the attribute by name or number. */
553  for (att = *attlist; att; att = att->l.next)
554  if (!strcmp(att->name, name))
555  break;
556 
557  /* If att is NULL, we couldn't find the attribute. */
558  if (!att)
559  BAIL_QUIET(NC_ENOTATT);
560 
561  /* Delete it from the HDF5 file, if it's been created. */
562  if (att->created)
563  {
564  assert(locid);
565 
566  if(H5Adelete(locid, att->name) < 0)
567  BAIL(NC_EATTMETA);
568  }
569 
570  /* Renumber all following attributes. */
571  for (natt = att->l.next; natt; natt = natt->l.next)
572  natt->attnum--;
573 
574  /* Delete this attribute from this list. */
575  if ((retval = nc4_att_list_del(attlist, att)))
576  BAIL(retval);
577 
578 exit:
579  if (datasetid > 0) H5Dclose(datasetid);
580  return retval;
581 }
582 
603 int
604 NC4_put_att(int ncid, int varid, const char *name, nc_type file_type,
605  size_t len, const void *data, nc_type mem_type)
606 {
607  NC *nc;
608  NC_GRP_INFO_T *grp;
609  NC_HDF5_FILE_INFO_T *h5;
610  NC_VAR_INFO_T *var = NULL;
611  NC_ATT_INFO_T *att, **attlist = NULL;
612  char norm_name[NC_MAX_NAME + 1];
613  nc_bool_t new_att = NC_FALSE;
614  int retval = NC_NOERR, range_error = 0;
615  size_t type_size;
616  int i;
617  int ret;
618 
619  /* Find info for this file, group, and h5 info. */
620  if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
621  return ret;
622  assert(nc && grp && h5);
623 
624  /* Find att, if it exists. (Must check varid first or nc_test will
625  * break.) */
626  if (varid == NC_GLOBAL)
627  attlist = &grp->att;
628  else
629  {
630  if (varid < 0 || varid >= grp->vars.nelems)
631  return NC_ENOTVAR;
632  var = grp->vars.value[varid];
633  if (!var) return NC_ENOTVAR;
634  attlist = &var->att;
635  assert(var->varid == varid);
636  }
637 
638  /* The length needs to be positive (cast needed for braindead
639  systems with signed size_t). */
640  if((unsigned long) len > X_INT_MAX)
641  return NC_EINVAL;
642 
643  /* Check name before LOG statement. */
644  if (!name || strlen(name) > NC_MAX_NAME)
645  return NC_EBADNAME;
646 
647  LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d",
648  __func__, ncid, varid, name, file_type, mem_type, len));
649 
650  /* If len is not zero, then there must be some data. */
651  if (len && !data)
652  return NC_EINVAL;
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(name, norm_name)))
660  return retval;
661 
662  /* Check that a reserved NC_GLOBAL att name is not being used. */
663  if (nc->ext_ncid == ncid && varid == NC_GLOBAL) {
664  const char** reserved = NC_RESERVED_ATT_LIST;
665  for ( ; *reserved; reserved++) {
666  if (strcmp(name, *reserved)==0)
667  return NC_ENAMEINUSE;
668  }
669  }
670 
671  /* Check that a reserved variable att name is not being used. */
672  if (varid != NC_GLOBAL) {
673  const char** reserved = NC_RESERVED_VARATT_LIST;
674  for ( ; *reserved; reserved++) {
675  if (strcmp(name, *reserved) == 0)
676  return NC_ENAMEINUSE;
677  }
678  }
679 
680  /* See if there is already an attribute with this name. */
681  for (att = *attlist; att; att = att->l.next)
682  if (!strcmp(att->name, norm_name))
683  break;
684 
685  LOG((1, "%s: ncid 0x%x varid %d name %s file_type %d mem_type %d len %d",
686  __func__, ncid, varid, name, file_type, mem_type, len));
687 
688  if (!att)
689  {
690  /* If this is a new att, require define mode. */
691  if (!(h5->flags & NC_INDEF))
692  {
693  if (h5->cmode & NC_CLASSIC_MODEL)
694  return NC_EINDEFINE;
695  if ((retval = NC4_redef(ncid)))
696  BAIL(retval);
697  }
698  new_att = NC_TRUE;
699  }
700  else
701  {
702  /* For an existing att, if we're not in define mode, the len
703  must not be greater than the existing len for classic model. */
704  if (!(h5->flags & NC_INDEF) &&
705  len * nc4typelen(file_type) > (size_t)att->len * nc4typelen(att->nc_typeid))
706  {
707  if (h5->cmode & NC_CLASSIC_MODEL)
708  return NC_EINDEFINE;
709  if ((retval = NC4_redef(ncid)))
710  BAIL(retval);
711  }
712  }
713 
714  /* We must have two valid types to continue. */
715  if (file_type == NC_NAT || mem_type == NC_NAT)
716  return NC_EBADTYPE;
717 
718  /* Get information about this type. */
719  if ((retval = nc4_get_typelen_mem(h5, file_type, 0, &type_size)))
720  return retval;
721 
722  /* No character conversions are allowed. */
723  if (file_type != mem_type &&
724  (file_type == NC_CHAR || mem_type == NC_CHAR ||
725  file_type == NC_STRING || mem_type == NC_STRING))
726  return NC_ECHAR;
727 
728  /* For classic mode file, only allow atts with classic types to be
729  * created. */
730  if (h5->cmode & NC_CLASSIC_MODEL && file_type > NC_DOUBLE)
731  return NC_ESTRICTNC3;
732 
733  /* Add to the end of the attribute list, if this att doesn't
734  already exist. */
735  if (new_att)
736  {
737  LOG((3, "adding attribute %s to the list...", norm_name));
738  if ((ret = nc4_att_list_add(attlist, &att)))
739  BAIL (ret);
740  if (!(att->name = strdup(norm_name)))
741  return NC_ENOMEM;
742  }
743 
744  /* Now fill in the metadata. */
745  att->dirty = NC_TRUE;
746  att->nc_typeid = file_type;
747 
748  /* If this att has vlen or string data, release it before we lose the length value. */
749  if (att->stdata)
750  {
751  for (i = 0; i < att->len; i++)
752  if(att->stdata[i])
753  free(att->stdata[i]);
754  free(att->stdata);
755  att->stdata = NULL;
756  }
757  if (att->vldata)
758  {
759  for (i = 0; i < att->len; i++)
760  nc_free_vlen(&att->vldata[i]);
761  free(att->vldata);
762  att->vldata = NULL;
763  }
764 
765  att->len = len;
766  if (att->l.prev)
767  att->attnum = ((NC_ATT_INFO_T *)att->l.prev)->attnum + 1;
768  else
769  att->attnum = 0;
770 
771  /* If this is the _FillValue attribute, then we will also have to
772  * copy the value to the fill_vlue pointer of the NC_VAR_INFO_T
773  * struct for this var. (But ignore a global _FillValue
774  * attribute). */
775  if (!strcmp(att->name, _FillValue) && varid != NC_GLOBAL)
776  {
777  int size;
778 
779  /* Fill value must be same type and have exactly one value */
780  if (att->nc_typeid != var->type_info->nc_typeid)
781  return NC_EBADTYPE;
782  if (att->len != 1)
783  return NC_EINVAL;
784 
785  /* If we already wrote to the dataset, then return an error. */
786  if (var->written_to)
787  return NC_ELATEFILL;
788 
789  /* Get the length of the veriable data type. */
790  if ((retval = nc4_get_typelen_mem(grp->nc4_info, var->type_info->nc_typeid, 0,
791  &type_size)))
792  return retval;
793 
794  /* Already set a fill value? Now I'll have to free the old
795  * one. Make up your damn mind, would you? */
796  if (var->fill_value)
797  {
798  if (var->type_info->nc_type_class == NC_VLEN)
799  {
800  if ((retval = nc_free_vlen(var->fill_value)))
801  return retval;
802  }
803  else if (var->type_info->nc_type_class == NC_STRING)
804  {
805  if (*(char **)var->fill_value)
806  free(*(char **)var->fill_value);
807  }
808  free(var->fill_value);
809  }
810 
811  /* Determine the size of the fill value in bytes. */
812  if (var->type_info->nc_type_class == NC_VLEN)
813  size = sizeof(hvl_t);
814  else if (var->type_info->nc_type_class == NC_STRING)
815  size = sizeof(char *);
816  else
817  size = type_size;
818 
819  /* Allocate space for the fill value. */
820  if (!(var->fill_value = calloc(1, size)))
821  return NC_ENOMEM;
822 
823  /* Copy the fill_value. */
824  LOG((4, "Copying fill value into metadata for variable %s", var->name));
825  if (var->type_info->nc_type_class == NC_VLEN)
826  {
827  nc_vlen_t *in_vlen = (nc_vlen_t *)data, *fv_vlen = (nc_vlen_t *)(var->fill_value);
828 
829  fv_vlen->len = in_vlen->len;
830  if (!(fv_vlen->p = malloc(size * in_vlen->len)))
831  return NC_ENOMEM;
832  memcpy(fv_vlen->p, in_vlen->p, in_vlen->len * size);
833  }
834  else if (var->type_info->nc_type_class == NC_STRING)
835  {
836  if (*(char **)data)
837  {
838  if (!(*(char **)(var->fill_value) = malloc(strlen(*(char **)data) + 1)))
839  return NC_ENOMEM;
840  strcpy(*(char **)var->fill_value, *(char **)data);
841  }
842  else
843  *(char **)var->fill_value = NULL;
844  }
845  else
846  memcpy(var->fill_value, data, type_size);
847 
848  /* Indicate that the fill value was changed, if the variable has already
849  * been created in the file, so the dataset gets deleted and re-created. */
850  if (var->created)
851  var->fill_val_changed = NC_TRUE;
852  }
853 
854  /* Copy the attribute data, if there is any. VLENs and string
855  * arrays have to be handled specially. */
856  if (att->len)
857  {
858  nc_type type_class; /* Class of attribute's type */
859 
860  /* Get class for this type. */
861  if ((retval = nc4_get_typeclass(h5, file_type, &type_class)))
862  return retval;
863 
864  assert(data);
865  if (type_class == NC_VLEN)
866  {
867  const hvl_t *vldata1;
868  NC_TYPE_INFO_T *type;
869  size_t base_typelen;
870 
871  /* Get the type object for the attribute's type */
872  if ((retval = nc4_find_type(h5, file_type, &type)))
873  BAIL(retval);
874 
875  /* Retrieve the size of the base type */
876  if ((retval = nc4_get_typelen_mem(h5, type->u.v.base_nc_typeid, 0, &base_typelen)))
877  BAIL(retval);
878 
879  vldata1 = data;
880  if (!(att->vldata = (nc_vlen_t*)malloc(att->len * sizeof(hvl_t))))
881  BAIL(NC_ENOMEM);
882  for (i = 0; i < att->len; i++)
883  {
884  att->vldata[i].len = vldata1[i].len;
885  if (!(att->vldata[i].p = malloc(base_typelen * att->vldata[i].len)))
886  BAIL(NC_ENOMEM);
887  memcpy(att->vldata[i].p, vldata1[i].p, base_typelen * att->vldata[i].len);
888  }
889  }
890  else if (type_class == NC_STRING)
891  {
892  LOG((4, "copying array of NC_STRING"));
893  if (!(att->stdata = malloc(sizeof(char *) * att->len))) {
894  BAIL(NC_ENOMEM);
895  }
896 
897  /* If we are overwriting an existing attribute,
898  specifically an NC_CHAR, we need to clean up
899  the pre-existing att->data. */
900  if (!new_att && att->data) {
901  free(att->data);
902  att->data = NULL;
903  }
904 
905  for (i = 0; i < att->len; i++)
906  {
907  if(NULL != ((char **)data)[i]) {
908  LOG((5, "copying string %d of size %d", i, strlen(((char **)data)[i]) + 1));
909  if (!(att->stdata[i] = strdup(((char **)data)[i])))
910  BAIL(NC_ENOMEM);
911  }
912  else
913  att->stdata[i] = ((char **)data)[i];
914  }
915  }
916  else
917  {
918  /* [Re]allocate memory for the attribute data */
919  if (!new_att)
920  free (att->data);
921  if (!(att->data = malloc(att->len * type_size)))
922  BAIL(NC_ENOMEM);
923 
924  /* Just copy the data, for non-atomic types */
925  if (type_class == NC_OPAQUE || type_class == NC_COMPOUND || type_class == NC_ENUM)
926  memcpy(att->data, data, len * type_size);
927  else
928  {
929  /* Data types are like religions, in that one can convert. */
930  if ((retval = nc4_convert_type(data, att->data, mem_type, file_type,
931  len, &range_error, NULL,
932  (h5->cmode & NC_CLASSIC_MODEL), 0, 0)))
933  BAIL(retval);
934  }
935  }
936  }
937  att->dirty = NC_TRUE;
938  att->created = NC_FALSE;
939 
940  /* Mark attributes on variable dirty, so they get written */
941  if(var)
942  var->attr_dirty = NC_TRUE;
943 
944 exit:
945  /* If there was an error return it, otherwise return any potential
946  range error value. If none, return NC_NOERR as usual.*/
947  if (retval)
948  return retval;
949  if (range_error)
950  return NC_ERANGE;
951  return NC_NOERR;
952 }
953 
967 int
968 NC4_get_att(int ncid, int varid, const char *name, void *value, nc_type memtype)
969 {
970  return nc4_get_att(ncid, varid, name, NULL, memtype, NULL, NULL, value);
971 }
#define _FillValue
Name of fill value attribute.
Definition: netcdf.h:112
#define NC_ENOMEM
Memory allocation (malloc) failure.
Definition: netcdf.h:395
#define NC_CHAR
ISO/ASCII character.
Definition: netcdf.h:35
#define NC_UBYTE
unsigned 1 byte int
Definition: netcdf.h:41
#define NC_CLASSIC_MODEL
Enforce classic model on netCDF-4.
Definition: netcdf.h:135
#define NC_ERANGE
Math result not representable.
Definition: netcdf.h:394
#define NC_UINT
unsigned 4-byte int
Definition: netcdf.h:43
#define NC_EHDFERR
Error at HDF5 layer.
Definition: netcdf.h:427
#define NC_OPAQUE
opaque types
Definition: netcdf.h:53
#define NC_ELATEFILL
Attempt to define fill value when data already exists.
Definition: netcdf.h:448
#define NC_INT64
signed 8-byte int
Definition: netcdf.h:44
#define NC_STRING
string
Definition: netcdf.h:46
#define NC_ENOTINDEFINE
Operation not allowed in data mode.
Definition: netcdf.h:331
#define NC_DOUBLE
double precision floating point number
Definition: netcdf.h:40
int nc_type
The nc_type type is just an int.
Definition: netcdf.h:24
#define NC_BYTE
signed 1 byte integer
Definition: netcdf.h:34
#define NC_EINDEFINE
Operation not allowed in define mode.
Definition: netcdf.h:340
size_t len
Length of VL data (in base type units)
Definition: netcdf.h:668
#define NC_ENAMEINUSE
String match to name in use.
Definition: netcdf.h:354
#define NC_EATTMETA
Problem with attribute metadata.
Definition: netcdf.h:433
#define NC_VLEN
vlen (variable-length) types
Definition: netcdf.h:52
#define NC_EBADTYPE
Not a netcdf data type.
Definition: netcdf.h:357
#define NC_EBADNAME
Attribute or variable name contains illegal characters.
Definition: netcdf.h:387
#define NC_EINVAL
Invalid Argument.
Definition: netcdf.h:325
#define NC_INT
signed 4 byte integer
Definition: netcdf.h:37
#define NC_ESTRICTNC3
Attempting netcdf-4 operation on strict nc3 netcdf-4 file.
Definition: netcdf.h:438
#define NC_MAX_NAME
Maximum for classic library.
Definition: netcdf.h:265
void * p
Pointer to VL data.
Definition: netcdf.h:669
#define NC_NAT
Not A Type.
Definition: netcdf.h:33
EXTERNL int nc_free_vlen(nc_vlen_t *vl)
Free memory in a VLEN object.
Definition: dvlen.c:31
#define NC_USHORT
unsigned 2-byte int
Definition: netcdf.h:42
#define NC_EBADID
Not a netcdf id.
Definition: netcdf.h:322
This is the type of arrays of vlens.
Definition: netcdf.h:667
#define NC_SHORT
signed 2 byte integer
Definition: netcdf.h:36
#define NC_ENOTVAR
Variable not found.
Definition: netcdf.h:369
#define NC_EMAXNAME
NC_MAX_NAME exceeded.
Definition: netcdf.h:373
#define NC_EPERM
Write to read only.
Definition: netcdf.h:326
#define NC_NOERR
No Error.
Definition: netcdf.h:315
#define NC_ENUM
enum types
Definition: netcdf.h:54
#define NC_ECHAR
Attempt to convert between text & numbers.
Definition: netcdf.h:376
#define NC_COMPOUND
compound types
Definition: netcdf.h:55
#define NC_GLOBAL
Attribute id to put/get a global attribute.
Definition: netcdf.h:238
#define NC_ENOTATT
Attribute not found.
Definition: netcdf.h:355
#define NC_UINT64
unsigned 8-byte int
Definition: netcdf.h:45

Return to the Main Unidata NetCDF page.
Generated on Fri May 11 2018 21:22:18 for NetCDF. NetCDF is a Unidata library.