/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the files COPYING and Copyright.html.  COPYING can be found at the root   *
 * of the source code distribution tree; Copyright.html can be found at the  *
 * root level of an installed copy of the electronic HDF5 document set and   *
 * is linked from the top-level documents page.  It can also be found at     *
 * http://hdfgroup.org/HDF5/doc/Copyright.html.  If you do not have          *
 * access to either file, you may request a copy from help@hdfgroup.org.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <stdlib.h>
#include "h5diff.h"
#include "H5private.h"


/*-------------------------------------------------------------------------
 * Function: print_objname
 *
 * Purpose: print object name only when:
 *  1) verbose mode
 *  2) when diff was found (normal mode)
 *-------------------------------------------------------------------------
 */
int print_objname(diff_opt_t *options, hsize_t nfound)
{
    return ( (options->m_verbose || nfound) && !options->m_quiet) ?1:0;
}

/*-------------------------------------------------------------------------
 * Function: do_print_objname
 *
 * Purpose: print object name 
 *
 *-------------------------------------------------------------------------
 */
void
do_print_objname (const char *OBJ, const char *path1, const char *path2)
{
    printf("%-7s: <%s> and <%s>\n", OBJ, path1, path2);
}

/*-------------------------------------------------------------------------
 * Function: h5diff
 *
 * Purpose: public function, can be called in an application program.
 *   return differences between 2 HDF5 files
 *
 * Return: Number of differences found.
 *
 * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
 *
 * Date: October 22, 2003
 *
 *-------------------------------------------------------------------------
 */

hsize_t h5diff(const char *fname1,
               const char *fname2,
               const char *objname1,
               const char *objname2,
               diff_opt_t *options)
{
    int          nobjects1, nobjects2;
    trav_info_t  *info1=NULL;
    trav_info_t  *info2=NULL;
    hid_t        file1_id=(-1), file2_id=(-1);
    hsize_t      nfound=0;
    
    if (options->m_quiet && (options->m_verbose || options->m_report))
    {
        printf("Error: -q (quiet mode) cannot be added to verbose or report modes\n");
        options->err_stat=1;
        return 0;
    }
    
   /*-------------------------------------------------------------------------
    * open the files first; if they are not valid, no point in continuing
    *-------------------------------------------------------------------------
    */
    
    /* disable error reporting */
    H5E_BEGIN_TRY 
    {
        /* Open the files */
        if ((file1_id=H5Fopen(fname1,H5F_ACC_RDONLY,H5P_DEFAULT))<0 )
        {
            printf("h5diff: <%s>: unable to open file\n", fname1 );
            options->err_stat=1;
            goto out;
        }
        if ((file2_id=H5Fopen(fname2,H5F_ACC_RDONLY,H5P_DEFAULT))<0 )
        {
            printf("h5diff: <%s>: unable to open file\n", fname2 );
            options->err_stat=1;
            goto out;
        }
        /* enable error reporting */
    } H5E_END_TRY;
    
    
   /*-------------------------------------------------------------------------
    * get the number of objects in the files
    *-------------------------------------------------------------------------
    */
    nobjects1 = h5trav_getinfo( file1_id, NULL, 0 );
    nobjects2 = h5trav_getinfo( file2_id, NULL, 0 );
    
    if (nobjects1<0 || nobjects2<0)
    {
        printf("Error: Could not get get file contents\n");
        options->err_stat=1;
        goto out;
    }
    
   /*-------------------------------------------------------------------------
    * get the list of objects in the files
    *-------------------------------------------------------------------------
    */
    
    info1 = (trav_info_t*) malloc( nobjects1 * sizeof(trav_info_t));
    info2 = (trav_info_t*) malloc( nobjects2 * sizeof(trav_info_t));
    if (info1==NULL || info2==NULL)
    {
        printf("Error: Not enough memory for object list\n");
        options->err_stat=1;
        if (info1) h5trav_freeinfo(info1,nobjects1);
        if (info2) h5trav_freeinfo(info2,nobjects1);
        goto out;
    }
    
    h5trav_getinfo( file1_id, info1, 0 );
    h5trav_getinfo( file2_id, info2, 0 );
    
   /*-------------------------------------------------------------------------
    * object name was supplied
    *-------------------------------------------------------------------------
    */
    
    if ( objname1 )
    {
        assert(objname2);
        options->cmn_objs=1; /* eliminate warning */
        nfound=diff_compare(file1_id,
            fname1,
            objname1,
            nobjects1,
            info1,
            file2_id,
            fname2,
            objname2,
            nobjects2,
            info2,
            options);
    }
    
   /*-------------------------------------------------------------------------
    * compare all
    *-------------------------------------------------------------------------
    */
    
    else
    {
        nfound=diff_match(file1_id,
            nobjects1,
            info1,
            file2_id,
            nobjects2,
            info2,
            options);
    }
    
    
    h5trav_freeinfo(info1,nobjects1);
    h5trav_freeinfo(info2,nobjects2);
    
out:
    /* close */
    H5E_BEGIN_TRY 
    {
        H5Fclose(file1_id);
        H5Fclose(file2_id);
    } H5E_END_TRY;
    
    
    return nfound;
}



