--- /dev/null
+/* Message list test for equality.
+ Copyright (C) 2001 Free Software Foundation, Inc.
+ Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+ 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
+
+/* Specification. */
+#include "msgl-equal.h"
+
+
+/* Prototypes for local functions. Needed to ensure compiler checking of
+ function argument counts despite of K&R C function definition syntax. */
+static inline bool pos_equal PARAMS ((const lex_pos_ty *pos1,
+ const lex_pos_ty *pos2));
+static inline bool string_list_equal PARAMS ((const string_list_ty *slp1,
+ const string_list_ty *slp2));
+static inline bool msgdomain_equal PARAMS ((const msgdomain_ty *mdp1,
+ const msgdomain_ty *mdp2));
+
+
+static inline bool
+pos_equal (pos1, pos2)
+ const lex_pos_ty *pos1;
+ const lex_pos_ty *pos2;
+{
+ return ((pos1->file_name == pos2->file_name
+ || strcmp (pos1->file_name, pos2->file_name) == 0)
+ && pos1->line_number == pos2->line_number);
+}
+
+static inline bool
+string_list_equal (slp1, slp2)
+ const string_list_ty *slp1;
+ const string_list_ty *slp2;
+{
+ size_t i, i1, i2;
+
+ i1 = (slp1 != NULL ? slp1->nitems : 0);
+ i2 = (slp2 != NULL ? slp2->nitems : 0);
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (strcmp (slp1->item[i], slp2->item[i]) != 0)
+ return false;
+ return true;
+}
+
+bool
+message_equal (mp1, mp2)
+ const message_ty *mp1;
+ const message_ty *mp2;
+{
+ size_t i, i1, i2;
+
+ if (strcmp (mp1->msgid, mp2->msgid) != 0)
+ return false;
+
+ if (!(mp1->msgid_plural != NULL
+ ? mp2->msgid_plural != NULL
+ && strcmp (mp1->msgid_plural, mp2->msgid_plural) == 0
+ : mp2->msgid_plural == NULL))
+ return false;
+
+ if (mp1->msgstr_len != mp2->msgstr_len)
+ return false;
+ if (memcmp (mp1->msgstr, mp2->msgstr, mp1->msgstr_len) != 0)
+ return false;
+
+ if (!pos_equal (&mp1->pos, &mp2->pos))
+ return false;
+
+ if (!string_list_equal (mp1->comment, mp2->comment))
+ return false;
+
+ if (!string_list_equal (mp1->comment_dot, mp2->comment_dot))
+ return false;
+
+ i1 = mp1->filepos_count;
+ i2 = mp2->filepos_count;
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (!pos_equal (&mp1->filepos[i], &mp2->filepos[i]))
+ return false;
+
+ if (mp1->is_fuzzy != mp2->is_fuzzy)
+ return false;
+
+ for (i = 0; i < NFORMATS; i++)
+ if (mp1->is_format[i] != mp2->is_format[i])
+ return false;
+
+ if (mp1->obsolete != mp2->obsolete)
+ return false;
+
+ return true;
+}
+
+bool
+message_list_equal (mlp1, mlp2)
+ const message_list_ty *mlp1;
+ const message_list_ty *mlp2;
+{
+ size_t i, i1, i2;
+
+ i1 = mlp1->nitems;
+ i2 = mlp2->nitems;
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (!message_equal (mlp1->item[i], mlp2->item[i]))
+ return false;
+ return true;
+}
+
+static inline bool
+msgdomain_equal (mdp1, mdp2)
+ const msgdomain_ty *mdp1;
+ const msgdomain_ty *mdp2;
+{
+ return (strcmp (mdp1->domain, mdp2->domain) == 0
+ && message_list_equal (mdp1->messages, mdp2->messages));
+}
+
+bool
+msgdomain_list_equal (mdlp1, mdlp2)
+ const msgdomain_list_ty *mdlp1;
+ const msgdomain_list_ty *mdlp2;
+{
+ size_t i, i1, i2;
+
+ i1 = mdlp1->nitems;
+ i2 = mdlp2->nitems;
+ if (i1 != i2)
+ return false;
+ for (i = 0; i < i1; i++)
+ if (!msgdomain_equal (mdlp1->item[i], mdlp2->item[i]))
+ return false;
+ return true;
+}
#include "read-po.h"
#include "write-po.h"
#include "system.h"
-#include "libgettext.h"
#include "po.h"
+#include "msgl-equal.h"
+#include "backupfile.h"
+#include "copy-file.h"
+#include "libgettext.h"
#define _(str) gettext (str)
/* List of user-specified compendiums. */
static message_list_list_ty *compendiums;
+/* Update mode. */
+static bool update_mode = false;
+static const char *version_control_string;
+static const char *backup_suffix_string;
+
/* Long options. */
static const struct option long_options[] =
{
{ "add-location", no_argument, &line_comment, 1 },
+ { "backup", required_argument, NULL, CHAR_MAX + 1 },
{ "compendium", required_argument, NULL, 'C', },
{ "directory", required_argument, NULL, 'D' },
{ "escape", no_argument, NULL, 'E' },
{ "sort-by-file", no_argument, NULL, 'F' },
{ "sort-output", no_argument, NULL, 's' },
{ "silent", no_argument, NULL, 'q' },
- { "strict", no_argument, NULL, 'S' },
+ { "strict", no_argument, NULL, CHAR_MAX + 2 },
+ { "suffix", required_argument, NULL, CHAR_MAX + 3 },
+ { "update", no_argument, NULL, 'U' },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w', },
/* Prototypes for local functions. Needed to ensure compiler checking of
function argument counts despite of K&R C function definition syntax. */
static void usage PARAMS ((int status));
-static msgdomain_list_ty *merge PARAMS ((const char *fn1, const char *fn2));
static void compendium PARAMS ((const char *filename));
static void match_domain PARAMS ((const char *fn1, const char *fn2,
message_list_list_ty *definitions,
message_list_ty *refmlp,
message_list_ty *resultmlp,
struct statistics *stats, int *processed));
+static msgdomain_list_ty *merge PARAMS ((const char *fn1, const char *fn2,
+ msgdomain_list_ty **defp));
int
bool do_help;
bool do_version;
char *output_file;
+ msgdomain_list_ty *def;
msgdomain_list_ty *result;
bool sort_by_filepos = false;
bool sort_by_msgid = false;
output_file = NULL;
while ((opt
- = getopt_long (argc, argv, "C:D:eEFhimo:qsvVw:", long_options, NULL))
+ = getopt_long (argc, argv, "C:D:eEFhimo:qsUvVw:", long_options, NULL))
!= EOF)
switch (opt)
{
sort_by_msgid = true;
break;
- case 'S':
- message_print_style_uniforum ();
+ case 'U':
+ update_mode = true;
break;
case 'v':
}
break;
+ case CHAR_MAX + 1: /* --backup */
+ version_control_string = optarg;
+ break;
+
+ case CHAR_MAX + 2: /* --strict */
+ message_print_style_uniforum ();
+ break;
+
+ case CHAR_MAX + 3: /* --suffix */
+ backup_suffix_string = optarg;
+ break;
+
default:
usage (EXIT_FAILURE);
break;
}
/* Verify selected options. */
+ if (update_mode)
+ {
+ if (output_file != NULL)
+ {
+ error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
+ "--update", "--output-file");
+ }
+ }
+ else
+ {
+ if (version_control_string != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s"),
+ "--backup", "--update");
+ usage (EXIT_FAILURE);
+ }
+ if (backup_suffix_string != NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s is only valid with %s"),
+ "--suffix", "--update");
+ usage (EXIT_FAILURE);
+ }
+ }
+
if (!line_comment && sort_by_filepos)
error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
"--no-location", "--sort-by-file");
error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
"--sort-output", "--sort-by-file");
- /* merge the two files */
- result = merge (argv[optind], argv[optind + 1]);
+ /* Merge the two files. */
+ result = merge (argv[optind], argv[optind + 1], &def);
/* Sort the results. */
if (sort_by_filepos)
else if (sort_by_msgid)
msgdomain_list_sort_by_msgid (result);
- /* Write the merged message list out. */
- msgdomain_list_print (result, output_file, force_po, false);
+ if (update_mode)
+ {
+ /* Do nothing if the original file and the result are equal. */
+ if (!msgdomain_list_equal (def, result))
+ {
+ /* Back up def.po. */
+ enum backup_type backup_type;
+ char *backup_file;
+
+ output_file = argv[optind];
+
+ if (backup_suffix_string == NULL)
+ {
+ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
+ if (backup_suffix_string != NULL
+ && backup_suffix_string[0] == '\0')
+ backup_suffix_string = NULL;
+ }
+ if (backup_suffix_string != NULL)
+ simple_backup_suffix = backup_suffix_string;
+
+ backup_type = xget_version (_("backup type"), version_control_string);
+ if (backup_type != none)
+ {
+ backup_file = find_backup_file_name (output_file, backup_type);
+ copy_file (output_file, backup_file);
+ }
+
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, true, false);
+ }
+ }
+ else
+ {
+ /* Write the merged message list out. */
+ msgdomain_list_print (result, output_file, force_po, false);
+ }
exit (EXIT_SUCCESS);
}
printf ("\n");
/* xgettext: no-wrap */
printf (_("\
+Operation mode:\n\
+ -U, --update update def.po,\n\
+ do nothing if def.po already up to date\n\
+"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
Output file location:\n\
-o, --output-file=FILE write output to specified file\n\
The results are written to standard output if no output file is specified\n\
or if it is -.\n\
+"));
+ printf ("\n");
+ /* xgettext: no-wrap */
+ printf (_("\
+Output file location in update mode:\n\
+The result is written back to def.po.\n\
+ --backup=CONTROL make a backup of def.po\n\
+ --suffix=SUFFIX override the usual backup suffix\n\
+The version control method may be selected via the --backup option or through\n\
+the VERSION_CONTROL environment variable. Here are the values:\n\
+ none, off never make backups (even if --backup is given)\n\
+ numbered, t make numbered backups\n\
+ existing, nil numbered if numbered backups exist, simple otherwise\n\
+ simple, never always make simple backups\n\
+The backup suffix is `~', unless set with --suffix or the SIMPLE_BACKUP_SUFFIX\n\
+environment variable.\n\
"));
printf ("\n");
/* xgettext: no-wrap */
}
-#define DOT_FREQUENCE 10
+#define DOT_FREQUENCY 10
static void
match_domain (fn1, fn2, definitions, refmlp, resultmlp, stats, processed)
/* Because merging can take a while we print something to signal
we are not dead. */
- if (!quiet && verbosity_level <= 1 && *processed % DOT_FREQUENCE == 0)
+ if (!quiet && verbosity_level <= 1 && *processed % DOT_FREQUENCY == 0)
fputc ('.', stderr);
refmsg = refmlp->item[j];
}
static msgdomain_list_ty *
-merge (fn1, fn2)
+merge (fn1, fn2, defp)
const char *fn1; /* definitions */
const char *fn2; /* references */
+ msgdomain_list_ty **defp; /* return definition list */
{
msgdomain_list_ty *def;
msgdomain_list_ty *ref;
{
/* Remember the old translation although it is not used anymore.
But we mark it as obsolete. */
- defmsg->obsolete = true;
+ message_ty *mp;
+
+ mp = message_copy (defmsg);
+ mp->obsolete = true;
message_list_append (msgdomain_list_sublist (result, domain, 1),
- defmsg);
+ mp);
stats.obsolete++;
}
}
else if (!quiet)
fputs (_(" done.\n"), stderr);
+ /* Return results. */
+ *defp = def;
return result;
}