/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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 COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * Portions of this work are derived from _Obfuscated C and Other Mysteries_,
 * by Don Libes, copyright (c) 1993 by John Wiley & Sons, Inc.
 */

#include "h5tools.h"
#include "h5tools_utils.h"
#include "H5private.h"
#include "h5trav.h"

/* global variables */
unsigned h5tools_nCols = 80;
/* ``get_option'' variables */
int         opt_err = 1;    /*get_option prints errors if this is on */
int         opt_ind = 1;    /*token pointer                          */
const char *opt_arg;        /*flag argument (or value)               */
static int  h5tools_d_status = 0;
static const char  *h5tools_progname = "h5tools";

/*
 * The output functions need a temporary buffer to hold a piece of the
 * dataset while it's being printed. This constant sets the limit on the
 * size of that temporary buffer in bytes. For efficiency's sake, choose the
 * largest value suitable for your machine (for testing use a small value).
 */
/* Maximum size used in a call to malloc for a dataset */
hsize_t H5TOOLS_MALLOCSIZE = (256 * 1024 * 1024);  /* 256 MB */
/* size of hyperslab buffer when a dataset is bigger than H5TOOLS_MALLOCSIZE */
hsize_t H5TOOLS_BUFSIZE = ( 32 * 1024 * 1024);  /* 32 MB */


/* ``parallel_print'' variables */
unsigned char  g_Parallel = 0;  /*0 for serial, 1 for parallel */
char     outBuff[OUTBUFF_SIZE];
unsigned outBuffOffset;
FILE*    overflow_file = NULL;

/* local functions */
static void init_table(table_t **tbl);
#ifdef H5DUMP_DEBUG
static void dump_table(char* tablename, table_t *table);
#endif  /* H5DUMP_DEBUG */
static void add_obj(table_t *table, haddr_t objno, const char *objname, hbool_t recorded);

/*-------------------------------------------------------------------------
 * Function: parallel_print
 *
 * Purpose:  wrapper for printf for use in parallel mode.
 *-------------------------------------------------------------------------
 */
void
parallel_print(const char* format, ...)
{
    int  bytes_written;
    va_list ap;

    HDva_start(ap, format);

    if(!g_Parallel)
        HDvprintf(format, ap);
    else {
        if(overflow_file == NULL) /*no overflow has occurred yet */ {
            bytes_written = HDvsnprintf(outBuff + outBuffOffset, OUTBUFF_SIZE - outBuffOffset, format, ap);
            HDva_end(ap);
            HDva_start(ap, format);

            if((bytes_written < 0) || ((unsigned)bytes_written >= (OUTBUFF_SIZE - outBuffOffset))) {
                /* Terminate the outbuff at the end of the previous output */
                outBuff[outBuffOffset] = '\0';

                overflow_file = HDtmpfile();
                if(overflow_file == NULL)
                    HDfprintf(rawerrorstream, "warning: could not create overflow file.  Output may be truncated.\n");
                else
                    bytes_written = HDvfprintf(overflow_file, format, ap);
            }
            else
                outBuffOffset += (unsigned)bytes_written;
        }
        else
            bytes_written = HDvfprintf(overflow_file, format, ap);

    }
    HDva_end(ap);
}


/*-------------------------------------------------------------------------
 * Function: error_msg
 *
 * Purpose:  Print a nicely formatted error message to stderr flushing the
 *              stdout stream first.
 *
 * Return:   Nothing
 *-------------------------------------------------------------------------
 */
void
error_msg(const char *fmt, ...)
{
    va_list ap;

    HDva_start(ap, fmt);
    FLUSHSTREAM(rawattrstream);
    FLUSHSTREAM(rawdatastream);
    FLUSHSTREAM(rawoutstream);
    HDfprintf(rawerrorstream, "%s error: ", h5tools_getprogname());
    HDvfprintf(rawerrorstream, fmt, ap);

    HDva_end(ap);
}


/*-------------------------------------------------------------------------
 * Function: warn_msg
 *
 * Purpose:  Print a nicely formatted warning message to stderr flushing
 *              the stdout stream first.
 *
 * Return:   Nothing
 *-------------------------------------------------------------------------
 */
