summaryrefslogtreecommitdiffstats
path: root/perl-install/common.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl-install/common.pm')
-rw-r--r--perl-install/common.pm1180
1 files changed, 667 insertions, 513 deletions
diff --git a/perl-install/common.pm b/perl-install/common.pm
index 9dcb55300..508e6b31f 100644
--- a/perl-install/common.pm
+++ b/perl-install/common.pm
@@ -1,642 +1,796 @@
-package common; # $Id$
+package common;
+use MDK::Common;
use diagnostics;
use strict;
-use vars qw(@ISA %EXPORT_TAGS @EXPORT_OK $printable_chars $sizeof_int $bitof_int $cancel $SECTORSIZE %compat_arch);
+BEGIN { eval { require Locale::gettext } } #- allow common.pm to be used in drakxtools-backend without perl-Locale-gettext
-@ISA = qw(Exporter);
-%EXPORT_TAGS = (
- common => [ qw(__ even odd arch better_arch compat_arch min max sqr sum and_ or_ sign product bool invbool listlength bool2text bool2yesno text2bool to_int to_float ikeys member divide is_empty_array_ref is_empty_hash_ref add2hash add2hash_ set_new set_add round round_up round_down first second top uniq translate untranslate warp_text formatAlaTeX formatLines deref next_val_in_array) ],
- functional => [ qw(fold_left compose mapgrep map_index grep_index find_index map_each grep_each list2kv map_tab_hash mapn mapn_ difference2 before_leaving catch_cdie cdie combine) ],
- file => [ qw(dirname basename touch all glob_ cat_ cat__ output symlinkf chop_ mode typeFromMagic expand_symlinks) ],
- system => [ qw(sync makedev unmakedev psizeof strcpy gettimeofday syscall_ salt getVarsFromSh setVarsInSh setVarsInShMode setVarsInCsh substInFile availableMemory availableRamMB removeXiBSuffix template2file template2userfile update_userkderc list_skels formatTime formatTimeRaw unix2dos setVirtual) ],
- constant => [ qw($printable_chars $sizeof_int $bitof_int $SECTORSIZE %compat_arch) ],
-);
-@EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
+use log;
+use run_program;
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw($SECTORSIZE N P N_ check_for_xserver files_exist formatTime MB formatXiB get_libdir get_parent_uid is_running is_uefi kernel_uefi_type makedev mageia_release mageia_release_info mount_efivars removeXiBSuffix require_root_capability setVirtual set_alternative set_l10n_sort set_permissions to_utf8 translate uefi_type unmakedev);
+
+# perl_checker: RE-EXPORT-ALL
+push @EXPORT, @MDK::Common::EXPORT;
+
+=head1 SYNOPSYS
+
+B<common> re-export L<MDK::Common> and offers a couple widely used functions.
+
+=cut
+
+$::prefix ||= ""; # no warning
#-#####################################################################################
#- Globals
#-#####################################################################################
-$printable_chars = "\x20-\x7E";
-$sizeof_int = psizeof("i");
-$bitof_int = $sizeof_int * 8;
-$SECTORSIZE = 512;
-%compat_arch = ( #- compatibilty arch mapping.
- 'noarch' => undef,
- 'i386' => 'noarch',
- 'i486' => 'i386',
- 'i586' => 'i486',
- 'i686' => 'i586',
- 'i786' => 'i686',
- 'k6' => 'i586',
- 'k7' => 'k6',
- 'k8' => 'k7',
- 'ppc' => 'noarch',
- 'alpha' => 'noarch',
- 'sparc' => 'noarch',
- 'sparc32' => 'sparc',
- 'sparc64' => 'sparc32',
- );
+our $SECTORSIZE = 512;
-#-#####################################################################################
-#- Functions
-#-#####################################################################################
+=head1 Functions
-sub fold_left(&@) {
- my $f = shift;
- local $a = shift;
- foreach $b (@_) { $a = &$f() }
- $a
-}
-
-sub _ {
- my $s = shift @_; my $t = translate($s);
- $t && ref $t or return sprintf $t, @_;
- my ($T, @p) = @$t;
- sprintf $T, @_[@p];
-}
-#-delete $main::{'_'};
-sub __ { $_[0] }
-sub even($) { $_[0] % 2 == 0 }
-sub odd($) { $_[0] % 2 == 1 }
-sub min { fold_left { $a < $b ? $a : $b } @_ }
-sub max { fold_left { $a > $b ? $a : $b } @_ }
-sub sum { fold_left { $a + $b } @_ }
-sub and_{ fold_left { $a && $b } @_ }
-sub or_ { fold_left { $a || $b } @_ }
-sub sqr { $_[0] * $_[0] }
-sub sign { $_[0] <=> 0 }
-sub product { fold_left { $a * $b } @_ }
-sub first { $_[0] }
-sub second { $_[1] }
-sub top { $_[-1] }
-sub uniq { my %l; @l{@_} = (); keys %l }
-sub to_int { $_[0] =~ /(\d*)/; $1 }
-sub to_float { $_[0] =~ /(\d*(\.\d*)?)/; $1 }
-sub ikeys { my %l = @_; sort { $a <=> $b } keys %l }
-sub add2hash($$) { my ($a, $b) = @_; while (my ($k, $v) = each %{$b || {}}) { $a->{$k} ||= $v } $a }
-sub add2hash_($$) { my ($a, $b) = @_; while (my ($k, $v) = each %{$b || {}}) { exists $a->{$k} or $a->{$k} = $v } $a }
-sub member { my $e = shift; foreach (@_) { $e eq $_ and return 1 } 0 }
-sub dirname { @_ == 1 or die "usage: dirname <name>\n"; local $_ = shift; s|[^/]*/*\s*$||; s|(.)/*$|$1|; $_ || '.' }
-sub basename { @_ == 1 or die "usage: basename <name>\n"; local $_ = shift; s|/*\s*$||; s|.*/||; $_ }
-sub bool($) { $_[0] ? 1 : 0 }
-sub invbool { my $a = shift; $$a = !$$a; $$a }
-sub listlength { scalar @_ }
-sub bool2text { $_[0] ? "true" : "false" }
-sub bool2yesno { $_[0] ? "yes" : "no" }
-sub text2bool { my $t = lc($_[0]); $t eq "true" || $t eq "yes" ? 1 : 0 }
-sub strcpy { substr($_[0], $_[2] || 0, length $_[1]) = $_[1] }
-sub cat_ { local *F; open F, $_[0] or $_[1] ? die "cat of file $_[0] failed: $!\n" : return; my @l = <F>; wantarray ? @l : join '', @l }
-sub cat__ { my ($f) = @_; my @l = <$f>; wantarray ? @l : join '', @l }
-sub output { my $f = shift; local *F; open F, ">$f" or die "output in file $f failed: $!\n"; print F foreach @_; }
-sub deref { ref $_[0] eq "ARRAY" ? @{$_[0]} : ref $_[0] eq "HASH" ? %{$_[0]} : $_[0] }
-sub linkf { unlink $_[1]; link $_[0], $_[1] }
-sub symlinkf { unlink $_[1]; symlink $_[0], $_[1] }
-sub chop_ { map { my $l = $_; chomp $l; $l } @_ }
-sub divide { my $d = int $_[0] / $_[1]; wantarray ? ($d, $_[0] % $_[1]) : $d }
-sub round { int ($_[0] + 0.5) }
-sub round_up { my ($i, $r) = @_; $i = int $i; $i += $r - ($i + $r - 1) % $r - 1; }
-sub round_down { my ($i, $r) = @_; $i = int $i; $i -= $i % $r; }
-sub is_empty_array_ref { my $a = shift; !defined $a || @$a == 0 }
-sub is_empty_hash_ref { my $a = shift; !defined $a || keys(%$a) == 0 }
-sub difference2 { my %l; @l{@{$_[1]}} = (); grep { !exists $l{$_} } @{$_[0]} }
-sub intersection { my (%l, @m); @l{@{shift @_}} = (); foreach (@_) { @m = grep { exists $l{$_} } @$_; %l = (); @l{@m} = (); } keys %l }
-
-sub set_new(@) { my %l; @l{@_} = undef; { list => [ @_ ], hash => \%l } }
-sub set_add($@) { my $o = shift; foreach (@_) { exists $o->{hash}{$_} and next; push @{$o->{list}}, $_; $o->{hash}{$_} = undef } }
-
-sub sync { syscall_('sync') }
-sub gettimeofday { my $t = pack "LL"; syscall_('gettimeofday', $t, 0) or die "gettimeofday failed: $!\n"; unpack("LL", $t) }
-sub unix2dos { local $_ = $_[0]; s/\015$//mg; s/$/\015/mg; $_ }
-
-sub remove_spaces { local $_ = shift; s/^ +//; s/ +$//; $_ }
-sub mode { my @l = stat $_[0] or die "unable to get mode of file $_[0]: $!\n"; $l[2] }
-sub psizeof { length pack $_[0] }
-
-sub concat_symlink {
- my ($f, $l) = @_;
- $l =~ m|^\.\./(/.*)| and return $1;
-
- $f =~ s|/$||;
- while ($l =~ s|^\.\./||) {
- $f =~ s|/[^/]+$|| or die "concat_symlink: $f $l\n";
- }
- "$f/$l";
-}
+=head2 Translating
-sub expand_symlinks {
- my ($first, @l) = split '/', $_[0];
- $first eq '' or die "expand_symlinks: $_[0] is relative\n";
- my ($f, $l);
- foreach (@l) {
- $f .= "/$_";
- $f = concat_symlink($f, "../$l") while $l = readlink $f;
- }
- $f;
-}
+=over
-sub arch() {
- require c;
- c::kernel_arch();
-}
-sub better_arch {
- my ($new, $old) = @_;
- while ($new && $new ne $old) { $new = $compat_arch{$new} }
- $new;
+=item N($format, ...)
+
+translate a message by calling gettext(). eg:
+
+ N("Summary")
+ N("File %s is on %s", "/etc/passwd", "/dev/sda2")
+
+It must B<not> be used called before L<ugtk3> is initialized, otherwise the gettext is not forced to UTF-8.
+
+=cut
+
+sub N {
+ my ($s, @para) = @_;
+ sprintf(translate($s), @para);
}
-sub compat_arch { better_arch(arch(), $_[0]) }
-sub touch {
- my ($f) = @_;
- unless (-e $f) {
- local *F;
- open F, ">$f";
- }
- my $now = time;
- utime $now, $now, $f;
+=item N_($format, @param)
+
+Similar to C<N()>, but message is not actually translated.
+It tags the message as translatable so that it will end in the
+translation catalogs.
+
+In order for the message to be translated, C<translate()> must be called
+before using the message.
+
+=cut
+
+sub N_ { $_[0] }
+
+=item P($s_singular, $s_plural, $nb, @extra_para)
+
+Similar to C<N()>, but two messages are provided: one for the singular case, one for the
+plural case. At runtime, a number is provided in order to choose the message to use.
+eg:
+
+ P("%d package", "%d packages", $nb, $nb)
+
+=cut
+
+sub P {
+ my ($s_singular, $s_plural, $nb, @para) = @_;
+ sprintf(translate($s_singular, $s_plural, $nb), @para);
}
-sub mapgrep(&@) {
- my $f = shift;
- my @l;
- foreach (@_) {
- my ($b, $v) = $f->($_);
- push @l, $v if $b;
+sub translate_real {
+ my ($s, $o_plural, $o_nb) = @_;
+ $s or return '';
+ my $s2;
+ foreach (@::textdomains, 'libDrakX') {
+ if ($o_plural) {
+ $s2 = Locale::gettext::dngettext($_, $s, $o_plural, $o_nb);
+ } else {
+ $s2 = Locale::gettext::dgettext($_, $s);
+ }
+ # when utf8 pragma is in use, Locale::gettext() returns an utf8 string not tagged as such:
+ c::set_tagged_utf8($s2) if !utf8::is_utf8($s2) && utf8::is_utf8($s);
+ return $s2 if !member($s2, $s, $o_plural);
}
- @l;
+ # didn't lookup anything or locale is "C":
+ $s2;
}
-sub map_index(&@) {
- my $f = shift;
- my @v; local $::i = 0;
- map { @v = &$f($::i); $::i++; @v } @_;
-}
-sub grep_index(&@) {
- my $f = shift;
- my $v; local $::i = 0;
- grep { $v = &$f($::i); $::i++; $v } @_;
+=item remove_translate_context($string)
+
+Remove an ala-KDE context message. eg:
+
+ "_: this is verb\nOpen"
+ "_: Keep these entry short\nNetworking"
+
+=cut
+
+sub remove_translate_context {
+ my ($s) = @_;
+ #- translation with context, kde-like
+ $s =~ s/^_:.*(?:\n)?//g;
+ $s;
}
-sub find_index(&@) {
- my $f = shift;
- local $_;
- for (my $i = 0; $i < @_; $i++) {
- $_ = $_[$i];
- &$f and return $i;
- }
- die "find_index failed in @_";
+
+=item translate($string)
+
+Translate the message according to the current locale.
+The string is B<not> tagged as translatable and will B<not> end in
+translations catalogs.
+
+perl_checker only extracts strings tagged by C<N()>, C<N_()> or C<P()>.
+
+=cut
+
+sub translate {
+ my $s = translate_real(@_);
+ $::one_message_has_been_translated ||= join(':', (caller(1))[1,2]); #- see mygtk3.pm
+ remove_translate_context($s);
}
-sub map_each(&%) {
- my ($f, %h) = @_;
- my @l;
- local ($::a, $::b);
- while (($::a, $::b) = each %h) { push @l, &$f($::a, $::b) }
- @l;
+=back
+
+=head2 Conversion
+
+=over
+
+=item from_utf8($s)
+
+Convert an UTF-8 string into current locale's encoding.
+
+=cut
+
+sub from_utf8 {
+ my ($s) = @_;
+ Locale::gettext::iconv($s, "utf-8", undef); #- undef = locale charmap = nl_langinfo(CODESET)
}
-sub grep_each(&%) {
- my ($f, %h) = @_;
- my %l;
- local ($::a, $::b);
- while (($::a, $::b) = each %h) { $l{$::a} = $::b if &$f($::a, $::b) }
- %l;
+
+=item to_utf8($s)
+
+The reverse of C<from_utf8()>: convert a string from current locale's encoding to UTF-8.
+Also make sure the string is properly tagged as UTF-8 as concerning Perl
+
+=cut
+
+sub to_utf8 {
+ my ($s) = @_;
+ my $str = Locale::gettext::iconv($s, undef, "utf-8"); #- undef = locale charmap = nl_langinfo(CODESET)
+ c::set_tagged_utf8($str);
+ $str;
}
-sub list2kv(@) { [ grep_index { even($::i) } @_ ], [ grep_index { odd($::i) } @_ ] }
-sub combine {
- my $nb = shift;
- my @l; while (my @m = splice(@_, 0, $nb)) { push @l, \@m }
- @l;
+=item set_l10n_sort()
+
+This is needed because text printed by L<Gtk3> will always be encoded
+in UTF-8;
+we first check if LC_ALL is defined, because if it is, changing
+only LC_COLLATE will have no effect.
+
+=cut
+
+sub set_l10n_sort() {
+ my $collation_locale = $ENV{LC_ALL};
+ if (!$collation_locale) {
+ $collation_locale = c::setlocale(c::LC_COLLATE());
+ $collation_locale =~ /UTF-8/ or c::setlocale(c::LC_COLLATE(), "$collation_locale.UTF-8");
+ }
}
-#- pseudo-array-hash :)
-sub map_tab_hash(&$@) {
- my ($f, $fields, @tab_hash) = @_;
- my %hash;
- my $key = { map_index {($_, $::i + 1)} @{$fields} };
- for (my $i = 0; $i < @tab_hash; $i += 2) {
- my $h = [$key, @{$tab_hash[$i + 1]}];
- &$f($i, $h) if $f;
- $hash{ $tab_hash[$i] } = $h;
- }
- %hash;
+sub setVirtual {
+ my ($vt_number) = @_;
+ my $vt = '';
+ sysopen(my $C, "/dev/console", 2) or die "failed to open /dev/console: $!";
+ ioctl($C, c::VT_GETSTATE(), $vt) &&
+ ioctl($C, c::VT_ACTIVATE(), $vt_number) &&
+ ioctl($C, c::VT_WAITACTIVE(), $vt_number) or die "setVirtual failed";
+ unpack "S", $vt;
}
-sub smapn {
- my $f = shift;
- my $n = shift;
- my @r = ();
- for (my $i = 0; $i < $n; $i++) { push @r, &$f(map { $_->[$i] } @_); }
- @r
+sub nonblock {
+ my ($F) = @_;
+ fcntl($F, c::F_SETFL(), fcntl($F, c::F_GETFL(), 0) | c::O_NONBLOCK()) or die "cannot fcntl F_SETFL: $!";
}
-sub mapn(&@) {
- my $f = shift;
- smapn($f, min(map { scalar @$_ } @_), @_);
+
+=item makedev($major, $minor)
+
+Given major and minor device IDs, makedev() combines these to return a device ID
+(eg for mknod())
+
+=cut
+
+sub makedev { ($_[0] << 8) | $_[1] }
+
+=item unmakedev($device_id)
+
+The reverse of C<makedev()>: given a device ID, it will return both major and minor device IDs.
+
+=cut
+
+sub unmakedev { $_[0] >> 8, $_[0] & 0xff }
+
+=item MB($suffixed_number)
+
+return a size in sector
+ie C<MB(1)> is 2048 sectors, which is 1MB
+
+eg:
+
+ MB("10") => 20480
+
+=cut
+
+sub MB {
+ my ($nb_MB) = @_;
+ $nb_MB * 2048;
}
-sub mapn_(&@) {
- my $f = shift;
- smapn($f, max(map { scalar @$_ } @_), @_);
+
+=item removeXiBSuffix($string)
+
+Remove the XiB suffix and return the number. eg:
+
+ "10k" => 10240
+ "2M" => 2097152
+
+=cut
+
+sub removeXiBSuffix {
+ local $_ = shift;
+
+ /(\d+)\s*kB?$/i and return $1 * 1024;
+ /(\d+)\s*MB?$/i and return $1 * 1024 * 1024;
+ /(\d+)\s*GB?$/i and return $1 * 1024 * 1024 * 1024;
+ /(\d+)\s*TB?$/i and return $1 * 1024 * 1024 * 1024 * 1024;
+ $_;
}
+=item formatXiB($number [, $base])
-sub add_f4before_leaving {
- my ($f, $b, $name) = @_;
+The reverse of C<removeXiBSuffix()>, returns a nicely human size. eg:
- unless ($common::before_leaving::{$name}) {
- no strict 'refs';
- ${"common::before_leaving::$name"} = 1;
- ${"common::before_leaving::list"} = 1;
- }
- local *N = *{$common::before_leaving::{$name}};
- my $list = *common::before_leaving::list;
- $list->{$b}{$name} = $f;
- *N = sub {
- my $f = $list->{$_[0]}{$name} or die '';
- $name eq 'DESTROY' and delete $list->{$_[0]};
- goto $f;
- } unless defined &{*N};
+ 2097152 => "2MB"
+The optional parameter enables to provide the unit size (default is one).
+eg for a 2000 512 sized sectors:
+
+ formatXiB(2000, 512)
+
+=cut
+
+sub formatXiB {
+ my ($newnb, $o_newbase) = @_;
+ my $newbase = $o_newbase || 1;
+ my $sign = $newnb < 0 ? -1 : 1;
+ $newnb = abs(int($newnb));
+ my ($nb, $base);
+ my $decr = sub {
+ ($nb, $base) = ($newnb, $newbase);
+ $base >= 1024 ? ($newbase = $base / 1024) : ($newnb = $nb / 1024);
+ };
+ my $suffix;
+ foreach (N("B"), N("KB"), N("MB"), N("GB"), N("TB")) {
+ $decr->();
+ if ($newnb < 1 && $newnb * $newbase < 1) {
+ $suffix = $_;
+ last;
+ }
+ }
+ my $v = $nb * $base;
+ my $s = $v < 10 && int(10 * $v - 10 * int($v));
+ int($v * $sign) . ($s ? "." . abs($s) : '') . ($suffix || N("TB"));
}
-#- ! the functions are not called in the order wanted, in case of multiple before_leaving :(
-sub before_leaving(&) {
- my ($f) = @_;
- my $b = bless {}, 'common::before_leaving';
- add_f4before_leaving($f, $b, 'DESTROY');
- $b;
+sub formatTime {
+ my ($s, $m, $h) = gmtime($_[0]);
+ if ($h) {
+ sprintf
+ #-PO: here, "2:30" is remaining installation time (eg: "2:30" == 2 hour & 30 minutes)
+ N("%02d:%02d", $h, $m);
+ } elsif ($m > 1) {
+ N("%d minutes", $m);
+ } elsif ($m == 1) {
+ N("1 minute");
+ } else {
+ N("%d seconds", $s);
+ }
}
-sub catch_cdie(&&) {
- my ($f, $catch) = @_;
+sub expand_symlinks_with_absolute_symlinks_in_prefix {
+ my ($prefix, $link) = @_;
- local @common::cdie_catches;
- unshift @common::cdie_catches, $catch;
- &$f();
+ my ($first, @l) = split '/', $link;
+ $first eq '' or die "expand_symlinks: $link is relative\n";
+ my ($f, $l);
+ foreach (@l) {
+ $f .= "/$_";
+ while ($l = readlink "$prefix$f") {
+ $f = $l =~ m!^/! ? $l : MDK::Common::File::concat_symlink($f, "../$l");
+ }
+ }
+ "$prefix$f";
}
-sub cdie($;&) {
- my ($err, $f) = @_;
- foreach (@common::cdie_catches) {
- $@ = $err;
- &{$_}(\$err) and return;
+sub expand_symlinks_but_simple {
+ my ($f) = @_;
+ my $link = readlink($f);
+ my $f2 = expand_symlinks($f);
+ if ($link && $link !~ m|/|) {
+ # put back the last simple symlink
+ $f2 =~ s|\Q$link\E$|basename($f)|e;
}
- die $err;
+ $f2;
+}
+
+sub sync { &MDK::Common::System::sync }
+
+BEGIN { undef *formatError }
+sub formatError {
+ my ($err) = @_;
+ ref($err) eq 'SCALAR' and $err = $$err;
+ log::l("error: $err");
+ &MDK::Common::String::formatError($err);
}
-sub all {
- my $d = shift;
+=item group_by($f, @list)
- local *F;
- opendir F, $d or return;
- my @l = grep { $_ ne '.' && $_ ne '..' } readdir F;
- closedir F;
+Group the elements of @list in array references according to the $f comparison function.
+=cut
+
+sub group_by(&@) {
+ my $f = shift;
+ @_ or return;
+ my $e = shift;
+ my @l = my $last_l = [$e];
+ foreach (@_) {
+ if ($f->($e, $_)) {
+ push @$last_l, $_;
+ } else {
+ push @l, $last_l = [$_];
+ $e = $_;
+ }
+ }
@l;
}
-sub glob_ {
- my ($d, $f) = ($_[0] =~ /\*/) ? (dirname($_[0]), basename($_[0])) : ($_[0], '*');
+=item group_n_lm($n, @list)
- $d =~ /\*/ and die "glob_: wildcard in directory not handled ($_[0])\n";
- ($f = quotemeta $f) =~ s/\\\*/.*/g;
+Group the list by n. Returns a reference of lists of length n
- $d =~ m|/$| or $d .= '/';
- map { $d eq './' ? $_ : "$d$_" } grep { /^$f$/ } all($d);
+See also C<group_by2(LIST)> from L<MDK::Common::DataStructure>
+
+=cut
+
+sub group_n_lm {
+ my $n = shift;
+ my @l;
+ push @l, [ splice(@_, 0, $n) ] while @_;
+ @l;
}
+=item join_lines(@strings)
-sub syscall_ {
- my $f = shift;
+Concatenate adjacent strings if laters begin with spaces.
- require 'syscall.ph';
- syscall(&{$common::{"SYS_$f"}}, @_) == 0;
+=cut
+
+sub join_lines {
+ my @l;
+ my $s;
+ foreach (@_) {
+ if (/^\s/) {
+ $s .= $_;
+ } else {
+ push @l, $s if $s;
+ $s = $_;
+ }
+ }
+ @l, if_($s, $s);
}
-sub salt($) {
- my ($nb) = @_;
- require 'devices.pm';
- open F, devices::make("random") or die "missing random";
- my $s; read F, $s, $nb;
- local $_ = pack "b8" x $nb, unpack "b6" x $nb, $s;
- tr [\0-\x3f] [0-9a-zA-Z./];
- $_;
+
+sub read_alternative {
+ my ($name) = @_;
+ my $alt = readlink("$::prefix/etc/alternatives/$name");
+ $alt && $::prefix . $alt;
}
-sub makedev { ($_[0] << 8) | $_[1] }
-sub unmakedev { $_[0] >> 8, $_[0] & 0xff }
+sub set_alternative {
+ my ($command, $executable) = @_;
-sub list_passwd() {
- my (@l, @e);
- setpwent();
- while (@e = getpwent()) { push @l, [ @e ] }
- endpwent();
- @l;
+ #- check the existence of $executable as an alternative for $command
+ #- (is this needed???)
+ run_program::rooted_get_stdout($::prefix, 'update-alternatives', '--display', $command) =~ /^\Q$executable /m or return;
+
+ #- this does not handle relative symlink, but neither does update-alternatives ;p
+ symlinkf $executable, "$::prefix/etc/alternatives/$command";
}
-sub list_home() {
- map { $_->[7] } grep { $_->[2] >= 500 } list_passwd();
+
+sub files_exist { and_(map { -f "$::prefix$_" } @_) }
+
+sub open_file {
+ my ($file) = @_;
+ my $F;
+ open($F, $file) ? $F : do { log::l("Cannot open $file: $!"); undef };
}
-sub list_skels {
- my ($prefix, $suffix) = @_;
- grep { -d $_ && -w $_ } map { "$prefix$_/$suffix" } '/etc/skel', '/root', list_home() }
-sub translate {
- my ($s) = @_;
- my ($lang) = $ENV{LANGUAGE} || $ENV{LC_MESSAGES} || $ENV{LC_ALL} || $ENV{LANG} || 'en';
-
- require lang;
- foreach (split ':', $lang) {
- lang::load_po($_) unless defined $po::I18N::{$_};
- if (%{$po::I18N::{$_}}) {
- return if $s eq '_I18N_';
- return ${$po::I18N::{$_}}{$s} || $s
+=item secured_file($f)
+
+A wrapper around c::is_secure_file().
+
+Callers should probably just use C<mkstemp()> in /tmp.
+
+=cut
+
+# FIXME: callers should just use mkstemp in /tmp instead of relying on $TMPDIR || $ENV{HOME}/tmp
+# or we should just move the choice of directory from callers to here:
+# my $tmpdir = find { -d $_ } $ENV{TMPDIR}, "$ENV{HOME}/tmp", "$::prefix/tmp";
+sub secured_file {
+ my ($f) = @_;
+ my $d = dirname($f);
+ if (! -d $d) {
+ mkdir_p($d);
+ if ($d =~ /^$ENV{HOME}/) {
+ my ($user) = grep { $_->[7] eq $ENV{HOME} } list_passwd();
+ chown($user->[2], $user->[3], $d);
}
}
- $s;
+ c::is_secure_file($f) or die "cannot ensure a safe $f";
+ $f;
}
-sub untranslate($@) {
- my $s = shift || return;
- foreach (@_) { translate($_) eq $s and return $_ }
- die "untranslate failed";
+sub unwind_protect {
+ my ($to_do, $cleanup) = @_;
+ my @l = eval { $to_do->() };
+ my $err = $@;
+ $cleanup->();
+ $err and die $err;
+ wantarray() ? @l : $l[0];
}
-sub warp_text($;$) {
- my ($text, $width) = @_;
- $width ||= 80;
+sub with_private_tmp_file {
+ my ($file, $content, $f) = @_;
- my @l;
- foreach (split "\n", $text) {
- my $t = '';
- foreach (split /\s+/, $_) {
- if (length "$t $_" > $width) {
- push @l, $t;
- $t = $_;
- } else {
- $t = "$t $_";
- }
- }
- push @l, $t;
- }
- @l;
+ my $prev_umask = umask 077;
+
+ unwind_protect(sub {
+ MDK::Common::File::secured_output($file, $content);
+ $f->($file);
+ }, sub {
+ umask $prev_umask;
+ unlink $file;
+ });
}
-sub formatAlaTeX($) {
- my ($t, $tmp);
- foreach (split "\n", $_[0]) {
- if (/^$/) {
- $t .= ($t && "\n") . $tmp;
- $tmp = '';
- } else {
- $tmp = ($tmp && "$tmp ") . first(/^\s*(.*?)\s*$/);
+sub chown_ {
+ my ($b_recursive, $name, $group, @files) = @_;
+
+ my ($uid, $gid) = (getpwnam($name) || $name, getgrnam($group) || $group);
+
+ require POSIX;
+ my $chown; $chown = sub {
+ foreach (@_) {
+ POSIX::lchown($uid, $gid, $_) or die "chown of file $_ failed: $!\n";
+ ! -l $_ && -d $_ && $b_recursive and &$chown(glob_($_));
}
- }
- $t . ($t && $tmp && "\n") . $tmp;
+ };
+ $chown->(@files);
}
-sub formatLines($) {
- my ($t, $tmp);
- foreach (split "\n", $_[0]) {
- if (/^\s/) {
- $t .= "$tmp\n";
- $tmp = $_;
- } else {
- $tmp = ($tmp ? "$tmp " : ($t && "\n") . $tmp) . $_;
- }
+
+sub set_permissions {
+ my ($file, $perms, $o_owner, $o_group) = @_;
+ # We only need to set the permissions during installation to be able to
+ # print test pages. After installation udev does the business automatically.
+ return 1 unless $::isInstall;
+ if ($o_owner || $o_group) {
+ $o_owner ||= (lstat($file))[4];
+ $o_group ||= (lstat($file))[5];
+ chown_(0, $o_owner, $o_group, $file);
}
- "$t$tmp\n";
-}
-
-sub getVarsFromSh($) {
- my %l;
- local *F;
- open F, $_[0] or return;
- foreach (<F>) {
- my ($v, $val, $val2) =
- /^\s* # leading space
- (\w+) = # variable
- (
- "([^"]*)" # double-quoted text
- | '([^']*)' # single-quoted text
- | [^'"\s]+ # normal text
- )
- \s*$ # end of line
- /x or next;
- $l{$v} = $val2 || $val;
+ chmod(oct($perms), $file) or die "chmod of file $file failed: $!\n";
+}
+
+sub is_running {
+ my ($name, $o_user) = @_;
+ my $user = $o_user || $ENV{USER};
+ foreach (`ps -o '%P %p %c' -u $user`) {
+ my ($ppid, $pid, $n) = /^\s*(\d+)\s+(\d+)\s+(.*)/;
+ return $pid if $ppid != 1 && $pid != $$ && $n eq $name;
}
- %l;
}
-sub setVarsInSh {
- my ($file, $l, @fields) = @_;
- setVarsInShMode($file, 0777 ^ umask(), $l, @fields);
+=back
+
+=head2 Release files
+
+=over
+
+=item parse_release_file($prefix, $f, $part)
+
+Parses the $f release file in $prefix from the $part device.
+
+Returns a hash containing C<release>, C<version>, C<release_file>, C<part> & C<arch> fields.
+
+=cut
+
+sub parse_release_file {
+ my ($prefix, $f, $part) = @_;
+ chomp(my $s = cat_("$prefix$f"));
+ my $version = $s =~ s/\s+release\s+(\S+)// && $1;
+ my $arch = $s =~ s/\s+for\s+(\S+)// && $1;
+ log::l("find_root_parts found $part->{device}: $s for $arch" . ($f !~ m!/etc/! ? " in special release file $f" : ''));
+ { release => $s, version => $version,
+ release_file => $f, part => $part, 'arch' => $arch };
}
-sub setVarsInShMode {
- my ($file, $mod, $l, @fields) = @_;
- @fields = keys %$l unless @fields;
+=item release_file($o_dir)
+
+Returns the release file name.
- local *F;
- open F, "> $file" or die "cannot create config file $file";
- chmod $mod, $file;
- $l->{$_} and print F "$_=$l->{$_}\n" foreach @fields;
+=cut
+
+sub release_file {
+ my ($o_dir) = @_;
+ my @names = ('mageia-release', 'mandriva-release', 'mandrakelinux-release', 'mandrake-release', 'conectiva-release', 'release', 'redhat-release', 'fedora-release', 'SuSE-release');
+ find { -r "$o_dir$_" } (
+ (map { "/root/drakx/$_.upgrading" } @names),
+ (map { "/etc/$_" } @names),
+ );
}
-sub setVarsInCsh {
- my ($file, $l, @fields) = @_;
- @fields = keys %$l unless @fields;
+=item mageia_release_info()
+
+Parses C</etc/product.id> and returns a hash.
- local *F;
- open F, "> $_[0]" or die "cannot create config file $file";
- $l->{$_} and print F "setenv $_ $l->{$_}\n" foreach @fields;
+=cut
+
+sub mageia_release_info() {
+ parse_LDAP_namespace_structure(cat_('/etc/product.id'));
}
-sub template2file {
- my ($in, $out, %toreplace) = @_;
- output $out, map { s/@@@(.*?)@@@/$toreplace{$1}/g; $_ } cat_($in);
+sub parse_LDAP_namespace_structure {
+ my ($s) = @_;
+ my %h = map { if_(/(.*?)=(.*)/, $1 => $2) } split(',', $s);
+ \%h;
}
-sub template2userfile {
- my ($prefix, $in, $out_rel, $force, %toreplace) = @_;
- foreach (list_skels($prefix, $out_rel)) {
- -d dirname($_) or !-e $_ or $force or next;
+=item mageia_release($o_dir)
- template2file($in, $_, %toreplace);
- m|/home/(.+?)/| and chown(getpwnam($1), getgrnam($1), $_);
- }
+returns the content of Mageia release file from given directory (optional).
+
+=cut
+
+sub mageia_release {
+ my ($o_dir) = @_;
+ my $f = release_file($o_dir);
+ $f && chomp_(cat_("$o_dir$f"));
}
-sub update_userkderc {
- my ($file, $category, %subst) = @_;
-
- output $file,
- (map {
- my $l = $_;
- s/^\s*//;
- if (my $i = /^\[$category\]/i ... /^\[/) {
- if ($i =~ /E/) { #- for last line of category
- $l = join('', map_each { "$::a=$::b\n" } %subst) . $l;
- %subst = ();
- } elsif (/^(\w*?)=/) {
- if (my $e = delete $subst{lc($1)}) {
- $l = "$1=$e\n";
- }
- }
- }
- $l;
- } cat_($file)),
- (%subst && "[$category]\n", map_each { "$::a=$::b\n" } %subst); #- if category has not been found above.
-}
-
-sub substInFile(&@) {
- my $f = shift;
- foreach my $file (@_) {
- if (-e $file) {
- local @ARGV = $file;
- local ($^I, $_) = '';
- while (<>) { &$f($_); print }
- } else {
- local *F; my $old = select F; # that way eof return true
- local $_ = '';
- &$f($_);
- select $old;
- eval { output($file, $_) };
- }
- }
+
+=back
+
+=head2 Misc
+
+=over
+
+
+=item get_parent_uid()
+
+Returns UID of the parent process.
+
+=cut
+
+sub get_parent_uid() {
+ cat_('/proc/' . getppid() . '/status') =~ /Uid:\s*(\d+)/ ? $1 : undef;
}
-sub best_match {
- my ($str, @lis) = @_;
- my @words = split /\W+/, $str;
- my ($max, $res) = 0;
+=item require_root_capability()
- foreach (@lis) {
- my $count = 0;
- foreach my $i (@words) {
- $count++ if /$i/i;
- }
- $max = $count, $res = $_ if $count >= $max;
- }
- $res;
+Ensure we are running as root.
+
+=cut
+
+sub require_root_capability() {
+ return if $::testing || !$>; # we're already root
+
+ die "you must be root to run this program";
}
-sub bestMatchSentence {
+=item check_for_xserver()
- my $best = -1;
- my $bestSentence;
- my @s = split /\W+/, shift;
- foreach (@_) {
- my $count = 0;
- foreach my $e (@s) {
- $count++ if /$e/i;
- }
- $best = $count, $bestSentence = $_ if $count > $best;
+Test if we have access to X11.
+This eg enables interactive to use the proper backend (either text mode or
+GUI)
+
+=cut
+
+sub check_for_xserver() {
+ if (!defined $::xtest) {
+ $::xtest = 0;
+ eval {
+ require Gtk3;
+ Gtk3->import;
+ $::xtest = Gtk3::init_check();
+ } if $ENV{DISPLAY};
}
- $bestSentence;
+ return $::xtest;
}
-# count the number of character that match
-sub bestMatchSentence2 {
+=item unpack_with_refs($format, $s)
- my $best = -1;
- my $bestSentence;
- my @s = split /\W+/, shift;
- foreach (@_) {
- my $count = 0;
- foreach my $e (@s) {
- $count+= length ($e) if /$e/i;
+special unpack:
+
+=over 4
+
+=item * returning an array refs for each element like C<s10>
+
+=item * handling things like C<s10*> at the end of the format
+
+=back
+
+=cut
+
+sub unpack_with_refs {
+ my ($format, $s) = @_;
+ my $initial_format = $format;
+ my @r;
+ while ($format =~ s/\s*(\w(\d*))(\*?)\s*//) {
+ my ($sub_format, $nb, $many) = ($1, $2, $3);
+ $many && $format and internal_error("bad * in the middle of format in $initial_format");
+
+ my $done = $many && !length($s);
+ while (!$done) {
+ my @l = unpack("$sub_format a*", $s);
+ $s = pop @l;
+ push @r, $nb ? \@l : @l;
+ $done = !$many || !length($s);
}
- $best = $count, $bestSentence = $_ if $count > $best;
}
- $bestSentence;
+ @r;
+}
+
+=item md5file(@files)
+
+Returns the MD5 signatures of @files in list context or signature of the first one in scalar context.
+
+=cut
+
+#- used in userdrake and mdkonline
+sub md5file {
+ require Digest::MD5;
+ my @md5 = map {
+ my $sum;
+ if (open(my $FILE, $_)) {
+ binmode($FILE);
+ $sum = Digest::MD5->new->addfile($FILE)->hexdigest;
+ close($FILE);
+ }
+ $sum;
+ } @_;
+ return wantarray() ? @md5 : $md5[0];
+}
+
+sub load_modules_from_base {
+ my ($base) = @_;
+ $base =~ s|::|/|g;
+ my $base_file = $base . ".pm";
+ require $base_file;
+ my ($inc_path) = substr($INC{$base_file}, 0, -length($base_file));
+ my @files = map { substr($_, length($inc_path)) } glob_($inc_path . $base . '/*.pm');
+ require $_ foreach @files;
+ #- return the matching modules list
+ map { local $_ = $_; s|/|::|g; s|\.pm$||g; $_ } @files;
+}
+
+sub get_alternatives {
+ my ($name) = @_;
+
+ my $dir = $::prefix . '/var/lib/rpm/alternatives';
+ my ($state, $main_link, @l) = chomp_(cat_("$dir/$name")) or return;
+ my @slaves;
+ while (@l && $l[0] ne '') {
+ my ($name, $link) = splice(@l, 0, 2);
+ push @slaves, { name => $name, link => $link };
+ }
+ shift @l; #- empty line
+ my @alternatives;
+ while (@l && $l[0] ne '') {
+ my ($file, $weight, @slave_files) = splice(@l, 0, 2 + @slaves);
+
+ push @alternatives, { file => $file, weight => $weight, slave_files => \@slave_files };
+ }
+ { name => $name, link => $main_link, state => $state, slaves => \@slaves, alternatives => \@alternatives };
}
-sub typeFromMagic($@) {
- my $f = shift;
- local *F; sysopen F, $f, 0 or return;
-
- my $tmp;
- M: foreach (@_) {
- my ($name, @l) = @$_;
- while (@l) {
- my ($offset, $signature) = splice(@l, 0, 2);
- sysseek(F, $offset, 0) or next M;
- sysread(F, $tmp, length $signature);
- $tmp eq $signature or next M;
- }
- return $name;
- }
- undef;
+sub symlinkf_update_alternatives {
+ my ($name, $wanted_file) = @_;
+ run_program::rooted($::prefix, 'update-alternatives', '--set', $name, $wanted_file);
}
-sub availableMemory() { sum map { /(\d+)/ } grep { /^(MemTotal|SwapTotal):/ } cat_("/proc/meminfo"); }
-sub availableRamMB() {
- my $s = 4 * int ((stat("/proc/kcore"))[7] / 1024 / 1024 / 4 + 0.5);
- #- HACK HACK: if i810 and memsize
- require detect_devices;
- return $s - 1 if $s == 128 && grep { $_->{driver} =~ /i810/ } detect_devices::probeall();
- $s;
+sub update_gnomekderc_no_create {
+ my ($file, $category, %subst_) = @_;
+ if (-e $file) {
+ update_gnomekderc($file, $category, %subst_);
+ }
}
-sub setVirtual($) {
- my $vt = '';
- local *C;
- sysopen C, "/dev/console", 2 or die "failed to open /dev/console: $!";
- ioctl(C, c::VT_GETSTATE(), $vt) or die "ioctl VT_GETSTATE failed";
- ioctl(C, c::VT_ACTIVATE(), $_[0]) or die "ioctl VT_ACTIVATE failed";
- ioctl(C, c::VT_WAITACTIVE(), $_[0]) or die "ioctl VT_WAITACTIVE failed";
- unpack "S", $vt;
+sub get_libdir() {
+ arch() =~ /x86_64/ ? "lib64" : "lib";
}
+=item is_uefi()
-sub removeXiBSuffix($) {
- local $_ = shift;
+Checks if we are on an uefi system
- /(\d+)k$/i and return $1 * 1024;
- /(\d+)M$/i and return $1 * 1024 * 1024;
- /(\d+)G$/i and return $1 * 1024 * 1024 * 1024;
- $_;
-}
+=cut
-sub truncate_list {
- my $nb = shift;
- @_ <= $nb ? @_ : (@_[0..$nb-1], '...');
+sub is_uefi() { -e "/sys/firmware/efi" }
+
+=item kernel_uefi_type()
+
+Returns the short name of the UEFI machine type supported by the kernel stub loader
+
+=cut
+
+sub kernel_uefi_type() {
+ my $arch = arch();
+ if ($arch eq 'aarch64') {
+ return 'aa64';
+ }
+ $arch =~ /i.86/ ? 'ia32' : 'x64';
}
-sub formatTime {
- my ($s, $m, $h) = gmtime($_[0]);
- if ($h) {
- sprintf "%02d:%02d", $h, $m;
- } elsif ($m > 1) {
- _("%d minutes", $m);
- } elsif ($m == 1) {
- _("1 minute");
+=item uefi_type()
+
+Returns the UEFI machine type short name
+
+=cut
+
+sub uefi_type() {
+ if (arch() eq 'aarch64') {
+ return 'aa64';
+ }
+ if (-e '/sys/firmware/efi/fw_platform_size') {
+ cat_('/sys/firmware/efi/fw_platform_size') =~ /32/ ? 'ia32' : 'x64';
} else {
- _("%d seconds", $s);
+ 'none';
}
}
-sub formatTimeRaw {
- my ($s, $m, $h) = gmtime($_[0]);
- sprintf "%d:%02d:%02d", $h, $m, $s;
-}
-#- return the size of the partition and its free space in KiB
-sub df {
- my ($mntpoint) = @_;
- my ($blocksize, $size, $free);
- my $buf = ' ' x 20000;
- syscall_('statfs', $mntpoint, $buf) or return;
- (undef, $blocksize, $size, $free, undef, undef) = unpack "L6", $buf;
- map { $_ * ($blocksize / 1024) } $size, $free;
+=item mount_efivars()
+
+Ensures the efivarfs pseudo-filesystem is mounted in the default location
+and returns the mount point and whether it was already mounted.
+
+=cut
+
+sub mount_efivars {
+ my $mount_point = "$::prefix/sys/firmware/efi/efivars";
+ my $already_mounted = cat_("$::prefix/proc/mounts") =~ /\s$mount_point\s/;
+ run_program::run('mount', '-t', 'efivarfs', 'none', $mount_point) if !$already_mounted;
+ ($mount_point, $already_mounted);
}
-sub next_val_in_array {
- my ($v, $l) = @_;
- my %l = mapn { @_ } $l, [ @$l[1..$#$l], $l->[0] ];
- $l{$v};
+=item cmp_kernel_versions($va, $vb)
+
+Compare two kernel versions
+
+=cut
+
+sub cmp_kernel_versions {
+ my ($va, $vb) = @_;
+ my $rel_a = $va =~ s/-(.*)$// && $1;
+ my $rel_b = $vb =~ s/-(.*)$// && $1;
+ ($va, $vb) = map { [ split /[.-]/ ] } $va, $vb;
+ my $r = 0;
+ mapn_ {
+ $r ||= $_[0] <=> $_[1];
+ } $va, $vb;
+ $r || $rel_a <=> $rel_b || $rel_a cmp $rel_b;
}
+=back
+
+=cut
-#-######################################################################################
-#- Wonderful perl :(
-#-######################################################################################
-1; #
+1;