Coverage Report

Created: 2023-05-28 06:42

/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_index 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
 * @return ::NC_NOERR No error.
729
 * @return ::NC_ENOMEM Out of memory.
730
 * @author Ed Hartnett
731
 */
732
int
733
nc4_var_list_add2(NC_GRP_INFO_T *grp, const char *name, NC_VAR_INFO_T **var)
734
0
{
735
0
    NC_VAR_INFO_T *new_var = NULL;
736
0
    NCglobalstate* gs = NC_getglobalstate();
737
738
    /* Allocate storage for new variable. */
739
0
    if (!(new_var = calloc(1, sizeof(NC_VAR_INFO_T))))
740
0
        return NC_ENOMEM;
741
0
    new_var->hdr.sort = NCVAR;
742
0
    new_var->container = grp;
743
744
    /* These are the HDF5-1.8.4 defaults. */
745
0
    new_var->chunkcache.size = gs->chunkcache.size;
746
0
    new_var->chunkcache.nelems = gs->chunkcache.nelems;
747
0
    new_var->chunkcache.preemption = gs->chunkcache.preemption;
748
749
    /* Now fill in the values in the var info structure. */
750
0
    new_var->hdr.id = ncindexsize(grp->vars);
751
0
    if (!(new_var->hdr.name = strdup(name))) {
752
0
      if(new_var)
753
0
        free(new_var);
754
0
      return NC_ENOMEM;
755
0
    }
756
757
    /* Create an indexed list for the attributes. */
758
0
    new_var->att = ncindexnew(0);
759
760
    /* Officially track it */
761
0
    ncindexadd(grp->vars, (NC_OBJ *)new_var);
762
763
    /* Set the var pointer, if one was given */
764
0
    if (var)
765
0
        *var = new_var;
766
767
0
    return NC_NOERR;
768
0
}
769
770
/**
771
 * @internal Set the number of dims in an NC_VAR_INFO_T struct.
772
 *
773
 * @param var Pointer to the var.
774
 * @param ndims Number of dimensions for this var.
775
 *
776
 * @return ::NC_NOERR No error.
777
 * @return ::NC_ENOMEM Out of memory.
778
 * @author Ed Hartnett
779
 */
780
int
781
nc4_var_set_ndims(NC_VAR_INFO_T *var, int ndims)
782
0
{
783
0
    assert(var);
784
785
    /* Remember the number of dimensions. */
786
0
    var->ndims = ndims;
787
788
    /* Allocate space for dimension information. */
789
0
    if (ndims)
790
0
    {
791
0
        if (!(var->dim = calloc(ndims, sizeof(NC_DIM_INFO_T *))))
792
0
            return NC_ENOMEM;
793
0
        if (!(var->dimids = calloc(ndims, sizeof(int))))
794
0
            return NC_ENOMEM;
795
796
        /* Initialize dimids to illegal values (-1). See the comment
797
           in nc4_rec_match_dimscales(). */
798
0
        memset(var->dimids, -1, ndims * sizeof(int));
799
0
    }
800
801
0
    return NC_NOERR;
802
0
}
803
804
/**
805
 * @internal Create a new variable and insert int relevant list.
806
 *
807
 * @param grp the containing group
808
 * @param name the name for the new variable
809
 * @param ndims the rank of the new variable
810
 * @param var Pointer in which to return a pointer to the new var.
811
 *
812
 * @return ::NC_NOERR No error.
813
 * @return ::NC_ENOMEM Out of memory.
814
 * @author Ed Hartnett
815
 */
816
int
817
nc4_var_list_add(NC_GRP_INFO_T* grp, const char* name, int ndims,
818
                 NC_VAR_INFO_T **var)
819
0
{
820
0
    int retval;
821
822
0
    if ((retval = nc4_var_list_add2(grp, name, var)))
823
0
        return retval;
824
0
    if ((retval = nc4_var_set_ndims(*var, ndims)))
825
0
        return retval;
826
827
0
    return NC_NOERR;
828
0
}
829
830
/**
831
 * @internal Add a dimension to the dimension list for a group.
832
 *
833
 * @param grp container for the dim
834
 * @param name for the dim
835
 * @param len for the dim
836
 * @param assignedid override dimid if >= 0
837
 * @param dim Pointer to pointer that gets the new dim info struct.
838
 *
839
 * @return ::NC_NOERR No error.
840
 * @return ::NC_ENOMEM Out of memory.
841
 * @author Ed Hartnett
842
 */
843
int
844
nc4_dim_list_add(NC_GRP_INFO_T *grp, const char *name, size_t len,
845
                 int assignedid, NC_DIM_INFO_T **dim)
846
0
{
847
0
    NC_DIM_INFO_T *new_dim = NULL;
848
849
0
    assert(grp && name);
850
851
    /* Allocate memory for dim metadata. */
852
0
    if (!(new_dim = calloc(1, sizeof(NC_DIM_INFO_T))))
853
0
        return NC_ENOMEM;
854
855
0
    new_dim->hdr.sort = NCDIM;
856
857
    /* Assign the dimension ID. */
858
0
    if (assignedid >= 0)
859
0
        new_dim->hdr.id = assignedid;
860
0
    else
861
0
        new_dim->hdr.id = grp->nc4_info->next_dimid++;
862
863
    /* Remember the name and create a hash. */
864
0
    if (!(new_dim->hdr.name = strdup(name))) {
865
0
      if(new_dim)
866
0
        free(new_dim);
867
868
0
      return NC_ENOMEM;
869
0
    }
870
871
    /* Is dimension unlimited? */
872
0
    new_dim->len = len;
873
0
    if (len == NC_UNLIMITED)
874
0
        new_dim->unlimited = NC_TRUE;
875
876
    /* Remember the containing group. */
877
0
    new_dim->container = grp;
878
879
    /* Add object to dimension list for this group. */
880
0
    ncindexadd(grp->dim, (NC_OBJ *)new_dim);
881
0
    obj_track(grp->nc4_info, (NC_OBJ *)new_dim);
882
883
    /* Set the dim pointer, if one was given */
884
0
    if (dim)
885
0
        *dim = new_dim;
886
887
0
    return NC_NOERR;
888
0
}
889
890
/**
891
 * @internal Add to an attribute list.
892
 *
893
 * @param list NCindex of att info structs.
894
 * @param name name of the new attribute
895
 * @param att Pointer to pointer that gets the new att info
896
 * struct. Ignored if NULL.
897
 *
898
 * @return ::NC_NOERR No error.
899
 * @return ::NC_ENOMEM Out of memory.
900
 * @author Ed Hartnett
901
 */