void
warn_msg(const char *fmt, ...)
{
    va_list ap;

    HDva_start(ap, fmt);
    FLUSHSTREAM(rawattrstream);
    FLUSHSTREAM(rawdatastream);
    FLUSHSTREAM(rawoutstream);
    HDfprintf(rawerrorstream, "%s warning: ", h5tools_getprogname());
    HDvfprintf(rawerrorstream, fmt, ap);
    HDva_end(ap);
}

/*-------------------------------------------------------------------------
 * Function: help_ref_msg
 *
 * Purpose:  Print a message to refer help page
 *
 * Return:   Nothing
 *-------------------------------------------------------------------------
 */
void
help_ref_msg(FILE *output)
{
    HDfprintf(output, "Try '-h' or '--help' for more information or ");
    HDfprintf(output, "see the <%s> entry in the 'HDF5 Reference Manual'.\n",h5tools_getprogname());
}


/*-------------------------------------------------------------------------
 * Function: get_option
 *
 * Purpose:  Determine the command-line options a user specified. We can
 *           accept both short and long type command-lines.
 *
 * Return:  Success:    The short valued "name" of the command line
 *              parameter or EOF if there are no more
 *              parameters to process.
 *
 *          Failure:    A question mark.
 *-------------------------------------------------------------------------
 */
int
get_option(int argc, const char **argv, const char *opts, const struct long_options *l_opts)
{
    static int sp = 1;    /* character index in current token */
    int opt_opt = '?';    /* option character passed back to user */

    if (sp == 1) {
        /* check for more flag-like tokens */
        if (opt_ind >= argc || argv[opt_ind][0] != '-' || argv[opt_ind][1] == '\0') {
            return EOF;
        }
        else if (HDstrcmp(argv[opt_ind], "--") == 0) {
            opt_ind++;
            return EOF;
        }
    }

    if (sp == 1 && argv[opt_ind][0] == '-' && argv[opt_ind][1] == '-') {
        /* long command line option */
        const char *arg = &argv[opt_ind][2];
        int i;

        for (i = 0; l_opts && l_opts[i].name; i++) {
            size_t len = HDstrlen(l_opts[i].name);

            if (HDstrncmp(arg, l_opts[i].name, len) == 0) {
                /* we've found a matching long command line flag */
                opt_opt = l_opts[i].shortval;

                if (l_opts[i].has_arg != no_arg) {
                    if (arg[len] == '=') {
                        opt_arg = &arg[len + 1];
                    }
                    else if (l_opts[i].has_arg != optional_arg) {
                        if (opt_ind < (argc - 1))
                            if (argv[opt_ind + 1][0] != '-')
                                opt_arg = argv[++opt_ind];
                    }
                    else if (l_opts[i].has_arg == require_arg) {
                        if (opt_err)
                            HDfprintf(rawerrorstream,
                                    "%s: option required for \"--%s\" flag\n",
                                    argv[0], arg);

                        opt_opt = '?';
                    }
                    else
                        opt_arg = NULL;
                }
                else {
                    if (arg[len] == '=') {
                        if (opt_err)
                            HDfprintf(rawerrorstream,
                                    "%s: no option required for \"%s\" flag\n",
                                    argv[0], arg);

                        opt_opt = '?';
                    }
                    opt_arg = NULL;
                }
                break;
            }
        }

        if (l_opts[i].name == NULL) {
            /* exhausted all of the l_opts we have and still didn't match */
            if (opt_err)
                HDfprintf(rawerrorstream, "%s: unknown option \"%s\"\n", argv[0], arg);

            opt_opt = '?';
        }

        opt_ind++;
        sp = 1;
    }
    else {
        register char *cp;    /* pointer into current token */

        /* short command line option */
        opt_opt = argv[opt_ind][sp];

        if (opt_opt == ':' || (cp = HDstrchr(opts, opt_opt)) == 0) {
            if (opt_err)
                HDfprintf(rawerrorstream, "%s: unknown option \"%c\"\n",
                        argv[0], opt_opt);

            /* if no chars left in this token, move to next token */
            if (argv[opt_ind][++sp] == '\0') {
                opt_ind++;
                sp = 1;
            }
            return '?';
        }

        if (*++cp == ':') {
            /* if a value is expected, get it */
            if (argv[opt_ind][sp + 1] != '\0') {
                /* flag value is rest of current token */
                opt_arg = &argv[opt_ind++][sp + 1];
            }
            else if (++opt_ind >= argc) {
                if (opt_err)
                    HDfprintf(rawerrorstream,
                            "%s: value expected for option \"%c\"\n",
                            argv[0], opt_opt);

                opt_opt = '?';
            }
            else {
                /* flag value is next token */
                opt_arg = argv[opt_ind++];
            }

            sp = 1;
        }
        /* wildcard argument */
        else if (*cp == '*') {
            /* check the next argument */
            opt_ind++;
            /* we do have an extra argument, check if not last */
            if ( (opt_ind+1) < argc ) {
                if ( argv[opt_ind][0] != '-' ) {
                    opt_arg = argv[opt_ind++];
                }
                else {
                    opt_arg = NULL;
                }
            }
            else {
                opt_arg = NULL;
            }
        }
        else {
            /* set up to look at next char in token, next time */
            if (argv[opt_ind][++sp] == '\0') {
                /* no more in current token, so setup next token */
                opt_ind++;
                sp = 1;
            }
            opt_arg = NULL;
        }
    }

    /* return the current flag character found */
    return opt_opt;
}


