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 #include "H5private.h"
15 #include "h5tools.h"
16 #include "h5tools_utils.h"
17 #include "h5diff.h"
18 #include "ph5diff.h"
19
20
21 /*-------------------------------------------------------------------------
22 * Function: print_objname
23 *
24 * Purpose: check if object name is to be printed, only when:
25 * 1) verbose mode
26 * 2) when diff was found (normal mode)
27 *-------------------------------------------------------------------------
28 */
29 H5_ATTR_PURE int
print_objname(diff_opt_t * opts,hsize_t nfound)30 print_objname (diff_opt_t * opts, hsize_t nfound)
31 {
32 return ((opts->m_verbose || nfound) && !opts->m_quiet) ? 1 : 0;
33 }
34
35 /*-------------------------------------------------------------------------
36 * Function: do_print_objname
37 *
38 * Purpose: print object name
39 *-------------------------------------------------------------------------
40 */
41 void
do_print_objname(const char * OBJ,const char * path1,const char * path2,diff_opt_t * opts)42 do_print_objname (const char *OBJ, const char *path1, const char *path2, diff_opt_t * opts)
43 {
44 /* if verbose level is higher than 0, put space line before
45 * displaying any object or symbolic links. This improves
46 * readability of the output.
47 */
48 if (opts->m_verbose_level >= 1)
49 parallel_print("\n");
50 parallel_print("%-7s: <%s> and <%s>\n", OBJ, path1, path2);
51 }
52
53 /*-------------------------------------------------------------------------
54 * Function: do_print_attrname
55 *
56 * Purpose: print attribute name
57 *-------------------------------------------------------------------------
58 */
59 void
do_print_attrname(const char * attr,const char * path1,const char * path2)60 do_print_attrname (const char *attr, const char *path1, const char *path2)
61 {
62 parallel_print("%-7s: <%s> and <%s>\n", attr, path1, path2);
63 }
64
65 /*-------------------------------------------------------------------------
66 * Function: print_warn
67 *
68 * Purpose: check print warning condition.
69 * Return:
70 * 1 if verbose mode
71 * 0 if not verbos mode
72 *-------------------------------------------------------------------------
73 */
74 static int
print_warn(diff_opt_t * opts)75 print_warn(diff_opt_t *opts)
76 {
77 return ((opts->m_verbose)) ? 1: 0;
78 }
79
80
81 #ifdef H5_HAVE_PARALLEL
82 /*-------------------------------------------------------------------------
83 * Function: phdiff_dismiss_workers
84 *
85 * Purpose: tell all workers to end.
86 *
87 * Return: none
88 *-------------------------------------------------------------------------
89 */
90 void
phdiff_dismiss_workers(void)91 phdiff_dismiss_workers(void)
92 {
93 int i;
94 for (i = 1; i < g_nTasks; i++)
95 MPI_Send(NULL, 0, MPI_BYTE, i, MPI_TAG_END, MPI_COMM_WORLD);
96 }
97
98
99 /*-------------------------------------------------------------------------
100 * Function: print_incoming_data
101 *
102 * Purpose: special function that prints any output that has been sent to the manager
103 * and is currently sitting in the incoming message queue
104 *
105 * Return: none
106 *-------------------------------------------------------------------------
107 */
108
109 static void
print_incoming_data(void)110 print_incoming_data(void)
111 {
112 char data[PRINT_DATA_MAX_SIZE + 1];
113 int incomingMessage;
114 MPI_Status Status;
115
116 do {
117 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_PRINT_DATA, MPI_COMM_WORLD, &incomingMessage, &Status);
118 if(incomingMessage) {
119 HDmemset(data, 0, PRINT_DATA_MAX_SIZE+1);
120 MPI_Recv(data, PRINT_DATA_MAX_SIZE, MPI_CHAR, Status.MPI_SOURCE, MPI_TAG_PRINT_DATA, MPI_COMM_WORLD, &Status);
121
122 HDprintf("%s", data);
123 }
124 } while(incomingMessage);
125 }
126 #endif
127
128 /*-------------------------------------------------------------------------
129 * Function: is_valid_options
130 *
131 * Purpose: check if options are valid
132 *
133 * Return:
134 * 1 : Valid
135 * 0 : Not valid
136 *------------------------------------------------------------------------*/
137 static int
is_valid_options(diff_opt_t * opts)138 is_valid_options(diff_opt_t *opts)
139 {
140 int ret_value = 1; /* init to valid */
141
142 /*-----------------------------------------------
143 * no -q(quiet) with -v (verbose) or -r (report) */
144 if(opts->m_quiet && (opts->m_verbose || opts->m_report)) {
145 parallel_print("Error: -q (quiet mode) cannot be added to verbose or report modes\n");
146 opts->err_stat = 1;
147 HGOTO_DONE(0);
148 }
149
150 /* -------------------------------------------------------
151 * only allow --no-dangling-links along with --follow-symlinks */
152 if(opts->no_dangle_links && !opts->follow_links) {
153 parallel_print("Error: --no-dangling-links must be used along with --follow-symlinks option.\n");
154 opts->err_stat = 1;
155 HGOTO_DONE(0);
156 }
157
158 done:
159
160 return ret_value;
161 }
162
163 /*-------------------------------------------------------------------------
164 * Function: is_exclude_path
165 *
166 * Purpose: check if 'paths' are part of exclude path list
167 *
168 * Return:
169 * 1 - excluded path
170 * 0 - not excluded path
171 *------------------------------------------------------------------------*/
172 static int
is_exclude_path(char * path,h5trav_type_t type,diff_opt_t * opts)173 is_exclude_path (char * path, h5trav_type_t type, diff_opt_t *opts)
174 {
175 struct exclude_path_list * exclude_path_ptr;
176 int ret_cmp;
177 int ret_value = 0;
178
179 /* check if exclude path option is given */
180 if (!opts->exclude_path)
181 HGOTO_DONE(0);
182
183 /* assign to local exclude list pointer */
184 exclude_path_ptr = opts->exclude;
185
186 /* search objects in exclude list */
187 while (NULL != exclude_path_ptr) {
188 /* if exclude path is is group, exclude its members as well */
189 if (exclude_path_ptr->obj_type == H5TRAV_TYPE_GROUP) {
190 ret_cmp = HDstrncmp(exclude_path_ptr->obj_path, path,
191 HDstrlen(exclude_path_ptr->obj_path));
192 if (ret_cmp == 0) { /* found matching members */
193 size_t len_grp;
194
195 /* check if given path belong to an excluding group, if so
196 * exclude it as well.
197 * This verifies if “/grp1/dset1” is only under “/grp1”, but
198 * not under “/grp1xxx/” group.
199 */
200 len_grp = HDstrlen(exclude_path_ptr->obj_path);
201 if (path[len_grp] == '/') {
202 /* belong to excluded group! */
203 ret_value = 1;
204 break; /* while */
205 }
206 }
207 }
208 /* exclude target is not group, just exclude the object */
209 else {
210 ret_cmp = HDstrcmp(exclude_path_ptr->obj_path, path);
211 if (ret_cmp == 0) { /* found matching object */
212 /* excluded non-group object */
213 ret_value = 1;
214 /* remember the type of this maching object.
215 * if it's group, it can be used for excluding its member
216 * objects in this while() loop */
217 exclude_path_ptr->obj_type = type;
218 break; /* while */
219 }
220 }
221 exclude_path_ptr = exclude_path_ptr->next;
222 }
223
224 done:
225 return ret_value;
226 }
227
228
229 /*-------------------------------------------------------------------------
230 * Function: free_exclude_path_list
231 *
232 * Purpose: free exclude object list from diff options
233 *------------------------------------------------------------------------*/
234 static void
free_exclude_path_list(diff_opt_t * opts)235 free_exclude_path_list(diff_opt_t *opts)
236 {
237 struct exclude_path_list *curr = opts->exclude;
238 struct exclude_path_list *next;
239
240 while (NULL != curr) {
241 next = curr->next;
242 HDfree(curr);
243 curr = next;
244 }
245 }
246
247 /*-------------------------------------------------------------------------
248 * Function: build_match_list
249 *
250 * Purpose: get list of matching path_name from info1 and info2
251 *
252 * Note:
253 * Find common objects; the algorithm used for this search is the
254 * cosequential match algorithm and is described in
255 * Folk, Michael; Zoellick, Bill. (1992). File Structures. Addison-Wesley.
256 * Moved out from diff_match() to make code more flexible.
257 *
258 * Parameter:
259 * table_out [OUT] : return the list
260 *------------------------------------------------------------------------*/
261 static void
build_match_list(const char * objname1,trav_info_t * info1,const char * objname2,trav_info_t * info2,trav_table_t ** table_out,diff_opt_t * opts)262 build_match_list (const char *objname1, trav_info_t *info1, const char *objname2, trav_info_t *info2,
263 trav_table_t ** table_out, diff_opt_t *opts)
264 {
265 size_t curr1 = 0;
266 size_t curr2 = 0;
267 unsigned infile[2];
268 char *path1_lp = NULL;
269 char *path2_lp = NULL;
270 h5trav_type_t type1_l;
271 h5trav_type_t type2_l;
272 size_t path1_offset = 0;
273 size_t path2_offset = 0;
274 int cmp;
275 trav_table_t *table = NULL;
276 size_t idx;
277 int ret_value = 0;
278
279 h5difftrace("build_match_list start\n");
280 /* init */
281 trav_table_init(&table);
282 if (table == NULL) {
283 H5TOOLS_INFO(H5E_tools_min_id_g, "Cannot create traverse table");
284 HGOTO_DONE(-1);
285 }
286 /*
287 * This is necessary for the case that given objects are group and
288 * have different names (ex: obj1 is /grp1 and obj2 is /grp5).
289 * All the objects belong to given groups are the candidates.
290 * So prepare to compare paths without the group names.
291 */
292
293 /* if obj1 is not root */
294 if (HDstrcmp (objname1,"/") != 0)
295 path1_offset = HDstrlen(objname1);
296 /* if obj2 is not root */
297 if (HDstrcmp (objname2,"/") != 0)
298 path2_offset = HDstrlen(objname2);
299
300 /*--------------------------------------------------
301 * build the list
302 */
303 while(curr1 < info1->nused && curr2 < info2->nused) {
304 path1_lp = (info1->paths[curr1].path) + path1_offset;
305 path2_lp = (info2->paths[curr2].path) + path2_offset;
306 type1_l = info1->paths[curr1].type;
307 type2_l = info2->paths[curr2].type;
308
309 /* criteria is string compare */
310 cmp = HDstrcmp(path1_lp, path2_lp);
311 if(cmp == 0) {
312 if(!is_exclude_path(path1_lp, type1_l, opts)) {
313 infile[0] = 1;
314 infile[1] = 1;
315 trav_table_addflags(infile, path1_lp, info1->paths[curr1].type, table);
316 /* if the two point to the same target object,
317 * mark that in table */
318 if (info1->paths[curr1].fileno == info2->paths[curr2].fileno &&
319 info1->paths[curr1].objno == info2->paths[curr2].objno) {
320 idx = table->nobjs - 1;
321 table->objs[idx].is_same_trgobj = 1;
322 }
323 }
324 curr1++;
325 curr2++;
326 } /* end if */
327 else if(cmp < 0) {
328 if(!is_exclude_path(path1_lp, type1_l, opts)) {
329 infile[0] = 1;
330 infile[1] = 0;
331 trav_table_addflags(infile, path1_lp, info1->paths[curr1].type, table);
332 }
333 curr1++;
334 } /* end else-if */
335 else {
336 if (!is_exclude_path(path2_lp, type2_l, opts)) {
337 infile[0] = 0;
338 infile[1] = 1;
339 trav_table_addflags(infile, path2_lp, info2->paths[curr2].type, table);
340 }
341 curr2++;
342 } /* end else */
343 } /* end while */
344
345 /* list1 did not end */
346 infile[0] = 1;
347 infile[1] = 0;
348 while(curr1 < info1->nused) {
349 path1_lp = (info1->paths[curr1].path) + path1_offset;
350 type1_l = info1->paths[curr1].type;
351
352 if(!is_exclude_path(path1_lp, type1_l, opts)) {
353 trav_table_addflags(infile, path1_lp, info1->paths[curr1].type, table);
354 }
355 curr1++;
356 } /* end while */
357
358 /* list2 did not end */
359 infile[0] = 0;
360 infile[1] = 1;
361 while(curr2 < info2->nused) {
362 path2_lp = (info2->paths[curr2].path) + path2_offset;
363 type2_l = info2->paths[curr2].type;
364
365 if (!is_exclude_path(path2_lp, type2_l, opts)) {
366 trav_table_addflags(infile, path2_lp, info2->paths[curr2].type, table);
367 }
368 curr2++;
369 } /* end while */
370
371 free_exclude_path_list (opts);
372
373 done:
374 *table_out = table;
375 h5difftrace("build_match_list finish\n");
376 }
377
378
379 /*-------------------------------------------------------------------------
380 * Function: trav_grp_objs
381 *
382 * Purpose: Call back function from h5trav_visit().
383 *------------------------------------------------------------------------*/
384 static herr_t
trav_grp_objs(const char * path,const H5O_info_t * oinfo,const char * already_visited,void * udata)385 trav_grp_objs(const char *path, const H5O_info_t *oinfo,
386 const char *already_visited, void *udata)
387 {
388 trav_info_visit_obj(path, oinfo, already_visited, udata);
389
390 return 0;
391 }
392
393 /*-------------------------------------------------------------------------
394 * Function: trav_grp_symlinks
395 *
396 * Purpose: Call back function from h5trav_visit().
397 * Track and extra checkings while visiting all symbolic-links.
398 *------------------------------------------------------------------------*/
399 static herr_t
trav_grp_symlinks(const char * path,const H5L_info_t * linfo,void * udata)400 trav_grp_symlinks(const char *path, const H5L_info_t *linfo, void *udata)
401 {
402 herr_t ret_value = 0;
403 trav_info_t *tinfo = (trav_info_t *)udata;
404 diff_opt_t *opts = (diff_opt_t *)tinfo->opts;
405 h5tool_link_info_t lnk_info;
406 const char *ext_fname;
407 const char *ext_path;
408
409 /* init linkinfo struct */
410 HDmemset(&lnk_info, 0, sizeof(h5tool_link_info_t));
411
412 if (!opts->follow_links) {
413 trav_info_visit_lnk(path, linfo, tinfo);
414 HGOTO_DONE(0);
415 }
416
417 switch(linfo->type) {
418 case H5L_TYPE_SOFT:
419 if((ret_value = H5tools_get_symlink_info(tinfo->fid, path, &lnk_info, opts->follow_links)) < 0) {
420 HGOTO_DONE(FAIL);
421 }
422 else if (ret_value == 0) {
423 /* no dangling link option given and detect dangling link */
424 tinfo->symlink_visited.dangle_link = TRUE;
425 trav_info_visit_lnk(path, linfo, tinfo);
426 if (opts->no_dangle_links)
427 opts->err_stat = 1; /* make dangling link is error */
428 HGOTO_DONE(0);
429 }
430
431 /* check if already visit the target object */
432 if(symlink_is_visited( &(tinfo->symlink_visited), linfo->type, NULL, lnk_info.trg_path))
433 HGOTO_DONE(0);
434
435 /* add this link as visited link */
436 if(symlink_visit_add( &(tinfo->symlink_visited), linfo->type, NULL, lnk_info.trg_path) < 0)
437 HGOTO_DONE(0);
438
439 if(h5trav_visit(tinfo->fid, path, TRUE, TRUE,
440 trav_grp_objs,trav_grp_symlinks, tinfo, H5O_INFO_BASIC) < 0) {
441 parallel_print("Error: Could not get file contents\n");
442 opts->err_stat = 1;
443 HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "Error: Could not get file contents");
444 }
445 break;
446
447 case H5L_TYPE_EXTERNAL:
448 if ((ret_value = H5tools_get_symlink_info(tinfo->fid, path, &lnk_info, opts->follow_links)) < 0) {
449 HGOTO_DONE(FAIL);
450 }
451 else if (ret_value == 0) {
452 /* no dangling link option given and detect dangling link */
453 tinfo->symlink_visited.dangle_link = TRUE;
454 trav_info_visit_lnk(path, linfo, tinfo);
455 if (opts->no_dangle_links)
456 opts->err_stat = 1; /* make dangling link is error */
457 HGOTO_DONE(0);
458 }
459
460 if(H5Lunpack_elink_val(lnk_info.trg_path, linfo->u.val_size, NULL, &ext_fname, &ext_path) < 0)
461 HGOTO_DONE(0);
462
463 /* check if already visit the target object */
464 if(symlink_is_visited( &(tinfo->symlink_visited), linfo->type, ext_fname, ext_path))
465 HGOTO_DONE(0);
466
467 /* add this link as visited link */
468 if(symlink_visit_add( &(tinfo->symlink_visited), linfo->type, ext_fname, ext_path) < 0)
469 HGOTO_DONE(0);
470
471 if(h5trav_visit(tinfo->fid, path, TRUE, TRUE,
472 trav_grp_objs,trav_grp_symlinks, tinfo, H5O_INFO_BASIC) < 0) {
473 parallel_print("Error: Could not get file contents\n");
474 opts->err_stat = 1;
475 HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "Error: Could not get file contents\n");
476 }
477 break;
478
479 case H5L_TYPE_HARD:
480 case H5L_TYPE_MAX:
481 case H5L_TYPE_ERROR:
482 default:
483 parallel_print("Error: Invalid link type\n");
484 opts->err_stat = 1;
485 HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "Error: Invalid link type");
486 break;
487 } /* end of switch */
488
489 done:
490 if (lnk_info.trg_path)
491 HDfree(lnk_info.trg_path);
492 return ret_value;
493 }
494
495
496 /*-------------------------------------------------------------------------
497 * Function: h5diff
498 *
499 * Purpose: public function, can be called in an application program.
500 * return differences between 2 HDF5 files
501 *
502 * Return: Number of differences found.
503 *-------------------------------------------------------------------------
504 */
505 hsize_t
h5diff(const char * fname1,const char * fname2,const char * objname1,const char * objname2,diff_opt_t * opts)506 h5diff(const char *fname1,
507 const char *fname2,
508 const char *objname1,
509 const char *objname2,
510 diff_opt_t *opts)
511 {
512 int ret_value = 0;
513 hid_t file1_id = -1;
514 hid_t file2_id = -1;
515 char filenames[2][MAX_FILENAME];
516 hsize_t nfound = 0;
517 int l_ret1 = -1;
518 int l_ret2 = -1;
519 char *obj1fullname = NULL;
520 char *obj2fullname = NULL;
521 int both_objs_grp = 0;
522 /* init to group type */
523 h5trav_type_t obj1type = H5TRAV_TYPE_GROUP;
524 h5trav_type_t obj2type = H5TRAV_TYPE_GROUP;
525 /* for single object */
526 H5O_info_t oinfo1, oinfo2; /* object info */
527 trav_info_t *info1_obj = NULL;
528 trav_info_t *info2_obj = NULL;
529 /* for group object */
530 trav_info_t *info1_grp = NULL;
531 trav_info_t *info2_grp = NULL;
532 /* local pointer */
533 trav_info_t *info1_lp = NULL;
534 trav_info_t *info2_lp = NULL;
535 /* link info from specified object */
536 H5L_info_t src_linfo1;
537 H5L_info_t src_linfo2;
538 /* link info from member object */
539 h5tool_link_info_t trg_linfo1;
540 h5tool_link_info_t trg_linfo2;
541 /* list for common objects */
542 trav_table_t *match_list = NULL;
543
544 h5difftrace("h5diff start\n");
545 /* init filenames */
546 HDmemset(filenames, 0, MAX_FILENAME * 2);
547 /* init link info struct */
548 HDmemset(&trg_linfo1, 0, sizeof(h5tool_link_info_t));
549 HDmemset(&trg_linfo2, 0, sizeof(h5tool_link_info_t));
550
551 /*-------------------------------------------------------------------------
552 * check invalid combination of options
553 *-----------------------------------------------------------------------*/
554 if(!is_valid_options(opts))
555 HGOTO_DONE(0);
556
557 opts->cmn_objs = 1; /* eliminate warning */
558 opts->err_stat = 0; /* initialize error status */
559
560 /*-------------------------------------------------------------------------
561 * open the files first; if they are not valid, no point in continuing
562 *-------------------------------------------------------------------------
563 */
564 /* open file 1 */
565 if((file1_id = h5tools_fopen(fname1, H5F_ACC_RDONLY, H5P_DEFAULT, NULL, NULL, (size_t)0)) < 0) {
566 parallel_print("h5diff: <%s>: unable to open file\n", fname1);
567 HGOTO_ERROR(1, H5E_tools_min_id_g, "<%s>: unable to open file\n", fname1);
568 } /* end if */
569
570
571 /* open file 2 */
572 if((file2_id = h5tools_fopen(fname2, H5F_ACC_RDONLY, H5P_DEFAULT, NULL, NULL, (size_t)0)) < 0) {
573 parallel_print("h5diff: <%s>: unable to open file\n", fname2);
574 HGOTO_ERROR(1, H5E_tools_min_id_g, "<%s>: unable to open file\n", fname2);
575 } /* end if */
576
577 /*-------------------------------------------------------------------------
578 * Initialize the info structs
579 *-------------------------------------------------------------------------
580 */
581 trav_info_init(fname1, file1_id, &info1_obj);
582 trav_info_init(fname2, file2_id, &info2_obj);
583
584 h5difftrace("trav_info_init initialized\n");
585 /* if any object is specified */
586 if (objname1) {
587 /* make the given object1 fullpath, start with "/" */
588 if (HDstrncmp(objname1, "/", 1)) {
589 #ifdef H5_HAVE_ASPRINTF
590 /* Use the asprintf() routine, since it does what we're trying to do below */
591 if(HDasprintf(&obj1fullname, "/%s", objname1) < 0)
592 HGOTO_ERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
593 #else /* H5_HAVE_ASPRINTF */
594 /* (malloc 2 more for "/" and end-of-line) */
595 if ((obj1fullname = (char*)HDmalloc(HDstrlen(objname1) + 2)) == NULL)
596 HGOTO_ERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
597
598 HDstrcpy(obj1fullname, "/");
599 HDstrcat(obj1fullname, objname1);
600 #endif /* H5_HAVE_ASPRINTF */
601 }
602 else
603 obj1fullname = HDstrdup(objname1);
604
605 /* make the given object2 fullpath, start with "/" */
606 if (HDstrncmp(objname2, "/", 1)) {
607 #ifdef H5_HAVE_ASPRINTF
608 /* Use the asprintf() routine, since it does what we're trying to do below */
609 if(HDasprintf(&obj2fullname, "/%s", objname2) < 0)
610 HGOTO_ERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
611 #else /* H5_HAVE_ASPRINTF */
612 /* (malloc 2 more for "/" and end-of-line) */
613 if ((obj2fullname = (char*)HDmalloc(HDstrlen(objname2) + 2)) == NULL)
614 HGOTO_ERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
615 HDstrcpy(obj2fullname, "/");
616 HDstrcat(obj2fullname, objname2);
617 #endif /* H5_HAVE_ASPRINTF */
618 }
619 else
620 obj2fullname = HDstrdup(objname2);
621
622 /*----------------------------------------------------------
623 * check if obj1 is root, group, single object or symlink
624 */
625 h5difftrace("h5diff check if obj1 is root, group, single object or symlink\n");
626 if(!HDstrcmp(obj1fullname, "/")) {
627 obj1type = H5TRAV_TYPE_GROUP;
628 }
629 else {
630 /* check if link itself exist */
631 if(H5Lexists(file1_id, obj1fullname, H5P_DEFAULT) <= 0) {
632 parallel_print ("Object <%s> could not be found in <%s>\n", obj1fullname, fname1);
633 HGOTO_ERROR(1, H5E_tools_min_id_g, "Error: Object could not be found");
634 }
635 /* get info from link */
636 if(H5Lget_info(file1_id, obj1fullname, &src_linfo1, H5P_DEFAULT) < 0) {
637 parallel_print("Unable to get link info from <%s>\n", obj1fullname);
638 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Lget_info failed");
639 }
640
641 info1_lp = info1_obj;
642
643 /*
644 * check the type of specified path for hard and symbolic links
645 */
646 if(src_linfo1.type == H5L_TYPE_HARD) {
647 size_t idx;
648
649 /* optional data pass */
650 info1_obj->opts = (diff_opt_t*)opts;
651
652 if(H5Oget_info_by_name2(file1_id, obj1fullname, &oinfo1, H5O_INFO_BASIC, H5P_DEFAULT) < 0) {
653 parallel_print("Error: Could not get file contents\n");
654 HGOTO_ERROR(1, H5E_tools_min_id_g, "Error: Could not get file contents");
655 }
656 obj1type = (h5trav_type_t)oinfo1.type;
657 trav_info_add(info1_obj, obj1fullname, obj1type);
658 idx = info1_obj->nused - 1;
659 info1_obj->paths[idx].objno = oinfo1.addr;
660 info1_obj->paths[idx].fileno = oinfo1.fileno;
661 }
662 else if (src_linfo1.type == H5L_TYPE_SOFT) {
663 obj1type = H5TRAV_TYPE_LINK;
664 trav_info_add(info1_obj, obj1fullname, obj1type);
665 }
666 else if (src_linfo1.type == H5L_TYPE_EXTERNAL) {
667 obj1type = H5TRAV_TYPE_UDLINK;
668 trav_info_add(info1_obj, obj1fullname, obj1type);
669 }
670 }
671
672 /*----------------------------------------------------------
673 * check if obj2 is root, group, single object or symlink
674 */
675 h5difftrace("h5diff check if obj2 is root, group, single object or symlink\n");
676 if(!HDstrcmp(obj2fullname, "/")) {
677 obj2type = H5TRAV_TYPE_GROUP;
678 }
679 else {
680 /* check if link itself exist */
681 if(H5Lexists(file2_id, obj2fullname, H5P_DEFAULT) <= 0) {
682 parallel_print ("Object <%s> could not be found in <%s>\n", obj2fullname, fname2);
683 HGOTO_ERROR(1, H5E_tools_min_id_g, "Error: Object could not be found");
684 }
685 /* get info from link */
686 if(H5Lget_info(file2_id, obj2fullname, &src_linfo2, H5P_DEFAULT) < 0) {
687 parallel_print("Unable to get link info from <%s>\n", obj2fullname);
688 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Lget_info failed");
689 }
690
691 info2_lp = info2_obj;
692
693 /*
694 * check the type of specified path for hard and symbolic links
695 */
696 if(src_linfo2.type == H5L_TYPE_HARD) {
697 size_t idx;
698
699 /* optional data pass */
700 info2_obj->opts = (diff_opt_t*)opts;
701
702 if(H5Oget_info_by_name2(file2_id, obj2fullname, &oinfo2, H5O_INFO_BASIC, H5P_DEFAULT) < 0) {
703 parallel_print("Error: Could not get file contents\n");
704 HGOTO_ERROR(1, H5E_tools_min_id_g, "Error: Could not get file contents");
705 }
706 obj2type = (h5trav_type_t)oinfo2.type;
707 trav_info_add(info2_obj, obj2fullname, obj2type);
708 idx = info2_obj->nused - 1;
709 info2_obj->paths[idx].objno = oinfo2.addr;
710 info2_obj->paths[idx].fileno = oinfo2.fileno;
711 }
712 else if (src_linfo2.type == H5L_TYPE_SOFT) {
713 obj2type = H5TRAV_TYPE_LINK;
714 trav_info_add(info2_obj, obj2fullname, obj2type);
715 }
716 else if (src_linfo2.type == H5L_TYPE_EXTERNAL) {
717 obj2type = H5TRAV_TYPE_UDLINK;
718 trav_info_add(info2_obj, obj2fullname, obj2type);
719 }
720 }
721 }
722 /* if no object specified */
723 else {
724 h5difftrace("h5diff no object specified\n");
725 /* set root group */
726 obj1fullname = (char*)HDstrdup("/");
727 obj1type = H5TRAV_TYPE_GROUP;
728 obj2fullname = (char*)HDstrdup("/");
729 obj2type = H5TRAV_TYPE_GROUP;
730 }
731
732 h5diffdebug2("get any symbolic links info - errstat:%d\n", opts->err_stat);
733 /* get any symbolic links info */
734 l_ret1 = H5tools_get_symlink_info(file1_id, obj1fullname, &trg_linfo1, opts->follow_links);
735 l_ret2 = H5tools_get_symlink_info(file2_id, obj2fullname, &trg_linfo2, opts->follow_links);
736
737 /*---------------------------------------------
738 * check for following symlinks
739 */
740 if (opts->follow_links) {
741 /* pass how to handle printing warning to linkinfo option */
742 if(print_warn(opts))
743 trg_linfo1.opt.msg_mode = trg_linfo2.opt.msg_mode = 1;
744
745 /*-------------------------------
746 * check symbolic link (object1)
747 */
748 h5difftrace("h5diff check symbolic link (object1)\n");
749 /* dangling link */
750 if (l_ret1 == 0) {
751 h5difftrace("h5diff ... dangling link\n");
752 if (opts->no_dangle_links) {
753 /* treat dangling link as error */
754 if(opts->m_verbose)
755 parallel_print("Warning: <%s> is a dangling link.\n", obj1fullname);
756 HGOTO_ERROR(1, H5E_tools_min_id_g, "treat dangling link as error");
757 }
758 else {
759 if(opts->m_verbose)
760 parallel_print("obj1 <%s> is a dangling link.\n", obj1fullname);
761 if (l_ret1 != 0 || l_ret2 != 0) {
762 nfound++;
763 print_found(nfound);
764 HGOTO_DONE(0);
765 }
766 }
767 }
768 else if(l_ret1 < 0) { /* fail */
769 parallel_print ("Object <%s> could not be found in <%s>\n", obj1fullname, fname1);
770 HGOTO_ERROR(1, H5E_tools_min_id_g, "Object could not be found");
771 }
772 else if(l_ret1 != 2) { /* symbolic link */
773 obj1type = (h5trav_type_t)trg_linfo1.trg_type;
774 h5difftrace("h5diff ... ... trg_linfo1.trg_type == H5L_TYPE_HARD\n");
775 if (info1_lp != NULL) {
776 size_t idx = info1_lp->nused - 1;
777
778 h5difftrace("h5diff ... ... ... info1_obj not null\n");
779 info1_lp->paths[idx].type = (h5trav_type_t)trg_linfo1.trg_type;
780 info1_lp->paths[idx].objno = trg_linfo1.objno;
781 info1_lp->paths[idx].fileno = trg_linfo1.fileno;
782 }
783 h5difftrace("h5diff check symbolic link (object1) finished\n");
784 }
785
786 /*-------------------------------
787 * check symbolic link (object2)
788 */
789 h5difftrace("h5diff check symbolic link (object2)\n");
790 /* dangling link */
791 if (l_ret2 == 0) {
792 h5difftrace("h5diff ... dangling link\n");
793 if (opts->no_dangle_links) {
794 /* treat dangling link as error */
795 if(opts->m_verbose)
796 parallel_print("Warning: <%s> is a dangling link.\n", obj2fullname);
797 HGOTO_ERROR(1, H5E_tools_min_id_g, "treat dangling link as error");
798 }
799 else {
800 if(opts->m_verbose)
801 parallel_print("obj2 <%s> is a dangling link.\n", obj2fullname);
802 if (l_ret1 != 0 || l_ret2 != 0) {
803 nfound++;
804 print_found(nfound);
805 HGOTO_DONE(0);
806 }
807 }
808 }
809 else if(l_ret2 < 0) { /* fail */
810 parallel_print ("Object <%s> could not be found in <%s>\n", obj2fullname, fname2);
811 HGOTO_ERROR(1, H5E_tools_min_id_g, "Object could not be found");
812 }
813 else if(l_ret2 != 2) { /* symbolic link */
814 obj2type = (h5trav_type_t)trg_linfo2.trg_type;
815 if (info2_lp != NULL) {
816 size_t idx = info2_lp->nused - 1;
817
818 h5difftrace("h5diff ... ... ... info2_obj not null\n");
819 info2_lp->paths[idx].type = (h5trav_type_t)trg_linfo2.trg_type;
820 info2_lp->paths[idx].objno = trg_linfo2.objno;
821 info2_lp->paths[idx].fileno = trg_linfo2.fileno;
822 }
823 h5difftrace("h5diff check symbolic link (object1) finished\n");
824 }
825 } /* end of if follow symlinks */
826
827 /*
828 * If verbose options is not used, don't need to traverse through the list
829 * of objects in the group to display objects information,
830 * So use h5tools_is_obj_same() to improve performance by skipping
831 * comparing details of same objects.
832 */
833
834 if(!(opts->m_verbose || opts->m_report)) {
835 h5difftrace("h5diff NOT (opts->m_verbose || opts->m_report)\n");
836 /* if no danglink links */
837 if (l_ret1 > 0 && l_ret2 > 0)
838 if (h5tools_is_obj_same(file1_id, obj1fullname, file2_id, obj2fullname) != 0)
839 HGOTO_DONE(0);
840 }
841
842 both_objs_grp = (obj1type == H5TRAV_TYPE_GROUP && obj2type == H5TRAV_TYPE_GROUP);
843 if (both_objs_grp) {
844 h5difftrace("h5diff both_objs_grp TRUE\n");
845 /*
846 * traverse group1
847 */
848 trav_info_init(fname1, file1_id, &info1_grp);
849 /* optional data pass */
850 info1_grp->opts = (diff_opt_t*)opts;
851
852 if(h5trav_visit(file1_id, obj1fullname, TRUE, TRUE,
853 trav_grp_objs, trav_grp_symlinks, info1_grp, H5O_INFO_BASIC) < 0) {
854 parallel_print("Error: Could not get file contents\n");
855 HGOTO_ERROR(1, H5E_tools_min_id_g, "Could not get file contents");
856 }
857 info1_lp = info1_grp;
858
859 /*
860 * traverse group2
861 */
862 trav_info_init(fname2, file2_id, &info2_grp);
863 /* optional data pass */
864 info2_grp->opts = (diff_opt_t*)opts;
865
866 if(h5trav_visit(file2_id, obj2fullname, TRUE, TRUE,
867 trav_grp_objs, trav_grp_symlinks, info2_grp, H5O_INFO_BASIC) < 0) {
868 parallel_print("Error: Could not get file contents\n");
869 HGOTO_ERROR(1, H5E_tools_min_id_g, "Could not get file contents");
870 } /* end if */
871 info2_lp = info2_grp;
872 }
873 h5diffdebug2("groups traversed - errstat:%d\n", opts->err_stat);
874
875 #ifdef H5_HAVE_PARALLEL
876 if(g_Parallel) {
877 int i;
878
879 if((HDstrlen(fname1) > MAX_FILENAME) || (HDstrlen(fname2) > MAX_FILENAME)) {
880 HDfprintf(stderr, "The parallel diff only supports path names up to %d characters\n", MAX_FILENAME);
881 MPI_Abort(MPI_COMM_WORLD, 0);
882 } /* end if */
883
884 HDstrcpy(filenames[0], fname1);
885 HDstrcpy(filenames[1], fname2);
886
887 /* Alert the worker tasks that there's going to be work. */
888 for(i = 1; i < g_nTasks; i++)
889 MPI_Send(filenames, (MAX_FILENAME * 2), MPI_CHAR, i, MPI_TAG_PARALLEL, MPI_COMM_WORLD);
890 } /* end if */
891 #endif
892
893 /* process the objects */
894 build_match_list (obj1fullname, info1_lp, obj2fullname, info2_lp, &match_list, opts);
895 if (both_objs_grp) {
896 /*------------------------------------------------------
897 * print the list
898 */
899 if(opts->m_verbose) {
900 unsigned u;
901
902 parallel_print("\n");
903 /* if given objects is group under root */
904 if (HDstrcmp (obj1fullname,"/") || HDstrcmp (obj2fullname,"/"))
905 parallel_print("group1 group2\n");
906 else
907 parallel_print("file1 file2\n");
908 parallel_print("---------------------------------------\n");
909 for(u = 0; u < match_list->nobjs; u++) {
910 char c1, c2;
911 c1 = (match_list->objs[u].flags[0]) ? 'x' : ' ';
912 c2 = (match_list->objs[u].flags[1]) ? 'x' : ' ';
913 parallel_print("%5c %6c %-15s\n", c1, c2, match_list->objs[u].name);
914 } /* end for */
915 parallel_print ("\n");
916 } /* end if */
917 }
918 nfound = diff_match(file1_id, obj1fullname, info1_lp,
919 file2_id, obj2fullname, info2_lp,
920 match_list, opts);
921
922 done:
923 opts->err_stat = opts->err_stat | ret_value;
924
925 #ifdef H5_HAVE_PARALLEL
926 if(g_Parallel)
927 /* All done at this point, let tasks know that they won't be needed */
928 phdiff_dismiss_workers();
929 #endif
930 /* free buffers in trav_info structures */
931 if (info1_obj)
932 trav_info_free(info1_obj);
933 if (info2_obj)
934 trav_info_free(info2_obj);
935
936 if (info1_grp)
937 trav_info_free(info1_grp);
938 if (info2_grp)
939 trav_info_free(info2_grp);
940
941 /* free buffers */
942 if (obj1fullname)
943 HDfree(obj1fullname);
944 if (obj2fullname)
945 HDfree(obj2fullname);
946
947 /* free link info buffer */
948 if (trg_linfo1.trg_path)
949 HDfree(trg_linfo1.trg_path);
950 if (trg_linfo2.trg_path)
951 HDfree(trg_linfo2.trg_path);
952
953 /* close */
954 H5E_BEGIN_TRY
955 {
956 H5Fclose(file1_id);
957 H5Fclose(file2_id);
958 } H5E_END_TRY;
959
960 h5difftrace("h5diff finish\n");
961
962 return nfound;
963 }
964
965
966
967 /*-------------------------------------------------------------------------
968 * Function: diff_match
969 *
970 * Purpose: Compare common objects in given groups according to table structure.
971 * The table structure has flags which can be used to find common objects
972 * and will be compared.
973 * Common object means same name (absolute path) objects in both location.
974 *
975 * Return: Number of differences found
976 *
977 * Modifications: Compare the graph and make h5diff return 1 for difference if
978 * 1) the number of objects in file1 is not the same as in file2
979 * 2) the graph does not match, i.e same names (absolute path)
980 * 3) objects with the same name are not of the same type
981 *-------------------------------------------------------------------------
982 */
983 hsize_t
diff_match(hid_t file1_id,const char * grp1,trav_info_t * info1,hid_t file2_id,const char * grp2,trav_info_t * info2,trav_table_t * table,diff_opt_t * opts)984 diff_match(hid_t file1_id, const char *grp1, trav_info_t *info1,
985 hid_t file2_id, const char *grp2, trav_info_t *info2,
986 trav_table_t *table, diff_opt_t *opts)
987 {
988 hsize_t nfound = 0;
989 unsigned i;
990 int ret_value = opts->err_stat;
991 const char *grp1_path = "";
992 const char *grp2_path = "";
993 char *obj1_fullpath = NULL;
994 char *obj2_fullpath = NULL;
995 diff_args_t argdata;
996 size_t idx1 = 0;
997 size_t idx2 = 0;
998
999 h5difftrace("diff_match start\n");
1000 /*
1001 * if not root, prepare object name to be pre-appended to group path to
1002 * make full path
1003 */
1004 if(HDstrcmp(grp1, "/"))
1005 grp1_path = grp1;
1006 if(HDstrcmp(grp2, "/"))
1007 grp2_path = grp2;
1008
1009 /*-------------------------------------------------------------------------
1010 * regarding the return value of h5diff (0, no difference in files, 1 difference )
1011 * 1) the number of objects in file1 must be the same as in file2
1012 * 2) the graph must match, i.e same names (absolute path)
1013 * 3) objects with the same name must be of the same type
1014 *-------------------------------------------------------------------------
1015 */
1016
1017 /* not valid compare used when --exclude-path option is used */
1018 if (!opts->exclude_path) {
1019 /* number of different objects */
1020 if (info1->nused != info2->nused) {
1021 opts->contents = 0;
1022 }
1023 }
1024
1025 /* objects in one file and not the other */
1026 for(i = 0; i < table->nobjs; i++) {
1027 if(table->objs[i].flags[0] != table->objs[i].flags[1]) {
1028 opts->contents = 0;
1029 break;
1030 }
1031 }
1032
1033 /*-------------------------------------------------------------------------
1034 * do the diff for common objects
1035 *-------------------------------------------------------------------------
1036 */
1037 #ifdef H5_HAVE_PARALLEL
1038 {
1039 char *workerTasks = (char*)HDmalloc((g_nTasks - 1) * sizeof(char));
1040 int n;
1041 int busyTasks = 0;
1042 struct diffs_found nFoundbyWorker;
1043 struct diff_mpi_args args;
1044 int havePrintToken = 1;
1045 MPI_Status Status;
1046
1047 /*set all tasks as free */
1048 HDmemset(workerTasks, 1, (g_nTasks - 1));
1049 #endif
1050
1051 for(i = 0; i < table->nobjs; i++) {
1052 h5diffdebug3("diff for common objects[%d] - errstat:%d\n", i, opts->err_stat);
1053 if(table->objs[i].flags[0] && table->objs[i].flags[1]) {
1054 /* make full path for obj1 */
1055 #ifdef H5_HAVE_ASPRINTF
1056 /* Use the asprintf() routine, since it does what we're trying to do below */
1057 if(HDasprintf(&obj1_fullpath, "%s%s", grp1_path, table->objs[i].name) < 0) {
1058 HERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
1059 }
1060 #else /* H5_HAVE_ASPRINTF */
1061 if((obj1_fullpath = (char*)HDmalloc(HDstrlen(grp1_path) + HDstrlen(table->objs[i].name) + 1)) == NULL) {
1062 HERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
1063 }
1064 else {
1065 HDstrcpy(obj1_fullpath, grp1_path);
1066 HDstrcat(obj1_fullpath, table->objs[i].name);
1067 }
1068 #endif /* H5_HAVE_ASPRINTF */
1069 h5diffdebug2("diff_match path1 - %s\n", obj1_fullpath);
1070
1071 /* make full path for obj2 */
1072 #ifdef H5_HAVE_ASPRINTF
1073 /* Use the asprintf() routine, since it does what we're trying to do below */
1074 if(HDasprintf(&obj2_fullpath, "%s%s", grp2_path, table->objs[i].name) < 0) {
1075 HERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
1076 }
1077 #else /* H5_HAVE_ASPRINTF */
1078 if((obj2_fullpath = (char*)HDmalloc(HDstrlen(grp2_path) + HDstrlen(table->objs[i].name) + 1)) == NULL) {
1079 HERROR(1, H5E_tools_min_id_g, "name buffer allocation failed");
1080 }
1081 else {
1082 HDstrcpy(obj2_fullpath, grp2_path);
1083 HDstrcat(obj2_fullpath, table->objs[i].name);
1084 }
1085 #endif /* H5_HAVE_ASPRINTF */
1086 h5diffdebug2("diff_match path2 - %s\n", obj2_fullpath);
1087
1088 /* get index to figure out type of the object in file1 */
1089 while(info1->paths[idx1].path && (HDstrcmp(obj1_fullpath, info1->paths[idx1].path) != 0))
1090 idx1++;
1091 /* get index to figure out type of the object in file2 */
1092 while(info2->paths[idx2].path && (HDstrcmp(obj2_fullpath, info2->paths[idx2].path) != 0))
1093 idx2++;
1094
1095 /* Set argdata to pass other args into diff() */
1096 argdata.type[0] = info1->paths[idx1].type;
1097 argdata.type[1] = info2->paths[idx2].type;
1098 argdata.is_same_trgobj = table->objs[i].is_same_trgobj;
1099
1100 opts->cmn_objs = 1;
1101 if(!g_Parallel) {
1102 nfound += diff(file1_id, obj1_fullpath,
1103 file2_id, obj2_fullpath,
1104 opts, &argdata);
1105 } /* end if */
1106 #ifdef H5_HAVE_PARALLEL
1107 else {
1108 int workerFound = 0;
1109
1110 h5difftrace("Beginning of big else block\n");
1111 /* We're in parallel mode */
1112 /* Since the data type of diff value is hsize_t which can
1113 * be arbitary large such that there is no MPI type that
1114 * matches it, the value is passed between processes as
1115 * an array of bytes in order to be portable. But this
1116 * may not work in non-homogeneous MPI environments.
1117 */
1118
1119 /*Set up args to pass to worker task. */
1120 if(HDstrlen(obj1_fullpath) > 255 ||
1121 HDstrlen(obj2_fullpath) > 255) {
1122 HDprintf("The parallel diff only supports object names up to 255 characters\n");
1123 MPI_Abort(MPI_COMM_WORLD, 0);
1124 } /* end if */
1125
1126 /* set args struct to pass */
1127 HDstrcpy(args.name1, obj1_fullpath);
1128 HDstrcpy(args.name2, obj2_fullpath);
1129 args.opts = *opts;
1130 args.argdata.type[0] = info1->paths[idx1].type;
1131 args.argdata.type[1] = info2->paths[idx2].type;
1132 args.argdata.is_same_trgobj = table->objs[i].is_same_trgobj;
1133
1134 /* if there are any outstanding print requests, let's handle one. */
1135 if(busyTasks > 0) {
1136 int incomingMessage;
1137
1138 /* check if any tasks freed up, and didn't need to print. */
1139 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_DONE, MPI_COMM_WORLD, &incomingMessage, &Status);
1140
1141 /* first block*/
1142 if(incomingMessage) {
1143 workerTasks[Status.MPI_SOURCE - 1] = 1;
1144 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_DONE, MPI_COMM_WORLD, &Status);
1145 nfound += nFoundbyWorker.nfound;
1146 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1147 busyTasks--;
1148 } /* end if */
1149
1150 /* check to see if the print token was returned. */
1151 if(!havePrintToken) {
1152 /* If we don't have the token, someone is probably sending us output */
1153 print_incoming_data();
1154
1155 /* check incoming queue for token */
1156 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &incomingMessage, &Status);
1157
1158 /* incoming token implies free task. */
1159 if(incomingMessage) {
1160 workerTasks[Status.MPI_SOURCE - 1] = 1;
1161 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &Status);
1162 nfound += nFoundbyWorker.nfound;
1163 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1164 busyTasks--;
1165 havePrintToken = 1;
1166 } /* end if */
1167 } /* end if */
1168
1169 /* check to see if anyone needs the print token. */
1170 if(havePrintToken) {
1171 /* check incoming queue for print token requests */
1172 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_TOK_REQUEST, MPI_COMM_WORLD, &incomingMessage, &Status);
1173 if(incomingMessage) {
1174 MPI_Recv(NULL, 0, MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_TOK_REQUEST, MPI_COMM_WORLD, &Status);
1175 MPI_Send(NULL, 0, MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_PRINT_TOK, MPI_COMM_WORLD);
1176 havePrintToken = 0;
1177 } /* end if */
1178 } /* end if */
1179 } /* end if */
1180
1181 /* check array of tasks to see which ones are free.
1182 * Manager task never does work, so freeTasks[0] is really
1183 * worker task 0. */
1184 for(n = 1; (n < g_nTasks) && !workerFound; n++) {
1185 if(workerTasks[n-1]) {
1186 /* send file id's and names to first free worker */
1187 MPI_Send(&args, sizeof(args), MPI_BYTE, n, MPI_TAG_ARGS, MPI_COMM_WORLD);
1188
1189 /* increment counter for total number of prints. */
1190 busyTasks++;
1191
1192 /* mark worker as busy */
1193 workerTasks[n - 1] = 0;
1194 workerFound = 1;
1195 } /* end if */
1196 } /* end for */
1197
1198 if(!workerFound) {
1199 /* if they were all busy, we've got to wait for one free up
1200 * before we can move on. If we don't have the token, some
1201 * task is currently printing so we'll wait for that task to
1202 * return it.
1203 */
1204
1205 if(!havePrintToken) {
1206 while(!havePrintToken) {
1207 int incomingMessage;
1208
1209 print_incoming_data();
1210 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &incomingMessage, &Status);
1211 if(incomingMessage) {
1212 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, MPI_ANY_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &Status);
1213 havePrintToken = 1;
1214 nfound += nFoundbyWorker.nfound;
1215 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1216 /* send this task the work unit. */
1217 MPI_Send(&args, sizeof(args), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_ARGS, MPI_COMM_WORLD);
1218 } /* end if */
1219 } /* end while */
1220 } /* end if */
1221 /* if we do have the token, check for task to free up, or wait for a task to request it */
1222 else {
1223 /* But first print all the data in our incoming queue */
1224 print_incoming_data();
1225 MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &Status);
1226 if(Status.MPI_TAG == MPI_TAG_DONE) {
1227 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_DONE, MPI_COMM_WORLD, &Status);
1228 nfound += nFoundbyWorker.nfound;
1229 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1230 MPI_Send(&args, sizeof(args), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_ARGS, MPI_COMM_WORLD);
1231 } /* end if */
1232 else if(Status.MPI_TAG == MPI_TAG_TOK_REQUEST) {
1233 int incomingMessage;
1234
1235 MPI_Recv(NULL, 0, MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_TOK_REQUEST, MPI_COMM_WORLD, &Status);
1236 MPI_Send(NULL, 0, MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_PRINT_TOK, MPI_COMM_WORLD);
1237
1238 do {
1239 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &incomingMessage, &Status);
1240
1241 print_incoming_data();
1242 } while(!incomingMessage);
1243
1244 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &Status);
1245 nfound += nFoundbyWorker.nfound;
1246 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1247 MPI_Send(&args, sizeof(args), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_ARGS, MPI_COMM_WORLD);
1248 } /* end else-if */
1249 else {
1250 HDprintf("ERROR: Invalid tag (%d) received \n", Status.MPI_TAG);
1251 MPI_Abort(MPI_COMM_WORLD, 0);
1252 MPI_Finalize();
1253 } /* end else */
1254 } /* end else */
1255 } /* end if */
1256 } /* end else */
1257 #endif /* H5_HAVE_PARALLEL */
1258 if(obj1_fullpath)
1259 HDfree(obj1_fullpath);
1260 if(obj2_fullpath)
1261 HDfree(obj2_fullpath);
1262 } /* end if */
1263 } /* end for */
1264 h5diffdebug2("done with for loop - errstat:%d\n", opts->err_stat);
1265
1266 #ifdef H5_HAVE_PARALLEL
1267 if(g_Parallel) {
1268 /* make sure all tasks are done */
1269 while(busyTasks > 0) {
1270 MPI_Probe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &Status);
1271 if(Status.MPI_TAG == MPI_TAG_DONE) {
1272 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_DONE, MPI_COMM_WORLD, &Status);
1273 nfound += nFoundbyWorker.nfound;
1274 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1275 busyTasks--;
1276 } /* end if */
1277 else if(Status.MPI_TAG == MPI_TAG_TOK_REQUEST) {
1278 MPI_Recv(NULL, 0, MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_TOK_REQUEST, MPI_COMM_WORLD, &Status);
1279 if(havePrintToken) {
1280 int incomingMessage;
1281
1282 MPI_Send(NULL, 0, MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_PRINT_TOK, MPI_COMM_WORLD);
1283
1284 do {
1285 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &incomingMessage, &Status);
1286
1287 print_incoming_data();
1288 } while(!incomingMessage);
1289
1290 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &Status);
1291 nfound += nFoundbyWorker.nfound;
1292 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1293 busyTasks--;
1294 } /* end if */
1295 /* someone else must have it...wait for them to return it, then give it to the task that just asked for it. */
1296 else {
1297 int source = Status.MPI_SOURCE;
1298 int incomingMessage;
1299
1300 do {
1301 MPI_Iprobe(MPI_ANY_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &incomingMessage, &Status);
1302
1303 print_incoming_data();
1304 } while(!incomingMessage);
1305
1306
1307 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, MPI_ANY_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &Status);
1308 nfound += nFoundbyWorker.nfound;
1309 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1310 busyTasks--;
1311 MPI_Send(NULL, 0, MPI_BYTE, source, MPI_TAG_PRINT_TOK, MPI_COMM_WORLD);
1312 } /* end else */
1313 } /* end else-if */
1314 else if(Status.MPI_TAG == MPI_TAG_TOK_RETURN) {
1315 MPI_Recv(&nFoundbyWorker, sizeof(nFoundbyWorker), MPI_BYTE, Status.MPI_SOURCE, MPI_TAG_TOK_RETURN, MPI_COMM_WORLD, &Status);
1316 nfound += nFoundbyWorker.nfound;
1317 opts->not_cmp = opts->not_cmp | nFoundbyWorker.not_cmp;
1318 busyTasks--;
1319 havePrintToken = 1;
1320 } /* end else-if */
1321 else if(Status.MPI_TAG == MPI_TAG_PRINT_DATA) {
1322 char data[PRINT_DATA_MAX_SIZE + 1];
1323 HDmemset(data, 0, PRINT_DATA_MAX_SIZE + 1);
1324
1325 MPI_Recv(data, PRINT_DATA_MAX_SIZE, MPI_CHAR, Status.MPI_SOURCE, MPI_TAG_PRINT_DATA, MPI_COMM_WORLD, &Status);
1326
1327 HDprintf("%s", data);
1328 } /* end else-if */
1329 else {
1330 HDprintf("ph5diff-manager: ERROR!! Invalid tag (%d) received \n", Status.MPI_TAG);
1331 MPI_Abort(MPI_COMM_WORLD, 0);
1332 } /* end else */
1333 } /* end while */
1334
1335 for(i = 1; i < g_nTasks; i++)
1336 MPI_Send(NULL, 0, MPI_BYTE, i, MPI_TAG_END, MPI_COMM_WORLD);
1337
1338 /* Print any final data waiting in our queue */
1339 print_incoming_data();
1340 } /* end if */
1341 h5difftrace("done with if block\n");
1342
1343 HDfree(workerTasks);
1344 }
1345 #endif /* H5_HAVE_PARALLEL */
1346
1347 opts->err_stat = opts->err_stat | ret_value;
1348
1349 /* free table */
1350 if (table)
1351 trav_table_free(table);
1352 h5diffdebug2("diff_match finish:%d\n", nfound);
1353
1354 return nfound;
1355 }
1356
1357
1358 /*-------------------------------------------------------------------------
1359 * Function: diff
1360 *
1361 * Purpose: switch between types and choose the diff function
1362 * TYPE is either
1363 * H5G_GROUP Object is a group
1364 * H5G_DATASET Object is a dataset
1365 * H5G_TYPE Object is a named data type
1366 * H5G_LINK Object is a symbolic link
1367 *
1368 * Return: Number of differences found
1369 *-------------------------------------------------------------------------
1370 */
1371 hsize_t
diff(hid_t file1_id,const char * path1,hid_t file2_id,const char * path2,diff_opt_t * opts,diff_args_t * argdata)1372 diff(hid_t file1_id,
1373 const char *path1,
1374 hid_t file2_id,
1375 const char *path2,
1376 diff_opt_t * opts,
1377 diff_args_t *argdata)
1378 {
1379 int ret_value = opts->err_stat;
1380 int status = -1;
1381 hid_t dset1_id = -1;
1382 hid_t dset2_id = -1;
1383 hid_t type1_id = -1;
1384 hid_t type2_id = -1;
1385 hid_t grp1_id = -1;
1386 hid_t grp2_id = -1;
1387 hbool_t is_dangle_link1 = FALSE;
1388 hbool_t is_dangle_link2 = FALSE;
1389 hbool_t is_hard_link = FALSE;
1390 hsize_t nfound = 0;
1391 h5trav_type_t object_type;
1392
1393 /* to get link info */
1394 h5tool_link_info_t linkinfo1;
1395 h5tool_link_info_t linkinfo2;
1396
1397 h5difftrace("diff start\n");
1398
1399 /*init link info struct */
1400 HDmemset(&linkinfo1, 0, sizeof(h5tool_link_info_t));
1401 HDmemset(&linkinfo2, 0, sizeof(h5tool_link_info_t));
1402
1403 /* pass how to handle printing warnings to linkinfo option */
1404 if(print_warn(opts))
1405 linkinfo1.opt.msg_mode = linkinfo2.opt.msg_mode = 1;
1406
1407 /* for symbolic links, take care follow symlink and no dangling link
1408 * options */
1409 if (argdata->type[0] == H5TRAV_TYPE_LINK ||
1410 argdata->type[0] == H5TRAV_TYPE_UDLINK ||
1411 argdata->type[1] == H5TRAV_TYPE_LINK ||
1412 argdata->type[1] == H5TRAV_TYPE_UDLINK) {
1413 /*
1414 * check dangling links for path1 and path2
1415 */
1416
1417 /* target object1 - get type and name */
1418 if ((status = H5tools_get_symlink_info(file1_id, path1, &linkinfo1, opts->follow_links)) < 0)
1419 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5tools_get_symlink_info failed");
1420
1421 /* dangling link */
1422 if (status == 0) {
1423 if (opts->no_dangle_links) {
1424 /* dangling link is error */
1425 if(opts->m_verbose)
1426 parallel_print("Warning: <%s> is a dangling link.\n", path1);
1427 HGOTO_ERROR(1, H5E_tools_min_id_g, "dangling link is error");
1428 }
1429 else
1430 is_dangle_link1 = TRUE;
1431 }
1432
1433 /* target object2 - get type and name */
1434 if ((status = H5tools_get_symlink_info(file2_id, path2, &linkinfo2, opts->follow_links)) < 0)
1435 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5tools_get_symlink_info failed");
1436 /* dangling link */
1437 if (status == 0) {
1438 if (opts->no_dangle_links) {
1439 /* dangling link is error */
1440 if(opts->m_verbose)
1441 parallel_print("Warning: <%s> is a dangling link.\n", path2);
1442 HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "dangling link is error");
1443 }
1444 else
1445 is_dangle_link2 = TRUE;
1446 }
1447
1448 /* found dangling link */
1449 if (is_dangle_link1 || is_dangle_link2) {
1450 HGOTO_DONE(0);
1451 }
1452
1453 /* follow symbolic link option */
1454 if (opts->follow_links) {
1455 if (linkinfo1.linfo.type == H5L_TYPE_SOFT ||
1456 linkinfo1.linfo.type == H5L_TYPE_EXTERNAL)
1457 argdata->type[0] = (h5trav_type_t)linkinfo1.trg_type;
1458
1459 if (linkinfo2.linfo.type == H5L_TYPE_SOFT || linkinfo2.linfo.type == H5L_TYPE_EXTERNAL)
1460 argdata->type[1] = (h5trav_type_t)linkinfo2.trg_type;
1461 }
1462 }
1463 /* if objects are not the same type */
1464 if (argdata->type[0] != argdata->type[1]) {
1465 if (opts->m_verbose||opts->m_list_not_cmp) {
1466 parallel_print("Not comparable: <%s> is of type %s and <%s> is of type %s\n",
1467 path1, get_type(argdata->type[0]),
1468 path2, get_type(argdata->type[1]));
1469 }
1470 opts->not_cmp = 1;
1471 /* TODO: will need to update non-comparable is different
1472 * opts->contents = 0;
1473 */
1474 HGOTO_DONE(0);
1475 }
1476 else /* now both object types are same */
1477 object_type = argdata->type[0];
1478
1479 /*
1480 * If both points to the same target object, skip comparing details inside
1481 * of the objects to improve performance.
1482 * Always check for the hard links, otherwise if follow symlink option is
1483 * specified.
1484 *
1485 * Perform this to match the outputs as bypassing.
1486 */
1487 if (argdata->is_same_trgobj) {
1488 h5difftrace("argdata->is_same_trgobj\n");
1489 is_hard_link = (object_type == H5TRAV_TYPE_DATASET ||
1490 object_type == H5TRAV_TYPE_NAMED_DATATYPE ||
1491 object_type == H5TRAV_TYPE_GROUP);
1492 if (opts->follow_links || is_hard_link) {
1493 /* print information is only verbose option is used */
1494 if(opts->m_verbose || opts->m_report) {
1495 switch(object_type) {
1496 case H5TRAV_TYPE_DATASET:
1497 do_print_objname("dataset", path1, path2, opts);
1498 break;
1499 case H5TRAV_TYPE_NAMED_DATATYPE:
1500 do_print_objname("datatype", path1, path2, opts);
1501 break;
1502 case H5TRAV_TYPE_GROUP:
1503 do_print_objname("group", path1, path2, opts);
1504 break;
1505 case H5TRAV_TYPE_LINK:
1506 do_print_objname("link", path1, path2, opts);
1507 break;
1508 case H5TRAV_TYPE_UDLINK:
1509 if(linkinfo1.linfo.type == H5L_TYPE_EXTERNAL && linkinfo2.linfo.type == H5L_TYPE_EXTERNAL)
1510 do_print_objname("external link", path1, path2, opts);
1511 else
1512 do_print_objname ("user defined link", path1, path2, opts);
1513 break;
1514 case H5TRAV_TYPE_UNKNOWN:
1515 default:
1516 parallel_print("Comparison not supported: <%s> and <%s> are of type %s\n",
1517 path1, path2, get_type(object_type) );
1518 opts->not_cmp = 1;
1519 break;
1520 } /* switch(type)*/
1521
1522 print_found(nfound);
1523 } /* if(opts->m_verbose || opts->m_report) */
1524
1525 /* exact same, so comparison is done */
1526 HGOTO_DONE(0);
1527 }
1528 }
1529
1530 switch(object_type) {
1531 /*----------------------------------------------------------------------
1532 * H5TRAV_TYPE_DATASET
1533 *----------------------------------------------------------------------
1534 */
1535 case H5TRAV_TYPE_DATASET:
1536 if((dset1_id = H5Dopen2(file1_id, path1, H5P_DEFAULT)) < 0)
1537 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Dopen2 failed");
1538 if((dset2_id = H5Dopen2(file2_id, path2, H5P_DEFAULT)) < 0)
1539 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Dopen2 failed");
1540 /* verbose (-v) and report (-r) mode */
1541 if(opts->m_verbose || opts->m_report) {
1542 do_print_objname("dataset", path1, path2, opts);
1543 nfound = diff_dataset(file1_id, file2_id, path1, path2, opts);
1544 print_found(nfound);
1545 }
1546 /* quiet mode (-q), just count differences */
1547 else if(opts->m_quiet) {
1548 nfound = diff_dataset(file1_id, file2_id, path1, path2, opts);
1549 }
1550 /* the rest (-c, none, ...) */
1551 else {
1552 nfound = diff_dataset(file1_id, file2_id, path1, path2, opts);
1553 /* print info if difference found */
1554 if (nfound) {
1555 do_print_objname("dataset", path1, path2, opts);
1556 print_found(nfound);
1557 }
1558 }
1559 h5diffdebug2("diff after dataset:%d\n", nfound);
1560
1561 /*---------------------------------------------------------
1562 * compare attributes
1563 * if condition refers to cases when the dataset is a
1564 * referenced object
1565 *---------------------------------------------------------
1566 */
1567 if(path1)
1568 nfound += diff_attr(dset1_id, dset2_id, path1, path2, opts);
1569
1570
1571 if(H5Dclose(dset1_id) < 0)
1572 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Dclose failed");
1573 if(H5Dclose(dset2_id) < 0)
1574 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Dclose failed");
1575 break;
1576
1577 /*----------------------------------------------------------------------
1578 * H5TRAV_TYPE_NAMED_DATATYPE
1579 *----------------------------------------------------------------------
1580 */
1581 case H5TRAV_TYPE_NAMED_DATATYPE:
1582 if((type1_id = H5Topen2(file1_id, path1, H5P_DEFAULT)) < 0)
1583 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Topen2 failed");
1584 if((type2_id = H5Topen2(file2_id, path2, H5P_DEFAULT)) < 0)
1585 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Topen2 failed");
1586
1587 if((status = H5Tequal(type1_id, type2_id)) < 0)
1588 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Tequal failed");
1589
1590 /* if H5Tequal is > 0 then the datatypes refer to the same datatype */
1591 nfound = (status > 0) ? 0 : 1;
1592
1593 if(print_objname(opts, nfound))
1594 do_print_objname("datatype", path1, path2, opts);
1595
1596 /* always print the number of differences found in verbose mode */
1597 if(opts->m_verbose)
1598 print_found(nfound);
1599
1600 /*-----------------------------------------------------------------
1601 * compare attributes
1602 * the if condition refers to cases when the dataset is a
1603 * referenced object
1604 *-----------------------------------------------------------------
1605 */
1606 if(path1)
1607 nfound += diff_attr(type1_id, type2_id, path1, path2, opts);
1608
1609 if(H5Tclose(type1_id) < 0)
1610 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Tclose failed");
1611 if(H5Tclose(type2_id) < 0)
1612 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Tclose failed");
1613 break;
1614
1615 /*----------------------------------------------------------------------
1616 * H5TRAV_TYPE_GROUP
1617 *----------------------------------------------------------------------
1618 */
1619 case H5TRAV_TYPE_GROUP:
1620 if(print_objname(opts, nfound))
1621 do_print_objname("group", path1, path2, opts);
1622
1623 /* always print the number of differences found in verbose mode */
1624 if(opts->m_verbose)
1625 print_found(nfound);
1626
1627 if((grp1_id = H5Gopen2(file1_id, path1, H5P_DEFAULT)) < 0)
1628 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Gclose failed");
1629 if((grp2_id = H5Gopen2(file2_id, path2, H5P_DEFAULT)) < 0)
1630 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Gclose failed");
1631
1632 /*-----------------------------------------------------------------
1633 * compare attributes
1634 * the if condition refers to cases when the dataset is a
1635 * referenced object
1636 *-----------------------------------------------------------------
1637 */
1638 if(path1)
1639 nfound += diff_attr(grp1_id, grp2_id, path1, path2, opts);
1640
1641 if(H5Gclose(grp1_id) < 0)
1642 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Gclose failed");
1643 if(H5Gclose(grp2_id) < 0)
1644 HGOTO_ERROR(1, H5E_tools_min_id_g, "H5Gclose failed");
1645 break;
1646
1647
1648 /*----------------------------------------------------------------------
1649 * H5TRAV_TYPE_LINK
1650 *----------------------------------------------------------------------
1651 */
1652 case H5TRAV_TYPE_LINK:
1653 {
1654 status = HDstrcmp(linkinfo1.trg_path, linkinfo2.trg_path);
1655
1656 /* if the target link name is not same then the links are "different" */
1657 nfound = (status != 0) ? 1 : 0;
1658
1659 if(print_objname(opts, nfound))
1660 do_print_objname("link", path1, path2, opts);
1661
1662 /* always print the number of differences found in verbose mode */
1663 if(opts->m_verbose)
1664 print_found(nfound);
1665
1666 }
1667 break;
1668
1669 /*----------------------------------------------------------------------
1670 * H5TRAV_TYPE_UDLINK
1671 *----------------------------------------------------------------------
1672 */
1673 case H5TRAV_TYPE_UDLINK:
1674 {
1675 /* Only external links will have a query function registered */
1676 if(linkinfo1.linfo.type == H5L_TYPE_EXTERNAL && linkinfo2.linfo.type == H5L_TYPE_EXTERNAL) {
1677 /* If the buffers are the same size, compare them */
1678 if(linkinfo1.linfo.u.val_size == linkinfo2.linfo.u.val_size) {
1679 status = HDmemcmp(linkinfo1.trg_path, linkinfo2.trg_path, linkinfo1.linfo.u.val_size);
1680 }
1681 else
1682 status = 1;
1683
1684 /* if "linkinfo1.trg_path" != "linkinfo2.trg_path" then the links
1685 * are "different" extlinkinfo#.path is combination string of
1686 * file_name and obj_name
1687 */
1688 nfound = (status != 0) ? 1 : 0;
1689
1690 if(print_objname(opts, nfound))
1691 do_print_objname("external link", path1, path2, opts);
1692
1693 } /* end if */
1694 else {
1695 /* If one or both of these links isn't an external link, we can only
1696 * compare information from H5Lget_info since we don't have a query
1697 * function registered for them.
1698 *
1699 * If the link classes or the buffer length are not the
1700 * same, the links are "different"
1701 */
1702 if((linkinfo1.linfo.type != linkinfo2.linfo.type) ||
1703 (linkinfo1.linfo.u.val_size != linkinfo2.linfo.u.val_size))
1704 nfound = 1;
1705 else
1706 nfound = 0;
1707
1708 if (print_objname (opts, nfound))
1709 do_print_objname ("user defined link", path1, path2, opts);
1710 } /* end else */
1711
1712 /* always print the number of differences found in verbose mode */
1713 if(opts->m_verbose)
1714 print_found(nfound);
1715 }
1716 break;
1717
1718 case H5TRAV_TYPE_UNKNOWN:
1719 default:
1720 if(opts->m_verbose)
1721 parallel_print("Comparison not supported: <%s> and <%s> are of type %s\n",
1722 path1, path2, get_type(object_type) );
1723 opts->not_cmp = 1;
1724 break;
1725 }
1726
1727 done:
1728 opts->err_stat = opts->err_stat | ret_value;
1729
1730 /*-----------------------------------
1731 * handle dangling link(s)
1732 */
1733 /* both path1 and path2 are dangling links */
1734 if(is_dangle_link1 && is_dangle_link2) {
1735 if(print_objname(opts, nfound)) {
1736 do_print_objname("dangling link", path1, path2, opts);
1737 print_found(nfound);
1738 }
1739 }
1740 /* path1 is dangling link */
1741 else if (is_dangle_link1) {
1742 if(opts->m_verbose)
1743 parallel_print("obj1 <%s> is a dangling link.\n", path1);
1744 nfound++;
1745 if(print_objname(opts, nfound))
1746 print_found(nfound);
1747 }
1748 /* path2 is dangling link */
1749 else if (is_dangle_link2) {
1750 if(opts->m_verbose)
1751 parallel_print("obj2 <%s> is a dangling link.\n", path2);
1752 nfound++;
1753 if(print_objname(opts, nfound))
1754 print_found(nfound);
1755 }
1756
1757 /* free link info buffer */
1758 if (linkinfo1.trg_path)
1759 HDfree(linkinfo1.trg_path);
1760 if (linkinfo2.trg_path)
1761 HDfree(linkinfo2.trg_path);
1762
1763 /* close */
1764 /* disable error reporting */
1765 H5E_BEGIN_TRY {
1766 H5Dclose(dset1_id);
1767 H5Dclose(dset2_id);
1768 H5Tclose(type1_id);
1769 H5Tclose(type2_id);
1770 H5Gclose(grp1_id);
1771 H5Gclose(grp2_id);
1772 /* enable error reporting */
1773 } H5E_END_TRY;
1774
1775 h5diffdebug3("diff finish:%d - errstat:%d\n", nfound, opts->err_stat);
1776
1777 return nfound;
1778 }
1779
1780