902
int
903
nc4_att_list_add(NCindex *list, const char *name, NC_ATT_INFO_T **att)
904
0
{
905
0
    NC_ATT_INFO_T *new_att = NULL;
906
907
0
    LOG((3, "%s: name %s ", __func__, name));
908
909
0
    if (!(new_att = calloc(1, sizeof(NC_ATT_INFO_T))))
910
0
        return NC_ENOMEM;
911
0
    new_att->hdr.sort = NCATT;
912
913
    /* Fill in the information we know. */
914
0
    new_att->hdr.id = ncindexsize(list);
915
0
    if (!(new_att->hdr.name = strdup(name))) {
916
0
      if(new_att)
917
0
        free(new_att);
918
0
      return NC_ENOMEM;
919
0
    }
920
921
    /* Add object to list as specified by its number */
922
0
    ncindexadd(list, (NC_OBJ *)new_att);
923
924
    /* Set the attribute pointer, if one was given */
925
0
    if (att)
926
0
        *att = new_att;
927
928
0
    return NC_NOERR;
929
0
}
930
931
/**
932
 * @internal Add a group to a group list.
933
 *
934
 * @param h5 Pointer to the file info.
935
 * @param parent Pointer to the parent group. Will be NULL when adding
936
 * the root group.
937
 * @param name Name of the group.
938
 * @param grp Pointer to pointer that gets new group info
939
 * struct. Ignored if NULL.
940
 *
941
 * @return ::NC_NOERR No error.
942
 * @return ::NC_ENOMEM Out of memory.
943
 * @author Ed Hartnett, Dennis Heimbigner
944
 */
945
int
946
nc4_grp_list_add(NC_FILE_INFO_T *h5, NC_GRP_INFO_T *parent, char *name,
947
                 NC_GRP_INFO_T **grp)
948
0
{
949
0
    NC_GRP_INFO_T *new_grp;
950
951
    /* Check inputs. */
952
0
    assert(h5 && name);
953
0
    LOG((3, "%s: name %s ", __func__, name));
954
955
    /* Get the memory to store this groups info. */
956
0
    if (!(new_grp = calloc(1, sizeof(NC_GRP_INFO_T))))
957
0
        return NC_ENOMEM;
958
959
    /* Fill in this group's information. */
960
0
    new_grp->hdr.sort = NCGRP;
961
0
    new_grp->nc4_info = h5;
962
0
    new_grp->parent = parent;
963
964
    /* Assign the group ID. The root group will get id 0. */
965
0
    new_grp->hdr.id = h5->next_nc_grpid++;
966
0
    assert(parent || !new_grp->hdr.id);
967
968
    /* Handle the group name. */
969
0
    if (!(new_grp->hdr.name = strdup(name)))
970
0
    {
971
0
        free(new_grp);
972
0
        return NC_ENOMEM;
973
0
    }
974
975
    /* Set up new indexed lists for stuff this group can contain. */
976
0
    new_grp->children = ncindexnew(0);
977
0
    new_grp->dim = ncindexnew(0);
978
0
    new_grp->att = ncindexnew(0);
979
0
    new_grp->type = ncindexnew(0);
980
0
    new_grp->vars = ncindexnew(0);
981
982
    /* Add object to lists */
983
0
    if (parent)
984
0
        ncindexadd(parent->children, (NC_OBJ *)new_grp);
985
0
    obj_track(h5, (NC_OBJ *)new_grp);
986
987
    /* Set the group pointer, if one was given */
988
0
    if (grp)
989
0
        *grp = new_grp;
990
991
0
    return NC_NOERR;
992
0
}
993
994
/**
995
 * @internal Names for groups, variables, and types must not be the
996
 * same. This function checks that a proposed name is not already in
997
 * use. Normalzation of UTF8 strings should happen before this
998
 * function is called.
999
 *
1000
 * @param grp Pointer to group info struct.
1001
 * @param name Name to check.
1002
 *
1003
 * @return ::NC_NOERR No error.
1004
 * @return ::NC_ENAMEINUSE Name is in use.
1005
 * @author Ed Hartnett, Dennis Heimbigner
1006
 */
1007
int
1008
nc4_check_dup_name(NC_GRP_INFO_T *grp, char *name)
1009
0
{
1010
0
    NC_TYPE_INFO_T *type;
1011
0
    NC_GRP_INFO_T *g;
1012
0
    NC_VAR_INFO_T *var;
1013
1014
    /* Any types of this name? */
1015
0
    type = (NC_TYPE_INFO_T*)ncindexlookup(grp->type,name);
1016
0
    if(type != NULL)
1017
0
        return NC_ENAMEINUSE;
1018
1019
    /* Any child groups of this name? */
1020
0
    g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,name);
1021
0
    if(g != NULL)
1022
0
        return NC_ENAMEINUSE;
1023
1024
    /* Any variables of this name? */
1025
0
    var = (NC_VAR_INFO_T*)ncindexlookup(grp->vars,name);
1026
0
    if(var != NULL)
1027
0
        return NC_ENAMEINUSE;
1028
1029
0
    return NC_NOERR;
1030
0
}
1031
1032
/**
1033
 * @internal Create a type, but do not add to various lists nor
1034
 * increment its ref count
1035
 *
1036
 * @param size Size of type in bytes.
1037
 * @param name Name of type.
1038
 * @param assignedid if >= 0 then override the default type id.
1039
 * @param type Pointer that gets pointer to new type info struct.
1040
 *
1041
 * @return ::NC_NOERR No error.
1042
 * @return ::NC_ENOMEM Out of memory.
1043
 * @author Ed Hartnett, Ward Fisher
1044
 */
1045
int
1046
nc4_type_new(size_t size, const char *name, int assignedid,
1047
             NC_TYPE_INFO_T **type)
1048
0
{
1049
0
    NC_TYPE_INFO_T *new_type;
1050
1051
0
    LOG((4, "%s: size %d name %s assignedid %d", __func__, size, name, assignedid));
1052
1053
    /* Check inputs. */
1054
0
    assert(type);
1055
1056
    /* Allocate memory for the type */
1057
0
    if (!(new_type = calloc(1, sizeof(NC_TYPE_INFO_T))))
1058
0
        return NC_ENOMEM;
1059
0
    new_type->hdr.sort = NCTYP;
1060
0
    new_type->hdr.id = assignedid;
1061
1062
    /* Remember info about this type. */
1063
0
    new_type->size = size;
1064
0
    if (!(new_type->hdr.name = strdup(name))) {
1065
0
        free(new_type);
1066
0
        return NC_ENOMEM;
1067
0
    }
1068
1069
    /* Return a pointer to the new type. */
1070
0
    *type = new_type;
1071
1072
0
    return NC_NOERR;
1073
0
}
1074
1075
/**
1076
 * @internal Add to the type list.
1077
 *
1078
 * @param grp Pointer to group info struct.
1079
 * @param size Size of type in bytes.
1080
 * @param name Name of type.
1081
 * @param type Pointer that gets pointer to new type info
1082
 * struct.
1083
 *
1084
 * @return ::NC_NOERR No error.
1085
 * @return ::NC_ENOMEM Out of memory.
1086
 * @author Ed Hartnett
1087
 */
1088
int
1089
nc4_type_list_add(NC_GRP_INFO_T *grp, size_t size, const char *name,
1090
                  NC_TYPE_INFO_T **type)
