/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by  The HDF Group and                                           *
 * The Board of Trustees of the University of Illinois.                      *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of H4H5TOOLS. The full H4H5TOOLS copyright notice,      *
 * including terms governing use, modification, and redistribution, is       *
 * contained in the COPYING file, which can be found at the root of the      *
 * source code distribution tree. The copyright notice can also be found     *
 * at https://www.hdfgroup.org/licenses.  If you do not have access to       *
 * either file, you may request a copy from help@hdfgroup.org.               * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/******************************************************************************

    Description:

1. converter

See HDF4 to HDF5 mapping specification at
(https://support.hdfgroup.org/products/hdf5_tools/h4toh5/) for the default mapping
from HDF4 object to HDF5 object.

The whole converter includes 10 files, h4toh5util.h, h4toh5main.h, h4toh5util.c, h4toh5main.c, h4toh5sds.c, h4toh5image.c,h4toh5vdata.c,h4toh5vgroup.c,h4toh5pal.c and h4toh5anno.c.

2. this file

This file describes the main driver of hdf to hdf5 converter. It checks
the inputting parameters, initializes the global tables, sets up the root level
hdf5 structure and also check the special case fof vgroup loops at HDF file.


Author:  Kent Yang(myang6@hdfgroup.org)


*****************************************************************************/


#include "h4toh5main.h"
#include "h4toh5.h"
#ifndef WIN32
#include <strings.h>
#include <string.h>
#endif


/**
* @brief no comment!
*
* I changed the whole main() function because it was hard to provide two
* additional options.
*
* @param argc as you know ...
* @param argv as you know ...
*
* @return as you know ...
*
* Exit Status:
*       EXIT_SUCCESS(0)     Succeeded.
*       EXIT_FAILURE(1)     An error occurred.
*/
int main(int argc, char **argv)
{
    int optargc = 1;

    /* Make the default option be using HDF5 dimension scale APIs and
         don't generate H4toH5 predefined attributes such as HDF4_OBJECT_TYPE etc.
         KY 2014-06-11
    */
    int h4_attr = H425_NO_PREDEF_ATTRS;

    /* int h4_attr = H425_OSPEC_ALLATTRS; */

    int status = -1;

    /* getopt(3) cannot be used here due to the long name '-na'.
     * getopt_long(3) may have legal issues.
     */
    {
        int eos2 = 0;
        int nc4hack = 0;
        int nc4strict = 0;
        int nc4fakedim = 0;
        int verbosemsg = 0;
        int hdf5latest = 0;
        int i;

        for (i = optargc; i < argc; ++i) {
            if (HDstrcmp(argv[i], "-h") == 0) {
                PrintOptions_h4toh5(0);
                return EXIT_SUCCESS;
            }
            if (HDstrcmp(argv[i], "-hv") == 0) {
                PrintOptions_h4toh5(1);
                return EXIT_SUCCESS;
            }
            /* Still keep the old syntax. */
            else if (HDstrcmp(argv[i], "-na") == 0)
                h4_attr = H425_OSPEC_NO_PREDEF_ATTRS;
            /* Generate the predefined attributes with HDF5 dimension scales. */
            else if (HDstrcmp(argv[i], "-pa") == 0)
                h4_attr = H425_ALLATTRS;
            /* Follow the old specification, this is the default option for the version 2.2.2 and before. */
            else if (HDstrcmp(argv[i], "-ospe") == 0)
                h4_attr = H425_OSPEC_ALLATTRS;
            /* No H4toH5 predefined attributes but still follow the old specification. Same as the -na option.*/
            else if (HDstrcmp(argv[i], "-osna") == 0)
                h4_attr = H425_OSPEC_NO_PREDEF_ATTRS;
            else if (HDstrcmp(argv[i], "-sv") == 0)
                h4_attr = H425_SV;
            else if (HDstrcmp(argv[i], "-eos") == 0) {
                /* The conversion of HDF-EOS2 to netCDF-4 needs to follow the old specification. */
                h4_attr = H425_OSPEC_ALLATTRS;
                eos2 = 1;
            }
            else if (HDstrcmp(argv[i], "-nc4") == 0) {
                /* The conversion of HDF-EOS2 to netCDF-4 needs to follow the old specification. */
                h4_attr = H425_OSPEC_ALLATTRS;
                nc4hack = 1;
            }
            else if (HDstrcmp(argv[i], "-nc4strict") == 0) {
                /* The conversion of HDF-EOS2 to netCDF-4 needs to follow the old specification. */
                h4_attr = H425_OSPEC_ALLATTRS;
                /* nc4strict implies nc4hack */
                nc4hack = 1;
                nc4strict = 1;
            }
            else if (HDstrcmp(argv[i], "-nc4fakedim") == 0) {
                /* The conversion of HDF-EOS2 to netCDF-4 needs to follow the old specification. */
                h4_attr = H425_OSPEC_ALLATTRS;
                /* nc4fakedim implies nc4hack */
                nc4hack = 1;
                nc4fakedim = 1;
            }
            else if (HDstrcmp(argv[i], "-v") == 0)
                verbosemsg = 1;
            else if (HDstrcmp(argv[i], "-latest") == 0)
                hdf5latest = 1;
            else
                break;
        }

        optargc = i;

#ifdef HAVE_LIBHDFEOS
#else
        if (eos2 || nc4hack || nc4strict || hdf5latest) {
            fprintf(stderr, "-eos, -nc4, -nc4strict, -nc4fakedim, or -latest is available only if this program is compiled with --with-hdfeos2 option\n");
            return EXIT_FAILURE;
        }
#endif
        if (H4toH5set_convert_flags(eos2, nc4hack, nc4strict, nc4fakedim, verbosemsg, hdf5latest) == FAIL) {
            return EXIT_FAILURE;
        }
    }

    switch (argc - optargc) {
        case 1:
            /* HDF5 file name is not given */
            status = gen_h4toh5(argv[optargc], NULL, h4_attr);
            break;
        case 2:
            status = gen_h4toh5(argv[optargc], argv[optargc + 1], h4_attr);
            break;
        default:
            fprintf(stderr, "\nError: Invalid Arguments\n");
            PrintOptions_h4toh5(0);
            break;
    }

    if (status == SUCCEED)
        return EXIT_SUCCESS;
    else
        return EXIT_FAILURE;
}

