diff options
Diffstat (limited to 'mysys/my_error.c')
-rw-r--r-- | mysys/my_error.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/mysys/my_error.c b/mysys/my_error.c new file mode 100644 index 00000000..106e51de --- /dev/null +++ b/mysys/my_error.c @@ -0,0 +1,329 @@ +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <m_string.h> +#include <stdarg.h> +#include <m_ctype.h> + +/* Max length of a error message. Should be kept in sync with MYSQL_ERRMSG_SIZE. */ +#define ERRMSGSIZE (512) + +/* Define some external variables for error handling */ + +/* + WARNING! + my_error family functions have to be used according following rules: + - if message have not parameters use my_message(ER_CODE, ER(ER_CODE), MYF(N)) + - if message registered use my_error(ER_CODE, MYF(N), ...). + - With some special text of errror message use: + my_printf_error(ER_CODE, format, MYF(N), ...) +*/ + +/* + Message texts are registered into a linked list of 'my_err_head' structs. + Each struct contains (1.) an array of pointers to C character strings with + '\0' termination, (2.) the error number for the first message in the array + (array index 0) and (3.) the error number for the last message in the array + (array index (last - first)). + The array may contain gaps with NULL pointers and pointers to empty strings. + Both kinds of gaps will be translated to "Unknown error %d.", if my_error() + is called with a respective error number. + The list of header structs is sorted in increasing order of error numbers. + Negative error numbers are allowed. Overlap of error numbers is not allowed. + Not registered error numbers will be translated to "Unknown error %d.". +*/ +static struct my_err_head +{ + struct my_err_head *meh_next; /* chain link */ + const char** (*get_errmsgs)(int nr); /* returns error message format */ + uint meh_first; /* error number matching array slot 0 */ + uint meh_last; /* error number matching last slot */ +} my_errmsgs_globerrs= +{NULL, get_global_errmsgs, EE_ERROR_FIRST, EE_ERROR_LAST}; + +static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs; + + +/** + @brief Get an error format string from one of the my_error_register()ed sets + + @note + NULL values are possible even within a registered range. + + @param nr Errno + + @retval NULL if no message is registered for this error number + @retval str C-string +*/ + +const char *my_get_err_msg(uint nr) +{ + const char *format; + struct my_err_head *meh_p; + + /* Search for the range this error is in. */ + for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next) + if (nr <= meh_p->meh_last) + break; + + /* + If we found the range this error number is in, get the format string. + If the string is empty, or a NULL pointer, or if we're out of return, + we return NULL. + */ + if (!(format= (meh_p && (nr >= meh_p->meh_first)) ? + meh_p->get_errmsgs(nr)[nr - meh_p->meh_first] : NULL) || + !*format) + return NULL; + + return format; +} + + +/** + Fill in and print a previously registered error message. + + @note + Goes through the (sole) function registered in error_handler_hook + + @param nr error number + @param MyFlags Flags + @param ... variable list matching that error format string +*/ + +void my_error(uint nr, myf MyFlags, ...) +{ + const char *format; + va_list args; + char ebuff[ERRMSGSIZE]; + DBUG_ENTER("my_error"); + DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d", nr, MyFlags, errno)); + if (!(format = my_get_err_msg(nr))) + (void) my_snprintf(ebuff, sizeof(ebuff), "Unknown error %d", nr); + else + { + va_start(args,MyFlags); + (void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, ebuff, + sizeof(ebuff), format, args); + va_end(args); + } + (*error_handler_hook)(nr, ebuff, MyFlags); + DBUG_VOID_RETURN; +} + + +/** + Print an error message. + + @note + Just like my_error, but for cases when the error message is not ER(error) + + @param error error number + @param format format string + @param MyFlags Flags + @param ... variable list matching that error format string +*/ + +void my_printf_error(uint error, const char *format, myf MyFlags, ...) +{ + va_list args; + char ebuff[ERRMSGSIZE]; + DBUG_ENTER("my_printf_error"); + DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d format: %s", + error, MyFlags, errno, format)); + + va_start(args,MyFlags); + (void) my_vsnprintf_ex(&my_charset_utf8mb3_general_ci, ebuff, + sizeof(ebuff), format, args); + va_end(args); + (*error_handler_hook)(error, ebuff, MyFlags); + DBUG_VOID_RETURN; +} + +/** + Print an error message. + + @note + Goes through the (sole) function registered in error_handler_hook + + @param error error number + @param format format string + @param MyFlags Flags + @param ap variable list matching that error format string +*/ + +void my_printv_error(uint error, const char *format, myf MyFlags, va_list ap) +{ + char ebuff[ERRMSGSIZE]; + DBUG_ENTER("my_printv_error"); + DBUG_PRINT("my", ("nr: %d MyFlags: %lu errno: %d format: %s", + error, MyFlags, errno, format)); + + (void) my_vsnprintf(ebuff, sizeof(ebuff), format, ap); + (*error_handler_hook)(error, ebuff, MyFlags); + DBUG_VOID_RETURN; +} + + +/** + Print an error message. + + @note + Goes through the (sole) function registered in error_handler_hook + + @param error error number + @param str error message + @param MyFlags Flags +*/ + +void my_message(uint error, const char *str, register myf MyFlags) +{ + (*error_handler_hook)(error, str, MyFlags); +} + + +/** + Register error messages for use with my_error(). + + @description + + The pointer array is expected to contain addresses to NUL-terminated + C character strings. The array contains (last - first + 1) pointers. + NULL pointers and empty strings ("") are allowed. These will be mapped to + "Unknown error" when my_error() is called with a matching error number. + This function registers the error numbers 'first' to 'last'. + No overlapping with previously registered error numbers is allowed. + + @param errmsgs array of pointers to error messages + @param first error number of first message in the array + @param last error number of last message in the array + + @retval 0 OK + @retval != 0 Error +*/ + +int my_error_register(const char** (*get_errmsgs)(int error), uint first, + uint last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + + /* Allocate a new header structure. */ + if (! (meh_p= (struct my_err_head*) my_malloc(key_memory_my_err_head, + sizeof(struct my_err_head), + MYF(MY_WME)))) + return 1; + meh_p->get_errmsgs= get_errmsgs; + meh_p->meh_first= first; + meh_p->meh_last= last; + + /* Search for the right position in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if ((*search_meh_pp)->meh_last > first) + break; + } + + /* Error numbers must be unique. No overlapping is allowed. */ + if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last)) + { + my_free(meh_p); + return 1; + } + + /* Insert header into the chain. */ + meh_p->meh_next= *search_meh_pp; + *search_meh_pp= meh_p; + return 0; +} + + +/** + Unregister formerly registered error messages. + + @description + + This function unregisters the error numbers 'first' to 'last'. + These must have been previously registered by my_error_register(). + 'first' and 'last' must exactly match the registration. + If a matching registration is present, the header is removed from the + list and the pointer to the error messages pointers array is returned. + (The messages themselves are not released here as they may be static.) + Otherwise, NULL is returned. + + @param first error number of first message + @param last error number of last message + + @retval NULL Error, no such number range registered. + @retval non-NULL OK, returns address of error messages pointers array. +*/ + +my_bool my_error_unregister(uint first, uint last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + + /* Search for the registration in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if (((*search_meh_pp)->meh_first == first) && + ((*search_meh_pp)->meh_last == last)) + break; + } + if (! *search_meh_pp) + return TRUE; + + /* Remove header from the chain. */ + meh_p= *search_meh_pp; + *search_meh_pp= meh_p->meh_next; + + my_free(meh_p); + + return FALSE; +} + + +/** + Unregister all formerly registered error messages. + + @description + + This function unregisters all error numbers that previously have + been previously registered by my_error_register(). + All headers are removed from the list; the messages themselves are + not released here as they may be static. +*/ + +void my_error_unregister_all(void) +{ + struct my_err_head *cursor, *saved_next; + + for (cursor= my_errmsgs_globerrs.meh_next; cursor != NULL; cursor= saved_next) + { + /* We need this ptr, but we're about to free its container, so save it. */ + saved_next= cursor->meh_next; + + my_free(cursor); + } + my_errmsgs_globerrs.meh_next= NULL; /* Freed in first iteration above. */ + + my_errmsgs_list= &my_errmsgs_globerrs; +} |