Coverage Report

Created: 2026-06-30 08:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/netcdf-c-4.7.4/libsrc4/nc4grp.c
Line
Count
Source
1
/* Copyright 2005-2018, University Corporation for Atmospheric
2
 * Research. See COPYRIGHT file for copying and redistribution
3
 * conditions. */
4
/**
5
 * @file
6
 * @internal This file is part of netcdf-4, a netCDF-like interface
7
 * for HDF5, or a HDF5 backend for netCDF, depending on your point of
8
 * view.
9
 *
10
 * This file handles groups.
11
 *
12
 * @author Ed Hartnett
13
 */
14
15
#include "nc4internal.h"
16
#include "nc4dispatch.h"
17
/**
18
 * @internal Given an ncid and group name (NULL gets root group),
19
 * return the ncid of that group.
20
 *
21
 * @param ncid File and group ID.
22
 * @param name Pointer that gets name.
23
 * @param grp_ncid Pointer that gets group ncid.
24
 *
25
 * @return ::NC_NOERR No error.
26
 * @return ::NC_EBADID Bad ncid.
27
 * @return ::NC_ENOTNC4 Not a netCDF-4 file.
28
 * @return ::NC_ENOGRP Group not found.
29
 * @author Ed Hartnett
30
 */
31
int
32
NC4_inq_ncid(int ncid, const char *name, int *grp_ncid)
33
5.62k
{
34
5.62k
    NC_GRP_INFO_T *grp, *g;
35
5.62k
    NC_FILE_INFO_T *h5;
36
5.62k
    char norm_name[NC_MAX_NAME + 1];
37
5.62k
    int retval;
38
39
5.62k
    LOG((2, "nc_inq_ncid: ncid 0x%x name %s", ncid, name));
40
41
    /* Find info for this file and group, and set pointer to each. */
42
5.62k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
43
0
        return retval;
44
5.62k
    assert(h5);
45
46
    /* Normalize name. */
47
5.62k
    if ((retval = nc4_check_name(name, norm_name)))
48
5.11k
        return retval;
49
50
513
    g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,norm_name);
51
513
    if(g != NULL)
52
0
    {
53
0
        if (grp_ncid)
54
0
            *grp_ncid = grp->nc4_info->controller->ext_ncid | g->hdr.id;
55
0
        return NC_NOERR;
56
0
    }
57
58
    /* If we got here, we didn't find the named group. */
59
513
    return NC_ENOGRP;
60
513
}
61
62
/**
63
 * @internal Given a location id, return the number of groups it
64
 * contains, and an array of their locids.
65
 *
66
 * @param ncid File and group ID.
67
 * @param numgrps Pointer that gets number of groups. Ignored if NULL.
68
 * @param ncids Pointer that gets array of ncids. Ignored if NULL.
69
 *
70
 * @return ::NC_NOERR No error.
71
 * @return ::NC_EBADID Bad ncid.
72
 * @author Ed Hartnett
73
 */
74
int
75
NC4_inq_grps(int ncid, int *numgrps, int *ncids)
76
128k
{
77
128k
    NC_GRP_INFO_T *grp, *g;
78
128k
    NC_FILE_INFO_T *h5;
79
128k
    int num = 0;
80
128k
    int retval;
81
128k
    int i;
82
83
128k
    LOG((2, "nc_inq_grps: ncid 0x%x", ncid));
84
85
    /* Find info for this file and group, and set pointer to each. */
86
128k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
87
0
        return retval;
88
128k
    assert(h5);
89
90
    /* Count the number of groups in this group. */
91
128k
    for(i=0;i<ncindexsize(grp->children);i++)
92
0
    {
93
0
        g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
94
0
        if(g == NULL) continue;
95
0
        if (ncids)
96
0
        {
97
            /* Combine the nc_grpid in a bitwise or with the ext_ncid,
98
             * which allows the returned ncid to carry both file and
99
             * group information. */
100
0
            *ncids = g->hdr.id | g->nc4_info->controller->ext_ncid;
101
0
            ncids++;
102
0
        }
103
0
        num++;
104
0
    }
105
106
128k
    if (numgrps)
107
67.7k
        *numgrps = num;
108
109
128k
    return NC_NOERR;
110
128k
}
111
112
/**
113
 * @internal Given locid, find name of group. (Root group is named
114
 * "/".)
115
 *
116
 * @param ncid File and group ID.
117
 * @param name Pointer that gets name.
118
119
 * @return ::NC_NOERR No error.
120
 * @return ::NC_EBADID Bad ncid.
121
 * @author Ed Hartnett
122
 */
