/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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.     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#ifndef _H5HLprivate2_H
#define _H5HLprivate2_H

/* Standard C Headers */
#include <assert.h>
#include <ctype.h>

/* Public HDF5 header */
#include "hdf5.h"

/* Public High-Level header */
#include "hdf5_hl.h"

/* Private header files needed */
#include "H5HLerror.h"          /* High-Level errors */

/* The following is copied from src/H5private.h */

/*
 * Status return values for the `herr_t' type.
 * Since some unix/c routines use 0 and -1 (or more precisely, non-negative
 * vs. negative) as their return code, and some assumption had been made in
 * the code about that, it is important to keep these constants the same
 * values.  When checking the success or failure of an integer-valued
 * function, remember to compare against zero and not one of these two
 * values.
 */
#define SUCCEED		0
#define FAIL		(-1)
#define UFAIL		(unsigned)(-1)

/* minimum of two, three, or four values */
#undef MIN
#define MIN(a,b)		(((a)<(b)) ? (a) : (b))
#define MIN2(a,b)		MIN(a,b)
#define MIN3(a,b,c)		MIN(a,MIN(b,c))
#define MIN4(a,b,c,d)		MIN(MIN(a,b),MIN(c,d))

/* maximum of two, three, or four values */
#undef MAX
#define MAX(a,b)		(((a)>(b)) ? (a) : (b))
#define MAX2(a,b)		MAX(a,b)
#define MAX3(a,b,c)		MAX(a,MAX(b,c))
#define MAX4(a,b,c,d)		MAX(MAX(a,b),MAX(c,d))

/*
 * HDF Boolean type.
 */
#ifndef FALSE
#   define FALSE 0
#endif
#ifndef TRUE
#   define TRUE 1
#endif
/* Macro for "glueing" together items, for re-scanning macros */
#define H5_GLUE(x,y)       x##y
#define H5_GLUE3(x,y,z)    x##y##z
#define H5_GLUE4(w,x,y,z)  w##x##y##z




/************************************************/
/* Revisions to FUNC_ENTER/LEAVE & Error Macros */
/************************************************/

/* `S' is the name of a function which is being tested to check if it's */
/*      a public API function */
#define H5_IS_PUB(S) (((isdigit(S[1]) || isupper(S[1])) && islower(S[2])) || \
    ((isdigit(S[2]) || isupper(S[2])) && islower(S[3])) || \
    (!S[4] || ((isdigit(S[3]) || isupper(S[3])) && islower(S[4]))))

/* `S' is the name of a function which is being tested to check if it's */
/*      a private library function */
#define H5_IS_PRIV(S) (((isdigit(S[1]) || isupper(S[1])) && '_' == S[2] && islower(S[3])) || \
    ((isdigit(S[2]) || isupper(S[2])) && '_' == S[3] && islower(S[4])) || \
    ((isdigit(S[3]) || isupper(S[3])) && '_' == S[4] && islower(S[5])))

/* `S' is the name of a function which is being tested to check if it's */
/*      a package private function */
#define H5_IS_PKG(S) (((isdigit(S[1]) || isupper(S[1])) && '_' == S[2] && '_' == S[3] && islower(S[4])) || \
    ((isdigit(S[2]) || isupper(S[2])) && '_' == S[3] && '_' == S[4] && islower(S[5])) || \
    ((isdigit(S[3]) || isupper(S[3])) && '_' == S[4] && '_' == S[5] && islower(S[6])))

#ifndef NDEBUG
#define FUNC_ENTER_NAME_CHECK(asrt)					      \
    {					          			      \
        static hbool_t func_check = FALSE;          			      \
                                                                              \
        if(!func_check) {		   				      \
            /* Check function naming status */				      \
            assert(asrt);			                              \
                                                                              \
            /* Don't check again */                 			      \
            func_check = TRUE;						      \
        } /* end if */							      \
    } /* end scope */
