@@ -, +, @@ 
  pyGrub: Implement Grub2 configuration classes to GrubConf.py
  pyGrub: Rewrite pygrub for Grub2 implementation
  pyGrub: Implement new_image to LiloConfigFile class
 tools/pygrub/src/GrubConf.py |  299 ++++++++++++++++++++++++++++++++----------
 tools/pygrub/src/LiloConf.py |    6 +
 tools/pygrub/src/pygrub      |   40 ++++---
 3 files changed, 257 insertions(+), 88 deletions(-)
 tools/pygrub/src/GrubConf.py |  299 ++++++++++++++++++++++++++++++++----------
 1 files changed, 228 insertions(+), 71 deletions(-)
--- a/tools/pygrub/src/GrubConf.py	
+++ a/tools/pygrub/src/GrubConf.py	
@@ -14,6 +14,7 @@ 
 
 import os, sys
 import logging
+import re
 
 def grub_split(s, maxsplit = -1):
     eq = s.find('=')
@@ -78,9 +79,10 @@ class GrubDiskPart(object):
         self._part = int(val)
     part = property(get_part, set_part)
 
-class GrubImage(object):
-    def __init__(self, lines):
+class _GrubImage(object):
+    def __init__(self, title, lines):
         self.reset(lines)
+        self.title = title.strip()
 
     def __repr__(self):
         return ("title: %s\n" 
@@ -90,29 +92,14 @@ class GrubImage(object):
                 "  initrd: %s\n" %(self.title, self.root, self.kernel,
                                    self.args, self.initrd))
 
+    def _parse(self, lines):
+        map(self.set_from_line, lines)
+
     def reset(self, lines):
         self._root = self._initrd = self._kernel = self._args = None
         self.title = ""
         self.lines = []
-        map(self.set_from_line, lines)
-
-    def set_from_line(self, line, replace = None):
-        (com, arg) = grub_exact_split(line, 2)
-
-        if self.commands.has_key(com):
-            if self.commands[com] is not None:
-                setattr(self, self.commands[com], arg.strip())
-            else:
-                logging.info("Ignored image directive %s" %(com,))
-        else:
-            logging.warning("Unknown image directive %s" %(com,))
-
-        # now put the line in the list of lines
-        if replace is None:
-            self.lines.append(line)
-        else:
-            self.lines.pop(replace)
-            self.lines.insert(replace, line)
+        self._parse(lines)
 
     def set_root(self, val):
         self._root = GrubDiskPart(val)
@@ -141,9 +128,30 @@ class GrubImage(object):
         return self._initrd
     initrd = property(get_initrd, set_initrd)
 
+class GrubImage(_GrubImage):
+    def __init__(self, title, lines):
+        _GrubImage.__init__(self, title, lines)
+    
+    def set_from_line(self, line, replace = None):
+        (com, arg) = grub_exact_split(line, 2)
+
+        if self.commands.has_key(com):
+            if self.commands[com] is not None:
+                setattr(self, self.commands[com], arg.strip())
+            else:
+                logging.info("Ignored image directive %s" %(com,))
+        else:
+            logging.warning("Unknown image directive %s" %(com,))
+
+        # now put the line in the list of lines
+        if replace is None:
+            self.lines.append(line)
+        else:
+            self.lines.pop(replace)
+            self.lines.insert(replace, line)
+
     # set up command handlers
