Initial commit

This commit is contained in:
Bahadir Balban
2008-01-13 13:53:52 +00:00
commit e2b791a3d8
789 changed files with 95825 additions and 0 deletions

861
tools/cml2-tools/autoconfigure.py Executable file
View 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
View 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

Binary file not shown.

857
tools/cml2-tools/cmladvent.py Executable file
View 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

File diff suppressed because it is too large Load Diff

3323
tools/cml2-tools/cmlconfigure.py Executable file

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

122
tools/cml2-tools/configtrans.py Executable file
View 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
View 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
View 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
View 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