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= &current_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(&current_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(&current_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= &current_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