Coverage Report

Created: 2025-10-28 07:06

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