mirror of
https://github.com/drasko/codezero.git
synced 2026-03-06 20:43:14 +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
|
||||
Reference in New Issue
Block a user