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-1335 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 #include <ctype.h>
35
36 #define MAX_ROWS 3000
37 #define ERRORS_PER_RANGE 1000
38 #define MAX_SECTIONS 4
39 #define HEADER_LENGTH 32 /* Length of header in errmsg.sys */
40 #define ERRMSG_VERSION 4 /* Version number of errmsg.sys */
41 #define DEFAULT_CHARSET_DIR "../sql/share/charsets"
42 #define ER_PREFIX "ER_"
43 #define ER_PREFIX2 "MARIA_ER_"
44 #define WARN_PREFIX "WARN_"
45 static char *OUTFILE= (char*) "errmsg.sys";
46 static char *HEADERFILE= (char*) "mysqld_error.h";
47 static char *NAMEFILE= (char*) "mysqld_ername.h";
48 static char *STATEFILE= (char*) "sql_state.h";
49 static char *TXTFILE= (char*) "../sql/share/errmsg-utf8.txt";
50 static char *DATADIRECTORY= (char*) "../sql/share/";
51 #ifndef DBUG_OFF
52 static char *default_dbug_option= (char*) "d:t:O,/tmp/comp_err.trace";
53 #endif
54
55 /* Header for errmsg.sys files */
56 uchar file_head[]= { 254, 254, 2, ERRMSG_VERSION };
57 /* Store positions to each error message row to store in errmsg.sys header */
58 uint file_pos[MAX_ROWS+1];
59 uint section_count,section_start;
60 uchar section_header[MAX_SECTIONS*2];
61
62 const char *empty_string= ""; /* For empty states */
63 /*
64 Default values for command line options. See getopt structure for definitions
65 for these.
66 */
67
68 const char *default_language= "eng";
69 my_bool default_language_changed= 0;
70 uint er_offset= 1000;
71 my_bool info_flag= 0;
72
73 /* Storage of one error message row (for one language) */
74
75 struct message
76 {
77 char *lang_short_name;
78 char *text;
79 };
80
81
82 /* Storage for languages and charsets (from start of error text file) */
83
84 struct languages
85 {
86 char *lang_long_name; /* full name of the language */
87 char *lang_short_name; /* abbreviation of the lang. */
88 char *charset; /* Character set name */
89 struct languages *next_lang; /* Pointer to next language */
90 };
91
92
93 /* Name, code and texts (for all lang) for one error message */
94
95 struct errors
96 {
97 const char *er_name; /* Name of the error (ER_HASHCK) */
98 uint d_code; /* Error code number */
99 const char *sql_code1; /* sql state */
100 const char *sql_code2; /* ODBC state */
101 struct errors *next_error; /* Pointer to next error */
102 DYNAMIC_ARRAY msg; /* All language texts for this error */
103 };
104
105
106 static struct my_option my_long_options[]=
107 {
108 #ifdef DBUG_OFF
109 {"debug", '#', "This is a non-debug version. Catch this and exit",
110 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
111 #else
112 {"debug", '#', "Output debug log", &default_dbug_option,
113 &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
114 #endif
115 {"debug-info", 'T', "Print some debug info at exit.", &info_flag,
116 &info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
117 {"help", '?', "Displays this help and exits.", 0, 0, 0, GET_NO_ARG,
118 NO_ARG, 0, 0, 0, 0, 0, 0},
119 {"version", 'V', "Prints version", 0, 0, 0, GET_NO_ARG,
120 NO_ARG, 0, 0, 0, 0, 0, 0},
121 {"charset", 'C', "Charset dir",
122 (char**) &charsets_dir, (char**) &charsets_dir,
123 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
124 {"in_file", 'F', "Input file", &TXTFILE, &TXTFILE,
125 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
126 {"out_dir", 'D', "Output base directory", &DATADIRECTORY, &DATADIRECTORY,
127 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
128 {"out_file", 'O', "Output filename (errmsg.sys)", &OUTFILE,
129 &OUTFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
130 {"header_file", 'H', "mysqld_error.h file ", &HEADERFILE,
131 &HEADERFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
132 {"name_file", 'N', "mysqld_ername.h file ", &NAMEFILE,
133 &NAMEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
134 {"state_file", 'S', "sql_state.h file", &STATEFILE,
135 &STATEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
136 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
137 };
138
139
140 static struct errors *generate_empty_message(uint dcode, my_bool skip);
141 static struct languages *parse_charset_string(char *str);
142 static struct errors *parse_error_string(char *ptr, int er_count);
143 static struct message *parse_message_string(struct message *new_message,
144 char *str);
145 static struct message *find_message(struct errors *err, const char *lang,
146 my_bool no_default);
147 static int check_message_format(struct errors *err,
148 const char* mess);
149 static uint parse_input_file(const char *file_name, struct errors **top_error,
150 struct languages **top_language,
151 uint *error_count);
152 static int get_options(int *argc, char ***argv);
153 static void print_version(void);
154 static void usage(void);
155 static char *parse_text_line(char *pos);
156 static int copy_rows(FILE * to, char *row, int row_nr, long start_pos);
157 static char *parse_default_language(char *str);
158 static uint parse_error_offset(char *str);
159
160 static char *skip_delimiters(char *str);
161 static char *get_word(char **str);
162 static char *find_end_of_word(char *str);
163 static void clean_up(struct languages *lang_head, struct errors *error_head);
164 static int create_header_files(struct errors *error_head);
165 static int create_sys_files(struct languages *lang_head,
166 struct errors *error_head, uint max_error,
167 uint error_count);
168
169
main(int argc,char * argv[])170 int 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
210 err:
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
print_escaped_string(FILE * f,const char * str)219 static 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
create_header_files(struct errors * error_head)239 static 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
create_sys_files(struct languages * lang_head,struct errors * error_head,uint max_error,uint error_count)326 static 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
432 err:
433 my_fclose(to, MYF(0));
434 DBUG_RETURN(1);
435 }
436
437
clean_up(struct languages * lang_head,struct errors * error_head)438 static 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 if (default_language_changed)
445 my_free((void*) default_language);
446
447 for (tmp_lang= lang_head; tmp_lang; tmp_lang= next_language)
448 {
449 next_language= tmp_lang->next_lang;
450 my_free(tmp_lang->lang_short_name);
451 my_free(tmp_lang->lang_long_name);
452 my_free(tmp_lang->charset);
453 my_free(tmp_lang);
454 }
455
456 for (tmp_error= error_head; tmp_error; tmp_error= next_error)
457 {
458 next_error= tmp_error->next_error;
459 count= (tmp_error->msg).elements;
460 for (i= 0; i < count; i++)
461 {
462 struct message *tmp;
463 tmp= dynamic_element(&tmp_error->msg, i, struct message*);
464 my_free(tmp->lang_short_name);
465 my_free(tmp->text);
466 }
467
468 delete_dynamic(&tmp_error->msg);
469 if (tmp_error->sql_code1[0])
470 my_free((void*) tmp_error->sql_code1);
471 if (tmp_error->sql_code2[0])
472 my_free((void*) tmp_error->sql_code2);
473 my_free((void*) tmp_error->er_name);
474 my_free(tmp_error);
475 }
476 }
477
478
parse_input_file(const char * file_name,struct errors ** top_error,struct languages ** top_lang,uint * error_count)479 static uint parse_input_file(const char *file_name, struct errors **top_error,
480 struct languages **top_lang, uint *error_count)
481 {
482 FILE *file;
483 char *str, buff[1000];
484 struct errors *current_error= 0, **tail_error= top_error;
485 struct message current_message;
486 uint rcount= 0, skiped_errors= 0;
487 my_bool er_offset_found= 0;
488 DBUG_ENTER("parse_input_file");
489
490 *top_error= 0;
491 *top_lang= 0;
492 *error_count= 0;
493 section_start= er_offset;
494 section_count= 0;
495
496 if (!(file= my_fopen(file_name, O_RDONLY | O_SHARE, MYF(MY_WME))))
497 DBUG_RETURN(0);
498
499 while ((str= fgets(buff, sizeof(buff), file)))
500 {
501 my_bool skip;
502 if (is_prefix(str, "language"))
503 {
504 if (!(*top_lang= parse_charset_string(str)))
505 {
506 fprintf(stderr, "Failed to parse the charset string!\n");
507 DBUG_RETURN(0);
508 }
509 continue;
510 }
511 skip= 0;
512 if (is_prefix(str, "start-error-number") ||
513 (skip= is_prefix(str, "skip-to-error-number")))
514 {
515 uint tmp_er_offset;
516
517 if (!(tmp_er_offset= parse_error_offset(str)))
518 {
519 fprintf(stderr, "Failed to parse the error offset string!\n");
520 DBUG_RETURN(0);
521 }
522 if (skip)
523 {
524 if (section_count >= MAX_SECTIONS-1)
525 {
526 fprintf(stderr, "Found too many skip-to-error-number entries. "
527 "We only support %d entries\n", MAX_SECTIONS);
528 DBUG_RETURN(0);
529 }
530 int2store(section_header + section_count*2,
531 er_offset +rcount - section_start);
532 section_count++;
533 section_start= tmp_er_offset;
534 }
535 if (!er_offset_found)
536 {
537 er_offset_found= 1;
538 er_offset= section_start= tmp_er_offset;
539 }
540 else
541 {
542 /* Create empty error messages between er_offset and tmp_err_offset */
543 if (tmp_er_offset < er_offset + rcount)
544 {
545 fprintf(stderr, "new start-error-number %u is smaller than current error message: %u\n", tmp_er_offset, er_offset + rcount);
546 DBUG_RETURN(0);
547 }
548 for ( ; er_offset + rcount < tmp_er_offset ; rcount++)
549 {
550 skiped_errors+= skip != 0;
551 current_error= generate_empty_message(er_offset + rcount, skip);
552 *tail_error= current_error;
553 tail_error= ¤t_error->next_error;
554 }
555 }
556 continue;
557 }
558 if (is_prefix(str, "default-language"))
559 {
560 if (!(default_language= parse_default_language(str)))
561 {
562 DBUG_PRINT("info", ("default_slang: %s", default_language));
563 fprintf(stderr,
564 "Failed to parse the default language line. Aborting\n");
565 DBUG_RETURN(0);
566 }
567 default_language_changed= 1;
568 continue;
569 }
570
571 if (*str == '\t' || *str == ' ')
572 {
573 /* New error message in another language for previous error */
574 if (!current_error)
575 {
576 fprintf(stderr, "Error in the input file format\n");
577 DBUG_RETURN(0);
578 }
579 if (!parse_message_string(¤t_message, str))
580 {
581 fprintf(stderr, "Failed to parse message string for error '%s'",
582 current_error->er_name);
583 DBUG_RETURN(0);
584 }
585 if (find_message(current_error, current_message.lang_short_name, TRUE))
586 {
587 fprintf(stderr, "Duplicate message string for error '%s'"
588 " in language '%s'\n",
589 current_error->er_name, current_message.lang_short_name);
590 DBUG_RETURN(0);
591 }
592 if (check_message_format(current_error, current_message.text))
593 {
594 fprintf(stderr, "Wrong formatspecifier of error message string"
595 " for error '%s' in language '%s'\n",
596 current_error->er_name, current_message.lang_short_name);
597 DBUG_RETURN(0);
598 }
599 if (insert_dynamic(¤t_error->msg, (uchar *) & current_message))
600 DBUG_RETURN(0);
601 continue;
602 }
603 if (is_prefix(str, ER_PREFIX) || is_prefix(str, WARN_PREFIX) ||
604 is_prefix(str, ER_PREFIX2))
605 {
606 if (!(current_error= parse_error_string(str, rcount)))
607 {
608 fprintf(stderr, "Failed to parse the error name string\n");
609 DBUG_RETURN(0);
610 }
611 rcount++; /* Count number of unique errors */
612
613 /* add error to the list */
614 *tail_error= current_error;
615 tail_error= ¤t_error->next_error;
616 continue;
617 }
618 if (*str == '#' || *str == '\n')
619 continue; /* skip comment or empty lines */
620
621 fprintf(stderr, "Wrong input file format. Stop!\nLine: %s\n", str);
622 DBUG_RETURN(0);
623 }
624 int2store(section_header + section_count*2,
625 er_offset + rcount - section_start);
626 section_count++;
627 *error_count= rcount - skiped_errors;
628
629 *tail_error= 0; /* Mark end of list */
630
631 my_fclose(file, MYF(0));
632 DBUG_RETURN(rcount);
633 }
634
635
parse_error_offset(char * str)636 static uint parse_error_offset(char *str)
637 {
638 char *soffset, *end;
639 int error;
640 uint ioffset;
641
642 DBUG_ENTER("parse_error_offset");
643 /* skipping the "start-error-number" keyword and spaces after it */
644 str= find_end_of_word(str);
645 str= skip_delimiters(str);
646
647 if (!*str)
648 DBUG_RETURN(0); /* Unexpected EOL: No error number after the keyword */
649
650 /* reading the error offset */
651 if (!(soffset= get_word(&str)))
652 DBUG_RETURN(0); /* OOM: Fatal error */
653 DBUG_PRINT("info", ("default_error_offset: %s", soffset));
654
655 /* skipping space(s) and/or tabs after the error offset */
656 str= skip_delimiters(str);
657 DBUG_PRINT("info", ("str: %s", str));
658 if (*str)
659 {
660 /* The line does not end with the error offset -> error! */
661 fprintf(stderr, "The error offset line does not end with an error offset");
662 DBUG_RETURN(0);
663 }
664 DBUG_PRINT("info", ("str: %s", str));
665
666 end= 0;
667 ioffset= (uint) my_strtoll10(soffset, &end, &error);
668 my_free(soffset);
669 DBUG_RETURN(ioffset);
670 }
671
672
673 /* Parsing of the default language line. e.g. "default-language eng" */
674
parse_default_language(char * str)675 static char *parse_default_language(char *str)
676 {
677 char *slang;
678
679 DBUG_ENTER("parse_default_language");
680 /* skipping the "default-language" keyword */
681 str= find_end_of_word(str);
682 /* skipping space(s) and/or tabs after the keyword */
683 str= skip_delimiters(str);
684 if (!*str)
685 {
686 fprintf(stderr,
687 "Unexpected EOL: No short language name after the keyword\n");
688 DBUG_RETURN(0);
689 }
690
691 /* reading the short language tag */
692 if (!(slang= get_word(&str)))
693 DBUG_RETURN(0); /* OOM: Fatal error */
694 DBUG_PRINT("info", ("default_slang: %s", slang));
695
696 str= skip_delimiters(str);
697 DBUG_PRINT("info", ("str: %s", str));
698 if (*str)
699 {
700 fprintf(stderr,
701 "The default language line does not end with short language "
702 "name\n");
703 DBUG_RETURN(0);
704 }
705 DBUG_PRINT("info", ("str: %s", str));
706 DBUG_RETURN(slang);
707 }
708
709
710 /*
711 Find the message in a particular language
712
713 SYNOPSIS
714 find_message()
715 err Error to find message for
716 lang Language of message to find
717 no_default Don't return default (English) if does not exit
718
719 RETURN VALUE
720 Returns the message structure if one is found, or NULL if not.
721 */
find_message(struct errors * err,const char * lang,my_bool no_default)722 static struct message *find_message(struct errors *err, const char *lang,
723 my_bool no_default)
724 {
725 struct message *tmp, *return_val= 0;
726 uint i, count;
727 DBUG_ENTER("find_message");
728
729 count= (err->msg).elements;
730 for (i= 0; i < count; i++)
731 {
732 tmp= dynamic_element(&err->msg, i, struct message*);
733
734 if (!strcmp(tmp->lang_short_name, lang))
735 DBUG_RETURN(tmp);
736 if (!strcmp(tmp->lang_short_name, default_language))
737 {
738 return_val= tmp;
739 }
740 }
741 DBUG_RETURN(no_default ? NULL : return_val);
742 }
743
744
745
746 /*
747 Check message format specifiers against error message for
748 previous language
749
750 SYNOPSIS
751 checksum_format_specifier()
752 msg String for which to generate checksum
753 for the format specifiers
754
755 RETURN VALUE
756 Returns the checksum for all letters of the
757 format specifiers
758
759 Ex.
760 "text '%-.64s' text part 2 %zu'"
761 ^ ^^
762 characters will be xored to form checksum
763
764 Non-letters are skipped, because they do not change the type
765 of the argument.
766
767 NOTE:
768 Does not support format specifiers with positional args like "%2$s"
769 */
770
checksum_format_specifier(const char * msg)771 static ha_checksum checksum_format_specifier(const char* msg)
772 {
773 ha_checksum chksum= 0;
774 const uchar* p= (const uchar*) msg;
775 const uchar* start= NULL;
776 uint32 num_format_specifiers= 0;
777 while (*p)
778 {
779
780 if (*p == '%')
781 {
782 start= p+1; /* Entering format specifier */
783 num_format_specifiers++;
784 }
785 else if (start && isalpha(*p))
786 {
787 chksum= my_checksum(chksum, p, 1);
788 switch(*p) {
789 case 'd':
790 case 'u':
791 case 'x':
792 case 's':
793 case 'M':
794 case 'T':
795 start= 0; /* Not in format specifier anymore */
796 break;
797 }
798 }
799
800 p++;
801 }
802
803 if (start)
804 {
805 /* Still inside a format specifier after end of string */
806
807 fprintf(stderr, "Still inside formatspecifier after end of string"
808 " in'%s'\n", msg);
809 DBUG_ASSERT(start==0);
810 }
811
812 /* Add number of format specifiers to checksum as extra safeguard */
813 chksum+= num_format_specifiers;
814
815 return chksum;
816 }
817
818
819 /*
820 Check message format specifiers against error message for
821 previous language
822
823 SYNOPSIS
824 check_message_format()
825 err Error to check message for
826 mess Message to check
827
828 RETURN VALUE
829 Returns 0 if no previous error message or message format is ok
830 */
check_message_format(struct errors * err,const char * mess)831 static int check_message_format(struct errors *err,
832 const char* mess)
833 {
834 struct message *first;
835 DBUG_ENTER("check_message_format");
836
837 /* Get first message(if any) */
838 if ((err->msg).elements == 0)
839 DBUG_RETURN(0); /* No previous message to compare against */
840
841 first= dynamic_element(&err->msg, 0, struct message*);
842 DBUG_ASSERT(first != NULL);
843
844 if (checksum_format_specifier(first->text) !=
845 checksum_format_specifier(mess))
846 {
847 /* Check sum of format specifiers failed, they should be equal */
848 DBUG_RETURN(1);
849 }
850 DBUG_RETURN(0);
851 }
852
853
854 /*
855 Skips spaces and or tabs till the beginning of the next word
856 Returns pointer to the beginning of the first character of the word
857 */
858
skip_delimiters(char * str)859 static char *skip_delimiters(char *str)
860 {
861 DBUG_ENTER("skip_delimiters");
862 for (;
863 *str == ' ' || *str == ',' || *str == '\t' || *str == '\r' ||
864 *str == '\n' || *str == '='; str++)
865 ;
866 DBUG_RETURN(str);
867 }
868
869
870 /*
871 Skips all characters till meets with space, or tab, or EOL
872 */
873
find_end_of_word(char * str)874 static char *find_end_of_word(char *str)
875 {
876 DBUG_ENTER("find_end_of_word");
877 for (;
878 *str != ' ' && *str != '\t' && *str != '\n' && *str != '\r' && *str &&
879 *str != ',' && *str != ';' && *str != '='; str++)
880 ;
881 DBUG_RETURN(str);
882 }
883
884
885 /* Read the word starting from *str */
886
get_word(char ** str)887 static char *get_word(char **str)
888 {
889 char *start= *str;
890 DBUG_ENTER("get_word");
891
892 *str= find_end_of_word(start);
893 DBUG_RETURN(my_strndup(PSI_NOT_INSTRUMENTED, start, (uint) (*str - start),
894 MYF(MY_WME | MY_FAE)));
895 }
896
897
898 /*
899 Parsing the string with short_lang - message text. Code - to
900 remember to which error does the text belong
901 */
902
parse_message_string(struct message * new_message,char * str)903 static struct message *parse_message_string(struct message *new_message,
904 char *str)
905 {
906 char *start;
907
908 DBUG_ENTER("parse_message_string");
909 DBUG_PRINT("enter", ("str: %s", str));
910
911 /*skip space(s) and/or tabs in the beginning */
912 while (*str == ' ' || *str == '\t' || *str == '\n')
913 str++;
914
915 if (!*str)
916 {
917 /* It was not a message line, but an empty line. */
918 DBUG_PRINT("info", ("str: %s", str));
919 DBUG_RETURN(0);
920 }
921
922 /* reading the short lang */
923 start= str;
924 while (*str != ' ' && *str != '\t' && *str)
925 str++;
926 if (!(new_message->lang_short_name=
927 my_strndup(PSI_NOT_INSTRUMENTED, start, (uint) (str - start),
928 MYF(MY_WME | MY_FAE))))
929 DBUG_RETURN(0); /* Fatal error */
930 DBUG_PRINT("info", ("msg_slang: %s", new_message->lang_short_name));
931
932 /*skip space(s) and/or tabs after the lang */
933 while (*str == ' ' || *str == '\t' || *str == '\n')
934 str++;
935
936 if (*str != '"')
937 {
938 fprintf(stderr, "Unexpected EOL");
939 DBUG_PRINT("info", ("str: %s", str));
940 DBUG_RETURN(0);
941 }
942
943 /* reading the text */
944 start= str + 1;
945 str= parse_text_line(start);
946
947 if (!(new_message->text= my_strndup(PSI_NOT_INSTRUMENTED, start,
948 (uint) (str - start), MYF(MY_WME | MY_FAE))))
949 DBUG_RETURN(0);
950 DBUG_PRINT("info", ("msg_text: %s", new_message->text));
951
952 DBUG_RETURN(new_message);
953 }
954
955
generate_empty_message(uint d_code,my_bool skip)956 static struct errors *generate_empty_message(uint d_code, my_bool skip)
957 {
958 struct errors *new_error;
959 struct message message;
960
961 /* create a new element */
962 if (!(new_error= (struct errors *) my_malloc(PSI_NOT_INSTRUMENTED,
963 sizeof(*new_error), MYF(MY_WME))))
964 return(0);
965 if (my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &new_error->msg,
966 sizeof(struct message), 0, 1, MYF(0)))
967 return(0); /* OOM: Fatal error */
968
969 new_error->er_name= NULL;
970 new_error->d_code= d_code;
971 new_error->sql_code1= empty_string;
972 new_error->sql_code2= empty_string;
973 new_error->next_error= 0;
974
975 message.text= 0; /* If skip set, don't generate a text */
976
977 if (!(message.lang_short_name= my_strdup(PSI_NOT_INSTRUMENTED,
978 default_language, MYF(MY_WME))) ||
979 (!skip && !(message.text= my_strdup(PSI_NOT_INSTRUMENTED,
980 "", MYF(MY_WME)))))
981 return(0);
982
983 /* Can't fail as msg is preallocated */
984 (void) insert_dynamic(&new_error->msg, (uchar*) &message);
985 return(new_error);
986 }
987
988
989 /*
990 Parsing the string with error name and codes; returns the pointer to
991 the errors struct
992 */
993
parse_error_string(char * str,int er_count)994 static struct errors *parse_error_string(char *str, int er_count)
995 {
996 struct errors *new_error;
997 DBUG_ENTER("parse_error_string");
998 DBUG_PRINT("enter", ("str: %s", str));
999
1000 /* create a new element */
1001 if (!(new_error= (struct errors *) my_malloc(PSI_NOT_INSTRUMENTED,
1002 sizeof(*new_error), MYF(MY_WME))))
1003 DBUG_RETURN(0);
1004
1005 new_error->next_error= 0;
1006 if (my_init_dynamic_array(PSI_NOT_INSTRUMENTED, &new_error->msg,
1007 sizeof(struct message), 0, 0, MYF(0)))
1008 DBUG_RETURN(0);
1009
1010 /* getting the error name */
1011 str= skip_delimiters(str);
1012
1013 if (!(new_error->er_name= get_word(&str)))
1014 DBUG_RETURN(0); /* OOM: Fatal error */
1015 DBUG_PRINT("info", ("er_name: %s", new_error->er_name));
1016
1017 str= skip_delimiters(str);
1018
1019 /* getting the code1 */
1020
1021 new_error->d_code= er_offset + er_count;
1022 DBUG_PRINT("info", ("d_code: %d", new_error->d_code));
1023
1024 str= skip_delimiters(str);
1025
1026 /* if we reached EOL => no more codes, but this can happen */
1027 if (!*str)
1028 {
1029 new_error->sql_code1= empty_string;
1030 new_error->sql_code2= empty_string;
1031 DBUG_PRINT("info", ("str: %s", str));
1032 DBUG_RETURN(new_error);
1033 }
1034
1035 /* getting the sql_code 1 */
1036
1037 if (!(new_error->sql_code1= get_word(&str)))
1038 DBUG_RETURN(0); /* OOM: Fatal error */
1039 DBUG_PRINT("info", ("sql_code1: %s", new_error->sql_code1));
1040
1041 str= skip_delimiters(str);
1042
1043 /* if we reached EOL => no more codes, but this can happen */
1044 if (!*str)
1045 {
1046 new_error->sql_code2= empty_string;
1047 DBUG_PRINT("info", ("str: %s", str));
1048 DBUG_RETURN(new_error);
1049 }
1050
1051 /* getting the sql_code 2 */
1052 if (!(new_error->sql_code2= get_word(&str)))
1053 DBUG_RETURN(0); /* OOM: Fatal error */
1054 DBUG_PRINT("info", ("sql_code2: %s", new_error->sql_code2));
1055
1056 str= skip_delimiters(str);
1057 if (*str)
1058 {
1059 fprintf(stderr, "The error line did not end with sql/odbc code!");
1060 DBUG_RETURN(0);
1061 }
1062
1063 DBUG_RETURN(new_error);
1064 }
1065
1066
1067 /*
1068 Parsing the string with full lang name/short lang name/charset;
1069 returns pointer to the language structure
1070 */
1071
parse_charset_string(char * str)1072 static struct languages *parse_charset_string(char *str)
1073 {
1074 struct languages *head=0, *new_lang;
1075 DBUG_ENTER("parse_charset_string");
1076 DBUG_PRINT("enter", ("str: %s", str));
1077
1078 /* skip over keyword */
1079 str= find_end_of_word(str);
1080 if (!*str)
1081 {
1082 /* unexpected EOL */
1083 DBUG_PRINT("info", ("str: %s", str));
1084 DBUG_RETURN(0);
1085 }
1086
1087 str= skip_delimiters(str);
1088 if (!(*str != ';' && *str))
1089 DBUG_RETURN(0);
1090
1091 do
1092 {
1093 /*creating new element of the linked list */
1094 new_lang= (struct languages *) my_malloc(PSI_NOT_INSTRUMENTED,
1095 sizeof(*new_lang), MYF(MY_WME));
1096 new_lang->next_lang= head;
1097 head= new_lang;
1098
1099 /* get the full language name */
1100
1101 if (!(new_lang->lang_long_name= get_word(&str)))
1102 DBUG_RETURN(0); /* OOM: Fatal error */
1103
1104 DBUG_PRINT("info", ("long_name: %s", new_lang->lang_long_name));
1105
1106 /* getting the short name for language */
1107 str= skip_delimiters(str);
1108 if (!*str)
1109 DBUG_RETURN(0); /* Error: No space or tab */
1110
1111 if (!(new_lang->lang_short_name= get_word(&str)))
1112 DBUG_RETURN(0); /* OOM: Fatal error */
1113 DBUG_PRINT("info", ("short_name: %s", new_lang->lang_short_name));
1114
1115 /* getting the charset name */
1116 str= skip_delimiters(str);
1117 if (!(new_lang->charset= get_word(&str)))
1118 DBUG_RETURN(0); /* Fatal error */
1119 DBUG_PRINT("info", ("charset: %s", new_lang->charset));
1120
1121 /* skipping space, tab or "," */
1122 str= skip_delimiters(str);
1123 }
1124 while (*str != ';' && *str);
1125
1126 DBUG_PRINT("info", ("long name: %s", new_lang->lang_long_name));
1127 DBUG_RETURN(head);
1128 }
1129
1130
1131 /* Read options */
1132
print_version(void)1133 static void print_version(void)
1134 {
1135 DBUG_ENTER("print_version");
1136 printf("%s (Compile errormessage) Ver %s\n", my_progname, "3.0");
1137 DBUG_VOID_RETURN;
1138 }
1139
1140
1141 static my_bool
get_one_option(const struct my_option * opt,const char * argument,const char * filename)1142 get_one_option(const struct my_option *opt,
1143 const char *argument __attribute__ ((unused)),
1144 const char *filename __attribute__ ((unused)))
1145 {
1146 DBUG_ENTER("get_one_option");
1147 switch (opt->id) {
1148 case 'V':
1149 print_version();
1150 my_end(0);
1151 exit(0);
1152 break;
1153 case '?':
1154 usage();
1155 my_end(0);
1156 exit(0);
1157 break;
1158 case '#':
1159 DBUG_PUSH(argument ? argument : default_dbug_option);
1160 break;
1161 }
1162 DBUG_RETURN(0);
1163 }
1164
1165
usage(void)1166 static void usage(void)
1167 {
1168 DBUG_ENTER("usage");
1169 print_version();
1170 printf("This software comes with ABSOLUTELY NO WARRANTY. "
1171 "This is free software,\n"
1172 "and you are welcome to modify and redistribute it under the GPL license.\n"
1173 "Usage:\n");
1174 my_print_help(my_long_options);
1175 my_print_variables(my_long_options);
1176 DBUG_VOID_RETURN;
1177 }
1178
1179
get_options(int * argc,char *** argv)1180 static int get_options(int *argc, char ***argv)
1181 {
1182 int ho_error;
1183 DBUG_ENTER("get_options");
1184
1185 if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
1186 DBUG_RETURN(ho_error);
1187 DBUG_RETURN(0);
1188 }
1189
1190
1191 /*
1192 Read rows and remember them until row that start with char Converts
1193 row as a C-compiler would convert a textstring
1194 */
1195
parse_text_line(char * pos)1196 static char *parse_text_line(char *pos)
1197 {
1198 int i, nr;
1199 char *row= pos;
1200 size_t len;
1201 DBUG_ENTER("parse_text_line");
1202
1203 len= strlen (pos);
1204 while (*pos)
1205 {
1206 if (*pos == '\\')
1207 {
1208 switch (*++pos) {
1209 case '\\':
1210 case '"':
1211 (void) memmove (pos - 1, pos, len - (row - pos));
1212 break;
1213 case 'n':
1214 pos[-1]= '\n';
1215 (void) memmove (pos, pos + 1, len - (row - pos));
1216 break;
1217 default:
1218 if (*pos >= '0' && *pos < '8')
1219 {
1220 nr= 0;
1221 for (i= 0; i < 3 && (*pos >= '0' && *pos < '8'); i++)
1222 nr= nr * 8 + (*(pos++) - '0');
1223 pos -= i;
1224 pos[-1]= nr;
1225 (void) memmove (pos, pos + i, len - (row - pos));
1226 }
1227 else if (*pos)
1228 (void) memmove (pos - 1, pos, len - (row - pos)); /* Remove '\' */
1229 }
1230 }
1231 else
1232 pos++;
1233 }
1234 while (pos > row + 1 && *pos != '"')
1235 pos--;
1236 *pos= 0;
1237 DBUG_RETURN(pos);
1238 }
1239
1240
1241 /* Copy rows from memory to file and remember position */
1242
copy_rows(FILE * to,char * row,int row_nr,long start_pos)1243 static int copy_rows(FILE *to, char *row, int row_nr, long start_pos)
1244 {
1245 DBUG_ENTER("copy_rows");
1246
1247 file_pos[row_nr]= (int) (ftell(to) - start_pos);
1248 if (fputs(row, to) == EOF || fputc('\0', to) == EOF)
1249 {
1250 fprintf(stderr, "Can't write to outputfile\n");
1251 DBUG_RETURN(1);
1252 }
1253
1254 DBUG_RETURN(0);
1255 }
1256