#else /* NDEBUG */
#define FUNC_ENTER_NAME_CHECK(asrt)
#endif /* NDEBUG */

/* Macros for referencing package initialization symbols */
#define H5_PACKAGE_INIT_VAR(x) H5_GLUE3(H5_, x, _init_g)
#define H5_PACKAGE_INIT_FUNC(x) H5_GLUE3( ,x, __pkg_init)

/* Macros to check if a package is initialized */
#define H5_CHECK_PACKAGE_INIT_REG_YES(asrt)       assert(H5_PACKAGE_INIT_VAR(pkg));
#define H5_CHECK_PACKAGE_INIT_REG_NO(asrt)
#define H5_CHECK_PACKAGE_INIT_INIT_YES(asrt)
#define H5_CHECK_PACKAGE_INIT_INIT_NO(asrt)

/* Macros to initialize package, if a package initialization routine is defined */
#define H5_PKG_YES_INIT(pkg)						      \
    if(!H5_PACKAGE_INIT_VAR(pkg)) {					      \
        if(H5_PACKAGE_INIT_FUNC(pkg)() < 0) {				      \
            /* (Can't use H5E_THROW here) */				      \
            H5E_PRINTF(H5E_CANTINIT, "interface initialization failed");      \
            ret_value = fail_value;					      \
            goto func_init_failed;					      \
        } /* end if */						              \
    } /* end if */
#define H5_PKG_NO_INIT(pkg)

/* Macros to declare package initialization symbols, if a package initialization routine is defined */
#define H5_PKG_YES_INIT_VAR(pkg) extern hbool_t H5_PACKAGE_INIT_VAR(pkg);
#define H5_PKG_NO_INIT_VAR(pkg)
#define H5_PKG_YES_INIT_FUNC(pkg) extern herr_t H5_PACKAGE_INIT_FUNC(pkg)(void);
#define H5_PKG_NO_INIT_FUNC(pkg)

/* Declare package initialization symbols (if in a package) */
#define H5_DECLARE_PKG_VAR(pkg_init, pkg) H5_GLUE3(H5_PKG_, pkg_init, _INIT_VAR)(pkg)
#define H5_DECLARE_PKG_FUNC(pkg_init, pkg) H5_GLUE3(H5_PKG_, pkg_init, _INIT_FUNC)(pkg)
#ifdef H5_MY_PKG
H5_DECLARE_PKG_VAR(H5_MY_PKG_INIT, H5_MY_PKG)
H5_DECLARE_PKG_FUNC(H5_MY_PKG_INIT, H5_MY_PKG)
#endif /* H5_MY_PKG */

/* API re-entrance variable */
extern hbool_t H5HL_api_entered_g;    /* Has library already been entered through API? */

/* extern global variables */
extern hbool_t H5HL_libinit_g;    /* Has the library been initialized? */
                                                                \

/* Use FUNC to safely handle variations of C99 __func__ keyword handling */
#ifdef H5_HAVE_C99_FUNC
#define FUNC __func__
#elif defined(H5_HAVE_FUNCTION)
#define FUNC __FUNCTION__
#else
#error "We need __func__ or __FUNCTION__ to test function names!"
#endif

