<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">from __future__ import nested_scopes
import sys
import string
import re
import os
import traceback
import whrandom
import types
import os.path

from sim_core import *
import conf

try:
    import pwd
    _use_pwd = 1
except:
    _use_pwd = 0

# os.altsep is, contrary to documentation, always None
if sys.platform == 'win32':
    _altsep = "/"
else:
    _altsep = None

if 'posix' in sys.builtin_module_names:
    _case_sensitive_files = 1
else:
    _case_sensitive_files = 0

# Redirect python output
class simics_stdout_class:
    def write(self, str):
        VT_write(str)
    def flush(self):
        SIM_flush()

sys.stdout = sys.stderr = simics_stdout_class()

# OBSOLETE
ask_for_expander = "expander"
ask_for_description = "description"

_terminal_width = 80
_terminal_height = 24

def terminal_width():
    return _terminal_width

def terminal_height():
    return _terminal_height

def define_float_re():
    dec = r'(\d*\.\d+|\d+\.)([eE][+-]?\d+)?'
    hex = r'0[xX]([\da-fA-F]*\.[\da-fA-F]+|[\da-fA-F]+\.?)[pP][+-]?\d+'
    return re.compile(r'[+-]?(' + dec + '|' + hex + ')')

whitespace = string.whitespace
letters = string.letters
alnum_qm = string.letters + string.digits + "?"
re_match = re.match
float_regexp = define_float_re()

deprecated_warned = {}

#
# Routines for converting numbers to strings and back again, with
# settable radix
#
_output_radix = 10
_output_grouping = {}

def set_output_radix(rad, group = 0, do_change = 1):
    global _output_radix, _output_grouping
    
    if rad != 2 and rad != 8 and rad != 10 and rad != 16:
        raise ValueError, "The radix must be either 2, 8, 10, or 16."
    if group &lt; 0:
        raise ValueError, "The digit grouping must be &gt;= 0."
    if do_change:
        _output_radix = rad
    _output_grouping[rad] = group
    
def get_output_radix():
    return _output_radix

def get_output_group(radix = -1):
    global _output_radix
    if radix &lt; 0:
        radix = _output_radix
    if radix in _output_grouping.keys():
        return _output_grouping[radix]
    return 0

#
# Return a tuple (sign, abs) where sign is either the empty string or
# the string "-" for negative val's. abs is the absolute value of val.
#
def de_sign(val, radix = -1):
    global _output_radix

    if radix &lt; 0:
        radix = _output_radix

    if val &lt; 0:
        if val &gt; -0x8000000000000000 and radix != 10:
            val = val + 0x10000000000000000
            return ("", val)
        return ("-", -val)
    return ("", val)

def number_group(prefix, str, group):
    if group == 0:
        return str
    n = len(str)
    while 1:
        n -= group
        if n &lt;= 0:
            break
        str = str[:n] + '_' + str[n:]

    if prefix:
        while n &lt; 0:
            n += 1
            str = '0' + str
    return str

def number_to_binary(val):
    res = ""
    
    while val &gt; 0:
        if val &amp; 1:
            res = "1" + res
        else:
            res = "0" + res
        val = val &gt;&gt; 1

    return res

#
# Return a string representation of val in the base specified by the
# _output_radix variable.
#
def number_str(val, radix = -1):
    if radix &lt; 0:
        radix = _output_radix
    prefix = ''
    
    (sign, val) = de_sign(val, radix)

    if val == 0:
        res = "0"
    elif radix == 2:
        res = number_to_binary(val)
        prefix = '0b'
    elif radix == 8:
        res = "%o" % val
        prefix = "0o"
    elif radix == 16:
        res = "%x" % val
        prefix = "0x"
    else:
        res = "%d" % val
    if res[-1] == 'L':
        res = res[:-1]

    return sign + prefix + number_group(prefix, res, get_output_group(radix))

class CliException(Exception):
    def value(self):
        (val,) = self.args
        return val
class CliError(CliException): pass
class CliTabComplete(CliException): pass
class CliSyntaxError(CliException): pass
class CliTypeError(CliException): pass
class CliParseError(CliException): pass
class CliParseErrorInDocText(CliException): pass
class CliErrorInPolyToSpec(CliException): pass
class CliArgNameError(CliException): pass
class CliOutOfArgs(CliException): pass
class CliAmbiguousCommand(CliException): pass

#
# Reads a number in base base from string text
# returns number and parsed chars in a tuple
# valid is valid characters for base
# Throws cliSyntaxError exception
#
def get_base_integer(text, base, valid, type):
    num = 0l
    len = 0
    for c in text:
        len = len + 1
        if c == '_':
            continue
        pos = string.find(valid, c)
        if pos == -1:
            if c in whitespace:
                return (num, len-1)
            elif c in alnum_qm:
                raise CliSyntaxError, ("illegal character '"
                                       + c +"' in " + type + " number")
            else:
                return (num, len-1)
        else:
            num = num * base + pos
    return (num, len)

#
# Reads an integer from text in some supported base.
# Supported bases are 2, 8, 10 and 16 and each base has
# a special prefix. "0b" for binary, "0o" for octal and
# "0x" for hexadecimal. Decimal numbers has no prefix.
# Throws cliSyntaxError exception.
#
def get_integer(text):
    len = 0
    if text == "":
        raise CliTypeError, "empty integer"
    if text[:2] == "0x":
        base = 16
        valid = "0123456789abcdef"
        type = "hexadecimal"
        len = 2
    elif text[:2] == "0b":
        base = 2
        valid = "01"
        type = "binary"
        len = 2
    elif text[:2] == "0o":
        base = 8
        valid = "01234567"
        type = "octal"
        len = 2
    elif text[0] in "0123456789":
        base = 10
        valid = "0123456789"
        type = "decimal"
    else:
        raise CliTypeError, "Unknown integer type"

    (num, pos) = get_base_integer(string.lower(text[len:]), base, valid, type)
    return (num, len + pos)

def iff(exp, a, b):
    if exp:
        return a
    else:
        return b

# Check if alias
def isalias(cmd, name):
    return name in get_alias(cmd)


#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;str_t&lt;/name&gt;
# Accepts any one word or quoted string.
# &lt;/add&gt;
#
def str_t(tokens):
    if istoken(tokens[0], "str"):
        return (tokens[0][1], 1)
    raise CliTypeError, "not a string"

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;int_t&lt;/name&gt;
# Accepts any integer (regardless of size).
# &lt;/add&gt;
#
def int_t(tokens):
    if istoken(tokens[0], "int"):
        return (tokens[0][1], 1)
    raise CliTypeError, "not an integer"

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;range_t(min, max, desc, positive = 0)&lt;/name&gt;
# Returns an argument which accepts any integers &lt;i&gt;x&lt;/i&gt;, such that
# &lt;math&gt;&lt;i&gt;min&lt;/i&gt; &amp;lt;= &lt;i&gt;x&lt;/i&gt; &amp;lt;= &lt;i&gt;max&lt;/i&gt;&lt;/math&gt;. &lt;i&gt;desc&lt;/i&gt;
# is the string returned as a description of the argument.
#
# If &lt;i&gt;positive&lt;/i&gt; is true, any negative values will be "cast" into
# positive ones using the formula &lt;math&gt;&lt;i&gt;max&lt;/i&gt; + &lt;i&gt;v&lt;/i&gt; +
# 1&lt;/math&gt;, where &lt;i&gt;v&lt;/i&gt; is the negative value.
# &lt;/add&gt;
#
def range_t(min, max, desc, positive = 0):
    def __range_t(tokens):
        if tokens == ask_for_description:
            return desc
        if tokens == ask_for_expander:
            return None
        if not istoken(tokens[0], "int"):
            raise CliTypeError, "not an integer"
        val = tokens[0][1]
        if val &lt; min or val &gt; max:
            raise CliTypeError, "integer out of range"
        if positive and val &lt; 0L:
            return (max + val + 1L, 1)
        return (val, 1)
    return __range_t

__int64_max  = 0x7fffffffffffffffL
__uint64_max = 0xffffffffffffffffL
__int64_min  = -__int64_max - 1
__int32_max  = 0x7fffffffL
__int32_min  = -__int32_max - 1
__uint32_max = 0xffffffffL
#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;int64_t&lt;/name&gt;
# Accepts any integer that fits in 64 bits (signed or unsigned). The
# value passed to the command function is the value cast to unsigned.
# &lt;/add&gt;
#
int64_t = range_t(__int64_min, __uint64_max, "a 64-bit integer", positive = 1)

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;sint64_t&lt;/name&gt;
# Accepts any signed integer that fits in 64 bits.
# &lt;/add&gt;
#
sint64_t = range_t(__int64_min, __int64_max, "a 64-bit signed integer")

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;uint64_t&lt;/name&gt;
# Accepts any unsigned integer that fits in 64 bits.
# &lt;/add&gt;
#
uint64_t = range_t(0L, __uint64_max, "a 64-bit unsigned integer")

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;int32_t&lt;/name&gt;
# Accepts any integer that fits in 32 bits (signed or unsigned). The
# value passed to the command function is cast into an unsigned value.
# &lt;/add&gt;
#
int32_t = range_t(__int32_min, __uint32_max, "a 32-bit integer", positive = 1)

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;sint32_t&lt;/name&gt;
# Accepts any signed integer that fits in 32 bits.
# &lt;/add&gt;
#
sint32_t = range_t(__int32_min, __int32_max, "a 32-bit signed integer")

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;uint32_t&lt;/name&gt;
# Accepts any unsigned integer that fits in 32 bits.
# &lt;/add&gt;
#
uint32_t = range_t(0L, __uint32_max, "a 32-bit unsigned integer")

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;integer_t&lt;/name&gt;
# Accepts any integer that fits in 64 bits (signed or
# unsigned). Corresponds to the Simics API's integer_t.
# &lt;/add&gt;
#
integer_t = int64_t

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;float_t&lt;/name&gt;
# Accepts floating-point numbers.
# &lt;/add&gt;
#
def float_t(tokens):
    t = tokens[0]
    if istoken(t, "float"):
        return (t[1], 1)
    elif istoken(t, "int"):
        return (float(t[1]), 1)
    else:
        raise CliTypeError, "not a float"

def float_arg(tokens):
    float_t(tokens)

#
# FLAG (this should never be called)
#
def flag(tokens):
    if istoken(tokens[0], "flag"):
        return (1,1)
    else:
        raise CliTypeError

def flag_t(tokens):
    if istoken(tokens[0], "flag"):
        return (1,1)
    else:
        raise CliTypeError

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;addr_t&lt;/name&gt;
# Accepts a target machine address, optionally with an address space
# prefix, such as &lt;tt&gt;v:&lt;/tt&gt; for virtual addresses or &lt;tt&gt;p:&lt;/tt&gt; for
# physical.
# &lt;/add&gt;
#
def addr_t(tokens):
    if istoken(tokens[0], "addr"):
        if istoken(tokens[1], "int"):
            addr = tokens[1][1]
            if addr &lt; 0:
                raise CliTypeError, "addresses cannot be negative"
            elif addr &gt; __uint64_max:
                raise CliTypeError, "addresses must fit in 64 bit integers"
            return ((tokens[0][1], addr), 2)
        else:
            raise CliSyntaxError, "address had prefix but no number"
    elif istoken(tokens[0], "int"):
        addr = tokens[0][1]
        if addr &lt; 0:
            raise CliTypeError, "addresses cannot be negative"
        elif addr &gt; __uint64_max:
            raise CliTypeError, "addresses must fit in 64 bit integers"
        return (("", addr), 1)
    else:
        raise CliTypeError, "not an address"

def addr(tokens):
    return addr_t(tokens)