123
int
124
NC4_inq_grpname(int ncid, char *name)
125
9.25k
{
126
9.25k
    NC_GRP_INFO_T *grp;
127
9.25k
    NC_FILE_INFO_T *h5;
128
9.25k
    int retval;
129
130
9.25k
    LOG((2, "nc_inq_grpname: ncid 0x%x", ncid));
131
132
    /* Find info for this file and group, and set pointer to each. */
133
9.25k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
134
0
        return retval;
135
9.25k
    assert(h5);
136
137
    /* Copy the name. */
138
9.25k
    if (name)
139
9.25k
        strcpy(name, grp->hdr.name);
140
141
9.25k
    return NC_NOERR;
142
9.25k
}
143
144
/**
145
 * @internal Find the full path name to the group represented by
146
 * ncid. Either pointer argument may be NULL; pass a NULL for the
147
 * third parameter to get the length of the full path name. The length
148
 * will not include room for a null pointer.
149
 *
150
 * @param ncid File and group ID.
151
 * @param lenp Pointer that gets length of full name.
152
 * @param full_name Pointer that gets name.
153
 *
154
 * @return ::NC_NOERR No error.
155
 * @return ::NC_EBADID Bad ncid.
156
 * @return ::NC_ENOMEM Out of memory.
157
 * @author Ed Hartnett
158
 */
159
int
160
NC4_inq_grpname_full(int ncid, size_t *lenp, char *full_name)
161
654k
{
162
654k
    char *name, grp_name[NC_MAX_NAME + 1];
163
654k
    int g, id = ncid, parent_id, *gid;
164
654k
    int i, ret = NC_NOERR;
165
166
    /* How many generations? */
167
654k
    for (g = 0; !NC4_inq_grp_parent(id, &parent_id); g++, id = parent_id)
168
0
        ;
169
170
    /* Allocate storage. */
171
654k
    if (!(name = malloc((g + 1) * (NC_MAX_NAME + 1) + 1)))
172
0
        return NC_ENOMEM;
173
654k
    if (!(gid = malloc((g + 1) * sizeof(int))))
174
0
    {
175
0
        free(name);
176
0
        return NC_ENOMEM;
177
0
    }
178
654k
    assert(name && gid);
179
180
    /* Always start with a "/" for the root group. */
181
654k
    strcpy(name, NC_GROUP_NAME);
182
183
    /* Get the ncids for all generations. */
184
654k
    gid[0] = ncid;
185
654k
    for (i = 1; i < g && !ret; i++)
186
0
        ret = NC4_inq_grp_parent(gid[i - 1], &gid[i]);
187
188
    /* Assemble the full name. */
189
654k
    for (i = g - 1; !ret && i >= 0 && !ret; i--)
190
0
    {
191
0
        if ((ret = NC4_inq_grpname(gid[i], grp_name)))
192
0
            break;
193
0
        strcat(name, grp_name);
194
0
        if (i)
195
0
            strcat(name, "/");
196
0
    }
197
198
    /* Give the user the length of the name, if he wants it. */
199
654k
    if (!ret && lenp)
200
654k
        *lenp = strlen(name);
201
202
    /* Give the user the name, if he wants it. */
203
654k
    if (!ret && full_name)
204
327k
        strcpy(full_name, name);
205
206
654k
    free(gid);
207
654k
    free(name);
208
209
654k
    return ret;
210
654k
}
211
212
/**
213
 * @internal Find the parent ncid of a group. For the root group,
214
 * return NC_ENOGRP error.  *Now* I know what kind of tinfoil hat
215
 * wearing nut job would call this function with a NULL pointer for
216
 * parent_ncid - Russ Rew!!
217
 *
218
 * @param ncid File and group ID.
219
 * @param parent_ncid Pointer that gets the ncid of parent group.
220
 *
221
 * @return ::NC_NOERR No error.
222
 * @return ::NC_EBADID Bad ncid.
223
 * @return ::NC_ENOGRP Root has no parent.
224
 * @author Ed Hartnett
225
 */
