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