nc4grp.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /*
  2. This file is part of netcdf-4, a netCDF-like interface for HDF5, or a
  3. HDF5 backend for netCDF, depending on your point of view.
  4. This file handles the nc4 groups.
  5. Copyright 2005, University Corporation for Atmospheric Research. See
  6. netcdf-4/docs/COPYRIGHT file for copying and redistribution
  7. conditions.
  8. $Id: nc4grp.c,v 1.44 2010/05/25 17:54:23 dmh Exp $
  9. */
  10. #include "nc4internal.h"
  11. #include "nc4dispatch.h"
  12. /* Create a group. It's ncid is returned in the new_ncid pointer. */
  13. int
  14. NC4_def_grp(int parent_ncid, const char *name, int *new_ncid)
  15. {
  16. NC_GRP_INFO_T *grp, *g;
  17. NC_HDF5_FILE_INFO_T *h5;
  18. char norm_name[NC_MAX_NAME + 1];
  19. int retval;
  20. LOG((2, "nc_def_grp: parent_ncid 0x%x name %s", parent_ncid, name));
  21. /* Find info for this file and group, and set pointer to each. */
  22. if ((retval = nc4_find_grp_h5(parent_ncid, &grp, &h5)))
  23. return retval;
  24. if (!h5)
  25. return NC_ENOTNC4;
  26. /* Check and normalize the name. */
  27. if ((retval = nc4_check_name(name, norm_name)))
  28. return retval;
  29. /* Check that this name is not in use as a var, grp, or type. */
  30. if ((retval = nc4_check_dup_name(grp, norm_name)))
  31. return retval;
  32. /* No groups in netcdf-3! */
  33. if (h5->cmode & NC_CLASSIC_MODEL)
  34. return NC_ESTRICTNC3;
  35. /* If it's not in define mode, switch to define mode. */
  36. if (!(h5->flags & NC_INDEF))
  37. if ((retval = NC4_redef(parent_ncid)))
  38. return retval;
  39. /* Update internal lists to reflect new group. The actual HDF5
  40. * group creation will be done when metadata is written by a
  41. * sync. */
  42. if ((retval = nc4_grp_list_add(&(grp->children), h5->next_nc_grpid,
  43. grp, grp->file, norm_name, &g)))
  44. return retval;
  45. if (new_ncid)
  46. *new_ncid = grp->file->ext_ncid | h5->next_nc_grpid;
  47. h5->next_nc_grpid++;
  48. return NC_NOERR;
  49. }
  50. /* Given an ncid and group name (NULL gets root group), return
  51. * the ncid of that group. */
  52. int
  53. NC4_inq_ncid(int ncid, const char *name, int *grp_ncid)
  54. {
  55. NC_GRP_INFO_T *grp, *g;
  56. NC_HDF5_FILE_INFO_T *h5;
  57. char norm_name[NC_MAX_NAME + 1];
  58. int retval;
  59. LOG((2, "nc_inq_ncid: ncid 0x%x name %s", ncid, name));
  60. /* Find info for this file and group, and set pointer to each. */
  61. if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
  62. return retval;
  63. /* Groups only work with netCDF-4/HDF5 files... */
  64. if (!h5)
  65. return NC_ENOTNC4;
  66. /* Normalize name. */
  67. if ((retval = nc4_normalize_name(name, norm_name)))
  68. return retval;
  69. /* Look through groups for one of this name. */
  70. for (g = grp->children; g; g = g->next)
  71. if (!strcmp(norm_name, g->name)) /* found it! */
  72. {
  73. if (grp_ncid)
  74. *grp_ncid = grp->file->ext_ncid | g->nc_grpid;
  75. return NC_NOERR;
  76. }
  77. /* If we got here, we didn't find the named group. */
  78. return NC_ENOGRP;
  79. }
  80. /* Given a location id, return the number of groups it contains, and
  81. * an array of their locids. */
  82. int
  83. NC4_inq_grps(int ncid, int *numgrps, int *ncids)
  84. {
  85. NC_GRP_INFO_T *grp, *g;
  86. NC_HDF5_FILE_INFO_T *h5;
  87. int num = 0;
  88. int retval;
  89. LOG((2, "nc_inq_grps: ncid 0x%x", ncid));
  90. /* Find info for this file and group, and set pointer to each. */
  91. if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
  92. return retval;
  93. /* For netCDF-3 files, just report zero groups. */
  94. if (!h5)
  95. {
  96. if (numgrps)
  97. *numgrps = 0;
  98. return NC_NOERR;
  99. }
  100. /* Count the number of groups in this group. */
  101. for (g = grp->children; g; g = g->next)
  102. {
  103. if (ncids)
  104. {
  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. *ncids = g->nc_grpid | g->file->ext_ncid;
  109. ncids++;
  110. }
  111. num++;
  112. }
  113. if (numgrps)
  114. *numgrps = num;
  115. return NC_NOERR;
  116. }
  117. /* Given locid, find name of group. (Root group is named "/".) */
  118. int
  119. NC4_inq_grpname(int ncid, char *name)
  120. {
  121. NC_GRP_INFO_T *grp;
  122. NC_HDF5_FILE_INFO_T *h5;
  123. int retval;
  124. LOG((2, "nc_inq_grpname: ncid 0x%x", ncid));
  125. /* Find info for this file and group, and set pointer to each. */
  126. if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
  127. return retval;
  128. if (name)
  129. {
  130. if (!h5)
  131. strcpy(name, "/");
  132. else
  133. strcpy(name, grp->name);
  134. }
  135. return NC_NOERR;
  136. }
  137. /* Find the full path name to the group represented by ncid. Either
  138. * pointer argument may be NULL; pass a NULL for the third parameter
  139. * to get the length of the full path name. The length will not
  140. * include room for a null pointer. */
  141. int
  142. NC4_inq_grpname_full(int ncid, size_t *lenp, char *full_name)
  143. {
  144. char *name, grp_name[NC_MAX_NAME + 1];
  145. int g, id = ncid, parent_id, *gid;
  146. int i, ret = NC_NOERR;
  147. /* How many generations? */
  148. for (g = 0; !nc_inq_grp_parent(id, &parent_id); g++, id = parent_id)
  149. ;
  150. /* Allocate storage. */
  151. if (!(name = malloc((g + 1) * (NC_MAX_NAME + 1) + 1)))
  152. return NC_ENOMEM;
  153. if (!(gid = malloc((g + 1) * sizeof(int))))
  154. {
  155. free(name);
  156. return NC_ENOMEM;
  157. }
  158. assert(name && gid);
  159. /* Always start with a "/" for the root group. */
  160. strcpy(name, "/");
  161. /* Get the ncids for all generations. */
  162. gid[0] = ncid;
  163. for (i = 1; i < g && !ret; i++)
  164. ret = nc_inq_grp_parent(gid[i - 1], &gid[i]);
  165. /* Assemble the full name. */
  166. for (i = g - 1; !ret && i >= 0 && !ret; i--)
  167. {
  168. if ((ret = nc_inq_grpname(gid[i], grp_name)))
  169. break;
  170. strcat(name, grp_name);
  171. if (i)
  172. strcat(name, "/");
  173. }
  174. /* Give the user the length of the name, if he wants it. */
  175. if (!ret && lenp)
  176. *lenp = strlen(name);
  177. /* Give the user the name, if he wants it. */
  178. if (!ret && full_name)
  179. strcpy(full_name, name);
  180. free(gid);
  181. free(name);
  182. return ret;
  183. }
  184. /* Find the parent ncid of a group. For the root group, return
  185. * NC_ENOGRP error. *Now* I know what kind of tinfoil hat wearing nut
  186. * job would call this function with a NULL pointer for parent_ncid -
  187. * Russ Rew!! */
  188. int
  189. NC4_inq_grp_parent(int ncid, int *parent_ncid)
  190. {
  191. NC_GRP_INFO_T *grp;
  192. NC_HDF5_FILE_INFO_T *h5;
  193. int retval;
  194. LOG((2, "nc_inq_grp_parent: ncid 0x%x", ncid));
  195. /* Find info for this file and group. */
  196. if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
  197. return retval;
  198. /* Groups only work with netCDF-4/HDF5 files... */
  199. if (!h5)
  200. return NC_ENOGRP;
  201. /* Set the parent ncid, if there is one. */
  202. if (grp->parent)
  203. {
  204. if (parent_ncid)
  205. *parent_ncid = grp->file->ext_ncid | grp->parent->nc_grpid;
  206. }
  207. else
  208. return NC_ENOGRP;
  209. return NC_NOERR;
  210. }
  211. /* Given a full name and ncid, find group ncid. */
  212. int
  213. NC4_inq_grp_full_ncid(int ncid, const char *full_name, int *grp_ncid)
  214. {
  215. NC_GRP_INFO_T *grp;
  216. NC_HDF5_FILE_INFO_T *h5;
  217. int id1 = ncid, id2;
  218. char *cp, *full_name_cpy;
  219. int ret;
  220. if (!full_name)
  221. return NC_EINVAL;
  222. /* Find info for this file and group, and set pointer to each. */
  223. if ((ret = nc4_find_grp_h5(ncid, &grp, &h5)))
  224. return ret;
  225. /* Copy full_name because strtok messes with the value it works
  226. * with, and we don't want to mess up full_name. */
  227. if (!(full_name_cpy = malloc(strlen(full_name) + 1)))
  228. return NC_ENOMEM;
  229. strcpy(full_name_cpy, full_name);
  230. /* Get the first part of the name. */
  231. if (!(cp = strtok(full_name_cpy, "/")))
  232. {
  233. /* If "/" is passed, and this is the root group, return the root
  234. * group id. */
  235. if (!grp->parent)
  236. id2 = ncid;
  237. else
  238. {
  239. free(full_name_cpy);
  240. return NC_ENOGRP;
  241. }
  242. }
  243. else
  244. {
  245. /* Keep parsing the string. */
  246. for (; cp; id1 = id2)
  247. {
  248. if ((ret = nc_inq_grp_ncid(id1, cp, &id2)))
  249. {
  250. free(full_name_cpy);
  251. return ret;
  252. }
  253. cp = strtok(NULL, "/");
  254. }
  255. }
  256. /* Give the user the requested value. */
  257. if (grp_ncid)
  258. *grp_ncid = id2;
  259. free(full_name_cpy);
  260. return NC_NOERR;
  261. }
  262. /* Get a list of ids for all the variables in a group. */
  263. int
  264. NC4_inq_varids(int ncid, int *nvars, int *varids)
  265. {
  266. NC_GRP_INFO_T *grp;
  267. NC_HDF5_FILE_INFO_T *h5;
  268. NC_VAR_INFO_T *var;
  269. int v, num_vars = 0;
  270. int retval;
  271. LOG((2, "nc_inq_varids: ncid 0x%x", ncid));
  272. /* Find info for this file and group, and set pointer to each. */
  273. if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
  274. return retval;
  275. if (!h5)
  276. {
  277. /* If this is a netcdf-3 file, there is only one group, the root
  278. * group, and its vars have ids 0 thru nvars - 1. */
  279. if ((retval = nc_inq(ncid, NULL, &num_vars, NULL, NULL)))
  280. return retval;
  281. if (varids)
  282. for (v = 0; v < num_vars; v++)
  283. varids[v] = v;
  284. }
  285. else
  286. {
  287. /* This is a netCDF-4 group. Round up them doggies and count
  288. * 'em. The list is in correct (i.e. creation) order. */
  289. if (grp->var)
  290. {
  291. for (var = grp->var; var; var = var->next)
  292. {
  293. if (varids)
  294. varids[num_vars] = var->varid;
  295. num_vars++;
  296. }
  297. }
  298. }
  299. /* If the user wants to know how many vars in the group, tell
  300. * him. */
  301. if (nvars)
  302. *nvars = num_vars;
  303. return NC_NOERR;
  304. }
  305. /* This is the comparison function used for sorting dim ids. Integer
  306. comparison: returns negative if b > a and positive if a > b. */
  307. int int_cmp(const void *a, const void *b)
  308. {
  309. const int *ia = (const int *)a;
  310. const int *ib = (const int *)b;
  311. return *ia - *ib;
  312. }
  313. /* Find all dimids for a location. This finds all dimensions in a
  314. * group, with or without any of its parents, depending on last
  315. * parameter. */
  316. int
  317. NC4_inq_dimids(int ncid, int *ndims, int *dimids, int include_parents)
  318. {
  319. NC_GRP_INFO_T *grp, *g;
  320. NC_HDF5_FILE_INFO_T *h5;
  321. NC_DIM_INFO_T *dim;
  322. int d, num = 0;
  323. int retval;
  324. LOG((2, "nc_inq_dimids: ncid 0x%x include_parents: %d", ncid,
  325. include_parents));
  326. /* Find info for this file and group, and set pointer to each. */
  327. if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
  328. return retval;
  329. if (!h5)
  330. {
  331. /* If this is a netcdf-3 file, then the dimids are going to be 0
  332. * thru ndims-1, so just provide them. */
  333. if ((retval = nc_inq(ncid, &num, NULL, NULL, NULL)))
  334. return retval;
  335. if (dimids)
  336. for (d = 0; d < num; d++)
  337. dimids[d] = d;
  338. }
  339. else
  340. {
  341. /* First count them. */
  342. for (dim = grp->dim; dim; dim = dim->next)
  343. num++;
  344. if (include_parents)
  345. for (g = grp->parent; g; g = g->parent)
  346. for (dim = g->dim; dim; dim = dim->next)
  347. num++;
  348. /* If the user wants the dimension ids, get them. */
  349. if (dimids)
  350. {
  351. int n = 0;
  352. /* Get dimension ids from this group. */
  353. for (dim = grp->dim; dim; dim = dim->next)
  354. dimids[n++] = dim->dimid;
  355. /* Get dimension ids from parent groups. */
  356. if (include_parents)
  357. for (g = grp->parent; g; g = g->parent)
  358. for (dim = g->dim; dim; dim = dim->next)
  359. dimids[n++] = dim->dimid;
  360. qsort(dimids, num, sizeof(int), int_cmp);
  361. }
  362. }
  363. /* If the user wants the number of dims, give it. */
  364. if (ndims)
  365. *ndims = num;
  366. return NC_NOERR;
  367. }