/src/netcdf-c/libsrc4/nc4internal.c
| Line | Count | Source | 
| 1 |  | /* Copyright 2003-2018, University Corporation for Atmospheric | 
| 2 |  |  * Research. See the COPYRIGHT file for copying and redistribution | 
| 3 |  |  * conditions. | 
| 4 |  |  */ | 
| 5 |  | /** | 
| 6 |  |  * @file | 
| 7 |  |  * @internal | 
| 8 |  |  * Internal netcdf-4 functions. | 
| 9 |  |  * | 
| 10 |  |  * This file contains functions internal to the netcdf4 library. None of | 
| 11 |  |  * the functions in this file are exposed in the exetnal API. These | 
| 12 |  |  * functions all relate to the manipulation of netcdf-4's in-memory | 
| 13 |  |  * buffer of metadata information, i.e. the linked list of NC | 
| 14 |  |  * structs. | 
| 15 |  |  * | 
| 16 |  |  * @author Ed Hartnett, Dennis Heimbigner, Ward Fisher | 
| 17 |  |  */ | 
| 18 |  | #include "config.h" | 
| 19 |  | #include "netcdf.h" | 
| 20 |  | #include "netcdf_filter.h" | 
| 21 |  | #include "netcdf_meta.h" | 
| 22 |  | #include "nc4internal.h" | 
| 23 |  | #include "nc.h" /* from libsrc */ | 
| 24 |  | #include "ncdispatch.h" /* from libdispatch */ | 
| 25 |  | #include "ncutf8.h" | 
| 26 |  | #include <stdarg.h> | 
| 27 |  | #include <stddef.h> | 
| 28 |  | #include "ncrc.h" | 
| 29 |  |  | 
| 30 |  | /** @internal Number of reserved attributes. These attributes are | 
| 31 |  |  * hidden from the netcdf user, but exist in the implementation | 
| 32 |  |  * datasets to help netcdf read the dataset. | 
| 33 |  |  * Moved here from hdf5file.c. | 
| 34 |  |  * These tables need to capture all reserved attributes | 
| 35 |  |  * across all possible dispatchers | 
| 36 |  | */ | 
| 37 |  |  | 
| 38 |  | /** @internal List of reserved attributes. | 
| 39 |  |     WARNING: This list will be sorted in (strcmp) sorted order for binary search.  | 
| 40 |  |     So order here does not matter; the table will be modified by sorting. | 
| 41 |  | */ | 
| 42 |  | static NC_reservedatt NC_reserved[] = { | 
| 43 |  |     {NC_ATT_CLASS, READONLYFLAG|HIDDENATTRFLAG},      /*CLASS*/ | 
| 44 |  |     {NC_ATT_DIMENSION_LIST, READONLYFLAG|HIDDENATTRFLAG},   /*DIMENSION_LIST*/ | 
| 45 |  |     {NC_ATT_NAME, READONLYFLAG|HIDDENATTRFLAG},       /*NAME*/ | 
| 46 |  |     {NC_ATT_REFERENCE_LIST, READONLYFLAG|HIDDENATTRFLAG},   /*REFERENCE_LIST*/ | 
| 47 |  |     {NC_XARRAY_DIMS, READONLYFLAG|HIDDENATTRFLAG},      /*_ARRAY_DIMENSIONS*/ | 
| 48 |  |     {NC_ATT_CODECS, VARFLAG|READONLYFLAG|NAMEONLYFLAG},     /*_Codecs*/ | 
| 49 |  |     {NC_ATT_FORMAT, READONLYFLAG},          /*_Format*/ | 
| 50 |  |     {ISNETCDF4ATT, READONLYFLAG|NAMEONLYFLAG|VIRTUALFLAG},    /*_IsNetcdf4*/ | 
| 51 |  |     {NCPROPS,READONLYFLAG|NAMEONLYFLAG|HIDDENATTRFLAG},     /*_NCProperties*/ | 
| 52 |  |     {NC_ATT_COORDINATES, READONLYFLAG|HIDDENATTRFLAG},      /*_Netcdf4Coordinates*/ | 
| 53 |  |     {NC_ATT_DIMID_NAME, READONLYFLAG|HIDDENATTRFLAG},     /*_Netcdf4Dimid*/ | 
| 54 |  |     {SUPERBLOCKATT, READONLYFLAG|NAMEONLYFLAG|VIRTUALFLAG},   /*_SuperblockVersion*/ | 
| 55 |  |     {NC_ATT_NC3_STRICT_NAME, READONLYFLAG},       /*_nc3_strict*/ | 
| 56 |  |     {NC_NCZARR_ATTR, READONLYFLAG|HIDDENATTRFLAG},      /*_nczarr_attr */ | 
| 57 |  |     {NC_NCZARR_GROUP, READONLYFLAG|HIDDENATTRFLAG},     /*_nczarr_group */ | 
| 58 |  |     {NC_NCZARR_ARRAY, READONLYFLAG|HIDDENATTRFLAG},     /*_nczarr_array */ | 
| 59 |  |     {NC_NCZARR_SUPERBLOCK, READONLYFLAG|HIDDENATTRFLAG},    /*_nczarr_superblock */ | 
| 60 |  | }; | 
| 61 | 1 | #define NRESERVED (sizeof(NC_reserved) / sizeof(NC_reservedatt))  /*|NC_reservedatt*/ | 
| 62 |  |  | 
| 63 |  | /*Forward */ | 
| 64 |  | static int NC4_move_in_NCList(NC* nc, int new_id); | 
| 65 |  | static int bincmp(const void* arg1, const void* arg2); | 
| 66 |  | static int sortcmp(const void* arg1, const void* arg2); | 
| 67 |  |  | 
| 68 |  | #if LOGGING | 
| 69 |  | /* This is the severity level of messages which will be logged. Use | 
| 70 |  |    severity 0 for errors, 1 for important log messages, 2 for less | 
| 71 |  |    important, etc. */ | 
| 72 |  | int nc_log_level = NC_TURN_OFF_LOGGING; | 
| 73 |  |  | 
| 74 |  |  | 
| 75 |  | #if NC_HAS_PARALLEL4 | 
| 76 |  | /* File pointer for the parallel I/O log file. */ | 
| 77 |  | FILE *LOG_FILE = NULL; | 
| 78 |  | #endif /* NC_HAS_PARALLEL4 */ | 
| 79 |  |  | 
| 80 |  | /* This function prints out a message, if the severity of | 
| 81 |  |  * the message is lower than the global nc_log_level. To use it, do | 
| 82 |  |  * something like this: | 
| 83 |  |  * | 
| 84 |  |  * nc_log(0, "this computer will explode in %d seconds", i); | 
| 85 |  |  * | 
| 86 |  |  * After the first arg (the severity), use the rest like a normal | 
| 87 |  |  * printf statement. Output will appear on stderr for sequential | 
| 88 |  |  * builds, and in a file nc4_log_R.log for each process for a parallel | 
| 89 |  |  * build, where R is the rank of the process. | 
| 90 |  |  * | 
| 91 |  |  * Ed Hartnett | 
| 92 |  |  */ | 
| 93 |  | void | 
| 94 |  | nc_log(int severity, const char *fmt, ...) | 
| 95 |  | { | 
| 96 |  |     va_list argp; | 
| 97 |  |     int t; | 
| 98 |  |     FILE *f = stderr; | 
| 99 |  |  | 
| 100 |  |     const char* envv = NULL; | 
| 101 |  |     envv = getenv("NC4LOGGING"); | 
| 102 |  |     if(envv != NULL) { | 
| 103 |  |         nc_log_level = atoi(envv); | 
| 104 |  |     } | 
| 105 |  |  | 
| 106 |  |     /* If the severity is greater than the log level, we don't print | 
| 107 |  |      * this message. */ | 
| 108 |  |     if (severity > nc_log_level) | 
| 109 |  |         return; | 
| 110 |  |  | 
| 111 |  | #if NC_HAS_PARALLEL4 | 
| 112 |  |     /* For parallel I/O build, if MPI has been initialized, instead of | 
| 113 |  |      * printing logging output to stderr, it goes to a file for each | 
| 114 |  |      * process. */ | 
| 115 |  |     { | 
| 116 |  |         int mpi_initialized; | 
| 117 |  |         int mpierr; | 
| 118 |  |  | 
| 119 |  |         /* Check to see if MPI has been initialized. */ | 
| 120 |  |         if ((mpierr = MPI_Initialized(&mpi_initialized))) | 
| 121 |  |             return; | 
| 122 |  |  | 
| 123 |  |         /* If MPI has been initialized use a log file. */ | 
| 124 |  |         assert(LOG_FILE); | 
| 125 |  |         if (mpi_initialized) | 
| 126 |  |             f = LOG_FILE; | 
| 127 |  |     } | 
| 128 |  | #endif /* NC_HAS_PARALLEL4 */ | 
| 129 |  |  | 
| 130 |  |     /* If the severity is zero, this is an error. Otherwise insert that | 
| 131 |  |        many tabs before the message. */ | 
| 132 |  |     if (!severity) | 
| 133 |  |         fprintf(f, "ERROR: "); | 
| 134 |  |     for (t = 0; t < severity; t++) | 
| 135 |  |         fprintf(f, "\t"); | 
| 136 |  |  | 
| 137 |  |     /* Print out the variable list of args with vprintf. */ | 
| 138 |  |     va_start(argp, fmt); | 
| 139 |  |     vfprintf(f, fmt, argp); | 
| 140 |  |     va_end(argp); | 
| 141 |  |  | 
| 142 |  |     /* Put on a final linefeed. */ | 
| 143 |  |     fprintf(f, "\n"); | 
| 144 |  |     fflush(f); | 
| 145 |  | } | 
| 146 |  | #endif /* LOGGING */ | 
| 147 |  |  | 
| 148 |  | /** | 
| 149 |  |  * @internal Check and normalize and name. | 
| 150 |  |  * | 
| 151 |  |  * @param name Name to normalize. | 
| 152 |  |  * @param norm_name The normalized name. | 
| 153 |  |  * | 
| 154 |  |  * @return ::NC_NOERR No error. | 
| 155 |  |  * @return ::NC_EMAXNAME Name too long. | 
| 156 |  |  * @return ::NC_EINVAL NULL given for name. | 
| 157 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 158 |  |  * @author Dennis Heimbigner | 
| 159 |  |  */ | 
| 160 |  | int | 
| 161 |  | nc4_check_name(const char *name, char *norm_name) | 
| 162 | 0 | { | 
| 163 | 0 |     char *temp; | 
| 164 | 0 |     int retval; | 
| 165 |  | 
 | 
| 166 | 0 |     assert(norm_name); | 
| 167 |  |  | 
| 168 |  |     /* Check for NULL. */ | 
| 169 | 0 |     if (!name) | 
| 170 | 0 |         return NC_EINVAL; | 
| 171 |  |  | 
| 172 |  |     /* Make sure this is a valid netcdf name. This should be done | 
| 173 |  |      * before the name is normalized, because it gives better error | 
| 174 |  |      * codes for bad utf8 strings. */ | 
| 175 | 0 |     if ((retval = NC_check_name(name))) | 
| 176 | 0 |         return retval; | 
| 177 |  |  | 
| 178 |  |     /* Normalize the name. */ | 
| 179 | 0 |     if ((retval = nc_utf8_normalize((const unsigned char *)name, | 
| 180 | 0 |                                     (unsigned char **)&temp))) | 
| 181 | 0 |         return retval; | 
| 182 |  |  | 
| 183 |  |     /* Check length of normalized name. */ | 
| 184 | 0 |     if (strlen(temp) > NC_MAX_NAME) | 
| 185 | 0 |     { | 
| 186 | 0 |         free(temp); | 
| 187 | 0 |         return NC_EMAXNAME; | 
| 188 | 0 |     } | 
| 189 |  |  | 
| 190 |  |     /* Copy the normalized name. */ | 
| 191 | 0 |     strcpy(norm_name, temp); | 
| 192 | 0 |     free(temp); | 
| 193 |  | 
 | 
| 194 | 0 |     return NC_NOERR; | 
| 195 | 0 | } | 
| 196 |  |  | 
| 197 |  | /** | 
| 198 |  |  * @internal Add a file to the list of libsrc4 open files. This is | 
| 199 |  |  * used by dispatch layers that wish to use the libsrc4 metadata | 
| 200 |  |  * model, but don't know about struct NC. This is the same as | 
| 201 |  |  * nc4_nc4f_list_add(), except it takes an ncid instead of an NC *, | 
| 202 |  |  * and also passes back the dispatchdata pointer. | 
| 203 |  |  * | 
| 204 |  |  * @param ncid The (already-assigned) ncid of the file (aka ext_ncid). | 
| 205 |  |  * @param path The file name of the new file. | 
| 206 |  |  * @param mode The mode flag. | 
| 207 |  |  * @param dispatchdata Void * that gets pointer to dispatch data, | 
| 208 |  |  * which is the NC_FILE_INFO_T struct allocated for this file and its | 
| 209 |  |  * metadata. Ignored if NULL. (This is passed as a void to allow | 
| 210 |  |  * external user-defined formats to use this function.) | 
| 211 |  |  * | 
| 212 |  |  * @return ::NC_NOERR No error. | 
| 213 |  |  * @return ::NC_EBADID No NC struct with this ext_ncid. | 
| 214 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 215 |  |  * @author Ed Hartnett | 
| 216 |  |  */ | 
| 217 |  | int | 
| 218 |  | nc4_file_list_add(int ncid, const char *path, int mode, void **dispatchdata) | 
| 219 | 0 | { | 
| 220 | 0 |     NC *nc; | 
| 221 | 0 |     int ret; | 
| 222 |  |  | 
| 223 |  |     /* Find NC pointer for this file. */ | 
| 224 | 0 |     if ((ret = NC_check_id(ncid, &nc))) | 
| 225 | 0 |         return ret; | 
| 226 |  |  | 
| 227 |  |     /* Add necessary structs to hold netcdf-4 file data. This is where | 
| 228 |  |      * the NC_FILE_INFO_T struct is allocated for the file. */ | 
| 229 | 0 |     if ((ret = nc4_nc4f_list_add(nc, path, mode))) | 
| 230 | 0 |         return ret; | 
| 231 |  |  | 
| 232 |  |     /* If the user wants a pointer to the NC_FILE_INFO_T, then provide | 
| 233 |  |      * it. */ | 
| 234 | 0 |     if (dispatchdata) | 
| 235 | 0 |         *dispatchdata = nc->dispatchdata; | 
| 236 |  | 
 | 
| 237 | 0 |     return NC_NOERR; | 
| 238 | 0 | } | 
| 239 |  |  | 
| 240 |  | /** | 
| 241 |  |  * @internal Change the ncid of an open file. This is needed for PIO | 
| 242 |  |  * integration. | 
| 243 |  |  * | 
| 244 |  |  * @param ncid The ncid of the file (aka ext_ncid). | 
| 245 |  |  * @param new_ncid_index The new ncid index to use (i.e. the first two bytes | 
| 246 |  |  * of the ncid). | 
| 247 |  |  * | 
| 248 |  |  * @return ::NC_NOERR No error. | 
| 249 |  |  * @return ::NC_EBADID No NC struct with this ext_ncid. | 
| 250 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 251 |  |  * @author Ed Hartnett | 
| 252 |  |  */ | 
| 253 |  | int | 
| 254 |  | nc4_file_change_ncid(int ncid, unsigned short new_ncid_index) | 
| 255 | 0 | { | 
| 256 | 0 |     NC *nc; | 
| 257 | 0 |     int ret; | 
| 258 |  | 
 | 
| 259 | 0 |     LOG((2, "%s: ncid %d new_ncid_index %d", __func__, ncid, new_ncid_index)); | 
| 260 |  |  | 
| 261 |  |     /* Find NC pointer for this file. */ | 
| 262 | 0 |     if ((ret = NC_check_id(ncid, &nc))) | 
| 263 | 0 |         return ret; | 
| 264 |  |  | 
| 265 |  |     /* Move it in the list. It will faile if list spot is already | 
| 266 |  |      * occupied. */ | 
| 267 | 0 |     LOG((3, "moving nc->ext_ncid %d nc->ext_ncid >> ID_SHIFT %d", | 
| 268 | 0 |          nc->ext_ncid, nc->ext_ncid >> ID_SHIFT)); | 
| 269 | 0 |     if (NC4_move_in_NCList(nc, new_ncid_index)) | 
| 270 | 0 |         return NC_EIO; | 
| 271 | 0 |     LOG((3, "moved to new_ncid_index %d new nc->ext_ncid %d", new_ncid_index, | 
| 272 | 0 |          nc->ext_ncid)); | 
| 273 |  | 
 | 
| 274 | 0 |     return NC_NOERR; | 
| 275 | 0 | } | 
| 276 |  |  | 
| 277 |  | /** | 
| 278 |  |  * @internal Get info about a file on the list of libsrc4 open | 
| 279 |  |  * files. This is used by dispatch layers that wish to use the libsrc4 | 
| 280 |  |  * metadata model, but don't know about struct NC. | 
| 281 |  |  * | 
| 282 |  |  * @param ncid The ncid of the file (aka ext_ncid). | 
| 283 |  |  * @param path A pointer that gets file name (< NC_MAX_NAME). Ignored | 
| 284 |  |  * if NULL. | 
| 285 |  |  * @param mode A pointer that gets the mode flag. Ignored if NULL. | 
| 286 |  |  * @param dispatchdata Void * that gets pointer to dispatch data, | 
| 287 |  |  * which is the NC_FILE_INFO_T struct allocated for this file and its | 
| 288 |  |  * metadata. Ignored if NULL. (This is passed as a void to allow | 
| 289 |  |  * external user-defined formats to use this function.) | 
| 290 |  |  * | 
| 291 |  |  * @return ::NC_NOERR No error. | 
| 292 |  |  * @return ::NC_EBADID No NC struct with this ext_ncid. | 
| 293 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 294 |  |  * @author Ed Hartnett | 
| 295 |  |  */ | 
| 296 |  | int | 
| 297 |  | nc4_file_list_get(int ncid, char **path, int *mode, void **dispatchdata) | 
| 298 | 0 | { | 
| 299 | 0 |     NC *nc; | 
| 300 | 0 |     int ret; | 
| 301 |  |  | 
| 302 |  |     /* Find NC pointer for this file. */ | 
| 303 | 0 |     if ((ret = NC_check_id(ncid, &nc))) | 
| 304 | 0 |         return ret; | 
| 305 |  |  | 
| 306 |  |     /* If the user wants path, give it. */ | 
| 307 | 0 |     if (path) | 
| 308 | 0 |         strncpy(*path, nc->path, NC_MAX_NAME); | 
| 309 |  |  | 
| 310 |  |     /* If the user wants mode, give it. */ | 
| 311 | 0 |     if (mode) | 
| 312 | 0 |         *mode = nc->mode; | 
| 313 |  |  | 
| 314 |  |     /* If the user wants dispatchdata, give it. */ | 
| 315 | 0 |     if (dispatchdata) | 
| 316 | 0 |         *dispatchdata = nc->dispatchdata; | 
| 317 |  | 
 | 
| 318 | 0 |     return NC_NOERR; | 
| 319 | 0 | } | 
| 320 |  |  | 
| 321 |  | /** | 
| 322 |  |  * @internal Given an NC pointer, add the necessary stuff for a | 
| 323 |  |  * netcdf-4 file. This allocates the NC_FILE_INFO_T struct for the | 
| 324 |  |  * file, which is used by libhdf5 and libhdf4 (and perhaps other | 
| 325 |  |  * future dispatch layers) to hold the metadata for the file. | 
| 326 |  |  * | 
| 327 |  |  * @param nc Pointer to file's NC struct. | 
| 328 |  |  * @param path The file name of the new file. | 
| 329 |  |  * @param mode The mode flag. | 
| 330 |  |  * | 
| 331 |  |  * @return ::NC_NOERR No error. | 
| 332 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 333 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 334 |  |  */ | 
| 335 |  | int | 
| 336 |  | nc4_nc4f_list_add(NC *nc, const char *path, int mode) | 
| 337 | 0 | { | 
| 338 | 0 |     NC_FILE_INFO_T *h5; | 
| 339 | 0 |     int retval; | 
| 340 |  | 
 | 
| 341 | 0 |     assert(nc && !NC4_DATA(nc) && path); | 
| 342 |  |  | 
| 343 |  |     /* We need to malloc and initialize the substructure | 
| 344 |  |        NC_FILE_INFO_T. */ | 
| 345 | 0 |     if (!(h5 = calloc(1, sizeof(NC_FILE_INFO_T)))) | 
| 346 | 0 |         return NC_ENOMEM; | 
| 347 | 0 |     nc->dispatchdata = h5; | 
| 348 | 0 |     h5->controller = nc; | 
| 349 |  | 
 | 
| 350 | 0 |     h5->hdr.sort = NCFIL; | 
| 351 | 0 |     h5->hdr.name = strdup(path);     | 
| 352 | 0 |     h5->hdr.id = nc->ext_ncid; | 
| 353 |  |  | 
| 354 |  |     /* Hang on to cmode, and note that we're in define mode. */ | 
| 355 | 0 |     h5->cmode = mode | NC_INDEF; | 
| 356 |  |  | 
| 357 |  |     /* The next_typeid needs to be set beyond the end of our atomic | 
| 358 |  |      * types. */ | 
| 359 | 0 |     h5->next_typeid = NC_FIRSTUSERTYPEID; | 
| 360 |  |  | 
| 361 |  |     /* Initialize lists for dimensions, types, and groups. */ | 
| 362 | 0 |     h5->alldims = nclistnew(); | 
| 363 | 0 |     h5->alltypes = nclistnew(); | 
| 364 | 0 |     h5->allgroups = nclistnew(); | 
| 365 |  |  | 
| 366 |  |     /* There's always at least one open group - the root | 
| 367 |  |      * group. Allocate space for one group's worth of information. Set | 
| 368 |  |      * its grp id, name, and allocate associated empty lists. */ | 
| 369 | 0 |     if ((retval = nc4_grp_list_add(h5, NULL, NC_GROUP_NAME, &h5->root_grp))) | 
| 370 | 0 |         return retval; | 
| 371 |  |  | 
| 372 | 0 |     return NC_NOERR; | 
| 373 | 0 | } | 
| 374 |  |  | 
| 375 |  | /** | 
| 376 |  |  * @internal Given an ncid, find the relevant group and return a | 
| 377 |  |  * pointer to it. | 
| 378 |  |  * | 
| 379 |  |  * @param ncid File and group ID. | 
| 380 |  |  * @param grp Pointer that gets pointer to group info struct. Ignored | 
| 381 |  |  * if NULL. | 
| 382 |  |  * | 
| 383 |  |  * @return ::NC_NOERR No error. | 
| 384 |  |  * @return ::NC_ENOTNC4 Not a netCDF-4 file. | 
| 385 |  |  * @author Ed Hartnett | 
| 386 |  |  */ | 
| 387 |  | int | 
| 388 |  | nc4_find_nc4_grp(int ncid, NC_GRP_INFO_T **grp) | 
| 389 | 0 | { | 
| 390 | 0 |     return nc4_find_nc_grp_h5(ncid, NULL, grp, NULL); | 
| 391 | 0 | } | 
| 392 |  |  | 
| 393 |  | /** | 
| 394 |  |  * @internal Given an ncid, find the relevant group and return a | 
| 395 |  |  * pointer to it, also set a pointer to the nc4_info struct of the | 
| 396 |  |  * related file. | 
| 397 |  |  * | 
| 398 |  |  * @param ncid File and group ID. | 
| 399 |  |  * @param grp Pointer that gets pointer to group info struct. Ignored | 
| 400 |  |  * if NULL. | 
| 401 |  |  * @param h5 Pointer that gets pointer to file info struct. Ignored if | 
| 402 |  |  * NULL. | 
| 403 |  |  * | 
| 404 |  |  * @return ::NC_NOERR No error. | 
| 405 |  |  * @return ::NC_EBADID Bad ncid. | 
| 406 |  |  * @author Ed Hartnett | 
| 407 |  |  */ | 
| 408 |  | int | 
| 409 |  | nc4_find_grp_h5(int ncid, NC_GRP_INFO_T **grp, NC_FILE_INFO_T **h5) | 
| 410 | 0 | { | 
| 411 | 0 |     return nc4_find_nc_grp_h5(ncid, NULL, grp, h5); | 
| 412 | 0 | } | 
| 413 |  |  | 
| 414 |  | /** | 
| 415 |  |  * @internal Find info for this file and group, and set pointers. | 
| 416 |  |  * | 
| 417 |  |  * @param ncid File and group ID. | 
| 418 |  |  * @param nc Pointer that gets a pointer to the file's NC | 
| 419 |  |  * struct. Ignored if NULL. | 
| 420 |  |  * @param grp Pointer that gets a pointer to the group | 
| 421 |  |  * struct. Ignored if NULL. | 
| 422 |  |  * @param h5 Pointer that gets HDF5 file struct. Ignored if NULL. | 
| 423 |  |  * | 
| 424 |  |  * @return ::NC_NOERR No error. | 
| 425 |  |  * @return ::NC_EBADID Bad ncid. | 
| 426 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 427 |  |  */ | 
| 428 |  | int | 
| 429 |  | nc4_find_nc_grp_h5(int ncid, NC **nc, NC_GRP_INFO_T **grp, NC_FILE_INFO_T **h5) | 
| 430 | 0 | { | 
| 431 | 0 |     NC_GRP_INFO_T *my_grp = NULL; | 
| 432 | 0 |     NC_FILE_INFO_T *my_h5 = NULL; | 
| 433 | 0 |     NC *my_nc; | 
| 434 | 0 |     int retval; | 
| 435 | 0 |     size_t index; | 
| 436 |  |  | 
| 437 |  |     /* Look up file metadata. */ | 
| 438 | 0 |     if ((retval = NC_check_id(ncid, &my_nc))) | 
| 439 | 0 |         return retval; | 
| 440 | 0 |     my_h5 = my_nc->dispatchdata; | 
| 441 | 0 |     assert(my_h5 && my_h5->root_grp); | 
| 442 |  |  | 
| 443 |  |     /* If we can't find it, the grp id part of ncid is bad. */ | 
| 444 | 0 |     index =  (ncid & GRP_ID_MASK); | 
| 445 | 0 |     if (!(my_grp = nclistget(my_h5->allgroups,index))) | 
| 446 | 0 |         return NC_EBADID; | 
| 447 |  |  | 
| 448 |  |     /* Return pointers to caller, if desired. */ | 
| 449 | 0 |     if (nc) | 
| 450 | 0 |         *nc = my_nc; | 
| 451 | 0 |     if (h5) | 
| 452 | 0 |         *h5 = my_h5; | 
| 453 | 0 |     if (grp) | 
| 454 | 0 |         *grp = my_grp; | 
| 455 |  | 
 | 
| 456 | 0 |     return NC_NOERR; | 
| 457 | 0 | } | 
| 458 |  |  | 
| 459 |  | /** | 
| 460 |  |  * @internal Given an ncid and varid, get pointers to the group and var | 
| 461 |  |  * metadata. | 
| 462 |  |  * | 
| 463 |  |  * @param ncid File ID. | 
| 464 |  |  * @param varid Variable ID. | 
| 465 |  |  * @param h5 Pointer that gets pointer to the NC_FILE_INFO_T struct | 
| 466 |  |  * for this file. Ignored if NULL. | 
| 467 |  |  * @param grp Pointer that gets pointer to group info. Ignored if | 
| 468 |  |  * NULL. | 
| 469 |  |  * @param var Pointer that gets pointer to var info. Ignored if NULL. | 
| 470 |  |  * | 
| 471 |  |  * @return ::NC_NOERR No error. | 
| 472 |  |  * @author Ed Hartnett | 
| 473 |  |  */ | 
| 474 |  | int | 
| 475 |  | nc4_find_grp_h5_var(int ncid, int varid, NC_FILE_INFO_T **h5, NC_GRP_INFO_T **grp, | 
| 476 |  |                     NC_VAR_INFO_T **var) | 
| 477 | 0 | { | 
| 478 | 0 |     NC_FILE_INFO_T *my_h5; | 
| 479 | 0 |     NC_GRP_INFO_T *my_grp; | 
| 480 | 0 |     NC_VAR_INFO_T *my_var; | 
| 481 | 0 |     int retval; | 
| 482 |  |  | 
| 483 |  |     /* Look up file and group metadata. */ | 
| 484 | 0 |     if ((retval = nc4_find_grp_h5(ncid, &my_grp, &my_h5))) | 
| 485 | 0 |         return retval; | 
| 486 | 0 |     assert(my_grp && my_h5); | 
| 487 |  |  | 
| 488 |  |     /* Find the var. */ | 
| 489 | 0 |     if (!(my_var = (NC_VAR_INFO_T *)ncindexith(my_grp->vars, (size_t)varid))) | 
| 490 | 0 |         return NC_ENOTVAR; | 
| 491 | 0 |     assert(my_var && my_var->hdr.id == varid); | 
| 492 |  |  | 
| 493 |  |     /* Return pointers that caller wants. */ | 
| 494 | 0 |     if (h5) | 
| 495 | 0 |         *h5 = my_h5; | 
| 496 | 0 |     if (grp) | 
| 497 | 0 |         *grp = my_grp; | 
| 498 | 0 |     if (var) | 
| 499 | 0 |         *var = my_var; | 
| 500 |  | 
 | 
| 501 | 0 |     return NC_NOERR; | 
| 502 | 0 | } | 
| 503 |  |  | 
| 504 |  | /** | 
| 505 |  |  * @internal Find a dim in the file. | 
| 506 |  |  * | 
| 507 |  |  * @param grp Pointer to group info struct. | 
| 508 |  |  * @param dimid Dimension ID to find. | 
| 509 |  |  * @param dim Pointer that gets pointer to dim info if found. | 
| 510 |  |  * @param dim_grp Pointer that gets pointer to group info of group | 
| 511 |  |  * that contains dimension. Ignored if NULL. | 
| 512 |  |  * | 
| 513 |  |  * @return ::NC_NOERR No error. | 
| 514 |  |  * @return ::NC_EBADDIM Dimension not found. | 
| 515 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 516 |  |  */ | 
| 517 |  | int | 
| 518 |  | nc4_find_dim(NC_GRP_INFO_T *grp, int dimid, NC_DIM_INFO_T **dim, | 
| 519 |  |              NC_GRP_INFO_T **dim_grp) | 
| 520 | 0 | { | 
| 521 | 0 |     assert(grp && grp->nc4_info && dim); | 
| 522 | 0 |     LOG((4, "%s: dimid %d", __func__, dimid)); | 
| 523 |  |  | 
| 524 |  |     /* Find the dim info. */ | 
| 525 | 0 |     if (!((*dim) = nclistget(grp->nc4_info->alldims, (size_t)dimid))) | 
| 526 | 0 |         return NC_EBADDIM; | 
| 527 |  |  | 
| 528 |  |     /* Give the caller the group the dimension is in. */ | 
| 529 | 0 |     if (dim_grp) | 
| 530 | 0 |         *dim_grp = (*dim)->container; | 
| 531 |  | 
 | 
| 532 | 0 |     return NC_NOERR; | 
| 533 | 0 | } | 
| 534 |  |  | 
| 535 |  | /** | 
| 536 |  |  * @internal Find a var (by name) in a grp. | 
| 537 |  |  * | 
| 538 |  |  * @param grp Pointer to group info. | 
| 539 |  |  * @param name Name of var to find. | 
| 540 |  |  * @param var Pointer that gets pointer to var info struct, if found. | 
| 541 |  |  * | 
| 542 |  |  * @return ::NC_NOERR No error. | 
| 543 |  |  * @author Ed Hartnett | 
| 544 |  |  */ | 
| 545 |  | int | 
| 546 |  | nc4_find_var(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var) | 
| 547 | 0 | { | 
| 548 | 0 |     assert(grp && var && name); | 
| 549 |  |  | 
| 550 |  |     /* Find the var info. */ | 
| 551 | 0 |     *var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name); | 
| 552 | 0 |     return NC_NOERR; | 
| 553 | 0 | } | 
| 554 |  |  | 
| 555 |  | /** | 
| 556 |  |  * @internal Locate netCDF type by name. | 
| 557 |  |  * | 
| 558 |  |  * @param start_grp Pointer to starting group info. | 
| 559 |  |  * @param name Name of type to find. | 
| 560 |  |  * | 
| 561 |  |  * @return Pointer to type info, or NULL if not found. | 
| 562 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 563 |  |  */ | 
| 564 |  | NC_TYPE_INFO_T * | 
| 565 |  | nc4_rec_find_named_type(NC_GRP_INFO_T *start_grp, char *name) | 
| 566 | 0 | { | 
| 567 | 0 |     NC_GRP_INFO_T *g; | 
| 568 | 0 |     NC_TYPE_INFO_T *type, *res; | 
| 569 |  | 
 | 
| 570 | 0 |     assert(start_grp); | 
| 571 |  |  | 
| 572 |  |     /* Does this group have the type we are searching for? */ | 
| 573 | 0 |     type  = (NC_TYPE_INFO_T*)ncindexlookup(start_grp->type,name); | 
| 574 | 0 |     if(type != NULL) | 
| 575 | 0 |         return type; | 
| 576 |  |  | 
| 577 |  |     /* Search subgroups. */ | 
| 578 | 0 |     for(size_t i=0;i<ncindexsize(start_grp->children);i++) { | 
| 579 | 0 |         g = (NC_GRP_INFO_T*)ncindexith(start_grp->children,i); | 
| 580 | 0 |         if(g == NULL) continue; | 
| 581 | 0 |         if ((res = nc4_rec_find_named_type(g, name))) | 
| 582 | 0 |             return res; | 
| 583 | 0 |     } | 
| 584 |  |     /* Can't find it. Oh, woe is me! */ | 
| 585 | 0 |     return NULL; | 
| 586 | 0 | } | 
| 587 |  |  | 
| 588 |  | /** | 
| 589 |  |  * @internal Use a netCDF typeid to find a type in a type_list. | 
| 590 |  |  * | 
| 591 |  |  * @param h5 Pointer to HDF5 file info struct. | 
| 592 |  |  * @param typeid The netCDF type ID. | 
| 593 |  |  * @param type Pointer to pointer to the list of type info structs. | 
| 594 |  |  * | 
| 595 |  |  * @return ::NC_NOERR No error. | 
| 596 |  |  * @return ::NC_EINVAL Invalid input. | 
| 597 |  |  * @author Ed Hartnett | 
| 598 |  |  */ | 
| 599 |  | int | 
| 600 |  | nc4_find_type(const NC_FILE_INFO_T *h5, nc_type typeid, NC_TYPE_INFO_T **type) | 
| 601 | 0 | { | 
| 602 |  |     /* Check inputs. */ | 
| 603 | 0 |     assert(h5); | 
| 604 | 0 |     if (typeid < 0 || !type) | 
| 605 | 0 |         return NC_EINVAL; | 
| 606 | 0 |     *type = NULL; | 
| 607 |  |  | 
| 608 |  |     /* Atomic types don't have associated NC_TYPE_INFO_T struct, just | 
| 609 |  |      * return NOERR. */ | 
| 610 | 0 |     if (typeid <= NC_STRING) | 
| 611 | 0 |         return NC_NOERR; | 
| 612 |  |  | 
| 613 |  |     /* Find the type. */ | 
| 614 | 0 |     if (!(*type = nclistget(h5->alltypes, (size_t)typeid))) | 
| 615 | 0 |         return NC_EBADTYPID; | 
| 616 |  |  | 
| 617 | 0 |     return NC_NOERR; | 
| 618 | 0 | } | 
| 619 |  |  | 
| 620 |  | /** | 
| 621 |  |  * @internal Given a group, find an att. If name is provided, use that, | 
| 622 |  |  * otherwise use the attnum. | 
| 623 |  |  * | 
| 624 |  |  * @param grp Pointer to group info struct. | 
| 625 |  |  * @param varid Variable ID. | 
| 626 |  |  * @param name Name to of attribute. | 
| 627 |  |  * @param attnum Number of attribute. | 
| 628 |  |  * @param att Pointer to pointer that gets attribute info struct. | 
| 629 |  |  * | 
| 630 |  |  * @return ::NC_NOERR No error. | 
| 631 |  |  * @return ::NC_ENOTVAR Variable not found. | 
| 632 |  |  * @return ::NC_ENOTATT Attribute not found. | 
| 633 |  |  * @author Ed Hartnett | 
| 634 |  |  */ | 
| 635 |  | int | 
| 636 |  | nc4_find_grp_att(NC_GRP_INFO_T *grp, int varid, const char *name, int attnum, | 
| 637 |  |                  NC_ATT_INFO_T **att) | 
| 638 | 0 | { | 
| 639 | 0 |     NC_VAR_INFO_T *var; | 
| 640 | 0 |     NC_ATT_INFO_T *my_att; | 
| 641 | 0 |     NCindex *attlist = NULL; | 
| 642 |  | 
 | 
| 643 | 0 |     assert(grp && grp->hdr.name && att); | 
| 644 |  | 
 | 
| 645 | 0 |     LOG((4, "%s: grp->name %s varid %d attnum %d", __func__, grp->hdr.name, | 
| 646 | 0 |          varid, attnum)); | 
| 647 |  |  | 
| 648 |  |     /* Get either the global or a variable attribute list. */ | 
| 649 | 0 |     if (varid == NC_GLOBAL) | 
| 650 | 0 |     { | 
| 651 | 0 |         attlist = grp->att; | 
| 652 | 0 |     } | 
| 653 | 0 |     else | 
| 654 | 0 |     { | 
| 655 | 0 |         var = (NC_VAR_INFO_T*)ncindexith(grp->vars,(size_t)varid); | 
| 656 | 0 |         if (!var) return NC_ENOTVAR; | 
| 657 |  |  | 
| 658 | 0 |         attlist = var->att; | 
| 659 | 0 |     } | 
| 660 | 0 |     assert(attlist); | 
| 661 |  |  | 
| 662 |  |     /* Now find the attribute by name or number. If a name is provided, | 
| 663 |  |      * ignore the attnum. */ | 
| 664 | 0 |     if (name) | 
| 665 | 0 |         my_att = (NC_ATT_INFO_T *)ncindexlookup(attlist, name); | 
| 666 | 0 |     else | 
| 667 | 0 |         my_att = (NC_ATT_INFO_T *)ncindexith(attlist, (size_t)attnum); | 
| 668 |  | 
 | 
| 669 | 0 |     if (!my_att) | 
| 670 | 0 |         return NC_ENOTATT; | 
| 671 |  |  | 
| 672 | 0 |     *att = my_att; | 
| 673 | 0 |     return NC_NOERR; | 
| 674 | 0 | } | 
| 675 |  |  | 
| 676 |  | /** | 
| 677 |  |  * @internal Given an ncid, varid, and name or attnum, find and return | 
| 678 |  |  * pointer to NC_ATT_INFO_T metadata. | 
| 679 |  |  * | 
| 680 |  |  * @param ncid File and group ID. | 
| 681 |  |  * @param varid Variable ID. | 
| 682 |  |  * @param name Name to of attribute. | 
| 683 |  |  * @param attnum Number of attribute. | 
| 684 |  |  * @param att Pointer to pointer that gets attribute info struct. | 
| 685 |  |  | 
| 686 |  |  * | 
| 687 |  |  * @return ::NC_NOERR No error. | 
| 688 |  |  * @return ::NC_ENOTVAR Variable not found. | 
| 689 |  |  * @return ::NC_ENOTATT Attribute not found. | 
| 690 |  |  * @author Ed Hartnett | 
| 691 |  |  */ | 
| 692 |  | int | 
| 693 |  | nc4_find_nc_att(int ncid, int varid, const char *name, int attnum, | 
| 694 |  |                 NC_ATT_INFO_T **att) | 
| 695 | 0 | { | 
| 696 | 0 |     NC_GRP_INFO_T *grp; | 
| 697 | 0 |     int retval; | 
| 698 |  | 
 | 
| 699 | 0 |     LOG((4, "nc4_find_nc_att: ncid 0x%x varid %d name %s attnum %d", | 
| 700 | 0 |          ncid, varid, name, attnum)); | 
| 701 |  |  | 
| 702 |  |     /* Find info for this file and group, and set pointer to each. */ | 
| 703 | 0 |     if ((retval = nc4_find_grp_h5(ncid, &grp, NULL))) | 
| 704 | 0 |         return retval; | 
| 705 | 0 |     assert(grp); | 
| 706 |  | 
 | 
| 707 | 0 |     return nc4_find_grp_att(grp, varid, name, attnum, att); | 
| 708 | 0 | } | 
| 709 |  |  | 
| 710 |  | /** | 
| 711 |  |  * @internal Add NC_OBJ to allXXX lists in a file | 
| 712 |  |  * | 
| 713 |  |  * @param file Pointer to the containing file | 
| 714 |  |  * @param obj Pointer to object to add. | 
| 715 |  |  * | 
| 716 |  |  * @author Dennis Heimbigner | 
| 717 |  |  */ | 
| 718 |  | static void | 
| 719 |  | obj_track(NC_FILE_INFO_T* file, NC_OBJ* obj) | 
| 720 | 0 | { | 
| 721 | 0 |     NClist* list = NULL; | 
| 722 |  |     /* record the object in the file  */ | 
| 723 | 0 |     switch (obj->sort) { | 
| 724 | 0 |     case NCDIM: list = file->alldims; break; | 
| 725 | 0 |     case NCTYP: list = file->alltypes; break; | 
| 726 | 0 |     case NCGRP: list = file->allgroups; break; | 
| 727 | 0 |     default: | 
| 728 | 0 |         assert(NC_FALSE); | 
| 729 | 0 |     } | 
| 730 |  |     /* Insert at the appropriate point in the list */ | 
| 731 | 0 |     nclistset(list,(size_t)obj->id,obj); | 
| 732 | 0 | } | 
| 733 |  |  | 
| 734 |  | /** | 
| 735 |  |  * @internal Create a new variable and insert into relevant | 
| 736 |  |  * lists. Dimensionality info need not be known. | 
| 737 |  |  * | 
| 738 |  |  * @param grp the containing group | 
| 739 |  |  * @param name the name for the new variable | 
| 740 |  |  * @param var Pointer in which to return a pointer to the new var. | 
| 741 |  |  * | 
| 742 |  |  * @return ::NC_NOERR No error. | 
| 743 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 744 |  |  * @author Ed Hartnett | 
| 745 |  |  */ | 
| 746 |  | int | 
| 747 |  | nc4_var_list_add2(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var) | 
| 748 | 0 | { | 
| 749 | 0 |     NC_VAR_INFO_T *new_var = NULL; | 
| 750 | 0 |     NCglobalstate* gs = NC_getglobalstate(); | 
| 751 |  |  | 
| 752 |  |     /* Allocate storage for new variable. */ | 
| 753 | 0 |     if (!(new_var = calloc(1, sizeof(NC_VAR_INFO_T)))) | 
| 754 | 0 |         return NC_ENOMEM; | 
| 755 | 0 |     new_var->hdr.sort = NCVAR; | 
| 756 | 0 |     new_var->container = grp; | 
| 757 |  |  | 
| 758 |  |     /* These are the HDF5-1.8.4 defaults. */ | 
| 759 | 0 |     new_var->chunkcache.size = gs->chunkcache.size; | 
| 760 | 0 |     new_var->chunkcache.nelems = gs->chunkcache.nelems; | 
| 761 | 0 |     new_var->chunkcache.preemption = gs->chunkcache.preemption; | 
| 762 |  |  | 
| 763 |  |     /* Now fill in the values in the var info structure. */ | 
| 764 | 0 |     new_var->hdr.id = (int)ncindexsize(grp->vars); | 
| 765 | 0 |     if (!(new_var->hdr.name = strdup(name))) { | 
| 766 | 0 |       if(new_var) | 
| 767 | 0 |         free(new_var); | 
| 768 | 0 |       return NC_ENOMEM; | 
| 769 | 0 |     } | 
| 770 |  |  | 
| 771 |  |     /* Create an indexed list for the attributes. */ | 
| 772 | 0 |     new_var->att = ncindexnew(0); | 
| 773 |  |  | 
| 774 |  |     /* Officially track it */ | 
| 775 | 0 |     ncindexadd(grp->vars, (NC_OBJ *)new_var); | 
| 776 |  |  | 
| 777 |  |     /* Set the var pointer, if one was given */ | 
| 778 | 0 |     if (var) | 
| 779 | 0 |         *var = new_var; | 
| 780 |  | 
 | 
| 781 | 0 |     return NC_NOERR; | 
| 782 | 0 | } | 
| 783 |  |  | 
| 784 |  | /** | 
| 785 |  |  * @internal Set the number of dims in an NC_VAR_INFO_T struct. | 
| 786 |  |  * | 
| 787 |  |  * @param var Pointer to the var. | 
| 788 |  |  * @param ndims Number of dimensions for this var. | 
| 789 |  |  * | 
| 790 |  |  * @return ::NC_NOERR No error. | 
| 791 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 792 |  |  * @author Ed Hartnett | 
| 793 |  |  */ | 
| 794 |  | int | 
| 795 |  | nc4_var_set_ndims(NC_VAR_INFO_T *var, int ndims) | 
| 796 | 0 | { | 
| 797 | 0 |     assert(var); | 
| 798 |  |  | 
| 799 |  |     /* Remember the number of dimensions. */ | 
| 800 | 0 |     var->ndims = (size_t)ndims; | 
| 801 |  |  | 
| 802 |  |     /* Allocate space for dimension information. */ | 
| 803 | 0 |     if (ndims) | 
| 804 | 0 |     { | 
| 805 | 0 |       if (!(var->dim = calloc((size_t)ndims, sizeof(NC_DIM_INFO_T *)))) | 
| 806 | 0 |             return NC_ENOMEM; | 
| 807 | 0 |       if (!(var->dimids = calloc((size_t)ndims, sizeof(int)))) | 
| 808 | 0 |             return NC_ENOMEM; | 
| 809 |  |  | 
| 810 |  |         /* Initialize dimids to illegal values (-1). See the comment | 
| 811 |  |            in nc4_rec_match_dimscales(). */ | 
| 812 | 0 |       memset(var->dimids, -1, (size_t)ndims * sizeof(int)); | 
| 813 | 0 |     } | 
| 814 |  |  | 
| 815 | 0 |     return NC_NOERR; | 
| 816 | 0 | } | 
| 817 |  |  | 
| 818 |  | /** | 
| 819 |  |  * @internal Create a new variable and insert int relevant list. | 
| 820 |  |  * | 
| 821 |  |  * @param grp the containing group | 
| 822 |  |  * @param name the name for the new variable | 
| 823 |  |  * @param ndims the rank of the new variable | 
| 824 |  |  * @param var Pointer in which to return a pointer to the new var. | 
| 825 |  |  * | 
| 826 |  |  * @return ::NC_NOERR No error. | 
| 827 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 828 |  |  * @author Ed Hartnett | 
| 829 |  |  */ | 
| 830 |  | int | 
| 831 |  | nc4_var_list_add(NC_GRP_INFO_T* grp, const char* name, int ndims, | 
| 832 |  |                  NC_VAR_INFO_T **var) | 
| 833 | 0 | { | 
| 834 | 0 |     int retval; | 
| 835 |  | 
 | 
| 836 | 0 |     if ((retval = nc4_var_list_add2(grp, name, var))) | 
| 837 | 0 |         return retval; | 
| 838 | 0 |     if ((retval = nc4_var_set_ndims(*var, ndims))) | 
| 839 | 0 |         return retval; | 
| 840 |  |  | 
| 841 | 0 |     return NC_NOERR; | 
| 842 | 0 | } | 
| 843 |  |  | 
| 844 |  | /** | 
| 845 |  |  * @internal Add a dimension to the dimension list for a group. | 
| 846 |  |  * | 
| 847 |  |  * @param grp container for the dim | 
| 848 |  |  * @param name for the dim | 
| 849 |  |  * @param len for the dim | 
| 850 |  |  * @param assignedid override dimid if >= 0 | 
| 851 |  |  * @param dim Pointer to pointer that gets the new dim info struct. | 
| 852 |  |  * | 
| 853 |  |  * @return ::NC_NOERR No error. | 
| 854 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 855 |  |  * @author Ed Hartnett | 
| 856 |  |  */ | 
| 857 |  | int | 
| 858 |  | nc4_dim_list_add(NC_GRP_INFO_T *grp, const char *name, size_t len, | 
| 859 |  |                  int assignedid, NC_DIM_INFO_T **dim) | 
| 860 | 0 | { | 
| 861 | 0 |     NC_DIM_INFO_T *new_dim = NULL; | 
| 862 |  | 
 | 
| 863 | 0 |     assert(grp && name); | 
| 864 |  |  | 
| 865 |  |     /* Allocate memory for dim metadata. */ | 
| 866 | 0 |     if (!(new_dim = calloc(1, sizeof(NC_DIM_INFO_T)))) | 
| 867 | 0 |         return NC_ENOMEM; | 
| 868 |  |  | 
| 869 | 0 |     new_dim->hdr.sort = NCDIM; | 
| 870 |  |  | 
| 871 |  |     /* Assign the dimension ID. */ | 
| 872 | 0 |     if (assignedid >= 0) | 
| 873 | 0 |         new_dim->hdr.id = assignedid; | 
| 874 | 0 |     else | 
| 875 | 0 |         new_dim->hdr.id = grp->nc4_info->next_dimid++; | 
| 876 |  |  | 
| 877 |  |     /* Remember the name and create a hash. */ | 
| 878 | 0 |     if (!(new_dim->hdr.name = strdup(name))) { | 
| 879 | 0 |       if(new_dim) | 
| 880 | 0 |         free(new_dim); | 
| 881 |  | 
 | 
| 882 | 0 |       return NC_ENOMEM; | 
| 883 | 0 |     } | 
| 884 |  |  | 
| 885 |  |     /* Is dimension unlimited? */ | 
| 886 | 0 |     new_dim->len = len; | 
| 887 | 0 |     if (len == NC_UNLIMITED) | 
| 888 | 0 |         new_dim->unlimited = NC_TRUE; | 
| 889 |  |  | 
| 890 |  |     /* Remember the containing group. */ | 
| 891 | 0 |     new_dim->container = grp; | 
| 892 |  |  | 
| 893 |  |     /* Add object to dimension list for this group. */ | 
| 894 | 0 |     ncindexadd(grp->dim, (NC_OBJ *)new_dim); | 
| 895 | 0 |     obj_track(grp->nc4_info, (NC_OBJ *)new_dim); | 
| 896 |  |  | 
| 897 |  |     /* Set the dim pointer, if one was given */ | 
| 898 | 0 |     if (dim) | 
| 899 | 0 |         *dim = new_dim; | 
| 900 |  | 
 | 
| 901 | 0 |     return NC_NOERR; | 
| 902 | 0 | } | 
| 903 |  |  | 
| 904 |  | /** | 
| 905 |  |  * @internal Add to an attribute list. | 
| 906 |  |  * | 
| 907 |  |  * @param list NCindex of att info structs. | 
| 908 |  |  * @param name name of the new attribute | 
| 909 |  |  * @param att Pointer to pointer that gets the new att info | 
| 910 |  |  * struct. Ignored if NULL. | 
| 911 |  |  * | 
| 912 |  |  * @return ::NC_NOERR No error. | 
| 913 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 914 |  |  * @author Ed Hartnett | 
| 915 |  |  */ | 
| 916 |  | int | 
| 917 |  | nc4_att_list_add(NCindex *list, const char *name, NC_ATT_INFO_T **att) | 
| 918 | 0 | { | 
| 919 | 0 |     NC_ATT_INFO_T *new_att = NULL; | 
| 920 |  | 
 | 
| 921 | 0 |     LOG((3, "%s: name %s ", __func__, name)); | 
| 922 |  | 
 | 
| 923 | 0 |     if (!(new_att = calloc(1, sizeof(NC_ATT_INFO_T)))) | 
| 924 | 0 |         return NC_ENOMEM; | 
| 925 | 0 |     new_att->hdr.sort = NCATT; | 
| 926 |  |  | 
| 927 |  |     /* Fill in the information we know. */ | 
| 928 | 0 |     new_att->hdr.id = (int)ncindexsize(list); | 
| 929 | 0 |     if (!(new_att->hdr.name = strdup(name))) { | 
| 930 | 0 |       if(new_att) | 
| 931 | 0 |         free(new_att); | 
| 932 | 0 |       return NC_ENOMEM; | 
| 933 | 0 |     } | 
| 934 |  |  | 
| 935 |  |     /* Add object to list as specified by its number */ | 
| 936 | 0 |     ncindexadd(list, (NC_OBJ *)new_att); | 
| 937 |  |  | 
| 938 |  |     /* Set the attribute pointer, if one was given */ | 
| 939 | 0 |     if (att) | 
| 940 | 0 |         *att = new_att; | 
| 941 |  | 
 | 
| 942 | 0 |     return NC_NOERR; | 
| 943 | 0 | } | 
| 944 |  |  | 
| 945 |  | /** | 
| 946 |  |  * @internal Add a group to a group list. | 
| 947 |  |  * | 
| 948 |  |  * @param h5 Pointer to the file info. | 
| 949 |  |  * @param parent Pointer to the parent group. Will be NULL when adding | 
| 950 |  |  * the root group. | 
| 951 |  |  * @param name Name of the group. | 
| 952 |  |  * @param grp Pointer to pointer that gets new group info | 
| 953 |  |  * struct. Ignored if NULL. | 
| 954 |  |  * | 
| 955 |  |  * @return ::NC_NOERR No error. | 
| 956 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 957 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 958 |  |  */ | 
| 959 |  | int | 
| 960 |  | nc4_grp_list_add(NC_FILE_INFO_T *h5, NC_GRP_INFO_T *parent, char *name, | 
| 961 |  |                  NC_GRP_INFO_T **grp) | 
| 962 | 0 | { | 
| 963 | 0 |     NC_GRP_INFO_T *new_grp; | 
| 964 |  |  | 
| 965 |  |     /* Check inputs. */ | 
| 966 | 0 |     assert(h5 && name); | 
| 967 | 0 |     LOG((3, "%s: name %s ", __func__, name)); | 
| 968 |  |  | 
| 969 |  |     /* Get the memory to store this groups info. */ | 
| 970 | 0 |     if (!(new_grp = calloc(1, sizeof(NC_GRP_INFO_T)))) | 
| 971 | 0 |         return NC_ENOMEM; | 
| 972 |  |  | 
| 973 |  |     /* Fill in this group's information. */ | 
| 974 | 0 |     new_grp->hdr.sort = NCGRP; | 
| 975 | 0 |     new_grp->nc4_info = h5; | 
| 976 | 0 |     new_grp->parent = parent; | 
| 977 |  |  | 
| 978 |  |     /* Assign the group ID. The root group will get id 0. */ | 
| 979 | 0 |     new_grp->hdr.id = h5->next_nc_grpid++; | 
| 980 | 0 |     assert(parent || !new_grp->hdr.id); | 
| 981 |  |  | 
| 982 |  |     /* Handle the group name. */ | 
| 983 | 0 |     if (!(new_grp->hdr.name = strdup(name))) | 
| 984 | 0 |     { | 
| 985 | 0 |         free(new_grp); | 
| 986 | 0 |         return NC_ENOMEM; | 
| 987 | 0 |     } | 
| 988 |  |  | 
| 989 |  |     /* Set up new indexed lists for stuff this group can contain. */ | 
| 990 | 0 |     new_grp->children = ncindexnew(0); | 
| 991 | 0 |     new_grp->dim = ncindexnew(0); | 
| 992 | 0 |     new_grp->att = ncindexnew(0); | 
| 993 | 0 |     new_grp->type = ncindexnew(0); | 
| 994 | 0 |     new_grp->vars = ncindexnew(0); | 
| 995 |  |  | 
| 996 |  |     /* Add object to lists */ | 
| 997 | 0 |     if (parent) | 
| 998 | 0 |         ncindexadd(parent->children, (NC_OBJ *)new_grp); | 
| 999 | 0 |     obj_track(h5, (NC_OBJ *)new_grp); | 
| 1000 |  |  | 
| 1001 |  |     /* Set the group pointer, if one was given */ | 
| 1002 | 0 |     if (grp) | 
| 1003 | 0 |         *grp = new_grp; | 
| 1004 |  | 
 | 
| 1005 | 0 |     return NC_NOERR; | 
| 1006 | 0 | } | 
| 1007 |  |  | 
| 1008 |  | /** | 
| 1009 |  |  * @internal Names for groups, variables, and types must not be the | 
| 1010 |  |  * same. This function checks that a proposed name is not already in | 
| 1011 |  |  * use. Normalzation of UTF8 strings should happen before this | 
| 1012 |  |  * function is called. | 
| 1013 |  |  * | 
| 1014 |  |  * @param grp Pointer to group info struct. | 
| 1015 |  |  * @param name Name to check. | 
| 1016 |  |  * | 
| 1017 |  |  * @return ::NC_NOERR No error. | 
| 1018 |  |  * @return ::NC_ENAMEINUSE Name is in use. | 
| 1019 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1020 |  |  */ | 
| 1021 |  | int | 
| 1022 |  | nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name) | 
| 1023 | 0 | { | 
| 1024 | 0 |     NC_TYPE_INFO_T *type; | 
| 1025 | 0 |     NC_GRP_INFO_T *g; | 
| 1026 | 0 |     NC_VAR_INFO_T *var; | 
| 1027 |  |  | 
| 1028 |  |     /* Any types of this name? */ | 
| 1029 | 0 |     type = (NC_TYPE_INFO_T*)ncindexlookup(grp->type,name); | 
| 1030 | 0 |     if(type != NULL) | 
| 1031 | 0 |         return NC_ENAMEINUSE; | 
| 1032 |  |  | 
| 1033 |  |     /* Any child groups of this name? */ | 
| 1034 | 0 |     g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,name); | 
| 1035 | 0 |     if(g != NULL) | 
| 1036 | 0 |         return NC_ENAMEINUSE; | 
| 1037 |  |  | 
| 1038 |  |     /* Any variables of this name? */ | 
| 1039 | 0 |     var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name); | 
| 1040 | 0 |     if(var != NULL) | 
| 1041 | 0 |         return NC_ENAMEINUSE; | 
| 1042 |  |  | 
| 1043 | 0 |     return NC_NOERR; | 
| 1044 | 0 | } | 
| 1045 |  |  | 
| 1046 |  | /** | 
| 1047 |  |  * @internal Create a type, but do not add to various lists nor | 
| 1048 |  |  * increment its ref count | 
| 1049 |  |  * | 
| 1050 |  |  * @param size Size of type in bytes. | 
| 1051 |  |  * @param name Name of type. | 
| 1052 |  |  * @param assignedid if >= 0 then override the default type id. | 
| 1053 |  |  * @param type Pointer that gets pointer to new type info struct. | 
| 1054 |  |  * | 
| 1055 |  |  * @return ::NC_NOERR No error. | 
| 1056 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 1057 |  |  * @author Ed Hartnett, Ward Fisher | 
| 1058 |  |  */ | 
| 1059 |  | int | 
| 1060 |  | nc4_type_new(size_t size, const char *name, int assignedid, | 
| 1061 |  |              NC_TYPE_INFO_T **type) | 
| 1062 | 0 | { | 
| 1063 | 0 |     NC_TYPE_INFO_T *new_type; | 
| 1064 |  | 
 | 
| 1065 | 0 |     LOG((4, "%s: size %d name %s assignedid %d", __func__, size, name, assignedid)); | 
| 1066 |  |  | 
| 1067 |  |     /* Check inputs. */ | 
| 1068 | 0 |     assert(type); | 
| 1069 |  |  | 
| 1070 |  |     /* Allocate memory for the type */ | 
| 1071 | 0 |     if (!(new_type = calloc(1, sizeof(NC_TYPE_INFO_T)))) | 
| 1072 | 0 |         return NC_ENOMEM; | 
| 1073 | 0 |     new_type->hdr.sort = NCTYP; | 
| 1074 | 0 |     new_type->hdr.id = assignedid; | 
| 1075 |  |  | 
| 1076 |  |     /* Remember info about this type. */ | 
| 1077 | 0 |     new_type->size = size; | 
| 1078 | 0 |     if (!(new_type->hdr.name = strdup(name))) { | 
| 1079 | 0 |         free(new_type); | 
| 1080 | 0 |         return NC_ENOMEM; | 
| 1081 | 0 |     } | 
| 1082 |  |  | 
| 1083 |  |     /* Return a pointer to the new type. */ | 
| 1084 | 0 |     *type = new_type; | 
| 1085 |  | 
 | 
| 1086 | 0 |     return NC_NOERR; | 
| 1087 | 0 | } | 
| 1088 |  |  | 
| 1089 |  | /** | 
| 1090 |  |  * @internal Add to the type list. | 
| 1091 |  |  * | 
| 1092 |  |  * @param grp Pointer to group info struct. | 
| 1093 |  |  * @param size Size of type in bytes. | 
| 1094 |  |  * @param name Name of type. | 
| 1095 |  |  * @param type Pointer that gets pointer to new type info | 
| 1096 |  |  * struct. | 
| 1097 |  |  * | 
| 1098 |  |  * @return ::NC_NOERR No error. | 
| 1099 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 1100 |  |  * @author Ed Hartnett | 
| 1101 |  |  */ | 
| 1102 |  | int | 
| 1103 |  | nc4_type_list_add(NC_GRP_INFO_T *grp, size_t size, const char *name, | 
| 1104 |  |                   NC_TYPE_INFO_T **type) | 
| 1105 | 0 | { | 
| 1106 | 0 |     NC_TYPE_INFO_T *new_type; | 
| 1107 | 0 |     int retval; | 
| 1108 |  |  | 
| 1109 |  |     /* Check inputs. */ | 
| 1110 | 0 |     assert(grp && name && type); | 
| 1111 | 0 |     LOG((4, "%s: size %d name %s", __func__, size, name)); | 
| 1112 |  |  | 
| 1113 |  |     /* Create the new TYPE_INFO struct. */ | 
| 1114 | 0 |     if ((retval = nc4_type_new(size, name, grp->nc4_info->next_typeid, | 
| 1115 | 0 |                                &new_type))) | 
| 1116 | 0 |         return retval; | 
| 1117 | 0 |     grp->nc4_info->next_typeid++; | 
| 1118 |  |  | 
| 1119 |  |     /* Increment the ref. count on the type */ | 
| 1120 | 0 |     new_type->rc++; | 
| 1121 |  |  | 
| 1122 |  |     /* Add object to lists */ | 
| 1123 | 0 |     ncindexadd(grp->type, (NC_OBJ *)new_type); | 
| 1124 | 0 |     obj_track(grp->nc4_info,(NC_OBJ*)new_type); | 
| 1125 |  |  | 
| 1126 |  |     /* back link */ | 
| 1127 | 0 |     new_type->container = grp; | 
| 1128 |  |  | 
| 1129 |  |     /* Return a pointer to the new type. */ | 
| 1130 | 0 |     *type = new_type; | 
| 1131 |  | 
 | 
| 1132 | 0 |     return NC_NOERR; | 
| 1133 | 0 | } | 
| 1134 |  |  | 
| 1135 |  | /** | 
| 1136 |  |  * @internal Add to the compound field list. | 
| 1137 |  |  * | 
| 1138 |  |  * @param parent parent type | 
| 1139 |  |  * @param name Name of the field. | 
| 1140 |  |  * @param offset Offset in bytes. | 
| 1141 |  |  * @param xtype The netCDF type of the field. | 
| 1142 |  |  * @param ndims The number of dimensions of the field. | 
| 1143 |  |  * @param dim_sizesp An array of dim sizes for the field. | 
| 1144 |  |  * | 
| 1145 |  |  * @return ::NC_NOERR No error. | 
| 1146 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1147 |  |  */ | 
| 1148 |  | int | 
| 1149 |  | nc4_field_list_add(NC_TYPE_INFO_T *parent, const char *name, | 
| 1150 |  |                    size_t offset, nc_type xtype, int ndims, | 
| 1151 |  |                    const int *dim_sizesp) | 
| 1152 | 0 | { | 
| 1153 | 0 |     NC_FIELD_INFO_T *field; | 
| 1154 |  |  | 
| 1155 |  |     /* Name has already been checked and UTF8 normalized. */ | 
| 1156 | 0 |     if (!name) | 
| 1157 | 0 |         return NC_EINVAL; | 
| 1158 |  |  | 
| 1159 |  |     /* Allocate storage for this field information. */ | 
| 1160 | 0 |     if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T)))) | 
| 1161 | 0 |         return NC_ENOMEM; | 
| 1162 | 0 |     field->hdr.sort = NCFLD; | 
| 1163 |  |  | 
| 1164 |  |     /* Store the information about this field. */ | 
| 1165 | 0 |     if (!(field->hdr.name = strdup(name))) | 
| 1166 | 0 |     { | 
| 1167 | 0 |         free(field); | 
| 1168 | 0 |         return NC_ENOMEM; | 
| 1169 | 0 |     } | 
| 1170 | 0 |     field->nc_typeid = xtype; | 
| 1171 | 0 |     field->offset = offset; | 
| 1172 | 0 |     field->ndims = ndims; | 
| 1173 | 0 |     if (ndims) | 
| 1174 | 0 |     { | 
| 1175 | 0 |         int i; | 
| 1176 | 0 |         if (!(field->dim_size = malloc((size_t)ndims * sizeof(int)))) | 
| 1177 | 0 |         { | 
| 1178 | 0 |             free(field->hdr.name); | 
| 1179 | 0 |             free(field); | 
| 1180 | 0 |             return NC_ENOMEM; | 
| 1181 | 0 |         } | 
| 1182 | 0 |         for (i = 0; i < ndims; i++) | 
| 1183 | 0 |             field->dim_size[i] = dim_sizesp[i]; | 
| 1184 | 0 |     } | 
| 1185 |  |  | 
| 1186 |  |     /* Add object to lists */ | 
| 1187 | 0 |     field->hdr.id = (int)nclistlength(parent->u.c.field); | 
| 1188 | 0 |     nclistpush(parent->u.c.field,field); | 
| 1189 |  | 
 | 
