1 | /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. |
2 | |
3 | This program is free software; you can redistribute it and/or modify |
4 | it under the terms of the GNU General Public License as published by |
5 | the Free Software Foundation; version 2 of the License. |
6 | |
7 | This program is distributed in the hope that it will be useful, |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | GNU General Public License for more details. |
11 | |
12 | You should have received a copy of the GNU General Public License |
13 | along with this program; if not, write to the Free Software |
14 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
15 | |
16 | #include "mysys_priv.h" |
17 | #include "mysys_err.h" |
18 | #include <m_string.h> |
19 | #include <stdarg.h> |
20 | #include <m_ctype.h> |
21 | |
22 | /* Max length of a error message. Should be kept in sync with MYSQL_ERRMSG_SIZE. */ |
23 | #define ERRMSGSIZE (512) |
24 | |
25 | /* Define some external variables for error handling */ |
26 | |
27 | /* |
28 | WARNING! |
29 | my_error family functions have to be used according following rules: |
30 | - if message have not parameters use my_message(ER_CODE, ER(ER_CODE), MYF(N)) |
31 | - if message registered use my_error(ER_CODE, MYF(N), ...). |
32 | - With some special text of errror message use: |
33 | my_printf_error(ER_CODE, format, MYF(N), ...) |
34 | */ |
35 | |
36 | /* |
37 | Message texts are registered into a linked list of 'my_err_head' structs. |
38 | Each struct contains (1.) an array of pointers to C character strings with |
39 | '\0' termination, (2.) the error number for the first message in the array |
40 | (array index 0) and (3.) the error number for the last message in the array |
41 | (array index (last - first)). |
42 | The array may contain gaps with NULL pointers and pointers to empty strings. |
43 | Both kinds of gaps will be translated to "Unknown error %d.", if my_error() |
44 | is called with a respective error number. |
45 | The list of header structs is sorted in increasing order of error numbers. |
46 | Negative error numbers are allowed. Overlap of error numbers is not allowed. |
47 | Not registered error numbers will be translated to "Unknown error %d.". |
48 | */ |
49 | static struct my_err_head |
50 | { |
51 | struct my_err_head *meh_next; /* chain link */ |
52 | const char** (*get_errmsgs)(int nr); /* returns error message format */ |
53 | uint meh_first; /* error number matching array slot 0 */ |
54 | uint meh_last; /* error number matching last slot */ |
55 | } my_errmsgs_globerrs= |
56 | {NULL, get_global_errmsgs, EE_ERROR_FIRST, EE_ERROR_LAST}; |
57 | |
58 | static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs; |
59 | |
60 | |
61 | /** |
62 | @brief Get an error format string from one of the my_error_register()ed sets |
63 | |
64 | @note |
65 | NULL values are possible even within a registered range. |
66 | |
67 | @param nr Errno |
68 | |
69 | @retval NULL if no message is registered for this error number |
70 | @retval str C-string |
71 | */ |
72 | |
73 | const char *my_get_err_msg(uint nr) |
74 | { |
75 | const char *format; |
76 | struct my_err_head *meh_p; |
77 | |
78 | /* Search for the range this error is in. */ |
79 | for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next) |
80 | if (nr <= meh_p->meh_last) |
81 | break; |
82 | |
83 | /* |
84 | If we found the range this error number is in, get the format string. |
85 | If the string is empty, or a NULL pointer, or if we're out of return, |
86 | we return NULL. |
87 | */ |
88 | if (!(format= (meh_p && (nr >= meh_p->meh_first)) ? |
89 | meh_p->get_errmsgs(nr)[nr - meh_p->meh_first] : NULL) || |
90 | !*format) |
91 | return NULL; |
92 | |
93 | return format; |
94 | } |
95 | |
96 | |
97 | /** |
98 | Fill in and print a previously registered error message. |
99 | |
100 | @note |
101 | Goes through the (sole) function registered in error_handler_hook |
102 | |
103 | @param nr error number |
104 | @param MyFlags Flags |
105 | @param ... variable list matching that error format string |
106 | */ |
107 | |
108 | void my_error(uint nr, myf MyFlags, ...) |
109 | { |
110 | const char *format; |
111 | va_list args; |
112 | char ebuff[ERRMSGSIZE]; |
113 | DBUG_ENTER("my_error" ); |
114 | DBUG_PRINT("my" , ("nr: %d MyFlags: %lu errno: %d" , nr, MyFlags, errno)); |
115 | |
116 | if (!(format = my_get_err_msg(nr))) |
117 | (void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d" , nr); |
118 | else |
119 | { |
120 | va_start(args,MyFlags); |
121 | (void) my_vsnprintf_ex(&my_charset_utf8_general_ci, ebuff, |
122 | sizeof(ebuff), format, args); |
123 | va_end(args); |
124 | } |
125 | (*error_handler_hook)(nr, ebuff, MyFlags); |
126 | DBUG_VOID_RETURN; |
127 | } |
128 | |
129 | |
130 | /** |
131 | Print an error message. |
132 | |
133 | @note |
134 | Just like my_error, but for cases when the error message is not ER(error) |
135 | |
136 | @param error error number |
137 | @param format format string |
138 | @param MyFlags Flags |
139 | @param ... variable list matching that error format string |
140 | */ |
141 | |
142 | void my_printf_error(uint error, const char *format, myf MyFlags, ...) |
143 | { |
144 | va_list args; |
145 | char ebuff[ERRMSGSIZE]; |
146 | DBUG_ENTER("my_printf_error" ); |
147 | DBUG_PRINT("my" , ("nr: %d MyFlags: %lu errno: %d format: %s" , |
148 | error, MyFlags, errno, format)); |
149 | |
150 | va_start(args,MyFlags); |
151 | (void) my_vsnprintf_ex(&my_charset_utf8_general_ci, ebuff, |
152 | sizeof(ebuff), format, args); |
153 | va_end(args); |
154 | (*error_handler_hook)(error, ebuff, MyFlags); |
155 | DBUG_VOID_RETURN; |
156 | } |
157 | |
158 | /** |
159 | Print an error message. |
160 | |
161 | @note |
162 | Goes through the (sole) function registered in error_handler_hook |
163 | |
164 | @param error error number |
165 | @param format format string |
166 | @param MyFlags Flags |
167 | @param ap variable list matching that error format string |
168 | */ |
169 | |
170 | void my_printv_error(uint error, const char *format, myf MyFlags, va_list ap) |
171 | { |
172 | char ebuff[ERRMSGSIZE]; |
173 | DBUG_ENTER("my_printv_error" ); |
174 | DBUG_PRINT("my" , ("nr: %d MyFlags: %lu errno: %d format: %s" , |
175 | error, MyFlags, errno, format)); |
176 | |
177 | (void) my_vsnprintf(ebuff, sizeof(ebuff), format, ap); |
178 | (*error_handler_hook)(error, ebuff, MyFlags); |
179 | DBUG_VOID_RETURN; |
180 | } |
181 | |
182 | |
183 | /** |
184 | Print an error message. |
185 | |
186 | @note |
187 | Goes through the (sole) function registered in error_handler_hook |
188 | |
189 | @param error error number |
190 | @param str error message |
191 | @param MyFlags Flags |
192 | */ |
193 | |
194 | void my_message(uint error, const char *str, register myf MyFlags) |
195 | { |
196 | (*error_handler_hook)(error, str, MyFlags); |
197 | } |
198 | |
199 | |
200 | /** |
201 | Register error messages for use with my_error(). |
202 | |
203 | @description |
204 | |
205 | The pointer array is expected to contain addresses to NUL-terminated |
206 | C character strings. The array contains (last - first + 1) pointers. |
207 | NULL pointers and empty strings ("") are allowed. These will be mapped to |
208 | "Unknown error" when my_error() is called with a matching error number. |
209 | This function registers the error numbers 'first' to 'last'. |
210 | No overlapping with previously registered error numbers is allowed. |
211 | |
212 | @param errmsgs array of pointers to error messages |
213 | @param first error number of first message in the array |
214 | @param last error number of last message in the array |
215 | |
216 | @retval 0 OK |
217 | @retval != 0 Error |
218 | */ |
219 | |
220 | int my_error_register(const char** (*get_errmsgs)(int error), uint first, |
221 | uint last) |
222 | { |
223 | struct my_err_head *meh_p; |
224 | struct my_err_head **search_meh_pp; |
225 | |
226 | /* Allocate a new header structure. */ |
227 | if (! (meh_p= (struct my_err_head*) my_malloc(sizeof(struct my_err_head), |
228 | MYF(MY_WME)))) |
229 | return 1; |
230 | meh_p->get_errmsgs= get_errmsgs; |
231 | meh_p->meh_first= first; |
232 | meh_p->meh_last= last; |
233 | |
234 | /* Search for the right position in the list. */ |
235 | for (search_meh_pp= &my_errmsgs_list; |
236 | *search_meh_pp; |
237 | search_meh_pp= &(*search_meh_pp)->meh_next) |
238 | { |
239 | if ((*search_meh_pp)->meh_last > first) |
240 | break; |
241 | } |
242 | |
243 | /* Error numbers must be unique. No overlapping is allowed. */ |
244 | if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last)) |
245 | { |
246 | my_free(meh_p); |
247 | return 1; |
248 | } |
249 | |
250 | /* Insert header into the chain. */ |
251 | meh_p->meh_next= *search_meh_pp; |
252 | *search_meh_pp= meh_p; |
253 | return 0; |
254 | } |
255 | |
256 | |
257 | /** |
258 | Unregister formerly registered error messages. |
259 | |
260 | @description |
261 | |
262 | This function unregisters the error numbers 'first' to 'last'. |
263 | These must have been previously registered by my_error_register(). |
264 | 'first' and 'last' must exactly match the registration. |
265 | If a matching registration is present, the header is removed from the |
266 | list and the pointer to the error messages pointers array is returned. |
267 | (The messages themselves are not released here as they may be static.) |
268 | Otherwise, NULL is returned. |
269 | |
270 | @param first error number of first message |
271 | @param last error number of last message |
272 | |
273 | @retval NULL Error, no such number range registered. |
274 | @retval non-NULL OK, returns address of error messages pointers array. |
275 | */ |
276 | |
277 | my_bool my_error_unregister(uint first, uint last) |
278 | { |
279 | struct my_err_head *meh_p; |
280 | struct my_err_head **search_meh_pp; |
281 | |
282 | /* Search for the registration in the list. */ |
283 | for (search_meh_pp= &my_errmsgs_list; |
284 | *search_meh_pp; |
285 | search_meh_pp= &(*search_meh_pp)->meh_next) |
286 | { |
287 | if (((*search_meh_pp)->meh_first == first) && |
288 | ((*search_meh_pp)->meh_last == last)) |
289 | break; |
290 | } |
291 | if (! *search_meh_pp) |
292 | return TRUE; |
293 | |
294 | /* Remove header from the chain. */ |
295 | meh_p= *search_meh_pp; |
296 | *search_meh_pp= meh_p->meh_next; |
297 | |
298 | my_free(meh_p); |
299 | |
300 | return FALSE; |
301 | } |
302 | |
303 | |
304 | /** |
305 | Unregister all formerly registered error messages. |
306 | |
307 | @description |
308 | |
309 | This function unregisters all error numbers that previously have |
310 | been previously registered by my_error_register(). |
311 | All headers are removed from the list; the messages themselves are |
312 | not released here as they may be static. |
313 | */ |
314 | |
315 | void my_error_unregister_all(void) |
316 | { |
317 | struct my_err_head *cursor, *saved_next; |
318 | |
319 | for (cursor= my_errmsgs_globerrs.meh_next; cursor != NULL; cursor= saved_next) |
320 | { |
321 | /* We need this ptr, but we're about to free its container, so save it. */ |
322 | saved_next= cursor->meh_next; |
323 | |
324 | my_free(cursor); |
325 | } |
326 | my_errmsgs_globerrs.meh_next= NULL; /* Freed in first iteration above. */ |
327 | |
328 | my_errmsgs_list= &my_errmsgs_globerrs; |
329 | } |
330 | |