226
int
227
NC4_inq_grp_parent(int ncid, int *parent_ncid)
228
760k
{
229
760k
    NC_GRP_INFO_T *grp;
230
760k
    NC_FILE_INFO_T *h5;
231
760k
    int retval;
232
233
760k
    LOG((2, "nc_inq_grp_parent: ncid 0x%x", ncid));
234
235
    /* Find info for this file and group. */
236
760k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
237
0
        return retval;
238
760k
    assert(h5);
239
240
    /* Set the parent ncid, if there is one. */
241
760k
    if (grp->parent)
242
0
    {
243
0
        if (parent_ncid)
244
0
            *parent_ncid = grp->nc4_info->controller->ext_ncid | grp->parent->hdr.id;
245
0
    }
246
760k
    else
247
760k
        return NC_ENOGRP;
248
249
0
    return NC_NOERR;
250
760k
}
251
252
/**
253
 * @internal Given a full name and ncid, find group ncid.
254
 *
255
 * @param ncid File and group ID.
256
 * @param full_name Full name of group.
257
 * @param grp_ncid Pointer that gets ncid of group.
258
 *
259
 * @return ::NC_NOERR No error.
260
 * @return ::NC_EBADID Bad ncid.
261
 * @return ::NC_ENOGRP Group not found.
262
 * @return ::NC_ENOMEM Out of memory.
263
 * @return ::NC_EINVAL Name is required.
264
 * @author Ed Hartnett
265
 */
266
int
267
NC4_inq_grp_full_ncid(int ncid, const char *full_name, int *grp_ncid)
268
5.54k
{
269
5.54k
    NC_GRP_INFO_T *grp;
270
5.54k
    NC_FILE_INFO_T *h5;
271
5.54k
    int id1 = ncid, id2;
272
5.54k
    char *cp, *full_name_cpy;
273
5.54k
    int ret;
274
275
5.54k
    if (!full_name)
276
0
        return NC_EINVAL;
277
278
    /* Find info for this file and group, and set pointer to each. */
279
5.54k
    if ((ret = nc4_find_grp_h5(ncid, &grp, &h5)))
280
0
        return ret;
281
5.54k
    assert(h5);
282
283
    /* Copy full_name because strtok messes with the value it works
284
     * with, and we don't want to mess up full_name. */
285
5.54k
    if (!(full_name_cpy = strdup(full_name)))
286
0
        return NC_ENOMEM;
287
288
    /* Get the first part of the name. */
289
5.54k
    if (!(cp = strtok(full_name_cpy, "/")))
290
42
    {
291
        /* If "/" is passed, and this is the root group, return the root
292
         * group id. */
293
42
        if (!grp->parent)
294
42
            id2 = ncid;
295
0
        else
296
0
        {
297
0
            free(full_name_cpy);
298
0
            return NC_ENOGRP;
299
0
        }
300
42
    }
301
5.50k
    else
302
5.50k
    {
303
        /* Keep parsing the string. */
304
5.50k
        for (; cp; id1 = id2)
305
5.50k
        {
306
5.50k
            if ((ret = NC4_inq_ncid(id1, cp, &id2)))
307
5.50k
            {
308
5.50k
                free(full_name_cpy);
309
5.50k
                return ret;
310
5.50k
            }
311
0
            cp = strtok(NULL, "/");
312
0
        }
313
5.50k
    }
314
315
    /* Give the user the requested value. */
316
42
    if (grp_ncid)
317
42
        *grp_ncid = id2;
318
319
42
    free(full_name_cpy);
320
321
42
    return NC_NOERR;
322
5.54k
}
323
324
/**
325
 * @internal Get a list of ids for all the variables in a group.
326
 *
327
 * @param ncid File and group ID.
328
 * @param nvars Pointer that gets number of vars in group.
329
 * @param varids Pointer that gets array of var IDs.
330
 *
331
 * @return ::NC_NOERR No error.
332
 * @return ::NC_EBADID Bad ncid.
333
 * @author Ed Hartnett
334
 */
