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

« back to all changes in this revision

Viewing changes to gettext-tools/src/msgmerge.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-2004 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
#include <alloca.h>
 
23
 
 
24
#include <getopt.h>
 
25
#include <limits.h>
 
26
#include <stdbool.h>
 
27
#include <stdio.h>
 
28
#include <stdlib.h>
 
29
#include <string.h>
 
30
#include <locale.h>
 
31
 
 
32
#include "closeout.h"
 
33
#include "dir-list.h"
 
34
#include "error.h"
 
35
#include "error-progname.h"
 
36
#include "progname.h"
 
37
#include "relocatable.h"
 
38
#include "basename.h"
 
39
#include "message.h"
 
40
#include "read-po.h"
 
41
#include "write-po.h"
 
42
#include "format.h"
 
43
#include "xalloc.h"
 
44
#include "obstack.h"
 
45
#include "strstr.h"
 
46
#include "exit.h"
 
47
#include "strcase.h"
 
48
#include "stpcpy.h"
 
49
#include "stpncpy.h"
 
50
#include "msgl-iconv.h"
 
51
#include "msgl-equal.h"
 
52
#include "plural-count.h"
 
53
#include "backupfile.h"
 
54
#include "copy-file.h"
 
55
#include "gettext.h"
 
56
 
 
57
#define _(str) gettext (str)
 
58
 
 
59
#define obstack_chunk_alloc xmalloc
 
60
#define obstack_chunk_free free
 
61
 
 
62
 
 
63
/* If true do not print unneeded messages.  */
 
64
static bool quiet;
 
65
 
 
66
/* Verbosity level.  */
 
67
static int verbosity_level;
 
68
 
 
69
/* Force output of PO file even if empty.  */
 
70
static int force_po;
 
71
 
 
72
/* Apply the .pot file to each of the domains in the PO file.  */
 
73
static bool multi_domain_mode = false;
 
74
 
 
75
/* Determines whether to use fuzzy matching.  */
 
76
static bool use_fuzzy_matching = true;
 
77
 
 
78
/* List of user-specified compendiums.  */
 
79
static message_list_list_ty *compendiums;
 
80
 
 
81
/* Update mode.  */
 
82
static bool update_mode = false;
 
83
static const char *version_control_string;
 
84
static const char *backup_suffix_string;
 
85
 
 
86
/* Long options.  */
 
87
static const struct option long_options[] =
 
88
{
 
89
  { "add-location", no_argument, &line_comment, 1 },
 
90
  { "backup", required_argument, NULL, CHAR_MAX + 1 },
 
91
  { "compendium", required_argument, NULL, 'C', },
 
92
  { "directory", required_argument, NULL, 'D' },
 
93
  { "escape", no_argument, NULL, 'E' },
 
94
  { "force-po", no_argument, &force_po, 1 },
 
95
  { "help", no_argument, NULL, 'h' },
 
96
  { "indent", no_argument, NULL, 'i' },
 
97
  { "multi-domain", no_argument, NULL, 'm' },
 
98
  { "no-escape", no_argument, NULL, 'e' },
 
99
  { "no-fuzzy-matching", no_argument, NULL, 'N' },
 
100
  { "no-location", no_argument, &line_comment, 0 },
 
101
  { "no-wrap", no_argument, NULL, CHAR_MAX + 4 },
 
102
  { "output-file", required_argument, NULL, 'o' },
 
103
  { "properties-input", no_argument, NULL, 'P' },
 
104
  { "properties-output", no_argument, NULL, 'p' },
 
105
  { "quiet", no_argument, NULL, 'q' },
 
106
  { "sort-by-file", no_argument, NULL, 'F' },
 
107
  { "sort-output", no_argument, NULL, 's' },
 
108
  { "silent", no_argument, NULL, 'q' },
 
109
  { "strict", no_argument, NULL, CHAR_MAX + 2 },
 
110
  { "stringtable-input", no_argument, NULL, CHAR_MAX + 5 },
 
111
  { "stringtable-output", no_argument, NULL, CHAR_MAX + 6 },
 
112
  { "suffix", required_argument, NULL, CHAR_MAX + 3 },
 
113
  { "update", no_argument, NULL, 'U' },
 
114
  { "verbose", no_argument, NULL, 'v' },
 
115
  { "version", no_argument, NULL, 'V' },
 
116
  { "width", required_argument, NULL, 'w', },
 
117
  { NULL, 0, NULL, 0 }
 
118
};
 
119
 
 
120
 
 
121
struct statistics
 
122
{
 
123
  size_t merged;
 
124
  size_t fuzzied;
 
125
  size_t missing;
 
126
  size_t obsolete;
 
127
};
 
128
 
 
129
 
 
130
/* Forward declaration of local functions.  */
 
131
static void usage (int status)
 
132
#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
 
133
        __attribute__ ((noreturn))
 
134
#endif
 
135
;
 
136
static void compendium (const char *filename);
 
137
static msgdomain_list_ty *merge (const char *fn1, const char *fn2,
 
138
                                 msgdomain_list_ty **defp);
 
139
 
 
140
 
 
141
int
 
142
main (int argc, char **argv)
 
