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