Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: grit/gather/rc.py

Issue 1442863002: Remove contents of grit's SVN repository. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « grit/gather/policy_json_unittest.py ('k') | grit/gather/rc_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 '''Support for gathering resources from RC files.
7 '''
8
9
10 import re
11
12 from grit import exception
13 from grit import lazy_re
14 from grit import tclib
15
16 from grit.gather import regexp
17
18
19 # Find portions that need unescaping in resource strings. We need to be
20 # careful that a \\n is matched _first_ as a \\ rather than matching as
21 # a \ followed by a \n.
22 # TODO(joi) Handle ampersands if we decide to change them into <ph>
23 # TODO(joi) May need to handle other control characters than \n
24 _NEED_UNESCAPE = lazy_re.compile(r'""|\\\\|\\n|\\t')
25
26 # Find portions that need escaping to encode string as a resource string.
27 _NEED_ESCAPE = lazy_re.compile(r'"|\n|\t|\\|\&nbsp\;')
28
29 # How to escape certain characters
30 _ESCAPE_CHARS = {
31 '"' : '""',
32 '\n' : '\\n',
33 '\t' : '\\t',
34 '\\' : '\\\\',
35 '&nbsp;' : ' '
36 }
37
38 # How to unescape certain strings
39 _UNESCAPE_CHARS = dict([[value, key] for key, value in _ESCAPE_CHARS.items()])
40
41
42
43 class Section(regexp.RegexpGatherer):
44 '''A section from a resource file.'''
45
46 @staticmethod
47 def Escape(text):
48 '''Returns a version of 'text' with characters escaped that need to be
49 for inclusion in a resource section.'''
50 def Replace(match):
51 return _ESCAPE_CHARS[match.group()]
52 return _NEED_ESCAPE.sub(Replace, text)
53
54 @staticmethod
55 def UnEscape(text):
56 '''Returns a version of 'text' with escaped characters unescaped.'''
57 def Replace(match):
58 return _UNESCAPE_CHARS[match.group()]
59 return _NEED_UNESCAPE.sub(Replace, text)
60
61 def _RegExpParse(self, rexp, text_to_parse):
62 '''Overrides _RegExpParse to add shortcut group handling. Otherwise
63 the same.
64 '''
65 super(Section, self)._RegExpParse(rexp, text_to_parse)
66
67 if not self.is_skeleton and len(self.GetTextualIds()) > 0:
68 group_name = self.GetTextualIds()[0]
69 for c in self.GetCliques():
70 c.AddToShortcutGroup(group_name)
71
72 def ReadSection(self):
73 rc_text = self._LoadInputFile()
74
75 out = ''
76 begin_count = 0
77 assert self.extkey
78 first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
79 for line in rc_text.splitlines(True):
80 if out or first_line_re.match(line):
81 out += line
82
83 # we stop once we reach the END for the outermost block.
84 begin_count_was = begin_count
85 if len(out) > 0 and line.strip() == 'BEGIN':
86 begin_count += 1
87 elif len(out) > 0 and line.strip() == 'END':
88 begin_count -= 1
89 if begin_count_was == 1 and begin_count == 0:
90 break
91
92 if len(out) == 0:
93 raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_fi le))
94
95 self.text_ = out.strip()
96
97
98 class Dialog(Section):
99 '''A resource section that contains a dialog resource.'''
100
101 # A typical dialog resource section looks like this:
102 #
103 # IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
104 # STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
105 # CAPTION "About"
106 # FONT 8, "System", 0, 0, 0x0
107 # BEGIN
108 # ICON IDI_KLONK,IDC_MYICON,14,9,20,20
109 # LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
110 # SS_NOPREFIX
111 # LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
112 # DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
113 # CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
114 # BS_AUTORADIOBUTTON,46,51,84,10
115 # END
116
117 # We are using a sorted set of keys, and we assume that the
118 # group name used for descriptions (type) will come after the "text"
119 # group in alphabetical order. We also assume that there cannot be
120 # more than one description per regular expression match.
121 # If that's not the case some descriptions will be clobbered.
122 dialog_re_ = lazy_re.compile('''
123 # The dialog's ID in the first line
124 (?P<id1>[A-Z0-9_]+)\s+DIALOG(EX)?
125 |
126 # The caption of the dialog
127 (?P<type1>CAPTION)\s+"(?P<text1>.*?([^"]|""))"\s
128 |
129 # Lines for controls that have text and an ID
130 \s+(?P<type2>[A-Z]+)\s+"(?P<text2>.*?([^"]|"")?)"\s*,\s*(?P<id2>[A-Z0-9_]+)\ s*,
131 |
132 # Lines for controls that have text only
133 \s+(?P<type3>[A-Z]+)\s+"(?P<text3>.*?([^"]|"")?)"\s*,
134 |
135 # Lines for controls that reference other resources
136 \s+[A-Z]+\s+[A-Z0-9_]+\s*,\s*(?P<id3>[A-Z0-9_]*[A-Z][A-Z0-9_]*)
137 |
138 # This matches "NOT SOME_STYLE" so that it gets consumed and doesn't get
139 # matched by the next option (controls that have only an ID and then just
140 # numbers)
141 \s+NOT\s+[A-Z][A-Z0-9_]+
142 |
143 # Lines for controls that have only an ID and then just numbers
144 \s+[A-Z]+\s+(?P<id4>[A-Z0-9_]*[A-Z][A-Z0-9_]*)\s*,
145 ''', re.MULTILINE | re.VERBOSE)
146
147 def Parse(self):
148 '''Knows how to parse dialog resource sections.'''
149 self.ReadSection()
150 self._RegExpParse(self.dialog_re_, self.text_)
151
152
153 class Menu(Section):
154 '''A resource section that contains a menu resource.'''
155
156 # A typical menu resource section looks something like this:
157 #
158 # IDC_KLONK MENU
159 # BEGIN
160 # POPUP "&File"
161 # BEGIN
162 # MENUITEM "E&xit", IDM_EXIT
163 # MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
164 # POPUP "gonk"
165 # BEGIN
166 # MENUITEM "Klonk && is ""good""", ID_GONK_KLONKIS
167 # END
168 # END
169 # POPUP "&Help"
170 # BEGIN
171 # MENUITEM "&About ...", IDM_ABOUT
172 # END
173 # END
174
175 # Description used for the messages generated for menus, to explain to
176 # the translators how to handle them.
177 MENU_MESSAGE_DESCRIPTION = (
178 'This message represents a menu. Each of the items appears in sequence '
179 '(some possibly within sub-menus) in the menu. The XX01XX placeholders '
180 'serve to separate items. Each item contains an & (ampersand) character '
181 'in front of the keystroke that should be used as a shortcut for that item '
182 'in the menu. Please make sure that no two items in the same menu share '
183 'the same shortcut.'
184 )
185
186 # A dandy regexp to suck all the IDs and translateables out of a menu
187 # resource
188 menu_re_ = lazy_re.compile('''
189 # Match the MENU ID on the first line
190 ^(?P<id1>[A-Z0-9_]+)\s+MENU
191 |
192 # Match the translateable caption for a popup menu
193 POPUP\s+"(?P<text1>.*?([^"]|""))"\s
194 |
195 # Match the caption & ID of a MENUITEM
196 MENUITEM\s+"(?P<text2>.*?([^"]|""))"\s*,\s*(?P<id2>[A-Z0-9_]+)
197 ''', re.MULTILINE | re.VERBOSE)
198
199 def Parse(self):
200 '''Knows how to parse menu resource sections. Because it is important that
201 menu shortcuts are unique within the menu, we return each menu as a single
202 message with placeholders to break up the different menu items, rather than
203 return a single message per menu item. we also add an automatic description
204 with instructions for the translators.'''
205 self.ReadSection()
206 self.single_message_ = tclib.Message(description=self.MENU_MESSAGE_DESCRIPTI ON)
207 self._RegExpParse(self.menu_re_, self.text_)
208
209
210 class Version(Section):
211 '''A resource section that contains a VERSIONINFO resource.'''
212
213 # A typical version info resource can look like this:
214 #
215 # VS_VERSION_INFO VERSIONINFO
216 # FILEVERSION 1,0,0,1
217 # PRODUCTVERSION 1,0,0,1
218 # FILEFLAGSMASK 0x3fL
219 # #ifdef _DEBUG
220 # FILEFLAGS 0x1L
221 # #else
222 # FILEFLAGS 0x0L
223 # #endif
224 # FILEOS 0x4L
225 # FILETYPE 0x2L
226 # FILESUBTYPE 0x0L
227 # BEGIN
228 # BLOCK "StringFileInfo"
229 # BEGIN
230 # BLOCK "040904e4"
231 # BEGIN
232 # VALUE "CompanyName", "TODO: <Company name>"
233 # VALUE "FileDescription", "TODO: <File description>"
234 # VALUE "FileVersion", "1.0.0.1"
235 # VALUE "LegalCopyright", "TODO: (c) <Company name>. All rights r eserved."
236 # VALUE "InternalName", "res_format_test.dll"
237 # VALUE "OriginalFilename", "res_format_test.dll"
238 # VALUE "ProductName", "TODO: <Product name>"
239 # VALUE "ProductVersion", "1.0.0.1"
240 # END
241 # END
242 # BLOCK "VarFileInfo"
243 # BEGIN
244 # VALUE "Translation", 0x409, 1252
245 # END
246 # END
247 #
248 #
249 # In addition to the above fields, VALUE fields named "Comments" and
250 # "LegalTrademarks" may also be translateable.
251
252 version_re_ = lazy_re.compile('''
253 # Match the ID on the first line
254 ^(?P<id1>[A-Z0-9_]+)\s+VERSIONINFO
255 |
256 # Match all potentially translateable VALUE sections
257 \s+VALUE\s+"
258 (
259 CompanyName|FileDescription|LegalCopyright|
260 ProductName|Comments|LegalTrademarks
261 )",\s+"(?P<text1>.*?([^"]|""))"\s
262 ''', re.MULTILINE | re.VERBOSE)
263
264 def Parse(self):
265 '''Knows how to parse VERSIONINFO resource sections.'''
266 self.ReadSection()
267 self._RegExpParse(self.version_re_, self.text_)
268
269 # TODO(joi) May need to override the Translate() method to change the
270 # "Translation" VALUE block to indicate the correct language code.
271
272
273 class RCData(Section):
274 '''A resource section that contains some data .'''
275
276 # A typical rcdataresource section looks like this:
277 #
278 # IDR_BLAH RCDATA { 1, 2, 3, 4 }
279
280 dialog_re_ = lazy_re.compile('''
281 ^(?P<id1>[A-Z0-9_]+)\s+RCDATA\s+(DISCARDABLE)?\s+\{.*?\}
282 ''', re.MULTILINE | re.VERBOSE | re.DOTALL)
283
284 def Parse(self):
285 '''Implementation for resource types w/braces (not BEGIN/END)
286 '''
287 rc_text = self._LoadInputFile()
288
289 out = ''
290 begin_count = 0
291 openbrace_count = 0
292 assert self.extkey
293 first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
294 for line in rc_text.splitlines(True):
295 if out or first_line_re.match(line):
296 out += line
297
298 # We stop once the braces balance (could happen in one line).
299 begin_count_was = begin_count
300 if len(out) > 0:
301 openbrace_count += line.count('{')
302 begin_count += line.count('{')
303 begin_count -= line.count('}')
304 if ((begin_count_was == 1 and begin_count == 0) or
305 (openbrace_count > 0 and begin_count == 0)):
306 break
307
308 if len(out) == 0:
309 raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_fi le))
310
311 self.text_ = out
312
313 self._RegExpParse(self.dialog_re_, out)
314
315
316 class Accelerators(Section):
317 '''An ACCELERATORS table.
318 '''
319
320 # A typical ACCELERATORS section looks like this:
321 #
322 # IDR_ACCELERATOR1 ACCELERATORS
323 # BEGIN
324 # "^C", ID_ACCELERATOR32770, ASCII, NOINVERT
325 # "^V", ID_ACCELERATOR32771, ASCII, NOINVERT
326 # VK_INSERT, ID_ACCELERATOR32772, VIRTKEY, CONTROL, NOINVERT
327 # END
328
329 accelerators_re_ = lazy_re.compile('''
330 # Match the ID on the first line
331 ^(?P<id1>[A-Z0-9_]+)\s+ACCELERATORS\s+
332 |
333 # Match accelerators specified as VK_XXX
334 \s+VK_[A-Z0-9_]+,\s*(?P<id2>[A-Z0-9_]+)\s*,
335 |
336 # Match accelerators specified as e.g. "^C"
337 \s+"[^"]*",\s+(?P<id3>[A-Z0-9_]+)\s*,
338 ''', re.MULTILINE | re.VERBOSE)
339
340 def Parse(self):
341 '''Knows how to parse ACCELERATORS resource sections.'''
342 self.ReadSection()
343 self._RegExpParse(self.accelerators_re_, self.text_)
OLDNEW
« no previous file with comments | « grit/gather/policy_json_unittest.py ('k') | grit/gather/rc_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698