add packaging
[platform/upstream/gettext.git] / packaging / msghack.py
1 #!/usr/bin/python
2 ## -*- coding: utf-8 -*-
3 ## Copyright (C) 2001 Red Hat, Inc.
4 ## Copyright (C) 2001 Trond Eivind Glomsrød <teg@redhat.com>
5
6 ## v0.2 - 2001-08-21
7
8 ## This program is free software; you can redistribute it and/or modify
9 ## it under the terms of the GNU General Public License as published by
10 ## the Free Software Foundation; either version 2 of the License, or
11 ## (at your option) any later version.
12
13 ## This program is distributed in the hope that it will be useful,
14 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 ## GNU General Public License for more details.
17
18 ## You should have received a copy of the GNU General Public License
19 ## along with this program; if not, write to the Free Software
20 ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 """
23 A msghack replacement
24 """
25
26 import string
27 import sys
28
29 class GTMessage:
30     """
31     A class containing a message, its msgid and various references pointing at it
32     """
33
34     def __init__(self,id=None,message=None,refs=[]):
35         """
36         The constructor for the GTMessage class
37         @self The object instance
38         @message The message
39         @id The messageid associated with the object
40         """
41         self._message=string.strip(message)
42         self._id=string.strip(id)
43         self._refs=[]
44         for ref in refs:
45             self._refs.append(ref)
46
47     def __str__(self):
48         """
49         Return a string representation of the object
50         @self The object instance
51         """
52         res=""
53         for ref in self._refs:
54             res=res+ref+"\n"
55         res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
56         return res
57
58     def invertedStrings(self):
59         """
60         Returns a string representation, but with msgid and msgstr inverted.
61         Note: Don't invert the "" string
62         @self The object instance
63         """
64         res=""
65         for ref in self._refs:
66             res=res+ref+"\n"
67         if not self._id=="\"\"":
68             res=res+"msgid %s\nmsgstr %s\n" % (self._message,self._id)
69         else:
70             res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
71         return res
72
73     def emptyMsgStrings(self):
74         """
75         Return a string representation of the object, but leave the msgstr
76         empty - create a pot file from a po file
77         Note: Won't remove the "" string
78         @self The object instance
79         """
80         res=""
81         for ref in self._refs:
82             res=res+ref+"\n"
83         if not self._id=="\"\"":
84             res=res+"msgid %s\nmsgstr \"\"\n" % (self._id)
85         else:
86             res=res+"msgid %s\nmsgstr %s\n" % (self._id,self._message)
87         return res
88         
89     def compareMessage(self,msg):
90         """
91         Return  if the messages have identical msgids, 0 otherwise
92         @self The object instance
93         @msg The message to compare to
94         """
95
96         if self._id == msg._id:
97             return 1
98         return 0
99         
100
101 class GTMasterMessage:
102     """
103     A class containing a message, its msgid and various references pointing at it
104     The difference between GTMessage and GTMasterMessage is that this class
105     can do less operations, but is able to store multiple msgstrs with identifiers
106     (usually language, like 'msgst(no)'
107     """
108
109     def __init__(self,id=None,refs=[]):
110         """
111         The constructor for the GTMessage class
112         @self The object instance
113         @id The messageid associated with the object
114         """
115         self._id=id
116         self._refs=[]
117         self._messages=[]
118         for ref in refs:
119             self._refs.append(ref)
120
121     def addMessage(self,message,identifier):
122         """
123         Add a new message and identifier to the GTMasterMessage object
124         @self The object instance
125         @message The message to append
126         @identifier The identifier of the message
127         """
128         self._messages.append((identifier,message))
129
130     def __str__(self):
131         """
132         Return a string representation of the object
133         @self The object instance
134         """
135         res=""
136         for ref in self._refs:
137             res=res+ref+"\n"
138         res=res+"msgid %s\n" % self._id
139         for message in self._messages:
140             res=res+"msgstr(%s) %s\n" %(message[0],message[1])
141         res=res+"\n"
142         return res
143
144 class GTFile:
145     """
146     A class containing the GTMessages contained in a file
147     """
148
149     def __init__(self,filename):
150         """
151         The constructor of the GTMFile class
152         @self The object instance
153         @filename The  file to initialize from
154         """
155         self._filename=filename
156         self._messages=[]
157         self.readFile(filename)
158
159     def __str__(self):
160         """
161         Return a string representation of the object
162         @self The object instance
163         """
164         res=""
165         for message in self._messages:
166             res=res+str(message)+"\n"
167         return res
168
169     def invertedStrings(self):
170         """
171         Return a string representation of the object, with msgid and msgstr
172         swapped. Will remove duplicates...
173         @self The object instance
174         """
175
176         msght={}
177         msgar=[]
178
179         for message in self._messages:
180             if message._id=='""' and len(msgar)==0:
181                 msgar.append(GTMessage(message._id,message._message,message._refs))
182                 continue
183             msg=GTMessage(message._message,message._id,message._refs)
184             if not msght.has_key(msg._id):
185                 msght[msg._id]=msg
186                 msgar.append(msg)
187             else:
188                 msg2=msght[msg._id]
189                 for ref in msg._refs:
190                     msg2._refs.append(ref)
191         res=""
192         for message in msgar:
193             res=res+str(message)+"\n"
194         return res
195
196     def msgidDupes(self):
197         """
198         Search for duplicates in the msgids.
199         @self The object instance
200         """
201         msgids={}
202         res=""
203         for message in self._messages:
204             msgid=message._id
205             if msgids.has_key(msgid):
206                 res=res+"Duplicate: %s\n" % (msgid)
207             else:
208                 msgids[msgid]=1
209         return res
210
211     def getMsgstr(self,msgid):
212         """
213         Return the msgstr matching the given id. 'None' if missing
214         @self The object instance
215         @msgid The msgid key
216         """
217
218         for message in self._messages:
219             if msgid == message._id:
220                 return message._message
221         return None
222
223     def emptyMsgStrings(self):
224         """
225         Return a string representation of the object, but leave the msgstr
226         empty - create a pot file from a po file
227         @self The object instance
228         """
229         
230         res=""
231         for message in self._messages:
232             res=res+message.emptyMsgStrings()+"\n"
233         return res
234
235             
236     def append(self,B):
237         """
238         Append entries from dictionary B which aren't
239         already present in this dictionary
240         @self The object instance
241         @B the dictionary to append messages from
242         """
243
244         for message in B._messages:
245             if not self.getMsgstr(message._id):
246                 self._messages.append(message)
247                 
248
249     def readFile(self,filename):
250         """
251         Read the contents of a file into the GTFile object
252         @self The object instance
253         @filename The name of the file to read
254         """
255         
256         file=open(filename,"r")
257         msgid=""
258         msgstr=""
259         refs=[]
260         lines=[]
261         inmsgid=0
262         inmsgstr=0
263         templines=file.readlines()
264         for line in templines:
265             lines.append(string.strip(line))
266         for line in lines:
267             pos=string.find(line,'"')
268             pos2=string.rfind(line,'"')
269             if line and line[0]=="#":
270                 refs.append(string.strip(line))
271             if inmsgstr==0 and line[:6]=="msgstr":
272                 msgstr=""
273                 inmsgstr=1
274                 inmsgid=0
275             if inmsgstr==1:
276                 if pos==-1:
277                     inmsgstr=0
278                     #Handle entries with and without "" consistently
279                     if msgid[:2]=='""' and len(msgid)>4: 
280                         msgid=msgid[2:]
281                     if msgstr[:2]=='""' and len(msgstr)>4: 
282                         msgstr=msgstr[2:]
283                     message=GTMessage(msgid,msgstr,refs)
284                     self._messages.append(message)
285                     msgstr=""
286                     msgid=""
287                     refs=[]
288                 else:
289                     msgstr=msgstr+line[pos:pos2+1]+"\n"
290             if inmsgid==0 and line[:5]=="msgid":
291                 msgid=""
292                 inmsgid=1
293             if inmsgid==1:
294                 if pos==-1:
295                     inmsgid=0
296                 else:
297                     msgid=msgid+line[pos:pos2+1]+"\n"
298         if msgstr and msgid:
299             message=GTMessage(msgid,msgstr,refs)
300             self._messages.append(message)
301
302
303 class GTMaster:
304     """
305     A class containing a master catalogue of gettext dictionaries
306     """
307
308     def __init__(self,dicts):
309         """
310         The constructor for the GTMaster class
311         @self The object instance
312         @dicts An array of dictionaries to merge
313         """
314         self._messages=[]
315         self.createMaster(dicts)
316
317     def createMaster(self,dicts):
318         """
319         Create the master catalogue
320         @self The object instance
321         @dicts An array of dictionaries to merge
322         """
323
324         self._master=dicts[0]
325         self._dicts=dicts[1:]
326
327         for message in self._master._messages:
328             gtm=GTMasterMessage(message._id,message._refs)
329             gtm.addMessage(message._message,self._master._filename[:-3])
330             for dict in self._dicts:
331                 res=dict.getMsgstr(message._id)
332                 if(res):
333                     gtm.addMessage(res,dict._filename[:-3])
334             self._messages.append(gtm)
335
336     def __str__(self):
337         """
338         Return a string representation of the object
339         @self The object instance
340         """
341         res=""
342         for message in self._messages:
343             res=res+str(message)+"\n"
344         return res
345
346
347 if __name__=="__main__":
348     output=None
349     res=None
350     if("-o") in sys.argv:
351         output=sys.argv[sys.argv.index("-o")+1]
352         sys.argv.remove("-o")
353         sys.argv.remove(output)
354     if("--invert") in sys.argv:
355         file=sys.argv[sys.argv.index("--invert")+1]
356         gtf=GTFile(file)
357         res1=gtf.msgidDupes()
358         if res1:
359             sys.stderr.write(res1)
360             sys.exit(1)
361         res=str(gtf.invertedStrings())
362     elif("--empty") in sys.argv:
363         file=sys.argv[sys.argv.index("--empty")+1]
364         gtf=GTFile(file)
365         res=str(gtf.emptyMsgStrings())
366     elif("--master") in sys.argv:
367         loc=sys.argv.index("--master")+1
368         gtfs=[]
369         for file in sys.argv[loc:]:
370             gtfs.append(GTFile(file))
371         master=GTMaster(gtfs)
372         res=str(master)
373     elif("--append") in sys.argv:
374         file=sys.argv[sys.argv.index("--append")+1]
375         file2=sys.argv[sys.argv.index("--append")+2]
376         gtf=GTFile(file)
377         gtf2=GTFile(file2)
378         gtf.append(gtf2)
379         res=str(gtf)
380     else:
381         #print "Not implemented: "+str(sys.argv)
382         print "\
383 Usage: ", str(sys.argv[0])," [OPTION] file.po [ref.po]\n\
384 This program can be used to alter .po files in ways no sane mind would think about.\n\
385       -o                result will be written to FILE\n\
386       --invert          invert a po file by switching msgid and msgstr\n\
387       --master          join any number of files in a master-formatted catalog\n\
388       --empty           empty the contents of the .po file, creating a .pot\n\
389       --append          append entries from ref.po that don't exist in file.po\n\
390 \n\
391 Note: It is just a replacement of msghack for backward support.\n\
392 "
393         sys.exit(1)
394     if not output:
395         print res
396     else:
397         file=open(output,"w")
398         file.write(res)
399     sys.exit(0)