libinstaller: Use SOURCE_DATE_EPOCH for synthesized modification time stamps
[syslinux.git] / com32 / cmenu / menugen.py
blobda64d9373ac702e783d8728320835910459790fe
1 #!/usr/bin/env python
3 import sys, re, getopt
5 class Menusystem:
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"}
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" }
30 menu_init = { "title" : "",
31 "row" : "0xFF", # let system decide position
32 "col" : "0xFF",
33 "_updated" : None,
34 "name" : "" }
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"
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"
66 reqd_templates = ["item","login","menu","system"]
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] != "_"])
79 def init_entry(self):
80 self.entry = self.entry_init.copy()
82 def init_menu(self):
83 self.menu = self.menu_init.copy()
85 def init_system(self):
86 self.system = self.system_init.copy()
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,[]) )
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()
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 ""
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 ""
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
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)
169 if not err: return
171 # all errors so return item's error message
172 print(err)
173 sys.exit(1)
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)
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)
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()
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))
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)
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
247 try:
248 # blank line -> starting a new entry
249 if not line:
250 if self.state == "item": self.add_item()
251 continue
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
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()
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)
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()
302 inst = Menusystem(tfile)
303 inst.input(ifile)
304 inst.output(ofile)
306 if __name__ == "__main__":
307 main()