~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to include/comp_err.c

MergedĀ inĀ remove-include-dir

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