143
{
 
144
  int opt;
 
145
  bool do_help;
 
146
  bool do_version;
 
147
  char *output_file;
 
148
  msgdomain_list_ty *def;
 
149
  msgdomain_list_ty *result;
 
150
  bool sort_by_filepos = false;
 
151
  bool sort_by_msgid = false;
 
152
 
 
153
  /* Set program name for messages.  */
 
154
  set_program_name (argv[0]);
 
155
  error_print_progname = maybe_print_progname;
 
156
  verbosity_level = 0;
 
157
  quiet = false;
 
158
  gram_max_allowed_errors = UINT_MAX;
 
159
 
 
160
#ifdef HAVE_SETLOCALE
 
161
  /* Set locale via LC_ALL.  */
 
162
  setlocale (LC_ALL, "");
 
163
#endif
 
164
 
 
165
  /* Set the text message domain.  */
 
166
  bindtextdomain (PACKAGE, relocate (LOCALEDIR));
 
167
  textdomain (PACKAGE);
 
168
 
 
169
  /* Ensure that write errors on stdout are detected.  */
 
170
  atexit (close_stdout);
 
171
 
 
172
  /* Set default values for variables.  */
 
173
  do_help = false;
 
174
  do_version = false;
 
175
  output_file = NULL;
 
176
 
 
177
  while ((opt = getopt_long (argc, argv, "C:D:eEFhimNo:pPqsUvVw:",
 
178
                             long_options, NULL))
 
179
         != EOF)
 
180
    switch (opt)
 
181
      {
 
182
      case '\0':                /* Long option.  */
 
183
        break;
 
184
 
 
185
      case 'C':
 
186
        compendium (optarg);
 
187
        break;
 
188
 
 
189
      case 'D':
 
190
        dir_list_append (optarg);
 
191
        break;
 
192
 
 
193
      case 'e':
 
194
        message_print_style_escape (false);
 
195
        break;
 
196
 
 
197
      case 'E':
 
198
        message_print_style_escape (true);
 
199
        break;
 
200
 
 
201
      case 'F':
 
202
        sort_by_filepos = true;
 
203
        break;
 
204
 
 
205
      case 'h':
 
206
        do_help = true;
 
207
        break;
 
208
 
 
209
      case 'i':
 
210
        message_print_style_indent ();
 
211
        break;
 
212
 
 
213
      case 'm':
 
214
        multi_domain_mode = true;
 
215
        break;
 
216
 
 
217
      case 'N':
 
218
        use_fuzzy_matching = false;
 
219
        break;
 
220
 
 
221
      case 'o':
 
222
        output_file = optarg;
 
223
        break;
 
224
 
 
225
      case 'p':
 
226
        message_print_syntax_properties ();
 
227
        break;
 
228
 
 
229
      case 'P':
 
230
        input_syntax = syntax_properties;
 
231
        break;
 
232
 
 
233
      case 'q':
 
234
        quiet = true;
 
235
        break;
 
236
 
 
237
      case 's':
 
238
        sort_by_msgid = true;
 
239
        break;
 
240
 
 
241
      case 'U':
 
242
        update_mode = true;
 
243
        break;
 
244
 
 
245
      case 'v':
 
246
        ++verbosity_level;
 
247
        break;
 
248
 
 
249
      case 'V':
 
250
        do_version = true;
 
251
        break;
 
252
 
 
253
      case 'w':
 
254
        {
 
255
          int value;
 
256
          char *endp;
 
257
          value = strtol (optarg, &endp, 10);
 
258
          if (endp != optarg)
 
259
            message_page_width_set (value);
 
260
        }
 
261
        break;
 
262
 
 
263
      case CHAR_MAX + 1: /* --backup */
 
264
        version_control_string = optarg;
 
265
        break;
 
266
 
 
267
      case CHAR_MAX + 2: /* --strict */
 
268
        message_print_style_uniforum ();
 
269
        break;
 
270
 
 
271
      case CHAR_MAX + 3: /* --suffix */
 
272
        backup_suffix_string = optarg;
 
273
        break;
 
274
 
 
275
      case CHAR_MAX + 4: /* --no-wrap */
 
276
        message_page_width_ignore ();
 
277
        break;
 
278
 
 
279
      case CHAR_MAX + 5: /* --stringtable-input */
 
280
        input_syntax = syntax_stringtable;
 
281
        break;
 
282
 
 
283
      case CHAR_MAX + 6: /* --stringtable-output */
 
284
        message_print_syntax_stringtable ();
 
285
        break;
 
286
 
 
287
      default:
 
288
        usage (EXIT_FAILURE);
 
289
        break;
 
290
      }
 
291
 
 
292
  /* Version information is requested.  */
 
293
  if (do_version)
 
294
    {
 
295
      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
 
296
      /* xgettext: no-wrap */
 
297
      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
 
298
This is free software; see the source for copying conditions.  There is NO\n\
 
299
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
 
300
"),
 
301
              "1995-1998, 2000-2004");
 
302
      printf (_("Written by %s.\n"), "Peter Miller");
 
303
      exit (EXIT_SUCCESS);
 
304
    }
 
305
 
 
306
  /* Help is requested.  */
 
307
  if (do_help)
 
308
    usage (EXIT_SUCCESS);
 
309
 
 
310
  /* Test whether we have an .po file name as argument.  */
 
311
  if (optind >= argc)
 
312
    {
 
313
      error (EXIT_SUCCESS, 0, _("no input files given"));
 
314
      usage (EXIT_FAILURE);
 
315
    }
 
316
  if (optind + 2 != argc)
 
317
    {
 
318
      error (EXIT_SUCCESS, 0, _("exactly 2 input files required"));
 
319
      usage (EXIT_FAILURE);
 
320
    }
 
321
 
 
322
  /* Verify selected options.  */
 
323
  if (update_mode)
 
324
    {
 
325
      if (output_file != NULL)
 
326
        {
 
327
          error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
 
328
                 "--update", "--output-file");
 
329
        }
 
330
    }
 
331
  else
 
332
    {
 
333
      if (version_control_string != NULL)
 
334
        {
 
335
          error (EXIT_SUCCESS, 0, _("%s is only valid with %s"),
 
336
                 "--backup", "--update");
 
337
          usage (EXIT_FAILURE);
 
338
        }
 
339
      if (backup_suffix_string != NULL)
 
340
        {
 
341
          error (EXIT_SUCCESS, 0, _("%s is only valid with %s"),
 
342
                 "--suffix", "--update");
 
343
          usage (EXIT_FAILURE);
 
344
        }
 
345
    }
 
346
 
 
347
  if (!line_comment && sort_by_filepos)
 
348
    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
 
349
           "--no-location", "--sort-by-file");
 
350
 
 
351
  if (sort_by_msgid && sort_by_filepos)
 
352
    error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
 
353
           "--sort-output", "--sort-by-file");
 
354
 
 
355
  /* In update mode, --properties-input implies --properties-output.  */
 
356
  if (update_mode && input_syntax == syntax_properties)
 
357
    message_print_syntax_properties ();
 
358
  /* In update mode, --stringtable-input implies --stringtable-output.  */
 
359
  if (update_mode && input_syntax == syntax_stringtable)
 
360
    message_print_syntax_stringtable ();
 
361
 
 
362
  /* Merge the two files.  */
 
363
  result = merge (argv[optind], argv[optind + 1], &def);
 
364
 
 
365
  /* Sort the results.  */
 
366
  if (sort_by_filepos)
 
367
    msgdomain_list_sort_by_filepos (result);
 
