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