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 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 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 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 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 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 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 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 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 */ 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 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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