• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/src/
1/* Initializes a new PO file.
2   Copyright (C) 2001-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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 3 of the License, or
8   (at your option) 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, see <http://www.gnu.org/licenses/>.  */
17
18
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22#include <alloca.h>
23
24#include <errno.h>
25#include <fcntl.h>
26#include <getopt.h>
27#include <limits.h>
28#include <locale.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33#include <sys/types.h>
34
35#if HAVE_PWD_H
36# include <pwd.h>
37#endif
38
39#include <unistd.h>
40
41#if HAVE_DIRENT_H
42# include <dirent.h>
43#endif
44
45#if HAVE_DIRENT_H
46# define HAVE_DIR 1
47#else
48# define HAVE_DIR 0
49#endif
50
51#include "closeout.h"
52#include "error.h"
53#include "error-progname.h"
54#include "progname.h"
55#include "relocatable.h"
56#include "basename.h"
57#include "c-strstr.h"
58#include "c-strcase.h"
59#include "message.h"
60#include "read-catalog.h"
61#include "read-po.h"
62#include "read-properties.h"
63#include "read-stringtable.h"
64#include "write-catalog.h"
65#include "write-po.h"
66#include "write-properties.h"
67#include "write-stringtable.h"
68#include "po-charset.h"
69#include "localcharset.h"
70#include "localename.h"
71#include "po-time.h"
72#include "plural-table.h"
73#include "lang-table.h"
74#include "xalloc.h"
75#include "xmalloca.h"
76#include "filename.h"
77#include "xerror.h"
78#include "xvasprintf.h"
79#include "msgl-english.h"
80#include "plural-count.h"
81#include "pipe.h"
82#include "wait-process.h"
83#include "xsetenv.h"
84#include "str-list.h"
85#include "propername.h"
86#include "gettext.h"
87
88#define _(str) gettext (str)
89#define N_(str) (str)
90
91/* Get F_OK.  It is lacking from <fcntl.h> on Woe32.  */
92#ifndef F_OK
93# define F_OK 0
94#endif
95
96#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
97
98extern const char * _nl_expand_alias (const char *name);
99
100/* Locale name.  */
101static const char *locale;
102
103/* Language (ISO-639 code) and optional territory (ISO-3166 code).  */
104static const char *catalogname;
105
106/* Language (ISO-639 code).  */
107static const char *language;
108
109/* If true, the user is not considered to be the translator.  */
110static bool no_translator;
111
112/* Long options.  */
113static const struct option long_options[] =
114{
115  { "help", no_argument, NULL, 'h' },
116  { "input", required_argument, NULL, 'i' },
117  { "locale", required_argument, NULL, 'l' },
118  { "no-translator", no_argument, NULL, CHAR_MAX + 1 },
119  { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
120  { "output-file", required_argument, NULL, 'o' },
121  { "properties-input", no_argument, NULL, 'P' },
122  { "properties-output", no_argument, NULL, 'p' },
123  { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
124  { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
125  { "version", no_argument, NULL, 'V' },
126  { "width", required_argument, NULL, 'w' },
127  { NULL, 0, NULL, 0 }
128};
129
130/* Forward declaration of local functions.  */
131static void usage (int status)
132#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
133     __attribute__ ((noreturn))
134#endif
135;
136static const char *find_pot (void);
137static const char *catalogname_for_locale (const char *locale);
138static const char *language_of_locale (const char *locale);
139static char *get_field (const char *header, const char *field);
140static msgdomain_list_ty *fill_header (msgdomain_list_ty *mdlp);
141static msgdomain_list_ty *update_msgstr_plurals (msgdomain_list_ty *mdlp);
142
143
144int
145main (int argc, char **argv)
146{
147  int opt;
148  bool do_help;
149  bool do_version;
150  char *output_file;
151  const char *input_file;
152  msgdomain_list_ty *result;
153  catalog_input_format_ty input_syntax = &input_format_po;
154  catalog_output_format_ty output_syntax = &output_format_po;
155
156  /* Set program name for messages.  */
157  set_program_name (argv[0]);
158  error_print_progname = maybe_print_progname;
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  bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
168  textdomain (PACKAGE);
169
170  /* Ensure that write errors on stdout are detected.  */
171  atexit (close_stdout);
172
173  /* Set default values for variables.  */
174  do_help = false;
175  do_version = false;
176  output_file = NULL;
177  input_file = NULL;
178  locale = NULL;
179
180  while ((opt = getopt_long (argc, argv, "hi:l:o:pPVw:", long_options, NULL))
181	 != EOF)
182    switch (opt)
183      {
184      case '\0':		/* Long option.  */
185	break;
186
187      case 'h':
188	do_help = true;
189	break;
190
191      case 'i':
192	if (input_file != NULL)
193	  {
194	    error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
195	    usage (EXIT_FAILURE);
196	  }
197	input_file = optarg;
198	break;
199
200      case 'l':
201	locale = optarg;
202	break;
203
204      case 'o':
205	output_file = optarg;
206	break;
207
208      case 'p':
209	output_syntax = &output_format_properties;
210	break;
211
212      case 'P':
213	input_syntax = &input_format_properties;
214	break;
215
216      case 'V':
217	do_version = true;
218	break;
219
220      case 'w':
221	{
222	  int value;
223	  char *endp;
224	  value = strtol (optarg, &endp, 10);
225	  if (endp != optarg)
226	    message_page_width_set (value);
227	}
228	break;
229
230      case CHAR_MAX + 1:
231	no_translator = true;
232	break;
233
234      case CHAR_MAX + 2: /* --no-wrap */
235	message_page_width_ignore ();
236	break;
237
238      case CHAR_MAX + 3: /* --stringtable-input */
239	input_syntax = &input_format_stringtable;
240	break;
241
242      case CHAR_MAX + 4: /* --stringtable-output */
243	output_syntax = &output_format_stringtable;
244	break;
245
246      default:
247	usage (EXIT_FAILURE);
248	break;
249      }
250
251  /* Version information is requested.  */
252  if (do_version)
253    {
254      printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
255      /* xgettext: no-wrap */
256      printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
257License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
258This is free software: you are free to change and redistribute it.\n\
259There is NO WARRANTY, to the extent permitted by law.\n\
260"),
261	      "2001-2007");
262      printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
263      exit (EXIT_SUCCESS);
264    }
265
266  /* Help is requested.  */
267  if (do_help)
268    usage (EXIT_SUCCESS);
269
270  /* Test for extraneous arguments.  */
271  if (optind != argc)
272    error (EXIT_FAILURE, 0, _("too many arguments"));
273
274  /* Search for the input file.  */
275  if (input_file == NULL)
276    input_file = find_pot ();
277
278  /* Determine target locale.  */
279  if (locale == NULL)
280    {
281      locale = gl_locale_name (LC_MESSAGES, "LC_MESSAGES");
282      if (strcmp (locale, "C") == 0)
283	{
284	  multiline_error (xstrdup (""),
285			   xstrdup (_("\
286You are in a language indifferent environment.  Please set\n\
287your LANG environment variable, as described in the ABOUT-NLS\n\
288file.  This is necessary so you can test your translations.\n")));
289	  exit (EXIT_FAILURE);
290	}
291    }
292  {
293    const char *alias = _nl_expand_alias (locale);
294    if (alias != NULL)
295      locale = alias;
296  }
297  catalogname = catalogname_for_locale (locale);
298  language = language_of_locale (locale);
299
300  /* Default output file name is CATALOGNAME.po.  */
301  if (output_file == NULL)
302    {
303      output_file = xasprintf ("%s.po", catalogname);
304
305      /* But don't overwrite existing PO files.  */
306      if (access (output_file, F_OK) == 0)
307	{
308	  multiline_error (xstrdup (""),
309			   xasprintf (_("\
310Output file %s already exists.\n\
311Please specify the locale through the --locale option or\n\
312the output .po file through the --output-file option.\n"),
313				      output_file));
314	  exit (EXIT_FAILURE);
315	}
316    }
317
318  /* Read input file.  */
319  result = read_catalog_file (input_file, input_syntax);
320
321  /* Fill the header entry.  */
322  result = fill_header (result);
323
324  /* Initialize translations.  */
325  if (strcmp (language, "en") == 0)
326    result = msgdomain_list_english (result);
327  else
328    result = update_msgstr_plurals (result);
329
330  /* Write the modified message list out.  */
331  msgdomain_list_print (result, output_file, output_syntax, true, false);
332
333  if (!no_translator)
334    fprintf (stderr, "\n");
335  fprintf (stderr, _("Created %s.\n"), output_file);
336
337  exit (EXIT_SUCCESS);
338}
339
340
341/* Display usage information and exit.  */
342static void
343usage (int status)
344{
345  if (status != EXIT_SUCCESS)
346    fprintf (stderr, _("Try `%s --help' for more information.\n"),
347	     program_name);
348  else
349    {
350      printf (_("\
351Usage: %s [OPTION]\n\
352"), program_name);
353      printf ("\n");
354      /* xgettext: no-wrap */
355      printf (_("\
356Creates a new PO file, initializing the meta information with values from the\n\
357user's environment.\n\
358"));
359      printf ("\n");
360      printf (_("\
361Mandatory arguments to long options are mandatory for short options too.\n"));
362      printf ("\n");
363      printf (_("\
364Input file location:\n"));
365      printf (_("\
366  -i, --input=INPUTFILE       input POT file\n"));
367      printf (_("\
368If no input file is given, the current directory is searched for the POT file.\n\
369If it is -, standard input is read.\n"));
370      printf ("\n");
371      printf (_("\
372Output file location:\n"));
373      printf (_("\
374  -o, --output-file=FILE      write output to specified PO file\n"));
375      printf (_("\
376If no output file is given, it depends on the --locale option or the user's\n\
377locale setting.  If it is -, the results are written to standard output.\n"));
378      printf ("\n");
379      printf (_("\
380Input file syntax:\n"));
381      printf (_("\
382  -P, --properties-input      input file is in Java .properties syntax\n"));
383      printf (_("\
384      --stringtable-input     input file is in NeXTstep/GNUstep .strings syntax\n"));
385      printf ("\n");
386      printf (_("\
387Output details:\n"));
388      printf (_("\
389  -l, --locale=LL_CC          set target locale\n"));
390      printf (_("\
391      --no-translator         assume the PO file is automatically generated\n"));
392      printf (_("\
393  -p, --properties-output     write out a Java .properties file\n"));
394      printf (_("\
395      --stringtable-output    write out a NeXTstep/GNUstep .strings file\n"));
396      printf (_("\
397  -w, --width=NUMBER          set output page width\n"));
398      printf (_("\
399      --no-wrap               do not break long message lines, longer than\n\
400                              the output page width, into several lines\n"));
401      printf ("\n");
402      printf (_("\
403Informative output:\n"));
404      printf (_("\
405  -h, --help                  display this help and exit\n"));
406      printf (_("\
407  -V, --version               output version information and exit\n"));
408      printf ("\n");
409      /* TRANSLATORS: The placeholder indicates the bug-reporting address
410         for this package.  Please add _another line_ saying
411         "Report translation bugs to <...>\n" with the address for translation
412         bugs (typically your translation team's web or email address).  */
413      fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
414	     stdout);
415    }
416
417  exit (status);
418}
419
420
421/* Search for the POT file and return its name.  */
422static const char *
423find_pot ()
424{
425#if HAVE_DIR
426  DIR *dirp;
427  char *found = NULL;
428
429  dirp = opendir (".");
430  if (dirp != NULL)
431    {
432      for (;;)
433	{
434	  struct dirent *dp;
435
436	  errno = 0;
437	  dp = readdir (dirp);
438	  if (dp != NULL)
439	    {
440	      const char *name = dp->d_name;
441	      size_t namlen = strlen (name);
442
443	      if (namlen > 4 && memcmp (name + namlen - 4, ".pot", 4) == 0)
444		{
445		  if (found == NULL)
446		    found = xstrdup (name);
447		  else
448		    {
449		      multiline_error (xstrdup (""),
450				       xstrdup (_("\
451Found more than one .pot file.\n\
452Please specify the input .pot file through the --input option.\n")));
453		      usage (EXIT_FAILURE);
454		    }
455		}
456	    }
457	  else if (errno != 0)
458	    error (EXIT_FAILURE, errno, _("error reading current directory"));
459	  else
460	    break;
461	}
462      if (closedir (dirp))
463	error (EXIT_FAILURE, errno, _("error reading current directory"));
464
465      if (found != NULL)
466	return found;
467    }
468#endif
469
470  multiline_error (xstrdup (""),
471		   xstrdup (_("\
472Found no .pot file in the current directory.\n\
473Please specify the input .pot file through the --input option.\n")));
474  usage (EXIT_FAILURE);
475  /* NOTREACHED */
476  return NULL;
477}
478
479
480/* Return the gettext catalog name corresponding to a locale.  If the locale
481   consists of a language and a territory, and the language is mainly spoken
482   in that territory, the territory is removed from the locale name.
483   For example, "de_DE" or "de_DE.ISO-8859-1" are simplified to "de",
484   because the resulting catalog can be used as a default for all "de_XX",
485   such as "de_AT".  */
486static const char *
487catalogname_for_locale (const char *locale)
488{
489  static const char *locales_with_principal_territory[] = {
490		/* Language	Main territory */
491    "ace_ID",	/* Achinese	Indonesia */
492    "af_ZA",	/* Afrikaans	South Africa */
493    "ak_GH",	/* Akan		Ghana */
494    "am_ET",	/* Amharic	Ethiopia */
495    "an_ES",	/* Aragonese	Spain */
496    "ang_GB",	/* Old English	Britain */
497    "as_IN",	/* Assamese	India */
498    "av_RU",	/* Avaric	Russia */
499    "awa_IN",	/* Awadhi	India */
500    "az_AZ",	/* Azerbaijani	Azerbaijan */
501    "bad_CF",	/* Banda	Central African Republic */
502    "ban_ID",	/* Balinese	Indonesia */
503    "be_BY",	/* Belarusian	Belarus */
504    "bem_ZM",	/* Bemba	Zambia */
505    "bg_BG",	/* Bulgarian	Bulgaria */
506    "bho_IN",	/* Bhojpuri	India */
507    "bik_PH",	/* Bikol	Philippines */
508    "bin_NG",	/* Bini		Nigeria */
509    "bm_ML",	/* Bambara	Mali */
510    "bn_IN",	/* Bengali	India */
511    "bo_CN",	/* Tibetan	China */
512    "br_FR",	/* Breton	France */
513    "bs_BA",	/* Bosnian	Bosnia */
514    "btk_ID",	/* Batak	Indonesia */
515    "bug_ID",	/* Buginese	Indonesia */
516    "ca_ES",	/* Catalan	Spain */
517    "ce_RU",	/* Chechen	Russia */
518    "ceb_PH",	/* Cebuano	Philippines */
519    "co_FR",	/* Corsican	France */
520    "cr_CA",	/* Cree		Canada */
521    "cs_CZ",	/* Czech	Czech Republic */
522    "csb_PL",	/* Kashubian	Poland */
523    "cy_GB",	/* Welsh	Britain */
524    "da_DK",	/* Danish	Denmark */
525    "de_DE",	/* German	Germany */
526    "din_SD",	/* Dinka	Sudan */
527    "doi_IN",	/* Dogri	India */
528    "dv_MV",	/* Divehi	Maldives */
529    "dz_BT",	/* Dzongkha	Bhutan */
530    "ee_GH",	/* ��w��		Ghana */
531    "el_GR",	/* Greek	Greece */
532    /* Don't put "en_GB" or "en_US" here.  That would be asking for fruitless
533       political discussion.  */
534    "es_ES",	/* Spanish	Spain */
535    "et_EE",	/* Estonian	Estonia */
536    "fa_IR",	/* Persian	Iran */
537    "fi_FI",	/* Finnish	Finland */
538    "fil_PH",	/* Filipino	Philippines */
539    "fj_FJ",	/* Fijian	Fiji */
540    "fo_FO",	/* Faroese	Faeroe Islands */
541    "fon_BJ",	/* Fon		Benin */
542    "fr_FR",	/* French	France */
543    "fy_NL",	/* Western Frisian	Netherlands */
544    "ga_IE",	/* Irish	Ireland */
545    "gd_GB",	/* Scots	Britain */
546    "gon_IN",	/* Gondi	India */
547    "gsw_CH",	/* Swiss German	Switzerland */
548    "gu_IN",	/* Gujarati	India */
549    "he_IL",	/* Hebrew	Israel */
550    "hi_IN",	/* Hindi	India */
551    "hil_PH",	/* Hiligaynon	Philippines */
552    "hr_HR",	/* Croatian	Croatia */
553    "ht_HT",	/* Haitian	Haiti */
554    "hu_HU",	/* Hungarian	Hungary */
555    "hy_AM",	/* Armenian	Armenia */
556    "id_ID",	/* Indonesian	Indonesia */
557    "ig_NG",	/* Igbo		Nigeria */
558    "ii_CN",	/* Sichuan Yi	China */
559    "ilo_PH",	/* Iloko	Philippines */
560    "is_IS",	/* Icelandic	Iceland */
561    "it_IT",	/* Italian	Italy */
562    "ja_JP",	/* Japanese	Japan */
563    "jab_NG",	/* Hyam		Nigeria */
564    "jv_ID",	/* Javanese	Indonesia */
565    "ka_GE",	/* Georgian	Georgia */
566    "kab_DZ",	/* Kabyle	Algeria */
567    "kaj_NG",	/* Jju		Nigeria */
568    "kam_KE",	/* Kamba	Kenya */
569    "kmb_AO",	/* Kimbundu	Angola */
570    "kcg_NG",	/* Tyap		Nigeria */
571    "kdm_NG",	/* Kagoma	Nigeria */
572    "kg_CD",	/* Kongo	Democratic Republic of Congo */
573    "kk_KZ",	/* Kazakh	Kazakhstan */
574    "kl_GL",	/* Kalaallisut	Greenland */
575    "km_KH",	/* Khmer	Cambodia */
576    "kn_IN",	/* Kannada	India */
577    "ko_KR",	/* Korean	Korea (South) */
578    "kok_IN",	/* Konkani	India */
579    "kr_NG",	/* Kanuri	Nigeria */
580    "kru_IN",	/* Kurukh	India */
581    "lg_UG",	/* Ganda	Uganda */
582    "li_BE",	/* Limburgish	Belgium */
583    "lo_LA",	/* Laotian	Laos */
584    "lt_LT",	/* Lithuanian	Lithuania */
585    "lu_CD",	/* Luba-Katanga	Democratic Republic of Congo */
586    "lua_CD",	/* Luba-Lulua	Democratic Republic of Congo */
587    "luo_KE",	/* Luo		Kenya */
588    "lv_LV",	/* Latvian	Latvia */
589    "mad_ID",	/* Madurese	Indonesia */
590    "mag_IN",	/* Magahi	India */
591    "mai_IN",	/* Maithili	India */
592    "mak_ID",	/* Makasar	Indonesia */
593    "man_ML",	/* Mandingo	Mali */
594    "men_SL",	/* Mende	Sierra Leone */
595    "mg_MG",	/* Malagasy	Madagascar */
596    "min_ID",	/* Minangkabau	Indonesia */
597    "mk_MK",	/* Macedonian	Macedonia */
598    "ml_IN",	/* Malayalam	India */
599    "mn_MN",	/* Mongolian	Mongolia */
600    "mni_IN",	/* Manipuri	India */
601    "mos_BF",	/* Mossi	Burkina Faso */
602    "mr_IN",	/* Marathi	India */
603    "ms_MY",	/* Malay	Malaysia */
604    "mt_MT",	/* Maltese	Malta */
605    "mwr_IN",	/* Marwari	India */
606    "my_MM",	/* Burmese	Myanmar */
607    "na_NR",	/* Nauru	Nauru */
608    "nah_MX",	/* Nahuatl	Mexico */
609    "nap_IT",	/* Neapolitan	Italy */
610    "nb_NO",	/* Norwegian Bokm��l	Norway */
611    "nds_DE",	/* Low Saxon	Germany */
612    "ne_NP",	/* Nepali	Nepal */
613    "nl_NL",	/* Dutch	Netherlands */
614    "nn_NO",	/* Norwegian Nynorsk	Norway */
615    "no_NO",	/* Norwegian	Norway */
616    "nr_ZA",	/* South Ndebele	South Africa */
617    "nso_ZA",	/* Northern Sotho	South Africa */
618    "nym_TZ",	/* Nyamwezi	Tanzania */
619    "nyn_UG",	/* Nyankole	Uganda */
620    "oc_FR",	/* Occitan	France */
621    "oj_CA",	/* Ojibwa	Canada */
622    "or_IN",	/* Oriya	India */
623    "pa_IN",	/* Punjabi	India */
624    "pag_PH",	/* Pangasinan	Philippines */
625    "pam_PH",	/* Pampanga	Philippines */
626    "pbb_CO",	/* P��ez		Colombia */
627    "pl_PL",	/* Polish	Poland */
628    "ps_AF",	/* Pashto	Afghanistan */
629    "pt_PT",	/* Portuguese	Portugal */
630    "raj_IN",	/* Rajasthani	India */
631    "rm_CH",	/* Rhaeto-Roman	Switzerland */
632    "rn_BI",	/* Kirundi	Burundi */
633    "ro_RO",	/* Romanian	Romania */
634    "ru_RU",	/* Russian	Russia */
635    "sa_IN",	/* Sanskrit	India */
636    "sas_ID",	/* Sasak	Indonesia */
637    "sat_IN",	/* Santali	India */
638    "sc_IT",	/* Sardinian	Italy */
639    "scn_IT",	/* Sicilian	Italy */
640    "sg_CF",	/* Sango	Central African Republic */
641    "shn_MM",	/* Shan		Myanmar */
642    "si_LK",	/* Sinhala	Sri Lanka */
643    "sid_ET",	/* Sidamo	Ethiopia */
644    "sk_SK",	/* Slovak	Slovakia */
645    "sl_SI",	/* Slovenian	Slovenia */
646    "so_SO",	/* Somali	Somalia */
647    "sq_AL",	/* Albanian	Albania */
648    "sr_RS",	/* Serbian	Serbia */
649    "sr_YU",	/* Serbian	Yugoslavia - this line can be removed in 2010 */
650    "srr_SN",	/* Serer	Senegal */
651    "suk_TZ",	/* Sukuma	Tanzania */
652    "sus_GN",	/* Susu		Guinea */
653    "sv_SE",	/* Swedish	Sweden */
654    "te_IN",	/* Telugu	India */
655    "tem_SL",	/* Timne	Sierra Leone */
656    "tet_ID",	/* Tetum	Indonesia */
657    "tg_TJ",	/* Tajik	Tajikistan */
658    "th_TH",	/* Thai		Thailand */
659    "tiv_NG",	/* Tiv		Nigeria */
660    "tk_TM",	/* Turkmen	Turkmenistan */
661    "tl_PH",	/* Tagalog	Philippines */
662    "to_TO",	/* Tonga	Tonga */
663    "tr_TR",	/* Turkish	Turkey */
664    "tum_MW",	/* Tumbuka	Malawi */
665    "uk_UA",	/* Ukrainian	Ukraine */
666    "umb_AO",	/* Umbundu	Angola */
667    "ur_PK",	/* Urdu		Pakistan */
668    "uz_UZ",	/* Uzbek	Uzbekistan */
669    "ve_ZA",	/* Venda	South Africa */
670    "vi_VN",	/* Vietnamese	Vietnam */
671    "wa_BE",	/* Walloon	Belgium */
672    "wal_ET",	/* Walamo	Ethiopia */
673    "war_PH",	/* Waray	Philippines */
674    "wen_DE",	/* Sorbian	Germany */
675    "yao_MW",	/* Yao		Malawi */
676    "zap_MX"	/* Zapotec	Mexico */
677  };
678  const char *dot;
679  size_t i;
680
681  /* Remove the ".codeset" part from the locale.  */
682  dot = strchr (locale, '.');
683  if (dot != NULL)
684    {
685      const char *codeset_end;
686      char *shorter_locale;
687
688      codeset_end = strpbrk (dot + 1, "_@");
689      if (codeset_end == NULL)
690	codeset_end = dot + strlen (dot);
691
692      shorter_locale = XNMALLOC (strlen (locale), char);
693      memcpy (shorter_locale, locale, dot - locale);
694      strcpy (shorter_locale + (dot - locale), codeset_end);
695      locale = shorter_locale;
696    }
697
698  /* If the territory is the language's principal territory, drop it.  */
699  for (i = 0; i < SIZEOF (locales_with_principal_territory); i++)
700    if (strcmp (locale, locales_with_principal_territory[i]) == 0)
701      {
702	const char *language_end;
703	size_t len;
704	char *shorter_locale;
705
706	language_end = strchr (locale, '_');
707	if (language_end == NULL)
708	  abort ();
709
710	len = language_end - locale;
711	shorter_locale = XNMALLOC (len + 1, char);
712	memcpy (shorter_locale, locale, len);
713	shorter_locale[len] = '\0';
714	locale = shorter_locale;
715	break;
716      }
717
718  return locale;
719}
720
721
722/* Return the language of a locale.  */
723static const char *
724language_of_locale (const char *locale)
725{
726  const char *language_end;
727
728  language_end = strpbrk (locale, "_.@");
729  if (language_end != NULL)
730    {
731      size_t len;
732      char *result;
733
734      len = language_end - locale;
735      result = XNMALLOC (len + 1, char);
736      memcpy (result, locale, len);
737      result[len] = '\0';
738
739      return result;
740    }
741  else
742    return locale;
743}
744
745
746/* Return the most likely desired charset for the PO file, as a portable
747   charset name.  */
748static const char *
749canonical_locale_charset ()
750{
751  const char *tmp;
752  char *old_LC_ALL;
753  const char *charset;
754
755  /* Save LC_ALL environment variable.  */
756
757  tmp = getenv ("LC_ALL");
758  old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
759
760  xsetenv ("LC_ALL", locale, 1);
761
762#ifdef HAVE_SETLOCALE
763  if (setlocale (LC_ALL, "") == NULL)
764    /* Nonexistent locale.  Use anything.  */
765    charset = "";
766  else
767#endif
768    /* Get the locale's charset.  */
769    charset = locale_charset ();
770
771  /* Restore LC_ALL environment variable.  */
772
773  if (old_LC_ALL != NULL)
774    xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
775  else
776    unsetenv ("LC_ALL");
777
778#ifdef HAVE_SETLOCALE
779  setlocale (LC_ALL, "");
780#endif
781
782  /* Canonicalize it.  */
783  charset = po_charset_canonicalize (charset);
784  if (charset == NULL)
785    charset = po_charset_ascii;
786
787  return charset;
788}
789
790
791/* Return the English name of the language.  */
792static const char *
793englishname_of_language ()
794{
795  size_t i;
796
797  for (i = 0; i < language_table_size; i++)
798    if (strcmp (language_table[i].code, language) == 0)
799      return language_table[i].english;
800
801  return xasprintf ("Language %s", language);
802}
803
804
805/* Construct the value for the PACKAGE name.  */
806static const char *
807project_id ()
808{
809  const char *gettextlibdir;
810  char *prog;
811  char *argv[3];
812  pid_t child;
813  int fd[1];
814  FILE *fp;
815  char *line;
816  size_t linesize;
817  size_t linelen;
818  int exitstatus;
819
820  gettextlibdir = getenv ("GETTEXTLIBDIR");
821  if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
822    gettextlibdir = relocate (LIBDIR "/gettext");
823
824  prog = concatenated_filename (gettextlibdir, "project-id", NULL);
825
826  /* Call the project-id shell script.  */
827  argv[0] = "/bin/sh";
828  argv[1] = prog;
829  argv[2] = NULL;
830  child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
831			  fd);
832  if (child == -1)
833    goto failed;
834
835  /* Retrieve its result.  */
836  fp = fdopen (fd[0], "r");
837  if (fp == NULL)
838    {
839      error (0, errno, _("fdopen() failed"));
840      goto failed;
841    }
842
843  line = NULL; linesize = 0;
844  linelen = getline (&line, &linesize, fp);
845  if (linelen == (size_t)(-1))
846    {
847      error (0, 0, _("%s subprocess I/O error"), prog);
848      fclose (fp);
849      goto failed;
850    }
851  if (linelen > 0 && line[linelen - 1] == '\n')
852    line[linelen - 1] = '\0';
853
854  fclose (fp);
855
856  /* Remove zombie process from process list, and retrieve exit status.  */
857  exitstatus = wait_subprocess (child, prog, false, false, true, false);
858  if (exitstatus != 0)
859    {
860      error (0, 0, _("%s subprocess failed with exit code %d"),
861	     prog, exitstatus);
862      goto failed;
863    }
864
865  return line;
866
867failed:
868  return "PACKAGE";
869}
870
871
872/* Construct the value for the Project-Id-Version field.  */
873static const char *
874project_id_version (const char *header)
875{
876  const char *old_field;
877  const char *gettextlibdir;
878  char *prog;
879  char *argv[4];
880  pid_t child;
881  int fd[1];
882  FILE *fp;
883  char *line;
884  size_t linesize;
885  size_t linelen;
886  int exitstatus;
887
888  /* Return the old value if present, assuming it was already filled in by
889     xgettext.  */
890  old_field = get_field (header, "Project-Id-Version");
891  if (old_field != NULL && strcmp (old_field, "PACKAGE VERSION") != 0)
892    return old_field;
893
894  gettextlibdir = getenv ("GETTEXTLIBDIR");
895  if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
896    gettextlibdir = relocate (LIBDIR "/gettext");
897
898  prog = concatenated_filename (gettextlibdir, "project-id", NULL);
899
900  /* Call the project-id shell script.  */
901  argv[0] = "/bin/sh";
902  argv[1] = prog;
903  argv[2] = "yes";
904  argv[3] = NULL;
905  child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
906			  fd);
907  if (child == -1)
908    goto failed;
909
910  /* Retrieve its result.  */
911  fp = fdopen (fd[0], "r");
912  if (fp == NULL)
913    {
914      error (0, errno, _("fdopen() failed"));
915      goto failed;
916    }
917
918  line = NULL; linesize = 0;
919  linelen = getline (&line, &linesize, fp);
920  if (linelen == (size_t)(-1))
921    {
922      error (0, 0, _("%s subprocess I/O error"), prog);
923      fclose (fp);
924      goto failed;
925    }
926  if (linelen > 0 && line[linelen - 1] == '\n')
927    line[linelen - 1] = '\0';
928
929  fclose (fp);
930
931  /* Remove zombie process from process list, and retrieve exit status.  */
932  exitstatus = wait_subprocess (child, prog, false, false, true, false);
933  if (exitstatus != 0)
934    {
935      error (0, 0, _("%s subprocess failed with exit code %d"),
936	     prog, exitstatus);
937      goto failed;
938    }
939
940  return line;
941
942failed:
943  return "PACKAGE VERSION";
944}
945
946
947/* Construct the value for the PO-Revision-Date field.  */
948static const char *
949po_revision_date (const char *header)
950{
951  if (no_translator)
952    /* Because the PO file is automatically generated, we use the
953       POT-Creation-Date, not the current time.  */
954    return get_field (header, "POT-Creation-Date");
955  else
956    {
957      /* Assume the translator will modify the PO file now.  */
958      time_t now;
959
960      time (&now);
961      return po_strftime (&now);
962    }
963}
964
965
966/* Returns the struct passwd entry for the current user.  */
967static struct passwd *
968get_user_pwd ()
969{
970#if HAVE_PWD_H  /* Only Unix, not native Woe32.  */
971  const char *username;
972  struct passwd *userpasswd;
973
974  /* 1. attempt: getpwnam(getenv("USER"))  */
975  username = getenv ("USER");
976  if (username != NULL)
977    {
978      errno = 0;
979      userpasswd = getpwnam (username);
980      if (userpasswd != NULL)
981	return userpasswd;
982      if (errno != 0)
983	error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
984    }
985
986  /* 2. attempt: getpwnam(getlogin())  */
987  username = getlogin ();
988  if (username != NULL)
989    {
990      errno = 0;
991      userpasswd = getpwnam (username);
992      if (userpasswd != NULL)
993	return userpasswd;
994      if (errno != 0)
995	error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
996    }
997
998  /* 3. attempt: getpwuid(getuid())  */
999  errno = 0;
1000  userpasswd = getpwuid (getuid ());
1001  if (userpasswd != NULL)
1002    return userpasswd;
1003  if (errno != 0)
1004    error (EXIT_FAILURE, errno, "getpwuid(\"%d\")", getuid ());
1005#endif
1006
1007  return NULL;
1008}
1009
1010
1011/* Return the user's full name.  */
1012static const char *
1013get_user_fullname ()
1014{
1015  struct passwd *pwd;
1016
1017  pwd = get_user_pwd ();
1018#if HAVE_PWD_H
1019  if (pwd != NULL)
1020    {
1021      const char *fullname;
1022      const char *fullname_end;
1023      char *result;
1024
1025      /* Return the pw_gecos field, upto the first comma (if any).  */
1026      fullname = pwd->pw_gecos;
1027      fullname_end = strchr (fullname, ',');
1028      if (fullname_end == NULL)
1029	fullname_end = fullname + strlen (fullname);
1030
1031      result = XNMALLOC (fullname_end - fullname + 1, char);
1032      memcpy (result, fullname, fullname_end - fullname);
1033      result[fullname_end - fullname] = '\0';
1034
1035      return result;
1036    }
1037#endif
1038
1039  return NULL;
1040}
1041
1042
1043/* Return the user's email address.  */
1044static const char *
1045get_user_email ()
1046{
1047  const char *prog = relocate (LIBDIR "/gettext/user-email");
1048  char *argv[4];
1049  pid_t child;
1050  int fd[1];
1051  FILE *fp;
1052  char *line;
1053  size_t linesize;
1054  size_t linelen;
1055  int exitstatus;
1056
1057  /* Ask the user for his email address.  */
1058  argv[0] = "/bin/sh";
1059  argv[1] = (char *) prog;
1060  argv[2] = (char *) _("\
1061The new message catalog should contain your email address, so that users can\n\
1062give you feedback about the translations, and so that maintainers can contact\n\
1063you in case of unexpected technical problems.\n");
1064  argv[3] = NULL;
1065  child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
1066			  fd);
1067  if (child == -1)
1068    goto failed;
1069
1070  /* Retrieve his answer.  */
1071  fp = fdopen (fd[0], "r");
1072  if (fp == NULL)
1073    {
1074      error (0, errno, _("fdopen() failed"));
1075      goto failed;
1076    }
1077
1078  line = NULL; linesize = 0;
1079  linelen = getline (&line, &linesize, fp);
1080  if (linelen == (size_t)(-1))
1081    {
1082      error (0, 0, _("%s subprocess I/O error"), prog);
1083      fclose (fp);
1084      goto failed;
1085    }
1086  if (linelen > 0 && line[linelen - 1] == '\n')
1087    line[linelen - 1] = '\0';
1088
1089  fclose (fp);
1090
1091  /* Remove zombie process from process list, and retrieve exit status.  */
1092  exitstatus = wait_subprocess (child, prog, false, false, true, false);
1093  if (exitstatus != 0)
1094    {
1095      error (0, 0, _("%s subprocess failed with exit code %d"),
1096	     prog, exitstatus);
1097      goto failed;
1098    }
1099
1100  return line;
1101
1102failed:
1103  return "EMAIL@ADDRESS";
1104}
1105
1106
1107/* Construct the value for the Last-Translator field.  */
1108static const char *
1109last_translator ()
1110{
1111  if (no_translator)
1112    return "Automatically generated";
1113  else
1114    {
1115      const char *fullname = get_user_fullname ();
1116      const char *email = get_user_email ();
1117
1118      if (fullname != NULL)
1119	return xasprintf ("%s <%s>", fullname, email);
1120      else
1121	return xasprintf ("<%s>", email);
1122    }
1123}
1124
1125
1126/* Return the name of the language used by the language team, in English.  */
1127static const char *
1128language_team_englishname ()
1129{
1130  size_t i;
1131
1132  /* Search for a name depending on the catalogname.  */
1133  for (i = 0; i < language_variant_table_size; i++)
1134    if (strcmp (language_variant_table[i].code, catalogname) == 0)
1135      return language_variant_table[i].english;
1136
1137  /* Search for a name depending on the language only.  */
1138  return englishname_of_language ();
1139}
1140
1141
1142/* Return the language team's mailing list address or homepage URL.  */
1143static const char *
1144language_team_address ()
1145{
1146  const char *prog = relocate (PROJECTSDIR "/team-address");
1147  char *argv[7];
1148  pid_t child;
1149  int fd[1];
1150  FILE *fp;
1151  char *line;
1152  size_t linesize;
1153  size_t linelen;
1154  int exitstatus;
1155
1156  /* Call the team-address shell script.  */
1157  argv[0] = "/bin/sh";
1158  argv[1] = (char *) prog;
1159  argv[2] = (char *) relocate (PROJECTSDIR);
1160  argv[3] = (char *) relocate (LIBDIR "/gettext");
1161  argv[4] = (char *) catalogname;
1162  argv[5] = (char *) language;
1163  argv[6] = NULL;
1164  child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
1165			  fd);
1166  if (child == -1)
1167    goto failed;
1168
1169  /* Retrieve its result.  */
1170  fp = fdopen (fd[0], "r");
1171  if (fp == NULL)
1172    {
1173      error (0, errno, _("fdopen() failed"));
1174      goto failed;
1175    }
1176
1177  line = NULL; linesize = 0;
1178  linelen = getline (&line, &linesize, fp);
1179  if (linelen == (size_t)(-1))
1180    line = "";
1181  else if (linelen > 0 && line[linelen - 1] == '\n')
1182    line[linelen - 1] = '\0';
1183
1184  fclose (fp);
1185
1186  /* Remove zombie process from process list, and retrieve exit status.  */
1187  exitstatus = wait_subprocess (child, prog, false, false, true, false);
1188  if (exitstatus != 0)
1189    {
1190      error (0, 0, _("%s subprocess failed with exit code %d"),
1191	     prog, exitstatus);
1192      goto failed;
1193    }
1194
1195  return line;
1196
1197failed:
1198  return "";
1199}
1200
1201
1202/* Construct the value for the Language-Team field.  */
1203static const char *
1204language_team ()
1205{
1206  if (no_translator)
1207    return "none";
1208  else
1209    {
1210      const char *englishname = language_team_englishname ();
1211      const char *address = language_team_address ();
1212
1213      if (address != NULL && address[0] != '\0')
1214	return xasprintf ("%s %s", englishname, address);
1215      else
1216	return englishname;
1217    }
1218}
1219
1220
1221/* Construct the value for the MIME-Version field.  */
1222static const char *
1223mime_version ()
1224{
1225  return "1.0";
1226}
1227
1228
1229/* Construct the value for the Content-Type field.  */
1230static const char *
1231content_type (const char *header)
1232{
1233  bool was_utf8;
1234  const char *old_field;
1235
1236  /* If the POT file contains charset=UTF-8, it means that the POT file
1237     contains non-ASCII characters, and we keep the UTF-8 encoding.
1238     Otherwise, when the POT file is plain ASCII, we use the locale's
1239     encoding.  */
1240  was_utf8 = false;
1241  old_field = get_field (header, "Content-Type");
1242  if (old_field != NULL)
1243    {
1244      const char *charsetstr = c_strstr (old_field, "charset=");
1245
1246      if (charsetstr != NULL)
1247	{
1248	  charsetstr += strlen ("charset=");
1249	  was_utf8 = (c_strcasecmp (charsetstr, "UTF-8") == 0);
1250	}
1251    }
1252  return xasprintf ("text/plain; charset=%s",
1253		    was_utf8 ? "UTF-8" : canonical_locale_charset ());
1254}
1255
1256
1257/* Construct the value for the Content-Transfer-Encoding field.  */
1258static const char *
1259content_transfer_encoding ()
1260{
1261  return "8bit";
1262}
1263
1264
1265/* Construct the value for the Plural-Forms field.  */
1266static const char *
1267plural_forms ()
1268{
1269  size_t i;
1270
1271  /* Search for a formula depending on the catalogname.  */
1272  for (i = 0; i < plural_table_size; i++)
1273    if (strcmp (plural_table[i].lang, catalogname) == 0)
1274      return plural_table[i].value;
1275
1276  /* Search for a formula depending on the language only.  */
1277  for (i = 0; i < plural_table_size; i++)
1278    if (strcmp (plural_table[i].lang, language) == 0)
1279      return plural_table[i].value;
1280
1281  return NULL;
1282}
1283
1284
1285static struct
1286{
1287  const char *name;
1288  const char * (*getter0) (void);
1289  const char * (*getter1) (const char *header);
1290}
1291fields[] =
1292  {
1293    { "Project-Id-Version", NULL, project_id_version },
1294    { "PO-Revision-Date", NULL, po_revision_date },
1295    { "Last-Translator", last_translator, NULL },
1296    { "Language-Team", language_team, NULL },
1297    { "MIME-Version", mime_version, NULL },
1298    { "Content-Type", NULL, content_type },
1299    { "Content-Transfer-Encoding", content_transfer_encoding, NULL },
1300    { "Plural-Forms", plural_forms, NULL }
1301  };
1302
1303#define NFIELDS SIZEOF (fields)
1304#define FIELD_LAST_TRANSLATOR 2
1305
1306
1307/* Retrieve a freshly allocated copy of a field's value.  */
1308static char *
1309get_field (const char *header, const char *field)
1310{
1311  size_t len = strlen (field);
1312  const char *line;
1313
1314  for (line = header;;)
1315    {
1316      if (strncmp (line, field, len) == 0
1317	  && line[len] == ':' && line[len + 1] == ' ')
1318	{
1319	  const char *value_start;
1320	  const char *value_end;
1321	  char *value;
1322
1323	  value_start = line + len + 2;
1324	  value_end = strchr (value_start, '\n');
1325	  if (value_end == NULL)
1326	    value_end = value_start + strlen (value_start);
1327
1328	  value = XNMALLOC (value_end - value_start + 1, char);
1329	  memcpy (value, value_start, value_end - value_start);
1330	  value[value_end - value_start] = '\0';
1331
1332	  return value;
1333	}
1334
1335      line = strchr (line, '\n');
1336      if (line != NULL)
1337	line++;
1338      else
1339	break;
1340    }
1341
1342  return NULL;
1343}
1344
1345/* Add a field with value to a header, and return the new header.  */
1346static char *
1347put_field (const char *old_header, const char *field, const char *value)
1348{
1349  size_t len = strlen (field);
1350  const char *line;
1351  char *new_header;
1352  char *p;
1353
1354  for (line = old_header;;)
1355    {
1356      if (strncmp (line, field, len) == 0
1357	  && line[len] == ':' && line[len + 1] == ' ')
1358	{
1359	  const char *value_start;
1360	  const char *value_end;
1361
1362	  value_start = line + len + 2;
1363	  value_end = strchr (value_start, '\n');
1364	  if (value_end == NULL)
1365	    value_end = value_start + strlen (value_start);
1366
1367	  new_header = XNMALLOC (strlen (old_header)
1368				 - (value_end - value_start)
1369				 + strlen (value)
1370				 + (*value_end != '\n' ? 1 : 0)
1371				 + 1,
1372				 char);
1373	  p = new_header;
1374	  memcpy (p, old_header, value_start - old_header);
1375	  p += value_start - old_header;
1376	  memcpy (p, value, strlen (value));
1377	  p += strlen (value);
1378	  if (*value_end != '\n')
1379	    *p++ = '\n';
1380	  strcpy (p, value_end);
1381
1382	  return new_header;
1383	}
1384
1385      line = strchr (line, '\n');
1386      if (line != NULL)
1387	line++;
1388      else
1389	break;
1390    }
1391
1392  new_header = XNMALLOC (strlen (old_header) + 1
1393			 + len + 2 + strlen (value) + 1
1394			 + 1,
1395			 char);
1396  p = new_header;
1397  memcpy (p, old_header, strlen (old_header));
1398  p += strlen (old_header);
1399  if (p > new_header && p[-1] != '\n')
1400    *p++ = '\n';
1401  memcpy (p, field, len);
1402  p += len;
1403  *p++ = ':';
1404  *p++ = ' ';
1405  memcpy (p, value, strlen (value));
1406  p += strlen (value);
1407  *p++ = '\n';
1408  *p = '\0';
1409
1410  return new_header;
1411}
1412
1413
1414/* Return the title format string.  */
1415static const char *
1416get_title ()
1417{
1418  /* This is tricky.  We want the translation in the given locale specified by
1419     the command line, not the current locale.  But we want it in the encoding
1420     that we put into the header entry, not the encoding of that locale.
1421     We could avoid the use of OUTPUT_CHARSET by using a separate message
1422     catalog and bind_textdomain_codeset(), but that doesn't seem worth the
1423     trouble for one single message.  */
1424  const char *encoding;
1425  const char *tmp;
1426  char *old_LC_ALL;
1427  char *old_LANGUAGE;
1428  char *old_OUTPUT_CHARSET;
1429  const char *msgid;
1430  const char *english;
1431  const char *result;
1432
1433  encoding = canonical_locale_charset ();
1434
1435  /* First, the English title.  */
1436  english = xasprintf ("%s translations for %%s package",
1437		       englishname_of_language ());
1438
1439  /* Save LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables.  */
1440
1441  tmp = getenv ("LC_ALL");
1442  old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
1443
1444  tmp = getenv ("LANGUAGE");
1445  old_LANGUAGE = (tmp != NULL ? xstrdup (tmp) : NULL);
1446
1447  tmp = getenv ("OUTPUT_CHARSET");
1448  old_OUTPUT_CHARSET = (tmp != NULL ? xstrdup (tmp) : NULL);
1449
1450  xsetenv ("LC_ALL", locale, 1);
1451  unsetenv ("LANGUAGE");
1452  xsetenv ("OUTPUT_CHARSET", encoding, 1);
1453
1454#ifdef HAVE_SETLOCALE
1455  if (setlocale (LC_ALL, "") == NULL)
1456    /* Nonexistent locale.  Use the English title.  */
1457    result = english;
1458  else
1459#endif
1460    {
1461      /* Fetch the translation.  */
1462      /* TRANSLATORS: "English" needs to be replaced by your language.
1463	 For example in it.po write "Traduzioni italiani ...",
1464	 *not* "Traduzioni inglesi ...".  */
1465      msgid = N_("English translations for %s package");
1466      result = gettext (msgid);
1467      if (result != msgid && strcmp (result, msgid) != 0)
1468	/* Use the English and the foreign title.  */
1469	result = xasprintf ("%s\n%s", english, result);
1470      else
1471	/* No translation found.  Use the English title.  */
1472	result = english;
1473    }
1474
1475  /* Restore LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables.  */
1476
1477  if (old_LC_ALL != NULL)
1478    xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
1479  else
1480    unsetenv ("LC_ALL");
1481
1482  if (old_LANGUAGE != NULL)
1483    xsetenv ("LANGUAGE", old_LANGUAGE, 1), free (old_LANGUAGE);
1484  else
1485    unsetenv ("LANGUAGE");
1486
1487  if (old_OUTPUT_CHARSET != NULL)
1488    xsetenv ("OUTPUT_CHARSET", old_OUTPUT_CHARSET, 1), free (old_OUTPUT_CHARSET);
1489  else
1490    unsetenv ("OUTPUT_CHARSET");
1491
1492#ifdef HAVE_SETLOCALE
1493  setlocale (LC_ALL, "");
1494#endif
1495
1496  return result;
1497}
1498
1499
1500/* Perform a set of substitutions in a string and return the resulting
1501   string.  When subst[j][0] found, it is replaced with subst[j][1].
1502   subst[j][0] must not be the empty string.  */
1503static const char *
1504subst_string (const char *str,
1505	      unsigned int nsubst, const char *(*subst)[2])
1506{
1507  if (nsubst > 0)
1508    {
1509      char *malloced = NULL;
1510      size_t *substlen;
1511      size_t i;
1512      unsigned int j;
1513
1514      substlen = (size_t *) xmalloca (nsubst * sizeof (size_t));
1515      for (j = 0; j < nsubst; j++)
1516	{
1517	  substlen[j] = strlen (subst[j][0]);
1518	  if (substlen[j] == 0)
1519	    abort ();
1520	}
1521
1522      for (i = 0;;)
1523	{
1524	  if (str[i] == '\0')
1525	    break;
1526	  for (j = 0; j < nsubst; j++)
1527	    if (*(str + i) == *subst[j][0]
1528		&& strncmp (str + i, subst[j][0], substlen[j]) == 0)
1529	      {
1530		size_t replacement_len = strlen (subst[j][1]);
1531		size_t new_len = strlen (str) - substlen[j] + replacement_len;
1532		char *new_str = XNMALLOC (new_len + 1, char);
1533		memcpy (new_str, str, i);
1534		memcpy (new_str + i, subst[j][1], replacement_len);
1535		strcpy (new_str + i + replacement_len, str + i + substlen[j]);
1536		if (malloced != NULL)
1537		  free (malloced);
1538		str = new_str;
1539		malloced = new_str;
1540		i += replacement_len;
1541		break;
1542	      }
1543	  if (j == nsubst)
1544	    i++;
1545	}
1546
1547      freea (substlen);
1548    }
1549
1550  return str;
1551}
1552
1553/* Perform a set of substitutions on each string of a string list.
1554   When subst[j][0] found, it is replaced with subst[j][1].  subst[j][0]
1555   must not be the empty string.  */
1556static void
1557subst_string_list (string_list_ty *slp,
1558		   unsigned int nsubst, const char *(*subst)[2])
1559{
1560  size_t j;
1561
1562  for (j = 0; j < slp->nitems; j++)
1563    slp->item[j] = subst_string (slp->item[j], nsubst, subst);
1564}
1565
1566
1567/* Fill the templates in all fields of the header entry.  */
1568static msgdomain_list_ty *
1569fill_header (msgdomain_list_ty *mdlp)
1570{
1571  /* Cache the strings filled in, for use when there are multiple domains
1572     and a header entry for each domain.  */
1573  const char *field_value[NFIELDS];
1574  size_t k, j, i;
1575
1576  for (i = 0; i < NFIELDS; i++)
1577    field_value[i] = NULL;
1578
1579  for (k = 0; k < mdlp->nitems; k++)
1580    {
1581      message_list_ty *mlp = mdlp->item[k]->messages;
1582
1583      if (mlp->nitems > 0)
1584	{
1585	  message_ty *header_mp = NULL;
1586	  char *header;
1587
1588	  /* Search the header entry.  */
1589	  for (j = 0; j < mlp->nitems; j++)
1590	    if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
1591	      {
1592		header_mp = mlp->item[j];
1593		break;
1594	      }
1595
1596	  /* If it wasn't found, provide one.  */
1597	  if (header_mp == NULL)
1598	    {
1599	      static lex_pos_ty pos = { __FILE__, __LINE__ };
1600
1601	      header_mp = message_alloc (NULL, "", NULL, "", 1, &pos);
1602	      message_list_prepend (mlp, header_mp);
1603	    }
1604
1605	  header = xstrdup (header_mp->msgstr);
1606
1607	  /* Fill in the fields.  */
1608	  for (i = 0; i < NFIELDS; i++)
1609	    {
1610	      if (field_value[i] == NULL)
1611		field_value[i] =
1612		  (fields[i].getter1 != NULL
1613		   ? fields[i].getter1 (header)
1614		   : fields[i].getter0 ());
1615
1616	      if (field_value[i] != NULL)
1617		{
1618		  char *old_header = header;
1619		  header = put_field (header, fields[i].name, field_value[i]);
1620		  free (old_header);
1621		}
1622	    }
1623
1624	  /* Replace the old translation in the header entry.  */
1625	  header_mp->msgstr = header;
1626	  header_mp->msgstr_len = strlen (header) + 1;
1627
1628	  /* Update the comments in the header entry.  */
1629	  if (header_mp->comment != NULL)
1630	    {
1631	      const char *subst[4][2];
1632	      const char *id;
1633	      time_t now;
1634
1635	      id = project_id ();
1636	      subst[0][0] = "SOME DESCRIPTIVE TITLE";
1637	      subst[0][1] = xasprintf (get_title (), id, id);
1638	      subst[1][0] = "PACKAGE";
1639	      subst[1][1] = id;
1640	      subst[2][0] = "FIRST AUTHOR <EMAIL@ADDRESS>";
1641	      subst[2][1] = field_value[FIELD_LAST_TRANSLATOR];
1642	      subst[3][0] = "YEAR";
1643	      subst[3][1] =
1644		xasprintf ("%d",
1645			   (time (&now), (localtime (&now))->tm_year + 1900));
1646	      subst_string_list (header_mp->comment, SIZEOF (subst), subst);
1647	    }
1648
1649	  /* Finally remove the fuzzy attribute.  */
1650	  header_mp->is_fuzzy = false;
1651	}
1652    }
1653
1654  return mdlp;
1655}
1656
1657
1658/* Update the msgstr plural entries according to the nplurals count.  */
1659static msgdomain_list_ty *
1660update_msgstr_plurals (msgdomain_list_ty *mdlp)
1661{
1662  size_t k;
1663
1664  for (k = 0; k < mdlp->nitems; k++)
1665    {
1666      message_list_ty *mlp = mdlp->item[k]->messages;
1667      message_ty *header_entry;
1668      unsigned long int nplurals;
1669      char *untranslated_plural_msgstr;
1670      size_t j;
1671
1672      header_entry = message_list_search (mlp, NULL, "");
1673      nplurals = get_plural_count (header_entry ? header_entry->msgstr : NULL);
1674      untranslated_plural_msgstr = XNMALLOC (nplurals, char);
1675      memset (untranslated_plural_msgstr, '\0', nplurals);
1676
1677      for (j = 0; j < mlp->nitems; j++)
1678	{
1679	  message_ty *mp = mlp->item[j];
1680	  bool is_untranslated;
1681	  const char *p;
1682	  const char *pend;
1683
1684	  if (mp->msgid_plural != NULL)
1685	    {
1686	      /* Test if mp is untranslated.  (It most likely is.)  */
1687	      is_untranslated = true;
1688	      for (p = mp->msgstr, pend = p + mp->msgstr_len; p < pend; p++)
1689		if (*p != '\0')
1690		  {
1691		    is_untranslated = false;
1692		    break;
1693		  }
1694	      if (is_untranslated)
1695		{
1696		  /* Change mp->msgstr_len consecutive empty strings into
1697		     nplurals consecutive empty strings.  */
1698		  if (nplurals > mp->msgstr_len)
1699		    mp->msgstr = untranslated_plural_msgstr;
1700		  mp->msgstr_len = nplurals;
1701		}
1702	    }
1703	}
1704    }
1705  return mdlp;
1706}
1707