/*-------------------------------------------------------------------------
 * Function:    h4toh5
 *
 * Purpose:      This routine checks out arguments sent, makes sure that hdf4
 *                       file is valid, makes sure filename for hdf5 file is correct,
 *                       and then call other functions to handle different HDF4 objects.
 * Return:       FAIL if failed, SUCCEED if successful.
 * In:           h4_filename: HDF4 file name
 *               h5_filename: HDF5 file name
 *               h4_attr: flag to indicate the option whether to include H4TOH5
 *                        predefined attributes. This flag also indicates whether
 *                        the conversion tool follows the old H4toH5 specification or
 *                        uses HDF5 dimension scale APIs to convert SDS.
 * Out:
 *-------------------------------------------------------------------------
 */
int h4toh5(char*h4_filename, char*h5_filename,int h4_attr) {

    /* define variables for hdf4. */
    int32  file_id = -1;/* file identfier of hdf file.*/
    int sd_id = -1;
    int filetype = -1;/* 0 HDF4 file, 1 netCDF file. */

    /* define variables for hdf5. */
    hid_t  file5_id;/* hdf5 file identifier. */

    hid_t  h4toh5id;

    /* these two ids are used to create an HDF5 file that can be read by netCDF4 */
    hid_t fapl_id = H5P_DEFAULT;
    hid_t fcpl_id = H5P_DEFAULT;

    /*0. check the file type. */
    if(Hishdf(h4_filename)== 1) {
        filetype = 0;
    }
    else {
        sd_id = SDstart(h4_filename,DFACC_READ);
        if(sd_id !=FAIL) {
            filetype = 1;
            SDend(sd_id);
        }
        else {
            fprintf(stderr, "error, the file is neither hdf4 nor netcdf file. \n");
            return FAIL;
        }
    }

#ifdef HAVE_LIBHDFEOS
    if (H4toH5config_use_eos2_conversion()) {
        int gdnum, swnum, ptnum;
        gdnum = H4toH5eos_num_grid(h4_filename);
        swnum = H4toH5eos_num_swath(h4_filename);
        ptnum = H4toH5eos_num_point(h4_filename);

        if (gdnum <= 0 && swnum <= 0 && ptnum <= 0) {
            fprintf(stderr, "-eos options is given, but could not find any GRID, SWATH or POINT.\n");
            return FAIL;
        }
        if (ptnum > 0) {
            fprintf(stderr, "This HDF-EOS2 file contains at least one POINT data. Curent h4toh5 does not support this.\n");
            return FAIL;
        }

        if (H4toH5eos_test_emptydataset(h4_filename) == -1) {
            fprintf(stderr, "This HDF-EOS2 file contains a data set that does not have any field.\n");
            return FAIL;
        }
    }
#endif

    if (H4toH5config_use_netcdf4_hack()) {
#ifdef HAVE_LIBHDFEOS
        if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED)) < 0)  {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
#else
        fprintf(stderr, "error, -nc4 or -nc4strict\n");
        return FAIL;
#endif
    }
    else if (H4toH5config_use_latest_format()) {
#ifdef HAVE_LIBHDFEOS
        if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        /* This may cause a problem if H4H5TOOLS is linked with HDF5 1.6 */
        if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) {
            fprintf(stderr, "error, H5Pset_libver_bounds\n");
            return FAIL;
        }
