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