From 53191cf661b395f0b0154bd4830b2b1a825a64e0 Mon Sep 17 00:00:00 2001
From: Ralf Habacker <ralf.habacker@freenet.de>
Date: Thu, 24 Feb 2022 23:30:11 +0100
Subject: [PATCH 3/4] Add command tool 'msghack', which is not packaged at
 least on openSUSE

---
 Makefile.am   |   5 +-
 tools/msghack | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 402 insertions(+), 2 deletions(-)
 create mode 100755 tools/msghack

diff --git a/Makefile.am b/Makefile.am
index 886e080a..d391d137 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -162,6 +162,7 @@ I18NLINGUAS = $(foreach lang,$(ALL_LINGUAS),$(srcdir)/po/$(lang).po)
 # you would simply set this to an empty variable.
 I18NCATALOGS = $(foreach lang,$(ALL_LINGUAS),$(top_builddir)/po/$(lang).mo)
 
+MSGHACK="$(srcdir)/tools/msghack"
 
 $(srcdir)/po/$(PACKAGE).pot: i18nsources dlgsources pdsources $(I18NFILES)
 	rm -f po/tmp*
@@ -177,7 +178,7 @@ $(srcdir)/po/$(PACKAGE).pot: i18nsources dlgsources pdsources $(I18NFILES)
 	for i in $(DLGFILES); do \
 	  cd $(srcdir); \
 	  po4a-gettextize -f xml -m $$i -p po/tmppot1 -o attributes=text && \
-	  msghack -o po/tmppot2 --append po/tmppot po/tmppot1 && \
+	  $(MSGHACK) -o po/tmppot2 --append po/tmppot po/tmppot1 && \
 	  mv po/tmppot2 po/tmppot; \
 	done
 	for i in $(PDFILES); do \
@@ -185,7 +186,7 @@ $(srcdir)/po/$(PACKAGE).pot: i18nsources dlgsources pdsources $(I18NFILES)
 	    -o translated="<plugin><short> <plugin><descr>" \
 	    -o untranslated="<plugin><version> <plugin><author>" \
 	    && \
-	  msghack -o $(srcdir)/po/tmppot2 --append $(srcdir)/po/tmppot $(srcdir)/po/tmppot1 && \
+	  $(MSGHACK) -o $(srcdir)/po/tmppot2 --append $(srcdir)/po/tmppot $(srcdir)/po/tmppot1 && \
 	  mv $(srcdir)/po/tmppot2 $(srcdir)/po/tmppot; \
 	done
 	mv $(srcdir)/po/tmppot $(srcdir)/po/$(PACKAGE).pot
diff --git a/tools/msghack b/tools/msghack
new file mode 100755
index 00000000..64cd6f88
--- /dev/null
+++ b/tools/msghack
@@ -0,0 +1,399 @@
+#!/usr/bin/python
+## -*- coding: utf-8 -*-
+## Copyright (C) 2001 Red Hat, Inc.
+## Copyright (C) 2001 Trond Eivind Glomsrød <teg@redhat.com>
+
+## v0.2 - 2001-08-21
+
+## 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 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, write to the Free Software
+## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+"""
+A msghack replacement
+"""
+
+import string
+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=string.strip(message)
+        self._id=string.strip(id)
+        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 not msght.has_key(msg._id):
+                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 msgids.has_key(msgid):
+                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(string.strip(line))
+        for line in lines:
+            pos=string.find(line,'"')
+            pos2=string.rfind(line,'"')
+            if line and line[0]=="#":
+                refs.append(string.strip(line))
+            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
+
+
+if __name__=="__main__":
+    output=None
+    res=None
+    if("-o") in sys.argv:
+        output=sys.argv[sys.argv.index("-o")+1]
+        sys.argv.remove("-o")
+        sys.argv.remove(output)
+    if("--invert") in sys.argv:
+        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:
+        file=sys.argv[sys.argv.index("--empty")+1]
+        gtf=GTFile(file)
+        res=str(gtf.emptyMsgStrings())
+    elif("--master") in sys.argv:
+        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:
+        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)
+        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\
+"
+        sys.exit(1)
+    if not output:
+        print res
+    else:
+        file=open(output,"w")
+        file.write(res)
+    sys.exit(0)
-- 
2.34.1

