diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/Makefile.am gettext.hacked/Makefile.am --- gettext.initial/Makefile.am Mon Feb 22 13:40:47 1999 +++ gettext.hacked/Makefile.am Mon Feb 22 14:10:19 1999 @@ -19,7 +19,7 @@ AUTOMAKE_OPTIONS = 1.3 gnits MAINT_CHARSET = latin1 -ACLOCAL_AMFLAGS = -I m4 +ACLOCAL_AMFLAGS = --acdir=m4 --acdir=$(shell aclocal --print-ac-dir) gettextsrcdir = $(datadir)/gettext gettextsrc_DATA = ABOUT-NLS diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/configure.in gettext.hacked/configure.in --- gettext.initial/configure.in Mon Feb 22 13:40:47 1999 +++ gettext.hacked/configure.in Mon Feb 22 14:10:20 1999 @@ -12,7 +12,7 @@ AM_PROG_LIBTOOL AC_PROG_CC AC_ISC_POSIX -AM_PROG_INSTALL +AC_PROG_INSTALL AC_PROG_YACC dnl Checks for libraries. diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/doc/stamp-vti gettext.hacked/doc/stamp-vti --- gettext.initial/doc/stamp-vti Mon Feb 22 13:40:47 1999 +++ gettext.hacked/doc/stamp-vti Mon Apr 19 18:54:47 1999 @@ -1,3 +1,3 @@ -@set UPDATED 30 April 1998 +@set UPDATED 21 March 1999 @set EDITION 0.10.35 @set VERSION 0.10.35 diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/doc/version.texi gettext.hacked/doc/version.texi --- gettext.initial/doc/version.texi Mon Feb 22 13:40:47 1999 +++ gettext.hacked/doc/version.texi Mon Apr 19 18:54:47 1999 @@ -1,3 +1,3 @@ -@set UPDATED 30 April 1998 +@set UPDATED 21 March 1999 @set EDITION 0.10.35 @set VERSION 0.10.35 diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/po/gettext.pot gettext.hacked/po/gettext.pot --- gettext.initial/po/gettext.pot Mon Feb 22 13:40:48 1999 +++ gettext.hacked/po/gettext.pot Thu May 6 17:32:13 1999 @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 1998-04-30 22:50-0700\n" +"POT-Creation-Date: 1999-05-05 18:24-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -143,23 +143,23 @@ msgid "Report bugs to .\n" msgstr "" -#: src/message.c:784 +#: src/message.c:787 #, c-format msgid "" "internationalized messages should not contain the `\\%c' escape sequence" msgstr "" -#: src/message.c:1115 +#: src/message.c:1268 src/message.c:1586 #, c-format msgid "cannot create output file \"%s\"" msgstr "" -#: src/message.c:1122 +#: src/message.c:1275 src/message.c:1591 #, no-c-format msgid "standard output" msgstr "" -#: src/message.c:1182 +#: src/message.c:1335 src/message.c:1633 #, c-format msgid "error while writing \"%s\" file" msgstr "" diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/Makefile.am gettext.hacked/src/Makefile.am --- gettext.initial/src/Makefile.am Mon Feb 22 13:40:48 1999 +++ gettext.hacked/src/Makefile.am Mon Feb 22 18:43:39 1999 @@ -19,7 +19,7 @@ AUTOMAKE_OPTIONS = 1.2 gnits -bin_PROGRAMS = gettext msgcmp msgfmt msgmerge msgunfmt xgettext msgcomm +bin_PROGRAMS = gettext msgcmp msgfmt msgmerge msgunfmt xgettext msgcomm msghack noinst_HEADERS = domain.h message.h po-gram.h po-hash.h po-lex.h po.h \ str-list.h xget-lex.h po-gram.gen.h po-hash.gen.h dir-list.h @@ -52,7 +52,8 @@ po.c str-list.c xget-lex.c xgettext.c dir-list.c msgcomm_SOURCES = msgcomm.c message.c po-gram.gen.c po-hash.gen.c po-lex.c \ open-po.c po.c str-list.c dir-list.c - +msghack_SOURCES = msghack.c open-po.c dir-list.c message.c str-list.c \ +po-gram.gen.c po-hash.gen.c po-lex.c po.c MAINTAINERCLEANFILES = po-gram.gen.c po-gram.gen.h po-hash.gen.c po-hash.gen.h # Some rules for yacc handling. diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/message.c gettext.hacked/src/message.c --- gettext.initial/src/message.c Mon Feb 22 13:40:48 1999 +++ gettext.hacked/src/message.c Mon Apr 19 05:21:23 1999 @@ -68,6 +68,9 @@ static void message_print PARAMS ((const message_ty *__mp, FILE *__fp, const char *__domain, int blank_line, int __debug)); +static void master_message_print PARAMS ((const message_ty *__mp, + FILE *__fp, + int blank_line)); static void message_print_obsolete PARAMS ((const message_ty *__mp, FILE *__fp, const char *__domain, int blank_line)); @@ -990,6 +993,156 @@ static void +master_message_print (mp, fp, blank_line) + const message_ty *mp; + FILE *fp; + int blank_line; +{ + size_t j; + + /* Separate messages with a blank line. Uniforum doesn't like blank + lines, so use an empty comment (unless there already is one). */ + if (blank_line && (!uniforum + || mp->comment == NULL + || mp->comment->nitems == 0 + || mp->comment->item[0][0] != '\0')) + print_blank_line (fp); + + if (mp->comment != NULL) + for (j = 0; j < mp->comment->nitems; ++j) { + const unsigned char *s = mp->comment->item[j]; + do { + const unsigned char *e; + putc ('#', fp); + /* FIXME This is the wrong locale. While + message_list_print set the "C" locale for LC_CTYPE, + the need to use the correct locale for the file's + contents. */ + if (*s != '\0' && !isspace (*s)) + putc (' ', fp); + e = strchr (s, '\n'); + if (e == NULL) { + fputs (s, fp); + s = NULL; + } else { + fwrite (s, 1, e - s, fp); + s = e + 1; + } + putc ('\n', fp); + } while (s != NULL); + } + + if (mp->comment_dot != NULL) + for (j = 0; j < mp->comment_dot->nitems; ++j) { + const unsigned char *s = mp->comment_dot->item[j]; + putc ('#', fp); + putc ('.', fp); + /* FIXME This is the wrong locale. While + message_list_print set the "C" locale for LC_CTYPE, the + need to use the correct locale for the file's contents. */ + if (*s && !isspace (*s)) + putc (' ', fp); + fputs (s, fp); + putc ('\n', fp); + } + + + /* Print the file position comments for every domain. This will + help a human who is trying to navigate the sources. There is no + problem of getting repeat positions, because duplicates are + checked for. */ + if (mp->filepos_count != 0) { + if (uniforum) + for (j = 0; j < mp->filepos_count; ++j) { + lex_pos_ty *pp = &mp->filepos[j]; + char *cp = pp->file_name; + while (cp[0] == '.' && cp[1] == '/') + cp += 2; + /* There are two Sun formats to choose from: SunOS and + Solaris. Use the Solaris form here. */ + fprintf (fp, "# File: %s, line: %ld\n", + cp, (long) pp->line_number); + } else { + size_t column; + + fputs ("#:", fp); + column = 2; + for (j = 0; j < mp->filepos_count; ++j) { + lex_pos_ty *pp; + char buffer[20]; + char *cp; + size_t len; + + pp = &mp->filepos[j]; + cp = pp->file_name; + while (cp[0] == '.' && cp[1] == '/') + cp += 2; + sprintf (buffer, "%ld", (long) pp->line_number); + len = strlen (cp) + strlen (buffer) + 2; + if (column > 2 && column + len >= page_width) + { + fputs ("\n#:", fp); + column = 2; + } + fprintf (fp, " %s:%s", cp, buffer); + column += len; + } + putc ('\n', fp); + } + } + + /* Print flag information in special comment. */ + if (mp->is_fuzzy || significant_c_format_p (mp->is_c_format) || mp->do_wrap == no) { + int first_flag = 1; + + putc ('#', fp); + putc (',', fp); + + /* We don't print the fuzzy flag if the msgstr is empty. This + might be introduced by the user but we want to normalize the + output. */ + if (mp->is_fuzzy) { + fputs (" fuzzy", fp); + first_flag = 0; + } + + if (significant_c_format_p (mp->is_c_format)) { + if (!first_flag) + putc (',', fp); + + fputs (make_c_format_description_string (mp->is_c_format, 0), + fp); + first_flag = 0; + } + + if (mp->do_wrap == no) { + if (!first_flag) + putc (',', fp); + + fputs (make_c_width_description_string (mp->do_wrap), fp); + first_flag = 0; + } + + putc ('\n', fp); + } + + /* Print each of the message components. Wrap them nicely so they + are as readable as possible. If there is no recorded msgstr for + this domain, emit an empty string. */ + wrap (fp, NULL, "msgid", mp->msgid, mp->do_wrap); + for (j = 0; j < mp->variant_count; ++j) { + char *msgstr; + + msgstr = malloc(strlen(mp->variant[j].domain) + 10); + sprintf(msgstr, "msgstr(%s)", mp->variant[j].domain); + + wrap (fp, NULL, msgstr, mp->variant[j].msgstr, mp->do_wrap); + } + +} + + +static void message_print_obsolete (mp, fp, domain, blank_line) const message_ty *mp; FILE *fp; @@ -1402,3 +1555,83 @@ page_width = n; } + + +/* + * HACKS... This prints a mesage list, but it uses the + * msgstr(somain) syntax instead + */ + void +master_list_print (mlp, filename) + message_list_ty *mlp; + const char *filename; +{ + FILE *fp; + size_t j; + int blank_line; +#ifdef HAVE_SETLOCALE + char *old_locale; +#endif + + /* We will not write anything if we have no message or only the + header entry. */ + if (mlp->nitems == 0 || (mlp->nitems == 1 && *mlp->item[0]->msgid == '\0')) + return; + + /* Open the output file. */ + if (filename != NULL && strcmp (filename, "-") != 0 + && strcmp (filename, "/dev/stdout") != 0) { + fp = fopen (filename, "w"); + if (fp == NULL) + error (EXIT_FAILURE, errno, _("cannot create output file \"%s\""), + filename); + } else { + fp = stdout; + /* xgettext:no-c-format */ + filename = _("standard output"); + } + +#ifdef HAVE_SETLOCALE + /* FIXME This is the wrong locale. The program is currently set for + the user's native language locale, for the error messages. This + code sets it to the "C" locale, but that isn't right either. The + need is to use the correct locale for the file's contents. */ + old_locale = setlocale (LC_CTYPE, "C"); + if (old_locale) + old_locale = xstrdup (old_locale); +#endif + + /* Write out each of the messages for this domain. */ + for (j = 0; j < mlp->nitems; ++j) + if (mlp->item[j]->obsolete == 0) { + master_message_print (mlp->item[j], fp, blank_line); + blank_line = 1; + } + +#if 0 /* screw the obsoletes */ + /* Write out each of the obsolete messages for this domain. */ + for (j = 0; j < mlp->nitems; ++j) + if (mlp->item[j]->obsolete != 0) { + message_print_obsolete (mlp->item[j], fp, dl->item[k], blank_line); + blank_line = 1; + } +#endif + + /* Restore the old locale. Do this before emitting error messages, + so that the correct locale is used for the error. (Ideally, + error should ensure this before calling gettext for the format + string.) */ +#ifdef HAVE_SETLOCALE + if (old_locale) { + setlocale (LC_CTYPE, old_locale); + free (old_locale); + } +#endif + + /* Make sure nothing went wrong. */ + if (fflush (fp)) + error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"), + filename); + fclose (fp); +} + diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/message.h gettext.hacked/src/message.h --- gettext.initial/src/message.h Mon Feb 22 13:40:48 1999 +++ gettext.hacked/src/message.h Mon Apr 19 05:21:23 1999 @@ -126,4 +126,7 @@ int possible_c_format_p PARAMS ((enum is_c_format)); void message_page_width_set PARAMS ((size_t width)); +/* Hacks go at the end */ +void master_list_print PARAMS ((message_list_ty *__mlp, const char *__filename)); + #endif /* message.h */ diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/msghack.c gettext.hacked/src/msghack.c --- gettext.initial/src/msghack.c Wed Dec 31 19:00:00 1969 +++ gettext.hacked/src/msghack.c Sun Feb 27 13:30:00 2000 @@ -0,0 +1,1606 @@ +/* GNU gettext - internationalization aids + Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + This file was written by Cristian Gafton + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + +#if HAVE_STRING_H +# include +#else +# include +#endif + +#if HAVE_LOCALE_H +# include +#endif + +#include +#include + +#include "dir-list.h" +#include "error.h" +#include "message.h" +#include +#include +#include "po.h" + +#define _(str) gettext (str) + + +/* This structure defines a derived class of the po_ty class. (See + po.h for an explanation.) */ +typedef struct merge_class_ty merge_class_ty; +struct merge_class_ty +{ + /* inherited instance variables, etc */ + PO_BASE_TY + + /* Name of domain we are currently examining. */ + char *domain; + + /* List of domains already appeared in the current file. */ + string_list_ty *domain_list; + + /* List of messages already appeared in the current file. */ + message_list_ty *mlp; + + /* Accumulate comments for next message directive */ + string_list_ty *comment; + string_list_ty *comment_dot; + + /* Flags transported in special comments. */ + int is_fuzzy; + enum is_c_format is_c_format; + enum is_c_format do_wrap; + + /* Accumulate filepos comments for the next message directive. */ + size_t filepos_count; + lex_pos_ty *filepos; +}; + + +/* String containing name the program is called with. */ +const char *program_name; + +/* Verbosity level. */ +static int verbosity_level; + +/* If nonzero, remember comments for file name and line number for each + msgid, if present in the reference input. Defaults to true. */ +static int line_comment = 1; + +/* Force output of PO file even if empty. */ +static int force_po; + +/* should we merge in the fuzzy messages ? */ +static int fuzzy = 0; + +/* Do we want to generate useless merges? */ +static int useless = 0; + +/* Possible split operations */ +#define SPLIT_MSGID 1 +#define SPLIT_MSGSTR 2 +static int split_flag = 0; + +/* Possible explode operations */ +#define EXPLODE_MSGID 4 +#define EXPLODE_MSGSTR 8 +static int explode_flag = 0; + +/* Sort orders */ +#define SORT_BY_FILEPOS 1 +#define SORT_BY_MSGID 2 +static int sort_order = 0; + +/* Other flags */ +static int comments = 0; + +/* Prototypes for local functions. */ +static void usage PARAMS ((int __status)); +static void error_print PARAMS ((void)); +static void merge_constructor PARAMS ((po_ty *__that)); +static void merge_destructor PARAMS ((po_ty *__that)); +static void merge_directive_domain PARAMS ((po_ty *__that, char *__name)); +static void merge_directive_message PARAMS ((po_ty *__that, char *__msgid, + lex_pos_ty *__msgid_pos, + char *__msgstr, lex_pos_ty *__msgstr_pos)); +static void merge_parse_brief PARAMS ((po_ty *__that)); +static void merge_parse_debrief PARAMS ((po_ty *__that)); +static void merge_comment PARAMS ((po_ty *__that, const char *__s)); +static void merge_comment_dot PARAMS ((po_ty *__that, const char *__s)); +static void merge_comment_special PARAMS ((po_ty *__that, const char *__s)); +static void merge_comment_filepos PARAMS ((po_ty *__that, const char *__name, + int __line)); +static message_list_ty *grammar PARAMS ((const char *__filename)); +static message_list_ty *append PARAMS ((const char *__fn1, const char *__fn2, int diff_only)); +static message_list_ty *merge PARAMS ((const char *__fn1, const char *__fn2)); +static message_list_ty *invert PARAMS ((const char *__fn)); +static message_list_ty *missing PARAMS ((const char *__fn)); +static message_list_ty *un_duplicate PARAMS ((message_list_ty *dup_list)); +static message_list_ty *empty PARAMS ((const char *__fn)); +static message_list_ty *dummy PARAMS ((const char *__fn)); +static message_list_ty *split PARAMS ((const char *__fn, int __flag)); +static message_list_ty *explode PARAMS ((const char *__fn, int __flag)); +static message_list_ty *master PARAMS ((message_list_ty **prev, const char *__fn, + const char *__master_id)); +static message_ty *message_join PARAMS ((message_ty *def, message_ty *ref)); +static struct message_ty *new_header PARAMS((void)); + +/* What kind of actions we can make */ +#define ACTION_DO_SPLIT 1 +#define ACTION_DO_INVERT 2 +#define ACTION_DO_MERGE 3 +#define ACTION_DO_EMPTY 4 +#define ACTION_DO_EXPLODE 5 +#define ACTION_DO_MASTER 6 +#define ACTION_DO_SORT 7 +#define ACTION_DO_APPEND 8 +#define ACTION_DO_UNDUP 9 +#define ACTION_DO_MISSING 10 +#define ACTION_DO_DUMMY 11 +#define ACTION_DO_DIFF 12 + +int main (argc, argv) + int argc; + char **argv; +{ + int opt; + int do_action = 0; + char *output_file; + message_list_ty *result; + + /* Long options. */ + const struct option long_options[] = + { + /* command pairs */ + { "split-id", no_argument, &split_flag, SPLIT_MSGID }, + { "split-str", no_argument, &split_flag, SPLIT_MSGSTR }, + + { "explode-id", required_argument, &explode_flag, EXPLODE_MSGID }, + { "explode-str", required_argument, &explode_flag, EXPLODE_MSGSTR }, + + { "sort-by-file", no_argument, &sort_order, SORT_BY_FILEPOS }, + { "sort-by-id", no_argument, &sort_order, SORT_BY_MSGID }, + + /* flags */ + { "fuzzy", no_argument, &fuzzy, 1 }, + { "force-po", no_argument, &force_po, 1 }, + { "width", required_argument, NULL, 'w', }, + { "output-file", required_argument, NULL, 'o' }, + { "useless", no_argument, &useless, 1 }, + + /* + * ACTIONS + */ + /* switch the msgid and the msgstr strings in a PO file */ + { "invert", no_argument, &do_action, ACTION_DO_INVERT }, + + /* fill the msgstr with the contents of msgid */ + { "dummy", no_argument, &do_action, ACTION_DO_DUMMY }, + + /* merge two files based on commin msgid. The msgid is stored as a comment */ + { "merge", no_argument, &do_action, ACTION_DO_MERGE }, + + /* strip out the msgstr creating a POT form a PO file */ + { "empty", no_argument, &do_action, ACTION_DO_EMPTY }, + + /* combine sevaral PO files into a "master" catalog */ + { "master", no_argument, &do_action, ACTION_DO_MASTER }, + + /* append entries from ref POT that do not exist in file PO */ + { "append", no_argument, &do_action, ACTION_DO_APPEND }, + + /* show only stuff from ref POT that does not exist in file PO */ + { "diff", no_argument, &do_action, ACTION_DO_DIFF }, + + /* ommit comments for the 'append' and 'diff' */ + { "comments", no_argument, &comments, 1 }, + + /* remove duplicates */ + { "unduplicate", no_argument, &do_action, ACTION_DO_UNDUP }, + + /* output strings that need translation (fuzzies and untranslated */ + { "missing", no_argument, &do_action, ACTION_DO_MISSING }, + + /* output strings that need translation (fuzzies and untranslated */ + { "missing", no_argument, &do_action, ACTION_DO_MISSING }, + + /* INFO */ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + + { NULL, 0, NULL, 0 } + }; + + + /* Set program name for messages. */ + program_name = argv[0]; + verbosity_level = 0; + error_print_progname = error_print; + gram_max_allowed_errors = INT_MAX; + +#ifdef HAVE_SETLOCALE + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); +#endif + + /* Set the text message domain. */ + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + /* Set default values for variables. */ + output_file = NULL; + + while ((opt + = getopt_long (argc, argv, "ho:Vw:v", long_options, NULL)) + != EOF) + switch (opt) { + case '\0': /* Long option. */ + break; + + case 'h': + /* Help is requested. */ + usage (EXIT_SUCCESS); + break; + + case 'o': + output_file = optarg; + break; + + case 'v': + ++verbosity_level; + break; + + case 'V': + /* Version information is requested. */ + printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION); + /* xgettext: no-wrap */ + 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\ +"), "1995, 1996, 1997, 1998"); + printf (_("Written by %s.\n"), "Cristian Gafton"); + exit (EXIT_SUCCESS); + break; + + case 'w': + { + int value; + char *endp; + value = strtol (optarg, &endp, 10); + if (endp != optarg) + message_page_width_set (value); + } + break; + + default: + usage (EXIT_FAILURE); + break; + } + + /* Test whether we have an .po file name as argument. */ + if (optind >= argc) { + error (EXIT_SUCCESS, 0, _("no input files given")); + usage (EXIT_FAILURE); + } + + /* transform one of the pair command into a real action */ + if (do_action == 0 && split_flag > 0) + do_action = ACTION_DO_SPLIT; + if (do_action == 0 && explode_flag > 0) + do_action = ACTION_DO_EXPLODE; + if (do_action == 0 && sort_order != 0) + do_action = ACTION_DO_SORT; + + /* okay, we have our file in argv[optind]*/ + /* what should we do */ + switch (do_action) { + case ACTION_DO_INVERT: + /* we are asked to invert the msgid and msgstr strings in a .po file */ + result = invert (argv[optind]); + break; + case ACTION_DO_EMPTY: + /* we are asked to delete the msgstr text in a .po file */ + result = empty (argv[optind]); + break; + case ACTION_DO_DUMMY: + /* dummy out a POT file in a PO file (msgstr = msgid) */ + result = dummy (argv[optind]); + break; + case ACTION_DO_SPLIT: + /* we are asked to split the file into corresponding meta po files */ + result = split(argv[optind], split_flag); + /* Whatever we are asked to split out */ + break; + case ACTION_DO_EXPLODE: + /* we are asked to generate a single meta entry for each xref */ + result = explode(argv[optind], explode_flag); + break; + case ACTION_DO_MERGE: + /* we gave to merge (id1,str1) and (id2,str2) into (str1,str2) */ + if (optind + 2 != argc) { + error (EXIT_SUCCESS, 0, _("exactly 2 input files required for merge")); + usage (EXIT_FAILURE); + } + result = merge (argv[optind], argv[optind+1]); + break; + case ACTION_DO_DIFF: + case ACTION_DO_APPEND: + /* append all (id2,str2) entries from second file to the first one + * provided that a id2 msgid is not already present there */ + if (optind + 2 != argc) { + error (EXIT_SUCCESS, 0, _("exactly 2 input files required for append")); + usage (EXIT_FAILURE); + } + result = append (argv[optind], argv[optind+1], do_action == ACTION_DO_DIFF); + break; + case ACTION_DO_UNDUP: + /* read in the message catalog and try to colapse + the duplicate messageids together */ + /* Read in the file */ + result = grammar (argv[optind]); + result = un_duplicate(result); + break; + case ACTION_DO_MASTER: + /* This is a little bit funkier: + * Suppose in A.po we have: + * msgid "id" + * msgstr "str1" + * And in B.po we have: + * msgid "id" + * msgstr "str2" + * then the result will look like: + * msgid "id" + * msgstr(A) "str1" + * msgstr(B) "str2" + */ + result = NULL; /* Start from scratch */ + for (opt = optind ; opt < argc ; opt++) { + char *file_name, *master_id; + char *tmp; + /* get the file name and the master id */ + file_name = strdup(argv[opt]); + master_id = strdup(basename(argv[opt])); + tmp = rindex(master_id, '.'); + if (tmp == NULL) { + error (EXIT_SUCCESS, 0, + _("Message catalog %s does not have a valid name"), argv[opt]); + usage (EXIT_FAILURE); + } + *tmp = '\0'; + result = master(&result, file_name, master_id); + } + master_list_print(result, output_file); + return EXIT_SUCCESS; + break; + case ACTION_DO_SORT: + /* we'll just read in the file and let the sort + * statements later deal with it */ + result = grammar (argv[optind]); + break; + case ACTION_DO_MISSING: + /* generate a new po file of only the missing and non-translated strings */ + result = missing (argv[optind]); + break; + default: + error (EXIT_SUCCESS, 0, _("Unknown Action")); + exit(EXIT_FAILURE); + } + + /* Sort the results. */ + switch (sort_order) { + case SORT_BY_FILEPOS: + message_list_sort_by_filepos (result); + break; + case SORT_BY_MSGID: + message_list_sort_by_msgid (result); + break; + } + + /* Write the merged message list out. */ + message_list_print (result, output_file, force_po, 0); + + exit (EXIT_SUCCESS); +} + + +/* Display usage information and exit. */ +static void +usage (status) + int status; +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else { + /* xgettext: no-wrap */ + printf (_("\ +Usage: %s [OPTION] file.po [ref.po]\n\ +Mandatory arguments to long options are mandatory for short options too.\n\ + -h, --help display this help and exit\n\ + --invert invert a po file by switching msgid and msgstr\n\ + -o, --output-file=FILE result will be written to FILE\n\ + -v, --verbose increase verbosity level\n\ + -V, --version output version information and exit\n\ + -w, --width=NUMBER set output page width\n\ + --sort-by-file sort by file in which the msgs appear\n\ + --sort-by-id sort by message ids\n\ + --split-id split msgid from message catalg into meta catalog\n\ + --split-str split msgstr from message catalg into meta catalog\n\ + --merge merge file.po and ref.po based on the common msgid\n\ + + --useless also generate entries for unmatched pairs.\n\ + --master join any number of files in a master-formatted catalog\n\ + + --fuzzy use the fuzzy entries when merging in PO files.\n\ + --empty empty the contents of the .po file, creating a .pot\n\ + --dummy create a .po file from a .pot by doing msgstr = msgid\n\ + --append append entries from ref.po that don't exist in file.po\n\ + --diff show what entries from ref.po don't exist in file.po\n\ + + --comments add comments from ref.po for existing entries. in file.po.\n\ + --explode-id generate a single meta msgid entry for each xref in file.po\n\ + --explode-str generate a single meta msgstr entry for each xref in file.po\n\ + --unduplicate colapse together duplicate message ids. (not working currently)\n\ + --missing output only the fuzzy and empty message strings\n\ +\n\ +This program can be used to alter .po files in ways no sane mind would think about.\n\ +Report problems to \n\ +"), program_name); + } + exit (status); +} + + +/* The address of this function will be assigned to the hook in the + error functions. */ +static void error_print () +{ + /* We don't want the program name to be printed in messages. Emacs' + compile.el does not like this. */ + + /* FIXME Why must this program toady to Emacs? Why can't compile.el + be enhanced to cope with a leading program name? --PMiller */ +} + + +static void +merge_constructor (that) + po_ty *that; +{ + merge_class_ty *this = (merge_class_ty *) that; + + this->mlp = message_list_alloc (); + this->domain = MESSAGE_DOMAIN_DEFAULT; + this->domain_list = string_list_alloc (); + this->comment = NULL; + this->comment_dot = NULL; + this->filepos_count = 0; + this->filepos = NULL; + this->is_fuzzy = 0; + this->is_c_format = undecided; + this->do_wrap = undecided; +} + + +static void +merge_destructor (that) + po_ty *that; +{ + merge_class_ty *this = (merge_class_ty *) that; + size_t j; + + string_list_free (this->domain_list); + /* Do not free this->mlp. */ + if (this->comment != NULL) + string_list_free (this->comment); + if (this->comment_dot != NULL) + string_list_free (this->comment_dot); + for (j = 0; j < this->filepos_count; ++j) + free (this->filepos[j].file_name); + if (this->filepos != NULL) + free (this->filepos); +} + + +static void +merge_directive_domain (that, name) + po_ty *that; + char *name; +{ + size_t j; + + merge_class_ty *this = (merge_class_ty *) that; + /* Override current domain name. Don't free memory. */ + this->domain = name; + + /* If there are accumulated comments, throw them away, they are + probably part of the file header, or about the domain directive, + and will be unrelated to the next message. */ + if (this->comment != NULL) + { + string_list_free (this->comment); + this->comment = NULL; + } + if (this->comment_dot != NULL) + { + string_list_free (this->comment_dot); + this->comment_dot = NULL; + } + for (j = 0; j < this->filepos_count; ++j) + free (this->filepos[j].file_name); + if (this->filepos != NULL) + free (this->filepos); + this->filepos_count = 0; + this->filepos = NULL; +} + + +static void +merge_directive_message (that, msgid, msgid_pos, msgstr, msgstr_pos) + po_ty *that; + char *msgid; + lex_pos_ty *msgid_pos; + char *msgstr; + lex_pos_ty *msgstr_pos; +{ + merge_class_ty *this = (merge_class_ty *) that; + message_ty *mp; + message_variant_ty *mvp; + size_t j; + + /* Remember the domain names for later. */ + string_list_append_unique (this->domain_list, this->domain); + + /* See if this message ID has been seen before. */ + mp = message_list_search (this->mlp, msgid); + if (mp) + free (msgid); + else + { + mp = message_alloc (msgid); + message_list_append (this->mlp, mp); + } + + /* Add the accumulated comments to the message. Clear the + accumulation in preparation for the next message. */ + if (this->comment != NULL) + { + for (j = 0; j < this->comment->nitems; ++j) + message_comment_append (mp, this->comment->item[j]); + string_list_free (this->comment); + this->comment = NULL; + } + if (this->comment_dot != NULL) + { + for (j = 0; j < this->comment_dot->nitems; ++j) + message_comment_dot_append (mp, this->comment_dot->item[j]); + string_list_free (this->comment_dot); + this->comment_dot = NULL; + } + for (j = 0; j < this->filepos_count; ++j) + { + lex_pos_ty *pp; + + pp = &this->filepos[j]; + message_comment_filepos (mp, pp->file_name, pp->line_number); + free (pp->file_name); + } + mp->is_fuzzy = this->is_fuzzy; + mp->is_c_format = this->is_c_format; + mp->do_wrap = this->do_wrap; + + if (this->filepos != NULL) + free (this->filepos); + this->filepos_count = 0; + this->filepos = NULL; + this->is_fuzzy = 0; + this->is_c_format = undecided; + this->do_wrap = undecided; + + /* See if this domain has been seen for this message ID. */ + mvp = message_variant_search (mp, this->domain); + if (mvp) + { + gram_error_at_line (msgid_pos, _("duplicate message definition")); + gram_error_at_line (&mvp->pos, _("\ +...this is the location of the first definition")); + free (msgstr); + } + else + message_variant_append (mp, this->domain, msgstr, msgstr_pos); +} + + +static void +merge_parse_brief (that) + po_ty *that; +{ + po_lex_pass_comments (1); +} + + +static void +merge_parse_debrief (that) + po_ty *that; +{ + merge_class_ty *this = (merge_class_ty *) that; + message_list_ty *mlp = this->mlp; + size_t j; + + /* For each domain in the used-domain-list, make sure each message + defines a msgstr in that domain. */ + for (j = 0; j < this->domain_list->nitems; ++j) + { + const char *domain_name; + size_t k; + + domain_name = this->domain_list->item[j]; + for (k = 0; k < mlp->nitems; ++k) + { + const message_ty *mp; + size_t m; + + mp = mlp->item[k]; + for (m = 0; m < mp->variant_count; ++m) + { + message_variant_ty *mvp; + + mvp = &mp->variant[m]; + if (strcmp (domain_name, mvp->domain) == 0) + break; + } + if (m >= mp->variant_count) + gram_error_at_line (&mp->variant[0].pos, _("\ +this message has no definition in the \"%s\" domain"), domain_name); + } + } +} + + +static void +merge_comment (that, s) + po_ty *that; + const char *s; +{ + merge_class_ty *this = (merge_class_ty *) that; + + if (this->comment == NULL) + this->comment = string_list_alloc (); + string_list_append (this->comment, s); +} + + +static void +merge_comment_dot (that, s) + po_ty *that; + const char *s; +{ + merge_class_ty *this = (merge_class_ty *) that; + + if (this->comment_dot == NULL) + this->comment_dot = string_list_alloc (); + string_list_append (this->comment_dot, s); +} + + +static void +merge_comment_special (that, s) + po_ty *that; + const char *s; +{ + merge_class_ty *this = (merge_class_ty *) that; + + if (strstr (s, "fuzzy") != NULL) + this->is_fuzzy = 1; + + this->is_c_format = parse_c_format_description_string (s); + this->do_wrap = parse_c_width_description_string (s); +} + + +static void +merge_comment_filepos (that, name, line) + po_ty *that; + const char *name; + int line; +{ + merge_class_ty *this = (merge_class_ty *) that; + size_t nbytes; + lex_pos_ty *pp; + + if (!line_comment) + return; + nbytes = (this->filepos_count + 1) * sizeof (this->filepos[0]); + this->filepos = xrealloc (this->filepos, nbytes); + pp = &this->filepos[this->filepos_count++]; + pp->file_name = xstrdup (name); + pp->line_number = line; +} + + +/* So that the one parser can be used for multiple programs, and also + use good data hiding and encapsulation practices, an object + oriented approach has been taken. An object instance is allocated, + and all actions resulting from the parse will be through + invokations of method functions of that object. */ + +static po_method_ty merge_methods = +{ + sizeof (merge_class_ty), + merge_constructor, + merge_destructor, + merge_directive_domain, + merge_directive_message, + merge_parse_brief, + merge_parse_debrief, + merge_comment, + merge_comment_dot, + merge_comment_filepos, + merge_comment_special +}; + + +static message_list_ty * +grammar (filename) + const char *filename; +{ + po_ty *pop; + message_list_ty *mlp; + + pop = po_alloc (&merge_methods); + po_lex_pass_obsolete_entries (1); + po_scan (pop, filename); + mlp = ((merge_class_ty *) pop)->mlp; + po_free (pop); + return mlp; +} + + +#define DOT_FREQUENCE 10 + +/* + * Given two meta files (m,X) and (m,N) generate a po file (N,X) + * preserving the meta information in special comments + */ +static message_list_ty * +merge (fn1, fn2) + const char *fn1; /* definitions */ + const char *fn2; /* references */ +{ + message_list_ty *def; + message_list_ty *ref; + message_ty *defmsg; + size_t j, k; + size_t merged, fuzzied, missing, obsolete; + message_list_ty *result; + + /* This is the definitions file, created by a human. */ + def = grammar (fn1); + + /* This is the references file, created by groping the sources with + * the xgettext program. */ + ref = grammar (fn2); + + result = message_list_alloc (); + + /* Every reference must be matched with its definition. */ + for (j = 0; j < ref->nitems; ++j) { + message_ty *refmsg; + + /* Because merging can take a while we print something to signal + * we are not dead. */ + if (verbosity_level >= 1 && j % DOT_FREQUENCE == 0) + fputc ('.', stderr); + + refmsg = ref->item[j]; + + /* See if it is in the other file. */ + defmsg = message_list_search (def, refmsg->msgid); + if (defmsg) { + /* Merge the reference with the definition: take the #. and + #: comments from the reference, take the # comments from + the definition, take the msgstr from the definition. Add + this merged entry to the output message list. */ + message_ty *mp = message_join (defmsg, refmsg); + + if (mp != NULL) + message_list_append (result, mp); + + /* Remember that this message has been used, when we scan + later to see if anything was omitted. */ + defmsg->used = 1; + ++merged; + continue; + } else if (useless) { + message_ty *mp; + const char *tmp; + if (verbosity_level > 1) + gram_error_at_line (&refmsg->variant[0].pos, + _("this message is used but not defined in %s"), + fn1); + + /* Insert the blank record */ + mp = message_copy (refmsg); + tmp = mp->msgid; + mp->msgid = mp->variant[0].msgstr; + mp->variant[0].msgstr = ""; + message_list_append (result, mp); + ++missing; + } + } + + /* Look for messages in the definition file, which are not present + in the reference file, indicating messages which defined but not + used in the program. */ + if (useless) + for (k = 0; k < def->nitems; ++k) { + defmsg = def->item[k]; + if (defmsg->used) + continue; + + /* Remember the old translation although it is not used anymore. + But we mark it as obsolete. */ + defmsg->obsolete = 1; + + message_list_append (result, defmsg); + ++obsolete; + } + + /* Report some statistics. */ + if (verbosity_level > 0) + fprintf (stderr, + _("%sRead %d old + %d reference, merged %d, fuzzied %d, missing %d, obsolete %d.\n"), + verbosity_level >= 1 ? "\n" : "", + def->nitems, ref->nitems, merged, fuzzied, missing, obsolete); + else if (verbosity_level > 0) + fputs (_(" done.\n"), stderr); + + /* get rid of the duplicate message ids */ + return un_duplicate(result); +} + +/* + * Given two meta files f1 and f2 append to f1 the entries from f2 that + * are not found in f1. First f1 is read in, the for each entry in f2 we + * try to locate it in f1; if it is not there already we add it. + */ +static message_list_ty * +append (fn1, fn2, diff_only) + const char *fn1; /* base file */ + const char *fn2; /* more stuff found in here */ + int diff_only; /* do we want only the diffs? */ +{ + message_list_ty *ml1; + message_list_ty *ml2; + message_list_ty *result; + size_t j; + size_t added = 0; + int initial; + + /* This is the definitions file, created by a human. */ + ml1 = grammar (fn1); + initial = ml1->nitems; + + /* This is the references file, created by groping the sources with + * the xgettext program. */ + ml2 = grammar (fn2); + + result = message_list_alloc(); + + /* Every reference must be matched with its definition. */ + for (j = 0; j < ml2->nitems; ++j) { + message_ty *oldmsg; + message_ty *newmsg; + int i; + + /* Because merging can take a while we print something to signal + * we are not dead. */ + if (verbosity_level >= 1 && j % DOT_FREQUENCE == 0) + fputc ('.', stderr); + + /* this is the message we want to add */ + newmsg = ml2->item[j]; + + /* See if it is in the other file. */ + oldmsg = message_list_search (ml1, newmsg->msgid); + if (oldmsg == NULL) { /* not found, stick it in */ + message_list_append (result, newmsg); + newmsg->used = 1; + ++added; + continue; + } + + /* else, if it is found, add the coments from it if we want them */ + if (comments != 0) { + if (newmsg->comment) + for (i = 0; i < newmsg->comment->nitems; ++i) + message_comment_append (oldmsg, newmsg->comment->item[i]); + + if (newmsg->comment_dot) + for (i = 0; i < newmsg->comment_dot->nitems; ++i) + message_comment_dot_append (oldmsg, newmsg->comment_dot->item[i]); + + /* file position comments */ + for (i = 0; i < newmsg->filepos_count; ++i) { + lex_pos_ty *pp = &newmsg->filepos[i]; + message_comment_filepos (oldmsg, pp->file_name, pp->line_number); + } + } + newmsg->used++; + } + + /* Report some statistics. */ + if (verbosity_level > 0) + fprintf (stderr, + _("%sRead %d file1 + %d file2, added %d\n"), + verbosity_level >= 1 ? "\n" : "", + initial, ml2->nitems, added); + else if (verbosity_level > 0) + fputs (_(" done.\n"), stderr); + + /* do we want only the diffs? */ + if (diff_only) { + return result; + } + + /* append the result to ml1 */ + for (j = 0; j < result->nitems; ++j) + message_list_append(ml1, result->item[j]); + + /* No need to free the result since message_list_alloc copies only the + references over */ + return ml1; +} + +static message_list_ty * +master (prev, fn, master_id) + message_list_ty **prev; /* The result of what we had before */ + const char *fn; /* file name */ + const char *master_id; /* what to put as msgstr(master_id) */ +{ + message_list_ty *list; + message_list_ty *result; + int j; + + /* Read in the file */ + list = grammar (fn); + + if (*prev == NULL) + *prev = message_list_alloc (); + + result = *prev; /* Work with that */ + + /* Every reference must be matched with its definition. */ + for (j = 0; j < list->nitems; ++j) { + message_ty *crtmsg, *merge_msg; + + /* check for empty message ids == headers */ + if (list->item[j]->msgid[0] == '\0') { + continue; + } + + if ((list->item[j])->is_fuzzy && (fuzzy == 0)) { + /* we don't want fuzzies */ + continue; + } + + crtmsg = message_copy (list->item[j]); + + /* First, search its pair in the result list ... */ + merge_msg = message_list_search (result, crtmsg->msgid); + + /* Now here is something broken: we get only the msgstr + * from the variant[0] and override its domain with our master_id + */ + crtmsg->variant_count = 1; + crtmsg->variant[0].domain = master_id; + + if (merge_msg != NULL) { + int variants; + void *temp; + + /* Ah! we have to do a nasty merge */ + variants = ++merge_msg->variant_count; + temp = realloc (merge_msg->variant, variants * sizeof(crtmsg->variant[0])); + if (temp != NULL) { + merge_msg->variant = temp; + } else { + error (EXIT_FAILURE, 0, "OUT of memory in %s", __FUNCTION__); + exit(-1); + } + merge_msg->variant[variants-1] = crtmsg->variant[0]; + /* that should be it */ + } else { + /* this is really simple - just add it */ + message_list_append (result, crtmsg); + } + + /* Remember that this message has been used, when we scan + later to see if anything was omitted. */ + crtmsg->used = 1; + } + + return result ; +} + +static message_list_ty * +invert (fn) + const char *fn; /* file name */ +{ + message_list_ty *list; + size_t j; + size_t inverted = 0; + message_list_ty *result; + + /* Read in the file */ + list = grammar (fn); + + result = message_list_alloc (); + + /* Every reference must be matched with its definition. */ + for (j = 0; j < list->nitems; ++j) { + message_ty *crtmsg; + int k; + + /* check for empty message ids == headers */ + if (list->item[j]->msgid[0] == '\0') { + message_list_append (result, list->item[j]); + continue; + } + + crtmsg = message_copy (list->item[j]); + + /* + * Now invert it. For each domain found in the file we create a record + * containing the inverted pair + */ + for (k = 0; k < crtmsg->variant_count; k++) { + message_ty *mp; + const char *tmp; + + mp = message_copy(crtmsg); + mp->variant_count = 1; + tmp = mp->msgid; + mp->msgid = crtmsg->variant[k].msgstr; + mp->variant[0] = crtmsg->variant[k]; + mp->variant[0].msgstr = tmp; + message_list_append (result, mp); + ++inverted; + } + + /* Remember that this message has been used, when we scan + later to see if anything was omitted. */ + crtmsg->used = 1; + } + + /* get rid of the duplicate message ids */ + return un_duplicate(result); +} + +static message_list_ty * +missing (fn) + const char *fn; /* file name */ +{ + message_list_ty *list; + size_t j; + message_list_ty *result; + + /* Read in the file */ + list = grammar (fn); + + result = message_list_alloc (); + + /* Every reference must be matched with its definition. */ + for (j = 0; j < list->nitems; ++j) { + int k; + + /* check for empty message ids == headers */ + if (list->item[j]->msgid[0] == '\0') { + message_list_append (result, list->item[j]); + continue; + } + + /* Fuzzies go in */ + if (list->item[j]->is_fuzzy) { + message_list_append (result, list->item[j]); + continue; + } + + /* Check for empty translations */ + for (k = 0; k < list->item[j]->variant_count; k++) { + if (strlen(list->item[j]->variant[k].msgstr) <= 1) { + message_list_append (result, list->item[j]); + break; + } + } + + /* Remember that this message has been used, when we scan + later to see if anything was omitted. */ + list->item[j]->used = 1; + } + return result; +} + +static message_list_ty * +un_duplicate (dup_list) + message_list_ty *dup_list; /* list containing duplicates */ +{ + message_list_ty *result; + size_t j; + + result = message_list_alloc (); + + /* First iteration, clear the used flag */ + for (j = 0; j < dup_list->nitems; ++j) + dup_list->item[j]->used = 0; + + /* This is a cheesy O(n^2) dupe elimination "algorithm" + * One day I will replace it by a quicksort() followed by + * a walk through the whole list one more time and that will bring + * it down to O(n log(n)) */ + for (j = 0; j < dup_list->nitems; ++j) { + message_ty *crtmsg; + size_t k; + + /* get the current message */ + crtmsg = dup_list->item[j]; + + if (crtmsg->used > 0) + continue; + + for (k = j + 1; k < dup_list->nitems; ++k) { + message_ty *newmsg; + size_t i; + + newmsg = dup_list->item[k]; + + if (strcmp(crtmsg->msgid, newmsg->msgid) != 0 || + newmsg->used > 0) + continue; + + if (newmsg->comment) + for (i = 0; i < newmsg->comment->nitems; ++i) + message_comment_append (crtmsg, newmsg->comment->item[i]); + + if (newmsg->comment_dot) + for (i = 0; i < newmsg->comment_dot->nitems; ++i) + message_comment_dot_append (crtmsg, newmsg->comment_dot->item[i]); + + /* file position comments */ + for (i = 0; i < newmsg->filepos_count; ++i) { + lex_pos_ty *pp = &newmsg->filepos[i]; + message_comment_filepos (crtmsg, pp->file_name, pp->line_number); + } + + fprintf(stderr, "%05d : %05d\r", j, k); + /* Now mark it as used */ + newmsg->used++; + } + + /* Now put the resulting entry on the final list */ + message_list_append (result, crtmsg); + } + + return result; + +} + +message_ty * +message_join (def, ref) + message_ty *def; + message_ty *ref; +{ + message_ty *result; + size_t j; + char *meta_comment; + + /* is this the header ? */ + if (ref->msgid[0] == '\0') { + result = message_copy(ref); + return result; + }; + + + /* XXX: FIX ME!!! + * We are not processing in any way the variant list at this time. + * We assume blindly that joining two pairs onn variant[0] will yield + * the correct result + */ + + /* here it goes: (m,X) join m(m,N) = (N,X) */ + result = message_copy(def); + /* here we should be looping over all domains */ + result->msgid = ref->variant[0].msgstr; + + if (!useless) { + /* Trim down on crap translations */ + if (result->variant[0].msgstr == NULL) + return NULL; + if (strlen(result->variant[0].msgstr) == 0) + return NULL; + if (strcmp(result->msgid, result->variant[0].msgstr) == 0) + return NULL; + } + + /* Take the comments from the definition file. There will be none at + * all in the reference file, as it was generated by xgettext. + */ + /* we don't do that here because result is already initialized from def + --cristiang */ + /*if (def->comment) */ + /* for (j = 0; j < def->comment->nitems; ++j) */ + /* message_comment_append (result, def->comment->item[j]); */ + + /* now add the meta information for the common join field */ + j = strlen(ref->msgid) + 10; + meta_comment = alloca(j); + if (meta_comment != NULL) { + snprintf(meta_comment, j-1, "meta=%s", ref->msgid); /* should be safe */ + message_comment_append(result, meta_comment); + } else { + message_comment_append(result, "meta=ERROR!!!"); + message_comment_append(result, ref->msgid); + } + + /* Take the dot comments from the reference file, as they are + generated by xgettext. Any in the definition file are old ones + collected by previous runs of xgettext and msgmerge. */ + if (ref->comment_dot) + for (j = 0; j < ref->comment_dot->nitems; ++j) + message_comment_dot_append (result, ref->comment_dot->item[j]); + +/* message_comment_dot_append(result, "this is my test"); */ + /* The flags are mixed in a special way. Some informations come + from the reference message (such as format/no-format), others + come from the definition file (fuzzy or not). */ + result->is_fuzzy = def->is_fuzzy; + result->is_c_format = ref->is_c_format; + result->do_wrap = ref->do_wrap; + + /* Take the file position comments from the reference file, as they + are generated by xgettext. Any in the definition file are old ones + collected by previous runs of xgettext and msgmerge. */ + for (j = 0; j < ref->filepos_count; ++j) { + lex_pos_ty *pp = &ref->filepos[j]; + message_comment_filepos (result, pp->file_name, pp->line_number); + } + + /* All done, return the merged message to the caller. */ + return result; +} + +static message_list_ty * +split (fn, flag) + const char *fn; /* file name */ + int flag; /* what should we be splitting out */ +{ + message_list_ty *list; + size_t j; + message_list_ty *result; + + /* Read in the file */ + list = grammar (fn); + + result = message_list_alloc (); + + /* Every reference must be matched with its definition. */ + for (j = 0; j < list->nitems; ++j) { + message_ty *crtmsg; + + /* get the current message */ + crtmsg = message_copy (list->item[j]); + + /* search all comments looking for our meta= */ + if (crtmsg->comment) { + int k; + for (k = 0; k < crtmsg->comment->nitems; ++k) { + char *tmp; + + tmp = strstr(crtmsg->comment->item[k], "meta="); + if (tmp != NULL) { /* found it */ + message_ty *res; + int i; + message_variant_ty *mvp = NULL; + + res = message_alloc(xstrdup(tmp+5)); + res->variant_count = 1; + mvp = malloc(sizeof(struct message_variant_ty)); + if (mvp == NULL) { + error (EXIT_FAILURE, 0, _("Weird out of memory situation")); + usage (EXIT_FAILURE); + } + *mvp = crtmsg->variant[0]; + res->variant = mvp; + switch(flag) { + case SPLIT_MSGID: + res->variant[0].msgstr = crtmsg->msgid; + /* transport over all the dot comments */ + if (crtmsg->comment_dot) + for (i = 0; i < crtmsg->comment_dot->nitems; ++i) + message_comment_dot_append(res, crtmsg->comment_dot->item[i]); + break; + case SPLIT_MSGSTR: + res->variant[0].msgstr = crtmsg->variant[0].msgstr; + /* transport over the comments with the exception of + the dot comments and the meta= one */ + for (i = 0; i < crtmsg->comment->nitems; ++i) + if (i != k) + message_comment_append(res, crtmsg->comment->item[i]); + break; + default: + error (EXIT_SUCCESS, 0, _("Unknwon split mode")); + usage (EXIT_FAILURE); + } + message_list_append (result, res); + } + } + } else { + /* we have no meta comment on this one */ + continue; + } + } + return result; +} + +static message_list_ty * +explode (fn, flag) + const char *fn; /* file name */ + int flag; /* what should we be splitting out */ +{ + message_list_ty *list; + size_t j; + message_list_ty *result; + + /* Read in the file */ + list = grammar (fn); + + result = message_list_alloc (); + + /* Every reference ... */ + for (j = 0; j < list->nitems; ++j) { + message_ty *crtmsg; + int k; + + /* Get the current message */ + crtmsg = message_copy (list->item[j]); + + /* Every "conformant" cross reference ... */ + for (k = 0; k < crtmsg->filepos_count; ++k) { + lex_pos_ty *pp; + char buf[1024]; + const char *fn, *tn; + const char *f, *fe; + int ln; + message_ty *res; + int i; + message_variant_ty *mvp = NULL; + + pp = &crtmsg->filepos[k]; + fn = pp->file_name; + ln = pp->line_number; + + switch(ln) { + default: + tn = NULL; + break; + case 1004: + tn = "(Summary)"; + break; + case 1005: + tn = "(Description)"; + break; + case 1016: + tn = "(Group)"; + break; + } + + /* ... weird tags we do not want */ + if (tn == NULL) + continue; + + /* Find the text after the name-version-release */ + if ((f = strrchr(fn, '-')) == NULL || + (fe = strchr(f, '.')) == NULL) { + fprintf(stderr, _("skipping malformed xref \"%s\"\n"), fn); + continue; + } + fe = fn + strlen(fn) - sizeof(".src.rpm") + 1; + + /* ... src.rpm's we do not want */ + if ( !strcmp(fe, ".src.rpm") ) + continue; + + /* Position at end of name */ + fe = strrchr(fn, '-') - 1; + while (fe > fn && *fe != '-') + --fe; + + /* Construct meta tag */ + strncpy(buf, fn, (fe-fn) ); + buf[fe-fn] = '\0'; + strncat(buf, tn, (sizeof(buf) - strlen(buf)) ); + + res = message_alloc(xstrdup(buf)); + res->variant_count = 1; + mvp = malloc(sizeof(struct message_variant_ty)); + if (mvp == NULL) { + error (EXIT_FAILURE, 0, _("Weird out of memory situation")); + usage (EXIT_FAILURE); + } + *mvp = crtmsg->variant[0]; + res->variant = mvp; + switch(flag) { + case EXPLODE_MSGID: + res->variant[0].msgstr = crtmsg->msgid; + /* transport over all the dot comments */ + if (crtmsg->comment_dot) + for (i = 0; i < crtmsg->comment_dot->nitems; ++i) + message_comment_dot_append(res, crtmsg->comment_dot->item[i]); + break; + case EXPLODE_MSGSTR: + res->variant[0].msgstr = crtmsg->variant[0].msgstr; +#if 0 + /* transport over the comments with the exception of + the dot comments */ + for (i = 0; i < crtmsg->comment->nitems; ++i) + message_comment_append(res, crtmsg->comment->item[i]); +#endif + break; + default: + error (EXIT_SUCCESS, 0, _("Unknwon explode mode")); + usage (EXIT_FAILURE); + } + message_list_append (result, res); + } + } + return result; +} + +static message_list_ty * +empty (fn) + const char *fn; /* file name */ +{ + message_list_ty *list; + size_t j; + int had_header = 0; + message_list_ty *result; + + /* Read in the file */ + list = grammar (fn); + + result = message_list_alloc (); + + /* Every reference must be matched with its definition. */ + for (j = 0; j < list->nitems; ++j) { + if (list->item[j]->msgid[0] == '\0') { + had_header++; + break; + } + } + + /* If we don't have a header, we should try to add one */ + if (!had_header) + message_list_append (result, new_header()); + + /* time to add the rest */ + for (j = 0; j < list->nitems; ++j) { + message_ty *crtmsg; + + crtmsg = list->item[j]; + if (crtmsg->msgid[0] != '\0') { + /* empty the translations */ + crtmsg->variant_count = 1; + crtmsg->variant[0].msgstr = ""; + } + crtmsg->used = 1; + message_list_append (result, crtmsg); + } + return result; +} + +static message_list_ty * +dummy (fn) + const char *fn; /* file name */ +{ + message_list_ty *list; + size_t j; + int had_header = 0; + message_list_ty *result; + + /* Read in the file */ + list = grammar (fn); + + result = message_list_alloc (); + + /* Every reference must be matched with its definition. */ + for (j = 0; j < list->nitems; ++j) { + if (list->item[j]->msgid[0] == '\0') { + had_header++; + break; + } + } + + /* If we don't have a header, we should try to add one */ + if (!had_header) + message_list_append (result, new_header()); + + /* time to add the rest */ + for (j = 0; j < list->nitems; ++j) { + message_ty *crtmsg; + + crtmsg = list->item[j]; + if (crtmsg->msgid[0] != '\0') { + /* dummy translation */ + crtmsg->variant_count = 1; + crtmsg->variant[0].msgstr = crtmsg->msgid; + } + crtmsg->used = 1; + message_list_append (result, crtmsg); + } + return result; +} + +#define TM_YEAR_ORIGIN 1900 +static struct message_ty * +new_header(void) +{ + time_t now; + struct tm local_time; + message_ty *mp; + char *msgstr; + static lex_pos_ty pos = { __FILE__, __LINE__, }; + + mp = message_alloc (""); + if (mp == NULL) + return NULL; + + message_comment_append (mp, "\ +SOME DESCRIPTIVE TITLE.\n\ +FIRST AUTHOR , YEAR.\n"); + + time (&now); + local_time = *localtime (&now); + + asprintf (&msgstr, "\ +Project-Id-Version: PACKAGE VERSION\n\ +POT-Creation-Date: %d-%02d-%02d %02d:%02d\n\ +PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n\ +Last-Translator: FULL NAME \n\ +Language-Team: LANGUAGE \n\ +MIME-Version: 1.0\n\ +Content-Type: text/plain; charset=CHARSET\n\ +Content-Transfer-Encoding: ENCODING\n", + local_time.tm_year + TM_YEAR_ORIGIN, + local_time.tm_mon + 1, + local_time.tm_mday, + local_time.tm_hour, + local_time.tm_min); + + if (msgstr == NULL) + error(EXIT_FAILURE, errno, _("while preparing output")); + message_variant_append (mp, MESSAGE_DOMAIN_DEFAULT, msgstr, &pos); + return mp; +}