/*-------------------------------------------------------------------------
 * Function: diff_match
 *
 * Purpose: Find common objects; the algorithm used for this search is the
 *  cosequential match algorithm and is described in
 *  Folk, Michael; Zoellick, Bill. (1992). File Structures. Addison-Wesley.
 *
 * Return: Number of differences found
 *
 * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
 *
 * Date: May 9, 2003
 *
 *-------------------------------------------------------------------------
 */
hsize_t diff_match( hid_t file1_id,
                    int nobjects1,
                    trav_info_t *info1,
                    hid_t file2_id,
                    int nobjects2,
                    trav_info_t *info2,
                    diff_opt_t *options )
{
    int           more_names_exist = (nobjects1>0 && nobjects2>0) ? 1 : 0;
    trav_table_t  *table=NULL;
    int           cmp;
    int           curr1=0;
    int           curr2=0;
    unsigned      infile[2];
    char          c1, c2;
    hsize_t       nfound=0;
    unsigned int  i;
    
   /*-------------------------------------------------------------------------
    * build the list
    *-------------------------------------------------------------------------
    */
    trav_table_init( &table );
    
    while ( more_names_exist )
    {
        /* criteria is string compare */
        cmp = strcmp( info1[curr1].name, info2[curr2].name );
        if ( cmp == 0 )
        {
            infile[0]=1; infile[1]=1;
            trav_table_addflags(infile, info1[curr1].name, info1[curr1].type, table );
            
            curr1++;
            curr2++;
        }
        else if ( cmp < 0 )
        {
            infile[0]=1; infile[1]=0;
            trav_table_addflags(infile, info1[curr1].name, info1[curr1].type, table );
            curr1++;
        }
        else
        {
            infile[0]=0; infile[1]=1;
            trav_table_addflags(infile, info2[curr2].name, info2[curr2].type, table );
            curr2++;
        }
        
        more_names_exist = (curr1<nobjects1 && curr2<nobjects2) ? 1 : 0;
        
        
    } /* end while */
    
    /* list1 did not end */
    if (curr1<nobjects1)
    {
        while ( curr1<nobjects1 )
        {
            infile[0]=1; infile[1]=0;
            trav_table_addflags(infile, info1[curr1].name, info1[curr1].type, table );
            curr1++;
        }
    }
    
    /* list2 did not end */
    if (curr2<nobjects2)
    {
        while ( curr2<nobjects2 )
        {
            infile[0]=0; infile[1]=1;
            trav_table_addflags(infile, info2[curr2].name, info2[curr2].type, table );
            curr2++;
        }
    }
    
   /*-------------------------------------------------------------------------
    * print the list
    *-------------------------------------------------------------------------
    */
    
    if (options->m_verbose)
    {
        printf("\n");
        printf("file1     file2\n");
        printf("---------------------------------------\n");
        for (i = 0; i < table->nobjs; i++)
        {
            c1 = (table->objs[i].flags[0]) ? 'x' : ' ';
            c2 = (table->objs[i].flags[1]) ? 'x' : ' ';
            printf("%5c %6c    %-15s\n", c1, c2, table->objs[i].name);
        }
        printf("\n");
    }
    
    
   /*-------------------------------------------------------------------------
    * do the diff for common objects
    *-------------------------------------------------------------------------
    */
    
    for (i = 0; i < table->nobjs; i++)
    {
        if ( table->objs[i].flags[0] && table->objs[i].flags[1] )
        {
            options->cmn_objs=1;
            nfound+=diff( file1_id,
                table->objs[i].name,
                file2_id,
                table->objs[i].name,
                options,
                table->objs[i].type );
        }
    }
    
    /* free table */
    trav_table_free(table);
    
    
   /*-------------------------------------------------------------------------
    * do the diff for the root, it compares only the root group attributes
    *-------------------------------------------------------------------------
    */
    
    nfound+=diff( file1_id,
        "/",
        file2_id,
        "/",
        options,
        H5G_GROUP );
    
    
    return nfound;
}


