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