# &lt;add id="cli argument types"&gt;
# &lt;name&gt;filename_t(dirs = 0, exist = 0, simpath = 0)&lt;/name&gt;
# Generator function for filename arguments. If the &lt;i&gt;dirs&lt;/i&gt;
# argument is zero (which is default), no directories will be
# accepted. The &lt;i&gt;exist&lt;/i&gt; flag, when set, forces the file to
# actually exist. If &lt;i&gt;simpath&lt;/i&gt; is true, files will be checked for
# existence using &lt;tt&gt;SIM_lookup_file()&lt;/tt&gt;, searching the Simics
# search path. &lt;i&gt;simpath&lt;/i&gt; implies &lt;i&gt;exist&lt;/i&gt;. On Windows, if Cygwin path
# conversion is performed (see &lt;tt&gt;SIM_native_path()&lt;/tt&gt; for details), the
# filename will be converted to host native format.
# &lt;/add&gt;
def filename_t(dirs = 0, exist = 0, simpath = 0):
    if simpath:
        exist = 1
    def __filename_t(tokens):
        if tokens == ask_for_expander:
            return __file_expander

        if tokens == ask_for_description:
            if exist:
                if not dirs:
                    s = "an existing file"
                else:
                    s = "an existing file or directory"
            else:
                if not dirs:
                    s = "a filename"
                else:
                    s = "a file or directory"
            if simpath:
                return s + " (in the Simics search path)"
            return s

        if not istoken(tokens[0], "str"):
            raise CliTypeError, "not a valid filename"

        filename = tokens[0][1]
        filename = os.path.expanduser(filename)
        if simpath:
            filename = SIM_lookup_file(filename)
            found = filename != None
        else:
            found = os.path.exists(SIM_native_path(filename))
        if exist and not found:
            raise CliTypeError, "file not found"
        if not dirs and found and os.path.isdir(filename):
            raise CliTypeError, "illegal directory"
        return (filename, 1)
    return __filename_t

#
# &lt;add id="cli argument types"&gt;
# &lt;name&gt;obj_t(desc, kind = None)&lt;/name&gt;
# Returns an argument which accepts any object.
#
# &lt;i&gt;desc&lt;/i&gt; is the string returned as a description of the
# argument.  &lt;i&gt;kind&lt;/i&gt; can be used to limit the accepted objects to
# only allow objects of a certain kind.  This parameter can either be
# a class name that the object should be an instance of, or the name
# of an interface that the object must implement.
# &lt;/add&gt;
#
def obj_t(desc, kind = None):
    def __obj_t(tokens):
        if tokens == ask_for_expander:
            return object_expander(kind)

        if tokens == ask_for_description:
            return desc

        if not istoken(tokens[0], "str"):
            raise CliTypeError, "not a valid object"

        objname = tokens[0][1]
        try:
            obj = SIM_get_object(objname)
        except:
            raise CliTypeError, "not an object"
        if kind and not instance_of(obj, kind):
            raise CliTypeError, "wrong object type"
        return (obj, 1)
    return __obj_t

#
# COMMANDS
#

def print_commands(cmd_list):
    max_len = 0
    for cmd in cmd_list:
        if type(cmd) == type([]):
            name = cmd[0]
        else:
            name = cmd["name"]
        if len(name) &gt; max_len:
            max_len = len(name)

    for cmd in cmd_list:
        if type(cmd) == type([]):
            name = cmd[0]
            cmd = cmd[1]
        else:
            name = cmd["name"]
            
        if cmd["short"]:
            line = cmd["short"]
        else:
            mo = re_match(r'(.*(\.|\n))', cmd["doc"])
            if mo:
                if mo.group(1)[-1] == "\n":
                    line = mo.group(1)[:-1] + "..."
                else:
                    line = mo.group(1)[:-1]
            else:
                line = ""
        print_word_wrap(line,
                        " " + name + " " * (max_len - len(name)) + " - ",
                        " " * (max_len + 4))

def get_category(cmd):
    return string.replace(cmd["type"], " ", "-")

def get_command_categories():
    d = {}
    for cmd in simics_commands():
        d[get_category(cmd)] = None

    l = d.keys()
    l.sort()
    return l

_tagfilter = re.compile("&lt;/?(tt|i|b|em|br/?)&gt;")

def _regexp_find(text, pattern):
    return pattern.search(_tagfilter.sub("", text))

def _substring_find(text, pattern):
    return string.find(_tagfilter.sub("", string.lower(text)), pattern) &gt;= 0


def help_cmd(poly):
    if poly[0] == flag_t:
        # list all commands

        print_commands(simics_commands())
        return

    text = poly[1]

    if text=="":
        print "List of command categories:\n"
        print_columns([ Just_Left ], get_command_categories(), has_title = 0)

        print
        format_print("""
        Type &lt;b&gt;help&lt;/b&gt; &lt;i&gt;category&lt;/i&gt; for a list of commands or
        &lt;b&gt;help&lt;/b&gt; &lt;i&gt;command&lt;/i&gt; for documentation of a specific
        command. &lt;b&gt;help&lt;/b&gt; &lt;i&gt;help&lt;/i&gt; prints documentation for the
        &lt;b&gt;help&lt;/b&gt; command. For a full description of how the
        frontend works consult the Simics User Guide.""",
                     0, terminal_width())

    else:
        pos = string.find(text, ".")

        # namespace? replace it by classname
        if pos &gt;= 0:
            name = text[:pos]
            meth = text[pos+1:]
            try:
                object = SIM_get_object(name)
                for cmd in simics_commands():
                    if (cmd["namespace"]
                        and instance_of(object, cmd["namespace"])):
                        if cmd["method"][:len(meth)] == meth:
                            text = "&lt;%s&gt;.%s" % (cmd["namespace"], meth)
                            break
                        elif meth in cmd["alias"]:
                            text = "&lt;%s&gt;.%s" % (cmd["namespace"],
                                                cmd["method"])
                            break
            except:
                pass
        else:
            try:
                object = SIM_get_object(text)
                l = []
                found_cmd = None
                for cmd in simics_commands():
                    if (cmd["namespace"]
                        and instance_of(object, cmd["namespace"])):
                        l.append(cmd)
                    if cmd["name"] == text or isalias(cmd, text):
                        found_cmd = cmd

                desc = VT_get_class_description(object.classname)
                if type(desc) == type("") and len(desc) &gt; 0:
                    format_print(VT_get_class_description(object.classname),
                                 0, terminal_width())
                    print
                    
                if l:
                    print "The following commands are available",
                    print "for the '%s' object:\n" % text
                    print_commands(l)
                else:
                    print ("There are no commands available for object '%s'."
                           % text)

                if found_cmd:
                    print "\n" + text + " is also a command:"
                else:
                    return
            except:
                pass

        # try find command

        for cmd in simics_commands():
            if cmd["name"] == text or isalias(cmd, text):
                format_command(cmd)
                return

        # look for commands that begins with text
        found = []
        for cmd in simics_commands():
            if cmd["namespace"]:
                pass
            elif cmd["name"][:len(text)] == text:
                found.append(cmd)
            else:
                for alias in cmd["alias"]:
                    if alias[:len(text)] == text:
                        found.append([alias, cmd])
                        break

        if len(found) &gt; 1:
            print ("Ambiguous command name. The following match '"
                   + text + "':\n")
            print_commands(found)
        elif len(found) == 1:
            cmd = found[0]
            if type(cmd) == type([]):
                cmd = cmd[1]
            format_command(cmd)
        else:
            # look for command category
            found = []
            max_len = 0
            for cmd in simics_commands():
                if get_category(cmd)[:len(text)] == text:
                    found.append(cmd)
                    if len(cmd["name"]) &gt; max_len:
                        max_len = len(cmd["name"])
            if found:
                print get_category(found[0]) + "\n"
                for c in found:
                    print_word_wrap(c["short"],
                                    " " + c["name"]
                                    + " "*(max_len-len(c["name"])) + " - ",
                                    " " * (max_len + 4))
                print
                format_print("Type &lt;b&gt;help&lt;/b&gt; &lt;i&gt;command-name&lt;/i&gt; "
                             + "for further documentation.\n",
                             0, terminal_width())
                return

            print ("No command, command category or object '" + text
                   + "' exists.")


def hex_str(val, size = 0):
    if size:
        return "%0*x" % (size, val)
    else:
        return "%x" % val

#
# -------------------- helper functions --------------------
#

current_proc = None

def get_frontend_current_processor(data, o, idx):
    return current_processor()

def set_frontend_current_processor(data, o, cpu, idx):
    if type(cpu) != type(SIM_get_object("sim")):
        return Sim_Set_Need_Object
    if not SIM_object_is_processor(cpu):
        return Sim_Set_Illegal_Value
    set_current_processor(cpu)
    return Sim_Set_Ok

SIM_register_attribute("sim", "frontend_current_processor",
                       get_frontend_current_processor, 0,
                       set_frontend_current_processor, 0,
                       Sim_Attr_Optional,
                       "Current frontend processor")

def set_current_processor(cpu):
    global current_proc

    prev_cpu = current_proc
    current_proc = cpu

    # Send a PSELECT message if we changed CPU
    if VT_remote_control() and prev_cpu != cpu:
        VT_send_control_msg(['pselect', 0, cpu.name])

def current_processor():
    global current_proc
    if not current_proc:
        try:
            current_proc = SIM_proc_no_2_ptr(0)
        except:
            raise CliError, ("This command requires a processor to"
                               + " be defined.")
    return current_proc

# asserts that we have at least one CPU defined
def assert_cpu():
    current_processor()

# Return the current CPU, or the CPU named 'cpu_name'.  If 'kind' is
# given, also check that the CPU is of the right kind.  The 'kind'
# parameter should be a tuple ("type", "description"), where "type"
# can be used in a call to instance_of().
def get_cpu(cpu_name = "", kind = None):
    if not cpu_name:
        obj = current_processor()
    else:
        try:
            obj = SIM_get_object(cpu_name)
        except:
            raise CliError, "There is no processor called " + cpu_name + "."
    if not SIM_object_is_processor(obj):
        raise CliError, "Object '%s' is not a processor" % cpu_name
    if kind and not instance_of(obj, kind[0]):
        raise CliError, "This command requires %s" % kind[1]
    return ( obj, obj.name )

# collect all cpu names
def all_cpus():
    if not SIM_initial_configuration_ok():
        return []

    cl = []
    for o in SIM_all_objects():
        if SIM_object_is_processor(o):
            cl.append(o.name)
    return cl

# expands to all CPUs
def cpu_expander(comp):
    return get_completions(comp, all_cpus())

# test if argument is a cpu
def cpu_is_a(cpu):
    return cpu in all_cpus()


# generic expanders:
#
#  object_expander(kind) returns an expander for objects of the
#   given class or with a given interface

def all_instances_of(cl):
    return [ obj.name for obj in SIM_all_objects()
             if instance_of(obj, cl) ]

def object_expander(kind):
    return lambda string: get_completions(string, all_instances_of(kind))

def pr(text):
    sys.stdout.write(text)
    sys.stdout.softspace = 0

term = os.environ.get("TERM", "")[:2] in ["vt", "xt"]
format_type = "term"

def bold_start():
    if format_type == "term":
        return iff(term, "\033[1m", "")
    else:
        return "&lt;b&gt;"

def bold_end():
    if format_type == "term":
        return iff(term, "\033[m", "")
    else:
        return "&lt;/b&gt;"

def bold(str):
    return bold_start() + str + bold_end()

def italic_start():
    if format_type == "term":
        return iff(term, "\033[4m", "")
    else:
        return "&lt;i&gt;"

def italic_end():
    if format_type == "term":
        return iff(term, "\033[m", "")
    else:
        return "&lt;/i&gt;"

def italic(str):
    return italic_start() + str + italic_end()

term_bold_active = term_italic_active = 0

def term_stop_font():
    if term:
        pr("\033[m")

def term_start_font():
    global term_bold_active, term_italic_active
    if term:
        pr("\033[m")
        if term_bold_active:
            pr(bold_start())
        if term_italic_active:
            pr(italic_start())

def term_set_italic(on):
    global term_italic_active
    if term_italic_active == on:
        return
    term_italic_active = on
    if on:
        pr(italic_start())
    else:
        term_stop_font()
        term_start_font()

def term_set_bold(on):
    global term_bold_active
    if term_bold_active == on:
        return
    term_bold_active = on
    if on:
        pr(bold_start())
    else:
        term_stop_font()
        term_start_font()

