1# Copyright (C) 2009 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""A module for reading and parsing event-log-tags files."""
16
17import re
18import sys
19
20class Tag(object):
21  __slots__ = ["tagnum", "tagname", "description", "filename", "linenum"]
22
23  def __init__(self, tagnum, tagname, description, filename, linenum):
24    self.tagnum = tagnum
25    self.tagname = tagname
26    self.description = description
27    self.filename = filename
28    self.linenum = linenum
29
30
31class TagFile(object):
32  """Read an input event-log-tags file."""
33  def AddError(self, msg, linenum=None):
34    if linenum is None:
35      linenum = self.linenum
36    self.errors.append((self.filename, linenum, msg))
37
38  def AddWarning(self, msg, linenum=None):
39    if linenum is None:
40      linenum = self.linenum
41    self.warnings.append((self.filename, linenum, msg))
42
43  def __init__(self, filename, file_object=None):
44    """'filename' is the name of the file (included in any error
45    messages).  If 'file_object' is None, 'filename' will be opened
46    for reading."""
47    self.errors = []
48    self.warnings = []
49    self.tags = []
50    self.options = {}
51
52    self.filename = filename
53    self.linenum = 0
54
55    if file_object is None:
56      try:
57        file_object = open(filename, "rb")
58      except (IOError, OSError), e:
59        self.AddError(str(e))
60        return
61
62    try:
63      for self.linenum, line in enumerate(file_object):
64        self.linenum += 1
65        line = re.sub('#.*$', '', line) # strip trailing comments
66        line = line.strip()
67        if not line: continue
68        parts = re.split(r"\s+", line, 2)
69
70        if len(parts) < 2:
71          self.AddError("failed to parse \"%s\"" % (line,))
72          continue
73
74        if parts[0] == "option":
75          self.options[parts[1]] = parts[2:]
76          continue
77
78        if parts[0] == "?":
79          tag = None
80        else:
81          try:
82            tag = int(parts[0])
83          except ValueError:
84            self.AddError("\"%s\" isn't an integer tag or '?'" % (parts[0],))
85            continue
86
87        tagname = parts[1]
88        if len(parts) == 3:
89          description = parts[2]
90        else:
91          description = None
92
93        if description:
94          # EventLog.java checks that the description field is
95          # surrounded by parens, so we should too (to avoid a runtime
96          # crash from badly-formatted descriptions).
97          if not re.match(r"\(.*\)\s*$", description):
98            self.AddError("tag \"%s\" has unparseable description" % (tagname,))
99            continue
100
101        self.tags.append(Tag(tag, tagname, description,
102                             self.filename, self.linenum))
103    except (IOError, OSError), e:
104      self.AddError(str(e))
105
106
107def BooleanFromString(s):
108  """Interpret 's' as a boolean and return its value.  Raise
109  ValueError if it's not something we can interpret as true or
110  false."""
111  s = s.lower()
112  if s in ("true", "t", "1", "on", "yes", "y"):
113    return True
114  if s in ("false", "f", "0", "off", "no", "n"):
115    return False
116  raise ValueError("'%s' not a valid boolean" % (s,))
117
118
119def WriteOutput(output_file, data):
120  """Write 'data' to the given output filename (which may be None to
121  indicate stdout).  Emit an error message and die on any failure.
122  'data' may be a string or a StringIO object."""
123  if not isinstance(data, str):
124    data = data.getvalue()
125  try:
126    if output_file is None:
127      out = sys.stdout
128      output_file = "<stdout>"
129    else:
130      out = open(output_file, "wb")
131    out.write(data)
132    out.close()
133  except (IOError, OSError), e:
134    print >> sys.stderr, "failed to write %s: %s" % (output_file, e)
135    sys.exit(1)
136