mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 02:43:15 +01:00
Initial commit
This commit is contained in:
861
tools/cml2-tools/autoconfigure.py
Executable file
861
tools/cml2-tools/autoconfigure.py
Executable file
@@ -0,0 +1,861 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# linux/scripts/autoconfigure.py : Automagical Kernel Configuration.
|
||||
#
|
||||
# Copyright (C) 2000-2002 Eric S. Raymond <esr@thyrsus.com>
|
||||
# This is free software, see GNU General Public License 2 for details.
|
||||
#
|
||||
# This script tries to autoconfigure the Linux kernel, detecting the
|
||||
# hardware (devices, ...) and software (protocols, filesystems, ...).
|
||||
# It uses soft detection: no direct IO access to unknown devices, thus
|
||||
# it is always safe to run this script and it never hangs, but it cannot
|
||||
# detect all hardware (mainly misses some very old hardware). You don't
|
||||
# need root, but you will need a CML2 rulebase handy.
|
||||
#
|
||||
# Most of the smarts in this script is in the file of probe rules
|
||||
# maintained by Giacomo Catenazzi and brought in by execfile.
|
||||
|
||||
import sys, getopt, os, glob, commands, re
|
||||
import cml, cmlsystem
|
||||
from cml import y, m, n # For use in the autoprobe rules
|
||||
|
||||
lang = {
|
||||
"COMPLETE":"Configuration complete.",
|
||||
"COMPLEMENT":"* Computing complement sets",
|
||||
"DERIVED":"Symbol %s is derived and cannot be set.",
|
||||
"DONE":"Done",
|
||||
"EFFECTS":"Side effects:",
|
||||
"NOCMDLINE":"%s is the wrong type to be set from the command line",
|
||||
"OPTUNKNOWN":"autoconfigure: unknown option.\n",
|
||||
"ROOTFS":"* %s will be hard-compiled in for the root filesystem\n",
|
||||
"ROOTHW":"* %s will be hard-compiled in to run the root device\n",
|
||||
"ROOTLOOK":"# Looking for your root filesystem...\n",
|
||||
"ROOTWARN":"** Warning: I could not identify the " \
|
||||
"bus type of your root drive!\n",
|
||||
"SETFAIL" : "%s failed while %s was being set to %s\n",
|
||||
"SYMUNKNOWN":"cmlconfigure: unknown symbol %s\n",
|
||||
"TURNOFF":"# Turning off unprobed device symbols",
|
||||
"UNAME":"Can't determine ARCH, uname failed.",
|
||||
}
|
||||
|
||||
class ConfigFile:
|
||||
"Object that represents a generated configuration."
|
||||
def __init__(self, myconfiguration, hardcompile, debuglevel=0):
|
||||
# Prepare an output object to accept the configuration file
|
||||
self.hardcompile = hardcompile
|
||||
self.myconfiguration = myconfiguration
|
||||
myconfiguration.debug = debuglevel
|
||||
self.modified = {}
|
||||
self.emitted = {}
|
||||
if debuglevel:
|
||||
sys.stderr.write("* Debug level %d" % debuglevel)
|
||||
|
||||
# 'found' sets the value 'y/m' (driver detected)
|
||||
# 'found_y' sets the value 'y' (driver detected, forces built-in)
|
||||
# 'found_m' sets the value 'm' (driver detected, build as module)
|
||||
# 'found_n' sets the value 'n' (driver not needed)
|
||||
#
|
||||
# The priority is: y > m > n > 'other'
|
||||
def found(self, symbol, val=None, label=None):
|
||||
if type(symbol) == type(""):
|
||||
symbol = self.myconfiguration.dictionary.get(symbol)
|
||||
# Ignore obsolete symbols
|
||||
if not symbol:
|
||||
return
|
||||
# Ignore attempts to set derived symbols. Some autoprobes
|
||||
# do this because they were composed in ignorance of the rulebase.
|
||||
elif symbol.is_derived():
|
||||
return
|
||||
# If no value specified, play some tricks.
|
||||
if val == None:
|
||||
if symbol.type=="bool" or (self.hardcompile and symbol.type=="trit"):
|
||||
val = cml.y
|
||||
elif symbol.type == "trit":
|
||||
val = cml.m
|
||||
elif symbol.is_numeric():
|
||||
val = 0
|
||||
elif symbol.type == "string":
|
||||
val = ""
|
||||
if not self.modified.has_key(symbol) or symbol.eval() < val:
|
||||
self.myconfiguration.set_symbol(symbol, val)
|
||||
self.modified[symbol] = 1
|
||||
(ok, effects, violations) = self.myconfiguration.set_symbol(symbol, val)
|
||||
if ok:
|
||||
if label:
|
||||
symbol.setprop(label)
|
||||
else:
|
||||
for violation in violations:
|
||||
sys.stderr.write(lang["SETFAIL"] % (`violation`, symbol.name, val))
|
||||
|
||||
def found_y(self, var, label=None): self.found(var, cml.y, label)
|
||||
def found_m(self, var, label=None): self.found(var, cml.m, label)
|
||||
def found_n(self, var, label=None): self.found(var, cml.n, label)
|
||||
|
||||
def yak(self, symbol):
|
||||
if not self.emitted.has_key(symbol):
|
||||
try:
|
||||
entry = self.myconfiguration.dictionary[symbol]
|
||||
if entry.prompt:
|
||||
sys.stderr.write("* " + symbol + ": " + entry.prompt + "\n")
|
||||
self.emitted[symbol] = 1
|
||||
except KeyError:
|
||||
sys.stderr.write("! Obsolete symbol: " + symbol + "\n")
|
||||
|
||||
def complement(self, symbol, value, baton, label):
|
||||
"Force a complement set to a specified value."
|
||||
symbol = self.myconfiguration.dictionary[symbol]
|
||||
if not symbol.eval():
|
||||
return
|
||||
for driver in self.myconfiguration.dictionary.values():
|
||||
if baton: baton.twirl()
|
||||
if driver.is_symbol() and driver.is_logical() \
|
||||
and self.myconfiguration.is_visible(driver) \
|
||||
and driver.setcount == 0 \
|
||||
and symbol.ancestor_of(driver):
|
||||
set_to = value
|
||||
if driver.type == "bool" and value == cml.m:
|
||||
set_to = cml.y
|
||||
self.found(driver.name, set_to, label)
|
||||
|
||||
def force_dependents_modular(self, symbol, legend):
|
||||
"Force all trit-valued dependents of a symbol to be modular."
|
||||
net_ethernet = self.myconfiguration.dictionary[symbol]
|
||||
for driver in self.myconfiguration.dictionary.values():
|
||||
if driver.is_symbol() and driver.type == "trit" \
|
||||
and driver.eval() == cml.y \
|
||||
and self.myconfiguration.is_visible(driver) \
|
||||
and net_ethernet.ancestor_of(driver):
|
||||
driver.setprop(legend)
|
||||
self.found(driver, cml.m)
|
||||
|
||||
def enabled(self, symbol):
|
||||
"Is a given symbol enabled?"
|
||||
return self.myconfiguration.dictionary[symbol]
|
||||
|
||||
# Now define classes for probing and reporting the system state
|
||||
|
||||
class PCIDevice:
|
||||
"Identification data for a device on the PCI bus."
|
||||
def __init__(self, procdata):
|
||||
"Initialize PCI device ID data based on what's in a /proc entry."
|
||||
procdata = map(ord, procdata)
|
||||
self.vendor = "%02x%02x" % (procdata[1], procdata[0])
|
||||
self.device = "%02x%02x" % (procdata[3], procdata[2])
|
||||
if procdata[14]:
|
||||
self.subvendor = None
|
||||
self.subdevice = None
|
||||
else:
|
||||
self.subvendor = "%02x%02x" % (procdata[45], procdata[44])
|
||||
self.subdevice = "%02x%02x" % (procdata[47], procdata[46])
|
||||
self.revision = "%02x" % procdata[8]
|
||||
self.deviceclass = "%02x%02x" % (procdata[11], procdata[10])
|
||||
self.interface = "%02x" % procdata[9]
|
||||
# Here is the digest format:
|
||||
# "pci: xxxx,yyyy,zz:Class:aabb,cc" or
|
||||
# "pci: xxxx,yyyy,ssss,rrrr,zz:Class:aabbb,cc"
|
||||
# where: xxxx,yyyy: the vendor and device id
|
||||
# ssss,rrrr: the sub-vendor and sub-device id
|
||||
# zz: revision
|
||||
# aabb,cc: Device Class, Interface
|
||||
self.digest = self.vendor + "," + self.device
|
||||
if self.subvendor:
|
||||
self.digest += "," + self.subvendor + "," + self.subdevice
|
||||
self.digest += ",%s;Class:%s,%s\n" % (self.revision,self.deviceclass,self.interface)
|
||||
def __repr__(self):
|
||||
return "pci: " + self.digest
|
||||
|
||||
class PCIScanner:
|
||||
"Encapsulate the PCI hardware registry state."
|
||||
def __init__(self):
|
||||
"Unpack data from the PCI hardware registry."
|
||||
self.devices = []
|
||||
for f in glob.glob("/proc/bus/pci/??/*"):
|
||||
dfp = open(f)
|
||||
self.devices.append(PCIDevice(dfp.read()))
|
||||
dfp.close()
|
||||
def search(self, pattern):
|
||||
"Search for a device match by prefix in the digest."
|
||||
pattern = re.compile(pattern, re.I)
|
||||
return not not filter(lambda x, p=pattern: p.search(x.digest), self.devices)
|
||||
def __repr__(self):
|
||||
return "".join(map(repr, self.devices))
|
||||
|
||||
class FieldParser:
|
||||
"Parse entire lines, or a given field, out of a file or command output."
|
||||
def __init__(self, sources):
|
||||
self.items = []
|
||||
for item in sources:
|
||||
if type(item) == type(()):
|
||||
file = item[0]
|
||||
field = item[1]
|
||||
else:
|
||||
file = item
|
||||
field = None
|
||||
try:
|
||||
if file[0] == '/':
|
||||
ifp = open(file, "r")
|
||||
lines = ifp.readlines()
|
||||
ifp.close()
|
||||
else:
|
||||
(status, output) = commands.getstatusoutput(file)
|
||||
if status:
|
||||
raise IOError
|
||||
lines = output.split("\n")
|
||||
except IOError:
|
||||
continue
|
||||
# No field specified, capture entire line
|
||||
if not field:
|
||||
self.items += lines
|
||||
# Numeric (1-origin) field index, capture that
|
||||
# space-separated field.
|
||||
elif type(field) == type(0):
|
||||
for line in lines:
|
||||
fields = line.split()
|
||||
if len(fields) >= field and fields[field-1] not in self.items:
|
||||
self.items.append(fields[field-1])
|
||||
# Regexp specified, collect group 1
|
||||
else:
|
||||
for line in lines:
|
||||
lookfor = re.compile(field)
|
||||
match = lookfor.search(line)
|
||||
if match:
|
||||
res = match.group(1)
|
||||
if res not in self.items:
|
||||
self.items.append(res)
|
||||
def find(self, str, ind=0):
|
||||
"Is given string or regexp pattern found in the file?"
|
||||
match = re.compile(str)
|
||||
result = filter(lambda x: x, map(lambda x, ma=match: ma.search(x), self.items))
|
||||
if result:
|
||||
result = result[ind]
|
||||
if result.groups():
|
||||
result = ",".join(result.groups())
|
||||
return result
|
||||
def __repr__(self):
|
||||
return `self.items`
|
||||
|
||||
#
|
||||
# Main sequence begins here
|
||||
#
|
||||
|
||||
def get_arch():
|
||||
# Get the architecture (taken from top-level Unix makefile).
|
||||
(error, ARCH) = commands.getstatusoutput('uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/')
|
||||
if error:
|
||||
sys.stderr.write(lang["UNAME"])
|
||||
raise SystemExit, 1
|
||||
# A platform symbol has to be set, otherwise many assignments will fail
|
||||
ARCHSYMBOL = re.compile("i.86").sub("x86", ARCH)
|
||||
ARCHSYMBOL = ARCHSYMBOL.replace("superh", "sh")
|
||||
ARCHSYMBOL = ARCHSYMBOL.replace("sparc32", "sparc")
|
||||
ARCHSYMBOL = ARCHSYMBOL.replace("sparc64", "sparc")
|
||||
ARCHSYMBOL = ARCHSYMBOL.upper()
|
||||
return(ARCH, ARCHSYMBOL)
|
||||
|
||||
# We can't assume 2.1 nested scopes, so refer shared stuff to global level.
|
||||
config = cpu = cpu_id = pci = isapnp = mca = usbp = usbc = usbi = None
|
||||
fs = devices = m_devices = misc = net = ide = dmesg = None
|
||||
modules = cpu_latch = None
|
||||
fsmap = {}
|
||||
reliable = {}
|
||||
|
||||
def autoconfigure(configuration, hardcompile, debuglevel):
|
||||
global config, cpu, cpu_id, pci, isapnp, mca, usbp, usbc, usbi, fs
|
||||
global devices, m_devices, misc, net, ide, dmesg, modules, cpu_latch
|
||||
global fsmap, reliable
|
||||
configuration.interactive = 0 # Don't deduce from visibility.
|
||||
|
||||
config = ConfigFile(configuration, hardcompile, debuglevel)
|
||||
|
||||
#
|
||||
# Here is where we query the system state.
|
||||
#
|
||||
(ARCH, ARCHSYMBOL) = get_arch()
|
||||
config.found_y(ARCHSYMBOL)
|
||||
config.yak(ARCHSYMBOL)
|
||||
|
||||
# Get the processor type
|
||||
cpu = FieldParser(("/proc/cpuinfo",))
|
||||
if ARCHSYMBOL == 'SPARC':
|
||||
processors = int(cpu.find("^ncpus active.*: *([0-9]*)"))
|
||||
vendor = cpu.find("^cpu.*: *(.*)")
|
||||
cpufam = cpu.find("^type.*: *([-A-Za-z0-9_]*)")
|
||||
mod = cpu.find("^fpu.*: *(.*)")
|
||||
name = cpu.find("^MMU Type.*: *(.*)")
|
||||
else:
|
||||
processors = int(cpu.find("^processor.*: *([0-9]*)", -1)) + 1
|
||||
vendor = cpu.find("^vendor_id.*: *([-A-Za-z0-9_]*)")
|
||||
cpufam = cpu.find("^cpu family.*: *([-A-Za-z0-9_]*)")
|
||||
mod = cpu.find("^model.*: *([-A-Za-z0-9_]*)")
|
||||
name = cpu.find("^model name.*: *(.*)")
|
||||
|
||||
cpu_id = vendor + ":" + cpufam + ":" + mod + ":" + name
|
||||
cpu_latch = 0
|
||||
|
||||
# Now query for features
|
||||
pci = PCIScanner()
|
||||
isapnp = FieldParser((("/proc/bus/isapnp/devices", 2),))
|
||||
mca = FieldParser(("/proc/mca/pos",))
|
||||
usbp = FieldParser((("/proc/bus/usb/devices", "^P:.*Vendor=([A-Fa-f0-9]*)\s.*ProdID=\([A-Fa-f0-9]*\)"),))
|
||||
usbc = FieldParser((("/proc/bus/usb/devices", "^D:.*Cls=([A-Fa-f0-9]*)[^A-Fa-f0-9].*Sub=([A-Fa-f0-9]*)[^A-Fa-f0-9].*Prot=([A-Fa-f0-9]*)"),))
|
||||
usbi = FieldParser((("/proc/bus/usb/devices", "^I:.*Cls=([A-Fa-f0-9]*)[^A-Fa-f0-9].*Sub=([A-Fa-f0-9]*)[^A-Fa-f0-9].*Prot=([A-Fa-f0-9]*)"),))
|
||||
fs = FieldParser((("/proc/mounts",3),
|
||||
("/etc/mtab", 3),
|
||||
("/etc/fstab", 3)))
|
||||
devices = FieldParser((("/proc/devices", "[0-9]+ (.*)"),))
|
||||
m_devices = FieldParser((("/proc/misc", "[0-9]+ (.*)"),))
|
||||
misc = FieldParser(("/proc/iomem", "/proc/ioports", "/proc/dma", "/proc/interrupts"))
|
||||
net = FieldParser((("/proc/net/sockstat","^([A-Z0-9]*): inuse [1-9]"),))
|
||||
ide = FieldParser(glob.glob('/proc/ide/hd?/media'))
|
||||
dmesg = FieldParser(("/var/log/dmesg", "dmesg"))
|
||||
modules = FieldParser((("/proc/modules", 1),))
|
||||
|
||||
#
|
||||
# Tests that won't fit in the rulesfile format
|
||||
#
|
||||
|
||||
# Source: linux/i386/kernel/setup.c
|
||||
if dmesg.find("Use a PAE"):
|
||||
config.found_y("HIGHMEM64G")
|
||||
elif dmesg.find("Use a HIGHMEM"):
|
||||
config.found_y("HIGHMEM4G") ##Source: linux/i386/kernel/setup.c
|
||||
else:
|
||||
highmem = dmesg.find("([0-9]*)MB HIGHMEM avail.")
|
||||
if not highmem:
|
||||
config.found_y("NOHIGHMEM")
|
||||
elif int(highmem) > 3072:
|
||||
config.found_y("HIGHMEM64G")
|
||||
else:
|
||||
config.found_y("HIGHMEM4G")
|
||||
|
||||
# SMP? This test is reliable.
|
||||
if processors == 0:
|
||||
processors = len(filter(lambda x: x.find('processor') > -1, cpu.items))
|
||||
|
||||
if processors > 1:
|
||||
config.found_y("SMP")
|
||||
config.yak("SMP")
|
||||
|
||||
fsmap = {}
|
||||
reliable = {}
|
||||
|
||||
#
|
||||
# Here are the function calls used by the rules file
|
||||
#
|
||||
TRUE = 1
|
||||
FALSE = 0
|
||||
PRESENT = 1
|
||||
ABSENT = 0
|
||||
|
||||
def DEBUG(str):
|
||||
sys.stderr.write("# " + str + "\n")
|
||||
|
||||
# Following three tests are reliable -- that is, if PCI or PNP
|
||||
# tests fail we know the feature is *not* there.
|
||||
|
||||
def PCI(prefix, symbol):
|
||||
global pci, config
|
||||
reliable[symbol] = "PCI"
|
||||
if pci.search("^" + prefix):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "PCI")
|
||||
|
||||
|
||||
def PCI_CLASS(match, symbol):
|
||||
global pci, config
|
||||
reliable[symbol] = "PCI_CLASS"
|
||||
if pci.search("Class:" + match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "PCI_CLASS")
|
||||
|
||||
def PNP(match, symbol):
|
||||
global isapnp, config
|
||||
reliable[symbol] = "PNP"
|
||||
if isapnp.find(match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "PNP")
|
||||
|
||||
def MCA(match, symbol):
|
||||
global mca, config
|
||||
reliable[symbol] = "MCA"
|
||||
# FIXME: Not certain I've got the byte order right here
|
||||
if mca.find(": " + match[2:] + " " + match[:2]):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "MCA")
|
||||
|
||||
# USB tests reliably detect connected devices, but the bus is hot-plug.
|
||||
|
||||
def USBP(match, symbol):
|
||||
global usbp, config
|
||||
if usbp.find(match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "USBP")
|
||||
|
||||
def USBC(match, symbol):
|
||||
global usbc, config
|
||||
if usbc.find(match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "USBC")
|
||||
|
||||
def USBI(match, symbol):
|
||||
global usbi, config
|
||||
if usbi.find(match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "USBI")
|
||||
|
||||
# Remaining tests rely on prior kernel configuration.
|
||||
|
||||
def FS(match, symbol):
|
||||
global fs, fsmap, config
|
||||
if fs.find(r"\b" + match + r"\b"):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "FS")
|
||||
# Also, build the map of file system types to symbols.
|
||||
fsmap[match] = symbol
|
||||
|
||||
def DEV(match, symbol):
|
||||
global devices, config
|
||||
if devices.find(r"\b" + match + r"\b"):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "DEV")
|
||||
|
||||
def DEVM(match, symbol):
|
||||
global m_devices, config
|
||||
if m_devices.find(r"\b" + match + r"\b"):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "DEV_M")
|
||||
|
||||
def CONS(match, symbol):
|
||||
global dmesg, config
|
||||
if dmesg.find("^Console: .* " + match + " "):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "CONS")
|
||||
|
||||
def DMESG(match, symbol, truthval=TRUE):
|
||||
global dmesg, config
|
||||
if dmesg.find(match):
|
||||
if truthval:
|
||||
config.found(symbol, None, "DMESG")
|
||||
config.yak(symbol)
|
||||
else:
|
||||
config.found_n(symbol, "DMESG")
|
||||
|
||||
def NET(match, symbol):
|
||||
global net, config
|
||||
if net.find(match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "NET")
|
||||
|
||||
def IDE(match, symbol):
|
||||
global ide, config
|
||||
if ide.find(match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "IDE")
|
||||
|
||||
def REQ(match, symbol):
|
||||
global misc, config
|
||||
if misc.find(match):
|
||||
config.yak(symbol)
|
||||
config.found(symbol, None, "REQ")
|
||||
|
||||
def CPUTYPE(match, symbol):
|
||||
global cpu_latch, config
|
||||
if not cpu_latch and re.search(match, cpu_id):
|
||||
config.found_y(symbol, "CPUTYPE")
|
||||
config.yak(symbol)
|
||||
cpu_latch = 1
|
||||
|
||||
def CPUINFO(match, symbol, present=PRESENT, truthval=cml.y):
|
||||
global cpu, config
|
||||
if (not not cpu.find(match)) == present:
|
||||
config.found(symbol, truthval, "CPUINFO")
|
||||
if truthval:
|
||||
config.yak(symbol)
|
||||
|
||||
def EXISTS(procfile, symbol):
|
||||
global config
|
||||
if os.path.exists(procfile):
|
||||
config.found(symbol, None, "EXISTS")
|
||||
config.yak(symbol)
|
||||
else:
|
||||
config.found(symbol, n, "EXISTS")
|
||||
|
||||
def MODULE(name, symbol):
|
||||
global modules, config
|
||||
if modules.find(r"\b" + name + r"\b"):
|
||||
config.found(symbol, None, "MODULES")
|
||||
config.yak(symbol)
|
||||
|
||||
def GREP(pattern, file, symbol):
|
||||
global config
|
||||
try:
|
||||
fp = open(file)
|
||||
except IOError:
|
||||
return
|
||||
if re.compile(pattern).search(fp.read()):
|
||||
config.found(symbol, None, "GREP")
|
||||
config.yak(symbol)
|
||||
fp.close()
|
||||
|
||||
def LINKTO(file, pattern, symbol):
|
||||
global config
|
||||
if not os.path.exists(file):
|
||||
return
|
||||
file = os.readlink(file)
|
||||
if re.compile(pattern).search(file):
|
||||
config.found(symbol, None, "LINKTO")
|
||||
config.yak(symbol)
|
||||
|
||||
# Use this to avoid conflicts
|
||||
|
||||
def PRIORITY(symbols, cnf=configuration):
|
||||
global config
|
||||
legend = "PRIORITY" + `symbols`
|
||||
dict = cnf.dictionary
|
||||
symbols = map(lambda x, d=dict: d[x], symbols)
|
||||
for i in range(len(symbols) - 1):
|
||||
if cml.evaluate(symbols[i]):
|
||||
for j in range(i+1, len(symbols)):
|
||||
cnf.set_symbol(symbols[j], n)
|
||||
symbols[j].setprop(legend)
|
||||
break
|
||||
|
||||
########################################################################
|
||||
##
|
||||
## Section Command Version Status
|
||||
## ------------------------------------------------------------------
|
||||
## /proc features EXISTS 2.5.2-pre7 Partial
|
||||
|
||||
########################################################################
|
||||
## Section: System Features
|
||||
## KernelOutput: /proc/*, /dev/*
|
||||
## Detect system features based on existence of /proc and /dev/* files
|
||||
DEBUG("autoconfigure.rules: EXISTS")
|
||||
|
||||
## These tests are unreliable; they depend on the current kernel config.
|
||||
EXISTS("/proc/sysvipc", 'SYSVIPC')
|
||||
EXISTS("/proc/sys", 'SYSCTL')
|
||||
EXISTS("/proc/scsi/ide-scsi", 'BLK_DEV_IDESCSI')
|
||||
EXISTS("/proc/scsi/imm", 'SCSI_IMM')
|
||||
EXISTS("/proc/scsi/ppa", 'SCSI_PPA')
|
||||
EXISTS("/dev/.devfsd", 'DEVFS_FS')
|
||||
# Giacomo does not have these yet.
|
||||
EXISTS("/proc/sys/net/khttpd", 'KHTTPD')
|
||||
EXISTS("/proc/sys/kernel/acct", 'BSD_PROCESS_ACCT')
|
||||
# This one is reliable, according to the MCA port documentation.
|
||||
EXISTS("/proc/mca", 'MCA')
|
||||
# This one is reliable too
|
||||
EXISTS("/proc/bus/isapnp/devices", 'ISAPNP')
|
||||
|
||||
# Test the new probe function.
|
||||
GREP("scsi0", "/proc/scsi/scsi", 'SCSI')
|
||||
|
||||
# These can be bogus because the file or directory in question
|
||||
# is empty, or consists of a banner string that does not describe
|
||||
# an actual device. We need to do more analysis here.
|
||||
# EXISTS("/proc/bus/pci", 'PCI')
|
||||
# EXISTS("/proc/bus/usb", 'USB')
|
||||
# EXISTS("/proc/net", 'NET')
|
||||
# EXISTS("/proc/scsi", 'SCSI')
|
||||
|
||||
# These look tempting, but they're no good unless we're on a pure
|
||||
# devfs system, without support for old devices, where devices
|
||||
# only exist when they're needed.
|
||||
# EXISTS("/dev/agpgart", 'AGP')
|
||||
# EXISTS("/dev/floppy", 'BLK_DEV_FD')
|
||||
# EXISTS("/dev/fd0", 'BLK_DEV_FD')
|
||||
|
||||
|
||||
########################################################################
|
||||
## Section: Mice
|
||||
## Detect the mouse type by looking at what's behind the /dev/mouse link.
|
||||
## These are probes for 2.4 with the old input core
|
||||
LINKTO("/dev/mouse", "psaux", 'PSMOUSE')
|
||||
LINKTO("/dev/mouse", "ttyS", 'SERIAL')
|
||||
LINKTO("/dev/mouse", "logibm", 'LOGIBUSMOUSE')
|
||||
LINKTO("/dev/mouse", "inportbm", 'MS_BUSMOUSE')
|
||||
LINKTO("/dev/mouse", "atibm", 'ATIXL_BUSMOUSE')
|
||||
## These are probes for 2.5 with the new input core
|
||||
LINKTO("/dev/mouse", "psaux", 'MOUSE_PS2')
|
||||
LINKTO("/dev/mouse", "ttyS", 'MOUSE_SERIAL')
|
||||
LINKTO("/dev/mouse", "logibm", 'MOUSE_LOGIBM')
|
||||
LINKTO("/dev/mouse", "inportbm", 'MOUSE_INPORT')
|
||||
LINKTO("/dev/mouse", "atibm", 'MOUSE_ATIXL')
|
||||
|
||||
########################################################################
|
||||
## Section: IDE devices
|
||||
## KernelOutput: /proc/ide/hd?/media
|
||||
## Detect IDE devices based on contents of /proc files
|
||||
## These tests are unreliable; they depend on the current kernel config.
|
||||
IDE('disk', 'BLK_DEV_IDEDISK')
|
||||
IDE('cdrom', 'BLK_DEV_IDECD')
|
||||
IDE('tape', 'BLK_DEV_IDETAPE')
|
||||
IDE('floppy', 'BLK_DEV_FLOPPY')
|
||||
EXISTS("/dev/ide/ide0", 'BLK_DEV_IDE')
|
||||
EXISTS("/dev/ide/ide1", 'BLK_DEV_IDE')
|
||||
EXISTS('/proc/ide/piix', 'PIIX_TUNING')
|
||||
|
||||
########################################################################
|
||||
# Miscellaneous tests that replace Giacomo's ad-hoc ones.
|
||||
DEV('pty', 'UNIX98_PTYS')
|
||||
REQ('SMBus', 'I2C')
|
||||
REQ('ATI.*Mach64', 'FB_ATY')
|
||||
#FS(r'xfs', 'XFS_FS')
|
||||
|
||||
########################################################################
|
||||
# This is a near complete set of MCA probes for hardware supported under
|
||||
# Linux, according to MCA maintainer David Weinehall. The exception is
|
||||
# the IBMTR card, which cannot be probed reliably.
|
||||
if config.enabled("MCA"):
|
||||
MCA("ddff", 'BLK_DEV_PS2')
|
||||
MCA("df9f", 'BLK_DEV_PS2')
|
||||
MCA("628b", 'EEXPRESS')
|
||||
MCA("627[cd]", 'EL3')
|
||||
MCA("62db", 'EL3')
|
||||
MCA("62f6", 'EL3')
|
||||
MCA("62f7", 'EL3')
|
||||
MCA("6042", 'ELMC')
|
||||
MCA("0041", 'ELMC_II')
|
||||
MCA("8ef5", 'ELMC_II')
|
||||
MCA("61c[89]", 'ULTRAMCA')
|
||||
MCA("6fc[012]", 'ULTRAMCA')
|
||||
MCA("efd[45]", 'ULTRAMCA')
|
||||
MCA("efe5", 'ULTRAMCA')
|
||||
MCA("641[036]", 'AT1700')
|
||||
MCA("6def", 'DEPCA')
|
||||
MCA("6afd", 'SKMC')
|
||||
MCA("6be9", 'SKMC')
|
||||
MCA("6354", 'NE2_MCA')
|
||||
MCA("7154", 'NE2_MCA')
|
||||
MCA("56ea", 'NE2_MCA')
|
||||
MCA("ffe0", 'IBMLANA')
|
||||
MCA("8ef[8cdef]", 'SCSI_IBMMCA')
|
||||
MCA("5137", 'SCSI_FD_MCS')
|
||||
MCA("60e9", 'SCSI_FD_MCS')
|
||||
MCA("6127", 'SCSI_FD_MCS')
|
||||
MCA("0092", 'SCSI_NCR_D700')
|
||||
MCA("7f4c", 'SCSI_MCA_53C9X')
|
||||
MCA("0f1f", 'SCSI_AHA_1542')
|
||||
MCA("002d", 'MADGEMC')
|
||||
MCA("6ec6", 'SMCTR')
|
||||
MCA("62f3", 'SOUND_SB')
|
||||
MCA("7113", 'SOUND_SB')
|
||||
|
||||
########################################################################
|
||||
## This requires Paul Gortmaker's EISA ID patch.
|
||||
REQ("EISA", "EISA") # Someday, IOPORTS()
|
||||
|
||||
########################################################################
|
||||
## The rest of the table is read in from Giacomo's Catenazzi's rulesfile.
|
||||
execfile(rulesfile)
|
||||
|
||||
# If it has a reliable test, but was not found by any test, switch it off.
|
||||
# We do things in this order to avoid losing on symbols that are only set
|
||||
# to n by PNP and PCI tests.
|
||||
baton = cml.Baton(lang["TURNOFF"])
|
||||
for symbol in configuration.dictionary.values():
|
||||
baton.twirl()
|
||||
if symbol.is_symbol() and configuration.saveable(symbol) \
|
||||
and reliable.has_key(symbol.name) and not cml.evaluate(symbol):
|
||||
config.found(symbol.name, n, reliable[symbol.name])
|
||||
baton.end()
|
||||
|
||||
########################################################################
|
||||
## Resolve conflicts.
|
||||
|
||||
PRIORITY(("SCSI_SYM53C8XX_2", "SCSI_SYM53C8XX", \
|
||||
"SCSI_NCR53C8XX", "SCSI_GENERIC_NCR5380"))
|
||||
PRIORITY(("DE2104X", "TULIP"))
|
||||
|
||||
## End of probe logic.
|
||||
##
|
||||
########################################################################
|
||||
|
||||
# More tests that don't fit the rulesfile format
|
||||
|
||||
# Filesystem, bus, and controller for root cannot be modules.
|
||||
sys.stderr.write(lang["ROOTLOOK"])
|
||||
fstab_to_bus_map = {
|
||||
r"^/dev/sd" : ("SCSI",),
|
||||
r"^/dev/hd" : ("IDE",),
|
||||
r"\bnfs\b" : ("NFS_FS", "NFS_ROOT", "NET"),
|
||||
}
|
||||
ifp = open("/etc/mtab", "r")
|
||||
while 1:
|
||||
line = ifp.readline()
|
||||
if not line:
|
||||
break
|
||||
fields = line.split()
|
||||
mountpoint = fields[1]
|
||||
fstype = fields[2]
|
||||
if mountpoint == "/":
|
||||
# Figure out the drive type of the root partition.
|
||||
rootsymbols = []
|
||||
for (pattern, symbols) in fstab_to_bus_map.items():
|
||||
if re.compile(pattern).search(line):
|
||||
rootsymbols = list(symbols)
|
||||
if fsmap.has_key(fstype):
|
||||
rootsymbols.append(fsmap[fstype])
|
||||
if not rootsymbols:
|
||||
sys.stderr.write(lang["ROOTWARN"])
|
||||
break
|
||||
# We should have a list of `buses' now...
|
||||
for roottype in rootsymbols:
|
||||
# First we have to force the bus the drive is on to y.
|
||||
config.found(roottype, y, "Root filesystem")
|
||||
sys.stderr.write(lang["ROOTFS"] % roottype)
|
||||
# Then force all bootable hardware previously set modular and
|
||||
# dependent on this bus to y.
|
||||
bus = configuration.dictionary[roottype]
|
||||
for symbol in configuration.dictionary.values():
|
||||
if cml.evaluate(symbol) == m \
|
||||
and symbol.hasprop("BOOTABLE") \
|
||||
and bus.ancestor_of(symbol):
|
||||
config.found(symbol.name, y, "Root filesystem")
|
||||
sys.stderr.write(lang["ROOTHW"] % symbol.name)
|
||||
ifp.close()
|
||||
|
||||
# PTY devices
|
||||
ptycount = dmesg.find('pty: ([0-9]*) Unix98 ptys')
|
||||
if ptycount:
|
||||
config.found("UNIX98_PTY_COUNT", int(ptycount))
|
||||
|
||||
# Helper functions.
|
||||
|
||||
def grepcmd(pattern, cmd):
|
||||
"Test for PATTERN in the output of COMMAND."
|
||||
(status, output) = commands.getstatusoutput(cmd)
|
||||
return status == 0 and re.compile(pattern).search(output)
|
||||
|
||||
# Apply those sanity checks
|
||||
|
||||
# Handle a subtle gotcha: if there are multiple NICs, they must be modular.
|
||||
if grepcmd("eth[1-3]", "/sbin/ifconfig -a"):
|
||||
config.force_dependents_modular("NET_ETHERNET",
|
||||
"Multiple NICs must be modular")
|
||||
|
||||
# Now freeze complement sets. With any luck, this will reduce the
|
||||
# set of drivers the user actually has to specify to zero.
|
||||
#
|
||||
# Giacomo writes:
|
||||
# "BTW I have done some test with USB, and it seems that you can
|
||||
# hotplug USB devices, also with hardcored drivers, and the driver
|
||||
# is initialized only at the hotplug event.
|
||||
# (This mean that USB devices can be set also to 'y', without
|
||||
# losing functionality.
|
||||
# This is not true for other 'hotplug' devices. I.e. my
|
||||
# parport ZIP will be loaded only at boot time (hardcoded) or
|
||||
# at modules loading (module)."
|
||||
#
|
||||
# So far I have not done anything about this.
|
||||
if not hardcompile:
|
||||
b = cml.Baton(lang["COMPLEMENT"])
|
||||
config.complement("HOTPLUG_PCI",cml.m, b, "PCI_HOTPLUG is a hot-plug bus")
|
||||
config.complement("USB", cml.m, b, "USB is a hot-plug bus")
|
||||
config.complement("PCMCIA", cml.m, b, "PCMCIA is a hot-plug bus")
|
||||
config.complement("IEEE1394", cml.m, b, "IEEE1394 ia a hot-plug bus")
|
||||
b.end(lang["DONE"])
|
||||
|
||||
DEBUG(lang["COMPLETE"])
|
||||
|
||||
def process_define(myconfiguration, val, freeze):
|
||||
"Process a -d=xxx or -D=xxx option."
|
||||
parts = val.split("=")
|
||||
sym = parts[0]
|
||||
if myconfiguration.dictionary.has_key(sym):
|
||||
sym = myconfiguration.dictionary[sym]
|
||||
else:
|
||||
myconfiguration.errout.write(lang["SYMUNKNOWN"] % (`sym`,))
|
||||
sys.exit(1)
|
||||
if sym.is_derived():
|
||||
myconfiguration.debug_emit(1, lang["DERIVED"] % (`sym`,))
|
||||
sys.exit(1)
|
||||
elif sym.is_logical():
|
||||
if len(parts) == 1:
|
||||
val = 'y'
|
||||
elif parts[1] == 'y':
|
||||
val = 'y'
|
||||
elif parts[1] == 'm':
|
||||
myconfiguration.trits_enabled = 1
|
||||
val = 'm'
|
||||
elif parts[1] == 'n':
|
||||
val = 'n'
|
||||
elif len(parts) == 1:
|
||||
print lang["NOCMDLINE"] % (`sym`,)
|
||||
sys.exit(1)
|
||||
else:
|
||||
val = parts[1]
|
||||
(ok, effects, violation) = myconfiguration.set_symbol(sym,
|
||||
myconfiguration.value_from_string(sym, val),
|
||||
freeze)
|
||||
if effects:
|
||||
sys.stderr.write(lang["EFFECTS"] + "\n")
|
||||
sys.stderr.write("\n".join(effects) + "\n\n")
|
||||
if not ok:
|
||||
sys.stderr.write((lang["ROLLBACK"] % (sym.name, val)) + "\n")
|
||||
sys.stderr.write("\n".join(violation)+"\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Process command-line options
|
||||
try:
|
||||
(options, arguments) = getopt.getopt(sys.argv[1:], "d:D:hr:st:v",
|
||||
("hardcompile",
|
||||
"rules=",
|
||||
"standalone",
|
||||
"target=",
|
||||
"verbose"))
|
||||
except getopt.GetoptError:
|
||||
sys.stderr.write(lang["OPTUNKNOWN"])
|
||||
raise SystemExit, 2
|
||||
|
||||
autoprobe_debug = hardcompile = standalone = 0
|
||||
objtree = os.environ.get("KBUILD_OBJTREE")
|
||||
rulesfile = "autoconfigure.rules"
|
||||
freeze_em = []
|
||||
set_em = []
|
||||
|
||||
for (opt, val) in options:
|
||||
if opt == '-D':
|
||||
freeze_em.append(val)
|
||||
elif opt == '-d':
|
||||
set_em.append(val)
|
||||
elif opt in ("-v", "--verbose"):
|
||||
autoprobe_debug += 1
|
||||
elif opt in ("--hardcompile", "-h"):
|
||||
hardcompile = 1
|
||||
elif opt in ("--rules", "-r"):
|
||||
rulesfile = val
|
||||
elif opt in ("--standalone", "-s"):
|
||||
standalone = 1
|
||||
elif opt in ("--target", "-t"):
|
||||
objtree = os.path.expanduser(val)
|
||||
|
||||
if objtree == None:
|
||||
objtree = "."
|
||||
|
||||
#
|
||||
# Now use the rulebase information
|
||||
#
|
||||
rulebase = os.path.join(objtree, "rules.out")
|
||||
if not os.path.exists(rulebase):
|
||||
sys.stderr.write("autoconfigure: rulebase %s does not exist!\n" % rulebase)
|
||||
raise SystemExit, 1
|
||||
configuration = cmlsystem.CMLSystem(rulebase)
|
||||
if not cmlsystem:
|
||||
sys.stderr.write("autoconfigure: rulebase %s could not be read!\n" % rulebase)
|
||||
raise SystemExit, 1
|
||||
|
||||
# Autoconfigure into the configuration object.
|
||||
for sym in freeze_em:
|
||||
process_define(configuration, sym, 1)
|
||||
for sym in set_em:
|
||||
process_define(configuration, sym, 0)
|
||||
autoconfigure(configuration, hardcompile, autoprobe_debug)
|
||||
|
||||
# Write out this configuration, we're done.
|
||||
if standalone:
|
||||
configuration.save(sys.stdout, None, "normal")
|
||||
else:
|
||||
configuration.save(sys.stdout, None, "probe")
|
||||
|
||||
# End
|
||||
438
tools/cml2-tools/cml.py
Executable file
438
tools/cml2-tools/cml.py
Executable file
@@ -0,0 +1,438 @@
|
||||
"""
|
||||
cml.py -- types for communication between CML2 compiler and configurators.
|
||||
"""
|
||||
import sys, os, time
|
||||
|
||||
version="2.3.0"
|
||||
|
||||
class trit:
|
||||
"A boolean or trit value"
|
||||
type = "trit"
|
||||
def __init__(self, value):
|
||||
if isinstance(value, trit):
|
||||
value = value.value
|
||||
self.value = value
|
||||
def __repr__(self):
|
||||
return "nmy"[self.value]
|
||||
def __nonzero__(self):
|
||||
return self.value
|
||||
def __hash__(self):
|
||||
return self.value # This magic needed to make trits valid dictionary keys
|
||||
def __long__(self):
|
||||
return self.value != 0
|
||||
def __cmp__(self, other):
|
||||
if not isinstance(other, trit):
|
||||
if other is None:
|
||||
return 1 # any trit > None
|
||||
else: # Standard no-__cmp__ behavior=20
|
||||
if id(self) < id(other):
|
||||
return -1
|
||||
elif id(self) > id(other):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
else:
|
||||
diff = self.value - other.value
|
||||
if diff == 0:
|
||||
return 0
|
||||
else:
|
||||
return diff / abs(diff)
|
||||
def __and__(self, other):
|
||||
return trit(min(self.value, other.value))
|
||||
def __or__(self, other):
|
||||
return trit(max(self.value, other.value))
|
||||
def eval(self):
|
||||
return self
|
||||
|
||||
# Trit value constants
|
||||
y = trit(2)
|
||||
m = trit(1)
|
||||
n = trit(0)
|
||||
|
||||
# This describes a configuration symbol...
|
||||
|
||||
class ConfigSymbol:
|
||||
"Compiled information about a menu or configuration symbol"
|
||||
def __init__(self, name, type=None, default=None, prompt=None, file=None, lineno=None):
|
||||
# Name, location, type, default.
|
||||
self.name = name
|
||||
self.file = file # Definition location source file
|
||||
self.lineno = lineno # Definition location source line
|
||||
self.type = type # Type of symbol
|
||||
self.range = None # Range tuple
|
||||
self.enum = None
|
||||
self.discrete = None
|
||||
self.helptext = None # Help reference
|
||||
self.default = default # Value to use if none has been set.
|
||||
# Hierarchy location
|
||||
self.ancestors = [] # Ancestors of symbol (as set up by {})
|
||||
self.dependents = [] # Dependents of symbol (as set up by {})
|
||||
self.choicegroup = [] # Other symbols in a choicegroup.
|
||||
self.menu = None # Unique parent menu of this symbol
|
||||
self.depth = 0 # Nesting depth in its subtree
|
||||
# Auxiliary information
|
||||
self.prompt = prompt # Associated question string
|
||||
self.properties = {} # Associated properties
|
||||
self.warnings = [] # Attached warndepend conditions
|
||||
self.visibility = None # Visibility predicate for symbol
|
||||
self.saveability = None # Saveability predicate for symbol
|
||||
self.items = [] # Menus only -- associated symbols
|
||||
# Compiler never touches these
|
||||
self.visits = 0 # Number of visits so far
|
||||
self.setcount = 0 # Should this symbol be written?
|
||||
self.included = 0 # Seen in an inclusion?
|
||||
self.inspected = 0 # Track menu inspections
|
||||
self.iced = 0 # Is this frozen?
|
||||
|
||||
# Compute the value of a symbol
|
||||
def eval(self, debug=0):
|
||||
"Value of symbol; passes back None if the symbol is unset."
|
||||
if self.default is not None:
|
||||
result = evaluate(self.default, debug)
|
||||
# Handle casting. This can matter in derivations
|
||||
if self.type == "bool":
|
||||
if isinstance(result, trit):
|
||||
result = trit(y.value * (result != n))
|
||||
elif type(result) == type(0):
|
||||
result = trit(y.value * (result != 0))
|
||||
elif self.type in ("decimal", "hexadecimal"):
|
||||
if isinstance(result, trit):
|
||||
result = (result != n)
|
||||
if debug > 3:
|
||||
sys.stderr.write("...eval(%s)->%s (through default %s)\n" % \
|
||||
(`self`, result, self.default))
|
||||
return result
|
||||
else:
|
||||
if debug > 2:
|
||||
sys.stderr.write("...eval(%s)->None (default empty)\n" % \
|
||||
(`self`))
|
||||
return None
|
||||
|
||||
# Access to help.
|
||||
#
|
||||
# This is the only place in the front end that knows about the CML1
|
||||
# helpfile conventions.
|
||||
def help(self):
|
||||
"Is there help for the given symbol?"
|
||||
if self.helptext:
|
||||
return self.helptext
|
||||
# Next five lines implement the CML1 convention for choices help;
|
||||
# attach it to the first alternative. But they check for help
|
||||
# attached to the symbol itself first.
|
||||
if self.menu and self.menu.type == "choices":
|
||||
self = self.menu
|
||||
if self.type == "choices" and not self.helptext:
|
||||
self = self.items[0]
|
||||
return self.helptext
|
||||
|
||||
def ancestor_of(self, entry):
|
||||
"Test transitive completion of dependency."
|
||||
# We don't also check visibility, because visibility guards can have
|
||||
# disjunctions and it would be wrong to propagate up both branches.
|
||||
if entry.menu:
|
||||
searchpath = entry.ancestors + [entry.menu]
|
||||
else:
|
||||
searchpath = entry.ancestors
|
||||
if self in searchpath:
|
||||
return 1
|
||||
for x in searchpath:
|
||||
if self.ancestor_of(x):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# Predicates
|
||||
def is_derived(self):
|
||||
"Is this a derived symbol?"
|
||||
return self.prompt is None
|
||||
def is_logical(self):
|
||||
"Is this a logical symbol?"
|
||||
return self.type in ("bool", "trit")
|
||||
def is_numeric(self):
|
||||
"Is this a numeric symbol?"
|
||||
return self.type in ("decimal", "hexadecimal")
|
||||
def is_symbol(self):
|
||||
"Is this a real symbol? (not a menu, not a choices, not a message)"
|
||||
return self.type in ("bool","trit", "decimal","hexadecimal", "string")
|
||||
|
||||
# Property functions
|
||||
def hasprop(self, prop):
|
||||
return self.properties.has_key(prop)
|
||||
def setprop(self, prop, val=1):
|
||||
self.properties[prop] = val
|
||||
def delprop(self, prop):
|
||||
del self.properties[prop]
|
||||
def showprops(self,):
|
||||
return ", ".join(self.properties.keys())
|
||||
|
||||
def __repr__(self):
|
||||
# So the right thing happens when we print symbols in expressions
|
||||
return self.name
|
||||
def dump(self):
|
||||
if self.prompt:
|
||||
res = "'%s'" % self.prompt
|
||||
else:
|
||||
res = "derived"
|
||||
res += ", type %s," % self.type
|
||||
if self.range:
|
||||
res = res + " range %s," % (self.range,)
|
||||
if self.menu:
|
||||
res = res + " in %s," % (self.menu.name,)
|
||||
if self.ancestors:
|
||||
res = res + " under %s," % (self.ancestors,)
|
||||
if self.dependents:
|
||||
res = res + " over %s," % (self.dependents,)
|
||||
if self.choicegroup:
|
||||
res = res + " choicegroup %s," % (self.choicegroup,)
|
||||
if self.visibility is not None:
|
||||
res = res + " visibility %s," % (display_expression(self.visibility),)
|
||||
if self.saveability is not None:
|
||||
res = res + " saveability %s," % (display_expression(self.saveability),)
|
||||
if self.default is not None:
|
||||
res = res + " default %s," % (`self.default`,)
|
||||
if self.items:
|
||||
res = res + " items %s," % (self.items,)
|
||||
if self.properties:
|
||||
res = res + " props=%s," % (self.showprops(),)
|
||||
if self.file and self.lineno is not None:
|
||||
res = res + " where=%s:%d," % (self.file, self.lineno)
|
||||
return res
|
||||
def __str__(self):
|
||||
# Note that requirements are not shown
|
||||
res = "%s={" % (self.name)
|
||||
res = res + self.dump()
|
||||
return res[:-1] + "}"
|
||||
|
||||
class Requirement:
|
||||
"A requirement, together with a message to be shown if it's violated."
|
||||
def __init__(self, wff, message, file, line):
|
||||
self.predicate = wff
|
||||
self.message = message
|
||||
self.file = file
|
||||
self.line = line
|
||||
|
||||
def str(self):
|
||||
return display_expression(self.predicate)[1:-1]
|
||||
|
||||
def __repr__(self):
|
||||
bindings = ""
|
||||
for sym in flatten_expr(self.predicate):
|
||||
bindings += "%s=%s, " % (sym.name, evaluate(sym))
|
||||
bindings = bindings[:-2]
|
||||
leader = '"%s", line %d: ' % (self.file, self.line)
|
||||
if self.message:
|
||||
return leader + self.message + " (" + bindings + ")"
|
||||
else:
|
||||
return leader + display_expression(self.predicate) + " (" + bindings + ")"
|
||||
|
||||
# This describes an entire configuration.
|
||||
|
||||
class CMLRulebase:
|
||||
"A dictionary of ConfigSymbols and a set of constraints."
|
||||
def __init__(self):
|
||||
self.version = version
|
||||
self.start = None # Start menu name
|
||||
self.dictionary = {} # Configuration symbols
|
||||
self.prefix = "" # Prepend this to all symbols
|
||||
self.banner = "" # ID the configuration domain
|
||||
self.constraints = [] # All requirements
|
||||
self.icon = None # Icon for this rulebase
|
||||
self.trit_tie = None # Are trits enabled?
|
||||
self.help_tie = None # Help required for visibility?
|
||||
self.expert_tie = None # Expert flag for UI control
|
||||
self.reduced = []
|
||||
def __repr__(self):
|
||||
res = "Start menu = %s\n" % (self.start,)
|
||||
for k in self.dictionary.keys():
|
||||
res = res + str(self.dictionary[k]) + "\n"
|
||||
if self.prefix:
|
||||
res = res + "Prefix:" + `self.prefix`
|
||||
if self.banner:
|
||||
res = res + "Banner:" + `self.banner`
|
||||
return res
|
||||
def optimize_constraint_access(self):
|
||||
"Assign constraints to their associated symbols."
|
||||
for entry in self.dictionary.values():
|
||||
entry.constraints = []
|
||||
for requirement in self.reduced:
|
||||
for symbol in flatten_expr(requirement):
|
||||
if not requirement in symbol.constraints:
|
||||
symbol.constraints.append(requirement)
|
||||
|
||||
# These functions are used by both interpreter and compiler
|
||||
|
||||
def evaluate(exp, debug=0):
|
||||
"Compute current value of an expression."
|
||||
def tritify(x):
|
||||
if x:
|
||||
return y
|
||||
else:
|
||||
return n
|
||||
if debug > 2:
|
||||
sys.stderr.write("evaluate(%s) begins...\n" % (`exp`,))
|
||||
if type(exp) is type(()):
|
||||
# Ternary operator
|
||||
if exp[0] == '?':
|
||||
guard = evaluate(exp[1], debug)
|
||||
if guard:
|
||||
return evaluate(exp[2], debug)
|
||||
else:
|
||||
return evaluate(exp[3], debug)
|
||||
# Logical operations -- always trit-valued
|
||||
elif exp[0] == 'not':
|
||||
return tritify(not evaluate(exp[1], debug))
|
||||
elif exp[0] == 'or':
|
||||
return tritify(evaluate(exp[1], debug) or evaluate(exp[2], debug))
|
||||
elif exp[0] == 'and':
|
||||
return tritify(evaluate(exp[1], debug) and evaluate(exp[2], debug))
|
||||
elif exp[0] == 'implies':
|
||||
return tritify(not ((evaluate(exp[1], debug) and not evaluate(exp[2], debug))))
|
||||
elif exp[0] == '==':
|
||||
return tritify(evaluate(exp[1], debug) == evaluate(exp[2], debug))
|
||||
elif exp[0] == '!=':
|
||||
return tritify(evaluate(exp[1], debug) != evaluate(exp[2], debug))
|
||||
elif exp[0] == '<=':
|
||||
return tritify(evaluate(exp[1], debug) <= evaluate(exp[2], debug))
|
||||
elif exp[0] == '>=':
|
||||
return tritify(evaluate(exp[1], debug) >= evaluate(exp[2], debug))
|
||||
elif exp[0] == '<':
|
||||
return tritify(evaluate(exp[1], debug) < evaluate(exp[2], debug))
|
||||
elif exp[0] == '>':
|
||||
return tritify(evaluate(exp[1], debug) > evaluate(exp[2], debug))
|
||||
# Arithmetic operations -- sometimes trit-valued
|
||||
elif exp[0] == '|':
|
||||
return evaluate(exp[1], debug) | evaluate(exp[2], debug)
|
||||
elif exp[0] == '&':
|
||||
return evaluate(exp[1], debug) & evaluate(exp[2], debug)
|
||||
elif exp[0] == '$':
|
||||
left = evaluate(exp[1])
|
||||
right = evaluate(exp[2])
|
||||
if left != right:
|
||||
return n
|
||||
else:
|
||||
return left
|
||||
elif exp[0] == '+':
|
||||
return long(evaluate(exp[1],debug)) + long(evaluate(exp[2],debug))
|
||||
elif exp[0] == '-':
|
||||
return long(evaluate(exp[1],debug)) - long(evaluate(exp[2],debug))
|
||||
elif exp[0] == '*':
|
||||
return long(evaluate(exp[1],debug)) * long(evaluate(exp[2],debug))
|
||||
else:
|
||||
raise SyntaxError, "Unknown operation %s in expression" % (exp[0],)
|
||||
elif isinstance(exp, trit) or type(exp) in (type(""), type(0), type(0L)):
|
||||
if debug > 2:
|
||||
sys.stderr.write("...evaluate(%s) returns itself\n" % (`exp`,))
|
||||
return exp
|
||||
elif isinstance(exp, ConfigSymbol):
|
||||
result = exp.eval(debug)
|
||||
if result:
|
||||
return result
|
||||
else:
|
||||
return n
|
||||
else:
|
||||
raise ValueError,"unknown object %s %s in expression" % (exp,type(exp))
|
||||
|
||||
def flatten_expr(node):
|
||||
"Flatten an expression -- skips the operators"
|
||||
if type(node) is type(()) or type(node) is type([]):
|
||||
sublists = map(flatten_expr, node)
|
||||
flattened = []
|
||||
for item in sublists:
|
||||
flattened = flattened + item
|
||||
return flattened
|
||||
elif isinstance(node, ConfigSymbol):
|
||||
if node.is_derived():
|
||||
return flatten_expr(node.default)
|
||||
else:
|
||||
return [node]
|
||||
else:
|
||||
return []
|
||||
|
||||
def display_expression(exp):
|
||||
"Display an expression in canonicalized infix form."
|
||||
if type(exp) is type(()):
|
||||
if exp[0] == "not":
|
||||
return "not " + display_expression(exp[1])
|
||||
elif exp[0] == '?':
|
||||
return "(%s ? %s : %s)" % (display_expression(exp[1]), display_expression(exp[2]), display_expression(exp[3]))
|
||||
else:
|
||||
return "(%s %s %s)" % (display_expression(exp[1]), exp[0], display_expression(exp[2]))
|
||||
elif isinstance(exp, ConfigSymbol):
|
||||
return exp.name
|
||||
else:
|
||||
return `exp`
|
||||
|
||||
class Baton:
|
||||
"Ship progress indication to stdout."
|
||||
def __init__(self, prompt, endmsg=None):
|
||||
if os.isatty(1):
|
||||
self.stream = sys.stdout
|
||||
elif os.isatty(2):
|
||||
self.stream = sys.stderr
|
||||
else:
|
||||
self.stream = None
|
||||
if self.stream:
|
||||
self.stream.write(prompt + "... \010")
|
||||
self.stream.flush()
|
||||
self.count = 0
|
||||
self.endmsg = endmsg
|
||||
self.time = time.time()
|
||||
return
|
||||
|
||||
def twirl(self, ch=None):
|
||||
if self.stream is None:
|
||||
return
|
||||
if ch:
|
||||
self.stream.write(ch)
|
||||
else:
|
||||
self.stream.write("-/|\\"[self.count % 4])
|
||||
self.stream.write("\010")
|
||||
self.count = self.count + 1
|
||||
self.stream.flush()
|
||||
return
|
||||
|
||||
def end(self, msg=None):
|
||||
if msg == None:
|
||||
msg = self.endmsg
|
||||
if self.stream:
|
||||
self.stream.write("...(%2.2f sec) %s.\n" % (time.time() - self.time, msg))
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Two classes without __cmp__
|
||||
class A:
|
||||
pass
|
||||
|
||||
class B:
|
||||
pass
|
||||
|
||||
a = A()
|
||||
b = B()
|
||||
|
||||
t0 = trit(0)
|
||||
t1 = trit(1)
|
||||
t2 = trit(2)
|
||||
|
||||
if not (t0 < t1 < t2 and t2 > t1 > t0) or t0 == t1 or t0 == t2 or t1 == t2:
|
||||
print "trit compare failed"
|
||||
|
||||
if t0 < None:
|
||||
print "a trit is less than None? Comparison failed"
|
||||
|
||||
if None > t0:
|
||||
print "None is greater than a trit? Comparison failed"
|
||||
|
||||
if id(a) > id(b):
|
||||
if a < b > a:
|
||||
print "a/b comparison failed"
|
||||
elif b < a > b:
|
||||
print "a/b comparison failed"
|
||||
|
||||
|
||||
# Simulate standard no-cmp() behavior for non-trits
|
||||
if id(a) > id(t0):
|
||||
if a < t0:
|
||||
print "a/t0 comparison failed (id(a) greater)"
|
||||
elif t0 < a:
|
||||
print "a/t0 comparison failed"
|
||||
|
||||
# cml.py ends here.
|
||||
BIN
tools/cml2-tools/cml.pyc
Normal file
BIN
tools/cml2-tools/cml.pyc
Normal file
Binary file not shown.
857
tools/cml2-tools/cmladvent.py
Executable file
857
tools/cml2-tools/cmladvent.py
Executable file
@@ -0,0 +1,857 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# cmladvent.py -- CML2 configurator adventure-game front end
|
||||
# by Eric S. Raymond, <esr@thyrsus.com>
|
||||
#
|
||||
# This illustrates how easy it is to wrap a front end around cmlsystem.
|
||||
# Purely in the interests of science, of course...
|
||||
#
|
||||
import sys
|
||||
|
||||
if sys.version[0] < '2':
|
||||
print "Python 2.0 or later is required for this program."
|
||||
sys.exit(0)
|
||||
|
||||
import os, string, getopt, cmd, time, whrandom, random
|
||||
import cml, cmlsystem
|
||||
|
||||
# Globals
|
||||
debug = 0
|
||||
proflog = partialsave = None
|
||||
banner = ""
|
||||
gruestate = darkturns = 0
|
||||
lanternloc = None
|
||||
configfile = None
|
||||
configuration = None
|
||||
|
||||
directions = ('n','e','s','w','ne','sw','se','nw','dn','up')
|
||||
|
||||
# User-visible strings in the configurator. Separated out in order to
|
||||
# support internationalization.
|
||||
_eng = {
|
||||
# Strings used in the command help -- these should align
|
||||
"LHELP":"look [target] -- look here or at target (direction or option).",
|
||||
"NHELP":"nearby -- list nearby rooms (useful with go)",
|
||||
"GHELP":"go -- go to a named menu (follow with the label).",
|
||||
"IHELP":"inventory -- show which options you have picked up.",
|
||||
"THELP":"take [module] -- set options, follow with option names.",
|
||||
"SETHELP":"set -- set numeric or string; follow with symbol and value.",
|
||||
"DHELP":"drop -- unset options, follow with option names or `all'.",
|
||||
"LDHELP":"load -- read in a configuration (follow with the filename).",
|
||||
"SHELP":"save -- save the configuration (follow with a filename).",
|
||||
"XYZZY":"xyzzy -- toggle suppression flag.",
|
||||
"QHELP":"quit -- quit, discarding changes.",
|
||||
"XHELP":"exit -- exit, saving the configuration.",
|
||||
# Grue/lantern messages
|
||||
"BRASSOFF":"A brass lantern (unlit).",
|
||||
"BRASSON":"A brass lantern (lit).",
|
||||
"DARK":"It is very dark. If you continue, you are likely to be eaten by a grue.",
|
||||
"EATEN":"*CHOMP*! You have been eaten by a slavering grue. Game over.",
|
||||
"GLOW":"The lantern radiates a mellow golden light.",
|
||||
"LANTERN":"lantern",
|
||||
"LANTERNDROP":"Lantern: dropped.",
|
||||
"LANTERNTAKE":"Lantern: taken.",
|
||||
"LONGLANTERN":"A brass lantern is here.",
|
||||
"LANTERNHELP":"""
|
||||
You see a brass lantern with a ring-shaped handle, hooded and paned with
|
||||
clear glass. A toggle on the lamp connects to a firestriker inside it.
|
||||
On the bottom is stamped a maker's mark that reads:
|
||||
|
||||
Another fine product of FrobozzCo.
|
||||
Made in Plumbat, Great Underground Empire
|
||||
""",
|
||||
# Other strings
|
||||
"ABORTED":"Configurator aborted.",
|
||||
"BADOPTION":"cmladvent: unknown option on command line.\n",
|
||||
"BOOLEAN":"`y' and `n' can only be applied to booleans or tristates",
|
||||
"CANNOTSET":" Can't assign this value for bool or trit symbol.",
|
||||
"CONSTRAINTS":"Constraints:",
|
||||
"DEFAULT":"Default: ",
|
||||
"DERIVED":"Symbol %s is derived and cannot be set.",
|
||||
"DIRHELP":"You can move in compass directions n,e,w,s,ne,nw,se,sw, up, or dn for down.",
|
||||
"DONE":"Done",
|
||||
"DROPPED":"%s: dropped.",
|
||||
"EFFECTS":"Side effects:",
|
||||
"EH?":"Eh?",
|
||||
"EXIT":"Exit",
|
||||
"EXITS":"Passages exit up, %s.",
|
||||
"EXTRAROOMS":"Other nearby rooms are: %s.",
|
||||
"GOODBYE":"You emerge, blinking, into the daylight.",
|
||||
"INROOM":"In %s room.",
|
||||
"INVISIBLE":"Symbol is invisible",
|
||||
"ISHERE":"There is an option named %s here.",
|
||||
"LOADFAIL":"Loading '%s' failed, continuing...",
|
||||
"MDISABLED":"Module-valued symbols are not enabled",
|
||||
"MNOTVALID":" m is not a valid value for %s",
|
||||
"NEW":"(NEW)",
|
||||
"NNOTVALID":" n is not a valid value for %s",
|
||||
"NOANCEST":"No ancestors.",
|
||||
"NOBUTTON":"I don't see button %s here.",
|
||||
"NOCMDLINE":"%s is the wrong type to be set from the command line",
|
||||
"NODEPS":"No dependents.",
|
||||
"NODIR":"You see nothing special in that direction.",
|
||||
"NOFILE":"cmlconfigure: '%s' does not exist or is unreadable.",
|
||||
"NOHAVE":"You don't have %s.",
|
||||
"NOHELP":"No help available for %s",
|
||||
"NOHERE":"I see no `%s' here.",
|
||||
"NOMATCHES":"No matches.",
|
||||
"NONEXIST":"No such location.",
|
||||
"NOSUCHAS":"No such thing as",
|
||||
"NOTSAVED":"Configuration not saved",
|
||||
"NOWAY":"You can't go in that direction from here.",
|
||||
"OUTOFBOUNDS":"Legal values are in %s",
|
||||
"PARAMS":" Config = %s, prefix = %s",
|
||||
"PASSAGEALL":"Passages lead off in all directions.",
|
||||
"PASSAGEUP":"A passage leads upwards.",
|
||||
"PHELP":"press -- press a button (follow with the button name).",
|
||||
"POSTMORTEM":"The ruleset was inconsistent. A state dump is in the file `post.mortem'.",
|
||||
"REALLY":"Really exit without saving?",
|
||||
"ROLLBACK":"%s=%s would have violated these requirements:",
|
||||
"ROOMBANNER":"The %s room. A sign reads `%s'.",
|
||||
"SAVEAS":"Save As...",
|
||||
"SAVEEND":"Done",
|
||||
"SAVESTART":"Saving %s",
|
||||
"SAVING":"Saving...",
|
||||
"SHOW_ANC":"Show ancestors of symbol: ",
|
||||
"SHOW_DEP":"Show dependents of symbol: ",
|
||||
"SIDEEFFECTS":"Side Effects",
|
||||
"SIDEFROM":"Side effects from %s:",
|
||||
"SUPPRESSOFF":"Suppression turned off.",
|
||||
"SUPPRESSON":"Suppression turned on.",
|
||||
"SYMUNKNOWN":"cmlconfigure: unknown symbol %s\n",
|
||||
"TAKEN":"%s: taken.",
|
||||
"TRIT":"`m' can only be applied to tristates",
|
||||
"TRYPRESS":"That doesn't work. You might try pressing another button.",
|
||||
"TWISTY":"You are in a maze of twisty little %s menus, all different.",
|
||||
"USESET":"What? Configure %s with your bare hands?",
|
||||
"VALUE":"Value of %s is %s.",
|
||||
"VISIBLE":"Symbol is visible.",
|
||||
"VISIBILITY":"Visibility: ",
|
||||
"WALLCHOICE":"There is a row of buttons on the wall of this room. They read:",
|
||||
"WALLDEFAULT":"The button marked %s is pressed.",
|
||||
"WELCOME":"Welcome to CML2 Adventure, version %s.",
|
||||
# General help
|
||||
"GENHELP":"""Welcome to the adventure configurator. For a command summary, type `commands'.
|
||||
In general, a three-letter abbreviation of any command word is sufficient
|
||||
to identify it to the parser.
|
||||
|
||||
This interface emulates the style of classic text adventure games such as
|
||||
Colossal Cave Adventure and Zork. Configuration menus are rooms, and
|
||||
configuration options are objects that can be taken and dropped (except
|
||||
for choice/radiobutton symbols, which become buttons on various room walls).
|
||||
Objects and rooms may silently appear and disappear as visibilities
|
||||
change.
|
||||
|
||||
Have fun, and beware of the lurking grue!
|
||||
"""
|
||||
}
|
||||
|
||||
grafitti = (
|
||||
'N tensvggb ernqf: "Gur Jhzchf jnf urer. Nera\'g lbh tynq ur\'f abg urer abj?"',
|
||||
'N tensvggb ernqf: "Uryyb, fnvybe!"',
|
||||
'N tensvggb ernqf: "Sebqb yvirf!"',
|
||||
'N tensvggb ernqf: "Guvf fcnpr sbe erag."',
|
||||
'N tensvggb ernqf: "Guvf Jnl gb gur Rterff..."',
|
||||
# Bofpher Pbybffny Pnir Nqiragher ersreraprf ortva urer.
|
||||
'Ba bar jnyy vf n tynff-sebagrq obk ubyqvat na nkr.\aBa gur tynff vf jevggra: "OERNX TYNFF VA PNFR BS QJNEIRF"',
|
||||
'N tensvggb ernqf: "Srr svr sbr sbb!',
|
||||
# Bofpher Mbex ersreraprf ortva urer.
|
||||
'N tensvggb ernqf: "Ragunevba gur Jvfr fyrcg urer."',
|
||||
'N tensvggb ernqf: "N mbexzvq fnirq vf n mbexzvq rnearq."',
|
||||
'Bar jnyy qvfcynlf n sbezny cbegenvg bs W. Cvrecbag Syngurnq.',
|
||||
'Bar jnyy qvfcynlf n qhfgl cbegenvg bs gur Rzcrebe Zhzob VV.',
|
||||
'Bar jnyy qvfcynlf n cvpgher bs gur terng tenabyn fzrygref bs Cyhzong.',
|
||||
'Bar jnyy qvfcynlf n gnpxl oynpx-iryirg cnvagvat bs n tbyqra-sheerq zvak jvgu uhtr rlrf.',
|
||||
# Bofpher Q&Q ersreraprf ortva urer
|
||||
'N tensvggb ernqf: "Vg pbhyq bayl or orggre ng Pnfgyr Terlunjx"',
|
||||
'N tensvggb ernqf: "Cnenylfvf vf va gur rlr bs gur orubyqre"',
|
||||
# Bofpher wbxr sbe QrPnzc/Cengg snaf
|
||||
'N tensvggb ernqf: "Lativ vf n ybhfr!"',
|
||||
# Abg-fb-bofpher Yvahk ersreraprf ortva urer.
|
||||
'Ba bar jnyy vf n cubgbtencu bs Yvahf Gbeinyqf, qevaxvat Thvaarff.',
|
||||
'N jnyy oenpxrg ubyqf n qvfpneqrq cnve bs Nyna Pbk\'f fhatynffrf. Oebamrq.',
|
||||
'Ba bar jnyy vf n cbegenvg bs EZF va shyy Fg. Vtahpvhf qent.',
|
||||
'Ba bar jnyy vf n cvpgher bs Yneel Jnyy ubyqvat n ynetr chzcxva.',
|
||||
'Ba bar jnyy vf jung nccrnef gb or n cubgbtencu bs Thvqb\'f gvzr znpuvar.',
|
||||
'Gur sybbe vf yvggrerq jvgu fcrag .45 furyyf. Revp Enlzbaq zhfg unir orra urer.',
|
||||
)
|
||||
grafittishuffle = []
|
||||
grafitticount = 0
|
||||
|
||||
# Eventually, do more intelligent selection using LOCALE
|
||||
lang = _eng
|
||||
|
||||
def roll(n):
|
||||
"Return a random number in the range 0..n-1."
|
||||
return random.randrange(n)
|
||||
|
||||
def shuffle(size):
|
||||
"Generate a random permutation of 0...(size - 1)."
|
||||
shuffle = range(size)
|
||||
for i in range(1, size+1):
|
||||
j = random.randrange(i)
|
||||
holder = shuffle[i - 1]
|
||||
shuffle[i - 1] = shuffle[j]
|
||||
shuffle[j] = holder
|
||||
return shuffle
|
||||
|
||||
def rot13(str):
|
||||
res = ""
|
||||
for c in str:
|
||||
if c in string.uppercase:
|
||||
res += chr(ord('A') + ((ord(c)-ord('A')) + 13) % 26)
|
||||
elif c in string.lowercase:
|
||||
res += chr(ord('a') + ((ord(c)-ord('a')) + 13) % 26)
|
||||
else:
|
||||
res += c
|
||||
return res
|
||||
|
||||
def newroom(room):
|
||||
# There is a chance of grafitti
|
||||
global grafitticount, grafittishuffle
|
||||
if grafitticount < len(grafitti):
|
||||
if not hasattr(room, "visits") and roll(3) == 0:
|
||||
room.grafitti = grafitti[grafittishuffle[grafitticount]]
|
||||
grafitticount += 1
|
||||
# State machine for lantern and grue
|
||||
global lanternloc, gruestate, darkturns
|
||||
if gruestate == 0: # Initial state
|
||||
if not hasattr(room, "visits") and roll(4) == 0:
|
||||
gruestate += 1
|
||||
lanternloc = room
|
||||
elif gruestate == 1: # Lantern has been placed
|
||||
if roll(4) == 0:
|
||||
gruestate += 1
|
||||
elif gruestate == 2: # It's dark now
|
||||
darkturns += 1
|
||||
if darkturns > 2 and roll(4) == 0:
|
||||
print lang["EATEN"]
|
||||
raise SystemExit
|
||||
|
||||
def visit(room, level=0):
|
||||
"Visit a room, and describe at any of four verbosity levels."
|
||||
# 0 = quiet, 1 = name only, 2 = name + help,
|
||||
# 3 = name + help + exits, 4 = name + help + exits + contents
|
||||
configuration.visit(room)
|
||||
# Compute visible exits
|
||||
room.exits = filter(lambda x: x.type in ("menu", "choices"), room.items)
|
||||
room.exits = filter(configuration.is_visible, room.exits)
|
||||
# This way of assigning directions has the defect that they may
|
||||
# change as submenus become visible/invisible. Unfortunately,
|
||||
# the alternative is not being able to assign directions at all
|
||||
# for long menus.
|
||||
room.directions = {}
|
||||
for (dir,other) in zip(directions[:-1], room.exits):
|
||||
room.directions[dir] = other
|
||||
if level == 0:
|
||||
return
|
||||
elif level == 1:
|
||||
print lang["INROOM"] % room.name
|
||||
else:
|
||||
print lang["ROOMBANNER"] % (room.name, room.prompt)
|
||||
# Only display room exits at level 3 or up
|
||||
if level >= 3:
|
||||
if len(room.exits) > 9:
|
||||
print lang["PASSAGEALL"]
|
||||
elif room.exits:
|
||||
print lang["EXITS"] % ", ".join(room.directions.keys())
|
||||
elif room != configuration.start:
|
||||
print lang["PASSAGEUP"]
|
||||
print
|
||||
# Display help at level 2 or up
|
||||
help = room.help()
|
||||
if help:
|
||||
sys.stdout.write(help)
|
||||
# Display grafitti at level 2 or up.
|
||||
if hasattr(room, "grafitti"):
|
||||
print rot13(room.grafitti) + "\n"
|
||||
# Only display other contents of room at level 4 or up
|
||||
if level >= 4:
|
||||
if room.type == "choices":
|
||||
print lang["WALLCHOICE"]
|
||||
print ", ".join(map(lambda x:x.name, room.items))
|
||||
print lang["WALLDEFAULT"] % room.menuvalue.name
|
||||
else:
|
||||
for symbol in room.items:
|
||||
if symbol.is_symbol() and configuration.is_visible(symbol) and not symbol.eval():
|
||||
print lang["ISHERE"] % symbol.name
|
||||
# Some things are always shown
|
||||
if lanternloc == room:
|
||||
print lang["LONGLANTERN"]
|
||||
if gruestate == 2:
|
||||
print lang["DARK"]
|
||||
|
||||
def inventory():
|
||||
# Write mutable symbols, including defaulted modular symbols.
|
||||
configuration.module_suppress = 0
|
||||
if lanternloc == 'user':
|
||||
if gruestate == 3:
|
||||
print lang["BRASSON"]
|
||||
else:
|
||||
print lang["BRASSOFF"]
|
||||
__inventory_recurse(configuration.start)
|
||||
if configuration.trit_tie:
|
||||
configuration.module_suppress = (configuration.trit_tie.eval() == cml.m)
|
||||
# Write all derived symbols
|
||||
#config_sh.write(configuration.lang["SHDERIVED"])
|
||||
#for entry in configuration.dictionary.values():
|
||||
# if entry.is_derived():
|
||||
# __inventory_recurse(entry, config_sh)
|
||||
|
||||
def __inventory_recurse(node):
|
||||
if not configuration.saveable(node):
|
||||
return
|
||||
elif node.items:
|
||||
for child in node.items:
|
||||
__inventory_recurse(child)
|
||||
elif node.type != 'message':
|
||||
symname = configuration.prefix + node.name
|
||||
value = node.eval(configuration.debug)
|
||||
if not value or not node.setcount:
|
||||
return
|
||||
try:
|
||||
if node.type == "decimal":
|
||||
sys.stdout.write("%s=%d\n" % (symname, value))
|
||||
elif node.type == "hexadecimal":
|
||||
sys.stdout.write("%s=0x%x\n" % (symname, value))
|
||||
elif node.type == "string":
|
||||
sys.stdout.write("%s=\"%s\"\n" % (symname, value))
|
||||
elif node.type in ("bool", "trit"):
|
||||
sys.stdout.write("%s=%s\n" % (symname, `value`))
|
||||
except:
|
||||
(errtype, errval, errtrace) = sys.exc_info()
|
||||
print "Internal error %s while writing %s." % (errtype, node)
|
||||
raise SystemExit, 1
|
||||
|
||||
class advent_menu(cmd.Cmd):
|
||||
"Adventure-game interface class."
|
||||
|
||||
def set_symbol(self, symbol, value, freeze=0):
|
||||
"Set the value of a symbol -- line-oriented error messages."
|
||||
if symbol.is_numeric() and symbol.range:
|
||||
if not configuration.range_check(symbol, value):
|
||||
print lang["OUTOFBOUNDS"] % (symbol.range,)
|
||||
return 0
|
||||
(ok, effects, violations) = configuration.set_symbol(symbol, value, freeze)
|
||||
if effects:
|
||||
print lang["EFFECTS"]
|
||||
sys.stdout.write(string.join(effects, "\n") + "\n\n")
|
||||
if not ok:
|
||||
print lang["ROLLBACK"] % (symbol.name, value)
|
||||
sys.stdout.write(string.join(violations, "\n") + "\n")
|
||||
return ok
|
||||
|
||||
def __init__(self, myconfigfile=None, mybanner=""):
|
||||
cmd.Cmd.__init__(self)
|
||||
self.configfile = myconfigfile
|
||||
if mybanner and configuration.banner.find("%s") > -1:
|
||||
self.banner = configuration.banner % mybanner
|
||||
elif banner:
|
||||
self.banner = mybanner
|
||||
else:
|
||||
self.banner = configuration.banner
|
||||
self.current = configuration.start;
|
||||
self.prompt = "> "
|
||||
print lang["TWISTY"]%(configuration.banner,)
|
||||
self.last = None
|
||||
visit(configuration.start, 4)
|
||||
|
||||
def do_look(self, line):
|
||||
if not line: # Look at where we are
|
||||
visit(self.current, 4)
|
||||
elif line == "up": # Look up
|
||||
if self.current == configuration.start:
|
||||
print lang["NODIR"]
|
||||
else:
|
||||
visit(self.current.menu, 2)
|
||||
elif line in directions: # Look in a direction
|
||||
if line in self.current.directions.keys():
|
||||
visit(self.current.directions[line], 2)
|
||||
else:
|
||||
print lang["NODIR"]
|
||||
# Look at an option
|
||||
elif line in map(lambda x: x.name, filter(lambda x: x.is_logical(), self.current.items)):
|
||||
symbol = configuration.dictionary[line]
|
||||
print lang["VALUE"] % (line, symbol.eval())
|
||||
help = symbol.help()
|
||||
if help:
|
||||
sys.stdout.write(help)
|
||||
else:
|
||||
print lang["NOHERE"] % line
|
||||
do_loo = do_look
|
||||
|
||||
def do_nearby(self, dummy):
|
||||
if self.current != configuration.start:
|
||||
print lang["ROOMBANNER"] % (self.current.menu.name, self.current.menu.prompt)
|
||||
for (dir, symbol) in self.current.directions.items():
|
||||
if symbol.type in ("menu", "choices") and configuration.is_visible(symbol):
|
||||
print ("%-2s: " % dir) + lang["ROOMBANNER"] % (symbol.name, symbol.prompt)
|
||||
if len(self.current.exits) > len(directions):
|
||||
print lang["EXTRAROOMS"] % ", ".join(map(lambda x: x.name, self.current.exits[9:]))
|
||||
print
|
||||
do_nea = do_nearby
|
||||
|
||||
def do_go(self, symname):
|
||||
if not symname:
|
||||
print lang["EH?"]
|
||||
return
|
||||
symbol = configuration.dictionary.get(symname)
|
||||
if symbol and symbol.type in ("menu", "choices"):
|
||||
self.current = symbol
|
||||
if not configuration.is_visible(self.current) and not self.current.frozen():
|
||||
print lang["SUPPRESSOFF"]
|
||||
self.suppressions = 0
|
||||
else:
|
||||
print lang["NONEXIST"]
|
||||
|
||||
def do_dir(self, dir):
|
||||
to = self.current.directions.get(dir)
|
||||
if to:
|
||||
self.current = to
|
||||
else:
|
||||
print lang["NOWAY"]
|
||||
def do_n(self, dummy): self.do_dir('n')
|
||||
def do_e(self, dummy): self.do_dir('e')
|
||||
def do_w(self, dummy): self.do_dir('w')
|
||||
def do_s(self, dummy): self.do_dir('s')
|
||||
def do_ne(self, dummy): self.do_dir('ne')
|
||||
def do_nw(self, dummy): self.do_dir('nw')
|
||||
def do_se(self, dummy): self.do_dir('se')
|
||||
def do_sw(self, dummy): self.do_dir('sw')
|
||||
def do_u(self, dummy): self.do_up(dummy)
|
||||
def do_d(self, dummy): self.do_dir('dn')
|
||||
|
||||
def do_up(self, dummy):
|
||||
if self.current == configuration.start:
|
||||
print lang["GOODBYE"]
|
||||
raise SystemExit
|
||||
else:
|
||||
self.current = self.current.menu
|
||||
|
||||
def do_inventory(self, dummy):
|
||||
inventory()
|
||||
do_inv = do_inventory
|
||||
do_i = do_inventory
|
||||
|
||||
def do_drop(self, line):
|
||||
global lanternloc, gruestate
|
||||
if not line:
|
||||
print lang["EH?"]
|
||||
return
|
||||
words = line.lower().split()
|
||||
if words == ["all"] and self.current.type != "choices":
|
||||
words = map(lambda x:x.name, filter(lambda x:x.is_logical() and configuration.is_visible(x) and not x.eval(), self.current.items))
|
||||
if lanternloc == 'user':
|
||||
words.append(lang["LANTERN"])
|
||||
for thing in words:
|
||||
if thing == lang["LANTERN"]:
|
||||
lanternloc = self.current
|
||||
gruestate = 1
|
||||
print lang["LANTERNDROP"]
|
||||
else:
|
||||
symbolname = thing.upper()
|
||||
symbol = configuration.dictionary.get(symbolname)
|
||||
if not symbol:
|
||||
print lang["NOSUCHAS"], symbolname
|
||||
continue
|
||||
elif not symbol.eval():
|
||||
print lang["NOHAVE"] % symbolname
|
||||
continue
|
||||
elif symbol.menu.type == "choices":
|
||||
if symbol.menu != self.current:
|
||||
print lang["NOBUTTON"] % symbolname
|
||||
else:
|
||||
print lang["TRYPRESS"]
|
||||
return
|
||||
elif symbol.is_logical():
|
||||
ok = self.set_symbol(symbol, cml.n)
|
||||
elif symbol.is_numeric():
|
||||
ok = self.set_symbol(symbol, 0)
|
||||
elif symbol.type == "string":
|
||||
ok = self.set_symbol(symbol, "")
|
||||
if ok:
|
||||
print lang["DROPPED"] % symbol.name
|
||||
do_dro = do_drop
|
||||
|
||||
def do_take(self, line):
|
||||
global lanternloc
|
||||
if not line:
|
||||
print lang["EH?"]
|
||||
return
|
||||
words = line.lower().split()
|
||||
if words == ["all"] and self.current.type != "choices":
|
||||
words = map(lambda x:x.name, filter(lambda x:x.is_logical() and configuration.is_visible(x) and not x.eval(), self.current.items))
|
||||
if lanternloc == self.current:
|
||||
words.append(lang["LANTERN"])
|
||||
if ("module" in words):
|
||||
tritval = cml.m
|
||||
words.remove("module")
|
||||
else:
|
||||
tritval = cml.y
|
||||
for thing in words:
|
||||
if thing == lang["LANTERN"]:
|
||||
lanternloc = 'user'
|
||||
print lang["LANTERNTAKE"]
|
||||
else:
|
||||
symbolname = thing.upper()
|
||||
symbol = configuration.dictionary.get(symbolname)
|
||||
if not symbol:
|
||||
print lang["NOSUCHAS"], symbolname
|
||||
elif symbol.menu != self.current:
|
||||
print lang["NOHERE"] % symbol.name
|
||||
elif symbol.is_logical():
|
||||
if self.set_symbol(symbol, tritval):
|
||||
print lang["TAKEN"] % symbol.name
|
||||
else:
|
||||
print lang["USESET"] % symbol.name
|
||||
do_tak = do_take
|
||||
|
||||
def do_press(self, line):
|
||||
if not line:
|
||||
print lang["EH?"]
|
||||
else:
|
||||
symbol = configuration.dictionary.get(line)
|
||||
if not symbol or symbol.menu != self.current:
|
||||
print lang["NOHERE"] % line
|
||||
else:
|
||||
self.set_symbol(symbol, cml.y)
|
||||
do_pus = do_push = do_pre = do_press
|
||||
|
||||
def do_light(self, dummy):
|
||||
global gruestate
|
||||
if lanternloc == 'user':
|
||||
print lang["GLOW"]
|
||||
gruestate = 3
|
||||
else:
|
||||
print lang["NOHERE"] % lang["LANTERN"]
|
||||
do_lig = do_light
|
||||
|
||||
def do_set(self, line):
|
||||
symbol = None
|
||||
try:
|
||||
(symname, value) = line.split()
|
||||
symbol = configuration.dictionary[symname]
|
||||
except:
|
||||
print lang["EH?"]
|
||||
if not symbol:
|
||||
print lang["NOSUCHAS"], symbol.name
|
||||
elif symbol.menu != self.current:
|
||||
print lang["NOHERE"] % symbol.name
|
||||
elif symbol.menu.type == "choices" or symbol.is_logical():
|
||||
print lang["CANTDO"]
|
||||
elif symbol.is_numeric():
|
||||
self.set_symbol(symbol, int(value))
|
||||
elif symbol.type == "string":
|
||||
self.set_symbol(symbol, value)
|
||||
|
||||
def do_xyzzy(self, dummy):
|
||||
# Toggle the suppressions flag
|
||||
configuration.suppressions = not configuration.suppressions
|
||||
if configuration.suppressions:
|
||||
print lang["SUPPRESSON"]
|
||||
else:
|
||||
print lang["SUPPRESSOFF"]
|
||||
return 0
|
||||
|
||||
def do_load(self, line):
|
||||
if not line:
|
||||
print lang["EH?"]
|
||||
return
|
||||
file = string.strip(line)
|
||||
if file.find(' ') > -1:
|
||||
(file, option) = file.split(' ')
|
||||
try:
|
||||
(changes, errors) = configuration.load(file, freeze=(option == "frozen"))
|
||||
except IOError:
|
||||
print lang["LOADFAIL"] % file
|
||||
else:
|
||||
if errors:
|
||||
print errors
|
||||
print lang["INCCHANGES"] % (changes,file)
|
||||
if configuration.side_effects:
|
||||
sys.stdout.write(string.join(configuration.side_effects, "\n") + "\n")
|
||||
do_loa = do_load
|
||||
|
||||
def do_save(self, line):
|
||||
if not line:
|
||||
print lang["EH?"]
|
||||
return
|
||||
file = string.strip(line)
|
||||
failure = configuration.save(file, cml.Baton(lang["SAVESTART"] % file, lang["SAVEEND"]))
|
||||
if failure:
|
||||
print failure
|
||||
do_sav = do_save
|
||||
|
||||
def do_exit(self, dummy):
|
||||
# Terminate this cmd instance, saving configuration
|
||||
self.do_s(configfile)
|
||||
return 1
|
||||
do_exi = do_exit
|
||||
|
||||
def do_quit(self, line):
|
||||
# Terminate this cmd instance, not saving configuration
|
||||
return 1
|
||||
do_qui = do_quit
|
||||
|
||||
# Debugging commands -- not documented
|
||||
def do_verbose(self, line):
|
||||
# Set the debug flag
|
||||
if not line:
|
||||
configuration.debug += 1
|
||||
else:
|
||||
configuration.debug = int(line)
|
||||
return 0
|
||||
do_ver = do_verbose
|
||||
|
||||
def do_examine(self, line):
|
||||
# Examine the state of a given symbol
|
||||
symbol = string.strip(line)
|
||||
if configuration.dictionary.has_key(symbol):
|
||||
entry = configuration.dictionary[symbol]
|
||||
print entry
|
||||
if entry.constraints:
|
||||
print lang["CONSTRAINTS"]
|
||||
for wff in entry.constraints:
|
||||
print cml.display_expression(wff)
|
||||
if configuration.is_visible(entry):
|
||||
print lang["VISIBLE"]
|
||||
else:
|
||||
print lang["INVISIBLE"]
|
||||
help = entry.help()
|
||||
if help:
|
||||
print help
|
||||
else:
|
||||
print lang["NOHELP"] % (entry.name,)
|
||||
elif symbol == "lantern":
|
||||
if lanternloc == "user" or lanternloc == self.current:
|
||||
print lang["LANTERNHELP"]
|
||||
else:
|
||||
print lang["NOHERE"] % lang["LANTERN"]
|
||||
else:
|
||||
print lang["NOSUCHAS"], symbol
|
||||
return 0
|
||||
do_exa = do_examine
|
||||
|
||||
def emptyline(self):
|
||||
return 0
|
||||
def do_commands(self, dummy):
|
||||
print string.join(map(lambda x: lang[x],
|
||||
("LHELP", "NHELP", "GHELP", "IHELP",
|
||||
"DHELP", "THELP", "PHELP", "SETHELP",
|
||||
"LDHELP", "SHELP", "XYZZY",
|
||||
"QHELP", "XHELP", "DIRHELP")),
|
||||
"\n")
|
||||
def help_look(self):
|
||||
print lang["LHELP"]
|
||||
help_loo = help_look
|
||||
def help_nearby(self):
|
||||
print lang["NHELP"]
|
||||
help_nea = help_nearby
|
||||
def help_go(self):
|
||||
print lang["GHELP"]
|
||||
def help_inventory(self):
|
||||
print lang["IHELP"]
|
||||
help_inv = help_inventory
|
||||
def help_drop(self):
|
||||
print lang["DHELP"]
|
||||
help_dro = help_drop
|
||||
def help_take(self):
|
||||
print lang["THELP"]
|
||||
help_tak = help_take
|
||||
def help_press(self):
|
||||
print lang["PHELP"]
|
||||
help_pus = help_push = help_pre = help_press
|
||||
def help_set(self):
|
||||
print lang["SETHELP"]
|
||||
def help_xyzzy(self):
|
||||
print lang["XYZZY"]
|
||||
def help_load(self):
|
||||
print lang["LDHELP"]
|
||||
help_loa = help_load
|
||||
def help_save(self):
|
||||
print lang["SHELP"]
|
||||
help_sav = help_save
|
||||
def help_quit(self):
|
||||
print lang["QHELP"]
|
||||
help_qui = help_quit
|
||||
def help_exit(self):
|
||||
print lang["XHELP"]
|
||||
help_exi = help_exit
|
||||
def do_help(self, dummy):
|
||||
print lang["GENHELP"]
|
||||
def postcmd(self, stop, dummy):
|
||||
if stop:
|
||||
return stop
|
||||
if self.current != self.last:
|
||||
newroom(self.current)
|
||||
visit(self.current, 4 - 3 * (self.current.visits > 1))
|
||||
self.last = self.current
|
||||
return None
|
||||
|
||||
# Rulebase loading and option processing
|
||||
|
||||
def load_system(cmd_options, cmd_arguments):
|
||||
"Read in the rulebase and handle command-line arguments."
|
||||
global debug, configfile, configuration
|
||||
debug = 0;
|
||||
configfile = None
|
||||
|
||||
if not cmd_arguments:
|
||||
rulebase = "rules.out"
|
||||
else:
|
||||
rulebase = cmd_arguments[0]
|
||||
try:
|
||||
open(rulebase, 'rb')
|
||||
except IOError:
|
||||
print lang["NOFILE"] % (rulebase,)
|
||||
raise SystemExit
|
||||
configuration = cmlsystem.CMLSystem(rulebase)
|
||||
|
||||
process_options(configuration, cmd_options)
|
||||
|
||||
configuration.debug_emit(1, lang["PARAMS"] % (configfile,configuration.prefix))
|
||||
|
||||
# Perhaps the user needs modules enabled initially
|
||||
if configuration.trit_tie and cml.evaluate(configuration.trit_tie):
|
||||
configuration.trits_enabled = 1
|
||||
|
||||
# Don't count all these automatically generated settings
|
||||
# for purposes of figuring out whether we should confirm a quit.
|
||||
configuration.commits = 0
|
||||
|
||||
return configuration
|
||||
|
||||
def process_include(myconfiguration, file, freeze):
|
||||
"Process a -i or -I inclusion option."
|
||||
# Failure to find an include file is non-fatal
|
||||
try:
|
||||
(changes, errors) = myconfiguration.load(file, freeze)
|
||||
except IOError:
|
||||
print lang["LOADFAIL"] % file
|
||||
return
|
||||
if errors:
|
||||
print errors
|
||||
elif myconfiguration.side_effects:
|
||||
print lang["SIDEFROM"] % file
|
||||
sys.stdout.write(string.join(myconfiguration.side_effects, "\n") + "\n")
|
||||
|
||||
def process_define(myconfiguration, val, freeze):
|
||||
"Process a -d=xxx or -D=xxx option."
|
||||
parts = string.split(val, "=")
|
||||
sym = parts[0]
|
||||
if myconfiguration.dictionary.has_key(sym):
|
||||
sym = myconfiguration.dictionary[sym]
|
||||
else:
|
||||
myconfiguration.errout.write(lang["SYMUNKNOWN"] % (`sym`,))
|
||||
sys.exit(1)
|
||||
if sym.is_derived():
|
||||
myconfiguration.debug_emit(1, lang["DERIVED"] % (`sym`,))
|
||||
sys.exit(1)
|
||||
elif sym.is_logical():
|
||||
if len(parts) == 1:
|
||||
val = 'y'
|
||||
elif parts[1] == 'y':
|
||||
val = 'y'
|
||||
elif parts[1] == 'm':
|
||||
myconfiguration.trits_enabled = 1
|
||||
val = 'm'
|
||||
elif parts[1] == 'n':
|
||||
val = 'n'
|
||||
elif len(parts) == 1:
|
||||
print lang["NOCMDLINE"] % (`sym`,)
|
||||
sys.exit(1)
|
||||
else:
|
||||
val = parts[1]
|
||||
(ok, effects, violations) = myconfiguration.set_symbol(sym,
|
||||
myconfiguration.value_from_string(sym, val),
|
||||
freeze)
|
||||
if effects:
|
||||
print lang["EFFECTS"]
|
||||
sys.stdout.write(string.join(effects, "\n") + "\n\n")
|
||||
if not ok:
|
||||
print lang["ROLLBACK"] % (sym.name, val)
|
||||
sys.stdout.write(string.join(violations,"\n")+"\n")
|
||||
|
||||
def process_options(myconfiguration, options):
|
||||
# Process command-line options second so they override
|
||||
global list, configfile, debug, banner
|
||||
configfile = "config.out"
|
||||
for (switch, val) in options:
|
||||
if switch == '-B':
|
||||
banner = val
|
||||
elif switch == '-d':
|
||||
process_define(myconfiguration, val, freeze=0)
|
||||
elif switch == '-D':
|
||||
process_define(myconfiguration, val, freeze=1)
|
||||
elif switch == '-i':
|
||||
process_include(myconfiguration, val, freeze=0)
|
||||
elif switch == '-I':
|
||||
process_include(myconfiguration, val, freeze=1)
|
||||
elif switch == '-l':
|
||||
list = 1
|
||||
elif switch == '-o':
|
||||
configfile = val
|
||||
elif switch == '-v':
|
||||
debug = debug + 1
|
||||
myconfiguration.debug = myconfiguration.debug + 1
|
||||
elif switch == '-S':
|
||||
myconfiguration.suppressions = 0
|
||||
|
||||
# Main sequence -- isolated here so we can profile it
|
||||
|
||||
def main(options, arguments):
|
||||
global configuration
|
||||
try:
|
||||
myconfiguration = load_system(options, arguments)
|
||||
except KeyboardInterrupt:
|
||||
raise SystemExit
|
||||
|
||||
# Set seed for random-number functions
|
||||
whrandom.seed(int(time.time()) % 256, os.getpid() % 256, 23)
|
||||
global grafittishuffle
|
||||
grafittishuffle = shuffle(len(grafitti))
|
||||
|
||||
print lang["WELCOME"] % cml.version
|
||||
myconfiguration.errout = sys.stdout
|
||||
advent_menu(configfile, banner).cmdloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
runopts = "aB:cD:d:h:i:I:lo:P:R:StVvWx"
|
||||
(options,arguments) = getopt.getopt(sys.argv[1:], runopts)
|
||||
if os.environ.has_key("CML2OPTIONS"):
|
||||
(envopts, envargs) = getopt.getopt(
|
||||
os.environ["CML2OPTIONS"].split(),
|
||||
runopts)
|
||||
options = envopts + options
|
||||
except:
|
||||
print lang["BADOPTION"]
|
||||
sys.exit(1)
|
||||
|
||||
for (switch, val) in options:
|
||||
if switch == "-V":
|
||||
print "cmladvent", cml.version
|
||||
raise SystemExit
|
||||
elif switch == '-P':
|
||||
proflog = val
|
||||
try:
|
||||
import readline
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
if proflog:
|
||||
import profile, pstats
|
||||
profile.run("main(options, arguments)", proflog)
|
||||
else:
|
||||
main(options, arguments)
|
||||
except KeyboardInterrupt:
|
||||
#if configuration.commits > 0:
|
||||
# print lang["NOTSAVED"]
|
||||
print lang["ABORTED"]
|
||||
except "UNSATISFIABLE":
|
||||
#configuration.save("post.mortem")
|
||||
print lang["POSTMORTEM"]
|
||||
raise SystemExit, 1
|
||||
|
||||
# That's all, folks!
|
||||
1327
tools/cml2-tools/cmlcompile.py
Executable file
1327
tools/cml2-tools/cmlcompile.py
Executable file
File diff suppressed because it is too large
Load Diff
3323
tools/cml2-tools/cmlconfigure.py
Executable file
3323
tools/cml2-tools/cmlconfigure.py
Executable file
File diff suppressed because it is too large
Load Diff
BIN
tools/cml2-tools/cmlconfigure.pyc
Normal file
BIN
tools/cml2-tools/cmlconfigure.pyc
Normal file
Binary file not shown.
1239
tools/cml2-tools/cmlsystem.py
Normal file
1239
tools/cml2-tools/cmlsystem.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
tools/cml2-tools/cmlsystem.pyc
Normal file
BIN
tools/cml2-tools/cmlsystem.pyc
Normal file
Binary file not shown.
122
tools/cml2-tools/configtrans.py
Executable file
122
tools/cml2-tools/configtrans.py
Executable file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
configtrans.py -- translate between CML1 and CML2 config formats.
|
||||
|
||||
This handles the impedance mismatch between CML2's explicit NAME=VALUE
|
||||
output format and the formats expected by the Linux build machinery.
|
||||
|
||||
Note: it also makes backups whenever it touches a file.
|
||||
|
||||
configtrans.py -h includeout -s configout cml2file
|
||||
configtrans.py -t <newconfig >oldconfig
|
||||
"""
|
||||
import sys, os, getopt, re
|
||||
|
||||
def linetrans(hook, instream, outstream, trailer=None):
|
||||
"Line-by-line translation between streams."
|
||||
if not hasattr(instream, "readline"):
|
||||
instream = open(instream, "r")
|
||||
if not hasattr(outstream, "readline"):
|
||||
outstream = open(outstream, "w")
|
||||
while 1:
|
||||
line = instream.readline()
|
||||
if not line:
|
||||
break
|
||||
new = hook(line)
|
||||
if new:
|
||||
outstream.write(new)
|
||||
instream.close()
|
||||
if trailer:
|
||||
outstream.write(trailer)
|
||||
outstream.close()
|
||||
|
||||
def write_include(line):
|
||||
"Transform a SYMBOL=VALUE line to CML1 include format."
|
||||
if line.find("PRIVATE") > -1 or line[:2] == "$$":
|
||||
return ""
|
||||
match = isnotset.match(line)
|
||||
if match:
|
||||
return "#undef %s\n" % match.group(1)
|
||||
if line == "#\n":
|
||||
return None
|
||||
elif line[0] == "#":
|
||||
return "/* " + line[1:].strip() + " */\n"
|
||||
eq = line.find("=")
|
||||
if eq == -1:
|
||||
return line
|
||||
else:
|
||||
line = line.split('#')[0]
|
||||
symbol = line[:eq]
|
||||
value = line[eq+1 :].strip()
|
||||
if value == 'y':
|
||||
return "#define %s 1\n" % symbol
|
||||
elif value == 'm':
|
||||
return "#undef %s\n#define %s_MODULE 1\n" % (symbol, symbol)
|
||||
elif value == 'n':
|
||||
return "#undef %s\n" % symbol
|
||||
else:
|
||||
return "#define %s %s\n" % (symbol, value)
|
||||
|
||||
def write_defconfig(line):
|
||||
"Transform a SYMBOL=VALUE line to CML1 defconfig format."
|
||||
if line[:2] == "$$":
|
||||
return ""
|
||||
eq = line.find("=")
|
||||
if eq == -1:
|
||||
return line
|
||||
else:
|
||||
line = line.split('#')[0]
|
||||
line = line.strip()
|
||||
if len(line) == 0 or line[-1] != "\n":
|
||||
line += "\n"
|
||||
symbol = line[:eq]
|
||||
value = line[eq+1:].strip()
|
||||
if value == 'n':
|
||||
return "# %s is not set\n" % symbol
|
||||
else:
|
||||
return line
|
||||
|
||||
def revert(line):
|
||||
"Translate a CML1 defconfig file to CML2 format."
|
||||
match = isnotset.match(line)
|
||||
if match:
|
||||
return "%s=n\n" % match.group(1)
|
||||
else:
|
||||
return line
|
||||
|
||||
if __name__ == '__main__':
|
||||
isnotset = re.compile("^# (.*) is not set")
|
||||
include = defconfig = translate = None
|
||||
(options, arguments) = getopt.getopt(sys.argv[1:], "h:s:t")
|
||||
for (switch, val) in options:
|
||||
if switch == '-h':
|
||||
includefile = val
|
||||
try:
|
||||
os.rename(val, val + ".old")
|
||||
except OSError:
|
||||
pass
|
||||
elif switch == '-s':
|
||||
defconfig = val
|
||||
try:
|
||||
os.rename(val, val + ".old")
|
||||
except OSError:
|
||||
pass
|
||||
elif switch == '-t':
|
||||
translate = 1
|
||||
if len(arguments) > 0:
|
||||
try:
|
||||
if includefile:
|
||||
linetrans(write_include, arguments[0], includefile, "#define AUTOCONF_INCLUDED\n")
|
||||
if defconfig:
|
||||
linetrans(write_defconfig, arguments[0], defconfig)
|
||||
except IOError, args:
|
||||
sys.stderr.write("configtrans: " + args[1] + "\n");
|
||||
raise SystemExit, 1
|
||||
elif translate:
|
||||
linetrans(revert, sys.stdin, sys.stdout)
|
||||
else:
|
||||
print "usage: configtrans.py -t [-h includefile] [-s defconfig] file"
|
||||
raise SystemExit, 1
|
||||
|
||||
# That's all, folks!
|
||||
|
||||
267
tools/cml2-tools/helpmerge.py
Executable file
267
tools/cml2-tools/helpmerge.py
Executable file
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Merge a Configure.help file into a file of CML2 symbol declarations
|
||||
#
|
||||
# The Configure.help file must be argument 1; the partial symbol file
|
||||
# must be argument 2.
|
||||
#
|
||||
# When given the -c option, suppress normal output and instead
|
||||
# consistency-check the prompts.
|
||||
|
||||
import sys, string, re, os, os.path
|
||||
|
||||
if sys.version[0] < '2':
|
||||
print "Python 2.0 or later is required for this program."
|
||||
sys.exit(0)
|
||||
|
||||
directory = ""
|
||||
|
||||
def extract_dir(line, splitlocs):
|
||||
global directory
|
||||
# Handle directory directive
|
||||
if line[:14] == "#% Directory: ":
|
||||
fields = line[14:].strip().split()
|
||||
if len(fields) == 1:
|
||||
directory = fields[0]
|
||||
else:
|
||||
splitlocs[fields[0]] = fields[2]
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def help_scan(file, prefix):
|
||||
# This assumes the format of Axel Boldt's Configure.help file
|
||||
global directory
|
||||
dict = {}
|
||||
splitlocs = {}
|
||||
stream = open(file)
|
||||
name = None
|
||||
ringbuffer = [0, 0, 0, 0, 0]
|
||||
ringindex = choiceflag = 0
|
||||
prompt = ""
|
||||
lastline = ""
|
||||
start = 0
|
||||
while 1:
|
||||
line = stream.readline()
|
||||
if extract_dir(line, splitlocs):
|
||||
continue
|
||||
# Now everything else
|
||||
if line and line[0] == '#':
|
||||
if line.find("Choice:") > -1:
|
||||
choiceflag = 1
|
||||
continue
|
||||
ringbuffer[ringindex] = here = stream.tell()
|
||||
ringindex = (ringindex + 1) % 5
|
||||
if line and line[0] in string.whitespace:
|
||||
continue
|
||||
if not line or line[0:7] == prefix:
|
||||
if name:
|
||||
if dict.has_key(name):
|
||||
sys.stderr.write("Duplicate help text for %s\n" % name)
|
||||
dict[name] = (file, start, ringbuffer[(ringindex - 4) % 5], prompt)
|
||||
if directory != "UNKNOWN":
|
||||
splitlocs[name] = directory
|
||||
directory = "UNKNOWN"
|
||||
if line:
|
||||
name = string.strip(line[7:])
|
||||
start = here
|
||||
if choiceflag:
|
||||
prompt = None # Disable prompt checking
|
||||
else:
|
||||
prompt = lastline.strip()
|
||||
choiceflag = 0
|
||||
else:
|
||||
break
|
||||
lastline = line
|
||||
stream.close()
|
||||
return (dict, splitlocs)
|
||||
|
||||
def fetch_help(symbol, helpdict):
|
||||
"Fetch help text associated with given symbol, if any."
|
||||
if helpdict.has_key(symbol):
|
||||
(file, start, end, prompt) = helpdict[symbol]
|
||||
stream = open(file)
|
||||
stream.seek(start)
|
||||
help = stream.read(end - start)
|
||||
# Canonicalize trailing whitespace
|
||||
help = help.rstrip() + "\n"
|
||||
stream.close()
|
||||
return help
|
||||
else:
|
||||
return None
|
||||
|
||||
def merge(helpfile, templatefile):
|
||||
"Merge a Configure.help with a symbols file, write to stdout."
|
||||
(helpdict, splitlocs) = help_scan(helpfile, "CONFIG_")
|
||||
template = open(templatefile, "r")
|
||||
promptre = re.compile("(?<=['\"])[^'\"]*(?=['\"])")
|
||||
|
||||
os.system('rm -f `find kernel-tree -name "*symbols.cml"`')
|
||||
|
||||
trim = re.compile("^ ", re.M)
|
||||
trailing_comment = re.compile("\s*#[^#']*$")
|
||||
outfp = None
|
||||
lineno = 0
|
||||
while 1:
|
||||
lineno += 1
|
||||
line = template.readline()
|
||||
if not line:
|
||||
break
|
||||
elif line == "\n":
|
||||
continue
|
||||
elif line[0] == "#":
|
||||
extract_dir(line, splitlocs)
|
||||
continue
|
||||
# Sanity check
|
||||
prompt = promptre.search(line)
|
||||
if not prompt:
|
||||
sys.stderr.write("Malformed line %s: %s" % (lineno, line))
|
||||
raise SystemExit, 1
|
||||
# We've hit something that ought to be a symbol line
|
||||
fields = line.split()
|
||||
symbol = fields[0]
|
||||
template_has_text = line.find("text\n") > -1
|
||||
if symbol[:7] == "CONFIG_":
|
||||
symbol = symbol[7:]
|
||||
if checkonly:
|
||||
# Consistency-check the prompts
|
||||
prompt = prompt.group(0)
|
||||
if helpdict.has_key(symbol):
|
||||
oldprompt = helpdict[symbol][3]
|
||||
if oldprompt == None:
|
||||
continue
|
||||
if oldprompt[-15:] == " (EXPERIMENTAL)":
|
||||
oldprompt = oldprompt[:-15]
|
||||
if oldprompt[-11:] == " (OBSOLETE)":
|
||||
oldprompt = oldprompt[:-11]
|
||||
if oldprompt[-12:] == " (DANGEROUS)":
|
||||
oldprompt = oldprompt[:-12]
|
||||
if oldprompt != prompt:
|
||||
sys.stdout.write("%s:\n" % (symbol,))
|
||||
sys.stdout.write("CML1: '" + oldprompt + "'\n")
|
||||
sys.stdout.write("CML2: '" + prompt + "'\n")
|
||||
while 1:
|
||||
line = template.readline()
|
||||
if line == ".\n":
|
||||
break
|
||||
else:
|
||||
# Now splice in the actual help text
|
||||
helptext = fetch_help(symbol, helpdict)
|
||||
if helptext and template_has_text:
|
||||
print line
|
||||
sys.stderr.write("Template already contains help text for %s!\n" % symbol)
|
||||
raise SystemExit, 1
|
||||
if outfp:
|
||||
outfp.close()
|
||||
if splitlocs.has_key(symbol):
|
||||
dest = splitlocs[symbol]
|
||||
else:
|
||||
dest = directory
|
||||
if dest == "UNKNOWN":
|
||||
sys.stderr.write("No directory for %s\n" % symbol)
|
||||
sys.exit(1)
|
||||
#print "%s -> %s" % (symbol, dest)
|
||||
dest = os.path.join("kernel-tree", dest[1:], "symbols.cml")
|
||||
exists = os.path.exists(dest)
|
||||
if exists:
|
||||
outfp = open(dest, "a")
|
||||
else:
|
||||
outfp = open(dest, "w")
|
||||
outfp.write("symbols\n")
|
||||
if helptext:
|
||||
leader = line.rstrip()
|
||||
comment_match = trailing_comment.search(leader)
|
||||
if comment_match:
|
||||
comment = comment_match.group(0)
|
||||
leader = leader[:comment_match.start(0)]
|
||||
else:
|
||||
comment = ""
|
||||
if len(leader) < 68:
|
||||
outfp.write(leader + "\ttext")
|
||||
if comment:
|
||||
outfp.write("\t" + comment)
|
||||
outfp.write("\n")
|
||||
else:
|
||||
outfp.write(leader + comment + "\ntext\n")
|
||||
outfp.write(trim.sub("", helptext) + ".\n")
|
||||
elif template_has_text:
|
||||
outfp.write(line)
|
||||
while 1:
|
||||
line = template.readline()
|
||||
outfp.write(line)
|
||||
if line == ".\n":
|
||||
break
|
||||
else:
|
||||
outfp.write(line)
|
||||
|
||||
def conditionalize(file, optset):
|
||||
"Handle conditional inclusions and drop out choice lines."
|
||||
import re
|
||||
cond = re.compile(r"^#% (\S*) only$")
|
||||
infp = open(file)
|
||||
if optset:
|
||||
sys.stdout.write("## This version generated for " + " with ".join(optset) + "\n")
|
||||
while 1:
|
||||
import re
|
||||
line = infp.readline()
|
||||
if not line:
|
||||
break
|
||||
if line[:9] == "# Choice:":
|
||||
continue
|
||||
match = cond.match(line)
|
||||
if match and match.group(1) not in optset:
|
||||
while 1:
|
||||
line = infp.readline()
|
||||
if not line:
|
||||
break
|
||||
if line == "\n":
|
||||
line = infp.readline()
|
||||
break
|
||||
if line[:2] == "#%": # Drop out other directives
|
||||
continue
|
||||
sys.stdout.write(line)
|
||||
|
||||
def dump_symbol(symbol, helpdict):
|
||||
"Dump a help entry."
|
||||
sys.stdout.write("%s\n" % helpdict[symbol][3])
|
||||
sys.stdout.write("CONFIG_%s\n" % symbol)
|
||||
sys.stdout.write(fetch_help(symbol, helpdict))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import getopt
|
||||
|
||||
checkonly = sort = 0
|
||||
optset = []
|
||||
(options, arguments) = getopt.getopt(sys.argv[1:], "D:Ecns")
|
||||
for (switch, val) in options:
|
||||
if switch == "-D":
|
||||
optset.append(val)
|
||||
elif switch == '-E': # Process conditionals
|
||||
conditionalize(arguments[0], optset)
|
||||
sys.exit(0)
|
||||
elif switch == '-c': # Consistency check
|
||||
checkonly = 1
|
||||
elif switch == '-n': # List symbols with no match in second arg
|
||||
import cmlsystem
|
||||
configuration = cmlsystem.CMLSystem(arguments[1])
|
||||
helpdict = help_scan(arguments[0], "CONFIG_")
|
||||
keys = helpdict.keys()
|
||||
keys.sort()
|
||||
for symbol in keys:
|
||||
if not configuration.dictionary.get(symbol):
|
||||
dump_symbol(symbol, helpdict)
|
||||
sys.exit(0)
|
||||
elif switch == '-s': # Emit sorted version
|
||||
helpdict = help_scan(arguments[0], "CONFIG_")
|
||||
keys = helpdict.keys()
|
||||
keys.sort()
|
||||
for symbol in keys:
|
||||
dump_symbol(symbol, helpdict)
|
||||
sys.exit(0)
|
||||
|
||||
help = arguments[0]
|
||||
symbols = arguments[1]
|
||||
merge(help, symbols)
|
||||
|
||||
# That's all, folks!
|
||||
764
tools/cml2-tools/kxref.py
Executable file
764
tools/cml2-tools/kxref.py
Executable file
@@ -0,0 +1,764 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
kxref.py -- generate configuration symbol cross-reference for the kernel tree
|
||||
|
||||
This is a report generator intended to catch problems and inconsistencies
|
||||
in the configuration-symbol namespace. It uses information generated by
|
||||
the CML2 compiler -- notably, it relies on the compiler's scanning of
|
||||
help files.
|
||||
|
||||
All this does is generate cross-reference reports on configuration
|
||||
symbols. But they can be filtered and presented in various
|
||||
interesting ways. Basic usage is like this:
|
||||
|
||||
kxref.py [-f filter | -h] [-l] [-x symbol] [-n re] [sourcetree]
|
||||
|
||||
You can set a filter using a boolean-expression minilanguage. The predicates
|
||||
available are as follows:
|
||||
|
||||
c -- select all symbols present in code (.c, .h, .S files)
|
||||
m -- select all symbols present in makefiles
|
||||
n -- select all symbols defined in CML2 rulesfiles
|
||||
h -- select all symbols for which help is available (CMl1 convention)
|
||||
H -- select all symbols for which help is available (CML2 convention)
|
||||
d -- select all symbols that occur in defconfigs
|
||||
x -- select all symbols that are derived in CML2.
|
||||
o -- select all symbols present in CML1 configuration files
|
||||
a -- select all symbols declared in CML1 configuration files
|
||||
p -- select all symbols for which autoconfigure.py has a probe
|
||||
|
||||
D(name) -- select all symbols transitively dependent on name
|
||||
A(name) -- select all symbols transitively ancestral to name
|
||||
T(type) -- select type (trit, bool, string, decimal, hexadecimal)
|
||||
P(property) -- select all symbols with given property
|
||||
V(symbol) -- select all symbols with given symbol in their
|
||||
visibility guard.
|
||||
|
||||
Operations available are as follows:
|
||||
|
||||
& -- and (set intersection)
|
||||
| -- or (set intersection)
|
||||
~ -- not (set complement).
|
||||
|
||||
You may use parentheses for expression grouping.
|
||||
|
||||
This program caches a cross-reference database in a file named
|
||||
xref.out, so all reports after the first are generated really fast.
|
||||
You should remove this file whenever you apply a patch.
|
||||
|
||||
The -i option inverts the report so it's keyed by file, rather than
|
||||
by symbol.
|
||||
|
||||
The -g option generates a patch removing file lines containing the
|
||||
reported (presumably orphaned) symbols. Use with caution...it's
|
||||
really only safe for hacking defconfigs.
|
||||
|
||||
The -x option is for debugging. It generates a report on an individual
|
||||
symbol specified as an argument to the option. Flag letters are as
|
||||
above, with f= giving the value of the computed filter predicate.
|
||||
|
||||
The -h option checks for duplicate or superfluous file inclusions
|
||||
in the source tree.
|
||||
|
||||
The -l switch suppresses printing printing of cross-references;
|
||||
only symbols matching the given filter(s) are listed.
|
||||
|
||||
The -n suppresses listing of files with names matching the given regexp.
|
||||
If all the files a symbol occurs in are excluded, it will be omitted
|
||||
from the listings.
|
||||
|
||||
The -t option produces a listing of symbols which either have
|
||||
inconsistent CML1 types or types that differ between CML1 and CML2.
|
||||
|
||||
The -k option accepts a file of kill-list symbols to be ignored.
|
||||
|
||||
The program has some knowledge of file syntax. It ignores the
|
||||
contents of comments in C, CML1, and CML2 files (e.g. does not
|
||||
cross-reference symbols in such comments).
|
||||
|
||||
Some interesting reports:
|
||||
|
||||
n&~p&~a -- identifies CML2 symbols no longer declared or defined in CML1
|
||||
|
||||
"""
|
||||
import sys, os, re, getopt, cPickle, cml, cmlsystem
|
||||
|
||||
xrefs = None
|
||||
rulebase = None
|
||||
typefind = choicere = configre = definere = mycml1types = None
|
||||
|
||||
def suffix(haystack, *needle):
|
||||
"Does a filename have any of the given suffixes?"
|
||||
for suf in needle:
|
||||
if haystack[-len(suf):] == suf:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def prefix(haystack, *needle):
|
||||
"Does a filename have any of the given prefixes?"
|
||||
for pre in needle:
|
||||
if haystack[len(pre):] == pre:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# Code for recognizing symbols and stripping out comments
|
||||
|
||||
# It's OK that this matches _MODULE symbols, we'll filter those out later.
|
||||
configpref = re.compile("(?<![A-Z0-9_])(CONFIG_[a-zA-Z0-9_][a-zA-Z0-9_]+)")
|
||||
|
||||
# Regular expressions for stripping out C comments. We're aided here by the
|
||||
# fact that we don't care about the contents of most of the file. So by
|
||||
# first stripping out / and characters that are not part of comment
|
||||
# delimiter pairs, we can make detecting comments pretty trivial. This won't
|
||||
# completely strip comments of the form /* aaaa /* bbbb */, but for this
|
||||
# application that's OK -- we don't have to be perfect, just reduce the
|
||||
# exception cases to the point where eyeball checking is feasible. Use
|
||||
# of lookaheads and lookbehinds avoids nipping off anything that might
|
||||
# be a nearby bit of symbol.
|
||||
#
|
||||
randomslash = re.compile("(?<=[^*])/(?=[^*])")
|
||||
randomstar = re.compile("(?<=[^/])\*(?=[^/])")
|
||||
c_comment = re.compile("/\*[^*]*\*/")
|
||||
|
||||
def c_comment_strip(str):
|
||||
str = randomslash.sub("", str,)
|
||||
str = randomstar.sub("", str)
|
||||
return c_comment.sub("", str)
|
||||
|
||||
# Shell, config-file, and Makefile-style comments.
|
||||
#
|
||||
hashcomment = re.compile("#.*\n", re.MULTILINE)
|
||||
|
||||
def hash_comment_strip(str):
|
||||
return hashcomment.sub("", str)
|
||||
|
||||
# Code for generating the cross-reference
|
||||
|
||||
def ignore(file):
|
||||
"Return 1 if the file should be ignored for cross-referencing purposes."
|
||||
# Ignore CML files because we look symbols up directly in the rulebase.
|
||||
return suffix(file, ".bak", ".orig", ".rej", ".cml", ".o", ".a", ".out", "log", "Log", ",v", "~")
|
||||
|
||||
# These are used in the language documentation
|
||||
kill_list = {"CHEER":1, "BOOM":1, "BOGUS":1}
|
||||
|
||||
def makexref(tree):
|
||||
"Generate a cross-reference dictionary for the given source tree."
|
||||
global typefind, choicere, configre, definere, mycml1types
|
||||
typefind = re.compile(r"(?<!define_)(bool|tristate|int|hex|string)\s+'.*'\s+CONFIG_(\w+)")
|
||||
choicere = re.compile(r"^\s*choice")
|
||||
configre = re.compile(rulebase.prefix + r"(\w*)")
|
||||
definere = re.compile(r"^\s+define_([a-z]*)\s+(\w*)")
|
||||
mycml1types = {}
|
||||
def xrefvisit(dict, dir, files):
|
||||
"Visit a directory on behalf of the cross-referencer."
|
||||
def filevisitor(dict, file):
|
||||
"Visit a file on behalf of the cross-referencer."
|
||||
if file[0] == '.':
|
||||
return
|
||||
fp = open(file)
|
||||
contents = fp.read()
|
||||
fp.close()
|
||||
if suffix(file, ".c", ".h", ".S"):
|
||||
contents = c_comment_strip(contents)
|
||||
elif suffix(file, ".in", ".cml"):
|
||||
contents = hash_comment_strip(contents)
|
||||
for match in configpref.findall(contents):
|
||||
if suffix(match, "_MODULE"):
|
||||
continue
|
||||
match = namestrip(match)
|
||||
if kill_list.has_key(match):
|
||||
continue
|
||||
elif not dict.has_key(match):
|
||||
dict[match] = []
|
||||
if file not in dict[match]:
|
||||
dict[match].append(file)
|
||||
# Parse file contents for choice symbols
|
||||
if suffix(file, ".in"):
|
||||
lines = contents.split("\n")
|
||||
while lines:
|
||||
if not choicere.match(lines[0]):
|
||||
# First extract type info for ordinary symbols
|
||||
m = typefind.search(lines[0])
|
||||
if m:
|
||||
symtype = m.group(1)
|
||||
symname = m.group(2)
|
||||
if not mycml1types.has_key(symname):
|
||||
mycml1types[symname] = []
|
||||
if (symtype, file) not in mycml1types[symname]:
|
||||
mycml1types[symname].append((symtype, file))
|
||||
# CML1 defines count with other symbols of their type
|
||||
symdef = definere.search(lines[0])
|
||||
if symdef:
|
||||
symbol = namestrip(symdef.group(2))
|
||||
type = symdef.group(1)
|
||||
if not mycml1types.has_key(symbol):
|
||||
mycml1types[symbol] = []
|
||||
if (type, file) not in mycml1types[symbol]:
|
||||
mycml1types[symbol].append((type, file))
|
||||
lines.pop(0)
|
||||
continue
|
||||
else:
|
||||
lines.pop(0)
|
||||
while lines[0].find(rulebase.prefix) > -1:
|
||||
findit = configre.search(lines[0])
|
||||
symbol = namestrip(findit.group(0))
|
||||
if not mycml1types.has_key(symbol):
|
||||
mycml1types[symbol] = []
|
||||
mycml1types[symbol].append(("choice", file))
|
||||
if lines[0].find('" ') > -1:
|
||||
break
|
||||
lines.pop(0)
|
||||
|
||||
for file in files:
|
||||
node = os.path.join(dir, file)[2:]
|
||||
if os.path.isfile(node) and not ignore(node):
|
||||
filevisitor(dict, node)
|
||||
|
||||
xrefdict = {}
|
||||
here = os.getcwd()
|
||||
os.chdir(sourcetree)
|
||||
os.path.walk(".", xrefvisit, xrefdict)
|
||||
os.chdir(here)
|
||||
# Data reduction -- collapse CML1 cross references of identical type
|
||||
for (key, value) in mycml1types.items():
|
||||
if len(value) <= 1:
|
||||
continue # Only interested in the multiples
|
||||
else:
|
||||
tdict = {}
|
||||
for (type, file) in value:
|
||||
tdict[type] = []
|
||||
for (type, file) in value:
|
||||
tdict[type].append(file)
|
||||
reslist = []
|
||||
for type in tdict.keys():
|
||||
reslist.append((type, tdict[type]))
|
||||
mycml1types[key] = reslist
|
||||
# Second stage of data reduction -- if a symbol has both a choice
|
||||
# declaration and another of a different type, suppress the non-choice
|
||||
# declaration -- we can assume it came from a CML1 define.
|
||||
for (key, value) in mycml1types.items():
|
||||
if "choice" in map(lambda x: x[0], value):
|
||||
mycml1types[key]=filter(lambda x: x[0]=="choice", mycml1types[key])
|
||||
return (xrefdict, mycml1types)
|
||||
|
||||
probe_table = {}
|
||||
|
||||
def load_probe_table():
|
||||
"Build a table of symbols for qhich we have probes."
|
||||
from autoconfigure import get_arch
|
||||
(ARCH, ARCHSYMBOL) = get_arch()
|
||||
|
||||
TRUE = 1
|
||||
FALSE = 0
|
||||
PRESENT = 1
|
||||
ABSENT = 0
|
||||
y = m = n = 0
|
||||
|
||||
def DEBUG(str):
|
||||
pass
|
||||
|
||||
def PCI(prefix, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def PCI_CLASS(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def PNP(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def MCA(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def USBP(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def USBC(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def USBI(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def FS(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def DEV(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def DEVM(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def CONS(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def DMESG(match, symbol, truthval=None):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def NET(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def IDE(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def REQ(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def CPUTYPE(match, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def CPUINFO(match, symbol, present=None, truthval=None):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def EXISTS(procfile, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def MODULE(name, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
def GREP(pattern, file, symbol):
|
||||
probe_table[symbol] = 1
|
||||
|
||||
execfile(rulesfile)
|
||||
|
||||
# Predicates for filtering the reports
|
||||
|
||||
def namestrip(name):
|
||||
if rulebase.prefix and name[:len(rulebase.prefix)] == rulebase.prefix:
|
||||
return name[len(rulebase.prefix):]
|
||||
else:
|
||||
return name
|
||||
|
||||
def in_code(name):
|
||||
"Does a name occur in code?"
|
||||
if not xrefs.has_key(name):
|
||||
return 0
|
||||
for file in xrefs[name]:
|
||||
if suffix(file, ".c", ".S") or (suffix(file, ".h") and not suffix(file, "autoconf.h")):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def in_help(name):
|
||||
"Is there help for a symbol (CML1 convention)?"
|
||||
# Catch choice names that aren't in Configure.help directly.
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if entry and entry.help():
|
||||
return 1
|
||||
# This catches names that are in a helpfile but not known to CML2.
|
||||
if not xrefs.has_key(name):
|
||||
return 0
|
||||
for file in xrefs[name]:
|
||||
if suffix(file, ".help"):
|
||||
return 1
|
||||
# False negative if there is ever a choice name that CML2
|
||||
# doesn't know about.
|
||||
return 0
|
||||
|
||||
def in_cml2_help(name):
|
||||
"Does a name occur in some help file (CML2 rules)?"
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if entry and entry.helptext:
|
||||
return 1
|
||||
# This catches names that are in a helpfile but not known to CML2.
|
||||
if not xrefs.has_key(name):
|
||||
return 0
|
||||
for file in xrefs[name]:
|
||||
if suffix(file, ".help"):
|
||||
return 1
|
||||
# False negative if there is ever a choice name that CML2
|
||||
# doesn't know about.
|
||||
return 0
|
||||
|
||||
def in_makefile(name):
|
||||
"Does a name occur in a makefile?"
|
||||
if not xrefs.has_key(name):
|
||||
return 0
|
||||
for file in xrefs[name]:
|
||||
if suffix(file, "akefile"):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def in_cml1(name):
|
||||
"Does a name occur in a CML1 file?"
|
||||
if not xrefs.has_key(name):
|
||||
return 0
|
||||
for file in xrefs[name]:
|
||||
if suffix(file, "onfig.in"):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def cml1_declared(name):
|
||||
"Is a name declared (assigned a type) in a CML1 file?"
|
||||
return mycml1types.has_key(name)
|
||||
|
||||
def in_defconfig(name):
|
||||
if not xrefs.has_key(name):
|
||||
return 0
|
||||
"Does a this symbol occur in a defconfig?"
|
||||
for file in xrefs[name]:
|
||||
if file.find("defconfig") > -1 or file.find("configs/") > -1:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def in_cml2(name):
|
||||
"Is this a valid CML2 symbol?"
|
||||
return rulebase.dictionary.has_key(namestrip(name))
|
||||
|
||||
def is_derived(name):
|
||||
"Is this a CML2 derived name?"
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if entry and entry.is_derived():
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def dependent_of(ancestor, name):
|
||||
"Is given symbol a dependent of given ancestor?"
|
||||
ancestor = rulebase.dictionary.get(namestrip(ancestor))
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if entry and ancestor.ancestor_of(entry):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def ancestor_of(dependent, name):
|
||||
"Is given symbol a an ancestor of given dependent?"
|
||||
dependent = rulebase.dictionary.get(namestrip(dependent))
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if entry and entry.ancestor_of(dependent):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def type_of(typename, name):
|
||||
"Is given symbol of given tyoe?"
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if entry and entry.type == typename:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def has_property(property, name):
|
||||
"Does given symbol have given property?"
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if entry and property in entry.properties:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def is_probed(name):
|
||||
"Does given symbol have a probe?"
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if not probe_table:
|
||||
load_probe_table()
|
||||
return entry and probe_table.has_key(entry.name)
|
||||
|
||||
def in_visibility(guard, name):
|
||||
"Does the symbol GUARD occur in the visibility predicate of NAME?"
|
||||
entry = rulebase.dictionary.get(namestrip(name))
|
||||
if not entry:
|
||||
return 0
|
||||
guard = rulebase.dictionary.get(namestrip(guard))
|
||||
return entry.visibility and guard in cml.flatten_expr(entry.visibility)
|
||||
|
||||
# Report generation
|
||||
|
||||
def setfilter(filterspec):
|
||||
"Set the filter function."
|
||||
if not filterspec:
|
||||
function = "def myfilter(name): return 1"
|
||||
else:
|
||||
state = 0
|
||||
expression = ""
|
||||
for c in filterspec:
|
||||
if state == 0:
|
||||
if c == "(" or c == ")":
|
||||
expression += c
|
||||
elif c == " " or c == "\t":
|
||||
pass
|
||||
elif c == "a":
|
||||
expression += " cml1_declared(name)"
|
||||
elif c == "c":
|
||||
expression += " in_code(name)"
|
||||
elif c == "h":
|
||||
expression += " in_help(name)"
|
||||
elif c == "H":
|
||||
expression += " in_cml2_help(name)"
|
||||
elif c == 'm':
|
||||
expression += " in_makefile(name)"
|
||||
elif c == "o":
|
||||
expression += " in_cml1(name)"
|
||||
elif c == "n":
|
||||
expression += " in_cml2(name)"
|
||||
elif c == "d":
|
||||
expression += " in_defconfig(name)"
|
||||
elif c == "x":
|
||||
expression += " is_derived(name)"
|
||||
elif c == "~":
|
||||
expression += " not"
|
||||
elif c == "&":
|
||||
expression += " and"
|
||||
elif c == "|":
|
||||
expression += " or"
|
||||
elif c == "p":
|
||||
expression += " is_probed(name)"
|
||||
elif c == "D":
|
||||
expression += " dependent_of"
|
||||
state = 1
|
||||
elif c == "A":
|
||||
expression += " ancestor_of"
|
||||
state = 1
|
||||
elif c == "T":
|
||||
expression += " type_of"
|
||||
state = 1
|
||||
elif c == "P":
|
||||
expression += " has_property"
|
||||
state = 1
|
||||
elif c == "V":
|
||||
expression += " in_visibility"
|
||||
state = 1
|
||||
elif state == 1:
|
||||
if c == ')':
|
||||
expression += '", name)'
|
||||
state = 0
|
||||
elif c == '(':
|
||||
expression += '("'
|
||||
else:
|
||||
expression += c
|
||||
function = "def myfilter(name): return " + expression
|
||||
#sys.stderr.write("Filter function: " + function + "\n")
|
||||
exec function in globals()
|
||||
|
||||
def report(keys, norefs=0):
|
||||
"Generate a filtered report on the cross-references."
|
||||
for symbol in keys:
|
||||
refs = filter(lambda x: not (suppress and suppress.search(x)), xrefs[symbol])
|
||||
if refs:
|
||||
if norefs:
|
||||
print symbol
|
||||
else:
|
||||
sys.stdout.write(symbol + ":")
|
||||
for file in refs:
|
||||
sys.stdout.write(" " + file)
|
||||
sys.stdout.write("\n")
|
||||
|
||||
def generate_patch(file, symbols):
|
||||
"Generate a patch deleting the given symbols from the given file."
|
||||
pfp = open(file, "rb")
|
||||
contents = pfp.read()
|
||||
pfp.close()
|
||||
for symbol in symbols:
|
||||
contents = re.compile("^.*" + symbol + "[^A-Z0-9].*\n", re.M).sub("", contents)
|
||||
pfp = open(file + ".tweaked", "wb")
|
||||
pfp.write(contents)
|
||||
pfp.close()
|
||||
os.system("diff -u %s %s.tweaked; rm %s.tweaked" % (file, file, file))
|
||||
|
||||
# Inclusion checking. This lives here because we use the CML2 rulebase to
|
||||
# check which CONFIG_ symbols are defined (just checking for a CONFIG_ stem
|
||||
# isn't reliable as CML2 doesn't completely own that namespace).
|
||||
|
||||
includere = re.compile(r'^\s*#\s*include\s*[<"](\S*)[>"]', re.M)
|
||||
|
||||
def includecheck(sourcetree):
|
||||
"Check the inclusion structure of a source tree."
|
||||
def includevisit(dummy, dir, files):
|
||||
"Visit a directory on behalf of the inclusion checker."
|
||||
def filevisitor(dummy, file):
|
||||
"Visit a file on behalf of the inclusion checker."
|
||||
fp = open(file)
|
||||
contents = fp.read()
|
||||
fp.close()
|
||||
# First get the list of included files
|
||||
inclusions = includere.findall(contents)
|
||||
# This strips slashes, so it has to be done after
|
||||
contents = c_comment_strip(contents)
|
||||
# Check to see if we have defined CONFIG_ symbols in the file
|
||||
matched = []
|
||||
for match in configpref.findall(contents):
|
||||
if suffix(match, "_MODULE"):
|
||||
match = match[:-7]
|
||||
match = namestrip(match) # Strip prefix
|
||||
if rulebase.dictionary.has_key(match) and match not in matched:
|
||||
matched.append(match)
|
||||
# Check for duplicates
|
||||
dups = {}
|
||||
for header in inclusions:
|
||||
dups[header] = 0
|
||||
for header in inclusions:
|
||||
dups[header] += 1
|
||||
for header in inclusions:
|
||||
if dups[header] > 1:
|
||||
print "%s: %s is included %d times" % (file, header, dups[header])
|
||||
# OK, check to see if we have autoconf inclusion.
|
||||
have_autoconf = 0
|
||||
for header in inclusions:
|
||||
if header == "autoconf.h" or header == "linux/config.h":
|
||||
have_autoconf = 1
|
||||
break
|
||||
if not matched and have_autoconf:
|
||||
print "%s: has unnecessary configure file inclusion" % file
|
||||
elif matched and not have_autoconf:
|
||||
print "%s: needs configure file inclusion for %s" % (file, matched)
|
||||
|
||||
for file in files:
|
||||
if suffix(file, ".c", ".h", ".S"):
|
||||
node = os.path.join(dir, file)[2:]
|
||||
if os.path.isfile(node) and not ignore(node):
|
||||
filevisitor(None, node)
|
||||
|
||||
here = os.getcwd()
|
||||
os.chdir(sourcetree)
|
||||
os.path.walk(".", includevisit, None)
|
||||
os.chdir(here)
|
||||
|
||||
# The main program
|
||||
|
||||
def load_context(tree):
|
||||
"Load context, including CML2 rulebase and cross-reference database."
|
||||
global rulebase, xrefs, mycml1types
|
||||
|
||||
# Get a CML2 rulebase.
|
||||
if not os.path.exists(os.path.join(tree, "rules.out")):
|
||||
print "This program requires a CML2 rulebase in the source tree."
|
||||
raise SystemExit, 1
|
||||
else:
|
||||
rulebase = cmlsystem.CMLSystem(os.path.join(tree, "rules.out"))
|
||||
|
||||
# Try to find a saved cross-reference database. If no such database
|
||||
# exists, generate one and cache it.
|
||||
xref_file = os.path.join(tree, "xref.out")
|
||||
if os.path.exists(xref_file):
|
||||
sys.stderr.write("Reading cross-reference database...")
|
||||
ifp = open(xref_file, "rb")
|
||||
(xrefs, mycml1types) = cPickle.load(ifp)
|
||||
ifp.close()
|
||||
sys.stderr.write("done.\n")
|
||||
else:
|
||||
sys.stderr.write("Regenerating cross-reference database...")
|
||||
(xrefs, mycml1types) = makexref(tree)
|
||||
ofp = open(xref_file, "w")
|
||||
cPickle.dump((xrefs, mycml1types), ofp, 1)
|
||||
ofp.close()
|
||||
sys.stderr.write("done.\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
setfilter(None)
|
||||
examine = ""
|
||||
norefs = 0
|
||||
typecheck = 0
|
||||
suppress = None
|
||||
rulesfile = None
|
||||
invert = genpatch = checkincludes = 0
|
||||
(options, arguments) = getopt.getopt(sys.argv[1:], "ef:ghik:ln:r:tx:")
|
||||
for (switch, val) in options:
|
||||
if switch == '-f':
|
||||
setfilter(val)
|
||||
elif switch == '-i':
|
||||
invert = 1
|
||||
elif switch == '-g':
|
||||
invert = genpatch = 1
|
||||
elif switch == '-h':
|
||||
checkincludes = 1
|
||||
elif switch == '-k':
|
||||
fp = open(val, "r")
|
||||
while 1:
|
||||
line = fp.readline()
|
||||
if not line:
|
||||
break
|
||||
kill_list[line.strip()] = 1
|
||||
elif switch == '-l':
|
||||
norefs = 1
|
||||
elif switch == '-n':
|
||||
suppress = re.compile(val)
|
||||
elif switch == '-r':
|
||||
rulesfile = val
|
||||
elif switch == '-t':
|
||||
typecheck = 1
|
||||
elif switch == '-x':
|
||||
examine = val
|
||||
|
||||
if len(arguments) < 1:
|
||||
sourcetree = "."
|
||||
else:
|
||||
sourcetree = arguments[0]
|
||||
|
||||
# Load or regenerate the cross-reference database
|
||||
load_context(sourcetree)
|
||||
|
||||
if not checkincludes:
|
||||
# OK, now filter the database
|
||||
keys = filter(myfilter, xrefs.keys())
|
||||
keys.sort()
|
||||
|
||||
# If invert was specified, invert the database so it's keyed by file
|
||||
if invert:
|
||||
inverted = {}
|
||||
for key in keys:
|
||||
for file in xrefs[key]:
|
||||
if not inverted.has_key(file):
|
||||
inverted[file] = []
|
||||
if key not in inverted[file]:
|
||||
inverted[file].append(key)
|
||||
xrefs = inverted
|
||||
keys = inverted.keys()
|
||||
keys.sort()
|
||||
|
||||
if genpatch:
|
||||
for file in keys:
|
||||
generate_patch(file, xrefs[file])
|
||||
elif checkincludes:
|
||||
includecheck(sourcetree)
|
||||
elif examine:
|
||||
shortname = namestrip(examine)
|
||||
if not rulebase.dictionary.has_key(shortname) and not mycml1types.has_key(examine):
|
||||
print "%s: no such symbol" % examine
|
||||
else:
|
||||
print "%s: a=%d c=%d h=%d o=%d n=%d m=%d d=%d x=%s f=%d" % (examine, cml1_declared(examine), in_code(examine), in_help(examine), in_cml1(examine), in_cml2(examine), in_makefile(examine), in_defconfig(examine), is_derived(examine), myfilter(examine))
|
||||
elif typecheck:
|
||||
print "CML1 type consistency report:"
|
||||
hits = []
|
||||
ok = 0
|
||||
for (key, item) in mycml1types.items():
|
||||
if len(item) == 1:
|
||||
ok += 1
|
||||
else:
|
||||
hits.append(key)
|
||||
print "%d symbols have consistent type declarations." % ok
|
||||
if hits:
|
||||
print "Non-declared or multiply-declared symbols:"
|
||||
for symbol in hits:
|
||||
print "%s:" % symbol
|
||||
for (type, locs) in mycml1types[symbol]:
|
||||
print " %-8s: %s" % (type, " ".join(locs))
|
||||
print "CML2 type cross-check:"
|
||||
typematch = 0
|
||||
missing = 0
|
||||
matching = 0
|
||||
typemap = {"bool":"bool", "trit":"tristate", "string":"string", "decimal":"int", "hexadecimal":"hex"}
|
||||
for (key, item) in mycml1types.items():
|
||||
if not rulebase.dictionary.has_key(namestrip(key)):
|
||||
missing += 1
|
||||
continue
|
||||
elif len(item) != 1:
|
||||
continue
|
||||
cml2symbol = rulebase.dictionary[namestrip(key)]
|
||||
cml1type = item[0][0]
|
||||
if typemap[cml2symbol.type] == cml1type:
|
||||
matching += 1
|
||||
elif cml2symbol.menu and cml2symbol.menu.type=="choices" and cml1type=="choice":
|
||||
matching += 1
|
||||
else:
|
||||
if cml2symbol.is_derived():
|
||||
derived = "(derived)"
|
||||
else:
|
||||
derived = ""
|
||||
print '"%s", line %d: %s, %s -> %s %s' % (cml2symbol.file, cml2symbol.lineno, key, item[0][0], cml2symbol.type, derived)
|
||||
print "%d CML1 symbols missing, %d type matches" % (missing, matching)
|
||||
else:
|
||||
# OK, list the filtered symbols
|
||||
try:
|
||||
report(keys, norefs)
|
||||
except (KeyboardInterrupt, IOError):
|
||||
pass # In case we break a pipe by interrupting
|
||||
|
||||
# That's all, folks!
|
||||
479
tools/cml2-tools/tree.py
Normal file
479
tools/cml2-tools/tree.py
Normal file
@@ -0,0 +1,479 @@
|
||||
# tree.py -- highly optimized tkinter tree control
|
||||
# by Charles E. "Gene" Cash (gcash@magicnet.net)
|
||||
#
|
||||
# 98/12/02 CEC started
|
||||
# 99/??/?? CEC release to comp.lang.python.announce
|
||||
# Trimmed for CML2 by ESR, December 2001 (cut-paste support removed).
|
||||
import os, string
|
||||
from Tkinter import *
|
||||
|
||||
# this is initialized later, after Tkinter is started
|
||||
open_icon=None
|
||||
|
||||
# tree node helper class
|
||||
class Node:
|
||||
# initialization creates node, draws it, and binds mouseclicks
|
||||
def __init__(self, parent, name, id, myclosed_icon, myopen_icon, x, y,
|
||||
parentwidget):
|
||||
self.parent=parent # immediate parent node
|
||||
self.name=name # name displayed on the label
|
||||
self.id=id # internal id used to manipulate things
|
||||
self.open_icon=myopen_icon # bitmaps to be displayed
|
||||
self.closed_icon=myclosed_icon
|
||||
self.widget=parentwidget # tree widget we belong to
|
||||
self.subnodes=[] # our list of child nodes
|
||||
self.spinlock=0 # cheap mutex spinlock
|
||||
self.open_flag=0 # closed to start with
|
||||
# draw horizontal connecting lines
|
||||
if self.widget.lineflag:
|
||||
self.line=self.widget.create_line(x-self.widget.distx, y, x, y)
|
||||
# draw approprate image
|
||||
if self.open_flag:
|
||||
self.symbol=self.widget.create_image(x, y, image=self.open_icon)
|
||||
else:
|
||||
self.symbol=self.widget.create_image(x, y, image=self.closed_icon)
|
||||
# add label
|
||||
self.label=self.widget.create_text(x+self.widget.textoff, y,
|
||||
text=self.name, justify='left',
|
||||
anchor='w' )
|
||||
# single-click to expand/collapse
|
||||
self.widget.tag_bind(self.symbol, '<1>', self.click)
|
||||
self.widget.tag_bind(self.label, '<1>', self.click) # njr
|
||||
# call customization hook
|
||||
if self.widget.init_hook:
|
||||
self.widget.init_hook(self)
|
||||
|
||||
# def __repr__(self):
|
||||
# return 'Node: %s Parent: %s (%d children)' % \
|
||||
# (self.name, self.parent.name, len(self.subnodes))
|
||||
|
||||
# recursively delete subtree & clean up cyclic references
|
||||
def _delete(self):
|
||||
for i in self.subnodes:
|
||||
if i.open_flag and i.subnodes:
|
||||
# delete vertical connecting line
|
||||
if self.widget.lineflag:
|
||||
self.widget.delete(i.tree)
|
||||
# delete node's subtree, if any
|
||||
i._delete()
|
||||
# the following unbinding hassle is because tkinter
|
||||
# keeps a callback reference for each binding
|
||||
# so if we want things GC'd...
|
||||
for j in (i.symbol, i.label):
|
||||
for k in self.widget.tag_bind(j):
|
||||
self.widget.tag_unbind(j, k)
|
||||
try:
|
||||
for k in self.widget._tagcommands.get(j, []):
|
||||
self.widget.deletecommand(k)
|
||||
self.widget._tagcommands[j].remove(k)
|
||||
except: # XXX not needed in >= 1.6?
|
||||
pass
|
||||
# delete widgets from canvas
|
||||
self.widget.delete(i.symbol, i.label)
|
||||
if self.widget.lineflag:
|
||||
self.widget.delete(i.line)
|
||||
# break cyclic reference
|
||||
i.parent=None
|
||||
# move cursor if it's in deleted subtree
|
||||
if self.widget.pos in self.subnodes:
|
||||
self.widget.move_cursor(self)
|
||||
# now subnodes will be properly garbage collected
|
||||
self.subnodes=[]
|
||||
|
||||
# move everything below current icon, to make room for subtree
|
||||
# using the magic of item tags
|
||||
def _tagmove(self, dist):
|
||||
# mark everything below current node as movable
|
||||
bbox1=self.widget.bbox(self.widget.root.symbol, self.label)
|
||||
bbox2=self.widget.bbox('all')
|
||||
self.widget.dtag('move')
|
||||
self.widget.addtag('move', 'overlapping',
|
||||
bbox2[0], bbox1[3], bbox2[2], bbox2[3])
|
||||
# untag cursor & node so they don't get moved too
|
||||
# this has to be done under Tk on X11
|
||||
self.widget.dtag(self.widget.cursor_box, 'move')
|
||||
self.widget.dtag(self.symbol, 'move')
|
||||
self.widget.dtag(self.label, 'move')
|
||||
# now do the move of all the tagged objects
|
||||
self.widget.move('move', 0, dist)
|
||||
# fix up connecting lines
|
||||
if self.widget.lineflag:
|
||||
n=self
|
||||
while n:
|
||||
if len(n.subnodes):
|
||||
# position of current icon
|
||||
x1, y1=self.widget.coords(n.symbol)
|
||||
# position of last node in subtree
|
||||
x2, y2=self.widget.coords(n.subnodes[-1:][0].symbol)
|
||||
self.widget.coords(n.tree, x1, y1, x1, y2)
|
||||
n=n.parent
|
||||
|
||||
# return list of subnodes that are expanded (not including self)
|
||||
# only includes unique leaf nodes (e.g. /home and /home/root won't
|
||||
# both be included) so expand() doesn't get called unnecessarily
|
||||
# thank $DEITY for Dr. Dutton's Data Structures classes at UCF!
|
||||
def expanded(self):
|
||||
# push initial node into stack
|
||||
stack=[(self, (self.id,))]
|
||||
list=[]
|
||||
while stack:
|
||||
# pop from stack
|
||||
p, i=stack[-1:][0]
|
||||
del stack[-1:]
|
||||
# flag to discard non-unique sub paths
|
||||
flag=1
|
||||
# check all children
|
||||
for n in p.subnodes:
|
||||
# if expanded, push onto stack
|
||||
if n.open_flag:
|
||||
flag=0
|
||||
stack.append((n, i+(n.id,)))
|
||||
# if we reached end of path, add to list
|
||||
if flag:
|
||||
list.append(i[1:])
|
||||
return list
|
||||
|
||||
# get full name, including names of all parents
|
||||
def full_id(self):
|
||||
if self.parent:
|
||||
return self.parent.full_id()+(self.id,)
|
||||
else:
|
||||
return (self.id,)
|
||||
|
||||
# expanding/collapsing folders
|
||||
def toggle_state(self, state=None):
|
||||
if self.widget.toggle_init_hook:
|
||||
self.widget.toggle_init_hook(self)
|
||||
if not self.open_icon:
|
||||
return # not an expandable folder
|
||||
if state == None:
|
||||
state = not self.open_flag # toggle to other state
|
||||
else:
|
||||
# are we already in the state we want to be?
|
||||
if (not state) == (not self.open_flag):
|
||||
return
|
||||
# not re-entrant
|
||||
# acquire mutex
|
||||
while self.spinlock:
|
||||
pass
|
||||
self.spinlock=1
|
||||
# call customization hook
|
||||
if self.widget.before_hook:
|
||||
self.widget.before_hook(self)
|
||||
# if we're closed, expand & draw our subtrees
|
||||
if not self.open_flag:
|
||||
self.open_flag=1
|
||||
self.widget.itemconfig(self.symbol, image=self.open_icon)
|
||||
# get contents of subdirectory or whatever
|
||||
contents=self.widget.get_contents(self)
|
||||
# move stuff to make room
|
||||
self._tagmove(self.widget.disty*len(contents))
|
||||
# now draw subtree
|
||||
self.subnodes=[]
|
||||
# get current position of icon
|
||||
x, y=self.widget.coords(self.symbol)
|
||||
yp=y
|
||||
for i in contents:
|
||||
# add new subnodes, they'll draw themselves
|
||||
yp=yp+self.widget.disty
|
||||
self.subnodes.append(Node(self, i[0], i[1], i[2], i[3],
|
||||
x+self.widget.distx, yp,
|
||||
self.widget))
|
||||
# the vertical line spanning the subtree
|
||||
if self.subnodes and self.widget.lineflag:
|
||||
self.tree=self.widget.create_line(x, y,
|
||||
x, y+self.widget.disty*len(self.subnodes))
|
||||
self.widget.lower(self.tree, self.symbol)
|
||||
# if we're open, collapse and delete subtrees
|
||||
elif self.open_flag:
|
||||
self.open_flag=0
|
||||
self.widget.itemconfig(self.symbol, image=self.closed_icon)
|
||||
# if we have any children
|
||||
if self.subnodes:
|
||||
# recursively delete subtree icons
|
||||
self._delete()
|
||||
# delete vertical line
|
||||
if self.widget.lineflag:
|
||||
self.widget.delete(self.tree)
|
||||
# find next (vertically-speaking) node
|
||||
n=self
|
||||
while n.parent:
|
||||
# position of next sibling in parent's list
|
||||
i=n.parent.subnodes.index(n)+1
|
||||
if i < len(n.parent.subnodes):
|
||||
n=n.parent.subnodes[i]
|
||||
break
|
||||
n=n.parent
|
||||
if n.parent:
|
||||
# move everything up so that distance to next subnode is
|
||||
# correct
|
||||
x1, y1=self.widget.coords(self.symbol)
|
||||
x2, y2=self.widget.coords(n.symbol)
|
||||
dist=y2-y1-self.widget.disty
|
||||
self._tagmove(-dist)
|
||||
# update scroll region for new size
|
||||
x1, y1, x2, y2=self.widget.bbox('all')
|
||||
self.widget.configure(scrollregion=(x1, y1, x2+5, y2+5))
|
||||
# call customization hook
|
||||
if self.widget.after_hook:
|
||||
print 'calling after_hook'
|
||||
self.widget.after_hook(self)
|
||||
# release mutex
|
||||
self.spinlock=0
|
||||
|
||||
# expand this subnode
|
||||
# doesn't have to exist, it expands what part of the path DOES exist
|
||||
def expand(self, dirs):
|
||||
# if collapsed, then expand
|
||||
self.toggle_state(1)
|
||||
# find next subnode
|
||||
if dirs:
|
||||
for n in self.subnodes:
|
||||
if n.id == dirs[0]:
|
||||
return n.expand(dirs[1:])
|
||||
print "Can't find path %s in %s" % (dirs, self.id)
|
||||
print "- Available subnodes: %s" % map(lambda n: n.id, self.subnodes)
|
||||
return self
|
||||
|
||||
# handle mouse clicks by moving cursor and toggling folder state
|
||||
def click(self, dummy):
|
||||
self.widget.move_cursor(self)
|
||||
self.toggle_state()
|
||||
|
||||
# return next lower visible node
|
||||
def next(self):
|
||||
n=self
|
||||
if n.subnodes:
|
||||
# if you can go right, do so
|
||||
return n.subnodes[0]
|
||||
while n.parent:
|
||||
# move to next sibling
|
||||
i=n.parent.subnodes.index(n)+1
|
||||
if i < len(n.parent.subnodes):
|
||||
return n.parent.subnodes[i]
|
||||
# if no siblings, move to parent's sibling
|
||||
n=n.parent
|
||||
# we're at bottom
|
||||
return self
|
||||
|
||||
# return next higher visible node
|
||||
def prev(self):
|
||||
n=self
|
||||
if n.parent:
|
||||
# move to previous sibling
|
||||
i=n.parent.subnodes.index(n)-1
|
||||
if i >= 0:
|
||||
# move to last child
|
||||
n=n.parent.subnodes[i]
|
||||
while n.subnodes:
|
||||
n=n.subnodes[-1]
|
||||
else:
|
||||
# punt if there's no previous sibling
|
||||
if n.parent:
|
||||
n=n.parent
|
||||
return n
|
||||
|
||||
class Tree(Canvas):
|
||||
def __init__(self, master, rootname, rootlabel=None, openicon=None,
|
||||
shuticon=None, getcontents=None, init=None,
|
||||
toggle_init=None,before=None, after=None, cut=None, paste=None,
|
||||
distx=15, disty=15, textoff=10, lineflag=1, **kw_args):
|
||||
global open_icon, shut_icon, file_icon,yes_icon,no_icon
|
||||
# pass args to superclass
|
||||
apply(Canvas.__init__, (self, master), kw_args)
|
||||
# try creating an image, work around Tkinter bug
|
||||
# ('global' should do it, but it doesn't)
|
||||
if open_icon is not None:
|
||||
try:
|
||||
item = self.create_image(0,0,image=open_icon)
|
||||
self.delete(item)
|
||||
except:
|
||||
print "recreating Tree PhotoImages"
|
||||
open_icon = None # need to recreate PhotoImages
|
||||
# default images (BASE64-encoded GIF files)
|
||||
# we have to delay initialization until Tk starts up or PhotoImage()
|
||||
# complains (otherwise I'd just put it up top)
|
||||
if open_icon == None:
|
||||
open_icon=PhotoImage(
|
||||
data='R0lGODlhEAANAKIAAAAAAMDAwICAgP//////ADAwMAAAAAAA' \
|
||||
'ACH5BAEAAAEALAAAAAAQAA0AAAM6GCrM+jCIQamIbw6ybXNSx3GVB' \
|
||||
'YRiygnA534Eq5UlO8jUqLYsquuy0+SXap1CxBHr+HoBjoGndDpNAAA7')
|
||||
shut_icon=PhotoImage(
|
||||
data='R0lGODlhDwANAKIAAAAAAMDAwICAgP//////ADAwMAAAAAAA' \
|
||||
'ACH5BAEAAAEALAAAAAAPAA0AAAMyGCHM+lAMMoeAT9Jtm5NDKI4Wo' \
|
||||
'FXcJphhipanq7Kvu8b1dLc5tcuom2foAQQAyKRSmQAAOw==')
|
||||
file_icon=PhotoImage(
|
||||
data='R0lGODlhCwAOAJEAAAAAAICAgP///8DAwCH5BAEAAAMALAAA' \
|
||||
'AAALAA4AAAIphA+jA+JuVgtUtMQePJlWCgSN9oSTV5lkKQpo2q5W+' \
|
||||
'wbzuJrIHgw1WgAAOw==')
|
||||
yes_icon=PhotoImage(
|
||||
data='R0lGODlhDAAPAKEAAP////9FRAAAAP///yH5BAEKAAMALAAA' \
|
||||
'AAAMAA8AAAIrhI8zyKAWUARCQGnqPVODuXlg0FkQ+WUmUzYpZYKv9'\
|
||||
'5Eg7VZKxffC7usVAAA7')
|
||||
no_icon=PhotoImage(
|
||||
data='R0lGODlhDAAPAKEAAP///wAAAERe/////yH+FUNyZWF0ZWQgd' \
|
||||
'2l0aCBUaGUgR0lNUAAh+QQBCgADACwAAAAADAAPAAACLISPM8i' \
|
||||
'gjUIAolILpDB70zxxnieJBxl6n1FiGLiuW4tgJcSGuKRI/h/oAX8FADs=')
|
||||
# function to return subnodes (not very much use w/o this)
|
||||
if not getcontents:
|
||||
raise ValueError, 'must have "get_contents" function'
|
||||
self.get_contents=getcontents
|
||||
# horizontal distance that subtrees are indented
|
||||
self.distx=distx
|
||||
# vertical distance between rows
|
||||
self.disty=disty
|
||||
# how far to offset text label
|
||||
self.textoff=textoff
|
||||
# called after new node initialization
|
||||
self.init_hook=init
|
||||
# called right after toggle state
|
||||
self.toggle_init_hook=toggle_init
|
||||
# called just before subtree expand/collapse
|
||||
self.before_hook=before
|
||||
# called just after subtree expand/collapse
|
||||
self.after_hook=after
|
||||
# flag to display lines
|
||||
self.lineflag=lineflag
|
||||
# create root node to get the ball rolling
|
||||
if openicon:
|
||||
oi = openicon
|
||||
else:
|
||||
oi = open_icon
|
||||
if shuticon:
|
||||
si = shuticon
|
||||
else:
|
||||
si = shut_icon
|
||||
if rootlabel:
|
||||
self.root=Node(None, rootlabel, rootname, si, oi, 11, 11, self)
|
||||
else:
|
||||
self.root=Node(None, rootname, rootname, si, oi, 11, 11, self)
|
||||
# configure for scrollbar(s)
|
||||
x1, y1, x2, y2=self.bbox('all')
|
||||
self.configure(scrollregion=(x1, y1, x2+5, y2+5))
|
||||
# add a cursor
|
||||
self.cursor_box=self.create_rectangle(0, 0, 0, 0)
|
||||
self.move_cursor(self.root)
|
||||
# make it easy to point to control
|
||||
self.bind('<Enter>', self.mousefocus)
|
||||
# bindings similar to those used by Microsoft tree control
|
||||
# page-up/page-down
|
||||
self.bind('<Next>', self.pagedown)
|
||||
self.bind('<Prior>', self.pageup)
|
||||
# arrow-up/arrow-down
|
||||
self.bind('<Down>', self.next)
|
||||
self.bind('<Up>', self.prev)
|
||||
# arrow-left/arrow-right
|
||||
self.bind('<Left>', self.ascend)
|
||||
# (hold this down and you expand the entire tree)
|
||||
self.bind('<Right>', self.descend)
|
||||
# home/end
|
||||
self.bind('<Home>', self.first)
|
||||
self.bind('<End>', self.last)
|
||||
# space bar
|
||||
self.bind('<Key-space>', self.toggle)
|
||||
|
||||
# scroll (in a series of nudges) so items are visible
|
||||
def see(self, *items):
|
||||
x1, y1, x2, y2=apply(self.bbox, items)
|
||||
while x2 > self.canvasx(0)+self.winfo_width():
|
||||
old=self.canvasx(0)
|
||||
self.xview('scroll', 1, 'units')
|
||||
# avoid endless loop if we can't scroll
|
||||
if old == self.canvasx(0):
|
||||
break
|
||||
while y2 > self.canvasy(0)+self.winfo_height():
|
||||
old=self.canvasy(0)
|
||||
self.yview('scroll', 1, 'units')
|
||||
if old == self.canvasy(0):
|
||||
break
|
||||
# done in this order to ensure upper-left of object is visible
|
||||
while x1 < self.canvasx(0):
|
||||
old=self.canvasx(0)
|
||||
self.xview('scroll', -1, 'units')
|
||||
if old == self.canvasx(0):
|
||||
break
|
||||
while y1 < self.canvasy(0):
|
||||
old=self.canvasy(0)
|
||||
self.yview('scroll', -1, 'units')
|
||||
if old == self.canvasy(0):
|
||||
break
|
||||
|
||||
# move cursor to node
|
||||
def move_cursor(self, node):
|
||||
self.pos=node
|
||||
x1, y1, x2, y2=self.bbox(node.symbol, node.label)
|
||||
self.coords(self.cursor_box, x1-1, y1-1, x2+1, y2+1)
|
||||
self.see(node.symbol, node.label)
|
||||
|
||||
# expand given path
|
||||
# note that the convention used in this program to identify a
|
||||
# particular node is to give a tuple listing it's id and parent ids
|
||||
# so you probably want to use os.path.split() a lot
|
||||
def expand(self, path):
|
||||
return self.root.expand(path[1:])
|
||||
|
||||
# soak up event argument when moused-over
|
||||
# could've used lambda but didn't...
|
||||
def mousefocus(self, event):
|
||||
self.focus_set()
|
||||
|
||||
# open/close subtree
|
||||
def toggle(self, event=None):
|
||||
self.pos.toggle_state()
|
||||
|
||||
# move to next lower visible node
|
||||
def next(self, event=None):
|
||||
self.move_cursor(self.pos.next())
|
||||
|
||||
# move to next higher visible node
|
||||
def prev(self, event=None):
|
||||
self.move_cursor(self.pos.prev())
|
||||
|
||||
# move to immediate parent
|
||||
def ascend(self, event=None):
|
||||
if self.pos.parent:
|
||||
# move to parent
|
||||
self.move_cursor(self.pos.parent)
|
||||
|
||||
# move right, expanding as we go
|
||||
def descend(self, event=None):
|
||||
self.pos.toggle_state(1)
|
||||
if self.pos.subnodes:
|
||||
# move to first subnode
|
||||
self.move_cursor(self.pos.subnodes[0])
|
||||
else:
|
||||
# if no subnodes, move to next sibling
|
||||
self.next()
|
||||
|
||||
# go to root
|
||||
def first(self, event=None):
|
||||
# move to root node
|
||||
self.move_cursor(self.root)
|
||||
|
||||
# go to last visible node
|
||||
def last(self, event=None):
|
||||
# move to bottom-most node
|
||||
n=self.root
|
||||
while n.subnodes:
|
||||
n=n.subnodes[-1]
|
||||
self.move_cursor(n)
|
||||
|
||||
# previous page
|
||||
def pageup(self, event=None):
|
||||
n=self.pos
|
||||
j=self.winfo_height()/self.disty
|
||||
for i in range(j-3):
|
||||
n=n.prev()
|
||||
self.yview('scroll', -1, 'pages')
|
||||
self.move_cursor(n)
|
||||
|
||||
# next page
|
||||
def pagedown(self, event=None):
|
||||
n=self.pos
|
||||
j=self.winfo_height()/self.disty
|
||||
for i in range(j-3):
|
||||
n=n.next()
|
||||
self.yview('scroll', 1, 'pages')
|
||||
self.move_cursor(n)
|
||||
|
||||
# End
|
||||
144
tools/cml2header.py
Executable file
144
tools/cml2header.py
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import StringIO
|
||||
import getopt
|
||||
|
||||
class cml2header_translator:
|
||||
'''
|
||||
Translates a cml2 configuration file to a C header file
|
||||
that can be included in source code.
|
||||
'''
|
||||
def __init__(self):
|
||||
self.isnotset = re.compile("^# (.*) is not set")
|
||||
|
||||
def linetrans(self, instream, outstream, trailer=None):
|
||||
'''
|
||||
Translates a stream line-by-line
|
||||
'''
|
||||
if not hasattr(instream, "readline"):
|
||||
instream = open(instream, "r")
|
||||
if not hasattr(outstream, "readline"):
|
||||
outstream = open(outstream, "w")
|
||||
while 1:
|
||||
line = instream.readline()
|
||||
if not line:
|
||||
break
|
||||
new = self.write_include(line)
|
||||
if new:
|
||||
outstream.write(new)
|
||||
instream.close()
|
||||
if trailer:
|
||||
outstream.write(trailer)
|
||||
outstream.close()
|
||||
|
||||
def write_include(self, line):
|
||||
'''
|
||||
Translate a line of CML2 config file statement into
|
||||
C preprocessor #define format.
|
||||
'''
|
||||
if line.find("PRIVATE") > -1 or line[:2] == "$$":
|
||||
return ""
|
||||
match = self.isnotset.match(line)
|
||||
if match:
|
||||
return "#undef %s\n" % match.group(1)
|
||||
if line == "#\n":
|
||||
return None
|
||||
elif line[0] == "#":
|
||||
return "/* " + line[1:].strip() + " */\n"
|
||||
eq = line.find("=")
|
||||
if eq == -1:
|
||||
return line
|
||||
else:
|
||||
line = line.split('#')[0]
|
||||
symbol = line[:eq]
|
||||
value = line[eq+1 :].strip()
|
||||
if value == 'y':
|
||||
return "#define %s 1\n" % symbol
|
||||
elif value == 'm':
|
||||
return "#undef %s\n#define %s_MODULE 1\n" % (symbol, symbol)
|
||||
elif value == 'n':
|
||||
return "#undef %s\n" % symbol
|
||||
else:
|
||||
return "#define %s %s\n" % (symbol, value)
|
||||
|
||||
def cml_configfile_exists(self, path):
|
||||
if not os.path.exists(path):
|
||||
print "CML2 configuration file not found."
|
||||
print "Given path:", path, "does not exist."
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
def translate(self, configfile_path = None, headerfile_path = None):
|
||||
|
||||
if configfile_path == None:
|
||||
configfile_path = os.getcwd() + '/' + "config.out"
|
||||
|
||||
if headerfile_path == None:
|
||||
headerfile_path = os.getcwd() + '/' + "config.h"
|
||||
'''
|
||||
Takes a configuration file generated by the cml2 configurator,
|
||||
and converts it to a C header file. This header file is then
|
||||
included by the kernel sources and the preprocessor definitions
|
||||
in it are used during the compilation of the kernel.
|
||||
'''
|
||||
if not self.cml_configfile_exists(configfile_path):
|
||||
print "Failed to translate cml configuration file", configfile_path, 'to', headerfile_path
|
||||
return -1
|
||||
|
||||
self.linetrans(configfile_path, headerfile_path)
|
||||
return 0
|
||||
|
||||
def usage(self):
|
||||
helpstr = \
|
||||
'''\
|
||||
Description:
|
||||
cml2header.py takes a cml2 configuration file as input and generates
|
||||
a valid C header file to be included in C source code.
|
||||
|
||||
Usage:
|
||||
cml2header.py -i inputfile -o outputfile [-h]
|
||||
|
||||
Options:
|
||||
-i The CML2 configuration file (config.out by default)
|
||||
-o The name of the header file (config.h by default)
|
||||
-h This help message
|
||||
'''
|
||||
print helpstr
|
||||
|
||||
def main():
|
||||
translator = cml2header_translator()
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "i:o:h:")
|
||||
except getopt.GetoptError:
|
||||
# print help information and exit:
|
||||
translator.usage()
|
||||
sys.exit(2)
|
||||
|
||||
ofile = None
|
||||
ifile = None
|
||||
|
||||
# Print help message if requested and quit
|
||||
if '-h' in opts:
|
||||
translator.usage()
|
||||
sys.exit()
|
||||
|
||||
# Get the input and output filenames.
|
||||
for switch, val in opts:
|
||||
if switch == '-i':
|
||||
ifile = val
|
||||
if switch == '-o':
|
||||
ofile = val
|
||||
|
||||
# Translate
|
||||
if translator.translate(ifile, ofile) < 0:
|
||||
print "Translation of", ifile, 'to', ofile, 'failed.'
|
||||
|
||||
# Run the main routine only if this file is explicitly executed.
|
||||
# (i.e. not referred from another module)
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
BIN
tools/cml2header.pyc
Normal file
BIN
tools/cml2header.pyc
Normal file
Binary file not shown.
6
tools/helper-scripts/replace_words
Executable file
6
tools/helper-scripts/replace_words
Executable file
@@ -0,0 +1,6 @@
|
||||
|
||||
for filename in `find ./ -name '*.[ch]'`
|
||||
do
|
||||
sed -i -e "s/ORIGINAL/REPLACEMENT/g" $filename
|
||||
done
|
||||
|
||||
63
tools/ksym_to_lds.py
Executable file
63
tools/ksym_to_lds.py
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/python
|
||||
import os
|
||||
import sys
|
||||
from string import atoi
|
||||
from os import popen2
|
||||
from os.path import join
|
||||
|
||||
symbols = ['bkpt_phys_to_virt']
|
||||
builddir = "build"
|
||||
loaderdir = "loader"
|
||||
image = "start.axf"
|
||||
imgpath = join(builddir, image)
|
||||
asmfile = join(loaderdir, image) + ".S"
|
||||
symfile = "ksyms"
|
||||
asmheader = \
|
||||
'''
|
||||
/*
|
||||
* %s autogenerated from %s
|
||||
*
|
||||
* This file is included by the loader sources so that any
|
||||
* kernel symbol address can be known in advance and stopped
|
||||
* at by debuggers before virtual memory is enabled.
|
||||
*/
|
||||
'''
|
||||
|
||||
asmcontent = \
|
||||
'''
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
.global %s;
|
||||
.type %s, function;
|
||||
.equ %s, %s
|
||||
|
||||
'''
|
||||
def virt_to_phys(addr):
|
||||
return hex(int(addr, 16) - 0xF0000000)[:-1]
|
||||
|
||||
def ksym_to_lds():
|
||||
asm = open(asmfile, "w+")
|
||||
asm.write(asmheader % (asmfile, imgpath))
|
||||
cmd = "arm-none-eabi-objdump -d " + imgpath + " | grep " + "\\<" + symbols[0] + "\\>" + " > " + symfile
|
||||
os.system(cmd)
|
||||
kf = open(symfile, "r")
|
||||
#child_out, child_in = popen2(cmd)
|
||||
while True:
|
||||
line = kf.readline()
|
||||
if len(line) is 0:
|
||||
break
|
||||
addr, sym = line.split()
|
||||
sym = sym[1:-2]
|
||||
addr = "0x" + addr
|
||||
addr = virt_to_phys(addr)
|
||||
if sym in symbols:
|
||||
print "Adding " + sym + " from " + imgpath + " to " + asmfile + " in physical"
|
||||
asm.write(asmcontent % (sym, sym, sym, addr))
|
||||
asm.close()
|
||||
kf.close()
|
||||
cmd = "rm -rf " + symfile
|
||||
os.system(cmd)
|
||||
if __name__ == "__main__":
|
||||
ksym_to_lds()
|
||||
|
||||
14
tools/l4-qemu
Executable file
14
tools/l4-qemu
Executable file
@@ -0,0 +1,14 @@
|
||||
cd build
|
||||
#arm-none-eabi-insight &
|
||||
qemu-system-arm -M versatilepb -kernel final.axf -nographic -s &
|
||||
sleep 0.5
|
||||
arm-none-eabi-insight ; pkill qemu-system-arm
|
||||
#arm-none-eabi-gdb ; pkill qemu-system-arm
|
||||
cd ..
|
||||
|
||||
|
||||
# TODO:
|
||||
# insight works ok if gdbinit script is run from the insight
|
||||
# command window. Then one needs to break at bkpt_phys_to_virt and do:
|
||||
# sym start.axf
|
||||
# Then continue execution to see all symbols. This must be done automatic.
|
||||
0
tools/pyelf/__init__.py
Normal file
0
tools/pyelf/__init__.py
Normal file
327
tools/pyelf/aistruct.py
Normal file
327
tools/pyelf/aistruct.py
Normal file
@@ -0,0 +1,327 @@
|
||||
import mmap
|
||||
import struct
|
||||
import os
|
||||
|
||||
class GFile(object):
|
||||
def __init__(self, filename, size):
|
||||
handle = open(filename, 'rb+')
|
||||
self.mapping = mmap.mmap(handle.fileno(), size)
|
||||
|
||||
def set_byte_ordering(self, ordering):
|
||||
self.byte_ordering = ordering
|
||||
|
||||
def get_byte_ordering(self):
|
||||
return self.byte_ordering
|
||||
|
||||
def create_new(filename, size):
|
||||
assert size < 10240 # This is only for testing, really
|
||||
handle = open(filename, 'wb')
|
||||
handle.write('\0' * size)
|
||||
handle.close()
|
||||
return GFile(filename, size)
|
||||
create_new = staticmethod(create_new)
|
||||
|
||||
def existing(filename):
|
||||
assert os.path.exists(filename)
|
||||
size = os.path.getsize(filename)
|
||||
return GFile(filename, size)
|
||||
existing = staticmethod(existing)
|
||||
|
||||
class BitPoker(object):
|
||||
""" """
|
||||
SIZE_MAP = {1: 'B',
|
||||
2: 'H',
|
||||
4: 'I',
|
||||
8: 'Q'}
|
||||
def set_mmapfile(self, mmapfile, offset_bytes = 0):
|
||||
self.mmapfile = mmapfile
|
||||
self.master_offset_bytes = offset_bytes
|
||||
|
||||
def set_byte_ordering(self, byte_ordering):
|
||||
self.byte_ordering = byte_ordering
|
||||
self.struct_byte_ordering = {'lsb': '<', 'msb': '>'}[byte_ordering]
|
||||
|
||||
def get_byte_ordering(self):
|
||||
return self.byte_ordering
|
||||
|
||||
def read_value(self, numbytes, offset_bytes):
|
||||
# Seek to the right spot using absolute (whence=0) positioning
|
||||
self.mmapfile.seek(offset_bytes + self.master_offset_bytes, 0)
|
||||
data = self.mmapfile.read(numbytes)
|
||||
return struct.unpack('%s%s' % (self.struct_byte_ordering,
|
||||
self.SIZE_MAP[numbytes]), data)
|
||||
|
||||
def write_value_sized(self, value, numbytes, offset_bytes):
|
||||
data = struct.pack('%s%s' % (self.struct_byte_ordering,
|
||||
self.SIZE_MAP[numbytes]), value)
|
||||
self.mmapfile.seek(offset_bytes + self.master_offset_bytes, 0)
|
||||
self.mmapfile.write(data)
|
||||
|
||||
def read_value_sized(self, numbytes, offset_bytes):
|
||||
self.mmapfile.seek(offset_bytes + self.master_offset_bytes, 0)
|
||||
raw_data = self.mmapfile.read(numbytes)
|
||||
data = struct.unpack('%s%s' % (self.struct_byte_ordering,
|
||||
self.SIZE_MAP[numbytes]), raw_data)
|
||||
return data[0]
|
||||
|
||||
def read_c_string(self, offset_bytes):
|
||||
self.mmapfile.seek(offset_bytes + self.master_offset_bytes.get(), 0)
|
||||
result = ''
|
||||
c = self.mmapfile.read_byte()
|
||||
while c != '\0':
|
||||
result += c
|
||||
c = self.mmapfile.read_byte()
|
||||
return result
|
||||
|
||||
def new_with_gfile(gfile, offset):
|
||||
poker = BitPoker()
|
||||
poker.set_mmapfile(gfile.mapping, offset)
|
||||
poker.set_byte_ordering(gfile.get_byte_ordering())
|
||||
return poker
|
||||
new_with_gfile = staticmethod(new_with_gfile)
|
||||
|
||||
def new_with_poker(origpoker, offset):
|
||||
poker = BitPoker()
|
||||
poker.set_mmapfile(origpoker.mmapfile, origpoker.master_offset_bytes + offset)
|
||||
poker.set_byte_ordering(origpoker.get_byte_ordering())
|
||||
return poker
|
||||
new_with_poker = staticmethod(new_with_poker)
|
||||
|
||||
class AIStruct(object):
|
||||
SIZE32 = 32
|
||||
SIZE64 = 64
|
||||
|
||||
class AIElement(object):
|
||||
_natural_size = None
|
||||
def __init__(self, word_size_in_bits, offset, names, format):
|
||||
self.offset = offset #offset into memory-mapped file.
|
||||
self.set_target_word_size(word_size_in_bits)
|
||||
self.value = 0
|
||||
self.names = names
|
||||
self.format = format
|
||||
|
||||
def set_target_word_size(self, target_word_size):
|
||||
self.target_word_size = target_word_size
|
||||
|
||||
def get_size_bits(self):
|
||||
return self._natural_size
|
||||
|
||||
def set_args(self):
|
||||
pass
|
||||
|
||||
def set(self, value):
|
||||
if type(value) is str and len(value) == 1:
|
||||
value = ord(value)
|
||||
self.value = value
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def write(self, bitpoker):
|
||||
bitpoker.write_value_sized(self.value, self.get_size_bits() / 8,
|
||||
self.offset)
|
||||
|
||||
def read(self, bitpoker):
|
||||
self.value = bitpoker.read_value_sized(self.get_size_bits() / 8, self.offset)
|
||||
|
||||
def __cmp__(self, value):
|
||||
return cmp(self.value, value)
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, AIStruct.AIElement):
|
||||
return self.value + other.value
|
||||
else:
|
||||
return self.value + other
|
||||
|
||||
def __mul__(self, other):
|
||||
if isinstance(other, AIStruct.AIElement):
|
||||
return self.value * other.value
|
||||
else:
|
||||
return self.value * other
|
||||
|
||||
|
||||
def __str__(self):
|
||||
if self.format:
|
||||
if type(self.format) == type(""):
|
||||
return self.format % self.value
|
||||
else:
|
||||
return self.format(self.value)
|
||||
if self.names:
|
||||
return self.names.get(self.value, str(self.value))
|
||||
else:
|
||||
return str(self.value)
|
||||
|
||||
class WORD(AIElement):
|
||||
def get_size_bits(self):
|
||||
return self.target_word_size
|
||||
|
||||
class INT8(AIElement):
|
||||
_natural_size = 8
|
||||
UINT8 = INT8
|
||||
|
||||
class INT16(AIElement):
|
||||
_natural_size = 16
|
||||
UINT16 = INT16
|
||||
|
||||
class INT32(AIElement):
|
||||
_natural_size = 32
|
||||
UINT32 = INT32
|
||||
|
||||
class INT64(AIElement):
|
||||
_natural_size = 64
|
||||
UINT64 = INT64
|
||||
|
||||
class BITFIELD(AIElement):
|
||||
class AttributeBasedProperty(object):
|
||||
def __init__(self, bitfield_inst):
|
||||
self.bitfield_inst = bitfield_inst
|
||||
|
||||
def get_length_and_offset_for_key(self, key):
|
||||
offset = 0
|
||||
length = 0
|
||||
for name, sizes_dict in self.bitfield_inst.components:
|
||||
if name == key:
|
||||
length = sizes_dict[self.bitfield_inst.target_word_size]
|
||||
break
|
||||
offset += sizes_dict[self.bitfield_inst.target_word_size]
|
||||
if length == 0:
|
||||
raise AttributeError(key)
|
||||
return length, offset
|
||||
|
||||
def __getitem__(self, key):
|
||||
length, offset = self.get_length_and_offset_for_key(key)
|
||||
# We have the position of the desired key in value.
|
||||
# Retrieve it by right-shifting by "offset" and
|
||||
# then masking away anything > length.
|
||||
result = self.bitfield_inst.value >> offset
|
||||
result &= ( (1<<length) - 1)
|
||||
if self.bitfield_inst.post_get is not None:
|
||||
return self.bitfield_inst.post_get(key, result)
|
||||
else:
|
||||
return result
|
||||
|
||||
def __setitem__(self, key, new_value):
|
||||
if self.bitfield_inst.pre_set is not None:
|
||||
new_value = self.bitfield_inst.pre_set(key, new_value)
|
||||
length, offset = self.get_length_and_offset_for_key(key)
|
||||
assert new_value < (1 << length)
|
||||
# Generate the result in three stages
|
||||
# ... insert the new value:
|
||||
result = new_value << offset
|
||||
# ... | the stuff above the value:
|
||||
result |= ( self.bitfield_inst.value >> (offset + length) << (offset + length) )
|
||||
# ... | the stuff below the value.
|
||||
result |= ( self.bitfield_inst.value & ( (1 << offset) - 1 ) )
|
||||
self.bitfield_inst.value = result
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AIStruct.BITFIELD, self).__init__(*args, **kwargs)
|
||||
self.components = []
|
||||
self.attribute_based_property = AIStruct.BITFIELD.AttributeBasedProperty(self)
|
||||
|
||||
def set_args(self, components=None, post_get=None, pre_set=None):
|
||||
if components is not None:
|
||||
self.components = components
|
||||
self.post_get = post_get
|
||||
self.pre_set = pre_set
|
||||
|
||||
def get_size_bits(self):
|
||||
size_bits = 0
|
||||
for comp_name, comp_sizes in self.components:
|
||||
size_bits += comp_sizes[self.target_word_size]
|
||||
return size_bits
|
||||
|
||||
def get(self):
|
||||
return self.attribute_based_property
|
||||
|
||||
def set(self, value):
|
||||
raise AttributeError("set() shouldn't be called here!")
|
||||
|
||||
def __init__(self, word_size_in_bits):
|
||||
self.word_size_in_bits = word_size_in_bits
|
||||
self.thestruct = []
|
||||
|
||||
def _setup_attributes(self, allprops):
|
||||
# So Python sucks and you can only set properties on class objects,
|
||||
# not class instance objects. Hence this crud...
|
||||
class AI(object):
|
||||
pass
|
||||
for key in allprops:
|
||||
setattr(AI, key, allprops[key])
|
||||
self.ai = AI()
|
||||
|
||||
def _setup_one(self, etypename, ename, args, offset, names, format):
|
||||
elementclass = getattr(self, etypename)
|
||||
elementinst = elementclass(self.word_size_in_bits, offset, names, format)
|
||||
elementinst.set_args(**args)
|
||||
self.thestruct.append(elementinst)
|
||||
def get_item(obj):
|
||||
return elementinst
|
||||
def set_item(obj, value):
|
||||
return elementinst.set(value)
|
||||
newprop = {'%s' % (ename): property(get_item, set_item)}
|
||||
return (elementinst.get_size_bits() / 8, newprop)
|
||||
|
||||
def _setup_multiprop(self, ename, times):
|
||||
def mp_get(obj):
|
||||
return [getattr(self.ai, '%s_%s' % (ename, counter)) for counter in range(1, times+1)]
|
||||
def mp_set(obj, value):
|
||||
for num, item in enumerate(value):
|
||||
setattr(self.ai, '%s_%s' % (ename, num + 1), item)
|
||||
return {'%s' % (ename): property(mp_get, mp_set)}
|
||||
|
||||
def setup(self, *elements):
|
||||
offset = 0
|
||||
allprops = {}
|
||||
for element in elements:
|
||||
etypename, ename = element[:2]
|
||||
args = {}
|
||||
if len(element) == 3:
|
||||
args = element[2]
|
||||
times = args.get('times', None)
|
||||
names = args.get('names', None)
|
||||
format = args.get('format', None)
|
||||
if names is not None:
|
||||
del args['names']
|
||||
if format is not None:
|
||||
del args['format']
|
||||
if times is not None:
|
||||
del args['times']
|
||||
for time in range(times):
|
||||
size, propdict = self._setup_one(etypename, '%s_%s' % (ename, time + 1), args, offset, names, format)
|
||||
offset += size
|
||||
allprops.update(propdict)
|
||||
allprops.update(self._setup_multiprop(ename, times))
|
||||
else:
|
||||
size, propdict = self._setup_one(etypename, ename, args, offset, names, format)
|
||||
offset += size
|
||||
allprops.update(propdict)
|
||||
|
||||
|
||||
self._setup_attributes(allprops)
|
||||
|
||||
def struct_size(self):
|
||||
size = 0
|
||||
for element in self.thestruct:
|
||||
size += element.get_size_bits()
|
||||
return size / 8
|
||||
|
||||
def set_poker(self, poker):
|
||||
self.poker = poker
|
||||
|
||||
def write(self):
|
||||
for element in self.thestruct:
|
||||
element.write(self.poker)
|
||||
|
||||
def read_from_poker(self, poker):
|
||||
self.set_poker(poker)
|
||||
for element in self.thestruct:
|
||||
element.read(self.poker)
|
||||
|
||||
def write_new(self, filename):
|
||||
"""convenience function to create a new file and store the KCP in it. """
|
||||
newfile = GFile.create_new(filename, self.struct_size())
|
||||
poker = BitPoker('lsb') #FIXME
|
||||
poker.set_mmapfile(newfile.mapping, offset_bytes = 0)
|
||||
self.set_poker(poker)
|
||||
self.write()
|
||||
BIN
tools/pyelf/aistruct.pyc
Normal file
BIN
tools/pyelf/aistruct.pyc
Normal file
Binary file not shown.
84
tools/pyelf/burn.py
Normal file
84
tools/pyelf/burn.py
Normal file
@@ -0,0 +1,84 @@
|
||||
from aistruct import AIStruct
|
||||
import elf, sys
|
||||
from optparse import OptionParser
|
||||
|
||||
|
||||
class AfterBurner(AIStruct):
|
||||
def __init__(self, *args, **kwargs):
|
||||
AIStruct.__init__(self, AIStruct.SIZE32)
|
||||
self.setup(
|
||||
('UINT32', 'addr')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "0x%x" % self.ai.addr.get()
|
||||
|
||||
|
||||
def arch_perms(rwx):
|
||||
# ia32 doesn't support read-noexec
|
||||
if rwx & (1 << 2):
|
||||
rwx |= 1
|
||||
return rwx
|
||||
|
||||
def align_up(value, align):
|
||||
mod = value % align
|
||||
if mod != 0:
|
||||
value += (align - mod)
|
||||
return value
|
||||
|
||||
def gen_pheaders(elf):
|
||||
old_rwx = 0
|
||||
old_offset = 0
|
||||
old_addr = 0
|
||||
old_bits = 0
|
||||
old_size = 0
|
||||
new_addr = 0
|
||||
new_offset = 0
|
||||
new_size = 0
|
||||
for section in [section for section in elf.sheaders if section.allocable()]:
|
||||
# Test - can we add this section to the current program header?
|
||||
new = 0
|
||||
rwx = arch_perms(section.get_perms())
|
||||
addr = section.ai.sh_addr.get()
|
||||
offset = section.ai.sh_offset.get()
|
||||
al = section.ai.sh_addralign.get()
|
||||
size = section.ai.sh_size.get()
|
||||
|
||||
if old_rwx != rwx:
|
||||
new = 1
|
||||
if addr != align_up(old_size + old_addr, al):
|
||||
new = 2
|
||||
if offset != align_up(old_size + old_offset, al):
|
||||
new = 3
|
||||
|
||||
if new != 0:
|
||||
#print hex(new_offset), hex(new_addr), hex(new_size)
|
||||
new_size = size
|
||||
new_addr = addr
|
||||
new_offset = offset
|
||||
else:
|
||||
new_size = (addr + size) - new_addr
|
||||
|
||||
old_rwx = rwx
|
||||
old_size = size
|
||||
old_bits = 0
|
||||
old_offset = offset
|
||||
old_addr = addr
|
||||
#print section.ai.sh_name, section.ai.sh_addr, section.ai.sh_offset, section.ai.sh_size, section.ai.sh_flags, rwx
|
||||
print hex(new_offset), hex(new_addr), hex(new_size)
|
||||
|
||||
def main():
|
||||
wedge = elf.ElfFile.from_file(sys.argv[1])
|
||||
guest = elf.ElfFile.from_file(sys.argv[2])
|
||||
print wedge.pheaders
|
||||
for section in wedge.sheaders:
|
||||
print section.name
|
||||
section.name += ".linux"
|
||||
print section.name
|
||||
#del wedge.pheaders[:]
|
||||
#print wedge.pheaders
|
||||
wedge.write_file("foobar")
|
||||
gen_pheaders(wedge)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
500
tools/pyelf/elf.py
Normal file
500
tools/pyelf/elf.py
Normal file
@@ -0,0 +1,500 @@
|
||||
from aistruct import GFile, BitPoker, AIStruct
|
||||
import StringIO
|
||||
import sys
|
||||
|
||||
class Elf32Header(AIStruct):
|
||||
EI_MAGIC = [0x7f, ord('E'), ord('L'), ord('F')]
|
||||
EI_CLASS_32 = 1
|
||||
EI_CLASS_64 = 2
|
||||
EI_DATA_LSB = 1
|
||||
EI_DATA_MSB = 2
|
||||
EI_TYPE_NONE = 0
|
||||
EI_TYPE_REL = 1
|
||||
EI_TYPE_EXEC = 2
|
||||
EI_TYPE_DYN = 3
|
||||
EI_TYPE_CORE = 4
|
||||
EI_TYPE_NUM = 5
|
||||
EI_TYPE_LOPROC = 0xff00
|
||||
EI_TYPE_HIPROC = 0xffff
|
||||
MACHINE_NONE = 0
|
||||
MACHINE_SPARC = 2
|
||||
MACHINE_386 = 3
|
||||
MACHINE_MIPS = 8
|
||||
MACHINE_MIPS_RS4_BE = 10
|
||||
MACHINE_SPARC32PLUS = 18
|
||||
MACHINE_ARM = 40
|
||||
MACHINE_FAKE_ALPHA = 41
|
||||
MACHINE_SPARCV9 = 43
|
||||
MACHINE_IA_64 = 50
|
||||
MACHINE_ALPHA = 0x9026
|
||||
VERSION_NONE = 0
|
||||
VERSION_CURRENT = 1
|
||||
|
||||
def __init__(self):
|
||||
AIStruct.__init__(self, AIStruct.SIZE32)
|
||||
self.setup(
|
||||
('UINT8', 'ei_magic', {'times': 4}),
|
||||
('UINT8', 'ei_class', {'names' : { 1: "ELF32", 2: "ELF64" }} ),
|
||||
('UINT8', 'ei_data', {'names' : { 1 : "2's complement, little endian", 2: "2's complement, big endian" }}),
|
||||
('UINT8', 'ei_version', {'names' : { 1 : "1 (current)" }}),
|
||||
('UINT8', 'ei_osabi', { 'names' : { 0 : "UNIX - System V", 1 : "HP-UX Operating System", 255 : "Standalone application"}}),
|
||||
('UINT8', 'ei_abiversion'),
|
||||
('UINT8', 'ei_padding', {'times': 7}),
|
||||
('UINT16', 'e_type', { 'names' : { 2 : "EXEC (Executable file)" }} ),
|
||||
('UINT16', 'e_machine', { 'names' : { 3 : "Intel 80836" } }),
|
||||
('UINT32', 'e_version', {'format': "0x%x" }),
|
||||
('UINT32', 'e_entry', {'format': "0x%x" }),
|
||||
('UINT32', 'e_phoff', { 'format' : "%d (bytes into file)" }),
|
||||
('UINT32', 'e_shoff', { 'format' : "%d (bytes into file)" }),
|
||||
('UINT32', 'e_flags', {'format': "0x%x" }),
|
||||
('UINT16', 'e_ehsize', { 'format' : "%d (bytes)" } ),
|
||||
('UINT16', 'e_phentsize', { 'format' : "%d (bytes)" } ),
|
||||
('UINT16', 'e_phnum'),
|
||||
('UINT16', 'e_shentsize', { 'format' : "%d (bytes)" } ),
|
||||
('UINT16', 'e_shnum'),
|
||||
('UINT16', 'e_shstrndx')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
f = StringIO.StringIO()
|
||||
f.write("ELF Header:\n")
|
||||
for name, attr in [("Class", "ei_class"),
|
||||
("Data", "ei_data"),
|
||||
("Version", "ei_version"),
|
||||
("OS/ABI", "ei_osabi"),
|
||||
("ABI Version", "ei_abiversion"),
|
||||
("Type", "e_type"),
|
||||
("Machine", "e_machine"),
|
||||
("Version", "e_version"),
|
||||
("Entry point address", "e_entry"),
|
||||
("Start of program headers", "e_phoff"),
|
||||
("Start of section headers", "e_shoff"),
|
||||
("Flags", "e_flags"),
|
||||
("Size of this header", "e_ehsize"),
|
||||
("Size of program headers", "e_phentsize"),
|
||||
("Number of program headers", "e_phnum"),
|
||||
("Size of section headers", "e_shentsize"),
|
||||
("Number of section headers", "e_shnum"),
|
||||
("Section header string table index", "e_shstrndx"),
|
||||
]:
|
||||
f.write(" %-35s%s\n" % ("%s:" % name, getattr(self.ai, attr)))
|
||||
return f.getvalue()
|
||||
|
||||
class Elf64Header(Elf32Header):
|
||||
# Inherit from Elf32Header to get the constants.
|
||||
def __init__(self):
|
||||
AIStruct.__init__(self, AIStruct.SIZE64)
|
||||
self.setup(
|
||||
('UINT8', 'ei_magic', {'times': 4}),
|
||||
('UINT8', 'ei_class'),
|
||||
('UINT8', 'ei_data'),
|
||||
('UINT8', 'ei_version'),
|
||||
('UINT8', 'ei_padding', {'times': 9}),
|
||||
('UINT16', 'e_type'),
|
||||
('UINT16', 'e_machine'),
|
||||
('UINT32', 'e_version'),
|
||||
('UINT64', 'e_entry'),
|
||||
('UINT64', 'e_phoff'),
|
||||
('UINT64', 'e_shoff'),
|
||||
('UINT32', 'e_flags'),
|
||||
('UINT16', 'e_ehsize'),
|
||||
('UINT16', 'e_phentsize'),
|
||||
('UINT16', 'e_phnum'),
|
||||
('UINT16', 'e_shentsize'),
|
||||
('UINT16', 'e_shnum'),
|
||||
('UINT16', 'e_shstrndx')
|
||||
)
|
||||
|
||||
class Elf32SectionHeader(AIStruct):
|
||||
SHF_WRITE = 1 << 0
|
||||
SHF_ALLOC = 1 << 1
|
||||
SHF_EXECINSTR = 1 << 2
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
AIStruct.__init__(self, AIStruct.SIZE32)
|
||||
self.elffile = kwargs["elffile"]
|
||||
self.index = kwargs["index"]
|
||||
|
||||
def format_flags(x):
|
||||
if x == (self.SHF_WRITE | self.SHF_ALLOC):
|
||||
return "WA"
|
||||
if x == (self.SHF_EXECINSTR | self.SHF_ALLOC):
|
||||
return "AX"
|
||||
if x == self.SHF_WRITE:
|
||||
return "W"
|
||||
if x == self.SHF_ALLOC:
|
||||
return "A"
|
||||
if x == self.SHF_EXECINSTR:
|
||||
return "X"
|
||||
return "%x" % x
|
||||
|
||||
self.mutated = 0
|
||||
self._name = None
|
||||
|
||||
self.setup(
|
||||
('UINT32', 'sh_name', {"format" : self.elffile.get_name}),
|
||||
('UINT32', 'sh_type', {"names" : {0:"NULL", 1:"PROGBITS", 2:"SYMTAB", 3:"STRTAB", 8:"NOBITS"}}),
|
||||
('UINT32', 'sh_flags', {"format": format_flags}),
|
||||
('UINT32', 'sh_addr', {"format" : "%08x"}),
|
||||
('UINT32', 'sh_offset', {"format" : "%06x"}),
|
||||
('UINT32', 'sh_size', {"format" : "%06x"}),
|
||||
('UINT32', 'sh_link'),
|
||||
('UINT32', 'sh_info', {"format" : "%3d"}),
|
||||
('UINT32', 'sh_addralign', {"format" : "%2d"}),
|
||||
('UINT32', 'sh_entsize', {"format" : "%02x"}),
|
||||
)
|
||||
|
||||
def get_name(self):
|
||||
if not self._name:
|
||||
self._name = self.elffile.get_name(self.ai.sh_name)
|
||||
return self._name
|
||||
|
||||
def set_name(self, value):
|
||||
self._name = value
|
||||
name = property(get_name, set_name)
|
||||
|
||||
def get_sym_name(self, value):
|
||||
strtab = ElfFileStringTable \
|
||||
(self.elffile.gfile, self.elffile.sheaders[self.ai.sh_link.get()])
|
||||
return strtab.read(value)
|
||||
|
||||
|
||||
def allocable(self):
|
||||
return self.ai.sh_flags.get() & self.SHF_ALLOC
|
||||
|
||||
def writable(self):
|
||||
return self.ai.sh_flags.get() & self.SHF_WRITE
|
||||
|
||||
def executable(self):
|
||||
return self.ai.sh_flags.get() & self.SHF_EXECINSTR
|
||||
|
||||
def get_perms(self):
|
||||
# Only call on allocatable
|
||||
assert self.allocable()
|
||||
rwx = (1 << 2);
|
||||
if self.writable():
|
||||
rwx |= (1 << 1)
|
||||
if self.executable():
|
||||
rwx |= 1
|
||||
return rwx
|
||||
|
||||
def container(self, cls):
|
||||
size = cls(section = self).struct_size()
|
||||
return ElfFileContainer(self.elffile.gfile, AIStruct.SIZE32, self.ai.sh_offset.get(),
|
||||
size, self.ai.sh_size.get() / size,
|
||||
cls, section=self)
|
||||
|
||||
|
||||
class Elf64SectionHeader(AIStruct):
|
||||
def __init__(self, *args, **kwargs):
|
||||
AIStruct.__init__(self, AIStruct.SIZE64)
|
||||
self.setup(
|
||||
('UINT32', 'sh_name'),
|
||||
('UINT32', 'sh_type'),
|
||||
('UINT64', 'sh_flags'),
|
||||
('UINT64', 'sh_addr'),
|
||||
('UINT64', 'sh_offset'),
|
||||
('UINT64', 'sh_size'),
|
||||
('UINT32', 'sh_link'),
|
||||
('UINT32', 'sh_info'),
|
||||
('UINT64', 'sh_addralign'),
|
||||
('UINT64', 'sh_entsize'),
|
||||
)
|
||||
|
||||
|
||||
class Elf32ProgramHeader(AIStruct):
|
||||
PT_NULL = 0
|
||||
PT_NULL = 0
|
||||
PT_LOAD = 1
|
||||
PT_DYNAMIC = 2
|
||||
PT_INTERP = 3
|
||||
PT_NOTE = 4
|
||||
PT_SHLIB = 5
|
||||
PT_PHDR = 6
|
||||
PT_NUM = 7
|
||||
PT_LOOS = 0x60000000
|
||||
PT_HIOS = 0x6fffffff
|
||||
PT_LOPROC = 0x70000000
|
||||
PT_HIPROC = 0x7fffffff
|
||||
|
||||
PF_X = 1 << 0
|
||||
PF_W = 1 << 1
|
||||
PF_R = 1 << 2
|
||||
PF_MASKPROC = 0xf0000000L
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
AIStruct.__init__(self, AIStruct.SIZE32)
|
||||
self.elffile = kwargs["elffile"]
|
||||
def format_flags(x):
|
||||
the_str = [' ', ' ', ' ']
|
||||
if (x & self.PF_X):
|
||||
the_str[2] = 'E'
|
||||
if (x & self.PF_W):
|
||||
the_str[1] = 'W'
|
||||
if (x & self.PF_R):
|
||||
the_str[0] = 'R'
|
||||
return "".join(the_str)
|
||||
|
||||
self.setup(
|
||||
('UINT32', 'p_type', {"names" :
|
||||
{0: "NULL",
|
||||
1: "LOAD",
|
||||
2 : "DYNAMIC",
|
||||
3 : "INTERP",
|
||||
4 : "NOTE",
|
||||
1685382481 : "GNU_STACK",
|
||||
1694766464 : "PAX_FLAGS",
|
||||
}
|
||||
} ),
|
||||
('UINT32', 'p_offset', {"format": "0x%06x"}),
|
||||
('UINT32', 'p_vaddr', {"format": "0x%08x"}),
|
||||
('UINT32', 'p_paddr', {"format": "0x%08x"}),
|
||||
('UINT32', 'p_filesz', {"format": "0x%05x"}),
|
||||
('UINT32', 'p_memsz', {"format": "0x%05x"}),
|
||||
('UINT32', 'p_flags', {"format": format_flags}),
|
||||
('UINT32', 'p_align', {"format": "0x%x"}),
|
||||
)
|
||||
|
||||
class Elf64ProgramHeader(Elf32ProgramHeader):
|
||||
def __init__(self, *args, **kwargs):
|
||||
AIStruct.__init__(self, AIStruct.SIZE64)
|
||||
self.setup(
|
||||
('UINT32', 'p_type', {"names" : {0: "NULL", 1: "LOAD", 2 : "DYNAMIC", 3 : "INTERP", 4 : "NOTE"}} ),
|
||||
('UINT32', 'p_flags'),
|
||||
('UINT64', 'p_offset'),
|
||||
('UINT64', 'p_vaddr'),
|
||||
('UINT64', 'p_paddr'),
|
||||
('UINT64', 'p_filesz'),
|
||||
('UINT64', 'p_memsz'),
|
||||
('UINT64', 'p_align'),
|
||||
)
|
||||
|
||||
class ElfFileException(Exception):
|
||||
pass
|
||||
|
||||
class ElfFileNotElfException(ElfFileException):
|
||||
pass
|
||||
|
||||
class ElfFileContainer(object):
|
||||
def __init__(self, gfile, word_size, offset, entsize, number, cls, **kwargs):
|
||||
self.gfile = gfile
|
||||
self.word_size = word_size
|
||||
self.offset = offset
|
||||
self.entsize = entsize
|
||||
self.number = number
|
||||
self.cls = cls
|
||||
self.mutated = 0
|
||||
self.container = []
|
||||
self.kwargs = kwargs
|
||||
|
||||
def mutate(self):
|
||||
print "Making it mutable"
|
||||
for each in self:
|
||||
self.container.append(each)
|
||||
self.mutated = 1
|
||||
|
||||
def __delitem__(self, idx):
|
||||
if not self.mutated: self.mutate()
|
||||
self.container.__delitem__(idx)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
if type(idx) == type(""):
|
||||
# Act like a dictionary
|
||||
for each in self:
|
||||
if str(each.ai.sh_name) == idx:
|
||||
return each
|
||||
raise "badness"
|
||||
else:
|
||||
if self.mutated:
|
||||
print "mutated", idx, self.container
|
||||
return self.container[idx]
|
||||
else:
|
||||
num = idx
|
||||
assert num >= 0
|
||||
if num >= self.number:
|
||||
raise StopIteration()
|
||||
inst = self.cls(self.word_size, index=num, **self.kwargs)
|
||||
poker = BitPoker.new_with_gfile(self.gfile, self.offset + (self.entsize * num))
|
||||
inst.read_from_poker(poker)
|
||||
return inst
|
||||
|
||||
def __len__(self):
|
||||
return self.number
|
||||
|
||||
class ElfFileProgramHeaderContainer(ElfFileContainer):
|
||||
def __str__(self):
|
||||
f = StringIO.StringIO()
|
||||
f.write("Program Headers:\n")
|
||||
format_str = " %-15s%-9s%-11s%-11s%-8s%-8s%-4s%s\n"
|
||||
f.write(format_str % ("Type", "Offset", "VirtAddr",
|
||||
"PhysAddr", "FileSiz", "MemSiz", "Flg", "Align"))
|
||||
for header in self:
|
||||
x = header.ai
|
||||
f.write(format_str % (x.p_type, x.p_offset, x.p_vaddr,
|
||||
x.p_paddr, x.p_filesz, x.p_memsz, x.p_flags, x.p_align))
|
||||
return f.getvalue()
|
||||
|
||||
class ElfFileSectionHeaderContainer(ElfFileContainer):
|
||||
def __str__(self):
|
||||
f = StringIO.StringIO()
|
||||
f.write("There are %d section headers, starting at offset 0x%x:\n\n" % (len(self), self.kwargs["elffile"].header.ai.e_shoff.get()))
|
||||
f.write("Section Headers:\n")
|
||||
format_str = " [%2s] %-17.17s %-15s %-8s %-6s %-6s %2s %3s %2s %-3s %-2s\n"
|
||||
f.write(format_str % ("Nr", "Name", "Type", "Addr", "Off", "Size",
|
||||
"ES", "Flg", "Lk", "Inf", "Al"))
|
||||
for idx, header in enumerate(self):
|
||||
x = header.ai
|
||||
f.write(format_str % (idx, x.sh_name, x.sh_type, x.sh_addr,
|
||||
x.sh_offset, x.sh_size, x.sh_entsize,
|
||||
x.sh_flags, x.sh_link, x.sh_info,
|
||||
x.sh_addralign))
|
||||
return f.getvalue()
|
||||
|
||||
|
||||
class ElfFileStringTable(object):
|
||||
def __init__(self, gfile, section_header):
|
||||
file_offset = section_header.ai.sh_offset
|
||||
self.poker = BitPoker.new_with_gfile(gfile, file_offset)
|
||||
|
||||
def read(self, offset):
|
||||
return self.poker.read_c_string(offset)
|
||||
|
||||
|
||||
class Symbols(AIStruct):
|
||||
def __init__(self, *args, **kwargs):
|
||||
AIStruct.__init__(self, AIStruct.SIZE32)
|
||||
self.section = kwargs["section"]
|
||||
self.setup(
|
||||
('UINT32', 'st_name', {"format" : self.section.get_sym_name}),
|
||||
('UINT32', 'value'),
|
||||
('UINT32', 'size'),
|
||||
('UINT8', 'info'),
|
||||
('UINT8', 'other'),
|
||||
('UINT16', 'st_shndx')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "[%s] 0x%08x %5d %3d" % \
|
||||
(self.ai.st_name, self.ai.value.get(),
|
||||
self.ai.size.get(), self.ai.st_shndx.get())
|
||||
|
||||
|
||||
class ElfFileSymbolTable(object):
|
||||
def __init__(self, gfile, section_header):
|
||||
self.header = section_header
|
||||
file_offset = section_header.ai.sh_offset
|
||||
self.poker = BitPoker.new_with_gfile(gfile, file_offset)
|
||||
|
||||
def get_symbols(self, section_num = None):
|
||||
if section_num:
|
||||
return [ sym for sym in self.header.container(Symbols) if sym.ai.st_shndx.get() == section_num]
|
||||
else:
|
||||
return self.header.container(Symbols)
|
||||
|
||||
class ElfFile(object):
|
||||
WORD_SIZE_MAP = {Elf32Header.EI_CLASS_32: Elf32Header.SIZE32,
|
||||
Elf32Header.EI_CLASS_64: Elf32Header.SIZE64}
|
||||
BYTE_ORDER_MAP = {Elf32Header.EI_DATA_LSB: 'lsb',
|
||||
Elf32Header.EI_DATA_MSB: 'msb'}
|
||||
HEADER_MAP = {Elf32Header.SIZE32: Elf32Header, Elf64Header.SIZE64: Elf64Header}
|
||||
PROGRAM_HEADER_MAP = {Elf32Header.SIZE32: Elf32ProgramHeader, Elf64Header.SIZE64: Elf64ProgramHeader}
|
||||
SECTION_HEADER_MAP = {Elf32Header.SIZE32: Elf32SectionHeader, Elf64Header.SIZE64: Elf64SectionHeader}
|
||||
""" Python representation of an Elf file. """
|
||||
def __init__(self, gfile, byte_ordering, word_size):
|
||||
self.gfile = gfile
|
||||
self.gfile.set_byte_ordering(byte_ordering)
|
||||
self.byte_order = byte_ordering
|
||||
self.word_size = word_size
|
||||
|
||||
self.header = ElfFile.HEADER_MAP[self.word_size]()
|
||||
self.header.read_from_poker(BitPoker.new_with_gfile(self.gfile, 0))
|
||||
# Setup the parts of the file
|
||||
# ... program headers
|
||||
self.pheaders = ElfFileProgramHeaderContainer \
|
||||
(gfile, word_size, self.header.ai.e_phoff, self.header.ai.e_phentsize,
|
||||
self.header.ai.e_phnum.get(), ElfFile.PROGRAM_HEADER_MAP[word_size], elffile=self)
|
||||
# ... section headers
|
||||
self.sheaders = ElfFileSectionHeaderContainer \
|
||||
(gfile, word_size, self.header.ai.e_shoff, self.header.ai.e_shentsize,
|
||||
self.header.ai.e_shnum.get(), ElfFile.SECTION_HEADER_MAP[word_size], elffile=self)
|
||||
# ... string table
|
||||
if self.header.ai.e_shstrndx != 0:
|
||||
self.string_table = ElfFileStringTable \
|
||||
(self.gfile, self.sheaders[self.header.ai.e_shstrndx])
|
||||
|
||||
# ... symbol table
|
||||
self.symtable = None
|
||||
for header in self.sheaders:
|
||||
if header.get_name() == ".symtab":
|
||||
self.symtable = ElfFileSymbolTable(self.gfile, header)
|
||||
|
||||
def set_source(self, poker):
|
||||
self.source_poker = poker
|
||||
|
||||
def get_name(self, idx):
|
||||
return self.string_table.read(idx)
|
||||
|
||||
def print_info(self):
|
||||
print "* Information for ELF file:"
|
||||
print "* Header info:"
|
||||
self.header.print_info()
|
||||
|
||||
|
||||
def write_file(selfm, filename):
|
||||
# Write out head
|
||||
print "Wirintg out", filename
|
||||
|
||||
def from_file(filename):
|
||||
gfile = GFile.existing(filename)
|
||||
poker = BitPoker()
|
||||
poker.set_mmapfile(gfile.mapping, 0) # Offset of 0 from start of file.
|
||||
poker.set_byte_ordering('lsb') # Use default because we don't (yet) know
|
||||
header = Elf32Header() # Once again, use a default size.
|
||||
header.read_from_poker(poker)
|
||||
# Examine the header for info we need.
|
||||
# Check the magic first. If we don't and the file is non-ELF, chances are
|
||||
# class & data won't match which will result in a confusing error message
|
||||
if header.ai.ei_magic != Elf32Header.EI_MAGIC:
|
||||
raise ElfFileNotElfException("Wanted magic %r, got %r" \
|
||||
% (Elf32Header.EI_MAGIC, header.ai.ei_magic))
|
||||
word_size = ElfFile.WORD_SIZE_MAP[header.ai.ei_class.get()]
|
||||
byte_order = ElfFile.BYTE_ORDER_MAP[header.ai.ei_data.get()]
|
||||
return ElfFile(gfile, byte_order, word_size)
|
||||
from_file = staticmethod(from_file)
|
||||
|
||||
|
||||
def test():
|
||||
"Test suite"
|
||||
|
||||
elf_file = ElfFile.from_file("a.out")
|
||||
|
||||
# Check can load an elf
|
||||
success = 1
|
||||
try:
|
||||
x = ElfFile.from_file("a.out")
|
||||
except:
|
||||
success = 0
|
||||
assert success
|
||||
|
||||
# CHeck can't load not and elf
|
||||
success = 0
|
||||
try:
|
||||
x = ElfFile.from_file("pyelf.py")
|
||||
except:
|
||||
success = 1
|
||||
|
||||
assert success
|
||||
|
||||
|
||||
def main():
|
||||
filename = sys.argv[1] # Consider this a usage message :)
|
||||
elf_file = ElfFile.from_file(filename)
|
||||
elf_file.print_info()
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.argv[1] != "test":
|
||||
main()
|
||||
else:
|
||||
test()
|
||||
|
||||
|
||||
BIN
tools/pyelf/elf.pyc
Normal file
BIN
tools/pyelf/elf.pyc
Normal file
Binary file not shown.
77
tools/pyelf/lmamove.py
Executable file
77
tools/pyelf/lmamove.py
Executable file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import re, elf, sys, os
|
||||
from optparse import OptionParser
|
||||
|
||||
toolprefix = "arm-none-linux-gnueabi-"
|
||||
objdump = toolprefix + "objdump"
|
||||
objcopy = toolprefix + "objcopy"
|
||||
readelf = toolprefix + "readelf"
|
||||
egrep = "egrep"
|
||||
snames_file = "section_names.txt"
|
||||
|
||||
def get_section_names(path):
|
||||
rest, filename = os.path.split(path)
|
||||
sections = []
|
||||
os.system(readelf + " -l " + path + " > readelf.out")
|
||||
os.system(egrep + r' "(\.){1}(\w){1}" ' + " readelf.out > sections.out")
|
||||
for line in file('sections.out'):
|
||||
secnum, secname = str.split(line)
|
||||
sections.append((secnum,secname))
|
||||
print "Sections:\n" + str(sections)
|
||||
return sections
|
||||
|
||||
helpmsg = \
|
||||
'''Usage:
|
||||
lmamove.py <filename> <[<op>]offset>
|
||||
|
||||
Where <filename> is the file to modify, <offset> is the offset to be added,
|
||||
subtracted or assigned, depending on the <op>, which can be either + or -
|
||||
|
||||
!!! NOTE THIS TOOL IS NOT VERY USEFUL BECAUSE IT TURNS OUT THAT SOME SECTIONS
|
||||
ARE LIKELY TO BE PUT TOGETHER INTO THE SAME SEGMENT. For example (.text with .rodata)
|
||||
and (.data with .bss) Therefore moving one sectoin's LMA confuses what to do with the
|
||||
other section. The segment layout is almost always not same as initial after LMA
|
||||
modification with objcopy. !!!
|
||||
'''
|
||||
|
||||
def check_args():
|
||||
if len(sys.argv) < 3:
|
||||
print helpmsg
|
||||
sys.exit()
|
||||
filename = sys.argv[1]
|
||||
if not os.path.exists(filename):
|
||||
print "Given path: " + filename + " does not exist.\n"
|
||||
sys.exit()
|
||||
|
||||
def move_sections(path, sections, op, offset):
|
||||
for secnum, secname in sections:
|
||||
os.system(objcopy + " --change-section-lma " + secname + op + offset + " " + path)
|
||||
|
||||
def lmamove():
|
||||
'''
|
||||
Moves an ARM ELF executable's LMA by moving each of its sections by
|
||||
the given offset. It uses binutils (namely readelf and objcopy) to do
|
||||
the actual work. Normally objcopy supports a similar operation; moving
|
||||
of VMA of the whole program by --adjust-vma switch. But it only supports
|
||||
modifying LMAs by section. Therefore this script extracts all loadable
|
||||
section names and moves them one by one in order to move the whole program.
|
||||
'''
|
||||
check_args()
|
||||
filename = sys.argv[1]
|
||||
offset = sys.argv[2]
|
||||
|
||||
if offset[0] in "+-":
|
||||
op = offset[0]
|
||||
offset = offset[1:]
|
||||
else:
|
||||
op = "+"
|
||||
|
||||
if offset[:1] == "0x":
|
||||
offset = int(offset, 16)
|
||||
section_names = get_section_names(filename)
|
||||
move_sections(filename, section_names, op, offset)
|
||||
print("Done.\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
lmamove()
|
||||
10
tools/pyelf/package.cfg
Normal file
10
tools/pyelf/package.cfg
Normal file
@@ -0,0 +1,10 @@
|
||||
[info]
|
||||
Author: Ben Leslie
|
||||
Name: PyELF
|
||||
Copyright: NICTA
|
||||
Licence: OzPLB
|
||||
Description: PyELF library allows the parsing and manipulation
|
||||
of ELF objects in a platform and toolchain independant manner.
|
||||
It is primarily designed for the Kenge build system, but would
|
||||
form a great basis for a general purpose Python ELF library.
|
||||
Category: tool
|
||||
45
tools/pyelf/pyelf.py
Normal file
45
tools/pyelf/pyelf.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import mmap
|
||||
import os
|
||||
|
||||
class ELF:
|
||||
EI_MAGIC = "\x7fELF"
|
||||
|
||||
def __init__(self, name):
|
||||
f = file(name, "rb")
|
||||
size = os.stat(name).st_size
|
||||
|
||||
self.data = mmap.mmap(f.fileno(), size, mmap.MAP_PRIVATE, mmap.PROT_READ)
|
||||
|
||||
if self.magic != self.EI_MAGIC:
|
||||
raise "Not an elf"
|
||||
|
||||
def get_magic(self):
|
||||
return self.data[:4]
|
||||
magic = property(get_magic)
|
||||
|
||||
def get_class(self):
|
||||
return self.data[4]
|
||||
elf_class = property(get_class)
|
||||
|
||||
|
||||
|
||||
"Test suite"
|
||||
|
||||
x = ELF("a.out")
|
||||
|
||||
# Check can load an elf
|
||||
success = 1
|
||||
try:
|
||||
x = ELF("a.out")
|
||||
except:
|
||||
success = 0
|
||||
assert success
|
||||
|
||||
# CHeck can't load not and elf
|
||||
success = 0
|
||||
try:
|
||||
x = ELF("pyelf.py")
|
||||
except:
|
||||
success = 1
|
||||
|
||||
assert success
|
||||
120
tools/pyelf/readelf.py
Executable file
120
tools/pyelf/readelf.py
Executable file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from aistruct import AIStruct
|
||||
import elf, sys
|
||||
from optparse import OptionParser
|
||||
from os import path
|
||||
|
||||
class AfterBurner(AIStruct):
|
||||
def __init__(self, *args, **kwargs):
|
||||
AIStruct.__init__(self, AIStruct.SIZE32)
|
||||
self.setup(
|
||||
('UINT32', 'addr')
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "0x%x" % self.ai.addr.get()
|
||||
|
||||
def main():
|
||||
parser = OptionParser(add_help_option=False)
|
||||
parser.add_option("-h", "--file-header",
|
||||
action="store_true", dest="header", default=False,
|
||||
help="Display the ELF file header")
|
||||
parser.add_option("-l", "--program-headers",
|
||||
action="store_true", dest="program_headers", default=False,
|
||||
help="Display the program headers")
|
||||
parser.add_option("-S", "--section-headers",
|
||||
action="store_true", dest="section_headers", default=False,
|
||||
help="Display the section headers")
|
||||
parser.add_option("--afterburn",
|
||||
action="store_true", dest="afterburn", default=False,
|
||||
help="Display the afterburn relocations")
|
||||
parser.add_option("--first-free-page",
|
||||
action="store_true", dest="ffpage", default=False,
|
||||
help="Prints out (in .lds format) the address of the first free physical" + \
|
||||
"page after this image at load time. Using this information at link" + \
|
||||
"time, images can be compiled and linked consecutively and loaded in" + \
|
||||
"consecutive memory regions at load time.")
|
||||
parser.add_option("--lma-start-end", action="store_true", dest="lma_boundary", default=False,
|
||||
help="Prints out the start and end LMA boundaries of an image." + \
|
||||
"This is useful for autogenerating a structure for the microkernel" + \
|
||||
"to discover at run-time where svc tasks are loaded.")
|
||||
(options, args) = parser.parse_args()
|
||||
if len(args) != 1:
|
||||
parser.print_help()
|
||||
return
|
||||
elffile = elf.ElfFile.from_file(args[0])
|
||||
|
||||
if options.header:
|
||||
print elffile.header
|
||||
if options.program_headers:
|
||||
print elffile.pheaders
|
||||
if options.section_headers:
|
||||
print elffile.sheaders
|
||||
if options.afterburn:
|
||||
burnheader = elffile.sheaders[".afterburn"]
|
||||
burns = burnheader.container(AfterBurner)
|
||||
print "There are %d afterburn entry points" % len(burns)
|
||||
print "Afterburn:"
|
||||
for burn in burns:
|
||||
print " ", burn
|
||||
if options.lma_boundary:
|
||||
paddr_first = 0
|
||||
paddr_start = 0
|
||||
paddr_end = 0
|
||||
for pheader in elffile.pheaders:
|
||||
x = pheader.ai
|
||||
if str(x.p_type) != "LOAD":
|
||||
continue
|
||||
# First time assign the first p_paddr.
|
||||
if paddr_first == 0:
|
||||
paddr_first = 1
|
||||
paddr_start = x.p_paddr.value
|
||||
# Then if new paddr is lesser, reassign start.
|
||||
if paddr_start > x.p_paddr.value:
|
||||
paddr_start = x.p_paddr.value
|
||||
# If end of segment is greater, reassign end.
|
||||
if paddr_end < x.p_paddr + x.p_memsz:
|
||||
paddr_end = x.p_paddr + x.p_memsz
|
||||
rest, image_name = path.split(args[0])
|
||||
if image_name[-4] == ".":
|
||||
image_name = image_name[:-4]
|
||||
print image_name
|
||||
print "image_start " + hex(paddr_start)[:-1]
|
||||
print "image_end " + hex(paddr_end)[:-1]
|
||||
|
||||
if options.ffpage:
|
||||
paddr_max = 0
|
||||
p_align = 0
|
||||
for pheader in elffile.pheaders:
|
||||
x = pheader.ai
|
||||
if str(x.p_type) == "LOAD":
|
||||
paddr = x.p_paddr + x.p_memsz
|
||||
p_align = x.p_align
|
||||
if paddr > paddr_max:
|
||||
paddr_max = paddr
|
||||
|
||||
print "/*\n" + \
|
||||
" * The next free p_align'ed LMA base address\n" + \
|
||||
" *\n" + \
|
||||
" * p_align = " + hex(p_align.value) + "\n" + \
|
||||
" *\n" + \
|
||||
" * Recap from ELF spec: p_align: Loadable process segments must have\n" + \
|
||||
" * congruent values for p_vaddr and p_offset, modulo the page size. \n" + \
|
||||
" * This member gives the value to which the segments are aligned in \n" + \
|
||||
" * memory and in the file. Values 0 and 1 mean that no alignment is \n" + \
|
||||
" * required. Otherwise, p_align should be a positive, integral power\n" + \
|
||||
" * of 2, and p_addr should equal p_offset, modulo p_align. \n" + \
|
||||
" * This essentially means next available address must be aligned at\n" + \
|
||||
" * p_align, rather than the page_size, which one (well, I) would \n" + \
|
||||
" * normally expect. \n" + \
|
||||
" */\n"
|
||||
paddr_aligned = paddr_max & ~(p_align.value - 1)
|
||||
if paddr_max & (p_align.value - 1):
|
||||
paddr_aligned += p_align.value
|
||||
|
||||
print "physical_base = " + hex(paddr_aligned)[:-1] + ";"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
17
tools/tagsgen/tagsgen_all
Executable file
17
tools/tagsgen/tagsgen_all
Executable file
@@ -0,0 +1,17 @@
|
||||
|
||||
rm -f cscope.*
|
||||
rm -f tags
|
||||
# Put all sources into a file list.
|
||||
find ./ -name '*.cc' > tagfilelist
|
||||
find ./ -name '*.c' >> tagfilelist
|
||||
find ./ -name '*.h' >> tagfilelist
|
||||
find ./ -name '*.s' >> tagfilelist
|
||||
find ./ -name '*.S' >> tagfilelist
|
||||
find ./ -name '*.lds' >> tagfilelist
|
||||
|
||||
# Use file list to include in tags.
|
||||
ctags --languages=C,C++,Asm --recurse -Ltagfilelist
|
||||
|
||||
cscope -q -k -R -i tagfilelist
|
||||
# Remove file list.
|
||||
rm -f tagfilelist
|
||||
22
tools/tagsgen/tagsgen_kernel
Executable file
22
tools/tagsgen/tagsgen_kernel
Executable file
@@ -0,0 +1,22 @@
|
||||
|
||||
rm -f cscope.*
|
||||
rm -f tags
|
||||
# Put all sources into a file list.
|
||||
find ./src -name '*.cc' > tagfilelist
|
||||
find ./src -name '*.c' >> tagfilelist
|
||||
find ./src -name '*.h' >> tagfilelist
|
||||
find ./src -name '*.s' >> tagfilelist
|
||||
find ./src -name '*.S' >> tagfilelist
|
||||
find ./src -name '*.lds' >> tagfilelist
|
||||
|
||||
find ./include -name '*.h' >> tagfilelist
|
||||
find ./include -name '*.s' >> tagfilelist
|
||||
find ./include -name '*.S' >> tagfilelist
|
||||
find ./include -name '*.lds' >> tagfilelist
|
||||
|
||||
# Use file list to include in tags.
|
||||
ctags --languages=C,Asm --recurse -Ltagfilelist
|
||||
|
||||
cscope -q -k -R -i tagfilelist
|
||||
# Remove file list.
|
||||
rm -f tagfilelist
|
||||
20
tools/tagsgen/tagsgen_loader
Executable file
20
tools/tagsgen/tagsgen_loader
Executable file
@@ -0,0 +1,20 @@
|
||||
|
||||
# Put all sources into a file list.
|
||||
find ./loader -name '*.cc' > tagfilelist
|
||||
find ./loader -name '*.c' >> tagfilelist
|
||||
find ./loader -name '*.h' >> tagfilelist
|
||||
find ./loader -name '*.s' >> tagfilelist
|
||||
find ./loader -name '*.S' >> tagfilelist
|
||||
|
||||
find ./libs -name '*.cc'>> tagfilelist
|
||||
find ./libs -name '*.c' >> tagfilelist
|
||||
find ./libs -name '*.h' >> tagfilelist
|
||||
find ./libs -name '*.s' >> tagfilelist
|
||||
find ./libs -name '*.S' >> tagfilelist
|
||||
|
||||
# Use file list to include in tags.
|
||||
exuberant-ctags --languages=C,Asm --recurse -Ltagfilelist
|
||||
|
||||
cscope -q -k -R -i tagfilelist
|
||||
# Remove file list.
|
||||
#rm -f tagfilelist
|
||||
Reference in New Issue
Block a user