1 /*
2 Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 /*
26 Written by Anjuta Widenius
27 */
28
29 /*
30 Creates one include file and multiple language-error message files from one
31 multi-language text file.
32 */
33
34 #include <my_global.h>
35 #include <m_ctype.h>
36 #include <my_sys.h>
37 #include <m_string.h>
38 #include <my_getopt.h>
39 #include <assert.h>
40 #include <my_dir.h>
41 #include <mysql_version.h>
42
43 #define MAX_ROWS 1000
44 #define HEADER_LENGTH 32 /* Length of header in errmsg.sys */
45 #define ERRMSG_VERSION 3 /* Version number of errmsg.sys */
46 #define DEFAULT_CHARSET_DIR "../sql/share/charsets"
47 #define ER_PREFIX "ER_"
48 #define WARN_PREFIX "WARN_"
49 static char *OUTFILE= (char*) "errmsg.sys";
50 static char *HEADERFILE= (char*) "mysqld_error.h";
51 static char *NAMEFILE= (char*) "mysqld_ername.h";
52 static char *STATEFILE= (char*) "sql_state.h";
53 static char *TXTFILE= (char*) "../sql/share/errmsg-utf8.txt";
54 static char *DATADIRECTORY= (char*) "../sql/share/";
55 #ifndef DBUG_OFF
56 static char *default_dbug_option= (char*) "d:t:O,/tmp/comp_err.trace";
57 #endif
58
59 /*
60 Header for errmsg.sys files
61 Byte 3 is treated as version number for errmsg.sys
62 With this version ERRMSG_VERSION = 3, number of bytes
63 used for the length, count and offset are increased
64 from 2 bytes to 4 bytes.
65 */
66 uchar file_head[]= { 254, 254, ERRMSG_VERSION, 1 };
67 /* Store positions to each error message row to store in errmsg.sys header */
68 uint file_pos[MAX_ROWS];
69
70 const char *empty_string= ""; /* For empty states */
71 /*
72 Default values for command line options. See getopt structure for definitions
73 for these.
74 */
75
76 const char *default_language= "eng";
77 int er_offset= 1000;
78 my_bool info_flag= 0;
79
80 /* Storage of one error message row (for one language) */
81
82 struct message
83 {
84 char *lang_short_name;
85 char *text;
86 };
87
88
89 /* Storage for languages and charsets (from start of error text file) */
90
91 struct languages
92 {
93 char *lang_long_name; /* full name of the language */
94 char *lang_short_name; /* abbreviation of the lang. */
95 char *charset; /* Character set name */
96 struct languages *next_lang; /* Pointer to next language */
97 };
98
99
100 /* Name, code and texts (for all lang) for one error message */
101
102 struct errors
103 {
104 const char *er_name; /* Name of the error (ER_HASHCK) */
105 int d_code; /* Error code number */
106 const char *sql_code1; /* sql state */
107 const char *sql_code2; /* ODBC state */
108 struct errors *next_error; /* Pointer to next error */
109 DYNAMIC_ARRAY msg; /* All language texts for this error */
110 };
111
112
113 static struct my_option my_long_options[]=
114 {
115 #ifdef DBUG_OFF
116 {"debug", '#', "This is a non-debug version. Catch this and exit",
117 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
118 #else
119 {"debug", '#', "Output debug log", &default_dbug_option,
120 &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
121 #endif
122 {"debug-info", 'T', "Print some debug info at exit.", &info_flag,
123 &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
124 {"help", '?', "Displays this help and exits.", 0, 0, 0, GET_NO_ARG,
125 NO_ARG, 0, 0, 0, 0, 0, 0},
126 {"version", 'V', "Prints version", 0, 0, 0, GET_NO_ARG,
127 NO_ARG, 0, 0, 0, 0, 0, 0},
128 {"charset", 'C', "Charset dir", &charsets_dir, &charsets_dir,
129 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
130 {"in_file", 'F', "Input file", &TXTFILE, &TXTFILE,
131 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
132 {"out_dir", 'D', "Output base directory", &DATADIRECTORY, &DATADIRECTORY,
133 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
134 {"out_file", 'O', "Output filename (errmsg.sys)", &OUTFILE,
135 &OUTFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
136 {"header_file", 'H', "mysqld_error.h file ", &HEADERFILE,
137 &HEADERFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
138 {"name_file", 'N', "mysqld_ername.h file ", &NAMEFILE,
139 &NAMEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
140 {"state_file", 'S', "sql_state.h file", &STATEFILE,
141 &STATEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
142 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
143 };
144
145
146 static struct languages *parse_charset_string(char *str);
147 static struct errors *parse_error_string(char *ptr, int er_count);
148 static struct message *parse_message_string(struct message *new_message,
149 char *str);
150 static struct message *find_message(struct errors *err, const char *lang,
151 my_bool no_default);
152 static int check_message_format(struct errors *err,
153 const char* mess);
154 static int parse_input_file(const char *file_name, struct errors **top_error,
155 struct languages **top_language);
156 static int get_options(int *argc, char ***argv);
157 static void print_version(void);
158 static void usage(void);
159 static my_bool get_one_option(int optid, const struct my_option *opt,
160 char *argument);
161 static char *parse_text_line(char *pos);
162 static int copy_rows(FILE * to, char *row, int row_nr, long start_pos);
163 static char *parse_default_language(char *str);
164 static uint parse_error_offset(char *str);
165
166 static char *skip_delimiters(char *str);
167 static char *get_word(char **str);
168 static char *find_end_of_word(char *str);
169 static void clean_up(struct languages *lang_head, struct errors *error_head);
170 static int create_header_files(struct errors *error_head);
171 static int create_sys_files(struct languages *lang_head,
172 struct errors *error_head, uint row_count);
173
174
main(int argc,char * argv[])175 int main(int argc, char *argv[])
176 {
177 MY_INIT(argv[0]);
178 {
179 uint row_count;
180 struct errors *error_head;
181 struct languages *lang_head;
182 DBUG_ENTER("main");
183
184 charsets_dir= DEFAULT_CHARSET_DIR;
185 my_umask_dir= 0777;
186 if (get_options(&argc, &argv))
187 DBUG_RETURN(1);
188 if (!(row_count= parse_input_file(TXTFILE, &error_head, &lang_head)))
189 {
190 fprintf(stderr, "Failed to parse input file %s\n", TXTFILE);
191 DBUG_RETURN(1);
192 }
193 #if MYSQL_VERSION_ID >= 50100 && MYSQL_VERSION_ID < 50500
194 /* Number of error messages in 5.1 - do not change this number! */
195 #define MYSQL_OLD_GA_ERROR_MESSAGE_COUNT 641
196 #elif MYSQL_VERSION_ID >= 50500 && MYSQL_VERSION_ID < 50600
197 /* Number of error messages in 5.5 - do not change this number! */
198 #define MYSQL_OLD_GA_ERROR_MESSAGE_COUNT 728
199 #endif
200 #if MYSQL_OLD_GA_ERROR_MESSAGE_COUNT
201 if (row_count != MYSQL_OLD_GA_ERROR_MESSAGE_COUNT)
202 {
203 fprintf(stderr, "Can only add new error messages to latest GA. ");
204 fprintf(stderr, "Use ER_UNKNOWN_ERROR instead.\n");
205 fprintf(stderr, "Expected %u messages, found %u.\n",
206 MYSQL_OLD_GA_ERROR_MESSAGE_COUNT, row_count);
207 DBUG_RETURN(1);
208 }
209 #endif
210 if (lang_head == NULL || error_head == NULL)
211 {
212 fprintf(stderr, "Failed to parse input file %s\n", TXTFILE);
213 DBUG_RETURN(1);
214 }
215
216 if (create_header_files(error_head))
217 {
218 fprintf(stderr, "Failed to create header files\n");
219 DBUG_RETURN(1);
220 }
221 if (create_sys_files(lang_head, error_head, row_count))
222 {
223 fprintf(stderr, "Failed to create sys files\n");
224 DBUG_RETURN(1);
225 }
226 clean_up(lang_head, error_head);
227 DBUG_LEAVE; /* Can't use dbug after my_end() */
228 my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
229 return 0;
230 }
231 }
232
233
print_escaped_string(FILE * f,const char * str)234 static void print_escaped_string(FILE *f, const char *str)
235 {
236 const char *tmp = str;
237
238 while (tmp[0] != 0)
239 {
240 switch (tmp[0])
241 {
242 case '\\': fprintf(f, "\\\\"); break;
243 case '\'': fprintf(f, "\\\'"); break;
244 case '\"': fprintf(f, "\\\""); break;
245 case '\n': fprintf(f, "\\n"); break;
246 case '\r': fprintf(f, "\\r"); break;
247 default: fprintf(f, "%c", tmp[0]);
248 }
249 tmp++;
250 }
251 }
252
253
create_header_files(struct errors * error_head)254 static int create_header_files(struct errors *error_head)
255 {
256 uint er_last= 0;
257 FILE *er_definef, *sql_statef, *er_namef;
258 struct errors *tmp_error;
259 struct message *er_msg;
260 const char *er_text;
261
262 DBUG_ENTER("create_header_files");
263
264 if (!(er_definef= my_fopen(HEADERFILE, O_WRONLY, MYF(MY_WME))))
265 {
266 DBUG_RETURN(1);
267 }
268 if (!(sql_statef= my_fopen(STATEFILE, O_WRONLY, MYF(MY_WME))))
269 {
270 my_fclose(er_definef, MYF(0));
271 DBUG_RETURN(1);
272 }
273 if (!(er_namef= my_fopen(NAMEFILE, O_WRONLY, MYF(MY_WME))))
274 {
275 my_fclose(er_definef, MYF(0));
276 my_fclose(sql_statef, MYF(0));
277 DBUG_RETURN(1);
278 }
279
280 fprintf(er_definef, "/* Autogenerated file, please don't edit */\n\n");
281 fprintf(sql_statef, "/* Autogenerated file, please don't edit */\n\n");
282 fprintf(er_namef, "/* Autogenerated file, please don't edit */\n\n");
283
284 fprintf(er_definef, "#define ER_ERROR_FIRST %d\n", error_head->d_code);
285
286 for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error)
287 {
288 /*
289 generating mysqld_error.h
290 fprintf() will automatically add \r on windows
291 */
292 fprintf(er_definef, "#define %s %d\n", tmp_error->er_name,
293 tmp_error->d_code);
294 er_last= tmp_error->d_code;
295
296 /* generating sql_state.h file */
297 if (tmp_error->sql_code1[0] || tmp_error->sql_code2[0])
298 fprintf(sql_statef,
299 "{ %-40s,\"%s\", \"%s\" },\n", tmp_error->er_name,
300 tmp_error->sql_code1, tmp_error->sql_code2);
301 /*generating er_name file */
302 er_msg= find_message(tmp_error, default_language, 0);
303 er_text = (er_msg ? er_msg->text : "");
304 fprintf(er_namef, "{ \"%s\", %d, \"", tmp_error->er_name,
305 tmp_error->d_code);
306 print_escaped_string(er_namef, er_text);
307 fprintf(er_namef, "\" },\n");
308 }
309 /* finishing off with mysqld_error.h */
310 fprintf(er_definef, "#define ER_ERROR_LAST %d\n", er_last);
311 my_fclose(er_definef, MYF(0));
312 my_fclose(sql_statef, MYF(0));
313 my_fclose(er_namef, MYF(0));
314 DBUG_RETURN(0);
315 }
316
317
create_sys_files(struct languages * lang_head,struct errors * error_head,uint row_count)318 static int create_sys_files(struct languages *lang_head,
319 struct errors *error_head, uint row_count)
320 {
321 FILE *to;
322 uint csnum= 0, length, i, row_nr;
323 uchar head[32];
324 char outfile[FN_REFLEN], *outfile_end;
325 long start_pos;
326 struct message *tmp;
327 struct languages *tmp_lang;
328 struct errors *tmp_error;
329
330 MY_STAT stat_info;
331 DBUG_ENTER("create_sys_files");
332
333 /*
334 going over all languages and assembling corresponding error messages
335 */
336 for (tmp_lang= lang_head; tmp_lang; tmp_lang= tmp_lang->next_lang)
337 {
338
339 /* setting charset name */
340 if (!(csnum= get_charset_number(tmp_lang->charset, MY_CS_PRIMARY)))
341 {
342 fprintf(stderr, "Unknown charset '%s' in '%s'\n", tmp_lang->charset,
343 TXTFILE);
344 DBUG_RETURN(1);
345 }
346
347 outfile_end= strxmov(outfile, DATADIRECTORY,
348 tmp_lang->lang_long_name, NullS);
349 if (!my_stat(outfile, &stat_info,MYF(0)))
350 {
351 if (my_mkdir(outfile, 0777,MYF(0)) < 0)
352 {
353 fprintf(stderr, "Can't create output directory for %s\n",
354 outfile);
355 DBUG_RETURN(1);
356 }
357 }
358
359 strxmov(outfile_end, FN_ROOTDIR, OUTFILE, NullS);
360
361 if (!(to= my_fopen(outfile, O_WRONLY | FILE_BINARY, MYF(MY_WME))))
362 DBUG_RETURN(1);
363
364 /* 4 is for 4 bytes to store row position / error message */
365 start_pos= (long) (HEADER_LENGTH + row_count * 4);
366 fseek(to, start_pos, 0);
367 row_nr= 0;
368 for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error)
369 {
370 /* dealing with messages */
371 tmp= find_message(tmp_error, tmp_lang->lang_short_name, FALSE);
372
373 if (!tmp)
374 {
375 fprintf(stderr,
376 "Did not find message for %s neither in %s nor in default "
377 "language\n", tmp_error->er_name, tmp_lang->lang_short_name);
378 goto err;
379 }
380 if (copy_rows(to, tmp->text, row_nr, start_pos))
381 {
382 fprintf(stderr, "Failed to copy rows to %s\n", outfile);
383 goto err;
384 }
385 row_nr++;
386 }
387
388 /* continue with header of the errmsg.sys file */
389 length= ftell(to) - HEADER_LENGTH - row_count * 4;
390 memset(head, 0, HEADER_LENGTH);
391 bmove((uchar *) head, (uchar *) file_head, 4);
392 head[4]= 1;
393 int4store(head + 6, length);
394 int4store(head + 10, row_count);
395 head[30]= csnum;
396
397 my_fseek(to, 0l, MY_SEEK_SET, MYF(0));
398 if (my_fwrite(to, (uchar*) head, HEADER_LENGTH, MYF(MY_WME | MY_FNABP)))
399 goto err;
400
401 for (i= 0; i < row_count; i++)
402 {
403 int4store(head, file_pos[i]);
404 if (my_fwrite(to, (uchar*) head, 4, MYF(MY_WME | MY_FNABP)))
405 goto err;
406 }
407 my_fclose(to, MYF(0));
408 }
409 DBUG_RETURN(0);
410
411 err:
412 my_fclose(to, MYF(0));
413 DBUG_RETURN(1);
414 }
415
416
clean_up(struct languages * lang_head,struct errors * error_head)417 static void clean_up(struct languages *lang_head, struct errors *error_head)
418 {
419 struct languages *tmp_lang, *next_language;
420 struct errors *tmp_error, *next_error;
421 uint count, i;
422
423 my_free((void*) default_language);
424
425 for (tmp_lang= lang_head; tmp_lang; tmp_lang= next_language)
426 {
427 next_language= tmp_lang->next_lang;
428 my_free(tmp_lang->lang_short_name);
429 my_free(tmp_lang->lang_long_name);
430 my_free(tmp_lang->charset);
431 my_free(tmp_lang);
432 }
433
434 for (tmp_error= error_head; tmp_error; tmp_error= next_error)
435 {
436 next_error= tmp_error->next_error;
437 count= (tmp_error->msg).elements;
438 for (i= 0; i < count; i++)
439 {
440 struct message *tmp;
441 tmp= dynamic_element(&tmp_error->msg, i, struct message*);
442 my_free(tmp->lang_short_name);
443 my_free(tmp->text);
444 }
445
446 delete_dynamic(&tmp_error->msg);
447 if (tmp_error->sql_code1[0])
448 my_free((void*) tmp_error->sql_code1);
449 if (tmp_error->sql_code2[0])
450 my_free((void*) tmp_error->sql_code2);
451 my_free((void*) tmp_error->er_name);
452 my_free(tmp_error);
453 }
454 }
455
456
parse_input_file(const char * file_name,struct errors ** top_error,struct languages ** top_lang)457 static int parse_input_file(const char *file_name, struct errors **top_error,
458 struct languages **top_lang)
459 {
460 FILE *file;
461 char *str, buff[1000];
462 struct errors *current_error= 0, **tail_error= top_error;
463 struct message current_message;
464 int rcount= 0;
465 DBUG_ENTER("parse_input_file");
466
467 *top_error= 0;
468 *top_lang= 0;
469 if (!(file= my_fopen(file_name, O_RDONLY | O_SHARE, MYF(MY_WME))))
470 DBUG_RETURN(0);
471
472 while ((str= fgets(buff, sizeof(buff), file)))
473 {
474 if (is_prefix(str, "language"))
475 {
476 if (!(*top_lang= parse_charset_string(str)))
477 {
478 fprintf(stderr, "Failed to parse the charset string!\n");
479 DBUG_RETURN(0);
480 }
481 continue;
482 }
483 if (is_prefix(str, "start-error-number"))
484 {
485 if (!(er_offset= parse_error_offset(str)))
486 {
487 fprintf(stderr, "Failed to parse the error offset string!\n");
488 DBUG_RETURN(0);
489 }
490 continue;
491 }
492 if (is_prefix(str, "default-language"))
493 {
494 if (!(default_language= parse_default_language(str)))
495 {
496 DBUG_PRINT("info", ("default_slang: %s", default_language));
497 fprintf(stderr,
498 "Failed to parse the default language line. Aborting\n");
499 DBUG_RETURN(0);
500 }
501 continue;
502 }
503
504 if (*str == '\t' || *str == ' ')
505 {
506 /* New error message in another language for previous error */
507 if (!current_error)
508 {
509 fprintf(stderr, "Error in the input file format\n");
510 DBUG_RETURN(0);
511 }
512 if (!parse_message_string(¤t_message, str))
513 {
514 fprintf(stderr, "Failed to parse message string for error '%s'",
515 current_error->er_name);
516 DBUG_RETURN(0);
517 }
518 if (find_message(current_error, current_message.lang_short_name, TRUE))
519 {
520 fprintf(stderr, "Duplicate message string for error '%s'"
521 " in language '%s'\n",
522 current_error->er_name, current_message.lang_short_name);
523 DBUG_RETURN(0);
524 }
525 if (check_message_format(current_error, current_message.text))
526 {
527 fprintf(stderr, "Wrong formatspecifier of error message string"
528 " for error '%s' in language '%s'\n",
529 current_error->er_name, current_message.lang_short_name);
530 DBUG_RETURN(0);
531 }
532 if (insert_dynamic(¤t_error->msg, ¤t_message))
533 DBUG_RETURN(0);
534 continue;
535 }
536 if (is_prefix(str, ER_PREFIX) || is_prefix(str, WARN_PREFIX))
537 {
538 if (!(current_error= parse_error_string(str, rcount)))
539 {
540 fprintf(stderr, "Failed to parse the error name string\n");
541 DBUG_RETURN(0);
542 }
543 rcount++; /* Count number of unique errors */
544
545 /* add error to the list */
546 *tail_error= current_error;
547 tail_error= ¤t_error->next_error;
548 continue;
549 }
550 if (*str == '#' || *str == '\n')
551 continue; /* skip comment or empty lines */
552
553 fprintf(stderr, "Wrong input file format. Stop!\nLine: %s\n", str);
554 DBUG_RETURN(0);
555 }
556 *tail_error= 0; /* Mark end of list */
557
558 my_fclose(file, MYF(0));
559 DBUG_RETURN(rcount);
560 }
561
562
parse_error_offset(char * str)563 static uint parse_error_offset(char *str)
564 {
565 char *soffset, *end;
566 int error;
567 uint ioffset;
568
569 DBUG_ENTER("parse_error_offset");
570 /* skipping the "start-error-number" keyword and spaces after it */
571 str= find_end_of_word(str);
572 str= skip_delimiters(str);
573
574 if (!*str)
575 DBUG_RETURN(0); /* Unexpected EOL: No error number after the keyword */
576
577 /* reading the error offset */
578 if (!(soffset= get_word(&str)))
579 DBUG_RETURN(0); /* OOM: Fatal error */
580 DBUG_PRINT("info", ("default_error_offset: %s", soffset));
581
582 /* skipping space(s) and/or tabs after the error offset */
583 str= skip_delimiters(str);
584 DBUG_PRINT("info", ("str: %s", str));
585 if (*str)
586 {
587 /* The line does not end with the error offset -> error! */
588 fprintf(stderr, "The error offset line does not end with an error offset");
589 DBUG_RETURN(0);
590 }
591 DBUG_PRINT("info", ("str: %s", str));
592
593 end= 0;
594 ioffset= (uint) my_strtoll10(soffset, &end, &error);
595 my_free(soffset);
596 DBUG_RETURN(ioffset);
597 }
598
599
600 /* Parsing of the default language line. e.g. "default-language eng" */
601
parse_default_language(char * str)602 static char *parse_default_language(char *str)
603 {
604 char *slang;
605
606 DBUG_ENTER("parse_default_language");
607 /* skipping the "default-language" keyword */
608 str= find_end_of_word(str);
609 /* skipping space(s) and/or tabs after the keyword */
610 str= skip_delimiters(str);
611 if (!*str)
612 {
613 fprintf(stderr,
614 "Unexpected EOL: No short language name after the keyword\n");
615 DBUG_RETURN(0);
616 }
617
618 /* reading the short language tag */
619 if (!(slang= get_word(&str)))
620 DBUG_RETURN(0); /* OOM: Fatal error */
621 DBUG_PRINT("info", ("default_slang: %s", slang));
622
623 str= skip_delimiters(str);
624 DBUG_PRINT("info", ("str: %s", str));
625 if (*str)
626 {
627 fprintf(stderr,
628 "The default language line does not end with short language "
629 "name\n");
630 DBUG_RETURN(0);
631 }
632 DBUG_PRINT("info", ("str: %s", str));
633 DBUG_RETURN(slang);
634 }
635
636
637 /*
638 Find the message in a particular language
639
640 SYNOPSIS
641 find_message()
642 err Error to find message for
643 lang Language of message to find
644 no_default Don't return default (English) if does not exit
645
646 RETURN VALUE
647 Returns the message structure if one is found, or NULL if not.
648 */
find_message(struct errors * err,const char * lang,my_bool no_default)649 static struct message *find_message(struct errors *err, const char *lang,
650 my_bool no_default)
651 {
652 struct message *tmp, *return_val= 0;
653 uint i, count;
654 DBUG_ENTER("find_message");
655
656 count= (err->msg).elements;
657 for (i= 0; i < count; i++)
658 {
659 tmp= dynamic_element(&err->msg, i, struct message*);
660
661 if (!strcmp(tmp->lang_short_name, lang))
662 DBUG_RETURN(tmp);
663 if (!strcmp(tmp->lang_short_name, default_language))
664 {
665 DBUG_ASSERT(tmp->text[0] != 0);
666 return_val= tmp;
667 }
668 }
669 DBUG_RETURN(no_default ? NULL : return_val);
670 }
671
672
673
674 /*
675 Check message format specifiers against error message for
676 previous language
677
678 SYNOPSIS
679 checksum_format_specifier()
680 msg String for which to generate checksum
681 for the format specifiers
682
683 RETURN VALUE
684 Returns the checksum for all the characters of the
685 format specifiers
686
687 Ex.
688 "text '%-64.s' text part 2 %d'"
689 ^^^^^^ ^^
690 characters will be xored to form checksum
691
692 NOTE:
693 Does not support format specifiers with positional args
694 like "%2$s" but that is not yet supported by my_vsnprintf
695 either.
696 */
697
checksum_format_specifier(const char * msg)698 static ha_checksum checksum_format_specifier(const char* msg)
699 {
700 ha_checksum chksum= 0;
701 const uchar* p= (const uchar*) msg;
702 const uchar* start= NULL;
703 uint32 num_format_specifiers= 0;
704 while (*p)
705 {
706
707 if (*p == '%')
708 {
709 start= p+1; /* Entering format specifier */
710 num_format_specifiers++;
711 }
712 else if (start)
713 {
714 switch(*p)
715 {
716 case 'd':
717 case 'u':
718 case 'x':
719 case 's':
720 chksum= my_checksum(chksum, (uchar*) start, (uint) (p + 1 - start));
721 start= 0; /* Not in format specifier anymore */
722 break;
723
724 default:
725 break;
726 }
727 }
728
729 p++;
730 }
731
732 if (start)
733 {
734 /* Still inside a format specifier after end of string */
735
736 fprintf(stderr, "Still inside formatspecifier after end of string"
737 " in'%s'\n", msg);
738 DBUG_ASSERT(start==0);
739 }
740
741 /* Add number of format specifiers to checksum as extra safeguard */
742 chksum+= num_format_specifiers;
743
744 return chksum;
745 }
746
747
748 /*
749 Check message format specifiers against error message for
750 previous language
751
752 SYNOPSIS
753 check_message_format()
754 err Error to check message for
755 mess Message to check
756
757 RETURN VALUE
758 Returns 0 if no previous error message or message format is ok
759 */
check_message_format(struct errors * err,const char * mess)760 static int check_message_format(struct errors *err,
761 const char* mess)
762 {
763 struct message *first;
764 DBUG_ENTER("check_message_format");
765
766 /* Get first message(if any) */
767 if ((err->msg).elements == 0)
768 DBUG_RETURN(0); /* No previous message to compare against */
769
770 first= dynamic_element(&err->msg, 0, struct message*);
771 DBUG_ASSERT(first != NULL);
772
773 if (checksum_format_specifier(first->text) !=
774 checksum_format_specifier(mess))
775 {
776 /* Check sum of format specifiers failed, they should be equal */
777 DBUG_RETURN(1);
778 }
779 DBUG_RETURN(0);
780 }
781
782
783 /*
784 Skips spaces and or tabs till the beginning of the next word
785 Returns pointer to the beginning of the first character of the word
786 */
787
skip_delimiters(char * str)788 static char *skip_delimiters(char *str)
789 {
790 DBUG_ENTER("skip_delimiters");
791 for (;
792 *str == ' ' || *str == ',' || *str == '\t' || *str == '\r' ||
793 *str == '\n' || *str == '='; str++)
794 ;
795 DBUG_RETURN(str);
796 }
797
798
799 /*
800 Skips all characters till meets with space, or tab, or EOL
801 */
802
find_end_of_word(char * str)803 static char *find_end_of_word(char *str)
804 {
805 DBUG_ENTER("find_end_of_word");
806 for (;
807 *str != ' ' && *str != '\t' && *str != '\n' && *str != '\r' && *str &&
808 *str != ',' && *str != ';' && *str != '='; str++)
809 ;
810 DBUG_RETURN(str);
811 }
812
813
814 /* Read the word starting from *str */
815
get_word(char ** str)816 static char *get_word(char **str)
817 {
818 char *start= *str;
819 DBUG_ENTER("get_word");
820
821 *str= find_end_of_word(start);
822 DBUG_RETURN(my_strndup(start, (uint) (*str - start),
823 MYF(MY_WME | MY_FAE)));
824 }
825
826
827 /*
828 Parsing the string with short_lang - message text. Code - to
829 remember to which error does the text belong
830 */
831
parse_message_string(struct message * new_message,char * str)832 static struct message *parse_message_string(struct message *new_message,
833 char *str)
834 {
835 char *start;
836
837 DBUG_ENTER("parse_message_string");
838 DBUG_PRINT("enter", ("str: %s", str));
839
840 /*skip space(s) and/or tabs in the beginning */
841 while (*str == ' ' || *str == '\t' || *str == '\n')
842 str++;
843
844 if (!*str)
845 {
846 /* It was not a message line, but an empty line. */
847 DBUG_PRINT("info", ("str: %s", str));
848 DBUG_RETURN(0);
849 }
850
851 /* reading the short lang */
852 start= str;
853 while (*str != ' ' && *str != '\t' && *str)
854 str++;
855 if (!(new_message->lang_short_name=
856 my_strndup(start, (uint) (str - start),
857 MYF(MY_WME | MY_FAE))))
858 DBUG_RETURN(0); /* Fatal error */
859 DBUG_PRINT("info", ("msg_slang: %s", new_message->lang_short_name));
860
861 /*skip space(s) and/or tabs after the lang */
862 while (*str == ' ' || *str == '\t' || *str == '\n')
863 str++;
864
865 if (*str != '"')
866 {
867 fprintf(stderr, "Unexpected EOL");
868 DBUG_PRINT("info", ("str: %s", str));
869 DBUG_RETURN(0);
870 }
871
872 /* reading the text */
873 start= str + 1;
874 str= parse_text_line(start);
875
876 if (!(new_message->text= my_strndup(start, (uint) (str - start),
877 MYF(MY_WME | MY_FAE))))
878 DBUG_RETURN(0); /* Fatal error */
879 DBUG_PRINT("info", ("msg_text: %s", new_message->text));
880
881 DBUG_RETURN(new_message);
882 }
883
884
885 /*
886 Parsing the string with error name and codes; returns the pointer to
887 the errors struct
888 */
889
parse_error_string(char * str,int er_count)890 static struct errors *parse_error_string(char *str, int er_count)
891 {
892 struct errors *new_error;
893 DBUG_ENTER("parse_error_string");
894 DBUG_PRINT("enter", ("str: %s", str));
895
896 /* create a new element */
897 new_error= (struct errors *) my_malloc(sizeof(*new_error), MYF(MY_WME));
898
899 if (my_init_dynamic_array(&new_error->msg, sizeof(struct message), 0, 0))
900 DBUG_RETURN(0); /* OOM: Fatal error */
901
902 /* getting the error name */
903 str= skip_delimiters(str);
904
905 if (!(new_error->er_name= get_word(&str)))
906 DBUG_RETURN(0); /* OOM: Fatal error */
907 DBUG_PRINT("info", ("er_name: %s", new_error->er_name));
908
909 str= skip_delimiters(str);
910
911 /* getting the code1 */
912
913 new_error->d_code= er_offset + er_count;
914 DBUG_PRINT("info", ("d_code: %d", new_error->d_code));
915
916 str= skip_delimiters(str);
917
918 /* if we reached EOL => no more codes, but this can happen */
919 if (!*str)
920 {
921 new_error->sql_code1= empty_string;
922 new_error->sql_code2= empty_string;
923 DBUG_PRINT("info", ("str: %s", str));
924 DBUG_RETURN(new_error);
925 }
926
927 /* getting the sql_code 1 */
928
929 if (!(new_error->sql_code1= get_word(&str)))
930 DBUG_RETURN(0); /* OOM: Fatal error */
931 DBUG_PRINT("info", ("sql_code1: %s", new_error->sql_code1));
932
933 str= skip_delimiters(str);
934
935 /* if we reached EOL => no more codes, but this can happen */
936 if (!*str)
937 {
938 new_error->sql_code2= empty_string;
939 DBUG_PRINT("info", ("str: %s", str));
940 DBUG_RETURN(new_error);
941 }
942
943 /* getting the sql_code 2 */
944 if (!(new_error->sql_code2= get_word(&str)))
945 DBUG_RETURN(0); /* OOM: Fatal error */
946 DBUG_PRINT("info", ("sql_code2: %s", new_error->sql_code2));
947
948 str= skip_delimiters(str);
949 if (*str)
950 {
951 fprintf(stderr, "The error line did not end with sql/odbc code!");
952 DBUG_RETURN(0);
953 }
954
955 DBUG_RETURN(new_error);
956 }
957
958
959 /*
960 Parsing the string with full lang name/short lang name/charset;
961 returns pointer to the language structure
962 */
963
parse_charset_string(char * str)964 static struct languages *parse_charset_string(char *str)
965 {
966 struct languages *head=0, *new_lang;
967 DBUG_ENTER("parse_charset_string");
968 DBUG_PRINT("enter", ("str: %s", str));
969
970 /* skip over keyword */
971 str= find_end_of_word(str);
972 if (!*str)
973 {
974 /* unexpected EOL */
975 DBUG_PRINT("info", ("str: %s", str));
976 DBUG_RETURN(0);
977 }
978
979 str= skip_delimiters(str);
980 if (!(*str != ';' && *str))
981 DBUG_RETURN(0);
982
983 do
984 {
985 /*creating new element of the linked list */
986 new_lang= (struct languages *) my_malloc(sizeof(*new_lang), MYF(MY_WME));
987 new_lang->next_lang= head;
988 head= new_lang;
989
990 /* get the full language name */
991
992 if (!(new_lang->lang_long_name= get_word(&str)))
993 DBUG_RETURN(0); /* OOM: Fatal error */
994
995 DBUG_PRINT("info", ("long_name: %s", new_lang->lang_long_name));
996
997 /* getting the short name for language */
998 str= skip_delimiters(str);
999 if (!*str)
1000 DBUG_RETURN(0); /* Error: No space or tab */
1001
1002 if (!(new_lang->lang_short_name= get_word(&str)))
1003 DBUG_RETURN(0); /* OOM: Fatal error */
1004 DBUG_PRINT("info", ("short_name: %s", new_lang->lang_short_name));
1005
1006 /* getting the charset name */
1007 str= skip_delimiters(str);
1008 if (!(new_lang->charset= get_word(&str)))
1009 DBUG_RETURN(0); /* Fatal error */
1010 DBUG_PRINT("info", ("charset: %s", new_lang->charset));
1011
1012 /* skipping space, tab or "," */
1013 str= skip_delimiters(str);
1014 }
1015 while (*str != ';' && *str);
1016
1017 DBUG_PRINT("info", ("long name: %s", new_lang->lang_long_name));
1018 DBUG_RETURN(head);
1019 }
1020
1021
1022 /* Read options */
1023
print_version(void)1024 static void print_version(void)
1025 {
1026 DBUG_ENTER("print_version");
1027 printf("%s (Compile errormessage) Ver %s\n", my_progname, "2.0");
1028 DBUG_VOID_RETURN;
1029 }
1030
1031
1032 static my_bool
get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument MY_ATTRIBUTE ((unused)))1033 get_one_option(int optid, const struct my_option *opt MY_ATTRIBUTE ((unused)),
1034 char *argument MY_ATTRIBUTE ((unused)))
1035 {
1036 DBUG_ENTER("get_one_option");
1037 switch (optid) {
1038 case 'V':
1039 print_version();
1040 exit(0);
1041 break;
1042 case '?':
1043 usage();
1044 exit(0);
1045 break;
1046 case '#':
1047 DBUG_PUSH(argument ? argument : default_dbug_option);
1048 break;
1049 }
1050 DBUG_RETURN(0);
1051 }
1052
1053
usage(void)1054 static void usage(void)
1055 {
1056 DBUG_ENTER("usage");
1057 print_version();
1058 printf("This software comes with ABSOLUTELY NO WARRANTY. "
1059 "This is free software,\n"
1060 "and you are welcome to modify and redistribute it under the GPL license.\n"
1061 "Usage:\n");
1062 my_print_help(my_long_options);
1063 my_print_variables(my_long_options);
1064 DBUG_VOID_RETURN;
1065 }
1066
1067
get_options(int * argc,char *** argv)1068 static int get_options(int *argc, char ***argv)
1069 {
1070 int ho_error;
1071 DBUG_ENTER("get_options");
1072
1073 if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
1074 DBUG_RETURN(ho_error);
1075 DBUG_RETURN(0);
1076 }
1077
1078
1079 /*
1080 Read rows and remember them until row that start with char Converts
1081 row as a C-compiler would convert a textstring
1082 */
1083
parse_text_line(char * pos)1084 static char *parse_text_line(char *pos)
1085 {
1086 int i, nr;
1087 char *row= pos;
1088 size_t len;
1089 DBUG_ENTER("parse_text_line");
1090
1091 len= strlen (pos);
1092 while (*pos)
1093 {
1094 if (*pos == '\\')
1095 {
1096 switch (*++pos) {
1097 case '\\':
1098 case '"':
1099 (void) memmove (pos - 1, pos, len - (row - pos));
1100 break;
1101 case 'n':
1102 pos[-1]= '\n';
1103 (void) memmove (pos, pos + 1, len - (row - pos));
1104 break;
1105 default:
1106 if (*pos >= '0' && *pos < '8')
1107 {
1108 nr= 0;
1109 for (i= 0; i < 3 && (*pos >= '0' && *pos < '8'); i++)
1110 nr= nr * 8 + (*(pos++) - '0');
1111 pos -= i;
1112 pos[-1]= nr;
1113 (void) memmove (pos, pos + i, len - (row - pos));
1114 }
1115 else if (*pos)
1116 (void) memmove (pos - 1, pos, len - (row - pos)); /* Remove '\' */
1117 }
1118 }
1119 else
1120 pos++;
1121 }
1122 while (pos > row + 1 && *pos != '"')
1123 pos--;
1124 *pos= 0;
1125 DBUG_RETURN(pos);
1126 }
1127
1128
1129 /* Copy rows from memory to file and remember position */
1130
copy_rows(FILE * to,char * row,int row_nr,long start_pos)1131 static int copy_rows(FILE *to, char *row, int row_nr, long start_pos)
1132 {
1133 DBUG_ENTER("copy_rows");
1134
1135 file_pos[row_nr]= (int) (ftell(to) - start_pos);
1136 if (fputs(row, to) == EOF || fputc('\0', to) == EOF)
1137 {
1138 fprintf(stderr, "Can't write to outputfile\n");
1139 DBUG_RETURN(1);
1140 }
1141
1142 DBUG_RETURN(0);
1143 }
1144