/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 | } |