/* Copyright (c) 2000, 2013, Oracle and/or its affiliates Copyright (c) 2008, 2011, Monty Program Ab 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 */ /* Written by Anjuta Widenius */ /* Creates one include file and multiple language-error message files from one multi-language text file. */ #include #include #include #include #include #include #include #define MAX_ROWS 3000 #define ERRORS_PER_RANGE 1000 #define MAX_SECTIONS 4 #define HEADER_LENGTH 32 /* Length of header in errmsg.sys */ #define ERRMSG_VERSION 5 /* Version number of errmsg.sys */ #define DEFAULT_CHARSET_DIR "../sql/share/charsets" #define ER_PREFIX "ER_" #define ER_PREFIX2 "MARIA_ER_" #define WARN_PREFIX "WARN_" static char *OUTFILE= (char*) "errmsg.sys"; static char *HEADERFILE= (char*) "mysqld_error.h"; static char *NAMEFILE= (char*) "mysqld_ername.h"; static char *STATEFILE= (char*) "sql_state.h"; static char *TXTFILE= (char*) "../sql/share/errmsg-utf8.txt"; static char *DATADIRECTORY= (char*) "../sql/share/"; #ifndef DBUG_OFF static char *default_dbug_option= (char*) "d:t:O,/tmp/comp_err.trace"; #endif /* Header for errmsg.sys files */ uchar file_head[]= { 254, 254, 2, ERRMSG_VERSION }; /* Store positions to each error message row to store in errmsg.sys header */ uint file_pos[MAX_ROWS+1]; uint section_count,section_start; uchar section_header[MAX_SECTIONS*2]; const char *empty_string= ""; /* For empty states */ /* Default values for command line options. See getopt structure for definitions for these. */ const char *default_language= "eng"; my_bool default_language_changed= 0; uint er_offset= 1000; my_bool info_flag= 0; /* Storage of one error message row (for one language) */ struct message { char *lang_short_name; char *text; }; /* Storage for languages and charsets (from start of error text file) */ struct languages { char *lang_long_name; /* full name of the language */ char *lang_short_name; /* abbreviation of the lang. */ struct languages *next_lang; /* Pointer to next language */ }; /* Name, code and texts (for all lang) for one error message */ struct errors { const char *er_name; /* Name of the error (ER_HASHCK) */ uint d_code; /* Error code number */ const char *sql_code1; /* sql state */ const char *sql_code2; /* ODBC state */ struct errors *next_error; /* Pointer to next error */ DYNAMIC_ARRAY msg; /* All language texts for this error */ }; static struct my_option my_long_options[]= { #ifdef DBUG_OFF {"debug", '#', "This is a non-debug version. Catch this and exit", 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, #else {"debug", '#', "Output debug log", &default_dbug_option, &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"debug-info", 'T', "Print some debug info at exit.", &info_flag, &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"help", '?', "Displays this help and exits.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"version", 'V', "Prints version", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"charset", 'C', "Charset dir", (char**) &charsets_dir, (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"in_file", 'F', "Input file", &TXTFILE, &TXTFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"out_dir", 'D', "Output base directory", &DATADIRECTORY, &DATADIRECTORY, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"out_file", 'O', "Output filename (errmsg.sys)", &OUTFILE, &OUTFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"header_file", 'H', "mysqld_error.h file ", &HEADERFILE, &HEADERFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"name_file", 'N', "mysqld_ername.h file ", &NAMEFILE, &NAMEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"state_file", 'S', "sql_state.h file", &STATEFILE, &STATEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; static struct errors *generate_empty_message(uint dcode, my_bool skip); static struct languages *parse_charset_string(char *str); static struct errors *parse_error_string(char *ptr, int er_count); static struct message *parse_message_string(struct message *new_message, char *str); static struct message *find_message(struct errors *err, const char *lang, my_bool no_default); static int check_message_format(struct errors *err, const char* mess); static uint parse_input_file(const char *file_name, struct errors **top_error, struct languages **top_language, uint *error_count); static int get_options(int *argc, char ***argv); static void print_version(void); static void usage(void); static char *parse_text_line(char *pos); static int copy_rows(FILE * to, char *row, int row_nr, long start_pos); static char *parse_default_language(char *str); static uint parse_error_offset(char *str); static char *skip_delimiters(char *str); static char *get_word(char **str); static char *find_end_of_word(char *str); static void clean_up(struct languages *lang_head, struct errors *error_head); static int create_header_files(struct errors *error_head); static int create_sys_files(struct languages *lang_head, struct errors *error_head, uint max_error, uint error_count); int main(int argc, char *argv[]) { MY_INIT(argv[0]); { uint max_error, error_count; struct errors *error_head= NULL; struct languages *lang_head= NULL; DBUG_ENTER("main"); charsets_dir= DEFAULT_CHARSET_DIR; my_umask_dir= 0777; if (get_options(&argc, &argv)) goto err; if (!(max_error= parse_input_file(TXTFILE, &error_head, &lang_head, &error_count))) { fprintf(stderr, "Failed to parse input file %s\n", TXTFILE); goto err; } if (lang_head == NULL || error_head == NULL) { fprintf(stderr, "Failed to parse input file %s\n", TXTFILE); goto err; } if (create_header_files(error_head)) { fprintf(stderr, "Failed to create header files\n"); goto err; } if (create_sys_files(lang_head, error_head, max_error, error_count)) { fprintf(stderr, "Failed to create sys files\n"); goto err; } clean_up(lang_head, error_head); DBUG_LEAVE; /* Can't use dbug after my_end() */ my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); return 0; err: clean_up(lang_head, error_head); DBUG_LEAVE; /* Can't use dbug after my_end() */ my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(1); } } static void print_escaped_string(FILE *f, const char *str) { const char *tmp = str; while (tmp[0] != 0) { switch (tmp[0]) { case '\\': fprintf(f, "\\\\"); break; case '\'': fprintf(f, "\\\'"); break; case '\"': fprintf(f, "\\\""); break; case '\n': fprintf(f, "\\n"); break; case '\r': fprintf(f, "\\r"); break; default: fprintf(f, "%c", tmp[0]); } tmp++; } } static int create_header_files(struct errors *error_head) { uint er_last= 0; uint section= 1; FILE *er_definef, *sql_statef, *er_namef; struct errors *tmp_error; struct message *er_msg; const char *er_text; uint current_d_code; DBUG_ENTER("create_header_files"); if (!(er_definef= my_fopen(HEADERFILE, O_WRONLY, MYF(MY_WME)))) { DBUG_RETURN(1); } if (!(sql_statef= my_fopen(STATEFILE, O_WRONLY, MYF(MY_WME)))) { my_fclose(er_definef, MYF(0)); DBUG_RETURN(1); } if (!(er_namef= my_fopen(NAMEFILE, O_WRONLY, MYF(MY_WME)))) { my_fclose(er_definef, MYF(0)); my_fclose(sql_statef, MYF(0)); DBUG_RETURN(1); } fprintf(er_definef, "/* Autogenerated file, please don't edit */\n\n"); fprintf(sql_statef, "/* Autogenerated file, please don't edit */\n\n"); fprintf(er_namef, "/* Autogenerated file, please don't edit */\n\n"); fprintf(er_definef, "#ifndef ER_ERROR_FIRST\n"); fprintf(er_definef, "#define ER_ERROR_FIRST %d\n", error_head->d_code); current_d_code= error_head->d_code -1; for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error) { /* generating mysqld_error.h fprintf() will automatically add \r on windows */ if (!tmp_error->er_name) continue; /* Placeholder for gap */ while (tmp_error->d_code > current_d_code + 1) { uint next_range= (((current_d_code + ERRORS_PER_RANGE) / ERRORS_PER_RANGE) * ERRORS_PER_RANGE); fprintf(er_definef, "#define ER_ERROR_LAST_SECTION_%d %d\n", section, current_d_code); fprintf(er_definef, "\n/* New section */\n\n"); fprintf(er_definef, "#define ER_ERROR_FIRST_SECTION_%d %d\n", section+1, MY_MIN(tmp_error->d_code, next_range)); section++; current_d_code= MY_MIN(tmp_error->d_code, next_range); } current_d_code= tmp_error->d_code; fprintf(er_definef, "#define %s %u\n", tmp_error->er_name, tmp_error->d_code); er_last= tmp_error->d_code; /* generating sql_state.h file */ if (tmp_error->sql_code1[0] || tmp_error->sql_code2[0]) fprintf(sql_statef, "{ %-40s,\"%s\", \"%s\" },\n", tmp_error->er_name, tmp_error->sql_code1, tmp_error->sql_code2); /*generating er_name file */ er_msg= find_message(tmp_error, default_language, 0); er_text = (er_msg ? er_msg->text : ""); fprintf(er_namef, "{ \"%s\", %d, \"", tmp_error->er_name, tmp_error->d_code); print_escaped_string(er_namef, er_text); fprintf(er_namef, "\" },\n"); } /* finishing off with mysqld_error.h */ fprintf(er_definef, "#define ER_ERROR_LAST %d\n", er_last); fprintf(er_definef, "#endif /* ER_ERROR_FIRST */\n"); my_fclose(er_definef, MYF(0)); my_fclose(sql_statef, MYF(0)); my_fclose(er_namef, MYF(0)); DBUG_RETURN(0); } static int create_sys_files(struct languages *lang_head, struct errors *error_head, uint max_error, uint error_count) { FILE *to; uint i, row_nr; ulong length; uchar head[HEADER_LENGTH]; char outfile[FN_REFLEN], *outfile_end; long start_pos; struct message *tmp; struct languages *tmp_lang; struct errors *tmp_error; MY_STAT stat_info; DBUG_ENTER("create_sys_files"); /* going over all languages and assembling corresponding error messages */ for (tmp_lang= lang_head; tmp_lang; tmp_lang= tmp_lang->next_lang) { outfile_end= strxmov(outfile, DATADIRECTORY, tmp_lang->lang_long_name, NullS); if (!my_stat(outfile, &stat_info,MYF(0))) { if (my_mkdir(outfile, 0777,MYF(0)) < 0) { fprintf(stderr, "Can't creqate output directory for %s\n", outfile); DBUG_RETURN(1); } } strxmov(outfile_end, FN_ROOTDIR, OUTFILE, NullS); if (!(to= my_fopen(outfile, O_WRONLY | FILE_BINARY, MYF(MY_WME)))) DBUG_RETURN(1); /* 2 is for 2 bytes to store row position / error message */ start_pos= (long) (HEADER_LENGTH + (error_count + section_count) * 2); my_fseek(to, start_pos, 0, MYF(0)); row_nr= 0; for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error) { /* dealing with messages */ tmp= find_message(tmp_error, tmp_lang->lang_short_name, FALSE); if (!tmp) { fprintf(stderr, "Did not find message for %s neither in %s nor in default " "language\n", tmp_error->er_name, tmp_lang->lang_short_name); goto err; } if (tmp->text) /* If not skipped row */ { if (copy_rows(to, tmp->text, row_nr, start_pos)) { fprintf(stderr, "Failed to copy rows to %s\n", outfile); goto err; } row_nr++; } } DBUG_ASSERT(error_count == row_nr); /* continue with header of the errmsg.sys file */ length= (ulong) (my_ftell(to, MYF(0)) - HEADER_LENGTH - (error_count + section_count) * 2); bzero((uchar*) head, HEADER_LENGTH); bmove((uchar*) head, (uchar*) file_head, 4); head[4]= 1; int4store(head + 6, length); int2store(head + 10, max_error); /* Max error */ int2store(head + 12, row_nr); int2store(head + 14, section_count); my_fseek(to, 0l, MY_SEEK_SET, MYF(0)); if (my_fwrite(to, (uchar*) head, HEADER_LENGTH, MYF(MY_WME | MY_FNABP)) || my_fwrite(to, (uchar*) section_header, section_count*2, MYF(MY_WME | MY_FNABP))) goto err; file_pos[row_nr]= (ftell(to) - start_pos); for (i= 0; i < row_nr; i++) { /* Store length of each string */ int2store(head, file_pos[i+1] - file_pos[i]); if (my_fwrite(to, (uchar*) head, 2, MYF(MY_WME | MY_FNABP))) goto err; } my_fclose(to, MYF(0)); } DBUG_RETURN(0); err: my_fclose(to, MYF(0)); DBUG_RETURN(1); } static void clean_up(struct languages *lang_head, struct errors *error_head) { struct languages *tmp_lang, *next_language; struct errors *tmp_error, *next_error; size_t count, i; if (default_language_changed) my_free((void*) default_language); for (tmp_lang= lang_head; tmp_lang; tmp_lang= next_language) { next_language= tmp_lang->next_lang; my_free(tmp_lang->lang_short_name); my_free(tmp_lang->lang_long_name); my_free(tmp_lang); } for (tmp_error= error_head; tmp_error; tmp_error= next_error) { next_error= tmp_error->next_error; count= (tmp_error->msg).elements; for (i= 0; i < count; i++) { struct message *tmp; tmp= dynamic_element(&tmp_error->msg, i, struct message*); my_free(tmp->lang_short_name); my_free(tmp->text); } delete_dynamic(&tmp_error->msg); if (tmp_error->sql_code1[0]) my_free((void*) tmp_error->sql_code1); if (tmp_error->sql_code2[0]) my_free((void*) tmp_error->sql_code2); my_free((void*) tmp_error->er_name); my_free(tmp_error); } } static uint parse_input_file(const char *file_name, struct errors **top_error, struct languages **top_lang, uint *error_count) { FILE *file; char *str, buff[1000]; struct errors *current_error= 0, **tail_error= top_error; struct message current_message; uint rcount= 0, skiped_errors= 0; my_bool er_offset_found= 0; DBUG_ENTER("parse_input_file"); *top_error= 0; *top_lang= 0; *error_count= 0; section_start= er_offset; section_count= 0; if (!(file= my_fopen(file_name, O_RDONLY | O_TEXT | O_SHARE, MYF(MY_WME)))) DBUG_RETURN(0); while ((str= fgets(buff, sizeof(buff), file))) { my_bool skip; if (is_prefix(str, "language")) { if (!(*top_lang= parse_charset_string(str))) { fprintf(stderr, "Failed to parse the charset string!\n"); DBUG_RETURN(0); } continue; } skip= 0; if (is_prefix(str, "start-error-number") || (skip= is_prefix(str, "skip-to-error-number"))) { uint tmp_er_offset; if (!(tmp_er_offset= parse_error_offset(str))) { fprintf(stderr, "Failed to parse the error offset string!\n"); DBUG_RETURN(0); } if (skip) { if (section_count >= MAX_SECTIONS-1) { fprintf(stderr, "Found too many skip-to-error-number entries. " "We only support %d entries\n", MAX_SECTIONS); DBUG_RETURN(0); } int2store(section_header + section_count*2, er_offset +rcount - section_start); section_count++; section_start= tmp_er_offset; } if (!er_offset_found) { er_offset_found= 1; er_offset= section_start= tmp_er_offset; } else { /* Create empty error messages between er_offset and tmp_err_offset */ if (tmp_er_offset < er_offset + rcount) { fprintf(stderr, "new start-error-number %u is smaller than current error message: %u\n", tmp_er_offset, er_offset + rcount); DBUG_RETURN(0); } for ( ; er_offset + rcount < tmp_er_offset ; rcount++) { skiped_errors+= skip != 0; current_error= generate_empty_message(er_offset + rcount, skip); *tail_error= current_error; tail_error= ¤t_error->next_error; } } continue; } if (is_prefix(str, "default-language")) { if (!(default_language= parse_default_language(str))) { DBUG_PRINT("info", ("default_slang: %s", default_language)); fprintf(stderr, "Failed to parse the default language line. Aborting\n"); DBUG_RETURN(0); } default_language_changed= 1; continue; } if (*str == '\t' || *str == ' ') { /* New error message in another language for previous error */ if (!current_error) { fprintf(stderr, "Error in the input file format\n"); DBUG_RETURN(0); } if (!parse_message_string(¤t_message, str)) { fprintf(stderr, "Failed to parse message string for error '%s'", current_error->er_name); DBUG_RETURN(0); } if (find_message(current_error, current_message.lang_short_name, TRUE)) { fprintf(stderr, "Duplicate message string for error '%s'" " in language '%s'\n", current_error->er_name, current_message.lang_short_name); DBUG_RETURN(0); } if (check_message_format(current_error, current_message.text)) { fprintf(stderr, "Wrong formatspecifier of error message string" " for error '%s' in language '%s'\n", current_error->er_name, current_message.lang_short_name); DBUG_RETURN(0); } if (insert_dynamic(¤t_error->msg, (uchar *) & current_message)) DBUG_RETURN(0); continue; } if (is_prefix(str, ER_PREFIX) || is_prefix(str, WARN_PREFIX) || is_prefix(str, ER_PREFIX2)) { if (!(current_error= parse_error_string(str, rcount))) { fprintf(stderr, "Failed to parse the error name string\n"); DBUG_RETURN(0); } rcount++; /* Count number of unique errors */ /* add error to the list */ *tail_error= current_error; tail_error= ¤t_error->next_error; continue; } if (*str == '#' || *str == '\n') continue; /* skip comment or empty lines */ fprintf(stderr, "Wrong input file format. Stop!\nLine: %s\n", str); DBUG_RETURN(0); } int2store(section_header + section_count*2, er_offset + rcount - section_start); section_count++; *error_count= rcount - skiped_errors; *tail_error= 0; /* Mark end of list */ my_fclose(file, MYF(0)); DBUG_RETURN(rcount); } static uint parse_error_offset(char *str) { char *soffset, *end; int error; uint ioffset; DBUG_ENTER("parse_error_offset"); /* skipping the "start-error-number" keyword and spaces after it */ str= find_end_of_word(str); str= skip_delimiters(str); if (!*str) DBUG_RETURN(0); /* Unexpected EOL: No error number after the keyword */ /* reading the error offset */ if (!(soffset= get_word(&str))) DBUG_RETURN(0); /* OOM: Fatal error */ DBUG_PRINT("info", ("default_error_offset: %s", soffset)); /* skipping space(s) and/or tabs after the error offset */ str= skip_delimiters(str); DBUG_PRINT("info", ("str: %s", str)); if (*str) { /* The line does not end with the error offset -> error! */ fprintf(stderr, "The error offset line does not end with an error offset"); DBUG_RETURN(0); } DBUG_PRINT("info", ("str: %s", str)); end= 0; ioffset= (uint) my_strtoll10(soffset, &end, &error); my_free(soffset); DBUG_RETURN(ioffset); } /* Parsing of the default language line. e.g. "default-language eng" */ static char *parse_default_language(char *str) { char *slang; DBUG_ENTER("parse_default_language"); /* skipping the "default-language" keyword */ str= find_end_of_word(str); /* skipping space(s) and/or tabs after the keyword */ str= skip_delimiters(str); if (!*str) { fprintf(stderr, "Unexpected EOL: No short language name after the keyword\n"); DBUG_RETURN(0); } /* reading the short language tag */ if (!(slang= get_word(&str))) DBUG_RETURN(0); /* OOM: Fatal error */ DBUG_PRINT("info", ("default_slang: %s", slang)); str= skip_delimiters(str); DBUG_PRINT("info", ("str: %s", str)); if (*str) { fprintf(stderr, "The default language line does not end with short language " "name\n"); DBUG_RETURN(0); } DBUG_PRINT("info", ("str: %s", str)); DBUG_RETURN(slang); } /* Find the message in a particular language SYNOPSIS find_message() err Error to find message for lang Language of message to find no_default Don't return default (English) if does not exit RETURN VALUE Returns the message structure if one is found, or NULL if not. */ static struct message *find_message(struct errors *err, const char *lang, my_bool no_default) { struct message *tmp, *return_val= 0; size_t i, count; DBUG_ENTER("find_message"); count= (err->msg).elements; for (i= 0; i < count; i++) { tmp= dynamic_element(&err->msg, i, struct message*); if (!strcmp(tmp->lang_short_name, lang)) DBUG_RETURN(tmp); if (!strcmp(tmp->lang_short_name, default_language)) { return_val= tmp; } } DBUG_RETURN(no_default ? NULL : return_val); } /* Check message format specifiers against error message for previous language SYNOPSIS checksum_format_specifier() msg String for which to generate checksum for the format specifiers RETURN VALUE Returns the checksum for all letters of the format specifiers Ex. "text '%-.64s' text part 2 %zu'" ^ ^^ characters will be xored to form checksum Non-letters are skipped, because they do not change the type of the argument. NOTE: Does not support format specifiers with positional args like "%2$s" */ static ha_checksum checksum_format_specifier(const char* msg) { ha_checksum chksum= 0; const uchar* p= (const uchar*) msg; const uchar* start= NULL; uint32 num_format_specifiers= 0; while (*p) { if (*p == '%') { start= p+1; /* Entering format specifier */ num_format_specifiers++; } else if (start && isalpha(*p)) { chksum= my_checksum(chksum, p, 1); switch(*p) { case 'd': case 'u': case 'x': case 's': case 'M': case 'T': start= 0; /* Not in format specifier anymore */ break; } } p++; } if (start) { /* Still inside a format specifier after end of string */ fprintf(stderr, "Still inside formatspecifier after end of string" " in'%s'\n", msg); DBUG_ASSERT(start==0); } /* Add number of format specifiers to checksum as extra safeguard */ chksum+= num_format_specifiers; return chksum; } /* Check message format specifiers against error message for previous language SYNOPSIS check_message_format() err Error to check message for mess Message to check RETURN VALUE Returns 0 if no previous error message or message format is ok */ static int check_message_format(struct errors *err, const char* mess) { struct message *first; DBUG_ENTER("check_message_format"); /* Get first message(if any) */ if ((err->msg).elements == 0) DBUG_RETURN(0); /* No previous message to compare against */ first= dynamic_element(&err->msg, 0, struct message*); DBUG_ASSERT(first != NULL); if (checksum_format_specifier(first->text) != checksum_format_specifier(mess)) { /* Check sum of format specifiers failed, they should be equal */ DBUG_RETURN(1); } DBUG_RETURN(0); } /* Skips spaces and or tabs till the beginning of the next word Returns pointer to the beginning of the first character of the word */ static char *skip_delimiters(char *str) { DBUG_ENTER("skip_delimiters"); for (; *str == ' ' || *str == ',' || *str == '\t' || *str == '\r' || *str == '\n' || *str == '='; str++) ; DBUG_RETURN(str); } /* Skips all characters till meets with space, or tab, or EOL */ static char *find_end_of_word(char *str) { DBUG_ENTER("find_end_of_word"); for (; *str != ' ' && *str != '\t' && *str != '\n' && *str != '\r' && *str && *str != ',' && *str != ';' && *str != '='; str++) ; DBUG_RETURN(str); } /* Read the word starting from *str */ static char *get_word(char **str) { char *start= *str; DBUG_ENTER("get_word"); *str= find_end_of_word(start); DBUG_RETURN(my_strndup(PSI_NOT_INSTRUMENTED, start, (uint) (*str - start), MYF(MY_WME | MY_FAE))); } /* Parsing the string with short_lang - message text. Code - to remember to which error does the text belong */ static struct message *parse_message_string(struct message *new_message, char *str) { char *start; DBUG_ENTER("parse_message_string"); DBUG_PRINT("enter", ("str: %s", str)); /*skip space(s) and/or tabs in the beginning */ while (*str == ' ' || *str == '\t' || *str == '\n') str++; if (!*str) { /* It was not a message line, but an empty line. */ DBUG_PRINT("info", ("str: %s", str)); DBUG_RETURN(0); } /* reading the short lang */ start= str; while (*str != ' ' && *str != '\t' && *str) str++; if (!(new_message->lang_short_name= my_strndup(PSI_NOT_INSTRUMENTED, start, (uint) (str - start), MYF(MY_WME | MY_FAE)))) DBUG_RETURN(0); /* Fatal error */ DBUG_PRINT("info", ("msg_slang: %s", new_message->lang_short_name)); /*skip space(s) and/or tabs after the lang */ while (*str == ' ' || *str == '\t' || *str == '\n') str++; if (*str != '"') { fprintf(stderr, "Unexpected EOL"); DBUG_PRINT("info", ("str: %s", str)); DBUG_RETURN(0); } /* reading the text */ start= str + 1; str= parse_text_line(start); if (!(new_message->text= my_strndup(PSI_NOT_INSTRUMENTED, start, (uint) (str - start), MYF(MY_WME | MY_FAE)))) DBUG_RETURN(0); DBUG_PRINT("info", ("msg_text: %s", new_message->text)); DBUG_RETURN(new_message); } static struct errors *generate_empty_message(uint d_code, my_bool skip) { struct errors *new_error; struct message message; /* create a new element */ if (!(new_error= (struct errors *) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*new_error), MYF(MY_WME)))) return(0); if (my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &new_error->msg, sizeof(struct message), 0, 1, MYF(0))) return(0); /* OOM: Fatal error */ new_error->er_name= NULL; new_error->d_code= d_code; new_error->sql_code1= empty_string; new_error->sql_code2= empty_string; new_error->next_error= 0; message.text= 0; /* If skip set, don't generate a text */ if (!(message.lang_short_name= my_strdup(PSI_NOT_INSTRUMENTED, default_language, MYF(MY_WME))) || (!skip && !(message.text= my_strdup(PSI_NOT_INSTRUMENTED, "", MYF(MY_WME))))) return(0); /* Can't fail as msg is preallocated */ (void) insert_dynamic(&new_error->msg, (uchar*) &message); return(new_error); } /* Parsing the string with error name and codes; returns the pointer to the errors struct */ static struct errors *parse_error_string(char *str, int er_count) { struct errors *new_error; DBUG_ENTER("parse_error_string"); DBUG_PRINT("enter", ("str: %s", str)); /* create a new element */ if (!(new_error= (struct errors *) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*new_error), MYF(MY_WME)))) DBUG_RETURN(0); new_error->next_error= 0; if (my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &new_error->msg, sizeof(struct message), 0, 0, MYF(0))) DBUG_RETURN(0); /* getting the error name */ str= skip_delimiters(str); if (!(new_error->er_name= get_word(&str))) DBUG_RETURN(0); /* OOM: Fatal error */ DBUG_PRINT("info", ("er_name: %s", new_error->er_name)); str= skip_delimiters(str); /* getting the code1 */ new_error->d_code= er_offset + er_count; DBUG_PRINT("info", ("d_code: %d", new_error->d_code)); str= skip_delimiters(str); /* if we reached EOL => no more codes, but this can happen */ if (!*str) { new_error->sql_code1= empty_string; new_error->sql_code2= empty_string; DBUG_PRINT("info", ("str: %s", str)); DBUG_RETURN(new_error); } /* getting the sql_code 1 */ if (!(new_error->sql_code1= get_word(&str))) DBUG_RETURN(0); /* OOM: Fatal error */ DBUG_PRINT("info", ("sql_code1: %s", new_error->sql_code1)); str= skip_delimiters(str); /* if we reached EOL => no more codes, but this can happen */ if (!*str) { new_error->sql_code2= empty_string; DBUG_PRINT("info", ("str: %s", str)); DBUG_RETURN(new_error); } /* getting the sql_code 2 */ if (!(new_error->sql_code2= get_word(&str))) DBUG_RETURN(0); /* OOM: Fatal error */ DBUG_PRINT("info", ("sql_code2: %s", new_error->sql_code2)); str= skip_delimiters(str); if (*str) { fprintf(stderr, "The error line did not end with sql/odbc code!"); DBUG_RETURN(0); } DBUG_RETURN(new_error); } /* Parsing the string with full lang name/short lang name/charset; returns pointer to the language structure */ static struct languages *parse_charset_string(char *str) { struct languages *head=0, *new_lang; DBUG_ENTER("parse_charset_string"); DBUG_PRINT("enter", ("str: %s", str)); /* skip over keyword */ str= find_end_of_word(str); if (!*str) { /* unexpected EOL */ DBUG_PRINT("info", ("str: %s", str)); DBUG_RETURN(0); } str= skip_delimiters(str); if (!(*str != ';' && *str)) DBUG_RETURN(0); do { /*creating new element of the linked list */ new_lang= (struct languages *) my_malloc(PSI_NOT_INSTRUMENTED, sizeof(*new_lang), MYF(MY_WME)); new_lang->next_lang= head; head= new_lang; /* get the full language name */ if (!(new_lang->lang_long_name= get_word(&str))) DBUG_RETURN(0); /* OOM: Fatal error */ DBUG_PRINT("info", ("long_name: %s", new_lang->lang_long_name)); /* getting the short name for language */ str= skip_delimiters(str); if (!*str) DBUG_RETURN(0); /* Error: No space or tab */ if (!(new_lang->lang_short_name= get_word(&str))) DBUG_RETURN(0); /* OOM: Fatal error */ DBUG_PRINT("info", ("short_name: %s", new_lang->lang_short_name)); /* skipping space, tab or "," */ str= skip_delimiters(str); } while (*str != ';' && *str); DBUG_PRINT("info", ("long name: %s", new_lang->lang_long_name)); DBUG_RETURN(head); } /* Read options */ static void print_version(void) { DBUG_ENTER("print_version"); printf("%s (Compile errormessage) Ver %s\n", my_progname, "3.0"); DBUG_VOID_RETURN; } static my_bool get_one_option(const struct my_option *opt, const char *argument __attribute__ ((unused)), const char *filename __attribute__ ((unused))) { DBUG_ENTER("get_one_option"); switch (opt->id) { case 'V': print_version(); my_end(0); exit(0); break; case '?': usage(); my_end(0); exit(0); break; case '#': DBUG_PUSH(argument ? argument : default_dbug_option); break; } DBUG_RETURN(0); } static void usage(void) { DBUG_ENTER("usage"); print_version(); printf("This software comes with ABSOLUTELY NO WARRANTY. " "This is free software,\n" "and you are welcome to modify and redistribute it under the GPL license.\n" "Usage:\n"); my_print_help(my_long_options); my_print_variables(my_long_options); DBUG_VOID_RETURN; } static int get_options(int *argc, char ***argv) { int ho_error; DBUG_ENTER("get_options"); if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option))) DBUG_RETURN(ho_error); DBUG_RETURN(0); } /* Read rows and remember them until row that start with char Converts row as a C-compiler would convert a textstring */ static char *parse_text_line(char *pos) { int i, nr; char *row= pos; size_t len; DBUG_ENTER("parse_text_line"); len= strlen (pos); while (*pos) { if (*pos == '\\') { switch (*++pos) { case '\\': case '"': (void) memmove (pos - 1, pos, len - (row - pos)); break; case 'n': pos[-1]= '\n'; (void) memmove (pos, pos + 1, len - (row - pos)); break; default: if (*pos >= '0' && *pos < '8') { nr= 0; for (i= 0; i < 3 && (*pos >= '0' && *pos < '8'); i++) nr= nr * 8 + (*(pos++) - '0'); pos -= i; pos[-1]= nr; (void) memmove (pos, pos + i, len - (row - pos)); } else if (*pos) (void) memmove (pos - 1, pos, len - (row - pos)); /* Remove '\' */ } } else pos++; } while (pos > row + 1 && *pos != '"') pos--; *pos= 0; DBUG_RETURN(pos); } /* Copy rows from memory to file and remember position */ static int copy_rows(FILE *to, char *row, int row_nr, long start_pos) { DBUG_ENTER("copy_rows"); file_pos[row_nr]= (int) (ftell(to) - start_pos); if (fputs(row, to) == EOF || fputc('\0', to) == EOF) { fprintf(stderr, "Can't write to outputfile\n"); DBUG_RETURN(1); } DBUG_RETURN(0); }