/*-------------------------------------------------------------------------
 * Function: indentation
 *
 * Purpose:  Print spaces for indentation
 *
 * Return:   void
 *-------------------------------------------------------------------------
 */
void
indentation(unsigned x)
{
    if (x < h5tools_nCols) {
        while (x-- > 0)
            PRINTVALSTREAM(rawoutstream, " ");
    }
    else {
        HDfprintf(rawerrorstream, "error: the indentation exceeds the number of cols.\n");
        HDexit(1);
    }
}


/*-------------------------------------------------------------------------
 * Function: print_version
 *
 * Purpose:  Print the program name and the version information which is
 *           defined the same as the HDF5 library version.
 *
 * Return:   void
 *-------------------------------------------------------------------------
 */
void
print_version(const char *progname)
{
    PRINTSTREAM(rawoutstream, "%s: Version %u.%u.%u%s%s\n",
           progname, H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE,
           ((const char *)H5_VERS_SUBRELEASE)[0] ? "-" : "", H5_VERS_SUBRELEASE);
}


/*-------------------------------------------------------------------------
 * Function:    init_table
 *
 * Purpose:     allocate and initialize tables for shared groups, datasets,
 *              and committed types
 *
 * Return:      void
 *-------------------------------------------------------------------------
 */
static void
init_table(table_t **tbl)
{
    table_t *table = (table_t *)HDmalloc(sizeof(table_t));

    table->size = 20;
    table->nobjs = 0;
    table->objs = (obj_t *)HDmalloc(table->size * sizeof(obj_t));

    *tbl = table;
}


/*-------------------------------------------------------------------------
 * Function:    free_table
 *
 * Purpose:     free tables for shared groups, datasets,
 *              and committed types
 *
 * Return:      void
 *-------------------------------------------------------------------------
 */
void
free_table(table_t *table)
{
    unsigned u;         /* Local index value */

    /* Free the names for the objects in the table */
    for(u = 0; u < table->nobjs; u++)
        if(table->objs[u].objname)
            HDfree(table->objs[u].objname);

    HDfree(table->objs);
}

#ifdef H5DUMP_DEBUG

/*-------------------------------------------------------------------------
 * Function:    dump_table
 *
 * Purpose:     display the contents of tables for debugging purposes
 *
 * Return:      void
 *-------------------------------------------------------------------------
 */
static void
dump_table(char* tablename, table_t *table)
{
    unsigned u;

    PRINTSTREAM(rawoutstream,"%s: # of entries = %d\n", tablename,table->nobjs);
    for (u = 0; u < table->nobjs; u++)
        PRINTSTREAM(rawoutstream,"%a %s %d %d\n", table->objs[u].objno,
           table->objs[u].objname,
           table->objs[u].displayed, table->objs[u].recorded);
}


/*-------------------------------------------------------------------------
 * Function:    dump_tables
 *
 * Purpose:     display the contents of tables for debugging purposes
 *
 * Return:      void
 *-------------------------------------------------------------------------
 */
void
dump_tables(find_objs_t *info)
{
    dump_table("group_table", info->group_table);
    dump_table("dset_table", info->dset_table);
    dump_table("type_table", info->type_table);
}
#endif  /* H5DUMP_DEBUG */


