New module 'msvc-inval'.
[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', nor 'goto'.>
33        }
34      CATCH_MSVC_INVAL
35        {
36          <Code that handles an invalid parameter notification
37           but does not do 'return', 'break', nor 'goto'.>
38        }
39      DONE_MSVC_INVAL
40  */
41
42 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
43 /* A native Windows platform with the "invalid parameter handler" concept.  */
44
45 /* Get _invalid_parameter_handler type and _set_invalid_parameter_handler
46    declaration.  */
47 #include <stdlib.h>
48
49 # if defined _MSC_VER
50 /* A compiler that supports __try/__except, as described in the page
51    "try-except statement" on microsoft.com
52    <http://msdn.microsoft.com/en-us/library/s58ftw19.aspx>.
53    With __try/__except, we can use the multithread-safe exception handling.  */
54
55 /* Gnulib can define its own status codes, as described in the page
56    "Raising Software Exceptions" on microsoft.com
57    <http://msdn.microsoft.com/en-us/library/het71c37.aspx>.
58    Our status codes are composed of
59      - 0xE0000000, mandatory for all user-defined status codes,
60      - 0x474E550, a API identifier ("GNU"),
61      - 0, 1, 2, ..., used to distinguish different status codes from the
62        same API.  */
63 #  define STATUS_GNULIB_INVALID_PARAMETER (0xE0000000 + 0x474E550 + 0)
64
65 #  ifdef __cplusplus
66 extern "C" {
67 #  endif
68
69 /* Ensure that the invalid parameter handler in installed that raises a
70    software exception with code STATUS_GNULIB_INVALID_PARAMETER.
71    Because we assume no other part of the program installs a different
72    invalid parameter handler, this solution is multithread-safe.  */
73 extern void gl_msvc_inval_ensure_handler (void);
74
75 #  ifdef __cplusplus
76 }
77 #  endif
78
79 #  define TRY_MSVC_INVAL \
80      gl_msvc_inval_ensure_handler ();                                          \
81      __try
82 #  define CATCH_MSVC_INVAL \
83      __except (GetExceptionCode () == STATUS_GNULIB_INVALID_PARAMETER          \
84                ? EXCEPTION_EXECUTE_HANDLER                                     \
85                : EXCEPTION_CONTINUE_SEARCH)
86 #  define DONE_MSVC_INVAL
87
88 # else
89 /* Any compiler.
90    We can only use setjmp/longjmp.
91    Unfortunately, this is *not* multithread-safe.  */
92
93 #  include <setjmp.h>
94
95 #  ifdef __cplusplus
96 extern "C" {
97 #  endif
98
99 /* The restart that will resume execution at the code between
100    CATCH_MSVC_INVAL and DONE_MSVC_INVAL.  It is enabled only between
101    TRY_MSVC_INVAL and CATCH_MSVC_INVAL.  */
102 extern jmp_buf gl_msvc_inval_restart;
103
104 /* The invalid parameter handler that unwinds the stack up to the
105    gl_msvc_inval_restart.  It is enabled only between TRY_MSVC_INVAL
106    and CATCH_MSVC_INVAL.  */
107 extern void cdecl gl_msvc_invalid_parameter_handler (const wchar_t *expression,
108                                                      const wchar_t *function,
109                                                      const wchar_t *file,
110                                                      unsigned int line,
111                                                      uintptr_t dummy);
112
113 #  ifdef __cplusplus
114 }
115 #  endif
116
117 #  define TRY_MSVC_INVAL \
118      {                                                                         \
119        _invalid_parameter_handler orig_handler;                                \
120        /* First, initialize gl_msvc_inval_restart.  */                         \
121        if (setjmp (gl_msvc_inval_restart) == 0)                                \
122          {                                                                     \
123            /* Then, enable gl_msvc_invalid_parameter_handler.  */              \
124            orig_handler =                                                      \
125              _set_invalid_parameter_handler (gl_msvc_invalid_parameter_handler);
126 #  define CATCH_MSVC_INVAL \
127            /* Execution completed.                                             \
128               Disable gl_msvc_invalid_parameter_handler.  */                   \
129            _set_invalid_parameter_handler (orig_handler);                      \
130          }                                                                     \
131        else                                                                    \
132          {                                                                     \
133            /* Execution triggered an invalid parameter notification.           \
134               Disable gl_msvc_invalid_parameter_handler.  */                   \
135            _set_invalid_parameter_handler (orig_handler);
136 #  define DONE_MSVC_INVAL \
137          }                                                                     \
138      }
139
140 # endif
141
142 #else
143
144 # define TRY_MSVC_INVAL if (1)
145 # define CATCH_MSVC_INVAL else
146 # define DONE_MSVC_INVAL
147
148 #endif
149
150 #endif /* _MSVC_INVAL_H */