#!/usr/bin/env perl
#-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-

# Boot manager configurator: grub-related routines.
#
# Copyright (C) 2001 Ximian, Inc.
#
# Authors: Arturo Espinosa <arturo@ximian.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library 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 Library General Public License for more details.
#
# You should have received a copy of the GNU Library 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.


$SCRIPTSDIR = "/usr/X11R6/share/setup-tool-backends/scripts";
if ($SCRIPTSDIR =~ /^@scriptsdir[@]/)
{
    $SCRIPTSDIR = ".";
    $DOTIN = ".in";
}

require "$SCRIPTSDIR/general.pl$DOTIN";
require "$SCRIPTSDIR/util.pl$DOTIN";
require "$SCRIPTSDIR/file.pl$DOTIN";
require "$SCRIPTSDIR/xml.pl$DOTIN";
require "$SCRIPTSDIR/parse.pl$DOTIN";
require "$SCRIPTSDIR/replace.pl$DOTIN";
require "$SCRIPTSDIR/partition.pl$DOTIN";

my @grub_common_image_vars = qw (title);
my @grub_image_vars = (@grub_common_image_vars, qw (root kernel ));
my @grub_other_vars = (@grub_common_image_vars, qw (rootnoverify makeactive chainloader));

sub gst_boot_grub_verify
{
  return "success";
}

sub gst_boot_grub_get_config_file
{
  return "/boot/grub/menu.lst"  if -f "/boot/grub/menu.lst";
  return "/boot/grub/grub.conf" if -f "/boot/grub/grub.conf";
  return "/etc/grub.conf"       if (-f "/etc/grub.conf");

  return undef;
}

# find out which is the partition where the file (image or initrd) is stored in, just to
# put /file instead of /path-to-boot/file
sub gst_boot_grub_set_file_path
{
  my ($image, $root) = @_;
  my ($mtab, $dir);

  $mtab = "$gst_prefix/etc/mtab";
  $mntopts = &gst_parse_split_first_array ($mtab, $root, "[ \t]+", "[ \t]+");
  $dir = $$mntopts[0];

  if ($dir ne "")
  {
    if ($dir ne "/") # if mount point is /, we don't need to remove it
    {
      $image =~ s/^$dir//;
    }
  }

  return $image;
}

sub gst_boot_grub_known_var
{
  my $key = shift;
  my $list = shift;
  my $from_xml = shift;

  if (ref ($list) ne "ARRAY")
  {
    # TODO: Give warning;
    return 0;
  }

  $key = lc ($key);
  
  # Hard coded known variables which are not standard variables.
  return 0 if ($key eq "key" && $from_xml); # "key" is valid in xml only.
  return 0 if ($key eq "type" && $from_xml); # "key" is valid in xml only.

  return &gst_item_is_in_list ($key, @$list);
}