/*-------------------------------------------------------------------------
 * Function:    search_obj
 *
 * Purpose:     search the object specified by objno in the table
 *
 * Return:      Success:    an integer, the location of the object
 *
 *              Failure:    FAIL   if object is not found
 *-------------------------------------------------------------------------
 */
H5_ATTR_PURE obj_t *
search_obj(table_t *table, haddr_t objno)
{
    unsigned u;

    for(u = 0; u < table->nobjs; u++)
        if(table->objs[u].objno == objno)
            return &(table->objs[u]);

    return NULL;
}


/*-------------------------------------------------------------------------
 * Function:    find_objs_cb
 *
 * Purpose:     Callback to find objects, committed types and store them in tables
 *
 * Return:      Success:    SUCCEED
 *
 *              Failure:    FAIL
 *-------------------------------------------------------------------------
 */
static herr_t
find_objs_cb(const char *name, const H5O_info_t *oinfo, const char *already_seen, void *op_data)
{
    find_objs_t *info = (find_objs_t*)op_data;
    herr_t ret_value = 0;

    switch(oinfo->type) {
        case H5O_TYPE_GROUP:
            if(NULL == already_seen)
                add_obj(info->group_table, oinfo->addr, name, TRUE);
            break;

        case H5O_TYPE_DATASET:
            if(NULL == already_seen) {
                hid_t dset = -1;

                /* Add the dataset to the list of objects */
                add_obj(info->dset_table, oinfo->addr, name, TRUE);

                /* Check for a dataset that uses a named datatype */
                if((dset = H5Dopen2(info->fid, name, H5P_DEFAULT)) >= 0) {
                    hid_t type = H5Dget_type(dset);

                    if(H5Tcommitted(type) > 0) {
                        H5O_info_t type_oinfo;

                        H5Oget_info2(type, &type_oinfo, H5O_INFO_BASIC);
                        if(search_obj(info->type_table, type_oinfo.addr) == NULL)
                            add_obj(info->type_table, type_oinfo.addr, name, FALSE);
                    } /* end if */

                    H5Tclose(type);
                    H5Dclose(dset);
                } /* end if */
                else
                    ret_value = FAIL;
            } /* end if */
            break;

        case H5O_TYPE_NAMED_DATATYPE:
            if(NULL == already_seen) {
                obj_t *found_obj;

                if((found_obj = search_obj(info->type_table, oinfo->addr)) == NULL)
                    add_obj(info->type_table, oinfo->addr, name, TRUE);
                else {
                    /* Use latest version of name */
                    HDfree(found_obj->objname);
                    found_obj->objname = HDstrdup(name);

                    /* Mark named datatype as having valid name */
                    found_obj->recorded = TRUE;
                } /* end else */
            } /* end if */
            break;

        case H5O_TYPE_UNKNOWN:
        case H5O_TYPE_NTYPES:
        default:
            break;
    } /* end switch */

    return ret_value;
}


/*-------------------------------------------------------------------------
 * Function:    init_objs
 *
 * Purpose:     Initialize tables for groups, datasets & named datatypes
 *
 * Return:      Success:    SUCCEED
 *
 *              Failure:    FAIL
 *-------------------------------------------------------------------------
 */
herr_t
init_objs(hid_t fid, find_objs_t *info, table_t **group_table,
    table_t **dset_table, table_t **type_table)
{
    herr_t ret_value = SUCCEED;

    /* Initialize the tables */
    init_table(group_table);
    init_table(dset_table);
    init_table(type_table);

    /* Init the find_objs_t */
    info->fid = fid;
    info->group_table = *group_table;
    info->type_table = *type_table;
    info->dset_table = *dset_table;

    /* Find all shared objects */
    if((ret_value = h5trav_visit(fid, "/", TRUE, TRUE, find_objs_cb, NULL, info, H5O_INFO_BASIC)) < 0)
        HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "finding shared objects failed")

done:
    /* Release resources */
    if(ret_value < 0) {
        free_table(*group_table);
        info->group_table = NULL;
        free_table(*type_table);
        info->type_table = NULL;
        free_table(*dset_table);
        info->dset_table = NULL;
    }
    return ret_value;
}


