1/* Invalid parameter handler for MSVC runtime libraries.
2   Copyright (C) 2011-2012 Free Software Foundation, Inc.
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 3, or (at your option)
7   any later version.
8
9   This program 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 General Public License for more details.
13
14   You should have received a copy of the GNU General Public License along
15   with this program; if not, see <http://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
68extern "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.  */
74extern 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   <http://msdn.microsoft.com/en-us/library/het71c37.aspx>.
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   <http://msdn.microsoft.com/en-us/library/s58ftw19.aspx>.
110   With __try/__except, we can use the multithread-safe exception handling.  */
111
112#   ifdef __cplusplus
113extern "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.  */
120extern 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
146extern "C" {
147#   endif
148
149struct 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.  */
165extern void gl_msvc_inval_ensure_handler (void);
166
167/* Return a pointer to the per-thread data for the current thread.  */
168extern 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 */
223