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