def get_synopsis(cmd, max_len = 0, format = 0):
    buf = ""
    i = 0

    if max_len == 0:
        max_len = len(cmd["name"])

    seen_quest_and = 0 # in an ?&amp; list

    command_name = (iff(format, bold_start(), "")
                    + cmd["name"]
                    + iff(format,bold_end(),""))

    for arg in cmd["args"]:
        if i == cmd["infix"]:
            buf = buf + command_name + " "*(1+max_len-len(cmd["name"]))
        if arg["doc"]:
            name = arg["doc"]
        elif arg["name"]:
            if type(arg["name"]) == type((1,1)):
                name = "("
                for j in range(len(arg["name"])):
                    if arg["handler"][j] == flag_t:
                        name = name + arg["name"][j] + "|"
                    elif (arg["handler"][j] == str
                          or arg["handler"][j] == str_t):
                        name = (name + italic_start() + '"' + arg["name"][j]
                                + '"' + italic_end() + "|")
                    else:
                        name = (name + italic_start() + arg["name"][j]
                                + italic_end() + "|")
                name = name[:-1] + ")"
            else:
                name = arg["name"]
        else:
            name = "arg" + str(i+1)


        if arg["handler"] != flag_t and type(arg["handler"]) != type((1,1)):
            if arg["handler"] == str or arg["handler"] == str_t:
                name = italic('"'+name+'"')
            else:
                name = italic(name)


        if arg["handler"] == flag_t:
            buf = buf + "[" + name + "]"
        elif arg["spec"] == "?":
            buf = buf + "[" + name + "]"
        elif arg["spec"] == "+":
            buf = buf + name + " ..."
        elif arg["spec"] == "*":
            buf = buf + "[ " + name + " ... ]"
        elif arg["spec"] == "?&amp;" and not seen_quest_and:
            buf = buf + "[ " + name
            seen_quest_and = 1
        else:
            buf = buf + name

        if arg["spec"] != "?&amp;" and seen_quest_and:
            buf = buf + " ]"
            seen_quest_and = 0

        buf = buf + " "

        i = i + 1

    if seen_quest_and:
        buf = buf + "]"
        seen_quest_and = 0

    if not buf:
        return command_name
    else:
        return buf

def get_alias(cmd):
    return cmd["alias"]

#
# --- TERM ---
#

def format_command(cmd):
    # first see if the command should be documented with
    # another command

    global format_type
    format_type = "term"

    if cmd["doc_with"]:
        cmd = simics_commands_dict(cmd["doc_with"])

    indent = 3
    width = terminal_width() - 1

    # find all commands that will doc with this one
    doc_with = []
    for c in simics_commands():
        if c["doc_with"] == cmd["name"] or c == cmd:
            doc_with.append(c)

    pr(bold("\nNAME\n"))

    text = cmd["name"]
    aliases = []
    max_len = 0
    cmd_len = 0
    for c in doc_with:
        if c != cmd:
            text = text + ", " + c["name"]
        if len(c["name"]) &gt; cmd_len:
            cmd_len = len(c["name"])
        a = get_alias(c)
        aliases = aliases + a
        if len(c["name"]) &gt; max_len:
            max_len = len(c["name"])

    if cmd["group_short"]:
        text += " - " + cmd["group_short"] + "\n"
    elif cmd["short"]:
        text += " - " + cmd["short"] + "\n"
    else:
        text += "\n"

    pr(" "*indent)
    format_print(string_jdocu(text), indent, width)

    pr(bold("\nSYNOPSIS\n"))

    for c in doc_with:
        pr(" "*indent + get_synopsis(c, 0, 1) + "\n") # max_len &lt;-&gt; 0

    seen = []
    if aliases:
        pr(bold("\nALIAS"))
        if len(aliases) &gt; 1:
            pr(bold("ES"))
        pr("\n")
        for c in doc_with:
            seen = []
            pr(" "*indent)
            text = ""
            alist = get_alias(c)
            if not alist:
                continue
            i = 0
            if len(doc_with) &gt; 1:
                fmt = "%%-%ds  " % cmd_len
                pr(fmt % c["name"])
            for a in alist:
                if a in seen:
                    continue
                seen.append(a)
                if i &gt; 0:
                    text += ", "
                text += bold(a)
                i = i + 1
            text += "\n"
            format_print(text, indent, width)

    text = string.strip(cmd["doc"])

    if text:
        pr(bold("\nDESCRIPTION\n") + " "*indent)
        format_print(text, indent, width)

    for di in cmd["doc_items"]:
        pr("\n" + bold(di[0]) + "\n" + " "*indent)
        format_print(di[1], indent, width)

    pr("\n")

def check_line_wrap(col, end, width, indent):
    if col + end &gt; width:
        col = indent + end
        if term and (term_bold_active or term_italic_active):
            term_stop_font()
        pr("\n" + " "*indent)
        if term and (term_bold_active or term_italic_active):
            term_start_font()
    else:
        col = col + end
    return col

def format_print(text, indent, width):
    width = min(79, width - 1)
    col = indent
    while(text):

        # look for line break (two or more blank lines)
        mo = re_match(r"[ \t\r\f\v]*\n[ \t\r\f\v]*\n\s*", text)
        if mo:
            pr("\n\n" + " "*indent)
            text = text[mo.end():]
            col = indent
            continue

        mo = re_match(r"\s+", text)
        if mo:
            text = text[mo.end():]
            if col &gt; indent:
                pr(" ")
                col = col + 1
            continue

        # break line
        mo = re_match(r"&lt;br/?&gt;", text)
        if mo:
            pr("\n" + " "*indent)
            text = text[mo.end():]
            col = indent
            continue

        # eat format strings
        if text[0] == "&lt;":
            mo = re_match(r"&lt;(/?)(i|var|cite)&gt;", text)
            if mo:
                col = check_line_wrap(col, 1, width, indent)
                term_set_italic(mo.group(1) == '')
                text = text[mo.end():]
                continue

            mo = re_match(r"&lt;(/?)(b|tt|em|class|module|cmd|arg|param|fun|type|obj|iface)&gt;", text)
            if mo:
                col = check_line_wrap(col, 1, width, indent)
                term_set_bold(mo.group(1) == '')
                text = text[mo.end():]
                continue
            
            mo = re_match(r"&lt;(/?)(math)&gt;", text)
            if mo:
                text = text[mo.end():]
                continue
                
            mo = re_match(r"(&lt;[^&lt;\s]*)", text)
            text = text[mo.end():]
            col = check_line_wrap(col, mo.end(), width, indent)
            pr(mo.group(1))
            continue

        mo = re_match(r"&amp;([a-z]+);", text)
        if mo:
            col = check_line_wrap(col, 1, width, indent)
            entities = {'lt':  '&lt;',
                        'gt':  '&gt;',
                        'amp': '&amp;',
                        'ndash' : '-',
                        'mdash' : '---',
                        'dquot' : '"',
                        'logor' : '||',
                        'rarr'  : '-&gt;',
                        'times' : 'x',
                        }
            ent = mo.group(1)
            pr(entities.get(ent, "&amp;" + ent + ";"))
            text = text[mo.end():]
            continue

        mo = re_match(r"([^&amp;&lt;\s]*)", text)
        if mo:
            text = text[mo.end():]
            col = check_line_wrap(col, mo.end(), width, indent)
            pr(mo.group(1))
            continue

        raise CliParseErrorInDocText

    pr("\n")

#
# --- jDOCU ---
#

def string_jdocu(str):
    res = ""
    for s in str:
        if s == "&amp;":
            res = res + "&amp;amp;"
        elif s == "&lt;":
            res = res + "&amp;lt;"
        elif s == "&gt;":
            res = res + "&amp;gt;"
        else:
            res = res + s
    return res

_format_jdocu_tag_matcher = re.compile(r"&lt;/?(i|b|em|tt|class|module|cmd|arg|param|fun|type|obj|var|cite|math)&gt;").match
_format_jdocu_br_matcher = re.compile(r"&lt;br/?&gt;").match

def format_jdocu(file, text, in_index = 0):
    while(text):
        mo = _format_jdocu_tag_matcher(text)
        if mo:
            text = text[mo.end():]
            file.write(mo.group(0))
            continue

        mo = _format_jdocu_br_matcher(text)
        if mo:
            text = text[mo.end():]
            file.write("&lt;br/&gt;")
            continue

        if text[0] == "&lt;":
            text = text[1:]
            file.write("&amp;lt;")
            continue

        if text[0] == "&gt;":
            text = text[1:]
            file.write("&amp;gt;")
            continue

        if text[0] == "&amp;":
            text = text[1:]
            file.write("&amp;amp;")
            continue

        if in_index and text[0] in "!@\\":
            file.write("\\")

        file.write(text[0])
        text = text[1:]

def format_jdocu_cmd_ndx(file, cmd):
    file.write("&lt;ndx&gt;")

    if cmd["method"] == "":
        format_jdocu(file, cmd["name"], in_index = 1)
        for a in cmd["alias"]:
            file.write("&lt;/ndx&gt;\n&lt;ndx&gt;")
            format_jdocu(file, a, in_index = 1)
    else:
        format_jdocu(file, cmd["namespace"], in_index = 1)
        file.write("!commands!")
        format_jdocu(file, cmd["method"], in_index = 1)
        file.write("&lt;/ndx&gt;\n")

        file.write("&lt;ndx&gt;")
        format_jdocu(file, cmd["method"], in_index = 1)
        file.write("!namespace command!")
        format_jdocu(file, cmd["namespace"], in_index = 1)

    file.write("&lt;/ndx&gt;\n")


def cmd_cmp(a, b):
    if a["name"] &lt; b["name"]:
        return -1
    elif a["name"] == b["name"]:
        return 0
    else:
        return 1

def format_as_jdocu(file, cmd):
    # first see if the command should be documented with
    # another command

    global format_type

    oformat = format_type
    
    if cmd["doc_with"]:
        return

    format_type = "jdocu"

    # find all commands that will doc with this one
    doc_with = []
    for c in simics_commands():
        if c["doc_with"] == cmd["name"]:
            doc_with.append(c)

    doc_with.sort(cmd_cmp)

    file.write("/*\n")
    file.write("&lt;pragma line=\"" + iff(1, "0", cmd["linenumber"]) + "\" file=\""
               + cmd["filename"] + "\"/&gt;\n")
    file.write('&lt;add id="%s"&gt;\n' % cmd["type"])
    file.write('&lt;name&gt;')
    format_jdocu(file, cmd["name"])
    aliases = get_alias(cmd)
    for c in doc_with:
        #format_jdocu(file, ", " + c["name"])
        aliases = aliases + get_alias(c)
    file.write('&lt;/name&gt;\n')

    format_jdocu_cmd_ndx(file, cmd)

    for c in doc_with:
        format_jdocu_cmd_ndx(file, c)

    file.write("&lt;doc&gt;\n")
    file.write('&lt;di name="NAME"&gt;')
    format_jdocu(file, cmd["name"])
    for c in doc_with:
        format_jdocu(file, ", " + c["name"])

    if cmd["group_short"]:
        file.write(" &amp;mdash; ")
        format_jdocu(file, cmd["group_short"])
    elif cmd["short"]:
        file.write(" &amp;mdash; ")
        format_jdocu(file, cmd["short"])
    file.write("&lt;/di&gt;\n")

    file.write('&lt;di name="SYNOPSIS"&gt;')
    format_jdocu(file, get_synopsis(cmd, 0, 1))

    for c in doc_with:
        file.write("&lt;br/&gt;")
        format_jdocu(file, get_synopsis(c, 0, 1))

    file.write('&lt;/di&gt;\n')

    i = 0
    seen = []
    if aliases:
        file.write("&lt;di name=\"ALIAS")
        if len(aliases) &gt; 1:
            file.write("ES")
        file.write("\"&gt;")

        if doc_with:
            file.write("&lt;table long=\"false\"&gt;\n")
            for c in [ cmd ] + doc_with:
                alist = get_alias(c)
                if not alist:
                    continue
                file.write("&lt;tr&gt;&lt;td&gt;")
                format_jdocu(file, c["name"])
                file.write("&lt;/td&gt;&lt;td&gt;")
                first = 1
                for a in alist:
                    if first:
                        first = 0
                    else:
                        file.write(", ")
                    file.write("&lt;cmd&gt;")
                    format_jdocu(file, a)
                    file.write("&lt;/cmd&gt;")
                file.write("&lt;/td&gt;&lt;/tr&gt;\n")
            file.write("&lt;/table&gt;\n")
        else:
            for a in aliases:
                if a in seen:
                    continue
                seen.append(a)
                if i &gt; 0:
                    file.write(", ")
                file.write("&lt;b&gt;")
                format_jdocu(file, a)
                file.write("&lt;/b&gt;")
                i = i + 1
        file.write("&lt;/di&gt;\n")

    text = string.strip(cmd["doc"])

    if text:
        file.write("&lt;di name=\"DESCRIPTION\"&gt;")
        format_jdocu(file, text)
        file.write('&lt;/di&gt;\n')

    for di in cmd["doc_items"]:
        file.write("&lt;di name=\"%s\"&gt;" % di[0])
        format_jdocu(file, di[1])
        file.write("&lt;/di&gt;\n")

    file.write("&lt;/doc&gt;\n&lt;/add&gt;\n*/\n")

    format_type = oformat

