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