#! /usr/bin/python
# 
# $Id: bootconf,v 1.8 2010/04/24 16:34:17 mrsam Exp $
#
# Copyright 2006-2010 Double Precision, Inc.
# See COPYING for distribution information.

import sys
import re
import stat
import os

import gettext
gettext.install('bootconf')

configfile="/boot/grub/grub.conf"
os.umask(077)

copyright="""bootconf

Copyright 2006-2010, Double Precision, Inc.

See COPYING file for distribution information
"""

if stat.S_ISLNK(os.stat(configfile)[0]):
    print _("Error: GRUB configuration file cannot be a link: ") + configfile
    sys.exit(1)

def quiet1(line):
    return re.sub(r'\s+quiet', '', line) + " quiet"

def quiet0(line):
    return re.sub(r'\s+quiet', '', line)

def isquiet(line):
    return re.search(r'\s+quiet', line) != None

def gui1(line):
    return re.sub(r'\s+rhgb', '', line) + " rhgb"

def gui0(line):
    return re.sub(r'\s+rhgb', '', line)

def isgui(line):
    return re.search(r'\s+rhgb', line) != None

parameters={ 'quiet': [ isquiet, quiet0, quiet1],
             'gui': [ isgui, gui0, gui1,]
             }

vesamodes={ '640x480x8': 0x301,
            '640x480x15': 0x310,
            '640x480x16': 0x311,
            '640x480x24': 0x312,
            '800x600x8': 0x303,
            '800x600x15': 0x313,
            '800x600x16': 0x314,
            '800x600x24': 0x315,
            '1024x768x8': 0x305,
            '1024x768x15': 0x316,
            '1024x768x16': 0x317,
            '1024x768x24': 0x318,
            '1280x1024x8': 0x307,
            '1280x1024x15': 0x319,
            '1280x1024x16': 0x31A,
            '1280x1024x24': 0x31B,
            '1600x1200x8': 0x31C,
            '1600x1200x15': 0x31D,
            '1600x1200x16': 0x31E,
            '1600x1200x24': 0x31F
            }


def vesacmp(a, b):
    a=a.split('x')
    b=b.split('x')

    an=""
    for n in a:
        an=an + ("00000"+n)[-4:]
    bn=""
    for n in b:
        bn=bn + ("00000"+n)[-4:]

    if an < bn:
        return -1
    if an > bn:
        return 1
    return 0

def vesa(line,mode):
    return re.sub(r'\s+vga=\d+', '', line) + " vga=" + mode

def novesa(line):
    return re.sub(r'\s+vga=\d+', '', line)

def getconfig():
    f=open(configfile, "r")
    c={'default': 0}

    titles=[]

    found=False

    for line in f:
        line=re.sub(r'\n$', '', line)
        if re.search(r'^title\s', line):
            titles.append(re.sub(r'^title\s+', '', line))
            continue
        if re.search(r'^default=', line):
            c['default']=re.sub(r'^default=', '', line)
            continue

        if re.search(r'^\s*kernel\s', line) and not found:
            found=True
            for name, value in parameters.iteritems():
                v="0"
                if value[0](line):
                    v="1"
                c[name]=v

            v=re.search(r'\s+vga=(\d+)', line)

            if v != None:
                m=v.group(1).lower()
                if m[0:2] == "0x":
                    m=int(m, 16)
                else:
                    m=int(m)

                for name, mode in vesamodes.iteritems():
                    if mode == m:
                        c["vesa"]=name

    c['$titles']=titles

    return c

def updateconfig(settings):

    funclist=[]

    defaultboot=""
    
    for arg in settings:
        if arg[0:8] == "default=":
            defaultboot=arg
            continue
        if arg[0] == "$":
            continue

        if arg[0:5] == "vesa=":
            if not vesamodes.has_key(arg[5:]):
                print _("Unknown VESA mode: ") + arg[5:]
                sys.exit(1)


            def setvesa(line):
                return vesa(line, str(vesamodes[arg[5:]]))

            funclist.append(setvesa)
            continue

        if arg == "novesa":
            funclist.append(novesa)
            continue

        if arg[0:2] == "no":
            if not parameters.has_key(arg[2:]):
                print _("Unknown parameter: ") + arg
                sys.exit(1)
            funclist.append(parameters[arg[2:]][1])
            continue

        if arg.find("=") < 0:
            arg=arg + "=1"

        n=arg.find("=")

        v=arg[n+1:]
        arg=arg[0:n]

        if not parameters.has_key(arg) or (v != "0" and v != "1"):
            print _("Unknown parameter: ") + arg + "=" + v
            sys.exit(1)

        funclist.append(parameters[arg][1+int(v)])

    f=open(configfile, "r")
    o=open(configfile + ".new", "w")

    for line in f:

        line=re.sub(r'\n$', '', line)

        if re.search(r'^default=', line):
            if defaultboot == "":
                defaultboot=line
            continue

        if re.search(r'^title\s', line):
            if defaultboot != "":
                o.write(defaultboot + "\n")
                defaultboot=""

        if re.search(r'^\s*kernel\s', line):
            for func in funclist:
                line=func(line)

        o.write(line + "\n")

    o.close()
    o=None

    prevname=configfile + ".5"
    for rotatename in [".4", ".3", ".2", ".1"]:
        name=configfile + rotatename
        try:
            os.rename(name, prevname)
        except:
            pass
        prevname=name

    os.link(configfile, configfile + ".1")
    os.rename(configfile + ".new", configfile)