# Scans @buff until finds first line which looks like entry.
# Returns line number or -1 if no entry found.
sub gst_boot_grub_find_entry
{
  my ($buff, $lineno) = @_;
  my $i;

  for (; $lineno <= $#$buff; $lineno++)
  {
    $i = $$buff[$lineno];

    return $lineno if ($i =~ /^[ \t]*(title)[ \t]+\S+/);
  }

  # Not found.
  return -1;
}

sub gst_boot_grub_delete_entry
{
	my ($buff, $lineno) = @_;
	my ($end);
	
	$end = &gst_boot_grub_find_entry ($buff, $lineno + 1);
	$end = scalar @$buff if ($end < 0);
	
	# We delete all entry lines
	for ($lineno; $lineno < $end; $lineno++)
	{
#		delete $$buff[$lineno];
		$$buff[$lineno] = "";
	}
	
	return $buff;
}

sub gst_boot_grub_add_entry
{
  my ($entry, $buff, $lineno, $device_map) = @_;
  my ($end, $val, $value, $image, $initrd, $module);
	
  if (exists ($entry->{"image"}))
  {
    # It's a linux image
    $value = $entry->{"root"};
    $value = &gst_boot_grub_system2grub ($device_map, $value);

    $image = &gst_boot_grub_set_file_path ($entry->{"image"}, $entry->{"root"});
    if (exists ($entry->{"initrd"}) && ($entry->{"initrd"} ne "")) {
      $initrd = &gst_boot_grub_set_file_path ($entry->{"initrd"}, $entry->{"root"});
    }

    if (exists ($entry->{"module"}) && ($entry->{"module"} ne "")) {
	   $module = &gst_boot_grub_set_file_path  ($entry->{"module"}, $entry->{"root"});
    }

    $val = "title " . $entry->{"label"} . "\n";
    $val .= "\troot (". $value. ")\n";
    $val .= "\tkernel " . $image . " " . $entry->{"append"} . "\n";
    if ($initrd ne "")
    {
      $val .= "\tinitrd ". $initrd . "\n";
    }

    if ($module ne "")
    {
	 $val .= "\tmodule ". $module. "\n";
    }
  }
  elsif (exists ($entry->{"other"}))
  {
    # It's another boot
    $value = $entry->{"other"};
    $value = &gst_boot_grub_system2grub ($device_map, $value);
		
    $val = "title " . $entry->{"label"} . "\n";
    $val .= "\trootnoverify (" . $value . ")\n";
    $val .= "\tmakeactive\n";
    $val .= "\tchainloader +1\n";
  }
  else
  {
    return;
  }

  if (exists ($entry->{"password"}))
  {
    $val .= "\tpassword " . $entry->{"password"} . "\n";
  }

  $val .= "\n";

  $$buff [$lineno - 1] = $$buff [$lineno - 1] . $val;
}

sub gst_boot_grub_entry_is_valid
{
  my ($buff, $lineno) = @_;
  my ($end, $value, $line, $i);

  $value = 0;
  $end = &gst_boot_grub_find_entry ($buff, $lineno + 1);

  if ($end < 1)
  {
    # no more entries left, get the size of the array
    $end = @$buff;
  }

  for ($i = $lineno; $i < $end; $i++)
  {
    $line = $$buff[$i];
      
    if (($line =~ /[\t ]*kernel.*/) ||
        ($line =~ /[\t ]*rootnoverify.*/)) {
      $value = 1;
    }
  }

  return $value;
}

sub gst_boot_grub_entries_set
{
  my ($file, $device_map, $mtab, $entries) = @_;
  my ($buff, $lineno, $entry, $entry_nr, $found);

  return if (scalar @$entries <=0);

  $buff = &gst_file_buffer_load ($file);

  if ($buff eq undef)
  {
    &gst_report ("boot_conf_read_failed", $file);
    return undef;
  }
  
  &gst_file_buffer_join_lines ($buff);
	
  $lineno = &gst_boot_grub_find_entry ($buff, 0);

  # delete all known entries
  while ($lineno > 0)
  {
    if (&gst_boot_grub_entry_is_valid ($buff, $lineno))
    {
	 $buff = &gst_boot_grub_delete_entry ($buff, $lineno);
    }

    $lineno = &gst_boot_grub_find_entry ($buff, $lineno + 1);
  }

  # insert all entries just before all the unknown entries (if any)
  $lineno = &gst_boot_grub_find_entry ($buff, 0);

  if ($lineno < 0)
  {
    $lineno = @$buff;
  }

  foreach $entry (@$entries)
  {
    &gst_boot_grub_add_entry ($entry, $buff, $lineno, $device_map);
  }

  # save the buffer  
  &gst_file_buffer_clean ($buff);
  return &gst_file_buffer_save ($buff, $file);
}

# Grub device notation converters.
sub gst_boot_grub_system2grub
{
  my ($device_map, $dev) = @_;
  my ($fd, $line, $res, $path, $drive, $part);

  if (($$tool{"system"} eq "Linux") &&
      ($dev =~ /(.*\/)?(fd[0-9]|hd|sd|scd)([a-z]*)([0-9]*)/))
  {
    $path  = "/dev/";
    $path  = $1 if ($1 ne undef);
    $drive = $2 . $3;
    $part  = $4 - 1 if ($4 ne undef);
  }
  elsif (($$tool{"system"} eq "FreeBSD") &&
         ($dev =~ /(.*\/)?(fd|da|ad|ar|fla|afd|mlx|amrd|idad|twed|aacd|ipsd|wd)([0-9]+)s?([0-9]*)/))
  {
    $path  = "/dev/";
    $path  = $1 if ($1 ne undef);
    $drive = $2 . $3;
    $part  = $4 - 1 if ($4 ne undef);
  }
  else
  {
    &gst_report ("boot_grub_convert_failed", $dev);
    return undef;
  }

  $fd = &gst_file_open_read_from_names ($device_map);
  $res = undef;
  
  while ($line = <$fd>)
  {
    chomp $line;
    if ($line =~ /\(([^\)]+)\)[ \t]+$path$drive$/)
    {
      $res = $1;
      last;
    }
  }
  close $fd;

  if ($res eq undef)
  {
    # ok, the device doesn't exist in device.map, try to guess it
    &gst_report ("boot_grub_convert_failed", $dev);

    if ($drive =~ /(fd.|hd.)/)
    {
      if ($$tool{"system"} eq "Linux")
      {
        $res = "hd0"  if $drive eq "hda";
        $res = "hd1"  if $drive eq "hdb";
        $res = "hd2"  if $drive eq "hdc";
        $res = "hd3"  if $drive eq "hdd";
        $res = $drive if $drive =~ /fd[0-9]/;
      }
      elsif ($$tool{"system"} eq "FreeBSD")
      {
        $res = "hd0"  if $drive eq "ad0";
        $res = "hd1"  if $drive eq "ad1";
        $res = "hd2"  if $drive eq "ad2";
        $res = "hd3"  if $drive eq "ad3";
        $res = $drive if $drive =~ /fd[0-9]/;
      }
    }
    else
    {
      # no way, the entry will be broken
      return undef;
    }
  }

  $res .= ",$part" if $part ne undef;

  return $res;
}

