From: Bruno Haible Date: Mon, 22 Oct 2001 08:45:29 +0000 (+0000) Subject: New msgmerge option --update. X-Git-Tag: v0.11~439 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=baa9f2d89f8651e5a51ca045d15a9794ace9720f;p=thirdparty%2Fgettext.git New msgmerge option --update. --- diff --git a/src/ChangeLog b/src/ChangeLog index fac6b35ab..e6180713a 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,21 @@ +2001-09-18 Bruno Haible + + * msgl-equal.h: New file. + * msgl-equal.c: New file. + * msgmerge.c: Include msgl-equal.h, backupfile.h, copy-file.h. + (update_mode, version_control_string, backup_suffix_string): New + variables. + (long_options): Add --backup, --suffix, --update. + (main): Accept options --backup, --suffix, -U/--update. If in update + mode, compare old and new message lists, backup the def.po file, and + overwrite the def.po file. + (usage): Document --backup, --suffix, -U/--update. + (DOT_FREQUENCY): Renamed from DOT_FREQUENCE. + (merge): Also return def.po message list. Don't share messages between + def and result; copy instead. + * Makefile.am (noinst_HEADERS): Add msgl-equal.h. + (msgmerge_SOURCES): Add msgl-equal.c. + 2001-09-19 Bruno Haible * po-hash-gen.y (number): Change type from 'int' to 'size_t'. diff --git a/src/Makefile.am b/src/Makefile.am index 5ea14dc7f..2a423636b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,9 +25,9 @@ msgattrib msgcat msgcomm msgconv msgen msgexec msggrep msguniq noinst_HEADERS = pos.h message.h po-gram.h po-hash.h po-charset.h po-lex.h \ po.h open-po.h read-po.h str-list.h write-po.h dir-list.h file-list.h \ -po-gram-gen.h po-hash-gen.h msgl-charset.h msgl-iconv.h msgl-ascii.h \ -msgl-cat.h msgfmt.h read-mo.h write-mo.h xgettext.h x-c.h x-po.h x-java.h \ -x-ycp.h x-rst.h +po-gram-gen.h po-hash-gen.h msgl-charset.h msgl-equal.h msgl-iconv.h \ +msgl-ascii.h msgl-cat.h msgfmt.h read-mo.h write-mo.h xgettext.h x-c.h x-po.h \ +x-java.h x-ycp.h x-rst.h EXTRA_DIST = FILES @@ -67,7 +67,7 @@ gettext_SOURCES = gettext.c ngettext_SOURCES = ngettext.c msgcmp_SOURCES = msgcmp.c $(COMMON_SOURCES) msgfmt_SOURCES = msgfmt.c $(COMMON_SOURCES) msgl-ascii.c msgl-iconv.c write-mo.c write-java.c plural.c $(FORMAT_SOURCES) -msgmerge_SOURCES = msgmerge.c $(COMMON_SOURCES) msgl-ascii.c write-po.c read-po.c +msgmerge_SOURCES = msgmerge.c $(COMMON_SOURCES) msgl-ascii.c write-po.c read-po.c msgl-equal.c msgunfmt_SOURCES = msgunfmt.c $(COMMON_SOURCES) msgl-ascii.c write-po.c read-po.c read-mo.c read-java.c xgettext_SOURCES = xgettext.c $(COMMON_SOURCES) msgl-ascii.c write-po.c file-list.c x-c.c x-po.c x-java.l x-ycp.c x-rst.c $(FORMAT_SOURCES) msgattrib_SOURCES = msgattrib.c $(COMMON_SOURCES) msgl-ascii.c write-po.c read-po.c diff --git a/src/msgl-equal.c b/src/msgl-equal.c new file mode 100644 index 000000000..4c827ec51 --- /dev/null +++ b/src/msgl-equal.c @@ -0,0 +1,157 @@ +/* Message list test for equality. + Copyright (C) 2001 Free Software Foundation, Inc. + Written by Bruno Haible , 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; +} diff --git a/src/msgl-equal.h b/src/msgl-equal.h new file mode 100644 index 000000000..9953f9746 --- /dev/null +++ b/src/msgl-equal.h @@ -0,0 +1,38 @@ +/* Message list test for equality. + Copyright (C) 2001 Free Software Foundation, Inc. + Written by Bruno Haible , 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. */ + +#ifndef _MSGL_EQUAL_H +#define _MSGL_EQUAL_H + +#include "message.h" + +#include + +/* Test whether the written representation of two messages / message lists + would be the same. */ + +extern bool + message_equal PARAMS ((const message_ty *mp1, const message_ty *mp2)); +extern bool + message_list_equal PARAMS ((const message_list_ty *mlp1, + const message_list_ty *mlp2)); +extern bool + msgdomain_list_equal PARAMS ((const msgdomain_list_ty *mdlp1, + const msgdomain_list_ty *mdlp2)); + +#endif /* _MSGL_EQUAL_H */ diff --git a/src/msgmerge.c b/src/msgmerge.c index ca9f547b8..7a6b2fcf5 100644 --- a/src/msgmerge.c +++ b/src/msgmerge.c @@ -34,8 +34,11 @@ #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) @@ -55,10 +58,16 @@ static bool multi_domain_mode = false; /* 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' }, @@ -73,7 +82,9 @@ static const struct option long_options[] = { "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', }, @@ -93,13 +104,14 @@ struct statistics /* 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 @@ -111,6 +123,7 @@ main (argc, argv) 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; @@ -137,7 +150,7 @@ main (argc, argv) 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) { @@ -188,8 +201,8 @@ main (argc, argv) sort_by_msgid = true; break; - case 'S': - message_print_style_uniforum (); + case 'U': + update_mode = true; break; case 'v': @@ -210,6 +223,18 @@ main (argc, argv) } 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; @@ -246,6 +271,30 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ } /* 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"); @@ -254,8 +303,8 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ 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) @@ -263,8 +312,43 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ 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); } @@ -315,10 +399,33 @@ Input file location:\n\ 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 */ @@ -374,7 +481,7 @@ compendium (filename) } -#define DOT_FREQUENCE 10 +#define DOT_FREQUENCY 10 static void match_domain (fn1, fn2, definitions, refmlp, resultmlp, stats, processed) @@ -395,7 +502,7 @@ 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]; @@ -471,9 +578,10 @@ this message is used but not defined in %s"), fn1); } 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; @@ -570,10 +678,13 @@ merge (fn1, fn2) { /* 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++; } } @@ -591,5 +702,7 @@ merged %ld, fuzzied %ld, missing %ld, obsolete %ld.\n"), else if (!quiet) fputs (_(" done.\n"), stderr); + /* Return results. */ + *defp = def; return result; }