~ubuntu-branches/ubuntu/trusty/gettext/trusty

« back to all changes in this revision

Viewing changes to src/msgcmp.c

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Vila
  • Date: 2002-04-10 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20020410131742-npf89tsaygdolprj
Tags: upstream-0.10.40
ImportĀ upstreamĀ versionĀ 0.10.40

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GNU gettext - internationalization aids
 
2
   Copyright (C) 1995-1998, 2000, 2001 Free Software Foundation, Inc.
 
3
   This file was written by Peter Miller <millerp@canb.auug.org.au>
 
4
 
 
5
   This program is free software; you can redistribute it and/or modify
 
6
   it under the terms of the GNU General Public License as published by
 
7
   the Free Software Foundation; either version 2, or (at your option)
 
8
   any later version.
 
9
 
 
10
   This program is distributed in the hope that it will be useful,
 
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
   GNU General Public License for more details.
 
14
 
 
15
   You should have received a copy of the GNU General Public License
 
16
   along with this program; if not, write to the Free Software Foundation,
 
17
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
18
 
 
19
#ifdef HAVE_CONFIG_H
 
20
# include <config.h>
 
21
#endif
 
22
 
 
23
#include <getopt.h>
 
24
#include <stdio.h>
 
25
#include <stdlib.h>
 
26
#include <locale.h>
 
27
 
 
28
#include "dir-list.h"
 
29
#include "error.h"
 
30
#include "message.h"
 
31
#include <system.h>
 
32
#include "libgettext.h"
 
33
#include "po.h"
 
34
#include "str-list.h"
 
35
 
 
36
#define _(str) gettext (str)
 
37
 
 
38
 
 
39
/* This structure defines a derived class of the po_ty class.  (See
 
40
   po.h for an explanation.)  */
 
41
typedef struct compare_class_ty compare_class_ty;
 
42
struct compare_class_ty
 
43
{
 
44
  /* inherited instance variables, etc */
 
45
  PO_BASE_TY
 
46
 
 
47
  /* Name of domain we are currently examining.  */
 
48
  char *domain;
 
49
 
 
50
  /* List of domains already appeared in the current file.  */
 
51
  string_list_ty *domain_list;
 
52
 
 
53
  /* List of messages already appeared in the current file.  */
 
54
  message_list_ty *mlp;
 
55
};
 
56
 
 
57
/* String containing name the program is called with.  */
 
58
const char *program_name;
 
59
 
 
60
/* Long options.  */
 
61
static const struct option long_options[] =
 
62
{
 
63
  { "directory", required_argument, NULL, 'D' },
 
64
  { "help", no_argument, NULL, 'h' },
 
65
  { "version", no_argument, NULL, 'V' },
 
66
  { NULL, 0, NULL, 0 }
 
67
};
 
68
 
 
69
 
 
70
/* Prototypes for local functions.  */
 
71
static void usage PARAMS ((int __status));
 
72
static void error_print PARAMS ((void));
 
73
static void compare PARAMS ((char *, char *));
 
74
static message_list_ty *grammar PARAMS ((char *__filename));
 
75
static void compare_constructor PARAMS ((po_ty *__that));
 
76
static void compare_destructor PARAMS ((po_ty *__that));
 
77
static void compare_directive_domain PARAMS ((po_ty *__that, char *__name));
 
78
static void compare_directive_message PARAMS ((po_ty *__that, char *__msgid,
 
79
                                               lex_pos_ty *msgid_pos,
 
80
                                               char *__msgid_plural,
 
81
                                               char *__msgstr,
 
82
                                               size_t __msgstr_len,
 
83
                                               lex_pos_ty *__msgstr_pos));
 
84
static void compare_parse_debrief PARAMS ((po_ty *__that));
 
85
 
 
86
 
 
87
int
 
88
main (argc, argv)
 
89
     int argc;
 
90
     char *argv[];
 
