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 "h5diff.h"
16 #include "h5diff_common.h"
17 #include "h5tools.h"
18 #include "h5tools_utils.h"
19 
20 static int check_n_input(const char*);
21 static int check_p_input(const char*);
22 static int check_d_input(const char*);
23 
24 /*
25  * Command-line options: The user can specify short or long-named
26  * parameters.
27  */
28 static const char *s_opts = "hVrv:qn:d:p:NcelxE:S";
29 static struct long_options l_opts[] = {
30     { "help", no_arg, 'h' },
31     { "version", no_arg, 'V' },
32     { "report", no_arg, 'r' },
33     { "verbose", optional_arg, 'v' },
34     { "quiet", no_arg, 'q' },
35     { "count", require_arg, 'n' },
36     { "delta", require_arg, 'd' },
37     { "relative", require_arg, 'p' },
38     { "nan", no_arg, 'N' },
39     { "compare", no_arg, 'c' },
40     { "use-system-epsilon", no_arg, 'e' },
41     { "follow-symlinks", no_arg, 'l' },
42     { "no-dangling-links", no_arg, 'x' },
43     { "exclude-path", require_arg, 'E' },
44     { "enable-error-stack", no_arg, 'S' },
45     { NULL, 0, '\0' }
46 };
47 
48 /*-------------------------------------------------------------------------
49  * Function: check_options
50  *
51  * Purpose: parse command line input
52  *
53  *-------------------------------------------------------------------------
54  */
check_options(diff_opt_t * opts)55 static void check_options(diff_opt_t* opts)
56 {
57     /*--------------------------------------------------------------
58      * check for mutually exclusive options
59      *--------------------------------------------------------------*/
60 
61     /* check between -d , -p, --use-system-epsilon.
62      * These options are mutually exclusive.
63      */
64     if ((opts->d + opts->p + opts->use_system_epsilon) > 1) {
65         printf("%s error: -d, -p and --use-system-epsilon options are mutually-exclusive;\n", PROGRAMNAME);
66         printf("use no more than one.\n");
67         printf("Try '-h' or '--help' option for more information or see the %s entry in the 'HDF5 Reference Manual'.\n", PROGRAMNAME);
68         h5diff_exit(EXIT_FAILURE);
69     }
70 }
71 
72 
73 /*-------------------------------------------------------------------------
74  * Function: parse_command_line
75  *
76  * Purpose: parse command line input
77  *
78  *-------------------------------------------------------------------------
79  */
80 
parse_command_line(int argc,const char * argv[],const char ** fname1,const char ** fname2,const char ** objname1,const char ** objname2,diff_opt_t * opts)81 void parse_command_line(int argc,
82                         const char* argv[],
83                         const char** fname1,
84                         const char** fname2,
85                         const char** objname1,
86                         const char** objname2,
87                         diff_opt_t* opts)
88 {
89     int i;
90     int opt;
91     struct exclude_path_list *exclude_head, *exclude_prev, *exclude_node;
92 
93     /* process the command-line */
94     memset(opts, 0, sizeof (diff_opt_t));
95 
96     /* assume equal contents initially */
97     opts->contents = 1;
98 
99     /* NaNs are handled by default */
100     opts->do_nans = 1;
101 
102     /* not Listing objects that are not comparable */
103     opts->m_list_not_cmp = 0;
104 
105     /* initially no not-comparable. */
106     /**this is bad in mixing option with results**/
107     opts->not_cmp=0;
108 
109     /* init for exclude-path option */
110     exclude_head = NULL;
111 
112     /* parse command line options */
113     while ((opt = get_option(argc, argv, s_opts, l_opts)) != EOF) {
114         switch ((char)opt) {
115         default:
116             usage();
117             h5diff_exit(EXIT_FAILURE);
118             break;
119 
120         case 'h':
121             usage();
122             h5diff_exit(EXIT_SUCCESS);
123             break;
124 
125         case 'V':
126             print_version(h5tools_getprogname());
127             h5diff_exit(EXIT_SUCCESS);
128             break;
129 
130         case 'v':
131             opts->m_verbose = 1;
132             /* This for loop is for handling style like
133              * -v, -v1, --verbose, --verbose=1.
134              */
135             for (i = 1; i < argc; i++) {
136                 /*
137                  * short opt
138                  */
139                 if (!strcmp (argv[i], "-v")) {  /* no arg */
140                     opt_ind--;
141                     opts->m_verbose_level = 0;
142                     break;
143                 }
144                 else if (!strncmp (argv[i], "-v", (size_t)2)) {
145                     opts->m_verbose_level = atoi(&argv[i][2]);
146                     break;
147                 }
148 
149                 /*
150                  * long opt
151                  */
152                 if (!strcmp (argv[i], "--verbose")) {  /* no arg */
153                     opts->m_verbose_level = 0;
154                     break;
155                 }
156                 else if ( !strncmp (argv[i], "--verbose", (size_t)9) && argv[i][9]=='=') {
157                     opts->m_verbose_level = atoi(&argv[i][10]);
158                     break;
159                 }
160             }
161             break;
162 
163         case 'q':
164             /* use quiet mode; supress the message "0 differences found" */
165             opts->m_quiet = 1;
166             break;
167 
168         case 'r':
169             opts->m_report = 1;
170             break;
171 
172         case 'l':
173             opts->follow_links = TRUE;
174             break;
175 
176         case 'x':
177             opts->no_dangle_links = 1;
178             break;
179 
180         case 'S':
181             enable_error_stack = 1;
182             break;
183 
184         case 'E':
185             opts->exclude_path = 1;
186 
187             /* create linked list of excluding objects */
188             if( (exclude_node = (struct exclude_path_list*) HDmalloc(sizeof(struct exclude_path_list))) == NULL) {
189                 printf("Error: lack of memory!\n");
190                 h5diff_exit(EXIT_FAILURE);
191             }
192 
193             /* init */
194             exclude_node->obj_path = (char*)opt_arg;
195             exclude_node->obj_type = H5TRAV_TYPE_UNKNOWN;
196             exclude_prev = exclude_head;
197 
198             if (NULL == exclude_head) {
199                 exclude_head = exclude_node;
200                 exclude_head->next = NULL;
201             }
202             else {
203                 while(NULL != exclude_prev->next)
204                     exclude_prev=exclude_prev->next;
205 
206                 exclude_node->next = NULL;
207                 exclude_prev->next = exclude_node;
208             }
209             break;
210 
211         case 'd':
212             opts->d=1;
213 
214             if (check_d_input(opt_arg) == - 1) {
215                 printf("<-d %s> is not a valid option\n", opt_arg);
216                 usage();
217                 h5diff_exit(EXIT_FAILURE);
218             }
219             opts->delta = atof(opt_arg);
220 
221             /* -d 0 is the same as default */
222             if (H5_DBL_ABS_EQUAL(opts->delta, (double)0.0F))
223                 opts->d=0;
224             break;
225 
226         case 'p':
227             opts->p=1;
228             if (check_p_input(opt_arg) == -1) {
229                 printf("<-p %s> is not a valid option\n", opt_arg);
230                 usage();
231                 h5diff_exit(EXIT_FAILURE);
232             }
233             opts->percent = atof(opt_arg);
234 
235             /* -p 0 is the same as default */
236             if (H5_DBL_ABS_EQUAL(opts->percent, (double)0.0F))
237                 opts->p = 0;
238             break;
239 
240         case 'n':
241             opts->n=1;
242             if ( check_n_input(opt_arg) == -1) {
243                 printf("<-n %s> is not a valid option\n", opt_arg);
244                 usage();
245                 h5diff_exit(EXIT_FAILURE);
246             }
247             opts->count = HDstrtoull(opt_arg, NULL, 0);
248             break;
249 
250         case 'N':
251             opts->do_nans = 0;
252             break;
253 
254         case 'c':
255             opts->m_list_not_cmp = 1;
256             break;
257 
258         case 'e':
259             opts->use_system_epsilon = 1;
260             break;
261         }
262     }
263 
264     /* check options */
265     check_options(opts);
266 
267     /* if exclude-path option is used, keep the exclude path list */
268     if (opts->exclude_path)
269         opts->exclude = exclude_head;
270 
271     /* check for file names to be processed */
272     if (argc <= opt_ind || argv[ opt_ind + 1 ] == NULL) {
273         error_msg("missing file names\n");
274         usage();
275         h5diff_exit(EXIT_FAILURE);
276     }
277 
278     *fname1 = argv[ opt_ind ];
279     *fname2 = argv[ opt_ind + 1 ];
280     *objname1 = argv[ opt_ind + 2 ];
281 
282     if (*objname1 == NULL) {
283         *objname2 = NULL;
284         return;
285     }
286 
287     if (argv[ opt_ind + 3 ] != NULL) {
288         *objname2 = argv[ opt_ind + 3 ];
289     }
290     else {
291         *objname2 = *objname1;
292     }
293 
294 
295 }
296 
297 
298 /*-------------------------------------------------------------------------
299  * Function: print_info
300  *
301  * Purpose: print several information messages after h5diff call
302  *
303  *-------------------------------------------------------------------------
304  */
305 
print_info(diff_opt_t * opts)306  void  print_info(diff_opt_t* opts)
307  {
308      if (opts->m_quiet || opts->err_stat)
309          return;
310 
311      if (opts->cmn_objs == 0) {
312          printf("No common objects found. Files are not comparable.\n");
313          if (!opts->m_verbose)
314              printf("Use -v for a list of objects.\n");
315      }
316 
317      if (opts->not_cmp == 1) {
318          if (opts->m_list_not_cmp == 0) {
319              printf("--------------------------------\n");
320              printf("Some objects are not comparable\n");
321              printf("--------------------------------\n");
322              if (opts->m_verbose)
323                  printf("Use -c for a list of objects without details of differences.\n");
324              else
325                  printf("Use -c for a list of objects.\n");
326          }
327      }
328  }
329 
330 /*-------------------------------------------------------------------------
331  * Function: check_n_input
332  *
333  * Purpose: check for valid input
334  *
335  * Return: 1 for ok, -1 for fail
336  *
337  * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
338  *
339  * Date: May 9, 2003
340  *
341  * Comments:
342  *
343  * Modifications:
344  *
345  *-------------------------------------------------------------------------
346  */
347 static int
check_n_input(const char * str)348 check_n_input( const char *str )
349 {
350     unsigned i;
351     char c;
352 
353     for (i = 0; i < strlen(str); i++) {
354         c = str[i];
355         if (i == 0) {
356             if (c < 49 || c > 57) /* ascii values between 1 and 9 */
357                 return -1;
358         }
359         else
360             if (c < 48 || c > 57) /* 0 also */
361                 return -1;
362     }
363     return 1;
364 }
365 
366 /*-------------------------------------------------------------------------
367  * Function: check_p_input
368  *
369  * Purpose: check for a valid p option input
370  *
371  * Return: 1 for ok, -1 for fail
372  *
373  * Date: May 9, 2003
374  *
375  * Comments:
376  *
377  * Modifications:
378  *
379  *-------------------------------------------------------------------------
380  */
381 static int
check_p_input(const char * str)382 check_p_input( const char *str )
383 {
384     double x;
385 
386     /*
387     the atof return value on a hexadecimal input is different
388     on some systems; we do a character check for this
389     */
390     if (strlen(str) > 2 && str[0] == '0' && str[1] == 'x')
391         return -1;
392 
393     x = atof(str);
394     if (x < 0)
395         return -1;
396 
397     return 1;
398 }
399 
400 /*-------------------------------------------------------------------------
401  * Function: check_d_input
402  *
403  * Purpose: check for a valid d option input
404  *
405  * Return: 1 for ok, -1 for fail
406  *
407  * Date: November 11, 2007
408  *
409  * Comments:
410  *
411  * Modifications:
412  *
413  *-------------------------------------------------------------------------
414  */
415 static int
check_d_input(const char * str)416 check_d_input( const char *str )
417 {
418     double x;
419 
420     /*
421     the atof return value on a hexadecimal input is different
422     on some systems; we do a character check for this
423     */
424     if (strlen(str) > 2 && str[0] == '0' && str[1] == 'x')
425         return -1;
426 
427     x = atof(str);
428     if (x < 0)
429         return -1;
430 
431     return 1;
432 }
433 
434 /*-------------------------------------------------------------------------
435  * Function: usage
436  *
437  * Purpose: print a usage message
438  *
439  * Return: void
440  *
441  *-------------------------------------------------------------------------
442  */
443 
usage(void)444 void usage(void)
445 {
446  PRINTVALSTREAM(rawoutstream, "usage: h5diff [OPTIONS] file1 file2 [obj1[ obj2]]\n");
447  PRINTVALSTREAM(rawoutstream, "  file1             File name of the first HDF5 file\n");
448  PRINTVALSTREAM(rawoutstream, "  file2             File name of the second HDF5 file\n");
449  PRINTVALSTREAM(rawoutstream, "  [obj1]            Name of an HDF5 object, in absolute path\n");
450  PRINTVALSTREAM(rawoutstream, "  [obj2]            Name of an HDF5 object, in absolute path\n");
451  PRINTVALSTREAM(rawoutstream, "\n");
452  PRINTVALSTREAM(rawoutstream, "  OPTIONS\n");
453  PRINTVALSTREAM(rawoutstream, "   -h, --help\n");
454  PRINTVALSTREAM(rawoutstream, "         Print a usage message and exit.\n");
455  PRINTVALSTREAM(rawoutstream, "   -V, --version\n");
456  PRINTVALSTREAM(rawoutstream, "         Print version number and exit.\n");
457  PRINTVALSTREAM(rawoutstream, "   -r, --report\n");
458  PRINTVALSTREAM(rawoutstream, "         Report mode. Print differences.\n");
459  PRINTVALSTREAM(rawoutstream, "   -v --verbose\n");
460  PRINTVALSTREAM(rawoutstream, "         Verbose mode. Print differences information and list of objects.\n");
461  PRINTVALSTREAM(rawoutstream, "   -vN --verbose=N\n");
462  PRINTVALSTREAM(rawoutstream, "         Verbose mode with level. Print differences and list of objects.\n");
463  PRINTVALSTREAM(rawoutstream, "         Level of detail depends on value of N:\n");
464  PRINTVALSTREAM(rawoutstream, "          0 : Identical to '-v' or '--verbose'.\n");
465  PRINTVALSTREAM(rawoutstream, "          1 : All level 0 information plus one-line attribute\n");
466  PRINTVALSTREAM(rawoutstream, "              status summary.\n");
467  PRINTVALSTREAM(rawoutstream, "          2 : All level 1 information plus extended attribute\n");
468  PRINTVALSTREAM(rawoutstream, "              status report.\n");
469  PRINTVALSTREAM(rawoutstream, "   -q, --quiet\n");
470  PRINTVALSTREAM(rawoutstream, "         Quiet mode. Do not produce output.\n");
471  PRINTVALSTREAM(rawoutstream, "   --enable-error-stack\n");
472  PRINTVALSTREAM(rawoutstream, "                   Prints messages from the HDF5 error stack as they occur.\n");
473  PRINTVALSTREAM(rawoutstream, "   --follow-symlinks\n");
474  PRINTVALSTREAM(rawoutstream, "         Follow symbolic links (soft links and external links and compare the)\n");
475  PRINTVALSTREAM(rawoutstream, "         links' target objects.\n");
476  PRINTVALSTREAM(rawoutstream, "         If symbolic link(s) with the same name exist in the files being\n");
477  PRINTVALSTREAM(rawoutstream, "         compared, then determine whether the target of each link is an existing\n");
478  PRINTVALSTREAM(rawoutstream, "         object (dataset, group, or named datatype) or the link is a dangling\n");
479  PRINTVALSTREAM(rawoutstream, "         link (a soft or external link pointing to a target object that does\n");
480  PRINTVALSTREAM(rawoutstream, "         not yet exist).\n");
481  PRINTVALSTREAM(rawoutstream, "         - If both symbolic links are dangling links, they are treated as being\n");
482  PRINTVALSTREAM(rawoutstream, "           the same; by default, h5diff returns an exit code of 0.\n");
483  PRINTVALSTREAM(rawoutstream, "           If, however, --no-dangling-links is used with --follow-symlinks,\n");
484  PRINTVALSTREAM(rawoutstream, "           this situation is treated as an error and h5diff returns an\n");
485  PRINTVALSTREAM(rawoutstream, "           exit code of 2.\n");
486  PRINTVALSTREAM(rawoutstream, "         - If only one of the two links is a dangling link,they are treated as\n");
487  PRINTVALSTREAM(rawoutstream, "           being different and h5diff returns an exit code of 1.\n");
488  PRINTVALSTREAM(rawoutstream, "           If, however, --no-dangling-links is used with --follow-symlinks,\n");
489  PRINTVALSTREAM(rawoutstream, "           this situation is treated as an error and h5diff returns an\n");
490  PRINTVALSTREAM(rawoutstream, "           exit code of 2.\n");
491  PRINTVALSTREAM(rawoutstream, "         - If both symbolic links point to existing objects, h5diff compares the\n");
492  PRINTVALSTREAM(rawoutstream, "           two objects.\n");
493  PRINTVALSTREAM(rawoutstream, "         If any symbolic link specified in the call to h5diff does not exist,\n");
494  PRINTVALSTREAM(rawoutstream, "         h5diff treats it as an error and returns an exit code of 2.\n");
495  PRINTVALSTREAM(rawoutstream, "   --no-dangling-links\n");
496  PRINTVALSTREAM(rawoutstream, "         Must be used with --follow-symlinks option; otherwise, h5diff shows\n");
497  PRINTVALSTREAM(rawoutstream, "         error message and returns an exit code of 2.\n");
498  PRINTVALSTREAM(rawoutstream, "         Check for any symbolic links (soft links or external links) that do not\n");
499  PRINTVALSTREAM(rawoutstream, "         resolve to an existing object (dataset, group, or named datatype).\n");
500  PRINTVALSTREAM(rawoutstream, "         If any dangling link is found, this situation is treated as an error\n");
501  PRINTVALSTREAM(rawoutstream, "         and h5diff returns an exit code of 2.\n");
502  PRINTVALSTREAM(rawoutstream, "   -c, --compare\n");
503  PRINTVALSTREAM(rawoutstream, "         List objects that are not comparable\n");
504  PRINTVALSTREAM(rawoutstream, "   -N, --nan\n");
505  PRINTVALSTREAM(rawoutstream, "         Avoid NaNs detection\n");
506  PRINTVALSTREAM(rawoutstream, "   -n C, --count=C\n");
507  PRINTVALSTREAM(rawoutstream, "         Print differences up to C. C must be a positive integer.\n");
508  PRINTVALSTREAM(rawoutstream, "   -d D, --delta=D\n");
509  PRINTVALSTREAM(rawoutstream, "         Print difference if (|a-b| > D). D must be a positive number. Where a\n");
510  PRINTVALSTREAM(rawoutstream, "         is the data point value in file1 and b is the data point value in file2.\n");
511  PRINTVALSTREAM(rawoutstream, "         Can not use with '-p' or '--use-system-epsilon'.\n");
512  PRINTVALSTREAM(rawoutstream, "   -p R, --relative=R\n");
513  PRINTVALSTREAM(rawoutstream, "         Print difference if (|(a-b)/b| > R). R must be a positive number. Where a\n");
514  PRINTVALSTREAM(rawoutstream, "         is the data point value in file1 and b is the data point value in file2.\n");
515  PRINTVALSTREAM(rawoutstream, "         Can not use with '-d' or '--use-system-epsilon'.\n");
516  PRINTVALSTREAM(rawoutstream, "   --use-system-epsilon\n");
517  PRINTVALSTREAM(rawoutstream, "         Print difference if (|a-b| > EPSILON), EPSILON is system defined value. Where a\n");
518  PRINTVALSTREAM(rawoutstream, "         is the data point value in file1 and b is the data point value in file2.\n");
519  PRINTVALSTREAM(rawoutstream, "         If the system epsilon is not defined,one of the following predefined\n");
520  PRINTVALSTREAM(rawoutstream, "         values will be used:\n");
521  PRINTVALSTREAM(rawoutstream, "           FLT_EPSILON = 1.19209E-07 for floating-point type\n");
522  PRINTVALSTREAM(rawoutstream, "           DBL_EPSILON = 2.22045E-16 for double precision type\n");
523  PRINTVALSTREAM(rawoutstream, "         Can not use with '-p' or '-d'.\n");
524  PRINTVALSTREAM(rawoutstream, "   --exclude-path \"path\"\n");
525  PRINTVALSTREAM(rawoutstream, "         Exclude the specified path to an object when comparing files or groups.\n");
526  PRINTVALSTREAM(rawoutstream, "         If a group is excluded, all member objects will also be excluded.\n");
527  PRINTVALSTREAM(rawoutstream, "         The specified path is excluded wherever it occurs.\n");
528  PRINTVALSTREAM(rawoutstream, "         This flexibility enables the same option to exclude either objects that\n");
529  PRINTVALSTREAM(rawoutstream, "         exist only in one file or common objects that are known to differ.\n");
530  PRINTVALSTREAM(rawoutstream, "\n");
531  PRINTVALSTREAM(rawoutstream, "         When comparing files, \"path\" is the absolute path to the excluded;\n");
532  PRINTVALSTREAM(rawoutstream, "         object; when comparing groups, \"path\" is similar to the relative\n");
533  PRINTVALSTREAM(rawoutstream, "         path from the group to the excluded object. This \"path\" can be\n");
534  PRINTVALSTREAM(rawoutstream, "         taken from the first section of the output of the --verbose option.\n");
535  PRINTVALSTREAM(rawoutstream, "         For example, if you are comparing the group /groupA in two files and\n");
536  PRINTVALSTREAM(rawoutstream, "         you want to exclude /groupA/groupB/groupC in both files, the exclude\n");
537  PRINTVALSTREAM(rawoutstream, "         option would read as follows:\n");
538  PRINTVALSTREAM(rawoutstream, "           --exclude-path \"/groupB/groupC\"\n");
539  PRINTVALSTREAM(rawoutstream, "\n");
540  PRINTVALSTREAM(rawoutstream, "         If there are multiple paths to an object, only the specified path(s)\n");
541  PRINTVALSTREAM(rawoutstream, "         will be excluded; the comparison will include any path not explicitly\n");
542  PRINTVALSTREAM(rawoutstream, "         excluded.\n");
543  PRINTVALSTREAM(rawoutstream, "         This option can be used repeatedly to exclude multiple paths.\n");
544  PRINTVALSTREAM(rawoutstream, "\n");
545 
546  PRINTVALSTREAM(rawoutstream, " Modes of output:\n");
547  PRINTVALSTREAM(rawoutstream, "  Default mode: print the number of differences found and where they occured\n");
548  PRINTVALSTREAM(rawoutstream, "  -r Report mode: print the above plus the differences\n");
549  PRINTVALSTREAM(rawoutstream, "  -v Verbose mode: print the above plus a list of objects and warnings\n");
550  PRINTVALSTREAM(rawoutstream, "  -q Quiet mode: do not print output\n");
551 
552  PRINTVALSTREAM(rawoutstream, "\n");
553 
554  PRINTVALSTREAM(rawoutstream, " File comparison:\n");
555  PRINTVALSTREAM(rawoutstream, "  If no objects [obj1[ obj2]] are specified, the h5diff comparison proceeds as\n");
556  PRINTVALSTREAM(rawoutstream, "  a comparison of the two files' root groups.  That is, h5diff first compares\n");
557  PRINTVALSTREAM(rawoutstream, "  the names of root group members, generates a report of root group objects\n");
558  PRINTVALSTREAM(rawoutstream, "  that appear in only one file or in both files, and recursively compares\n");
559  PRINTVALSTREAM(rawoutstream, "  common objects.\n");
560  PRINTVALSTREAM(rawoutstream, "\n");
561 
562  PRINTVALSTREAM(rawoutstream, " Object comparison:\n");
563  PRINTVALSTREAM(rawoutstream, "  1) Groups\n");
564  PRINTVALSTREAM(rawoutstream, "      First compares the names of member objects (relative path, from the\n");
565  PRINTVALSTREAM(rawoutstream, "      specified group) and generates a report of objects that appear in only\n");
566  PRINTVALSTREAM(rawoutstream, "      one group or in both groups. Common objects are then compared recursively.\n");
567  PRINTVALSTREAM(rawoutstream, "  2) Datasets\n");
568  PRINTVALSTREAM(rawoutstream, "      Array rank and dimensions, datatypes, and data values are compared.\n");
569  PRINTVALSTREAM(rawoutstream, "  3) Datatypes\n");
570  PRINTVALSTREAM(rawoutstream, "      The comparison is based on the return value of H5Tequal.\n");
571  PRINTVALSTREAM(rawoutstream, "  4) Symbolic links\n");
572  PRINTVALSTREAM(rawoutstream, "      The paths to the target objects are compared.\n");
573  PRINTVALSTREAM(rawoutstream, "      (The option --follow-symlinks overrides the default behavior when\n");
574  PRINTVALSTREAM(rawoutstream, "       symbolic links are compared.).\n");
575  PRINTVALSTREAM(rawoutstream, "\n");
576 
577  PRINTVALSTREAM(rawoutstream, " Exit code:\n");
578  PRINTVALSTREAM(rawoutstream, "  0 if no differences, 1 if differences found, 2 if error\n");
579  PRINTVALSTREAM(rawoutstream, "\n");
580  PRINTVALSTREAM(rawoutstream, " Examples of use:\n");
581  PRINTVALSTREAM(rawoutstream, " 1) h5diff file1 file2 /g1/dset1 /g1/dset2\n");
582  PRINTVALSTREAM(rawoutstream, "    Compares object '/g1/dset1' in file1 with '/g1/dset2' in file2\n");
583  PRINTVALSTREAM(rawoutstream, "\n");
584  PRINTVALSTREAM(rawoutstream, " 2) h5diff file1 file2 /g1/dset1\n");
585  PRINTVALSTREAM(rawoutstream, "    Compares object '/g1/dset1' in both files\n");
586  PRINTVALSTREAM(rawoutstream, "\n");
587  PRINTVALSTREAM(rawoutstream, " 3) h5diff file1 file2\n");
588  PRINTVALSTREAM(rawoutstream, "    Compares all objects in both files\n");
589  PRINTVALSTREAM(rawoutstream, "\n");
590  PRINTVALSTREAM(rawoutstream, " Notes:\n");
591  PRINTVALSTREAM(rawoutstream, "  file1 and file2 can be the same file.\n");
592  PRINTVALSTREAM(rawoutstream, "  Use h5diff file1 file1 /g1/dset1 /g1/dset2 to compare\n");
593  PRINTVALSTREAM(rawoutstream, "  '/g1/dset1' and '/g1/dset2' in the same file\n");
594  PRINTVALSTREAM(rawoutstream, "\n");
595 }
596