#######

if (not os.environ.has_key("DISPLAY")) or os.environ["DISPLAY"] == "" or len(sys.argv) > 1:
    if (not os.access(configfile, os.W_OK)):
        sys.stderr.write(_("bootconf can only be invoked by root") + "\n");
        sys.exit(1)

    if len(sys.argv) <= 1:
        conf=getconfig()
        for name, value in conf.iteritems():
            if name[0] != "$":
                print name + "=" + value

        if not conf.has_key("vesa"):
            print "novesa"
        sys.exit(0)

    if sys.argv[1] == "-titles":
        conf=getconfig()
        n=0
        for title in conf["$titles"]:
            print str(n) + ": " + title
            n=n+1

        sys.exit(0)

    sys.stderr.write(copyright)
    argv=sys.argv
    argv.pop(0)

    updateconfig(argv)
    sys.exit(0)

changed_settings={}

def destroy_event(widget, data=None):
    gtk.main_quit()

def cancel_clicked(widget, data=None):
    gtk.main_quit()

def ok_clicked(widget, data=None):
    updateconfig(changed_settings.itervalues())
    gtk.main_quit()

def about_bootconf(widget, mainwindow):

    tb=gtk.TextBuffer()
    tb.set_text(copyright)
    tv=gtk.TextView(tb)
    tv.set_editable(False)
    
    d=gtk.Dialog(_("About bootconf"), mainwindow,
                 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                 (gtk.STOCK_OK, gtk.RESPONSE_OK))
    d.vbox.pack_start(tv)
    d.show_all()
    d.run()
    d.destroy()

import pygtk
pygtk.require('2.0')
import gnome
import gtk
import gtk.gdk
import gtk.keysyms

if (not os.access(configfile, os.W_OK)):
    dialog=gtk.MessageDialog(None,
                             gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                             gtk.MESSAGE_ERROR,
                             gtk.BUTTONS_OK,
                             _("This function is only available to the system administrator"));
    dialog.set_title(_("Error"));
    dialog.run()
    sys.exit(1)

setting_names=[("quiet", _("Quiet boot")), ("gui", _("Graphical boot"))]

w=gtk.Window(gtk.WINDOW_TOPLEVEL)
w.set_title(_("Boot options"))
w.connect("destroy", destroy_event)

vbox=gtk.VBox(False, 4)
w.add(vbox)

mb=gtk.MenuBar()
mb.show()

vbox.pack_start(mb, False, False, 2)

mi=gtk.MenuItem(_("About"))
mi.set_right_justified(True)
mb.append(mi)

m=gtk.Menu()
mi.set_submenu(m)

mi=gtk.MenuItem(_("bootconf"))
mi.show()
mi.connect("activate", about_bootconf, w)
m.append(mi)

c=getconfig()

khb=gtk.HBox()
vbox.add(khb)

khb.add(gtk.Label(_("Default kernel: ")))

kcb=gtk.combo_box_new_text()

khb.add(kcb)

for title in c["$titles"]:
    kcb.append_text(title)

try:
    n=int(c["default"])
    kcb.set_active(n)
except:
    pass

def kcb_changed(widget):
    n=widget.get_active()
    changed_settings["default"]="default=" + str(n)

kcb.connect("changed", kcb_changed)



for label, descr in setting_names:
    t=gtk.CheckButton(descr)

    def new_setting(widget, name):
        value="0"
        if widget.get_active():
            value="1"
        changed_settings[name]=name + "=" + value

    if c.has_key(label) and c[label] == "1":
        t.set_active(True)
    t.connect("toggled", new_setting, label)
    vbox.add(t)

vbox.add(gtk.HSeparator())

vhb=gtk.HBox()
vbox.add(vhb)

vhb.add(gtk.Label(_("VESA framebuffer: ")))

vesacb=gtk.combo_box_new_text()

vesacb.append_text("(none)")

vesamodelist=[]
for name, value in vesamodes.iteritems():
    vesamodelist.append(name)

def vesacb_changed(widget):
    n=widget.get_active()

    if n == 0:
        changed_settings["vesa"]="novesa"
    else:
        changed_settings["vesa"]="vesa=" + vesamodelist[n-1]

vesamodelist.sort(vesacmp)

for name in vesamodelist:
    vesacb.append_text(name)

vesacb.connect("changed", vesacb_changed)

vhb.add(vesacb)

if c.has_key("vesa"):
    i=1

    for n in vesamodelist:
        if n == c["vesa"]:
            vesacb.set_active(i)
        i=i+1

vbox.add(gtk.HSeparator())

hba=gtk.Alignment(xalign=1.0)
vbox.add(hba)

bb=gtk.HBox(True)
hba.add(bb);

ok=gtk.Button("gtk-ok");
ok.set_use_stock(True)
ok.set_property("border-width", 4)
ok.connect("clicked", ok_clicked)
bb.add(ok)

cancel=gtk.Button("gtk-cancel");
cancel.set_use_stock(True)
cancel.set_property("border-width", 4)
cancel.connect("clicked", cancel_clicked)
bb.add(cancel)

w.show_all()

gtk.main()