91
{
 
92
  int optchar;
 
93
  int do_help;
 
94
  int do_version;
 
95
 
 
96
  /* Set program name for messages.  */
 
97
  program_name = argv[0];
 
98
  error_print_progname = error_print;
 
99
 
 
100
#ifdef HAVE_SETLOCALE
 
101
  /* Set locale via LC_ALL.  */
 
102
  setlocale (LC_ALL, "");
 
103
#endif
 
104
 
 
105
  /* Set the text message domain.  */
 
106
  bindtextdomain (PACKAGE, LOCALEDIR);
 
107
  textdomain (PACKAGE);
 
108
 
 
109
  do_help = 0;
 
110
  do_version = 0;
 
111
  while ((optchar = getopt_long (argc, argv, "D:hV", long_options, NULL))
 
112
         != EOF)
 
113
    switch (optchar)
 
114
      {
 
115
      case '\0':                /* long option */
 
116
        break;
 
117
 
 
118
      case 'D':
 
119
        dir_list_append (optarg);
 
120
        break;
 
121
 
 
122
      case 'h':
 
123
        do_help = 1;
 
124
        break;
 
125
 
 
126
      case 'V':
 
127
        do_version = 1;
 
128
        break;
 
129
 
 
130
      default:
 
131
        usage (EXIT_FAILURE);
 
132
        break;
 
133
      }
 
134
 
 
135
  /* Version information is requested.  */
 
136
  if (do_version)
 
137
    {
 
138
      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
 
139
      /* xgettext: no-wrap */
 
140
      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
 
141
This is free software; see the source for copying conditions.  There is NO\n\
 
142
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
 
143
"),
 
144
              "1995-1998, 2000, 2001");
 
145
      printf (_("Written by %s.\n"), "Peter Miller");
 
146
      exit (EXIT_SUCCESS);
 
147
    }
 
148
 
 
149
  /* Help is requested.  */
 
150
  if (do_help)
 
151
    usage (EXIT_SUCCESS);
 
152
 
 
153
  /* Test whether we have an .po file name as argument.  */
 
154
  if (optind >= argc)
 
155
    {
 
156
      error (EXIT_SUCCESS, 0, _("no input files given"));
 
157
      usage (EXIT_FAILURE);
 
158
    }
 
159
  if (optind + 2 != argc)
 
160
    {
 
161
      error (EXIT_SUCCESS, 0, _("exactly 2 input files required"));
 
162
      usage (EXIT_FAILURE);
 
163
    }
 
164
 
 
165
  /* compare the two files */
 
166
  compare (argv[optind], argv[optind + 1]);
 
167
  exit (EXIT_SUCCESS);
 
168
}
 
169
 
 
170
 
 
171
/* Display usage information and exit.  */
 
172
static void
 
173
usage (status)
 
174
     int status;
 
175
{
 
176
  if (status != EXIT_SUCCESS)
 
177
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
 
178
             program_name);
 
179
  else
 
180
    {
 
181
      /* xgettext: no-wrap */
 
182
      printf (_("\
 
183
Usage: %s [OPTION] def.po ref.po\n\
 
184
Mandatory arguments to long options are mandatory for short options too.\n\
 
185
  -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n\
 
186
  -h, --help                  display this help and exit\n\
 
187
  -V, --version               output version information and exit\n\
 
188
\n\
 
189
Compare two Uniforum style .po files to check that both contain the same\n\
 
190
set of msgid strings.  The def.po file is an existing PO file with the\n\
 
191
old translations.  The ref.po file is the last created PO file\n\
 
192
(generally by xgettext).  This is useful for checking that you have\n\
 
193
translated each and every message in your program.  Where an exact match\n\
 
194
cannot be found, fuzzy matching is used to produce better diagnostics.\n"),
 
195
              program_name);
 
196
      fputs (_("Report bugs to <bug-gnu-utils@gnu.org>.\n"), stdout);
 
197
    }
 
198
 
 
199
  exit (status);
 
200
}
 
201
 
 
202
 
 
203
/* The address of this function will be assigned to the hook in the error
 
204
   functions.  */
 
205
static void
 
206
error_print ()
 
207
{
 
208
  /* We don't want the program name to be printed in messages.  Emacs'
 
209
     compile.el does not like this.  */
 
210
}
 
211
 
 
212
 
 
213
static void
 
214
compare (fn1, fn2)
 
215
     char *fn1;
 
216
     char *fn2;
 
