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
NC4_inq_ncid(int ncid,const char * name,int * grp_ncid)32 NC4_inq_ncid(int ncid, const char *name, int *grp_ncid)
33 {
34 NC_GRP_INFO_T *grp, *g;
35 NC_FILE_INFO_T *h5;
36 char norm_name[NC_MAX_NAME + 1];
37 int retval;
38
39 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 if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
43 return retval;
44 assert(h5);
45
46 /* Short circuit the case of name == NULL => return the root group */
47 if(name == NULL) {
48 if(grp_ncid) {
49 NC_FILE_INFO_T* file = grp->nc4_info;
50 *grp_ncid = file->controller->ext_ncid | file->root_grp->hdr.id;
51 }
52 return NC_NOERR;
53 }
54
55 /* Normalize name. */
56 if ((retval = nc4_check_name(name, norm_name)))
57 return retval;
58
59 g = (NC_GRP_INFO_T*)ncindexlookup(grp->children,norm_name);
60 if(g != NULL)
61 {
62 if (grp_ncid)
63 *grp_ncid = grp->nc4_info->controller->ext_ncid | g->hdr.id;
64 return NC_NOERR;
65 }
66
67 /* If we got here, we didn't find the named group. */
68 return NC_ENOGRP;
69 }
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
NC4_inq_grps(int ncid,int * numgrps,int * ncids)84 NC4_inq_grps(int ncid, int *numgrps, int *ncids)
85 {
86 NC_GRP_INFO_T *grp, *g;
87 NC_FILE_INFO_T *h5;
88 int num = 0;
89 int retval;
90 int i;
91
92 LOG((2, "nc_inq_grps: ncid 0x%x", ncid));
93
94 /* Find info for this file and group, and set pointer to each. */
95 if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
96 return retval;
97 assert(h5);
98
99 /* Count the number of groups in this group. */
100 for(i=0;i<ncindexsize(grp->children);i++)
101 {
102 g = (NC_GRP_INFO_T*)ncindexith(grp->children,i);
103 if(g == NULL) continue;
104 if (ncids)
105 {
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 *ncids = g->hdr.id | g->nc4_info->controller->ext_ncid;
110 ncids++;
111 }
112 num++;
113 }
114
115 if (numgrps)
116 *numgrps = num;
117
118 return NC_NOERR;
119 }
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
NC4_inq_grpname(int ncid,char * name)133 NC4_inq_grpname(int ncid, char *name)
134 {
135 NC_GRP_INFO_T *grp;
136 NC_FILE_INFO_T *h5;
137 int retval;
138
139 LOG((2, "nc_inq_grpname: ncid 0x%x", ncid));
140
141 /* Find info for this file and group, and set pointer to each. */
142 if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
143 return retval;
144 assert(h5);
145
146 /* Copy the name. */
147 if (name)
148 strcpy(name, grp->hdr.name);
149
150 return NC_NOERR;
151 }
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
NC4_inq_grpname_full(int ncid,size_t * lenp,char * full_name)169 NC4_inq_grpname_full(int ncid, size_t *lenp, char *full_name)
170 {
171 char *name, grp_name[NC_MAX_NAME + 1];
172 int g, id = ncid, parent_id, *gid;
173 int i, ret = NC_NOERR;
174
175 /* How many generations? */
176 for (g = 0; !NC4_inq_grp_parent(id, &parent_id); g++, id = parent_id)
177 ;
178
179 /* Allocate storage. */
180 if (!(name = malloc((g + 1) * (NC_MAX_NAME + 1) + 1)))
181 return NC_ENOMEM;
182 if (!(gid = malloc((g + 1) * sizeof(int))))
183 {
184 free(name);
185 return NC_ENOMEM;
186 }
187 assert(name && gid);
188
189 /* Always start with a "/" for the root group. */
190 strcpy(name, NC_GROUP_NAME);
191
192 /* Get the ncids for all generations. */
193 gid[0] = ncid;
194 for (i = 1; i < g && !ret; i++)
195 ret = NC4_inq_grp_parent(gid[i - 1], &gid[i]);
196
197 /* Assemble the full name. */
198 for (i = g - 1; !ret && i >= 0 && !ret; i--)
199 {
200 if ((ret = NC4_inq_grpname(gid[i], grp_name)))
201 break;
202 strcat(name, grp_name);
203 if (i)
204 strcat(name, "/");
205 }
206
207 /* Give the user the length of the name, if he wants it. */
208 if (!ret && lenp)
209 *lenp = strlen(name);
210
211 /* Give the user the name, if he wants it. */
212 if (!ret && full_name)
213 strcpy(full_name, name);
214
215 free(gid);
216 free(name);
217
218 return ret;
219 }
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
NC4_inq_grp_parent(int ncid,int * parent_ncid)236 NC4_inq_grp_parent(int ncid, int *parent_ncid)
237 {
238 NC_GRP_INFO_T *grp;
239 NC_FILE_INFO_T *h5;
240 int retval;
241
242 LOG((2, "nc_inq_grp_parent: ncid 0x%x", ncid));
243
244 /* Find info for this file and group. */
245 if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
246 return retval;
247 assert(h5);
248
249 /* Set the parent ncid, if there is one. */
250 if (grp->parent)
251 {
252 if (parent_ncid)
253 *parent_ncid = grp->nc4_info->controller->ext_ncid | grp->parent->hdr.id;
254 }
255 else
256 return NC_ENOGRP;
257
258 return NC_NOERR;
259 }
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
NC4_inq_grp_full_ncid(int ncid,const char * full_name,int * grp_ncid)276 NC4_inq_grp_full_ncid(int ncid, const char *full_name, int *grp_ncid)
277 {
278 NC_GRP_INFO_T *grp;
279 NC_FILE_INFO_T *h5;
280 int id1 = ncid, id2;
281 char *cp, *full_name_cpy;
282 int ret;
283
284 if (!full_name)
285 return NC_EINVAL;
286
287 /* Find info for this file and group, and set pointer to each. */
288 if ((ret = nc4_find_grp_h5(ncid, &grp, &h5)))
289 return ret;
290 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 if (!(full_name_cpy = strdup(full_name)))
295 return NC_ENOMEM;
296
297 /* Get the first part of the name. */
298 if (!(cp = strtok(full_name_cpy, "/")))
299 {
300 /* If "/" is passed, and this is the root group, return the root
301 * group id. */
302 if (!grp->parent)
303 id2 = ncid;
304 else
305 {
306 free(full_name_cpy);
307 return NC_ENOGRP;
308 }
309 }
310 else
311 {
312 /* Keep parsing the string. */
313 for (; cp; id1 = id2)
314 {
315 if ((ret = NC4_inq_ncid(id1, cp, &id2)))
316 {
317 free(full_name_cpy);
318 return ret;
319 }
320 cp = strtok(NULL, "/");
321 }
322 }
323
324 /* Give the user the requested value. */
325 if (grp_ncid)
326 *grp_ncid = id2;
327
328 free(full_name_cpy);
329
330 return NC_NOERR;
331 }
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
NC4_inq_varids(int ncid,int * nvars,int * varids)345 NC4_inq_varids(int ncid, int *nvars, int *varids)
346 {
347 NC_GRP_INFO_T *grp;
348 NC_FILE_INFO_T *h5;
349 NC_VAR_INFO_T *var;
350 int num_vars = 0;
351 int retval;
352 int i;
353
354 LOG((2, "nc_inq_varids: ncid 0x%x", ncid));
355
356 /* Find info for this file and group, and set pointer to each. */
357 if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
358 return retval;
359 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 for (i=0; i < ncindexsize(grp->vars); i++)
364 {
365 var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
366 if (!var) continue;
367 if (varids)
368 varids[num_vars] = var->hdr.id;
369 num_vars++;
370 }
371
372 /* If the user wants to know how many vars in the group, tell
373 * him. */
374 if (nvars)
375 *nvars = num_vars;
376
377 return NC_NOERR;
378 }
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 */
int_cmp(const void * a,const void * b)391 int int_cmp(const void *a, const void *b)
392 {
393 const int *ia = (const int *)a;
394 const int *ib = (const int *)b;
395 return *ia - *ib;
396 }
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
NC4_inq_dimids(int ncid,int * ndims,int * dimids,int include_parents)414 NC4_inq_dimids(int ncid, int *ndims, int *dimids, int include_parents)
415 {
416 NC_GRP_INFO_T *grp, *g;
417 NC_FILE_INFO_T *h5;
418 NC_DIM_INFO_T *dim;
419 int num = 0;
420 int retval;
421
422 LOG((2, "nc_inq_dimids: ncid 0x%x include_parents: %d", ncid,
423 include_parents));
424
425 /* Find info for this file and group, and set pointer to each. */
426 if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
427 return retval;
428 assert(h5);
429
430 /* First count them. */
431 num = ncindexcount(grp->dim);
432 if (include_parents) {
433 for (g = grp->parent; g; g = g->parent)
434 num += ncindexcount(g->dim);
435 }
436
437 /* If the user wants the dimension ids, get them. */
438 if (dimids)
439 {
440 int n = 0;
441 int i;
442
443 /* Get dimension ids from this group. */
444 for(i=0;i<ncindexsize(grp->dim);i++) {
445 dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
446 if(dim == NULL) continue;
447 dimids[n++] = dim->hdr.id;
448 }
449
450 /* Get dimension ids from parent groups. */
451 if (include_parents)
452 for (g = grp->parent; g; g = g->parent) {
453 for(i=0;i<ncindexsize(g->dim);i++) {
454 dim = (NC_DIM_INFO_T*)ncindexith(g->dim,i);
455 if(dim == NULL) continue;
456 dimids[n++] = dim->hdr.id;
457 }
458 }
459 qsort(dimids, num, sizeof(int), int_cmp);
460 }
461
462 /* If the user wants the number of dims, give it. */
463 if (ndims)
464 *ndims = num;
465
466 return NC_NOERR;
467 }
468