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