/*-------------------------------------------------------------------------
 * Function: diff_compare
 *
 * Purpose: get objects from list, and check for the same type
 *
 * Return: Number of differences found
 *
 * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
 *
 * Date: May 9, 2003
 *
 *-------------------------------------------------------------------------
 */

hsize_t diff_compare( hid_t file1_id,
                      const char *file1_name,
                      const char *obj1_name,
                      int nobjects1,
                      trav_info_t *info1,
                      hid_t file2_id,
                      const char *file2_name,
                      const char *obj2_name,
                      int nobjects2,
                      trav_info_t *info2,
                      diff_opt_t *options )
{
    
    int     f1=0, f2=0;
    hsize_t nfound=0;
    
    int i = h5trav_getindex( obj1_name, nobjects1, info1 );
    int j = h5trav_getindex( obj2_name, nobjects2, info2 );
    
    if ( i == -1 )
    {
        printf( "Object <%s> could not be found in <%s>\n", obj1_name, file1_name );
        f1=1;
    }
    if ( j == -1 )
    {
        printf( "Object <%s> could not be found in <%s>\n", obj2_name, file2_name );
        f2=1;
    }
    if ( f1 || f2 ) 
    {
        options->err_stat=1;
        return 0;
    }
    
    /* use the name with "/" first, as obtained by iterator function */
    obj1_name=info1[i].name;
    obj2_name=info2[j].name;
    
    /* objects are not the same type */
    if ( info1[i].type != info2[j].type)
    {
        if (options->m_verbose)
            printf("Comparison not possible: <%s> is of type %s and <%s> is of type %s\n",
            obj1_name, get_type(info1[i].type),
            obj2_name, get_type(info2[j].type) );
        options->not_cmp=1;
        return 0;
    }
    
    nfound=diff( file1_id, 
                 obj1_name, 
                 file2_id, 
                 obj2_name, 
                 options, 
                 info1[i].type );
    
    return nfound;
}


/*-------------------------------------------------------------------------
 * Function: diff
 *
 * Purpose: switch between types and choose the diff function
 * TYPE is either
 *  H5G_GROUP		  Object is a group
 *  H5G_DATASET 	  Object is a dataset
 *  H5G_TYPE          Object is a named data type
 *  H5G_LINK          Object is a symbolic link
 *
 * Return: Number of differences found
 *
 * Programmer: Pedro Vicente, pvn@ncsa.uiuc.edu
 *
 * Date: May 9, 2003
 *
 *-------------------------------------------------------------------------
 */

