1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright by The HDF Group.                                               *
3  * Copyright by the Board of Trustees of the University of Illinois.         *
4  * All rights reserved.                                                      *
5  *                                                                           *
6  * This file is part of HDF5.  The full HDF5 copyright notice, including     *
7  * terms governing use, modification, and redistribution, is contained in    *
8  * the COPYING file, which can be found at the root of the source code       *
9  * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
10  * If you do not have access to either file, you may request a copy from     *
11  * help@hdfgroup.org.                                                        *
12  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13 
14 /***********************************************************
15 *
16 * Test program:     titerate
17 *
18 * Test the Group & Attribute functionality
19 *
20 *************************************************************/
21 
22 #include "testhdf5.h"
23 #include "H5srcdir.h"
24 
25 #define DATAFILE   "titerate.h5"
26 
27 /* Number of datasets for group iteration test */
28 #define NDATASETS 50
29 
30 /* Number of attributes for attribute iteration test */
31 #define NATTR 50
32 
33 /* Number of groups for second group iteration test */
34 #define ITER_NGROUPS 150
35 
36 /* General maximum length of names used */
37 #define NAMELEN     80
38 
39 /* 1-D dataset with fixed dimensions */
40 #define SPACE1_RANK    1
41 #define SPACE1_DIM1    4
42 
43 typedef enum {
44     RET_ZERO,
45     RET_TWO,
46     RET_CHANGE,
47     RET_CHANGE2
48 } iter_enum;
49 
50 /* Custom group iteration callback data */
51 typedef struct {
52     char name[NAMELEN];     /* The name of the object */
53     H5O_type_t type;        /* The type of the object */
54     iter_enum command;      /* The type of return value */
55 } iter_info;
56 
57 /* Definition for test_corrupted_attnamelen */
58 #define CORRUPTED_ATNAMELEN_FILE   "corrupted_name_len.h5"
59 #define DSET_NAME   "image"
60 typedef struct searched_err_t {
61     char message[256];
62     bool found;
63 } searched_err_t;
64 
65 /* Call back function for test_corrupted_attnamelen */
66 static int find_err_msg_cb(unsigned n, const H5E_error2_t *err_desc, void *_client_data);
67 
68 /* Local functions */
69 int iter_strcmp(const void *s1, const void *s2);
70 int iter_strcmp2(const void *s1, const void *s2);
71 static herr_t liter_cb(hid_t group, const char *name, const H5L_info_t *info,
72     void *op_data);
73 static herr_t liter_cb2(hid_t group, const char *name, const H5L_info_t *info,
74     void *op_data);
75 herr_t aiter_cb(hid_t group, const char *name, const H5A_info_t *ainfo,
76     void *op_data);
77 
78 /****************************************************************
79 **
80 **  iter_strcmp(): String comparison routine for qsort
81 **
82 ****************************************************************/
iter_strcmp(const void * s1,const void * s2)83 H5_ATTR_PURE int iter_strcmp(const void *s1, const void *s2)
84 {
85     return(HDstrcmp(*(const char * const *)s1,*(const char * const *)s2));
86 }
87 
88 /****************************************************************
89 **
90 **  liter_cb(): Custom link iteration callback routine.
91 **
92 ****************************************************************/
93 static herr_t
liter_cb(hid_t H5_ATTR_UNUSED group,const char * name,const H5L_info_t H5_ATTR_UNUSED * link_info,void * op_data)94 liter_cb(hid_t H5_ATTR_UNUSED group, const char *name, const H5L_info_t H5_ATTR_UNUSED *link_info,
95     void *op_data)
96 {
97     iter_info *info = (iter_info *)op_data;
98     static int count = 0;
99     static int count2 = 0;
100 
101     HDstrcpy(info->name, name);
102 
103     switch(info->command) {
104         case RET_ZERO:
105             return(0);
106 
107         case RET_TWO:
108             return(2);
109 
110         case RET_CHANGE:
111             count++;
112             return(count > 10 ? 1 : 0);
113 
114         case RET_CHANGE2:
115             count2++;
116             return(count2 > 10 ? 1 : 0);
117 
118         default:
119             HDprintf("invalid iteration command");
120             return(-1);
121     } /* end switch */
122 } /* end liter_cb() */
123 
124 /****************************************************************
125 **
126 **  test_iter_group(): Test group iteration functionality
127 **
128 ****************************************************************/
129 static void
test_iter_group(hid_t fapl,hbool_t new_format)130 test_iter_group(hid_t fapl, hbool_t new_format)
131 {
132     hid_t file;             /* File ID */
133     hid_t dataset;          /* Dataset ID */
134     hid_t datatype;         /* Common datatype ID */
135     hid_t filespace;        /* Common dataspace ID */
136     hid_t root_group,grp;   /* Root group ID */
137     int i;                  /* counting variable */
138     hsize_t idx;            /* Index in the group */
139     char name[NAMELEN];     /* temporary name buffer */
140     char *lnames[NDATASETS + 2];/* Names of the links created */
141     char dataset_name[NAMELEN];  /* dataset name */
142     iter_info info;         /* Custom iteration information */
143     H5G_info_t ginfo;       /* Buffer for querying object's info */
144     herr_t ret;            /* Generic return value */
145 
146     /* Output message about test being performed */
147     MESSAGE(5, ("Testing Group Iteration Functionality\n"));
148 
149     /* Create the test file with the datasets */
150     file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
151     CHECK(file, FAIL, "H5Fcreate");
152 
153     /* Test iterating over empty group */
154     info.command = RET_ZERO;
155     idx = 0;
156     ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info);
157     VERIFY(ret, SUCCEED, "H5Literate");
158 
159     datatype = H5Tcopy(H5T_NATIVE_INT);
160     CHECK(datatype, FAIL, "H5Tcopy");
161 
162     filespace=H5Screate(H5S_SCALAR);
163     CHECK(filespace, FAIL, "H5Screate");
164 
165     for(i=0; i< NDATASETS; i++) {
166         HDsprintf(name,"Dataset %d",i);
167         dataset = H5Dcreate2(file, name, datatype, filespace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
168         CHECK(dataset, FAIL, "H5Dcreate2");
169 
170         /* Keep a copy of the dataset names around for later */
171         lnames[i] = HDstrdup(name);
172         CHECK_PTR(lnames[i], "strdup");
173 
174         ret = H5Dclose(dataset);
175         CHECK(ret, FAIL, "H5Dclose");
176     } /* end for */
177 
178     /* Create a group and named datatype under root group for testing */
179     grp = H5Gcreate2(file, "grp", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
180     CHECK(ret, FAIL, "H5Gcreate2");
181 
182     lnames[NDATASETS] = HDstrdup("grp");
183     CHECK_PTR(lnames[NDATASETS], "strdup");
184 
185     ret = H5Tcommit2(file, "dtype", datatype, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
186     CHECK(ret, FAIL, "H5Tcommit2");
187 
188     lnames[NDATASETS + 1] = HDstrdup("dtype");
189     CHECK_PTR(lnames[NDATASETS], "strdup");
190 
191     /* Close everything up */
192     ret = H5Tclose(datatype);
193     CHECK(ret, FAIL, "H5Tclose");
194 
195     ret = H5Gclose(grp);
196     CHECK(ret, FAIL, "H5Gclose");
197 
198     ret = H5Sclose(filespace);
199     CHECK(ret, FAIL, "H5Sclose");
200 
201     ret = H5Fclose(file);
202     CHECK(ret, FAIL, "H5Fclose");
203 
204     /* Sort the dataset names */
205     HDqsort(lnames, (size_t)(NDATASETS + 2), sizeof(char *), iter_strcmp);
206 
207 
208     /* Iterate through the datasets in the root group in various ways */
209     file = H5Fopen(DATAFILE, H5F_ACC_RDONLY, fapl);
210     CHECK(file, FAIL, "H5Fopen");
211 
212     /* These two functions, H5Oget_info_by_idx and H5Lget_name_by_idx, actually
213      * iterate through B-tree for group members in internal library design.
214      */
215     root_group = H5Gopen2(file, "/", H5P_DEFAULT);
216     CHECK(root_group, FAIL, "H5Gopen2");
217 
218     ret = H5Gget_info(root_group, &ginfo);
219     CHECK(ret, FAIL, "H5Gget_info");
220     VERIFY(ginfo.nlinks, (NDATASETS + 2), "H5Gget_info");
221 
222     for(i = 0; i< (int)ginfo.nlinks; i++) {
223         H5O_info_t oinfo;               /* Object info */
224 
225         ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, dataset_name, (size_t)NAMELEN, H5P_DEFAULT);
226         CHECK(ret, FAIL, "H5Lget_name_by_idx");
227 
228         ret = H5Oget_info_by_idx2(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT);
229         CHECK(ret, FAIL, "H5Oget_info_by_idx");
230     } /* end for */
231 
232     H5E_BEGIN_TRY {
233         ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)(NDATASETS+3), dataset_name, (size_t)NAMELEN, H5P_DEFAULT);
234     } H5E_END_TRY;
235     VERIFY(ret, FAIL, "H5Lget_name_by_idx");
236 
237     ret = H5Gclose(root_group);
238     CHECK(ret, FAIL, "H5Gclose");
239 
240     /* These two functions, H5Oget_info_by_idx and H5Lget_name_by_idx, actually
241      * iterate through B-tree for group members in internal library design.
242      *  (Same as test above, but with the file ID instead of opening the root group)
243      */
244     ret = H5Gget_info(file, &ginfo);
245     CHECK(ret, FAIL, "H5Gget_info");
246     VERIFY(ginfo.nlinks, NDATASETS + 2, "H5Gget_info");
247 
248     for(i = 0; i< (int)ginfo.nlinks; i++) {
249         H5O_info_t oinfo;               /* Object info */
250 
251         ret = (herr_t)H5Lget_name_by_idx(file, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, dataset_name, (size_t)NAMELEN, H5P_DEFAULT);
252         CHECK(ret, FAIL, "H5Lget_name_by_idx");
253 
254         ret = H5Oget_info_by_idx2(file, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT);
255         CHECK(ret, FAIL, "H5Oget_info_by_idx");
256     } /* end for */
257 
258     H5E_BEGIN_TRY {
259         ret = (herr_t)H5Lget_name_by_idx(file, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)(NDATASETS + 3), dataset_name, (size_t)NAMELEN, H5P_DEFAULT);
260     } H5E_END_TRY;
261     VERIFY(ret, FAIL, "H5Lget_name_by_idx");
262 
263     /* Test invalid indices for starting iteration */
264     info.command = RET_ZERO;
265     idx = (hsize_t)-1;
266     H5E_BEGIN_TRY {
267         ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info);
268     } H5E_END_TRY;
269     VERIFY(ret, FAIL, "H5Literate");
270 
271     /* Test skipping exactly as many entries as in the group */
272     idx = NDATASETS + 2;
273     H5E_BEGIN_TRY {
274         ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info);
275     } H5E_END_TRY;
276     VERIFY(ret, FAIL, "H5Literate");
277 
278     /* Test skipping more entries than are in the group */
279     idx = NDATASETS + 3;
280     H5E_BEGIN_TRY {
281         ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info);
282     } H5E_END_TRY;
283     VERIFY(ret, FAIL, "H5Literate");
284 
285     /* Test all objects in group, when callback always returns 0 */
286     info.command = RET_ZERO;
287     idx = 0;
288     if((ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info)) > 0)
289         TestErrPrintf("Group iteration function didn't return zero correctly!\n");
290 
291     /* Test all objects in group, when callback always returns 1 */
292     /* This also tests the "restarting" ability, because the index changes */
293     info.command = RET_TWO;
294     i = 0;
295     idx = 0;
296     while((ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info)) > 0) {
297         /* Verify return value from iterator gets propagated correctly */
298         VERIFY(ret, 2, "H5Literate");
299 
300         /* Increment the number of times "2" is returned */
301         i++;
302 
303         /* Verify that the index is the correct value */
304         VERIFY(idx, (hsize_t)i, "H5Literate");
305         if(idx > (NDATASETS + 2))
306             TestErrPrintf("Group iteration function walked too far!\n");
307 
308         /* Verify that the correct name is retrieved */
309         if(HDstrcmp(info.name, lnames[(size_t)(idx - 1)]) != 0)
310             TestErrPrintf("Group iteration function didn't return name correctly for link - lnames[%u] = '%s'!\n", (unsigned)(idx - 1), lnames[(size_t)(idx - 1)]);
311     } /* end while */
312     VERIFY(ret, -1, "H5Literate");
313 
314     if(i != (NDATASETS + 2))
315         TestErrPrintf("%u: Group iteration function didn't perform multiple iterations correctly!\n", __LINE__);
316 
317     /* Test all objects in group, when callback changes return value */
318     /* This also tests the "restarting" ability, because the index changes */
319     info.command = new_format ? RET_CHANGE2 : RET_CHANGE;
320     i = 0;
321     idx = 0;
322     while((ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb, &info)) >= 0) {
323         /* Verify return value from iterator gets propagated correctly */
324         VERIFY(ret, 1, "H5Literate");
325 
326         /* Increment the number of times "1" is returned */
327         i++;
328 
329         /* Verify that the index is the correct value */
330         VERIFY(idx, (hsize_t)(i + 10), "H5Literate");
331         if(idx > (NDATASETS + 2))
332             TestErrPrintf("Group iteration function walked too far!\n");
333 
334         /* Verify that the correct name is retrieved */
335         if(HDstrcmp(info.name, lnames[(size_t)(idx - 1)]) != 0)
336             TestErrPrintf("Group iteration function didn't return name correctly for link - lnames[%u] = '%s'!\n", (unsigned)(idx - 1), lnames[(size_t)(idx - 1)]);
337     } /* end while */
338     VERIFY(ret, -1, "H5Literate");
339 
340     if(i != 42 || idx != 52)
341         TestErrPrintf("%u: Group iteration function didn't perform multiple iterations correctly!\n", __LINE__);
342 
343     ret = H5Fclose(file);
344     CHECK(ret, FAIL, "H5Fclose");
345 
346     /* Free the dataset names */
347     for(i = 0; i< (NDATASETS + 2); i++)
348         HDfree(lnames[i]);
349 } /* test_iter_group() */
350 
351 /****************************************************************
352 **
353 **  aiter_cb(): Custom group iteration callback routine.
354 **
355 ****************************************************************/
356 herr_t
aiter_cb(hid_t H5_ATTR_UNUSED group,const char * name,const H5A_info_t H5_ATTR_UNUSED * ainfo,void * op_data)357 aiter_cb(hid_t H5_ATTR_UNUSED group, const char *name, const H5A_info_t H5_ATTR_UNUSED *ainfo,
358     void *op_data)
359 {
360     iter_info *info = (iter_info *)op_data;
361     static int count = 0;
362     static int count2 = 0;
363 
364     HDstrcpy(info->name, name);
365 
366     switch(info->command) {
367         case RET_ZERO:
368             return(0);
369 
370         case RET_TWO:
371             return(2);
372 
373         case RET_CHANGE:
374             count++;
375             return(count > 10 ? 1 : 0);
376 
377         case RET_CHANGE2:
378             count2++;
379             return(count2 > 10 ? 1 : 0);
380 
381         default:
382             HDprintf("invalid iteration command");
383             return(-1);
384     } /* end switch */
385 } /* end aiter_cb() */
386 
387 /****************************************************************
388 **
389 **  test_iter_attr(): Test attribute iteration functionality
390 **
391 ****************************************************************/
test_iter_attr(hid_t fapl,hbool_t new_format)392 static void test_iter_attr(hid_t fapl, hbool_t new_format)
393 {
394     hid_t file;             /* File ID */
395     hid_t dataset;          /* Common Dataset ID */
396     hid_t filespace;        /* Common dataspace ID */
397     hid_t attribute;        /* Attribute ID */
398     int i;                  /* counting variable */
399     hsize_t idx;            /* Index in the attribute list */
400     char name[NAMELEN];     /* temporary name buffer */
401     char *anames[NATTR];    /* Names of the attributes created */
402     iter_info info;         /* Custom iteration information */
403     herr_t        ret;        /* Generic return value        */
404 
405     /* Output message about test being performed */
406     MESSAGE(5, ("Testing Attribute Iteration Functionality\n"));
407 
408     /* Create the test file with the datasets */
409     file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
410     CHECK(file, FAIL, "H5Fcreate");
411 
412     filespace = H5Screate(H5S_SCALAR);
413     CHECK(filespace, FAIL, "H5Screate");
414 
415     dataset = H5Dcreate2(file, "Dataset", H5T_NATIVE_INT, filespace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
416     CHECK(dataset, FAIL, "H5Dcreate2");
417 
418     for(i = 0; i < NATTR; i++) {
419         HDsprintf(name, "Attribute %02d", i);
420         attribute = H5Acreate2(dataset, name, H5T_NATIVE_INT, filespace, H5P_DEFAULT, H5P_DEFAULT);
421         CHECK(attribute, FAIL, "H5Acreate2");
422 
423         /* Keep a copy of the attribute names around for later */
424         anames[i] = HDstrdup(name);
425         CHECK_PTR(anames[i], "strdup");
426 
427         ret = H5Aclose(attribute);
428         CHECK(ret, FAIL, "H5Aclose");
429     } /* end for */
430 
431     /* Close everything up */
432     ret = H5Dclose(dataset);
433     CHECK(ret, FAIL, "H5Dclose");
434 
435     ret = H5Sclose(filespace);
436     CHECK(ret, FAIL, "H5Sclose");
437 
438     ret = H5Fclose(file);
439     CHECK(ret, FAIL, "H5Fclose");
440 
441 
442     /* Iterate through the attributes on the dataset in various ways */
443     file = H5Fopen(DATAFILE, H5F_ACC_RDONLY, fapl);
444     CHECK(file, FAIL, "H5Fopen");
445 
446     dataset = H5Dopen2(file, "Dataset", H5P_DEFAULT);
447     CHECK(dataset, FAIL, "H5Dopen2");
448 
449     /* Test invalid indices for starting iteration */
450     info.command = RET_ZERO;
451 
452     /* Test skipping exactly as many attributes as there are */
453     idx = NATTR;
454     H5E_BEGIN_TRY {
455         ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info);
456     } H5E_END_TRY;
457     VERIFY(ret, FAIL, "H5Aiterate2");
458 
459     /* Test skipping more attributes than there are */
460     idx = NATTR + 1;
461     H5E_BEGIN_TRY {
462         ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info);
463     } H5E_END_TRY;
464     VERIFY(ret, FAIL, "H5Aiterate2");
465 
466     /* Test all attributes on dataset, when callback always returns 0 */
467     info.command = RET_ZERO;
468     idx = 0;
469     if((ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info)) > 0)
470         TestErrPrintf("Attribute iteration function didn't return zero correctly!\n");
471 
472     /* Test all attributes on dataset, when callback always returns 1 */
473     /* This also tests the "restarting" ability, because the index changes */
474     info.command = RET_TWO;
475     i = 0;
476     idx = 0;
477     while((ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info)) > 0) {
478         /* Verify return value from iterator gets propagated correctly */
479         VERIFY(ret, 2, "H5Aiterate2");
480 
481         /* Increment the number of times "2" is returned */
482         i++;
483 
484         /* Verify that the index is the correct value */
485         VERIFY(idx, (unsigned)i, "H5Aiterate2");
486 
487         /* Don't check name when new format is used */
488         if(!new_format) {
489             /* Verify that the correct name is retrieved */
490             if(HDstrcmp(info.name, anames[(size_t)idx - 1]) != 0)
491                 TestErrPrintf("%u: Attribute iteration function didn't set names correctly, info.name = '%s', anames[%u] = '%s'!\n", __LINE__, info.name, (unsigned)(idx - 1), anames[(size_t)idx - 1]);
492         } /* end if */
493     } /* end while */
494     VERIFY(ret, -1, "H5Aiterate2");
495     if(i != 50 || idx != 50)
496         TestErrPrintf("%u: Attribute iteration function didn't perform multiple iterations correctly!\n", __LINE__);
497 
498 
499     /* Test all attributes on dataset, when callback changes return value */
500     /* This also tests the "restarting" ability, because the index changes */
501     info.command = new_format ? RET_CHANGE2 : RET_CHANGE;
502     i = 0;
503     idx = 0;
504     while((ret = H5Aiterate2(dataset, H5_INDEX_NAME, H5_ITER_INC, &idx, aiter_cb, &info)) > 0) {
505         /* Verify return value from iterator gets propagated correctly */
506         VERIFY(ret, 1, "H5Aiterate2");
507 
508         /* Increment the number of times "1" is returned */
509         i++;
510 
511         /* Verify that the index is the correct value */
512         VERIFY(idx, (unsigned)i + 10, "H5Aiterate2");
513 
514         /* Don't check name when new format is used */
515         if(!new_format) {
516             /* Verify that the correct name is retrieved */
517             if(HDstrcmp(info.name, anames[(size_t)idx - 1]) != 0)
518                 TestErrPrintf("%u: Attribute iteration function didn't set names correctly, info.name = '%s', anames[%u] = '%s'!\n", __LINE__, info.name, (unsigned)(idx - 1), anames[(size_t)idx - 1]);
519         } /* end if */
520     } /* end while */
521     VERIFY(ret, -1, "H5Aiterate2");
522     if(i != 40 || idx != 50)
523         TestErrPrintf("%u: Attribute iteration function didn't perform multiple iterations correctly!\n", __LINE__);
524 
525     ret=H5Fclose(file);
526     CHECK(ret, FAIL, "H5Fclose");
527 
528     ret=H5Dclose(dataset);
529     CHECK(ret, FAIL, "H5Dclose");
530 
531     /* Free the attribute names */
532     for(i=0; i< NATTR; i++)
533         HDfree(anames[i]);
534 
535 } /* test_iter_attr() */
536 
537 /****************************************************************
538 **
539 **  iter_strcmp2(): String comparison routine for qsort
540 **
541 ****************************************************************/
iter_strcmp2(const void * s1,const void * s2)542 H5_ATTR_PURE int iter_strcmp2(const void *s1, const void *s2)
543 {
544     return(HDstrcmp((const char *)s1, (const char *)s2));
545 } /* end iter_strcmp2() */
546 
547 /****************************************************************
548 **
549 **  liter_cb2(): Custom link iteration callback routine.
550 **
551 ****************************************************************/
552 static herr_t
liter_cb2(hid_t loc_id,const char * name,const H5L_info_t H5_ATTR_UNUSED * link_info,void * opdata)553 liter_cb2(hid_t loc_id, const char *name, const H5L_info_t H5_ATTR_UNUSED *link_info,
554     void *opdata)
555 {
556     const iter_info *test_info = (const iter_info *)opdata;
557     H5O_info_t oinfo;
558     herr_t ret;        /* Generic return value        */
559 
560     if(HDstrcmp(name, test_info->name)) {
561         TestErrPrintf("name = '%s', test_info = '%s'\n", name, test_info->name);
562         return(H5_ITER_ERROR);
563     } /* end if */
564 
565     /*
566      * Get type of the object and check it.
567      */
568     ret = H5Oget_info_by_name2(loc_id, name, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT);
569     CHECK(ret, FAIL, "H5Oget_info_by_name");
570 
571     if(test_info->type != oinfo.type) {
572         TestErrPrintf("test_info->type = %d, oinfo.type = %d\n", test_info->type, (int)oinfo.type);
573         return(H5_ITER_ERROR);
574     } /* end if */
575 
576     return(H5_ITER_STOP);
577 } /* liter_cb2() */
578 
579 /****************************************************************
580 **
581 **  test_iter_group_large(): Test group iteration functionality
582 **          for groups with large #'s of objects
583 **
584 ****************************************************************/
585 static void
test_iter_group_large(hid_t fapl)586 test_iter_group_large(hid_t fapl)
587 {
588     hid_t        file;        /* HDF5 File IDs        */
589     hid_t        dataset;    /* Dataset ID            */
590     hid_t        group;      /* Group ID             */
591     hid_t        sid;       /* Dataspace ID            */
592     hid_t        tid;       /* Datatype ID            */
593     hsize_t        dims[] = {SPACE1_DIM1};
594     herr_t        ret;        /* Generic return value        */
595     char gname[20];             /* Temporary group name */
596     iter_info *names;           /* Names of objects in the root group */
597     iter_info *curr_name;       /* Pointer to the current name in the root group */
598     int                 i;
599 
600     /* Compound datatype */
601     typedef struct s1_t {
602         unsigned int a;
603         unsigned int b;
604         float c;
605     } s1_t;
606 
607     /* Allocate & initialize array */
608     names = (iter_info *)HDcalloc(sizeof(iter_info), (ITER_NGROUPS + 2));
609     CHECK_PTR(names, "HDcalloc");
610 
611     /* Output message about test being performed */
612     MESSAGE(5, ("Testing Large Group Iteration Functionality\n"));
613 
614     /* Create file */
615     file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
616     CHECK(file, FAIL, "H5Fcreate");
617 
618     /* Create dataspace for datasets */
619     sid = H5Screate_simple(SPACE1_RANK, dims, NULL);
620     CHECK(sid, FAIL, "H5Screate_simple");
621 
622     /* Create a bunch of groups */
623     for(i = 0; i < ITER_NGROUPS; i++) {
624         HDsprintf(gname, "Group_%d", i);
625 
626         /* Add the name to the list of objects in the root group */
627         HDstrcpy(names[i].name, gname);
628         names[i].type = H5O_TYPE_GROUP;
629 
630         /* Create a group */
631         group = H5Gcreate2(file, gname, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
632         CHECK(group, FAIL, "H5Gcreate2");
633 
634         /* Close a group */
635         ret = H5Gclose(group);
636         CHECK(ret, FAIL, "H5Gclose");
637     } /* end for */
638 
639     /* Create a dataset  */
640     dataset = H5Dcreate2(file, "Dataset1", H5T_STD_U32LE, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
641     CHECK(dataset, FAIL, "H5Dcreate2");
642 
643     /* Add the name to the list of objects in the root group */
644     HDstrcpy(names[ITER_NGROUPS].name, "Dataset1");
645     names[ITER_NGROUPS].type = H5O_TYPE_DATASET;
646 
647     /* Close Dataset */
648     ret = H5Dclose(dataset);
649     CHECK(ret, FAIL, "H5Dclose");
650 
651     /* Close Dataspace */
652     ret = H5Sclose(sid);
653     CHECK(ret, FAIL, "H5Sclose");
654 
655     /* Create a datatype */
656     tid = H5Tcreate(H5T_COMPOUND, sizeof(s1_t));
657     CHECK(tid, FAIL, "H5Tcreate");
658 
659     /* Insert fields */
660     ret = H5Tinsert(tid, "a", HOFFSET(s1_t, a), H5T_NATIVE_INT);
661     CHECK(ret, FAIL, "H5Tinsert");
662 
663     ret = H5Tinsert(tid, "b", HOFFSET(s1_t, b), H5T_NATIVE_INT);
664     CHECK(ret, FAIL, "H5Tinsert");
665 
666     ret = H5Tinsert(tid, "c", HOFFSET(s1_t, c), H5T_NATIVE_FLOAT);
667     CHECK(ret, FAIL, "H5Tinsert");
668 
669     /* Save datatype for later */
670     ret = H5Tcommit2(file, "Datatype1", tid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
671     CHECK(ret, FAIL, "H5Tcommit2");
672 
673     /* Add the name to the list of objects in the root group */
674     HDstrcpy(names[ITER_NGROUPS + 1].name, "Datatype1");
675     names[ITER_NGROUPS + 1].type = H5O_TYPE_NAMED_DATATYPE;
676 
677     /* Close datatype */
678     ret = H5Tclose(tid);
679     CHECK(ret, FAIL, "H5Tclose");
680 
681     /* Need to sort the names in the root group, cause that's what the library does */
682     HDqsort(names, (size_t)(ITER_NGROUPS + 2), sizeof(iter_info), iter_strcmp2);
683 
684     /* Iterate through the file to see members of the root group */
685     curr_name = &names[0];
686     ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, NULL, liter_cb2, curr_name);
687     CHECK(ret, FAIL, "H5Literate");
688     for(i = 1; i < 100; i++) {
689         hsize_t idx = (hsize_t)i;
690 
691         curr_name = &names[i];
692         ret = H5Literate(file, H5_INDEX_NAME, H5_ITER_INC, &idx, liter_cb2, curr_name);
693         CHECK(ret, FAIL, "H5Literate");
694     } /* end for */
695 
696     /* Close file */
697     ret = H5Fclose(file);
698     CHECK(ret, FAIL, "H5Fclose");
699 
700     /* Release memory */
701     HDfree(names);
702 } /* test_iterate_group_large() */
703 
704 /****************************************************************
705 **
706 **  test_grp_memb_funcs(): Test group member information
707 **                         functionality
708 **
709 ****************************************************************/
test_grp_memb_funcs(hid_t fapl)710 static void test_grp_memb_funcs(hid_t fapl)
711 {
712     hid_t file;             /* File ID */
713     hid_t dataset;          /* Dataset ID */
714     hid_t datatype;         /* Common datatype ID */
715     hid_t filespace;        /* Common dataspace ID */
716     hid_t root_group,grp;   /* Root group ID */
717     int i;                  /* counting variable */
718     char name[NAMELEN];     /* temporary name buffer */
719     char *dnames[NDATASETS+2];/* Names of the datasets created */
720     char *obj_names[NDATASETS+2];/* Names of the objects in group */
721     char dataset_name[NAMELEN];  /* dataset name */
722     ssize_t name_len;       /* Length of object's name */
723     H5G_info_t ginfo;       /* Buffer for querying object's info */
724     herr_t ret = SUCCEED;    /* Generic return value */
725 
726     /* Output message about test being performed */
727     MESSAGE(5, ("Testing Group Member Information Functionality\n"));
728 
729     /* Create the test file with the datasets */
730     file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
731     CHECK(file, FAIL, "H5Fcreate");
732 
733     datatype = H5Tcopy(H5T_NATIVE_INT);
734     CHECK(datatype, FAIL, "H5Tcopy");
735 
736     filespace = H5Screate(H5S_SCALAR);
737     CHECK(filespace, FAIL, "H5Screate");
738 
739     for(i = 0; i < NDATASETS; i++) {
740         HDsprintf(name, "Dataset %d", i);
741         dataset = H5Dcreate2(file, name, datatype, filespace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
742         CHECK(dataset, FAIL, "H5Dcreate2");
743 
744         /* Keep a copy of the dataset names around for later */
745         dnames[i] = HDstrdup(name);
746         CHECK_PTR(dnames[i], "strdup");
747 
748         ret = H5Dclose(dataset);
749         CHECK(ret, FAIL, "H5Dclose");
750     } /* end for */
751 
752     /* Create a group and named datatype under root group for testing */
753     grp = H5Gcreate2(file, "grp", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
754     CHECK(ret, FAIL, "H5Gcreate2");
755 
756     dnames[NDATASETS] = HDstrdup("grp");
757     CHECK_PTR(dnames[NDATASETS], "strdup");
758 
759     ret = H5Tcommit2(file, "dtype", datatype, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
760     CHECK(ret, FAIL, "H5Tcommit2");
761 
762     dnames[NDATASETS + 1] = HDstrdup("dtype");
763     CHECK_PTR(dnames[NDATASETS], "strdup");
764 
765     /* Close everything up */
766     ret = H5Tclose(datatype);
767     CHECK(ret, FAIL, "H5Tclose");
768 
769     ret = H5Gclose(grp);
770     CHECK(ret, FAIL, "H5Gclose");
771 
772     ret = H5Sclose(filespace);
773     CHECK(ret, FAIL, "H5Sclose");
774 
775     ret = H5Fclose(file);
776     CHECK(ret, FAIL, "H5Fclose");
777 
778     /* Sort the dataset names */
779     HDqsort(dnames, (size_t)(NDATASETS + 2), sizeof(char *), iter_strcmp);
780 
781     /* Iterate through the datasets in the root group in various ways */
782     file = H5Fopen(DATAFILE, H5F_ACC_RDONLY, fapl);
783     CHECK(file, FAIL, "H5Fopen");
784 
785     /* These two functions, H5Oget_info_by_idx and H5Lget_name_by_idx, actually
786      * iterate through B-tree for group members in internal library design.
787      */
788     root_group = H5Gopen2(file, "/", H5P_DEFAULT);
789     CHECK(root_group, FAIL, "H5Gopen2");
790 
791     ret = H5Gget_info(root_group, &ginfo);
792     CHECK(ret, FAIL, "H5Gget_info");
793     VERIFY(ginfo.nlinks, (NDATASETS + 2), "H5Gget_info");
794 
795     for(i = 0; i < (int)ginfo.nlinks; i++) {
796         H5O_info_t oinfo;               /* Object info */
797 
798         /* Test with NULL for name, to query length */
799         name_len = H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, NULL, (size_t)NAMELEN, H5P_DEFAULT);
800         CHECK(name_len, FAIL, "H5Lget_name_by_idx");
801 
802         ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, dataset_name, (size_t)(name_len + 1), H5P_DEFAULT);
803         CHECK(ret, FAIL, "H5Lget_name_by_idx");
804 
805         /* Double-check that the length is the same */
806         VERIFY(ret, name_len, "H5Lget_name_by_idx");
807 
808         /* Keep a copy of the dataset names around for later */
809         obj_names[i] = HDstrdup(dataset_name);
810         CHECK_PTR(obj_names[i], "strdup");
811 
812         ret = H5Oget_info_by_idx2(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT);
813         CHECK(ret, FAIL, "H5Oget_info_by_idx");
814 
815         if(!HDstrcmp(dataset_name, "grp"))
816             VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx");
817         if(!HDstrcmp(dataset_name, "dtype"))
818             VERIFY(oinfo.type, H5O_TYPE_NAMED_DATATYPE, "H5Lget_name_by_idx");
819         if(!HDstrncmp(dataset_name, "Dataset", (size_t)7))
820             VERIFY(oinfo.type, H5O_TYPE_DATASET, "H5Lget_name_by_idx");
821     } /* end for */
822 
823     H5E_BEGIN_TRY {
824         ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)(NDATASETS+3), dataset_name, (size_t)NAMELEN, H5P_DEFAULT);
825     } H5E_END_TRY;
826     VERIFY(ret, FAIL, "H5Lget_name_by_idx");
827 
828     /* Sort the dataset names */
829     HDqsort(obj_names, (size_t)(NDATASETS + 2), sizeof(char *), iter_strcmp);
830 
831     /* Compare object names */
832     for(i = 0; i< (int)ginfo.nlinks; i++) {
833         ret = HDstrcmp(dnames[i], obj_names[i]);
834         VERIFY(ret, 0, "HDstrcmp");
835     } /* end for */
836 
837     ret = H5Gclose(root_group);
838     CHECK(ret, FAIL, "H5Gclose");
839 
840 
841     ret = H5Fclose(file);
842     CHECK(ret, FAIL, "H5Fclose");
843 
844     /* Free the dataset names */
845     for(i = 0; i< (NDATASETS + 2); i++) {
846         HDfree(dnames[i]);
847         HDfree(obj_names[i]);
848     } /* end for */
849 } /* test_grp_memb_funcs() */
850 
851 /****************************************************************
852 **
853 **  test_links(): Test soft and hard link iteration
854 **
855 ****************************************************************/
test_links(hid_t fapl)856 static void test_links(hid_t fapl)
857 {
858     hid_t file;             /* File ID */
859     char obj_name[NAMELEN]; /* Names of the object in group */
860     ssize_t name_len;       /* Length of object's name */
861     hid_t    gid, gid1;
862     H5G_info_t ginfo;       /* Buffer for querying object's info */
863     hsize_t i;
864     herr_t ret;            /* Generic return value */
865 
866     /* Output message about test being performed */
867     MESSAGE(5, ("Testing Soft and Hard Link Iteration Functionality\n"));
868 
869     /* Create the test file with the datasets */
870     file = H5Fcreate(DATAFILE, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
871     CHECK(file, FAIL, "H5Fcreate");
872 
873     /* create groups */
874     gid = H5Gcreate2(file, "/g1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
875     CHECK(gid, FAIL, "H5Gcreate2");
876 
877     gid1 = H5Gcreate2(file, "/g1/g1.1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
878     CHECK(gid1, FAIL, "H5Gcreate2");
879 
880     /* create soft and hard links to the group "/g1". */
881     ret = H5Lcreate_soft("something", gid, "softlink", H5P_DEFAULT, H5P_DEFAULT);
882     CHECK(ret, FAIL, "H5Lcreate_soft");
883 
884     ret = H5Lcreate_hard(gid, "/g1", H5L_SAME_LOC, "hardlink", H5P_DEFAULT, H5P_DEFAULT);
885     CHECK(ret, FAIL, "H5Lcreate_hard");
886 
887     ret = H5Gget_info(gid, &ginfo);
888     CHECK(ret, FAIL, "H5Gget_info");
889     VERIFY(ginfo.nlinks, 3, "H5Gget_info");
890 
891     /* Test these two functions, H5Oget_info_by_idx and H5Lget_name_by_idx */
892     for(i = 0; i < ginfo.nlinks; i++) {
893         H5O_info_t oinfo;               /* Object info */
894         H5L_info_t linfo;               /* Link info */
895 
896         /* Get link name */
897         name_len = H5Lget_name_by_idx(gid, ".", H5_INDEX_NAME, H5_ITER_INC, i, obj_name, (size_t)NAMELEN, H5P_DEFAULT);
898         CHECK(name_len, FAIL, "H5Lget_name_by_idx");
899 
900         /* Get link type */
901         ret = H5Lget_info_by_idx(gid, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &linfo, H5P_DEFAULT);
902         CHECK(ret, FAIL, "H5Lget_info_by_idx");
903 
904         /* Get object type */
905         if(linfo.type == H5L_TYPE_HARD) {
906             ret = H5Oget_info_by_idx2(gid, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, &oinfo, H5O_INFO_BASIC, H5P_DEFAULT);
907             CHECK(ret, FAIL, "H5Oget_info_by_idx");
908         } /* end if */
909 
910         if(!HDstrcmp(obj_name, "g1.1"))
911             VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx");
912         else if(!HDstrcmp(obj_name, "hardlink"))
913             VERIFY(oinfo.type, H5O_TYPE_GROUP, "H5Lget_name_by_idx");
914         else if(!HDstrcmp(obj_name, "softlink"))
915             VERIFY(linfo.type, H5L_TYPE_SOFT, "H5Lget_name_by_idx");
916         else
917             CHECK(0, 0, "unknown object name");
918     } /* end for */
919 
920     ret = H5Gclose(gid);
921     CHECK(ret, FAIL, "H5Gclose");
922 
923     ret = H5Gclose(gid1);
924     CHECK(ret, FAIL, "H5Gclose");
925 
926     ret = H5Fclose(file);
927     CHECK(ret, FAIL, "H5Fclose");
928 } /* test_links() */
929 
930 /*-------------------------------------------------------------------------
931  * Function:    find_err_msg_cb
932  *
933  * Purpose:     Callback function to find the given error message.
934  *              Helper function for test_corrupted_attnamelen().
935  *
936  * Return:      H5_ITER_STOP when the message is found
937  *              H5_ITER_CONT, otherwise
938  *
939  *-------------------------------------------------------------------------
940  */
941 static int
find_err_msg_cb(unsigned n,const H5E_error2_t * err_desc,void * _client_data)942 find_err_msg_cb(unsigned n, const H5E_error2_t *err_desc, void *_client_data)
943 {
944     int status = H5_ITER_CONT;
945     searched_err_t *searched_err = (searched_err_t *)_client_data;
946 
947     if (searched_err == NULL)
948         return H5_ITER_ERROR;
949 
950     /* If the searched error message is found, stop the iteration */
951     if (err_desc->desc != NULL && strcmp(err_desc->desc, searched_err->message) == 0)
952     {
953         searched_err->found = true;
954         status = H5_ITER_STOP;
955     }
956 
957     return status;
958 } /* end find_err_msg_cb() */
959 
960 /**************************************************************************
961 **
962 **  test_corrupted_attnamelen(): Test the fix for the JIRA issue HDFFV-10588,
963 **                      where corrupted attribute's name length can be
964 **                      detected and invalid read can be avoided.
965 **
966 **************************************************************************/
test_corrupted_attnamelen(void)967 static void test_corrupted_attnamelen(void)
968 {
969     hid_t          fid = -1;            /* File ID */
970     hid_t          did = -1;            /* Dataset ID */
971     searched_err_t err_caught;          /* Data to be passed to callback func */
972     int            err_status;          /* Status returned by H5Aiterate2 */
973     herr_t         ret;                 /* Return value */
974     const char *testfile = H5_get_srcdir_filename(CORRUPTED_ATNAMELEN_FILE); /* Corrected test file name */
975 
976     const char *err_message = "attribute name has different length than stored length";
977                         /* the error message produced when the failure occurs */
978 
979     /* Output message about test being performed */
980     MESSAGE(5, ("Testing the Handling of Corrupted Attribute's Name Length\n"));
981 
982     fid = H5Fopen(testfile, H5F_ACC_RDONLY, H5P_DEFAULT);
983     CHECK(fid, FAIL, "H5Fopen");
984 
985     /* Open the dataset */
986     did = H5Dopen2(fid, DSET_NAME, H5P_DEFAULT);
987     CHECK(did, FAIL, "H5Dopen2");
988 
989     /* Call H5Aiterate2 to trigger the failure in HDFFV-10588.  Failure should
990        occur in the decoding stage, so some arguments are not needed. */
991     err_status = H5Aiterate2(did, H5_INDEX_NAME, H5_ITER_INC, NULL, NULL, NULL);
992     VERIFY(err_status, FAIL, "H5Aiterate2");
993 
994     /* Make sure the intended error was caught */
995     if(err_status == -1)
996     {
997         /* Initialize client data */
998         HDstrcpy(err_caught.message, err_message);
999         err_caught.found = false;
1000 
1001         /* Look for the correct error message */
1002         ret = H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, find_err_msg_cb, &err_caught);
1003         CHECK(ret, FAIL, "H5Ewalk2");
1004 
1005         /* Fail if the indicated message is not found */
1006         CHECK(err_caught.found, false, "test_corrupted_attnamelen: Expected error not found");
1007     }
1008 
1009     /* Close the dataset and file */
1010     ret = H5Dclose(did);
1011     CHECK(ret, FAIL, "H5Dclose");
1012 
1013     ret = H5Fclose(fid);
1014     CHECK(ret, FAIL, "H5Fclose");
1015 
1016 } /* test_corrupted_attnamelen() */
1017 
1018 /****************************************************************
1019 **
1020 **  test_iterate(): Main iteration testing routine.
1021 **
1022 ****************************************************************/
1023 void
test_iterate(void)1024 test_iterate(void)
1025 {
1026     hid_t fapl, fapl2;          /* File access property lists */
1027     unsigned new_format;        /* Whether to use the new format or not */
1028     herr_t ret;                /* Generic return value */
1029 
1030     /* Output message about test being performed */
1031     MESSAGE(5, ("Testing Iteration Operations\n"));
1032 
1033     /* Get the default FAPL */
1034     fapl = H5Pcreate(H5P_FILE_ACCESS);
1035     CHECK(fapl, FAIL, "H5Pcreate");
1036 
1037     /* Copy the file access property list */
1038     fapl2 = H5Pcopy(fapl);
1039     CHECK(fapl2, FAIL, "H5Pcopy");
1040 
1041     /* Set the "use the latest version of the format" bounds for creating objects in the file */
1042     ret = H5Pset_libver_bounds(fapl2, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST);
1043     CHECK(ret, FAIL, "H5Pset_libver_bounds");
1044 
1045     /* These next tests use the same file */
1046     for(new_format = FALSE; new_format <= TRUE; new_format++) {
1047         test_iter_group(new_format ? fapl2 : fapl, new_format); /* Test group iteration */
1048         test_iter_group_large(new_format ? fapl2 : fapl);   /* Test group iteration for large # of objects */
1049         test_iter_attr(new_format ? fapl2 : fapl, new_format);  /* Test attribute iteration */
1050         test_grp_memb_funcs(new_format ? fapl2 : fapl);     /* Test group member information functions */
1051         test_links(new_format ? fapl2 : fapl);              /* Test soft and hard link iteration */
1052     } /* end for */
1053 
1054     /* Test the fix for issue HDFFV-10588 */
1055     test_corrupted_attnamelen();
1056 
1057     /* Close FAPLs */
1058     ret = H5Pclose(fapl);
1059     CHECK(ret, FAIL, "H5Pclose");
1060     ret = H5Pclose(fapl2);
1061     CHECK(ret, FAIL, "H5Pclose");
1062 }   /* test_iterate() */
1063 
1064 
1065 /*-------------------------------------------------------------------------
1066  * Function:    cleanup_iterate
1067  *
1068  * Purpose:    Cleanup temporary test files
1069  *
1070  * Return:    none
1071  *
1072  * Programmer:    Quincey Koziol
1073  *              April 5, 2000
1074  *
1075  * Modifications:
1076  *
1077  *-------------------------------------------------------------------------
1078  */
1079 void
cleanup_iterate(void)1080 cleanup_iterate(void)
1081 {
1082     HDremove(DATAFILE);
1083 }
1084 
1085