#else
        fprintf(stderr, "error, -latest\n");
        return FAIL;
#endif
    }

    /* Add the creation order for the options that use the dimension scale APIs. */
#ifndef HAVE_LIBHDFEOS
    if(h4_attr == H425_NO_PREDEF_ATTRS || h4_attr == H425_ALLATTRS || h4_attr == H425_SV) {
        if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED)) < 0)  {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
        if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | H5P_CRT_ORDER_INDEXED)) < 0) {
            fprintf(stderr, "error, H5Pcreate fails\n");
            return FAIL;
        }
    }
#endif

    /*1. open the current HDF4 file. */
    if(filetype == 0) {
        file_id = Hopen(h4_filename, DFACC_READ, 0);
        if(file_id == FAIL) {
             fprintf(stderr, "error: no such hdf4 files. \n");
             return FAIL;
        }
    }

    /* Create HDF5 file. */
    file5_id = H5Fcreate(h5_filename,H5F_ACC_TRUNC,fcpl_id,fapl_id);

    if (file5_id < 0) {
        fprintf(stderr, "unable to create hdf5 file \n");
        Hclose(file_id);
        return FAIL;
    }

    if (H4toH5config_use_netcdf4_hack() || H4toH5config_use_latest_format()) {
        if (H5Pclose(fapl_id) < 0) {
            fprintf(stderr, "unable to close hdf5 property\n");
            Hclose(file_id);
            H5Fclose(file5_id);
            return FAIL;
        }
        if (H5Pclose(fcpl_id) < 0) {
            fprintf(stderr, "unable to close hdf5 property\n");
            Hclose(file_id);
            H5Fclose(file5_id);
            return FAIL;
        }
    }

    h4toh5id = H4toH5open_id(h4_filename,file5_id);
    if(h4toh5id < 0) {
        fprintf(stderr, "cannot initialize the H4toH5 library interface.\n");
        Hclose(file_id);
        H5Fclose(file5_id);
        return FAIL;
    }

