~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to extra/comp_err.c

  • Committer: Brian Aker
  • Date: 2008-07-20 07:10:24 UTC
  • Revision ID: brian@tangent.org-20080720071024-o1wv37gyy8ln5xgk
More my_bool cleanup.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
{
 
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},
 
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},
 
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},
 
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);
 
134
static bool get_one_option(int optid, const struct my_option *opt,
 
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))
 
161
      return(1);
 
162
    if (!(row_count= parse_input_file(TXTFILE, &error_head, &lang_head)))
 
163
    {
 
164
      fprintf(stderr, "Failed to parse input file %s\n", TXTFILE);
 
165
      return(1);
 
166
    }
 
167
    if (lang_head == NULL || error_head == NULL)
 
168
    {
 
169
      fprintf(stderr, "Failed to parse input file %s\n", TXTFILE);
 
170
      return(1);
 
171
    }
 
172
 
 
173
    if (create_header_files(error_head))
 
174
    {
 
175
      fprintf(stderr, "Failed to create header files\n");
 
176
      return(1);
 
177
    }
 
178
    if (create_sys_files(lang_head, error_head, row_count))
 
179
    {
 
180
      fprintf(stderr, "Failed to create sys files\n");
 
181
      return(1);
 
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
  {
 
198
    return(1);
 
199
  }
 
200
  if (!(sql_statef= my_fopen(STATEFILE, O_WRONLY, MYF(MY_WME))))
 
201
  {
 
202
    my_fclose(er_definef, MYF(0));
 
203
    return(1);
 
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));
 
209
    return(1);
 
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));
 
243
  return(0);
 
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
 
 
259
  struct stat stat_info;
 
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);
 
272
      return(1);
 
273
    }
 
274
 
 
275
    outfile_end= strxmov(outfile, DATADIRECTORY, 
 
276
                         tmp_lang->lang_long_name, NullS);
 
277
    if (stat(outfile, &stat_info))
 
278
    {
 
279
      if (my_mkdir(outfile, 0777,MYF(0)) < 0)
 
280
      {
 
281
        fprintf(stderr, "Can't create output directory for %s\n", 
 
282
                outfile);
 
283
        return(1);
 
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))))
 
290
      return(1);
 
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 */
 
299
      tmp= find_message(tmp_error, tmp_lang->lang_short_name, false);
 
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
  }
 
337
  return(0);
 
338
 
 
339
err:
 
340
  my_fclose(to, MYF(0));
 
341
  return(1);
 
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))))
 
397
    return(0);
 
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");
 
406
        return(0);
 
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");
 
415
        return(0);
 
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");
 
425
        return(0);
 
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");
 
436
        return(0);
 
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);
 
442
        return(0);
 
443
      }
 
444
      if (find_message(current_error, current_message.lang_short_name, true))
 
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);
 
449
        return(0);
 
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);
 
456
        return(0);
 
457
      }
 
458
      if (insert_dynamic(&current_error->msg, (uchar *) & current_message))
 
459
        return(0);
 
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");
 
467
        return(0);
 
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);
 
480
    return(0);
 
481
  }
 
482
  *tail_error= 0;                               /* Mark end of list */
 
483
 
 
484
  my_fclose(file, MYF(0));
 
485
  return(rcount);
 
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)
 
500
    return(0);     /* Unexpected EOL: No error number after the keyword */
 
501
 
 
502
  /* reading the error offset */
 
503
  if (!(soffset= get_word(&str)))
 
504
    return(0);                          /* OOM: Fatal error */
 
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");
 
512
    return(0);
 
513
  }
 
514
 
 
515
  end= 0;
 
516
  ioffset= (uint) my_strtoll10(soffset, &end, &error);
 
517
  my_free((uchar*) soffset, MYF(0));
 
518
  return(ioffset);
 
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");
 
536
    return(0);
 
537
  }
 
538
 
 
539
  /* reading the short language tag */
 
540
  if (!(slang= get_word(&str)))
 
541
    return(0);                          /* OOM: Fatal error */
 
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");
 
