Coverage Report

Created: 2025-10-28 07:06

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