sub gst_boot_grub_grub2system
{
  my ($device_map, $grubdev) = @_;
  ($drive, $part, @add) = split (',', $grubdev);

  $dev = &gst_parse_split_first_str ($device_map, "\\($drive\\)", "[ \t]+");

  if ($dev eq undef)
  {
    # ok, the device doesn't exist in devices.map, we'll have to guess it
    if ($drive =~ /fd[0-9]/)
    {
      $dev = "/dev/" . $drive;
    }
    else
    {
      if ($$tool{"system"} eq "Linux")
      {
        $dev = "/dev/hda" if $drive eq "hd0";
        $dev = "/dev/hdb" if $drive eq "hd1";
        $dev = "/dev/hdc" if $drive eq "hd2";
        $dev = "/dev/hdd" if $drive eq "hd3";
      }
      elsif ($$tool{"system"} eq "FreeBSD")
      {
        $dev = "/dev/ad0" if $drive eq "hd0";
        $dev = "/dev/ad1" if $drive eq "hd1";
        $dev = "/dev/ad2" if $drive eq "hd2";
        $dev = "/dev/ad3" if $drive eq "hd3";
      }
    }
  }

  # FreeBSD insists in putting an "s" between the device and the partition number
  $dev .= "s" if ($$tool{"system"} eq "FreeBSD");

  $dev .= $part + 1 if $part ne undef;

  return $dev;
}

sub gst_boot_grub_clean_line
{
  my ($line) = @_;

  chomp $line;
  $line =~ s/\#.*//;
    
  $line =~ s/^[ \t]+//;
  $line =~ s/[ \t]+$//;

  return $line;
}