def format_commands_as_jdocu(filename = 0):
    internals()
    cmd_types = {}
    if filename == 0:
        file = sys.stdout
    else:
        file = open(filename, "w")

    for cmd in simics_commands():
        if not cmd_types.has_key(cmd["type"]):
            cmd_types[cmd["type"]] = 1
        print "generating doc for ", cmd["name"]
        format_as_jdocu(file, cmd)

    # get the list of loaded modules
    class_list = VT_get_all_classes()

    file.write("/*\n")
    file.write("&lt;add id=\"__generated_module_commands\"&gt;\n")
    cmd_group_list = cmd_types.keys()
    cmd_group_list.sort()
    for t in cmd_group_list:
        if t[-8:] == "commands":
            if t[0:-9] in class_list:
                file.write("&lt;doclist id=\"" + t + "\" sort=\"az\" name=\"" + t[0:-9] + "\"/&gt;\n")
            else:
                print "Ignoring the group", t[0:-9], "(not in class list)"
        else:
            print "Error: command group name '" + t + "' does not end with 'commands'"

    file.write("&lt;/add&gt;\n */")

    file.write("/*\n")
    file.write("&lt;pragma line=\"" + iff(1, "0", cmd["linenumber"]) + "\" file=\""
               + cmd["filename"] + "\"/&gt;\n")
    file.write("&lt;add id=\"quick command reference\"&gt;\n")
    file.write("&lt;name&gt;Commands&lt;/name&gt;\n")
    file.write("\n&lt;dl&gt;\n")
    for cmd in simics_commands():
        file.write("&lt;dt&gt;&lt;b&gt;")
        format_jdocu(file, cmd["name"])
        file.write("&lt;/b&gt;&lt;/dt&gt; &lt;dd&gt;")
        format_jdocu(file, cmd["short"])
        file.write("&lt;/dd&gt;\n")
    file.write("&lt;/dl&gt;")
    file.write("&lt;/add&gt;")

    # now write a list of all command groups we generated
    
    
    file.write("*/\n")

def command_name_category(n):
    l = len(n)
    if l == 0:
        return 0
    for i in range(l):
        if n[i] in letters:
            return 2
    return 1
    
def command_name_sort(a, b):
    ca = command_name_category(a)
    cb = command_name_category(b)

    if ca != cb:
        return ca - cb

    if a &lt; b:
        return -1
    if a == b:
        return 0
    return 1

def format_command_summary_as_jdocu(filename = 0):
    internals()
    if filename == 0:
        file = sys.stdout
    else:
        file = open(filename, "w")

    cmds = []
    for cmd in simics_commands():
        if (not cmd["internal"] and
            cmd["type"] != "arithmetic commands" and
            cmd["type"] != "bitwise commands" and
            cmd["type"] != "command line commands"):

            cmds += [ [ cmd["name"], cmd["short"], cmd["doc_with"] ] ]
            for a in cmd["alias"]:
                if cmd["namespace"]:
                    cmds += [ [ "&lt;" + cmd["namespace"] + "&gt;." + a,
                                [ "&lt;" + cmd["namespace"] + "&gt;."
                                  + cmd["method"] ],
                                cmd["doc_with"] ] ]
                else:
                    cmds += [ [ a, [ cmd["name"] ], cmd["doc_with"] ] ]
    cmds.sort(lambda a, b: command_name_sort(a[0], b[0]))

    file.write("/*\n&lt;add id=\"command summary\"&gt;\n")
    file.write("&lt;name&gt;Summary of Commands&lt;/name&gt;\n")
    file.write("&lt;table&gt;\n")
    for cmd in cmds:
        print cmd[0], cmd[1], cmd[2]

        # check for doc_with, then check for alias
        if (cmd[2]):
            target = cmd[2]
        else:
            if type("") == type(cmd[1]):
                target = cmd[0]
            else:
                target = cmd[1][0]
                
        file.write("&lt;tr&gt;&lt;td&gt;&lt;nref label=\"__jdocu_seealso_"
                   + string_jdocu(target) + "\"&gt;")
        format_jdocu(file, cmd[0])
        file.write("&lt;/nref&gt;&lt;/td&gt;&lt;td&gt;")
        if type("") == type(cmd[1]):
            file.write(cmd[1])
        else:
            file.write("alias for &lt;cmd&gt;")
            format_jdocu(file, cmd[1][0])
            file.write("&lt;/cmd&gt;")
        file.write("&lt;/td&gt;&lt;/tr&gt;\n")
    file.write("&lt;/table&gt;\n")
    file.write("&lt;/add&gt;\n*/\n")
    
def one_c_type(c):
    if c == "n":
        return "void "
    if c == "i":
        return "int "
    if c == "I":
        return "integer_t "
    if c == "e":
        return "exception_type_t "
    if c == "o":
        return "lang_void *"
    if c == "s":
        return "char *"
    if c == "m":
        return "memory_transaction_t *"
    if c == "c":
        return "conf_object_t *"
    if c == "v":
        return "void *"
    return "&amp;lt;unknown type %s&amp;gt;" % c

def hap_c_arguments(str, argnames, width = 60, indent = 0):
    res = " " * indent + one_c_type(str[0]) + "(*)("
    indent = len(res)
    str = str[1:]
    if str == "":
        res = res + "void"
    else:
        col = indent
        while str != "":
            this = one_c_type(str[0]) + argnames[0]
            if col &gt; indent and col + len(this) &gt; width - 2:
                res = res + "\n" + " " * indent
                col = indent
            res = res + this
            col = col + len(this)
            str = str[1:]
            argnames = argnames[1:]
            if str != "":
                res = res + ", "
                col = col + 2
    return res + ");"
    
def format_haps_as_jdocu(filename = 0):
    if filename == 0:
        file = sys.stdout
    else:
        file = open(filename, "w")

    haps = conf.sim.hap_list
    for i in range(len(haps)):
        for hap_chapter in [0, 1]:
            if hap_chapter:
                file.write("/* &lt;add id=\"hap API haps\"&gt;\n")
            else:
                file.write("/* &lt;add id=\"%s-hap\"&gt;\n" % haps[i][0])
            file.write("&lt;name index=\"true\"&gt;%s&lt;/name&gt;\n" % haps[i][0])
            file.write("&lt;doc&gt;\n")
            file.write("&lt;di name=\"NAME\"&gt;%s&lt;/di&gt;\n" % haps[i][0])
            file.write("&lt;di name=\"CALLBACK TYPE\"&gt;&lt;pre&gt;")
            argnames = [ "callback_data", "trigger_obj" ]
            if haps[i][2] != None:
                argnames = argnames + haps[i][2]
            file.write(hap_c_arguments("noc" + haps[i][1], argnames))
            file.write("&lt;/pre&gt;")
            file.write("&lt;/di&gt;\n")
            file.write("&lt;di name=\"INDEX\"&gt;")
            if haps[i][3] == None:
                file.write("no index")
            else:
                file.write("&lt;arg&gt;")
                format_jdocu(file, haps[i][3])
                file.write("&lt;/arg&gt;")
            file.write("&lt;/di&gt;\n")
            file.write("&lt;di name=\"DESCRIPTION\"&gt;")
            file.write(haps[i][4])
            file.write("&lt;/di&gt;\n&lt;/doc&gt;&lt;/add&gt; */\n\n")

#
# --- HTML ---
#

ignore_tags = ["&lt;i&gt;", "&lt;/i&gt;", "&lt;b&gt;", "&lt;/b&gt;", "&lt;em&gt;", "&lt;/em&gt;", "&lt;tt&gt;", "&lt;/tt&gt;"]

def ignore_tag(text):
    try:
        for tag in ignore_tags:
            f = 1
            for i in range(len(tag)):
                if tag[i] != text[i]:
                    f = 0
                    break
            if f:
                return len(tag)
        return 0
    except IndexError:
        return 0

_format_html_paragraph_matcher = re.compile(r"[ \t\r\f\v]*\n[ \t\r\f\v]*\n\s*").match

def format_html(text):
    t = ""
    while(text):
        # look for line break (two or more blank lines)
        mo = _format_html_paragraph_matcher(text)
        if mo:
            t = t + "&lt;br&gt;&lt;br&gt;\n"
            text = text[mo.end():]
            continue

        # break line
        mo = _format_jdocu_br_matcher(text)
        if mo:
            text = text[mo.end():]
            t = t + "&lt;br&gt;\n"
            continue

        if text[0] == "&lt;":
            l = ignore_tag(text)
            if l:
                t = t + text[:l]
                text = text[l:]
            else:
                text = text[1:]
                t = t + "&amp;lt;"
            continue

        if text[0] == "&gt;":
            text = text[1:]
            t = t + "&amp;gt;"
            continue

        if text[0] == "&amp;":
            text = text[1:]
            t = t + "&amp;amp;"
            continue

        mo = re_match(r"([^\n&lt;&gt;]+)", text)
        if mo:
            text = text[mo.end():]
            t = t + mo.group(1)
            continue

        t = t + text[0]
        text = text[1:]
    return t

def write_doc_item(file, name, text):
    file.write("&lt;dt&gt;&lt;b&gt;%s&lt;/b&gt;&lt;/dt&gt;\n&lt;dd&gt;" % name)
    file.write(format_html(text))
    file.write("&lt;/dd&gt;\n")

def format_commands_as_html(name):
    global format_type

    oformat = format_type
    
    format_type = "html"

    file = open(name, "w")
    file.write("&lt;html&gt;\n")
    file.write("&lt;head&gt;\n")
    file.write("&lt;title&gt;Simics commands&lt;/title&gt;\n")
    file.write("&lt;/head&gt;\n")
    file.write("&lt;body&gt;\n")

    file.write("&lt;h1&gt;List of commands&lt;/h1&gt;&lt;br&gt;\n")
    for cmd in simics_commands():
        file.write("&lt;a href=\"#" + iff(cmd["doc_with"],
                                       cmd["doc_with"],
                                       cmd["name"])
                   + "\"&gt;" + format_html(cmd["name"]) + "&lt;/a&gt;&lt;br&gt;\n")
    file.write("&lt;hr WIDTH=\"100%\"&gt;\n")

    for cmd in simics_commands():
        if cmd["doc_with"]:
            continue

        file.write("&lt;a name=\"" + cmd["name"] + "\"&gt;\n")
        file.write("&lt;dl&gt;\n")
        # find all commands that will doc with this one
        doc_with = []
        for c in simics_commands():
            if c["doc_with"] == cmd["name"]:
                doc_with.append(c)

        doc_with.sort(cmd_cmp)

        text = cmd["name"]

        aliases = get_alias(cmd)
        for c in doc_with:
            text = text + ", " + c["name"]
            aliases = aliases + get_alias(c)

        if cmd["group_short"]:
            text = text + " - " + cmd["group_short"]
        elif cmd["short"]:
            text = text + " - " + cmd["short"]

        write_doc_item(file, "NAME", text)

        text = "&lt;tt&gt;" + get_synopsis(cmd, 0, 1)
        for c in doc_with:
            text = text + "&lt;br&gt;\n" + get_synopsis(c, 0, 1)
        text = text + "&lt;/tt&gt;"

        write_doc_item(file, "SYNOPSIS", text)

        write_doc_item(file, "DESCRIPTION", cmd["doc"])

        for di in cmd["doc_items"]:
            write_doc_item(file, di[0], di[1])
        file.write("&lt;/dl&gt;\n")

        file.write("&lt;hr WIDTH=\"100%\"&gt;\n")
    file.write("&lt;body&gt;\n")
    file.write("&lt;/html&gt;\n")
    file.close()

    format_type = oformat

def commands_text(file = "commands.txt"):
    file = open(file, "w")
    file.write("COMMANDS - for spell-checking\n\n")

    for cmd in simics_commands():
        if cmd["doc_with"]:
            continue
        file.write("Command - %s\n\n" % cmd["name"])
        file.write(cmd["doc"])
        file.write("\n\n")

    file.close()

internals_on = 0
simics_cmds = []
simics_cmds_dict = {}
simics_args = {}

def simics_commands():
    return filter(lambda cmd: iff(cmd["internal"], internals_on, 1),
                  simics_cmds)

def simics_command_exists(key):
    return iff(simics_cmds_dict.get(key), 1, 0)    
    

def simics_commands_dict(key):
    cmd = simics_cmds_dict.get(key)
    if not cmd or not cmd["internal"]:
        return cmd

    if internals_on:
        return cmd
    else:
        return None

