~drizzle-trunk/drizzle/development

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