335
int
336
NC4_inq_varids(int ncid, int *nvars, int *varids)
337
8.74k
{
338
8.74k
    NC_GRP_INFO_T *grp;
339
8.74k
    NC_FILE_INFO_T *h5;
340
8.74k
    NC_VAR_INFO_T *var;
341
8.74k
    int num_vars = 0;
342
8.74k
    int retval;
343
8.74k
    int i;
344
345
8.74k
    LOG((2, "nc_inq_varids: ncid 0x%x", ncid));
346
347
    /* Find info for this file and group, and set pointer to each. */
348
8.74k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
349
0
        return retval;
350
8.74k
    assert(h5);
351
352
    /* This is a netCDF-4 group. Round up them doggies and count
353
     * 'em. The list is in correct (i.e. creation) order. */
354
253k
    for (i=0; i < ncindexsize(grp->vars); i++)
355
245k
    {
356
245k
        var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
357
245k
        if (!var) continue;
358
245k
        if (varids)
359
245k
            varids[num_vars] = var->hdr.id;
360
245k
        num_vars++;
361
245k
    }
362
363
    /* If the user wants to know how many vars in the group, tell
364
     * him. */
365
8.74k
    if (nvars)
366
0
        *nvars = num_vars;
367
368
8.74k
    return NC_NOERR;
369
8.74k
}
370
371
/**
372
 * @internal This is the comparison function used for sorting dim
373
 * ids. Integer comparison: returns negative if b > a and positive if
374
 * a > b.
375
 *
376
 * @param a A pointer to an item to compare to b.
377
 * @param b A pointer to an item to compare to a.
378
 *
379
 * @return a - b
380
 * @author Ed Hartnett
381
 */
382
int int_cmp(const void *a, const void *b)
383
40.7k
{
384
40.7k
    const int *ia = (const int *)a;
385
40.7k
    const int *ib = (const int *)b;
386
40.7k
    return *ia  - *ib;
387
40.7k
}
388
389
/**
390
 * @internal Find all dimids for a location. This finds all dimensions
391
 * in a group, with or without any of its parents, depending on last
392
 * parameter.
393
 *
394
 * @param ncid File and group ID.
395
 * @param ndims Pointer that gets number of dimensions available in group.
396
 * @param dimids Pointer that gets dim IDs.
397
 * @param include_parents If non-zero, include dimensions from parent
398
 * groups.
399
 *
400
 * @return ::NC_NOERR No error.
401
 * @return ::NC_EBADID Bad ncid.
402
 * @author Ed Hartnett
403
 */
404
int
405
NC4_inq_dimids(int ncid, int *ndims, int *dimids, int include_parents)
406
2.88k
{
407
2.88k
    NC_GRP_INFO_T *grp, *g;
408
2.88k
    NC_FILE_INFO_T *h5;
409
2.88k
    NC_DIM_INFO_T *dim;
410
2.88k
    int num = 0;
411
2.88k
    int retval;
412
413
2.88k
    LOG((2, "nc_inq_dimids: ncid 0x%x include_parents: %d", ncid,
414
2.88k
         include_parents));
415
416
    /* Find info for this file and group, and set pointer to each. */
417
2.88k
    if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
418
0
        return retval;
419
2.88k
    assert(h5);
420
421
    /* First count them. */
422
2.88k
    num = ncindexcount(grp->dim);
423
2.88k
    if (include_parents) {
424
266
        for (g = grp->parent; g; g = g->parent)
425
0
            num += ncindexcount(g->dim);
426
266
    }
427
428
    /* If the user wants the dimension ids, get them. */
429
2.88k
    if (dimids)
430
2.75k
    {
431
2.75k
        int n = 0;
432
2.75k
        int i;
433
434
        /* Get dimension ids from this group. */
435
25.1k
        for(i=0;i<ncindexsize(grp->dim);i++) {
436
22.3k
            dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
437
22.3k
            if(dim == NULL) continue;
438
22.3k
            dimids[n++] = dim->hdr.id;
439
22.3k
        }
440
441
        /* Get dimension ids from parent groups. */
442
2.75k
        if (include_parents)
443
133
            for (g = grp->parent; g; g = g->parent) {
444
0
                for(i=0;i<ncindexsize(g->dim);i++) {
445
0
                    dim = (NC_DIM_INFO_T*)ncindexith(g->dim,i);
446
0
                    if(dim == NULL) continue;
447
0
                    dimids[n++] = dim->hdr.id;
448
0
                }
449
0
            }
450
2.75k
        qsort(dimids, num, sizeof(int), int_cmp);
451
2.75k
    }
452
453
    /* If the user wants the number of dims, give it. */
454
2.88k
    if (ndims)
455
2.75k
        *ndims = num;
456
457
2.88k
    return NC_NOERR;
458
2.88k
}