def internals():
    global internals_on
    internals_on = 1
    print "Activating access to internal Simics commands."
    print "These are subject to change and may be removed from future"
    print "versions of Simics."

def internals_off():
    global internals_on
    internals_on = 0

# ---------------- front end vars -----------------

_last_cli_repeat  = 0      # 0: no repeat, 1: repeat-cmd-line, 2: repeat-fun
_last_cli_cmdline = ""     # what was the exact command-line
_last_cli_hap = 0          # if a hap cancels the previous command or not

class simenv_class:

    variables = {}

    def get_all_variables(self):
        return self.variables

    def __getattr__(self, name):
        return self.variables[name]

    def __setattr__(self, name, value):
        self.variables[name] = value

simenv = simenv_class()

def getenv(data, o, idx):
    if type(idx) != type(''):
        return None
    return getattr(simenv, idx)

def setenv(data, o, val, idx):
    if type(idx) != type(''):
        return Sim_Set_Need_String

    setattr(simenv, idx, str(val))
    return Sim_Set_Ok

SIM_register_attribute("sim", "env",
                       getenv, 0, setenv, 0,
                       Sim_Attr_Pseudo | Sim_Attr_String_Indexed,
                       "get/set simics environment variable")

# -------------------------------------------------

def command_sort_category(n):
    l = len(n)
    if l == 0:
        return 0
    for i in range(l):
        if n[i] in letters:
            return 2
    return 1
    
def command_sorts_before(a, b):
    ca = command_sort_category(a)
    cb = command_sort_category(b)

    if ca != cb:
        return ca &lt; cb

    return a &lt; b

# defines a new argument type
def new_argtype(fun, name, doc = ""):
    simics_args[fun] = { "name":name, "fun":fun, "doc":doc }

_type = type

# keep track of the current module defining commands
current_module_loading = ["Simics Core"]

def push_current_module(m):
    global current_module_loading
    current_module_loading = current_module_loading + [m]

def pop_current_module():
    global current_module_loading
    current_module_loading = current_module_loading[0:-1]

# defines a new command
def new_command(name, fun, args = [], doc = "", type = "misc commands",
                pri = 0, infix = 0, left = 1,
                short = "", group_short = "", alias = [], doc_with = "",
                check_args = 2,
                doc_items = [], namespace = "",
                method = "", namespace_copy = (), internal = 0,
                filename = "", linenumber = "", module = "",
                object = None, repeat = None, deprecated = None):

    global current_module_loading

    # if infix operator do not check superfluous arguments since they are often
    # used in expressions. can be overridden by setting check_args to 1
    if infix == 1 and check_args == 2:
        check_args = 0

    if alias == "":
        alias = []

    if _type(alias) == _type(""):
        alias = [alias]

    if namespace:
        meth = name
        name = "&lt;"+namespace+"&gt;."+name
    else:
        meth = ""

    # Fixes the "help sfmmu0.i-probe" bug.
    if doc_with and not simics_cmds_dict.has_key(doc_with):
        print "*** The doc_with-field in command", name,
        print "points to non-existing command", doc_with

        doc_with_ns = "&lt;"+namespace+"&gt;."+doc_with
        if simics_cmds_dict.has_key(doc_with_ns):
            print "    (this seems like a missing namespace-prefix."
            print "     Using doc-with=\"%s\" instead)" % (doc_with_ns)
            doc_with = doc_with_ns

    err = "*** Error when defining command '" + name + "': "

    # check to see if command name already exists
    try:
        try_cmd = simics_cmds_dict[name]
        # the command exist, check if it's the same
        if (try_cmd["filename"] == filename
            and try_cmd["linenumber"] == linenumber):
            print err + "redefining the same command"
        else:
            # complain, but add the command anyway
            print err + "two different commands have the same name."
    except:
        pass

    if type == "internal commands":
        internal = 1

    if deprecated:
        doc_items = doc_items + [
            ('NOTE',
             "This command is deprecated. Use &lt;cmd&gt;%s&lt;/cmd&gt;." % deprecated) ]

    cmd = { "name" : name, "fun" : fun, "pri" : pri, "infix" : infix,
            "left" : left, "doc" : doc, "type" : type, "short" : short,
            "group_short" : group_short, "args" : args, "alias" : alias,
            "doc_with" : doc_with, "check_args" : check_args,
            "doc_items" : doc_items, "namespace" : namespace, "method" : meth,
            "namespace_copy" : (), "internal" : internal,
            "filename" : filename, "linenumber" : linenumber,
            "module" : current_module_loading[-1], "object" : None,
            "repeat" :  repeat, "deprecated" : deprecated }

    inserted = 0
    for i in range(len(simics_cmds)):
        if command_sorts_before(name, simics_cmds[i]["name"]):
            simics_cmds[i:i] = [cmd]
            inserted = 1
            break

    if not inserted:
        simics_cmds.append(cmd)

    simics_cmds_dict[name] = cmd
    if not namespace and alias:
        for a in alias:
            simics_cmds_dict[a] = cmd

    # check the doc_list
    for d in doc_items:
        if _type(d) == _type((0,0)):
            if len(d) == 2:
                if _type(d[0]) == _type("") and _type(d[1]) == _type(""):
                    continue
        print err + "Illformed doc_items"
        break

    # check the arguments
    arg_names = []
    for arg in args:
        if arg["handler"] == flag_t:
            if not arg["name"]:
                print err + "flag argument must have a name"
            if arg["name"][0] != "-":
                print err + "flag name must begin with -"
        # poly
        if _type(arg["handler"]) == _type((0,0)):

            if arg["name"]:
                if _type(arg["name"]) != _type((0,0)):
                    print err,
                    print "name must be a tuple of names to match the handler"
                if len(arg["handler"]) != len(arg["name"]):
                    print err,
                    print "handler and name must be a tuple of equal length"
                arg_names = arg_names + list(arg["name"])
            if arg["expander"]:
                if _type(arg["expander"]) != _type((0,0)):
                    print err,
                    print "expander must be a tuple of functions to match the",
                    print "handler"
                if len(arg["handler"]) != len(arg["expander"]):
                    print err,
                    print "handler and expander must be a tuple of equal",
                    print "length"

            for i in range(len(arg["handler"])):
                if not arg["expander"] or not arg["expander"][i]:
                    try:
                        exp = arg["handler"][i](ask_for_expander)
                        if not arg["expander"]:
                            arg["expander"] = [None] * len(arg["handler"])
                        arg["expander"][i] = exp
                    except:
                        pass

            # convert the expander back to a tuple if the code above converted
            # it to a list of expanders
            if _type(arg["expander"]) == _type([]):
                arg["expander"] = tuple(arg["expander"])

            for t in arg["handler"]:
                _check_handler(err, t)
        else:
            _check_handler(err, arg["handler"])

            if arg["handler"] == int:
                arg["handler"] = int_t
            elif arg["handler"] == str:
                arg["handler"] = str_t

            if (arg["handler"] == int_t and arg["default"]
                and _type(arg["default"]) != _type(0L)
                and _type(arg["default"]) != _type(0)):
                print err,
                print "int_t args must have default values of type int",
                print "or long int (suffix L)"
            if (arg["handler"] == str_t and arg["default"]
                and _type(arg["default"]) != _type("")):
                print err + "str_t args not string as default value"
            if arg["name"]:
                arg_names.append(arg["name"])

    i = 0
    for a in arg_names:
        if a in arg_names[i+1:]:
            print err + "argument names must be unique"
        i = i + 1

    if namespace_copy:

        nsc = copy_dict(cmd)
        nsc["fun"] = namespace_copy[1]
        if doc:
            nsc["doc"] = ""
            nsc["doc_with"] = name
        nsc["args"] = args[1:]
        nsc["alias"] = alias
        nsc["namespace"] = namespace_copy[0]
        apply(new_command, (), nsc)

def _check_handler(err, t):
    if t == int:
        print err + "int arg type is obsolete, use int_t instead."

    if t == str:
        print err + "str arg type is obsolete, use str_t instead."

    if t == flag:
        print err + "flag arg type is obsolete, use flag_t instead."

    if t == addr:
        print err + "addr arg type is obsolete, use addr_t instead."

    if t == float_arg:
        print err + "float_arg type is obsolete, use float_t instead."


_is_filename_completion = 0

def __file_expander(txt):
    global _is_filename_completion
    _is_filename_completion = 1
    VT_filename_completion()
    orig_txt = txt
    if txt and txt[0] == "~":
        pos = txt.find(os.sep, 1)
        if _altsep:
            altpos = txt.find(_altsep, 1)
            if altpos != -1 and altpos &lt; pos:
                pos = altpos
            
        try:
            if pos &lt; 0:                 # no dirseparator after ~
                user = txt[1:]
                if _use_pwd:
                    users = pwd.getpwall()
                else:
                    users = []
                if os.getenv('HOME'):
                    users = users + [['']]
                res = []
                for u in users:
                    if len(u[0]) &gt;= len(user) and u[0][:len(user)] == user:
                        res.append('~' + u[0])
                return res
            else:
                user = txt[1:pos]

            if not user:
                home = os.getenv('HOME')
            else:
                # getpwnam()[5] is the user's home directory
                home = pwd.getpwnam(user)[5]
            txt = home + txt[pos:]
        except Exception, msg:
            pass

    pos = txt.rfind(os.sep)
    if _altsep is not None:
        altpos = txt.rfind(_altsep, 1)
        if altpos != -1 and altpos &gt; pos:
            pos = altpos

    if pos &lt; 0:
        dirname = os.curdir
        filename = txt
    else:
        dirname = txt[:pos]
        filename = txt[pos + 1:]

    dirname += os.sep

    try:
        files = []
        for f in os.listdir(dirname):
            if len(filename) &gt; len(f):
                continue
            if _case_sensitive_files:
                if f[:len(filename)] != filename:
                    continue
            else:
                if string.lower(f[:len(filename)]) != string.lower(filename):
                    continue
            files.append(f)
    except Exception, msg:
        return []

    if filename:
        path = orig_txt[:-len(filename)]
    else:
    	path = orig_txt
    res = map(lambda f: path + f, files)

    return res

# defines an argument for a command
def arg(handler, name = "", spec = "1", default = None, data = None,
        doc = "", expander = None, pars = [], is_a = None):
    if spec == "":
        spec = "1"
    if handler == flag_t:
        spec = "?"
        default = 0
    if type(default) == type(1):
        default = default + 0L
    if expander == None:
        try:
            expander = handler(ask_for_expander)
        except:
            expander = None
    return { "handler":handler, "name":name, "default":default, "data":data,
             "spec":spec, "doc":doc, "expander":expander, "pars":pars,
             "is_a": is_a }

new_argtype(int_t,  "integer")
new_argtype(str_t,  "string")
new_argtype(flag_t, "flag")
new_argtype(addr_t, "address")
new_argtype(float_t, "float")
new_argtype(integer_t, "64-bit integer")

def istoken(token, tokentype, val = None):
    if tokentype == "str":
        if token[0] in [ "strval", "str" ]:
            if val:
                return val == token[1]
            return 1
        else:
            return 0

    if token[0] == tokentype:
        return 1
    else:
        return 0

def istabcomplete(token):
    if token[0] == "str" and token[1][-1] == "?":
        return 1
    else:
        return 0

def get_arg_description(handler):
    if type(handler) == type((1,1)):
        desc = ""
        for h in handler:
            try:
                tmp = simics_args[h]["name"]
            except KeyError:
                tmp = h(ask_for_description)
            desc = desc + " or " + tmp
        return desc[4:]
    else:
        try:
            return simics_args[handler]["name"]
        except KeyError:
            return handler(ask_for_description)

def find_arg(name, arglist):
    i = 0
    for a in arglist:
        if type(a["name"]) == type((1,1)): # tuple of names
            if name in a["name"]:
                return (a, i)
        elif name == a["name"]:
            return (a, i)
        i = i + 1
    return (None, 0)

def poly_to_spec(arg, spec):
    for i in range(len(arg["name"])):
        if arg["name"][i] == spec:
            arg["name"] = (spec,)
            arg["handler"] = (arg["handler"][i],)
            if arg["expander"]:
                arg["expander"] = arg["expander"][i]
            return arg
    raise CliErrorInPolyToSpec

def call_arg_expander(exp, comp, cmd):
    if cmd["namespace"]:
        try:
            return exp(comp, cmd["object"])
        except TypeError:
            pass
    return exp(comp)

