1  /* Invalid parameter handler for MSVC runtime libraries.
       2     Copyright (C) 2011-2022 Free Software Foundation, Inc.
       3  
       4     This file is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU Lesser General Public License as
       6     published by the Free Software Foundation; either version 2.1 of the
       7     License, or (at your option) any later version.
       8  
       9     This file is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  #ifndef _MSVC_INVAL_H
      18  #define _MSVC_INVAL_H
      19  
      20  /* With MSVC runtime libraries with the "invalid parameter handler" concept,
      21     functions like fprintf(), dup2(), or close() crash when the caller passes
      22     an invalid argument.  But POSIX wants error codes (such as EINVAL or EBADF)
      23     instead.
      24     This file defines macros that turn such an invalid parameter notification
      25     into a non-local exit.  An error code can then be produced at the target
      26     of this exit.  You can thus write code like
      27  
      28       TRY_MSVC_INVAL
      29         {
      30           <Code that can trigger an invalid parameter notification
      31            but does not do 'return', 'break', 'continue', nor 'goto'.>
      32         }
      33       CATCH_MSVC_INVAL
      34         {
      35           <Code that handles an invalid parameter notification
      36            but does not do 'return', 'break', 'continue', nor 'goto'.>
      37         }
      38       DONE_MSVC_INVAL;
      39  
      40     This entire block expands to a single statement.
      41  
      42     The handling of invalid parameters can be done in three ways:
      43  
      44       * The default way, which is reasonable for programs (not libraries):
      45         AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [DEFAULT_HANDLING])
      46  
      47       * The way for libraries that make "hairy" calls (like close(-1), or
      48         fclose(fp) where fileno(fp) is closed, or simply getdtablesize()):
      49         AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [HAIRY_LIBRARY_HANDLING])
      50  
      51       * The way for libraries that make no "hairy" calls:
      52         AC_DEFINE([MSVC_INVALID_PARAMETER_HANDLING], [SANE_LIBRARY_HANDLING])
      53   */
      54  
      55  #define DEFAULT_HANDLING       0
      56  #define HAIRY_LIBRARY_HANDLING 1
      57  #define SANE_LIBRARY_HANDLING  2
      58  
      59  #if HAVE_MSVC_INVALID_PARAMETER_HANDLER \
      60      && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING)
      61  /* A native Windows platform with the "invalid parameter handler" concept,
      62     and either DEFAULT_HANDLING or HAIRY_LIBRARY_HANDLING.  */
      63  
      64  # if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING
      65  /* Default handling.  */
      66  
      67  #  ifdef __cplusplus
      68  extern "C" {
      69  #  endif
      70  
      71  /* Ensure that the invalid parameter handler in installed that just returns.
      72     Because we assume no other part of the program installs a different
      73     invalid parameter handler, this solution is multithread-safe.  */
      74  extern void gl_msvc_inval_ensure_handler (void);
      75  
      76  #  ifdef __cplusplus
      77  }
      78  #  endif
      79  
      80  #  define TRY_MSVC_INVAL \
      81       do                                                                        \
      82         {                                                                       \
      83           gl_msvc_inval_ensure_handler ();                                      \
      84           if (1)
      85  #  define CATCH_MSVC_INVAL \
      86           else
      87  #  define DONE_MSVC_INVAL \
      88         }                                                                       \
      89       while (0)
      90  
      91  # else
      92  /* Handling for hairy libraries.  */
      93  
      94  #  include <excpt.h>
      95  
      96  /* Gnulib can define its own status codes, as described in the page
      97     "Raising Software Exceptions" on microsoft.com
      98     <https://docs.microsoft.com/en-us/cpp/cpp/raising-software-exceptions>.
      99     Our status codes are composed of
     100       - 0xE0000000, mandatory for all user-defined status codes,
     101       - 0x474E550, a API identifier ("GNU"),
     102       - 0, 1, 2, ..., used to distinguish different status codes from the
     103         same API.  */
     104  #  define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0)
     105  
     106  #  if defined _MSC_VER
     107  /* A compiler that supports __try/__except, as described in the page
     108     "try-except statement" on microsoft.com
     109     <https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement>.
     110     With __try/__except, we can use the multithread-safe exception handling.  */
     111  
     112  #   ifdef __cplusplus
     113  extern "C" {
     114  #   endif
     115  
     116  /* Ensure that the invalid parameter handler in installed that raises a
     117     software exception with code STATUS_GNULIB_INVALID_PARAMETER.
     118     Because we assume no other part of the program installs a different
     119     invalid parameter handler, this solution is multithread-safe.  */
     120  extern void gl_msvc_inval_ensure_handler (void);
     121  
     122  #   ifdef __cplusplus
     123  }
     124  #   endif
     125  
     126  #   define TRY_MSVC_INVAL \
     127        do                                                                       \
     128          {                                                                      \
     129            gl_msvc_inval_ensure_handler ();                                     \
     130            __try
     131  #   define CATCH_MSVC_INVAL \
     132            __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER     \
     133                      ? EXCEPTION_EXECUTE_HANDLER                                \
     134                      : EXCEPTION_CONTINUE_SEARCH)
     135  #   define DONE_MSVC_INVAL \
     136          }                                                                      \
     137        while (0)
     138  
     139  #  else
     140  /* Any compiler.
     141     We can only use setjmp/longjmp.  */
     142  
     143  #   include <setjmp.h>
     144  
     145  #   ifdef __cplusplus
     146  extern "C" {
     147  #   endif
     148  
     149  struct gl_msvc_inval_per_thread
     150  {
     151    /* The restart that will resume execution at the code between
     152       CATCH_MSVC_INVAL and DONE_MSVC_INVAL.  It is enabled only between
     153       TRY_MSVC_INVAL and CATCH_MSVC_INVAL.  */
     154    jmp_buf restart;
     155  
     156    /* Tells whether the contents of restart is valid.  */
     157    int restart_valid;
     158  };
     159  
     160  /* Ensure that the invalid parameter handler in installed that passes
     161     control to the gl_msvc_inval_restart if it is valid, or raises a
     162     software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise.
     163     Because we assume no other part of the program installs a different
     164     invalid parameter handler, this solution is multithread-safe.  */
     165  extern void gl_msvc_inval_ensure_handler (void);
     166  
     167  /* Return a pointer to the per-thread data for the current thread.  */
     168  extern struct gl_msvc_inval_per_thread *gl_msvc_inval_current (void);
     169  
     170  #   ifdef __cplusplus
     171  }
     172  #   endif
     173  
     174  #   define TRY_MSVC_INVAL \
     175        do                                                                       \
     176          {                                                                      \
     177            struct gl_msvc_inval_per_thread *msvc_inval_current;                 \
     178            gl_msvc_inval_ensure_handler ();                                     \
     179            msvc_inval_current = gl_msvc_inval_current ();                       \
     180            /* First, initialize gl_msvc_inval_restart.  */                      \
     181            if (setjmp (msvc_inval_current->restart) == 0)                       \
     182              {                                                                  \
     183                /* Then, mark it as valid.  */                                   \
     184                msvc_inval_current->restart_valid = 1;
     185  #   define CATCH_MSVC_INVAL \
     186                /* Execution completed.                                          \
     187                   Mark gl_msvc_inval_restart as invalid.  */                    \
     188                msvc_inval_current->restart_valid = 0;                           \
     189              }                                                                  \
     190            else                                                                 \
     191              {                                                                  \
     192                /* Execution triggered an invalid parameter notification.        \
     193                   Mark gl_msvc_inval_restart as invalid.  */                    \
     194                msvc_inval_current->restart_valid = 0;
     195  #   define DONE_MSVC_INVAL \
     196              }                                                                  \
     197          }                                                                      \
     198        while (0)
     199  
     200  #  endif
     201  
     202  # endif
     203  
     204  #else
     205  /* A platform that does not need to the invalid parameter handler,
     206     or when SANE_LIBRARY_HANDLING is desired.  */
     207  
     208  /* The braces here avoid GCC warnings like
     209     "warning: suggest explicit braces to avoid ambiguous 'else'".  */
     210  # define TRY_MSVC_INVAL \
     211      do                                                                         \
     212        {                                                                        \
     213          if (1)
     214  # define CATCH_MSVC_INVAL \
     215          else
     216  # define DONE_MSVC_INVAL \
     217        }                                                                        \
     218      while (0)
     219  
     220  #endif
     221  
     222  #endif /* _MSVC_INVAL_H */