/* Macros for entering different scopes of routines */
#define H5_PACKAGE_ENTER(pkg, pkg_init, init)				      \
    FUNC_ENTER_NAME_CHECK(H5_IS_PKG(FUNC))				      \
                                                                              \
    /* The library should be initialized already */			      \
    assert(H5HL_libinit_g);						      \
                                                                              \
    /* This interface should be initialized already */			      \
    /* (except for package initialization routines :-) */		      \
    H5_GLUE4(H5_CHECK_PACKAGE_INIT_, init, _, pkg_init)(pkg)		      \
                                                                              \
    /* Enter scope for this type of function */				      \
    {

#define H5_PRIVATE_ENTER(pkg, pkg_init)					      \
    FUNC_ENTER_NAME_CHECK(H5_IS_PRIV(FUNC))				      \
                                                                              \
    /* The library should be initialized already */			      \
    assert(H5HL_libinit_g);						      \
                                                                              \
    /* Initialize this interface if desired */				      \
    H5_GLUE3(H5_PKG_, pkg_init, _INIT)(pkg)				      \
                                                                              \
    /* Enter scope for this type of function */				      \
    {{
#define H5_PUBLIC_ENTER(pkg, pkg_init)					      \
    FUNC_ENTER_NAME_CHECK(H5_IS_PUB(FUNC))				      \
                                                                              \
    /* Clear thread error stack when entering public functions */	      \
    H5Eclear2(H5E_DEFAULT);				                      \
                                                                              \
    /* Initialize the library or bust */				      \
    if(!(H5HL_libinit_g)) {						      \
        H5HL_libinit_g = TRUE;                                                \
        if(H5HL_init_library() < 0) {  					      \
            /* (Can't use H5E_THROW here) */				      \
            H5E_PRINTF(H5E_CANTINIT, "interface initialization failed");      \
            ret_value = fail_value;					      \
            goto func_init_failed;					      \
        } /* end if */                                                        \
    } /* end if */						              \
                                                                              \
    /* Initialize this interface if desired */				      \
    H5_GLUE3(H5_PKG_, pkg_init, _INIT)(pkg)				      \
                                                                              \
    /* Check for re-entering API routine */				      \
    /* assert(!H5HL_api_entered_g); */					      \
    /* H5HL_api_entered_g = TRUE; */					      \
                                                                              \
    /* Enter scope for this type of function */				      \
    {{{

/* Macros for substituting the package name */
#define FUNC_ENTER_STATIC	H5_PACKAGE_ENTER(H5_MY_PKG, H5_MY_PKG_INIT, REG)
#define FUNC_ENTER_PKGINIT	H5_PACKAGE_ENTER(H5_MY_PKG, H5_MY_PKG_INIT, INIT)
#define FUNC_ENTER_PKG		H5_PACKAGE_ENTER(H5_MY_PKG, H5_MY_PKG_INIT, REG)
#define FUNC_ENTER_PRIV		H5_PRIVATE_ENTER(H5_MY_PKG, H5_MY_PKG_INIT)
#define FUNC_ENTER_PUB		H5_PUBLIC_ENTER(H5_MY_PKG, H5_MY_PKG_INIT)

/* Macros for substituting a function prefix */
#define FUNC_PREFIX_STATIC	static
#define FUNC_PREFIX_PKGINIT
#define FUNC_PREFIX_PKG
#define FUNC_PREFIX_PRIV
#define FUNC_PREFIX_PUB

/* Macros for declaring error variables */
#define FUNC_ERR_VAR_ERR(ret_typ, err)					      \
    hbool_t past_catch = FALSE;						      \
    ret_typ fail_value = err;
#define FUNC_ERR_VAR_ERRCATCH(ret_typ, err)				      \
    hbool_t past_catch = FALSE;
#define FUNC_ERR_VAR_NOERR(ret_typ, err)

/* Use this macro when entering all functions */
#define BEGIN_FUNC(scope, use_err, ret_typ, ret_init, err, func)	      \
H5_GLUE(FUNC_PREFIX_, scope)						      \
ret_typ									      \
func									      \
/* Open function */							      \
{									      \
    ret_typ ret_value = ret_init;					      \
    H5E_auto2_t efunc2;					                      \
    void *H5E_saved_edata;						      \
    H5_GLUE(FUNC_ERR_VAR_, use_err)(ret_typ, err)			      \
    herr_t err1 = H5Eget_auto2(H5E_DEFAULT, &efunc2, &H5E_saved_edata);       \
    herr_t err2 = H5Eset_auto2(H5E_DEFAULT, NULL, NULL);                      \
    H5_GLUE(FUNC_ENTER_, scope)			     

/* Macros for label when a function initialization can fail */
#define H5_PRIV_YES_FUNC_INIT_FAILED func_init_failed:
#define H5_PRIV_NO_FUNC_INIT_FAILED
#define H5_PRIV_FUNC_INIT_FAILED(pkg_init) H5_GLUE3(H5_PRIV_, pkg_init, _FUNC_INIT_FAILED)

/* Macros for leaving different scopes of routines */
#define FUNC_LEAVE_PKGINIT					       	      \
    /* Leave scope for this type of function */				      \
    }

#define FUNC_LEAVE_STATIC					       	      \
    /* Leave scope for this type of function */				      \
    }

#define FUNC_LEAVE_PKG						       	      \
    /* Leave scope for this type of function */				      \
    }

#define FUNC_LEAVE_PRIV						       	      \
    /* Leave scope for this type of function */				      \
    }}									      \
                                                                              \
    /* Label for errors during FUNC_ENTER */				      \
    H5_PRIV_FUNC_INIT_FAILED(H5_MY_PKG_INIT)

#define FUNC_LEAVE_PUB						       	      \
    /* Leave scope for this type of function */				      \
    }}}									      \
                                                                              \
    /* Label for errors during FUNC_ENTER */				      \