368
  else if (sort_by_msgid)
 
369
    msgdomain_list_sort_by_msgid (result);
 
370
 
 
371
  if (update_mode)
 
372
    {
 
373
      /* Do nothing if the original file and the result are equal.  Also do
 
374
         nothing if the original file and the result differ only by the
 
375
         POT-Creation-Date in the header entry; this is needed for projects
 
376
         which don't put the .pot file under CVS.  */
 
377
      if (!msgdomain_list_equal (def, result, true))
 
378
        {
 
379
          /* Back up def.po.  */
 
380
          enum backup_type backup_type;
 
381
          char *backup_file;
 
382
 
 
383
          output_file = argv[optind];
 
384
 
 
385
          if (backup_suffix_string == NULL)
 
386
            {
 
387
              backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
 
388
              if (backup_suffix_string != NULL
 
389
                  && backup_suffix_string[0] == '\0')
 
390
                backup_suffix_string = NULL;
 
391
            }
 
392
          if (backup_suffix_string != NULL)
 
393
            simple_backup_suffix = backup_suffix_string;
 
394
 
 
395
          backup_type = xget_version (_("backup type"), version_control_string);
 
396
          if (backup_type != none)
 
397
            {
 
398
              backup_file = find_backup_file_name (output_file, backup_type);
 
399
              copy_file_preserving (output_file, backup_file);
 
400
            }
 
401
 
 
402
          /* Write the merged message list out.  */
 
403
          msgdomain_list_print (result, output_file, true, false);
 
404
        }
 
405
    }
 
406
  else
 
407
    {
 
408
      /* Write the merged message list out.  */
 
409
      msgdomain_list_print (result, output_file, force_po, false);
 
410
    }
 
411
 
 
412
  exit (EXIT_SUCCESS);
 
413
}
 
414
 
 
415
 
 
416
/* Display usage information and exit.  */
 
417
static void
 
418
usage (int status)
 
419
{
 
420
  if (status != EXIT_SUCCESS)
 
421
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
 
422
             program_name);
 
423
  else
 
