-
Notifications
You must be signed in to change notification settings - Fork 4
/
applypvs
executable file
·185 lines (150 loc) · 5.61 KB
/
applypvs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Copyright (c) 2015 Michael Davidsaver
Copyright (c) 2012 Brookhaven Science Associates, as Operator of
Brookhaven National Laboratory.
pyPDB is distributed subject to a Software License Agreement found
in file LICENSE that is included with this distribution.
"""
import re, sys, os.path
from warnings import warn
from optparse import OptionParser
import re
parser = OptionParser(usage='%prog [options] <-i file.po ...> <input.db | in.edl>')
parser.add_option('-i', '--po', action='append', default=[],
help='Translation file(s) to apply', metavar='FILE')
parser.add_option('-o', '--output', default='.',
help='Location to write output files', metavar='DIR')
parser.add_option('-F', '--flat', action="store_true", default=False,
help="Don't preserve directory structure in output")
parser.add_option('-R', '--reverse', action='store_true', default=False,
help='Apply reverse translation')
parser.add_option('-m', '--mode', default="db", metavar="NAME",
help='Replacement mode (db, edl, opi, xml, proto, alias)')
opts, args = parser.parse_args()
if len(opts.po)==0:
parser.error('No translation files specified')
mappings={}
src={}
def loadPO(file):
with open(file, 'r') as F:
orig, sub = None, None
for L in F:
L = L.strip()
if not L:
orig, sub = None, None # reset on blank line
elif L.startswith('msgid'):
M = re.match(r'msgid\s+"(.*)"\s*', L)
assert M is not None, L
orig = M.group(1)
assert sub is None
elif L.startswith('msgstr'):
M = re.match(r'msgstr\s+"(.*)"\s*', L)
assert M is not None, L
sub = M.group(1)
assert orig is not None
if not sub:
continue
if orig in mappings:
if mappings[orig] != sub:
warn("Found duplicate definition of '%s'\nIn '%s'\nPrevious '%s'"% \
(orig, file, src[orig]))
else:
pass # duplicate, but idential
else: # new mapping
mappings[orig]=sub
src[orig]=file
elif L.startswith('#'):
pass
elif L.startswith('"'):
pass
else:
raise RuntimeError('Unknown line: '+repr(L))
for po in opts.po:
loadPO(po)
if opts.reverse:
fail=False
nm={}
for orig, sub in mappings.items():
if sub not in nm:
nm[sub]=orig
else:
sys.stderr.write("""Un-reversable mapping
From %(origfile)s '%(orig)s = %(sub)s'
and
From %(prevfile)s '%(prev)s = %(sub)s'
""") % {'orig':orig, 'sub':sub, 'prev':nm[sub],
'origfile':src[orig], 'prevfile':src[nm[sub]]}
fail=True
if fail:
sys.exit(1)
mappings=nm
#valid = PP.alphanums + '$:-[]{}<>()'
actions=[]
if opts.mode in ['db', 'edl']:
for orig, sub in mappings.items():
if opts.mode=='edl':
orig = orig.replace('{', '\{')
sub = sub.replace('{', '\{')
orig = orig.replace('}', '\}')
sub = sub.replace('}', '\}')
# look behind for beginning of line or a non-name charactor
# look ahead for end of line or a non-name charactor
# Records names look like
# record(type, "name")
# field(INPA, "name.VAL EXTRA")
# field(INPA, name.VAL)
# So break names on whitespace, '.', or '"'
tst = b'(?:^|(?<=[\s,"\(]))' + re.escape(orig).encode('utf-8') + b'(?:$|(?=[\s.,"\)]))'
actions.append((re.compile(tst), sub.encode('utf-8')))
elif opts.mode in ['xml','opi']:
from xml.sax.saxutils import escape
for orig, sub in mappings.items():
# Like DB except record names are found
# enclosed like ">$(1):name.FLD<"
orig, sub = map(escape, (orig,sub))
tst = b'''(?:^|(?<=[\s'">]))''' + re.escape(orig).encode('utf-8') + b'''(?:$|(?=[\s.'"<]))'''
actions.append((re.compile(tst), sub.encode('utf-8')))
elif opts.mode=='proto':
for orig, sub in mappings.items():
# Like DB except that macros are "\$1" and record names are found
# enclosed like "(\$1:name.FLD)"
tst = b'(?:^|(?<=[\s"\(]))' + re.escape(orig).encode('utf-8') + b'(?:$|(?=[\)\s."]))'
actions.append((re.compile(tst), sub.encode('utf-8')))
elif opts.mode=='alias':
if opts.output=='.':
out=sys.stdout
else:
out=open(opts.output, 'w')
for orig, sub in mappings.items():
out.write(b'alias("%s",\t"%s")\n'%(orig,sub))
out.close()
sys.exit(0)
else:
sys.stderr.write("Unknown replacement mode %s\n"%opts.mode)
sys.exit(1)
if not os.path.exists(opts.output):
os.makedirs(opts.output)
for f in args:
if opts.flat:
out=os.path.join(opts.output, os.path.basename(f))
else:
out=os.path.normpath(os.path.join(opts.output, f))
DN=os.path.dirname(out)
if len(DN.strip())>0 and not os.path.exists(DN):
os.makedirs(DN)
# Use binary I/O to ensure the EOL stays the same
# in output files
inp=open(f, 'rb')
val=inp.read()
for pat, sub in actions:
try:
val=pat.sub(sub, val)
except TypeError as e:
raise TypeError("%s"%((type(pat),type(sub),type(val)),))
inp.close()
# close input file in case it is about to be overwritten
out=open(out, 'wb')
out.write(val)
out.close()