#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#if HAVE_PWD_H
# include <pwd.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#if CLOSEDIR_VOID
# define CLOSEDIR(d) (closedir (d), 0)
#else
# define CLOSEDIR(d) closedir (d)
#endif
#if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H
# define HAVE_DIR 1
#else
# define HAVE_DIR 0
#endif
#include "closeout.h"
#include "error.h"
#include "error-progname.h"
#include "progname.h"
#include "relocatable.h"
#include "basename.h"
#include "strpbrk.h"
#include "strstr.h"
#include "strcase.h"
#include "message.h"
#include "read-po.h"
#include "write-po.h"
#include "po-charset.h"
#include "localcharset.h"
#include "po-time.h"
#include "plural-table.h"
#include "xalloc.h"
#include "exit.h"
#include "pathname.h"
#include "xerror.h"
#include "msgl-english.h"
#include "pipe.h"
#include "wait-process.h"
#include "getline.h"
#include "xsetenv.h"
#include "str-list.h"
#include "gettext.h"
#define _(str) gettext (str)
#define N_(str) (str)
#ifndef F_OK
# define F_OK 0
#endif
#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
extern const char * _nl_locale_name (int category, const char *categoryname);
extern const char * _nl_expand_alias (const char *name);
static const char *locale;
static const char *catalogname;
static const char *language;
static bool no_translator;
static const struct option long_options[] =
{
{ "help", no_argument, NULL, 'h' },
{ "input", required_argument, NULL, 'i' },
{ "locale", required_argument, NULL, 'l' },
{ "no-translator", no_argument, NULL, CHAR_MAX + 1 },
{ "no-wrap", no_argument, NULL, CHAR_MAX + 2 },
{ "output-file", required_argument, NULL, 'o' },
{ "properties-input", no_argument, NULL, 'P' },
{ "properties-output", no_argument, NULL, 'p' },
{ "stringtable-input", no_argument, NULL, CHAR_MAX + 3 },
{ "stringtable-output", no_argument, NULL, CHAR_MAX + 4 },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w' },
{ NULL, 0, NULL, 0 }
};
static void usage (int status)
#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
__attribute__ ((noreturn))
#endif
;
static const char *find_pot (void);
static const char *catalogname_for_locale (const char *locale);
static const char *language_of_locale (const char *locale);
static char *get_field (const char *header, const char *field);
static msgdomain_list_ty *fill_header (msgdomain_list_ty *mdlp);
int
main (int argc, char **argv)
{
int opt;
bool do_help;
bool do_version;
char *output_file;
const char *input_file;
msgdomain_list_ty *result;
set_program_name (argv[0]);
error_print_progname = maybe_print_progname;
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
#endif
bindtextdomain (PACKAGE, relocate (LOCALEDIR));
textdomain (PACKAGE);
atexit (close_stdout);
do_help = false;
do_version = false;
output_file = NULL;
input_file = NULL;
locale = NULL;
while ((opt = getopt_long (argc, argv, "hi:l:o:pPVw:", long_options, NULL))
!= EOF)
switch (opt)
{
case '\0':
break;
case 'h':
do_help = true;
break;
case 'i':
if (input_file != NULL)
{
error (EXIT_SUCCESS, 0, _("at most one input file allowed"));
usage (EXIT_FAILURE);
}
input_file = optarg;
break;
case 'l':
locale = optarg;
break;
case 'o':
output_file = optarg;
break;
case 'p':
message_print_syntax_properties ();
break;
case 'P':
input_syntax = syntax_properties;
break;
case 'V':
do_version = true;
break;
case 'w':
{
int value;
char *endp;
value = strtol (optarg, &endp, 10);
if (endp != optarg)
message_page_width_set (value);
}
break;
case CHAR_MAX + 1:
no_translator = true;
break;
case CHAR_MAX + 2:
message_page_width_ignore ();
break;
case CHAR_MAX + 3:
input_syntax = syntax_stringtable;
break;
case CHAR_MAX + 4:
message_print_syntax_stringtable ();
break;
default:
usage (EXIT_FAILURE);
break;
}
if (do_version)
{
printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
"),
"2001-2003");
printf (_("Written by %s.\n"), "Bruno Haible");
exit (EXIT_SUCCESS);
}
if (do_help)
usage (EXIT_SUCCESS);
if (optind != argc)
error (EXIT_FAILURE, 0, _("too many arguments"));
if (input_file == NULL)
input_file = find_pot ();
if (locale == NULL)
{
locale = _nl_locale_name (LC_MESSAGES, "LC_MESSAGES");
if (strcmp (locale, "C") == 0)
{
multiline_error (xstrdup (""),
xstrdup (_("\
You are in a language indifferent environment. Please set\n\
your LANG environment variable, as described in the ABOUT-NLS\n\
file. This is necessary so you can test your translations.\n")));
exit (EXIT_FAILURE);
}
}
{
const char *alias = _nl_expand_alias (locale);
if (alias != NULL)
locale = alias;
}
catalogname = catalogname_for_locale (locale);
language = language_of_locale (locale);
if (output_file == NULL)
{
size_t cnlen = strlen (catalogname);
output_file = (char *) xmalloc (cnlen + 3 + 1);
memcpy (output_file, catalogname, cnlen);
memcpy (output_file + cnlen, ".po", 3 + 1);
if (access (output_file, F_OK) == 0)
{
multiline_error (xstrdup (""),
xasprintf (_("\
Output file %s already exists.\n\
Please specify the locale through the --locale option or\n\
the output .po file through the --output-file option.\n"),
output_file));
exit (EXIT_FAILURE);
}
}
result = read_po_file (input_file);
result = fill_header (result);
if (strcmp (language, "en") == 0)
result = msgdomain_list_english (result);
msgdomain_list_print (result, output_file, true, false);
if (!no_translator)
fprintf (stderr, "\n");
fprintf (stderr, _("Created %s.\n"), output_file);
exit (EXIT_SUCCESS);
}
static void
usage (int status)
{
if (status != EXIT_SUCCESS)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
program_name);
else
{
printf (_("\
Usage: %s [OPTION]\n\
"), program_name);
printf ("\n");
printf (_("\
Creates a new PO file, initializing the meta information with values from the\n\
user's environment.\n\
"));
printf ("\n");
printf (_("\
Mandatory arguments to long options are mandatory for short options too.\n"));
printf ("\n");
printf (_("\
Input file location:\n"));
printf (_("\
-i, --input=INPUTFILE input POT file\n"));
printf (_("\
If no input file is given, the current directory is searched for the POT file.\n\
If it is -, standard input is read.\n"));
printf ("\n");
printf (_("\
Output file location:\n"));
printf (_("\
-o, --output-file=FILE write output to specified PO file\n"));
printf (_("\
If no output file is given, it depends on the --locale option or the user's\n\
locale setting. If it is -, the results are written to standard output.\n"));
printf ("\n");
printf (_("\
Input file syntax:\n"));
printf (_("\
-P, --properties-input input file is in Java .properties syntax\n"));
printf (_("\
--stringtable-input input file is in NeXTstep/GNUstep .strings syntax\n"));
printf ("\n");
printf (_("\
Output details:\n"));
printf (_("\
-l, --locale=LL_CC set target locale\n"));
printf (_("\
--no-translator assume the PO file is automatically generated\n"));
printf (_("\
-p, --properties-output write out a Java .properties file\n"));
printf (_("\
--stringtable-output write out a NeXTstep/GNUstep .strings file\n"));
printf (_("\
-w, --width=NUMBER set output page width\n"));
printf (_("\
--no-wrap do not break long message lines, longer than\n\
the output page width, into several lines\n"));
printf ("\n");
printf (_("\
Informative output:\n"));
printf (_("\
-h, --help display this help and exit\n"));
printf (_("\
-V, --version output version information and exit\n"));
printf ("\n");
fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
stdout);
}
exit (status);
}
static const char *
find_pot ()
{
#if HAVE_DIR
DIR *dirp;
char *found = NULL;
dirp = opendir (".");
if (dirp != NULL)
{
for (;;)
{
struct dirent *dp;
errno = 0;
dp = readdir (dirp);
if (dp != NULL)
{
const char *name = dp->d_name;
size_t namlen = strlen (name);
if (namlen > 4 && memcmp (name + namlen - 4, ".pot", 4) == 0)
{
if (found == NULL)
found = xstrdup (name);
else
{
multiline_error (xstrdup (""),
xstrdup (_("\
Found more than one .pot file.\n\
Please specify the input .pot file through the --input option.\n")));
usage (EXIT_FAILURE);
}
}
}
else if (errno != 0)
error (EXIT_FAILURE, errno, _("error reading current directory"));
else
break;
}
if (CLOSEDIR (dirp))
error (EXIT_FAILURE, errno, _("error reading current directory"));
if (found != NULL)
return found;
}
#endif
multiline_error (xstrdup (""),
xstrdup (_("\
Found no .pot file in the current directory.\n\
Please specify the input .pot file through the --input option.\n")));
usage (EXIT_FAILURE);
return NULL;
}
static const char *
catalogname_for_locale (const char *locale)
{
static const char *locales_with_principal_territory[] = {
"af_ZA",
"am_ET",
"an_ES",
"as_IN",
"az_AZ",
"be_BY",
"bg_BG",
"bn_IN",
"bo_CN",
"br_FR",
"bs_BA",
"ca_ES",
"ce_RU",
"co_FR",
"cs_CZ",
"cy_GB",
"da_DK",
"de_DE",
"dz_BT",
"el_GR",
"es_ES",
"et_EE",
"fa_IR",
"fi_FI",
"fj_FJ",
"fo_FO",
"fr_FR",
"ga_IE",
"gd_GB",
"gu_IN",
"he_IL",
"hi_IN",
"hr_HR",
"ht_HT",
"hu_HU",
"hy_AM",
"id_ID",
"ii_CN",
"is_IS",
"it_IT",
"ja_JP",
"jv_ID",
"ka_GE",
"kk_KZ",
"kl_GL",
"km_KH",
"kn_IN",
"ko_KR",
"kok_IN",
"li_BE",
"lo_LA",
"lt_LT",
"lv_LV",
"mg_MG",
"mk_MK",
"ml_IN",
"mn_MN",
"mr_IN",
"ms_MY",
"mt_MT",
"my_MM",
"mni_IN",
"na_NR",
"nb_NO",
"ne_NP",
"nl_NL",
"nn_NO",
"no_NO",
"oc_FR",
"or_IN",
"pa_IN",
"pl_PL",
"ps_AF",
"pt_PT",
"rm_CH",
"rn_BI",
"ro_RO",
"ru_RU",
"sa_IN",
"sc_IT",
"sg_CF",
"si_LK",
"sk_SK",
"sl_SI",
"so_SO",
"sq_AL",
"sr_YU",
"sv_SE",
"te_IN",
"tg_TJ",
"th_TH",
"tk_TM",
"tl_PH",
"to_TO",
"tr_TR",
"uk_UA",
"ur_PK",
"uz_UZ",
"vi_VN",
"wa_BE",
"wen_DE"
};
const char *dot;
size_t i;
dot = strchr (locale, '.');
if (dot != NULL)
{
const char *codeset_end;
char *shorter_locale;
codeset_end = strpbrk (dot + 1, "_@+,");
if (codeset_end == NULL)
codeset_end = dot + strlen (dot);
shorter_locale = (char *) xmalloc (strlen (locale));
memcpy (shorter_locale, locale, dot - locale);
strcpy (shorter_locale + (dot - locale), codeset_end);
locale = shorter_locale;
}
for (i = 0; i < SIZEOF (locales_with_principal_territory); i++)
if (strcmp (locale, locales_with_principal_territory[i]) == 0)
{
const char *language_end;
size_t len;
char *shorter_locale;
language_end = strchr (locale, '_');
if (language_end == NULL)
abort ();
len = language_end - locale;
shorter_locale = (char *) xmalloc (len + 1);
memcpy (shorter_locale, locale, len);
shorter_locale[len] = '\0';
locale = shorter_locale;
break;
}
return locale;
}
static const char *
language_of_locale (const char *locale)
{
const char *language_end;
language_end = strpbrk (locale, "_.@+,");
if (language_end != NULL)
{
size_t len;
char *result;
len = language_end - locale;
result = (char *) xmalloc (len + 1);
memcpy (result, locale, len);
result[len] = '\0';
return result;
}
else
return locale;
}
static const char *
canonical_locale_charset ()
{
const char *tmp;
char *old_LC_ALL;
const char *charset;
tmp = getenv ("LC_ALL");
old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
xsetenv ("LC_ALL", locale, 1);
#ifdef HAVE_SETLOCALE
if (setlocale (LC_ALL, "") == NULL)
charset = "";
else
#endif
charset = locale_charset ();
if (old_LC_ALL != NULL)
xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
else
unsetenv ("LC_ALL");
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
#endif
charset = po_charset_canonicalize (charset);
if (charset == NULL)
charset = po_charset_ascii;
return charset;
}
static const char *
englishname_of_language ()
{
static struct { const char *code; const char *english; } table[] = {
{ "aa", "Afar" },
{ "ab", "Abkhazian" },
{ "ae", "Avestan" },
{ "af", "Afrikaans" },
{ "am", "Amharic" },
{ "an", "Aragonese" },
{ "ar", "Arabic" },
{ "as", "Assamese" },
{ "ay", "Aymara" },
{ "az", "Azerbaijani" },
{ "ba", "Bashkir" },
{ "be", "Belarusian" },
{ "bg", "Bulgarian" },
{ "bh", "Bihari" },
{ "bi", "Bislama" },
{ "bn", "Bengali" },
{ "bo", "Tibetan" },
{ "br", "Breton" },
{ "bs", "Bosnian" },
{ "ca", "Catalan" },
{ "ce", "Chechen" },
{ "ch", "Chamorro" },
{ "co", "Corsican" },
{ "cs", "Czech" },
{ "cu", "Church Slavic" },
{ "cv", "Chuvash" },
{ "cy", "Welsh" },
{ "da", "Danish" },
{ "de", "German" },
{ "dz", "Dzongkha" },
{ "el", "Greek" },
{ "en", "English" },
{ "eo", "Esperanto" },
{ "es", "Spanish" },
{ "et", "Estonian" },
{ "eu", "Basque" },
{ "fa", "Persian" },
{ "fi", "Finnish" },
{ "fj", "Fijian" },
{ "fo", "Faroese" },
{ "fr", "French" },
{ "fy", "Frisian" },
{ "ga", "Irish" },
{ "gd", "Scots" },
{ "gl", "Galician" },
{ "gn", "Guarani" },
{ "gu", "Gujarati" },
{ "gv", "Manx" },
{ "ha", "Hausa" },
{ "he", "Hebrew" },
{ "hi", "Hindi" },
{ "ho", "Hiri Motu" },
{ "hr", "Croatian" },
{ "ht", "Haitian" },
{ "hu", "Hungarian" },
{ "hy", "Armenian" },
{ "hz", "Herero" },
{ "ia", "Interlingua" },
{ "id", "Indonesian" },
{ "ie", "Interlingue" },
{ "ii", "Sichuan Yi" },
{ "ik", "Inupiak" },
{ "is", "Icelandic" },
{ "it", "Italian" },
{ "iu", "Inuktitut" },
{ "ja", "Japanese" },
{ "jw", "Javanese" },
{ "ka", "Georgian" },
{ "ki", "Kikuyu" },
{ "kj", "Kuanyama" },
{ "kk", "Kazakh" },
{ "kl", "Kalaallisut" },
{ "km", "Khmer" },
{ "kn", "Kannada" },
{ "ko", "Korean" },
{ "ks", "Kashmiri" },
{ "ku", "Kurdish" },
{ "kv", "Komi" },
{ "kw", "Cornish" },
{ "ky", "Kirghiz" },
{ "kok", "Konkani" },
{ "la", "Latin" },
{ "lb", "Letzeburgesch" },
{ "li", "Limburgish" },
{ "ln", "Lingala" },
{ "lo", "Laotian" },
{ "lt", "Lithuanian" },
{ "lv", "Latvian" },
{ "mg", "Malagasy" },
{ "mh", "Marshall" },
{ "mi", "Maori" },
{ "mk", "Macedonian" },
{ "ml", "Malayalam" },
{ "mn", "Mongolian" },
{ "mo", "Moldavian" },
{ "mr", "Marathi" },
{ "ms", "Malay" },
{ "mt", "Maltese" },
{ "my", "Burmese" },
{ "mni", "Manipuri" },
{ "na", "Nauru" },
{ "nb", "Norwegian Bokmal" },
{ "nd", "North Ndebele" },
{ "ne", "Nepali" },
{ "ng", "Ndonga" },
{ "nl", "Dutch" },
{ "nn", "Norwegian Nynorsk" },
{ "no", "Norwegian" },
{ "nr", "South Ndebele" },
{ "nv", "Navajo" },
{ "ny", "Nyanja" },
{ "oc", "Occitan" },
{ "om", "(Afan) Oromo" },
{ "or", "Oriya" },
{ "os", "Ossetian" },
{ "pa", "Punjabi" },
{ "pi", "Pali" },
{ "pl", "Polish" },
{ "ps", "Pashto" },
{ "pt", "Portuguese" },
{ "qu", "Quechua" },
{ "rm", "Rhaeto-Roman" },
{ "rn", "Kirundi" },
{ "ro", "Romanian" },
{ "ru", "Russian" },
{ "rw", "Kinyarwanda" },
{ "sa", "Sanskrit" },
{ "sc", "Sardinian" },
{ "sd", "Sindhi" },
{ "se", "Northern Sami" },
{ "sg", "Sango" },
{ "si", "Sinhalese" },
{ "sk", "Slovak" },
{ "sl", "Slovenian" },
{ "sm", "Samoan" },
{ "sn", "Shona" },
{ "so", "Somali" },
{ "sq", "Albanian" },
{ "sr", "Serbian" },
{ "ss", "Siswati" },
{ "st", "Sesotho" },
{ "su", "Sundanese" },
{ "sv", "Swedish" },
{ "sw", "Swahili" },
{ "ta", "Tamil" },
{ "te", "Telugu" },
{ "tg", "Tajik" },
{ "th", "Thai" },
{ "ti", "Tigrinya" },
{ "tk", "Turkmen" },
{ "tl", "Tagalog" },
{ "tn", "Setswana" },
{ "to", "Tonga" },
{ "tr", "Turkish" },
{ "ts", "Tsonga" },
{ "tt", "Tatar" },
{ "tw", "Twi" },
{ "ty", "Tahitian" },
{ "ug", "Uighur" },
{ "uk", "Ukrainian" },
{ "ur", "Urdu" },
{ "uz", "Uzbek" },
{ "vi", "Vietnamese" },
{ "vo", "Volapuk" },
{ "wo", "Wolof" },
{ "wen", "Sorbian" },
{ "xh", "Xhosa" },
{ "yi", "Yiddish" },
{ "yo", "Yoruba" },
{ "za", "Zhuang" },
{ "zh", "Chinese" },
{ "zu", "Zulu" }
};
size_t i;
for (i = 0; i < SIZEOF (table); i ++)
if (strcmp (table[i].code, language) == 0)
return table[i].english;
return xasprintf ("Language %s", language);
}
static const char *
project_id ()
{
const char *gettextlibdir;
char *prog;
char *argv[3];
pid_t child;
int fd[1];
FILE *fp;
char *line;
size_t linesize;
size_t linelen;
int exitstatus;
gettextlibdir = getenv ("GETTEXTLIBDIR");
if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
gettextlibdir = relocate (LIBDIR "/gettext");
prog = concatenated_pathname (gettextlibdir, "project-id", NULL);
argv[0] = "/bin/sh";
argv[1] = prog;
argv[2] = NULL;
child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
fd);
if (child == -1)
goto failed;
fp = fdopen (fd[0], "r");
if (fp == NULL)
{
error (0, errno, _("fdopen() failed"));
goto failed;
}
line = NULL; linesize = 0;
linelen = getline (&line, &linesize, fp);
if (linelen == (size_t)(-1))
{
error (0, 0, _("%s subprocess I/O error"), prog);
goto failed;
}
if (linelen > 0 && line[linelen - 1] == '\n')
line[linelen - 1] = '\0';
fclose (fp);
exitstatus = wait_subprocess (child, prog, false, true, false);
if (exitstatus != 0)
{
error (0, 0, _("%s subprocess failed with exit code %d"),
prog, exitstatus);
goto failed;
}
return line;
failed:
return "PACKAGE";
}
static const char *
project_id_version ()
{
const char *gettextlibdir;
char *prog;
char *argv[4];
pid_t child;
int fd[1];
FILE *fp;
char *line;
size_t linesize;
size_t linelen;
int exitstatus;
gettextlibdir = getenv ("GETTEXTLIBDIR");
if (gettextlibdir == NULL || gettextlibdir[0] == '\0')
gettextlibdir = relocate (LIBDIR "/gettext");
prog = concatenated_pathname (gettextlibdir, "project-id", NULL);
argv[0] = "/bin/sh";
argv[1] = prog;
argv[2] = "yes";
argv[3] = NULL;
child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
fd);
if (child == -1)
goto failed;
fp = fdopen (fd[0], "r");
if (fp == NULL)
{
error (0, errno, _("fdopen() failed"));
goto failed;
}
line = NULL; linesize = 0;
linelen = getline (&line, &linesize, fp);
if (linelen == (size_t)(-1))
{
error (0, 0, _("%s subprocess I/O error"), prog);
goto failed;
}
if (linelen > 0 && line[linelen - 1] == '\n')
line[linelen - 1] = '\0';
fclose (fp);
exitstatus = wait_subprocess (child, prog, false, true, false);
if (exitstatus != 0)
{
error (0, 0, _("%s subprocess failed with exit code %d"),
prog, exitstatus);
goto failed;
}
return line;
failed:
return "PACKAGE VERSION";
}
static const char *
po_revision_date (const char *header)
{
if (no_translator)
return get_field (header, "POT-Creation-Date");
else
{
time_t now;
time (&now);
return po_strftime (&now);
}
}
static struct passwd *
get_user_pwd ()
{
#if HAVE_PWD_H
const char *username;
struct passwd *userpasswd;
username = getenv ("USER");
if (username != NULL)
{
errno = 0;
userpasswd = getpwnam (username);
if (userpasswd != NULL)
return userpasswd;
if (errno != 0)
error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
}
username = getlogin ();
if (username != NULL)
{
errno = 0;
userpasswd = getpwnam (username);
if (userpasswd != NULL)
return userpasswd;
if (errno != 0)
error (EXIT_FAILURE, errno, "getpwnam(\"%s\")", username);
}
errno = 0;
userpasswd = getpwuid (getuid ());
if (userpasswd != NULL)
return userpasswd;
if (errno != 0)
error (EXIT_FAILURE, errno, "getpwuid(\"%d\")", getuid ());
#endif
return NULL;
}
static const char *
get_user_fullname ()
{
struct passwd *pwd;
const char *fullname;
const char *fullname_end;
char *result;
pwd = get_user_pwd ();
#if HAVE_PWD_H
if (pwd != NULL)
{
fullname = pwd->pw_gecos;
fullname_end = strchr (fullname, ',');
if (fullname_end == NULL)
fullname_end = fullname + strlen (fullname);
result = (char *) xmalloc (fullname_end - fullname + 1);
memcpy (result, fullname, fullname_end - fullname);
result[fullname_end - fullname] = '\0';
return result;
}
#endif
return NULL;
}
static const char *
get_user_email ()
{
const char *prog = relocate (LIBDIR "/gettext/user-email");
char *argv[4];
pid_t child;
int fd[1];
FILE *fp;
char *line;
size_t linesize;
size_t linelen;
int exitstatus;
argv[0] = "/bin/sh";
argv[1] = (char *) prog;
argv[2] = _("\
The new message catalog should contain your email address, so that users can\n\
give you feedback about the translations, and so that maintainers can contact\n\
you in case of unexpected technical problems.\n");
argv[3] = NULL;
child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
fd);
if (child == -1)
goto failed;
fp = fdopen (fd[0], "r");
if (fp == NULL)
{
error (0, errno, _("fdopen() failed"));
goto failed;
}
line = NULL; linesize = 0;
linelen = getline (&line, &linesize, fp);
if (linelen == (size_t)(-1))
{
error (0, 0, _("%s subprocess I/O error"), prog);
goto failed;
}
if (linelen > 0 && line[linelen - 1] == '\n')
line[linelen - 1] = '\0';
fclose (fp);
exitstatus = wait_subprocess (child, prog, false, true, false);
if (exitstatus != 0)
{
error (0, 0, _("%s subprocess failed with exit code %d"),
prog, exitstatus);
goto failed;
}
return line;
failed:
return "EMAIL@ADDRESS";
}
static const char *
last_translator ()
{
if (no_translator)
return "Automatically generated";
else
{
const char *fullname = get_user_fullname ();
const char *email = get_user_email ();
if (fullname != NULL)
return xasprintf ("%s <%s>", fullname, email);
else
return xasprintf ("<%s>", email);
}
}
static const char *
language_team_address ()
{
const char *prog = relocate (PROJECTSDIR "/team-address");
char *argv[7];
pid_t child;
int fd[1];
FILE *fp;
char *line;
size_t linesize;
size_t linelen;
int exitstatus;
argv[0] = "/bin/sh";
argv[1] = (char *) prog;
argv[2] = (char *) relocate (PROJECTSDIR);
argv[3] = (char *) relocate (LIBDIR "/gettext");
argv[4] = (char *) catalogname;
argv[5] = (char *) language;
argv[6] = NULL;
child = create_pipe_in (prog, "/bin/sh", argv, DEV_NULL, false, true, false,
fd);
if (child == -1)
goto failed;
fp = fdopen (fd[0], "r");
if (fp == NULL)
{
error (0, errno, _("fdopen() failed"));
goto failed;
}
line = NULL; linesize = 0;
linelen = getline (&line, &linesize, fp);
if (linelen == (size_t)(-1))
line = "";
else if (linelen > 0 && line[linelen - 1] == '\n')
line[linelen - 1] = '\0';
fclose (fp);
exitstatus = wait_subprocess (child, prog, false, true, false);
if (exitstatus != 0)
{
error (0, 0, _("%s subprocess failed with exit code %d"),
prog, exitstatus);
goto failed;
}
return line;
failed:
return "";
}
static const char *
language_team ()
{
if (no_translator)
return "none";
else
{
const char *englishname = englishname_of_language ();
const char *address = language_team_address ();
if (address != NULL && address[0] != '\0')
return xasprintf ("%s %s", englishname, address);
else
return englishname;
}
}
static const char *
mime_version ()
{
return "1.0";
}
static const char *
content_type (const char *header)
{
bool was_utf8;
const char *old_field;
was_utf8 = false;
old_field = get_field (header, "Content-Type");
if (old_field != NULL)
{
const char *charsetstr = strstr (old_field, "charset=");
if (charsetstr != NULL)
{
charsetstr += strlen ("charset=");
was_utf8 = (strcasecmp (charsetstr, "UTF-8") == 0);
}
}
return xasprintf ("text/plain; charset=%s",
was_utf8 ? "UTF-8" : canonical_locale_charset ());
}
static const char *
content_transfer_encoding ()
{
return "8bit";
}
static const char *
plural_forms ()
{
size_t i;
for (i = 0; i < plural_table_size; i++)
if (strcmp (plural_table[i].lang, catalogname) == 0)
return plural_table[i].value;
for (i = 0; i < plural_table_size; i++)
if (strcmp (plural_table[i].lang, language) == 0)
return plural_table[i].value;
return NULL;
}
static struct
{
const char *name;
const char * (*getter0) (void);
const char * (*getter1) (const char *header);
}
fields[] =
{
{ "Project-Id-Version", project_id_version, NULL },
{ "PO-Revision-Date", NULL, po_revision_date },
{ "Last-Translator", last_translator, NULL },
{ "Language-Team", language_team, NULL },
{ "MIME-Version", mime_version, NULL },
{ "Content-Type", NULL, content_type },
{ "Content-Transfer-Encoding", content_transfer_encoding, NULL },
{ "Plural-Forms", plural_forms, NULL }
};
#define NFIELDS SIZEOF (fields)
#define FIELD_LAST_TRANSLATOR 2
static char *
get_field (const char *header, const char *field)
{
size_t len = strlen (field);
const char *line;
for (line = header;;)
{
if (strncmp (line, field, len) == 0
&& line[len] == ':' && line[len + 1] == ' ')
{
const char *value_start;
const char *value_end;
char *value;
value_start = line + len + 2;
value_end = strchr (value_start, '\n');
if (value_end == NULL)
value_end = value_start + strlen (value_start);
value = (char *) xmalloc (value_end - value_start + 1);
memcpy (value, value_start, value_end - value_start);
value[value_end - value_start] = '\0';
return value;
}
line = strchr (line, '\n');
if (line != NULL)
line++;
else
break;
}
return NULL;
}
static char *
put_field (const char *old_header, const char *field, const char *value)
{
size_t len = strlen (field);
const char *line;
char *new_header;
char *p;
for (line = old_header;;)
{
if (strncmp (line, field, len) == 0
&& line[len] == ':' && line[len + 1] == ' ')
{
const char *value_start;
const char *value_end;
value_start = line + len + 2;
value_end = strchr (value_start, '\n');
if (value_end == NULL)
value_end = value_start + strlen (value_start);
new_header = (char *) xmalloc (strlen (old_header)
- (value_end - value_start)
+ strlen (value)
+ (*value_end != '\n' ? 1 : 0)
+ 1);
p = new_header;
memcpy (p, old_header, value_start - old_header);
p += value_start - old_header;
memcpy (p, value, strlen (value));
p += strlen (value);
if (*value_end != '\n')
*p++ = '\n';
strcpy (p, value_end);
return new_header;
}
line = strchr (line, '\n');
if (line != NULL)
line++;
else
break;
}
new_header = (char *) xmalloc (strlen (old_header) + 1
+ len + 2 + strlen (value) + 1
+ 1);
p = new_header;
memcpy (p, old_header, strlen (old_header));
p += strlen (old_header);
if (p > new_header && p[-1] != '\n')
*p++ = '\n';
memcpy (p, field, len);
p += len;
*p++ = ':';
*p++ = ' ';
memcpy (p, value, strlen (value));
p += strlen (value);
*p++ = '\n';
*p = '\0';
return new_header;
}
static const char *
get_title ()
{
const char *encoding;
const char *tmp;
char *old_LC_ALL;
char *old_LANGUAGE;
char *old_OUTPUT_CHARSET;
const char *msgid;
const char *english;
const char *result;
encoding = canonical_locale_charset ();
english = xasprintf ("%s translations for %%s package",
englishname_of_language ());
tmp = getenv ("LC_ALL");
old_LC_ALL = (tmp != NULL ? xstrdup (tmp) : NULL);
tmp = getenv ("LANGUAGE");
old_LANGUAGE = (tmp != NULL ? xstrdup (tmp) : NULL);
tmp = getenv ("OUTPUT_CHARSET");
old_OUTPUT_CHARSET = (tmp != NULL ? xstrdup (tmp) : NULL);
xsetenv ("LC_ALL", locale, 1);
unsetenv ("LANGUAGE");
xsetenv ("OUTPUT_CHARSET", encoding, 1);
#ifdef HAVE_SETLOCALE
if (setlocale (LC_ALL, "") == NULL)
result = english;
else
#endif
{
msgid = N_("English translations for %s package");
result = gettext (msgid);
if (result != msgid && strcmp (result, msgid) != 0)
result = xasprintf ("%s\n%s", english, result);
else
result = english;
}
if (old_LC_ALL != NULL)
xsetenv ("LC_ALL", old_LC_ALL, 1), free (old_LC_ALL);
else
unsetenv ("LC_ALL");
if (old_LANGUAGE != NULL)
xsetenv ("LANGUAGE", old_LANGUAGE, 1), free (old_LANGUAGE);
else
unsetenv ("LANGUAGE");
if (old_OUTPUT_CHARSET != NULL)
xsetenv ("OUTPUT_CHARSET", old_OUTPUT_CHARSET, 1), free (old_OUTPUT_CHARSET);
else
unsetenv ("OUTPUT_CHARSET");
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
#endif
return result;
}
static const char *
subst_string (const char *str,
unsigned int nsubst, const char *(*subst)[2])
{
if (nsubst > 0)
{
char *malloced = NULL;
size_t *substlen;
size_t i;
unsigned int j;
substlen = (size_t *) alloca (nsubst * sizeof (size_t));
for (j = 0; j < nsubst; j++)
{
substlen[j] = strlen (subst[j][0]);
if (substlen[j] == 0)
abort ();
}
for (i = 0;;)
{
if (str[i] == '\0')
break;
for (j = 0; j < nsubst; j++)
if (*(str + i) == *subst[j][0]
&& strncmp (str + i, subst[j][0], substlen[j]) == 0)
{
size_t replacement_len = strlen (subst[j][1]);
size_t new_len = strlen (str) - substlen[j] + replacement_len;
char *new_str = (char *) xmalloc (new_len + 1);
memcpy (new_str, str, i);
memcpy (new_str + i, subst[j][1], replacement_len);
strcpy (new_str + i + replacement_len, str + i + substlen[j]);
if (malloced != NULL)
free (malloced);
str = new_str;
malloced = new_str;
i += replacement_len;
break;
}
if (j == nsubst)
i++;
}
}
return str;
}
static void
subst_string_list (string_list_ty *slp,
unsigned int nsubst, const char *(*subst)[2])
{
size_t j;
for (j = 0; j < slp->nitems; j++)
slp->item[j] = subst_string (slp->item[j], nsubst, subst);
}
static msgdomain_list_ty *
fill_header (msgdomain_list_ty *mdlp)
{
const char *field_value[NFIELDS];
size_t k, j, i;
for (i = 0; i < NFIELDS; i++)
field_value[i] = NULL;
for (k = 0; k < mdlp->nitems; k++)
{
message_list_ty *mlp = mdlp->item[k]->messages;
if (mlp->nitems > 0)
{
message_ty *header_mp = NULL;
char *header;
const char *subst[4][2];
const char *id;
time_t now;
for (j = 0; j < mlp->nitems; j++)
if (mlp->item[j]->msgid[0] == '\0' && !mlp->item[j]->obsolete)
{
header_mp = mlp->item[j];
break;
}
if (header_mp == NULL)
{
static lex_pos_ty pos = { __FILE__, __LINE__ };
header_mp = message_alloc ("", NULL, "", 1, &pos);
message_list_prepend (mlp, header_mp);
}
header = xstrdup (header_mp->msgstr);
for (i = 0; i < NFIELDS; i++)
{
if (field_value[i] == NULL)
field_value[i] =
(fields[i].getter1 != NULL
? fields[i].getter1 (header)
: fields[i].getter0 ());
if (field_value[i] != NULL)
{
char *old_header = header;
header = put_field (header, fields[i].name, field_value[i]);
free (old_header);
}
}
header_mp->msgstr = header;
header_mp->msgstr_len = strlen (header) + 1;
id = project_id ();
subst[0][0] = "SOME DESCRIPTIVE TITLE";
subst[0][1] = xasprintf (get_title (), id, id);
subst[1][0] = "PACKAGE";
subst[1][1] = id;
subst[2][0] = "FIRST AUTHOR <EMAIL@ADDRESS>";
subst[2][1] = field_value[FIELD_LAST_TRANSLATOR];
subst[3][0] = "YEAR";
subst[3][1] =
xasprintf ("%d", (time (&now), (localtime (&now))->tm_year + 1900));
subst_string_list (header_mp->comment, SIZEOF (subst), subst);
header_mp->is_fuzzy = false;
}
}
return mdlp;
}