[409] | 1 | /** \file \internal |
---|
| 2 | Internal netcdf-4 functions. |
---|
| 3 | |
---|
| 4 | This file contains functions internal to the netcdf4 library. None of |
---|
| 5 | the functions in this file are exposed in the exetnal API. These |
---|
| 6 | functions all relate to the manipulation of netcdf-4's in-memory |
---|
| 7 | buffer of metadata information, i.e. the linked list of NC_FILE_INFO_T |
---|
| 8 | structs. |
---|
| 9 | |
---|
| 10 | Copyright 2003-2011, University Corporation for Atmospheric |
---|
| 11 | Research. See the COPYRIGHT file for copying and redistribution |
---|
| 12 | conditions. |
---|
| 13 | |
---|
| 14 | */ |
---|
| 15 | #include "config.h" |
---|
| 16 | #include "nc4internal.h" |
---|
| 17 | #include "nc.h" /* from libsrc */ |
---|
| 18 | #include "ncdispatch.h" /* from libdispatch */ |
---|
| 19 | #include <utf8proc.h> |
---|
| 20 | |
---|
| 21 | #define MEGABYTE 1048576 |
---|
| 22 | |
---|
| 23 | /* These are the default chunk cache sizes for HDF5 files created or |
---|
| 24 | * opened with netCDF-4. */ |
---|
| 25 | extern size_t nc4_chunk_cache_size; |
---|
| 26 | extern size_t nc4_chunk_cache_nelems; |
---|
| 27 | extern float nc4_chunk_cache_preemption; |
---|
| 28 | |
---|
| 29 | /* This is to track opened HDF5 objects to make sure they are |
---|
| 30 | * closed. */ |
---|
| 31 | #ifdef EXTRA_TESTS |
---|
| 32 | extern int num_spaces; |
---|
| 33 | #endif /* EXTRA_TESTS */ |
---|
| 34 | |
---|
| 35 | #ifdef LOGGING |
---|
| 36 | /* This is the severity level of messages which will be logged. Use |
---|
| 37 | severity 0 for errors, 1 for important log messages, 2 for less |
---|
| 38 | important, etc. */ |
---|
| 39 | int nc_log_level = -1; |
---|
| 40 | |
---|
| 41 | #endif /* LOGGING */ |
---|
| 42 | |
---|
| 43 | /* Check and normalize and name. */ |
---|
| 44 | int |
---|
| 45 | nc4_check_name(const char *name, char *norm_name) |
---|
| 46 | { |
---|
| 47 | char *temp; |
---|
| 48 | int retval; |
---|
| 49 | |
---|
| 50 | /* Check the length. */ |
---|
| 51 | if (strlen(name) > NC_MAX_NAME) |
---|
| 52 | return NC_EMAXNAME; |
---|
| 53 | |
---|
| 54 | /* Make sure this is a valid netcdf name. This should be done |
---|
| 55 | * before the name is normalized, because it gives better error |
---|
| 56 | * codes for bad utf8 strings. */ |
---|
| 57 | if ((retval = NC_check_name(name))) |
---|
| 58 | return retval; |
---|
| 59 | |
---|
| 60 | /* Normalize the name. */ |
---|
| 61 | if (!(temp = (char *)utf8proc_NFC((const unsigned char *)name))) |
---|
| 62 | return NC_EINVAL; |
---|
| 63 | strcpy(norm_name, temp); |
---|
| 64 | free(temp); |
---|
| 65 | |
---|
| 66 | return NC_NOERR; |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | /* Given a varid, find its shape. For unlimited dimensions, return |
---|
| 70 | the current number of records. */ |
---|
| 71 | static int |
---|
| 72 | find_var_shape_grp(NC_GRP_INFO_T *grp, int varid, int *ndims, |
---|
| 73 | int *dimid, size_t *dimlen) |
---|
| 74 | { |
---|
| 75 | hid_t datasetid = 0, spaceid = 0; |
---|
| 76 | NC_VAR_INFO_T *var; |
---|
| 77 | hsize_t *h5dimlen = NULL, *h5dimlenmax = NULL; |
---|
| 78 | int d, dataset_ndims = 0; |
---|
| 79 | int retval = NC_NOERR; |
---|
| 80 | |
---|
| 81 | /* Find this var. */ |
---|
| 82 | for (var = grp->var; var; var = var->next) |
---|
| 83 | if (var->varid == varid) |
---|
| 84 | break; |
---|
| 85 | if (!var) |
---|
| 86 | return NC_ENOTVAR; |
---|
| 87 | |
---|
| 88 | /* Get the dimids and the ndims for this var. */ |
---|
| 89 | if (ndims) |
---|
| 90 | *ndims = var->ndims; |
---|
| 91 | |
---|
| 92 | if (dimid) |
---|
| 93 | for (d = 0; d < var->ndims; d++) |
---|
| 94 | dimid[d] = var->dimids[d]; |
---|
| 95 | |
---|
| 96 | if (dimlen) |
---|
| 97 | { |
---|
| 98 | /* If the var hasn't been created yet, its size is 0. */ |
---|
| 99 | if (!var->created) |
---|
| 100 | { |
---|
| 101 | for (d = 0; d < var->ndims; d++) |
---|
| 102 | dimlen[d] = 0; |
---|
| 103 | } |
---|
| 104 | else |
---|
| 105 | { |
---|
| 106 | /* Get the number of records in the dataset. */ |
---|
| 107 | if ((retval = nc4_open_var_grp2(grp, var->varid, &datasetid))) |
---|
| 108 | BAIL(retval); |
---|
| 109 | if ((spaceid = H5Dget_space(datasetid)) < 0) |
---|
| 110 | BAIL(NC_EHDFERR); |
---|
| 111 | #ifdef EXTRA_TESTS |
---|
| 112 | num_spaces++; |
---|
| 113 | #endif |
---|
| 114 | /* If it's a scalar dataset, it has length one. */ |
---|
| 115 | if (H5Sget_simple_extent_type(spaceid) == H5S_SCALAR) |
---|
| 116 | { |
---|
| 117 | dimlen[0] = 1; |
---|
| 118 | } |
---|
| 119 | else |
---|
| 120 | { |
---|
| 121 | /* Check to make sure ndims is right, then get the len of each |
---|
| 122 | dim in the space. */ |
---|
| 123 | if ((dataset_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) |
---|
| 124 | BAIL(NC_EHDFERR); |
---|
| 125 | if (ndims && dataset_ndims != *ndims) |
---|
| 126 | BAIL(NC_EHDFERR); |
---|
| 127 | if (!(h5dimlen = malloc(dataset_ndims * sizeof(hsize_t)))) |
---|
| 128 | BAIL(NC_ENOMEM); |
---|
| 129 | if (!(h5dimlenmax = malloc(dataset_ndims * sizeof(hsize_t)))) |
---|
| 130 | BAIL(NC_ENOMEM); |
---|
| 131 | if ((dataset_ndims = H5Sget_simple_extent_dims(spaceid, |
---|
| 132 | h5dimlen, h5dimlenmax)) < 0) |
---|
| 133 | BAIL(NC_EHDFERR); |
---|
| 134 | LOG((5, "find_var_shape_nc: varid %d len %d max: %d", |
---|
| 135 | varid, (int)h5dimlen[0], (int)h5dimlenmax[0])); |
---|
| 136 | for (d=0; d<dataset_ndims; d++) |
---|
| 137 | dimlen[d] = h5dimlen[d]; |
---|
| 138 | } |
---|
| 139 | } |
---|
| 140 | } |
---|
| 141 | |
---|
| 142 | exit: |
---|
| 143 | if (spaceid > 0 && H5Sclose(spaceid) < 0) |
---|
| 144 | BAIL2(NC_EHDFERR); |
---|
| 145 | #ifdef EXTRA_TESTS |
---|
| 146 | num_spaces--; |
---|
| 147 | #endif |
---|
| 148 | if (h5dimlen) free(h5dimlen); |
---|
| 149 | if (h5dimlenmax) free(h5dimlenmax); |
---|
| 150 | return retval; |
---|
| 151 | } |
---|
| 152 | |
---|
| 153 | /* Given an NC_FILE_INFO_T pointer, add the necessary stuff for a |
---|
| 154 | * netcdf-4 file. */ |
---|
| 155 | int |
---|
| 156 | nc4_nc4f_list_add(NC_FILE_INFO_T *nc, const char *path, int mode) |
---|
| 157 | { |
---|
| 158 | NC_HDF5_FILE_INFO_T *h5; |
---|
| 159 | NC_GRP_INFO_T *grp; |
---|
| 160 | |
---|
| 161 | assert(nc && !nc->nc4_info && path); |
---|
| 162 | |
---|
| 163 | /* The NC_FILE_INFO_T was allocated and inited by |
---|
| 164 | ncfunc.c before this function is called. We need to malloc and |
---|
| 165 | initialize the substructure NC_HDF_FILE_INFO_T. */ |
---|
| 166 | if (!(nc->nc4_info = calloc(1, sizeof(NC_HDF5_FILE_INFO_T)))) |
---|
| 167 | return NC_ENOMEM; |
---|
| 168 | h5 = nc->nc4_info; |
---|
| 169 | |
---|
| 170 | /* Hang on to the filename for nc_abort. */ |
---|
| 171 | if (!(h5->path = malloc((strlen(path) + 1) * sizeof(char)))) |
---|
| 172 | return NC_ENOMEM; |
---|
| 173 | strcpy(h5->path, path); |
---|
| 174 | |
---|
| 175 | /* Hang on to cmode, and note that we're in define mode. */ |
---|
| 176 | h5->cmode = mode | NC_INDEF; |
---|
| 177 | |
---|
| 178 | /* The next_typeid needs to be set beyond the end of our atomic |
---|
| 179 | * types. */ |
---|
| 180 | h5->next_typeid = NC_FIRSTUSERTYPEID; |
---|
| 181 | |
---|
| 182 | /* There's always at least one open group - the root |
---|
| 183 | * group. Allocate space for one group's worth of information. Set |
---|
| 184 | * its hdf id, name, and a pointer to it's file structure. */ |
---|
| 185 | return nc4_grp_list_add(&(h5->root_grp), h5->next_nc_grpid++, |
---|
| 186 | NULL, nc, NC_GROUP_NAME, &grp); |
---|
| 187 | } |
---|
| 188 | /* /\* Given an ncid, find the relevant group and return a pointer to */ |
---|
| 189 | /* * it. *\/ */ |
---|
| 190 | /* NC_GRP_INFO_T * */ |
---|
| 191 | /* find_nc_grp(int ncid) */ |
---|
| 192 | /* { */ |
---|
| 193 | /* NC_FILE_INFO_T *f; */ |
---|
| 194 | |
---|
| 195 | /* for (f = nc_file; f; f = f->next) */ |
---|
| 196 | /* { */ |
---|
| 197 | /* if (f->ext_ncid == (ncid & FILE_ID_MASK)) */ |
---|
| 198 | /* { */ |
---|
| 199 | /* assert(f->nc4_info && f->nc4_info->root_grp); */ |
---|
| 200 | /* return nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)); */ |
---|
| 201 | /* } */ |
---|
| 202 | /* } */ |
---|
| 203 | |
---|
| 204 | /* return NULL; */ |
---|
| 205 | /* } */ |
---|
| 206 | |
---|
| 207 | /* Given an ncid, find the relevant group and return a pointer to it, |
---|
| 208 | * return an error of this is not a netcdf-4 file (or if strict nc3 is |
---|
| 209 | * turned on for this file.) */ |
---|
| 210 | |
---|
| 211 | |
---|
| 212 | int |
---|
| 213 | nc4_find_nc4_grp(int ncid, NC_GRP_INFO_T **grp) |
---|
| 214 | { |
---|
| 215 | NC_FILE_INFO_T *f = nc4_find_nc_file(ncid); |
---|
| 216 | if(f == NULL) return NC_EBADID; |
---|
| 217 | |
---|
| 218 | /* No netcdf-3 files allowed! */ |
---|
| 219 | if (!f->nc4_info) return NC_ENOTNC4; |
---|
| 220 | assert(f->nc4_info->root_grp); |
---|
| 221 | |
---|
| 222 | /* This function demands netcdf-4 files without strict nc3 |
---|
| 223 | * rules.*/ |
---|
| 224 | if (f->nc4_info->cmode & NC_CLASSIC_MODEL) return NC_ESTRICTNC3; |
---|
| 225 | |
---|
| 226 | /* If we can't find it, the grp id part of ncid is bad. */ |
---|
| 227 | if (!(*grp = nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)))) |
---|
| 228 | return NC_EBADID; |
---|
| 229 | return NC_NOERR; |
---|
| 230 | } |
---|
| 231 | |
---|
| 232 | /* Given an ncid, find the relevant group and return a pointer to it, |
---|
| 233 | * also set a pointer to the nc4_info struct of the related file. For |
---|
| 234 | * netcdf-3 files, *h5 will be set to NULL. */ |
---|
| 235 | int |
---|
| 236 | nc4_find_grp_h5(int ncid, NC_GRP_INFO_T **grp, NC_HDF5_FILE_INFO_T **h5) |
---|
| 237 | { |
---|
| 238 | NC_FILE_INFO_T *f = nc4_find_nc_file(ncid); |
---|
| 239 | if(f == NULL) return NC_EBADID; |
---|
| 240 | if (f->nc4_info) { |
---|
| 241 | assert(f->nc4_info->root_grp); |
---|
| 242 | /* If we can't find it, the grp id part of ncid is bad. */ |
---|
| 243 | if (!(*grp = nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)))) |
---|
| 244 | return NC_EBADID; |
---|
| 245 | *h5 = (*grp)->file->nc4_info; |
---|
| 246 | assert(*h5); |
---|
| 247 | } else { |
---|
| 248 | *h5 = NULL; |
---|
| 249 | *grp = NULL; |
---|
| 250 | } |
---|
| 251 | return NC_NOERR; |
---|
| 252 | } |
---|
| 253 | |
---|
| 254 | int |
---|
| 255 | nc4_find_nc_grp_h5(int ncid, NC_FILE_INFO_T **nc, NC_GRP_INFO_T **grp, |
---|
| 256 | NC_HDF5_FILE_INFO_T **h5) |
---|
| 257 | { |
---|
| 258 | NC_FILE_INFO_T *f = nc4_find_nc_file(ncid); |
---|
| 259 | if(f == NULL) return NC_EBADID; |
---|
| 260 | *nc = f; |
---|
| 261 | if (f->nc4_info) { |
---|
| 262 | assert(f->nc4_info->root_grp); |
---|
| 263 | /* If we can't find it, the grp id part of ncid is bad. */ |
---|
| 264 | if (!(*grp = nc4_rec_find_grp(f->nc4_info->root_grp, (ncid & GRP_ID_MASK)))) |
---|
| 265 | return NC_EBADID; |
---|
| 266 | |
---|
| 267 | *h5 = (*grp)->file->nc4_info; |
---|
| 268 | assert(*h5); |
---|
| 269 | } else { |
---|
| 270 | *h5 = NULL; |
---|
| 271 | *grp = NULL; |
---|
| 272 | } |
---|
| 273 | return NC_NOERR; |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | /* Recursively hunt for a group id. */ |
---|
| 277 | NC_GRP_INFO_T * |
---|
| 278 | nc4_rec_find_grp(NC_GRP_INFO_T *start_grp, int target_nc_grpid) |
---|
| 279 | { |
---|
| 280 | NC_GRP_INFO_T *g, *res; |
---|
| 281 | |
---|
| 282 | assert(start_grp); |
---|
| 283 | |
---|
| 284 | /* Is this the group we are searching for? */ |
---|
| 285 | if (start_grp->nc_grpid == target_nc_grpid) |
---|
| 286 | return start_grp; |
---|
| 287 | |
---|
| 288 | /* Shake down the kids. */ |
---|
| 289 | if (start_grp->children) |
---|
| 290 | for (g = start_grp->children; g; g = g->next) |
---|
| 291 | if ((res = nc4_rec_find_grp(g, target_nc_grpid))) |
---|
| 292 | return res; |
---|
| 293 | |
---|
| 294 | /* Can't find if. Fate, why do you mock me? */ |
---|
| 295 | return NULL; |
---|
| 296 | } |
---|
| 297 | |
---|
| 298 | /* Given an ncid and varid, get pointers to the group and var |
---|
| 299 | * metadata. */ |
---|
| 300 | int |
---|
| 301 | nc4_find_g_var_nc(NC_FILE_INFO_T *nc, int ncid, int varid, |
---|
| 302 | NC_GRP_INFO_T **grp, NC_VAR_INFO_T **var) |
---|
| 303 | { |
---|
| 304 | /* Find the group info. */ |
---|
| 305 | assert(grp && var && nc && nc->nc4_info && nc->nc4_info->root_grp); |
---|
| 306 | *grp = nc4_rec_find_grp(nc->nc4_info->root_grp, (ncid & GRP_ID_MASK)); |
---|
| 307 | |
---|
| 308 | /* Find the var info. */ |
---|
| 309 | for ((*var) = (*grp)->var; (*var); (*var) = (*var)->next) |
---|
| 310 | if ((*var)->varid == varid) |
---|
| 311 | break; |
---|
| 312 | if (!(*var)) |
---|
| 313 | return NC_ENOTVAR; |
---|
| 314 | |
---|
| 315 | return NC_NOERR; |
---|
| 316 | } |
---|
| 317 | |
---|
| 318 | /* Find a dim in a grp (or parents). */ |
---|
| 319 | int |
---|
| 320 | nc4_find_dim(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T **dim, |
---|
| 321 | NC_GRP_INFO_T **dim_grp) |
---|
| 322 | { |
---|
| 323 | NC_GRP_INFO_T *g, *dg = NULL; |
---|
| 324 | int finished = 0; |
---|
| 325 | |
---|
| 326 | assert(grp && dim); |
---|
| 327 | |
---|
| 328 | /* Find the dim info. */ |
---|
| 329 | for (g = grp; g && !finished; g = g->parent) |
---|
| 330 | for ((*dim) = g->dim; (*dim); (*dim) = (*dim)->next) |
---|
| 331 | if ((*dim)->dimid == dimid) |
---|
| 332 | { |
---|
| 333 | dg = g; |
---|
| 334 | finished++; |
---|
| 335 | break; |
---|
| 336 | } |
---|
| 337 | |
---|
| 338 | /* If we didn't find it, return an error. */ |
---|
| 339 | if (!(*dim)) |
---|
| 340 | return NC_EBADDIM; |
---|
| 341 | |
---|
| 342 | /* Give the caller the group the dimension is in. */ |
---|
| 343 | if (dim_grp) |
---|
| 344 | *dim_grp = dg; |
---|
| 345 | |
---|
| 346 | return NC_NOERR; |
---|
| 347 | } |
---|
| 348 | |
---|
| 349 | /* Recursively hunt for a HDF type id. */ |
---|
| 350 | NC_TYPE_INFO_T * |
---|
| 351 | nc4_rec_find_hdf_type(NC_GRP_INFO_T *start_grp, hid_t target_hdf_typeid) |
---|
| 352 | { |
---|
| 353 | NC_GRP_INFO_T *g; |
---|
| 354 | NC_TYPE_INFO_T *type, *res; |
---|
| 355 | htri_t equal; |
---|
| 356 | |
---|
| 357 | assert(start_grp); |
---|
| 358 | |
---|
| 359 | /* Does this group have the type we are searching for? */ |
---|
| 360 | for (type = start_grp->type; type; type = type->next) |
---|
| 361 | { |
---|
| 362 | if ((equal = H5Tequal(type->native_typeid ? type->native_typeid : type->hdf_typeid, target_hdf_typeid)) < 0) |
---|
| 363 | return NULL; |
---|
| 364 | if (equal) |
---|
| 365 | return type; |
---|
| 366 | } |
---|
| 367 | |
---|
| 368 | /* Shake down the kids. */ |
---|
| 369 | if (start_grp->children) |
---|
| 370 | for (g = start_grp->children; g; g = g->next) |
---|
| 371 | if ((res = nc4_rec_find_hdf_type(g, target_hdf_typeid))) |
---|
| 372 | return res; |
---|
| 373 | |
---|
| 374 | /* Can't find if. Fate, why do you mock me? */ |
---|
| 375 | return NULL; |
---|
| 376 | } |
---|
| 377 | |
---|
| 378 | /* Recursively hunt for a netCDF type id. */ |
---|
| 379 | NC_TYPE_INFO_T * |
---|
| 380 | nc4_rec_find_nc_type(NC_GRP_INFO_T *start_grp, nc_type target_nc_typeid) |
---|
| 381 | { |
---|
| 382 | NC_GRP_INFO_T *g; |
---|
| 383 | NC_TYPE_INFO_T *type, *res; |
---|
| 384 | |
---|
| 385 | assert(start_grp); |
---|
| 386 | |
---|
| 387 | /* Does this group have the type we are searching for? */ |
---|
| 388 | for (type = start_grp->type; type; type = type->next) |
---|
| 389 | if (type->nc_typeid == target_nc_typeid) |
---|
| 390 | return type; |
---|
| 391 | |
---|
| 392 | /* Shake down the kids. */ |
---|
| 393 | if (start_grp->children) |
---|
| 394 | for (g = start_grp->children; g; g = g->next) |
---|
| 395 | if ((res = nc4_rec_find_nc_type(g, target_nc_typeid))) |
---|
| 396 | return res; |
---|
| 397 | |
---|
| 398 | /* Can't find if. Fate, why do you mock me? */ |
---|
| 399 | return NULL; |
---|
| 400 | } |
---|
| 401 | |
---|
| 402 | /* Recursively hunt for a netCDF type by name. */ |
---|
| 403 | NC_TYPE_INFO_T * |
---|
| 404 | nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name) |
---|
| 405 | { |
---|
| 406 | NC_GRP_INFO_T *g; |
---|
| 407 | NC_TYPE_INFO_T *type, *res; |
---|
| 408 | |
---|
| 409 | assert(start_grp); |
---|
| 410 | |
---|
| 411 | /* Does this group have the type we are searching for? */ |
---|
| 412 | for (type = start_grp->type; type; type = type->next) |
---|
| 413 | if (!strcmp(type->name, name)) |
---|
| 414 | return type; |
---|
| 415 | |
---|
| 416 | /* Search subgroups. */ |
---|
| 417 | if (start_grp->children) |
---|
| 418 | for (g = start_grp->children; g; g = g->next) |
---|
| 419 | if ((res = nc4_rec_find_named_type(g, name))) |
---|
| 420 | return res; |
---|
| 421 | |
---|
| 422 | /* Can't find if. Oh, woe is me! */ |
---|
| 423 | return NULL; |
---|
| 424 | } |
---|
| 425 | |
---|
| 426 | /* Use a netCDF typeid to find a type in a type_list. */ |
---|
| 427 | int |
---|
| 428 | nc4_find_type(NC_HDF5_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **type) |
---|
| 429 | { |
---|
| 430 | if (typeid < 0 || !type) |
---|
| 431 | return NC_EINVAL; |
---|
| 432 | *type = NULL; |
---|
| 433 | |
---|
| 434 | /* Atomic types don't have associated NC_TYPE_INFO_T struct, just |
---|
| 435 | * return NOERR. */ |
---|
| 436 | if (typeid <= NC_STRING) |
---|
| 437 | return NC_NOERR; |
---|
| 438 | |
---|
| 439 | /* Find the type. */ |
---|
| 440 | if(!(*type = nc4_rec_find_nc_type(h5->root_grp, typeid))) |
---|
| 441 | return NC_EBADTYPID; |
---|
| 442 | |
---|
| 443 | return NC_NOERR; |
---|
| 444 | } |
---|
| 445 | |
---|
| 446 | /* Find the actual length of a dim by checking the length of that dim |
---|
| 447 | * in all variables that use it, in grp or children. *len must be |
---|
| 448 | * initialized to zero before this function is called. */ |
---|
| 449 | int |
---|
| 450 | nc4_find_dim_len(NC_GRP_INFO_T *grp, int dimid, size_t **len) |
---|
| 451 | { |
---|
| 452 | NC_GRP_INFO_T *g; |
---|
| 453 | NC_VAR_INFO_T *var; |
---|
| 454 | int d, ndims, dimids[NC_MAX_DIMS]; |
---|
| 455 | size_t dimlen[NC_MAX_DIMS]; |
---|
| 456 | int retval; |
---|
| 457 | |
---|
| 458 | assert(grp && len); |
---|
| 459 | LOG((3, "nc4_find_dim_len: grp->name %s dimid %d", grp->name, dimid)); |
---|
| 460 | |
---|
| 461 | /* If there are any groups, call this function recursively on |
---|
| 462 | * them. */ |
---|
| 463 | for (g = grp->children; g; g = g->next) |
---|
| 464 | if ((retval = nc4_find_dim_len(g, dimid, len))) |
---|
| 465 | return retval; |
---|
| 466 | |
---|
| 467 | /* For all variables in this group, find the ones that use this |
---|
| 468 | * dimension, and remember the max length. */ |
---|
| 469 | for (var = grp->var; var; var = var->next) |
---|
| 470 | { |
---|
| 471 | /* Find dimensions of this var. */ |
---|
| 472 | if ((retval = find_var_shape_grp(grp, var->varid, &ndims, |
---|
| 473 | dimids, dimlen))) |
---|
| 474 | return retval; |
---|
| 475 | |
---|
| 476 | /* Check for any dimension that matches dimid. If found, check |
---|
| 477 | * if its length is longer than *lenp. */ |
---|
| 478 | for (d = 0; d < ndims; d++) |
---|
| 479 | { |
---|
| 480 | if (dimids[d] == dimid) |
---|
| 481 | { |
---|
| 482 | /* Remember the max length in *lenp. */ |
---|
| 483 | **len = dimlen[d] > **len ? dimlen[d] : **len; |
---|
| 484 | break; |
---|
| 485 | } |
---|
| 486 | } |
---|
| 487 | } |
---|
| 488 | |
---|
| 489 | return NC_NOERR; |
---|
| 490 | } |
---|
| 491 | |
---|
| 492 | /* Given a group, find an att. */ |
---|
| 493 | int |
---|
| 494 | nc4_find_grp_att(NC_GRP_INFO_T *grp, int varid, const char *name, int attnum, |
---|
| 495 | NC_ATT_INFO_T **att) |
---|
| 496 | { |
---|
| 497 | NC_VAR_INFO_T *var; |
---|
| 498 | NC_ATT_INFO_T *attlist = NULL; |
---|
| 499 | |
---|
| 500 | assert(grp && grp->name); |
---|
| 501 | LOG((4, "nc4_find_grp_att: grp->name %s varid %d name %s attnum %d", |
---|
| 502 | grp->name, varid, name, attnum)); |
---|
| 503 | |
---|
| 504 | /* Get either the global or a variable attribute list. */ |
---|
| 505 | if (varid == NC_GLOBAL) |
---|
| 506 | attlist = grp->att; |
---|
| 507 | else |
---|
| 508 | { |
---|
| 509 | for(var = grp->var; var; var = var->next) |
---|
| 510 | { |
---|
| 511 | if (var->varid == varid) |
---|
| 512 | { |
---|
| 513 | attlist = var->att; |
---|
| 514 | break; |
---|
| 515 | } |
---|
| 516 | } |
---|
| 517 | if (!var) |
---|
| 518 | return NC_ENOTVAR; |
---|
| 519 | } |
---|
| 520 | |
---|
| 521 | /* Now find the attribute by name or number. If a name is provided, |
---|
| 522 | * ignore the attnum. */ |
---|
| 523 | for (*att = attlist; *att; *att = (*att)->next) |
---|
| 524 | if ((name && !strcmp((*att)->name, name)) || |
---|
| 525 | (!name && (*att)->attnum == attnum)) |
---|
| 526 | return NC_NOERR; |
---|
| 527 | |
---|
| 528 | /* If we get here, we couldn't find the attribute. */ |
---|
| 529 | return NC_ENOTATT; |
---|
| 530 | } |
---|
| 531 | |
---|
| 532 | /* Given an ncid, varid, and name or attnum, find and return pointer |
---|
| 533 | to NC_ATT_INFO_T metadata. */ |
---|
| 534 | int |
---|
| 535 | nc4_find_nc_att(int ncid, int varid, const char *name, int attnum, |
---|
| 536 | NC_ATT_INFO_T **att) |
---|
| 537 | { |
---|
| 538 | NC_GRP_INFO_T *grp; |
---|
| 539 | NC_HDF5_FILE_INFO_T *h5; |
---|
| 540 | NC_VAR_INFO_T *var; |
---|
| 541 | NC_ATT_INFO_T *attlist = NULL; |
---|
| 542 | int retval; |
---|
| 543 | |
---|
| 544 | LOG((4, "nc4_find_nc_att: ncid 0x%x varid %d name %s attnum %d", |
---|
| 545 | ncid, varid, name, attnum)); |
---|
| 546 | |
---|
| 547 | /* Find info for this file and group, and set pointer to each. */ |
---|
| 548 | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
---|
| 549 | return retval; |
---|
| 550 | assert(grp && h5); |
---|
| 551 | |
---|
| 552 | /* Get either the global or a variable attribute list. */ |
---|
| 553 | if (varid == NC_GLOBAL) |
---|
| 554 | attlist = grp->att; |
---|
| 555 | else |
---|
| 556 | { |
---|
| 557 | for(var = grp->var; var; var = var->next) |
---|
| 558 | { |
---|
| 559 | if (var->varid == varid) |
---|
| 560 | { |
---|
| 561 | attlist = var->att; |
---|
| 562 | break; |
---|
| 563 | } |
---|
| 564 | } |
---|
| 565 | if (!var) |
---|
| 566 | return NC_ENOTVAR; |
---|
| 567 | } |
---|
| 568 | |
---|
| 569 | /* Now find the attribute by name or number. If a name is provided, ignore the attnum. */ |
---|
| 570 | for (*att = attlist; *att; *att = (*att)->next) |
---|
| 571 | if ((name && !strcmp((*att)->name, name)) || |
---|
| 572 | (!name && (*att)->attnum == attnum)) |
---|
| 573 | return NC_NOERR; |
---|
| 574 | |
---|
| 575 | /* If we get here, we couldn't find the attribute. */ |
---|
| 576 | return NC_ENOTATT; |
---|
| 577 | } |
---|
| 578 | |
---|
| 579 | void |
---|
| 580 | nc4_file_list_free(void) |
---|
| 581 | { |
---|
| 582 | free_NCList(); |
---|
| 583 | } |
---|
| 584 | |
---|
| 585 | |
---|
| 586 | int |
---|
| 587 | NC4_new_nc(NC** ncpp) |
---|
| 588 | { |
---|
| 589 | NC_FILE_INFO_T** ncp; |
---|
| 590 | /* Allocate memory for this info. */ |
---|
| 591 | if (!(ncp = calloc(1, sizeof(NC_FILE_INFO_T)))) |
---|
| 592 | return NC_ENOMEM; |
---|
| 593 | if(ncpp) *ncpp = (NC*)ncp; |
---|
| 594 | return NC_NOERR; |
---|
| 595 | } |
---|
| 596 | |
---|
| 597 | int |
---|
| 598 | nc4_file_list_add(NC_FILE_INFO_T** ncp, NC_Dispatch* dispatch) |
---|
| 599 | { |
---|
| 600 | NC_FILE_INFO_T *nc; |
---|
| 601 | int status = NC_NOERR; |
---|
| 602 | |
---|
| 603 | /* Allocate memory for this info; use the dispatcher to do this */ |
---|
| 604 | status = dispatch->new_nc((NC**)&nc); |
---|
| 605 | if(status) return status; |
---|
| 606 | |
---|
| 607 | /* Add this file to the list. */ |
---|
| 608 | if ((status = add_to_NCList((NC *)nc))) |
---|
| 609 | { |
---|
| 610 | if(nc && nc->ext_ncid > 0) |
---|
| 611 | { |
---|
| 612 | del_from_NCList((NC *)nc); |
---|
| 613 | free(nc); |
---|
| 614 | } |
---|
| 615 | return status; |
---|
| 616 | } |
---|
| 617 | |
---|
| 618 | /* Return a pointer to the new struct. */ |
---|
| 619 | if(ncp) |
---|
| 620 | *ncp = nc; |
---|
| 621 | |
---|
| 622 | return NC_NOERR; |
---|
| 623 | } |
---|
| 624 | |
---|
| 625 | /* Remove a NC_FILE_INFO_T from the linked list. This will nc_free the |
---|
| 626 | memory too. */ |
---|
| 627 | void |
---|
| 628 | nc4_file_list_del(NC_FILE_INFO_T *nc) |
---|
| 629 | { |
---|
| 630 | /* Remove file from master list. */ |
---|
| 631 | del_from_NCList((NC *)nc); |
---|
| 632 | free(nc); |
---|
| 633 | } |
---|
| 634 | |
---|
| 635 | |
---|
| 636 | /* Given an id, walk the list and find the appropriate |
---|
| 637 | NC_FILE_INFO_T. */ |
---|
| 638 | NC_FILE_INFO_T* |
---|
| 639 | nc4_find_nc_file(int ext_ncid) |
---|
| 640 | { |
---|
| 641 | return (NC_FILE_INFO_T*)find_in_NCList(ext_ncid); |
---|
| 642 | } |
---|
| 643 | |
---|
| 644 | |
---|
| 645 | /* Add to the end of a var list. Return a pointer to the newly |
---|
| 646 | * added var. */ |
---|
| 647 | int |
---|
| 648 | nc4_var_list_add(NC_VAR_INFO_T **list, NC_VAR_INFO_T **var) |
---|
| 649 | { |
---|
| 650 | NC_VAR_INFO_T *v; |
---|
| 651 | |
---|
| 652 | /* Allocate storage for new variable. */ |
---|
| 653 | if (!(*var = calloc(1, sizeof(NC_VAR_INFO_T)))) |
---|
| 654 | return NC_ENOMEM; |
---|
| 655 | |
---|
| 656 | /* Go to the end of the list and set the last one to point at our |
---|
| 657 | * new var, or, if the list is empty, our new var becomes the |
---|
| 658 | * list. */ |
---|
| 659 | if(*list) |
---|
| 660 | { |
---|
| 661 | for (v = *list; v; v = v->next) |
---|
| 662 | if (!v->next) |
---|
| 663 | break; |
---|
| 664 | v->next = *var; |
---|
| 665 | (*var)->prev = v; |
---|
| 666 | } |
---|
| 667 | else |
---|
| 668 | *list = *var; |
---|
| 669 | |
---|
| 670 | /* These are the HDF5-1.8.4 defaults. */ |
---|
| 671 | (*var)->chunk_cache_size = nc4_chunk_cache_size; |
---|
| 672 | (*var)->chunk_cache_nelems = nc4_chunk_cache_nelems; |
---|
| 673 | (*var)->chunk_cache_preemption = nc4_chunk_cache_preemption; |
---|
| 674 | |
---|
| 675 | return NC_NOERR; |
---|
| 676 | } |
---|
| 677 | |
---|
| 678 | /* Add to the beginning of a dim list. */ |
---|
| 679 | int |
---|
| 680 | nc4_dim_list_add(NC_DIM_INFO_T **list) |
---|
| 681 | { |
---|
| 682 | NC_DIM_INFO_T *dim; |
---|
| 683 | if (!(dim = calloc(1, sizeof(NC_DIM_INFO_T)))) |
---|
| 684 | return NC_ENOMEM; |
---|
| 685 | if(*list) |
---|
| 686 | (*list)->prev = dim; |
---|
| 687 | dim->next = *list; |
---|
| 688 | *list = dim; |
---|
| 689 | return NC_NOERR; |
---|
| 690 | } |
---|
| 691 | |
---|
| 692 | /* Add to the beginning of a dim list. */ |
---|
| 693 | int |
---|
| 694 | nc4_dim_list_add2(NC_DIM_INFO_T **list, NC_DIM_INFO_T **new_dim) |
---|
| 695 | { |
---|
| 696 | NC_DIM_INFO_T *dim; |
---|
| 697 | if (!(dim = calloc(1, sizeof(NC_DIM_INFO_T)))) |
---|
| 698 | return NC_ENOMEM; |
---|
| 699 | if(*list) |
---|
| 700 | (*list)->prev = dim; |
---|
| 701 | dim->next = *list; |
---|
| 702 | *list = dim; |
---|
| 703 | |
---|
| 704 | /* Return pointer to new dimension. */ |
---|
| 705 | if (new_dim) |
---|
| 706 | *new_dim = dim; |
---|
| 707 | return NC_NOERR; |
---|
| 708 | } |
---|
| 709 | |
---|
| 710 | /* Add to the end of an att list. */ |
---|
| 711 | int |
---|
| 712 | nc4_att_list_add(NC_ATT_INFO_T **list) |
---|
| 713 | { |
---|
| 714 | NC_ATT_INFO_T *att, *a1; |
---|
| 715 | if (!(att = calloc(1, sizeof(NC_ATT_INFO_T)))) |
---|
| 716 | return NC_ENOMEM; |
---|
| 717 | if (*list) |
---|
| 718 | { |
---|
| 719 | for (a1 = *list; a1; a1 = a1->next) |
---|
| 720 | if (!a1->next) |
---|
| 721 | break; |
---|
| 722 | a1->next = att; |
---|
| 723 | att->prev = a1; |
---|
| 724 | } |
---|
| 725 | else |
---|
| 726 | { |
---|
| 727 | *list = att; |
---|
| 728 | } |
---|
| 729 | |
---|
| 730 | return NC_NOERR; |
---|
| 731 | } |
---|
| 732 | |
---|
| 733 | /* Add to the end of a group list. Can't use 0 as a new_nc_grpid - |
---|
| 734 | * it's reserverd for the root group. */ |
---|
| 735 | int |
---|
| 736 | nc4_grp_list_add(NC_GRP_INFO_T **list, int new_nc_grpid, |
---|
| 737 | NC_GRP_INFO_T *parent_grp, NC_FILE_INFO_T *nc, |
---|
| 738 | char *name, NC_GRP_INFO_T **grp) |
---|
| 739 | { |
---|
| 740 | NC_GRP_INFO_T *g; |
---|
| 741 | |
---|
| 742 | LOG((3, "grp_list_add: new_nc_grpid %d name %s ", |
---|
| 743 | new_nc_grpid, name)); |
---|
| 744 | |
---|
| 745 | /* Get the memory to store this groups info. */ |
---|
| 746 | if (!(*grp = calloc(1, sizeof(NC_GRP_INFO_T)))) |
---|
| 747 | return NC_ENOMEM; |
---|
| 748 | |
---|
| 749 | /* If the list is not NULL, add this group to it. Otherwise, this |
---|
| 750 | * group structure becomes the list. */ |
---|
| 751 | if (*list) |
---|
| 752 | { |
---|
| 753 | /* Move to end of the list. */ |
---|
| 754 | for (g = *list; g; g = g->next) |
---|
| 755 | if (!g->next) |
---|
| 756 | break; |
---|
| 757 | g->next = *grp; /* Add grp to end of list. */ |
---|
| 758 | (*grp)->prev = g; |
---|
| 759 | } |
---|
| 760 | else |
---|
| 761 | { |
---|
| 762 | *list = *grp; |
---|
| 763 | } |
---|
| 764 | |
---|
| 765 | /* Fill in this group's information. */ |
---|
| 766 | (*grp)->nc_grpid = new_nc_grpid; |
---|
| 767 | (*grp)->parent = parent_grp; |
---|
| 768 | if (!((*grp)->name = malloc((strlen(name) + 1) * sizeof(char)))) |
---|
| 769 | return NC_ENOMEM; |
---|
| 770 | strcpy((*grp)->name, name); |
---|
| 771 | (*grp)->file = nc; |
---|
| 772 | |
---|
| 773 | return NC_NOERR; |
---|
| 774 | } |
---|
| 775 | |
---|
| 776 | /* Names for groups, variables, and types must not be the same. This |
---|
| 777 | * function checks that a proposed name is not already in |
---|
| 778 | * use. Normalzation of UTF8 strings should happen before this |
---|
| 779 | * function is called. */ |
---|
| 780 | int |
---|
| 781 | nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name) |
---|
| 782 | { |
---|
| 783 | NC_TYPE_INFO_T *type; |
---|
| 784 | NC_GRP_INFO_T *g; |
---|
| 785 | NC_VAR_INFO_T *var; |
---|
| 786 | |
---|
| 787 | /* Any types of this name? */ |
---|
| 788 | for (type = grp->type; type; type = type->next) |
---|
| 789 | if (!strcmp(type->name, name)) |
---|
| 790 | return NC_ENAMEINUSE; |
---|
| 791 | |
---|
| 792 | /* Any child groups of this name? */ |
---|
| 793 | for (g = grp->children; g; g = g->next) |
---|
| 794 | if (!strcmp(g->name, name)) |
---|
| 795 | return NC_ENAMEINUSE; |
---|
| 796 | |
---|
| 797 | /* Any variables of this name? */ |
---|
| 798 | for (var = grp->var; var; var = var->next) |
---|
| 799 | if (!strcmp(var->name, name)) |
---|
| 800 | return NC_ENAMEINUSE; |
---|
| 801 | |
---|
| 802 | return NC_NOERR; |
---|
| 803 | } |
---|
| 804 | |
---|
| 805 | /* Add to the end of a type list. */ |
---|
| 806 | int |
---|
| 807 | nc4_type_list_add(NC_TYPE_INFO_T **list, NC_TYPE_INFO_T **new_type) |
---|
| 808 | { |
---|
| 809 | NC_TYPE_INFO_T *type, *t; |
---|
| 810 | |
---|
| 811 | if (!(type = calloc(1, sizeof(NC_TYPE_INFO_T)))) |
---|
| 812 | return NC_ENOMEM; |
---|
| 813 | |
---|
| 814 | if (*list) |
---|
| 815 | { |
---|
| 816 | for (t = *list; t; t = t->next) |
---|
| 817 | if (!t->next) |
---|
| 818 | break; |
---|
| 819 | t->next = type; |
---|
| 820 | type->prev = t; |
---|
| 821 | } |
---|
| 822 | else |
---|
| 823 | { |
---|
| 824 | *list = type; |
---|
| 825 | } |
---|
| 826 | |
---|
| 827 | if (new_type) |
---|
| 828 | *new_type = type; |
---|
| 829 | |
---|
| 830 | return NC_NOERR; |
---|
| 831 | } |
---|
| 832 | |
---|
| 833 | /* Add to the end of a compound field list. */ |
---|
| 834 | int |
---|
| 835 | nc4_field_list_add(NC_FIELD_INFO_T **list, int fieldid, const char *name, |
---|
| 836 | size_t offset, hid_t field_hdf_typeid, hid_t native_typeid, |
---|
| 837 | nc_type xtype, int ndims, const int *dim_sizesp) |
---|
| 838 | { |
---|
| 839 | NC_FIELD_INFO_T *field, *f; |
---|
| 840 | int i; |
---|
| 841 | |
---|
| 842 | /* Name has already been checked and UTF8 normalized. */ |
---|
| 843 | if (!name) |
---|
| 844 | return NC_EINVAL; |
---|
| 845 | |
---|
| 846 | /* Allocate storage for this field information. */ |
---|
| 847 | if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T)))) |
---|
| 848 | return NC_ENOMEM; |
---|
| 849 | |
---|
| 850 | /* Add this field to list. */ |
---|
| 851 | if (*list) |
---|
| 852 | { |
---|
| 853 | for (f = *list; f; f = f->next) |
---|
| 854 | if (!f->next) |
---|
| 855 | break; |
---|
| 856 | f->next = field; |
---|
| 857 | field->prev = f; |
---|
| 858 | } |
---|
| 859 | else |
---|
| 860 | { |
---|
| 861 | *list = field; |
---|
| 862 | } |
---|
| 863 | |
---|
| 864 | /* Store the information about this field. */ |
---|
| 865 | field->fieldid = fieldid; |
---|
| 866 | if (!(field->name = malloc((strlen(name) + 1) * sizeof(char)))) |
---|
| 867 | return NC_ENOMEM; |
---|
| 868 | strcpy(field->name, name); |
---|
| 869 | field->hdf_typeid = field_hdf_typeid; |
---|
| 870 | field->native_typeid = native_typeid; |
---|
| 871 | field->nctype = xtype; |
---|
| 872 | field->offset = offset; |
---|
| 873 | field->ndims = ndims; |
---|
| 874 | if (ndims) |
---|
| 875 | { |
---|
| 876 | if (!(field->dim_size = malloc(ndims * sizeof(int)))) |
---|
| 877 | return NC_ENOMEM; |
---|
| 878 | for (i = 0; i < ndims; i++) |
---|
| 879 | field->dim_size[i] = dim_sizesp[i]; |
---|
| 880 | } |
---|
| 881 | |
---|
| 882 | return NC_NOERR; |
---|
| 883 | } |
---|
| 884 | |
---|
| 885 | /* Add a member to an enum type. */ |
---|
| 886 | int |
---|
| 887 | nc4_enum_member_add(NC_ENUM_MEMBER_INFO_T **list, size_t size, |
---|
| 888 | const char *name, const void *value) |
---|
| 889 | { |
---|
| 890 | NC_ENUM_MEMBER_INFO_T *member, *m; |
---|
| 891 | |
---|
| 892 | /* Name has already been checked. */ |
---|
| 893 | assert(name && size > 0 && value); |
---|
| 894 | LOG((4, "nc4_enum_member_add: size %d name %s", size, name)); |
---|
| 895 | |
---|
| 896 | /* Allocate storage for this field information. */ |
---|
| 897 | if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T))) || |
---|
| 898 | !(member->value = calloc(1, size))) |
---|
| 899 | return NC_ENOMEM; |
---|
| 900 | |
---|
| 901 | /* Add this field to list. */ |
---|
| 902 | if (*list) |
---|
| 903 | { |
---|
| 904 | for (m = *list; m; m = m->next) |
---|
| 905 | if (!m->next) |
---|
| 906 | break; |
---|
| 907 | m->next = member; |
---|
| 908 | member->prev = m; |
---|
| 909 | } |
---|
| 910 | else |
---|
| 911 | { |
---|
| 912 | *list = member; |
---|
| 913 | } |
---|
| 914 | |
---|
| 915 | /* Store the information about this member. */ |
---|
| 916 | if (!(member->name = malloc((strlen(name) + 1) * sizeof(char)))) |
---|
| 917 | return NC_ENOMEM; |
---|
| 918 | strcpy(member->name, name); |
---|
| 919 | memcpy(member->value, value, size); |
---|
| 920 | |
---|
| 921 | return NC_NOERR; |
---|
| 922 | } |
---|
| 923 | |
---|
| 924 | /* Delete a var from a var list, and free the memory. */ |
---|
| 925 | static int |
---|
| 926 | var_list_del(NC_VAR_INFO_T **list, NC_VAR_INFO_T *var) |
---|
| 927 | { |
---|
| 928 | NC_ATT_INFO_T *a, *att; |
---|
| 929 | int ret; |
---|
| 930 | |
---|
| 931 | /* First delete all the attributes attached to this var. */ |
---|
| 932 | att = (*list)->att; |
---|
| 933 | while (att) |
---|
| 934 | { |
---|
| 935 | a = att->next; |
---|
| 936 | if ((ret = nc4_att_list_del(&var->att, att))) |
---|
| 937 | return ret; |
---|
| 938 | att = a; |
---|
| 939 | } |
---|
| 940 | |
---|
| 941 | /* Free some things that may be allocated. */ |
---|
| 942 | if (var->chunksizes) |
---|
| 943 | free(var->chunksizes); |
---|
| 944 | if (var->hdf5_name) |
---|
| 945 | free(var->hdf5_name); |
---|
| 946 | if (var->name) |
---|
| 947 | free(var->name); |
---|
| 948 | if (var->dimids) |
---|
| 949 | free(var->dimids); |
---|
| 950 | if (var->dim) |
---|
| 951 | free(var->dim); |
---|
| 952 | |
---|
| 953 | /* Remove the var from the linked list. */ |
---|
| 954 | if(*list == var) |
---|
| 955 | *list = var->next; |
---|
| 956 | else |
---|
| 957 | var->prev->next = var->next; |
---|
| 958 | |
---|
| 959 | if(var->next) |
---|
| 960 | var->next->prev = var->prev; |
---|
| 961 | |
---|
| 962 | /* Delete any fill value allocation. This must be done before the |
---|
| 963 | * type_info is freed. */ |
---|
| 964 | if (var->fill_value) |
---|
| 965 | { |
---|
| 966 | if (var->hdf_datasetid) |
---|
| 967 | { |
---|
| 968 | if (var->type_info->class == NC_VLEN) |
---|
| 969 | nc_free_vlen((nc_vlen_t *)var->fill_value); |
---|
| 970 | else if (var->type_info->nc_typeid == NC_STRING) |
---|
| 971 | free(*(char **)var->fill_value); |
---|
| 972 | } |
---|
| 973 | free(var->fill_value); |
---|
| 974 | } |
---|
| 975 | |
---|
| 976 | /* For atomic types we have allocated space for type information. */ |
---|
| 977 | /* if (var->hdf_datasetid && var->xtype <= NC_STRING)*/ |
---|
| 978 | if (var->xtype <= NC_STRING) |
---|
| 979 | { |
---|
| 980 | if (var->type_info->native_typeid) |
---|
| 981 | if ((H5Tclose(var->type_info->native_typeid)) < 0) |
---|
| 982 | return NC_EHDFERR; |
---|
| 983 | |
---|
| 984 | /* Only need to close the hdf_typeid when it was obtained with |
---|
| 985 | * H5Dget_type (which happens when reading a file, but not when |
---|
| 986 | * creating a variable). */ |
---|
| 987 | if (var->type_info->close_hdf_typeid || var->xtype == NC_STRING) |
---|
| 988 | if ((H5Tclose(var->type_info->hdf_typeid)) < 0) |
---|
| 989 | return NC_EHDFERR; |
---|
| 990 | |
---|
| 991 | /* Free the name. */ |
---|
| 992 | if (var->type_info->name) |
---|
| 993 | free(var->type_info->name); |
---|
| 994 | |
---|
| 995 | free(var->type_info); |
---|
| 996 | } |
---|
| 997 | |
---|
| 998 | /* Delete any HDF5 dimscale objid information. */ |
---|
| 999 | if (var->dimscale_hdf5_objids) |
---|
| 1000 | free(var->dimscale_hdf5_objids); |
---|
| 1001 | |
---|
| 1002 | /* Delete information about the attachment status of dimscales. */ |
---|
| 1003 | if (var->dimscale_attached) |
---|
| 1004 | free(var->dimscale_attached); |
---|
| 1005 | |
---|
| 1006 | /* Delete the var. */ |
---|
| 1007 | free(var); |
---|
| 1008 | |
---|
| 1009 | return NC_NOERR; |
---|
| 1010 | } |
---|
| 1011 | |
---|
| 1012 | /* Delete a field from a field list, and nc_free the memory. */ |
---|
| 1013 | static void |
---|
| 1014 | field_list_del(NC_FIELD_INFO_T **list, NC_FIELD_INFO_T *field) |
---|
| 1015 | { |
---|
| 1016 | |
---|
| 1017 | /* Take this field out of the list. */ |
---|
| 1018 | if(*list == field) |
---|
| 1019 | *list = field->next; |
---|
| 1020 | else |
---|
| 1021 | field->prev->next = field->next; |
---|
| 1022 | |
---|
| 1023 | if(field->next) |
---|
| 1024 | field->next->prev = field->prev; |
---|
| 1025 | |
---|
| 1026 | /* Free some stuff. */ |
---|
| 1027 | if (field->name) |
---|
| 1028 | free(field->name); |
---|
| 1029 | if (field->dim_size) |
---|
| 1030 | free(field->dim_size); |
---|
| 1031 | |
---|
| 1032 | /* Nc_Free the memory. */ |
---|
| 1033 | free(field); |
---|
| 1034 | } |
---|
| 1035 | |
---|
| 1036 | /* Delete a type from a type list, and nc_free the memory. */ |
---|
| 1037 | int |
---|
| 1038 | type_list_del(NC_TYPE_INFO_T **list, NC_TYPE_INFO_T *type) |
---|
| 1039 | { |
---|
| 1040 | NC_FIELD_INFO_T *field, *f; |
---|
| 1041 | NC_ENUM_MEMBER_INFO_T *enum_member, *em; |
---|
| 1042 | |
---|
| 1043 | /* Close any open user-defined HDF5 typieds. */ |
---|
| 1044 | if (type->hdf_typeid) |
---|
| 1045 | { |
---|
| 1046 | if (H5Tclose(type->hdf_typeid) < 0) |
---|
| 1047 | return NC_EHDFERR; |
---|
| 1048 | } |
---|
| 1049 | if (type->native_typeid) |
---|
| 1050 | { |
---|
| 1051 | if (H5Tclose(type->native_typeid) < 0) |
---|
| 1052 | return NC_EHDFERR; |
---|
| 1053 | } |
---|
| 1054 | |
---|
| 1055 | /* Free the name. */ |
---|
| 1056 | if (type->name) |
---|
| 1057 | free(type->name); |
---|
| 1058 | |
---|
| 1059 | /* Delete all the fields in this type (there will be some if its a |
---|
| 1060 | * compound). */ |
---|
| 1061 | field = type->field; |
---|
| 1062 | while (field) |
---|
| 1063 | { |
---|
| 1064 | f = field->next; |
---|
| 1065 | field_list_del(&type->field, field); |
---|
| 1066 | field = f; |
---|
| 1067 | } |
---|
| 1068 | |
---|
| 1069 | /* Delete all the enum_members, if any. */ |
---|
| 1070 | enum_member = type->enum_member; |
---|
| 1071 | while (enum_member) |
---|
| 1072 | { |
---|
| 1073 | em = enum_member->next; |
---|
| 1074 | free(enum_member->value); |
---|
| 1075 | free(enum_member->name); |
---|
| 1076 | free(enum_member); |
---|
| 1077 | enum_member = em; |
---|
| 1078 | } |
---|
| 1079 | |
---|
| 1080 | /* Take this type out of the list. */ |
---|
| 1081 | if(*list == type) |
---|
| 1082 | *list = type->next; |
---|
| 1083 | else |
---|
| 1084 | type->prev->next = type->next; |
---|
| 1085 | |
---|
| 1086 | if(type->next) |
---|
| 1087 | type->next->prev = type->prev; |
---|
| 1088 | |
---|
| 1089 | /* Nc_Free the memory. */ |
---|
| 1090 | free(type); |
---|
| 1091 | |
---|
| 1092 | return NC_NOERR; |
---|
| 1093 | } |
---|
| 1094 | |
---|
| 1095 | /* Delete a del from a var list, and nc_free the memory. */ |
---|
| 1096 | int |
---|
| 1097 | nc4_dim_list_del(NC_DIM_INFO_T **list, NC_DIM_INFO_T *dim) |
---|
| 1098 | { |
---|
| 1099 | /* Take this dimension out of the list. */ |
---|
| 1100 | if(*list == dim) |
---|
| 1101 | *list = dim->next; |
---|
| 1102 | else |
---|
| 1103 | dim->prev->next = dim->next; |
---|
| 1104 | |
---|
| 1105 | if(dim->next) |
---|
| 1106 | dim->next->prev = dim->prev; |
---|
| 1107 | |
---|
| 1108 | /* Free memory allocated for names. */ |
---|
| 1109 | if (dim->name) |
---|
| 1110 | free(dim->name); |
---|
| 1111 | if (dim->old_name) |
---|
| 1112 | free(dim->old_name); |
---|
| 1113 | |
---|
| 1114 | free(dim); |
---|
| 1115 | return NC_NOERR; |
---|
| 1116 | } |
---|
| 1117 | |
---|
| 1118 | /* Remove a NC_GRP_INFO_T from the linked list. This will nc_free the |
---|
| 1119 | memory too. */ |
---|
| 1120 | static void |
---|
| 1121 | grp_list_del(NC_GRP_INFO_T **list, NC_GRP_INFO_T *grp) |
---|
| 1122 | { |
---|
| 1123 | if(*list == grp) |
---|
| 1124 | *list = grp->next; |
---|
| 1125 | else |
---|
| 1126 | grp->prev->next = grp->next; |
---|
| 1127 | |
---|
| 1128 | if(grp->next) |
---|
| 1129 | grp->next->prev = grp->prev; |
---|
| 1130 | |
---|
| 1131 | free(grp); |
---|
| 1132 | } |
---|
| 1133 | |
---|
| 1134 | /* Recursively delete the data for a group (and everything it |
---|
| 1135 | * contains) in our internal metadata store. */ |
---|
| 1136 | int |
---|
| 1137 | nc4_rec_grp_del(NC_GRP_INFO_T **list, NC_GRP_INFO_T *grp) |
---|
| 1138 | { |
---|
| 1139 | NC_GRP_INFO_T *g, *c; |
---|
| 1140 | NC_VAR_INFO_T *v, *var; |
---|
| 1141 | NC_ATT_INFO_T *a, *att; |
---|
| 1142 | NC_DIM_INFO_T *d, *dim; |
---|
| 1143 | NC_TYPE_INFO_T *type, *t; |
---|
| 1144 | int retval; |
---|
| 1145 | |
---|
| 1146 | assert(grp); |
---|
| 1147 | LOG((3, "nc4_rec_grp_del: grp->name %s", grp->name)); |
---|
| 1148 | |
---|
| 1149 | /* Recursively call this function for each child, if any, stopping |
---|
| 1150 | * if there is an error. */ |
---|
| 1151 | g = grp->children; |
---|
| 1152 | while(g) |
---|
| 1153 | { |
---|
| 1154 | c = g->next; |
---|
| 1155 | if ((retval = nc4_rec_grp_del(&(grp->children), g))) |
---|
| 1156 | return retval; |
---|
| 1157 | g = c; |
---|
| 1158 | } |
---|
| 1159 | |
---|
| 1160 | /* Delete all the list contents for vars, dims, and atts, in each |
---|
| 1161 | * group. */ |
---|
| 1162 | att = grp->att; |
---|
| 1163 | while (att) |
---|
| 1164 | { |
---|
| 1165 | LOG((4, "nc4_rec_grp_del: deleting att %s", att->name)); |
---|
| 1166 | a = att->next; |
---|
| 1167 | if ((retval = nc4_att_list_del(&grp->att, att))) |
---|
| 1168 | return retval; |
---|
| 1169 | att = a; |
---|
| 1170 | } |
---|
| 1171 | |
---|
| 1172 | /* Delete all vars. */ |
---|
| 1173 | var = grp->var; |
---|
| 1174 | while (var) |
---|
| 1175 | { |
---|
| 1176 | LOG((4, "nc4_rec_grp_del: deleting var %s", var->name)); |
---|
| 1177 | /* Close HDF5 dataset associated with this var, unless it's a |
---|
| 1178 | * scale. */ |
---|
| 1179 | if (var->hdf_datasetid && !var->dimscale && |
---|
| 1180 | H5Dclose(var->hdf_datasetid) < 0) |
---|
| 1181 | return NC_EHDFERR; |
---|
| 1182 | v = var->next; |
---|
| 1183 | if ((retval = var_list_del(&grp->var, var))) |
---|
| 1184 | return retval; |
---|
| 1185 | var = v; |
---|
| 1186 | } |
---|
| 1187 | |
---|
| 1188 | /* Delete all dims. */ |
---|
| 1189 | dim = grp->dim; |
---|
| 1190 | while (dim) |
---|
| 1191 | { |
---|
| 1192 | LOG((4, "nc4_rec_grp_del: deleting dim %s", dim->name)); |
---|
| 1193 | /* Close HDF5 dataset associated with this dim. */ |
---|
| 1194 | if (dim->hdf_dimscaleid && H5Dclose(dim->hdf_dimscaleid) < 0) |
---|
| 1195 | return NC_EHDFERR; |
---|
| 1196 | d = dim->next; |
---|
| 1197 | if ((retval = nc4_dim_list_del(&grp->dim, dim))) |
---|
| 1198 | return retval; |
---|
| 1199 | dim = d; |
---|
| 1200 | } |
---|
| 1201 | |
---|
| 1202 | /* Delete all types. */ |
---|
| 1203 | type = grp->type; |
---|
| 1204 | while (type) |
---|
| 1205 | { |
---|
| 1206 | LOG((4, "nc4_rec_grp_del: deleting type %s", type->name)); |
---|
| 1207 | t = type->next; |
---|
| 1208 | if ((retval = type_list_del(&grp->type, type))) |
---|
| 1209 | return retval; |
---|
| 1210 | type = t; |
---|
| 1211 | } |
---|
| 1212 | |
---|
| 1213 | /* Tell HDF5 we're closing this group. */ |
---|
| 1214 | LOG((4, "nc4_rec_grp_del: closing group %s", grp->name)); |
---|
| 1215 | if (grp->hdf_grpid && H5Gclose(grp->hdf_grpid) < 0) |
---|
| 1216 | return NC_EHDFERR; |
---|
| 1217 | |
---|
| 1218 | /* Free the name. */ |
---|
| 1219 | free(grp->name); |
---|
| 1220 | |
---|
| 1221 | /* Finally, redirect pointers around this entry in the list, and |
---|
| 1222 | * nc_free its memory. */ |
---|
| 1223 | grp_list_del(list, grp); |
---|
| 1224 | |
---|
| 1225 | return NC_NOERR; |
---|
| 1226 | } |
---|
| 1227 | |
---|
| 1228 | /* Remove a NC_ATT_INFO_T from the linked list. This will nc_free the |
---|
| 1229 | memory too. |
---|
| 1230 | */ |
---|
| 1231 | int |
---|
| 1232 | nc4_att_list_del(NC_ATT_INFO_T **list, NC_ATT_INFO_T *att) |
---|
| 1233 | { |
---|
| 1234 | int i; |
---|
| 1235 | |
---|
| 1236 | /* Take this att out of the list. */ |
---|
| 1237 | if(*list == att) |
---|
| 1238 | *list = att->next; |
---|
| 1239 | else |
---|
| 1240 | att->prev->next = att->next; |
---|
| 1241 | |
---|
| 1242 | if(att->next) |
---|
| 1243 | att->next->prev = att->prev; |
---|
| 1244 | |
---|
| 1245 | /* Free memory that was malloced to hold data for this |
---|
| 1246 | * attribute. */ |
---|
| 1247 | if (att->data) |
---|
| 1248 | free(att->data); |
---|
| 1249 | |
---|
| 1250 | /* Free the name. */ |
---|
| 1251 | if (att->name) |
---|
| 1252 | free(att->name); |
---|
| 1253 | |
---|
| 1254 | /* Close the HDF5 typeid. */ |
---|
| 1255 | if (att->native_typeid && H5Tclose(att->native_typeid) < 0) |
---|
| 1256 | return NC_EHDFERR; |
---|
| 1257 | |
---|
| 1258 | /* If this is a string array attribute, delete all members of the |
---|
| 1259 | * string array, then delete the array of pointers to strings. (The |
---|
| 1260 | * array was filled with pointers by HDF5 when the att was read, |
---|
| 1261 | * and memory for each string was allocated by HDF5. That's why I |
---|
| 1262 | * use free and not nc_free, because the netCDF library didn't |
---|
| 1263 | * allocate the memory that is being freed.) */ |
---|
| 1264 | if (att->stdata) |
---|
| 1265 | { |
---|
| 1266 | for (i = 0; i < att->len; i++) |
---|
| 1267 | free(att->stdata[i]); |
---|
| 1268 | free(att->stdata); |
---|
| 1269 | } |
---|
| 1270 | |
---|
| 1271 | /* If this att has vlen data, release it. */ |
---|
| 1272 | if (att->vldata) |
---|
| 1273 | { |
---|
| 1274 | for (i = 0; i < att->len; i++) |
---|
| 1275 | nc_free_vlen(&att->vldata[i]); |
---|
| 1276 | free(att->vldata); |
---|
| 1277 | } |
---|
| 1278 | |
---|
| 1279 | free(att); |
---|
| 1280 | return NC_NOERR; |
---|
| 1281 | } |
---|
| 1282 | |
---|
| 1283 | /* Normalize a UTF8 name. Put the result in norm_name, which can be |
---|
| 1284 | * NC_MAX_NAME + 1 in size. This function makes sure the free() gets |
---|
| 1285 | * called on the return from utf8proc_NFC, and also ensures that the |
---|
| 1286 | * name is not too long. */ |
---|
| 1287 | int |
---|
| 1288 | nc4_normalize_name(const char *name, char *norm_name) |
---|
| 1289 | { |
---|
| 1290 | char *temp_name; |
---|
| 1291 | if (!(temp_name = (char *)utf8proc_NFC((const unsigned char *)name))) |
---|
| 1292 | return NC_EINVAL; |
---|
| 1293 | if (strlen(temp_name) > NC_MAX_NAME) |
---|
| 1294 | { |
---|
| 1295 | free(temp_name); |
---|
| 1296 | return NC_EMAXNAME; |
---|
| 1297 | } |
---|
| 1298 | strcpy(norm_name, temp_name); |
---|
| 1299 | free(temp_name); |
---|
| 1300 | return NC_NOERR; |
---|
| 1301 | } |
---|
| 1302 | |
---|
| 1303 | /* Print out a bunch of info to stderr about the metadata for |
---|
| 1304 | debugging purposes. */ |
---|
| 1305 | #ifdef LOGGING |
---|
| 1306 | /* Use this to set the global log level. Set it to NC_TURN_OFF_LOGGING |
---|
| 1307 | (-1) to turn off all logging. Set it to 0 to show only errors, and |
---|
| 1308 | to higher numbers to show more and more logging details. */ |
---|
| 1309 | int |
---|
| 1310 | nc_set_log_level(int new_level) |
---|
| 1311 | { |
---|
| 1312 | /* If the user wants to completely turn off logging, turn off HDF5 |
---|
| 1313 | logging too. Now I truely can't think of what to do if this |
---|
| 1314 | fails, so just ignore the return code. */ |
---|
| 1315 | if (new_level == NC_TURN_OFF_LOGGING) |
---|
| 1316 | { |
---|
| 1317 | H5Eset_auto(NULL, NULL); |
---|
| 1318 | LOG((1, "HDF5 error messages turned off!")); |
---|
| 1319 | } |
---|
| 1320 | |
---|
| 1321 | /* Do we need to turn HDF5 logging back on? */ |
---|
| 1322 | if (new_level > NC_TURN_OFF_LOGGING && |
---|
| 1323 | nc_log_level <= NC_TURN_OFF_LOGGING) |
---|
| 1324 | { |
---|
| 1325 | if (H5Eset_auto((H5E_auto_t)&H5Eprint, stderr) < 0) |
---|
| 1326 | LOG((0, "H5Eset_auto failed!")); |
---|
| 1327 | LOG((1, "HDF5 error messages turned on.")); |
---|
| 1328 | } |
---|
| 1329 | |
---|
| 1330 | /* Now remember the new level. */ |
---|
| 1331 | nc_log_level = new_level; |
---|
| 1332 | LOG((4, "log_level changed to %d", nc_log_level)); |
---|
| 1333 | return 0; |
---|
| 1334 | } |
---|
| 1335 | |
---|
| 1336 | /* Recursively print the metadata of a group. */ |
---|
| 1337 | #define MAX_NESTS 10 |
---|
| 1338 | static int |
---|
| 1339 | rec_print_metadata(NC_GRP_INFO_T *grp, int *tab_count) |
---|
| 1340 | { |
---|
| 1341 | NC_GRP_INFO_T *g; |
---|
| 1342 | NC_ATT_INFO_T *att; |
---|
| 1343 | NC_VAR_INFO_T *var; |
---|
| 1344 | NC_DIM_INFO_T *dim; |
---|
| 1345 | NC_TYPE_INFO_T *type; |
---|
| 1346 | NC_FIELD_INFO_T *field; |
---|
| 1347 | char tabs[MAX_NESTS] = ""; |
---|
| 1348 | char dims_string[NC_MAX_DIMS*4]; |
---|
| 1349 | char temp_string[10]; |
---|
| 1350 | int t, retval, d; |
---|
| 1351 | |
---|
| 1352 | /* Come up with a number of tabs relative to the group. */ |
---|
| 1353 | for (t = 0; t < *tab_count && t < MAX_NESTS; t++) |
---|
| 1354 | strcat(tabs, "\t"); |
---|
| 1355 | |
---|
| 1356 | LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d", |
---|
| 1357 | tabs, grp->name, grp->nc_grpid, grp->nvars, grp->natts)); |
---|
| 1358 | |
---|
| 1359 | for(att = grp->att; att; att = att->next) |
---|
| 1360 | LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d", |
---|
| 1361 | tabs, att->attnum, att->name, att->xtype, att->len)); |
---|
| 1362 | |
---|
| 1363 | /* To display dims starting with 0 and going up, go through list is |
---|
| 1364 | * reverse order. */ |
---|
| 1365 | for(dim = grp->dim; dim && dim->next; dim = dim->next) |
---|
| 1366 | ; |
---|
| 1367 | for( ; dim; dim = dim->prev) |
---|
| 1368 | LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d", |
---|
| 1369 | tabs, dim->dimid, dim->name, dim->len, dim->unlimited)); |
---|
| 1370 | |
---|
| 1371 | /* To display vars starting with 0 and going up, go through list is |
---|
| 1372 | * reverse order. */ |
---|
| 1373 | for(var = grp->var; var && var->next; var = var->next) |
---|
| 1374 | ; |
---|
| 1375 | for( ; var; var = var->prev) |
---|
| 1376 | { |
---|
| 1377 | strcpy(dims_string, ""); |
---|
| 1378 | for (d = 0; d < var->ndims; d++) |
---|
| 1379 | { |
---|
| 1380 | sprintf(temp_string, " %d", var->dimids[d]); |
---|
| 1381 | strcat(dims_string, temp_string); |
---|
| 1382 | } |
---|
| 1383 | LOG((2, "%s VARIABLE - varid: %d name: %s type: %d ndims: %d dimscale: %d dimids:%s", |
---|
| 1384 | tabs, var->varid, var->name, var->xtype, var->ndims, var->dimscale, |
---|
| 1385 | dims_string)); |
---|
| 1386 | for(att = var->att; att; att = att->next) |
---|
| 1387 | LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d", |
---|
| 1388 | tabs, att->attnum, att->name, att->xtype, att->len)); |
---|
| 1389 | } |
---|
| 1390 | |
---|
| 1391 | for (type = grp->type; type; type = type->next) |
---|
| 1392 | { |
---|
| 1393 | LOG((2, "%s TYPE - nc_typeid: %d hdf_typeid: 0x%x size: %d committed: %d " |
---|
| 1394 | "name: %s num_fields: %d base_nc_type: %d", tabs, type->nc_typeid, |
---|
| 1395 | type->hdf_typeid, type->size, type->committed, type->name, |
---|
| 1396 | type->num_fields, type->base_nc_type)); |
---|
| 1397 | /* Is this a compound type? */ |
---|
| 1398 | if (type->class == NC_COMPOUND) |
---|
| 1399 | { |
---|
| 1400 | LOG((3, "compound type")); |
---|
| 1401 | for (field = type->field; field; field = field->next) |
---|
| 1402 | LOG((4, "field %s offset %d nctype %d ndims %d", field->name, |
---|
| 1403 | field->offset, field->nctype, field->ndims)); |
---|
| 1404 | } |
---|
| 1405 | else if (type->class == NC_VLEN) |
---|
| 1406 | LOG((3, "VLEN type")); |
---|
| 1407 | else if (type->class == NC_OPAQUE) |
---|
| 1408 | LOG((3, "Opaque type")); |
---|
| 1409 | else if (type->class == NC_ENUM) |
---|
| 1410 | LOG((3, "Enum type")); |
---|
| 1411 | else |
---|
| 1412 | { |
---|
| 1413 | LOG((0, "Unknown class: %d", type->class)); |
---|
| 1414 | return NC_EBADTYPE; |
---|
| 1415 | } |
---|
| 1416 | } |
---|
| 1417 | |
---|
| 1418 | /* Call self for each child of this group. */ |
---|
| 1419 | if (grp->children) |
---|
| 1420 | { |
---|
| 1421 | (*tab_count)++; |
---|
| 1422 | for (g = grp->children; g; g = g->next) |
---|
| 1423 | if ((retval = rec_print_metadata(g, tab_count))) |
---|
| 1424 | return retval; |
---|
| 1425 | (*tab_count)--; |
---|
| 1426 | } |
---|
| 1427 | |
---|
| 1428 | return NC_NOERR; |
---|
| 1429 | } |
---|
| 1430 | |
---|
| 1431 | /* Print out the internal metadata for a file. This is useful to check |
---|
| 1432 | * that netCDF is working! Nonetheless, this function will print |
---|
| 1433 | * nothing if logging is not set to at least two. */ |
---|
| 1434 | int |
---|
| 1435 | log_metadata_nc(NC_FILE_INFO_T *nc) |
---|
| 1436 | { |
---|
| 1437 | NC_HDF5_FILE_INFO_T *h5 = nc->nc4_info; |
---|
| 1438 | int tab_count = 0; |
---|
| 1439 | |
---|
| 1440 | LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x", |
---|
| 1441 | nc->int_ncid, nc->ext_ncid)); |
---|
| 1442 | if (!h5) |
---|
| 1443 | { |
---|
| 1444 | LOG((2, "This is a netCDF-3 file.")); |
---|
| 1445 | return NC_NOERR; |
---|
| 1446 | } |
---|
| 1447 | LOG((2, "FILE - hdfid: 0x%x path: %s cmode: 0x%x parallel: %d redef: %d " |
---|
| 1448 | "fill_mode: %d no_write: %d next_nc_grpid: %d", h5->hdfid, h5->path, |
---|
| 1449 | h5->cmode, h5->parallel, h5->redef, h5->fill_mode, h5->no_write, |
---|
| 1450 | h5->next_nc_grpid)); |
---|
| 1451 | return rec_print_metadata(h5->root_grp, &tab_count); |
---|
| 1452 | } |
---|
| 1453 | |
---|
| 1454 | #endif /*LOGGING */ |
---|
| 1455 | |
---|
| 1456 | /* Show the in-memory metadata for a netcdf file. */ |
---|
| 1457 | int |
---|
| 1458 | NC4_show_metadata(int ncid) |
---|
| 1459 | { |
---|
| 1460 | int retval = NC_NOERR; |
---|
| 1461 | #ifdef LOGGING |
---|
| 1462 | NC_FILE_INFO_T *nc; |
---|
| 1463 | int old_log_level = nc_log_level; |
---|
| 1464 | |
---|
| 1465 | /* Find file metadata. */ |
---|
| 1466 | if (!(nc = nc4_find_nc_file(ncid))) |
---|
| 1467 | return NC_EBADID; |
---|
| 1468 | |
---|
| 1469 | /* Log level must be 2 to see metadata. */ |
---|
| 1470 | nc_log_level = 2; |
---|
| 1471 | retval = log_metadata_nc(nc); |
---|
| 1472 | nc_log_level = old_log_level; |
---|
| 1473 | #endif /*LOGGING*/ |
---|
| 1474 | return retval; |
---|
| 1475 | } |
---|
| 1476 | |
---|