-    commands = { "title": "title",
-                 "root": "root",
+    commands = { "root": "root",
                  "rootnoverify": "root",
                  "kernel": "kernel",
                  "initrd": "initrd",
@@ -151,7 +159,7 @@ class GrubImage(object):
                  "module": None}
         
 
-class GrubConfigFile(object):
+class _GrubConfigFile(object):
     def __init__(self, fn = None):
         self.filename = fn
         self.images = []
@@ -164,50 +172,7 @@ class GrubConfigFile(object):
             self.parse()
 
     def parse(self, buf = None):
-        if buf is None:
-            if self.filename is None:
-                raise ValueError, "No config file defined to parse!"
-
-            f = open(self.filename, 'r')
-            lines = f.readlines()
-            f.close()
-        else:
-            lines = buf.split("\n")
-
-        img = []
-        for l in lines:
-            l = l.strip()
-            # skip blank lines
-            if len(l) == 0:
-                continue
-            # skip comments
-            if l.startswith('#'):
-                continue
-            # new image
-            if l.startswith("title"):
-                if len(img) > 0:
-                    self.add_image(GrubImage(img))
-                img = [l]
-                continue
-                
-            if len(img) > 0:
-                img.append(l)
-                continue
-
-            (com, arg) = grub_exact_split(l, 2)
-            if self.commands.has_key(com):
-                if self.commands[com] is not None:
-                    setattr(self, self.commands[com], arg.strip())
-                else:
-                    logging.info("Ignored directive %s" %(com,))
-            else:
-                logging.warning("Unknown directive %s" %(com,))
-                
-        if len(img) > 0:
-            self.add_image(GrubImage(img))
-
-        if self.hasPassword():
-            self.setPasswordAccess(False)
+        raise RuntimeError, "unimplemented parse function"
 
     def hasPasswordAccess(self):
         return self.passwordAccess
@@ -286,10 +251,202 @@ class GrubConfigFile(object):
         commands[c] = None
     del c
 
+class GrubConfigFile(_GrubConfigFile):
+    def __init__(self, fn = None):
+        _GrubConfigFile.__init__(self,fn)
+        
+    def new_image(self, title, lines):
+        return GrubImage(title, lines)
+
+    def parse(self, buf = None):
+        if buf is None:
+            if self.filename is None:
+                raise ValueError, "No config file defined to parse!"
+
+            f = open(self.filename, 'r')
+            lines = f.readlines()
+            f.close()
+        else:
+            lines = buf.split("\n")
+
+        img = None
+        title = ""
+        for l in lines:
+            l = l.strip()
+            # skip blank lines
+            if len(l) == 0:
+                continue
+            # skip comments
+            if l.startswith('#'):
+                continue
+            # new image
+            if l.startswith("title"):
+                if img is not None:
+                    self.add_image(GrubImage(title, img))
+                img = []
+                title = l[6:]
+                continue
+                
+            if img is not None:
+                img.append(l)
+                continue
+
+            (com, arg) = grub_exact_split(l, 2)
+            if self.commands.has_key(com):
+                if self.commands[com] is not None:
+                    setattr(self, self.commands[com], arg.strip())
+                else:
+                    logging.info("Ignored directive %s" %(com,))
+            else:
+                logging.warning("Unknown directive %s" %(com,))
+        if img:
+            self.add_image(GrubImage(title, img))
+
+        if self.hasPassword():
+            self.setPasswordAccess(False)
+
+def grub2_handle_set(arg):
+    (com,arg) = grub_split(arg,2)
+    com="set:" + com
+    m = re.match("([\"\'])(.*)\\1", arg)
+    if m is not None:
+        arg=m.group(2) 
+    return (com,arg)
+
+class Grub2Image(_GrubImage):
+    def __init__(self, title, lines):
+        _GrubImage.__init__(self, title, lines)
+
+    def set_from_line(self, line, replace = None):
+        (com, arg) = grub_exact_split(line, 2)
+
+        if com == "set":
+            (com,arg) = grub2_handle_set(arg)
+    
+        if self.commands.has_key(com):
+            if self.commands[com] is not None:
+                setattr(self, self.commands[com], arg.strip())
+            else:
+                logging.info("Ignored image directive %s" %(com,))
+        elif com.startswith('set:'):
+            pass
+        else:
+            logging.warning("Unknown image directive %s" %(com,))
+
+        # now put the line in the list of lines
+        if replace is None:
+            self.lines.append(line)
+        else:
+            self.lines.pop(replace)
+            self.lines.insert(replace, line)
+    commands = {'set:root': 'root',
+                'linux': 'kernel',
+                'initrd': 'initrd',
+                'echo': None,
+                'insmod': None,
+                'search': None}
+    
+class Grub2ConfigFile(_GrubConfigFile):
+    def __init__(self, fn = None):
+        _GrubConfigFile.__init__(self, fn)
+        
+    def new_image(self, title, lines):
+        return Grub2Image(title, lines)
+
+    def parse(self, buf = None):
+        if buf is None:
+            if self.filename is None:
+                raise ValueError, "No config file defined to parse!"
+
+            f = open(self.filename, 'r')
+            lines = f.readlines()
+            f.close()
+        else:
+            lines = buf.split("\n")
+
+        in_function = False
+        img = None
+        title = ""
+        for l in lines:
+            l = l.strip()
+            # skip blank lines
+            if len(l) == 0:
+                continue
+            # skip comments
+            if l.startswith('#'):
+                continue
+
+            # skip function declarations
+            if l.startswith('function'):
+                in_function = True
+                continue
+            if in_function:
+                if l.startswith('}'):
+                    in_function = False
+                continue
+
+            # new image
+            title_match = re.match('^menuentry ["\'](.*)["\'] (.*){', l)
+            if title_match:
+                if img is not None:
+                    raise RuntimeError, "syntax error: cannot nest menuentry (%d %s)" % (len(img),img)
+                img = []
+                title = title_match.group(1)
+                continue
+            
+            if l.startswith("}"):
+                if img is None:
+                   raise RuntimeError, "syntax error: closing brace without menuentry"
+
+                self.add_image(Grub2Image(title, img))
+                img = None
+                continue
+
+            if img is not None:
+                img.append(l)
+                continue
+
+            (com, arg) = grub_exact_split(l, 2)
+        
+            if com == "set":
+                (com,arg) = grub2_handle_set(arg)
+
+            if self.commands.has_key(com):
+                if self.commands[com] is not None:
+                    setattr(self, self.commands[com], arg.strip())
+                else:
+                    logging.info("Ignored directive %s" %(com,))
+            elif com.startswith('set:'):
+                pass
+            else:
+                logging.warning("Unknown directive %s" %(com,))
+            
+        if img is not None:
+            raise RuntimeError, "syntax error: end of file with open menuentry(%d %s)" % (len(img),img)
+
+        if self.hasPassword():
+            self.setPasswordAccess(False)
+
+    commands = {'set:default': 'default',
+                'set:root': 'root',
+                'set:timeout': 'timeout',
+                'terminal': None,
+                'insmod': None,
+                'load_env': None,
+                'save_env': None,
+                'search': None,
+                'if': None,
+                'fi': None,
+                }
 
 if __name__ == "__main__":
-    if sys.argv < 2:
-        raise RuntimeError, "Need a grub.conf to read"
-    g = GrubConfigFile(sys.argv[1])
+    if sys.argv < 3:
+        raise RuntimeError, "Need a grub version (\"grub\" or \"grub2\") and a grub.conf or grub.cfg to read"
+    if sys.argv[1] == "grub":
+        g = GrubConfigFile(sys.argv[2])
+    elif sys.argv[1] == "grub2":
+        g = Grub2ConfigFile(sys.argv[2])
+    else:
+        raise RuntimeError, "Unknown config type %s" % sys.argv[1]
     for i in g.images:
         print i #, i.title, i.root, i.kernel, i.args, i.initrd
 tools/pygrub/src/pygrub |   40 +++++++++++++++++++++++-----------------
 1 files changed, 23 insertions(+), 17 deletions(-)
--- a/tools/pygrub/src/pygrub	
+++ a/tools/pygrub/src/pygrub	
@@ -354,7 +354,7 @@ class Grub:
                     continue
 
                 # if we got boot, then we want to boot the entered image 
-                img = grub.GrubConf.GrubImage(lines)
+                img = self.cf.new_image("entered", lines)
                 self.cf.add_image(img)
                 self.selected_image = len(self.cf.images) - 1
                 self.isdone = True
@@ -371,26 +371,32 @@ class Grub:
             raise RuntimeError, "Unable to access %s" %(fn,)
 
         if platform.machine() == 'ia64':
-            self.cf = grub.LiloConf.LiloConfigFile()
-            # common distributions
-            file_list = ("/efi/debian/elilo.conf", "/efi/gentoo/elilo.conf", 
-                         "/efi/redflag/elilo.conf", "/efi/redhat/elilo.conf", 
-                         "/efi/SuSE/elilo.conf",)
-            # fallbacks
-            file_list += ("/efi/boot/elilo.conf", "/elilo.conf",)
+            cfg_list = map(lambda x: (x,grub.LiloConf.LiloConfigFile),
+                           # common distributions
+                           ["/efi/debian/elilo.conf", "/efi/gentoo/elilo.conf",
+                            "/efi/redflag/elilo.conf", "/efi/redhat/elilo.conf",
+                            "/efi/SuSE/elilo.conf",] +
+                           # fallbacks
+                           ["/efi/boot/elilo.conf", "/elilo.conf",])
         else:
-            self.cf = grub.GrubConf.GrubConfigFile()
-            file_list = ("/boot/grub/grub.conf", "/boot/grub/menu.lst",
-                         "/grub/grub.conf", "/grub/menu.lst")
+            cfg_list = map(lambda x: (x,grub.GrubConf.GrubConfigFile),
+                           ["/boot/grub/menu.lst", "/boot/grub/grub.conf",
+                            "/grub/menu.lst", "/grub/grub.conf"]) + \
+                       map(lambda x: (x,grub.GrubConf.Grub2ConfigFile),
+                           ["/boot/grub/grub.cfg", "/grub/grub.cfg"])
 
         if not fs:
             # set the config file and parse it
-            self.cf.filename = fn
-            self.cf.parse()
-            return
+            for f,parser in cfg_list:
+                self.cf = parser()
+                self.cf.filename = fn
+                self.cf.parse()
+                return
 
-        for f in file_list:
+        for f,parser in cfg_list:
             if fs.file_exists(f):
+                print >>sys.stderr, "Using %s to parse %s" % (parser,f)
+                self.cf = parser()
                 self.cf.filename = f
                 break
         if self.cf.filename is None:
@@ -667,9 +673,9 @@ if __name__ == "__main__":
 
     # debug
     if isconfig:
-        chosencfg = run_grub(file, entry)
+        chosencfg = run_grub(file, entry, None)
         print "  kernel: %s" % chosencfg["kernel"]
-        if img.initrd:
+        if chosencfg["ramdisk"]:
             print "  initrd: %s" % chosencfg["ramdisk"]
         print "  args: %s" % chosencfg["args"]
         sys.exit(0)
 tools/pygrub/src/LiloConf.py |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)
--- a/tools/pygrub/src/LiloConf.py	
+++ a/tools/pygrub/src/LiloConf.py	
@@ -147,6 +147,12 @@ class LiloConfigFile(object):
     def add_image(self, image):
         self.images.append(image)
 
+    def new_image(self, title, lines):
+        # LiloImage constructor doesn't have title but since path
+        # is being used by get_{kernel|initrd} functions we pass
+        # empty string rather than None (see lines above)
+        return LiloImage(lines, "")
+
     def _get_default(self):
         for i in range(len(self.images)):
             if self.images[i].title == self._default:
