Blame lib/msvc-inval.h

Packit 8f70b4
/* Invalid parameter handler for MSVC runtime libraries.
Packit 8f70b4
   Copyright (C) 2011-2018 Free Software Foundation, Inc.
Packit 8f70b4
Packit 8f70b4
   This program is free software; you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3, or (at your option)
Packit 8f70b4
   any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License along
Packit 8f70b4
   with this program; if not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
#ifndef _MSVC_INVAL_H
Packit 8f70b4
#define _MSVC_INVAL_H
Packit 8f70b4
Packit 8f70b4
/* With MSVC runtime libraries with the "invalid parameter handler" concept,
Packit 8f70b4
   functions like fprintf(), dup2(), or close() crash when the caller passes
Packit 8f70b4
   an invalid argument.  But POSIX wants error codes (such as EINVAL or EBADF)
Packit 8f70b4
   instead.
Packit 8f70b4
   This file defines macros that turn such an invalid parameter notification
Packit 8f70b4
   into a non-local exit.  An error code can then be produced at the target
Packit 8f70b4
   of this exit.  You can thus write code like
Packit 8f70b4
Packit 8f70b4
     TRY_MSVC_INVAL
Packit 8f70b4
       {
Packit 8f70b4
         
Packit 8f70b4
          but does not do 'return', 'break', 'continue', nor 'goto'.>
Packit 8f70b4
       }
Packit 8f70b4
     CATCH_MSVC_INVAL
Packit 8f70b4
       {
Packit 8f70b4
         
Packit 8f70b4
          but does not do 'return', 'break', 'continue', nor 'goto'.>
Packit 8f70b4
       }
Packit 8f70b4
     DONE_MSVC_INVAL;
Packit 8f70b4
Packit 8f70b4
   This entire block expands to a single statement.
Packit 8f70b4
Packit 8f70b4
   The handling of invalid parameters can be done in three ways:
Packit 8f70b4
Packit 8f70b4
     * The default way, which is reasonable for programs (not libraries):
Packit 8f70b4
       AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [DEFAULT_HANDLING])
Packit 8f70b4
Packit 8f70b4
     * The way for libraries that make "hairy" calls (like close(-1), or
Packit 8f70b4
       fclose(fp) where fileno(fp) is closed, or simply getdtablesize()):
Packit 8f70b4
       AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [HAIRY_LIBRARY_HANDLING])
Packit 8f70b4
Packit 8f70b4
     * The way for libraries that make no "hairy" calls:
Packit 8f70b4
       AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [SANE_LIBRARY_HANDLING])
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#define DEFAULT_HANDLING       0
Packit 8f70b4
#define HAIRY_LIBRARY_HANDLING 1
Packit 8f70b4
#define SANE_LIBRARY_HANDLING  2
Packit 8f70b4
Packit 8f70b4
#if HAVE_MSVC_INVALID_PARAMETER_HANDLER \
Packit 8f70b4
    && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING)
Packit 8f70b4
/* A native Windows platform with the "invalid parameter handler" concept,
Packit 8f70b4
   and either DEFAULT_HANDLING or HAIRY_LIBRARY_HANDLING.  */
Packit 8f70b4
Packit 8f70b4
# if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING
Packit 8f70b4
/* Default handling.  */
Packit 8f70b4
Packit 8f70b4
#  ifdef __cplusplus
Packit 8f70b4
extern "C" {
Packit 8f70b4
#  endif
Packit 8f70b4
Packit 8f70b4
/* Ensure that the invalid parameter handler in installed that just returns.
Packit 8f70b4
   Because we assume no other part of the program installs a different
Packit 8f70b4
   invalid parameter handler, this solution is multithread-safe.  */
Packit 8f70b4
extern void gl_msvc_inval_ensure_handler (void);
Packit 8f70b4
Packit 8f70b4
#  ifdef __cplusplus
Packit 8f70b4
}
Packit 8f70b4
#  endif
Packit 8f70b4
Packit 8f70b4
#  define TRY_MSVC_INVAL \
Packit 8f70b4
     do                                                                        \