sub gst_boot_grub_line_is_title
{
  my ($line) = @_;

  if ($line =~ /^title[= \t][ \t]*(.*)$/)
  {
    return $1;
  }

  return undef;
}

sub gst_boot_grub_get_next_entry
{
  my ($buff, $line_no) = @_;
  my ($line, $res);

  while ($$line_no < scalar (@$buff))
  {
    $line = $$buff[$$line_no];
    
    $line = &gst_boot_grub_clean_line ($line);
    $res = &gst_boot_grub_line_is_title ($line);
    last if $res ne undef;
    
    $$line_no ++;
  }
  
  return $res;
}

sub gst_boot_grub_search_entry
{
  my ($buff, $line_no, $entry) = @_;
  my ($key, $title);

  $key = 0;
  while ($title = &gst_boot_grub_get_next_entry ($buff, $line_no))
  {
    return $title if $key == $entry;
    $key ++;
    $$line_no ++;
  }

  return undef;
}

sub gst_boot_grub_get_entries
{
  my ($grub_conf) = @_;
  my ($fd, $line, @res, $title, $i);
  my ($buff, $line_no);
  
  $grub_conf = [$grub_conf] if (ref $grub_conf eq undef);

  foreach $i (@$grub_conf)
  {
    $fd = &gst_file_open_read_from_names ($i);
    last if $fd;
  }

  if (!$fd)
  {
    &gst_report ("boot_conf_read_failed", "/boot/grub/menu.lst");
    return undef;
  }
  
  $buff = &gst_file_buffer_load_fd ($fd);
  $line_no = 0;
  
  while ($title = &gst_boot_grub_get_next_entry ($buff, \$line_no))
  {
    push @res, $title;
    $line_no ++;
  }

  return @res;
}

sub gst_boot_grub_get_entries_fn
{
  my ($fn) = @_;

  return &gst_boot_grub_get_entries ($$fn{"GRUB_CONF"});
}

sub gst_boot_grub_insert_entry
{
  my ($buff, $line_no, $title) = @_;

  splice (@$buff, $line_no, 0, "title $title\n");
}

