]> git.mxchange.org Git - flightgear.git/commitdiff
lsprop: list properties of <PropertyList> XML files
authormfranz <mfranz>
Mon, 18 Aug 2008 07:35:03 +0000 (07:35 +0000)
committermfranz <mfranz>
Mon, 18 Aug 2008 07:35:03 +0000 (07:35 +0000)
scripts/tools/lsprop [new file with mode: 0755]

diff --git a/scripts/tools/lsprop b/scripts/tools/lsprop
new file mode 100755 (executable)
index 0000000..1f1922c
--- /dev/null
@@ -0,0 +1,274 @@
+#!/usr/bin/python
+import glob, os, sys, string, xml.sax, getopt
+
+
+__doc__ = """\
+List properties of FlightGear's <PropertyList> XML files.
+
+Usage:
+       lsprop -h
+       lsprop [-v] [-i|-I] [-f <format>] [<list-of-xml-files>]
+
+Options:
+       -h, --help         this help screen
+       -v, --verbose      increase verbosity (can be used multiple times)
+       -i, --all-indices  also show null indices in properties
+       -I, --no-indices   don't show any indices in properties
+       -f, --format       set output format  (default: --format="%f +%l: %p = '%'v'")
+
+Format:
+       %f   file path
+       %l   line number
+       %c   column number
+       %p   property path
+       %t   property type
+       %V   raw value (unescaped)
+       %v   cooked value (carriage return, non printable chars etc. escaped)
+       %'v  like %v, but single quotes escaped to \\'
+       %"v  like %v, but double quotes escaped to \\"
+       %%   percent sign
+
+Environment:
+       FG_ROOT
+       FG_HOME
+       LSPROP_FORMAT      overrides default format
+
+If no file arguments are specified, then the following files are assumed:
+       $FG_ROOT/preferences.xml
+       $FG_ROOT/Aircraft/*/*-set.xml
+"""
+
+
+class config:
+       root = "/usr/local/share/FlightGear"
+       home = os.environ["HOME"] + "/.fgfs"
+       format = "%f +%l: %p = '%'v'"
+       verbose = 1
+       indices = 1     # 0: no indices;   1: only indices != [0];   2: all indices
+
+
+def errmsg(msg, color = "31;1"):
+       if os.isatty(2):
+               print >>sys.stderr, "\033[%sm%s\033[m" % (color, msg)
+       else:
+               print >>sys.stderr, msg
+
+
+class Error(Exception):
+       pass
+
+
+class Abort(Exception):
+       pass
+
+
+class XMLError(Exception):
+       def __init__(self, locator, msg):
+               msg = "%s\n\tin %s, line %d, column %d" \
+                               % (msg, locator.getSystemId(), locator.getLineNumber(), \
+                               locator.getColumnNumber())
+               raise Error(msg)
+
+
+class parse_xml_file(xml.sax.handler.ContentHandler):
+       def __init__(self, path, nesting = 0, stack = None):
+               self.level = 0
+               self.path = path
+               self.nesting = nesting
+               self.type = None
+               if stack:
+                       self.stack = stack
+               else:
+                       self.stack = [[None, None, {}, ""]]  # name, index, indices, data
+
+               self.pretty_path = os.path.normpath(path)
+               if path.startswith(config.root):
+                       self.pretty_path = self.pretty_path.replace(config.root, "$FG_ROOT", 1)
+               elif path.startswith(config.home):
+                       self.pretty_path = self.pretty_path.replace(config.home, "$FG_HOME", 1)
+
+               if config.verbose > 1:
+                       errmsg("%s  (%d)" % (path, nesting), "35")
+               if not os.path.exists(path):
+                       raise Error("file doesn't exist: " + path)
+               xml.sax.parse(path, self, self)
+
+       def startElement(self, name, attrs):
+               self.level += 1
+               if self.level == 1:
+                       if name != "PropertyList":
+                               raise XMLError(self.locator, "XML file isn't a <PropertyList>")
+                       return
+
+               if attrs.has_key("n"):
+                       index = int(attrs["n"])
+               elif name in self.stack[-1][2]:
+                       index = self.stack[-1][2][name] + 1
+               else:
+                       index = 0
+               self.stack[-1][2][name] = index
+
+               if attrs.has_key("include"):
+                       path = os.path.dirname(self.path) + "/" + attrs["include"]
+                       if attrs.has_key("omit-node") and attrs["omit-node"] == "y":
+                               self.stack.append([None, None, self.stack[-1][2], ""])
+                       else:
+                               self.stack.append([name, index, {}, ""])
+                       parse_xml_file(path, self.nesting + 1, self.stack)
+               else:
+                       self.stack.append([name, index, {}, ""])
+
+               self.type = "unspecified"
+               if attrs.has_key("type"):
+                       self.type = attrs["type"]
+
+       def endElement(self, name):
+               if not len(self.stack[-1][2]):
+                       path = self.pathname()
+                       if path:
+                               value = self.stack[-1][3]
+                               cooked_value = self.escape(value.encode("iso-8859-15", "backslashreplace"))
+                               try:
+                                       print config.cooked_format % {
+                                               "f": self.pretty_path,
+                                               "l": self.locator.getLineNumber(),
+                                               "c": self.locator.getColumnNumber(),
+                                               "p": path,
+                                               "t": self.type,
+                                               "V": value,
+                                               "v": cooked_value,
+                                               "v'": cooked_value.replace("'", "\\'"),
+                                               'v"': cooked_value.replace('"', '\\"'),
+                                       }
+                               except TypeError, e:
+                                       raise Abort("invalid --format value")
+
+               elif len(string.strip(self.stack[-1][3])):
+                       raise XMLError(self.locator, "child data '" + string.strip(self.stack[-1][3]) + "'")
+
+               self.level -= 1
+               if self.level:
+                       self.stack.pop()
+
+       def characters(self, data):
+               self.stack[-1][3] += data
+
+       def setDocumentLocator(self, locator):
+               self.locator = locator
+
+       def pathname(self):
+               path = ""
+               for e in self.stack[1:]:
+                       if e[0] == None:        # omit-node
+                               continue
+                       path += "/" + e[0]
+                       if e[1] and config.indices == 1 or config.indices == 2:
+                               path += "[%d]" % e[1]
+               return path
+
+       def escape(self, string):
+               s = ""
+               for c in string:
+                       if c == '\n':
+                               s += '\\n'
+                       elif c == '\r':
+                               s += '\\r'
+                       elif c == '\v':
+                               s += '\\v'
+                       elif c == '\\':
+                               s += '\\\\'
+                       elif not c.isalnum() and " \t!@#$%^&*()_+|~-=\`[]{};':\",./<>?".find(c) < 0:
+                               s += "\\x%02x" % ord(c)
+                       else:
+                               s += c
+               return s
+
+       def warning(self, exception):
+               raise XMLError(self.locator, "WARNING: " + str(exception))
+       def error(self, exception):
+               raise XMLError(self.locator, "ERROR: " + str(exception))
+       def fatalError(self, exception):
+               raise XMLError(self.locator, "FATAL: " + str(exception))
+
+
+def main():
+       if 'FG_ROOT' in os.environ:
+               config.root = os.environ['FG_ROOT'].rstrip("/\\\t ").lstrip()
+       if 'FG_HOME' in os.environ:
+               config.home = os.environ['FG_HOME'].rstrip("/\\\t ").lstrip()
+       if 'LSPROP_FORMAT' in os.environ:
+               config.format = os.environ['LSPROP_FORMAT']
+
+       # options
+       try:
+               opts, args = getopt.getopt(sys.argv[1:], \
+                               "hviIf:", \
+                               ["help", "verbose", "all-indices", "no-indices", "format="])
+       except getopt.GetoptError, msg:
+               print >>sys.stderr, str(msg)
+               return 0
+
+       for o, a in opts:
+               if o in ("-h", "--help"):
+                       print __doc__
+                       print "Current settings:"
+                       print '\t--format="%s"' % config.format.replace('"', '\\"')
+                       return 0
+               if o in ("-v", "--verbose"):
+                       config.verbose += 1
+               if o in ("-i", "--all-indices"):
+                       config.indices = 2
+               if o in ("-I", "--no-indices"):
+                       config.indices = 0
+               if o in ("-f", "--format"):
+                       config.format = a
+
+       # format
+       f = config.format
+       f = f.replace("\\e", "\x1b")
+       f = f.replace("\\033", "\x1b")
+       f = f.replace("\\x1b", "\x1b")
+       f = f.replace("%%", "\x01\x01")
+       f = f.replace("%(", "\x01\x01(")
+       f = f.replace("%f", "\x01(f)s")
+       f = f.replace("%l", "\x01(l)d")
+       f = f.replace("%c", "\x01(c)d")
+       f = f.replace("%p", "\x01(p)s")
+       f = f.replace("%t", "\x01(t)s")
+       f = f.replace("%V", "\x01(V)s")
+       f = f.replace("%v", "\x01(v)s")
+       f = f.replace("%'v", "\x01(v')s")
+       f = f.replace('%"v', '\x01(v")s')
+       f = f.replace("%", "%%")
+       f = f.replace("\x01", "%")
+       config.cooked_format = f
+
+       if config.verbose > 1:
+               print >>sys.stderr, "internal format = [%s]" % config.cooked_format
+
+       if not len(args):
+               args = [config.root + "/preferences.xml"]
+               if not os.path.exists(args[0]):
+                       errmsg("Error: environment variable FG_ROOT not set or set wrongly?")
+                       return -1
+               for f in glob.glob(config.root + '/Aircraft/*/*-set.xml'):
+                       args.append(f)
+
+       for arg in args:
+               try:
+                       parse_xml_file(arg)
+               except Abort, e:
+                       errmsg("Abort: " + e.args[0])
+                       return -1
+               except Error, e:
+                       errmsg("Error: " + e.args[0])
+               except IOError, (errno, msg):
+                       errmsg("Error: " + msg)
+                       return errno
+               except KeyboardInterrupt:
+                       print >>sys.stderr, "\033[m"
+                       return 0
+
+
+if __name__ == "__main__":
+       sys.exit(main())