Packit 8f70b4
       {                                                                       \
Packit 8f70b4
         gl_msvc_inval_ensure_handler ();                                      \
Packit 8f70b4
         if (1)
Packit 8f70b4
#  define CATCH_MSVC_INVAL \
Packit 8f70b4
         else
Packit 8f70b4
#  define DONE_MSVC_INVAL \
Packit 8f70b4
       }                                                                       \
Packit 8f70b4
     while (0)
Packit 8f70b4
Packit 8f70b4
# else
Packit 8f70b4
/* Handling for hairy libraries.  */
Packit 8f70b4
Packit 8f70b4
#  include <excpt.h>
Packit 8f70b4
Packit 8f70b4
/* Gnulib can define its own status codes, as described in the page
Packit 8f70b4
   "Raising Software Exceptions" on microsoft.com
Packit 8f70b4
   <https://msdn.microsoft.com/en-us/library/het71c37.aspx>.
Packit 8f70b4
   Our status codes are composed of
Packit 8f70b4
     - 0xE0000000, mandatory for all user-defined status codes,
Packit 8f70b4
     - 0x474E550, a API identifier ("GNU"),
Packit 8f70b4
     - 0, 1, 2, ..., used to distinguish different status codes from the
Packit 8f70b4
       same API.  */
Packit 8f70b4
#  define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0)
Packit 8f70b4
Packit 8f70b4
#  if defined _MSC_VER
Packit 8f70b4
/* A compiler that supports __try/__except, as described in the page
Packit 8f70b4
   "try-except statement" on microsoft.com
Packit 8f70b4
   <https://msdn.microsoft.com/en-us/library/s58ftw19.aspx>.
Packit 8f70b4
   With __try/__except, we can use the multithread-safe exception handling.  */
Packit 8f70b4
Packit 8f70b4
#   ifdef __cplusplus
Packit 8f70b4
extern "C" {
Packit 8f70b4
#   endif
Packit 8f70b4
Packit 8f70b4
/* Ensure that the invalid parameter handler in installed that raises a
Packit 8f70b4
   software exception with code STATUS_GNULIB_INVALID_PARAMETER.
Packit 8f70b4
   Because we assume no other part of the program installs a different
Packit 8f70b4
   invalid parameter handler, this solution is multithread-safe.  */
Packit 8f70b4
extern void gl_msvc_inval_ensure_handler (void);
Packit 8f70b4
Packit 8f70b4
#   ifdef __cplusplus
Packit 8f70b4
}
Packit 8f70b4
#   endif
Packit 8f70b4
Packit 8f70b4
#   define TRY_MSVC_INVAL \
Packit 8f70b4
      do                                                                       \
Packit 8f70b4
        {                                                                      \
Packit 8f70b4
          gl_msvc_inval_ensure_handler ();                                     \
Packit 8f70b4
          __try
Packit 8f70b4
#   define CATCH_MSVC_INVAL \
Packit 8f70b4
          __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER     \
Packit 8f70b4
                    ? EXCEPTION_EXECUTE_HANDLER                                \
Packit 8f70b4
                    : EXCEPTION_CONTINUE_SEARCH)
Packit 8f70b4
#   define DONE_MSVC_INVAL \
Packit 8f70b4
        }                                                                      \
Packit 8f70b4
      while (0)
Packit 8f70b4
Packit 8f70b4
#  else
Packit 8f70b4
/* Any compiler.
Packit 8f70b4
   We can only use setjmp/longjmp.  */
