Enchant
Generic spell checking library
msvc-inval.h
1 /* Invalid parameter handler for MSVC runtime libraries.
2  Copyright (C) 2011-2023 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 /* This file uses HAVE_MSVC_INVALID_PARAMETER_HANDLER. */
56 #if !_GL_CONFIG_H_INCLUDED
57  #error "Please include config.h first."
58 #endif
59 
60 #define DEFAULT_HANDLING 0
61 #define HAIRY_LIBRARY_HANDLING 1
62 #define SANE_LIBRARY_HANDLING 2
63 
64 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER \
65  && !(MSVC_INVALID_PARAMETER_HANDLING == SANE_LIBRARY_HANDLING)
66 /* A native Windows platform with the "invalid parameter handler" concept,
67  and either DEFAULT_HANDLING or HAIRY_LIBRARY_HANDLING. */
68 
69 # if MSVC_INVALID_PARAMETER_HANDLING == DEFAULT_HANDLING
70 /* Default handling. */
71 
72 # ifdef __cplusplus
73 extern "C" {
74 # endif
75 
76 /* Ensure that the invalid parameter handler in installed that just returns.
77  Because we assume no other part of the program installs a different
78  invalid parameter handler, this solution is multithread-safe. */
79 extern void gl_msvc_inval_ensure_handler (void);
80 
81 # ifdef __cplusplus
82 }
83 # endif
84 
85 # define TRY_MSVC_INVAL \
86  do \
87  { \
88  gl_msvc_inval_ensure_handler (); \
89  if (1)
90 # define CATCH_MSVC_INVAL \
91  else
92 # define DONE_MSVC_INVAL \
93  } \
94  while (0)
95 
96 # else
97 /* Handling for hairy libraries. */
98 
99 # include <excpt.h>
100 
101 /* Gnulib can define its own status codes, as described in the page
102  "Raising Software Exceptions" on microsoft.com
103  <https://docs.microsoft.com/en-us/cpp/cpp/raising-software-exceptions>.
104  Our status codes are composed of
105  - 0xE0000000, mandatory for all user-defined status codes,
106  - 0x474E550, a API identifier ("GNU"),
107  - 0, 1, 2, ..., used to distinguish different status codes from the
108  same API. */
109 # define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0)
110 
111 # if defined _MSC_VER
112 /* A compiler that supports __try/__except, as described in the page
113  "try-except statement" on microsoft.com
114  <https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement>.
115  With __try/__except, we can use the multithread-safe exception handling. */
116 
117 # ifdef __cplusplus
118 extern "C" {
119 # endif
120 
121 /* Ensure that the invalid parameter handler in installed that raises a
122  software exception with code STATUS_GNULIB_INVALID_PARAMETER.
123  Because we assume no other part of the program installs a different
124  invalid parameter handler, this solution is multithread-safe. */
125 extern void gl_msvc_inval_ensure_handler (void);
126 
127 # ifdef __cplusplus
128 }
129 # endif
130 
131 # define TRY_MSVC_INVAL \
132  do \
133  { \
134  gl_msvc_inval_ensure_handler (); \
135  __try
136 # define CATCH_MSVC_INVAL \
137  __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER \
138  ? EXCEPTION_EXECUTE_HANDLER \
139  : EXCEPTION_CONTINUE_SEARCH)
140 # define DONE_MSVC_INVAL \
141  } \
142  while (0)
143 
144 # else
145 /* Any compiler.
146  We can only use setjmp/longjmp. */
147 
148 # include <setjmp.h>
149 
150 # ifdef __cplusplus
151 extern "C" {
152 # endif
153 
154 struct gl_msvc_inval_per_thread
155 {
156  /* The restart that will resume execution at the code between
157  CATCH_MSVC_INVAL and DONE_MSVC_INVAL. It is enabled only between
158  TRY_MSVC_INVAL and CATCH_MSVC_INVAL. */
159  jmp_buf restart;
160 
161  /* Tells whether the contents of restart is valid. */
162  int restart_valid;
163 };
164 
165 /* Ensure that the invalid parameter handler in installed that passes
166  control to the gl_msvc_inval_restart if it is valid, or raises a
167  software exception with code STATUS_GNULIB_INVALID_PARAMETER otherwise.
168  Because we assume no other part of the program installs a different
169  invalid parameter handler, this solution is multithread-safe. */
170 extern void gl_msvc_inval_ensure_handler (void);
171 
172 /* Return a pointer to the per-thread data for the current thread. */
173 extern struct gl_msvc_inval_per_thread *gl_msvc_inval_current (void);
174 
175 # ifdef __cplusplus
176 }
177 # endif
178 
179 # define TRY_MSVC_INVAL \
180  do \
181  { \
182  struct gl_msvc_inval_per_thread *msvc_inval_current; \
183  gl_msvc_inval_ensure_handler (); \
184  msvc_inval_current = gl_msvc_inval_current (); \
185  /* First, initialize gl_msvc_inval_restart. */ \
186  if (setjmp (msvc_inval_current->restart) == 0) \
187  { \
188  /* Then, mark it as valid. */ \
189  msvc_inval_current->restart_valid = 1;
190 # define CATCH_MSVC_INVAL \
191  /* Execution completed. \
192  Mark gl_msvc_inval_restart as invalid. */ \
193  msvc_inval_current->restart_valid = 0; \
194  } \
195  else \
196  { \
197  /* Execution triggered an invalid parameter notification. \
198  Mark gl_msvc_inval_restart as invalid. */ \
199  msvc_inval_current->restart_valid = 0;
200 # define DONE_MSVC_INVAL \
201  } \
202  } \
203  while (0)
204 
205 # endif
206 
207 # endif
208 
209 #else
210 /* A platform that does not need to the invalid parameter handler,
211  or when SANE_LIBRARY_HANDLING is desired. */
212 
213 /* The braces here avoid GCC warnings like
214  "warning: suggest explicit braces to avoid ambiguous 'else'". */
215 # define TRY_MSVC_INVAL \
216  do \
217  { \
218  if (1)
219 # define CATCH_MSVC_INVAL \
220  else
221 # define DONE_MSVC_INVAL \
222  } \
223  while (0)
224 
225 #endif
226 
227 #endif /* _MSVC_INVAL_H */