def token_string(ts):
    t = ts[0]
    ts = ts[1:]
    if t[0] == "addr":
        return t[1]+':'+token_string(ts)
    elif t[0] in ["str", "strval", "flag"]:
        return t[1]
    elif t[0] == "int" or t[0] == "float":
        return str(t[1])
    else:
        return ""
            
def format_tokens(ts):
    s = []
    for t in ts:
        if t[0] == "str":
            s.append("s"+repr(t[1]))
        elif t[0] in ["int", "float"]:
            s.append(repr(t[1]))
        else:
            s.append(repr(t[0]))
    return string.join(s, " ")

def _arg_interpreter(cmd, arglist, tokens):

    command = cmd["name"]
    used_args = []
    processed_args = 0
    require_next = 0
    pos = -1

    # add argument number to args
    i = 0
    for a in arglist:
        a["num"] = i
        i = i + 1

    dict = {}

    tokenlen = len(tokens)

    # create a return list of equal length as arglist
    retlist = []
    for a in arglist:
        retlist.append(0)

    while arglist:
        if istoken(tokens[0], "str") and istoken(tokens[1], "str", "="):
            # named parameter?
            name = tokens[0][1]

            (arg, i) = find_arg(name, arglist)

            if arg:
                used_args.append(arg)
                del arglist[i]
                tokens = tokens[2:]
                retentry = arg["num"]

                if type(arg["name"]) == type((0,0)):
                    arg = poly_to_spec(arg, name)

                # tab completion stuff
                if istabcomplete(tokens[0]):
                    comp = tokens[0][1][:-1]
                    if arg["expander"]:
                        comps = call_arg_expander(arg["expander"], comp, cmd)
                        if comps:
                            raise CliTabComplete, comps
                    if (arg["spec"] == "?"
                        and type(arg["default"]) != type((0,0))):
                        if type(arg["default"]) == type(""):
                            if arg["default"] == "":
                                raise CliTabComplete, []
                            else:
                                raise CliTabComplete, [arg["default"]]
                        elif (type(arg["default"]) == type(1)
                              or type(arg["default"]) == type(1L)):
                            raise CliTabComplete, [str(arg["default"])]
                        else:
                            raise CliTabComplete, []
                        raise CliTabComplete, [str(arg["default"])]
                    raise CliTabComplete, []

                if arg["spec"] == "?":
                    # require argument now when name has been specified
                    arg["spec"] = "1"

            else:
                (arg, i) = find_arg(name, used_args)
                if arg:
                    raise CliArgNameError, ("argument '" + name
                                              + "' used twice in command '"
                                              + command + "'")
                else:
                    raise CliArgNameError, ("unknown argument name '" + name
                                              + "' in '" + command + "'")
        elif istoken(tokens[0], "flag"): #flag?
            flagname = tokens[0][1]

            (arg, i) = find_arg(flagname, arglist)

            if arg:
                used_args.append(arg)
                del arglist[i]
                retentry = arg["num"]
                if type(arg["name"]) == type((0,0)):
                    arg = poly_to_spec(arg, flagname)
            else:
                (arg, i) = find_arg(flagname, used_args)
                if arg:
                    raise CliArgNameError, ("flag '" + flagname
                                              + "' used twice in command '"
                                              + command + "'")
                else:
                    raise CliArgNameError, ("unknown flag '" + flagname
                                              + "' in '" + command + "'")
        else:
            arg = arglist[0]

            # completion stuff
            if istabcomplete(tokens[0]):
                comp = tokens[0][1][:-1]
                comps = []

                for a in arglist:
                    # tuple ? then poly arg
                    if type(a["handler"]) == type((0,0)):
                        for i in range(len(a["handler"])):
                            if comp == a["name"][i][:len(comp)]:
                                comps.append(a["name"][i]
                                             + iff(a["handler"][i] == flag_t,
                                                   "", " ="))
                    else:
                        if a["name"] and comp == a["name"][:len(comp)]:
                            comps.append(a["name"]
                                         + iff(a["handler"] == flag_t,
                                               "", " ="))

                if comps:
                    raise CliTabComplete, comps

                exp = arg["expander"]
                if exp:
                    if type(exp) == type((0,0)):
                        exp = exp[0]
                    comps = call_arg_expander(exp, comp, cmd)
                    if comps:
                        raise CliTabComplete, comps

                if arg["spec"] == "?":
                    arglist = arglist[1:]
                    continue

                raise CliTabComplete, []

            used_args.append(arg)
            arglist = arglist[1:]
            retentry = arg["num"]

        anslist = []
        while 1:
            if istabcomplete(tokens[0]):
                raise CliTabComplete, []

            try:
                # tuple? then it is or beteen entries == polyvalue
                if type(arg["handler"]) == type((1,1)):
                    fail = 1
                    for h in range(len(arg["handler"])):
                        try:
                            (val, pos) = arg["handler"][h](tokens)
                            val = (arg["handler"][h], val, arg["name"][h])
                            fail = 0
                            break
                        except CliTypeError, msg:
                            continue
                    if fail:
                        raise CliTypeError
                else:
                    # call the argtypes handler with rest of tokens
                    (val, pos) = arg["handler"](tokens)
                    # if we have a is_a function test the argument
                    if arg["is_a"]:
                        if not arg["is_a"](val):
                            raise CliTypeError

                processed_args = processed_args + 1
	        tokens = tokens[pos:]
                anslist.append(val)

                # if this is set the next ?&amp; entry must be there
                if arg["spec"] == "?&amp;":
                    require_next = 1
                else:
                    require_next = 0

                if arg["spec"] != "+" and arg["spec"] != "*":
                    break

            except CliTypeError, msg:
                if arg["spec"][0] == "?" and not require_next:
                    # last arg and wrong type
                    if arglist == [] and not istoken(tokens[0], ")"):
                        raise CliOutOfArgs, \
                              ("argument "
                               + str(processed_args+1) + " ("
                               + token_string(tokens)
                               + ") given to '" + command
                               + "' has the wrong type;\n"
                               + get_arg_description(arg["handler"])
                               + " expected.\nSYNOPSIS: "
                               + get_synopsis(cmd, len(command)))
                    else:
                        val = arg["default"]

                    if arg["spec"] == "?&amp;":
                        while arglist:
                            if arglist[0]["spec"] == "?&amp;":
                                del arglist[0]
                            else:
                                break
                    break
                elif arg["spec"] == "+":
                    if anslist == []:
                        raise CliOutOfArgs, \
                              ("out of arguments for command '" + command
                               + "';\nexpecting "
                               + get_arg_description(arg["handler"])
                               + " list.\nSYNOPSIS: "
                               + get_synopsis(cmd, len(command)))
                    else:
                        val = anslist
                        break
                elif arg["spec"] == "*":
                    val = anslist
                    break
                else:
                    if istoken(tokens[0], ")"):
                        raise CliOutOfArgs, \
                              ("argument number "+ str(retentry+1)
                               + " is missing in '"
                               + command + "';\n"
                               + get_arg_description(arg["handler"])
                               + " expected.\n"
                               + "SYNOPSIS: "
                               + get_synopsis(cmd, len(command)))
                    else:
                        raise CliOutOfArgs, \
                              ("argument " + str(processed_args + 1)
                               + " (" + token_string(tokens)
                               + ") given to '" + command
                               + "' has the wrong type;\n"
                               + get_arg_description(arg["handler"])
                               + " expected.\nSYNOPSIS: "
                               + get_synopsis(cmd, len(command)))

        retlist[retentry] = val

    if istabcomplete(tokens[0]):
        raise CliTabComplete, []

    if cmd["check_args"] and not istoken(tokens[0], ')'):
        raise CliParseError, ("too many arguments for command '" + command
                                + "'.\nSYNOPSIS: "
                                + get_synopsis(cmd, len(command)))

    return (retlist, tokenlen - len(tokens))

#
# tokenizes the input command line string.
#
#

found_separator = 0

def tokenize(text):

    global found_separator
    found_separator = 0

    escapes = {'n': '\n', 'r': '\r', 't': '\t', 'b': '\b', 'a': '\a',
               'v': '\v', 'f': '\f', 'e': '\033'}

    # Get commands with no letter characters (except -,/), i.e. +, * etc. These
    # will separate tokens.
    no_letter_cmds = []
    for command in simics_commands():
        if (command["name"] != "-"
            and command["name"] != "/"
            and command["name"] != "~"):
            f = 1
            for c in command["name"]:
                if c in letters:
                    f = 0
                    break
            if f:
                no_letter_cmds.append(command["name"])

#   print no_letter_cmds

    # put a sentinel last so that we always end with a white space
    text = " " + text + " "

    tokens = []
    while text != "":
        # look for flag token, i.e. \s+-wordchars\s
        mo = re_match(r"\s+(-[a-zA-Z][a-zA-Z0-9-]*)(?=(\s|\)|;))", text)
        if mo:
            tokens.append(("flag", mo.group(1)))
            text = text[mo.end():]
            continue

        # Eat whites
        mo = re_match(r"\s*", text)
        text = text[mo.end():]

        if text == "": break

        # look for comments
        if text[0] == "#":
            return tokens

        # command separator
        if text[0] == ";":
            found_separator = text[1:]
            return tokens

        # look for variables, i.e. $wordchars
        mo = re_match(r"\$([a-zA-Z][a-zA-Z0-9_]*)", text)
        if mo:
            tokens.append(("str", "$",))
            tokens.append(("str", mo.group(1)))
            text = text[mo.end():]
            continue

        # look for address prefix
        mo = re_match(r"(v|p|l|li|ld|cs|ds|es|fs|gs|ss|)\s*:", text)
        if mo:
            tokens.append(("addr", mo.group(1)))
            text = text[mo.end():]
            continue

        # string?
        if text[0] == '"':
            mo = re_match(r'"((\\"|[^"])*)"', text)
            new = ""

            if mo:
                tmp = mo.group(1)
                l = len(tmp)
                if l &gt; 1:
                    i = 0
                    while i &lt; l:
                        if tmp[i] == '\\':
                            i = i + 1
                            if i == l:
                                raise CliSyntaxError, "Unterminated string"

                            new += escapes.get(tmp[i], tmp[i])
                        else:
                            new = new + tmp[i]
                        i = i + 1
                else:
                    new = tmp

                tokens.append(("strval", new))
                text = text[mo.end():]
                continue
            else:
                raise CliSyntaxError, "Unterminated string"

        # eval python expr -&gt; pass it to eval python commad
        mo = re_match(r"`(.*)`", text)
        if mo:
            # This is translated to "python exp", which is a specially
            # treated prefix operato with high priority 
            tokens.append(("str", "python"))
            tokens.append(("str", mo.group(1)))
            text = text[mo.end():]
            continue

        mo = float_regexp.match(text)
        if mo:
            # convert floats to python floats
            tokens.append(("float", float(mo.group(0))))
            text = text[mo.end():]
            continue

        if text[0] in '()':
            tokens.append((text[0],))
            text = text[1:]
        elif text[0] in "0123456789":
            (num, pos) = get_integer(text)
            text = text[pos:]
            tokens.append(("int", num))
        elif (text[0] == "-" and text[1] != "?" and tokens
              and (istoken(tokens[-1], ")")
                   or istoken(tokens[-1], "var")
                   or istoken(tokens[-1], "int"))):
            text = text[1:]
            tokens.append(("str", "-"))

        elif (text[0] == "/" and tokens
              and (istoken(tokens[-1], ")")
                   or istoken(tokens[-1], "var")
                   or istoken(tokens[-1], "int"))):
            tokens.append(("str", "/"))
            text = text[1:]
        elif text[0] == "-" and text[1] in "0123456789":
            text = text[1:]
            (num, pos) = get_integer(text)
            text = text[pos:]
            tokens.append(("int", -num))
        elif text[0] == "~" and text[1] in "0123456789":
            text = text[1:]
            (num, pos) = get_integer(text)
            text = text[pos:]
            tokens.append(("int", ~num))
        else:
            for i in range(len(text)):
                # look for no-letter-commands in string, they separate tokens
                match = 0
                for cmd in no_letter_cmds:
                    if text[i:i+len(cmd)] == cmd:
                        match = 1
                        break
                if match:
                    if i &gt; 0:
                        tokens.append(("str",text[:i]))
                    tokens.append(("str", text[i:i+len(cmd)]))
                    text = text[i+len(cmd):]
                    break
                elif text[i] in whitespace + "();":
                    tokens.append(("str", text[:i]))
                    text = text[i:]
                    break

    return tokens

#python does not provide this function
def copy_dict(dict):
    new = {}
    for key in dict.keys():
        new[key] = dict[key]
    return new