/*-------------------------------------------------------------------------
 * Function:    add_obj
 *
 * Purpose:     add a shared object to the table
 *              realloc the table if necessary
 *
 * Return:      void
 *-------------------------------------------------------------------------
 */
static void
add_obj(table_t *table, haddr_t objno, const char *objname, hbool_t record)
{
    size_t u;

    /* See if we need to make table larger */
    if(table->nobjs == table->size) {
        table->size *= 2;
        table->objs = (struct obj_t *)HDrealloc(table->objs, table->size * sizeof(table->objs[0]));
    } /* end if */

    /* Increment number of objects in table */
    u = table->nobjs++;

    /* Set information about object */
    table->objs[u].objno = objno;
    table->objs[u].objname = HDstrdup(objname);
    table->objs[u].recorded = record;
    table->objs[u].displayed = 0;
}


#ifndef H5_HAVE_TMPFILE
/*-------------------------------------------------------------------------
 * Function:    tmpfile
 *
 * Purpose:     provide tmpfile() function when it is not supported by the
 *              system.  Always return NULL for now.
 *
 * Return:      a stream description when succeeds.
 *              NULL if fails.
 *-------------------------------------------------------------------------
 */
FILE *
tmpfile(void)
{
    return NULL;
}

#endif

/*-------------------------------------------------------------------------
 * Function: H5tools_get_symlink_info
 *
 * Purpose: Get symbolic link (soft, external) info and its target object type
            (dataset, group, named datatype) and path, if exist
 *
 * Patameters:
 *  - [IN]  fileid : link file id
 *  - [IN]  linkpath : link path
 *  - [OUT] link_info: returning target object info (h5tool_link_info_t)
 *
 * Return:
 *   2 : given pathname is object
 *   1 : Succeed to get link info.
 *   0 : Detected as a dangling link
 *  -1 : H5 API failed.
 *
 * NOTE:
 *  link_info->trg_path must be freed out of this function
 *-------------------------------------------------------------------------*/