424
    {
 
425
      printf (_("\
 
426
Usage: %s [OPTION] def.po ref.pot\n\
 
427
"), program_name);
 
428
      printf ("\n");
 
429
      /* xgettext: no-wrap */
 
430
      printf (_("\
 
431
Merges two Uniforum style .po files together.  The def.po file is an\n\
 
432
existing PO file with translations which will be taken over to the newly\n\
 
433
created file as long as they still match; comments will be preserved,\n\
 
434
but extracted comments and file positions will be discarded.  The ref.pot\n\
 
435
file is the last created PO file with up-to-date source references but\n\
 
436
old translations, or a PO Template file (generally created by xgettext);\n\
 
437
any translations or comments in the file will be discarded, however dot\n\
 
438
comments and file positions will be preserved.  Where an exact match\n\
 
439
cannot be found, fuzzy matching is used to produce better results.\n\
 
440
"));
 
441
      printf ("\n");
 
442
      printf (_("\
 
443
Mandatory arguments to long options are mandatory for short options too.\n"));
 
444
      printf ("\n");
 
445
      printf (_("\
 
446
Input file location:\n"));
 
447
      printf (_("\
 
448
  def.po                      translations referring to old sources\n"));
 
449
      printf (_("\
 
450
  ref.pot                     references to new sources\n"));
 
451
      printf (_("\
 
452
  -D, --directory=DIRECTORY   add DIRECTORY to list for input files search\n"));
 
453
      printf (_("\
 
454
  -C, --compendium=FILE       additional library of message translations,\n\
 
455
                              may be specified more than once\n"));
 
456
      printf ("\n");
 
457
      printf (_("\
 
458
Operation mode:\n"));
 
459
      printf (_("\
 
460
  -U, --update                update def.po,\n\
 
461
                              do nothing if def.po already up to date\n"));
 
462
      printf ("\n");
 
463
      printf (_("\
 
464
Output file location:\n"));
 
465
      printf (_("\
 
466
  -o, --output-file=FILE      write output to specified file\n"));
 
467
      printf (_("\
 
468
The results are written to standard output if no output file is specified\n\
 
469
or if it is -.\n"));
 
470
      printf ("\n");
 
471
      printf (_("\
 
472
Output file location in update mode:\n"));
 
473
      printf (_("\
 
474
The result is written back to def.po.\n"));
 
475
      printf (_("\
 
476
      --backup=CONTROL        make a backup of def.po\n"));
 
477
      printf (_("\
 
478
      --suffix=SUFFIX         override the usual backup suffix\n"));
 
479
      printf (_("\
 
480
The version control method may be selected via the --backup option or through\n\
 
481
the VERSION_CONTROL environment variable.  Here are the values:\n\
 
482
  none, off       never make backups (even if --backup is given)\n\
 
483
  numbered, t     make numbered backups\n\
 
484
  existing, nil   numbered if numbered backups exist, simple otherwise\n\
 
485
  simple, never   always make simple backups\n"));
 
486
      printf (_("\
 
487
The backup suffix is `~', unless set with --suffix or the SIMPLE_BACKUP_SUFFIX\n\
 
488
environment variable.\n\
 
489
"));
 
490
      printf ("\n");
 
491
      printf (_("\
 
492
Operation modifiers:\n"));
 
493
      printf (_("\
 
494
  -m, --multi-domain          apply ref.pot to each of the domains in def.po\n"));
 
495
      printf (_("\
 
496
  -N, --no-fuzzy-matching     do not use fuzzy matching\n"));
 
497
      printf ("\n");
 
498
      printf (_("\
 
499
Input file syntax:\n"));
 
500
      printf (_("\
 
501
  -P, --properties-input      input files are in Java .properties syntax\n"));
 
502
      printf (_("\
 
503
      --stringtable-input     input files are in NeXTstep/GNUstep .strings\n\
 
504
                              syntax\n"));
 
505
      printf ("\n");
 
506
      printf (_("\
 
507
Output details:\n"));
 
508
      printf (_("\
 
509
  -e, --no-escape             do not use C escapes in output (default)\n"));
 
510
      printf (_("\
 
511
  -E, --escape                use C escapes in output, no extended chars\n"));
 
512
      printf (_("\
 
513
      --force-po              write PO file even if empty\n"));
 
514
      printf (_("\
 
515
  -i, --indent                indented output style\n"));
 
516
      printf (_("\
 
517
      --no-location           suppress '#: filename:line' lines\n"));
 
518
      printf (_("\
 
519
      --add-location          preserve '#: filename:line' lines (default)\n"));
 
520
      printf (_("\
 
521
      --strict                strict Uniforum output style\n"));
 
522
      printf (_("\
 
523
  -p, --properties-output     write out a Java .properties file\n"));
 
524
      printf (_("\
 
525
      --stringtable-output    write out a NeXTstep/GNUstep .strings file\n"));
 
526
      printf (_("\
 
527
  -w, --width=NUMBER          set output page width\n"));
 
528
      printf (_("\
 
529
      --no-wrap               do not break long message lines, longer than\n\
 
530
                              the output page width, into several lines\n"));
 
531
      printf (_("\
 
532
  -s, --sort-output           generate sorted output\n"));
 
533
      printf (_("\
 
534
  -F, --sort-by-file          sort output by file location\n"));
 
535
      printf ("\n");
 
536
      printf (_("\
 
537
Informative output:\n"));
 
538
      printf (_("\
 
539
  -h, --help                  display this help and exit\n"));
 
540
      printf (_("\
 
541
  -V, --version               output version information and exit\n"));
 
542
      printf (_("\
 
543
  -v, --verbose               increase verbosity level\n"));
 
544
      printf (_("\
 
545
  -q, --quiet, --silent       suppress progress indicators\n"));
 
546
      printf ("\n");
 
547
      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
 
548
             stdout);
 
549
    }
 
550
 
 
551
  exit (status);
 
552
}
 
553
 
 
554
 
 
555
static void
 
556
compendium (const char *filename)
 
557
{
 
558
  msgdomain_list_ty *mdlp;
 
559
  size_t k;
 
560
 
 
561
  mdlp = read_po_file (filename);
 
562
  if (!compendiums)
 
563
    compendiums = message_list_list_alloc ();
 
564
  for (k = 0; k < mdlp->nitems; k++)
 
565
    message_list_list_append (compendiums, mdlp->item[k]->messages);
 
566
}
 
567
 
 
568
 
 
569
static bool
 
570
msgfmt_check_pair_fails (const lex_pos_ty *pos,
 
571
                         const char *msgid, const char *msgid_plural,
 
572
                         const char *msgstr, size_t msgstr_len,
 
573
                         size_t fmt)
 
574
{
 
575
  bool failure;
 
576
  struct formatstring_parser *parser = formatstring_parsers[fmt];
 
577
  char *invalid_reason = NULL;
 
578
  void *msgid_descr =
 
579
    parser->parse (msgid_plural != NULL ? msgid_plural : msgid, false,
 
580
                   &invalid_reason);
 
581
 
 
582
  failure = false;
 
583
  if (msgid_descr != NULL)
 
584
    {
 
585
      const char *p_end = msgstr + msgstr_len;
 
586
      const char *p;
 
587
 
 
588
      for (p = msgstr; p < p_end; p += strlen (p) + 1)
 
589
        {
 
590
          void *msgstr_descr = parser->parse (msgstr, true, &invalid_reason);
 
591
 
 
592
          if (msgstr_descr != NULL)
 
593
            {
 
594
              failure = parser->check (pos, msgid_descr, msgstr_descr,
 
595
                                       msgid_plural == NULL, false, NULL);
 
596
              parser->free (msgstr_descr);
 
597
            }
 
598
          else
 
599
            {
 
600
              failure = true;
 
601
              free (invalid_reason);
 
602
            }
 
603
 
 
604
          if (failure)
 
605
            break;
 
606
        }
 
607
 
 
608
      parser->free (msgid_descr);
 
609
    }
 
610
  else
 
611
    free (invalid_reason);
 
612
 
 
613
  return failure;
 
614
}
 
615
 
 
616
 
 
617
static message_ty *
 
618
message_merge (message_ty *def, message_ty *ref)
 
619
{
 
620
  const char *msgstr;
 
621
  size_t msgstr_len;
 
622
  message_ty *result;
 
623
  size_t j, i;
 
624
 
 
625
  /* Take the msgid from the reference.  When fuzzy matches are made,
 
626
     the definition will not be unique, but the reference will be -
 
627
     usually because it has only been slightly changed.  */
 
628
 
 
629
  /* Take the msgstr from the definition.  The msgstr of the reference
 
630
     is usually empty, as it was generated by xgettext.  If we currently
 
631
     process the header entry we have to merge the msgstr by using the
 
632
     Report-Msgid-Bugs-To and POT-Creation-Date fields from the reference.  */
 
633
  if (ref->msgid[0] == '\0')
 
634
    {
 
635
      /* Oh, oh.  The header entry and we have something to fill in.  */
 
636
      static const struct
 
637
      {
 
638
        const char *name;
 
639
        size_t len;
 
640
      } known_fields[] =
 
641
      {
 
642
        { "Project-Id-Version:", sizeof ("Project-Id-Version:") - 1 },
 
643
#define PROJECT_ID              0
 
644
        { "Report-Msgid-Bugs-To:", sizeof ("Report-Msgid-Bugs-To:") - 1 },
 
645
#define REPORT_MSGID_BUGS_TO    1
 
646
        { "POT-Creation-Date:", sizeof ("POT-Creation-Date:") - 1 },
 
647
#define POT_CREATION_DATE       2
 
648
        { "PO-Revision-Date:", sizeof ("PO-Revision-Date:") - 1 },
 
649
#define PO_REVISION_DATE        3
 
650
        { "Last-Translator:", sizeof ("Last-Translator:") - 1 },
 
651
#define LAST_TRANSLATOR         4
 
652
        { "Language-Team:", sizeof ("Language-Team:") - 1 },
 
653
#define LANGUAGE_TEAM           5
 
654
        { "MIME-Version:", sizeof ("MIME-Version:") - 1 },
 
655
#define MIME_VERSION            6
 
656
        { "Content-Type:", sizeof ("Content-Type:") - 1 },
 
657
#define CONTENT_TYPE            7
 
658
        { "Content-Transfer-Encoding:",
 
659
          sizeof ("Content-Transfer-Encoding:") - 1 }
 
660
#define CONTENT_TRANSFER        8
 
661
      };
 
662
#define UNKNOWN 9
 
663
      struct
 
664
      {
 
665
        const char *string;
 
666
        size_t len;
 
667
      } header_fields[UNKNOWN + 1];
 
668
      struct obstack pool;
 
669
      const char *cp;
 
670
      char *newp;
 
671
      size_t len, cnt;
 
672
 
 
673
      /* Clear all fields.  */
 
674
      memset (header_fields, '\0', sizeof (header_fields));
 
675
 
 
676
      /* Prepare a temporary memory pool.  */
 
677
      obstack_init (&pool);
 
678
 
 
679
      cp = def->msgstr;
 
680
      while (*cp != '\0')
 
681
        {
 
682
          const char *endp = strchr (cp, '\n');
 
683
          int terminated = endp != NULL;
 
684
 
 
685
          if (!terminated)
 
686
            {
 
687
              /* Add a trailing newline.  */
 
688
              char *copy;
 
689
              endp = strchr (cp, '\0');
 
690
 
 
691
              len = endp - cp + 1;
 
692
 
 
693
              copy = (char *) obstack_alloc (&pool, len + 1);
 
694
              stpcpy (stpcpy (copy, cp), "\n");
 
695
              cp = copy;
 
696
            }
 
697
          else
 
698
            {
 
699
              len = (endp - cp) + 1;
 
700
              ++endp;
 
701
            }
 
702
 
 
703
          /* Compare with any of the known fields.  */
 
704
          for (cnt = 0;
 
705
               cnt < sizeof (known_fields) / sizeof (known_fields[0]);
 
706
               ++cnt)
 
707
            if (strncasecmp (cp, known_fields[cnt].name, known_fields[cnt].len)
 
708
                == 0)
 
709
              break;
 
710
 
 
711
          if (cnt < sizeof (known_fields) / sizeof (known_fields[0]))
 
712
            {
 
713
              header_fields[cnt].string = &cp[known_fields[cnt].len];
 
714
              header_fields[cnt].len = len - known_fields[cnt].len;
 
715
            }
 
716
          else
 
717
            {
 
718
              /* It's an unknown field.  Append content to what is already
 
719
                 known.  */
 
720
              char *extended =
 
721
                (char *) obstack_alloc (&pool,
 
722
                                        header_fields[UNKNOWN].len + len + 1);
 
723
              memcpy (extended, header_fields[UNKNOWN].string,
 
724
                      header_fields[UNKNOWN].len);
 
725
              memcpy (&extended[header_fields[UNKNOWN].len], cp, len);
 
726
              extended[header_fields[UNKNOWN].len + len] = '\0';
 
727
              header_fields[UNKNOWN].string = extended;
 
728
              header_fields[UNKNOWN].len += len;
 
729
            }
 
730
 
 
731
          cp = endp;
 
732
        }
 
733
 
 
734
      {
 
735
        const char *msgid_bugs_ptr;
 
736
 
 
737
        msgid_bugs_ptr = strstr (ref->msgstr, "Report-Msgid-Bugs-To:");
 
738
        if (msgid_bugs_ptr != NULL)
 
739
          {
 
740
            size_t msgid_bugs_len;
 
741
            const char *endp;
 
742
 
 
743
            msgid_bugs_ptr += sizeof ("Report-Msgid-Bugs-To:") - 1;
 
744
 
 
745
            endp = strchr (msgid_bugs_ptr, '\n');
 
746
            if (endp == NULL)
 
747
              {
 
748
                /* Add a trailing newline.  */
 
749
                char *extended;
 
750
                endp = strchr (msgid_bugs_ptr, '\0');
 
751
                msgid_bugs_len = (endp - msgid_bugs_ptr) + 1;
 
752
                extended = (char *) obstack_alloc (&pool, msgid_bugs_len + 1);
 
753
                stpcpy (stpcpy (extended, msgid_bugs_ptr), "\n");
 
754
                msgid_bugs_ptr = extended;
 
755
              }
 
756
            else
 
757
              msgid_bugs_len = (endp - msgid_bugs_ptr) + 1;
 
758
 
 
759
            header_fields[REPORT_MSGID_BUGS_TO].string = msgid_bugs_ptr;
 
760
            header_fields[REPORT_MSGID_BUGS_TO].len = msgid_bugs_len;
 
761
          }
 
762
      }
 
763
 
 
764
      {
 
765
        const char *pot_date_ptr;
 
766
 
 
767
        pot_date_ptr = strstr (ref->msgstr, "POT-Creation-Date:");
 
768
        if (pot_date_ptr != NULL)
 
769
          {
 
770
            size_t pot_date_len;
 
771
            const char *endp;
 
772
 
 
773
            pot_date_ptr += sizeof ("POT-Creation-Date:") - 1;
 
774
 
 
775
            endp = strchr (pot_date_ptr, '\n');
 
776
            if (endp == NULL)
 
777
              {
 
778
                /* Add a trailing newline.  */
 
779
                char *extended;
 
780
                endp = strchr (pot_date_ptr, '\0');
 
781
                pot_date_len = (endp - pot_date_ptr) + 1;
 
782
                extended = (char *) obstack_alloc (&pool, pot_date_len + 1);
 
783
                stpcpy (stpcpy (extended, pot_date_ptr), "\n");
 
784
                pot_date_ptr = extended;
 
785
              }
 
786
            else
 
787
              pot_date_len = (endp - pot_date_ptr) + 1;
 
788
 
 
789
            header_fields[POT_CREATION_DATE].string = pot_date_ptr;
 
790
            header_fields[POT_CREATION_DATE].len = pot_date_len;
 
791
          }
 
792
      }
 
793
 
 
794
      /* Concatenate all the various fields.  */
 
795
      len = 0;
 
796
      for (cnt = 0; cnt < UNKNOWN; ++cnt)
 
797
        if (header_fields[cnt].string != NULL)
 
798
          len += known_fields[cnt].len + header_fields[cnt].len;
 
799
      len += header_fields[UNKNOWN].len;
 
800
 
 
801
      cp = newp = (char *) xmalloc (len + 1);
 
802
      newp[len] = '\0';
 
803
 
 
804
#define IF_FILLED(idx)                                                        \
 
805
      if (header_fields[idx].string)                                          \
 
806
        newp = stpncpy (stpcpy (newp, known_fields[idx].name),                \
 
807
                        header_fields[idx].string, header_fields[idx].len)
 
808
 
 
809
      IF_FILLED (PROJECT_ID);
 
810
      IF_FILLED (REPORT_MSGID_BUGS_TO);
 
811
      IF_FILLED (POT_CREATION_DATE);
 
812
      IF_FILLED (PO_REVISION_DATE);
 
813
      IF_FILLED (LAST_TRANSLATOR);
 
814
      IF_FILLED (LANGUAGE_TEAM);
 
815
      IF_FILLED (MIME_VERSION);
 
816
      IF_FILLED (CONTENT_TYPE);
 
817
      IF_FILLED (CONTENT_TRANSFER);
 
818
      if (header_fields[UNKNOWN].string != NULL)
 
819
        stpcpy (newp, header_fields[UNKNOWN].string);
 
820
 
 
821
#undef IF_FILLED
 
822
 
 
823
      /* Free the temporary memory pool.  */
 
824
      obstack_free (&pool, NULL);
 
825
 
 
826
      msgstr = cp;
 
827
      msgstr_len = strlen (cp) + 1;
 
828
    }
 
829
  else
 
830
    {
 
831
      msgstr = def->msgstr;
 
832
      msgstr_len = def->msgstr_len;
 
833
    }
 
834
 
 
835
  result = message_alloc (xstrdup (ref->msgid), ref->msgid_plural,
 
836
                          msgstr, msgstr_len, &def->pos);
 
837
 
 
838
  /* Take the comments from the definition file.  There will be none at
 
839
     all in the reference file, as it was generated by xgettext.  */
 
840
  if (def->comment)
 
841
    for (j = 0; j < def->comment->nitems; ++j)
 
842
      message_comment_append (result, def->comment->item[j]);
 
843
 
 
844
  /* Take the dot comments from the reference file, as they are
 
845
     generated by xgettext.  Any in the definition file are old ones
 
846
     collected by previous runs of xgettext and msgmerge.  */
 
847
  if (ref->comment_dot)
 
848
    for (j = 0; j < ref->comment_dot->nitems; ++j)
 
849
      message_comment_dot_append (result, ref->comment_dot->item[j]);
 
850
 
 
851
  /* The flags are mixed in a special way.  Some informations come
 
852
     from the reference message (such as format/no-format), others
 
853
     come from the definition file (fuzzy or not).  */
 
854
  result->is_fuzzy = def->is_fuzzy;
 
855
 
 
856
  for (i = 0; i < NFORMATS; i++)
 
857
    {
 
858
      result->is_format[i] = ref->is_format[i];
 
859
 
 
860
      /* If the reference message is marked as being a format specifier,
 
861
         but the definition message is not, we check if the resulting
 
862
         message would pass "msgfmt -c".  If yes, then all is fine.  If
 
863
         not, we add a fuzzy marker, because
 
864
         1. the message needs the translator's attention,
 
865
         2. msgmerge must not transform a PO file which passes "msgfmt -c"
 
866
            into a PO file which doesn't.  */
 
867
      if (!result->is_fuzzy
 
868
          && possible_format_p (ref->is_format[i])
 
869
          && !possible_format_p (def->is_format[i])
 
870
          && msgfmt_check_pair_fails (&def->pos, ref->msgid, ref->msgid_plural,
 
871
                                      msgstr, msgstr_len, i))
 
872
        result->is_fuzzy = true;
 
873
    }
 
874
 
 
875
  result->do_wrap = ref->do_wrap;
 
876
 
 
877
  /* Take the file position comments from the reference file, as they
 
878
     are generated by xgettext.  Any in the definition file are old ones
 
879
     collected by previous runs of xgettext and msgmerge.  */
 
880
  for (j = 0; j < ref->filepos_count; ++j)
 
881
    {
 
882
      lex_pos_ty *pp = &ref->filepos[j];
 
883
      message_comment_filepos (result, pp->file_name, pp->line_number);
 
884
    }
 
885
 
 
886
  /* Special postprocessing is needed if the reference message is a
 
887
     plural form and the definition message isn't, or vice versa.  */
 
888
  if (ref->msgid_plural != NULL)
 
889
    {
 
890
      if (def->msgid_plural == NULL)
 
891
        result->used = 1;
 
892
    }
 
893
  else
 
894
    {
 
895
      if (def->msgid_plural != NULL)
 
896
        result->used = 2;
 
897
    }
 
898
 
 
899
  /* All done, return the merged message to the caller.  */
 
900
  return result;
 
901
}
 
902
 
 
903
 
 
904
#define DOT_FREQUENCY 10
 
905
 
 
906
static void
 
907
match_domain (const char *fn1, const char *fn2,
 
908
              message_list_list_ty *definitions, message_list_ty *refmlp,
 
909
              message_list_ty *resultmlp,
 
910
              struct statistics *stats, unsigned int *processed)
 
911
{
 
912
  size_t j;
 
913
 
 
914
  for (j = 0; j < refmlp->nitems; j++, (*processed)++)
 
915
    {
 
916
      message_ty *refmsg;
 
917
      message_ty *defmsg;
 
918
 
 
919
      /* Because merging can take a while we print something to signal
 
920
         we are not dead.  */
 
921
      if (!quiet && verbosity_level <= 1 && *processed % DOT_FREQUENCY == 0)
 
922
        fputc ('.', stderr);
 
923
 
 
924
      refmsg = refmlp->item[j];
 
925
 
 
926
      /* See if it is in the other file.  */
 
927
      defmsg = message_list_list_search (definitions, refmsg->msgid);
 
928
      if (defmsg)
 
929
        {
 
930
          /* Merge the reference with the definition: take the #. and
 
931
             #: comments from the reference, take the # comments from
 
932
             the definition, take the msgstr from the definition.  Add
 
933
             this merged entry to the output message list.  */
 
934
          message_ty *mp = message_merge (defmsg, refmsg);
 
935
 
 
936
          message_list_append (resultmlp, mp);
 
937
 
 
938
          /* Remember that this message has been used, when we scan
 
939
             later to see if anything was omitted.  */
 
940
          defmsg->used = 1;
 
941
          stats->merged++;
 
942
        }
 
943
      else if (refmsg->msgid[0] != '\0')
 
944
        {
 
945
          /* If the message was not defined at all, try to find a very
 
946
             similar message, it could be a typo, or the suggestion may
 
947
             help.  */
 
948
          if (use_fuzzy_matching
 
949
              && ((defmsg =
 
950
                     message_list_list_search_fuzzy (definitions,
 
951
                                                     refmsg->msgid)) != NULL))
 
952
            {
 
953
              message_ty *mp;
 
954
 
 
955
              if (verbosity_level > 1)
 
956
                {
 
957
                  po_gram_error_at_line (&refmsg->pos, _("\
 
958
this message is used but not defined..."));
 
959
                  po_gram_error_at_line (&defmsg->pos, _("\
 
960
...but this definition is similar"));
 
961
                }
 
962
 
 
963
              /* Merge the reference with the definition: take the #. and
 
964
                 #: comments from the reference, take the # comments from
 
965
                 the definition, take the msgstr from the definition.  Add
 
966
                 this merged entry to the output message list.  */
 
967
              mp = message_merge (defmsg, refmsg);
 
968
 
 
969
              mp->is_fuzzy = true;
 
970
 
 
971
              message_list_append (resultmlp, mp);
 
972
 
 
973
              /* Remember that this message has been used, when we scan
 
974
                 later to see if anything was omitted.  */
 
975
              defmsg->used = 1;
 
976
              stats->fuzzied++;
 
977
              if (!quiet && verbosity_level <= 1)
 
978
                /* Always print a dot if we handled a fuzzy match.  */
 
979
                fputc ('.', stderr);
 
980
            }
 
981
          else
 
982
            {
 
983
              message_ty *mp;
 
984
 
 
985
              if (verbosity_level > 1)
 
986
                po_gram_error_at_line (&refmsg->pos, _("\
 
987
this message is used but not defined in %s"), fn1);
 
988
 
 
989
              mp = message_copy (refmsg);
 
990
 
 
991
              message_list_append (resultmlp, mp);
 
992
              stats->missing++;
 
993
            }
 
994
        }
 
995
    }
 
996
 
 
997
  /* Now postprocess the problematic merges.  This is needed because we
 
998
     want the result to pass the "msgfmt -c -v" check.  */
 
999
  {
 
1000
    /* message_merge sets mp->used to 1 or 2, depending on the problem.
 
1001
       Compute the bitwise OR of all these.  */
 
1002
    int problematic = 0;
 
1003
 
 
1004
    for (j = 0; j < resultmlp->nitems; j++)
 
1005
      problematic |= resultmlp->item[j]->used;
 
1006
 
 
1007
    if (problematic)
 
1008
      {
 
1009
        unsigned long int nplurals = 0;
 
1010
 
 
1011
        if (problematic & 1)
 
1012
          {
 
1013
            /* Need to know nplurals of the result domain.  */
 
1014
            message_ty *header_entry = message_list_search (resultmlp, "");
 
1015
 
 
1016
            nplurals = get_plural_count (header_entry
 
1017
                                         ? header_entry->msgstr
 
1018
                                         : NULL);
 
1019
          }
 
1020
 
 
1021
        for (j = 0; j < resultmlp->nitems; j++)
 
1022
          {
 
1023
            message_ty *mp = resultmlp->item[j];
 
1024
 
 
1025
            if ((mp->used & 1) && (nplurals > 0))
 
1026
              {
 
1027
                /* ref->msgid_plural != NULL but def->msgid_plural == NULL.
 
1028
                   Use a copy of def->msgstr for each possible plural form.  */
 
1029
                size_t new_msgstr_len;
 
1030
                char *new_msgstr;
 
1031
                char *p;
 
1032
                unsigned long i;
 
1033
 
 
1034
                if (verbosity_level > 1)
 
1035
                  {
 
1036
                    po_gram_error_at_line (&mp->pos, _("\
 
1037
this message should define plural forms"));
 
1038
                  }
 
1039
 
 
1040
                new_msgstr_len = nplurals * mp->msgstr_len;
 
1041
                new_msgstr = (char *) xmalloc (new_msgstr_len);
 
1042
                for (i = 0, p = new_msgstr; i < nplurals; i++)
 
1043
                  {
 
1044
                    memcpy (p, mp->msgstr, mp->msgstr_len);
 
1045
                    p += mp->msgstr_len;
 
1046
                  }
 
1047
                mp->msgstr = new_msgstr;
 
1048
                mp->msgstr_len = new_msgstr_len;
 
1049
                mp->is_fuzzy = true;
 
1050
              }
 
1051
 
 
1052
            if ((mp->used & 2) && (mp->msgstr_len > strlen (mp->msgstr) + 1))
 
1053
              {
 
1054
                /* ref->msgid_plural == NULL but def->msgid_plural != NULL.
 
1055
                   Use only the first among the plural forms.  */
 
1056
 
 
1057
                if (verbosity_level > 1)
 
1058
                  {
 
1059
                    po_gram_error_at_line (&mp->pos, _("\
 
1060
this message should not define plural forms"));
 
1061
                  }
 
1062
 
 
1063
                mp->msgstr_len = strlen (mp->msgstr) + 1;
 
1064
                mp->is_fuzzy = true;
 
1065
              }
 
1066
 
 
1067
            /* Postprocessing of this message is done.  */
 
1068
            mp->used = 0;
 
1069
          }
 
1070
      }
 
1071
  }
 
1072
}
 
1073
 
 
1074
static msgdomain_list_ty *
 
1075
merge (const char *fn1, const char *fn2, msgdomain_list_ty **defp)
 
1076
{
 
1077
  msgdomain_list_ty *def;
 
1078
  msgdomain_list_ty *ref;
 
1079
  size_t j, k;
 
1080
  unsigned int processed;
 
1081
  struct statistics stats;
 
1082
  msgdomain_list_ty *result;
 
1083
  message_list_list_ty *definitions;
 
1084
  message_list_ty *empty_list;
 
1085
 
 
1086
  stats.merged = stats.fuzzied = stats.missing = stats.obsolete = 0;
 
1087
 
 
1088
  /* This is the definitions file, created by a human.  */
 
1089
  def = read_po_file (fn1);
 
1090
 
 
1091
  /* Create the set of places to look for message definitions: a list
 
1092
     whose first element will be definitions for the current domain, and
 
1093
     whose other elements come from the compendiums.  */
 
1094
  definitions = message_list_list_alloc ();
 
1095
  message_list_list_append (definitions, NULL);
 
1096
  if (compendiums)
 
1097
    message_list_list_append_list (definitions, compendiums);
 
1098
  empty_list = message_list_alloc (false);
 
1099
 
 
1100
  /* This is the references file, created by groping the sources with
 
1101
     the xgettext program.  */
 
1102
  ref = read_po_file (fn2);
 
1103
  /* Add a dummy header entry, if the references file contains none.  */
 
1104
  for (k = 0; k < ref->nitems; k++)
 
1105
    if (message_list_search (ref->item[k]->messages, "") == NULL)
 
1106
      {
 
1107
        static lex_pos_ty pos = { __FILE__, __LINE__ };
 
1108
        message_ty *refheader = message_alloc ("", NULL, "", 1, &pos);
 
1109
 
 
1110
        message_list_prepend (ref->item[k]->messages, refheader);
 
1111
      }
 
1112
 
 
1113
  /* The references file can be either in ASCII or in UTF-8.  If it is
 
1114
     in UTF-8, we have to convert the definitions to UTF-8 as well.  */
 
1115
  {
 
1116
    bool was_utf8 = false;
 
1117
    for (k = 0; k < ref->nitems; k++)
 
1118
      {
 
1119
        message_list_ty *mlp = ref->item[k]->messages;
 
1120
 
 
1121
        for (j = 0; j < mlp->nitems; j++)
 
1122
          if (mlp->item[j]->msgid[0] == '\0' && !mlp->item[j]->obsolete)
 
1123
            {
 
1124
              const char *header = mlp->item[j]->msgstr;
 
1125
 
 
1126
              if (header != NULL)
 
1127
                {
 
1128
                  const char *charsetstr = strstr (header, "charset=");
 
1129
 
 
1130
                  if (charsetstr != NULL)
 
1131
                    {
 
1132
                      size_t len;
 
1133
 
 
1134
                      charsetstr += strlen ("charset=");
 
1135
                      len = strcspn (charsetstr, " \t\n");
 
1136
                      if (len == strlen ("UTF-8")
 
1137
                          && strncasecmp (charsetstr, "UTF-8", len) == 0)
 
1138
                        was_utf8 = true;
 
1139
                    }
 
1140
                }
 
1141
            }
 
1142
        }
 
1143
    if (was_utf8)
 
1144
      def = iconv_msgdomain_list (def, "UTF-8", fn1);
 
1145
  }
 
1146
 
 
1147
  result = msgdomain_list_alloc (false);
 
1148
  processed = 0;
 
1149
 
 
1150
  /* Every reference must be matched with its definition. */
 
1151
  if (!multi_domain_mode)
 
1152
    for (k = 0; k < ref->nitems; k++)
 
1153
      {
 
1154
        const char *domain = ref->item[k]->domain;
 
1155
        message_list_ty *refmlp = ref->item[k]->messages;
 
1156
        message_list_ty *resultmlp =
 
1157
          msgdomain_list_sublist (result, domain, true);
 
1158
 
 
1159
        definitions->item[0] = msgdomain_list_sublist (def, domain, false);
 
1160
        if (definitions->item[0] == NULL)
 
1161
          definitions->item[0] = empty_list;
 
1162
 
 
1163
        match_domain (fn1, fn2, definitions, refmlp, resultmlp,
 
1164
                      &stats, &processed);
 
1165
      }
 
1166
  else
 
1167
    {
 
1168
      /* Apply the references messages in the default domain to each of
 
1169
         the definition domains.  */
 
1170
      message_list_ty *refmlp = ref->item[0]->messages;
 
1171
 
 
1172
      for (k = 0; k < def->nitems; k++)
 
1173
        {
 
1174
          const char *domain = def->item[k]->domain;
 
1175
          message_list_ty *defmlp = def->item[k]->messages;
 
1176
 
 
1177
          /* Ignore the default message domain if it has no messages.  */
 
1178
          if (k > 0 || defmlp->nitems > 0)
 
1179
            {
 
1180
              message_list_ty *resultmlp =
 
1181
                msgdomain_list_sublist (result, domain, true);
 
1182
 
 
1183
              definitions->item[0] = defmlp;
 
1184
 
 
1185
              match_domain (fn1, fn2, definitions, refmlp, resultmlp,
 
1186
                            &stats, &processed);
 
1187
            }
 
1188
        }
 
1189
    }
 
1190
 
 
1191
  /* Look for messages in the definition file, which are not present
 
1192
     in the reference file, indicating messages which defined but not
 
1193
     used in the program.  Don't scan the compendium(s).  */
 
1194
  for (k = 0; k < def->nitems; ++k)
 
1195
    {
 
1196
      const char *domain = def->item[k]->domain;
 
1197
      message_list_ty *defmlp = def->item[k]->messages;
 
1198
 
 
1199
      for (j = 0; j < defmlp->nitems; j++)
 
1200
        {
 
1201
          message_ty *defmsg = defmlp->item[j];
 
1202
 
 
1203
          if (!defmsg->used)
 
1204
            {
 
1205
              /* Remember the old translation although it is not used anymore.
 
1206
                 But we mark it as obsolete.  */
 
1207
              message_ty *mp;
 
1208
 
 
1209
              mp = message_copy (defmsg);
 
1210
              mp->obsolete = true;
 
1211
 
 
1212
              message_list_append (msgdomain_list_sublist (result, domain, true),
 
1213
                                   mp);
 
1214
              stats.obsolete++;
 
1215
            }
 
1216
        }
 
1217
    }
 
1218
 
 
1219
  /* Determine the known a-priori encoding, if any.  */
 
1220
  if (def->encoding == ref->encoding)
 
1221
    result->encoding = def->encoding;
 
1222
 
 
1223
  /* Report some statistics.  */
 
1224
  if (verbosity_level > 0)
 
1225
    fprintf (stderr, _("%s\
 
1226
Read %ld old + %ld reference, \
 
1227
merged %ld, fuzzied %ld, missing %ld, obsolete %ld.\n"),
 
1228
             !quiet && verbosity_level <= 1 ? "\n" : "",
 
1229
             (long) def->nitems, (long) ref->nitems,
 
1230
             (long) stats.merged, (long) stats.fuzzied, (long) stats.missing,
 
1231
             (long) stats.obsolete);
 
1232
  else if (!quiet)
 
1233
    fputs (_(" done.\n"), stderr);
 
1234
 
 
1235
  /* Return results.  */
 
1236
  *defp = def;
 
1237
  return result;
 
1238
}