17d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#!/usr/bin/env python
27d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)# Copyright (c) 2013 The Chromium Authors. All rights reserved.
37d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
47d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)# found in the LICENSE file.
57d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
67d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)"""Creates simple HTML for running a NaCl module.
77d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
87d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)This script is designed to make the process of creating running
97d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)Native Client executables in the browers simple by creating
107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)boilderplate a .html (and optionally a .nmf) file for a given
117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)Native Client executable (.nexe).
127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
137d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)If the script if given a .nexe file it will produce both html
147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)the nmf files.  If it is given an nmf it will only create
157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)the html file.
167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)"""
177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)import optparse
197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)import os
207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)import sys
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)import subprocess
227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)HTML_TEMPLATE = '''\
257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)<!DOCTYPE html>
267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)<!--
277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)Sample html container for embedded NaCl module.  This file was auto-generated
287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)by the create_html tool which is part of the NaCl SDK.
297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
30f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)The embed tag is setup with PS_STDOUT, PS_STDERR and PS_TTY_PREFIX attributes
31f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)which, for applications linked with ppapi_simple, will cause stdout and stderr
32f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)to be sent to javascript via postMessage.  Also, the postMessage listener
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)assumes that all messages sent via postMessage are strings to be displayed in
34f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)the output textarea.
357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)-->
367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)<html>
377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)<head>
387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <meta http-equiv="Pragma" content="no-cache">
397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <meta http-equiv="Expires" content="-1">
407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <title>%(title)s</title>
417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
427d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)</head>
437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)<body>
447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <h2>Native Client Module: %(module_name)s</h2>
457d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <p>Status: <code id="status">Loading</code></p>
467d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <div id="listener">
487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    <embed id="nacl_module" name="%(module_name)s" src="%(nmf)s"
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)           type="application/x-nacl" width=640 height=480
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)           PS_TTY_PREFIX="tty:"
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)           PS_STDOUT="/dev/tty"
52f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)           PS_STDERR="/dev/tty" >/
537d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  </div>
547d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
557d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <p>Standard output/error:</p>
567d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <textarea id="stdout" rows="25" cols="80">
577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)</textarea>
587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  <script>
607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)listenerDiv = document.getElementById("listener")
617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)stdout = document.getElementById("stdout")
627d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)nacl_module = document.getElementById("nacl_module")
637d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
647d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)function updateStatus(message) {
657d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  document.getElementById("status").innerHTML = message
667d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)function addToStdout(message) {
697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  stdout.value += message;
707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  stdout.scrollTop = stdout.scrollHeight;
717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)function handleMessage(message) {
74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var payload = message.data;
75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  var prefix = "tty:";
76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (typeof(payload) == 'string' && payload.indexOf(prefix) == 0) {
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    addToStdout(payload.slice(prefix.length));
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
797d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
807d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
817d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)function handleCrash(event) {
827d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  updateStatus("Crashed/exited with status: " + nacl_module.exitStatus)
837d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
847d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
857d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)function handleLoad(event) {
867d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  updateStatus("Loaded")
877d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
887d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
897d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)listenerDiv.addEventListener("load", handleLoad, true);
907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)listenerDiv.addEventListener("message", handleMessage, true);
917d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)listenerDiv.addEventListener("crash", handleCrash, true);
927d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  </script>
937d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)</body>
947d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)</html>
957d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)'''
967d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
977d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
987d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)class Error(Exception):
997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  pass
1007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1017d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1027d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)def Log(msg):
1037d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if Log.enabled:
1047d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    sys.stderr.write(str(msg) + '\n')
1057d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)Log.enabled = False
1067d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1077d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1087d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)def CreateHTML(filenames, options):
1097d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  nmf = None
1107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  for filename in filenames:
1127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if not os.path.exists(filename):
1137d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      raise Error('file not found: %s' % filename)
1147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if not os.path.isfile(filename):
1167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      raise Error('specified input is not a file: %s' % filename)
1177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    basename, ext = os.path.splitext(filename)
1197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if ext not in ('.nexe', '.pexe', '.nmf'):
1207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      raise Error('input file must be .nexe, .pexe or .nmf: %s' % filename)
1217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if ext == '.nmf':
1237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      if len(filenames) > 1:
1247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        raise Error('Only one .nmf argument can be specified')
1257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      nmf = filename
1267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    elif len(filenames) > 1 and not options.output:
1277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      raise Error('When specifying muliple input files -o must'
1287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                  ' also be specified.')
1297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  htmlfile = options.output
1317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if not htmlfile:
1327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    htmlfile = basename + '.html'
1337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  basename = os.path.splitext(os.path.basename(htmlfile))[0]
1347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if not nmf:
1367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    nmf = os.path.splitext(htmlfile)[0] + '.nmf'
1377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    Log('creating nmf: %s' % nmf)
1387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    create_nmf = os.path.join(SCRIPT_DIR, 'create_nmf.py')
1397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    staging = os.path.dirname(nmf)
1407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if not staging:
1417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      staging = '.'
1427d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    cmd = [create_nmf, '-s', staging, '-o', nmf] + filenames
1437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if options.verbose:
1447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      cmd.append('-v')
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if options.debug_libs:
146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      cmd.append('--debug-libs')
1477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    Log(cmd)
1487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    try:
1497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      subprocess.check_call(cmd)
1507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    except subprocess.CalledProcessError:
1517d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      raise Error('create_nmf failed')
1527d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1537d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  Log('creating html: %s' % htmlfile)
1547d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  with open(htmlfile, 'w') as outfile:
1557d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    args = {}
1567d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    args['title'] = basename
1577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    args['module_name'] = basename
1587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    args['nmf'] = os.path.basename(nmf)
1597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    outfile.write(HTML_TEMPLATE % args)
1607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1627d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)def main(argv):
1637d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  usage = 'Usage: %prog [options] <.nexe/.pexe or .nmf>'
1647d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  epilog = 'Example: create_html.py -o index.html my_nexe.nexe'
1650f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  parser = optparse.OptionParser(usage, description=__doc__, epilog=epilog)
1667d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  parser.add_option('-v', '--verbose', action='store_true',
1677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                    help='Verbose output')
168eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  parser.add_option('-d', '--debug-libs', action='store_true',
169eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    help='When calling create_nmf request debug libaries')
1707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  parser.add_option('-o', '--output', dest='output',
1717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                    help='Name of html file to write (default is '
1727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                         'input name with .html extension)',
1737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                    metavar='FILE')
1747d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1750f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  # To enable bash completion for this command first install optcomplete
1760f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  # and then add this line to your .bashrc:
1770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  #  complete -F _optcomplete create_html.py
1780f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  try:
1790f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    import optcomplete
1800f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    optcomplete.autocomplete(parser)
1810f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  except ImportError:
1820f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    pass
1830f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
1847d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  options, args = parser.parse_args(argv)
1857d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1867d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if not args:
1877d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    parser.error('no input file specified')
1887d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1897d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if options.verbose:
1907d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    Log.enabled = True
1917d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1927d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  CreateHTML(args, options)
1937d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return 0
1947d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1957d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1967d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)if __name__ == '__main__':
1977d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  try:
1987d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    rtn = main(sys.argv[1:])
1997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  except Error, e:
2007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e))
2017d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    rtn = 1
2027d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  except KeyboardInterrupt:
2037d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    sys.stderr.write('%s: interrupted\n' % os.path.basename(__file__))
2047d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    rtn = 1
2057d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  sys.exit(rtn)
206