617411
#!/usr/bin/python3
617411
## -*- coding: utf-8 -*-
617411
## Copyright (C) 2001, 2004, 2008, 2012 Red Hat, Inc.
617411
## Copyright (C) 2001 Trond Eivind Glomsrød <teg@redhat.com>
617411
617411
## This program is free software: you can redistribute it and/or modify
617411
## it under the terms of the GNU General Public License as published by
617411
## the Free Software Foundation, either version 3 of the License, or
617411
## (at your option) any later version.
617411
617411
## This program is distributed in the hope that it will be useful,
617411
## but WITHOUT ANY WARRANTY; without even the implied warranty of
617411
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
617411
## GNU General Public License for more details.
617411
617411
## You should have received a copy of the GNU General Public License
617411
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
617411
617411
"""
617411
A msghack replacement
617411
"""
617411
617411
import sys
617411
617411
class GTMessage:
617411
    """
617411
    A class containing a message, its msgid and various references pointing at it
617411
    """
617411
617411
    def __init__(self,id=None,message=None,refs=[]):
617411
        """
617411
        The constructor for the GTMessage class
617411
        @self The object instance
617411
        @message The message
617411
        @id The messageid associated with the object
617411
        """
617411
        self._message=message.strip()
617411
        self._id=id.strip()
617411
        self._refs=[]
617411
        for ref in refs:
617411
            self._refs.append(ref)
617411
617411
    def __str__(self):
617411
        """
617411
        Return a string representation of the object
617411
        @self The object instance
617411
        """
617411
        res=""
617411
        for ref in self._refs:
617411
            res=res+ref+"\n"
617411
        res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
617411
        return res
617411
617411
    def invertedStrings(self):
617411
        """
617411
        Returns a string representation, but with msgid and msgstr inverted.
617411
        Note: Don't invert the "" string
617411
        @self The object instance
617411
        """
617411
        res=""
617411
        for ref in self._refs:
617411
            res=res+ref+"\n"
617411
        if not self._id=="\"\"":
617411
            res=res+"msgid %s\nmsgstr %s\n" % (self._message,self._id)
617411
        else:
617411
            res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
617411
        return res
617411
617411
    def emptyMsgStrings(self):
617411
        """
617411
        Return a string representation of the object, but leave the msgstr
617411
        empty - create a pot file from a po file
617411
        Note: Won't remove the "" string
617411
        @self The object instance
617411
        """
617411
        res=""
617411
        for ref in self._refs:
617411
            res=res+ref+"\n"
617411
        if not self._id=="\"\"":
617411
            res=res+"msgid %s\nmsgstr \"\"\n" % (self._id)
617411
        else:
617411
            res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
617411
        return res
617411
        
617411
    def compareMessage(self,msg):
617411
        """
617411
        Return  if the messages have identical msgids, 0 otherwise
617411
        @self The object instance
617411
        @msg The message to compare to
617411
        """
617411
617411
        if self._id == msg._id:
617411
            return 1
617411
        return 0
617411
        
617411
617411
class GTMasterMessage:
617411
    """
617411
    A class containing a message, its msgid and various references pointing at it
617411
    The difference between GTMessage and GTMasterMessage is that this class
617411
    can do less operations, but is able to store multiple msgstrs with identifiers
617411
    (usually language, like 'msgst(no)'
617411
    """
617411
617411
    def __init__(self,id=None,refs=[]):
617411
        """
617411
        The constructor for the GTMessage class
617411
        @self The object instance
617411
        @id The messageid associated with the object
617411
        """
617411
        self._id=id
617411
        self._refs=[]
617411
        self._messages=[]
617411
        for ref in refs:
617411
            self._refs.append(ref)
617411
617411
    def addMessage(self,message,identifier):
617411
        """
617411
        Add a new message and identifier to the GTMasterMessage object
617411
        @self The object instance
617411
        @message The message to append
617411
        @identifier The identifier of the message
617411
        """
617411
        self._messages.append((identifier,message))
617411
617411
    def __str__(self):
617411
        """
617411
        Return a string representation of the object
617411
        @self The object instance
617411
        """
617411
        res=""
617411
        for ref in self._refs:
617411
            res=res+ref+"\n"
617411
        res=res+"msgid %s\n" % self._id
617411
        for message in self._messages:
617411
            res=res+"msgstr(%s) %s\n" %(message[0],message[1])
617411
        res=res+"\n"
617411
        return res
617411
617411
class GTFile:
617411
    """
617411
    A class containing the GTMessages contained in a file
617411
    """
617411
617411
    def __init__(self,filename):
617411
        """
617411
        The constructor of the GTMFile class
617411
        @self The object instance
617411
        @filename The  file to initialize from
617411
        """
