~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to extra/comp_err.c

Merged build changes from Antony.

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
 
}