1 /* Copyright 2003-2018, University Corporation for Atmospheric
2  * Research. See the COPYRIGHT file for copying and redistribution
3  * conditions. */
4 /**
5  * @file @internal This file is part of netcdf-4, a netCDF-like
6  * interface for HDF5, or a HDF5 backend for netCDF, depending on your
7  * point of view.
8  *
9  * This file includes the HDF5 code to deal with dimensions.
10  *
11  * @author Ed Hartnett
12  */
13 
14 #include "config.h"
15 #include "hdf5internal.h"
16 
17 /**
18  * @internal Dimensions are defined in attributes attached to the
19  * appropriate group in the data file.
20  *
21  * @param ncid File and group ID.
22  * @param name Name of the new dimension.
23  * @param len Length of the new dimension.
24  * @param idp Pointer that gets the ID of the new dimension. Ignored
25  * if NULL.
26  *
27  * @return ::NC_NOERR No error.
28  * @return ::NC_EBADID Bad ncid.
29  * @return ::NC_EMAXNAME Name is too long.
30  * @return ::NC_EBADNAME Name breaks netCDF name rules.
31  * @return ::NC_EINVAL Invalid input.
32  * @return ::NC_EPERM Read-only file.
33  * @return ::NC_EUNLIMIT Only one unlimited dim for classic model.
34  * @return ::NC_ENOTINDEFINE Not in define mode.
35  * @return ::NC_EDIMSIZE Dim length too large.
36  * @return ::NC_ENAMEINUSE Name already in use in group.
37  * @return ::NC_ENOMEM Out of memory.
38  * @author Ed Hartnett
39  */
40 int
NC4_def_dim(int ncid,const char * name,size_t len,int * idp)41 NC4_def_dim(int ncid, const char *name, size_t len, int *idp)
42 {
43    NC *nc;
44    NC_GRP_INFO_T *grp;
45    NC_FILE_INFO_T *h5;
46    NC_DIM_INFO_T *dim;
47    char norm_name[NC_MAX_NAME + 1];
48    int retval = NC_NOERR;
49    int i;
50 
51    LOG((2, "%s: ncid 0x%x name %s len %d", __func__, ncid, name,
52         (int)len));
53 
54    /* Find our global metadata structure. */
55    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
56       return retval;
57    assert(h5 && nc && grp);
58 
59    /* If the file is read-only, return an error. */
60    if (h5->no_write)
61       return NC_EPERM;
62 
63    /* Check some stuff if strict nc3 rules are in effect. */
64    if (h5->cmode & NC_CLASSIC_MODEL)
65    {
66       /* Only one limited dimenson for strict nc3. */
67       if (len == NC_UNLIMITED) {
68 	 for(i=0;i<ncindexsize(grp->dim);i++) {
69 	    dim = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
70 	    if(dim == NULL) continue;
71             if (dim->unlimited)
72                return NC_EUNLIMIT;
73 	}
74       }
75       /* Must be in define mode for stict nc3. */
76       if (!(h5->flags & NC_INDEF))
77          return NC_ENOTINDEFINE;
78    }
79 
80    /* Make sure this is a valid netcdf name. */
81    if ((retval = nc4_check_name(name, norm_name)))
82       return retval;
83 
84    /* For classic model: dim length has to fit in a 32-bit unsigned
85     * int, as permitted for 64-bit offset format. */
86    if (h5->cmode & NC_CLASSIC_MODEL)
87       if(len > X_UINT_MAX) /* Backward compat */
88          return NC_EDIMSIZE;
89 
90    /* Make sure the name is not already in use. */
91    dim = (NC_DIM_INFO_T*)ncindexlookup(grp->dim,norm_name);
92    if(dim != NULL)
93          return NC_ENAMEINUSE;
94 
95    /* If it's not in define mode, enter define mode. Do this only
96     * after checking all input data, so we only enter define mode if
97     * input is good. */
98    if (!(h5->flags & NC_INDEF))
99       if ((retval = NC4_redef(ncid)))
100          return retval;
101 
102    /* Add a dimension to the list. The ID must come from the file
103     * information, since dimids are visible in more than one group. */
104    if ((retval = nc4_dim_list_add(grp, norm_name, len, -1, &dim)))
105       return retval;
106 
107    /* Create struct for HDF5-specific dim info. */
108    if (!(dim->format_dim_info = calloc(1, sizeof(NC_HDF5_DIM_INFO_T))))
109       return NC_ENOMEM;
110 
111    /* Pass back the dimid. */
112    if (idp)
113       *idp = dim->hdr.id;
114 
115    return retval;
116 }
117 
118 /**
119  * @internal Find out name and len of a dim. For an unlimited
120  * dimension, the length is the largest length so far written. If the
121  * name of lenp pointers are NULL, they will be ignored.
122  *
123  * @param ncid File and group ID.
124  * @param dimid Dimension ID.
125  * @param name Pointer that gets name of the dimension.
126  * @param lenp Pointer that gets length of dimension.
127  *
128  * @return ::NC_NOERR No error.
129  * @return ::NC_EBADID Bad ncid.
130  * @return ::NC_EDIMSIZE Dimension length too large.
131  * @return ::NC_EBADDIM Dimension not found.
132  * @author Ed Hartnett
133  */
134 int
NC4_inq_dim(int ncid,int dimid,char * name,size_t * lenp)135 NC4_inq_dim(int ncid, int dimid, char *name, size_t *lenp)
136 {
137    NC *nc;
138    NC_FILE_INFO_T *h5;
139    NC_GRP_INFO_T *grp, *dim_grp;
140    NC_DIM_INFO_T *dim;
141    int ret = NC_NOERR;
142 
143    LOG((2, "%s: ncid 0x%x dimid %d", __func__, ncid, dimid));
144 
145    /* Find our global metadata structure. */
146    if ((ret = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
147       return ret;
148    assert(h5 && nc && grp);
149 
150    /* Find the dimension and its home group. */
151    if ((ret = nc4_find_dim(grp, dimid, &dim, &dim_grp)))
152       return ret;
153    assert(dim);
154 
155    /* Return the dimension name, if the caller wants it. */
156    if (name && dim->hdr.name)
157       strcpy(name, dim->hdr.name);
158 
159    /* Return the dimension length, if the caller wants it. */
160    if (lenp)
161    {
162       if (dim->unlimited)
163       {
164          /* Since this is an unlimited dimension, go to the file
165             and see how many records there are. Take the max number
166             of records from all the vars that share this
167             dimension. */
168          *lenp = 0;
169          if ((ret = nc4_find_dim_len(dim_grp, dimid, &lenp)))
170             return ret;
171       }
172       else
173       {
174          if (dim->too_long)
175          {
176             ret = NC_EDIMSIZE;
177             *lenp = NC_MAX_UINT;
178          }
179          else
180             *lenp = dim->len;
181       }
182    }
183 
184    return ret;
185 }
186 
187 /**
188  * @internal Rename a dimension, for those who like to prevaricate.
189  *
190  * @param ncid File and group ID.
191  * @param dimid Dimension ID.
192  * @param name New dimension name.
193  *
194  * @return ::NC_NOERR No error.
195  * @return ::NC_EBADID Bad ncid.
196  * @return ::NC_EHDFERR HDF5 returned error.
197  * @return ::NC_ENOMEM Out of memory.
198  * @return ::NC_EINVAL Name must be provided.
199  * @return ::NC_ENAMEINUSE Name is already in use in group.
200  * @return ::NC_EMAXNAME Name is too long.
201  * @return ::NC_EBADDIM Dimension not found.
202  * @return ::NC_EBADNAME Name breaks netCDF name rules.
203  * @return ::NC_EDIMMETA Unable to delete HDF5 dataset.
204  * @author Ed Hartnett
205  */
206 int
NC4_rename_dim(int ncid,int dimid,const char * name)207 NC4_rename_dim(int ncid, int dimid, const char *name)
208 {
209    NC *nc;
210    NC_GRP_INFO_T *grp;
211    NC_DIM_INFO_T *dim;
212    NC_HDF5_DIM_INFO_T *hdf5_dim;
213    NC_FILE_INFO_T *h5;
214    char norm_name[NC_MAX_NAME + 1];
215    int retval;
216 
217    /* Note: name is new name */
218    if (!name)
219       return NC_EINVAL;
220 
221    LOG((2, "%s: ncid 0x%x dimid %d name %s", __func__, ncid,
222         dimid, name));
223 
224    /* Find info for this file and group, and set pointer to each. */
225    if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
226       return retval;
227    assert(nc && h5 && grp);
228 
229    /* Trying to write to a read-only file? No way, Jose! */
230    if (h5->no_write)
231       return NC_EPERM;
232 
233    /* Make sure this is a valid netcdf name. */
234    if ((retval = nc4_check_name(name, norm_name)))
235       return retval;
236 
237    /* Get the original dim. */
238    if ((retval = nc4_find_dim(grp, dimid, &dim, NULL)))
239       return retval;
240    assert(dim && dim->format_dim_info);
241    hdf5_dim = (NC_HDF5_DIM_INFO_T *)dim->format_dim_info;
242 
243    /* Check if new name is in use. */
244    if (ncindexlookup(grp->dim, norm_name))
245       return NC_ENAMEINUSE;
246 
247    /* Check for renaming dimension w/o variable. */
248    if (hdf5_dim->hdf_dimscaleid)
249    {
250       assert(!dim->coord_var);
251       LOG((3, "dim %s is a dim without variable", dim->hdr.name));
252 
253       /* Delete the dimscale-only dataset. */
254       if ((retval = delete_existing_dimscale_dataset(grp, dimid, dim)))
255          return retval;
256    }
257 
258    /* Give the dimension its new name in metadata. UTF8 normalization
259     * has been done. */
260    assert(dim->hdr.name);
261    free(dim->hdr.name);
262    if (!(dim->hdr.name = strdup(norm_name)))
263       return NC_ENOMEM;
264    LOG((3, "dim is now named %s", dim->hdr.name));
265    dim->hdr.hashkey = NC_hashmapkey(dim->hdr.name,strlen(dim->hdr.name)); /* Fix hash key */
266 
267    if (!ncindexrebuild(grp->dim))
268       return NC_EINTERNAL;
269 
270    /* Check if dimension was a coordinate variable, but names are
271     * different now. */
272    if (dim->coord_var && strcmp(dim->hdr.name, dim->coord_var->hdr.name))
273    {
274       /* Break up the coordinate variable. */
275       if ((retval = nc4_break_coord_var(grp, dim->coord_var, dim)))
276          return retval;
277    }
278 
279    /* Check if dimension should become a coordinate variable. */
280    if (!dim->coord_var)
281    {
282       NC_VAR_INFO_T *var;
283 
284       /* Attempt to find a variable with the same name as the
285        * dimension in the current group. */
286       if ((retval = nc4_find_var(grp, dim->hdr.name, &var)))
287          return retval;
288 
289       /* Check if we found a variable and the variable has the
290        * dimension in index 0. */
291       if (var && var->dim[0] == dim)
292       {
293          /* Sanity check */
294          assert(var->dimids[0] == dim->hdr.id);
295 
296          /* Reform the coordinate variable. */
297          if ((retval = nc4_reform_coord_var(grp, var, dim)))
298             return retval;
299       }
300    }
301 
302    return NC_NOERR;
303 }
304