func_init_failed:							      \
                                                                              \
    /* Dump error stack if an error occurred during API routine */	      \
    if(ret_value == fail_value)						      \
        (void)H5HLE_dump_api_stack(TRUE);				      \
                                                                              \
    /* Check for leaving API routine */					      \
    /* assert(H5HL_api_entered_g); */						      \
    /* H5HL_api_entered_g = FALSE;	 */					      \
                                                                              \

/* Use this macro when leaving all functions */
#define END_FUNC(scope)                                                       \
    (void)H5Eset_auto2(H5E_DEFAULT, efunc2, H5E_saved_edata);	              \
    /* Scope-specific function conclusion */				      \
    H5_GLUE(FUNC_LEAVE_, scope)						      \
    /* Leave routine */							      \
    return(ret_value);							      \
                                                                              \
    /* Close Function */						      \
}


/*
 * H5E_PRINTF macro, used to facilitate error reporting between a BEGIN_FUNC()
 * and an END_FUNC() within a function body.  The arguments are the minor
 * error number, a description of the error (as a printf-like format string),
 * and an optional set of arguments for the printf format arguments.
 */
#define H5E_PRINTF(...) H5Epush2(H5E_DEFAULT, __FILE__, FUNC, __LINE__, H5HL_ERR_CLS_g, H5_MY_PKG_ERR,  __VA_ARGS__)

/*
 * H5_LEAVE macro, used to facilitate control flow between a
 * BEGIN_FUNC() and an END_FUNC() within a function body.  The argument is
 * the return value.
 * The return value is assigned to a variable `ret_value' and control branches
 * to the `catch_except' label, if we're not already past it.
 */
#define H5_LEAVE(v) {							      \
    ret_value = v;							      \
    if(!past_catch)							      \
        goto catch_except;						      \
}

/*
 * H5E_THROW macro, used to facilitate error reporting between a
 * FUNC_ENTER() and a FUNC_LEAVE() within a function body.  The arguments are
 * the minor error number, and an error string.
 * The return value is assigned to a variable `ret_value' and control branches
 * to the `catch_except' label, if we're not already past it.
 */
#define H5E_THROW(...) {                                                      \
    H5E_PRINTF(__VA_ARGS__);						      \
    H5_LEAVE(fail_value)						      \
}

/* Macro for "catching" flow of control when an error occurs.  Note that the
 *      H5_LEAVE macro won't jump back here once it's past this point.
 */
#define CATCH past_catch = TRUE; catch_except:;

/* Private functions */
herr_t H5HL_init_library(void);

void H5HL_close(void);

#endif /* _H5HLprivate2_H */