Imported Upstream version 0.18.3.2
[platform/upstream/gettext.git] / gettext-tools / src / msginit.c
1 /* Initializes a new PO file.
2    Copyright (C) 2001-2012 Free Software Foundation, Inc.
3    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 #include <alloca.h>
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <limits.h>
28 #include <locale.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include <sys/types.h>
34
35 #if HAVE_PWD_H
36 # include <pwd.h>
37 #endif
38
39 #include <unistd.h>
40
41 #if HAVE_DIRENT_H
42 # include <dirent.h>
43 #endif
44
45 #if HAVE_DIRENT_H
46 # define HAVE_DIR 1
47 #else
48 # define HAVE_DIR 0
49 #endif
50
51 #include "closeout.h"
52 #include "error.h"
53 #include "error-progname.h"
54 #include "progname.h"
55 #include "relocatable.h"
56 #include "basename.h"
57 #include "c-strstr.h"
58 #include "c-strcase.h"
59 #include "message.h"
60 #include "read-catalog.h"
61 #include "read-po.h"
62 #include "read-properties.h"
63 #include "read-stringtable.h"
64 #include "write-catalog.h"
65 #include "write-po.h"
66 #include "write-properties.h"
67 #include "write-stringtable.h"
68 #include "color.h"
69 #include "po-charset.h"
70 #include "localcharset.h"
71 #include "localename.h"
72 #include "po-time.h"
73 #include "plural-table.h"
74 #include "lang-table.h"
75 #include "xalloc.h"
76 #include "xmalloca.h"
77 #include "concat-filename.h"
78 #include "xerror.h"
79 #include "xvasprintf.h"
80 #include "msgl-english.h"
81 #include "plural-count.h"
82 #include "spawn-pipe.h"
83 #include "wait-process.h"
84 #include "xsetenv.h"
85 #include "str-list.h"
86 #include "propername.h"
87 #include "gettext.h"
88
89 #define _(str) gettext (str)
90 #define N_(str) (str)
91
92 /* Get F_OK.  It is lacking from <fcntl.h> on Woe32.  */
93 #ifndef F_OK
94 # define F_OK 0
95 #endif
96
97 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
98
99 extern const char * _nl_expand_alias (const char *name);
100
101 /* Locale name.  */
102 static const char *locale;
103
104 /* Language (ISO-639 code) and optional territory (ISO-3166 code).  */
105 static const char *catalogname;
106
107 /* Language (ISO-639 code).  */
108 static const char *language;
109
110 /* If true, the user is not considered to be the translator.  */
111 static bool no_translator;
112
113 /* Long options.  */
114 static const struct option long_options[] =
115 {
116   { "color", optional_argument, NULL, CHAR_MAX + 5 },
117   { "help", no_argument, NULL, 'h' },
118   { "input", required_argument, NULL, 'i' },
119   { "locale", required_argument, NULL, 'l' },
120   { "no-translator", no_argument, NULL, CHAR_MAX + 1 },
121   { "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
122   { "output-file", required_argument, NULL, 'o' },
123   { "properties-input", no_argument, NULL, 'P' },
124   { "properties-output", no_argument, NULL, 'p' },
125   { "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
126   { "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
127   { "style", required_argument, NULL, CHAR_MAX + 6 },
128   { "version", no_argument, NULL, 'V' },
129   { "width", required_argument, NULL, 'w' },
130   { NULL, 0, NULL, 0 }
131 };
132
133 /* Forward declaration of local functions.  */
134 static void usage (int status)
135 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
136      __attribute__ ((noreturn))
137 #endif
138 ;
139 static const char *find_pot (void);
140 static const char *catalogname_for_locale (const char *locale);
141 static const char *language_of_locale (const char *locale);
142 static char *get_field (const char *header, const char *field);
143 static msgdomain_list_ty *fill_header (msgdomain_list_ty *mdlp);
144 static msgdomain_list_ty *update_msgstr_plurals (msgdomain_list_ty *mdlp);
145
146
147 int
148 main (int argc, char **argv)
149 {
150   int opt;
151   bool do_help;
152   bool do_version;
153   char *output_file;
154   const char *input_file;
155   msgdomain_list_ty *result;
156   catalog_input_format_ty input_syntax = &input_format_po;
157   catalog_output_format_ty output_syntax = &output_format_po;
158
159   /* Set program name for messages.  */
160   set_program_name (argv[0]);
161   error_print_progname = maybe_print_progname;
162
163 #ifdef HAVE_SETLOCALE
164   /* Set locale via LC_ALL.  */
165   setlocale (LC_ALL, "");
166 #endif
167
168   /* Set the text message domain.  */
169   bindtextdomain (PACKAGE, relocate (LOCALEDIR));
170   bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
171   textdomain (PACKAGE);
172
173   /* Ensure that write errors on stdout are detected.  */
174   atexit (close_stdout);
175
176   /* Set default values for variables.  */
177   do_help = false;
178   do_version = false;
179   output_file = NULL;
180   input_file = NULL;
181   locale = NULL;
182
183   while ((opt = getopt_long (argc, argv, "hi:l:o:pPVw:", long_options, NULL))
184          != EOF)
185     switch (opt)
186       {
187       case '\0':                /* Long option.  */
188         break;
189
190       case 'h':
191         do_help = true;
192         break;
193
194       case 'i':
195         if (input_file != NULL)
196           {
197             error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
198             usage (EXIT_FAILURE);
199           }
200         input_file = optarg;
201         break;
202
203       case 'l':
204         locale = optarg;
205         break;
206
207       case 'o':
208         output_file = optarg;
209         break;
210
211       case 'p':
212         output_syntax = &output_format_properties;
213         break;
214
215       case 'P':
216         input_syntax = &input_format_properties;
217         break;
218
219       case 'V':
220         do_version = true;
221         break;
222
223       case 'w':
224         {
225           int value;
226           char *endp;
227           value = strtol (optarg, &endp, 10);
228           if (endp != optarg)
229             message_page_width_set (value);
230         }
231         break;
232
233       case CHAR_MAX + 1:
234         no_translator = true;
235         break;
236
237       case CHAR_MAX + 2: /* --no-wrap */
238         message_page_width_ignore ();
239         break;
240
241       case CHAR_MAX + 3: /* --stringtable-input */
242         input_syntax = &input_format_stringtable;
243         break;
244
245       case CHAR_MAX + 4: /* --stringtable-output */
246         output_syntax = &output_format_stringtable;
247         break;
248
249       case CHAR_MAX + 5: /* --color */
250         if (handle_color_option (optarg) || color_test_mode)
251           usage (EXIT_FAILURE);
252         break;
253
254       case CHAR_MAX + 6: /* --style */
255         handle_style_option (optarg);
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\
269 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
270 This is free software: you are free to change and redistribute it.\n\
271 There is NO WARRANTY, to the extent permitted by law.\n\
272 "),
273               "2001-2010");
274       printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
275       exit (EXIT_SUCCESS);
276     }
277
278   /* Help is requested.  */
279   if (do_help)
280     usage (EXIT_SUCCESS);
281
282   /* Test for extraneous arguments.  */
283   if (optind != argc)
284     error (EXIT_FAILURE, 0, _("too many arguments"));
285
286   /* Search for the input file.  */
287   if (input_file == NULL)
288     input_file = find_pot ();
289
290   /* Determine target locale.  */
291   if (locale == NULL)
292     {
293       locale = gl_locale_name (LC_MESSAGES, "LC_MESSAGES");
294       if (strcmp (locale, "C") == 0)
295         {
296           multiline_error (xstrdup (""),
297                            xstrdup (_("\
298 You are in a language indifferent environment.  Please set\n\
299 your LANG environment variable, as described in the ABOUT-NLS\n\
300 file.  This is necessary so you can test your translations.\n")));
301           exit (EXIT_FAILURE);
302         }
303     }
304   {
305     const char *alias = _nl_expand_alias (locale);
306     if (alias != NULL)
307       locale = alias;
308   }
309   catalogname = catalogname_for_locale (locale);
310   language = language_of_locale (locale);
311
312   /* Default output file name is CATALOGNAME.po.  */
313   if (output_file == NULL)
314     {
315       output_file = xasprintf ("%s.po", catalogname);
316
317       /* But don't overwrite existing PO files.  */
318       if (access (output_file, F_OK) == 0)
319         {
320           multiline_error (xstrdup (""),
321                            xasprintf (_("\
322 Output file %s already exists.\n\
323 Please specify the locale through the --locale option or\n\
324 the output .po file through the --output-file option.\n"),
325                                       output_file));
326           exit (EXIT_FAILURE);
327         }
328     }
329
330   /* Read input file.  */
331   result = read_catalog_file (input_file, input_syntax);
332
333   /* Fill the header entry.  */
334   result = fill_header (result);
335
336   /* Initialize translations.  */
337   if (strcmp (language, "en") == 0)
338     result = msgdomain_list_english (result);
339   else
340     result = update_msgstr_plurals (result);
341
342   /* Write the modified message list out.  */
343   msgdomain_list_print (result, output_file, output_syntax, true, false);
344
345   if (!no_translator)
346     fprintf (stderr, "\n");
347   fprintf (stderr, _("Created %s.\n"), output_file);
348
349   exit (EXIT_SUCCESS);
350 }
351
352
353 /* Display usage information and exit.  */
354 static void
355 usage (int status)
356 {
357   if (status != EXIT_SUCCESS)
358     fprintf (stderr, _("Try '%s --help' for more information.\n"),
359              program_name);
360   else
361     {
362       printf (_("\
363 Usage: %s [OPTION]\n\
364 "), program_name);
365       printf ("\n");
366       /* xgettext: no-wrap */
367       printf (_("\
368 Creates a new PO file, initializing the meta information with values from the\n\
369 user's environment.\n\
370 "));
371       printf ("\n");
372       printf (_("\
373 Mandatory arguments to long options are mandatory for short options too.\n"));
374       printf ("\n");
375       printf (_("\
376 Input file location:\n"));
377       printf (_("\
378   -i, --input=INPUTFILE       input POT file\n"));
379       printf (_("\
380 If no input file is given, the current directory is searched for the POT file.\n\
381 If it is -, standard input is read.\n"));
382       printf ("\n");
383       printf (_("\
384 Output file location:\n"));
385       printf (_("\
386   -o, --output-file=FILE      write output to specified PO file\n"));
387       printf (_("\
388 If no output file is given, it depends on the --locale option or the user's\n\
389 locale setting.  If it is -, the results are written to standard output.\n"));
390       printf ("\n");
391       printf (_("\
392 Input file syntax:\n"));
393       printf (_("\
394   -P, --properties-input      input file is in Java .properties syntax\n"));
395       printf (_("\
396       --stringtable-input     input file is in NeXTstep/GNUstep .strings syntax\n"));
397       printf ("\n");
398       printf (_("\
399 Output details:\n"));
400       printf (_("\
401   -l, --locale=LL_CC          set target locale\n"));
402       printf (_("\
403       --no-translator         assume the PO file is automatically generated\n"));
404       printf (_("\
405       --color                 use colors and other text attributes always\n\
406       --color=WHEN            use colors and other text attributes if WHEN.\n\
407                               WHEN may be 'always', 'never', 'auto', or 'html'.\n"));
408       printf (_("\
409       --style=STYLEFILE       specify CSS style rule file for --color\n"));
410       printf (_("\
411   -p, --properties-output     write out a Java .properties file\n"));
412       printf (_("\
413       --stringtable-output    write out a NeXTstep/GNUstep .strings file\n"));
414       printf (_("\
415   -w, --width=NUMBER          set output page width\n"));
416       printf (_("\
417       --no-wrap               do not break long message lines, longer than\n\
418                               the output page width, into several lines\n"));
419       printf ("\n");
420       printf (_("\
421 Informative output:\n"));
422       printf (_("\
423   -h, --help                  display this help and exit\n"));
424       printf (_("\
425   -V, --version               output version information and exit\n"));
426       printf ("\n");
427       /* TRANSLATORS: The placeholder indicates the bug-reporting address
428          for this package.  Please add _another line_ saying
429          "Report translation bugs to <...>\n" with the address for translation
430          bugs (typically your translation team's web or email address).  */
431       fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
432              stdout);
433     }
434
435   exit (status);
436 }
437
438
439 /* Search for the POT file and return its name.  */
440 static const char *
441 find_pot ()
442 {
443 #if HAVE_DIR
444   DIR *dirp;
445   char *found = NULL;
446
447   dirp = opendir (".");
448   if (dirp != NULL)
449     {
450       for (;;)
451         {
452           struct dirent *dp;
453
454           errno = 0;
455           dp = readdir (dirp);
456           if (dp != NULL)
457             {
458               const char *name = dp->d_name;
459               size_t namlen = strlen (name);
460
461               if (namlen > 4 && memcmp (name + namlen - 4, ".pot", 4) == 0)
462                 {
463                   if (found == NULL)
464                     found = xstrdup (name);
465                   else
466                     {
467                       multiline_error (xstrdup (""),
468                                        xstrdup (_("\
469 Found more than one .pot file.\n\
470 Please specify the input .pot file through the --input option.\n")));
471                       usage (EXIT_FAILURE);
472                     }
473                 }
474             }
475           else if (errno != 0)
476             error (EXIT_FAILURE, errno, _("error reading current directory"));
477           else
478             break;
479         }
480       if (closedir (dirp))
481         error (EXIT_FAILURE, errno, _("error reading current directory"));
482
483       if (found != NULL)
484         return found;
485     }
486 #endif
487
488   multiline_error (xstrdup (""),
489                    xstrdup (_("\
490 Found no .pot file in the current directory.\n\
491 Please specify the input .pot file through the --input option.\n")));
492   usage (EXIT_FAILURE);
493   /* NOTREACHED */
494   return NULL;
495 }
496
497
498 /* Return the gettext catalog name corresponding to a locale.  If the locale
499    consists of a language and a territory, and the language is mainly spoken
500    in that territory, the territory is removed from the locale name.
501    For example, "de_DE" or "de_DE.ISO-8859-1" are simplified to "de",
502    because the resulting catalog can be used as a default for all "de_XX",
503    such as "de_AT".  */
504 static const char *
505 catalogname_for_locale (const char *locale)
506 {
507   static const char *locales_with_principal_territory[] = {
508                 /* Language     Main territory */
509     "ace_ID",   /* Achinese     Indonesia */
510     "af_ZA",    /* Afrikaans    South Africa */
511     "ak_GH",    /* Akan         Ghana */
512     "am_ET",    /* Amharic      Ethiopia */
513     "an_ES",    /* Aragonese    Spain */
514     "ang_GB",   /* Old English  Britain */
515     "arn_CL",   /* Mapudungun   Chile */
516     "as_IN",    /* Assamese     India */
517     "ast_ES",   /* Asturian     Spain */
518     "av_RU",    /* Avaric       Russia */
519     "awa_IN",   /* Awadhi       India */
520     "az_AZ",    /* Azerbaijani  Azerbaijan */
521     "ban_ID",   /* Balinese     Indonesia */
522     "be_BY",    /* Belarusian   Belarus */
523     "bej_SD",   /* Beja         Sudan */
524     "bem_ZM",   /* Bemba        Zambia */
525     "bg_BG",    /* Bulgarian    Bulgaria */
526     "bho_IN",   /* Bhojpuri     India */
527     "bik_PH",   /* Bikol        Philippines */
528     "bin_NG",   /* Bini         Nigeria */
529     "bm_ML",    /* Bambara      Mali */
530     "bn_IN",    /* Bengali      India */
531     "bo_CN",    /* Tibetan      China */
532     "br_FR",    /* Breton       France */
533     "bs_BA",    /* Bosnian      Bosnia */
534     "bug_ID",   /* Buginese     Indonesia */
535     "ca_ES",    /* Catalan      Spain */
536     "ce_RU",    /* Chechen      Russia */
537     "ceb_PH",   /* Cebuano      Philippines */
538     "co_FR",    /* Corsican     France */
539     "cr_CA",    /* Cree         Canada */
540     /* Don't put "crh_UZ" or "crh_UA" here.  That would be asking for fruitless
541        political discussion.  */
542     "cs_CZ",    /* Czech        Czech Republic */
543     "csb_PL",   /* Kashubian    Poland */
544     "cy_GB",    /* Welsh        Britain */
545     "da_DK",    /* Danish       Denmark */
546     "de_DE",    /* German       Germany */
547     "din_SD",   /* Dinka        Sudan */
548     "doi_IN",   /* Dogri        India */
549     "dsb_DE",   /* Lower Sorbian        Germany */
550     "dv_MV",    /* Divehi       Maldives */
551     "dz_BT",    /* Dzongkha     Bhutan */
552     "ee_GH",    /* Ã‰wé          Ghana */
553     "el_GR",    /* Greek        Greece */
554     /* Don't put "en_GB" or "en_US" here.  That would be asking for fruitless
555        political discussion.  */
556     "es_ES",    /* Spanish      Spain */
557     "et_EE",    /* Estonian     Estonia */
558     "fa_IR",    /* Persian      Iran */
559     "fi_FI",    /* Finnish      Finland */
560     "fil_PH",   /* Filipino     Philippines */
561     "fj_FJ",    /* Fijian       Fiji */
562     "fo_FO",    /* Faroese      Faeroe Islands */
563     "fon_BJ",   /* Fon          Benin */
564     "fr_FR",    /* French       France */
565     "fur_IT",   /* Friulian     Italy */
566     "fy_NL",    /* Western Frisian      Netherlands */
567     "ga_IE",    /* Irish        Ireland */
568     "gd_GB",    /* Scottish Gaelic      Britain */
569     "gon_IN",   /* Gondi        India */
570     "gsw_CH",   /* Swiss German Switzerland */
571     "gu_IN",    /* Gujarati     India */
572     "he_IL",    /* Hebrew       Israel */
573     "hi_IN",    /* Hindi        India */
574     "hil_PH",   /* Hiligaynon   Philippines */
575     "hr_HR",    /* Croatian     Croatia */
576     "hsb_DE",   /* Upper Sorbian        Germany */
577     "ht_HT",    /* Haitian      Haiti */
578     "hu_HU",    /* Hungarian    Hungary */
579     "hy_AM",    /* Armenian     Armenia */
580     "id_ID",    /* Indonesian   Indonesia */
581     "ig_NG",    /* Igbo         Nigeria */
582     "ii_CN",    /* Sichuan Yi   China */
583     "ilo_PH",   /* Iloko        Philippines */
584     "is_IS",    /* Icelandic    Iceland */
585     "it_IT",    /* Italian      Italy */
586     "ja_JP",    /* Japanese     Japan */
587     "jab_NG",   /* Hyam         Nigeria */
588     "jv_ID",    /* Javanese     Indonesia */
589     "ka_GE",    /* Georgian     Georgia */
590     "kab_DZ",   /* Kabyle       Algeria */
591     "kaj_NG",   /* Jju          Nigeria */
592     "kam_KE",   /* Kamba        Kenya */
593     "kmb_AO",   /* Kimbundu     Angola */
594     "kcg_NG",   /* Tyap         Nigeria */
595     "kdm_NG",   /* Kagoma       Nigeria */
596     "kg_CD",    /* Kongo        Democratic Republic of Congo */
597     "kk_KZ",    /* Kazakh       Kazakhstan */
598     "kl_GL",    /* Kalaallisut  Greenland */
599     "km_KH",    /* Central Khmer        Cambodia */
600     "kn_IN",    /* Kannada      India */
601     "ko_KR",    /* Korean       Korea (South) */
602     "kok_IN",   /* Konkani      India */
603     "kr_NG",    /* Kanuri       Nigeria */
604     "kru_IN",   /* Kurukh       India */
605     "lg_UG",    /* Ganda        Uganda */
606     "li_BE",    /* Limburgish   Belgium */
607     "lo_LA",    /* Laotian      Laos */
608     "lt_LT",    /* Lithuanian   Lithuania */
609     "lu_CD",    /* Luba-Katanga Democratic Republic of Congo */
610     "lua_CD",   /* Luba-Lulua   Democratic Republic of Congo */
611     "luo_KE",   /* Luo          Kenya */
612     "lv_LV",    /* Latvian      Latvia */
613     "mad_ID",   /* Madurese     Indonesia */
614     "mag_IN",   /* Magahi       India */
615     "mai_IN",   /* Maithili     India */
616     "mak_ID",   /* Makasar      Indonesia */
617     "man_ML",   /* Mandingo     Mali */
618     "men_SL",   /* Mende        Sierra Leone */
619     "mg_MG",    /* Malagasy     Madagascar */
620     "mi_NZ",    /* Maori        New Zealand */
621     "min_ID",   /* Minangkabau  Indonesia */
622     "mk_MK",    /* Macedonian   Macedonia */
623     "ml_IN",    /* Malayalam    India */
624     "mn_MN",    /* Mongolian    Mongolia */
625     "mni_IN",   /* Manipuri     India */
626     "mos_BF",   /* Mossi        Burkina Faso */
627     "mr_IN",    /* Marathi      India */
628     "ms_MY",    /* Malay        Malaysia */
629     "mt_MT",    /* Maltese      Malta */
630     "mwr_IN",   /* Marwari      India */
631     "my_MM",    /* Burmese      Myanmar */
632     "na_NR",    /* Nauru        Nauru */
633     "nah_MX",   /* Nahuatl      Mexico */
634     "nap_IT",   /* Neapolitan   Italy */
635     "nb_NO",    /* Norwegian BokmÃ¥l    Norway */
636     "nds_DE",   /* Low Saxon    Germany */
637     "ne_NP",    /* Nepali       Nepal */
638     "nl_NL",    /* Dutch        Netherlands */
639     "nn_NO",    /* Norwegian Nynorsk    Norway */
640     "no_NO",    /* Norwegian    Norway */
641     "nr_ZA",    /* South Ndebele        South Africa */
642     "nso_ZA",   /* Northern Sotho       South Africa */
643     "nym_TZ",   /* Nyamwezi     Tanzania */
644     "nyn_UG",   /* Nyankole     Uganda */
645     "oc_FR",    /* Occitan      France */
646     "oj_CA",    /* Ojibwa       Canada */
647     "or_IN",    /* Oriya        India */
648     "pa_IN",    /* Punjabi      India */
649     "pag_PH",   /* Pangasinan   Philippines */
650     "pam_PH",   /* Pampanga     Philippines */
651     "pap_AN",   /* Papiamento   Netherlands Antilles */
652     "pbb_CO",   /* Páez                Colombia */
653     "pl_PL",    /* Polish       Poland */
654     "ps_AF",    /* Pashto       Afghanistan */
655     "pt_PT",    /* Portuguese   Portugal */
656     "raj_IN",   /* Rajasthani   India */
657     "rm_CH",    /* Romansh      Switzerland */
658     "rn_BI",    /* Kirundi      Burundi */
659     "ro_RO",    /* Romanian     Romania */
660     "ru_RU",    /* Russian      Russia */
661     "sa_IN",    /* Sanskrit     India */
662     "sah_RU",   /* Yakut        Russia */
663     "sas_ID",   /* Sasak        Indonesia */
664     "sat_IN",   /* Santali      India */
665     "sc_IT",    /* Sardinian    Italy */
666     "scn_IT",   /* Sicilian     Italy */
667     "sg_CF",    /* Sango        Central African Republic */
668     "shn_MM",   /* Shan         Myanmar */
669     "si_LK",    /* Sinhala      Sri Lanka */
670     "sid_ET",   /* Sidamo       Ethiopia */
671     "sk_SK",    /* Slovak       Slovakia */
672     "sl_SI",    /* Slovenian    Slovenia */
673     "smn_FI",   /* Inari Sami   Finland */
674     "sms_FI",   /* Skolt Sami   Finland */
675     "so_SO",    /* Somali       Somalia */
676     "sq_AL",    /* Albanian     Albania */
677     "sr_RS",    /* Serbian      Serbia */
678     "sr_YU",    /* Serbian      Yugoslavia - this line can be removed in 2010 */
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(\"%d\")", 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   size_t i;
1336
1337   /* Search for a formula depending on the catalogname.  */
1338   for (i = 0; i < plural_table_size; i++)
1339     if (strcmp (plural_table[i].lang, catalogname) == 0)
1340       return plural_table[i].value;
1341
1342   /* Search for a formula depending on the language only.  */
1343   for (i = 0; i < plural_table_size; i++)
1344     if (strcmp (plural_table[i].lang, language) == 0)
1345       return plural_table[i].value;
1346
1347   return NULL;
1348 }
1349
1350
1351 static struct
1352 {
1353   const char *name;
1354   const char * (*getter0) (void);
1355   const char * (*getter1) (const char *header);
1356 }
1357 fields[] =
1358   {
1359     { "Project-Id-Version", NULL, project_id_version },
1360     { "PO-Revision-Date", NULL, po_revision_date },
1361     { "Last-Translator", last_translator, NULL },
1362     { "Language-Team", language_team, NULL },
1363     { "Language", language_value, NULL },
1364     { "MIME-Version", mime_version, NULL },
1365     { "Content-Type", NULL, content_type },
1366     { "Content-Transfer-Encoding", content_transfer_encoding, NULL },
1367     { "Plural-Forms", plural_forms, NULL }
1368   };
1369
1370 #define NFIELDS SIZEOF (fields)
1371 #define FIELD_LAST_TRANSLATOR 2
1372
1373
1374 /* Retrieve a freshly allocated copy of a field's value.  */
1375 static char *
1376 get_field (const char *header, const char *field)
1377 {
1378   size_t len = strlen (field);
1379   const char *line;
1380
1381   for (line = header;;)
1382     {
1383       if (strncmp (line, field, len) == 0 && line[len] == ':')
1384         {
1385           const char *value_start;
1386           const char *value_end;
1387           char *value;
1388
1389           value_start = line + len + 1;
1390           if (*value_start == ' ')
1391             value_start++;
1392           value_end = strchr (value_start, '\n');
1393           if (value_end == NULL)
1394             value_end = value_start + strlen (value_start);
1395
1396           value = XNMALLOC (value_end - value_start + 1, char);
1397           memcpy (value, value_start, value_end - value_start);
1398           value[value_end - value_start] = '\0';
1399
1400           return value;
1401         }
1402
1403       line = strchr (line, '\n');
1404       if (line != NULL)
1405         line++;
1406       else
1407         break;
1408     }
1409
1410   return NULL;
1411 }
1412
1413 /* Add a field with value to a header, and return the new header.  */
1414 static char *
1415 put_field (const char *old_header, const char *field, const char *value)
1416 {
1417   size_t len = strlen (field);
1418   const char *line;
1419   char *new_header;
1420   char *p;
1421
1422   for (line = old_header;;)
1423     {
1424       if (strncmp (line, field, len) == 0 && line[len] == ':')
1425         {
1426           const char *value_start;
1427           const char *value_end;
1428
1429           value_start = line + len + 1;
1430           if (*value_start == ' ')
1431             value_start++;
1432           value_end = strchr (value_start, '\n');
1433           if (value_end == NULL)
1434             value_end = value_start + strlen (value_start);
1435
1436           new_header = XNMALLOC (strlen (old_header)
1437                                  - (value_end - value_start)
1438                                  + strlen (value)
1439                                  + (*value_end != '\n' ? 1 : 0)
1440                                  + 1,
1441                                  char);
1442           p = new_header;
1443           memcpy (p, old_header, value_start - old_header);
1444           p += value_start - old_header;
1445           memcpy (p, value, strlen (value));
1446           p += strlen (value);
1447           if (*value_end != '\n')
1448             *p++ = '\n';
1449           strcpy (p, value_end);
1450
1451           return new_header;
1452         }
1453
1454       line = strchr (line, '\n');
1455       if (line != NULL)
1456         line++;
1457       else
1458         break;
1459     }
1460
1461   new_header = XNMALLOC (strlen (old_header) + 1
1462                          + len + 2 + strlen (value) + 1
1463                          + 1,
1464                          char);
1465   p = new_header;
1466   memcpy (p, old_header, strlen (old_header));
1467   p += strlen (old_header);
1468   if (p > new_header && p[-1] != '\n')
1469     *p++ = '\n';
1470   memcpy (p, field, len);
1471   p += len;
1472   *p++ = ':';
1473   *p++ = ' ';
1474   memcpy (p, value, strlen (value));
1475   p += strlen (value);
1476   *p++ = '\n';
1477   *p = '\0';
1478
1479   return new_header;
1480 }
1481
1482
1483 /* Return the title format string.  */
1484 static const char *
1485 get_title ()
1486 {
1487   /* This is tricky.  We want the translation in the given locale specified by
1488      the command line, not the current locale.  But we want it in the encoding
1489      that we put into the header entry, not the encoding of that locale.
1490      We could avoid the use of OUTPUT_CHARSET by using a separate message
1491      catalog and bind_textdomain_codeset(), but that doesn't seem worth the
1492      trouble for one single message.  */
1493   const char *encoding;
1494   const char *tmp;
1495   char *old_LC_ALL;
1496   char *old_LANGUAGE;
1497   char *old_OUTPUT_CHARSET;
1498   const char *msgid;
1499   const char *english;
1500   const char *result;
1501
1502   encoding = canonical_locale_charset ();
1503
1504   /* First, the English title.  */
1505   english = xasprintf ("%s translations for %%s package",
1506                        englishname_of_language ());
1507
1508   /* Save LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables.  */
1509
1510   tmp = getenv ("LC_ALL");
1511   old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
1512
1513   tmp = getenv ("LANGUAGE");
1514   old_LANGUAGE = (tmp != NULL ? xstrdup (tmp) : NULL);
1515
1516   tmp = getenv ("OUTPUT_CHARSET");
1517   old_OUTPUT_CHARSET = (tmp != NULL ? xstrdup (tmp) : NULL);
1518
1519   xsetenv ("LC_ALL", locale, 1);
1520   unsetenv ("LANGUAGE");
1521   xsetenv ("OUTPUT_CHARSET", encoding, 1);
1522
1523 #ifdef HAVE_SETLOCALE
1524   if (setlocale (LC_ALL, "") == NULL)
1525     /* Nonexistent locale.  Use the English title.  */
1526     result = english;
1527   else
1528 #endif
1529     {
1530       /* Fetch the translation.  */
1531       /* TRANSLATORS: "English" needs to be replaced by your language.
1532          For example in it.po write "Traduzioni italiani ...",
1533          *not* "Traduzioni inglesi ...".  */
1534       msgid = N_("English translations for %s package");
1535       result = gettext (msgid);
1536       if (result != msgid && strcmp (result, msgid) != 0)
1537         /* Use the English and the foreign title.  */
1538         result = xasprintf ("%s\n%s", english, result);
1539       else
1540         /* No translation found.  Use the English title.  */
1541         result = english;
1542     }
1543
1544   /* Restore LC_ALL, LANGUAGE, OUTPUT_CHARSET environment variables.  */
1545
1546   if (old_LC_ALL != NULL)
1547     xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
1548   else
1549     unsetenv ("LC_ALL");
1550
1551   if (old_LANGUAGE != NULL)
1552     xsetenv ("LANGUAGE", old_LANGUAGE, 1), free (old_LANGUAGE);
1553   else
1554     unsetenv ("LANGUAGE");
1555
1556   if (old_OUTPUT_CHARSET != NULL)
1557     xsetenv ("OUTPUT_CHARSET", old_OUTPUT_CHARSET, 1), free (old_OUTPUT_CHARSET);
1558   else
1559     unsetenv ("OUTPUT_CHARSET");
1560
1561 #ifdef HAVE_SETLOCALE
1562   setlocale (LC_ALL, "");
1563 #endif
1564
1565   return result;
1566 }
1567
1568
1569 /* Perform a set of substitutions in a string and return the resulting
1570    string.  When subst[j][0] found, it is replaced with subst[j][1].
1571    subst[j][0] must not be the empty string.  */
1572 static const char *
1573 subst_string (const char *str,
1574               unsigned int nsubst, const char *(*subst)[2])
1575 {
1576   if (nsubst > 0)
1577     {
1578       char *malloced = NULL;
1579       size_t *substlen;
1580       size_t i;
1581       unsigned int j;
1582
1583       substlen = (size_t *) xmalloca (nsubst * sizeof (size_t));
1584       for (j = 0; j < nsubst; j++)
1585         {
1586           substlen[j] = strlen (subst[j][0]);
1587           if (substlen[j] == 0)
1588             abort ();
1589         }
1590
1591       for (i = 0;;)
1592         {
1593           if (str[i] == '\0')
1594             break;
1595           for (j = 0; j < nsubst; j++)
1596             if (*(str + i) == *subst[j][0]
1597                 && strncmp (str + i, subst[j][0], substlen[j]) == 0)
1598               {
1599                 size_t replacement_len = strlen (subst[j][1]);
1600                 size_t new_len = strlen (str) - substlen[j] + replacement_len;
1601                 char *new_str = XNMALLOC (new_len + 1, char);
1602                 memcpy (new_str, str, i);
1603                 memcpy (new_str + i, subst[j][1], replacement_len);
1604                 strcpy (new_str + i + replacement_len, str + i + substlen[j]);
1605                 if (malloced != NULL)
1606                   free (malloced);
1607                 str = new_str;
1608                 malloced = new_str;
1609                 i += replacement_len;
1610                 break;
1611               }
1612           if (j == nsubst)
1613             i++;
1614         }
1615
1616       freea (substlen);
1617     }
1618
1619   return str;
1620 }
1621
1622 /* Perform a set of substitutions on each string of a string list.
1623    When subst[j][0] found, it is replaced with subst[j][1].  subst[j][0]
1624    must not be the empty string.  */
1625 static void
1626 subst_string_list (string_list_ty *slp,
1627                    unsigned int nsubst, const char *(*subst)[2])
1628 {
1629   size_t j;
1630
1631   for (j = 0; j < slp->nitems; j++)
1632     slp->item[j] = subst_string (slp->item[j], nsubst, subst);
1633 }
1634
1635
1636 /* Fill the templates in all fields of the header entry.  */
1637 static msgdomain_list_ty *
1638 fill_header (msgdomain_list_ty *mdlp)
1639 {
1640   /* Cache the strings filled in, for use when there are multiple domains
1641      and a header entry for each domain.  */
1642   const char *field_value[NFIELDS];
1643   size_t k, j, i;
1644
1645   for (i = 0; i < NFIELDS; i++)
1646     field_value[i] = NULL;
1647
1648   for (k = 0; k < mdlp->nitems; k++)
1649     {
1650       message_list_ty *mlp = mdlp->item[k]->messages;
1651
1652       if (mlp->nitems > 0)
1653         {
1654           message_ty *header_mp = NULL;
1655           char *header;
1656
1657           /* Search the header entry.  */
1658           for (j = 0; j < mlp->nitems; j++)
1659             if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
1660               {
1661                 header_mp = mlp->item[j];
1662                 break;
1663               }
1664
1665           /* If it wasn't found, provide one.  */
1666           if (header_mp == NULL)
1667             {
1668               static lex_pos_ty pos = { __FILE__, __LINE__ };
1669
1670               header_mp = message_alloc (NULL, "", NULL, "", 1, &pos);
1671               message_list_prepend (mlp, header_mp);
1672             }
1673
1674           header = xstrdup (header_mp->msgstr);
1675
1676           /* Fill in the fields.  */
1677           for (i = 0; i < NFIELDS; i++)
1678             {
1679               if (field_value[i] == NULL)
1680                 field_value[i] =
1681                   (fields[i].getter1 != NULL
1682                    ? fields[i].getter1 (header)
1683                    : fields[i].getter0 ());
1684
1685               if (field_value[i] != NULL)
1686                 {
1687                   char *old_header = header;
1688                   header = put_field (header, fields[i].name, field_value[i]);
1689                   free (old_header);
1690                 }
1691             }
1692
1693           /* Replace the old translation in the header entry.  */
1694           header_mp->msgstr = header;
1695           header_mp->msgstr_len = strlen (header) + 1;
1696
1697           /* Update the comments in the header entry.  */
1698           if (header_mp->comment != NULL)
1699             {
1700               const char *subst[4][2];
1701               const char *id;
1702               time_t now;
1703
1704               id = project_id (header);
1705               subst[0][0] = "SOME DESCRIPTIVE TITLE";
1706               subst[0][1] = xasprintf (get_title (), id, id);
1707               subst[1][0] = "PACKAGE";
1708               subst[1][1] = id;
1709               subst[2][0] = "FIRST AUTHOR <EMAIL@ADDRESS>";
1710               subst[2][1] = field_value[FIELD_LAST_TRANSLATOR];
1711               subst[3][0] = "YEAR";
1712               subst[3][1] =
1713                 xasprintf ("%d",
1714                            (time (&now), (localtime (&now))->tm_year + 1900));
1715               subst_string_list (header_mp->comment, SIZEOF (subst), subst);
1716             }
1717
1718           /* Finally remove the fuzzy attribute.  */
1719           header_mp->is_fuzzy = false;
1720         }
1721     }
1722
1723   return mdlp;
1724 }
1725
1726
1727 /* Update the msgstr plural entries according to the nplurals count.  */
1728 static msgdomain_list_ty *
1729 update_msgstr_plurals (msgdomain_list_ty *mdlp)
1730 {
1731   size_t k;
1732
1733   for (k = 0; k < mdlp->nitems; k++)
1734     {
1735       message_list_ty *mlp = mdlp->item[k]->messages;
1736       message_ty *header_entry;
1737       unsigned long int nplurals;
1738       char *untranslated_plural_msgstr;
1739       size_t j;
1740
1741       header_entry = message_list_search (mlp, NULL, "");
1742       nplurals = get_plural_count (header_entry ? header_entry->msgstr : NULL);
1743       untranslated_plural_msgstr = XNMALLOC (nplurals, char);
1744       memset (untranslated_plural_msgstr, '\0', nplurals);
1745
1746       for (j = 0; j < mlp->nitems; j++)
1747         {
1748           message_ty *mp = mlp->item[j];
1749           bool is_untranslated;
1750           const char *p;
1751           const char *pend;
1752
1753           if (mp->msgid_plural != NULL)
1754             {
1755               /* Test if mp is untranslated.  (It most likely is.)  */
1756               is_untranslated = true;
1757               for (p = mp->msgstr, pend = p + mp->msgstr_len; p < pend; p++)
1758                 if (*p != '\0')
1759                   {
1760                     is_untranslated = false;
1761                     break;
1762                   }
1763               if (is_untranslated)
1764                 {
1765                   /* Change mp->msgstr_len consecutive empty strings into
1766                      nplurals consecutive empty strings.  */
1767                   if (nplurals > mp->msgstr_len)
1768                     mp->msgstr = untranslated_plural_msgstr;
1769                   mp->msgstr_len = nplurals;
1770                 }
1771             }
1772         }
1773     }
1774   return mdlp;
1775 }