1091
0
{
1092
0
    NC_TYPE_INFO_T *new_type;
1093
0
    int retval;
1094
1095
    /* Check inputs. */
1096
0
    assert(grp && name && type);
1097
0
    LOG((4, "%s: size %d name %s", __func__, size, name));
1098
1099
    /* Create the new TYPE_INFO struct. */
1100
0
    if ((retval = nc4_type_new(size, name, grp->nc4_info->next_typeid,
1101
0
                               &new_type)))
1102
0
        return retval;
1103
0
    grp->nc4_info->next_typeid++;
1104
1105
    /* Increment the ref. count on the type */
1106
0
    new_type->rc++;
1107
1108
    /* Add object to lists */
1109
0
    ncindexadd(grp->type, (NC_OBJ *)new_type);
1110
0
    obj_track(grp->nc4_info,(NC_OBJ*)new_type);
1111
1112
    /* Return a pointer to the new type. */
1113
0
    *type = new_type;
1114
1115
0
    return NC_NOERR;
1116
0
}
1117
1118
/**
1119
 * @internal Add to the compound field list.
1120
 *
1121
 * @param parent parent type
1122
 * @param name Name of the field.
1123
 * @param offset Offset in bytes.
1124
 * @param xtype The netCDF type of the field.
1125
 * @param ndims The number of dimensions of the field.
1126
 * @param dim_sizesp An array of dim sizes for the field.
1127
 *
1128
 * @return ::NC_NOERR No error.
1129
 * @author Ed Hartnett, Dennis Heimbigner
1130
 */
1131
int
1132
nc4_field_list_add(NC_TYPE_INFO_T *parent, const char *name,
1133
                   size_t offset, nc_type xtype, int ndims,
1134
                   const int *dim_sizesp)
1135
0
{
1136
0
    NC_FIELD_INFO_T *field;
1137
1138
    /* Name has already been checked and UTF8 normalized. */
1139
0
    if (!name)
1140
0
        return NC_EINVAL;
1141
1142
    /* Allocate storage for this field information. */
1143
0
    if (!(field = calloc(1, sizeof(NC_FIELD_INFO_T))))
1144
0
        return NC_ENOMEM;
1145
0
    field->hdr.sort = NCFLD;
1146
1147
    /* Store the information about this field. */
1148
0
    if (!(field->hdr.name = strdup(name)))
1149
0
    {
1150
0
        free(field);
1151
0
        return NC_ENOMEM;
1152
0
    }
1153
0
    field->nc_typeid = xtype;
1154
0
    field->offset = offset;
1155
0
    field->ndims = ndims;
1156
0
    if (ndims)
1157
0
    {
1158
0
        int i;
1159
0
        if (!(field->dim_size = malloc(ndims * sizeof(int))))
1160
0
        {
1161
0
            free(field->hdr.name);
1162
0
            free(field);
1163
0
            return NC_ENOMEM;
1164
0
        }
1165
0
        for (i = 0; i < ndims; i++)
1166
0
            field->dim_size[i] = dim_sizesp[i];
1167
0
    }
1168
1169
    /* Add object to lists */
1170
0
    field->hdr.id = nclistlength(parent->u.c.field);
1171
0
    nclistpush(parent->u.c.field,field);
1172
1173
0
    return NC_NOERR;
1174
0
}
1175
1176
/**
1177
 * @internal Add a member to an enum type.
1178
 *
1179
 * @param parent Containing NC_TYPE_INFO_T object
1180
 * @param size Size in bytes of new member.
1181
 * @param name Name of the member.
1182
 * @param value Value to associate with member.
1183
 *
1184
 * @return ::NC_NOERR No error.
1185
 * @return ::NC_ENOMEM Out of memory.
1186
 * @author Ed Hartnett
1187
 */
1188
int
1189
nc4_enum_member_add(NC_TYPE_INFO_T *parent, size_t size,
1190
                    const char *name, const void *value)
1191
0
{
1192
0
    NC_ENUM_MEMBER_INFO_T *member;
1193
1194
    /* Name has already been checked. */
1195
0
    assert(name && size > 0 && value);
1196
0
    LOG((4, "%s: size %d name %s", __func__, size, name));
1197
1198
    /* Allocate storage for this field information. */
1199
0
    if (!(member = calloc(1, sizeof(NC_ENUM_MEMBER_INFO_T))))
1200
0
        return NC_ENOMEM;
1201
0
    if (!(member->value = malloc(size))) {
1202
0
        free(member);
1203
0
        return NC_ENOMEM;
1204
0
    }
1205
0
    if (!(member->name = strdup(name))) {
1206
0
        free(member->value);
1207
0
        free(member);
1208
0
        return NC_ENOMEM;
1209
0
    }
1210
1211
    /* Store the value for this member. */
1212
0
    memcpy(member->value, value, size);
1213
1214
    /* Add object to list */
1215
0
    nclistpush(parent->u.e.enum_member,member);
1216
1217
0
    return NC_NOERR;
1218
0
}
1219
1220
/**
1221
 * @internal Free up a field
1222
 *
1223
 * @param field Pointer to field info of field to delete.
1224
 *
1225
 * @author Ed Hartnett
1226
 */
1227
static void
1228
field_free(NC_FIELD_INFO_T *field)
1229
0
{
1230
    /* Free some stuff. */
1231
0
    if (field->hdr.name)
1232
0
        free(field->hdr.name);
1233
0
    if (field->dim_size)
1234
0
        free(field->dim_size);
1235
1236
    /* Nc_Free the memory. */
1237
0
    free(field);
1238
0
}
1239
1240
/**
1241
 * @internal Free allocated space for type information.
1242
 *
1243
 * @param type Pointer to type info struct.
1244
 *
1245
 * @return ::NC_NOERR No error.
1246
 * @return ::NC_EHDFERR HDF5 error.
1247
 * @author Ed Hartnett, Dennis Heimbigner
1248
 */
1249
int
1250
nc4_type_free(NC_TYPE_INFO_T *type)
1251
0
{
1252
0
    int i;
1253
1254
0
    assert(type && type->rc && type->hdr.name);
1255
1256
    /* Decrement the ref. count on the type */
1257
0
    type->rc--;
1258
1259
    /* Release the type, if the ref. count drops to zero */
1260
0
    if (type->rc == 0)
1261
0
    {
1262
0
        LOG((4, "%s: deleting type %s", __func__, type->hdr.name));
1263
1264
        /* Free the name. */
1265
0
        free(type->hdr.name);
1266
1267
        /* Enums and compound types have lists of fields to clean up. */
1268
0
        switch (type->nc_type_class)
1269
0
        {
1270
0
        case NC_COMPOUND:
1271
0
        {
1272
0
            NC_FIELD_INFO_T *field;
1273
1274
            /* Delete all the fields in this type (there will be some if its a
1275
             * compound). */
1276
0
            for(i=0;i<nclistlength(type->u.c.field);i++) {
1277
0
                field = nclistget(type->u.c.field,i);
1278
0
                field_free(field);
1279
0
            }
1280
0
            nclistfree(type->u.c.field);
1281
0
        }
1282
0
        break;
1283
1284
0
        case NC_ENUM:
1285
0
        {
1286
0
            NC_ENUM_MEMBER_INFO_T *enum_member;
1287
1288
            /* Delete all the enum_members, if any. */
1289
0
            for(i=0;i<nclistlength(type->u.e.enum_member);i++) {
1290
0
                enum_member = nclistget(type->u.e.enum_member,i);
1291
0
                free(enum_member->value);
1292
0
                free(enum_member->name);
1293
0
                free(enum_member);
1294
0
            }
1295
0
            nclistfree(type->u.e.enum_member);
1296
0
        }
1297
0
        break;
1298
1299
0
        default:
1300
0
            break;
1301
0
        }
1302
1303
        /* Release the memory. */
1304
0
        free(type);
1305
0
    }
1306
1307
0
    return NC_NOERR;
1308
0
}
1309
1310
/**
1311
 * @internal Free memory of an attribute object
1312
 *
1313
 * @param att Pointer to attribute info struct.
1314
 *
1315
 * @return ::NC_NOERR No error.
1316
 * @author Ed Hartnett
1317
 */