| 1190 | 0 |     return NC_NOERR; | 
| 1191 | 0 | } | 
| 1192 |  |  | 
| 1193 |  | /** | 
| 1194 |  |  * @internal Add a member to an enum type. | 
| 1195 |  |  * | 
| 1196 |  |  * @param parent Containing NC_TYPE_INFO_T object | 
| 1197 |  |  * @param size Size in bytes of new member. | 
| 1198 |  |  * @param name Name of the member. | 
| 1199 |  |  * @param value Value to associate with member. | 
| 1200 |  |  * | 
| 1201 |  |  * @return ::NC_NOERR No error. | 
| 1202 |  |  * @return ::NC_ENOMEM Out of memory. | 
| 1203 |  |  * @author Ed Hartnett | 
| 1204 |  |  */ | 
| 1205 |  | int | 
| 1206 |  | nc4_enum_member_add(NC_TYPE_INFO_T *parent, size_t size, | 
| 1207 |  |                     const char *name, const void *value) | 
| 1208 | 0 | { | 
| 1209 | 0 |     NC_ENUM_MEMBER_INFO_T *member; | 
| 1210 |  |  | 
| 1211 |  |     /* Name has already been checked. */ | 
| 1212 | 0 |     assert(name && size > 0 && value); | 
| 1213 | 0 |     LOG((4, "%s: size %d name %s", __func__, size, name)); | 
| 1214 |  |  | 
| 1215 |  |     /* Allocate storage for this field information. */ | 
| 1216 | 0 |     if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T)))) | 
| 1217 | 0 |         return NC_ENOMEM; | 
| 1218 | 0 |     if (!(member->value = malloc(size))) { | 
| 1219 | 0 |         free(member); | 
| 1220 | 0 |         return NC_ENOMEM; | 
| 1221 | 0 |     } | 
| 1222 | 0 |     if (!(member->name = strdup(name))) { | 
| 1223 | 0 |         free(member->value); | 
| 1224 | 0 |         free(member); | 
| 1225 | 0 |         return NC_ENOMEM; | 
| 1226 | 0 |     } | 
| 1227 |  |  | 
| 1228 |  |     /* Store the value for this member. */ | 
| 1229 | 0 |     memcpy(member->value, value, size); | 
| 1230 |  |  | 
| 1231 |  |     /* Add object to list */ | 
| 1232 | 0 |     nclistpush(parent->u.e.enum_member,member); | 
| 1233 |  | 
 | 