617411
        self._filename=filename
617411
        self._messages=[]
617411
        self.readFile(filename)
617411
617411
    def __str__(self):
617411
        """
617411
        Return a string representation of the object
617411
        @self The object instance
617411
        """
617411
        res=""
617411
        for message in self._messages:
617411
            res=res+str(message)+"\n"
617411
        return res
617411
617411
    def invertedStrings(self):
617411
        """
617411
        Return a string representation of the object, with msgid and msgstr
617411
        swapped. Will remove duplicates...
617411
        @self The object instance
617411
        """
617411
617411
        msght={}
617411
        msgar=[]
617411
617411
        for message in self._messages:
617411
            if message._id=='""' and len(msgar)==0:
617411
                msgar.append(GTMessage(message._id,message._message,message._refs))
617411
                continue
617411
            msg=GTMessage(message._message,message._id,message._refs)
617411
            if msg._id not in msght:
617411
                msght[msg._id]=msg
617411
                msgar.append(msg)
617411
            else:
617411
                msg2=msght[msg._id]
617411
                for ref in msg._refs:
617411
                    msg2._refs.append(ref)
617411
        res=""
617411
        for message in msgar:
617411
            res=res+str(message)+"\n"
617411
        return res
617411
617411
    def msgidDupes(self):
617411
        """
617411
        Search for duplicates in the msgids.
617411
        @self The object instance
617411
        """
617411
        msgids={}
617411
        res=""
617411
        for message in self._messages:
617411
            msgid=message._id
617411
            if msgid in msgids:
617411
                res=res+"Duplicate: %s\n" % (msgid)
617411
            else:
617411
                msgids[msgid]=1
617411
        return res
617411
617411
    def getMsgstr(self,msgid):
617411
        """
617411
        Return the msgstr matching the given id. 'None' if missing
617411
        @self The object instance
617411
        @msgid The msgid key
617411
        """
617411
617411
        for message in self._messages:
617411
            if msgid == message._id:
617411
                return message._message
617411
        return None
617411
617411
    def emptyMsgStrings(self):
617411
        """
617411
        Return a string representation of the object, but leave the msgstr
617411
        empty - create a pot file from a po file
617411
        @self The object instance
617411
        """
617411
        
617411
        res=""
617411
        for message in self._messages:
617411
            res=res+message.emptyMsgStrings()+"\n"
617411
        return res
617411
617411
            
617411
    def append(self,B):
617411
        """
617411
        Append entries from dictionary B which aren't
617411
        already present in this dictionary
617411
        @self The object instance
617411
        @B the dictionary to append messages from
617411
        """
617411
617411
        for message in B._messages:
617411
            if not self.getMsgstr(message._id):
617411
                self._messages.append(message)
617411
                
617411
617411
    def readFile(self,filename):
617411
        """
617411
        Read the contents of a file into the GTFile object
617411
        @self The object instance
617411
        @filename The name of the file to read
617411
        """
617411
        
617411
        file=open(filename,"r")
617411
        msgid=""
617411
        msgstr=""
617411
        refs=[]
617411
        lines=[]
617411
        inmsgid=0
617411
        inmsgstr=0
617411
        templines=file.readlines()
617411
        for line in templines:
617411
            lines.append(line.strip())
617411
        for line in lines:
617411
            pos=line.find('"')
617411
            pos2=line.rfind('"')
617411
            if line and line[0]=="#":
617411
                refs.append(line.strip())
617411
            if inmsgstr==0 and line[:6]=="msgstr":
617411
                msgstr=""
617411
                inmsgstr=1
617411
                inmsgid=0
617411
            if inmsgstr==1:
617411
                if pos==-1:
617411
                    inmsgstr=0
617411
                    #Handle entries with and without "" consistently
617411
                    if msgid[:2]=='""' and len(msgid)>4: 
617411
                        msgid=msgid[2:]
617411
                    if msgstr[:2]=='""' and len(msgstr)>4: 
617411
                        msgstr=msgstr[2:]
617411
                    message=GTMessage(msgid,msgstr,refs)
617411
                    self._messages.append(message)
617411
                    msgstr=""
617411
                    msgid=""
617411
                    refs=[]
617411
                else:
617411
                    msgstr=msgstr+line[pos:pos2+1]+"\n"
617411
            if inmsgid==0 and line[:5]=="msgid":
617411
                msgid=""
617411
                inmsgid=1
617411
            if inmsgid==1:
617411
                if pos==-1:
617411
                    inmsgid=0
617411
                else:
617411
                    msgid=msgid+line[pos:pos2+1]+"\n"
617411
        if msgstr and msgid:
617411
            message=GTMessage(msgid,msgstr,refs)
617411
            self._messages.append(message)