1318
int
1319
nc4_att_free(NC_ATT_INFO_T *att)
1320
0
{
1321
0
    int stat = NC_NOERR;
1322
1323
0
    assert(att);
1324
0
    LOG((3, "%s: name %s ", __func__, att->hdr.name));
1325
1326
    /* Free the name. */
1327
0
    if (att->hdr.name)
1328
0
        free(att->hdr.name);
1329
1330
0
    if (att->data) {
1331
0
  NC_OBJ* parent;
1332
0
  NC_FILE_INFO_T* h5 = NULL;
1333
1334
  /* Locate relevant objects */
1335
0
  parent = att->container;
1336
0
  if(parent->sort == NCVAR) parent = (NC_OBJ*)(((NC_VAR_INFO_T*)parent)->container);
1337
0
  assert(parent->sort == NCGRP);
1338
0
  h5 = ((NC_GRP_INFO_T*)parent)->nc4_info;
1339
  /* Reclaim the attribute data */
1340
0
  if((stat = nc_reclaim_data(h5->controller->ext_ncid,att->nc_typeid,att->data,att->len))) goto done;
1341
0
  free(att->data); /* reclaim top level */
1342
0
  att->data = NULL;
1343
0
    }
1344
1345
0
done:
1346
0
    free(att);
1347
0
    return stat;
1348
0
}
1349
1350
/**
1351
 * @internal Delete a var, and free the memory. All HDF5 objects for
1352
 * the var must be closed before this is called.
1353
 *
1354
 * @param var Pointer to the var info struct of var to delete.
1355
 *
1356
 * @return ::NC_NOERR No error.
1357
 * @author Ed Hartnett, Dennis Heimbigner
1358
 */
1359
static int
1360
var_free(NC_VAR_INFO_T *var)
1361
0
{
1362
0
    int i;
1363
0
    int retval;
1364
1365
0
    assert(var);
1366
0
    LOG((4, "%s: deleting var %s", __func__, var->hdr.name));
1367
1368
    /* First delete all the attributes attached to this var. */
1369
0
    for (i = 0; i < ncindexsize(var->att); i++)
1370
0
        if ((retval = nc4_att_free((NC_ATT_INFO_T *)ncindexith(var->att, i))))
1371
0
            return retval;
1372
0
    ncindexfree(var->att);
1373
1374
    /* Free some things that may be allocated. */
1375
0
    if (var->chunksizes)
1376
0
        free(var->chunksizes);
1377
1378
0
    if (var->alt_name)
1379
0
        free(var->alt_name);
1380
1381
0
    if (var->dimids)
1382
0
        free(var->dimids);
1383
1384
0
    if (var->dim)
1385
0
        free(var->dim);
1386
1387
    /* Delete any fill value allocation. */
1388
0
    if (var->fill_value) {
1389
0
  int ncid = var->container->nc4_info->controller->ext_ncid;
1390
0
  int tid = var->type_info->hdr.id;
1391
0
        if((retval = nc_reclaim_data_all(ncid, tid, var->fill_value, 1))) return retval;
1392
0
  var->fill_value = NULL;
1393
0
    }
1394
1395
    /* Release type information */
1396
0
    if (var->type_info)
1397
0
        if ((retval = nc4_type_free(var->type_info)))
1398
0
            return retval;
1399
1400
    /* Do this last because debugging may need it */
1401
0
    if (var->hdr.name)
1402
0
        free(var->hdr.name);
1403
1404
    /* Delete the var. */
1405
0
    free(var);
1406
1407
0
    return NC_NOERR;
1408
0
}
1409
1410
/**
1411
 * @internal  Delete a var, and free the memory.
1412
 *
1413
 * @param grp Pointer to the strct for the containing group.
1414
 * @param var Pointer to the var info struct of var to delete.
1415
 *
1416
 * @return ::NC_NOERR No error.
1417
 * @author Ed Hartnett, Dennis Heimbigner
1418
 */
1419
int
1420
nc4_var_list_del(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var)
1421
0
{
1422
0
    int i;
1423
1424
0
    assert(var && grp);
1425
1426
    /* Remove from lists */
1427
0
    i = ncindexfind(grp->vars, (NC_OBJ *)var);
1428
0
    if (i >= 0)
1429
0
        ncindexidel(grp->vars, i);
1430
1431
0
    return var_free(var);
1432
0
}
1433
1434
/**
1435
 * @internal Free a dim
1436
 *
1437
 * @param dim Pointer to dim info struct of type to delete.
1438
 *
1439
 * @return ::NC_NOERR No error.
1440
 * @author Ed Hartnett, Ward Fisher
1441
 */
1442
static int
1443
dim_free(NC_DIM_INFO_T *dim)
1444
0
{
1445
0
    assert(dim);
1446
0
    LOG((4, "%s: deleting dim %s", __func__, dim->hdr.name));
1447
1448
    /* Free memory allocated for names. */
1449
0
    if (dim->hdr.name)
1450
0
        free(dim->hdr.name);
1451
1452
0
    free(dim);
1453
0
    return NC_NOERR;
1454
0
}
1455
1456
/**
1457
 * @internal Free a dim and unlist it
1458
 *
1459
 * @param grp Pointer to dim's containing group
1460
 * @param dim Pointer to dim info struct of type to delete.
1461
 *
1462
 * @return ::NC_NOERR No error.
1463
 * @author Dennis Heimbigner
1464
 */
1465
int
1466
nc4_dim_list_del(NC_GRP_INFO_T *grp, NC_DIM_INFO_T *dim)
1467
0
{
1468
0
    if (grp && dim)
1469
0
    {
1470
0
        int pos = ncindexfind(grp->dim, (NC_OBJ *)dim);
1471
0
        if(pos >= 0)
1472
0
            ncindexidel(grp->dim, pos);
1473
0
    }
1474
1475
0
    return dim_free(dim);
1476
0
}
1477
1478
/**
1479
 * @internal Recursively delete the data for a group (and everything
1480
 * it contains) in our internal metadata store.
1481
 *
1482
 * @param grp Pointer to group info struct.
1483
 *
1484
 * @return ::NC_NOERR No error.
1485
 * @author Ed Hartnett, Dennis Heimbigner
1486
 */
1487
int
1488
nc4_rec_grp_del(NC_GRP_INFO_T *grp)
1489
0
{
1490
0
    int i;
1491
0
    int retval;
1492
1493
0
    assert(grp);
1494
0
    LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));
1495
1496
    /* Recursively call this function for each child, if any, stopping
1497
     * if there is an error. */
1498
0
    for (i = 0; i < ncindexsize(grp->children); i++)