549
    return(0);
 
550
  }
 
551
  return(slang);
 
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))
 
579
      return(tmp);
 
580
    if (!strcmp(tmp->lang_short_name, default_language))
 
581
    {
 
582
      assert(tmp->text[0] != 0);
 
583
      return_val= tmp;
 
584
    }
 
585
  }
 
586
  return(no_default ? NULL : return_val);
 
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
 
611
      like "%2$s" 
 
612
*/
 
613
 
 
614
static ha_checksum checksum_format_specifier(const char* msg)
 
615
{
 
616
  ha_checksum chksum= 0;
 
617
  const uchar* p= (uchar *)msg;
 
618
  const uchar* start= 0;
 
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);
 
654
    assert(start==0);
 
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)
 
683
    return(0); /* No previous message to compare against */
 
684
 
 
685
  first= dynamic_element(&err->msg, 0, struct message*);
 
686
  assert(first != NULL);
 
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 */
 
692
    return(1);
 
693
  }
 
694
  return(0);
 
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
    ;
 
709
  return(str);
 
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
    ;
 
723
  return(str);
 
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);
 
734
  return(my_strndup(start, (uint) (*str - start),
 
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. */
 
756
    return(0);
 
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))))
 
766
    return(0);                          /* Fatal error */
 
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");
 
775
    return(0);
 
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))))
 
784
    return(0);                          /* Fatal error */
 
785
 
 
786
  return(new_message);
 
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))
 
804
    return(0);                          /* OOM: Fatal error */
 
805
 
 
806
  /* getting the error name */
 
807
  start= str;
 
808
  str= skip_delimiters(str);
 
809
 
 
810
  if (!(new_error->er_name= get_word(&str)))
 
811
    return(0);                          /* OOM: Fatal error */
 
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;
 
826
    return(new_error);
 
827
  }
 
828
 
 
829
  /* getting the sql_code 1 */
 
830
 
 
831
  if (!(new_error->sql_code1= get_word(&str)))
 
832
    return(0);                          /* OOM: Fatal error */
 
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;
 
840
    return(new_error);
 
841
  }
 
842
 
 
843
  /* getting the sql_code 2 */
 
844
  if (!(new_error->sql_code2= get_word(&str)))
 
845
    return(0);                          /* OOM: Fatal error */
 
846
 
 
847
  str= skip_delimiters(str);
 
848
  if (*str)
 
849
  {
 
850
    fprintf(stderr, "The error line did not end with sql/odbc code!");
 
851
    return(0);
 
852
  }
 
853
 
 
854
  return(new_error);
 
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 */
 
872
    return(0);
 
873
  }
 
874
 
 
875
  str= skip_delimiters(str);
 
876
  if (!(*str != ';' && *str))
 
877
    return(0);
 
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)))
 
889
      return(0);                                /* OOM: Fatal error */
 
890
 
 
891
    /* getting the short name for language */
 
892
    str= skip_delimiters(str);
 
893
    if (!*str)
 
894
      return(0);                                /* Error: No space or tab */
 
895
 
 
896
    if (!(new_lang->lang_short_name= get_word(&str)))
 
897
      return(0);                                /* OOM: Fatal error */
 
898
 
 
899
    /* getting the charset name */
 
900
    str= skip_delimiters(str);
 
901
    if (!(new_lang->charset= get_word(&str)))
 
902
      return(0);                                /* Fatal error */
 
903
 
 
904
    /* skipping space, tab or "," */
 
905
    str= skip_delimiters(str);
 
906
  }
 
907
  while (*str != ';' && *str);
 
908
 
 
909
  return(head);
 
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");
 
918
  return;
 
919
}
 
920
 
 
921
 
 
922
static bool
 
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
  }
 
936
  return(0);
 
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);
 
949
  return;
 
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)))
 
958
    return(ho_error);
 
959
  return(0);
 
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;
 
1006
  return(pos);
 
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");
 
1019
    return(1);
 
1020
  }
 
1021
 
 
1022
  return(0);
 
1023
}