Packit 8f70b4
Packit 8f70b4
#   include <setjmp.h>
Packit 8f70b4
Packit 8f70b4
#   ifdef __cplusplus
Packit 8f70b4
extern "C" {
Packit 8f70b4
#   endif
Packit 8f70b4
Packit 8f70b4
struct gl_msvc_inval_per_thread
Packit 8f70b4
{
Packit 8f70b4
  /* The restart that will resume execution at the code between
Packit 8f70b4
     CATCH_MSVC_INVAL and DONE_MSVC_INVAL.  It is enabled only between
Packit 8f70b4
     TRY_MSVC_INVAL and CATCH_MSVC_INVAL.  */
Packit 8f70b4
  jmp_buf restart;
Packit 8f70b4
Packit 8f70b4
  /* Tells whether the contents of restart is valid.  */
Packit 8f70b4
  int restart_valid;
Packit 8f70b4
};
Packit 8f70b4
Packit 8f70b4
/* Ensure that the invalid parameter handler in installed that passes
Packit 8f70b4
   control to the gl_msvc_inval_restart if it is valid, or raises a
Packit 8f70b4
   software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise.
Packit 8f70b4
   Because we assume no other part of the program installs a different
Packit 8f70b4
   invalid parameter handler, this solution is multithread-safe.  */
Packit 8f70b4
extern void gl_msvc_inval_ensure_handler (void);
Packit 8f70b4
Packit 8f70b4
/* Return a pointer to the per-thread data for the current thread.  */
Packit 8f70b4
extern struct gl_msvc_inval_per_thread *gl_msvc_inval_current (void);
Packit 8f70b4
Packit 8f70b4
#   ifdef __cplusplus
Packit 8f70b4
}
Packit 8f70b4
#   endif
Packit 8f70b4
Packit 8f70b4
#   define TRY_MSVC_INVAL \
Packit 8f70b4
      do                                                                       \
Packit 8f70b4
        {                                                                      \
Packit 8f70b4
          struct gl_msvc_inval_per_thread *msvc_inval_current;                 \
Packit 8f70b4
          gl_msvc_inval_ensure_handler ();                                     \
Packit 8f70b4
          msvc_inval_current = gl_msvc_inval_current ();                       \
Packit 8f70b4
          /* First, initialize gl_msvc_inval_restart.  */                      \
Packit 8f70b4
          if (setjmp (msvc_inval_current->restart) == 0)                       \
Packit 8f70b4
            {                                                                  \
Packit 8f70b4
              /* Then, mark it as valid.  */                                   \
Packit 8f70b4
              msvc_inval_current->restart_valid = 1;
Packit 8f70b4
#   define CATCH_MSVC_INVAL \
Packit 8f70b4
              /* Execution completed.                                          \
Packit 8f70b4
                 Mark gl_msvc_inval_restart as invalid.  */                    \
Packit 8f70b4
              msvc_inval_current->restart_valid = 0;                           \
Packit 8f70b4
            }                                                                  \
Packit 8f70b4
          else                                                                 \
Packit 8f70b4
            {                                                                  \
Packit 8f70b4
              /* Execution triggered an invalid parameter notification.        \
Packit 8f70b4
                 Mark gl_msvc_inval_restart as invalid.  */                    \
Packit 8f70b4
              msvc_inval_current->restart_valid = 0;
Packit 8f70b4
#   define DONE_MSVC_INVAL \
Packit 8f70b4
            }                                                                  \
Packit 8f70b4
        }                                                                      \
Packit 8f70b4
      while (0)
Packit 8f70b4
Packit 8f70b4
#  endif
Packit 8f70b4
Packit 8f70b4
# endif
Packit 8f70b4
Packit 8f70b4
#else
Packit 8f70b4
/* A platform that does not need to the invalid parameter handler,
Packit 8f70b4
   or when SANE_LIBRARY_HANDLING is desired.  */
Packit 8f70b4
Packit 8f70b4
/* The braces here avoid GCC warnings like
Packit 8f70b4
   "warning: suggest explicit braces to avoid ambiguous 'else'".  */
Packit 8f70b4
# define TRY_MSVC_INVAL \
Packit 8f70b4
    do                                                                         \
Packit 8f70b4
      {                                                                        \
Packit 8f70b4
        if (1)
Packit 8f70b4
# define CATCH_MSVC_INVAL \
Packit 8f70b4
        else
Packit 8f70b4
# define DONE_MSVC_INVAL \
Packit 8f70b4
      }                                                                        \
Packit 8f70b4
    while (0)
Packit 8f70b4
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
#endif /* _MSVC_INVAL_H */