#ifdef HAVE_LIBHDFEOS
    if (H4toH5config_use_eos2_conversion()) {
        if (H4toH5eos_initialization(h4toh5id) == FAIL) {
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in finalizing HDF-EOS2 conversion.\n");
            Hclose(file_id);
            H5Fclose(file5_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }
    }
#endif

     if(filetype <0 || filetype >2) {
         fprintf(stderr, "wrong file; cannot be converted \n");
        Hclose(file_id);
        H5Fclose(file5_id);
        return FAIL;
     }

    /* convert all objects in lone vgroups into corresponding hdf5 objects.
         if(h4toh5lonevgs(file_id,sd_id,h5_root,h5_dimg,h5_palg,h4_attr)== FAIL)*/
    if(filetype == 0) {
        if(h4toh5lonevgs(h4toh5id,file_id,h4_attr)== FAIL)
        {
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in translating lone vgroup into hdf5 objects.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }

        /*convert all objects in group rings into corresponding hdf5 objects.
        if(h4toh5vgrings(file_id,sd_id,h5_root,h5_dimg,h5_palg,h4_attr) == FAIL)*/
        if(h4toh5vgrings(h4toh5id,file_id,h4_attr) == FAIL){
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in translating vgroup rings into hdf5 objects.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }

        /*convert all independent lone vdata into corresponding hdf5 datasets with
            compound data type. */

        if(H4toH5all_lone_vdata(h4toh5id,"/",h4_attr)==FAIL){
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in translating lone independent vdata into hdf5 objects.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }
    }

    /* convert SD and GR global images */
    if(H4toH5glo_sds_attr(h4toh5id,h4_attr)<0){
        H4toH5error_get(h4toh5id);
        fprintf(stderr, "error in converting global SD attributes.\n");
        if(filetype == 0)
             Hclose(file_id);
        else SDend(sd_id);
        H4toH5close(h4toh5id);
        return FAIL;
    }

    if(filetype ==0) {
        if(H4toH5glo_image_attr(h4toh5id,h4_attr)<0){
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in converting global GR attributes.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }

        /*** convert hdf file annotations into hdf5 attributes under the root.***/
        /*  if(Annofil_h4_to_h5(file_id,h5_root) == FAIL) */
        if(H4toH5anno_file_all_labels(h4toh5id)<0){
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in converting file annotations into root attributes.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }

        if(H4toH5anno_file_all_descs(h4toh5id)<0){
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in converting file annotations into root attributes.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }
    }

    /*** deal with untouched sds objects.convert them into hdf5 datasets under root group.***/
    if(H4toH5all_lone_sds(h4toh5id,"/",NULL,1,h4_attr) == FAIL) {
        H4toH5error_get(h4toh5id);
        fprintf(stderr, "error in converting unvisited sds objects into hdf5 file.\n");
        if(filetype == 0)
             Hclose(file_id);
        else SDend(sd_id);
        H5Fclose(file5_id);
        H4toH5close(h4toh5id);
        return FAIL;
    }

    /*** deal with untouched image objects. convert them into hdf5 datasets under root group. ***/
    if(filetype == 0) {
        if(H4toH5all_lone_image(h4toh5id,"/",NULL,1,h4_attr) == FAIL) {
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in converting unvisited image objects into hdf5 file.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }
    }
#ifdef HAVE_LIBHDFEOS
    if (H4toH5config_use_eos2_conversion()) {
        if (H4toH5eos_finalization(h4toh5id) == FAIL) {
            H4toH5error_get(h4toh5id);
            fprintf(stderr, "error in finalizing HDF-EOS2 conversion.\n");
            Hclose(file_id);
            H4toH5close(h4toh5id);
            return FAIL;
        }
    }
#endif

    Hclose(file_id);
    if(FAIL == H4toH5close(h4toh5id)) {
        fprintf(stderr,"error in close the HDF4 to HDF5 interface. \n");
        return FAIL;
    }
    return SUCCEED;
}

/*-------------------------------------------------------------------------
 * Function:        h4toh5lonevgs
 *
 * Purpose:         Recursively convert hdf4 objects in lone vgroups into
 *                           corresponding hdf5 datasets
 *
 * Return:  FAIL if failed, SUCCEED if successful.
 *
 * In :             h4toh5id: h4toh5 identifier
 *                  file_id:  hdf file id
 *                  h4_attr:  flag to indicate the option whether to include H4TOH5
 *                            predefined attributes. This flag also indicates whether
 *                            the conversion tool follows the old H4toH5 specification or
 *                            uses HDF5 dimension scale APIs to convert SDS.

 *Out:
 *Modification:
 *-------------------------------------------------------------------------
 */
