1 /* Invalid parameter handler for MSVC runtime libraries. 2 Copyright (C) 2011-2018 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 <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://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 <https://msdn.microsoft.com/en-us/library/s58ftw19.aspx>. 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 */ 223