--- /dev/null
+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/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 Wed Oct 6 17:59:54 1999
+@@ -0,0 +1,1482 @@
++/* GNU gettext - internationalization aids
++ Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
++ This file was written by Cristian Gafton <gafton@redhat.com>
++
++ 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 <config.h>
++#endif
++
++#include <getopt.h>
++#include <limits.h>
++#include <stdio.h>
++
++#ifdef STDC_HEADERS
++# include <stdlib.h>
++#endif
++
++#if HAVE_STRING_H
++# include <string.h>
++#else
++# include <strings.h>
++#endif
++
++#if HAVE_LOCALE_H
++# include <locale.h>
++#endif
++
++#include <time.h>
++#include <errno.h>
++
++#include "dir-list.h"
++#include "error.h"
++#include "message.h"
++#include <system.h>
++#include <libintl.h>
++#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;
++
++/* 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;
++
++/* 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));
++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 *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));
++
++/* 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
++
++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' },
++
++ /* ACTIONS */
++ { "invert", no_argument, &do_action, ACTION_DO_INVERT },
++ { "merge", no_argument, &do_action, ACTION_DO_MERGE },
++ { "empty", no_argument, &do_action, ACTION_DO_EMPTY },
++ { "master", no_argument, &do_action, ACTION_DO_MASTER },
++ { "append", no_argument, &do_action, ACTION_DO_APPEND },
++ { "unduplicate", no_argument, &do_action, ACTION_DO_UNDUP },
++ { "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_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_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]);
++ 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\
++ --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\
++ --append append entries from ref.po that don't exist 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 <gafton@redhat.com>\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);
++
++ 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 {
++ 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. */
++ 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)
++ const char *fn1; /* base file */
++ const char *fn2; /* more stuff found in here */
++{
++ message_list_ty *ml1;
++ message_list_ty *ml2;
++ 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);
++
++ /* 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 (ml1, newmsg);
++ newmsg->used = 1;
++ ++added;
++ continue;
++ }
++ /* else, if it is found, add the coments from it */
++ 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);
++
++ /* get rid of the duplicate message ids */
++ 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;
++
++ /* 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;
++}
++
++#define TM_YEAR_ORIGIN 1900
++
++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) {
++ time_t now;
++ struct tm local_time;
++ message_ty *mp;
++ char *msgstr;
++ static lex_pos_ty pos = { __FILE__, __LINE__, };
++
++ mp = message_alloc ("");
++
++ message_comment_append (mp, "\
++SOME DESCRIPTIVE TITLE.\n\
++FIRST AUTHOR <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n\
++Language-Team: LANGUAGE <LL@li.org>\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);
++ message_list_append (result, mp);
++ }
++
++ /* 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;
++}