~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to extra/comp_err.c

Removed/replaced DBUG symbols and standardized TRUE/FALSE

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