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