2 # Functions for parsing and editing a grub menu file
4 BEGIN { push(@INC, ".."); };
8 %title_order = ( 'lock', 10,
16 # Parses the config file into a list of title structures
21 open(CONF, $config{'menu_file'});
25 if (/^\s*(\S+)\s*=\s*(.*)/ || /^\s*(\S+)\s*(.*)/) {
26 if ($title && $1 ne 'title') {
27 # directive in an existing section
28 if (defined($title->{$1})) {
30 $title->{$1} .= "\0".$2;
35 $title->{'eline'} = $lnum;
38 # top-level title or option
39 local $d = { 'name' => $1,
43 'index' => scalar(@rv) };
45 $title = $d if ($1 eq 'title');
54 # save_directive(&config, &old|name, &new)
58 if (!$_[1] || ref($_[1])) {
62 $old = &find($_[1], $_[0]);
66 @lines = ( "$_[2]->{'name'} $_[2]->{'value'}" );
67 foreach $k (sort { $title_order{$b} <=> $title_order{$a} }
69 if ($k !~ /^(name|value|line|eline|index)$/) {
70 if ($_[2]->{$k} eq '') {
74 foreach my $v (split(/\0/, $_[2]->{$k})) {
75 push(@lines, $k." ".$v);
81 local $lref = &read_file_lines($config{'menu_file'});
83 # Replace one entry in the file
84 splice(@$lref, $old->{'line'}, $old->{'eline'} - $old->{'line'} + 1,
87 elsif ($_[2]->{'name'} eq 'title') {
89 push(@$lref, "", @lines);
92 # Insert before titles
93 local $t = &find("title", $_[0]);
95 splice(@$lref, $t->{'line'}, 0, @lines);
98 push(@$lref, "", @lines);
103 # swap_directives(&dir1, &dir2)
104 # Swaps two blocks in the config file
107 my ($dir1, $dir2) = @_;
108 local $lref = &read_file_lines($config{'menu_file'});
109 if ($dir1->{'line'} > $dir2->{'line'}) {
110 ($dir1, $dir2) = ($dir2, $dir1);
112 my @lines1 = @$lref[$dir1->{'line'} .. $dir1->{'eline'}];
113 my @lines2 = @$lref[$dir2->{'line'} .. $dir2->{'eline'}];
114 my $len1 = $dir1->{'eline'} - $dir1->{'line'} + 1;
115 my $len2 = $dir2->{'eline'} - $dir2->{'line'} + 1;
116 splice(@$lref, $dir2->{'line'}, $len2, @lines1);
117 splice(@$lref, $dir1->{'line'}, $len1, @lines2);
120 # find(name, &config)
124 foreach $c (@{$_[1]}) {
125 push(@rv, $c) if ($c->{'name'} eq $_[0]);
127 return wantarray ? @rv : $rv[0];
130 # find_value(name, &config)
133 local @rv = &find($_[0], $_[1]);
134 return !@rv ? undef : wantarray ? map { $_->{'value'} } @rv : $rv[0]->{'value'};
137 # linux_to_bios(device)
138 # Converts a Linux device file like /dev/hda into a GRUB bios disk like (hd0)
141 if ($_[0] =~ /^(\/dev\/[hs]d[a-z])(\d+)$/ ||
142 $_[0] =~ /^(\/dev\S+\/)part(\d+)$/ ||
143 $_[0] =~ /^(\/dev\S+c\d+d\d+)p(\d+)$/) {
144 # A partition on a disk .. get the disk's device, and then add the part
145 local ($dev, $part) = ($1, $2-1);
146 $dev .= "disc" if ($dev =~ /\/$/);
147 local $dsk = &linux_to_bios($dev);
148 $dsk =~ /^\(([a-z]+\d+)\)$/ || return undef;
151 local @map = &get_device_map();
152 local @st = stat($_[0]);
155 local @mst = stat($m->[1]);
156 if ($m->[1] eq $_[0] ||
157 @mst && @st && $mst[0] == $st[0] && $mst[1] == $st[1]) {
163 # Have to guess based on the device name :(
164 return $_[0] =~ /\/dev\/hd([a-d])$/ ? "(hd".(ord($1)-97).")" :
165 $_[0] =~ /\/dev\/fd([0-4])$/ ? "(fd$1)" : undef;
168 # bios_to_linux(device)
169 # Converts a GRUB bios disk like (hd0) to a Linux device file like /dev/hda
172 if ($_[0] =~ /^\(([a-z]+\d+),(\d+)\)$/) {
173 # A partition on a BIOS disk .. get the disk device, and add the part
174 local ($dev, $part) = ($1, $2+1);
175 local $dsk = &bios_to_linux("($dev)");
176 if ($dsk =~ /^(\/dev\/[hs]d[a-z])$/) {
179 elsif ($dsk =~ /^(\/dev\S+\/)disc$/) {
180 return $1."part".$part;
182 elsif ($dsk =~ /^(\/dev\S+c\d+d\d+)$/) {
189 local @map = &get_device_map();
192 if ($m->[0] eq $_[0]) {
198 # Have to guess from BIOS name :(
199 return $_[0] =~ /^\(hd(\d+)\)$/ ? "/dev/hd".chr($1+97) :
200 $_[0] =~ /^\(fd([0-4])\)$/ ? "/dev/fd$1" : undef;
204 # Returns the device.map file contents, or an empty list if there is none
207 local ($dm, $temp, @rv);
208 if (!$config{'device_map'} || !-r $config{'device_map'}) {
209 # Run GRUB to build the map now
210 $dm = $temp = &transname();
211 open(GRUB, "|$config{'grub_path'} --batch --device-map=$temp >/dev/null 2>&1");
216 # Just use the existing file
217 $dm = $config{'device_map'};
223 if (/^(\S+)\s+(\S+)/) {
224 push(@rv, [ $1, $2 ]);
228 unlink($temp) if ($temp);