| 1234 | 0 |     return NC_NOERR; | 
| 1235 | 0 | } | 
| 1236 |  |  | 
| 1237 |  | /** | 
| 1238 |  |  * @internal Free up a field | 
| 1239 |  |  * | 
| 1240 |  |  * @param field Pointer to field info of field to delete. | 
| 1241 |  |  * | 
| 1242 |  |  * @author Ed Hartnett | 
| 1243 |  |  */ | 
| 1244 |  | static void | 
| 1245 |  | field_free(NC_FIELD_INFO_T *field) | 
| 1246 | 0 | { | 
| 1247 |  |     /* Free some stuff. */ | 
| 1248 | 0 |     if (field->hdr.name) | 
| 1249 | 0 |         free(field->hdr.name); | 
| 1250 | 0 |     if (field->dim_size) | 
| 1251 | 0 |         free(field->dim_size); | 
| 1252 |  |  | 
| 1253 |  |     /* Nc_Free the memory. */ | 
| 1254 | 0 |     free(field); | 
| 1255 | 0 | } | 
| 1256 |  |  | 
| 1257 |  | /** | 
| 1258 |  |  * @internal Free allocated space for type information. | 
| 1259 |  |  * | 
| 1260 |  |  * @param type Pointer to type info struct. | 
| 1261 |  |  * | 
| 1262 |  |  * @return ::NC_NOERR No error. | 
| 1263 |  |  * @return ::NC_EHDFERR HDF5 error. | 
| 1264 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1265 |  |  */ | 
| 1266 |  | int | 
| 1267 |  | nc4_type_free(NC_TYPE_INFO_T *type) | 
| 1268 | 0 | { | 
| 1269 | 0 |     size_t i; | 
| 1270 |  | 
 | 
| 1271 | 0 |     assert(type && type->rc && type->hdr.name); | 
| 1272 |  |  | 
| 1273 |  |     /* Decrement the ref. count on the type */ | 
| 1274 | 0 |     type->rc--; | 
| 1275 |  |  | 
| 1276 |  |     /* Release the type, if the ref. count drops to zero */ | 
| 1277 | 0 |     if (type->rc == 0) | 
| 1278 | 0 |     { | 
| 1279 | 0 |         LOG((4, "%s: deleting type %s", __func__, type->hdr.name)); | 
| 1280 |  |  | 
| 1281 |  |         /* Free the name. */ | 
| 1282 | 0 |         free(type->hdr.name); | 
| 1283 |  |  | 
| 1284 |  |         /* Enums and compound types have lists of fields to clean up. */ | 
| 1285 | 0 |         switch (type->nc_type_class) | 
| 1286 | 0 |         { | 
| 1287 | 0 |         case NC_COMPOUND: | 
| 1288 | 0 |         { | 
| 1289 | 0 |             NC_FIELD_INFO_T *field; | 
| 1290 |  |  | 
| 1291 |  |             /* Delete all the fields in this type (there will be some if its a | 
| 1292 |  |              * compound). */ | 
| 1293 | 0 |             for(i=0;i<nclistlength(type->u.c.field);i++) { | 
| 1294 | 0 |                 field = nclistget(type->u.c.field,i); | 
| 1295 | 0 |                 field_free(field); | 
| 1296 | 0 |             } | 
| 1297 | 0 |             nclistfree(type->u.c.field); | 
| 1298 | 0 |         } | 
| 1299 | 0 |         break; | 
| 1300 |  |  | 
| 1301 | 0 |         case NC_ENUM: | 
| 1302 | 0 |         { | 
| 1303 | 0 |             NC_ENUM_MEMBER_INFO_T *enum_member; | 
| 1304 |  |  | 
| 1305 |  |             /* Delete all the enum_members, if any. */ | 
| 1306 | 0 |             for(i=0;i<nclistlength(type->u.e.enum_member);i++) { | 
| 1307 | 0 |                 enum_member = nclistget(type->u.e.enum_member,i); | 
| 1308 | 0 |                 free(enum_member->value); | 
| 1309 | 0 |                 free(enum_member->name); | 
| 1310 | 0 |                 free(enum_member); | 
| 1311 | 0 |             } | 
| 1312 | 0 |             nclistfree(type->u.e.enum_member); | 
| 1313 | 0 |         } | 
| 1314 | 0 |         break; | 
| 1315 |  |  | 
| 1316 | 0 |         default: | 
| 1317 | 0 |             break; | 
| 1318 | 0 |         } | 
| 1319 |  |  | 
| 1320 |  |         /* Release the memory. */ | 
| 1321 | 0 |         free(type); | 
| 1322 | 0 |     } | 
| 1323 |  |  | 
| 1324 | 0 |     return NC_NOERR; | 
| 1325 | 0 | } | 
| 1326 |  |  | 
| 1327 |  | /** | 
| 1328 |  |  * @internal Free memory of an attribute object | 
| 1329 |  |  * | 
| 1330 |  |  * @param att Pointer to attribute info struct. | 
| 1331 |  |  * | 
| 1332 |  |  * @return ::NC_NOERR No error. | 
| 1333 |  |  * @author Ed Hartnett | 
| 1334 |  |  */ | 
| 1335 |  | int | 
| 1336 |  | nc4_att_free(NC_ATT_INFO_T *att) | 
| 1337 | 0 | { | 
| 1338 | 0 |     int stat = NC_NOERR; | 
| 1339 |  | 
 | 
| 1340 | 0 |     assert(att); | 
| 1341 | 0 |     LOG((3, "%s: name %s ", __func__, att->hdr.name)); | 
| 1342 |  |  | 
| 1343 |  |     /* Free the name. */ | 
| 1344 | 0 |     if (att->hdr.name) | 
| 1345 | 0 |         free(att->hdr.name); | 
| 1346 |  | 
 | 
| 1347 | 0 |     if (att->data) { | 
| 1348 | 0 |   NC_OBJ* parent; | 
| 1349 | 0 |   NC_FILE_INFO_T* h5 = NULL; | 
| 1350 |  |  | 
| 1351 |  |   /* Locate relevant objects */ | 
| 1352 | 0 |   parent = att->container; | 
| 1353 | 0 |   if(parent->sort == NCVAR) parent = (NC_OBJ*)(((NC_VAR_INFO_T*)parent)->container); | 
| 1354 | 0 |   assert(parent->sort == NCGRP); | 
| 1355 | 0 |   h5 = ((NC_GRP_INFO_T*)parent)->nc4_info; | 
| 1356 |  |   /* Reclaim the attribute data */ | 
| 1357 | 0 |   if((stat = NC_reclaim_data(h5->controller,att->nc_typeid,att->data,att->len))) goto done; | 
| 1358 | 0 |   free(att->data); /* reclaim top level */ | 
| 1359 | 0 |   att->data = NULL; | 
| 1360 | 0 |     } | 
| 1361 |  |  | 
| 1362 | 0 | done: | 
| 1363 | 0 |     free(att); | 
| 1364 | 0 |     return stat; | 
| 1365 | 0 | } | 
| 1366 |  |  | 
| 1367 |  | /** | 
| 1368 |  |  * @internal Delete a var, and free the memory. All HDF5 objects for | 
| 1369 |  |  * the var must be closed before this is called. | 
| 1370 |  |  * | 
| 1371 |  |  * @param var Pointer to the var info struct of var to delete. | 
| 1372 |  |  * | 
| 1373 |  |  * @return ::NC_NOERR No error. | 
| 1374 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1375 |  |  */ | 
| 1376 |  | static int | 
| 1377 |  | var_free(NC_VAR_INFO_T *var) | 
| 1378 | 0 | { | 
| 1379 | 0 |     int retval; | 
| 1380 |  | 
 | 
| 1381 | 0 |     assert(var); | 
| 1382 | 0 |     LOG((4, "%s: deleting var %s", __func__, var->hdr.name)); | 
| 1383 |  |  | 
| 1384 |  |     /* First delete all the attributes attached to this var. */ | 
| 1385 | 0 |     for (size_t i = 0; i < ncindexsize(var->att); i++) | 
| 1386 | 0 |         if ((retval = nc4_att_free((NC_ATT_INFO_T *)ncindexith(var->att, i)))) | 
| 1387 | 0 |             return retval; | 
| 1388 | 0 |     ncindexfree(var->att); | 
| 1389 |  |  | 
| 1390 |  |     /* Free some things that may be allocated. */ | 
| 1391 | 0 |     if (var->chunksizes) | 
| 1392 | 0 |         free(var->chunksizes); | 
| 1393 |  | 
 | 
| 1394 | 0 |     if (var->alt_name) | 
| 1395 | 0 |         free(var->alt_name); | 
| 1396 |  | 
 | 
| 1397 | 0 |     if (var->dimids) | 
| 1398 | 0 |         free(var->dimids); | 
| 1399 |  | 
 | 
| 1400 | 0 |     if (var->dim) | 
| 1401 | 0 |         free(var->dim); | 
| 1402 |  |  | 
| 1403 |  |     /* Delete any fill value allocation. */ | 
| 1404 | 0 |     if (var->fill_value) { | 
| 1405 | 0 |   int tid = var->type_info->hdr.id; | 
| 1406 | 0 |         if((retval = NC_reclaim_data_all(var->container->nc4_info->controller, tid, var->fill_value, 1))) return retval; | 
| 1407 | 0 |   var->fill_value = NULL; | 
| 1408 | 0 |     } | 
| 1409 |  |  | 
| 1410 |  |     /* Release type information */ | 
| 1411 | 0 |     if (var->type_info) | 
| 1412 | 0 |         if ((retval = nc4_type_free(var->type_info))) | 
| 1413 | 0 |             return retval; | 
| 1414 |  |  | 
| 1415 |  |     /* Do this last because debugging may need it */ | 
| 1416 | 0 |     if (var->hdr.name) | 
| 1417 | 0 |         free(var->hdr.name); | 
| 1418 |  |  | 
| 1419 |  |     /* Delete the var. */ | 
| 1420 | 0 |     free(var); | 
| 1421 |  | 
 | 
| 1422 | 0 |     return NC_NOERR; | 
| 1423 | 0 | } | 
| 1424 |  |  | 
| 1425 |  | /** | 
| 1426 |  |  * @internal  Delete a var, and free the memory. | 
| 1427 |  |  * | 
| 1428 |  |  * @param grp Pointer to the strct for the containing group. | 
| 1429 |  |  * @param var Pointer to the var info struct of var to delete. | 
| 1430 |  |  * | 
| 1431 |  |  * @return ::NC_NOERR No error. | 
| 1432 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1433 |  |  */ | 
| 1434 |  | int | 
| 1435 |  | nc4_var_list_del(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var) | 
| 1436 | 0 | { | 
| 1437 | 0 |     int i; | 
| 1438 |  | 
 | 
| 1439 | 0 |     assert(var && grp); | 
| 1440 |  |  | 
| 1441 |  |     /* Remove from lists */ | 
| 1442 | 0 |     i = ncindexfind(grp->vars, (NC_OBJ *)var); | 
| 1443 | 0 |     if (i >= 0) | 
| 1444 | 0 |         ncindexidel(grp->vars, (size_t)i); | 
| 1445 |  | 
 | 
| 1446 | 0 |     return var_free(var); | 
| 1447 | 0 | } | 
| 1448 |  |  | 
| 1449 |  | /** | 
| 1450 |  |  * @internal Free a dim | 
| 1451 |  |  * | 
| 1452 |  |  * @param dim Pointer to dim info struct of type to delete. | 
| 1453 |  |  * | 
| 1454 |  |  * @return ::NC_NOERR No error. | 
| 1455 |  |  * @author Ed Hartnett, Ward Fisher | 
| 1456 |  |  */ | 
| 1457 |  | static int | 
| 1458 |  | dim_free(NC_DIM_INFO_T *dim) | 
| 1459 | 0 | { | 
| 1460 | 0 |     assert(dim); | 
| 1461 | 0 |     LOG((4, "%s: deleting dim %s", __func__, dim->hdr.name)); | 
| 1462 |  |  | 
| 1463 |  |     /* Free memory allocated for names. */ | 
| 1464 | 0 |     if (dim->hdr.name) | 
| 1465 | 0 |         free(dim->hdr.name); | 
| 1466 |  | 
 | 
| 1467 | 0 |     free(dim); | 
| 1468 | 0 |     return NC_NOERR; | 
| 1469 | 0 | } | 
| 1470 |  |  | 
| 1471 |  | /** | 
| 1472 |  |  * @internal Free a dim and unlist it | 
| 1473 |  |  * | 
| 1474 |  |  * @param grp Pointer to dim's containing group | 
| 1475 |  |  * @param dim Pointer to dim info struct of type to delete. | 
| 1476 |  |  * | 
| 1477 |  |  * @return ::NC_NOERR No error. | 
| 1478 |  |  * @author Dennis Heimbigner | 
| 1479 |  |  */ | 
| 1480 |  | int | 
| 1481 |  | nc4_dim_list_del(NC_GRP_INFO_T *grp, NC_DIM_INFO_T *dim) | 
| 1482 | 0 | { | 
| 1483 | 0 |     if (grp && dim) | 
| 1484 | 0 |     { | 
| 1485 | 0 |         int pos = ncindexfind(grp->dim, (NC_OBJ *)dim); | 
| 1486 | 0 |         if(pos >= 0) | 
| 1487 | 0 |             ncindexidel(grp->dim, (size_t)pos); | 
| 1488 | 0 |     } | 
| 1489 |  | 
 | 
| 1490 | 0 |     return dim_free(dim); | 
| 1491 | 0 | } | 
| 1492 |  |  | 
| 1493 |  | /** | 
| 1494 |  |  * @internal Recursively delete the data for a group (and everything | 
| 1495 |  |  * it contains) in our internal metadata store. | 
| 1496 |  |  * | 
| 1497 |  |  * @param grp Pointer to group info struct. | 
| 1498 |  |  * | 
| 1499 |  |  * @return ::NC_NOERR No error. | 
| 1500 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1501 |  |  */ | 
| 1502 |  | int | 
| 1503 |  | nc4_rec_grp_del(NC_GRP_INFO_T *grp) | 
| 1504 | 0 | { | 
| 1505 | 0 |     int retval; | 
| 1506 |  | 
 | 
| 1507 | 0 |     assert(grp); | 
| 1508 | 0 |     LOG((3, "%s: grp->name %s", __func__, grp->hdr.name)); | 
| 1509 |  |  | 
| 1510 |  |     /* Recursively call this function for each child, if any, stopping | 
| 1511 |  |      * if there is an error. */ | 
| 1512 | 0 |     for (size_t i = 0; i < ncindexsize(grp->children); i++) | 
| 1513 | 0 |         if ((retval = nc4_rec_grp_del((NC_GRP_INFO_T *)ncindexith(grp->children, | 
| 1514 | 0 |                                                                   i)))) | 
| 1515 | 0 |             return retval; | 
| 1516 | 0 |     ncindexfree(grp->children); | 
| 1517 |  |  | 
| 1518 |  |     /* Free attributes */ | 
| 1519 | 0 |     for (size_t i = 0; i < ncindexsize(grp->att); i++) | 
| 1520 | 0 |         if ((retval = nc4_att_free((NC_ATT_INFO_T *)ncindexith(grp->att, i)))) | 
| 1521 | 0 |             return retval; | 
| 1522 | 0 |     ncindexfree(grp->att); | 
| 1523 |  |  | 
| 1524 |  |     /* Delete all vars. */ | 
| 1525 | 0 |     for (size_t i = 0; i < ncindexsize(grp->vars); i++) { | 
| 1526 | 0 |         NC_VAR_INFO_T* v = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); | 
| 1527 | 0 |         if ((retval = var_free(v))) | 
| 1528 | 0 |             return retval; | 
| 1529 | 0 |     } | 
| 1530 | 0 |     ncindexfree(grp->vars); | 
| 1531 |  |  | 
| 1532 |  |     /* Delete all dims, and free the list of dims. */ | 
| 1533 | 0 |     for (size_t i = 0; i < ncindexsize(grp->dim); i++) | 
| 1534 | 0 |         if ((retval = dim_free((NC_DIM_INFO_T *)ncindexith(grp->dim, i)))) | 
| 1535 | 0 |             return retval; | 
| 1536 | 0 |     ncindexfree(grp->dim); | 
| 1537 |  |  | 
| 1538 |  |     /* Delete all types. */ | 
| 1539 | 0 |     for (size_t i = 0; i < ncindexsize(grp->type); i++) | 
| 1540 | 0 |         if ((retval = nc4_type_free((NC_TYPE_INFO_T *)ncindexith(grp->type, i)))) | 
| 1541 | 0 |             return retval; | 
| 1542 | 0 |     ncindexfree(grp->type); | 
| 1543 |  |  | 
| 1544 |  |     /* Free the name. */ | 
| 1545 | 0 |     free(grp->hdr.name); | 
| 1546 |  |  | 
| 1547 |  |     /* Free up this group */ | 
| 1548 | 0 |     free(grp); | 
| 1549 |  | 
 | 
