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