File 0001-msgcat-Add-feature-to-use-the-newest-po-file.patch of Package gettext-runtime
From dbf95c6ed2239f4831c06d84cf1eef5890cee1c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mark=C3=A9ta=20Cal=C3=A1bkov=C3=A1?=
<meggy.calabkova@gmail.com>
Date: Thu, 9 Jan 2020 14:40:25 +0100
Subject: [PATCH 1/2] msgcat: Add feature to use the newest po file.
When concatenating po files, it is often useful to prefer strings from
the newest file regardless their ordering on the command line.
Updated to 0.24
---
gettext-tools/src/Makefile.am | 3 +-
gettext-tools/src/message.c | 8 +++
gettext-tools/src/message.h | 4 ++
gettext-tools/src/msgcat.c | 10 ++++
gettext-tools/src/msgl-age.c | 96 +++++++++++++++++++++++++++++++++
gettext-tools/src/msgl-age.h | 36 +++++++++++++
gettext-tools/src/msgl-cat.c | 15 ++++++
gettext-tools/src/msgl-cat.h | 1 +
gettext-tools/src/msgl-header.c | 46 ++++++++++++++++
gettext-tools/src/msgl-header.h | 6 +++
10 files changed, 224 insertions(+), 1 deletion(-)
create mode 100644 gettext-tools/src/msgl-age.c
create mode 100644 gettext-tools/src/msgl-age.h
diff --git a/gettext-tools/src/Makefile.am b/gettext-tools/src/Makefile.am
index 27d2340..bb9f92f 100644
--- a/gettext-tools/src/Makefile.am
+++ b/gettext-tools/src/Makefile.am
@@ -53,7 +53,7 @@ noinst_HEADERS = \
write-catalog.h write-po.h write-properties.h write-stringtable.h \
dir-list.h file-list.h read-po-gram.h cldr-plural.h \
cldr-plural-exp.h locating-rules.h its.h search-path.h \
- msgl-charset.h msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-ofn.h msgl-cat.h \
+ msgl-age.h msgl-charset.h msgl-equal.h msgl-iconv.h msgl-ascii.h msgl-ofn.h msgl-cat.h \
msgl-header.h msgl-english.h msgl-check.h msgl-fsearch.h msgfmt.h msgunfmt.h \
plural-count.h plural-eval.h plural-distrib.h \
read-mo.h write-mo.h \
@@ -218,6 +218,7 @@ libgettextsrc_la_SOURCES = \
read-catalog-special.c \
read-catalog.c read-catalog-file.c \
write-catalog.c write-properties.c write-stringtable.c write-po.c \
+ msgl-age.c \
msgl-ascii.c \
msgl-ofn.c \
msgl-iconv.c \
diff --git a/gettext-tools/src/message.c b/gettext-tools/src/message.c
index ea1f61c..6e3aada 100644
--- a/gettext-tools/src/message.c
+++ b/gettext-tools/src/message.c
@@ -363,6 +363,14 @@ message_list_append (message_list_ty *mlp, message_ty *mp)
}
+void
+message_list_append_list (message_list_ty *mlp, message_list_ty *mlp2)
+{
+ for (int i = 0; i < mlp2->nitems; i++)
+ message_list_append (mlp,mlp2->item[i]);
+}
+
+
void
message_list_prepend (message_list_ty *mlp, message_ty *mp)
{
diff --git a/gettext-tools/src/message.h b/gettext-tools/src/message.h
index c7c9699..6ca13cd 100644
--- a/gettext-tools/src/message.h
+++ b/gettext-tools/src/message.h
@@ -24,6 +24,7 @@
#include "mem-hash-map.h"
#include <stdbool.h>
+#include <time.h>
#ifdef __cplusplus
@@ -271,6 +272,8 @@ extern void
message_list_free (message_list_ty *mlp, int keep_messages);
extern void
message_list_append (message_list_ty *mlp, message_ty *mp);
+extern void
+ message_list_append_list (message_list_ty *mlp, message_list_ty *mlp2);
extern void
message_list_prepend (message_list_ty *mlp, message_ty *mp);
extern void
@@ -353,6 +356,7 @@ struct msgdomain_list_ty
size_t nitems_max;
bool use_hashtable;
const char *encoding; /* canonicalized encoding or NULL if unknown */
+ time_t msgage;
};
extern msgdomain_list_ty *
diff --git a/gettext-tools/src/msgcat.c b/gettext-tools/src/msgcat.c
index 595493e..909f073 100644
--- a/gettext-tools/src/msgcat.c
+++ b/gettext-tools/src/msgcat.c
@@ -89,6 +89,7 @@ static const struct option long_options[] =
{ "to-code", required_argument, NULL, 't' },
{ "unique", no_argument, NULL, 'u' },
{ "use-first", no_argument, NULL, CHAR_MAX + 1 },
+ { "use-newest", no_argument, NULL, CHAR_MAX + 9 },
{ "version", no_argument, NULL, 'V' },
{ "width", required_argument, NULL, 'w' },
{ "more-than", required_argument, NULL, '>' },
@@ -144,6 +145,7 @@ main (int argc, char **argv)
more_than = 0;
less_than = INT_MAX;
use_first = false;
+ use_newest = false;
while ((optchar = getopt_long (argc, argv, "<:>:D:eEf:Fhino:pPst:uVw:",
long_options, NULL)) != EOF)
@@ -280,6 +282,11 @@ main (int argc, char **argv)
message_print_style_filepos (filepos_comment_none);
break;
+ case CHAR_MAX + 9: /* --use-newest */
+ use_newest = true;
+ use_first = true;
+ break;
+
default:
usage (EXIT_FAILURE);
/* NOTREACHED */
@@ -432,6 +439,9 @@ Output details:\n"));
--use-first use first available translation for each\n\
message, don't merge several translations\n"));
printf (_("\
+ --use-newest use the most up-to-date available translation\n\
+ for each message, don't merge several translations\n"));
+ printf (_("\
--lang=CATALOGNAME set 'Language' field in the header entry\n"));
printf (_("\
--color use colors and other text attributes always\n\
diff --git a/gettext-tools/src/msgl-age.c b/gettext-tools/src/msgl-age.c
new file mode 100644
index 0000000..6a902fa
--- /dev/null
+++ b/gettext-tools/src/msgl-age.c
@@ -0,0 +1,96 @@
+/* Message list header time simple parser.
+ Copyright (C) 2019 Free Software Foundation, Inc.
+ Written by Markéta Calábková <mcalabkova@suse.cz>, 2019.
+
+ 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 3 of the License, 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, see <https://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <time.h>
+
+#include "xalloc.h"
+#include "error.h"
+#include "xerror.h"
+#include "xvasprintf.h"
+
+#include "msgl-header.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include "gettext.h"
+
+#include "msgl-age.h"
+
+#define _(str) gettext (str)
+
+void
+msgdomain_sort_by_ages (msgdomain_list_ty **mdlps, size_t nfiles)
+{
+ qsort (mdlps, nfiles, sizeof(msgdomain_list_ty*), msgdomain_compare_ages);
+}
+
+int
+msgdomain_compare_ages (const void * p1, const void * p2)
+{
+ if (difftime((*(msgdomain_list_ty **)p2)->msgage, (*(msgdomain_list_ty **)p1)->msgage) > 0)
+ return 1;
+ else
+ return 0;
+}
+
+void
+msgdomain_read_ages (msgdomain_list_ty *mdlp)
+{
+ time_t times[mdlp->nitems];
+ const char * field = "PO-Revision-Date:";
+ char * where;
+
+ for (int i = 0; i < mdlp->nitems; i++)
+ {
+ message_list_ty *mlp = mdlp->item[i]->messages;
+
+ message_list_read_header_field (mlp, field, &where);
+ }
+ struct tm tmptm;
+ char *trail;
+ memset (&tmptm, 0, sizeof(struct tm));
+ if ((trail = strptime (where, "%Y-%m-%d %H:%M:%S%z", &tmptm)) != NULL)
+ mdlp->msgage = mktime (&tmptm);
+ else if ((trail = strptime (where, "%Y-%m-%d %H:%M:%S", &tmptm)) != NULL)
+ mdlp->msgage = mktime (&tmptm);
+ else if ((trail = strptime (where, "%Y-%m-%d %H:%M%z", &tmptm)) != NULL)
+ mdlp->msgage = mktime (&tmptm);
+ else if ((trail = strptime (where, "%Y-%m-%d %H:%M", &tmptm)) != NULL)
+ mdlp->msgage = mktime (&tmptm);
+ else
+ {
+ /* There is probably no creation date. Assign 0 and throw warning. */
+ mdlp->msgage = 0;
+ multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
+PO-Revision-Date has no or invalid value, assuming it is old.\n\
+")));
+ return;
+ }
+ if ((*trail != '\n') && (*trail != '\0'))
+ multiline_warning (xasprintf (_("warning: ")),
+ xasprintf (_("\
+Unknown trailing characters after PO-Revision-Date, ignoring.\n\
+")));
+}
+
+
+
diff --git a/gettext-tools/src/msgl-age.h b/gettext-tools/src/msgl-age.h
new file mode 100644
index 0000000..af12dd9
--- /dev/null
+++ b/gettext-tools/src/msgl-age.h
@@ -0,0 +1,36 @@
+/* Message list header time simple parser.
+ Copyright (C) 2019 Free Software Foundation, Inc.
+ Written by Markéta Calábková <mcalabkova@suse.cz>, 2019.
+
+ 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 3 of the License, 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, see <https://www.gnu.org/licenses/>. */
+
+#ifndef _MSGL_AGE_H
+#define _MSGL_AGE_H
+
+#include "message.h"
+
+/* Helper function to compare the ages, needed by qsort. */
+int
+ msgdomain_compare_ages (const void *, const void *);
+
+/* Simple function to modify intern ordering of files
+ * such that the newer file comes first. In fact it just calls qsort. */
+void
+ msgdomain_sort_by_ages (msgdomain_list_ty **, size_t);
+
+/* Parse dates in PO-Revision-Date: and store them inside the structure. */
+void
+ msgdomain_read_ages (msgdomain_list_ty *);
+
+#endif
diff --git a/gettext-tools/src/msgl-cat.c b/gettext-tools/src/msgl-cat.c
index afa01f6..728d685 100644
--- a/gettext-tools/src/msgl-cat.c
+++ b/gettext-tools/src/msgl-cat.c
@@ -36,6 +36,7 @@
#include "message.h"
#include "read-catalog-file.h"
#include "po-charset.h"
+#include "msgl-age.h"
#include "msgl-ascii.h"
#include "msgl-ofn.h"
#include "msgl-equal.h"
@@ -58,6 +59,11 @@ int less_than;
If false, merge all available translations into one and fuzzy it. */
bool use_first;
+/* If true, sort all the translation according to their age and let
+ use_first finish the job.
+ If false, keep the order of translations. */
+bool use_newest;
+
/* If true, merge like msgcomm.
If false, merge like msgcat and msguniq. */
bool msgcomm_mode = false;
@@ -124,6 +130,15 @@ catenate_msgdomain_list (string_list_ty *file_list,
for (n = 0; n < nfiles; n++)
mdlps[n] = read_catalog_file (files[n], input_syntax);
+ /* --use-newest case -- sort messages by time and then let --use-first finish the job */
+ if (use_newest)
+ {
+ for (n = 0; n < nfiles; n++)
+ msgdomain_read_ages (mdlps[n]);
+
+ msgdomain_sort_by_ages (mdlps, nfiles);
+ }
+
/* Determine the canonical name of each input file's encoding. */
canon_charsets = XNMALLOC (nfiles, const char **);
for (n = 0; n < nfiles; n++)
diff --git a/gettext-tools/src/msgl-cat.h b/gettext-tools/src/msgl-cat.h
index 878fa05..811244f 100644
--- a/gettext-tools/src/msgl-cat.h
+++ b/gettext-tools/src/msgl-cat.h
@@ -37,6 +37,7 @@ extern LIBGETTEXTSRC_DLL_VARIABLE int less_than;
/* If true, use the first available translation.
If false, merge all available translations into one and fuzzy it. */
extern LIBGETTEXTSRC_DLL_VARIABLE bool use_first;
+extern LIBGETTEXTSRC_DLL_VARIABLE bool use_newest;
/* If true, merge like msgcomm.
If false, merge like msgcat and msguniq. */
diff --git a/gettext-tools/src/msgl-header.c b/gettext-tools/src/msgl-header.c
index 0e36375..611c540 100644
--- a/gettext-tools/src/msgl-header.c
+++ b/gettext-tools/src/msgl-header.c
@@ -244,3 +244,49 @@ message_list_delete_header_field (message_list_ty *mlp,
}
}
}
+
+void
+message_list_read_header_field (message_list_ty *mlp,
+ const char *field, char **where_ptr)
+{
+ size_t field_len = strlen (field);
+ size_t j;
+
+ /* Search the header entry. */
+ for (j = 0; j < mlp->nitems; j++)
+ if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
+ {
+ /* We found the correct message. */
+ message_ty *mp = mlp->item[j];
+
+ /* Tag the header entry. */
+ const char *header = mp->msgstr;
+
+ /* Test whether the field occurs in the header entry. */
+ const char *h;
+
+ for (h = header; *h != '\0'; )
+ {
+ if (strncmp (h, field, field_len) == 0)
+ break;
+ /* Jump by lines. */
+ h = strchr (h, '\n');
+ if (h == NULL)
+ break;
+ h++;
+ }
+ if (h != NULL && *h != '\0')
+ {
+ /* We found it and it is nonempty. Read the value of the field. */
+ h += field_len + 1;
+ char *enh = strchr (h, '\n');
+ if (enh != NULL && *enh != '\0')
+ {
+ *where_ptr = (char *)XNMALLOC (((enh - h) + 1), char);
+ memcpy (*where_ptr, h, enh - h);
+ /* Make the string null-terminated. */
+ (*where_ptr)[enh-h] = '\0';
+ }
+ }
+ }
+}
diff --git a/gettext-tools/src/msgl-header.h b/gettext-tools/src/msgl-header.h
index 4ca5314..b441de2 100644
--- a/gettext-tools/src/msgl-header.h
+++ b/gettext-tools/src/msgl-header.h
@@ -48,6 +48,12 @@ extern void
message_list_delete_header_field (message_list_ty *mlp,
const char *field);
+/* Read the given field from the header.
+ The FIELD name ends in a colon. */
+extern void
+ message_list_read_header_field (message_list_ty *mlp,
+ const char *field, char **where_ptr);
+
#ifdef __cplusplus
}
--
2.49.0