| 1550 | 0 |     return NC_NOERR; | 
| 1551 | 0 | } | 
| 1552 |  |  | 
| 1553 |  | /** | 
| 1554 |  |  * @internal Recursively delete the data for a group (and everything | 
| 1555 |  |  * it contains) in our internal metadata store. | 
| 1556 |  |  * | 
| 1557 |  |  * @param grp Pointer to group info struct. | 
| 1558 |  |  * | 
| 1559 |  |  * @return ::NC_NOERR No error. | 
| 1560 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1561 |  |  */ | 
| 1562 |  | int | 
| 1563 |  | nc4_rec_grp_del_att_data(NC_GRP_INFO_T *grp) | 
| 1564 | 0 | { | 
| 1565 | 0 |     int retval; | 
| 1566 |  | 
 | 
| 1567 | 0 |     assert(grp); | 
| 1568 | 0 |     LOG((3, "%s: grp->name %s", __func__, grp->hdr.name)); | 
| 1569 |  |  | 
| 1570 |  |     /* Recursively call this function for each child, if any, stopping | 
| 1571 |  |      * if there is an error. */ | 
| 1572 | 0 |     for (size_t i = 0; i < ncindexsize(grp->children); i++) | 
| 1573 | 0 |         if ((retval = nc4_rec_grp_del_att_data((NC_GRP_INFO_T *)ncindexith(grp->children, i)))) | 
| 1574 | 0 |             return retval; | 
| 1575 |  |  | 
| 1576 |  |     /* Free attribute data in this group */ | 
| 1577 | 0 |     for (size_t i = 0; i < ncindexsize(grp->att); i++) { | 
| 1578 | 0 |         NC_ATT_INFO_T * att = (NC_ATT_INFO_T*)ncindexith(grp->att, i); | 
| 1579 | 0 |         if((retval = NC_reclaim_data_all(grp->nc4_info->controller,att->nc_typeid,att->data,att->len))) | 
| 1580 | 0 |             return retval; | 
| 1581 | 0 |   att->data = NULL; | 
| 1582 | 0 |   att->len = 0; | 
| 1583 | 0 |   att->dirty = 0; | 
| 1584 | 0 |     } | 
| 1585 |  |  | 
| 1586 |  |     /* Delete att data from all contained vars in this group */ | 
| 1587 | 0 |     for (size_t i = 0; i < ncindexsize(grp->vars); i++) { | 
| 1588 | 0 |   NC_VAR_INFO_T* v = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); | 
| 1589 | 0 |   for(size_t j=0;j<ncindexsize(v->att);j++) { | 
| 1590 | 0 |       NC_ATT_INFO_T* att = (NC_ATT_INFO_T*)ncindexith(v->att, j); | 
| 1591 | 0 |         if((retval = NC_reclaim_data_all(grp->nc4_info->controller,att->nc_typeid,att->data,att->len))) | 
| 1592 | 0 |           return retval; | 
| 1593 | 0 |       att->data = NULL; | 
| 1594 | 0 |       att->len = 0; | 
| 1595 | 0 |       att->dirty = 0; | 
| 1596 | 0 |   } | 
| 1597 | 0 |     } | 
| 1598 |  |  | 
| 1599 | 0 |     return NC_NOERR; | 
| 1600 | 0 | } | 
| 1601 |  |  | 
| 1602 |  | /** | 
| 1603 |  |  * @internal Remove a NC_ATT_INFO_T from an index. | 
| 1604 |  |  * This will nc_free the memory too. | 
| 1605 |  |  * | 
| 1606 |  |  * @param list Pointer to pointer of list. | 
| 1607 |  |  * @param att Pointer to attribute info struct. | 
| 1608 |  |  * | 
| 1609 |  |  * @return ::NC_NOERR No error. | 
| 1610 |  |  * @author Dennis Heimbigner | 
| 1611 |  |  */ | 
| 1612 |  | int | 
| 1613 |  | nc4_att_list_del(NCindex *list, NC_ATT_INFO_T *att) | 
| 1614 | 0 | { | 
| 1615 | 0 |     assert(att && list); | 
| 1616 | 0 |     ncindexidel(list, (size_t)((NC_OBJ *)att)->id); | 
| 1617 | 0 |     return nc4_att_free(att); | 
| 1618 | 0 | } | 
| 1619 |  |  | 
| 1620 |  | /** | 
| 1621 |  |  * @internal Free all resources and memory associated with a | 
| 1622 |  |  * NC_FILE_INFO_T. This is the same as nc4_nc4f_list_del(), except it | 
| 1623 |  |  * takes ncid. This function allows external dispatch layers, like | 
| 1624 |  |  * PIO, to manipulate the file list without needing to know about | 
| 1625 |  |  * internal netcdf structures. | 
| 1626 |  |  * | 
| 1627 |  |  * @param ncid The ncid of the file to release. | 
| 1628 |  |  * | 
| 1629 |  |  * @return ::NC_NOERR No error. | 
| 1630 |  |  * @return ::NC_EBADID Bad ncid. | 
| 1631 |  |  * @author Ed Hartnett | 
| 1632 |  |  */ | 
| 1633 |  | int | 
| 1634 |  | nc4_file_list_del(int ncid) | 
| 1635 | 0 | { | 
| 1636 | 0 |     NC_FILE_INFO_T *h5; | 
| 1637 | 0 |     int retval; | 
| 1638 |  |  | 
| 1639 |  |     /* Find our metadata for this file. */ | 
| 1640 | 0 |     if ((retval = nc4_find_grp_h5(ncid, NULL, &h5))) | 
| 1641 | 0 |         return retval; | 
| 1642 | 0 |     assert(h5); | 
| 1643 |  |  | 
| 1644 |  |     /* Delete the file resources. */ | 
| 1645 | 0 |     if ((retval = nc4_nc4f_list_del(h5))) | 
| 1646 | 0 |         return retval; | 
| 1647 |  |  | 
| 1648 | 0 |     return NC_NOERR; | 
| 1649 | 0 | } | 
| 1650 |  |  | 
| 1651 |  | /** | 
| 1652 |  |  * @internal Free all resources and memory associated with a | 
| 1653 |  |  * NC_FILE_INFO_T. | 
| 1654 |  |  * | 
| 1655 |  |  * @param h5 Pointer to NC_FILE_INFO_T to be freed. | 
| 1656 |  |  * | 
| 1657 |  |  * @return ::NC_NOERR No error. | 
| 1658 |  |  * @author Ed Hartnett | 
| 1659 |  |  */ | 
| 1660 |  | int | 
| 1661 |  | nc4_nc4f_list_del(NC_FILE_INFO_T *h5) | 
| 1662 | 0 | { | 
| 1663 | 0 |     int retval; | 
| 1664 |  | 
 | 
| 1665 | 0 |     assert(h5); | 
| 1666 |  |  | 
| 1667 |  |     /* Order is important here. We must delete the attribute contents | 
| 1668 |  |        before deleteing any metadata because nc_reclaim_data depends | 
| 1669 |  |        on the existence of the type info. | 
| 1670 |  |     */ | 
| 1671 |  |  | 
| 1672 |  |     /* Delete all the attribute data contents in each group and variable. */ | 
| 1673 | 0 |     if ((retval = nc4_rec_grp_del_att_data(h5->root_grp))) | 
| 1674 | 0 |         return retval; | 
| 1675 |  |  | 
| 1676 |  |     /* Delete all the list contents for vars, dims, and atts, in each | 
| 1677 |  |      * group. */ | 
| 1678 | 0 |     if ((retval = nc4_rec_grp_del(h5->root_grp))) | 
| 1679 | 0 |         return retval; | 
| 1680 |  |  | 
| 1681 |  |     /* Cleanup these (extra) lists of all dims, groups, and types. */ | 
| 1682 | 0 |     nclistfree(h5->alldims); | 
| 1683 | 0 |     nclistfree(h5->allgroups); | 
| 1684 | 0 |     nclistfree(h5->alltypes); | 
| 1685 |  |  | 
| 1686 |  |     /* Free the NC_FILE_INFO_T struct. */ | 
| 1687 | 0 |     nullfree(h5->hdr.name); | 
| 1688 | 0 |     free(h5); | 
| 1689 |  | 
 | 
| 1690 | 0 |     return NC_NOERR; | 
| 1691 | 0 | } | 
| 1692 |  |  | 
| 1693 |  | /** | 
| 1694 |  |  * @internal Normalize a UTF8 name. Put the result in norm_name, which | 
| 1695 |  |  * can be NC_MAX_NAME + 1 in size. This function makes sure the free() | 
| 1696 |  |  * gets called on the return from utf8proc_NFC, and also ensures that | 
| 1697 |  |  * the name is not too long. | 
| 1698 |  |  * | 
| 1699 |  |  * @param name Name to normalize. | 
| 1700 |  |  * @param norm_name The normalized name. | 
| 1701 |  |  * | 
| 1702 |  |  * @return ::NC_NOERR No error. | 
| 1703 |  |  * @return ::NC_EMAXNAME Name too long. | 
| 1704 |  |  * @author Dennis Heimbigner | 
| 1705 |  |  */ | 
| 1706 |  | int | 
| 1707 |  | nc4_normalize_name(const char *name, char *norm_name) | 
| 1708 | 0 | { | 
| 1709 | 0 |     char *temp_name; | 
| 1710 | 0 |     int stat = nc_utf8_normalize((const unsigned char *)name,(unsigned char **)&temp_name); | 
| 1711 | 0 |     if(stat != NC_NOERR) | 
| 1712 | 0 |         return stat; | 
| 1713 | 0 |     if (strlen(temp_name) > NC_MAX_NAME) | 
| 1714 | 0 |     { | 
| 1715 | 0 |         free(temp_name); | 
| 1716 | 0 |         return NC_EMAXNAME; | 
| 1717 | 0 |     } | 
| 1718 | 0 |     strcpy(norm_name, temp_name); | 
| 1719 | 0 |     free(temp_name); | 
| 1720 | 0 |     return NC_NOERR; | 
| 1721 | 0 | } | 
| 1722 |  |  | 
| 1723 |  | #ifdef NETCDF_ENABLE_SET_LOG_LEVEL | 
| 1724 |  |  | 
| 1725 |  | /** | 
| 1726 |  |  * Initialize parallel I/O logging. For parallel I/O builds, open log | 
| 1727 |  |  * file, if not opened yet, or increment ref count if already open. | 
| 1728 |  |  * | 
| 1729 |  |  * @author Ed Hartnett | 
| 1730 |  |  */ | 
| 1731 |  | int | 
| 1732 |  | nc4_init_logging(void) | 
| 1733 |  | { | 
| 1734 |  |     int ret = NC_NOERR; | 
| 1735 |  |  | 
| 1736 |  | #if LOGGING | 
| 1737 |  | #if NC_HAS_PARALLEL4 | 
| 1738 |  |     if (!LOG_FILE && nc_log_level >= 0) | 
| 1739 |  |     { | 
| 1740 |  |         char log_filename[NC_MAX_NAME]; | 
| 1741 |  |         int my_rank = 0; | 
| 1742 |  |         int mpierr; | 
| 1743 |  |         int mpi_initialized; | 
| 1744 |  |  | 
| 1745 |  |         /* If MPI has been initialized find the rank. */ | 
| 1746 |  |         if ((mpierr = MPI_Initialized(&mpi_initialized))) | 
| 1747 |  |             return NC_EMPI; | 
| 1748 |  |         if (mpi_initialized) | 
| 1749 |  |         { | 
| 1750 |  |             if ((mpierr = MPI_Comm_rank(MPI_COMM_WORLD, &my_rank))) | 
| 1751 |  |                 return NC_EMPI; | 
| 1752 |  |         } | 
| 1753 |  |  | 
| 1754 |  |         /* Create a filename with the rank in it. */ | 
| 1755 |  |         snprintf(log_filename, sizeof(log_filename), "nc4_log_%d.log", my_rank); | 
| 1756 |  |  | 
| 1757 |  |         /* Open a file for this rank to log messages. */ | 
| 1758 |  |         if (!(LOG_FILE = fopen(log_filename, "w"))) | 
| 1759 |  |             return NC_EINTERNAL; | 
| 1760 |  |     } | 
| 1761 |  | #endif /* NC_HAS_PARALLEL4 */ | 
| 1762 |  | #endif /* LOGGING */ | 
| 1763 |  |  | 
| 1764 |  |     return ret; | 
| 1765 |  | } | 
| 1766 |  |  | 
| 1767 |  | /** | 
| 1768 |  |  * Finalize logging - close parallel I/O log files, if open. This does | 
| 1769 |  |  * nothing if logging is not enabled. | 
| 1770 |  |  * | 
| 1771 |  |  * @author Ed Hartnett | 
| 1772 |  |  */ | 
| 1773 |  | void | 
| 1774 |  | nc4_finalize_logging(void) | 
| 1775 |  | { | 
| 1776 |  | #if LOGGING | 
| 1777 |  | #if NC_HAS_PARALLEL4 | 
| 1778 |  |     if (LOG_FILE) | 
| 1779 |  |     { | 
| 1780 |  |         fclose(LOG_FILE); | 
| 1781 |  |         LOG_FILE = NULL; | 
| 1782 |  |     } | 
| 1783 |  | #endif /* NC_HAS_PARALLEL4 */ | 
| 1784 |  | #endif /* LOGGING */ | 
| 1785 |  | } | 
| 1786 |  |  | 
| 1787 |  | /** | 
| 1788 |  |  * Use this to set the global log level.  | 
| 1789 |  |  *  | 
| 1790 |  |  * Set it to NC_TURN_OFF_LOGGING (-1) to turn off all logging. Set it | 
| 1791 |  |  * to 0 to show only errors, and to higher numbers to show more and | 
| 1792 |  |  * more logging details. If logging is not enabled when building | 
| 1793 |  |  * netCDF, this function will do nothing. | 
| 1794 |  |  * | 
| 1795 |  |  * @param new_level The new logging level. | 
| 1796 |  |  * | 
| 1797 |  |  * @return ::NC_NOERR No error. | 
| 1798 |  |  * @author Ed Hartnett | 
| 1799 |  |  */ | 
| 1800 |  | int | 
| 1801 |  | nc_set_log_level(int new_level) | 
| 1802 |  | { | 
| 1803 |  | #if LOGGING | 
| 1804 |  |     /* Remember the new level. */ | 
| 1805 |  |     nc_log_level = new_level; | 
| 1806 |  |  | 
| 1807 |  | #if NC_HAS_PARALLEL4 | 
| 1808 |  |     /* For parallel I/O builds, call the log init/finalize functions | 
| 1809 |  |      * as needed, to open and close the log files. */ | 
| 1810 |  |     if (new_level >= 0) | 
| 1811 |  |     { | 
| 1812 |  |         if (!LOG_FILE) | 
| 1813 |  |             nc4_init_logging(); | 
| 1814 |  |     } | 
| 1815 |  |     else | 
| 1816 |  |         nc4_finalize_logging(); | 
| 1817 |  | #endif /* NC_HAS_PARALLEL4 */ | 
| 1818 |  |      | 
| 1819 |  |     LOG((1, "log_level changed to %d", nc_log_level)); | 
| 1820 |  | #endif /* LOGGING */ | 
| 1821 |  |      | 
| 1822 |  |     return NC_NOERR; | 
| 1823 |  | } | 
| 1824 |  | #endif /* NETCDF_ENABLE_SET_LOG_LEVEL */ | 
| 1825 |  |  | 
| 1826 |  | #if LOGGING | 
| 1827 |  | #define MAX_NESTS 10 | 
| 1828 |  | /** | 
| 1829 |  |  * @internal Recursively print the metadata of a group. | 
| 1830 |  |  * | 
| 1831 |  |  * @param grp Pointer to group info struct. | 
| 1832 |  |  * @param tab_count Number of tabs. | 
| 1833 |  |  * | 
| 1834 |  |  * @return ::NC_NOERR No error. | 
| 1835 |  |  * @author Ed Hartnett, Dennis Heimbigner | 
| 1836 |  |  */ | 
| 1837 |  | static int | 
| 1838 |  | rec_print_metadata(NC_GRP_INFO_T *grp, int tab_count) | 
| 1839 |  | { | 
| 1840 |  |     NC_ATT_INFO_T *att; | 
| 1841 |  |     NC_VAR_INFO_T *var; | 
| 1842 |  |     NC_DIM_INFO_T *dim; | 
| 1843 |  |     NC_TYPE_INFO_T *type; | 
| 1844 |  |     NC_FIELD_INFO_T *field; | 
| 1845 |  |     char tabs[MAX_NESTS+1] = ""; | 
| 1846 |  |     char temp_string[10]; | 
| 1847 |  |     int t, retval, d, i; | 
| 1848 |  |  | 
| 1849 |  |     /* Come up with a number of tabs relative to the group. */ | 
| 1850 |  |     for (t = 0; t < tab_count && t < MAX_NESTS; t++) | 
| 1851 |  |         tabs[t] = '\t'; | 
| 1852 |  |     tabs[t] = '\0'; | 
| 1853 |  |  | 
| 1854 |  |     LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d", | 
| 1855 |  |          tabs, grp->hdr.name, grp->hdr.id, ncindexsize(grp->vars), ncindexsize(grp->att))); | 
| 1856 |  |  | 
| 1857 |  |     for (i = 0; i < ncindexsize(grp->att); i++) | 
| 1858 |  |     { | 
| 1859 |  |         att = (NC_ATT_INFO_T *)ncindexith(grp->att, i); | 
| 1860 |  |         assert(att); | 
| 1861 |  |         LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d", | 
| 1862 |  |              tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len)); | 
| 1863 |  |     } | 
| 1864 |  |  | 
| 1865 |  |     for (i = 0; i < ncindexsize(grp->dim); i++) | 
| 1866 |  |     { | 
| 1867 |  |         dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, i); | 
| 1868 |  |         assert(dim); | 
| 1869 |  |         LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d", | 
| 1870 |  |              tabs, dim->hdr.id, dim->hdr.name, dim->len, dim->unlimited)); | 
| 1871 |  |     } | 
| 1872 |  |  | 
| 1873 |  |     for (i = 0; i < ncindexsize(grp->vars); i++) | 
| 1874 |  |     { | 
| 1875 |  |         int j; | 
| 1876 |  |         char storage_str[NC_MAX_NAME] = ""; | 
| 1877 |  |         char *dims_string = NULL; | 
| 1878 |  |  | 
| 1879 |  |         var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i); | 
| 1880 |  |         assert(var); | 
| 1881 |  |         if (var->ndims > 0) | 
| 1882 |  |         { | 
| 1883 |  |             if (!(dims_string = malloc(sizeof(char) * var->ndims * 4))) | 
| 1884 |  |                 return NC_ENOMEM; | 
| 1885 |  |             strcpy(dims_string, ""); | 
| 1886 |  |             for (d = 0; d < var->ndims; d++) | 
| 1887 |  |             { | 
| 1888 |  |                 snprintf(temp_string, sizeof(temp_string), " %d", var->dimids[d]); | 
| 1889 |  |                 strcat(dims_string, temp_string); | 
| 1890 |  |             } | 
| 1891 |  |         } | 
| 1892 |  |         if (!var->meta_read) | 
| 1893 |  |             strcat(storage_str, "unknown"); | 
| 1894 |  |         else if (var->storage == NC_CONTIGUOUS) | 
| 1895 |  |             strcat(storage_str, "contiguous"); | 
| 1896 |  |         else if (var->storage == NC_COMPACT) | 
| 1897 |  |             strcat(storage_str, "compact"); | 
| 1898 |  |         else if (var->storage == NC_CHUNKED) | 
| 1899 |  |             strcat(storage_str, "chunked"); | 
| 1900 |  |         else if (var->storage == NC_VIRTUAL) | 
| 1901 |  |             strcat(storage_str, "virtual"); | 
| 1902 |  |         else | 
| 1903 |  |             strcat(storage_str, "unknown"); | 
| 1904 |  |         LOG((2, "%s VARIABLE - varid: %d name: %s ndims: %d " | 
| 1905 |  |              "dimids:%s storage: %s", tabs, var->hdr.id, var->hdr.name, | 
| 1906 |  |              var->ndims, | 
| 1907 |  |              (dims_string ? dims_string : " -"), storage_str)); | 
| 1908 |  |         for (j = 0; j < ncindexsize(var->att); j++) | 
| 1909 |  |         { | 
| 1910 |  |             att = (NC_ATT_INFO_T *)ncindexith(var->att, j); | 
| 1911 |  |             assert(att); | 
| 1912 |  |             LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d", | 
| 1913 |  |                  tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len)); | 
| 1914 |  |         } | 
| 1915 |  |         if (dims_string) | 
| 1916 |  |             free(dims_string); | 
| 1917 |  |     } | 
| 1918 |  |  | 
| 1919 |  |     for (i = 0; i < ncindexsize(grp->type); i++) | 
| 1920 |  |     { | 
| 1921 |  |         type = (NC_TYPE_INFO_T*)ncindexith(grp->type, i); | 
| 1922 |  |         assert(type); | 
| 1923 |  |         LOG((2, "%s TYPE - nc_typeid: %d size: %d committed: %d name: %s", | 
| 1924 |  |              tabs, type->hdr.id, type->size, (int)type->committed, type->hdr.name)); | 
| 1925 |  |         /* Is this a compound type? */ | 
| 1926 |  |         if (type->nc_type_class == NC_COMPOUND) | 
| 1927 |  |         { | 
| 1928 |  |             int j; | 
| 1929 |  |             LOG((3, "compound type")); | 
| 1930 |  |             for (j = 0; j < nclistlength(type->u.c.field); j++) | 
| 1931 |  |             { | 
| 1932 |  |                 field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, j); | 
| 1933 |  |                 LOG((4, "field %s offset %d nctype %d ndims %d", field->hdr.name, | 
| 1934 |  |                      field->offset, field->nc_typeid, field->ndims)); | 
| 1935 |  |             } | 
| 1936 |  |         } | 
| 1937 |  |         else if (type->nc_type_class == NC_VLEN) | 
| 1938 |  |         { | 
| 1939 |  |             LOG((3, "VLEN type")); | 
| 1940 |  |             LOG((4, "base_nc_type: %d", type->u.v.base_nc_typeid)); | 
| 1941 |  |         } | 
| 1942 |  |         else if (type->nc_type_class == NC_OPAQUE) | 
| 1943 |  |             LOG((3, "Opaque type")); | 
| 1944 |  |         else if (type->nc_type_class == NC_ENUM) | 
| 1945 |  |         { | 
| 1946 |  |             LOG((3, "Enum type")); | 
| 1947 |  |             LOG((4, "base_nc_type: %d", type->u.e.base_nc_typeid)); | 
| 1948 |  |         } | 
| 1949 |  |         else | 
| 1950 |  |         { | 
| 1951 |  |             LOG((0, "Unknown class: %d", type->nc_type_class)); | 
| 1952 |  |             return NC_EBADTYPE; | 
| 1953 |  |         } | 
| 1954 |  |     } | 
| 1955 |  |  | 
| 1956 |  |     /* Call self for each child of this group. */ | 
| 1957 |  |     for (i = 0; i < ncindexsize(grp->children); i++) | 
| 1958 |  |         if ((retval = rec_print_metadata((NC_GRP_INFO_T *)ncindexith(grp->children, i), | 
| 1959 |  |                                          tab_count + 1))) | 
| 1960 |  |             return retval; | 
| 1961 |  |  | 
| 1962 |  |     return NC_NOERR; | 
| 1963 |  | } | 
| 1964 |  |  | 
| 1965 |  | /** | 
| 1966 |  |  * @internal Print out the internal metadata for a file. This is | 
| 1967 |  |  * useful to check that netCDF is working! Nonetheless, this function | 
| 1968 |  |  * will print nothing if logging is not set to at least two. | 
| 1969 |  |  * | 
| 1970 |  |  * @param Pointer to the file info struct. | 
| 1971 |  |  * | 
| 1972 |  |  * @return ::NC_NOERR No error. | 
| 1973 |  |  * @author Ed Hartnett | 
| 1974 |  |  */ | 
| 1975 |  | int | 
| 1976 |  | log_metadata_nc(NC_FILE_INFO_T *h5) | 
| 1977 |  | { | 
| 1978 |  |     LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x", | 
| 1979 |  |          h5->root_grp->nc4_info->controller->int_ncid, | 
| 1980 |  |          h5->root_grp->nc4_info->controller->ext_ncid)); | 
| 1981 |  |     if (!h5) | 
| 1982 |  |     { | 
| 1983 |  |         LOG((2, "This is a netCDF-3 file.")); | 
| 1984 |  |         return NC_NOERR; | 
| 1985 |  |     } | 
| 1986 |  |     LOG((2, "FILE - path: %s cmode: 0x%x parallel: %d redef: %d " | 
| 1987 |  |          "fill_mode: %d no_write: %d next_nc_grpid: %d", h5->root_grp->nc4_info->controller->path, | 
| 1988 |  |          h5->cmode, (int)h5->parallel, (int)h5->redef, h5->fill_mode, (int)h5->no_write, | 
| 1989 |  |          h5->next_nc_grpid)); | 
| 1990 |  |     if(nc_log_level >= 2) | 
| 1991 |  |         return rec_print_metadata(h5->root_grp, 0); | 
| 1992 |  |     return NC_NOERR; | 
| 1993 |  | } | 
| 1994 |  |  | 
| 1995 |  | #endif /*LOGGING */ | 
| 1996 |  |  | 
| 1997 |  | /** | 
| 1998 |  |  * @internal Show the in-memory metadata for a netcdf file. This | 
| 1999 |  |  * function does nothing unless netCDF was built with | 
| 2000 |  |  * the configure option --enable-logging. | 
| 2001 |  |  * | 
| 2002 |  |  * @param ncid File and group ID. | 
| 2003 |  |  * | 
| 2004 |  |  * @return ::NC_NOERR No error. | 
| 2005 |  |  * @return ::NC_EBADID Bad ncid. | 
| 2006 |  |  * @author Ed Hartnett | 
| 2007 |  |  */ | 
| 2008 |  | int | 
| 2009 |  | NC4_show_metadata(int ncid) | 
| 2010 | 0 | { | 
| 2011 | 0 |     int retval = NC_NOERR; | 
| 2012 |  | #if LOGGING | 
| 2013 |  |     NC_FILE_INFO_T *h5; | 
| 2014 |  |     int old_log_level = nc_log_level; | 
| 2015 |  |  | 
| 2016 |  |     /* Find file metadata. */ | 
| 2017 |  |     if ((retval = nc4_find_grp_h5(ncid, NULL, &h5))) | 
| 2018 |  |         return retval; | 
| 2019 |  |  | 
| 2020 |  |     /* Log level must be 2 to see metadata. */ | 
| 2021 |  |     nc_log_level = 2; | 
| 2022 |  |     retval = log_metadata_nc(h5); | 
| 2023 |  |     nc_log_level = old_log_level; | 
| 2024 |  | #endif /*LOGGING*/ | 
| 2025 | 0 |     return retval; | 
| 2026 | 0 | } | 
| 2027 |  |  | 
| 2028 |  | /** | 
| 2029 |  |  * @internal Define a binary searcher for reserved attributes | 
| 2030 |  |  * @param name for which to search | 
| 2031 |  |  * @return pointer to the matching NC_reservedatt structure. | 
| 2032 |  |  * @return NULL if not found. | 
| 2033 |  |  * @author Dennis Heimbigner | 
| 2034 |  |  */ | 
| 2035 |  | const NC_reservedatt* | 
| 2036 |  | NC_findreserved(const char* name) | 
| 2037 | 0 | { | 
| 2038 |  | #if 0 | 
| 2039 |  |     int n = NRESERVED; | 
| 2040 |  |     int L = 0; | 
| 2041 |  |     int R = (n - 1); | 
| 2042 |  |  | 
| 2043 |  |     for(;;) { | 
| 2044 |  |         if(L > R) break; | 
| 2045 |  |         int m = (L + R) / 2; | 
| 2046 |  |         const NC_reservedatt* p = &NC_reserved[m]; | 
| 2047 |  |         int cmp = strcmp(p->name,name); | 
| 2048 |  |   if(cmp == 0) return p; | 
| 2049 |  |         if(cmp < 0) | 
| 2050 |  |             L = (m + 1); | 
| 2051 |  |         else /*cmp > 0*/ | 
| 2052 |  |             R = (m - 1); | 
| 2053 |  |     } | 
| 2054 |  |     return NULL; | 
| 2055 |  | #else | 
| 2056 | 0 |     return (const NC_reservedatt*)bsearch(name,NC_reserved,NRESERVED,sizeof(NC_reservedatt),bincmp); | 
| 2057 | 0 | #endif | 
| 2058 | 0 | } | 
| 2059 |  |  | 
| 2060 |  | /* Ed Hartness requires this function */ | 
| 2061 |  | static int | 
| 2062 |  | NC4_move_in_NCList(NC* nc, int new_id) | 
| 2063 | 0 | { | 
| 2064 | 0 |     int stat = move_in_NCList(nc,new_id); | 
| 2065 | 0 |     if(stat == NC_NOERR) { | 
| 2066 |  |         /* Synchronize header */ | 
| 2067 | 0 |         if(nc->dispatchdata) | 
| 2068 | 0 |       ((NC_OBJ*)nc->dispatchdata)->id = nc->ext_ncid; | 
| 2069 | 0 |     } | 
| 2070 | 0 |     return stat; | 
| 2071 | 0 | } | 
| 2072 |  |  | 
| 2073 |  | static int | 
| 2074 |  | sortcmp(const void* arg1, const void* arg2) | 
| 2075 | 35 | { | 
| 2076 | 35 |     NC_reservedatt* r1 = (NC_reservedatt*)arg1; | 
| 2077 | 35 |     NC_reservedatt* r2 = (NC_reservedatt*)arg2; | 
| 2078 | 35 |     return strcmp(r1->name,r2->name); | 
| 2079 | 35 | } | 
| 2080 |  |  | 
| 2081 |  | static int | 
| 2082 |  | bincmp(const void* arg1, const void* arg2) | 
| 2083 | 0 | { | 
| 2084 | 0 |     const char* name = (const char*)arg1; | 
| 2085 | 0 |     NC_reservedatt* ra = (NC_reservedatt*)arg2; | 
| 2086 | 0 |     return strcmp(name,ra->name); | 
| 2087 | 0 | } | 
| 2088 |  |  | 
| 2089 |  | void | 
| 2090 |  | NC_initialize_reserved(void) | 
| 2091 | 1 | { | 
| 2092 |  |     /* Guarantee the reserved attribute list is sorted */ | 
| 2093 | 1 |     qsort((void*)NC_reserved,NRESERVED,sizeof(NC_reservedatt),sortcmp); | 
| 2094 | 1 | } |