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

Unified Diff: grit/gather/rc.py

Issue 7994004: Initial source commit to grit-i18n project. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 9 years, 3 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « grit/gather/policy_json_unittest.py ('k') | grit/gather/rc_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: grit/gather/rc.py
===================================================================
--- grit/gather/rc.py (revision 0)
+++ grit/gather/rc.py (revision 0)
@@ -0,0 +1,403 @@
+#!/usr/bin/python2.4
+# Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''Support for gathering resources from RC files.
+'''
+
+
+import re
+import types
+
+from grit import clique
+from grit import exception
+from grit import util
+from grit import tclib
+
+from grit.gather import regexp
+
+
+# Find portions that need unescaping in resource strings. We need to be
+# careful that a \\n is matched _first_ as a \\ rather than matching as
+# a \ followed by a \n.
+# TODO(joi) Handle ampersands if we decide to change them into <ph>
+# TODO(joi) May need to handle other control characters than \n
+_NEED_UNESCAPE = re.compile(r'""|\\\\|\\n|\\t')
+
+# Find portions that need escaping to encode string as a resource string.
+_NEED_ESCAPE = re.compile(r'"|\n|\t|\\|\&nbsp\;')
+
+# How to escape certain characters
+_ESCAPE_CHARS = {
+ '"' : '""',
+ '\n' : '\\n',
+ '\t' : '\\t',
+ '\\' : '\\\\',
+ '&nbsp;' : ' '
+}
+
+# How to unescape certain strings
+_UNESCAPE_CHARS = dict([[value, key] for key, value in _ESCAPE_CHARS.items()])
+
+
+
+class Section(regexp.RegexpGatherer):
+ '''A section from a resource file.'''
+
+ def __init__(self, section_text):
+ '''Creates a new object.
+
+ Args:
+ section_text: 'ID_SECTION_ID SECTIONTYPE\n.....\nBEGIN\n.....\nEND'
+ '''
+ regexp.RegexpGatherer.__init__(self, section_text)
+
+ # static method
+ def Escape(text):
+ '''Returns a version of 'text' with characters escaped that need to be
+ for inclusion in a resource section.'''
+ def Replace(match):
+ return _ESCAPE_CHARS[match.group()]
+ return _NEED_ESCAPE.sub(Replace, text)
+ Escape = staticmethod(Escape)
+
+ # static method
+ def UnEscape(text):
+ '''Returns a version of 'text' with escaped characters unescaped.'''
+ def Replace(match):
+ return _UNESCAPE_CHARS[match.group()]
+ return _NEED_UNESCAPE.sub(Replace, text)
+ UnEscape = staticmethod(UnEscape)
+
+ def _RegExpParse(self, rexp, text_to_parse):
+ '''Overrides _RegExpParse to add shortcut group handling. Otherwise
+ the same.
+ '''
+ regexp.RegexpGatherer._RegExpParse(self, rexp, text_to_parse)
+
+ if not self.IsSkeleton() and len(self.GetTextualIds()) > 0:
+ group_name = self.GetTextualIds()[0]
+ for c in self.GetCliques():
+ c.AddToShortcutGroup(group_name)
+
+ # Static method
+ def FromFileImpl(rc_file, extkey, encoding, type):
+ '''Implementation of FromFile. Need to keep separate so we can have
+ a FromFile in this class that has its type set to Section by default.
+ '''
+ if isinstance(rc_file, types.StringTypes):
+ rc_file = util.WrapInputStream(file(rc_file, 'r'), encoding)
+
+ out = ''
+ begin_count = 0
+ for line in rc_file.readlines():
+ if len(out) > 0 or (line.strip().startswith(extkey) and
+ line.strip().split()[0] == extkey):
+ out += line
+
+ # we stop once we reach the END for the outermost block.
+ begin_count_was = begin_count
+ if len(out) > 0 and line.strip() == 'BEGIN':
+ begin_count += 1
+ elif len(out) > 0 and line.strip() == 'END':
+ begin_count -= 1
+ if begin_count_was == 1 and begin_count == 0:
+ break
+
+ if len(out) == 0:
+ raise exception.SectionNotFound('%s in file %s' % (extkey, rc_file))
+
+ return type(out)
+ FromFileImpl = staticmethod(FromFileImpl)
+
+ # static method
+ def FromFile(rc_file, extkey, encoding='cp1252'):
+ '''Retrieves the section of 'rc_file' that has the key 'extkey'. This is
+ matched against the start of a line, and that line and the rest of that
+ section in the RC file is returned.
+
+ If 'rc_file' is a filename, it will be opened for reading using 'encoding'.
+ Otherwise the 'encoding' parameter is ignored.
+
+ This method instantiates an object of type 'type' with the text from the
+ file.
+
+ Args:
+ rc_file: file('') | 'filename.rc'
+ extkey: 'ID_MY_DIALOG'
+ encoding: 'utf-8'
+ type: class to instantiate with text of section
+
+ Return:
+ type(text_of_section)
+ '''
+ return Section.FromFileImpl(rc_file, extkey, encoding, Section)
+ FromFile = staticmethod(FromFile)
+
+
+class Dialog(Section):
+ '''A resource section that contains a dialog resource.'''
+
+ # A typical dialog resource section looks like this:
+ #
+ # IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
+ # STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+ # CAPTION "About"
+ # FONT 8, "System", 0, 0, 0x0
+ # BEGIN
+ # ICON IDI_KLONK,IDC_MYICON,14,9,20,20
+ # LTEXT "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
+ # SS_NOPREFIX
+ # LTEXT "Copyright (C) 2005",IDC_STATIC,49,20,119,8
+ # DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP
+ # CONTROL "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
+ # BS_AUTORADIOBUTTON,46,51,84,10
+ # END
+
+ # We are using a sorted set of keys, and we assume that the
+ # group name used for descriptions (type) will come after the "text"
+ # group in alphabetical order. We also assume that there cannot be
+ # more than one description per regular expression match.
+ # If that's not the case some descriptions will be clobbered.
+ dialog_re_ = re.compile('''
+ # The dialog's ID in the first line
+ (?P<id1>[A-Z0-9_]+)\s+DIALOG(EX)?
+ |
+ # The caption of the dialog
+ (?P<type1>CAPTION)\s+"(?P<text1>.*?([^"]|""))"\s
+ |
+ # Lines for controls that have text and an ID
+ \s+(?P<type2>[A-Z]+)\s+"(?P<text2>.*?([^"]|"")?)"\s*,\s*(?P<id2>[A-Z0-9_]+)\s*,
+ |
+ # Lines for controls that have text only
+ \s+(?P<type3>[A-Z]+)\s+"(?P<text3>.*?([^"]|"")?)"\s*,
+ |
+ # Lines for controls that reference other resources
+ \s+[A-Z]+\s+[A-Z0-9_]+\s*,\s*(?P<id3>[A-Z0-9_]*[A-Z][A-Z0-9_]*)
+ |
+ # This matches "NOT SOME_STYLE" so that it gets consumed and doesn't get
+ # matched by the next option (controls that have only an ID and then just
+ # numbers)
+ \s+NOT\s+[A-Z][A-Z0-9_]+
+ |
+ # Lines for controls that have only an ID and then just numbers
+ \s+[A-Z]+\s+(?P<id4>[A-Z0-9_]*[A-Z][A-Z0-9_]*)\s*,
+ ''', re.MULTILINE | re.VERBOSE)
+
+ def Parse(self):
+ '''Knows how to parse dialog resource sections.'''
+ self._RegExpParse(self.dialog_re_, self.text_)
+
+ # static method
+ def FromFile(rc_file, extkey, encoding = 'cp1252'):
+ return Section.FromFileImpl(rc_file, extkey, encoding, Dialog)
+ FromFile = staticmethod(FromFile)
+
+
+class Menu(Section):
+ '''A resource section that contains a menu resource.'''
+
+ # A typical menu resource section looks something like this:
+ #
+ # IDC_KLONK MENU
+ # BEGIN
+ # POPUP "&File"
+ # BEGIN
+ # MENUITEM "E&xit", IDM_EXIT
+ # MENUITEM "This be ""Klonk"" me like", ID_FILE_THISBE
+ # POPUP "gonk"
+ # BEGIN
+ # MENUITEM "Klonk && is ""good""", ID_GONK_KLONKIS
+ # END
+ # END
+ # POPUP "&Help"
+ # BEGIN
+ # MENUITEM "&About ...", IDM_ABOUT
+ # END
+ # END
+
+ # Description used for the messages generated for menus, to explain to
+ # the translators how to handle them.
+ MENU_MESSAGE_DESCRIPTION = (
+ 'This message represents a menu. Each of the items appears in sequence '
+ '(some possibly within sub-menus) in the menu. The XX01XX placeholders '
+ 'serve to separate items. Each item contains an & (ampersand) character '
+ 'in front of the keystroke that should be used as a shortcut for that item '
+ 'in the menu. Please make sure that no two items in the same menu share '
+ 'the same shortcut.'
+ )
+
+ # A dandy regexp to suck all the IDs and translateables out of a menu
+ # resource
+ menu_re_ = re.compile('''
+ # Match the MENU ID on the first line
+ ^(?P<id1>[A-Z0-9_]+)\s+MENU
+ |
+ # Match the translateable caption for a popup menu
+ POPUP\s+"(?P<text1>.*?([^"]|""))"\s
+ |
+ # Match the caption & ID of a MENUITEM
+ MENUITEM\s+"(?P<text2>.*?([^"]|""))"\s*,\s*(?P<id2>[A-Z0-9_]+)
+ ''', re.MULTILINE | re.VERBOSE)
+
+ def Parse(self):
+ '''Knows how to parse menu resource sections. Because it is important that
+ menu shortcuts are unique within the menu, we return each menu as a single
+ message with placeholders to break up the different menu items, rather than
+ return a single message per menu item. we also add an automatic description
+ with instructions for the translators.'''
+ self.single_message_ = tclib.Message(description=self.MENU_MESSAGE_DESCRIPTION)
+ self._RegExpParse(self.menu_re_, self.text_)
+
+ # static method
+ def FromFile(rc_file, extkey, encoding = 'cp1252'):
+ return Section.FromFileImpl(rc_file, extkey, encoding, Menu)
+ FromFile = staticmethod(FromFile)
+
+
+class Version(Section):
+ '''A resource section that contains a VERSIONINFO resource.'''
+
+ # A typical version info resource can look like this:
+ #
+ # VS_VERSION_INFO VERSIONINFO
+ # FILEVERSION 1,0,0,1
+ # PRODUCTVERSION 1,0,0,1
+ # FILEFLAGSMASK 0x3fL
+ # #ifdef _DEBUG
+ # FILEFLAGS 0x1L
+ # #else
+ # FILEFLAGS 0x0L
+ # #endif
+ # FILEOS 0x4L
+ # FILETYPE 0x2L
+ # FILESUBTYPE 0x0L
+ # BEGIN
+ # BLOCK "StringFileInfo"
+ # BEGIN
+ # BLOCK "040904e4"
+ # BEGIN
+ # VALUE "CompanyName", "TODO: <Company name>"
+ # VALUE "FileDescription", "TODO: <File description>"
+ # VALUE "FileVersion", "1.0.0.1"
+ # VALUE "LegalCopyright", "TODO: (c) <Company name>. All rights reserved."
+ # VALUE "InternalName", "res_format_test.dll"
+ # VALUE "OriginalFilename", "res_format_test.dll"
+ # VALUE "ProductName", "TODO: <Product name>"
+ # VALUE "ProductVersion", "1.0.0.1"
+ # END
+ # END
+ # BLOCK "VarFileInfo"
+ # BEGIN
+ # VALUE "Translation", 0x409, 1252
+ # END
+ # END
+ #
+ #
+ # In addition to the above fields, VALUE fields named "Comments" and
+ # "LegalTrademarks" may also be translateable.
+
+ version_re_ = re.compile('''
+ # Match the ID on the first line
+ ^(?P<id1>[A-Z0-9_]+)\s+VERSIONINFO
+ |
+ # Match all potentially translateable VALUE sections
+ \s+VALUE\s+"
+ (
+ CompanyName|FileDescription|LegalCopyright|
+ ProductName|Comments|LegalTrademarks
+ )",\s+"(?P<text1>.*?([^"]|""))"\s
+ ''', re.MULTILINE | re.VERBOSE)
+
+ def Parse(self):
+ '''Knows how to parse VERSIONINFO resource sections.'''
+ self._RegExpParse(self.version_re_, self.text_)
+
+ # TODO(joi) May need to override the Translate() method to change the
+ # "Translation" VALUE block to indicate the correct language code.
+
+ # static method
+ def FromFile(rc_file, extkey, encoding = 'cp1252'):
+ return Section.FromFileImpl(rc_file, extkey, encoding, Version)
+ FromFile = staticmethod(FromFile)
+
+class RCData(Section):
+ '''A resource section that contains some data .'''
+
+ # A typical rcdataresource section looks like this:
+ #
+ # IDR_BLAH RCDATA { 1, 2, 3, 4 }
+
+ dialog_re_ = re.compile('''
+ ^(?P<id1>[A-Z0-9_]+)\s+RCDATA\s+(DISCARDABLE)?\s+\{.*?\}
+ ''', re.MULTILINE | re.VERBOSE | re.DOTALL)
+
+ def Parse(self):
+ '''Knows how to parse RCDATA resource sections.'''
+ self._RegExpParse(self.dialog_re_, self.text_)
+
+ # static method
+ def FromFile(rc_file, extkey, encoding = 'cp1252'):
+ '''Implementation of FromFile for resource types w/braces (not BEGIN/END)
+ '''
+ if isinstance(rc_file, types.StringTypes):
+ rc_file = util.WrapInputStream(file(rc_file, 'r'), encoding)
+
+ out = ''
+ begin_count = 0
+ openbrace_count = 0
+ for line in rc_file.readlines():
+ if len(out) > 0 or line.strip().startswith(extkey):
+ out += line
+
+ # we stop once balance the braces (could happen on one line)
+ begin_count_was = begin_count
+ if len(out) > 0:
+ openbrace_count += line.count('{')
+ begin_count += line.count('{')
+ begin_count -= line.count('}')
+ if ((begin_count_was == 1 and begin_count == 0) or
+ (openbrace_count > 0 and begin_count == 0)):
+ break
+
+ if len(out) == 0:
+ raise exception.SectionNotFound('%s in file %s' % (extkey, rc_file))
+
+ return RCData(out)
+ FromFile = staticmethod(FromFile)
+
+
+class Accelerators(Section):
+ '''An ACCELERATORS table.
+ '''
+
+ # A typical ACCELERATORS section looks like this:
+ #
+ # IDR_ACCELERATOR1 ACCELERATORS
+ # BEGIN
+ # "^C", ID_ACCELERATOR32770, ASCII, NOINVERT
+ # "^V", ID_ACCELERATOR32771, ASCII, NOINVERT
+ # VK_INSERT, ID_ACCELERATOR32772, VIRTKEY, CONTROL, NOINVERT
+ # END
+
+ accelerators_re_ = re.compile('''
+ # Match the ID on the first line
+ ^(?P<id1>[A-Z0-9_]+)\s+ACCELERATORS\s+
+ |
+ # Match accelerators specified as VK_XXX
+ \s+VK_[A-Z0-9_]+,\s*(?P<id2>[A-Z0-9_]+)\s*,
+ |
+ # Match accelerators specified as e.g. "^C"
+ \s+"[^"]*",\s+(?P<id3>[A-Z0-9_]+)\s*,
+ ''', re.MULTILINE | re.VERBOSE)
+
+ def Parse(self):
+ '''Knows how to parse ACCELERATORS resource sections.'''
+ self._RegExpParse(self.accelerators_re_, self.text_)
+
+ # static method
+ def FromFile(rc_file, extkey, encoding = 'cp1252'):
+ return Section.FromFileImpl(rc_file, extkey, encoding, Accelerators)
+ FromFile = staticmethod(FromFile)
+
Property changes on: grit/gather/rc.py
___________________________________________________________________
Added: svn:eol-style
+ LF
« 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