sub gst_boot_grub_remove_entry
{
  my ($buff, $line_no) = @_;

  $$buff[$$line_no] = "";
  $$line_no ++;

  while (&gst_boot_grub_line_is_title ($$buff[$$line_no]) eq undef)
  {
    $$buff[$$line_no] = "" if (! ($$buff[$$line_no] =~ /^\#/));
    $$line_no ++;
  }
}

# @$entries must be sorted.
sub gst_boot_grub_remove_entries
{
  my ($fn, $entries) = @_;
  my ($grub_conf, $buff, $line_no);
  my ($find, $curr);

  return 0 if scalar (@$entries) == 0;

  $grub_conf = $$fn{"GRUB_CONF"};
  $buff = &gst_file_buffer_load ($grub_conf);
  $buff = [] if $buff eq undef;
  $line_no = 0;

  $curr = 0;
  $find = shift (@$entries);

  while (&gst_boot_grub_get_next_entry ($buff, \$line_no))
  {
    if ($find == $curr)
    {
      &gst_boot_grub_remove_entry ($buff, \$line_no);
      $find = shift (@$entries);
      last if $find eq undef;
    }
    else
    {
      $line_no ++;
    }
    $curr ++;
  }
  
  return &gst_file_buffer_save ($buff, $grub_conf);
}

sub gst_boot_grub_search_cmd
{
  my ($buff, $line_no, $cmd) = @_;
  my ($line);

  while ($$line_no < scalar (@$buff))
  {
    $line = $$buff[$$line_no];
    
    $line = &gst_boot_grub_clean_line ($line);
    if ($line eq "")
    {
      $$line_no ++;
      next;
    }
    
    return 0 if &gst_boot_grub_line_is_title ($line) ne undef;
    return 1 if ($line =~ /^$cmd[= \t]/);

    $$line_no ++;
  }

  return 0;
}

sub gst_boot_grub_parse_cmd
{
  my ($grub_conf, $cmd, $entry) = @_;
  my ($line, $buff, $line_no);

  $buff = &gst_file_buffer_load ($grub_conf);
  return undef if $buff eq undef;
  $line_no = 0;
  
  if ($entry ne undef)
  {
    return undef if &gst_boot_grub_search_entry ($buff, \$line_no, $entry) eq undef;
    $line_no ++;
  }

  if (&gst_boot_grub_search_cmd ($buff, \$line_no, $cmd))
  {
    $line = &gst_boot_grub_clean_line ($$buff[$line_no]);
    if ($line =~ /^$cmd[= \t][ \t]*(.*)/)
    {
      return 1 if $1 eq "";
      return $1;
    }
  }
  
  return undef;
}

sub gst_boot_grub_insert_cmd
{
  my ($buff, $line_no, $cmd, $value) = @_;

  splice (@$buff, $line_no, 0, "$cmd $value\n") unless $value eq undef;
}

sub gst_boot_grub_parse_file_cmd
{
  my ($grub_conf, $cmd, $device_map, $mtab, $entry) = @_;
  my ($value, $mtopts, $grubdev, $file, $dev, $root);

  $value = &gst_boot_grub_parse_cmd ($grub_conf, $cmd, $entry);
  return undef if $value eq undef;

  $value =~ /^\(([^\)]+)\)(.*)/;
  $grubdev = $1;
  $file = $2;

  if ($grubdev eq undef)
  {
    $file = $value;
    $root = &gst_boot_grub_parse_cmd ($grub_conf, "root", $entry);
    $root =~ /^\(([^\)]+)\)/;
    $grubdev = $1;
  }

  $file =~ s/[ \t].*//;
  $dev = &gst_boot_grub_grub2system ($device_map, $grubdev);
  
  $mntopts = &gst_parse_split_first_array ($mtab, $dev, "[ \t]+", "[ \t]+");

  $res = $$mntopts[0] . "/$file";
  $res =~ s/\/+/\//g;

  return $res;
}

sub gst_boot_grub_parse_pixmap
{
  my ($grub_conf, $device_map, $mtab) = @_;
  
  return &gst_boot_grub_parse_file_cmd ($grub_conf, "splashimage", $device_map, $mtab);
}

sub gst_boot_grub_parse_image
{
  my ($grub_conf, $device_map, $mtab, $entry) = @_;
  my ($image);

  $image = &gst_boot_grub_parse_file_cmd ($grub_conf, "kernel", $device_map, $mtab, $entry);

  return $image;
}

sub gst_boot_grub_parse_other
{
  my ($grub_conf, $device_map, $mtab, $entry) = @_;
  my ($value, $grubdev, $dev, $root);

  $value = &gst_boot_grub_parse_cmd ($grub_conf, "rootnoverify", $entry);
  $value = &gst_boot_grub_parse_cmd ($grub_conf, "root", $entry) if ($value eq undef);
  return undef if $value eq undef;

  $value =~ /^\(([^\)]+)\)/;
  $grubdev = $1;

  if ($grubdev eq undef)
  {
    $root = &gst_boot_grub_parse_cmd ($grub_conf, "root", $entry);
    $root =~ /^\(([^\)]+)\)/;
    $grubdev = $1;
  }

  $dev = &gst_boot_grub_grub2system ($device_map, $grubdev);
  return $dev;
}

sub gst_boot_grub_parse_timeout
{
  my ($grub_conf) = @_;
  my ($timeout);

  $timeout = &gst_boot_grub_parse_cmd ($grub_conf, "timeout");
  return undef if $timeout eq undef;
  return $timeout * 10;
}

