| 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 | |