commit ab2b3c9d4d81a0138ee0ef6c32ac37b70d21c616 Author: overweight <5324761+overweight@user.noreply.gitee.com> Date: Mon Sep 30 10:40:17 2019 -0400 Package init diff --git a/backport-CVE-2018-18751.patch b/backport-CVE-2018-18751.patch new file mode 100644 index 0000000..4df3ba1 --- /dev/null +++ b/backport-CVE-2018-18751.patch @@ -0,0 +1,136 @@ +From dce3a16e5e9368245735e29bf498dcd5e3e474a4 Mon Sep 17 00:00:00 2001 +From: Daiki Ueno +Date: Thu, 15 Sep 2016 13:57:24 +0200 +Subject: [PATCH] xgettext: Fix crash with *.po file input + +When xgettext was given two *.po files with the same msgid_plural, it +crashed with double-free. Problem reported by Davlet Panech in: +http://lists.gnu.org/archive/html/bug-gettext/2016-09/msg00001.html +* gettext-tools/src/po-gram-gen.y: Don't free msgid_pluralform after +calling do_callback_message, assuming that it takes ownership. +* gettext-tools/src/read-catalog.c (default_add_message): Free +msgid_plural after calling message_alloc. +* gettext-tools/tests/xgettext-po-2: New file. +* gettext-tools/tests/Makefile.am (TESTS): Add new test. +--- + gettext-tools/src/po-gram-gen.y | 13 ++++----- + gettext-tools/src/read-catalog.c | 2 ++ + gettext-tools/tests/Makefile.am | 2 +- + gettext-tools/tests/xgettext-po-2 | 55 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 63 insertions(+), 9 deletions(-) + create mode 100755 gettext-tools/tests/xgettext-po-2 + +diff --git a/gettext-tools/src/po-gram-gen.y b/gettext-tools/src/po-gram-gen.y +index becf5e6..4428e77 100644 +--- a/gettext-tools/src/po-gram-gen.y ++++ b/gettext-tools/src/po-gram-gen.y +@@ -221,14 +221,11 @@ message + check_obsolete ($1, $3); + check_obsolete ($1, $4); + if (!$1.obsolete || pass_obsolete_entries) +- { +- do_callback_message ($1.ctxt, string2, &$1.pos, $3.string, +- $4.rhs.msgstr, $4.rhs.msgstr_len, &$4.pos, +- $1.prev_ctxt, +- $1.prev_id, $1.prev_id_plural, +- $1.obsolete); +- free ($3.string); +- } ++ do_callback_message ($1.ctxt, string2, &$1.pos, $3.string, ++ $4.rhs.msgstr, $4.rhs.msgstr_len, &$4.pos, ++ $1.prev_ctxt, ++ $1.prev_id, $1.prev_id_plural, ++ $1.obsolete); + else + { + free_message_intro ($1); +diff --git a/gettext-tools/src/read-catalog.c b/gettext-tools/src/read-catalog.c +index 571d18e..6af6d20 100644 +--- a/gettext-tools/src/read-catalog.c ++++ b/gettext-tools/src/read-catalog.c +@@ -397,6 +397,8 @@ default_add_message (default_catalog_reader_ty *this, + appropriate. */ + mp = message_alloc (msgctxt, msgid, msgid_plural, msgstr, msgstr_len, + msgstr_pos); ++ if (msgid_plural != NULL) ++ free (msgid_plural); + mp->prev_msgctxt = prev_msgctxt; + mp->prev_msgid = prev_msgid; + mp->prev_msgid_plural = prev_msgid_plural; +diff --git a/gettext-tools/tests/Makefile.am b/gettext-tools/tests/Makefile.am +index 23b09b1..0dfb4d8 100644 +--- a/gettext-tools/tests/Makefile.am ++++ b/gettext-tools/tests/Makefile.am +@@ -95,7 +95,7 @@ TESTS = gettext-1 gettext-2 gettext-3 gettext-4 gettext-5 gettext-6 gettext-7 \ + xgettext-perl-1 xgettext-perl-2 xgettext-perl-3 xgettext-perl-4 \ + xgettext-perl-5 xgettext-perl-6 xgettext-perl-7 xgettext-perl-8 \ + xgettext-php-1 xgettext-php-2 xgettext-php-3 xgettext-php-4 \ +- xgettext-po-1 \ ++ xgettext-po-1 xgettext-po-2 \ + xgettext-properties-1 \ + xgettext-python-1 xgettext-python-2 xgettext-python-3 \ + xgettext-python-4 \ +diff --git a/gettext-tools/tests/xgettext-po-2 b/gettext-tools/tests/xgettext-po-2 +new file mode 100755 +index 0000000..c4bd9d0 +--- /dev/null ++++ b/gettext-tools/tests/xgettext-po-2 +@@ -0,0 +1,55 @@ ++#! /bin/sh ++. "${srcdir=.}/init.sh"; path_prepend_ . ../src ++ ++# Test PO extractors with multiple input files. ++ ++cat < xg-po-2-1.po ++msgid "first msgid" ++msgid_plural "first msgid (plural)" ++msgstr[0] "" ++msgstr[1] "" ++ ++msgid "second msgid" ++msgid_plural "second msgid (plural)" ++msgstr[0] "" ++msgstr[1] "" ++EOF ++ ++cat < xg-po-2-2.po ++msgid "third msgid" ++msgid_plural "third msgid (plural)" ++msgstr[0] "" ++msgstr[1] "" ++ ++msgid "second msgid" ++msgid_plural "second msgid (plural)" ++msgstr[0] "" ++msgstr[1] "" ++EOF ++ ++: ${XGETTEXT=xgettext} ++${XGETTEXT} --omit-header xg-po-2-1.po xg-po-2-2.po -o xg-po-2.tmp.po || Exit 1 ++LC_ALL=C tr -d '\r' < xg-po-2.tmp.po > xg-po-2.po || Exit 1 ++ ++cat < xg-po-2.ok ++msgid "first msgid" ++msgid_plural "first msgid (plural)" ++msgstr[0] "" ++msgstr[1] "" ++ ++msgid "second msgid" ++msgid_plural "second msgid (plural)" ++msgstr[0] "" ++msgstr[1] "" ++ ++msgid "third msgid" ++msgid_plural "third msgid (plural)" ++msgstr[0] "" ++msgstr[1] "" ++EOF ++ ++: ${DIFF=diff} ++${DIFF} xg-po-2.ok xg-po-2.po ++result=$? ++ ++exit $result +-- +1.9.1 + diff --git a/disable-gettext-runtime-test-lock.patch b/disable-gettext-runtime-test-lock.patch new file mode 100644 index 0000000..ec3a59b --- /dev/null +++ b/disable-gettext-runtime-test-lock.patch @@ -0,0 +1,47 @@ +commit bd2c6ca2b7ae0be02d4bb85db79def454f3153fb +Author: rpm-build +AuthorDate: Wed Dec 21 12:50:54 2016 +0100 +Commit: rpm-build +CommitDate: Wed Dec 21 12:52:36 2016 +0100 + + disable-gettext-runtime-test-lock.patch + +diff --git a/gettext-runtime/tests/Makefile.am b/gettext-runtime/tests/Makefile.am +index 88a0684..3a27b79 100644 +--- a/gettext-runtime/tests/Makefile.am ++++ b/gettext-runtime/tests/Makefile.am +@@ -19,7 +19,7 @@ + AUTOMAKE_OPTIONS = 1.11 gnits no-dependencies color-tests subdir-objects + EXTRA_DIST = + +-TESTS = test-lock ++TESTS = + + AM_CPPFLAGS = \ + -I.. \ +diff --git a/gettext-runtime/tests/Makefile.in b/gettext-runtime/tests/Makefile.in +index 4327733..5879146 100644 +--- a/gettext-runtime/tests/Makefile.in ++++ b/gettext-runtime/tests/Makefile.in +@@ -86,7 +86,7 @@ PRE_UNINSTALL = : + POST_UNINSTALL = : + build_triplet = @build@ + host_triplet = @host@ +-TESTS = test-lock$(EXEEXT) ++TESTS = + check_PROGRAMS = test-lock$(EXEEXT) + subdir = tests + ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +diff --git a/gettext-tools/gnulib-tests/Makefile.in b/gettext-tools/gnulib-tests/Makefile.in +index 26becd9..4b9d0e4 100644 +--- a/gettext-tools/gnulib-tests/Makefile.in ++++ b/gettext-tools/gnulib-tests/Makefile.in +@@ -143,7 +143,7 @@ TESTS = test-set-mode-acl.sh test-set-mode-acl-1.sh \ + test-isnanl-nolibm$(EXEEXT) test-isnanl$(EXEEXT) \ + test-iswblank$(EXEEXT) test-langinfo$(EXEEXT) \ + test-linkedhash_list$(EXEEXT) test-locale$(EXEEXT) \ +- test-localename$(EXEEXT) test-lock$(EXEEXT) \ ++ test-localename$(EXEEXT) \ + test-log10$(EXEEXT) test-lseek.sh test-lstat$(EXEEXT) \ + test-malloca$(EXEEXT) test-math$(EXEEXT) test-mbrtowc1.sh \ + test-mbrtowc2.sh test-mbrtowc3.sh test-mbrtowc4.sh \ diff --git a/gettext-0.19.8-its-segfault.patch b/gettext-0.19.8-its-segfault.patch new file mode 100644 index 0000000..430719f --- /dev/null +++ b/gettext-0.19.8-its-segfault.patch @@ -0,0 +1,43 @@ +From a0cab23332a254e3500cac2a3a984472d02180e5 Mon Sep 17 00:00:00 2001 +From: Bruno Haible +Date: Fri, 9 Dec 2016 21:04:31 +0100 +Subject: [PATCH] Fix crash of xgettext with --its option. + +* gettext-tools/src/xgettext.c (main): Free contents of its_dirs only when it +was initialized. Fixes bug introduced on 2016-05-16. +--- + gettext-tools/src/xgettext.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/gettext-tools/src/xgettext.c b/gettext-tools/src/xgettext.c +index f848d76d1..a80ee51ac 100644 +--- a/gettext-tools/src/xgettext.c ++++ b/gettext-tools/src/xgettext.c +@@ -330,7 +330,7 @@ main (int argc, char *argv[]) + bool sort_by_msgid = false; + bool sort_by_filepos = false; + char **dirs; +- char **its_dirs; ++ char **its_dirs = NULL; + char *explicit_its_filename = NULL; + const char *file_name; + const char *files_from = NULL; +@@ -1016,9 +1016,12 @@ warning: file '%s' extension '%s' is unknown; will try C"), filename, extension) + if (its_locating_rules) + locating_rule_list_free (its_locating_rules); + +- for (i = 0; its_dirs[i] != NULL; i++) +- free (its_dirs[i]); +- free (its_dirs); ++ if (its_dirs != NULL) ++ { ++ for (i = 0; its_dirs[i] != NULL; i++) ++ free (its_dirs[i]); ++ free (its_dirs); ++ } + + exit (EXIT_SUCCESS); + } +-- +2.14.3 + diff --git a/gettext-0.19.8.1.tar.xz b/gettext-0.19.8.1.tar.xz new file mode 100644 index 0000000..9f9b075 Binary files /dev/null and b/gettext-0.19.8.1.tar.xz differ diff --git a/gettext-po-send-mail.patch b/gettext-po-send-mail.patch new file mode 100644 index 0000000..2602e1d --- /dev/null +++ b/gettext-po-send-mail.patch @@ -0,0 +1,21 @@ +Index: gettext-0.19.8.1/gettext-tools/misc/po-mode.el +=================================================================== +--- gettext-0.19.8.1.orig/gettext-tools/misc/po-mode.el ++++ gettext-0.19.8.1/gettext-tools/misc/po-mode.el +@@ -3518,10 +3518,12 @@ Write to your team? ('n' if writing to + (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "\n")) + (save-excursion +- (insert-buffer-substring buffer) +- (shell-command-on-region +- (region-beginning) (region-end) +- (concat po-gzip-uuencode-command " " name ".gz") t t)))))) ++ (save-restriction ++ (narrow-to-region (point) (point)) ++ (insert-buffer-substring buffer) ++ (shell-command-on-region ++ (point-min) (point-max) ++ (concat po-gzip-uuencode-command " " name ".gz") t t))))))) + (message "")) + + (defun po-confirm-and-quit () diff --git a/gettext.spec b/gettext.spec new file mode 100644 index 0000000..d9e839e --- /dev/null +++ b/gettext.spec @@ -0,0 +1,247 @@ +%bcond_with jar +%bcond_with java +%bcond_without check + +%global archiveversion 0.19.8 + +Name: gettext +Version: 0.19.8.1 +Release: 19 +License: GPLv3+ and LGPLv2+ +Summary: GNU gettext utilities are a set of tools that provides a framework to help other GNU packages produce multi-lingual messages. +URL: https://www.gnu.org/software/gettext/ +Source: ftp://ftp.gnu.org/gnu/gettext/%{name}-%{version}.tar.xz +Patch0: disable-gettext-runtime-test-lock.patch +Patch1: gettext-po-send-mail.patch +Patch2: gettext-0.19.8-its-segfault.patch + +Patch6000: backport-CVE-2018-18751.patch + +Source2: msghack.py +Source3: msghack.1 + +BuildRequires: gcc-c++, emacs, git, xz, chrpath, autoconf +BuildRequires: ncurses-devel, libxml2-devel, glib2-devel, libcroco-devel, libunistring-devel +%if %{with java} +BuildRequires: gcc-java, libgcj +BuildRequires: java-1.6.0-openjdk-devel +%if %{with jar} +BuildRequires: %{_bindir}/fastjar +BuildRequires: zip, unzip +%endif +%endif +# Requires: %{name}-libs%{_isa} = %{version}-%{release} +Conflicts: filesystem < 3 +Provides: /bin/gettext +Provides: bundled(gnulib) +Provides: %{name}-libs +Obsoletes: %{name}-libs + +%description +GNU gettext is an important step for the GNU Translation Project, as it +is an asset on which we may build many other steps. This package offers +to programmers, translators, and even users, a well integrated set of +tools and documentation. Specifically, the GNU gettext utilities are a +set of tools that provides a framework to help other GNU packages +produce multi-lingual messages. These tools include a set of conventions +about how programs should be written to support message catalogs, a +directory and file naming organization for the message catalogs themselves, +a runtime library supporting the retrieval of translated messages, and +a few stand-alone programs to massage in various ways the sets of +translatable strings, or already translated strings. A special GNU Emacs +mode also helps interested parties in preparing these sets, or bringing +them up to date. + +%package devel +Summary: Development files for %{name} +License: LGPLv2+ and GPLv3+ +Requires: %{name} = %{version}-%{release} +Requires(post): info +Requires(preun): info +Requires: xz +Obsoletes: gettext-autopoint < 0.18.1.1-3 +Provides: gettext-autopoint = %{version}-%{release} +Obsoletes: %{name}-common-devel +Provides: %{name}-common-devel +Provides: emacs-po-mode +Requires: emacs(bin) >= %{_emacs_version} +Provides: emacs-%{name}-el = %{version}-%{release} +Provides: emacs-%{name} +Obsoletes: emacs-%{name} +Obsoletes: emacs-%{name}-el < %{version}-%{release} +Obsoletes: msghack +Provides: msghack + +%description devel +This package provides development files and tools which help edit/alter +po files, if you need to compile gettext related application/library or +edit/alter po files, you should install this package. +want to add gettext support for your project. + +%package help +Summary: help info for %{name} + +%description help +This package contains man, license, info, readme, copyright, license. + +%prep +%autosetup -n %{name}-%{version} -S git + +%build +%if %{with java} +export JAVAC=gcj +%if %{with jar} +export JAR=fastjar +%endif +%endif +%ifarch ppc ppc64 ppc64le +export CFLAGS="$RPM_OPT_FLAGS -D__SUPPORT_SNAN__" +%endif +autoreconf +%configure --without-included-gettext --enable-nls --disable-static \ + --enable-shared --with-pic --disable-csharp --disable-rpath \ +%if %{with java} + --enable-java \ +%else + --disable-java --disable-native-java \ +%endif + --with-xz + +make %{?_smp_mflags} %{?with_java:GCJFLAGS="-findirect-dispatch"} + + +%install +make install DESTDIR=${RPM_BUILD_ROOT} INSTALL="%{__install} -p" \ + lispdir=%{_datadir}/emacs/site-lisp/gettext \ + aclocaldir=%{_datadir}/aclocal EXAMPLESFILES="" + + +install -pm 755 %SOURCE2 ${RPM_BUILD_ROOT}/%{_bindir}/msghack +install -pm 644 %SOURCE3 ${RPM_BUILD_ROOT}/%{_mandir}/man1/msghack.1 + + +# make preloadable_libintl.so executable +chmod 755 ${RPM_BUILD_ROOT}%{_libdir}/preloadable_libintl.so + +rm -f ${RPM_BUILD_ROOT}%{_infodir}/dir + +# doc relocations +for i in gettext-runtime/man/*.html; do + rm ${RPM_BUILD_ROOT}%{_datadir}/doc/gettext/`basename $i` +done +rm -r ${RPM_BUILD_ROOT}%{_datadir}/doc/gettext/javadoc* +rm -rf ${RPM_BUILD_ROOT}%{_datadir}/doc/gettext/examples + +rm -rf htmldoc +mkdir htmldoc +mv ${RPM_BUILD_ROOT}%{_datadir}/doc/gettext/* ${RPM_BUILD_ROOT}/%{_datadir}/doc/libasprintf/* htmldoc +rm -r ${RPM_BUILD_ROOT}%{_datadir}/doc/libasprintf +rm -r ${RPM_BUILD_ROOT}%{_datadir}/doc/gettext + +%if %{with jar} +%else +rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{name}/libintl.jar +%endif + +rm -f ${RPM_BUILD_ROOT}%{_datadir}/%{name}/gettext.jar + +mkdir -p $RPM_BUILD_ROOT%{_datadir}/%{name}/its + +rm ${RPM_BUILD_ROOT}%{_libdir}/lib*.la +rm ${RPM_BUILD_ROOT}%{_libdir}/libgettext{src,lib}.so + +install -d ${RPM_BUILD_ROOT}%{_emacs_sitestartdir} +mv ${RPM_BUILD_ROOT}%{_emacs_sitelispdir}/%{name}/start-po.el ${RPM_BUILD_ROOT}%{_emacs_sitestartdir} +rm ${RPM_BUILD_ROOT}%{_emacs_sitelispdir}/%{name}/start-po.elc + +%find_lang %{name}-runtime +%find_lang %{name}-tools +cat %{name}-*.lang > %{name}.lang + +# cleanup rpaths +for i in $RPM_BUILD_ROOT%{_bindir}/* `find $RPM_BUILD_ROOT%{_libdir} -type f`; do + if file $i | grep "ELF 64-bit" >/dev/null; then + chrpath -l $i && chrpath --delete $i + fi +done + + +%if %{with check} +%check +make check LIBUNISTRING=-lunistring +%endif + +%ldconfig_scriptlets libs + +%files -f %{name}.lang +%doc AUTHORS gettext-runtime/BUGS +%doc COPYING gettext-tools/misc/DISCLAIM README +%doc NEWS THANKS +%doc gettext-runtime/man/*.1.html +%doc gettext-runtime/intl/COPYING* +%{_bindir}/* +%exclude %{_bindir}/autopoint +%exclude %{_bindir}/gettextize +%exclude %{_bindir}/msghack +%{_libdir}/%{name} +%if %{with java} +%exclude %{_libdir}/%{name}/gnu.gettext.* +%endif +%dir %{_datadir}/%{name} +%dir %{_datadir}/%{name}/its +%{_datadir}/%{name}/ABOUT-NLS +%{_datadir}/%{name}/po +%{_datadir}/%{name}/styles +%dir %{_datadir}/%{name}-%{archiveversion} +%{_datadir}/%{name}-%{archiveversion}/its +%{_libdir}/libasprintf.so.0* +%{_libdir}/libgettextpo.so.0* +%{_libdir}/libgettextlib-0.*.so +%{_libdir}/libgettextsrc-0.*.so +%if %{with jar} +%{_datadir}/%{name}/libintl.jar +%endif + +%files devel +%doc gettext-runtime/man/*.3.html ChangeLog +%{_bindir}/autopoint +%{_bindir}/gettextize +%{_datadir}/%{name}/projects/ +%{_datadir}/%{name}/config.rpath +%{_datadir}/%{name}/*.h +%{_datadir}/%{name}/msgunfmt.tcl +%{_datadir}/aclocal/* +%{_includedir}/* +%{_infodir}/autosprintf* +%{_libdir}/libasprintf.so +%{_libdir}/libgettextpo.so +%{_libdir}/preloadable_libintl.so +%{_mandir}/man1/autopoint.1* +%{_mandir}/man3/* +%{_datadir}/%{name}/intl +%{_datadir}/%{name}/javaversion.class +%doc gettext-runtime/intl-java/javadoc* +%if %{with java} +%{_libdir}/%{name}/gnu.gettext.* +%endif +%{_datadir}/%{name}/archive.*.tar.xz +%dir %{_emacs_sitelispdir}/%{name} +%{_emacs_sitelispdir}/%{name}/*.elc +%{_emacs_sitelispdir}/%{name}/*.el +%{_emacs_sitestartdir}/*.el +%license COPYING +%{_bindir}/msghack +%{_mandir}/man1/msghack.1* + +%files help +%{_infodir}/gettext* +%{_mandir}/man1/* +%exclude %{_mandir}/man1/autopoint.1* +%exclude %{_mandir}/man1/msghack.1* + +%changelog +* Fri Sep 27 2019 openEuler Buildteam - 0.19.8.1-19 +- fix CVE-2018-18751 + +* Tue Sep 17 2019 openEuler Buildteam - 0.19.8.1-18 +- Package init diff --git a/msghack.1 b/msghack.1 new file mode 100644 index 0000000..42fcc5b --- /dev/null +++ b/msghack.1 @@ -0,0 +1,45 @@ +.TH MSGHACK "1" "June 2013" "msghack" "User Commands" +.SH NAME +msghack \- alter PO files in ways +.SH SYNOPSIS +.B msghack +[\fIOPTION\fR] \fIfile.po \fR[\fIref.po\fR] +.SH DESCRIPTION +.PP +This program can be used to alter .po files in ways no sane mind would think about. +.TP +\fB\-o\fR +result will be written to FILE +.TP +\fB\-\-invert\fR +invert a po file by switching msgid and msgstr +.TP +\fB\-\-master\fR +join any number of files in a master\-formatted catalog +.TP +\fB\-\-empty\fR +empty the contents of the .po file, creating a .pot +.TP +\fB\-\-append\fR +append entries from ref.po that don't exist in file.po +.PP +Note: It is just a replacement of msghack for backward support. +.PP +This program can be used to alter .po files in ways no sane mind would think about. +.TP +\fB\-o\fR +result will be written to FILE +.TP +\fB\-\-invert\fR +invert a po file by switching msgid and msgstr +.TP +\fB\-\-master\fR +join any number of files in a master\-formatted catalog +.TP +\fB\-\-empty\fR +empty the contents of the .po file, creating a .pot +.TP +\fB\-\-append\fR +append entries from ref.po that don't exist in file.po +.PP +Note: It is just a replacement of msghack for backward support. diff --git a/msghack.py b/msghack.py new file mode 100755 index 0000000..358552c --- /dev/null +++ b/msghack.py @@ -0,0 +1,417 @@ +#!/usr/bin/python3 +## -*- coding: utf-8 -*- +## Copyright (C) 2001, 2004, 2008, 2012 Red Hat, Inc. +## Copyright (C) 2001 Trond Eivind Glomsrød + +## 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 . + +""" +A msghack replacement +""" + +import sys + +class GTMessage: + """ + A class containing a message, its msgid and various references pointing at it + """ + + def __init__(self,id=None,message=None,refs=[]): + """ + The constructor for the GTMessage class + @self The object instance + @message The message + @id The messageid associated with the object + """ + self._message=message.strip() + self._id=id.strip() + self._refs=[] + for ref in refs: + self._refs.append(ref) + + def __str__(self): + """ + Return a string representation of the object + @self The object instance + """ + res="" + for ref in self._refs: + res=res+ref+"\n" + res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message) + return res + + def invertedStrings(self): + """ + Returns a string representation, but with msgid and msgstr inverted. + Note: Don't invert the "" string + @self The object instance + """ + res="" + for ref in self._refs: + res=res+ref+"\n" + if not self._id=="\"\"": + res=res+"msgid %s\nmsgstr %s\n" % (self._message,self._id) + else: + res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message) + return res + + def emptyMsgStrings(self): + """ + Return a string representation of the object, but leave the msgstr + empty - create a pot file from a po file + Note: Won't remove the "" string + @self The object instance + """ + res="" + for ref in self._refs: + res=res+ref+"\n" + if not self._id=="\"\"": + res=res+"msgid %s\nmsgstr \"\"\n" % (self._id) + else: + res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message) + return res + + def compareMessage(self,msg): + """ + Return if the messages have identical msgids, 0 otherwise + @self The object instance + @msg The message to compare to + """ + + if self._id == msg._id: + return 1 + return 0 + + +class GTMasterMessage: + """ + A class containing a message, its msgid and various references pointing at it + The difference between GTMessage and GTMasterMessage is that this class + can do less operations, but is able to store multiple msgstrs with identifiers + (usually language, like 'msgst(no)' + """ + + def __init__(self,id=None,refs=[]): + """ + The constructor for the GTMessage class + @self The object instance + @id The messageid associated with the object + """ + self._id=id + self._refs=[] + self._messages=[] + for ref in refs: + self._refs.append(ref) + + def addMessage(self,message,identifier): + """ + Add a new message and identifier to the GTMasterMessage object + @self The object instance + @message The message to append + @identifier The identifier of the message + """ + self._messages.append((identifier,message)) + + def __str__(self): + """ + Return a string representation of the object + @self The object instance + """ + res="" + for ref in self._refs: + res=res+ref+"\n" + res=res+"msgid %s\n" % self._id + for message in self._messages: + res=res+"msgstr(%s) %s\n" %(message[0],message[1]) + res=res+"\n" + return res + +class GTFile: + """ + A class containing the GTMessages contained in a file + """ + + def __init__(self,filename): + """ + The constructor of the GTMFile class + @self The object instance + @filename The file to initialize from + """ + self._filename=filename + self._messages=[] + self.readFile(filename) + + def __str__(self): + """ + Return a string representation of the object + @self The object instance + """ + res="" + for message in self._messages: + res=res+str(message)+"\n" + return res + + def invertedStrings(self): + """ + Return a string representation of the object, with msgid and msgstr + swapped. Will remove duplicates... + @self The object instance + """ + + msght={} + msgar=[] + + for message in self._messages: + if message._id=='""' and len(msgar)==0: + msgar.append(GTMessage(message._id,message._message,message._refs)) + continue + msg=GTMessage(message._message,message._id,message._refs) + if msg._id not in msght: + msght[msg._id]=msg + msgar.append(msg) + else: + msg2=msght[msg._id] + for ref in msg._refs: + msg2._refs.append(ref) + res="" + for message in msgar: + res=res+str(message)+"\n" + return res + + def msgidDupes(self): + """ + Search for duplicates in the msgids. + @self The object instance + """ + msgids={} + res="" + for message in self._messages: + msgid=message._id + if msgid in msgids: + res=res+"Duplicate: %s\n" % (msgid) + else: + msgids[msgid]=1 + return res + + def getMsgstr(self,msgid): + """ + Return the msgstr matching the given id. 'None' if missing + @self The object instance + @msgid The msgid key + """ + + for message in self._messages: + if msgid == message._id: + return message._message + return None + + def emptyMsgStrings(self): + """ + Return a string representation of the object, but leave the msgstr + empty - create a pot file from a po file + @self The object instance + """ + + res="" + for message in self._messages: + res=res+message.emptyMsgStrings()+"\n" + return res + + + def append(self,B): + """ + Append entries from dictionary B which aren't + already present in this dictionary + @self The object instance + @B the dictionary to append messages from + """ + + for message in B._messages: + if not self.getMsgstr(message._id): + self._messages.append(message) + + + def readFile(self,filename): + """ + Read the contents of a file into the GTFile object + @self The object instance + @filename The name of the file to read + """ + + file=open(filename,"r") + msgid="" + msgstr="" + refs=[] + lines=[] + inmsgid=0 + inmsgstr=0 + templines=file.readlines() + for line in templines: + lines.append(line.strip()) + for line in lines: + pos=line.find('"') + pos2=line.rfind('"') + if line and line[0]=="#": + refs.append(line.strip()) + if inmsgstr==0 and line[:6]=="msgstr": + msgstr="" + inmsgstr=1 + inmsgid=0 + if inmsgstr==1: + if pos==-1: + inmsgstr=0 + #Handle entries with and without "" consistently + if msgid[:2]=='""' and len(msgid)>4: + msgid=msgid[2:] + if msgstr[:2]=='""' and len(msgstr)>4: + msgstr=msgstr[2:] + message=GTMessage(msgid,msgstr,refs) + self._messages.append(message) + msgstr="" + msgid="" + refs=[] + else: + msgstr=msgstr+line[pos:pos2+1]+"\n" + if inmsgid==0 and line[:5]=="msgid": + msgid="" + inmsgid=1 + if inmsgid==1: + if pos==-1: + inmsgid=0 + else: + msgid=msgid+line[pos:pos2+1]+"\n" + if msgstr and msgid: + message=GTMessage(msgid,msgstr,refs) + self._messages.append(message) + + +class GTMaster: + """ + A class containing a master catalogue of gettext dictionaries + """ + + def __init__(self,dicts): + """ + The constructor for the GTMaster class + @self The object instance + @dicts An array of dictionaries to merge + """ + self._messages=[] + self.createMaster(dicts) + + def createMaster(self,dicts): + """ + Create the master catalogue + @self The object instance + @dicts An array of dictionaries to merge + """ + + self._master=dicts[0] + self._dicts=dicts[1:] + + for message in self._master._messages: + gtm=GTMasterMessage(message._id,message._refs) + gtm.addMessage(message._message,self._master._filename[:-3]) + for dict in self._dicts: + res=dict.getMsgstr(message._id) + if(res): + gtm.addMessage(res,dict._filename[:-3]) + self._messages.append(gtm) + + def __str__(self): + """ + Return a string representation of the object + @self The object instance + """ + res="" + for message in self._messages: + res=res+str(message)+"\n" + return res + +def printUsage(): + "Print the usage messages" + print("Usage: " + str(sys.argv[0]) + " [OPTION] file.po [ref.po]\n\ +This program can be used to alter .po files in ways no sane mind would think about.\n\ + -o result will be written to FILE\n\ + --invert invert a po file by switching msgid and msgstr\n\ + --master join any number of files in a master-formatted catalog\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\ +\n\ +Note: It is just a replacement of msghack for backward support.\n") + + +if __name__=="__main__": + output=None + res=None + if("-o") in sys.argv: + if (len(sys.argv)<=sys.argv.index("-o")+1): + print("file.po and ref.po are not specified!\n") + printUsage() + exit(1) + output=sys.argv[sys.argv.index("-o")+1] + sys.argv.remove("-o") + sys.argv.remove(output) + if("--invert") in sys.argv: + if (len(sys.argv)<=sys.argv.index("--invert")+1): + print("file.po is not specified!\n") + printUsage() + exit(1) + file=sys.argv[sys.argv.index("--invert")+1] + gtf=GTFile(file) + res1=gtf.msgidDupes() + if res1: + sys.stderr.write(res1) + sys.exit(1) + res=str(gtf.invertedStrings()) + elif("--empty") in sys.argv: + if (len(sys.argv)<=sys.argv.index("--empty")+1): + print("file.po is not specified!\n") + printUsage() + exit(1) + file=sys.argv[sys.argv.index("--empty")+1] + gtf=GTFile(file) + res=str(gtf.emptyMsgStrings()) + elif("--master") in sys.argv: + if (len(sys.argv)<=sys.argv.index("--master")+1): + print("file.po is not specified!\n") + printUsage() + exit(1) + loc=sys.argv.index("--master")+1 + gtfs=[] + for file in sys.argv[loc:]: + gtfs.append(GTFile(file)) + master=GTMaster(gtfs) + res=str(master) + elif("--append") in sys.argv: + if (len(sys.argv)<=sys.argv.index("--append")+2): + print("file.po and/or ref.po are not specified!\n") + printUsage() + exit(1) + file=sys.argv[sys.argv.index("--append")+1] + file2=sys.argv[sys.argv.index("--append")+2] + gtf=GTFile(file) + gtf2=GTFile(file2) + gtf.append(gtf2) + res=str(gtf) + else: + #print("Not implemented: "+str(sys.argv)) + printUsage() + sys.exit(1) + if not output: + print(res) + else: + file=open(output,"w") + file.write(res) + sys.exit(0)