int h4toh5lonevgs(hid_t h4toh5id,int32 file_id,int h4_attr) {

    int32  vgroup_id;
/*  char     vgroup_name[VGNAMELENMAX]; */
    char*  h5pgroup_name;
    int      num_lonevg; /* number of lone vgroup.*/
    int32  *ref_array;
    int32  istat;
    int      lone_vg_number;

    istat = Vstart(file_id);
    if (istat == FAIL) {
        fprintf(stderr, "unable to start hdf4 V interface.\n");
        return FAIL;
    }

    num_lonevg = Vlone(file_id,NULL,0);

    if (num_lonevg == FAIL) {
        fprintf(stderr, "error in obtaining lone vgroup number. \n");
        return FAIL;
    }

    /* obtain object reference array. */

    /* if no lone vgroup, quit from this function. */
    if(num_lonevg == 0)
        return SUCCEED;

    ref_array = (int32 *)malloc(sizeof(int32) *num_lonevg);

    if(ref_array == NULL) {
        fprintf(stderr, "error in allocating memory for ref_array.\n");
        return FAIL;
    }

    num_lonevg = Vlone(file_id,ref_array,num_lonevg);

    /* walk through every lone group in the file */

    for(lone_vg_number = 0; lone_vg_number < num_lonevg;
            lone_vg_number++) {
        int adv_group;

/*      util_h4toh5_ZeroMemory(vgroup_name,VGNAMELENMAX);*/
        vgroup_id = Vattach(file_id,ref_array[lone_vg_number],"r");
        if(vgroup_id ==FAIL) {
            fprintf(stderr, "error in attaching lone vgroup.\n");
            free(ref_array);
            return FAIL;
        }
/*      if(Vgetname(vgroup_id,vgroup_name)==FAIL){
            fprintf(stderr, "error in obtaining vgroup name.\n");
            return FAIL;
        }
*/
        h5pgroup_name = H4toH5get_group_name(h4toh5id,vgroup_id,NULL);
        adv_group = H4toH5adv_group(h4toh5id,vgroup_id,"/",h5pgroup_name+1,h4_attr);
        Vdetach(vgroup_id);
        free(h5pgroup_name);
        if (adv_group == FAIL) {
/*          fprintf(stderr, "error in handling a lone vgroup.\n"); */
            free(ref_array);
            return FAIL;
        }
    }

    free(ref_array);
    Vend(file_id);

    return SUCCEED;
}



/*-------------------------------------------------------------------------
 * Function:        h4toh5vgrings
 *
 * Purpose:         Recursively convert objects at special hdf4 vgroups
 *                          (vgroup rings)
 *                          to objects of corresponding hdf5 groups. The strategy here
 *                          is to arbitrarily grab any vgroup in the group ring and put it
 *                          under the hdf5 root group.
 *
 * Return:  FAIL if failed, SUCCEED if successful.
 * In :             h4toh5id: h4toh5 identifier
 *                  file_id:    hdf file id
 *                  h4_attr:    flag to indicate the option whether to include H4TOH5
 *                              predefined attributes. This flag also indicates whether
 *                              the conversion tool follows the old H4toH5 specification or
 *                              uses HDF5 dimension scale APIs to convert SDS.

 Out:
 Modification:
 *-------------------------------------------------------------------------
 */

int h4toh5vgrings(hid_t h4toh5id,int32 file_id,int h4_attr){

    int32  vgroup_id;
    int32  istat = -1;
    int32  ref_num;
    int      check_vgroup;
    char* h5pgroup_name;
    char* h5obj_name;
    check_vgroup = -1;

    istat = Vstart(file_id);
    if (istat == FAIL) {
        fprintf(stderr, "unable to start hdf4 V interface.\n");
        return FAIL;
    }

    ref_num = Vgetid(file_id,-1);

    while (ref_num != -1) {

        /* if we find a group that is not touched, grab it under root group.*/

        h5obj_name = H4toH5check_object(h4toh5id,ref_num,DFTAG_VG,&check_vgroup);
        if(h5obj_name == NULL && check_vgroup == -1) {
            fprintf(stderr, "error in obtaining object information\n");
            return FAIL;
        }
        if(h5obj_name == NULL && check_vgroup == 1) {
            fprintf(stderr, "the name should not be NULL\n");
            return FAIL;
        }

        if(check_vgroup == 1)
            free(h5obj_name);

        if (check_vgroup == 0){
            vgroup_id = Vattach(file_id,ref_num,"r");
            if(vgroup_id ==FAIL) {
                fprintf(stderr, "error in attaching group in a group ring. \n");
                return FAIL;
            }
            h5pgroup_name = H4toH5get_group_name(h4toh5id,vgroup_id,NULL);
            H4toH5adv_group(h4toh5id,vgroup_id,"/",h5pgroup_name+1,h4_attr);
            free(h5pgroup_name);
            Vdetach(vgroup_id);
        }
        ref_num = Vgetid(file_id,ref_num);
    }

    Vend(file_id);
    return SUCCEED;

}