def copy_arg_list(org):
    new = []
    for arg in org:
        new.append(copy_dict(arg))
    return new

def instance_of(object, classname):
    if object.classname == classname:
        return 1
    if classname in dir(object.iface):
        return 1
    return 0

def get_namespace_commands(prefix):
    pos = prefix.find(".")
    if pos &gt;= 0:
        obj_name = prefix[:pos]
    else:
        l = []
        obj_name = prefix
        # no dot in prefix, add only object names with a trailing dot.
        for o in SIM_all_objects():
            name = o.name
            if len(name) &lt; len(obj_name) or name[:len(obj_name)] != obj_name:
                continue
            l.append(name+".")

        # if there is only one possible object, we continue bellow and expand
        # to every command as well
        if len(l) != 1:
            return l
            
    l = []
    for o in SIM_all_objects():
        name = o.name
        if len(name) &lt; len(obj_name) or name[:len(obj_name)] != obj_name:
            continue
        for cmd in simics_commands():
            if instance_of(o, cmd["namespace"]):
                l.append(name + "."
                         + cmd["name"][string.find(cmd["name"], ".") + 1:])
    return l

def get_conf_objects(prefix):
    l = []
    for o in SIM_all_objects():
        name = o.name
        if len(name) &lt; len(prefix) or name[:len(prefix)] != prefix:
            continue
        l.append(name)
    return l

def run(text, comp = 0, user_typed = 0, watch_eval_python = 0):
    global _last_cli_repeat
    global _last_cli_hap
    # first enclose it in parentheses then tokenize the line

    global found_separator
    chars_seen = 0

    nb_cmd = 0
    while(1):        
        tokens = tokenize(text)
        # cancel all repeat if more than one command is run
        if nb_cmd and user_typed:
            _last_cli_repeat = 0

        if found_separator:
            chars_seen += len(text) - len(found_separator)
            if comp &lt; chars_seen:
                run_one(tokens, user_typed, watch_eval_python)
        else:
            run_one(tokens, user_typed, watch_eval_python)
        
        nb_cmd = nb_cmd + 1
        if found_separator:
            text = found_separator
            found_separator = 0
        else:
            break

    # if it's a multi-command, then just repeat it without taking care of what
    # the functions say
    if (nb_cmd &gt; 1) and user_typed:
        _last_cli_repeat = 1 # repeat cmd line

    # if there was a hap signaled, then the command should not be repeated
    # at all
    if _last_cli_hap:
        _last_cli_repeat = 0
        _last_cli_hap = 0

def run_one(tokens, user_typed = 0, watch_eval_python = 0):
    global _last_cli_repeat

    # use a local copy to keep _last_cli_repeat as it was at the start
    loc_last_cli_repeat = _last_cli_repeat
        
    # put parentheses around the expression, makes it always have a deepest
    # pair
    if tokens:
        tokens = [("(",)] + tokens + [(")",)]

    object = None

    # main eval loop

    while len(tokens) &gt; 1:
        #print tokens
        #find no nested parentheses
        start = -1
        end = -1
        i = 0

        for t in tokens:
            if istoken(t, "("):
                start = i
            elif istoken(t, ")"):
                if start == -1:
                    raise CliSyntaxError, "unbalanced parentheses"
                end = i
                sub = tokens[start:i+1]
                del tokens[start:i+1]
                break
            i = i + 1

        if end == -1:
            raise CliSyntaxError, "unbalanced parentheses"

        if len(sub) == 2:
            raise CliParseError, "empty parentheses"

        while 1:
            # be compatible with $var = ...
            if (len(sub) &gt;= 4 and istoken(sub[1], "str", "$")
                and istoken(sub[3], "str", "=")):
                del sub[1]

            # now, find command with highest priority, this only
            # applies to non-namespace infix commands

            found = cmd = None
            hi_pri_pos = 0
            hi_pri = -1000000
            for i in range(len(sub)):
                if istoken(sub[i], "str") and not istoken(sub[i], "strval"):
                    cmd = simics_commands_dict(sub[i][1])
                if cmd and ((i == 1 and not cmd["infix"])
                            or cmd["infix"]
                            or cmd["name"] in ["%","~", "python", "$"]):
                    if cmd["pri"] &gt; hi_pri:
                        # higher priority operator
                        hi_pri_pos = i
                        hi_pri = cmd["pri"]
                        found = cmd
                    elif cmd == found and cmd["infix"] and not cmd["left"]:
                        # right-associative infix operator
                        hi_pri_pos = i
                        hi_pri = cmd["pri"]
                        found = cmd
            i = hi_pri_pos

            # only allow assignment in position 2, i.e. ( foo = 1 ... )
            # requiered not to mess up with named args
            if found and found["name"] == "=" and i != 2:
                found = None

            # help and appropos are handled special here, this allows help +,
            # etc
            if (istoken(sub[i], "str")
                and sub[i][1] in ["help", "h", "apropos", "a"]):
                found = simics_commands_dict(sub[i][1])

            # if no command found, try to find one by prefix, first word within
            # brackets
            if not found:
                if istoken(sub[1], "str"):
                    name = sub[1][1]
                    if name[-1] == "?":
                        name = name[:-1]

                    cmds = []

                    # needed if namespace command and exact match of command
                    exact_match = 0

                    # namespace
                    pos = string.find(name, ".")
                    if pos &gt;= 0:
                        namespace = name[:pos]
                        name = name[pos+1:]
                        try:
                            object = SIM_get_object(namespace)
                        except:
                            raise CliParseError, ("No name space '"
                                                    + namespace + "'")

                        for cmd in simics_commands():
                            if instance_of(object, cmd["namespace"]):
                                if name == cmd["method"][:len(name)]:
                                    cmds.append(namespace + "."
                                                + cmd["method"])
                                    if not exact_match:
                                        found = cmd
                                        if name == cmd["method"]:
                                            exact_match = 1
                                if cmd["alias"]:
                                    for a in cmd["alias"]:
                                        if name == a[:len(name)]:
                                            cmds.append(namespace + "." + a)
                                            if not exact_match:
                                                found = cmd
                                                if name == a:
                                                    exact_match = 1
                    else:
                        for cmd in simics_commands():
                            tmp = get_completions(name,
                                                  [cmd["name"]] + cmd["alias"])
                            if not cmd["namespace"] and tmp:
                                found = cmd
                                cmds = cmds + tmp
                                if len(tmp) == 1 and tmp[0] == name:
                                    exact_match = 1
                                    break
                        for n in get_namespace_commands(name):
                            if n[:len(name)] == name:
                                cmds.append(n)
                                if n == name:
                                    exact_match = 1
                                    break

                    if sub[1][1][-1] == "?":
                        raise CliTabComplete, cmds

                    if not exact_match:
                        found = 0
                    else:
                        i = 1

            if not found:

                if len(sub) == 3:
                    if istoken(sub[1], "strval") or istoken(sub[1], "int"):
                        break

                if istoken(sub[1], "str"):
                    raise CliError, "unknown command '" + sub[1][1] + "'."

                raise CliParseError, "cannot understand that!"

            if not object and found["namespace"]:
                raise CliError, ("A classname cannot be used as a namespace."
                                   + " An instance name must be used. ")
            del sub[i]
            if found["infix"] and i &gt; found["infix"]:
                i = i - found["infix"]

            # save the object for tab completion stuff
            found["object"] = object

            (args, num) = _arg_interpreter(found,
                                           copy_arg_list(found["args"]),
                                           sub[i:])
            del sub[i:i+num]

            if user_typed and (loc_last_cli_repeat == 2) and (found["repeat"]):
                retval = apply(found["repeat"],
                               iff(object, [object], []) + args)
                # repeat because another command may have changed that
                _last_cli_repeat = 2
            else:
                if found['deprecated']:
                    global deprecated_warned
                    
                    if not found['fun'] in deprecated_warned.keys():
                        deprecated_warned[found['fun']] = 1
                        print "Warning: This command is deprecated. Use %s." % found['deprecated']
                        print
                        
                retval = apply(found["fun"], iff(object, [object], []) + args)
                if user_typed:
                    if found["repeat"]:
                        _last_cli_repeat = 2
                    else:
                        _last_cli_repeat = 0
                                    
            object = None
            if type(retval) in [ type(0), type(0L) ]:
                sub[i:i] = [("int", retval)]
            elif type(retval) == type(""):
                sub[i:i] = [("strval", retval)]
            else:
                if len(sub) &gt; 3 or start &gt; 0:
                    raise CliParseError, "command '" + found["name"] + \
                          "' used in expression although it returns no value."
                sub[i:i] = [("void",)]

            if len(sub) &lt;= 3: # can it be &lt; 3 ?
                break

        tokens[start:start] = [sub[1]]

    if tokens and tokens[0] != None and SIM_is_interactive():
        # Ugly hack to get command return value back to GUI frontend
        if watch_eval_python:
            if istoken(tokens[0], "int"):
                raise WatchCommandReturnValue, str(tokens[0][1])
        
        if istoken(tokens[0], "void"):
            pass
        elif istoken(tokens[0], "int"):
            pr(number_str(tokens[0][1]) + "\n")
        else:
            pr(token_string(tokens) + "\n")

def get_completions(comp, list):
    l = []
    for cc in list:
        if cc[:len(comp)] == comp:
            l.append(cc)
    return l

def proto_cmd_complete(req):
    global _is_filename_completion
    _is_filename_completion = 0
    
    cmd, id, text = req
        
    try:
        run(text + "?", len(text))
    except CliTabComplete, ex:
        VT_send_control_msg(['reply', id,
                             _is_filename_completion, ex.value()])
    except:
        VT_send_control_msg(['reply', id,
                             _is_filename_completion, []])
                       
def tab_complete(dummy, text, word, start, end):
    end = int(end)
    try:
        run(text[:end]+"? "+text[end:], end)
    except CliTabComplete, ex:
        for t in ex.value():
            VT_add_completion(t)
    except:
        pass

try:
    SIM_hap_register_callback("Python_Tab_Completion", tab_complete, 0)
except:
    print "SIM_hap_register_callback() failed.",
    print "This is ok when generating documentation."

last_cli_error = None

def get_last_cli_error():
    global last_cli_error
    return last_cli_error

def cli_sim_stop_cb(dummy, exc, str):
    global _last_cli_hap

    if (exc == SimExc_Break.number):
        _last_cli_hap = 1 # signal a hap for run()

# add the callback to the Core_Simulation_Stopped Hap
if SIM_hap_register_callback("Core_Simulation_Stopped",
                             cli_sim_stop_cb, 0) == -1:
    print "Failed to set cli.py callback"
    
# user_typed is used only from the frontend
# it enables the repeating system
def eval_cli_line(text, user_typed = 0):
    global last_cli_error
    global _last_cli_cmdline
    global _last_cli_repeat
    global _is_filename_completion

    _is_filename_completion = 0
    
    # if we got a repeat command (empty text)
    # then check and re-use the previous command
    # unless we're reading from a file or someone
    # is sending us commands directly
    if user_typed: 
        if ((text == "" and SIM_is_interactive())
            or text == "cli-repeat-last-command"):
            if _last_cli_repeat: # all kind of repeat
                text = _last_cli_cmdline
                VT_logit("cli-repeat-last-command\n") # log in .simics-log
        else:
            _last_cli_repeat = 0 # cancel repeat

    if text == "":
        return

    command_ok=0
    msg = None
    if user_typed:
        _last_cli_cmdline = text

    try:
        run(text, 0, user_typed)
    except CliSyntaxError, msg:
        print "Syntax error:", msg
    except CliParseError, msg:
        print "Parse error:", msg
    except CliArgNameError, msg:
        print "Argument error:", msg
    except CliOutOfArgs, msg:
        print "Argument error:", msg
    except CliAmbiguousCommand, msg:
        print msg
    except CliTabComplete, ex:
        # this code is probably never reach during normal Simics usage;
        # normally tab completion goes through either the tab_complete()
        # function or proto_cmd_complete()
        for n in ex.value():
            print "   " + n
    except CliError, msg:
        print msg
    except SimExc_Index, msg:
        print msg
    except SimExc_General, msg:
        print msg
    else:
        command_ok=1

    if command_ok == 0:
        if user_typed:
            _last_cli_repeat = 0
        SIM_command_has_problem()

    if msg:
        last_cli_error = str(msg)

# Support for Script branches

def wait_for_hap(hap_name):
    return SIM_get_attribute_idx(SIM_get_object("python"), "hap-sleep",
                                 [hap_name])