217
{
 
218
  message_list_ty *list1;
 
219
  message_list_ty *list2;
 
220
  int nerrors;
 
221
  message_ty *mp1;
 
222
  size_t j, k;
 
223
 
 
224
  /* This is the master file, created by a human.  */
 
225
  list1 = grammar (fn1);
 
226
 
 
227
  /* This is the generated file, created by groping the sources with
 
228
     the xgettext program.  */
 
229
  list2 = grammar (fn2);
 
230
 
 
231
  /* Every entry in the xgettext generated file must be matched by a
 
232
     (single) entry in the human created file.  */
 
233
  nerrors = 0;
 
234
  for (j = 0; j < list2->nitems; ++j)
 
235
    {
 
236
      message_ty *mp2;
 
237
 
 
238
      mp2 = list2->item[j];
 
239
 
 
240
      /* See if it is in the other file.  */
 
241
      mp1 = message_list_search (list1, mp2->msgid);
 
242
      if (mp1)
 
243
        {
 
244
          mp1->used = 1;
 
245
          continue;
 
246
        }
 
247
 
 
248
      /* If the message was not defined at all, try to find a very
 
249
         similar message, it could be a typo, or the suggestion may
 
250
         help.  */
 
251
      ++nerrors;
 
252
      mp1 = message_list_search_fuzzy (list1, mp2->msgid);
 
253
      if (mp1)
 
254
        {
 
255
          po_gram_error_at_line (&mp2->variant[0].pos, _("\
 
256
this message is used but not defined..."));
 
257
          po_gram_error_at_line (&mp1->variant[0].pos, _("\
 
258
...but this definition is similar"));
 
259
          mp1->used = 1;
 
260
        }
 
261
      else
 
262
        {
 
263
          po_gram_error_at_line (&mp2->variant[0].pos, _("\
 
264
this message is used but not defined in %s"), fn1);
 
265
        }
 
266
    }
 
267
 
 
268
  /* Look for messages in the human generated file, which are not
 
269
     present in the xgettext generated file, indicating messages which
 
270
     are not used in the program.  */
 
271
  for (k = 0; k < list1->nitems; ++k)
 
272
    {
 
273
      mp1 = list1->item[k];
 
274
      if (mp1->used)
 
275
        continue;
 
276
      po_gram_error_at_line (&mp1->variant[0].pos,
 
277
                           _("warning: this message is not used"));
 
278
    }
 
279
 
 
280
  /* Exit with status 1 on any error.  */
 
281
  if (nerrors > 0)
 
282
    error (EXIT_FAILURE, 0,
 
283
           ngettext ("found %d fatal error", "found %d fatal errors", nerrors),
 
284
           nerrors);
 
285
}
 
286
 
 
287
 
 
288
/* Local functions.  */
 
289
 
 
290
static void
 
291
compare_constructor (that)
 
292
     po_ty *that;
 
293
{
 
294
  compare_class_ty *this = (compare_class_ty *) that;
 
295
 
 
296
  this->mlp = message_list_alloc ();
 
297
  this->domain = MESSAGE_DOMAIN_DEFAULT;
 
298
  this->domain_list = string_list_alloc ();
 
299
}
 
300
 
 
301
 
 
302
static void
 
303
compare_destructor (that)
 
304
     po_ty *that;
 
305
{
 
306
  compare_class_ty *this = (compare_class_ty *) that;
 
307
 
 
308
  string_list_free (this->domain_list);
 
309
  /* Do not free this->mlp!  */
 
310
}
 
311
 
 
312
 
 
313
static void
 
314
compare_directive_domain (that, name)
 
315
     po_ty *that;
 
316
     char *name;
 
317
{
 
318
  compare_class_ty *this = (compare_class_ty *)that;
 
319
  /* Override current domain name.  Don't free memory.  */
 
320
  this->domain = name;
 
321
}
 
322
 
 
323
 
 
324
static void
 
325
compare_directive_message (that, msgid, msgid_pos, msgid_plural,
 
326
                           msgstr, msgstr_len, msgstr_pos)
 
327
     po_ty *that;
 
328
     char *msgid;
 
329
     lex_pos_ty *msgid_pos;
 
330
     char *msgid_plural;
 
331
     char *msgstr;
 
332
     size_t msgstr_len;
 
333
     lex_pos_ty *msgstr_pos;
 
334
{
 
335
  compare_class_ty *this = (compare_class_ty *) that;
 
336
  message_ty *mp;
 
337
  message_variant_ty *mvp;
 
338
 
 
339
  /* Remember the domain names for later.  */
 
340
  string_list_append_unique (this->domain_list, this->domain);
 
341
 
 
342
  /* See if this message ID has been seen before.  */
 
343
  mp = message_list_search (this->mlp, msgid);
 
344
  if (mp)
 
345
    free (msgid);
 
346
  else
 
347
    {
 
348
      mp = message_alloc (msgid, msgid_plural);
 
349
      message_list_append (this->mlp, mp);
 
350
    }
 
351
 
 
352
  /* See if this domain has been seen for this message ID.  */
 
353
  mvp = message_variant_search (mp, this->domain);
 
354
  if (mvp)
 
355
    {
 
356
      po_gram_error_at_line (msgid_pos, _("duplicate message definition"));
 
357
      po_gram_error_at_line (&mvp->pos, _("\
 
358
...this is the location of the first definition"));
 
359
      free (msgstr);
 
360
    }
 
361
  else
 
362
    message_variant_append (mp, this->domain, msgstr, msgstr_len, msgstr_pos);
 
363
}
 
364
 
 
365
 
 
366
static void
 
367
compare_parse_debrief (that)
 
368
     po_ty *that;
 
369
{
 
370
  compare_class_ty *this = (compare_class_ty *) that;
 
371
  message_list_ty *mlp = this->mlp;
 
372
  size_t j;
 
373
 
 
374
  /* For each domain in the used-domain-list, make sure each message
 
375
     defines a msgstr in that domain.  */
 
376
  for (j = 0; j < this->domain_list->nitems; ++j)
 
377
    {
 
378
      const char *domain_name;
 
379
      size_t k;
 
380
 
 
381
      domain_name = this->domain_list->item[j];
 
382
      for (k = 0; k < mlp->nitems; ++k)
 
383
        {
 
384
          const message_ty *mp;
 
385
          size_t m;
 
386
 
 
387
          mp = mlp->item[k];
 
388
          for (m = 0; m < mp->variant_count; ++m)
 
389
            {
 
390
              message_variant_ty *mvp;
 
391
 
 
392
              mvp = &mp->variant[m];
 
393
              if (strcmp (domain_name, mvp->domain) == 0)
 
394
                break;
 
395
            }
 
396
          if (m >= mp->variant_count)
 
397
            po_gram_error_at_line (&mp->variant[0].pos, _("\
 
398
this message has no definition in the \"%s\" domain"), domain_name);
 
399
        }
 
400
    }
 
401
}
 
402
 
 
403
 
 
404
/* So that the one parser can be used for multiple programs, and also
 
405
   use good data hiding and encapsulation practices, an object
 
406
   oriented approach has been taken.  An object instance is allocated,
 
407
   and all actions resulting from the parse will be through
 
408
   invokations of method functions of that object.  */
 
409
 
 
410
static po_method_ty compare_methods =
 
411
{
 
412
  sizeof (compare_class_ty),
 
413
  compare_constructor,
 
414
  compare_destructor,
 
415
  compare_directive_domain,
 
416
  compare_directive_message,
 
417
  NULL, /* parse_brief */
 
418
  compare_parse_debrief,
 
419
  NULL, /* comment */
 
420
  NULL, /* comment_dot */
 
421
  NULL, /* comment_filepos */
 
422
  NULL, /* comment_special */
 
423
};
 
424
 
 
425
 
 
426
static message_list_ty *
 
427
grammar (filename)
 
428
     char *filename;
 
429
{
 
430
  po_ty *pop;
 
431
  message_list_ty *mlp;
 
432
 
 
433
  pop = po_alloc (&compare_methods);
 
434
  po_scan (pop, filename);
 
435
  mlp = ((compare_class_ty *)pop)->mlp;
 
436
  po_free (pop);
 
437
  return mlp;
 
438
}