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