Coverage Report

Created: 2026-04-09 06:16

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