617411
617411
617411
class GTMaster:
617411
    """
617411
    A class containing a master catalogue of gettext dictionaries
617411
    """
617411
617411
    def __init__(self,dicts):
617411
        """
617411
        The constructor for the GTMaster class
617411
        @self The object instance
617411
        @dicts An array of dictionaries to merge
617411
        """
617411
        self._messages=[]
617411
        self.createMaster(dicts)
617411
617411
    def createMaster(self,dicts):
617411
        """
617411
        Create the master catalogue
617411
        @self The object instance
617411
        @dicts An array of dictionaries to merge
617411
        """
617411
617411
        self._master=dicts[0]
617411
        self._dicts=dicts[1:]
617411
617411
        for message in self._master._messages:
617411
            gtm=GTMasterMessage(message._id,message._refs)
617411
            gtm.addMessage(message._message,self._master._filename[:-3])
617411
            for dict in self._dicts:
617411
                res=dict.getMsgstr(message._id)
617411
                if(res):
617411
                    gtm.addMessage(res,dict._filename[:-3])
617411
            self._messages.append(gtm)
617411
617411
    def __str__(self):
617411
        """
617411
        Return a string representation of the object
617411
        @self The object instance
617411
        """
617411
        res=""
617411
        for message in self._messages:
617411
            res=res+str(message)+"\n"
617411
        return res
617411
617411
def printUsage():
617411
    "Print the usage messages"
617411
    print("Usage: " + str(sys.argv[0]) + " [OPTION] file.po [ref.po]\n\
617411
This program can be used to alter .po files in ways no sane mind would think about.\n\
617411
    -o                result will be written to FILE\n\
617411
    --invert          invert a po file by switching msgid and msgstr\n\
617411
    --master          join any number of files in a master-formatted catalog\n\
617411
    --empty           empty the contents of the .po file, creating a .pot\n\
617411
    --append          append entries from ref.po that don't exist in file.po\n\
617411
\n\
617411
Note: It is just a replacement of msghack for backward support.\n")
617411
617411
617411
if __name__=="__main__":
617411
    output=None
617411
    res=None
617411
    if("-o") in sys.argv:
617411
        if (len(sys.argv)<=sys.argv.index("-o")+1):
617411
                print("file.po and ref.po are not specified!\n")
617411
                printUsage()
617411
                exit(1)
617411
        output=sys.argv[sys.argv.index("-o")+1]
617411
        sys.argv.remove("-o")
617411
        sys.argv.remove(output)
617411
    if("--invert") in sys.argv:
617411
        if (len(sys.argv)<=sys.argv.index("--invert")+1):
617411
            print("file.po is not specified!\n")
617411
            printUsage()
617411
            exit(1)
617411
        file=sys.argv[sys.argv.index("--invert")+1]
617411
        gtf=GTFile(file)
617411
        res1=gtf.msgidDupes()
617411
        if res1:
617411
            sys.stderr.write(res1)
617411
            sys.exit(1)
617411
        res=str(gtf.invertedStrings())
617411
    elif("--empty") in sys.argv:
617411
        if (len(sys.argv)<=sys.argv.index("--empty")+1):
617411
            print("file.po is not specified!\n")
617411
            printUsage()
617411
            exit(1)
617411
        file=sys.argv[sys.argv.index("--empty")+1]
617411
        gtf=GTFile(file)
617411
        res=str(gtf.emptyMsgStrings())
617411
    elif("--master") in sys.argv:
617411
        if (len(sys.argv)<=sys.argv.index("--master")+1):
617411
            print("file.po is not specified!\n")
617411
            printUsage()
617411
            exit(1)
617411
        loc=sys.argv.index("--master")+1
617411
        gtfs=[]
617411
        for file in sys.argv[loc:]:
617411
            gtfs.append(GTFile(file))
617411
        master=GTMaster(gtfs)
617411
        res=str(master)
617411
    elif("--append") in sys.argv:
617411
        if (len(sys.argv)<=sys.argv.index("--append")+2):
617411
            print("file.po and/or ref.po are not specified!\n")
617411
            printUsage()
617411
            exit(1)
617411
        file=sys.argv[sys.argv.index("--append")+1]
617411
        file2=sys.argv[sys.argv.index("--append")+2]
617411
        gtf=GTFile(file)
617411
        gtf2=GTFile(file2)
617411
        gtf.append(gtf2)
617411
        res=str(gtf)
617411
    else:
617411
        #print("Not implemented: "+str(sys.argv))
617411
        printUsage()
617411
        sys.exit(1)
617411
    if not output:
617411
        print(res)
617411
    else:
617411
        file=open(output,"w")
617411
        file.write(res)
617411
    sys.exit(0)