def wait_for_hap_idx(hap_name, idx):
    return SIM_get_attribute_idx(SIM_get_object("python"), "hap-sleep",
                                 [hap_name, idx])

def wait_for_hap_range(hap_name, idx0, idx1):
    return SIM_get_attribute_idx(SIM_get_object("python"), "hap-sleep",
                                 [hap_name, idx0, idx1])

import __main__

class unused_arg:
    pass

def start_branch(branch, arg = unused_arg()):
    if not "branch" in dir(conf.python):
        return
    if isinstance(branch, str):
        print "Warning: calling start_branch() with string is obsolete."
        print "         function: %s" % branch
        name = branch
        try:
            branch = eval("__main__." + name)
        except:
            print "start_branch(): Cannot find function %s" % name
            return
    if isinstance(arg, unused_arg):
        conf.python.iface.python.create_branch(branch)
    else:
        conf.python.iface.python.create_branch(lambda: branch(arg))

def fmt_value(value):
    if type(value) == type(conf.sim):
        return value.name
    elif type(value) == type([]):
        if value:
            l = [ fmt_value(v) for v in value ]
            return ", ".join(l)
        else:
            return "none"
    elif type(value) == type(None):
        return "none"
    else:
        return str(value)

def print_info(info, key_width = 20):
    fmt = "%%%ds :" % key_width
    for section, data in info:
        print
        if section:
            print "%s:" % section
        for key, value in data:
            print fmt % key, fmt_value(value)


Just_Right = 0
Just_Center = 1
Just_Left = 2

_column_disable = 0

#
# Print data in nicely formatted columns. Data is an array or arrays, where
# each entry in the "outer" array is a data row. Each entry in the inner array
# is a field in a column. Each column has a justification (Just_Right,
# Just_Center, or Just_Left) in the just array, which must have the same number
# of entries as every inner array of data.
#
# If has_title is true, data[0] is considered to contain column titles.
#
# column_space is the number of spaces added between each column.
#
# wrap_space is the text added between any "outer" columns.
#
# The inner arrays can optionally contain one extra element which will be
# printed on a line of its own after the "real" line. If such elements are
# present, column wrappning will never be done.
#
# The outer array can also contain strings, which are automatically converted
# to arrays containing just that element.
#
def print_columns(just, data, has_title = 1, column_space = 2,
                  wrap_space = " | "):
    global _column_disable

    def fix_fields(x):
        def fix_elements(y):
            if type(y) != type(""):
                return "%s" % y
            return y

        if type(x) != type([]):
            x = [ x ]
        return map(fix_elements, x)

    has_title = not not has_title

    data = map(fix_fields, data)

    has_extra = 0

    cols = len(just)

    if data == [] or cols &lt;= has_title:
        return

    widths = [0] * len(data[0])
    for row in data:
        for i in range(0, cols):
            l = len(row[i])
            if l &gt; widths[i]:
                widths[i] = l
        if len(row) &gt; cols:
            has_extra = 1

    rwid = -column_space
    for i in range(0, cols):
        rwid = rwid + widths[i] + column_space

    if rwid &lt;= 0:
        return

    if has_extra or _column_disable:
        columns = 1

    else:
        columns = ((terminal_width() - 1 + len(wrap_space))
                   / (rwid + len(wrap_space)))
        if columns &gt; len(data) - has_title:
            columns = len(data) - has_title
        if columns &lt; 1:
            columns = 1

    fields = len(data)
    if has_title:
        fields = fields + columns - 1

    rows = (fields + columns - 1) / columns

    for row in range(0, rows):
        line = ""
        for col in range(0, columns):
            if row == 0 and has_title:
                field = data[0]
            else:
                if has_title:
                    f = col * (rows - 1) + row
                else:
                    f = col * rows + row
                if f &gt;= len(data):
                    field = [""] * cols
                else:
                    field = data[f]

            if col &gt; 0:
                str = wrap_space
            else:
                str = ""
            for i in range(0, cols):
                if i &gt; 0:
                    str = str + " " * column_space
                spc = widths[i] - len(field[i])
                if just[i] == Just_Right:
                    str = str + " " * spc + field[i]
                elif just[i] == Just_Center:
                    str += " " * (spc / 2) + field[i] + " " * ((spc + 1) / 2)
                else:
                    str += field[i] + " " * spc

            line = line + str
            if len(field) &gt; cols:
                line = line + "\n" + field[cols]

        print line
        if row == 0 and has_title:
            print (("-" * (rwid + len(wrap_space) / 2) + "+" +
                    "-" * ((len(wrap_space) - 1) / 2)) * (columns - 1) +
                   "-" * rwid)

def enable_columns():
    global _column_disable
    if _column_disable &gt; 0:
        _column_disable = _column_disable - 1

def disable_columns():
    global _column_disable
    _column_disable = _column_disable + 1

def _screen_resized(dummy, width, height):
    global _terminal_width, _terminal_height
    _terminal_width = width
    _terminal_height = height

SIM_hap_register_callback("Core_Screen_Resized", _screen_resized, None)

def print_simple_wrap_code(line, width):
    spaces = 0
    while spaces &lt; len(line) and line[spaces] == ' ':
        spaces = spaces + 1

    if spaces &gt;= width:
        print line
        return

    words = line.split()
    indent = " " * spaces
    while words:
        rem = width - len(indent) - len(words[0])
        print "%s%s" % (indent, words[0]),
        words = words[1:]
        while words and rem &gt; len(words[0]):
            print words[0],
            rem = rem - len(words[0]) - 1
            words = words[1:]
        print
        indent = " " * spaces + "    "

def print_wrap_code(code, width):
    for line in code.splitlines():
        if len(line) &lt;= width:
            print line
            continue

        print_simple_wrap_code(line, width)

def print_word_wrap(line, first_prefix, indent):
    words = line.split()
    prefix = first_prefix
    while words:
        print "%s%s" % (prefix, words[0]),
        rem = terminal_width() - 1 - len(prefix) - len(words[0])
        words = words[1:]
        while words and len(words[0]) &lt; rem:
            rem = rem - len(words[0]) - 1
            print words[0],
            words = words[1:]
        print
        prefix = indent
        
# watch expression support
class WatchCommandReturnValue(Exception):
    pass

def promote_value(val):
    try:
        return long(str(val))
    except:
        return str(val)

def eval_cli_expr(expr):
    try:
        run(expr, 0, watch_eval_python = 1)
    except WatchCommandReturnValue, msg:
        return promote_value(msg)

    return "?? (CLI command did not return a value)"

def eval_python_expr(expr):
    return promote_value(eval(expr))

def watch_expr(req):
    try:
        cmd, id, type, expr = req
        if type == "python":
            val = eval_python_expr(expr)
        elif type == "cli":
            val = eval_cli_expr(expr)
        else:
            raise Exception, "unknown expression type '%s'" % str(type)

        VT_send_control_msg(['reply', id, 1, val])

    except:
        _, value, _ = sys.exc_info()
        VT_send_control_msg(['reply', id, 0, str(value)])

def proto_cmd_pselect(req):
    # It's an internal error if this fails for some reason. It means that
    # the frontend has tried to set a CPU which doesn't exist.
    if len(req) == 2:
        id = req[1]
    elif len(req) == 3:
        id = req[1]
        cpu = req[2]
        set_current_processor(SIM_get_object(cpu))
    else:
        print "proto_cmd_pselect(): argument error"
        return

    try:
        name = current_processor().name
    except:
        name = ""
        
    VT_send_control_msg(['reply', id, name])


def proto_cmd_run(req):
    try:
        _, id = req
        eval_cli_line("run")
        VT_send_control_msg(["reply", id])
    except Exception, msg:
        print "exception while calling proto_cmd_run:", msg, msg.__class__

def proto_cmd_break(req):
    try:
        _, id = req
        eval_cli_line("stop")
        SIM_command_has_problem()       # cause scripts to be interrupted
        VT_send_control_msg(["reply", id]) 
    except Exception, msg:
        print "exception while calling proto_cmd_break:", msg, msg.__class__
       
def proto_cmd_single_step(req):
    try:
        _, id = req
        eval_cli_line("stepi")
        VT_send_control_msg(["reply", id])
    except Exception, msg:
        print "exception while calling proto_cmd_single_step:",
        print msg, msg.__class__


# hack to share commands.py (source) files between modules
            
last_module_loaded = None

def get_last_loaded_module():
    return last_module_loaded

def set_last_loaded_module(name):
    global last_module_loaded
    last_module_loaded = name

def help_cmd_exp(comp):
    l = []
    for cmd in simics_commands():
        if cmd["name"][:len(comp)] == comp:
            l.append(cmd["name"])
        for alias in cmd["alias"]:
            if alias[:len(comp)] == comp:
                l.append(alias)

    return get_completions(comp,
                           get_command_categories()
                           + get_namespace_commands(comp)
                           + get_conf_objects(comp)
                           + l)

new_command("help", help_cmd, [arg((str_t,flag_t),
                                   ("help-string","-all"), "?", (str_t, ""),
                                   expander = (help_cmd_exp,0))],
            short = "help command",
            alias=["h", "man"],
            pri=1000,
            type = "help commands",
            doc_items = [("SEE ALSO", "apropos, api-help, api-apropos")],
            doc = """
All Simics commands are divided into different categories. Typing
"&lt;b&gt;help&lt;/b&gt;" with no arguments will list these categories. Type
"&lt;b&gt;help&lt;/b&gt; &lt;i&gt;command-category&lt;/i&gt;" to list the commands in a
category. "&lt;b&gt;help&lt;/b&gt; &lt;i&gt;command-name&lt;/i&gt;" will print the
documentation for a specific command.

Some commands look like this: &lt;b&gt;&lt;class-name&gt;.command&lt;/b&gt;. They are called
name-space commands and belongs to a configuration object. To see the
documentation for a name-space command type "&lt;b&gt;help&lt;/b&gt;
&lt;i&gt;object&lt;/i&gt;.&lt;i&gt;command&lt;/i&gt;" or "&lt;b&gt;help&lt;/b&gt;
&lt;&lt;i&gt;class-name&lt;/i&gt;&gt;.&lt;i&gt;command&lt;/i&gt;"; e.g. "help cpu0.enable" or "help
&lt;processor&gt;.enable" (the angle brackets should be typed). "&lt;b&gt;help&lt;/b&gt;
&lt;i&gt;object-name&lt;/i&gt;" will list all commands available for an object
(name-space).

"&lt;b&gt;help&lt;/b&gt; -all" will print a list of all commands.
""", filename="/home/mp/simics-2.0.23/src/extensions/apps-python/cli_impl.py", linenumber="3435")

def apropos_cmd(text, regexp = 0):
    # search all commands for a string
    found = []

    if regexp:
        finder = _regexp_find
        try:
            pattern = re.compile(text, re.IGNORECASE)
        except Exception, msg:
            print "Invalid regular expression '%s': %s" % (text, msg)
            return
        desc = "Text matching the regular expression"
    else:
        finder = _substring_find
	pattern = string.lower(text)
        desc = "The text"

    for cmd in simics_commands():
        if (finder(cmd["doc"], pattern)
            or finder(cmd["name"], pattern)
            or finder(cmd["short"], pattern)
            or finder(cmd["group_short"], pattern)):
            found.append(cmd)
        elif type("") == type(cmd["alias"]):
            if finder(cmd["alias"], pattern):
                found.append(cmd)
        else:
            for a in cmd["alias"]:
                if finder(a, pattern):
                    found.append(cmd)
                    break

    if found:
        print "%s '%s' appears in the documentation" % (desc, text)
        print "for the following commands:\n"
        print_commands(found)
    else:
        print "%s '%s' cannot be found in any documentation." % (desc, text)

new_command("apropos", lambda re, str: apropos_cmd(str, re),
            [arg(flag_t, "-r"), arg(str_t, "string") ],
            short = "search for text in documentation",
            alias = "a", pri = 1000,
            type = "help commands",
            doc_items = [("SEE ALSO", "api-help, api-apropos, help")],
            doc = """
Use &lt;b&gt;apropos&lt;/b&gt; &lt;i&gt;string&lt;/i&gt; to list all commands for which the
documentation contains the text &lt;i&gt;string&lt;/i&gt;. If the &lt;tt&gt;-r&lt;/tt&gt; flag
is used, interpret &lt;i&gt;string&lt;/i&gt; as a regular expression.
""", filename="/home/mp/simics-2.0.23/src/extensions/apps-python/cli_impl.py", linenumber="3501")

</pre></body></html>