Home | History | Annotate | Download | only in cmenu
      1 #!/usr/bin/env python
      2 
      3 import sys, re, getopt
      4 
      5 class Menusystem:
      6 
      7    types = {"run"      : "OPT_RUN",
      8             "inactive" : "OPT_INACTIVE",
      9             "checkbox" : "OPT_CHECKBOX",
     10             "radiomenu": "OPT_RADIOMENU",
     11             "sep"      : "OPT_SEP",
     12             "invisible": "OPT_INVISIBLE",
     13             "radioitem": "OPT_RADIOITEM",
     14             "exitmenu" : "OPT_EXITMENU",
     15             "login"    : "login", # special type
     16             "submenu"  : "OPT_SUBMENU"}
     17 
     18    entry_init = { "item" : "",
     19                   "info" : "",
     20                   "data" : "",
     21                   "ipappend" : 0, # flag to send in case of PXELINUX
     22                   "helpid" : 65535, # 0xFFFF
     23                   "shortcut":"-1",
     24                   "state"  : 0, # initial state of checkboxes
     25                   "argsmenu": "", # name of menu containing arguments
     26                   "perms"  : "", # permission required to execute this entry
     27                   "_updated" : None, # has this dictionary been updated
     28                   "type" : "run" }
     29 
     30    menu_init = {  "title" : "",
     31                   "row" : "0xFF", # let system decide position
     32                   "col" : "0xFF",
     33                   "_updated" : None,
     34                   "name" : "" }
     35 
     36    system_init ={ "videomode" : "0xFF",
     37                   "title" : "Menu System",
     38                   "top" : "1",
     39                   "left" : "1" ,
     40                   "bot" : "21",
     41                   "right":"79",
     42                   "helpdir" : "/isolinux/help",
     43                   "pwdfile" : "",
     44                   "pwdrow"  : "23",
     45                   "editrow" : "23",
     46                   "skipcondn"  : "0",
     47                   "skipcmd" : ".exit",
     48                   "startfile": "",
     49                   "onerrorcmd":".repeat",
     50                   "exitcmd"  : ".exit",
     51                   "exitcmdroot"  : "",
     52                   "timeout"  : "600",
     53                   "timeoutcmd":".beep",
     54                   "totaltimeout" : "0",
     55                   "totaltimeoutcmd" : ".wait"
     56                  }
     57 
     58    shift_flags = { "alt"  : "ALT_PRESSED",
     59                    "ctrl" : "CTRL_PRESSED",
     60                    "shift": "SHIFT_PRESSED",
     61                    "caps" : "CAPSLOCK_ON",
     62                    "num"  : "NUMLOCK_ON",
     63                    "ins"  : "INSERT_ON"
     64                  }
     65 
     66    reqd_templates = ["item","login","menu","system"]
     67 
     68    def __init__(self,template):
     69        self.state = "system"
     70        self.code_template_filename = template
     71        self.menus = []
     72        self.init_entry()
     73        self.init_menu()
     74        self.init_system()
     75        self.vtypes = " OR ".join(list(self.types.keys()))
     76        self.vattrs = " OR ".join([x for x in list(self.entry.keys()) if x[0] != "_"])
     77        self.mattrs = " OR ".join([x for x in list(self.menu.keys()) if x[0] != "_"])
     78 
     79    def init_entry(self):
     80        self.entry = self.entry_init.copy()
     81 
     82    def init_menu(self):
     83        self.menu = self.menu_init.copy()
     84 
     85    def init_system(self):
     86        self.system = self.system_init.copy()
     87 
     88    def add_menu(self,name):
     89        self.add_item()
     90        self.init_menu()
     91        self.menu["name"] = name
     92        self.menu["_updated"] = 1
     93        self.menus.append( (self.menu,[]) )
     94 
     95    def add_item(self):
     96        if self.menu["_updated"]: # menu details have changed
     97           self.menus[-1][0].update(self.menu)
     98           self.init_menu()
     99        if self.entry["_updated"]:
    100           if not self.entry["info"]:
    101              self.entry["info"] = self.entry["data"]
    102           if not self.menus:
    103              print("Error before line %d" % self.lineno)
    104              print("REASON: menu must be declared before a menu item is declared")
    105              sys.exit(1)
    106           self.menus[-1][1].append(self.entry)
    107        self.init_entry()
    108 
    109    def set_item(self,name,value):
    110        if name not in self.entry:
    111           msg = ["Unknown attribute %s in line %d" % (name,self.lineno)]
    112           msg.append("REASON: Attribute must be one of %s" % self.vattrs)
    113           return "\n".join(msg)
    114        if name=="type" and value not in self.types:
    115           msg = [ "Unrecognized type %s in line %d" % (value,self.lineno)]
    116           msg.append("REASON: Valid types are %s" % self.vtypes)
    117           return "\n".join(msg)
    118        if name=="shortcut":
    119           if (value != "-1") and not re.match("^[A-Za-z0-9]$",value):
    120              msg = [ "Invalid shortcut char '%s' in line %d" % (value,self.lineno) ]
    121              msg.append("REASON: Valid values are [A-Za-z0-9]")
    122              return "\n".join(msg)
    123           elif value != "-1": value = "'%s'" % value
    124        elif name in ["state","helpid","ipappend"]:
    125           try:
    126               value = int(value)
    127           except:
    128               return "Value of %s in line %d must be an integer" % (name,self.lineno)
    129        self.entry[name] = value
    130        self.entry["_updated"] = 1
    131        return ""
    132 
    133    def set_menu(self,name,value):
    134        if name not in self.menu:
    135           return "Error: Unknown keyword %s" % name
    136        self.menu[name] = value
    137        self.menu["_updated"] = 1
    138        return ""
    139 
    140    def set_system(self,name,value):
    141        if name not in self.system:
    142           return "Error: Unknown keyword %s" % name
    143        if name == "skipcondn":
    144           try: # is skipcondn a number?
    145              a = int(value)
    146           except: # it is a "-" delimited sequence
    147              value = value.lower()
    148              parts = [ self.shift_flags.get(x.strip(),None) for x in value.split("-") ]
    149              self.system["skipcondn"] = " | ".join([_f for _f in parts if _f])
    150        else:
    151           self.system[name] = value
    152 
    153    def set(self,name,value):
    154        # remove quotes if given
    155        if (value[0] == value[-1]) and (value[0] in ['"',"'"]): # remove quotes
    156           value = value[1:-1]
    157        if self.state == "system":
    158           err = self.set_system(name,value)
    159           if not err: return
    160        if self.state == "menu":
    161           err = self.set_menu(name,value)
    162           # change state to entry it menu returns error
    163           if err:
    164              err = None
    165              self.state = "item"
    166        if self.state == "item":
    167           err = self.set_item(name,value)
    168 
    169        if not err: return
    170 
    171        # all errors so return item's error message
    172        print(err)
    173        sys.exit(1)
    174 
    175    def print_entry(self,entry,fd):
    176        entry["type"] = self.types[entry["type"]]
    177        if entry["type"] == "login": #special type
    178           fd.write(self.templates["login"] % entry)
    179        else:
    180           fd.write(self.templates["item"] % entry)
    181 
    182    def print_menu(self,menu,fd):
    183        if menu["name"] == "main": self.foundmain = 1
    184        fd.write(self.templates["menu"] % menu)
    185        if (menu["row"] != "0xFF") or (menu["col"] != "0xFF"):
    186           fd.write('  set_menu_pos(%(row)s,%(col)s);\n' % menu)
    187 
    188 
    189    def output(self,filename):
    190        curr_template = None
    191        contents = []
    192        self.templates = {}
    193        regbeg = re.compile(r"^--(?P<name>[a-z]+) BEGINS?--\n$")
    194        regend = re.compile(r"^--[a-z]+ ENDS?--\n$")
    195        ifd = open(self.code_template_filename,"r")
    196        for line in ifd.readlines():
    197            b = regbeg.match(line)
    198            e = regend.match(line)
    199            if e: # end of template
    200               if curr_template:
    201                  self.templates[curr_template] = "".join(contents)
    202               curr_template = None
    203               continue
    204            if b:
    205               curr_template = b.group("name")
    206               contents = []
    207               continue
    208            if not curr_template: continue # lines between templates are ignored
    209            contents.append(line)
    210        ifd.close()
    211 
    212        missing = None
    213        for x in self.reqd_templates:
    214            if x not in self.templates: missing = x
    215        if missing:
    216            print("Template %s required but not defined in %s" % (missing,self.code_template_filename))
    217 
    218        if filename == "-":
    219           fd = sys.stdout
    220        else: fd = open(filename,"w")
    221        self.foundmain = None
    222        fd.write(self.templates["header"])
    223        fd.write(self.templates["system"] % self.system)
    224        for (menu,items) in self.menus:
    225            self.print_menu(menu,fd)
    226            for entry in items: self.print_entry(entry,fd)
    227        fd.write(self.templates["footer"])
    228        fd.close()
    229        if not self.foundmain:
    230           print("main menu not found")
    231           print(self.menus)
    232           sys.exit(1)
    233 
    234    def input(self,filename):
    235        if filename == "-":
    236           fd = sys.stdin
    237        else: fd = open(filename,"r")
    238        self.lineno = 0
    239        self.state = "system"
    240        for line in fd.readlines():
    241          self.lineno = self.lineno + 1
    242          if line and line[-1] in ["\r","\n"]: line = line[:-1]
    243          if line and line[-1] in ["\r","\n"]: line = line[:-1]
    244          line = line.strip()
    245          if line and line[0] in ["#",";"]: continue
    246 
    247          try:
    248            # blank line -> starting a new entry
    249            if not line:
    250               if self.state == "item": self.add_item()
    251               continue
    252 
    253            # starting a new section?
    254            if line[0] == "[" and line[-1] == "]":
    255               self.state = "menu"
    256               self.add_menu(line[1:-1])
    257               continue
    258 
    259            # add property of current entry
    260            pos = line.find("=") # find the first = in string
    261            if pos < 0:
    262               print("Syntax error in line %d" % self.lineno)
    263               print("REASON: non-section lines must be of the form ATTRIBUTE=VALUE")
    264               sys.exit(1)
    265            attr = line[:pos].strip().lower()
    266            value = line[pos+1:].strip()
    267            self.set(attr,value)
    268          except:
    269             print("Error while parsing line %d: %s" % (self.lineno,line))
    270             raise
    271        fd.close()
    272        self.add_item()
    273 
    274 def usage():
    275     print(sys.argv[0]," [options]")
    276     print("--input=<file>    is the name of the .menu file declaring the menu structure")
    277     print("--output=<file>   is the name of generated C source")
    278     print("--template=<file> is the name of template to be used")
    279     print()
    280     print("input and output default to - (stdin and stdout respectively)")
    281     print("template defaults to adv_menu.tpl")
    282     sys.exit(1)
    283 
    284 def main():
    285     tfile = "adv_menu.tpl"
    286     ifile = "-"
    287     ofile = "-"
    288     opts,args = getopt.getopt(sys.argv[1:], "hi:o:t:",["input=","output=","template=","help"])
    289     if args:
    290        print("Unknown options %s" % args)
    291        usage()
    292     for o,a in opts:
    293         if o in ["-i","--input"]:
    294            ifile = a
    295         elif o in ["-o", "--output"]:
    296            ofile = a
    297         elif o in ["-t","--template"]:
    298            tfile = a
    299         elif o in ["-h","--help"]:
    300            usage()
    301 
    302     inst = Menusystem(tfile)
    303     inst.input(ifile)
    304     inst.output(ofile)
    305 
    306 if __name__ == "__main__":
    307    main()
    308