NetCDF  4.7.4
nc4info.c
Go to the documentation of this file.
1 
10 #include "config.h"
11 #include "nc4internal.h"
12 #include "hdf5internal.h"
13 #include "nc_provenance.h"
14 #include "nclist.h"
15 #include "ncbytes.h"
16 
17 /* Provide a hack to suppress the writing of _NCProperties attribute.
18  This is for creating a file without _NCProperties for testing purposes.
19 */
20 #undef SUPPRESSNCPROPS
21 
22 /* Various Constants */
23 #define NCPROPS_MAX_NAME 1024 /* max key name size */
24 #define NCPROPS_MAX_VALUE 1024 /* max value size */
25 #define HDF5_MAX_NAME 1024
27 #define ESCAPECHARS "\\=|,"
28 
30 #define NCHECK(expr) {if((expr)!=NC_NOERR) {goto done;}}
31 
33 #define HCHECK(expr) {if((expr)<0) {ncstat = NC_EHDFERR; goto done;}}
34 
35 static int NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring);
36 static int NC4_write_ncproperties(NC_FILE_INFO_T* h5);
37 
38 static int globalpropinitialized = 0;
39 static NC4_Provenance globalprovenance;
40 
50 int
51 NC4_provenance_init(void)
52 {
53  int stat = NC_NOERR;
54  char* name = NULL;
55  char* value = NULL;
56  unsigned major,minor,release;
57  NCbytes* buffer = NULL; /* for constructing the global _NCProperties */
58  char printbuf[1024];
59  const char* p = NULL;
60 
61  if(globalpropinitialized)
62  return stat;
63 
64  /* Build _NCProperties info */
65 
66  /* Initialize globalpropinfo */
67  memset((void*)&globalprovenance,0,sizeof(NC4_Provenance));
68  globalprovenance.version = NCPROPS_VERSION;
69 
70  buffer = ncbytesnew();
71 
72  /* Insert version as first entry */
73  ncbytescat(buffer,NCPVERSION);
74  ncbytescat(buffer,"=");
75 
76  snprintf(printbuf,sizeof(printbuf),"%d",globalprovenance.version);
77  ncbytescat(buffer,printbuf);
78 
79  /* Insert the netcdf version */
80  ncbytesappend(buffer,NCPROPSSEP2);
81  ncbytescat(buffer,NCPNCLIB2);
82  ncbytescat(buffer,"=");
83  ncbytescat(buffer,PACKAGE_VERSION);
84 
85  /* Insert the HDF5 as underlying storage format library */
86  ncbytesappend(buffer,NCPROPSSEP2);
87  ncbytescat(buffer,NCPHDF5LIB2);
88  ncbytescat(buffer,"=");
89  if((stat = NC4_hdf5get_libversion(&major,&minor,&release))) goto done;
90  snprintf(printbuf,sizeof(printbuf),"%1u.%1u.%1u",major,minor,release);
91  ncbytescat(buffer,printbuf);
92 
93 #ifdef NCPROPERTIES_EXTRA
94  /* Add any extra fields */
95  p = NCPROPERTIES_EXTRA;
96  if(p[0] == NCPROPSSEP2) p++; /* If leading separator */
97  ncbytesappend(buffer,NCPROPSSEP2);
98  ncbytescat(buffer,p);
99 #endif
100  ncbytesnull(buffer);
101  globalprovenance.ncproperties = ncbytesextract(buffer);
102 
103 done:
104  ncbytesfree(buffer);
105  if(name != NULL) free(name);
106  if(value != NULL) free(value);
107  if(stat == NC_NOERR)
108  globalpropinitialized = 1; /* avoid repeating it */
109  return stat;
110 }
111 
118 int
119 NC4_provenance_finalize(void)
120 {
121  return NC4_clear_provenance(&globalprovenance);
122 }
123 
137 int
138 NC4_new_provenance(NC_FILE_INFO_T* file)
139 {
140  int ncstat = NC_NOERR;
141  NC4_Provenance* provenance = NULL;
142  int superblock = -1;
143 
144  LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id));
145 
146  assert(file->provenance.ncproperties == NULL); /* not yet defined */
147 
148  provenance = &file->provenance;
149  memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */
150 
151  /* Set the version */
152  provenance->version = globalprovenance.version;
153 
154  /* Set the superblock number */
155  if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done;
156  provenance->superblockversion = superblock;
157 
158  if(globalprovenance.ncproperties != NULL) {
159  if((provenance->ncproperties = strdup(globalprovenance.ncproperties)) == NULL)
160  {ncstat = NC_ENOMEM; goto done;}
161  }
162 
163 done:
164  if(ncstat) {
165  LOG((0,"Could not create _NCProperties attribute"));
166  }
167  return NC_NOERR;
168 }
169 
181 int
182 NC4_read_provenance(NC_FILE_INFO_T* file)
183 {
184  int ncstat = NC_NOERR;
185  NC4_Provenance* provenance = NULL;
186  int superblock = -1;
187  char* propstring = NULL;
188 
189  LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id));
190 
191  assert(file->provenance.version == 0); /* not yet defined */
192 
193  provenance = &file->provenance;
194  memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */
195 
196  /* Set the superblock number */
197  if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done;
198  provenance->superblockversion = superblock;
199 
200  /* Read the _NCProperties value from the file */
201  /* We do not return a size and assume the size is that upto the
202  first nul character */
203  if((ncstat = NC4_read_ncproperties(file,&propstring))) goto done;
204  provenance->ncproperties = propstring;
205  propstring = NULL;
206 
207 done:
208  nullfree(propstring);
209  if(ncstat) {
210  LOG((0,"Could not create _NCProperties attribute"));
211  }
212  return NC_NOERR;
213 }
214 
226 int
227 NC4_write_provenance(NC_FILE_INFO_T* file)
228 {
229  int ncstat = NC_NOERR;
230  if((ncstat = NC4_write_ncproperties(file)))
231  goto done;
232 done:
233  return ncstat;
234 }
235 
236 /* HDF5 Specific attribute read/write of _NCProperties */
237 static int
238 NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring)
239 {
240  int retval = NC_NOERR;
241  hid_t hdf5grpid = -1;
242  hid_t attid = -1;
243  hid_t aspace = -1;
244  hid_t atype = -1;
245  hid_t ntype = -1;
246  char* text = NULL;
247  H5T_class_t t_class;
248  hsize_t size;
249 
250  LOG((5, "%s", __func__));
251 
252  hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
253 
254  if(H5Aexists(hdf5grpid,NCPROPS) <= 0) { /* Does not exist */
255  /* File did not contain a _NCProperties attribute; leave empty */
256  goto done;
257  }
258 
259  /* NCPROPS Attribute exists, make sure it is legitimate */
260  attid = H5Aopen_name(hdf5grpid, NCPROPS);
261  assert(attid > 0);
262  aspace = H5Aget_space(attid);
263  atype = H5Aget_type(attid);
264  /* Verify atype and size */
265  t_class = H5Tget_class(atype);
266  if(t_class != H5T_STRING)
267  {retval = NC_EINVAL; goto done;}
268  size = H5Tget_size(atype);
269  if(size == 0)
270  {retval = NC_EINVAL; goto done;}
271  text = (char*)malloc(1+(size_t)size);
272  if(text == NULL)
273  {retval = NC_ENOMEM; goto done;}
274  if((ntype = H5Tget_native_type(atype, H5T_DIR_DEFAULT)) < 0)
275  {retval = NC_EHDFERR; goto done;}
276  if((H5Aread(attid, ntype, text)) < 0)
277  {retval = NC_EHDFERR; goto done;}
278  /* Make sure its null terminated */
279  text[(size_t)size] = '\0';
280  if(propstring) {*propstring = text; text = NULL;}
281 
282 done:
283  if(text != NULL) free(text);
284  /* Close out the HDF5 objects */
285  if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR;
286  if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR;
287  if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR;
288  if(ntype > 0 && H5Tclose(ntype) < 0) retval = NC_EHDFERR;
289 
290  /* For certain errors, actually fail, else log that attribute was invalid and ignore */
291  if(retval != NC_NOERR) {
292  if(retval != NC_ENOMEM && retval != NC_EHDFERR) {
293  LOG((0,"Invalid _NCProperties attribute: ignored"));
294  retval = NC_NOERR;
295  }
296  }
297  return retval;
298 }
299 
300 static int
301 NC4_write_ncproperties(NC_FILE_INFO_T* h5)
302 {
303 #ifdef SUPPRESSNCPROPERTY
304  return NC_NOERR;
305 #else
306  int retval = NC_NOERR;
307  hid_t hdf5grpid = -1;
308  hid_t attid = -1;
309  hid_t aspace = -1;
310  hid_t atype = -1;
311  size_t len = 0;
312  NC4_Provenance* prov = &h5->provenance;
313 
314  LOG((5, "%s", __func__));
315 
316  /* If the file is read-only, return an error. */
317  if (h5->no_write)
318  {retval = NC_EPERM; goto done;}
319 
320  hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
321 
322  if(H5Aexists(hdf5grpid,NCPROPS) > 0) /* Already exists, no overwrite */
323  goto done;
324 
325  /* Build the property if we have legit value */
326  if(prov->ncproperties != NULL) {
327  /* Build the HDF5 string type */
328  if ((atype = H5Tcopy(H5T_C_S1)) < 0)
329  {retval = NC_EHDFERR; goto done;}
330  if (H5Tset_strpad(atype, H5T_STR_NULLTERM) < 0)
331  {retval = NC_EHDFERR; goto done;}
332  if(H5Tset_cset(atype, H5T_CSET_ASCII) < 0)
333  {retval = NC_EHDFERR; goto done;}
334  len = strlen(prov->ncproperties);
335  if(H5Tset_size(atype, len) < 0)
336  {retval = NC_EFILEMETA; goto done;}
337  /* Create NCPROPS attribute */
338  if((aspace = H5Screate(H5S_SCALAR)) < 0)
339  {retval = NC_EFILEMETA; goto done;}
340  if ((attid = H5Acreate(hdf5grpid, NCPROPS, atype, aspace, H5P_DEFAULT)) < 0)
341  {retval = NC_EFILEMETA; goto done;}
342  if (H5Awrite(attid, atype, prov->ncproperties) < 0)
343  {retval = NC_EFILEMETA; goto done;}
344 /* Verify */
345 #if 0
346  {
347  hid_t spacev, typev;
348  hsize_t dsize, tsize;
349  typev = H5Aget_type(attid);
350  spacev = H5Aget_space(attid);
351  dsize = H5Aget_storage_size(attid);
352  tsize = H5Tget_size(typev);
353  fprintf(stderr,"dsize=%lu tsize=%lu\n",(unsigned long)dsize,(unsigned long)tsize);
354  }
355 #endif
356  }
357 
358 done:
359  /* Close out the HDF5 objects */
360  if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR;
361  if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR;
362  if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR;
363 
364  /* For certain errors, actually fail, else log that attribute was invalid and ignore */
365  switch (retval) {
366  case NC_ENOMEM:
367  case NC_EHDFERR:
368  case NC_EPERM:
369  case NC_EFILEMETA:
370  case NC_NOERR:
371  break;
372  default:
373  LOG((0,"Invalid _NCProperties attribute"));
374  retval = NC_NOERR;
375  break;
376  }
377  return retval;
378 #endif
379 }
380 
381 /**************************************************/
382 /* Utilities */
383 
384 /* Debugging */
385 
386 void
387 ncprintprovenance(NC4_Provenance* info)
388 {
389  fprintf(stderr,"[%p] version=%d superblockversion=%d ncproperties=|%s|\n",
390  info,
391  info->version,
392  info->superblockversion,
393  (info->ncproperties==NULL?"":info->ncproperties));
394 }
395 
405 int
406 NC4_clear_provenance(NC4_Provenance* prov)
407 {
408  LOG((5, "%s", __func__));
409 
410  if(prov == NULL) return NC_NOERR;
411  nullfree(prov->ncproperties);
412  memset(prov,0,sizeof(NC4_Provenance));
413  return NC_NOERR;
414 }
415 
416 #if 0
417 /* Unused functions */
418 
428 static int
429 properties_parse(const char* text0, NClist* pairs)
430 {
431  int ret = NC_NOERR;
432  char* p;
433  char* q;
434  char* text = NULL;
435 
436  if(text0 == NULL || strlen(text0) == 0)
437  goto done;
438 
439  text = strdup(text0);
440  if(text == NULL) return NC_ENOMEM;
441 
442  /* For back compatibility with version 1, translate '|' -> ',' */
443  for(p=text;*p;p++) {
444  if(*p == NCPROPSSEP1)
445  *p = NCPROPSSEP2;
446  }
447 
448  /* Walk and fill in ncinfo */
449  p = text;
450  while(*p) {
451  char* name = p;
452  char* value = NULL;
453  char* next = NULL;
454 
455  /* Delimit whole (key,value) pair */
456  q = locate(p,NCPROPSSEP2);
457  if(*q != '\0') /* Never go beyond the final nul term */
458  *q++ = '\0';
459  next = q;
460  /* split key and value */
461  q = locate(p,'=');
462  name = p;
463  *q++ = '\0';
464  value = q;
465  /* Set up p for next iteration */
466  p = next;
467  nclistpush(pairs,strdup(name));
468  nclistpush(pairs,strdup(value));
469  }
470 done:
471  if(text) free(text);
472  return ret;
473 }
474 
475 /* Locate a specific character and return its pointer
476  or EOS if not found
477  take \ escapes into account */
478 static char*
479 locate(char* p, char tag)
480 {
481  char* next;
482  int c;
483  assert(p != NULL);
484  for(next = p;(c = *next);next++) {
485  if(c == tag)
486  return next;
487  else if(c == '\\' && next[1] != '\0')
488  next++; /* skip escaped char */
489  }
490  return next; /* not found */
491 }
492 
493 /* Utility to transfer a string to a buffer with escaping */
494 static void
495 escapify(NCbytes* buffer, const char* s)
496 {
497  const char* p;
498  for(p=s;*p;p++) {
499  if(strchr(ESCAPECHARS,*p) != NULL)
500  ncbytesappend(buffer,'\\');
501  ncbytesappend(buffer,*p);
502  }
503 }
504 
518 static int
519 build_propstring(int version, NClist* list, char** spropp)
520 {
521  int stat = NC_NOERR;
522  int i;
523  NCbytes* buffer = NULL;
524  char sversion[64];
525 
526  LOG((5, "%s version=%d", __func__, version));
527 
528  if(spropp != NULL) *spropp = NULL;
529 
530  if(version == 0 || version > NCPROPS_VERSION) /* unknown case */
531  goto done;
532  if(list == NULL)
533  {stat = NC_EINVAL; goto done;}
534 
535  if((buffer = ncbytesnew()) == NULL)
536  {stat = NC_ENOMEM; goto done;}
537 
538  /* start with version */
539  ncbytescat(buffer,NCPVERSION);
540  ncbytesappend(buffer,'=');
541  /* Use current version */
542  snprintf(sversion,sizeof(sversion),"%d",NCPROPS_VERSION);
543  ncbytescat(buffer,sversion);
544 
545  for(i=0;i<nclistlength(list);i+=2) {
546  char* value, *name;
547  name = nclistget(list,i);
548  if(name == NULL) continue;
549  value = nclistget(list,i+1);
550  ncbytesappend(buffer,NCPROPSSEP2); /* terminate last entry */
551  escapify(buffer,name);
552  ncbytesappend(buffer,'=');
553  escapify(buffer,value);
554  }
555  /* Force null termination */
556  ncbytesnull(buffer);
557  if(spropp) *spropp = ncbytesextract(buffer);
558 
559 done:
560  if(buffer != NULL) ncbytesfree(buffer);
561  return stat;
562 }
563 
564 static int
565 properties_getversion(const char* propstring, int* versionp)
566 {
567  int ncstat = NC_NOERR;
568  int version = 0;
569  /* propstring should begin with "version=dddd" */
570  if(propstring == NULL || strlen(propstring) < strlen("version=") + strlen("1"))
571  {ncstat = NC_EINVAL; goto done;} /* illegal version */
572  if(memcmp(propstring,"version=",strlen("version=")) != 0)
573  {ncstat = NC_EINVAL; goto done;} /* illegal version */
574  propstring += strlen("version=");
575  /* get version */
576  version = atoi(propstring);
577  if(version < 0)
578  {ncstat = NC_EINVAL; goto done;} /* illegal version */
579  if(versionp) *versionp = version;
580 done:
581  return ncstat;
582 }
583 
596 static int
597 parse_provenance(NC4_Provenance* prov)
598 {
599  int ncstat = NC_NOERR;
600  char *name = NULL;
601  char *value = NULL;
602  int version = 0;
603  NClist* list = NULL;
604 
605  LOG((5, "%s: prov 0x%x", __func__, prov));
606 
607  if(prov->ncproperty == NULL || strlen(prov->ncproperty) < strlen("version="))
608  {ncstat = NC_EINVAL; goto done;}
609  if((list = nclistnew()) == NULL)
610  {ncstat = NC_ENOMEM; goto done;}
611 
612  /* Do we understand the version? */
613  if(prov->version > 0 && prov->version <= NCPROPS_VERSION) {/* recognized version */
614  if((ncstat=properties_parse(prov->ncproperty,list)))
615  goto done;
616  /* Remove version pair from properties list*/
617  if(nclistlength(list) < 2)
618  {ncstat = NC_EINVAL; goto done;} /* bad _NCProperties attribute */
619  /* Throw away the purported version=... */
620  nclistremove(list,0); /* version key */
621  nclistremove(list,0); /* version value */
622 
623  /* Now, rebuild to the latest version */
624  switch (version) {
625  default: break; /* do nothing */
626  case 1: {
627  int i;
628  for(i=0;i<nclistlength(list);i+=2) {
629  char* newname = NULL;
630  name = nclistget(list,i);
631  if(name == NULL) continue; /* ignore */
632  if(strcmp(name,NCPNCLIB1) == 0)
633  newname = NCPNCLIB2; /* change name */
634  else if(strcmp(name,NCPHDF5LIB1) == 0)
635  newname = NCPHDF5LIB2;
636  else continue; /* ignore */
637  /* Do any rename */
638  nclistset(list,i,strdup(newname));
639  if(name) {free(name); name = NULL;}
640  }
641  } break;
642  } /*switch*/
643  }
644  prov->properties = list;
645  list = NULL;
646 
647 done:
648  nclistfreeall(list);
649  if(name != NULL) free(name);
650  if(value != NULL) free(value);
651  return ncstat;
652 }
653 
663 static int
664 NC4_free_provenance(NC4_Provenance* prov)
665 {
666  LOG((5, "%s", __func__));
667 
668  if(prov == NULL) return NC_NOERR;
669  NC4_clear_provenance(prov);
670  free(prov);
671  return NC_NOERR;
672 }
673 
674 /* Utility to copy contents of the dfalt into an NCPROPINFO object */
675 static int
676 propinfo_default(NC4_Properties* dst, const NC4_Properties* dfalt)
677 {
678  int i;
679  if(dst->properties == NULL) {
680  dst->properties = nclistnew();
681  if(dst->properties == NULL) return NC_ENOMEM;
682  }
683  dst->version = dfalt->version;
684  for(i=0;i<nclistlength(dfalt->properties);i++) {
685  char* s = nclistget(dfalt->properties,i);
686  s = strdup(s);
687  if(s == NULL) return NC_ENOMEM;
688  nclistpush(dst->properties,s);
689  }
690  return NC_NOERR;
691 }
692 
693 #endif /*0*/
#define NC_ENOMEM
Memory allocation (malloc) failure.
Definition: netcdf.h:409
#define NC_EHDFERR
Error at HDF5 layer.
Definition: netcdf.h:442
#define NC_EFILEMETA
Problem with file metadata.
Definition: netcdf.h:446
#define NC_EINVAL
Invalid Argument.
Definition: netcdf.h:339
#define NC_EPERM
Write to read only.
Definition: netcdf.h:340
#define NC_NOERR
No Error.
Definition: netcdf.h:329
static int NC4_write_ncproperties(NC_FILE_INFO_T *h5)
Definition: nc4info.c:301

Return to the Main Unidata NetCDF page.
Generated on Mon Nov 16 2020 02:07:56 for NetCDF. NetCDF is a Unidata library.