1499
0
        if ((retval = nc4_rec_grp_del((NC_GRP_INFO_T *)ncindexith(grp->children,
1500
0
                                                                  i))))
1501
0
            return retval;
1502
0
    ncindexfree(grp->children);
1503
1504
    /* Free attributes */
1505
0
    for (i = 0; i < ncindexsize(grp->att); i++)
1506
0
        if ((retval = nc4_att_free((NC_ATT_INFO_T *)ncindexith(grp->att, i))))
1507
0
            return retval;
1508
0
    ncindexfree(grp->att);
1509
1510
    /* Delete all vars. */
1511
0
    for (i = 0; i < ncindexsize(grp->vars); i++) {
1512
0
  NC_VAR_INFO_T* v = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
1513
0
        if ((retval = var_free(v)))
1514
0
            return retval;
1515
0
    }
1516
0
    ncindexfree(grp->vars);
1517
1518
    /* Delete all dims, and free the list of dims. */
1519
0
    for (i = 0; i < ncindexsize(grp->dim); i++)
1520
0
        if ((retval = dim_free((NC_DIM_INFO_T *)ncindexith(grp->dim, i))))
1521
0
            return retval;
1522
0
    ncindexfree(grp->dim);
1523
1524
    /* Delete all types. */
1525
0
    for (i = 0; i < ncindexsize(grp->type); i++)
1526
0
        if ((retval = nc4_type_free((NC_TYPE_INFO_T *)ncindexith(grp->type, i))))
1527
0
            return retval;
1528
0
    ncindexfree(grp->type);
1529
1530
    /* Free the name. */
1531
0
    free(grp->hdr.name);
1532
1533
    /* Free up this group */
1534
0
    free(grp);
1535
1536
0
    return NC_NOERR;
1537
0
}
1538
1539
/**
1540
 * @internal Recursively delete the data for a group (and everything
1541
 * it contains) in our internal metadata store.
1542
 *
1543
 * @param grp Pointer to group info struct.
1544
 *
1545
 * @return ::NC_NOERR No error.
1546
 * @author Ed Hartnett, Dennis Heimbigner
1547
 */
