| #!/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(list(self.types.keys())) |
| self.vattrs = " OR ".join([x for x in list(self.entry.keys()) if x[0] != "_"]) |
| self.mattrs = " OR ".join([x for x in list(self.menu.keys()) if x[0] != "_"]) |
| |
| 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 name not in self.entry: |
| 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 value not in self.types: |
| 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 name not in self.menu: |
| return "Error: Unknown keyword %s" % name |
| self.menu[name] = value |
| self.menu["_updated"] = 1 |
| return "" |
| |
| def set_system(self,name,value): |
| if name not in self.system: |
| 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([_f for _f in parts if _f]) |
| 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 x not in self.templates: 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() |