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
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_"
44 static char *OUTFILE= (char*) "errmsg.sys";
45 static char *HEADERFILE= (char*) "mysqld_error.h";
46 static char *NAMEFILE= (char*) "mysqld_ername.h";
47 static char *STATEFILE= (char*) "sql_state.h";
48 static char *TXTFILE= (char*) "../sql/share/errmsg-utf8.txt";
49 static char *DATADIRECTORY= (char*) "../sql/share/";
50 #ifndef DBUG_OFF
51 static char *default_dbug_option= (char*) "d:t:O,/tmp/comp_err.trace";
52 #endif
53
54 /* Header for errmsg.sys files */
55 uchar file_head[]= { 254, 254, 2, ERRMSG_VERSION };
56 /* Store positions to each error message row to store in errmsg.sys header */
57 uint file_pos[MAX_ROWS+1];
58 uint section_count,section_start;
59 uchar section_header[MAX_SECTIONS*2];
60
61 const char *empty_string= ""; /* For empty states */
62 /*
63 Default values for command line options. See getopt structure for definitions
64 for these.
65 */
66
67 const char *default_language= "eng";
68 uint er_offset= 1000;
69 my_bool info_flag= 0;
70
71 /* Storage of one error message row (for one language) */
72
73 struct 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
82 struct 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
93 struct 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
104 static 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
138 static struct errors *generate_empty_message(uint dcode, my_bool skip);
139 static struct languages *parse_charset_string(char *str);
140 static struct errors *parse_error_string(char *ptr, int er_count);
141 static struct message *parse_message_string(struct message *new_message,
142 char *str);
143 static struct message *find_message(struct errors *err, const char *lang,
144 my_bool no_default);
145 static int check_message_format(struct errors *err,
146 const char* mess);
147 static uint parse_input_file(const char *file_name, struct errors **top_error,
148 struct languages **top_language,
149 uint *error_count);
150 static int get_options(int *argc, char ***argv);
151 static void print_version(void);
152 static void usage(void);
153 static my_bool get_one_option(int optid, const struct my_option *opt,
154 char *argument);
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 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
parse_input_file(const char * file_name,struct errors ** top_error,struct languages ** top_lang,uint * error_count)478 static 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= ¤t_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(¤t_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(¤t_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= ¤t_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
parse_error_offset(char * str)634 static 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
parse_default_language(char * str)673 static 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 */
find_message(struct errors * err,const char * lang,my_bool no_default)720 static 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
checksum_format_specifier(const char * msg)768 static 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 case 'T':
791 chksum= my_checksum(chksum, (uchar*) start, (uint) (p + 1 - start));
792 start= 0; /* Not in format specifier anymore */
793 break;
794
795 default:
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(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(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(start, (uint) (str - start),
948 MYF(MY_WME | MY_FAE))))
949 DBUG_RETURN(0); /* Fatal error */
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(sizeof(*new_error),
963 MYF(MY_WME))))
964 return(0);
965 if (my_init_dynamic_array(&new_error->msg, sizeof(struct message), 0, 1,
966 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(default_language, MYF(MY_WME))) ||
978 (!skip && !(message.text= my_strdup("", MYF(MY_WME)))))
979 return(0);
980
981 /* Can't fail as msg is preallocated */
982 (void) insert_dynamic(&new_error->msg, (uchar*) &message);
983 return(new_error);
984 }
985
986
987 /*
988 Parsing the string with error name and codes; returns the pointer to
989 the errors struct
990 */
991
parse_error_string(char * str,int er_count)992 static struct errors *parse_error_string(char *str, int er_count)
993 {
994 struct errors *new_error;
995 DBUG_ENTER("parse_error_string");
996 DBUG_PRINT("enter", ("str: %s", str));
997
998 /* create a new element */
999 if (!(new_error= (struct errors *) my_malloc(sizeof(*new_error),
1000 MYF(MY_WME))))
1001 DBUG_RETURN(0);
1002
1003 new_error->next_error= 0;
1004 if (my_init_dynamic_array(&new_error->msg, sizeof(struct message), 0, 0, MYF(0)))
1005 DBUG_RETURN(0); /* OOM: Fatal error */
1006
1007 /* getting the error name */
1008 str= skip_delimiters(str);
1009
1010 if (!(new_error->er_name= get_word(&str)))
1011 DBUG_RETURN(0); /* OOM: Fatal error */
1012 DBUG_PRINT("info", ("er_name: %s", new_error->er_name));
1013
1014 str= skip_delimiters(str);
1015
1016 /* getting the code1 */
1017
1018 new_error->d_code= er_offset + er_count;
1019 DBUG_PRINT("info", ("d_code: %d", new_error->d_code));
1020
1021 str= skip_delimiters(str);
1022
1023 /* if we reached EOL => no more codes, but this can happen */
1024 if (!*str)
1025 {
1026 new_error->sql_code1= empty_string;
1027 new_error->sql_code2= empty_string;
1028 DBUG_PRINT("info", ("str: %s", str));
1029 DBUG_RETURN(new_error);
1030 }
1031
1032 /* getting the sql_code 1 */
1033
1034 if (!(new_error->sql_code1= get_word(&str)))
1035 DBUG_RETURN(0); /* OOM: Fatal error */
1036 DBUG_PRINT("info", ("sql_code1: %s", new_error->sql_code1));
1037
1038 str= skip_delimiters(str);
1039
1040 /* if we reached EOL => no more codes, but this can happen */
1041 if (!*str)
1042 {
1043 new_error->sql_code2= empty_string;
1044 DBUG_PRINT("info", ("str: %s", str));
1045 DBUG_RETURN(new_error);
1046 }
1047
1048 /* getting the sql_code 2 */
1049 if (!(new_error->sql_code2= get_word(&str)))
1050 DBUG_RETURN(0); /* OOM: Fatal error */
1051 DBUG_PRINT("info", ("sql_code2: %s", new_error->sql_code2));
1052
1053 str= skip_delimiters(str);
1054 if (*str)
1055 {
1056 fprintf(stderr, "The error line did not end with sql/odbc code!");
1057 DBUG_RETURN(0);
1058 }
1059
1060 DBUG_RETURN(new_error);
1061 }
1062
1063
1064 /*
1065 Parsing the string with full lang name/short lang name/charset;
1066 returns pointer to the language structure
1067 */
1068
parse_charset_string(char * str)1069 static struct languages *parse_charset_string(char *str)
1070 {
1071 struct languages *head=0, *new_lang;
1072 DBUG_ENTER("parse_charset_string");
1073 DBUG_PRINT("enter", ("str: %s", str));
1074
1075 /* skip over keyword */
1076 str= find_end_of_word(str);
1077 if (!*str)
1078 {
1079 /* unexpected EOL */
1080 DBUG_PRINT("info", ("str: %s", str));
1081 DBUG_RETURN(0);
1082 }
1083
1084 str= skip_delimiters(str);
1085 if (!(*str != ';' && *str))
1086 DBUG_RETURN(0);
1087
1088 do
1089 {
1090 /*creating new element of the linked list */
1091 new_lang= (struct languages *) my_malloc(sizeof(*new_lang), MYF(MY_WME));
1092 new_lang->next_lang= head;
1093 head= new_lang;
1094
1095 /* get the full language name */
1096
1097 if (!(new_lang->lang_long_name= get_word(&str)))
1098 DBUG_RETURN(0); /* OOM: Fatal error */
1099
1100 DBUG_PRINT("info", ("long_name: %s", new_lang->lang_long_name));
1101
1102 /* getting the short name for language */
1103 str= skip_delimiters(str);
1104 if (!*str)
1105 DBUG_RETURN(0); /* Error: No space or tab */
1106
1107 if (!(new_lang->lang_short_name= get_word(&str)))
1108 DBUG_RETURN(0); /* OOM: Fatal error */
1109 DBUG_PRINT("info", ("short_name: %s", new_lang->lang_short_name));
1110
1111 /* getting the charset name */
1112 str= skip_delimiters(str);
1113 if (!(new_lang->charset= get_word(&str)))
1114 DBUG_RETURN(0); /* Fatal error */
1115 DBUG_PRINT("info", ("charset: %s", new_lang->charset));
1116
1117 /* skipping space, tab or "," */
1118 str= skip_delimiters(str);
1119 }
1120 while (*str != ';' && *str);
1121
1122 DBUG_PRINT("info", ("long name: %s", new_lang->lang_long_name));
1123 DBUG_RETURN(head);
1124 }
1125
1126
1127 /* Read options */
1128
print_version(void)1129 static void print_version(void)
1130 {
1131 DBUG_ENTER("print_version");
1132 printf("%s (Compile errormessage) Ver %s\n", my_progname, "3.0");
1133 DBUG_VOID_RETURN;
1134 }
1135
1136
1137 static my_bool
get_one_option(int optid,const struct my_option * opt,char * argument)1138 get_one_option(int optid, const struct my_option *opt __attribute__ ((unused)),
1139 char *argument __attribute__ ((unused)))
1140 {
1141 DBUG_ENTER("get_one_option");
1142 switch (optid) {
1143 case 'V':
1144 print_version();
1145 my_end(0);
1146 exit(0);
1147 break;
1148 case '?':
1149 usage();
1150 my_end(0);
1151 exit(0);
1152 break;
1153 case '#':
1154 DBUG_PUSH(argument ? argument : default_dbug_option);
1155 break;
1156 }
1157 DBUG_RETURN(0);
1158 }
1159
1160
usage(void)1161 static void usage(void)
1162 {
1163 DBUG_ENTER("usage");
1164 print_version();
1165 printf("This software comes with ABSOLUTELY NO WARRANTY. "
1166 "This is free software,\n"
1167 "and you are welcome to modify and redistribute it under the GPL license.\n"
1168 "Usage:\n");
1169 my_print_help(my_long_options);
1170 my_print_variables(my_long_options);
1171 DBUG_VOID_RETURN;
1172 }
1173
1174
get_options(int * argc,char *** argv)1175 static int get_options(int *argc, char ***argv)
1176 {
1177 int ho_error;
1178 DBUG_ENTER("get_options");
1179
1180 if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
1181 DBUG_RETURN(ho_error);
1182 DBUG_RETURN(0);
1183 }
1184
1185
1186 /*
1187 Read rows and remember them until row that start with char Converts
1188 row as a C-compiler would convert a textstring
1189 */
1190
parse_text_line(char * pos)1191 static char *parse_text_line(char *pos)
1192 {
1193 int i, nr;
1194 char *row= pos;
1195 size_t len;
1196 DBUG_ENTER("parse_text_line");
1197
1198 len= strlen (pos);
1199 while (*pos)
1200 {
1201 if (*pos == '\\')
1202 {
1203 switch (*++pos) {
1204 case '\\':
1205 case '"':
1206 (void) memmove (pos - 1, pos, len - (row - pos));
1207 break;
1208 case 'n':
1209 pos[-1]= '\n';
1210 (void) memmove (pos, pos + 1, len - (row - pos));
1211 break;
1212 default:
1213 if (*pos >= '0' && *pos < '8')
1214 {
1215 nr= 0;
1216 for (i= 0; i < 3 && (*pos >= '0' && *pos < '8'); i++)
1217 nr= nr * 8 + (*(pos++) - '0');
1218 pos -= i;
1219 pos[-1]= nr;
1220 (void) memmove (pos, pos + i, len - (row - pos));
1221 }
1222 else if (*pos)
1223 (void) memmove (pos - 1, pos, len - (row - pos)); /* Remove '\' */
1224 }
1225 }
1226 else
1227 pos++;
1228 }
1229 while (pos > row + 1 && *pos != '"')
1230 pos--;
1231 *pos= 0;
1232 DBUG_RETURN(pos);
1233 }
1234
1235
1236 /* Copy rows from memory to file and remember position */
1237
copy_rows(FILE * to,char * row,int row_nr,long start_pos)1238 static int copy_rows(FILE *to, char *row, int row_nr, long start_pos)
1239 {
1240 DBUG_ENTER("copy_rows");
1241
1242 file_pos[row_nr]= (int) (ftell(to) - start_pos);
1243 if (fputs(row, to) == EOF || fputc('\0', to) == EOF)
1244 {
1245 fprintf(stderr, "Can't write to outputfile\n");
1246 DBUG_RETURN(1);
1247 }
1248
1249 DBUG_RETURN(0);
1250 }
1251