aboutsummaryrefslogtreecommitdiffstats
path: root/com32/cmenu/menugen.py
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2009-03-02 10:51:25 -0800
committerH. Peter Anvin <hpa@linux.intel.com>2009-03-02 10:51:25 -0800
commit37bfb1cb8b4c1d670e0d407b83c5270f7c457389 (patch)
tree81096599d4315dc2debb8c3faa189371d1d15566 /com32/cmenu/menugen.py
parent2b570465a0df2070aca723559a9f309d92530e6a (diff)
downloadsyslinux-37bfb1cb8b4c1d670e0d407b83c5270f7c457389.tar.gz
syslinux-37bfb1cb8b4c1d670e0d407b83c5270f7c457389.tar.xz
syslinux-37bfb1cb8b4c1d670e0d407b83c5270f7c457389.zip
Move complex menu to com32/cmenu; clean up the Makefiles
Move the complex menu system into the com32 directory so it can be sequenced properly with respect to hdt (it needs to be built after com32/lib but before com32/hdt). While we're at it, clean up the Makefiles somewhat.
Diffstat (limited to 'com32/cmenu/menugen.py')
-rw-r--r--com32/cmenu/menugen.py307
1 files changed, 307 insertions, 0 deletions
diff --git a/com32/cmenu/menugen.py b/com32/cmenu/menugen.py
new file mode 100644
index 00000000..70ec1f87
--- /dev/null
+++ b/com32/cmenu/menugen.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+
+import sys, re, getopt
+
+class Menusystem:
+
+ types = {"run" : "OPT_RUN",
+ "inactive" : "OPT_INACTIVE",
+ "checkbox" : "OPT_CHECKBOX",
+ "radiomenu": "OPT_RADIOMENU",
+ "sep" : "OPT_SEP",
+ "invisible": "OPT_INVISIBLE",
+ "radioitem": "OPT_RADIOITEM",
+ "exitmenu" : "OPT_EXITMENU",
+ "login" : "login", # special type
+ "submenu" : "OPT_SUBMENU"}
+
+ entry_init = { "item" : "",
+ "info" : "",
+ "data" : "",
+ "ipappend" : 0, # flag to send in case of PXELINUX
+ "helpid" : 65535, # 0xFFFF
+ "shortcut":"-1",
+ "state" : 0, # initial state of checkboxes
+ "argsmenu": "", # name of menu containing arguments
+ "perms" : "", # permission required to execute this entry
+ "_updated" : None, # has this dictionary been updated
+ "type" : "run" }
+
+ menu_init = { "title" : "",
+ "row" : "0xFF", # let system decide position
+ "col" : "0xFF",
+ "_updated" : None,
+ "name" : "" }
+
+ system_init ={ "videomode" : "0xFF",
+ "title" : "Menu System",
+ "top" : "1",
+ "left" : "1" ,
+ "bot" : "21",
+ "right":"79",
+ "helpdir" : "/isolinux/help",
+ "pwdfile" : "",
+ "pwdrow" : "23",
+ "editrow" : "23",
+ "skipcondn" : "0",
+ "skipcmd" : ".exit",
+ "startfile": "",
+ "onerrorcmd":".repeat",
+ "exitcmd" : ".exit",
+ "exitcmdroot" : "",
+ "timeout" : "600",
+ "timeoutcmd":".beep",
+ "totaltimeout" : "0",
+ "totaltimeoutcmd" : ".wait"
+ }
+
+ shift_flags = { "alt" : "ALT_PRESSED",
+ "ctrl" : "CTRL_PRESSED",
+ "shift": "SHIFT_PRESSED",
+ "caps" : "CAPSLOCK_ON",
+ "num" : "NUMLOCK_ON",
+ "ins" : "INSERT_ON"
+ }
+
+ reqd_templates = ["item","login","menu","system"]
+
+ def __init__(self,template):
+ self.state = "system"
+ self.code_template_filename = template
+ self.menus = []
+ self.init_entry()
+ self.init_menu()
+ self.init_system()
+ self.vtypes = " OR ".join(self.types.keys())
+ self.vattrs = " OR ".join(filter(lambda x: x[0] != "_", self.entry.keys()))
+ self.mattrs = " OR ".join(filter(lambda x: x[0] != "_", self.menu.keys()))
+
+ def init_entry(self):
+ self.entry = self.entry_init.copy()
+
+ def init_menu(self):
+ self.menu = self.menu_init.copy()
+
+ def init_system(self):
+ self.system = self.system_init.copy()
+
+ def add_menu(self,name):
+ self.add_item()
+ self.init_menu()
+ self.menu["name"] = name
+ self.menu["_updated"] = 1
+ self.menus.append( (self.menu,[]) )
+
+ def add_item(self):
+ if self.menu["_updated"]: # menu details have changed
+ self.menus[-1][0].update(self.menu)
+ self.init_menu()
+ if self.entry["_updated"]:
+ if not self.entry["info"]:
+ self.entry["info"] = self.entry["data"]
+ if not self.menus:
+ print "Error before line %d" % self.lineno
+ print "REASON: menu must be declared before a menu item is declared"
+ sys.exit(1)
+ self.menus[-1][1].append(self.entry)
+ self.init_entry()
+
+ def set_item(self,name,value):
+ if not self.entry.has_key(name):
+ msg = ["Unknown attribute %s in line %d" % (name,self.lineno)]
+ msg.append("REASON: Attribute must be one of %s" % self.vattrs)
+ return "\n".join(msg)
+ if name=="type" and not self.types.has_key(value):
+ msg = [ "Unrecognized type %s in line %d" % (value,self.lineno)]
+ msg.append("REASON: Valid types are %s" % self.vtypes)
+ return "\n".join(msg)
+ if name=="shortcut":
+ if (value <> "-1") and not re.match("^[A-Za-z0-9]$",value):
+ msg = [ "Invalid shortcut char '%s' in line %d" % (value,self.lineno) ]
+ msg.append("REASON: Valid values are [A-Za-z0-9]")
+ return "\n".join(msg)
+ elif value <> "-1": value = "'%s'" % value
+ elif name in ["state","helpid","ipappend"]:
+ try:
+ value = int(value)
+ except:
+ return "Value of %s in line %d must be an integer" % (name,self.lineno)
+ self.entry[name] = value
+ self.entry["_updated"] = 1
+ return ""
+
+ def set_menu(self,name,value):
+ if not self.menu.has_key(name):
+ return "Error: Unknown keyword %s" % name
+ self.menu[name] = value
+ self.menu["_updated"] = 1
+ return ""
+
+ def set_system(self,name,value):
+ if not self.system.has_key(name):
+ return "Error: Unknown keyword %s" % name
+ if name == "skipcondn":
+ try: # is skipcondn a number?
+ a = int(value)
+ except: # it is a "-" delimited sequence
+ value = value.lower()
+ parts = [ self.shift_flags.get(x.strip(),None) for x in value.split("-") ]
+ self.system["skipcondn"] = " | ".join(filter(None, parts))
+ else:
+ self.system[name] = value
+
+ def set(self,name,value):
+ # remove quotes if given
+ if (value[0] == value[-1]) and (value[0] in ['"',"'"]): # remove quotes
+ value = value[1:-1]
+ if self.state == "system":
+ err = self.set_system(name,value)
+ if not err: return
+ if self.state == "menu":
+ err = self.set_menu(name,value)
+ # change state to entry it menu returns error
+ if err:
+ err = None
+ self.state = "item"
+ if self.state == "item":
+ err = self.set_item(name,value)
+
+ if not err: return
+
+ # all errors so return item's error message
+ print err
+ sys.exit(1)
+
+ def print_entry(self,entry,fd):
+ entry["type"] = self.types[entry["type"]]
+ if entry["type"] == "login": #special type
+ fd.write(self.templates["login"] % entry)
+ else:
+ fd.write(self.templates["item"] % entry)
+
+ def print_menu(self,menu,fd):
+ if menu["name"] == "main": self.foundmain = 1
+ fd.write(self.templates["menu"] % menu)
+ if (menu["row"] != "0xFF") or (menu["col"] != "0xFF"):
+ fd.write(' set_menu_pos(%(row)s,%(col)s);\n' % menu)
+
+
+ def output(self,filename):
+ curr_template = None
+ contents = []
+ self.templates = {}
+ regbeg = re.compile(r"^--(?P<name>[a-z]+) BEGINS?--\n$")
+ regend = re.compile(r"^--[a-z]+ ENDS?--\n$")
+ ifd = open(self.code_template_filename,"r")
+ for line in ifd.readlines():
+ b = regbeg.match(line)
+ e = regend.match(line)
+ if e: # end of template
+ if curr_template:
+ self.templates[curr_template] = "".join(contents)
+ curr_template = None
+ continue
+ if b:
+ curr_template = b.group("name")
+ contents = []
+ continue
+ if not curr_template: continue # lines between templates are ignored
+ contents.append(line)
+ ifd.close()
+
+ missing = None
+ for x in self.reqd_templates:
+ if not self.templates.has_key(x): missing = x
+ if missing:
+ print "Template %s required but not defined in %s" % (missing,self.code_template_filename)
+
+ if filename == "-":
+ fd = sys.stdout
+ else: fd = open(filename,"w")
+ self.foundmain = None
+ fd.write(self.templates["header"])
+ fd.write(self.templates["system"] % self.system)
+ for (menu,items) in self.menus:
+ self.print_menu(menu,fd)
+ for entry in items: self.print_entry(entry,fd)
+ fd.write(self.templates["footer"])
+ fd.close()
+ if not self.foundmain:
+ print "main menu not found"
+ print self.menus
+ sys.exit(1)
+
+ def input(self,filename):
+ if filename == "-":
+ fd = sys.stdin
+ else: fd = open(filename,"r")
+ self.lineno = 0
+ self.state = "system"
+ for line in fd.readlines():
+ self.lineno = self.lineno + 1
+ if line and line[-1] in ["\r","\n"]: line = line[:-1]
+ if line and line[-1] in ["\r","\n"]: line = line[:-1]
+ line = line.strip()
+ if line and line[0] in ["#",";"]: continue
+
+ try:
+ # blank line -> starting a new entry
+ if not line:
+ if self.state == "item": self.add_item()
+ continue
+
+ # starting a new section?
+ if line[0] == "[" and line[-1] == "]":
+ self.state = "menu"
+ self.add_menu(line[1:-1])
+ continue
+
+ # add property of current entry
+ pos = line.find("=") # find the first = in string
+ if pos < 0:
+ print "Syntax error in line %d" % self.lineno
+ print "REASON: non-section lines must be of the form ATTRIBUTE=VALUE"
+ sys.exit(1)
+ attr = line[:pos].strip().lower()
+ value = line[pos+1:].strip()
+ self.set(attr,value)
+ except:
+ print "Error while parsing line %d: %s" % (self.lineno,line)
+ raise
+ fd.close()
+ self.add_item()
+
+def usage():
+ print sys.argv[0]," [options]"
+ print "--input=<file> is the name of the .menu file declaring the menu structure"
+ print "--output=<file> is the name of generated C source"
+ print "--template=<file> is the name of template to be used"
+ print
+ print "input and output default to - (stdin and stdout respectively)"
+ print "template defaults to adv_menu.tpl"
+ sys.exit(1)
+
+def main():
+ tfile = "adv_menu.tpl"
+ ifile = "-"
+ ofile = "-"
+ opts,args = getopt.getopt(sys.argv[1:], "hi:o:t:",["input=","output=","template=","help"])
+ if args:
+ print "Unknown options %s" % args
+ usage()
+ for o,a in opts:
+ if o in ["-i","--input"]:
+ ifile = a
+ elif o in ["-o", "--output"]:
+ ofile = a
+ elif o in ["-t","--template"]:
+ tfile = a
+ elif o in ["-h","--help"]:
+ usage()
+
+ inst = Menusystem(tfile)
+ inst.input(ifile)
+ inst.output(ofile)
+
+if __name__ == "__main__":
+ main()