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"""Formats as a .C file for compilation. 7""" 8 9import os 10import re 11import types 12 13from grit import util 14 15 16def _FormatHeader(root, output_dir): 17 """Returns the required preamble for C files.""" 18 # Find the location of the resource header file, so that we can include 19 # it. 20 resource_header = 'resource.h' # fall back to this 21 for output in root.GetOutputFiles(): 22 if output.attrs['type'] == 'rc_header': 23 resource_header = os.path.abspath(output.GetOutputFilename()) 24 resource_header = util.MakeRelativePath(output_dir, resource_header) 25 return """// This file is automatically generated by GRIT. Do not edit. 26 27#include "%s" 28 29// All strings are UTF-8 30""" % (resource_header) 31# end _FormatHeader() function 32 33 34def Format(root, lang='en', output_dir='.'): 35 """Outputs a C switch statement representing the string table.""" 36 from grit.node import message 37 assert isinstance(lang, types.StringTypes) 38 39 yield _FormatHeader(root, output_dir) 40 41 yield 'const char* GetString(int id) {\n switch (id) {' 42 43 for item in root.ActiveDescendants(): 44 with item: 45 if isinstance(item, message.MessageNode): 46 yield _FormatMessage(item, lang) 47 48 yield '\n default:\n return 0;\n }\n}' 49 50 51def _HexToOct(match): 52 "Return the octal form of the hex numbers" 53 hex = match.group("hex") 54 result = "" 55 while len(hex): 56 next_num = int(hex[2:4], 16) 57 result += "\\" + '%03d' % int(oct(next_num), 10) 58 hex = hex[4:] 59 return match.group("escaped_backslashes") + result 60 61 62def _FormatMessage(item, lang): 63 """Format a single <message> element.""" 64 65 message = item.ws_at_start + item.Translate(lang) + item.ws_at_end 66 # output message with non-ascii chars escaped as octal numbers 67 # C's grammar allows escaped hexadecimal numbers to be infinite, 68 # but octal is always of the form \OOO 69 message = message.encode('utf-8').encode('string_escape') 70 # an escaped char is (\xHH)+ but only if the initial 71 # backslash is not escaped. 72 not_a_backslash = r"(^|[^\\])" # beginning of line or a non-backslash char 73 escaped_backslashes = not_a_backslash + r"(\\\\)*" 74 hex_digits = r"((\\x)[0-9a-f]{2})+" 75 two_digit_hex_num = re.compile( 76 r"(?P<escaped_backslashes>%s)(?P<hex>%s)" 77 % (escaped_backslashes, hex_digits)) 78 message = two_digit_hex_num.sub(_HexToOct, message) 79 # unescape \ (convert \\ back to \) 80 message = message.replace('\\\\', '\\') 81 message = message.replace('"', '\\"') 82 message = util.LINEBREAKS.sub(r'\\n', message) 83 84 name_attr = item.GetTextualIds()[0] 85 86 return '\n case %s:\n return "%s";' % (name_attr, message) 87