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*/
49static 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
58static 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
73const 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
108void 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
142void 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
170void 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
194void 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
220int 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
277my_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
315void 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