/* Function util_h4toh5_ZeroMemory
     Purpose: Zero out memory
     return:    None
     In: size_t n(DWORD in windows)
            void* s(PVOID in windows)
*/
void util_h4toh5_ZeroMemory(void*s,size_t n) {
#ifdef WIN32
         ZeroMemory(s,n);
#else
        memset(s,0,n);
#endif
}





/********The following routines are adapted from h5toh4 converter. *******/
/*****************************************************************************

    Routine: test_file

    Description: Test a file for read/write - ability.

    Input: filename - Unix filename

    Output: function return,    global variable - errno

*****************************************************************************/

int util_test_file(char *filename,int oflag,mode_t mode)
{
    int fid;

    errno = 0;

    fid = open(filename, oflag, mode);
    if (fid < 0) {
        perror(filename);
        return errno;
    }
    close(fid);

    return errno;

}


/*****************************************************************************

     Routine: test_dir

     Description: Test pathway to determine if it is a directory

     Input: path    - pathname given

     Output:    function return TRUE/FALSE

*****************************************************************************/

int util_test_dir(char *path)
{

    struct stat buf;
    struct stat *buf_ptr;
    int idir;

    buf_ptr = &buf;

    idir = stat(path, buf_ptr);
    if (idir < 0) {
        if (errno == 2) {
            return 0;
        } else {
            perror(path);
        }
    }

    return S_ISDIR(buf_ptr->st_mode);
}

/*****************************************************************************

        Routine: BuildFilename()

        Description: Build a filename with new extension

        Input: filename- present filename
        ext - extension to root of filename
        Output: (filename:r).ext

*****************************************************************************/

char *util_BuildFilename(char *filename, char *ext)
{
    /* build outgoing filename */

    char *filename_out;
    char *lastper_ptr;
    char *lastdir_ptr;
    int root_len;

    lastper_ptr = strrchr(filename,'.');
    lastdir_ptr = strrchr(filename,'/');

    if ( lastper_ptr <= lastdir_ptr ) { /* no extension */
        root_len = strlen(filename);
    } else {    /* existing extension */
        root_len = (int)(lastper_ptr - filename);
    }

    filename_out = (char *)HDmalloc(root_len + strlen(ext) + 2);
    filename_out = strncpy(filename_out, filename, (size_t)root_len);
    filename_out[root_len] = '\0';
    filename_out = strcat(filename_out,".");
    filename_out = strcat(filename_out,ext);

    return filename_out;
}


/*****************************************************************************

    Routine: PrintOptions_h4toh5()

    Description: This routine prints the acceptable argument formats out to stderr.

    Input: None

    Output: output to stderr

*****************************************************************************/

void PrintOptions_h4toh5(int version_info)
{
    fprintf(stderr,"\nUsage: ");
    fprintf(stderr,"\n  h4toh5 -h (gives this print-out)\n");
    fprintf(stderr,"    h4toh5 -hv (gives this print-out plus hdf library versions)\n");
    fprintf(stderr,"    h4toh5 input.hdf output.h5\n");
    fprintf(stderr,"    h4toh5 input.hdf\n");
    fprintf(stderr,"    h4toh5 -pa input.hdf output.h5\n");
    fprintf(stderr,"    h4toh5 -ospe input.hdf output.h5\n");
    fprintf(stderr,"    h4toh5 -osna input.hdf output.h5\n");
    fprintf(stderr,"    h4toh5 -na input.hdf output.h5\n\n");
    fprintf(stderr,"    h4toh5 -sv input.hdf output.h5\n");
    fprintf(stderr,"    h4toh5:          Use the HDF5 dimension scale APIs.\n");
    fprintf(stderr,"                             Not add the H4toH5 predefined attributes.\n\n");
    fprintf(stderr,"    h4toh5 -pa:  Use the HDF5 dimension scale APIs.\n");
    fprintf(stderr,"                             Add the H4toH5 predefined attributes.\n");
    fprintf(stderr,"    h4toh5 -ospe: Follow the old specification.\n");
    fprintf(stderr,"    h4toh5 -na or h4toh5 -osna: \n");
    fprintf(stderr,"                             Follow the old specification.\n");
    fprintf(stderr,"                             Not add the H4toH5 predefined attributes.\n");
    fprintf(stderr,"    h4toh5 -sv:  Map one field vdata to an HDF5 atomic datatype dataset.\n");
    fprintf(stderr,"                 Use the HDF5 dimension scale APIs.             \n");
    fprintf(stderr,"                 Not add the H4toH5 predefined attributes.             \n");
    fprintf(stderr,"    H4toH5 predefined attributes provide the HDF4 object type and reference number information.\n");
    fprintf(stderr,"    H4toH5 specification can be found under https://support.hdfgroup.org/products/hdf5_tools/h4toh5/ .\n");
    if(version_info > 0) {
        fprintf(stderr,"    hdf4:            %s.\n", H4TOH5_LIBVER_HDF_STRING);
        fprintf(stderr,"    hdf5:            %s.\n", H4TOH5_LIBVER_HDF5_STRING);
        fprintf(stderr,"    h4toh5:          %s.\n", H4TOH5_LIBVER_STRING);
    }
}