int
H5tools_get_symlink_info(hid_t file_id, const char * linkpath, h5tool_link_info_t *link_info, hbool_t get_obj_type)
{
    htri_t l_ret;
    H5O_info_t trg_oinfo;
    hid_t fapl = H5P_DEFAULT;
    hid_t lapl = H5P_DEFAULT;
    int   ret_value = -1; /* init to fail */

    /* init */
    link_info->trg_type = H5O_TYPE_UNKNOWN;

    /* if path is root, return group type */
    if(!HDstrcmp(linkpath,"/")) {
        link_info->trg_type = H5O_TYPE_GROUP;
        HGOTO_DONE(2);
    }

    /* check if link itself exist */
    if(H5Lexists(file_id, linkpath, H5P_DEFAULT) <= 0) {
        if(link_info->opt.msg_mode == 1)
            parallel_print("Warning: link <%s> doesn't exist \n",linkpath);
        HGOTO_DONE(FAIL);
    } /* end if */

    /* get info from link */
    if(H5Lget_info(file_id, linkpath, &(link_info->linfo), H5P_DEFAULT) < 0) {
        if(link_info->opt.msg_mode == 1)
            parallel_print("Warning: unable to get link info from <%s>\n",linkpath);
        HGOTO_DONE(FAIL);
    } /* end if */

    /* given path is hard link (object) */
    if(link_info->linfo.type == H5L_TYPE_HARD)
        HGOTO_DONE(2);

    /* trg_path must be freed out of this function when finished using */
    if((link_info->trg_path = (char*)HDcalloc(link_info->linfo.u.val_size, sizeof(char))) == NULL) {
        if(link_info->opt.msg_mode == 1)
            parallel_print("Warning: unable to allocate buffer for <%s>\n",linkpath);
        HGOTO_DONE(FAIL);
    } /* end if */

    /* get link value */
    if(H5Lget_val(file_id, linkpath, (void *)link_info->trg_path, link_info->linfo.u.val_size, H5P_DEFAULT) < 0) {
        if(link_info->opt.msg_mode == 1)
            parallel_print("Warning: unable to get link value from <%s>\n",linkpath);
        HGOTO_DONE(FAIL);
    } /* end if */

    /*-----------------------------------------------------
     * if link type is external link use different lapl to
     * follow object in other file
     */
    if(link_info->linfo.type == H5L_TYPE_EXTERNAL) {
        if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
            HGOTO_DONE(FAIL);
        if(H5Pset_fapl_sec2(fapl) < 0)
            HGOTO_DONE(FAIL);
        if((lapl = H5Pcreate(H5P_LINK_ACCESS)) < 0)
            HGOTO_DONE(FAIL);
        if(H5Pset_elink_fapl(lapl, fapl) < 0)
            HGOTO_DONE(FAIL);
    } /* end if */

    /* Check for retrieving object info */
    if(get_obj_type) {
        /*--------------------------------------------------------------
         * if link's target object exist, get type
         */
         /* check if target object exist */
        l_ret = H5Oexists_by_name(file_id, linkpath, lapl);

        /* detect dangling link */
        if(l_ret == FALSE) {
            HGOTO_DONE(0);
        }
        else if(l_ret < 0) {       /* function failed */
            HGOTO_DONE(FAIL);
        }

        /* get target object info */
        if(H5Oget_info_by_name2(file_id, linkpath, &trg_oinfo, H5O_INFO_BASIC, lapl) < 0) {
            if(link_info->opt.msg_mode == 1)
                parallel_print("Warning: unable to get object information for <%s>\n", linkpath);
            HGOTO_DONE(FAIL);
        } /* end if */

        /* check unknown type */
        if(trg_oinfo.type < H5O_TYPE_GROUP || trg_oinfo.type >=H5O_TYPE_NTYPES) {
            if(link_info->opt.msg_mode == 1)
                parallel_print("Warning: target object of <%s> is unknown type\n", linkpath);
            HGOTO_DONE(FAIL);
        }  /* end if */

        /* set target obj type to return */
        link_info->trg_type = trg_oinfo.type;
        link_info->objno = trg_oinfo.addr;
        link_info->fileno = trg_oinfo.fileno;
    } /* end if */
    else
        link_info->trg_type = H5O_TYPE_UNKNOWN;

    /* succeed */
    ret_value = 1;

done:
    if(fapl != H5P_DEFAULT)
        H5Pclose(fapl);
    if(lapl != H5P_DEFAULT)
        H5Pclose(lapl);

    return ret_value;
} /* end H5tools_get_symlink_info() */

/*-------------------------------------------------------------------------
 * Audience:    Public
 *
 * Purpose:     Initialize the name and operation status of the H5 Tools library
 *
 * Description:
 *      These are utility functions to set/get the program name and operation status.
 *-------------------------------------------------------------------------
 */
void
h5tools_setprogname(const char *Progname)
{
    h5tools_progname = Progname;
}

void
h5tools_setstatus(int D_status)
{
    h5tools_d_status = D_status;
}

H5_ATTR_PURE const char *
h5tools_getprogname(void)
{
   return h5tools_progname;
}

H5_ATTR_PURE int
h5tools_getstatus(void)
{
   return h5tools_d_status;
}

/*-----------------------------------------------------------
 * PURPOSE :
 * if environment variable H5TOOLS_BUFSIZE is set,
 * update H5TOOLS_BUFSIZE and H5TOOLS_MALLOCSIZE from the env
 * This can be called from each tools main() as part of initial act.
 * Note: this is more of debugging purpose for now.
 */
int
h5tools_getenv_update_hyperslab_bufsize(void)
{
    const char *env_str = NULL;
    long hyperslab_bufsize_mb;
    int ret_value = 1;

    /* check if environment variable is set for the hyperslab buffer size */
    if (NULL != (env_str = HDgetenv ("H5TOOLS_BUFSIZE"))) {
        errno = 0;
        hyperslab_bufsize_mb = HDstrtol(env_str, (char**)NULL, 10);
        if (errno != 0 || hyperslab_bufsize_mb <= 0)
            HGOTO_ERROR(FAIL, H5E_tools_min_id_g, "hyperslab buffer size failed");

        /* convert MB to byte */
        H5TOOLS_BUFSIZE = (hsize_t)hyperslab_bufsize_mb * 1024 * 1024;

        H5TOOLS_MALLOCSIZE = MAX(H5TOOLS_BUFSIZE, H5TOOLS_MALLOCSIZE);
    }

done:
    return ret_value;
}