sub gst_boot_grub_parse_prompt
{
  my ($grub_conf) = @_;

  return (&gst_boot_grub_parse_cmd ($grub_conf, "hiddenmenu"))? 0: 1;
}

sub gst_boot_grub_parse_default
{
  my ($grub_conf) = @_;
  my ($default, @entries);

  @entries = &gst_boot_grub_get_entries ($grub_conf);
  return undef if (scalar @entries) == 0;

  $default = &gst_boot_grub_parse_cmd ($grub_conf, "default");
  $default = 0 if $default eq undef;

  return $entries[$default];
}

sub gst_boot_grub_parse_root
{
  my ($grub_conf, $device_map, $entry) = @_;
  my ($value, $dev);

  # we check if there is the 'root' label, then extract root device from it
  $value = &gst_boot_grub_parse_cmd ($grub_conf, "root", $entry);
  if ($value ne undef)
  {
    $value =~ s/[()]//g;
    return &gst_boot_grub_grub2system ($device_map, $value);
  }
  return undef;
}

sub gst_boot_grub_parse_append
{
  my ($grub_conf, $entry) = @_;
  my ($cmd);

  $cmd = &gst_boot_grub_parse_cmd ($grub_conf, "kernel", $entry);
  return undef if $cmd eq undef;

  $cmd =~ s/^[^ \t]+[ \t]?//;

  return $cmd;
}

