1
/* Copyright (C) 2004 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
Written by Anjuta Widenius
21
Creates one include file and multiple language-error message files from one
22
multi-language text file.
25
#include <drizzled/global.h>
26
#include <mystrings/m_ctype.h>
27
#include <mysys/my_sys.h>
28
#include <mystrings/m_string.h>
29
#include <mysys/my_getopt.h>
33
#define HEADER_LENGTH 32 /* Length of header in errmsg.sys */
34
#define DEFAULT_CHARSET_DIR "../sql/share/charsets"
35
#define ER_PREFIX "ER_"
36
#define WARN_PREFIX "WARN_"
37
static char *OUTFILE= (char*) "errmsg.sys";
38
static char *HEADERFILE= (char*) "drizzled_error.h";
39
static char *NAMEFILE= (char*) "drizzled_ername.h";
40
static char *STATEFILE= (char*) "sql_state.h";
41
static char *TXTFILE= (char*) "../sql/share/errmsg.txt";
42
static char *DATADIRECTORY= (char*) "../sql/share/";
44
/* Header for errmsg.sys files */
45
uchar file_head[]= { 254, 254, 2, 1 };
46
/* Store positions to each error message row to store in errmsg.sys header */
47
uint file_pos[MAX_ROWS];
49
const char *empty_string= ""; /* For empty states */
51
Default values for command line options. See getopt structure for definitions
55
const char *default_language= "eng";
59
/* Storage of one error message row (for one language) */
63
char *lang_short_name;
68
/* Storage for languages and charsets (from start of error text file) */
72
char *lang_long_name; /* full name of the language */
73
char *lang_short_name; /* abbreviation of the lang. */
74
char *charset; /* Character set name */
75
struct languages *next_lang; /* Pointer to next language */
79
/* Name, code and texts (for all lang) for one error message */
83
const char *er_name; /* Name of the error (ER_HASHCK) */
84
int d_code; /* Error code number */
85
const char *sql_code1; /* sql state */
86
const char *sql_code2; /* ODBC state */
87
struct errors *next_error; /* Pointer to next error */
88
DYNAMIC_ARRAY msg; /* All language texts for this error */
92
static struct my_option my_long_options[]=
94
{"debug-info", 'T', "Print some debug info at exit.", (char**) & info_flag,
95
(char**) & info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
96
{"help", '?', "Displays this help and exits.", 0, 0, 0, GET_NO_ARG,
97
NO_ARG, 0, 0, 0, 0, 0, 0},
98
{"version", 'V', "Prints version", 0, 0, 0, GET_NO_ARG,
99
NO_ARG, 0, 0, 0, 0, 0, 0},
100
{"charset", 'C', "Charset dir", (char**) & charsets_dir,
101
(char**) & charsets_dir,
102
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
103
{"in_file", 'F', "Input file", (char**) & TXTFILE, (char**) & TXTFILE,
104
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
105
{"out_dir", 'D', "Output base directory", (char**) & DATADIRECTORY,
106
(char**) & DATADIRECTORY,
107
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
108
{"out_file", 'O', "Output filename (errmsg.sys)", (char**) & OUTFILE,
109
(char**) & OUTFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
110
{"header_file", 'H', "drizzled_error.h file ", (char**) & HEADERFILE,
111
(char**) & HEADERFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
112
{"name_file", 'N', "drizzled_ername.h file ", (char**) & NAMEFILE,
113
(char**) & NAMEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
114
{"state_file", 'S', "sql_state.h file", (char**) & STATEFILE,
115
(char**) & STATEFILE, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
116
{0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
120
static struct languages *parse_charset_string(char *str);
121
static struct errors *parse_error_string(char *ptr, int er_count);
122
static struct message *parse_message_string(struct message *new_message,
124
static struct message *find_message(struct errors *err, const char *lang,
126
static int check_message_format(struct errors *err,
128
static int parse_input_file(const char *file_name, struct errors **top_error,
129
struct languages **top_language);
130
static int get_options(int *argc, char ***argv);
131
static void print_version(void);
132
static void usage(void);
133
static bool get_one_option(int optid, const struct my_option *opt,
135
static char *parse_text_line(char *pos);
136
static int copy_rows(FILE * to, char *row, int row_nr, long start_pos);
137
static char *parse_default_language(char *str);
138
static uint parse_error_offset(char *str);
140
static char *skip_delimiters(char *str);
141
static char *get_word(char **str);
142
static char *find_end_of_word(char *str);
143
static void clean_up(struct languages *lang_head, struct errors *error_head);
144
static int create_header_files(struct errors *error_head);
145
static int create_sys_files(struct languages *lang_head,
146
struct errors *error_head, uint row_count);
149
int main(int argc, char *argv[])
154
struct errors *error_head;
155
struct languages *lang_head;
157
charsets_dir= DEFAULT_CHARSET_DIR;
159
if (get_options(&argc, &argv))
161
if (!(row_count= parse_input_file(TXTFILE, &error_head, &lang_head)))
163
fprintf(stderr, "Failed to parse input file %s\n", TXTFILE);
166
if (lang_head == NULL || error_head == NULL)
168
fprintf(stderr, "Failed to parse input file %s\n", TXTFILE);
172
if (create_header_files(error_head))
174
fprintf(stderr, "Failed to create header files\n");
177
if (create_sys_files(lang_head, error_head, row_count))
179
fprintf(stderr, "Failed to create sys files\n");
182
clean_up(lang_head, error_head);
183
my_end(info_flag ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
189
static int create_header_files(struct errors *error_head)
192
FILE *er_definef, *sql_statef, *er_namef;
193
struct errors *tmp_error;
195
if (!(er_definef= my_fopen(HEADERFILE, O_WRONLY, MYF(MY_WME))))
199
if (!(sql_statef= my_fopen(STATEFILE, O_WRONLY, MYF(MY_WME))))
201
my_fclose(er_definef, MYF(0));
204
if (!(er_namef= my_fopen(NAMEFILE, O_WRONLY, MYF(MY_WME))))
206
my_fclose(er_definef, MYF(0));
207
my_fclose(sql_statef, MYF(0));
211
fprintf(er_definef, "/* Autogenerated file, please don't edit */\n\n");
212
fprintf(sql_statef, "/* Autogenerated file, please don't edit */\n\n");
213
fprintf(er_namef, "/* Autogenerated file, please don't edit */\n\n");
215
fprintf(er_definef, "#define ER_ERROR_FIRST %d\n", error_head->d_code);
217
for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error)
220
generating drizzled_error.h
221
fprintf() will automatically add \r on windows
223
fprintf(er_definef, "#define %s %d\n", tmp_error->er_name,
225
er_last= tmp_error->d_code;
227
/* generating sql_state.h file */
228
if (tmp_error->sql_code1[0] || tmp_error->sql_code2[0])
230
"{ %-40s,\"%s\", \"%s\" },\n", tmp_error->er_name,
231
tmp_error->sql_code1, tmp_error->sql_code2);
232
/*generating er_name file */
233
fprintf(er_namef, "{ \"%s\", %d },\n", tmp_error->er_name,
237
/* finishing off with drizzled_error.h */
238
fprintf(er_definef, "#define ER_ERROR_LAST %d\n", er_last);
239
my_fclose(er_definef, MYF(0));
240
my_fclose(sql_statef, MYF(0));
241
my_fclose(er_namef, MYF(0));
246
static int create_sys_files(struct languages *lang_head,
247
struct errors *error_head, uint row_count)
250
uint csnum= 0, length, i, row_nr;
252
char outfile[FN_REFLEN], *outfile_end;
255
struct languages *tmp_lang;
256
struct errors *tmp_error;
258
struct stat stat_info;
261
going over all languages and assembling corresponding error messages
263
for (tmp_lang= lang_head; tmp_lang; tmp_lang= tmp_lang->next_lang)
266
/* setting charset name */
267
if (!(csnum= get_charset_number(tmp_lang->charset, MY_CS_PRIMARY)))
269
fprintf(stderr, "Unknown charset '%s' in '%s'\n", tmp_lang->charset,
274
outfile_end= strxmov(outfile, DATADIRECTORY,
275
tmp_lang->lang_long_name, NullS);
276
if (stat(outfile, &stat_info))
278
if (my_mkdir(outfile, 0777,MYF(0)) < 0)
280
fprintf(stderr, "Can't create output directory for %s\n",
286
strxmov(outfile_end, FN_ROOTDIR, OUTFILE, NullS);
288
if (!(to= my_fopen(outfile, O_WRONLY | FILE_BINARY, MYF(MY_WME))))
291
/* 2 is for 2 bytes to store row position / error message */
292
start_pos= (long) (HEADER_LENGTH + row_count * 2);
293
fseek(to, start_pos, 0);
295
for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error)
297
/* dealing with messages */
298
tmp= find_message(tmp_error, tmp_lang->lang_short_name, false);
303
"Did not find message for %s neither in %s nor in default "
304
"language\n", tmp_error->er_name, tmp_lang->lang_short_name);
307
if (copy_rows(to, tmp->text, row_nr, start_pos))
309
fprintf(stderr, "Failed to copy rows to %s\n", outfile);
315
/* continue with header of the errmsg.sys file */
316
length= ftell(to) - HEADER_LENGTH - row_count * 2;
317
memset((uchar*) head, 0, HEADER_LENGTH);
318
bmove((uchar *) head, (uchar *) file_head, 4);
320
int2store(head + 6, length);
321
int2store(head + 8, row_count);
324
my_fseek(to, 0l, MY_SEEK_SET, MYF(0));
325
if (my_fwrite(to, (uchar*) head, HEADER_LENGTH, MYF(MY_WME | MY_FNABP)))
328
for (i= 0; i < row_count; i++)
330
int2store(head, file_pos[i]);
331
if (my_fwrite(to, (uchar*) head, 2, MYF(MY_WME | MY_FNABP)))
334
my_fclose(to, MYF(0));
339
my_fclose(to, MYF(0));
344
static void clean_up(struct languages *lang_head, struct errors *error_head)
346
struct languages *tmp_lang, *next_language;
347
struct errors *tmp_error, *next_error;
350
my_free((uchar*) default_language, MYF(0));
352
for (tmp_lang= lang_head; tmp_lang; tmp_lang= next_language)
354
next_language= tmp_lang->next_lang;
355
my_free(tmp_lang->lang_short_name, MYF(0));
356
my_free(tmp_lang->lang_long_name, MYF(0));
357
my_free(tmp_lang->charset, MYF(0));
358
my_free((uchar*) tmp_lang, MYF(0));
361
for (tmp_error= error_head; tmp_error; tmp_error= next_error)
363
next_error= tmp_error->next_error;
364
count= (tmp_error->msg).elements;
365
for (i= 0; i < count; i++)
368
tmp= dynamic_element(&tmp_error->msg, i, struct message*);
369
my_free((uchar*) tmp->lang_short_name, MYF(0));
370
my_free((uchar*) tmp->text, MYF(0));
373
delete_dynamic(&tmp_error->msg);
374
if (tmp_error->sql_code1[0])
375
my_free((uchar*) tmp_error->sql_code1, MYF(0));
376
if (tmp_error->sql_code2[0])
377
my_free((uchar*) tmp_error->sql_code2, MYF(0));
378
my_free((uchar*) tmp_error->er_name, MYF(0));
379
my_free((uchar*) tmp_error, MYF(0));
384
static int parse_input_file(const char *file_name, struct errors **top_error,
385
struct languages **top_lang)
388
char *str, buff[1000];
389
struct errors *current_error= 0, **tail_error= top_error;
390
struct message current_message;
395
if (!(file= my_fopen(file_name, O_RDONLY | O_SHARE, MYF(MY_WME))))
398
while ((str= fgets(buff, sizeof(buff), file)))
400
if (is_prefix(str, "language"))
402
if (!(*top_lang= parse_charset_string(str)))
404
fprintf(stderr, "Failed to parse the charset string!\n");
409
if (is_prefix(str, "start-error-number"))
411
if (!(er_offset= parse_error_offset(str)))
413
fprintf(stderr, "Failed to parse the error offset string!\n");
418
if (is_prefix(str, "default-language"))
420
if (!(default_language= parse_default_language(str)))
423
"Failed to parse the default language line. Aborting\n");
429
if (*str == '\t' || *str == ' ')
431
/* New error message in another language for previous error */
434
fprintf(stderr, "Error in the input file format\n");
437
if (!parse_message_string(¤t_message, str))
439
fprintf(stderr, "Failed to parse message string for error '%s'",
440
current_error->er_name);
443
if (find_message(current_error, current_message.lang_short_name, true))
445
fprintf(stderr, "Duplicate message string for error '%s'"
446
" in language '%s'\n",
447
current_error->er_name, current_message.lang_short_name);
450
if (check_message_format(current_error, current_message.text))
452
fprintf(stderr, "Wrong formatspecifier of error message string"
453
" for error '%s' in language '%s'\n",
454
current_error->er_name, current_message.lang_short_name);
457
if (insert_dynamic(¤t_error->msg, (uchar *) & current_message))
461
if (is_prefix(str, ER_PREFIX) || is_prefix(str, WARN_PREFIX))
463
if (!(current_error= parse_error_string(str, rcount)))
465
fprintf(stderr, "Failed to parse the error name string\n");
468
rcount++; /* Count number of unique errors */
470
/* add error to the list */
471
*tail_error= current_error;
472
tail_error= ¤t_error->next_error;
475
if (*str == '#' || *str == '\n')
476
continue; /* skip comment or empty lines */
478
fprintf(stderr, "Wrong input file format. Stop!\nLine: %s\n", str);
481
*tail_error= 0; /* Mark end of list */
483
my_fclose(file, MYF(0));
488
static uint parse_error_offset(char *str)
494
/* skipping the "start-error-number" keyword and spaces after it */
495
str= find_end_of_word(str);
496
str= skip_delimiters(str);
499
return(0); /* Unexpected EOL: No error number after the keyword */
501
/* reading the error offset */
502
if (!(soffset= get_word(&str)))
503
return(0); /* OOM: Fatal error */
505
/* skipping space(s) and/or tabs after the error offset */
506
str= skip_delimiters(str);
509
/* The line does not end with the error offset -> error! */
510
fprintf(stderr, "The error offset line does not end with an error offset");
515
ioffset= (uint) my_strtoll10(soffset, &end, &error);
516
my_free((uchar*) soffset, MYF(0));
521
/* Parsing of the default language line. e.g. "default-language eng" */
523
static char *parse_default_language(char *str)
527
/* skipping the "default-language" keyword */
528
str= find_end_of_word(str);
529
/* skipping space(s) and/or tabs after the keyword */
530
str= skip_delimiters(str);
534
"Unexpected EOL: No short language name after the keyword\n");
538
/* reading the short language tag */
539
if (!(slang= get_word(&str)))
540
return(0); /* OOM: Fatal error */
542
str= skip_delimiters(str);
546
"The default language line does not end with short language "
555
Find the message in a particular language
559
err Error to find message for
560
lang Language of message to find
561
no_default Don't return default (English) if does not exit
564
Returns the message structure if one is found, or NULL if not.
566
static struct message *find_message(struct errors *err, const char *lang,
569
struct message *tmp, *return_val= 0;
572
count= (err->msg).elements;
573
for (i= 0; i < count; i++)
575
tmp= dynamic_element(&err->msg, i, struct message*);
577
if (!strcmp(tmp->lang_short_name, lang))
579
if (!strcmp(tmp->lang_short_name, default_language))
581
assert(tmp->text[0] != 0);
585
return(no_default ? NULL : return_val);
591
Check message format specifiers against error message for
595
checksum_format_specifier()
596
msg String for which to generate checksum
597
for the format specifiers
600
Returns the checksum for all the characters of the
604
"text '%-64.s' text part 2 %d'"
606
characters will be xored to form checksum
609
Does not support format specifiers with positional args
613
static ha_checksum checksum_format_specifier(const char* msg)
615
ha_checksum chksum= 0;
616
const uchar* p= (uchar *)msg;
617
const uchar* start= 0;
618
int num_format_specifiers= 0;
624
start= p+1; /* Entering format specifier */
625
num_format_specifiers++;
635
chksum= my_checksum(chksum, start, p-start);
636
start= 0; /* Not in format specifier anymore */
649
/* Still inside a format specifier after end of string */
651
fprintf(stderr, "Still inside formatspecifier after end of string"
656
/* Add number of format specifiers to checksum as extra safeguard */
657
chksum+= num_format_specifiers;
664
Check message format specifiers against error message for
668
check_message_format()
669
err Error to check message for
670
mess Message to check
673
Returns 0 if no previous error message or message format is ok
675
static int check_message_format(struct errors *err,
678
struct message *first;
680
/* Get first message(if any) */
681
if ((err->msg).elements == 0)
682
return(0); /* No previous message to compare against */
684
first= dynamic_element(&err->msg, 0, struct message*);
685
assert(first != NULL);
687
if (checksum_format_specifier(first->text) !=
688
checksum_format_specifier(mess))
690
/* Check sum of format specifiers failed, they should be equal */
698
Skips spaces and or tabs till the beginning of the next word
699
Returns pointer to the beginning of the first character of the word
702
static char *skip_delimiters(char *str)
705
*str == ' ' || *str == ',' || *str == '\t' || *str == '\r' ||
706
*str == '\n' || *str == '='; str++)
713
Skips all characters till meets with space, or tab, or EOL
716
static char *find_end_of_word(char *str)
719
*str != ' ' && *str != '\t' && *str != '\n' && *str != '\r' && *str &&
720
*str != ',' && *str != ';' && *str != '='; str++)
726
/* Read the word starting from *str */
728
static char *get_word(char **str)
732
*str= find_end_of_word(start);
733
return(my_strndup(start, (uint) (*str - start),
734
MYF(MY_WME | MY_FAE)));
739
Parsing the string with short_lang - message text. Code - to
740
remember to which error does the text belong
743
static struct message *parse_message_string(struct message *new_message,
748
/*skip space(s) and/or tabs in the beginning */
749
while (*str == ' ' || *str == '\t' || *str == '\n')
754
/* It was not a message line, but an empty line. */
758
/* reading the short lang */
760
while (*str != ' ' && *str != '\t' && *str)
762
if (!(new_message->lang_short_name=
763
my_strndup(start, (uint) (str - start),
764
MYF(MY_WME | MY_FAE))))
765
return(0); /* Fatal error */
767
/*skip space(s) and/or tabs after the lang */
768
while (*str == ' ' || *str == '\t' || *str == '\n')
773
fprintf(stderr, "Unexpected EOL");
777
/* reading the text */
779
str= parse_text_line(start);
781
if (!(new_message->text= my_strndup(start, (uint) (str - start),
782
MYF(MY_WME | MY_FAE))))
783
return(0); /* Fatal error */
790
Parsing the string with error name and codes; returns the pointer to
794
static struct errors *parse_error_string(char *str, int er_count)
796
struct errors *new_error;
799
/* create a new element */
800
new_error= (struct errors *) my_malloc(sizeof(*new_error), MYF(MY_WME));
802
if (my_init_dynamic_array(&new_error->msg, sizeof(struct message), 0, 0))
803
return(0); /* OOM: Fatal error */
805
/* getting the error name */
807
str= skip_delimiters(str);
809
if (!(new_error->er_name= get_word(&str)))
810
return(0); /* OOM: Fatal error */
812
str= skip_delimiters(str);
814
/* getting the code1 */
816
new_error->d_code= er_offset + er_count;
818
str= skip_delimiters(str);
820
/* if we reached EOL => no more codes, but this can happen */
823
new_error->sql_code1= empty_string;
824
new_error->sql_code2= empty_string;
828
/* getting the sql_code 1 */
830
if (!(new_error->sql_code1= get_word(&str)))
831
return(0); /* OOM: Fatal error */
833
str= skip_delimiters(str);
835
/* if we reached EOL => no more codes, but this can happen */
838
new_error->sql_code2= empty_string;
842
/* getting the sql_code 2 */
843
if (!(new_error->sql_code2= get_word(&str)))
844
return(0); /* OOM: Fatal error */
846
str= skip_delimiters(str);
849
fprintf(stderr, "The error line did not end with sql/odbc code!");
858
Parsing the string with full lang name/short lang name/charset;
859
returns pointer to the language structure
862
static struct languages *parse_charset_string(char *str)
864
struct languages *head=0, *new_lang;
866
/* skip over keyword */
867
str= find_end_of_word(str);
874
str= skip_delimiters(str);
875
if (!(*str != ';' && *str))
880
/*creating new element of the linked list */
881
new_lang= (struct languages *) my_malloc(sizeof(*new_lang), MYF(MY_WME));
882
new_lang->next_lang= head;
885
/* get the full language name */
887
if (!(new_lang->lang_long_name= get_word(&str)))
888
return(0); /* OOM: Fatal error */
890
/* getting the short name for language */
891
str= skip_delimiters(str);
893
return(0); /* Error: No space or tab */
895
if (!(new_lang->lang_short_name= get_word(&str)))
896
return(0); /* OOM: Fatal error */
898
/* getting the charset name */
899
str= skip_delimiters(str);
900
if (!(new_lang->charset= get_word(&str)))
901
return(0); /* Fatal error */
903
/* skipping space, tab or "," */
904
str= skip_delimiters(str);
906
while (*str != ';' && *str);
914
static void print_version(void)
916
printf("%s (Compile errormessage) Ver %s\n", my_progname, "2.0");
922
get_one_option(int optid, const struct my_option *opt __attribute__ ((unused)),
923
char *argument __attribute__ ((unused)))
939
static void usage(void)
942
printf("This software comes with ABSOLUTELY NO WARRANTY. "
943
"This is free software,\n"
944
"and you are welcome to modify and redistribute it under the GPL license.\n"
946
my_print_help(my_long_options);
947
my_print_variables(my_long_options);
952
static int get_options(int *argc, char ***argv)
956
if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
963
Read rows and remember them until row that start with char Converts
964
row as a C-compiler would convert a textstring
967
static char *parse_text_line(char *pos)
979
VOID(strmov(pos - 1, pos));
983
VOID(strmov(pos, pos + 1));
986
if (*pos >= '0' && *pos < '8')
989
for (i= 0; i < 3 && (*pos >= '0' && *pos < '8'); i++)
990
nr= nr * 8 + (*(pos++) - '0');
993
VOID(strmov(pos, pos + i));
996
VOID(strmov(pos - 1, pos)); /* Remove '\' */
1002
while (pos > row + 1 && *pos != '"')
1009
/* Copy rows from memory to file and remember position */
1011
static int copy_rows(FILE *to, char *row, int row_nr, long start_pos)
1014
file_pos[row_nr]= (int) (ftell(to) - start_pos);
1015
if (fputs(row, to) == EOF || fputc('\0', to) == EOF)
1017
fprintf(stderr, "Can't write to outputfile\n");