195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python
295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved.
301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be
401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file.
501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''Support for gathering resources from RC files.
701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''
801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport re
1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import exception
1301fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.orgfrom grit import lazy_re
1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import tclib
1501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.gather import regexp
1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Find portions that need unescaping in resource strings.  We need to be
2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# careful that a \\n is matched _first_ as a \\ rather than matching as
2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# a \ followed by a \n.
2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# TODO(joi) Handle ampersands if we decide to change them into <ph>
2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# TODO(joi) May need to handle other control characters than \n
2401fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org_NEED_UNESCAPE = lazy_re.compile(r'""|\\\\|\\n|\\t')
2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Find portions that need escaping to encode string as a resource string.
2701fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org_NEED_ESCAPE = lazy_re.compile(r'"|\n|\t|\\|\&nbsp\;')
2801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# How to escape certain characters
3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org_ESCAPE_CHARS = {
3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '"' : '""',
3201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '\n' : '\\n',
3301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '\t' : '\\t',
3401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '\\' : '\\\\',
3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '&nbsp;' : ' '
3601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org}
3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# How to unescape certain strings
3901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org_UNESCAPE_CHARS = dict([[value, key] for key, value in _ESCAPE_CHARS.items()])
4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Section(regexp.RegexpGatherer):
4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A section from a resource file.'''
4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
46b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org  @staticmethod
4701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Escape(text):
4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Returns a version of 'text' with characters escaped that need to be
4901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for inclusion in a resource section.'''
5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    def Replace(match):
5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return _ESCAPE_CHARS[match.group()]
5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return _NEED_ESCAPE.sub(Replace, text)
5301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
54b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org  @staticmethod
5501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def UnEscape(text):
5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Returns a version of 'text' with escaped characters unescaped.'''
5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    def Replace(match):
5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return _UNESCAPE_CHARS[match.group()]
5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return _NEED_UNESCAPE.sub(Replace, text)
6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def _RegExpParse(self, rexp, text_to_parse):
6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Overrides _RegExpParse to add shortcut group handling.  Otherwise
6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    the same.
6401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''
65b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    super(Section, self)._RegExpParse(rexp, text_to_parse)
6601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
67ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    if not self.is_skeleton and len(self.GetTextualIds()) > 0:
6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      group_name = self.GetTextualIds()[0]
6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      for c in self.GetCliques():
7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        c.AddToShortcutGroup(group_name)
7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
72ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org  def ReadSection(self):
73b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    rc_text = self._LoadInputFile()
7401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    out = ''
7601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    begin_count = 0
77ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    assert self.extkey
78ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
79ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    for line in rc_text.splitlines(True):
80ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org      if out or first_line_re.match(line):
8101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        out += line
8201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
8301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # we stop once we reach the END for the outermost block.
8401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      begin_count_was = begin_count
8501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if len(out) > 0 and line.strip() == 'BEGIN':
8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        begin_count += 1
8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      elif len(out) > 0 and line.strip() == 'END':
8801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        begin_count -= 1
8901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if begin_count_was == 1 and begin_count == 0:
9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        break
9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(out) == 0:
93ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org      raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_file))
9401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
95ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.text_ = out.strip()
9601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Dialog(Section):
9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A resource section that contains a dialog resource.'''
10001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
10101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # A typical dialog resource section looks like this:
10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # IDD_ABOUTBOX DIALOGEX 22, 17, 230, 75
10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # CAPTION "About"
10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # FONT 8, "System", 0, 0, 0x0
10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # BEGIN
10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     ICON            IDI_KLONK,IDC_MYICON,14,9,20,20
10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     LTEXT           "klonk Version ""yibbee"" 1.0",IDC_STATIC,49,10,119,8,
11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #                     SS_NOPREFIX
11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     LTEXT           "Copyright (C) 2005",IDC_STATIC,49,20,119,8
11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     DEFPUSHBUTTON   "OK",IDOK,195,6,30,11,WS_GROUP
11301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     CONTROL         "Jack ""Black"" Daniels",IDC_RADIO1,"Button",
11401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #                     BS_AUTORADIOBUTTON,46,51,84,10
11501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # END
11601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
11701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # We are using a sorted set of keys, and we assume that the
11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # group name used for descriptions (type) will come after the "text"
11901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # group in alphabetical order. We also assume that there cannot be
12001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # more than one description per regular expression match.
12101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # If that's not the case some descriptions will be clobbered.
12201fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org  dialog_re_ = lazy_re.compile('''
12301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # The dialog's ID in the first line
12401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    (?P<id1>[A-Z0-9_]+)\s+DIALOG(EX)?
12501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # The caption of the dialog
12701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    (?P<type1>CAPTION)\s+"(?P<text1>.*?([^"]|""))"\s
12801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
12901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Lines for controls that have text and an ID
13001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+(?P<type2>[A-Z]+)\s+"(?P<text2>.*?([^"]|"")?)"\s*,\s*(?P<id2>[A-Z0-9_]+)\s*,
13101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
13201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Lines for controls that have text only
13301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+(?P<type3>[A-Z]+)\s+"(?P<text3>.*?([^"]|"")?)"\s*,
13401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
13501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Lines for controls that reference other resources
13601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+[A-Z]+\s+[A-Z0-9_]+\s*,\s*(?P<id3>[A-Z0-9_]*[A-Z][A-Z0-9_]*)
13701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
13801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # This matches "NOT SOME_STYLE" so that it gets consumed and doesn't get
13901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # matched by the next option (controls that have only an ID and then just
14001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # numbers)
14101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+NOT\s+[A-Z][A-Z0-9_]+
14201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
14301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Lines for controls that have only an ID and then just numbers
14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+[A-Z]+\s+(?P<id4>[A-Z0-9_]*[A-Z][A-Z0-9_]*)\s*,
14501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ''', re.MULTILINE | re.VERBOSE)
14601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Parse(self):
14801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Knows how to parse dialog resource sections.'''
149ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.ReadSection()
15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self._RegExpParse(self.dialog_re_, self.text_)
15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Menu(Section):
15401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A resource section that contains a menu resource.'''
15501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # A typical menu resource section looks something like this:
15701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # IDC_KLONK MENU
15901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # BEGIN
16001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     POPUP "&File"
16101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     BEGIN
16201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         MENUITEM "E&xit",                       IDM_EXIT
16301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         MENUITEM "This be ""Klonk"" me like",   ID_FILE_THISBE
16401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         POPUP "gonk"
16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         BEGIN
16601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             MENUITEM "Klonk && is ""good""",           ID_GONK_KLONKIS
16701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         END
16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     END
16901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     POPUP "&Help"
17001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     BEGIN
17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         MENUITEM "&About ...",                  IDM_ABOUT
17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     END
17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # END
17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # Description used for the messages generated for menus, to explain to
17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # the translators how to handle them.
17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  MENU_MESSAGE_DESCRIPTION = (
17801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    'This message represents a menu. Each of the items appears in sequence '
17901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '(some possibly within sub-menus) in the menu. The XX01XX placeholders '
18001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    'serve to separate items. Each item contains an & (ampersand) character '
18101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    'in front of the keystroke that should be used as a shortcut for that item '
18201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    'in the menu. Please make sure that no two items in the same menu share '
18301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    'the same shortcut.'
18401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  )
18501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # A dandy regexp to suck all the IDs and translateables out of a menu
18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # resource
18801fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org  menu_re_ = lazy_re.compile('''
18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match the MENU ID on the first line
19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ^(?P<id1>[A-Z0-9_]+)\s+MENU
19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match the translateable caption for a popup menu
19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    POPUP\s+"(?P<text1>.*?([^"]|""))"\s
19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
19501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match the caption & ID of a MENUITEM
19601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    MENUITEM\s+"(?P<text2>.*?([^"]|""))"\s*,\s*(?P<id2>[A-Z0-9_]+)
19701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ''', re.MULTILINE | re.VERBOSE)
19801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Parse(self):
20001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Knows how to parse menu resource sections.  Because it is important that
20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    menu shortcuts are unique within the menu, we return each menu as a single
20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    message with placeholders to break up the different menu items, rather than
20301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return a single message per menu item.  we also add an automatic description
20401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    with instructions for the translators.'''
205ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.ReadSection()
20601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.single_message_ = tclib.Message(description=self.MENU_MESSAGE_DESCRIPTION)
20701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self._RegExpParse(self.menu_re_, self.text_)
20801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
20901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Version(Section):
21101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A resource section that contains a VERSIONINFO resource.'''
21201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # A typical version info resource can look like this:
21401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # VS_VERSION_INFO VERSIONINFO
21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  FILEVERSION 1,0,0,1
21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  PRODUCTVERSION 1,0,0,1
21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  FILEFLAGSMASK 0x3fL
21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # #ifdef _DEBUG
22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  FILEFLAGS 0x1L
22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # #else
22201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  FILEFLAGS 0x0L
22301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # #endif
22401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  FILEOS 0x4L
22501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  FILETYPE 0x2L
22601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #  FILESUBTYPE 0x0L
22701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # BEGIN
22801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     BLOCK "StringFileInfo"
22901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     BEGIN
23001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         BLOCK "040904e4"
23101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         BEGIN
23201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "CompanyName", "TODO: <Company name>"
23301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "FileDescription", "TODO: <File description>"
23401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "FileVersion", "1.0.0.1"
23501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "LegalCopyright", "TODO: (c) <Company name>.  All rights reserved."
23601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "InternalName", "res_format_test.dll"
23701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "OriginalFilename", "res_format_test.dll"
23801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "ProductName", "TODO: <Product name>"
23901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #             VALUE "ProductVersion", "1.0.0.1"
24001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         END
24101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     END
24201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     BLOCK "VarFileInfo"
24301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     BEGIN
24401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #         VALUE "Translation", 0x409, 1252
24501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #     END
24601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # END
24701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
24801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
24901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # In addition to the above fields, VALUE fields named "Comments" and
25001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # "LegalTrademarks" may also be translateable.
25101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
25201fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org  version_re_ = lazy_re.compile('''
25301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match the ID on the first line
25401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ^(?P<id1>[A-Z0-9_]+)\s+VERSIONINFO
25501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
25601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match all potentially translateable VALUE sections
25701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+VALUE\s+"
25801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    (
25901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      CompanyName|FileDescription|LegalCopyright|
26001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      ProductName|Comments|LegalTrademarks
26101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    )",\s+"(?P<text1>.*?([^"]|""))"\s
26201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ''', re.MULTILINE | re.VERBOSE)
26301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Parse(self):
26501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Knows how to parse VERSIONINFO resource sections.'''
266ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.ReadSection()
26701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self._RegExpParse(self.version_re_, self.text_)
26801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # TODO(joi) May need to override the Translate() method to change the
27001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # "Translation" VALUE block to indicate the correct language code.
27101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass RCData(Section):
27401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A resource section that contains some data .'''
27501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # A typical rcdataresource section looks like this:
27701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
27801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # IDR_BLAH        RCDATA      { 1, 2, 3, 4 }
27901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
28001fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org  dialog_re_ = lazy_re.compile('''
28101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ^(?P<id1>[A-Z0-9_]+)\s+RCDATA\s+(DISCARDABLE)?\s+\{.*?\}
28201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ''', re.MULTILINE | re.VERBOSE | re.DOTALL)
28301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
28401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Parse(self):
285ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    '''Implementation for resource types w/braces (not BEGIN/END)
28601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''
287b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    rc_text = self._LoadInputFile()
28801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
28901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    out = ''
29001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    begin_count = 0
29101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    openbrace_count = 0
292ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    assert self.extkey
293ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    first_line_re = re.compile(r'\s*' + self.extkey + r'\b')
294ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    for line in rc_text.splitlines(True):
295ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org      if out or first_line_re.match(line):
29601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        out += line
29701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
298ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org      # We stop once the braces balance (could happen in one line).
29901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      begin_count_was = begin_count
30001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if len(out) > 0:
30101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        openbrace_count += line.count('{')
30201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        begin_count += line.count('{')
30301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        begin_count -= line.count('}')
30401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if ((begin_count_was == 1 and begin_count == 0) or
30501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org         (openbrace_count > 0 and begin_count == 0)):
30601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        break
30701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
30801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(out) == 0:
309ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org      raise exception.SectionNotFound('%s in file %s' % (self.extkey, self.rc_file))
310ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org
311ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.text_ = out
31201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
313ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self._RegExpParse(self.dialog_re_, out)
31401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
31501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
31601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass Accelerators(Section):
31701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''An ACCELERATORS table.
31801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''
31901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
32001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # A typical ACCELERATORS section looks like this:
32101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #
32201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # IDR_ACCELERATOR1 ACCELERATORS
32301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # BEGIN
32401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #   "^C",           ID_ACCELERATOR32770,    ASCII,  NOINVERT
32501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #   "^V",           ID_ACCELERATOR32771,    ASCII,  NOINVERT
32601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  #   VK_INSERT,      ID_ACCELERATOR32772,    VIRTKEY, CONTROL, NOINVERT
32701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  # END
32801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
32901fadb72b6e94e6511eaffd1874a8cc095f098a7joi@chromium.org  accelerators_re_ = lazy_re.compile('''
33001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match the ID on the first line
33101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ^(?P<id1>[A-Z0-9_]+)\s+ACCELERATORS\s+
33201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
33301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match accelerators specified as VK_XXX
33401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+VK_[A-Z0-9_]+,\s*(?P<id2>[A-Z0-9_]+)\s*,
33501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    |
33601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Match accelerators specified as e.g. "^C"
33701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    \s+"[^"]*",\s+(?P<id3>[A-Z0-9_]+)\s*,
33801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    ''', re.MULTILINE | re.VERBOSE)
33901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
34001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Parse(self):
34101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Knows how to parse ACCELERATORS resource sections.'''
342ec8016c73b3b945b6284746230913d88653f35e7benrg@chromium.org    self.ReadSection()
34301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self._RegExpParse(self.accelerators_re_, self.text_)
344