Coverage Report

Created: 2022-11-18 06:58

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