~ubuntu-branches/ubuntu/breezy/gettext/breezy

« back to all changes in this revision

Viewing changes to src/msgcmp.c

  • Committer: Bazaar Package Importer
  • Author(s): Santiago Vila
  • Date: 2004-03-14 17:40:02 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040314174002-p1ad5ldve1hqzhye
Tags: 0.14.1-2
* Added libexpat1-dev to Build-Depends, for glade support.
* Added libc0.1-dev to Build-Depends, for GNU/kFreeBSD.
* Removed special-casing of knetbsd-gnu in debian/rules.

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
 
}