# Copyright (c) 2012 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. '''
from __future__ import print_function
import re
from grit import exception from grit import lazy_re 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 = lazy_re.compile(r'""|\\\\|\\n|\\t')
# Find portions that need escaping to encode string as a resource string.
_NEED_ESCAPE = lazy_re.compile(r'"|\n|\t|\\|\ \;')
# How to escape certain characters
_ESCAPE_CHARS = { '"' : '""', '\n' : '\\n', '\t' : '\\t', '\\' : '\\\\', ' ' : ' '
}
# 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.'''
@staticmethod 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)
@staticmethod 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)
def _RegExpParse(self, rexp, text_to_parse): '''Overrides _RegExpParse to add shortcut group handling. Otherwise
the same. '''
super(Section, self)._RegExpParse(rexp, text_to_parse)
ifnot self.is_skeleton and len(self.GetTextualIds()) > 0:
group_name = self.GetTextualIds()[0] for c in self.GetCliques():
c.AddToShortcutGroup(group_name)
out = ''
begin_count = 0 assert self.extkey
first_line_re = re.compile(r'\s*' + self.extkey + r'\b') for line in rc_text.splitlines(True): if out or first_line_re.match(line):
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' % (self.extkey, self.rc_file))
self.text_ = out.strip()
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_ = lazy_re.compile(r''' # 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.*?([^"]|""))"\s
| # Lines for controls that have text and an ID
\s+(?P<type2>[A-Z]+)\s+"(?P.*?([^"]|"")?)"\s*,\s*(?P[A-Z0-9_]+)\s*,
| # Lines for controls that have text only
\s+(?P<type3>[A-Z]+)\s+"(?P.*?([^"]|"")?)"\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.ReadSection()
self._RegExpParse(self.dialog_re_, self.text_)
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_ = lazy_re.compile(r''' # 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.*?([^"]|""))"\s
| # Match the caption & ID of a MENUITEM
MENUITEM\s+"(?P.*?([^"]|""))"\s*,\s*(?P[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.ReadSection()
self.single_message_ = tclib.Message(description=self.MENU_MESSAGE_DESCRIPTION)
self._RegExpParse(self.menu_re_, self.text_)
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_ = lazy_re.compile(r''' # 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.ReadSection()
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.
class RCData(Section): '''A resource section that contains some data .'''
# A typical rcdataresource section looks like this: # # IDR_BLAH RCDATA { 1, 2, 3, 4 }
out = ''
begin_count = 0
openbrace_count = 0 assert self.extkey
first_line_re = re.compile(r'\s*' + self.extkey + r'\b') for line in rc_text.splitlines(True): if out or first_line_re.match(line):
out += line
# We stop once the braces balance (could happen in 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' % (self.extkey, self.rc_file))
self.text_ = out
self._RegExpParse(self.dialog_re_, out)
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_ = lazy_re.compile(r''' # 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[A-Z0-9_]+)\s*, ''', re.MULTILINE | re.VERBOSE)
def Parse(self): '''Knows how to parse ACCELERATORS resource sections.'''
self.ReadSection()
self._RegExpParse(self.accelerators_re_, self.text_)
Messung V0.5
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.