1548
int
1549
nc4_rec_grp_del_att_data(NC_GRP_INFO_T *grp)
1550
0
{
1551
0
    int i;
1552
0
    int retval;
1553
1554
0
    assert(grp);
1555
0
    LOG((3, "%s: grp->name %s", __func__, grp->hdr.name));
1556
1557
    /* Recursively call this function for each child, if any, stopping
1558
     * if there is an error. */
1559
0
    for (i = 0; i < ncindexsize(grp->children); i++)
1560
0
        if ((retval = nc4_rec_grp_del_att_data((NC_GRP_INFO_T *)ncindexith(grp->children, i))))
1561
0
            return retval;
1562
1563
    /* Free attribute data in this group */
1564
0
    for (i = 0; i < ncindexsize(grp->att); i++) {
1565
0
  NC_ATT_INFO_T * att = (NC_ATT_INFO_T*)ncindexith(grp->att, i);
1566
0
  if((retval = nc_reclaim_data_all(grp->nc4_info->controller->ext_ncid,att->nc_typeid,att->data,att->len)))
1567
0
      return retval;
1568
0
  att->data = NULL;
1569
0
  att->len = 0;
1570
0
  att->dirty = 0;
1571
0
    }
1572
1573
    /* Delete att data from all contained vars in this group */
1574
0
    for (i = 0; i < ncindexsize(grp->vars); i++) {
1575
0
  int j;
1576
0
  NC_VAR_INFO_T* v = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
1577
0
  for(j=0;j<ncindexsize(v->att);j++) {
1578
0
      NC_ATT_INFO_T* att = (NC_ATT_INFO_T*)ncindexith(v->att, j);
1579
0
        if((retval = nc_reclaim_data_all(grp->nc4_info->controller->ext_ncid,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
0
    }
1586
1587
0
    return NC_NOERR;
1588
0
}
1589
1590
/**
1591
 * @internal Remove a NC_ATT_INFO_T from an index.
1592
 * This will nc_free the memory too.
1593
 *
1594
 * @param list Pointer to pointer of list.
1595
 * @param att Pointer to attribute info struct.
1596
 *
1597
 * @return ::NC_NOERR No error.
1598
 * @author Dennis Heimbigner
1599
 */
1600
int
1601
nc4_att_list_del(NCindex *list, NC_ATT_INFO_T *att)
1602
0
{
1603
0
    assert(att && list);
1604
0
    ncindexidel(list, ((NC_OBJ *)att)->id);
1605
0
    return nc4_att_free(att);
1606
0
}
1607
1608
/**
1609
 * @internal Free all resources and memory associated with a
1610
 * NC_FILE_INFO_T. This is the same as nc4_nc4f_list_del(), except it
1611
 * takes ncid. This function allows external dispatch layers, like
1612
 * PIO, to manipulate the file list without needing to know about
1613
 * internal netcdf structures.
1614
 *
1615
 * @param ncid The ncid of the file to release.
1616
 *
1617
 * @return ::NC_NOERR No error.
1618
 * @return ::NC_EBADID Bad ncid.
1619
 * @author Ed Hartnett
1620
 */
1621
int
1622
nc4_file_list_del(int ncid)
1623
0
{
1624
0
    NC_FILE_INFO_T *h5;
1625
0
    int retval;
1626
1627
    /* Find our metadata for this file. */
1628
0
    if ((retval = nc4_find_grp_h5(ncid, NULL, &h5)))
1629
0
        return retval;
1630
0
    assert(h5);
1631
1632
    /* Delete the file resources. */
1633
0
    if ((retval = nc4_nc4f_list_del(h5)))
1634
0
        return retval;
1635
1636
0
    return NC_NOERR;
1637
0
}
1638
1639
/**
1640
 * @internal Free all resources and memory associated with a
1641
 * NC_FILE_INFO_T.
1642
 *
1643
 * @param h5 Pointer to NC_FILE_INFO_T to be freed.
1644
 *
1645
 * @return ::NC_NOERR No error.
1646
 * @author Ed Hartnett
1647
 */
1648
int
1649
nc4_nc4f_list_del(NC_FILE_INFO_T *h5)
1650
0
{
1651
0
    int retval;
1652
1653
0
    assert(h5);
1654
1655
    /* Order is important here. We must delete the attribute contents
1656
       before deleteing any metadata because nc_reclaim_data depends
1657
       on the existence of the type info.
1658
    */
1659
1660
    /* Delete all the attribute data contents in each group and variable. */
1661
0
    if ((retval = nc4_rec_grp_del_att_data(h5->root_grp)))
1662
0
        return retval;
1663
1664
    /* Delete all the list contents for vars, dims, and atts, in each
1665
     * group. */
1666
0
    if ((retval = nc4_rec_grp_del(h5->root_grp)))
1667
0
        return retval;
1668
1669
    /* Cleanup these (extra) lists of all dims, groups, and types. */
1670
0
    nclistfree(h5->alldims);
1671
0
    nclistfree(h5->allgroups);
1672
0
    nclistfree(h5->alltypes);
1673
1674
    /* Free the NC_FILE_INFO_T struct. */
1675
0
    nullfree(h5->hdr.name);
1676
0
    free(h5);
1677
1678
0
    return NC_NOERR;
1679
0
}
1680
1681
/**
1682
 * @internal Normalize a UTF8 name. Put the result in norm_name, which
1683
 * can be NC_MAX_NAME + 1 in size. This function makes sure the free()
1684
 * gets called on the return from utf8proc_NFC, and also ensures that
1685
 * the name is not too long.
1686
 *
1687
 * @param name Name to normalize.
1688
 * @param norm_name The normalized name.
1689
 *
1690
 * @return ::NC_NOERR No error.
1691
 * @return ::NC_EMAXNAME Name too long.
1692
 * @author Dennis Heimbigner
1693
 */
1694
int
1695
nc4_normalize_name(const char *name, char *norm_name)
1696
0
{
1697
0
    char *temp_name;
1698
0
    int stat = nc_utf8_normalize((const unsigned char *)name,(unsigned char **)&temp_name);
1699
0
    if(stat != NC_NOERR)
1700
0
        return stat;
1701
0
    if (strlen(temp_name) > NC_MAX_NAME)
1702
0
    {
1703
0
        free(temp_name);
1704
0
        return NC_EMAXNAME;
1705
0
    }
1706
0
    strcpy(norm_name, temp_name);
1707
0
    free(temp_name);
1708
0
    return NC_NOERR;
1709
0
}
1710
1711
#ifdef ENABLE_SET_LOG_LEVEL
1712
1713
/**
1714
 * Initialize parallel I/O logging. For parallel I/O builds, open log
1715
 * file, if not opened yet, or increment ref count if already open.
1716
 *
1717
 * @author Ed Hartnett
1718
 */
1719
int
1720
nc4_init_logging(void)
1721
0
{
1722
0
    int ret = NC_NOERR;
1723
1724
#if NC_HAS_LOGGING
1725
#if NC_HAS_PARALLEL4
1726
    if (!LOG_FILE && nc_log_level >= 0)
1727
    {
1728
        char log_filename[NC_MAX_NAME];
1729
        int my_rank = 0;
1730
        int mpierr;
1731
        int mpi_initialized;
1732
1733
        /* If MPI has been initialized find the rank. */
1734
        if ((mpierr = MPI_Initialized(&mpi_initialized)))
1735
            return NC_EMPI;
1736
        if (mpi_initialized)
1737
        {
1738
            if ((mpierr = MPI_Comm_rank(MPI_COMM_WORLD, &my_rank)))
1739
                return NC_EMPI;
1740
        }
1741
1742
        /* Create a filename with the rank in it. */
1743
        sprintf(log_filename, "nc4_log_%d.log", my_rank);
1744
1745
        /* Open a file for this rank to log messages. */
1746
        if (!(LOG_FILE = fopen(log_filename, "w")))
1747
            return NC_EINTERNAL;
1748
    }
1749
#endif /* NC_HAS_PARALLEL4 */
1750
#endif /* NC_HAS_LOGGING */
1751
1752
0
    return ret;
1753
0
}
1754
1755
/**
1756
 * Finalize logging - close parallel I/O log files, if open. This does
1757
 * nothing if logging is not enabled.
1758
 *
1759
 * @author Ed Hartnett
1760
 */
1761
void
1762
nc4_finalize_logging(void)
1763
0
{
1764
#if NC_HAS_LOGGING
1765
#if NC_HAS_PARALLEL4
1766
    if (LOG_FILE)
1767
    {
1768
        fclose(LOG_FILE);
1769
        LOG_FILE = NULL;
1770
    }
1771
#endif /* NC_HAS_PARALLEL4 */
1772
#endif /* NC_HAS_LOGGING */
1773
0
}
1774
1775
/**
1776
 * Use this to set the global log level. 
1777
 * 
1778
 * Set it to NC_TURN_OFF_LOGGING (-1) to turn off all logging. Set it
1779
 * to 0 to show only errors, and to higher numbers to show more and
1780
 * more logging details. If logging is not enabled when building
1781
 * netCDF, this function will do nothing.
1782
 *
1783
 * @param new_level The new logging level.
1784
 *
1785
 * @return ::NC_NOERR No error.
1786
 * @author Ed Hartnett
1787
 */
1788
int
1789
nc_set_log_level(int new_level)
1790
0
{
1791
#if NC_HAS_LOGGING
1792
    /* Remember the new level. */
1793
    nc_log_level = new_level;
1794
1795
#if NC_HAS_PARALLEL4
1796
    /* For parallel I/O builds, call the log init/finalize functions
1797
     * as needed, to open and close the log files. */
1798
    if (new_level >= 0)
1799
    {
1800
        if (!LOG_FILE)
1801
            nc4_init_logging();
1802
    }
1803
    else
1804
        nc4_finalize_logging();
1805
#endif /* NC_HAS_PARALLEL4 */
1806
    
1807
    LOG((1, "log_level changed to %d", nc_log_level));
1808
#endif /*NC_HAS_LOGGING */
1809
    
1810
0
    return NC_NOERR;
1811
0
}
1812
#endif /* ENABLE_SET_LOG_LEVEL */
1813
1814
#if NC_HAS_LOGGING
1815
#define MAX_NESTS 10
1816
/**
1817
 * @internal Recursively print the metadata of a group.
1818
 *
1819
 * @param grp Pointer to group info struct.
1820
 * @param tab_count Number of tabs.
1821
 *
1822
 * @return ::NC_NOERR No error.
1823
 * @author Ed Hartnett, Dennis Heimbigner
1824
 */
1825
static int
1826
rec_print_metadata(NC_GRP_INFO_T *grp, int tab_count)
1827
{
1828
    NC_ATT_INFO_T *att;
1829
    NC_VAR_INFO_T *var;
1830
    NC_DIM_INFO_T *dim;
1831
    NC_TYPE_INFO_T *type;
1832
    NC_FIELD_INFO_T *field;
1833
    char tabs[MAX_NESTS+1] = "";
1834
    char temp_string[10];
1835
    int t, retval, d, i;
1836
1837
    /* Come up with a number of tabs relative to the group. */
1838
    for (t = 0; t < tab_count && t < MAX_NESTS; t++)
1839
        tabs[t] = '\t';
1840
    tabs[t] = '\0';
1841
1842
    LOG((2, "%s GROUP - %s nc_grpid: %d nvars: %d natts: %d",
1843
         tabs, grp->hdr.name, grp->hdr.id, ncindexsize(grp->vars), ncindexsize(grp->att)));
1844
1845
    for (i = 0; i < ncindexsize(grp->att); i++)
1846
    {
1847
        att = (NC_ATT_INFO_T *)ncindexith(grp->att, i);
1848
        assert(att);
1849
        LOG((2, "%s GROUP ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
1850
             tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
1851
    }
1852
1853
    for (i = 0; i < ncindexsize(grp->dim); i++)
1854
    {
1855
        dim = (NC_DIM_INFO_T *)ncindexith(grp->dim, i);
1856
        assert(dim);
1857
        LOG((2, "%s DIMENSION - dimid: %d name: %s len: %d unlimited: %d",
1858
             tabs, dim->hdr.id, dim->hdr.name, dim->len, dim->unlimited));
1859
    }
1860
1861
    for (i = 0; i < ncindexsize(grp->vars); i++)
1862
    {
1863
        int j;
1864
        char storage_str[NC_MAX_NAME] = "";
1865
        char *dims_string = NULL;
1866
1867
        var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
1868
        assert(var);
1869
        if (var->ndims > 0)
1870
        {
1871
            if (!(dims_string = malloc(sizeof(char) * var->ndims * 4)))
1872
                return NC_ENOMEM;
1873
            strcpy(dims_string, "");
1874
            for (d = 0; d < var->ndims; d++)
1875
            {
1876
                sprintf(temp_string, " %d", var->dimids[d]);
1877
                strcat(dims_string, temp_string);
1878
            }
1879
        }
1880
        if (!var->meta_read)
1881
            strcat(storage_str, "unknown");
1882
        else if (var->storage == NC_CONTIGUOUS)
1883
            strcat(storage_str, "contiguous");
1884
        else if (var->storage == NC_COMPACT)
1885
            strcat(storage_str, "compact");
1886
        else if (var->storage == NC_CHUNKED)
1887
            strcat(storage_str, "chunked");
1888
        else if (var->storage == NC_VIRTUAL)
1889
            strcat(storage_str, "virtual");
1890
        else
1891
            strcat(storage_str, "unknown");
1892
        LOG((2, "%s VARIABLE - varid: %d name: %s ndims: %d "
1893
             "dimids:%s storage: %s", tabs, var->hdr.id, var->hdr.name,
1894
             var->ndims,
1895
             (dims_string ? dims_string : " -"), storage_str));
1896
        for (j = 0; j < ncindexsize(var->att); j++)
1897
        {
1898
            att = (NC_ATT_INFO_T *)ncindexith(var->att, j);
1899
            assert(att);
1900
            LOG((2, "%s VAR ATTRIBUTE - attnum: %d name: %s type: %d len: %d",
1901
                 tabs, att->hdr.id, att->hdr.name, att->nc_typeid, att->len));
1902
        }
1903
        if (dims_string)
1904
            free(dims_string);
1905
    }
1906
1907
    for (i = 0; i < ncindexsize(grp->type); i++)
1908
    {
1909
        type = (NC_TYPE_INFO_T*)ncindexith(grp->type, i);
1910
        assert(type);
1911
        LOG((2, "%s TYPE - nc_typeid: %d size: %d committed: %d name: %s",
1912
             tabs, type->hdr.id, type->size, (int)type->committed, type->hdr.name));
1913
        /* Is this a compound type? */
1914
        if (type->nc_type_class == NC_COMPOUND)
1915
        {
1916
            int j;
1917
            LOG((3, "compound type"));
1918
            for (j = 0; j < nclistlength(type->u.c.field); j++)
1919
            {
1920
                field = (NC_FIELD_INFO_T *)nclistget(type->u.c.field, j);
1921
                LOG((4, "field %s offset %d nctype %d ndims %d", field->hdr.name,
1922
                     field->offset, field->nc_typeid, field->ndims));
1923
            }
1924
        }
1925
        else if (type->nc_type_class == NC_VLEN)
1926
        {
1927
            LOG((3, "VLEN type"));
1928
            LOG((4, "base_nc_type: %d", type->u.v.base_nc_typeid));
1929
        }
1930
        else if (type->nc_type_class == NC_OPAQUE)
1931
            LOG((3, "Opaque type"));
1932
        else if (type->nc_type_class == NC_ENUM)
1933
        {
1934
            LOG((3, "Enum type"));
1935
            LOG((4, "base_nc_type: %d", type->u.e.base_nc_typeid));
1936
        }
1937
        else
1938
        {
1939
            LOG((0, "Unknown class: %d", type->nc_type_class));
1940
            return NC_EBADTYPE;
1941
        }
1942
    }
1943
1944
    /* Call self for each child of this group. */
1945
    for (i = 0; i < ncindexsize(grp->children); i++)
1946
        if ((retval = rec_print_metadata((NC_GRP_INFO_T *)ncindexith(grp->children, i),
1947
                                         tab_count + 1)))
1948
            return retval;
1949
1950
    return NC_NOERR;
1951
}
1952
1953
/**
1954
 * @internal Print out the internal metadata for a file. This is
1955
 * useful to check that netCDF is working! Nonetheless, this function
1956
 * will print nothing if logging is not set to at least two.
1957
 *
1958
 * @param Pointer to the file info struct.
1959
 *
1960
 * @return ::NC_NOERR No error.
1961
 * @author Ed Hartnett
1962
 */
1963
int
1964
log_metadata_nc(NC_FILE_INFO_T *h5)
1965
{
1966
    LOG((2, "*** NetCDF-4 Internal Metadata: int_ncid 0x%x ext_ncid 0x%x",
1967
         h5->root_grp->nc4_info->controller->int_ncid,
1968
         h5->root_grp->nc4_info->controller->ext_ncid));
1969
    if (!h5)
1970
    {
1971
        LOG((2, "This is a netCDF-3 file."));
1972
        return NC_NOERR;
1973
    }
1974
    LOG((2, "FILE - path: %s cmode: 0x%x parallel: %d redef: %d "
1975
         "fill_mode: %d no_write: %d next_nc_grpid: %d", h5->root_grp->nc4_info->controller->path,
1976
         h5->cmode, (int)h5->parallel, (int)h5->redef, h5->fill_mode, (int)h5->no_write,
1977
         h5->next_nc_grpid));
1978
    if(nc_log_level >= 2)
1979
        return rec_print_metadata(h5->root_grp, 0);
1980
    return NC_NOERR;
1981
}
1982
1983
#endif /*NC_HAS_LOGGING */
1984
1985
/**
1986
 * @internal Show the in-memory metadata for a netcdf file. This
1987
 * function does nothing unless netCDF was built with
1988
 * the configure option --enable-logging.
1989
 *
1990
 * @param ncid File and group ID.
1991
 *
1992
 * @return ::NC_NOERR No error.
1993
 * @return ::NC_EBADID Bad ncid.
1994
 * @author Ed Hartnett
1995
 */
1996
int
1997
NC4_show_metadata(int ncid)
1998
0
{
1999
0
    int retval = NC_NOERR;
2000
#if NC_HAS_LOGGING
2001
    NC_FILE_INFO_T *h5;
2002
    int old_log_level = nc_log_level;
2003
2004
    /* Find file metadata. */
2005
    if ((retval = nc4_find_grp_h5(ncid, NULL, &h5)))
2006
        return retval;
2007
2008
    /* Log level must be 2 to see metadata. */
2009
    nc_log_level = 2;
2010
    retval = log_metadata_nc(h5);
2011
    nc_log_level = old_log_level;
2012
#endif /*NC_HAS_LOGGING*/
2013
0
    return retval;
2014
0
}
2015
2016
/**
2017
 * @internal Define a binary searcher for reserved attributes
2018
 * @param name for which to search
2019
 * @return pointer to the matching NC_reservedatt structure.
2020
 * @return NULL if not found.
2021
 * @author Dennis Heimbigner
2022
 */
2023
const NC_reservedatt*
2024
NC_findreserved(const char* name)
2025
0
{
2026
0
    int n = NRESERVED;
2027
0
    int L = 0;
2028
0
    int R = (n - 1);
2029
2030
0
    for(;;) {
2031
0
        if(L > R) break;
2032
0
        int m = (L + R) / 2;
2033
0
        const NC_reservedatt* p = &NC_reserved[m];
2034
0
        int cmp = strcmp(p->name,name);
2035
0
  if(cmp == 0) return p;
2036
0
        if(cmp < 0)
2037
0
            L = (m + 1);
2038
0
        else /*cmp > 0*/
2039
0
            R = (m - 1);
2040
0
    }
2041
0
    return NULL;
2042
0
}
2043
2044
static int
2045
NC4_move_in_NCList(NC* nc, int new_id)
2046
0
{
2047
0
    int stat = move_in_NCList(nc,new_id);
2048
0
    if(stat == NC_NOERR) {
2049
        /* Synchronize header */
2050
0
        if(nc->dispatchdata)
2051
0
      ((NC_OBJ*)nc->dispatchdata)->id = nc->ext_ncid;
2052
0
    }
2053
0
    return stat;
2054
0
}
2055
2056
/**************************************************/
2057
/* NCglobal state management */
2058
2059
static NCglobalstate* nc_globalstate = NULL;
2060
2061
static int
2062
NC_createglobalstate(void)
2063
1
{
2064
1
    int stat = NC_NOERR;
2065
1
    const char* tmp = NULL;
2066
    
2067
1
    if(nc_globalstate == NULL) {
2068
1
        nc_globalstate = calloc(1,sizeof(NCglobalstate));
2069
1
    }
2070
    /* Initialize struct pointers */
2071
1
    if((nc_globalstate->rcinfo = calloc(1,sizeof(struct NCRCinfo)))==NULL)
2072
0
            {stat = NC_ENOMEM; goto done;}
2073
1
    if((nc_globalstate->rcinfo->entries = nclistnew())==NULL)
2074
0
            {stat = NC_ENOMEM; goto done;}
2075
1
    if((nc_globalstate->rcinfo->s3profiles = nclistnew())==NULL)
2076
0
            {stat = NC_ENOMEM; goto done;}
2077
2078
    /* Get environment variables */
2079
1
    if(getenv(NCRCENVIGNORE) != NULL)
2080
0
        nc_globalstate->rcinfo->ignore = 1;
2081
1
    tmp = getenv(NCRCENVRC);
2082
1
    if(tmp != NULL && strlen(tmp) > 0)
2083
0
        nc_globalstate->rcinfo->rcfile = strdup(tmp);
2084
    /* Initialize chunk cache defaults */
2085
1
    nc_globalstate->chunkcache.size = CHUNK_CACHE_SIZE;            /**< Default chunk cache size. */
2086
1
    nc_globalstate->chunkcache.nelems = CHUNK_CACHE_NELEMS;        /**< Default chunk cache number of elements. */
2087
1
    nc_globalstate->chunkcache.preemption = CHUNK_CACHE_PREEMPTION; /**< Default chunk cache preemption. */
2088
    
2089
1
done:
2090
1
    return stat;
2091
1
}
2092
2093
/* Get global state */
2094
NCglobalstate*
2095
NC_getglobalstate(void)
2096
7
{
2097
7
    if(nc_globalstate == NULL)
2098
1
        NC_createglobalstate();
2099
7
    return nc_globalstate;
2100
7
}
2101
2102
void
2103
NC_freeglobalstate(void)
2104
1
{
2105
1
    if(nc_globalstate != NULL) {
2106
1
        nullfree(nc_globalstate->tempdir);
2107
1
        nullfree(nc_globalstate->home);
2108
1
        nullfree(nc_globalstate->cwd);
2109
1
        NC_rcclear(nc_globalstate->rcinfo);
2110
1
  free(nc_globalstate->rcinfo);
2111
1
  free(nc_globalstate);
2112
1
  nc_globalstate = NULL;
2113
1
    }
2114
1
}
2115
/**************************************************/
2116
/* Specific property functions */
2117
2118
/**
2119
Provide a function to store global data alignment
2120
information.
2121
Repeated calls to nc_set_alignment will overwrite any existing values.
2122
2123
If defined, then for every file created or opened after the call to
2124
nc_set_alignment, and for every new variable added to the file, the
2125
most recently set threshold and alignment values will be applied
2126
to that variable.
2127
2128
The nc_set_alignment function causes new data written to a
2129
netCDF-4 file to be aligned on disk to a specified block
2130
size. To be effective, alignment should be the system disk block
2131
size, or a multiple of it. This setting is effective with MPI
2132
I/O and other parallel systems.
2133
2134
This is a trade-off of write speed versus file size. Alignment
2135
leaves holes between file objects. The default of no alignment
2136
writes file objects contiguously, without holes. Alignment has
2137
no impact on file readability.
2138
2139
Alignment settings apply only indirectly, through the file open
2140
functions. Call nc_set_alignment first, then nc_create or
2141
nc_open for one or more files. Current alignment settings are
2142
locked in when each file is opened, then forgotten when the same
2143
file is closed. For illustration, it is possible to write
2144
different files at the same time with different alignments, by
2145
interleaving nc_set_alignment and nc_open calls.
2146
2147
Alignment applies to all newly written low-level file objects at
2148
or above the threshold size, including chunks of variables,
2149
attributes, and internal infrastructure. Alignment is not locked
2150
in to a data variable. It can change between data chunks of the
2151
same variable, based on a file's history.
2152
2153
Refer to H5Pset_alignment in HDF5 documentation for more
2154
specific details, interactions, and additional rules.
2155
2156
@param threshold The minimum size to which alignment is applied.
2157
@param alignment The alignment value.
2158
2159
@return ::NC_NOERR No error.
2160
@return ::NC_EINVAL Invalid input.
2161
@author Dennis Heimbigner
2162
@ingroup datasets
2163
*/
2164
int
2165
nc_set_alignment(int threshold, int alignment)
2166
0
{
2167
0
    NCglobalstate* gs = NC_getglobalstate();
2168
0
    gs->alignment.threshold = threshold;
2169
0
    gs->alignment.alignment = alignment;
2170
0
    gs->alignment.defined = 1;
2171
0
    return NC_NOERR;
2172
0
}
2173
2174
/**
2175
Provide get function to retrieve global data alignment
2176
information.
2177
2178
The nc_get_alignment function return the last values set by
2179
nc_set_alignment.  If nc_set_alignment has not been called, then
2180
it returns the value 0 for both threshold and alignment.
2181
2182
@param thresholdp Return the current minimum size to which alignment is applied or zero.
2183
@param alignmentp Return the current alignment value or zero.
2184
2185
@return ::NC_NOERR No error.
2186
@return ::NC_EINVAL Invalid input.
2187
@author Dennis Heimbigner
2188
@ingroup datasets
2189
*/
2190
2191
int
2192
nc_get_alignment(int* thresholdp, int* alignmentp)
2193
0
{
2194
0
    NCglobalstate* gs = NC_getglobalstate();
2195
0
    if(thresholdp) *thresholdp = gs->alignment.threshold;
2196
0
    if(alignmentp) *alignmentp = gs->alignment.alignment;
2197
0
    return NC_NOERR;
2198
0
}