/*****************************************************************************

    Routine: gen_h4toh5()

    Description: This routine prints the acceptable argument formats out to stderr.

    Input: h4_filename: HDF4 file name
           h5_filename: HDF5 file name
           h4_attr:     flag to indicate the option whether to include H4TOH5
                        predefined attributes. This flag also indicates whether
                        the conversion tool follows the H4toH5 specification or
                         uses HDF5 dimension scale APIs to convert SDS.

    Output: -1 FAIL
            0  SUCCEED

*****************************************************************************/


int gen_h4toh5(char*h4_filename,char*h5_filename,int h4_attr){
    char* h5_extension;
    int status;

#ifndef WIN32
    if (util_test_file(h4_filename,O_EXCL,292) != 0 ) {
        /* 292 Decimal - 0444 Octal, a+r */
        fprintf(stderr, "the current hdf4 file name is not set properly.\n");
        status = -1;
        return status;
    }
    if (util_test_dir(h4_filename) != 0 ) {
        fprintf(stderr,"%s: Is a directory\n",h4_filename);
        status = -1;
        return status;
    }
#endif
    /*0. check whether this file is an hdf file. */

#if 0
    if(!Hishdf(h4_filename)){
        fprintf(stderr, "error: not an hdf file. \n");
        fprintf(stderr, "the file will not be converted. \n");
        status = -1;
        return status;
    }
#endif

    if(h5_filename == NULL){
        h5_extension = HDstrdup("h5");
        h5_filename = util_BuildFilename(h4_filename,h5_extension);
        free(h5_extension);
        if (h5_filename == NULL) {
            fprintf(stderr, "error in creating hdf5 file name.\n");
            status = -1;
            return status;
        }
#ifndef WIN32
        if (util_test_file(h5_filename,O_CREAT|O_EXCL,436) != 0) {
            /* 436 Decimal - 0664 Octal, ug+rw,o+r */
            fprintf(stderr, "permission of the hdf5 file is not set properly.\n");
            free(h5_filename);
            status = -1;
            return status;
        }
#endif

        status = h4toh5(h4_filename, h5_filename,h4_attr);
        if ( status == FAIL ) {
            fprintf(stderr, "error in converting %s into %s\n",h4_filename,h5_filename);
            free(h5_filename);
            return status;
        }
        if (h5_filename != NULL) {
            HDfree(h5_filename);
        }
        return 0;
    }

    else {

#ifndef WIN32
        if (util_test_file(h5_filename,O_CREAT|O_RDWR,436) != 0) { /* 436 Decimal - 0664 Octal, ug+rw,o+r */
            fprintf(stderr, "permission of hdf5 file is not set properly.\n");
            free(h5_filename);
            status = -1;
            return status;
        }

        if (util_test_dir(h4_filename) != 0 ) {
            fprintf(stderr,"%s: Is a directory\n",h4_filename);
            status = -1;
            return status;
        }

#endif
    status = h4toh5(h4_filename, h5_filename,h4_attr);
        if ( status == FAIL ) {
            fprintf(stderr, "error in converting %sinto %s\n",h4_filename,h5_filename);
            return status;
        }
    }
    return 0;
}

/* vi:set ts=8 sts=2 sw=2: */
