text: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   * 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.                                                        *
   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  
  /*-------------------------------------------------------------------------
   *
   * Created:    H5Eint.c
   *        April 11 2007
   *        Quincey Koziol <koziol@hdfgroup.org>
   *
   * Purpose:    General use, "internal" routines for error handling.
   *
   *-------------------------------------------------------------------------
   */
  
  /****************/
  /* Module Setup */
  /****************/
  
  #include "H5Emodule.h"          /* This source code file is part of the H5E module */
  
  
  /***********/
  /* Headers */
  /***********/
  #include "H5private.h"        /* Generic Functions            */
  #include "H5CXprivate.h"        /* API Contexts                         */
  #include "H5Epkg.h"        /* Error handling              */
  #include "H5Iprivate.h"        /* IDs                                  */
  #include "H5MMprivate.h"    /* Memory management            */
  
  
  /****************/
  /* Local Macros */
  /****************/
  
  
  /******************/
  /* Local Typedefs */
  /******************/
  
  /* Printing information */
  typedef struct H5E_print_t {
      FILE        *stream;
      H5E_cls_t   cls;
  } H5E_print_t;
  
  
  /********************/
  /* Package Typedefs */
  /********************/
  
  
  /********************/
  /* Local Prototypes */
  /********************/
  #ifndef H5_NO_DEPRECATED_SYMBOLS
  static herr_t H5E_walk1_cb(int n, H5E_error1_t *err_desc,
      void *client_data);
  #endif /* H5_NO_DEPRECATED_SYMBOLS */
  static herr_t H5E_walk2_cb(unsigned n, const H5E_error2_t *err_desc,
      void *client_data);
  static herr_t  H5E_clear_entries(H5E_t *estack, size_t nentries);
  
  
  /*********************/
  /* Package Variables */
  /*********************/
  
  #ifndef H5_HAVE_THREADSAFE
  /*
   * The current error stack.
   */
  H5E_t H5E_stack_g[1];
  #endif /* H5_HAVE_THREADSAFE */
  
  
  /*****************************/
  /* Library Private Variables */
  /*****************************/
  
  /* HDF5 error class ID */
  hid_t H5E_ERR_CLS_g = FAIL;
  
  /*
   * Predefined errors. These are initialized at runtime in H5E_init_interface()
   * in this source file.
   */
  /* Include the automatically generated error code definitions */
  #include "H5Edefin.h"
  
  
  /*******************/
  /* Local Variables */
  /*******************/
  
  #ifdef H5_HAVE_PARALLEL
  /*
   * variables used for MPI error reporting
   */
  char    H5E_mpi_error_str[MPI_MAX_ERROR_STRING];
  int    H5E_mpi_error_str_len;
  #endif /* H5_HAVE_PARALLEL */
  
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_get_msg
   *
   * Purpose:    Private function to retrieve an error message.
   *
   * Return:      Non-negative for name length if succeeds(zero means no name);
   *              otherwise returns negative value.
   *
   * Programmer:    Raymond Lu
   *              Friday, July 14, 2003
   *
   *-------------------------------------------------------------------------
   */
  ssize_t
  H5E_get_msg(const H5E_msg_t *msg, H5E_type_t *type, char *msg_str, size_t size)
  {
      ssize_t       len = -1;     /* Length of error message */
  
      FUNC_ENTER_NOAPI_NOINIT_NOERR
  
      /* Check arguments */
      HDassert(msg);
  
      /* Get the length of the message string */
      len = (ssize_t)HDstrlen(msg->msg);
  
      /* Copy the message into the user's buffer, if given */
      if(msg_str) {
          HDstrncpy(msg_str, msg->msg, size);
          msg_str[size - 1] = '\0';
      } /* end if */
  
      /* Give the message type, if asked */
      if(type)
          *type = msg->type;
  
      /* Set the return value to the full length of the message */
      FUNC_LEAVE_NOAPI(len)
  } /* end H5E_get_msg() */
  
  #ifndef H5_NO_DEPRECATED_SYMBOLS
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_walk1_cb
   *
   * Purpose:    This function is for backward compatibility.
   *              This is a default error stack traversal callback function
   *        that prints error messages to the specified output stream.
   *        This function is for backward compatibility with v1.6.
   *        It is not meant to be called directly but rather as an
   *        argument to the H5Ewalk() function.  This function is called
   *        also by H5Eprint().  Application writers are encouraged to
   *        use this function as a model for their own error stack
   *        walking functions.
   *
   *        N is a counter for how many times this function has been
   *        called for this particular traversal of the stack.  It always
   *        begins at zero for the first error on the stack (either the
   *        top or bottom error, or even both, depending on the traversal
   *        direction and the size of the stack).
   *
   *        ERR_DESC is an error description.  It contains all the
   *        information about a particular error.
   *
   *        CLIENT_DATA is the same pointer that was passed as the
   *        CLIENT_DATA argument of H5Ewalk().  It is expected to be a
   *        file pointer (or stderr if null).
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:  Raymond Lu
   *        Thursday, May 11, 2006
   *
   *-------------------------------------------------------------------------
   */
  static herr_t
  H5E_walk1_cb(int n, H5E_error1_t *err_desc, void *client_data)
  {
      H5E_print_t         *eprint  = (H5E_print_t *)client_data;
      FILE        *stream;        /* I/O stream to print output to */
      H5E_cls_t           *cls_ptr;       /* Pointer to error class */
      H5E_msg_t           *maj_ptr;       /* Pointer to major error info */
      H5E_msg_t           *min_ptr;       /* Pointer to minor error info */
      const char        *maj_str = "No major description";      /* Major error description */
      const char        *min_str = "No minor description";      /* Minor error description */
      unsigned            have_desc = 1;  /* Flag to indicate whether the error has a "real" description */
      herr_t              ret_value = SUCCEED;
  
      FUNC_ENTER_NOAPI_NOINIT_NOERR
  
      /* Check arguments */
      HDassert(err_desc);
  
      /* If no client data was passed, output to stderr */
      if(!client_data)
          stream = stderr;
      else
          stream = eprint->stream;
  
      /* Get descriptions for the major and minor error numbers */
      maj_ptr = (H5E_msg_t *)H5I_object_verify(err_desc->maj_num, H5I_ERROR_MSG);
      min_ptr = (H5E_msg_t *)H5I_object_verify(err_desc->min_num, H5I_ERROR_MSG);
  
      /* Check for bad pointer(s), but can't issue error, just leave */
      if(!maj_ptr || !min_ptr)
          HGOTO_DONE(FAIL)
  
      if(maj_ptr->msg)
          maj_str = maj_ptr->msg;
      if(min_ptr->msg)
          min_str = min_ptr->msg;
  
      /* Get error class info */
      cls_ptr = maj_ptr->cls;
  
      /* Print error class header if new class */
      if(eprint->cls.lib_name == NULL || HDstrcmp(cls_ptr->lib_name, eprint->cls.lib_name)) {
          /* update to the new class information */
          if(cls_ptr->cls_name)
              eprint->cls.cls_name = cls_ptr->cls_name;
          if(cls_ptr->lib_name)
              eprint->cls.lib_name = cls_ptr->lib_name;
          if(cls_ptr->lib_vers)
              eprint->cls.lib_vers = cls_ptr->lib_vers;
  
          HDfprintf(stream, "%s-DIAG: Error detected in %s (%s) ",
              (cls_ptr->cls_name ? cls_ptr->cls_name : "(null)"),
              (cls_ptr->lib_name ? cls_ptr->lib_name : "(null)"),
              (cls_ptr->lib_vers ? cls_ptr->lib_vers : "(null)"));
  
          /* try show the process or thread id in multiple processes cases*/
  #ifdef H5_HAVE_PARALLEL
          {
              int mpi_rank, mpi_initialized, mpi_finalized;
  
          MPI_Initialized(&mpi_initialized);
              MPI_Finalized(&mpi_finalized);
  
              if(mpi_initialized && !mpi_finalized) {
              MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
              HDfprintf(stream, "MPI-process %d", mpi_rank);
          } /* end if */
              else
              HDfprintf(stream, "thread 0");
          } /* end block */
  #elif defined(H5_HAVE_THREADSAFE)
          HDfprintf(stream, "thread %lu", (unsigned long)HDpthread_self_ulong());
  #else
          HDfprintf(stream, "thread 0");
  #endif
          HDfprintf(stream, ":\n");
      } /* end if */
  
      /* Check for "real" error description - used to format output more nicely */
      if(err_desc->desc == NULL || HDstrlen(err_desc->desc) == 0)
          have_desc=0;
  
      /* Print error message */
      HDfprintf(stream, "%*s#%03d: %s line %u in %s()%s%s\n",
          H5E_INDENT, "", n, err_desc->file_name, err_desc->line,
          err_desc->func_name, (have_desc ? ": " : ""),
               (have_desc ? err_desc->desc : ""));
      HDfprintf(stream, "%*smajor: %s\n", (H5E_INDENT * 2), "", maj_str);
      HDfprintf(stream, "%*sminor: %s\n", (H5E_INDENT * 2), "", min_str);
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_walk1_cb() */
  #endif /* H5_NO_DEPRECATED_SYMBOLS */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_walk2_cb
   *
   * Purpose:    This is a default error stack traversal callback function
   *        that prints error messages to the specified output stream.
   *        It is not meant to be called directly but rather as an
   *        argument to the H5Ewalk2() function.  This function is
   *        called also by H5Eprint2().  Application writers are
   *        encouraged to use this function as a model for their own
   *        error stack walking functions.
   *
   *        N is a counter for how many times this function has been
   *        called for this particular traversal of the stack.  It always
   *        begins at zero for the first error on the stack (either the
   *        top or bottom error, or even both, depending on the traversal
   *        direction and the size of the stack).
   *
   *        ERR_DESC is an error description.  It contains all the
   *        information about a particular error.
   *
   *        CLIENT_DATA is the same pointer that was passed as the
   *        CLIENT_DATA argument of H5Ewalk().  It is expected to be a
   *        file pointer (or stderr if null).
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Robb Matzke
   *        Friday, December 12, 1997
   *
   *-------------------------------------------------------------------------
   */
  static herr_t
  H5E_walk2_cb(unsigned n, const H5E_error2_t *err_desc, void *client_data)
  {
      H5E_print_t         *eprint  = (H5E_print_t *)client_data;
      FILE        *stream;        /* I/O stream to print output to */
      H5E_cls_t           *cls_ptr;       /* Pointer to error class */
      H5E_msg_t           *maj_ptr;       /* Pointer to major error info */
      H5E_msg_t           *min_ptr;       /* Pointer to minor error info */
      const char        *maj_str = "No major description";      /* Major error description */
      const char        *min_str = "No minor description";      /* Minor error description */
      unsigned            have_desc = 1;  /* Flag to indicate whether the error has a "real" description */
      herr_t              ret_value = SUCCEED;
  
      FUNC_ENTER_NOAPI_NOINIT_NOERR
  
      /* Check arguments */
      HDassert(err_desc);
  
      /* If no client data was passed, output to stderr */
      if(!client_data)
          stream = stderr;
      else
          stream = eprint->stream;
  
      /* Get descriptions for the major and minor error numbers */
      maj_ptr = (H5E_msg_t *)H5I_object_verify(err_desc->maj_num, H5I_ERROR_MSG);
      min_ptr = (H5E_msg_t *)H5I_object_verify(err_desc->min_num, H5I_ERROR_MSG);
  
      /* Check for bad pointer(s), but can't issue error, just leave */
      if(!maj_ptr || !min_ptr)
          HGOTO_DONE(FAIL)
  
      if(maj_ptr->msg)
          maj_str = maj_ptr->msg;
      if(min_ptr->msg)
          min_str = min_ptr->msg;
  
      /* Get error class info.  Don't use the class of the major or minor error because
       * they might be different. */
      cls_ptr = (H5E_cls_t *)H5I_object_verify(err_desc->cls_id, H5I_ERROR_CLASS);
  
      /* Check for bad pointer(s), but can't issue error, just leave */
      if(!cls_ptr)
          HGOTO_DONE(FAIL)
  
      /* Print error class header if new class */
      if(eprint->cls.lib_name == NULL || HDstrcmp(cls_ptr->lib_name, eprint->cls.lib_name)) {
          /* update to the new class information */
          if(cls_ptr->cls_name)
              eprint->cls.cls_name = cls_ptr->cls_name;
          if(cls_ptr->lib_name)
              eprint->cls.lib_name = cls_ptr->lib_name;
          if(cls_ptr->lib_vers)
              eprint->cls.lib_vers = cls_ptr->lib_vers;
  
          HDfprintf(stream, "%s-DIAG: Error detected in %s (%s) ",
              (cls_ptr->cls_name ? cls_ptr->cls_name : "(null)"),
              (cls_ptr->lib_name ? cls_ptr->lib_name : "(null)"),
              (cls_ptr->lib_vers ? cls_ptr->lib_vers : "(null)"));
  
          /* try show the process or thread id in multiple processes cases*/
  #ifdef H5_HAVE_PARALLEL
          {
              int mpi_rank, mpi_initialized, mpi_finalized;
  
          MPI_Initialized(&mpi_initialized);
              MPI_Finalized(&mpi_finalized);
  
              if(mpi_initialized && !mpi_finalized) {
              MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
              HDfprintf(stream, "MPI-process %d", mpi_rank);
          } /* end if */
              else
              HDfprintf(stream, "thread 0");
          } /* end block */
  #elif defined(H5_HAVE_THREADSAFE)
          HDfprintf(stream, "thread %lu", (unsigned long)HDpthread_self_ulong());
  #else
          HDfprintf(stream, "thread 0");
  #endif
          HDfprintf(stream, ":\n");
      } /* end if */
  
      /* Check for "real" error description - used to format output more nicely */
      if(err_desc->desc == NULL || HDstrlen(err_desc->desc) == 0)
          have_desc = 0;
  
      /* Print error message */
      HDfprintf(stream, "%*s#%03u: %s line %u in %s()%s%s\n",
          H5E_INDENT, "", n, err_desc->file_name, err_desc->line,
          err_desc->func_name, (have_desc ? ": " : ""),
               (have_desc ? err_desc->desc : ""));
      HDfprintf(stream, "%*smajor: %s\n", (H5E_INDENT * 2), "", maj_str);
      HDfprintf(stream, "%*sminor: %s\n", (H5E_INDENT * 2), "", min_str);
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_walk2_cb() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_print
   *
   * Purpose:     Private function to print the error stack in some default
   *              way.  This is just a convenience function for H5Ewalk() and
   *              H5Ewalk2() with a function that prints error messages.
   *              Users are encouraged to write their own more specific error
   *              handlers.
   *
   * Return:      Non-negative on success/Negative on failure
   *
   * Programmer:    Robb Matzke
   *              Friday, February 27, 1998
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_print(const H5E_t *estack, FILE *stream, hbool_t bk_compatible)
  {
      H5E_print_t eprint;         /* Callback information to pass to H5E_walk() */
      H5E_walk_op_t walk_op;      /* Error stack walking callback */
      herr_t ret_value = SUCCEED;
  
      FUNC_ENTER_NOAPI_NOINIT
  
      /* Sanity check */
      HDassert(estack);
  
      /* If no stream was given, use stderr */
      if(!stream)
          eprint.stream = stderr;
      else
          eprint.stream = stream;
  
      /* Reset the original error class information */
      HDmemset(&eprint.cls, 0, sizeof(H5E_cls_t));
  
      /* Walk the error stack */
      if(bk_compatible) {
  #ifndef H5_NO_DEPRECATED_SYMBOLS
          walk_op.vers = 1;
          walk_op.u.func1 = H5E_walk1_cb;
          if(H5E_walk(estack, H5E_WALK_DOWNWARD, &walk_op, (void*)&eprint) < 0)
              HGOTO_ERROR(H5E_ERROR, H5E_CANTLIST, FAIL, "can't walk error stack")
  #else /* H5_NO_DEPRECATED_SYMBOLS */
          HDassert(0 && "version 1 error stack print without deprecated symbols!");
  #endif /* H5_NO_DEPRECATED_SYMBOLS */
      } /* end if */
      else {
          walk_op.vers = 2;
          walk_op.u.func2 = H5E_walk2_cb;
          if(H5E_walk(estack, H5E_WALK_DOWNWARD, &walk_op, (void*)&eprint) < 0)
              HGOTO_ERROR(H5E_ERROR, H5E_CANTLIST, FAIL, "can't walk error stack")
      } /* end else */
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_print() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_walk
   *
   * Purpose:    Private function for H5Ewalk.
   *              Walks the error stack, calling the specified function for
   *        each error on the stack.  The DIRECTION argument determines
   *        whether the stack is walked from the inside out or the
   *        outside in.  The value H5E_WALK_UPWARD means begin with the
   *        most specific error and end at the API; H5E_WALK_DOWNWARD
   *        means to start at the API and end at the inner-most function
   *        where the error was first detected.
   *
   *        The function pointed to by STACK_FUNC will be called for
   *        each error record in the error stack. It's arguments will
   *        include an index number (beginning at zero regardless of
   *        stack traversal    direction), an error stack entry, and the
   *        CLIENT_DATA pointer passed to H5E_print.
   *
   *        The function FUNC is also provided for backward compatibility.
   *        When BK_COMPATIBLE is set to be TRUE, FUNC is used to be
   *        compatible with older library.  If BK_COMPATIBLE is FALSE,
   *        STACK_FUNC is used.
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Robb Matzke
   *        Friday, December 12, 1997
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_walk(const H5E_t *estack, H5E_direction_t direction, const H5E_walk_op_t *op,
      void *client_data)
  {
      int       i;                          /* Local index variable */
      herr_t ret_value = H5_ITER_CONT;   /* Return value */
  
      FUNC_ENTER_NOAPI_NOINIT
  
      /* Sanity check */
      HDassert(estack);
      HDassert(op);
  
      /* check args, but rather than failing use some default value */
      if(direction != H5E_WALK_UPWARD && direction != H5E_WALK_DOWNWARD)
      direction = H5E_WALK_UPWARD;
  
      /* Walk the stack if a callback function was given */
      if(op->vers == 1) {
  #ifndef H5_NO_DEPRECATED_SYMBOLS
          if(op->u.func1) {
              H5E_error1_t old_err;
  
              if(H5E_WALK_UPWARD == direction) {
                  for(i = 0; i < (int)estack->nused && ret_value == H5_ITER_CONT; i++) {
                      /* Point to each error record on the stack and pass it to callback function.*/
                      old_err.maj_num = estack->slot[i].maj_num;
                      old_err.min_num = estack->slot[i].min_num;
                      old_err.func_name = estack->slot[i].func_name;
                      old_err.file_name = estack->slot[i].file_name;
                      old_err.desc = estack->slot[i].desc;
                      old_err.line = estack->slot[i].line;
  
                      ret_value = (op->u.func1)(i, &old_err, client_data);
                  } /* end for */
              } /* end if */
              else {
                  H5_CHECK_OVERFLOW(estack->nused - 1, size_t, int);
                  for(i = (int)(estack->nused - 1); i >= 0 && ret_value == H5_ITER_CONT; i--) {
                      /* Point to each error record on the stack and pass it to callback function.*/
                      old_err.maj_num = estack->slot[i].maj_num;
                      old_err.min_num = estack->slot[i].min_num;
                      old_err.func_name = estack->slot[i].func_name;
                      old_err.file_name = estack->slot[i].file_name;
                      old_err.desc = estack->slot[i].desc;
                      old_err.line = estack->slot[i].line;
  
                      ret_value = (op->u.func1)((int)(estack->nused - (size_t)(i + 1)), &old_err, client_data);
                  } /* end for */
              } /* end else */
  
              if(ret_value < 0)
                  HERROR(H5E_ERROR, H5E_CANTLIST, "can't walk error stack");
          } /* end if */
  #else /* H5_NO_DEPRECATED_SYMBOLS */
          HDassert(0 && "version 1 error stack walk without deprecated symbols!");
  #endif /* H5_NO_DEPRECATED_SYMBOLS */
      } /* end if */
      else {
          HDassert(op->vers == 2);
          if(op->u.func2) {
              if(H5E_WALK_UPWARD == direction) {
                  for(i = 0; i < (int)estack->nused && ret_value == H5_ITER_CONT; i++)
                      ret_value = (op->u.func2)((unsigned)i, estack->slot + i, client_data);
              } /* end if */
              else {
                  H5_CHECK_OVERFLOW(estack->nused - 1, size_t, int);
                  for(i = (int)(estack->nused - 1); i >= 0 && ret_value == H5_ITER_CONT; i--)
                      ret_value = (op->u.func2)((unsigned)(estack->nused - (size_t)(i + 1)), estack->slot + i, client_data);
              } /* end else */
  
              if(ret_value < 0)
                  HERROR(H5E_ERROR, H5E_CANTLIST, "can't walk error stack");
          } /* end if */
      } /* end else */
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_walk() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_get_auto
   *
   * Purpose:    Private function to return the current settings for the
   *              automatic error stack traversal function and its data
   *              for specific error stack. Either (or both) arguments may
   *              be null in which case the value is not returned.
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Raymond Lu
   *              July 18, 2003
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_get_auto(const H5E_t *estack, H5E_auto_op_t *op, void **client_data)
  {
      FUNC_ENTER_NOAPI_NOINIT_NOERR
  
      HDassert(estack);
  
      /* Retrieve the requested information */
      if(op)
          *op = estack->auto_op;
      if(client_data)
          *client_data = estack->auto_data;
  
      FUNC_LEAVE_NOAPI(SUCCEED)
  } /* end H5E_get_auto2() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_set_auto
   *
   * Purpose:    Private function to turn on or off automatic printing of
   *              errors for certain error stack.  When turned on (non-null
   *              FUNC pointer) any API function which returns an error
   *              indication will first call FUNC passing it CLIENT_DATA
   *              as an argument.
   *
   *        The default values before this function is called are
   *        H5Eprint2() with client data being the standard error stream,
   *        stderr.
   *
   *        Automatic stack traversal is always in the H5E_WALK_DOWNWARD
   *        direction.
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Robb Matzke
   *              Friday, February 27, 1998
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_set_auto(H5E_t *estack, const H5E_auto_op_t *op, void *client_data)
  {
      FUNC_ENTER_NOAPI_NOINIT_NOERR
  
      HDassert(estack);
  
      /* Set the automatic error reporting info */
      estack->auto_op = *op;
      estack->auto_data = client_data;
  
      FUNC_LEAVE_NOAPI(SUCCEED)
  } /* end H5E_set_auto() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_printf_stack
   *
   * Purpose:    Printf-like wrapper around H5E__push_stack.
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Quincey Koziol
   *        Tuesday, August 12, 2008
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_printf_stack(H5E_t *estack, const char *file, const char *func, unsigned line,
      hid_t cls_id, hid_t maj_id, hid_t min_id, const char *fmt, ...)
  {
      va_list     ap;                     /* Varargs info */
  #ifndef H5_HAVE_VASPRINTF
      int         tmp_len;        /* Current size of description buffer */
      int         desc_len;       /* Actual length of description when formatted */
  #endif /* H5_HAVE_VASPRINTF */
      char        *tmp = NULL;      /* Buffer to place formatted description in */
      hbool_t     va_started = FALSE; /* Whether the variable argument list is open */
      herr_t    ret_value = SUCCEED;    /* Return value */
  
      /*
       * WARNING: We cannot call HERROR() from within this function or else we
       *        could enter infinite recursion.  Furthermore, we also cannot
       *        call any other HDF5 macro or function which might call
       *        HERROR().  HERROR() is called by HRETURN_ERROR() which could
       *        be called by FUNC_ENTER().
       */
      FUNC_ENTER_NOAPI_NOINIT_NOERR
  
      /* Sanity check */
      HDassert(cls_id > 0);
      HDassert(maj_id > 0);
      HDassert(min_id > 0);
      HDassert(fmt);
  
  /* Note that the variable-argument parsing for the format is identical in
   *      the H5Epush2() routine - correct errors and make changes in both
   *      places. -QAK
   */
  
      /* Start the variable-argument parsing */
      va_start(ap, fmt);
      va_started = TRUE;
  
  #ifdef H5_HAVE_VASPRINTF
      /* Use the vasprintf() routine, since it does what we're trying to do below */
      if(HDvasprintf(&tmp, fmt, ap) < 0)
          HGOTO_DONE(FAIL)
  #else /* H5_HAVE_VASPRINTF */
      /* Allocate space for the formatted description buffer */
      tmp_len = 128;
      if(NULL == (tmp = H5MM_malloc((size_t)tmp_len)))
          HGOTO_DONE(FAIL)
  
      /* If the description doesn't fit into the initial buffer size, allocate more space and try again */
      while((desc_len = HDvsnprintf(tmp, (size_t)tmp_len, fmt, ap)) > (tmp_len - 1)) {
          /* shutdown & restart the va_list */
          va_end(ap);
          va_start(ap, fmt);
  
          /* Release the previous description, it's too small */
          H5MM_xfree(tmp);
  
          /* Allocate a description of the appropriate length */
          tmp_len = desc_len + 1;
          if(NULL == (tmp = H5MM_malloc((size_t)tmp_len)))
              HGOTO_DONE(FAIL)
      } /* end while */
  #endif /* H5_HAVE_VASPRINTF */
  
      /* Push the error on the stack */
      if(H5E__push_stack(estack, file, func, line, cls_id, maj_id, min_id, tmp) < 0)
          HGOTO_DONE(FAIL)
  
  done:
      if(va_started)
          va_end(ap);
  #ifdef H5_HAVE_VASPRINTF
      /* Memory was allocated with HDvasprintf so it needs to be freed
       * with HDfree
       */
      if(tmp)
          HDfree(tmp);
  #else /* H5_HAVE_VASPRINTF */
      if(tmp)
          H5MM_xfree(tmp);
  #endif /* H5_HAVE_VASPRINTF */
  
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_printf_stack() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E__push_stack
   *
   * Purpose:    Pushes a new error record onto error stack for the current
   *        thread.  The error has major and minor IDs MAJ_ID and
   *        MIN_ID, the name of a function where the error was detected,
   *        the name of the file where the error was detected, the
   *        line within that file, and an error description string.  The
   *        function name, file name, and error description strings must
   *        be statically allocated (the FUNC_ENTER() macro takes care of
   *        the function name and file name automatically, but the
   *        programmer is responsible for the description string).
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Robb Matzke
   *        Friday, December 12, 1997
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E__push_stack(H5E_t *estack, const char *file, const char *func, unsigned line,
      hid_t cls_id, hid_t maj_id, hid_t min_id, const char *desc)
  {
      herr_t    ret_value = SUCCEED;      /* Return value */
  
      /*
       * WARNING: We cannot call HERROR() from within this function or else we
       *        could enter infinite recursion.  Furthermore, we also cannot
       *        call any other HDF5 macro or function which might call
       *        HERROR().  HERROR() is called by HRETURN_ERROR() which could
       *        be called by FUNC_ENTER().
       */
      FUNC_ENTER_PACKAGE_NOERR
  
      /* Sanity check */
      HDassert(cls_id > 0);
      HDassert(maj_id > 0);
      HDassert(min_id > 0);
  
      /* Check for 'default' error stack */
      if(estack == NULL)
          if(NULL == (estack = H5E_get_my_stack())) /*lint !e506 !e774 Make lint 'constant value Boolean' in non-threaded case */
              HGOTO_DONE(FAIL)
  
      /*
       * Don't fail if arguments are bad.  Instead, substitute some default
       * value.
       */
      if(!func)
          func = "Unknown_Function";
      if(!file)
          file = "Unknown_File";
      if(!desc)
          desc = "No description given";
  
      /*
       * Push the error if there's room.  Otherwise just forget it.
       */
      HDassert(estack);
  
      if(estack->nused < H5E_NSLOTS) {
          /* Increment the IDs to indicate that they are used in this stack */
          if(H5I_inc_ref(cls_id, FALSE) < 0)
              HGOTO_DONE(FAIL)
      estack->slot[estack->nused].cls_id = cls_id;
          if(H5I_inc_ref(maj_id, FALSE) < 0)
              HGOTO_DONE(FAIL)
      estack->slot[estack->nused].maj_num = maj_id;
          if(H5I_inc_ref(min_id, FALSE) < 0)
              HGOTO_DONE(FAIL)
      estack->slot[estack->nused].min_num = min_id;
      if(NULL == (estack->slot[estack->nused].func_name = H5MM_xstrdup(func)))
              HGOTO_DONE(FAIL)
      if(NULL == (estack->slot[estack->nused].file_name = H5MM_xstrdup(file)))
              HGOTO_DONE(FAIL)
      estack->slot[estack->nused].line = line;
      if(NULL == (estack->slot[estack->nused].desc = H5MM_xstrdup(desc)))
              HGOTO_DONE(FAIL)
      estack->nused++;
      } /* end if */
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E__push_stack() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_clear_entries
   *
   * Purpose:    Private function to clear the error stack entries for the
   *              specified error stack.
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Quincey Koziol
   *              Wednesday, August 6, 2003
   *
   *-------------------------------------------------------------------------
   */
  static herr_t
  H5E_clear_entries(H5E_t *estack, size_t nentries)
  {
      H5E_error2_t *error;        /* Pointer to error stack entry to clear */
      unsigned u;                 /* Local index variable */
      herr_t ret_value=SUCCEED;   /* Return value */
  
      FUNC_ENTER_NOAPI_NOINIT
  
      /* Sanity check */
      HDassert(estack);
      HDassert(estack->nused >= nentries);
  
      /* Empty the error stack from the top down */
      for(u = 0; nentries > 0; nentries--, u++) {
          error = &(estack->slot[estack->nused - (u + 1)]);
  
          /* Decrement the IDs to indicate that they are no longer used by this stack */
          /* (In reverse order that they were incremented, so that reference counts work well) */
          if(H5I_dec_ref(error->min_num) < 0)
              HGOTO_ERROR(H5E_ERROR, H5E_CANTDEC, FAIL, "unable to decrement ref count on error message")
          if(H5I_dec_ref(error->maj_num) < 0)
              HGOTO_ERROR(H5E_ERROR, H5E_CANTDEC, FAIL, "unable to decrement ref count on error message")
          if(H5I_dec_ref(error->cls_id) < 0)
              HGOTO_ERROR(H5E_ERROR, H5E_CANTDEC, FAIL, "unable to decrement ref count on error class")
  
          /* Release strings */
          if(error->func_name)
              H5MM_xfree((void *)error->func_name);        /* Casting away const OK - QAK */
          if(error->file_name)
              H5MM_xfree((void *)error->file_name);        /* Casting away const OK - QAK */
          if(error->desc)
              H5MM_xfree((void *)error->desc);     /* Casting away const OK - QAK */
      } /* end for */
  
      /* Decrement number of errors on stack */
      estack->nused -= u;
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_clear_entries() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_clear_stack
   *
   * Purpose:    Private function to clear the error stack for the
   *              specified error stack.
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Raymond Lu
   *              Wednesday, July 16, 2003
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_clear_stack(H5E_t *estack)
  {
      herr_t ret_value = SUCCEED;   /* Return value */
  
      FUNC_ENTER_NOAPI(FAIL)
  
      /* Check for 'default' error stack */
      if(estack == NULL)
          if(NULL == (estack = H5E_get_my_stack())) /*lint !e506 !e774 Make lint 'constant value Boolean' in non-threaded case */
              HGOTO_ERROR(H5E_ERROR, H5E_CANTGET, FAIL, "can't get current error stack")
  
      /* Empty the error stack */
      HDassert(estack);
      if(estack->nused)
          if(H5E_clear_entries(estack, estack->nused) < 0)
              HGOTO_ERROR(H5E_ERROR, H5E_CANTSET, FAIL, "can't clear error stack")
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_clear_stack() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_pop
   *
   * Purpose:    Private function to delete some error messages from the top
   *              of error stack.
   *
   * Return:    Non-negative value on success/Negative on failure
   *
   * Programmer:    Raymond Lu
   *              Friday, July 16, 2003
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_pop(H5E_t *estack, size_t count)
  {
      herr_t      ret_value = SUCCEED;   /* Return value */
  
      FUNC_ENTER_NOAPI_NOINIT
  
      /* Sanity check */
      HDassert(estack);
      HDassert(estack->nused >= count);
  
      /* Remove the entries from the error stack */
      if(H5E_clear_entries(estack, count) < 0)
          HGOTO_ERROR(H5E_ERROR, H5E_CANTRELEASE, FAIL, "can't remove errors from stack")
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_pop() */
  
  
  /*-------------------------------------------------------------------------
   * Function:    H5E_dump_api_stack
   *
   * Purpose:    Private function to dump the error stack during an error in
   *              an API function if a callback function is defined for the
   *              current error stack.
   *
   * Return:    Non-negative on success/Negative on failure
   *
   * Programmer:    Quincey Koziol
   *              Wednesday, August 6, 2003
   *
   *-------------------------------------------------------------------------
   */
  herr_t
  H5E_dump_api_stack(hbool_t is_api)
  {
      herr_t ret_value = SUCCEED;   /* Return value */
  
      FUNC_ENTER_NOAPI(FAIL)
  
      /* Only dump the error stack during an API call */
      if(is_api) {
          H5E_t *estack = H5E_get_my_stack();
  
          HDassert(estack);
  
  #ifdef H5_NO_DEPRECATED_SYMBOLS
              if(estack->auto_op.func2)
                  (void)((estack->auto_op.func2)(H5E_DEFAULT, estack->auto_data));
  #else /* H5_NO_DEPRECATED_SYMBOLS */
          if(estack->auto_op.vers == 1) {
              if(estack->auto_op.func1)
                  (void)((estack->auto_op.func1)(estack->auto_data));
          } /* end if */
          else {
              if(estack->auto_op.func2)
                  (void)((estack->auto_op.func2)(H5E_DEFAULT, estack->auto_data));
          } /* end else */
  #endif /* H5_NO_DEPRECATED_SYMBOLS */
      } /* end if */
  
  done:
      FUNC_LEAVE_NOAPI(ret_value)
  } /* end H5E_dump_api_stack() */
  
  