27 #include <my_global.h>
31 #include <my_getopt.h>
34 #include <mysql_version.h>
37 #define HEADER_LENGTH 32
38 #define ERRMSG_VERSION 3
39 #define DEFAULT_CHARSET_DIR "../sql/share/charsets"
40 #define ER_PREFIX "ER_"
41 #define WARN_PREFIX "WARN_"
42 static char *OUTFILE= (
char*)
"errmsg.sys";
43 static char *HEADERFILE= (
char*)
"mysqld_error.h";
44 static char *NAMEFILE= (
char*)
"mysqld_ername.h";
45 static char *STATEFILE= (
char*)
"sql_state.h";
46 static char *TXTFILE= (
char*)
"../sql/share/errmsg-utf8.txt";
47 static char *DATADIRECTORY= (
char*)
"../sql/share/";
49 static char *default_dbug_option= (
char*)
"d:t:O,/tmp/comp_err.trace";
59 uchar file_head[]= { 254, 254, ERRMSG_VERSION, 1 };
61 uint file_pos[MAX_ROWS];
63 const char *empty_string=
"";
69 const char *default_language=
"eng";
77 char *lang_short_name;
87 char *lang_short_name;
99 const char *sql_code1;
100 const char *sql_code2;
101 struct errors *next_error;
106 static struct my_option my_long_options[]=
109 {
"debug",
'#',
"This is a non-debug version. Catch this and exit",
110 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
112 {
"debug",
'#',
"Output debug log", &default_dbug_option,
113 &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
115 {
"debug-info",
'T',
"Print some debug info at exit.", &info_flag,
116 &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
117 {
"help",
'?',
"Displays this help and exits.", 0, 0, 0, GET_NO_ARG,
118 NO_ARG, 0, 0, 0, 0, 0, 0},
119 {
"version",
'V',
"Prints version", 0, 0, 0, GET_NO_ARG,
120 NO_ARG, 0, 0, 0, 0, 0, 0},
121 {
"charset",
'C',
"Charset dir", &charsets_dir, &charsets_dir,
122 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
123 {
"in_file",
'F',
"Input file", &TXTFILE, &TXTFILE,
124 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
125 {
"out_dir",
'D',
"Output base directory", &DATADIRECTORY, &DATADIRECTORY,
126 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
127 {
"out_file",
'O',
"Output filename (errmsg.sys)", &OUTFILE,
128 &OUTFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
129 {
"header_file",
'H',
"mysqld_error.h file ", &HEADERFILE,
130 &HEADERFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
131 {
"name_file",
'N',
"mysqld_ername.h file ", &NAMEFILE,
132 &NAMEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
133 {
"state_file",
'S',
"sql_state.h file", &STATEFILE,
134 &STATEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
135 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
139 static struct languages *parse_charset_string(
char *str);
140 static struct errors *parse_error_string(
char *ptr,
int er_count);
141 static struct message *parse_message_string(
struct message *new_message,
143 static struct message *find_message(
struct errors *err,
const char *lang,
145 static int check_message_format(
struct errors *err,
147 static int parse_input_file(
const char *file_name,
struct errors **top_error,
149 static int get_options(
int *argc,
char ***argv);
150 static void print_version(
void);
151 static void usage(
void);
152 static my_bool get_one_option(
int optid,
const struct my_option *opt,
154 static char *parse_text_line(
char *pos);
155 static int copy_rows(FILE *
to,
char *row,
int row_nr,
long start_pos);
156 static char *parse_default_language(
char *str);
157 static uint parse_error_offset(
char *str);
159 static char *skip_delimiters(
char *str);
160 static char *get_word(
char **str);
161 static char *find_end_of_word(
char *str);
162 static void clean_up(
struct languages *lang_head,
struct errors *error_head);
163 static int create_header_files(
struct errors *error_head);
164 static int create_sys_files(
struct languages *lang_head,
165 struct errors *error_head, uint row_count);
168 int main(
int argc,
char *argv[])
173 struct errors *error_head;
177 charsets_dir= DEFAULT_CHARSET_DIR;
179 if (get_options(&argc, &argv))
181 if (!(row_count= parse_input_file(TXTFILE, &error_head, &lang_head)))
183 fprintf(stderr,
"Failed to parse input file %s\n", TXTFILE);
186 #if MYSQL_VERSION_ID >= 50100 && MYSQL_VERSION_ID < 50500
188 #define MYSQL_OLD_GA_ERROR_MESSAGE_COUNT 641
189 #elif MYSQL_VERSION_ID >= 50500 && MYSQL_VERSION_ID < 50600
191 #define MYSQL_OLD_GA_ERROR_MESSAGE_COUNT 728
193 #if MYSQL_OLD_GA_ERROR_MESSAGE_COUNT
194 if (row_count != MYSQL_OLD_GA_ERROR_MESSAGE_COUNT)
196 fprintf(stderr,
"Can only add new error messages to latest GA. ");
197 fprintf(stderr,
"Use ER_UNKNOWN_ERROR instead.\n");
198 fprintf(stderr,
"Expected %u messages, found %u.\n",
199 MYSQL_OLD_GA_ERROR_MESSAGE_COUNT, row_count);
203 if (lang_head == NULL || error_head == NULL)
205 fprintf(stderr,
"Failed to parse input file %s\n", TXTFILE);
209 if (create_header_files(error_head))
211 fprintf(stderr,
"Failed to create header files\n");
214 if (create_sys_files(lang_head, error_head, row_count))
216 fprintf(stderr,
"Failed to create sys files\n");
219 clean_up(lang_head, error_head);
221 my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
227 static void print_escaped_string(FILE *f,
const char *str)
229 const char *tmp = str;
235 case '\\': fprintf(f,
"\\\\");
break;
236 case '\'': fprintf(f,
"\\\'");
break;
237 case '\"': fprintf(f,
"\\\"");
break;
238 case '\n': fprintf(f,
"\\n");
break;
239 case '\r': fprintf(f,
"\\r");
break;
240 default: fprintf(f,
"%c", tmp[0]);
247 static int create_header_files(
struct errors *error_head)
250 FILE *er_definef, *sql_statef, *er_namef;
255 DBUG_ENTER(
"create_header_files");
257 if (!(er_definef= my_fopen(HEADERFILE, O_WRONLY, MYF(MY_WME))))
261 if (!(sql_statef= my_fopen(STATEFILE, O_WRONLY, MYF(MY_WME))))
263 my_fclose(er_definef, MYF(0));
266 if (!(er_namef= my_fopen(NAMEFILE, O_WRONLY, MYF(MY_WME))))
268 my_fclose(er_definef, MYF(0));
269 my_fclose(sql_statef, MYF(0));
273 fprintf(er_definef,
"/* Autogenerated file, please don't edit */\n\n");
274 fprintf(sql_statef,
"/* Autogenerated file, please don't edit */\n\n");
275 fprintf(er_namef,
"/* Autogenerated file, please don't edit */\n\n");
277 fprintf(er_definef,
"#define ER_ERROR_FIRST %d\n", error_head->d_code);
279 for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error)
285 fprintf(er_definef,
"#define %s %d\n", tmp_error->er_name,
287 er_last= tmp_error->d_code;
290 if (tmp_error->sql_code1[0] || tmp_error->sql_code2[0])
292 "{ %-40s,\"%s\", \"%s\" },\n", tmp_error->er_name,
293 tmp_error->sql_code1, tmp_error->sql_code2);
295 er_msg= find_message(tmp_error, default_language, 0);
296 er_text = (er_msg ? er_msg->text :
"");
297 fprintf(er_namef,
"{ \"%s\", %d, \"", tmp_error->er_name,
299 print_escaped_string(er_namef, er_text);
300 fprintf(er_namef,
"\" },\n");
303 fprintf(er_definef,
"#define ER_ERROR_LAST %d\n", er_last);
304 my_fclose(er_definef, MYF(0));
305 my_fclose(sql_statef, MYF(0));
306 my_fclose(er_namef, MYF(0));
311 static int create_sys_files(
struct languages *lang_head,
312 struct errors *error_head, uint row_count)
315 uint csnum= 0, length,
i, row_nr;
317 char outfile[FN_REFLEN], *outfile_end;
324 DBUG_ENTER(
"create_sys_files");
329 for (tmp_lang= lang_head; tmp_lang; tmp_lang= tmp_lang->next_lang)
333 if (!(csnum= get_charset_number(tmp_lang->charset, MY_CS_PRIMARY)))
335 fprintf(stderr,
"Unknown charset '%s' in '%s'\n", tmp_lang->charset,
340 outfile_end= strxmov(outfile, DATADIRECTORY,
341 tmp_lang->lang_long_name, NullS);
342 if (!my_stat(outfile, &stat_info,MYF(0)))
344 if (my_mkdir(outfile, 0777,MYF(0)) < 0)
346 fprintf(stderr,
"Can't create output directory for %s\n",
352 strxmov(outfile_end, FN_ROOTDIR, OUTFILE, NullS);
354 if (!(to= my_fopen(outfile, O_WRONLY | FILE_BINARY, MYF(MY_WME))))
358 start_pos= (long) (HEADER_LENGTH + row_count * 4);
359 fseek(to, start_pos, 0);
361 for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error)
364 tmp= find_message(tmp_error, tmp_lang->lang_short_name, FALSE);
369 "Did not find message for %s neither in %s nor in default "
370 "language\n", tmp_error->er_name, tmp_lang->lang_short_name);
373 if (copy_rows(to, tmp->text, row_nr, start_pos))
375 fprintf(stderr,
"Failed to copy rows to %s\n", outfile);
382 length= ftell(to) - HEADER_LENGTH - row_count * 4;
383 memset(head, 0, HEADER_LENGTH);
384 bmove((uchar *) head, (uchar *) file_head, 4);
386 int4store(head + 6, length);
387 int4store(head + 10, row_count);
390 my_fseek(to, 0l, MY_SEEK_SET, MYF(0));
391 if (my_fwrite(to, (uchar*) head, HEADER_LENGTH, MYF(MY_WME | MY_FNABP)))
394 for (i= 0; i < row_count; i++)
396 int4store(head, file_pos[i]);
397 if (my_fwrite(to, (uchar*) head, 4, MYF(MY_WME | MY_FNABP)))
400 my_fclose(to, MYF(0));
405 my_fclose(to, MYF(0));
410 static void clean_up(
struct languages *lang_head,
struct errors *error_head)
412 struct languages *tmp_lang, *next_language;
413 struct errors *tmp_error, *next_error;
416 my_free((
void*) default_language);
418 for (tmp_lang= lang_head; tmp_lang; tmp_lang= next_language)
420 next_language= tmp_lang->next_lang;
421 my_free(tmp_lang->lang_short_name);
422 my_free(tmp_lang->lang_long_name);
423 my_free(tmp_lang->charset);
427 for (tmp_error= error_head; tmp_error; tmp_error= next_error)
429 next_error= tmp_error->next_error;
430 count= (tmp_error->msg).elements;
431 for (i= 0; i < count; i++)
434 tmp= dynamic_element(&tmp_error->msg, i,
struct message*);
435 my_free(tmp->lang_short_name);
439 delete_dynamic(&tmp_error->msg);
440 if (tmp_error->sql_code1[0])
441 my_free((
void*) tmp_error->sql_code1);
442 if (tmp_error->sql_code2[0])
443 my_free((
void*) tmp_error->sql_code2);
444 my_free((
void*) tmp_error->er_name);
450 static int parse_input_file(
const char *file_name,
struct errors **top_error,
454 char *str, buff[1000];
455 struct errors *current_error= 0, **tail_error= top_error;
456 struct message current_message;
458 DBUG_ENTER(
"parse_input_file");
462 if (!(file= my_fopen(file_name, O_RDONLY | O_SHARE, MYF(MY_WME))))
465 while ((str= fgets(buff,
sizeof(buff), file)))
467 if (is_prefix(str,
"language"))
469 if (!(*top_lang= parse_charset_string(str)))
471 fprintf(stderr,
"Failed to parse the charset string!\n");
476 if (is_prefix(str,
"start-error-number"))
478 if (!(er_offset= parse_error_offset(str)))
480 fprintf(stderr,
"Failed to parse the error offset string!\n");
485 if (is_prefix(str,
"default-language"))
487 if (!(default_language= parse_default_language(str)))
489 DBUG_PRINT(
"info", (
"default_slang: %s", default_language));
491 "Failed to parse the default language line. Aborting\n");
497 if (*str ==
'\t' || *str ==
' ')
502 fprintf(stderr,
"Error in the input file format\n");
505 if (!parse_message_string(¤t_message, str))
507 fprintf(stderr,
"Failed to parse message string for error '%s'",
508 current_error->er_name);
511 if (find_message(current_error, current_message.lang_short_name, TRUE))
513 fprintf(stderr,
"Duplicate message string for error '%s'"
514 " in language '%s'\n",
515 current_error->er_name, current_message.lang_short_name);
518 if (check_message_format(current_error, current_message.text))
520 fprintf(stderr,
"Wrong formatspecifier of error message string"
521 " for error '%s' in language '%s'\n",
522 current_error->er_name, current_message.lang_short_name);
525 if (insert_dynamic(¤t_error->msg, ¤t_message))
529 if (is_prefix(str, ER_PREFIX) || is_prefix(str, WARN_PREFIX))
531 if (!(current_error= parse_error_string(str, rcount)))
533 fprintf(stderr,
"Failed to parse the error name string\n");
539 *tail_error= current_error;
540 tail_error= ¤t_error->next_error;
543 if (*str ==
'#' || *str ==
'\n')
546 fprintf(stderr,
"Wrong input file format. Stop!\nLine: %s\n", str);
551 my_fclose(file, MYF(0));
556 static uint parse_error_offset(
char *str)
562 DBUG_ENTER(
"parse_error_offset");
564 str= find_end_of_word(str);
565 str= skip_delimiters(str);
571 if (!(soffset= get_word(&str)))
573 DBUG_PRINT(
"info", (
"default_error_offset: %s", soffset));
576 str= skip_delimiters(str);
577 DBUG_PRINT(
"info", (
"str: %s", str));
581 fprintf(stderr,
"The error offset line does not end with an error offset");
584 DBUG_PRINT(
"info", (
"str: %s", str));
587 ioffset= (uint) my_strtoll10(soffset, &end, &error);
589 DBUG_RETURN(ioffset);
595 static char *parse_default_language(
char *str)
599 DBUG_ENTER(
"parse_default_language");
601 str= find_end_of_word(str);
603 str= skip_delimiters(str);
607 "Unexpected EOL: No short language name after the keyword\n");
612 if (!(slang= get_word(&str)))
614 DBUG_PRINT(
"info", (
"default_slang: %s", slang));
616 str= skip_delimiters(str);
617 DBUG_PRINT(
"info", (
"str: %s", str));
621 "The default language line does not end with short language "
625 DBUG_PRINT(
"info", (
"str: %s", str));
642 static struct message *find_message(
struct errors *err,
const char *lang,
645 struct message *tmp, *return_val= 0;
647 DBUG_ENTER(
"find_message");
649 count= (err->msg).elements;
650 for (i= 0; i < count; i++)
652 tmp= dynamic_element(&err->msg, i,
struct message*);
654 if (!strcmp(tmp->lang_short_name, lang))
656 if (!strcmp(tmp->lang_short_name, default_language))
658 DBUG_ASSERT(tmp->text[0] != 0);
662 DBUG_RETURN(no_default ? NULL : return_val);
691 static ha_checksum checksum_format_specifier(
const char*
msg)
693 ha_checksum chksum= 0;
694 const uchar* p= (
const uchar*) msg;
695 const uchar* start= NULL;
696 uint32 num_format_specifiers= 0;
703 num_format_specifiers++;
713 chksum= my_checksum(chksum, (uchar*) start, (uint) (p + 1 - start));
729 fprintf(stderr,
"Still inside formatspecifier after end of string"
731 DBUG_ASSERT(start==0);
735 chksum+= num_format_specifiers;
753 static int check_message_format(
struct errors *err,
757 DBUG_ENTER(
"check_message_format");
760 if ((err->msg).elements == 0)
763 first= dynamic_element(&err->msg, 0,
struct message*);
764 DBUG_ASSERT(first != NULL);
766 if (checksum_format_specifier(first->text) !=
767 checksum_format_specifier(mess))
781 static char *skip_delimiters(
char *str)
783 DBUG_ENTER(
"skip_delimiters");
785 *str ==
' ' || *str ==
',' || *str ==
'\t' || *str ==
'\r' ||
786 *str ==
'\n' || *str ==
'='; str++)
796 static char *find_end_of_word(
char *str)
798 DBUG_ENTER(
"find_end_of_word");
800 *str !=
' ' && *str !=
'\t' && *str !=
'\n' && *str !=
'\r' && *str &&
801 *str !=
',' && *str !=
';' && *str !=
'='; str++)
809 static char *get_word(
char **str)
812 DBUG_ENTER(
"get_word");
814 *str= find_end_of_word(start);
815 DBUG_RETURN(my_strndup(start, (uint) (*str - start),
816 MYF(MY_WME | MY_FAE)));
825 static struct message *parse_message_string(
struct message *new_message,
830 DBUG_ENTER(
"parse_message_string");
831 DBUG_PRINT(
"enter", (
"str: %s", str));
834 while (*str ==
' ' || *str ==
'\t' || *str ==
'\n')
840 DBUG_PRINT(
"info", (
"str: %s", str));
846 while (*str !=
' ' && *str !=
'\t' && *str)
848 if (!(new_message->lang_short_name=
849 my_strndup(start, (uint) (str - start),
850 MYF(MY_WME | MY_FAE))))
852 DBUG_PRINT(
"info", (
"msg_slang: %s", new_message->lang_short_name));
855 while (*str ==
' ' || *str ==
'\t' || *str ==
'\n')
860 fprintf(stderr,
"Unexpected EOL");
861 DBUG_PRINT(
"info", (
"str: %s", str));
867 str= parse_text_line(start);
869 if (!(new_message->text= my_strndup(start, (uint) (str - start),
870 MYF(MY_WME | MY_FAE))))
872 DBUG_PRINT(
"info", (
"msg_text: %s", new_message->text));
874 DBUG_RETURN(new_message);
883 static struct errors *parse_error_string(
char *str,
int er_count)
886 DBUG_ENTER(
"parse_error_string");
887 DBUG_PRINT(
"enter", (
"str: %s", str));
890 new_error= (
struct errors *) my_malloc(
sizeof(*new_error), MYF(MY_WME));
892 if (my_init_dynamic_array(&new_error->msg,
sizeof(
struct message), 0, 0))
896 str= skip_delimiters(str);
898 if (!(new_error->er_name= get_word(&str)))
900 DBUG_PRINT(
"info", (
"er_name: %s", new_error->er_name));
902 str= skip_delimiters(str);
906 new_error->d_code= er_offset + er_count;
907 DBUG_PRINT(
"info", (
"d_code: %d", new_error->d_code));
909 str= skip_delimiters(str);
914 new_error->sql_code1= empty_string;
915 new_error->sql_code2= empty_string;
916 DBUG_PRINT(
"info", (
"str: %s", str));
917 DBUG_RETURN(new_error);
922 if (!(new_error->sql_code1= get_word(&str)))
924 DBUG_PRINT(
"info", (
"sql_code1: %s", new_error->sql_code1));
926 str= skip_delimiters(str);
931 new_error->sql_code2= empty_string;
932 DBUG_PRINT(
"info", (
"str: %s", str));
933 DBUG_RETURN(new_error);
937 if (!(new_error->sql_code2= get_word(&str)))
939 DBUG_PRINT(
"info", (
"sql_code2: %s", new_error->sql_code2));
941 str= skip_delimiters(str);
944 fprintf(stderr,
"The error line did not end with sql/odbc code!");
948 DBUG_RETURN(new_error);
957 static struct languages *parse_charset_string(
char *str)
960 DBUG_ENTER(
"parse_charset_string");
961 DBUG_PRINT(
"enter", (
"str: %s", str));
964 str= find_end_of_word(str);
968 DBUG_PRINT(
"info", (
"str: %s", str));
972 str= skip_delimiters(str);
973 if (!(*str !=
';' && *str))
979 new_lang= (
struct languages *) my_malloc(
sizeof(*new_lang), MYF(MY_WME));
980 new_lang->next_lang= head;
985 if (!(new_lang->lang_long_name= get_word(&str)))
988 DBUG_PRINT(
"info", (
"long_name: %s", new_lang->lang_long_name));
991 str= skip_delimiters(str);
995 if (!(new_lang->lang_short_name= get_word(&str)))
997 DBUG_PRINT(
"info", (
"short_name: %s", new_lang->lang_short_name));
1000 str= skip_delimiters(str);
1001 if (!(new_lang->charset= get_word(&str)))
1003 DBUG_PRINT(
"info", (
"charset: %s", new_lang->charset));
1006 str= skip_delimiters(str);
1008 while (*str !=
';' && *str);
1010 DBUG_PRINT(
"info", (
"long name: %s", new_lang->lang_long_name));
1017 static void print_version(
void)
1019 DBUG_ENTER(
"print_version");
1020 printf(
"%s (Compile errormessage) Ver %s\n", my_progname,
"2.0");
1026 get_one_option(
int optid,
const struct my_option *opt __attribute__ ((unused)),
1027 char *argument __attribute__ ((unused)))
1029 DBUG_ENTER(
"get_one_option");
1040 DBUG_PUSH(argument ? argument : default_dbug_option);
1047 static void usage(
void)
1049 DBUG_ENTER(
"usage");
1051 printf(
"This software comes with ABSOLUTELY NO WARRANTY. "
1052 "This is free software,\n"
1053 "and you are welcome to modify and redistribute it under the GPL license.\n"
1055 my_print_help(my_long_options);
1056 my_print_variables(my_long_options);
1061 static int get_options(
int *argc,
char ***argv)
1064 DBUG_ENTER(
"get_options");
1066 if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
1067 DBUG_RETURN(ho_error);
1077 static char *parse_text_line(
char *pos)
1082 DBUG_ENTER(
"parse_text_line");
1092 (void) memmove (pos - 1, pos, len - (row - pos));
1096 (void) memmove (pos, pos + 1, len - (row - pos));
1099 if (*pos >=
'0' && *pos <
'8')
1102 for (i= 0; i < 3 && (*pos >=
'0' && *pos <
'8'); i++)
1103 nr= nr * 8 + (*(pos++) -
'0');
1106 (void) memmove (pos, pos + i, len - (row - pos));
1109 (void) memmove (pos - 1, pos, len - (row - pos));
1115 while (pos > row + 1 && *pos !=
'"')
1124 static int copy_rows(FILE *to,
char *row,
int row_nr,
long start_pos)
1126 DBUG_ENTER(
"copy_rows");
1128 file_pos[row_nr]= (int) (ftell(to) - start_pos);
1129 if (fputs(row, to) == EOF || fputc(
'\0', to) == EOF)
1131 fprintf(stderr,
"Can't write to outputfile\n");