sub gst_boot_grub_parse_type
{
  my ($grub_conf, $entry, $partition, $rootdev) = @_;
  my ($line, $res, $buff, $line_no);

  $buff = &gst_file_buffer_load ($grub_conf);
  return undef if $buff eq undef;
  $line_no = 0;
  return undef if &gst_boot_grub_search_entry ($buff, \$line_no, $entry) eq undef;

  $line_no ++;
  while ($line_no < scalar (@$buff))
  {
    $line = $$buff[$line_no];
    
    last if &gst_boot_grub_line_is_title ($line) ne undef;
    
    if ($line =~ /^\#[ \t]*GstEntryType[ \t]+(.*)$/)
    {
      $res = $1;
      last;
    }

    $line_no ++;
  }

  if ($res eq undef)
  {
    $rootdev =~ s/.*\///;
    $res = $ {$$partition{$rootdev}}{"typestr"} if exists $$partition{$rootdev};
  }
  return $res;
}

sub gst_boot_grub_parse_initrd
{
  my ($grub_conf, $device_map, $mtab, $entry) = @_;
  my ($initrd);

  $initrd = &gst_boot_grub_parse_file_cmd ($grub_conf, "initrd", $device_map, $mtab, $entry);

  return $initrd;
}

sub gst_boot_grub_parse_module
{
  my ($grub_conf, $device_map, $mtab, $entry) = @_;
  my ($module);
 
  $module = &gst_boot_grub_parse_file_cmd ($grub_conf, "module", $device_map, $mtab, $entry);
   
  return $module;
}

sub gst_boot_grub_parse_password
{
  my ($grub_conf, $entry) = @_;

  return &gst_boot_grub_parse_cmd ($grub_conf, "password", $entry);
}

# Watch it: this assumes that the entry exists.
sub gst_boot_grub_replace_cmd_in_buff
{
  my ($buff, $cmd, $value, $entry) = @_;
  my ($line, $line_no);
  my ($pre, $post);
  $line_no = 0;
  
  if ($entry ne undef)
  {
    # Watch it: this assumes that the entry exists.
    &gst_boot_grub_search_entry ($buff, \$line_no, $entry);
    $line_no ++;
  }

  if (&gst_boot_grub_search_cmd ($buff, \$line_no, $cmd))
  {
    if ($value eq undef)
    {
      $$buff[$line_no] = "";
    }
    else
    {
      $line = $$buff[$line_no];
      
      $line =~ s/^([ \t]+)//;
      $pre = $1;
      
      $line =~ s/([ \t]+)$//;
      $post = $1;
      
      $line =~ s/^$cmd([= \t][ \t]*).*/$pre$cmd\1$value$post/;
      $$buff[$line_no] = $line;
    }
  }
  else
  {
    $cmd = "\t$cmd" if $entry ne undef;
    &gst_boot_grub_insert_cmd ($buff, $line_no, $cmd, $value) if ($value ne undef);
  }
}

sub gst_boot_grub_replace_cmd
{
  my ($grub_conf, $cmd, $value, $entry) = @_;
  $buff = &gst_file_buffer_load ($grub_conf);
  $buff = [] if $buff eq undef;

  &gst_boot_grub_replace_cmd_in_buff ($buff, $cmd, $value, $entry);
  
  return &gst_file_buffer_save ($buff, $grub_conf);
}

sub gst_boot_grub_get_stat
{
  my ($file, $field) = @_;
  my (@tmp);

  @tmp = stat ($file);
  return $tmp[$field];
}

sub gst_boot_grub_find_device
{
  my ($file) = @_;
  my ($filedev, $dev, $res);
  local *DIR;

  $res = undef;
  $filedev = &gst_boot_grub_get_stat ($file, 0);

  opendir (DIR, "/dev");
  
  while ($dev = readdir (DIR))
  {
    $dev = "/dev/$dev";
    if ((-b $dev) && (&gst_boot_grub_get_stat ($dev, 6) eq $filedev))
    {
      $res = $dev;
      last;
    }
  }

  closedir (DIR);
  return $res;
}

sub gst_boot_grub_replace_file_cmd
{
  my ($grub_conf, $cmd, $device_map, $mtab, $value, $entry) = @_;
  my ($grubdev, $file, $dev, $root);
  my ($mntopts, $res, $dev, $dir, $oldval);

  $file = $value;

  $dev = &gst_boot_grub_find_device ($file);
  $mntopts = &gst_parse_split_first_array ($mtab, $dev, "[ \t]+", "[ \t]+");
  $dir = $$mntopts[0];

  $file =~ s/^$dir//;
  $file =~ s/\/+/\//g;

  $grubdev = &gst_boot_grub_system2grub ($device_map, $dev);

  $res = "($grubdev)$file";

  $oldval = &gst_boot_grub_parse_cmd ($grub_conf, $cmd, $entry);
  $oldval =~ s/^[^ \t]+//;
  $res .= $oldval;

  return &gst_boot_grub_replace_cmd ($grub_conf, $cmd, $res, $entry);
}

sub gst_boot_grub_replace_pixmap
{
  my ($grub_conf, $device_map, $mtab, $value) = @_;

  return &gst_boot_grub_replace_file_cmd ($grub_conf, "splashimage", $device_map, $mtab, $value);
}

sub gst_boot_grub_replace_image
{
  my ($grub_conf, $device_map, $mtab, $entry, $value) = @_;

  return &gst_boot_grub_replace_cmd ($grub_conf, "kernel", $value, $entry);
}

sub gst_boot_grub_replace_other
{
  my ($grub_conf, $device_map, $entry, $value) = @_;
  my ($grubdev);

  $grubdev = &gst_boot_grub_system2grub ($device_map, $value);
  &gst_boot_grub_replace_cmd ($grub_conf, "rootnoverify", "($grubdev)", $entry);
#  &gst_boot_grub_replace_cmd ($grub_conf, "makeactive", " ", $entry);
  return &gst_boot_grub_replace_cmd ($grub_conf, "chainloader", "+1", $entry);
}

sub gst_boot_grub_replace_timeout
{
  my ($grub_conf, $value) = @_;
  my ($timeout);

  if ($value == -1 || $value eq undef)
  {
    return &gst_boot_grub_replace_cmd ($grub_conf, "timeout", undef);
  }
  
  $timeout = int ($value / 10);

  return &gst_boot_grub_replace_cmd ($grub_conf, "timeout", $timeout);
}

sub gst_boot_grub_replace_prompt
{
  my ($grub_conf, $value) = @_;

  return &gst_boot_grub_replace_cmd ($grub_conf, "hiddenmenu", ($value)? undef: " ");
}

sub gst_boot_grub_replace_default
{
  my ($grub_conf, $entries, $value) = @_;
  my ($default);

  return -1 if (scalar @$entries) == 0;
  for ($default = 0; $default < scalar (@$entries); $default++)
  {
    last if $value eq $$entries[$default]{"label"};
  }
  return -1 if $default == scalar (@$entries); # didn't find it.
  return &gst_boot_grub_replace_cmd ($grub_conf, "default", $default);
}

sub gst_boot_grub_replace_root
{
  my ($grub_conf, $device_map, $entry, $type, $value) = @_;
  my ($root, $buff, $line_no, $entry_line, $currval, $root_dev);

  $root_dev = "(". &gst_boot_grub_system2grub ($device_map, $value) . ")";
  return &gst_boot_grub_replace_cmd ($grub_conf, "root", $root_dev, $entry);	  
}

sub gst_boot_grub_replace_append
{
  my ($grub_conf, $device_map, $entry, $value) = @_;
  my ($kernel, $append, $currval, $root);

  $kernel = &gst_boot_grub_parse_cmd ($grub_conf, "kernel", $entry);
  $currval = &gst_boot_grub_parse_cmd ($grub_conf, "root", $entry);
  $currval =~ s/[()]//g;
  $root = &gst_boot_grub_grub2system ($device_map, $currval);
	
  $kernel .= " root=$root $value";
  return &gst_boot_grub_replace_cmd ($grub_conf, "kernel", $kernel, $entry);
}

sub gst_boot_grub_replace_type
{
  my ($grub_conf, $entry, $value) = @_;
  my ($buff, $line_no, $line, $res);
	
  $buff = &gst_file_buffer_load ($grub_conf);
  $buff = [] if $buff eq undef;
  $line_no = 0;
  return -1 if &gst_boot_grub_search_entry ($buff, \$line_no, $entry) eq undef;

  $line_no ++;
  while ($line_no < scalar (@$buff))
  {
    $line = $$buff[$line_no];
    
    last if &gst_boot_grub_line_is_title ($line) ne undef;
    
    if ($line =~ /^\#[ \t]*GstEntryType[ \t]+(.*)$/)
    {
      $res = $1;
      last;
    }

    $line_no ++;
  }

  if ($res eq undef)
  {
    &gst_boot_grub_insert_cmd ($buff, $line_no, "#\tGstEntryType",  $value);
  }
  else
  {
    $$buff[$line_no] =~ s/^(\#[ \t]*GstEntryType[ \t]+).*$/\1$value/;
  }
  
  return &gst_file_buffer_save ($buff, $grub_conf);
}

sub gst_boot_grub_replace_label
{
  my ($grub_conf, $entry, $value) = @_;
  my ($buff, $line_no, $title);
	
  $buff = &gst_file_buffer_load ($grub_conf);
  $buff = [] if $buff eq undef;
  $line_no = 0;

  $title = &gst_boot_grub_search_entry ($buff, \$line_no, $entry);
  if ($title eq undef)
  {
    $line_no ++;
    &gst_boot_grub_insert_entry ($buff, \$line_no, $value);
  }
  else
  {
    $$buff[$line_no] =~ s/^title([= \t][ \t]*).*/title\1$value/;
  }
  
  return &gst_file_buffer_save ($buff, $grub_conf);
}

sub gst_boot_grub_check_device_map
{
  my ($file) = "$gst_prefix/boot/grub/device.map";
  my ($fd);

  if (! -f $file)
  {
    $fd = &gst_file_run_pipe_write ("grub --batch --device-map=$file");
    print $fd "quit\n";
    &gst_file_close ($fd);
  }
}

1;
