/src/gdal/netcdf-c-4.7.4/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 | 4.64k | { |
34 | 4.64k | NC_GRP_INFO_T *grp, *g; |
35 | 4.64k | NC_FILE_INFO_T *h5; |
36 | 4.64k | char norm_name[NC_MAX_NAME + 1]; |
37 | 4.64k | int retval; |
38 | | |
39 | 4.64k | 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 | 4.64k | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
43 | 0 | return retval; |
44 | 4.64k | assert(h5); |
45 | | |
46 | | /* Normalize name. */ |
47 | 4.64k | if ((retval = nc4_check_name(name, norm_name))) |
48 | 4.07k | return retval; |
49 | | |
50 | 571 | g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,norm_name); |
51 | 571 | 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 | 571 | return NC_ENOGRP; |
60 | 571 | } |
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 | 146k | { |
77 | 146k | NC_GRP_INFO_T *grp, *g; |
78 | 146k | NC_FILE_INFO_T *h5; |
79 | 146k | int num = 0; |
80 | 146k | int retval; |
81 | 146k | int i; |
82 | | |
83 | 146k | LOG((2, "nc_inq_grps: ncid 0x%x", ncid)); |
84 | | |
85 | | /* Find info for this file and group, and set pointer to each. */ |
86 | 146k | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
87 | 0 | return retval; |
88 | 146k | assert(h5); |
89 | | |
90 | | /* Count the number of groups in this group. */ |
91 | 146k | 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 | 146k | if (numgrps) |
107 | 76.3k | *numgrps = num; |
108 | | |
109 | 146k | return NC_NOERR; |
110 | 146k | } |
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 | 8.69k | { |
126 | 8.69k | NC_GRP_INFO_T *grp; |
127 | 8.69k | NC_FILE_INFO_T *h5; |
128 | 8.69k | int retval; |
129 | | |
130 | 8.69k | LOG((2, "nc_inq_grpname: ncid 0x%x", ncid)); |
131 | | |
132 | | /* Find info for this file and group, and set pointer to each. */ |
133 | 8.69k | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
134 | 0 | return retval; |
135 | 8.69k | assert(h5); |
136 | | |
137 | | /* Copy the name. */ |
138 | 8.69k | if (name) |
139 | 8.69k | strcpy(name, grp->hdr.name); |
140 | | |
141 | 8.69k | return NC_NOERR; |
142 | 8.69k | } |
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 | 628k | { |
162 | 628k | char *name, grp_name[NC_MAX_NAME + 1]; |
163 | 628k | int g, id = ncid, parent_id, *gid; |
164 | 628k | int i, ret = NC_NOERR; |
165 | | |
166 | | /* How many generations? */ |
167 | 628k | for (g = 0; !NC4_inq_grp_parent(id, &parent_id); g++, id = parent_id) |
168 | 0 | ; |
169 | | |
170 | | /* Allocate storage. */ |
171 | 628k | if (!(name = malloc((g + 1) * (NC_MAX_NAME + 1) + 1))) |
172 | 0 | return NC_ENOMEM; |
173 | 628k | if (!(gid = malloc((g + 1) * sizeof(int)))) |
174 | 0 | { |
175 | 0 | free(name); |
176 | 0 | return NC_ENOMEM; |
177 | 0 | } |
178 | 628k | assert(name && gid); |
179 | | |
180 | | /* Always start with a "/" for the root group. */ |
181 | 628k | strcpy(name, NC_GROUP_NAME); |
182 | | |
183 | | /* Get the ncids for all generations. */ |
184 | 628k | gid[0] = ncid; |
185 | 628k | 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 | 628k | 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 | 628k | if (!ret && lenp) |
200 | 628k | *lenp = strlen(name); |
201 | | |
202 | | /* Give the user the name, if he wants it. */ |
203 | 628k | if (!ret && full_name) |
204 | 314k | strcpy(full_name, name); |
205 | | |
206 | 628k | free(gid); |
207 | 628k | free(name); |
208 | | |
209 | 628k | return ret; |
210 | 628k | } |
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 | 743k | { |
229 | 743k | NC_GRP_INFO_T *grp; |
230 | 743k | NC_FILE_INFO_T *h5; |
231 | 743k | int retval; |
232 | | |
233 | 743k | LOG((2, "nc_inq_grp_parent: ncid 0x%x", ncid)); |
234 | | |
235 | | /* Find info for this file and group. */ |
236 | 743k | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
237 | 0 | return retval; |
238 | 743k | assert(h5); |
239 | | |
240 | | /* Set the parent ncid, if there is one. */ |
241 | 743k | 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 | 743k | else |
247 | 743k | return NC_ENOGRP; |
248 | | |
249 | 0 | return NC_NOERR; |
250 | 743k | } |
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 | 4.37k | { |
269 | 4.37k | NC_GRP_INFO_T *grp; |
270 | 4.37k | NC_FILE_INFO_T *h5; |
271 | 4.37k | int id1 = ncid, id2; |
272 | 4.37k | char *cp, *full_name_cpy; |
273 | 4.37k | int ret; |
274 | | |
275 | 4.37k | if (!full_name) |
276 | 0 | return NC_EINVAL; |
277 | | |
278 | | /* Find info for this file and group, and set pointer to each. */ |
279 | 4.37k | if ((ret = nc4_find_grp_h5(ncid, &grp, &h5))) |
280 | 0 | return ret; |
281 | 4.37k | 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 | 4.37k | if (!(full_name_cpy = strdup(full_name))) |
286 | 0 | return NC_ENOMEM; |
287 | | |
288 | | /* Get the first part of the name. */ |
289 | 4.37k | if (!(cp = strtok(full_name_cpy, "/"))) |
290 | 12 | { |
291 | | /* If "/" is passed, and this is the root group, return the root |
292 | | * group id. */ |
293 | 12 | if (!grp->parent) |
294 | 12 | id2 = ncid; |
295 | 0 | else |
296 | 0 | { |
297 | 0 | free(full_name_cpy); |
298 | 0 | return NC_ENOGRP; |
299 | 0 | } |
300 | 12 | } |
301 | 4.36k | else |
302 | 4.36k | { |
303 | | /* Keep parsing the string. */ |
304 | 4.36k | for (; cp; id1 = id2) |
305 | 4.36k | { |
306 | 4.36k | if ((ret = NC4_inq_ncid(id1, cp, &id2))) |
307 | 4.36k | { |
308 | 4.36k | free(full_name_cpy); |
309 | 4.36k | return ret; |
310 | 4.36k | } |
311 | 0 | cp = strtok(NULL, "/"); |
312 | 0 | } |
313 | 4.36k | } |
314 | | |
315 | | /* Give the user the requested value. */ |
316 | 12 | if (grp_ncid) |
317 | 12 | *grp_ncid = id2; |
318 | | |
319 | 12 | free(full_name_cpy); |
320 | | |
321 | 12 | return NC_NOERR; |
322 | 4.37k | } |
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.01k | { |
338 | 8.01k | NC_GRP_INFO_T *grp; |
339 | 8.01k | NC_FILE_INFO_T *h5; |
340 | 8.01k | NC_VAR_INFO_T *var; |
341 | 8.01k | int num_vars = 0; |
342 | 8.01k | int retval; |
343 | 8.01k | int i; |
344 | | |
345 | 8.01k | 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.01k | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
349 | 0 | return retval; |
350 | 8.01k | 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 | 279k | for (i=0; i < ncindexsize(grp->vars); i++) |
355 | 271k | { |
356 | 271k | var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i); |
357 | 271k | if (!var) continue; |
358 | 271k | if (varids) |
359 | 271k | varids[num_vars] = var->hdr.id; |
360 | 271k | num_vars++; |
361 | 271k | } |
362 | | |
363 | | /* If the user wants to know how many vars in the group, tell |
364 | | * him. */ |
365 | 8.01k | if (nvars) |
366 | 0 | *nvars = num_vars; |
367 | | |
368 | 8.01k | return NC_NOERR; |
369 | 8.01k | } |
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 | 45.3k | { |
384 | 45.3k | const int *ia = (const int *)a; |
385 | 45.3k | const int *ib = (const int *)b; |
386 | 45.3k | return *ia - *ib; |
387 | 45.3k | } |
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.84k | { |
407 | 2.84k | NC_GRP_INFO_T *grp, *g; |
408 | 2.84k | NC_FILE_INFO_T *h5; |
409 | 2.84k | NC_DIM_INFO_T *dim; |
410 | 2.84k | int num = 0; |
411 | 2.84k | int retval; |
412 | | |
413 | 2.84k | LOG((2, "nc_inq_dimids: ncid 0x%x include_parents: %d", ncid, |
414 | 2.84k | include_parents)); |
415 | | |
416 | | /* Find info for this file and group, and set pointer to each. */ |
417 | 2.84k | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
418 | 0 | return retval; |
419 | 2.84k | assert(h5); |
420 | | |
421 | | /* First count them. */ |
422 | 2.84k | num = ncindexcount(grp->dim); |
423 | 2.84k | if (include_parents) { |
424 | 584 | for (g = grp->parent; g; g = g->parent) |
425 | 0 | num += ncindexcount(g->dim); |
426 | 584 | } |
427 | | |
428 | | /* If the user wants the dimension ids, get them. */ |
429 | 2.84k | if (dimids) |
430 | 2.55k | { |
431 | 2.55k | int n = 0; |
432 | 2.55k | int i; |
433 | | |
434 | | /* Get dimension ids from this group. */ |
435 | 26.1k | for(i=0;i<ncindexsize(grp->dim);i++) { |
436 | 23.6k | dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i); |
437 | 23.6k | if(dim == NULL) continue; |
438 | 23.6k | dimids[n++] = dim->hdr.id; |
439 | 23.6k | } |
440 | | |
441 | | /* Get dimension ids from parent groups. */ |
442 | 2.55k | if (include_parents) |
443 | 292 | 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.55k | qsort(dimids, num, sizeof(int), int_cmp); |
451 | 2.55k | } |
452 | | |
453 | | /* If the user wants the number of dims, give it. */ |
454 | 2.84k | if (ndims) |
455 | 2.55k | *ndims = num; |
456 | | |
457 | 2.84k | return NC_NOERR; |
458 | 2.84k | } |