hsize_t diff (hid_t file1_id,
              const char *path1,
              hid_t file2_id, 
              const char *path2, 
              diff_opt_t * options, 
              H5G_obj_t1 type)
{
    hid_t       type1_id=(-1);
    hid_t       type2_id=(-1);
    hid_t       grp1_id=(-1);
    hid_t       grp2_id=(-1);
    int ret;
    H5G_stat_t sb1;
    H5G_stat_t sb2;
    hsize_t nfound = 0;
    
    switch (type)
    {
    
   /*-------------------------------------------------------------------------
    * H5G_DATASET
    *-------------------------------------------------------------------------
    */
    case H5G_DATASET:
        
       /*-------------------------------------------------------------------------
        * verbose, always print name
        *-------------------------------------------------------------------------
        */
        if (options->m_verbose)
        {
            if (print_objname(options,(hsize_t)1))
                do_print_objname ("dataset", path1, path2);
            nfound=diff_dataset(file1_id,file2_id,path1,path2,options);
            print_found(nfound);
            
        }
       /*-------------------------------------------------------------------------
        * non verbose, check first if we have differences by enabling quiet mode
        * so that printing is off, and compare again if differences found,
        * disabling quite mode
        *-------------------------------------------------------------------------
        */
        else
        {
            if (options->m_quiet==0)
            {
                /* shut up temporarily */
                options->m_quiet=1;
                nfound=diff_dataset(file1_id,file2_id,path1,path2,options);
                /* print again */
                options->m_quiet=0;
                if (nfound)
                {
                    if (print_objname(options,nfound))
                        do_print_objname ("dataset", path1, path2);
                    nfound=diff_dataset(file1_id,file2_id,path1,path2,options);
                    print_found(nfound);
                } 
            } 
            /* in quiet mode, just count differences */
            else
            {
                nfound=diff_dataset(file1_id,file2_id,path1,path2,options);
            }
        }
        
        break;
        
   /*-------------------------------------------------------------------------
    * H5G_TYPE
    *-------------------------------------------------------------------------
    */
    case H5G_TYPE:

        if ((type1_id = H5Topen(file1_id, path1))<0)
            goto out;
        if ((type2_id = H5Topen(file2_id, path2))<0)
            goto out;
        
        if ((ret = H5Tequal(type1_id,type2_id))<0)
            goto out;
        
        /* if H5Tequal is > 0 then the datatypes refer to the same datatype */
        nfound = (ret>0) ? 0 : 1;
        
        if (print_objname(options,nfound))
            do_print_objname ("datatype", path1, path2);

        /* always print the number of differences found in verbose mode */
        if (options->m_verbose)
            print_found(nfound);
        
        /*-------------------------------------------------------------------------
         * compare attributes
         * the if condition refers to cases when the dataset is a referenced object
         *-------------------------------------------------------------------------
         */
        if (path1)
            nfound += diff_attr(type1_id,type2_id,path1,path2,options);
        
        if ( H5Tclose(type1_id)<0)
            goto out;
        if ( H5Tclose(type2_id)<0)
            goto out;
        
        break;
        
   /*-------------------------------------------------------------------------
    * H5G_GROUP
    *-------------------------------------------------------------------------
    */
    case H5G_GROUP:

        ret = HDstrcmp(path1,path2);
    
        /* if "path1" != "path2" then the groups are "different" */
        nfound = (ret!=0) ? 1 : 0;
        
        if (print_objname(options,nfound))
            do_print_objname ("group", path1, path2);

        /* always print the number of differences found in verbose mode */
        if (options->m_verbose)
            print_found(nfound);
        
        if ((grp1_id = H5Gopen(file1_id, path1))<0)
            goto out;
        if ((grp2_id = H5Gopen(file2_id, path2))<0)
            goto out;
        /*-------------------------------------------------------------------------
         * compare attributes
         * the if condition refers to cases when the dataset is a referenced object
         *-------------------------------------------------------------------------
         */
        if (path1)
            nfound += diff_attr(grp1_id,grp2_id,path1,path2,options);
        
        if ( H5Gclose(grp1_id)<0)
            goto out;
        if ( H5Gclose(grp2_id)<0)
            goto out;
        
        break;
        
        
   /*-------------------------------------------------------------------------
    * H5G_LINK
    *-------------------------------------------------------------------------
    */
    case H5G_LINK:
        {
            char *buf1 = NULL;
            char *buf2 = NULL;
            
            if (H5Gget_objinfo (file1_id, path1, FALSE, &sb1) < 0)
                goto out;
            if (H5Gget_objinfo (file1_id, path1, FALSE, &sb2) < 0)
                goto out;
            
            buf1 = HDmalloc (sb1.linklen);
            buf2 = HDmalloc (sb2.linklen);
            
            if (H5Gget_linkval (file1_id, path1, sb1.linklen, buf1) < 0)
                goto out;
            if (H5Gget_linkval (file2_id, path2, sb1.linklen, buf2) < 0)
                goto out;
            
            ret = HDstrcmp (buf1, buf2);
            
            /* if "buf1" != "buf2" then the links are "different" */
            nfound = (ret != 0) ? 1 : 0;
            
            if (print_objname (options, nfound))
                do_print_objname ("link", path1, path2);

            /* always print the number of differences found in verbose mode */
            if (options->m_verbose)
                print_found(nfound);
            
            HDfree (buf1);
            HDfree (buf2);
        }
        break;
        
    default:
        if (options->m_verbose) 
        {
            printf("Comparison not supported: <%s> and <%s> are of type %s\n",
                path1, path2, get_type(type) );
        }
        options->not_cmp=1;
        break;
 }
 
 
 return nfound;
 
out:

 options->err_stat=1;
 
 /* close */
 /* disable error reporting */
 H5E_BEGIN_TRY
 {
     H5Tclose (type1_id);
     H5Tclose (type2_id);
     H5Gclose (grp1_id);
     H5Tclose (grp2_id);
     /* enable error reporting */
 }
 H5E_END_TRY;
 
 return nfound;
}



syntax highlighted by Code2HTML, v. 0.9.1