Commit | Line | Data |
---|---|---|
35428fb6 LC |
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 Lesser 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 Lesser General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Lesser 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 */ |