#!/bin/sh
#
#  Copyright (c) 2009 Canonical
#
#  Author: Oliver Grawert <ogra@canonical.com>
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA
#
#####################################################################
# This script is supposed to sit in a minimal initramfs as /bin/init#
# it needs kexec and stty installed in this initramfs and will      #
# execute a kernel it finds on a root device (either MMC, USB or    #
# SATA/PATA) It requires a /boot/grub/menu.lst file on the target   #
# device                                                            #
#####################################################################

MOUNTPOINT="/root"
FILE="/boot/grub/menu.lst" # indeed that can be any file we want in any format we like
                           # though the pattern matches below need adjustment

# TODO find rootdevice and mount at $MOUNTPOINT (needs to probe partitions in order:
# mmcblk0p[1-n], then sd[a-n][1-n] and check for $FILE existence)

DEFAULT="$(grep ^default ${FILE}|sed -s 's/[a-z]//g')"
TIMEOUT="$(grep ^timeout ${FILE}|sed -s 's/[a-z]//g'|sed -s 's/\t//g')"
HIDDENMENU="$(grep ^hiddenmenu ${FILE})"

row=$DEFAULT
printrow=0

LIST="$(cat ${FILE}|grep ^title|sed -s 's/title//g'|sed -s 's/ /_/g'|sed -s 's/\t//g'|tr '\n' ' ')"

redraw()
{
    clear
    echo "Select a kernel to boot (Hit e to edit entry):\n"
    for option in $LIST;do
        option="$(echo $option|sed -s 's/_/ /g')"
        if [ $row = $printrow ];then
            echo "\033[7m $option\033[27m"
        else
            echo " $option"
        fi
        maxrow=$printrow
        if [ "$maxrow" -gt "20" ];then
            break
        fi
        printrow=$(($printrow+1))
    done

    printrow=0
}

show_menu()
{
    redraw

    old_tty_setting=$(stty -g) 
    stty -icanon -echo

    while true
    do
      key=$(dd bs=1 count=1 2> /dev/null)
      case $key in
          'A' )
            row=$(($row-1))
            if [ "$row" -lt 0 ];then
                row=0
            fi
            redraw
            ;;
          'B')
            row=$(($row+1))
            if [ "$row" -gt $maxrow ];then
                row=$maxrow
            fi
            redraw
            ;;
          'e')
            parse_data $row
            stty $old_tty_setting
            editor
            ;;
          "")
            parse_data $row
            stty $old_tty_setting
            boot
          ;;
      esac
    done
    stty $old_tty_setting
}

parse_data()
{
    clear
    KERNELS="$(cat ${FILE}|grep ^kernel|sed -s 's/^kernel//g'|sed -s 's/ /_/g'|sed -s 's/\t//g'|tr '\n' ' ')"
    for kernel in $KERNELS;do
        kernel="$(echo $kernel|sed -s 's/_/ /g')"
        if [ $1 = $printrow ];then
            TMPKERNEL=$kernel
        fi
        printrow=$(($printrow+1))
    done

    KERNEL=${TMPKERNEL%% *}
    OPTS="$(echo ${TMPKERNEL#* }|sed -s 's/ $//g')"

    printrow=0

    INITRDS="$(cat ${FILE}|grep ^initrd|sed -s 's/^initrd//g'|sed -s 's/ /_/g'|sed -s 's/\t//g'|tr '\n' ' ')"
    for initrd in $INITRDS;do
        initrd="$(echo $initrd|sed -s 's/_/ /g')"
        if [ $1 = $printrow ];then
            INITRD=$initrd
        fi
        printrow=$(($printrow+1))
    done
}

draw_edit()
{
    clear
    eprintrow=0
    echo "Select an entry to edit. Hit b to boot, q to go back\n"

    for line in "${KERNEL}" "${INITRD}" "${OPTS}";do
        line="$(echo $line|sed -s 's/_/ /g')"
        if [ $edrow = $eprintrow ];then
            echo "\033[7m$line\033[27m"
        else
            echo "$line"
        fi
        eprintrow=$(($eprintrow+1))
    done
}

showline()
{
    clear
    echo "Edit the line below and hit enter if you are done\n"

    case $1 in
        0)
          LINE="${KERNEL}"
          ;;
        1)
          LINE="${INITRD}"
          ;;
        2)
          LINE="${OPTS}"
          ;;
        3)
          LINE="${LINE}"
          ;;
    esac
    echo -n "${LINE}"
    old_tty_setting=$(stty -g) 
    stty -icanon -echo

    key=$(dd bs=1 count=1 2> /dev/null)
    case $key in
        '')
          LINE=${LINE%?}
          stty $old_tty_setting
          case $edrow in
          0)
            KERNEL="${LINE}"
            ;;
          1)
            INITRD="${LINE}"
            ;;
          2)
            OPTS="${LINE}"
            ;;
          esac
          showline 3
          ;;
        [[:alnum:][:blank:][:digit:][:punct:]])
          LINE="${LINE}${key}"
          stty $old_tty_setting
          case $edrow in
          0)
            KERNEL="${LINE}"
            ;;
          1)
            INITRD="${LINE}"
            ;;
          2)
            OPTS="${LINE}"
            ;;
          esac
          showline 3
          ;;
        "")
          stty $old_tty_setting
          editor
          ;;
        *)
          stty $old_tty_setting
          showline 3
          ;;
    esac

    stty $old_tty_setting
    exit 0
}

editor()
{
    edrow=0
    draw_edit
    old_tty_setting=$(stty -g) 
    stty -icanon -echo

    while true
    do
      key=$(dd bs=1 count=1 2> /dev/null)
      case $key in
          'A' )
              edrow=$(($edrow-1))
              if [ "$edrow" -lt 0 ];then
                  edrow=0
              fi
              draw_edit
              ;;
          'B')
              edrow=$(($edrow+1))
              if [ "$edrow" -gt 2 ];then
                  edrow=2
              fi
              draw_edit
            ;;
          'b')
            stty $old_tty_setting
            boot
            ;;
          'q')
            stty $old_tty_setting
            redraw
            show_menu
            ;;
          "")
            stty $old_tty_setting
            showline $edrow
            ;;
        esac
    done
}

boot()
{
    # TODO indeed remove the echo wrapped below, only there for testing purpose atm
    echo "kexec -l --command-line=\"${OPTS}\" --initrd=${MOUNTPOINT}${INITRD} ${MOUNTPOINT}${KERNEL}"
    echo "kexec -e"
    exit 0
}

init()
{
    if [ ! "${HIDDENMENU}" ];then
        show_menu
    else
        old_tty_setting=$(stty -g) 
        stty -icanon -echo time 0 min 0

        clear

        while true
        do
          key=$(dd bs=1 count=1 2> /dev/null)
          case $key in
              '')
                stty $old_tty_setting
                show_menu
                ;;
              *)
                ;;
          esac
          clear
          if [ "$TIMEOUT" -lt 1 ];then
              break
          else
              echo "Booting in: $TIMEOUT (Hit ESC for Menu)"
              TIMEOUT=$(($TIMEOUT-1))
              sleep 1
          fi
        done
        stty $old_tty_setting
    fi
    parse_data $row
    boot
}

init

exit 0
