FunctionalPerl
view release on metacpan or search on metacpan
lib/Chj/xperlfunc.pm view on Meta::CPAN
xsystem
xxsystem
xsystem_safe
xxsystem_safe
xrename
xmkdir
xrmdir
xchmod
xchown
xchdir
xstat
xlstat
Xstat
Xlstat
xlocaltime
xreadlink
xunlink
xlink
xsymlink
xutime
xkill
xeval
xwaitpid
xxwaitpid
xwait
xxwait
xsysread
xchroot
);
our @EXPORT_OK = qw(
xspawn
xlaunch
xmvmkdir
xmkdir_with_paragon
xtmpdir_with_paragon
xlinkunlink
xlinkreplace
xxcarefulrename
xfileno
basename
dirname
xmkdir_p
xlink_p
xgetpwnam
xgetgrnam
caching_getpwnam
caching_getgrnam
xprint
xprintln
xLmtimed
XLmtimed
xLmtime
XLmtime
min max
fstype_for_device
xgetfile_utf8 xslurp
maybe_getfile_utf8
);
# would we really want to export these?:
#caching_getpwuid
#caching_getgrgid
our %EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]);
use Carp;
use FP::Carp;
use Chj::singlequote 'singlequote_many'; # the only dependency so far
use Chj::Unix::Exitcode qw(exitcode);
BEGIN {
if ($^O eq 'linux') {
eval 'sub EEXIST() {17} sub ENOENT() {2}';
die if $@;
} else {
eval 'use POSIX "EEXIST","ENOENT"';
die if $@;
}
}
sub xfork {
@_ == 0 or fp_croak_arity 0;
my $pid = fork;
defined $pid or croak "xfork: $!";
$pid
}
# thread-like API; incomplete, for sure.
sub xfork_(&) {
@_ == 1 or fp_croak_arity 1;
my ($thunk) = @_;
my $pid = xfork;
if ($pid) {
$pid
} else {
# kinda run in a new dynamic context, please... (evil,
# e.g. $SIG{__WARN__} is still set; do all of this?)
eval {
&$thunk();
# drop return value (transfer via pipe? 'No.')
exit 0; # POSIX::_exit ? no? Anyway docs say otherwise.
} || do {
warn "uncaught exception in subprocess $$, exiting: $@";
exit 1; # POSIX::_exit ? no?
}
}
}
sub xexec {
no warnings;
exec @_;
croak "xexec " . singlequote_many(@_) . ": $!";
}
sub xexec_safe {
no warnings;
exec { $_[0] } @_;
croak "xexec_safe " . singlequote_many(@_) . ": $!";
}
lib/Chj/xperlfunc.pm view on Meta::CPAN
return;
} else {
croak(@_ ? "Xstat: '@_': $!" : "Xstat: '$_': $!");
}
};
my $wantarray = wantarray; ## no critic
if ($wantarray) {
cluck "Xstat call in array context doesn't make sense";
@r
} elsif (defined $wantarray) {
bless \@r, 'Chj::xperlfunc::xstat'
} else {
cluck "Xstat call in void context doesn't make sense";
}
}
# XX ditto
sub Xlstat {
@_ <= 2 or croak "Xlstat: too many arguments";
my ($path, $accept_errors) = @_;
$path = $_ unless @_;
my @r = lstat_possiblyhires($path);
@r or do {
if ($accept_errors or $! == ENOENT) {
return;
} else {
croak("Xlstat: '$path': $!");
}
};
my $wantarray = wantarray; ## no critic
if ($wantarray) {
cluck "Xlstat call in array context doesn't make sense";
@r
} elsif (defined $wantarray) {
bless \@r, 'Chj::xperlfunc::xstat'
} else {
cluck "Xlstat call in void context doesn't make sense";
}
}
# caching variants of perlfuncs:
sub mk_caching_getANYid {
my ($function, $scalarindex, $methodname) = @_;
my %cache;
sub {
@_ == 1 or fp_croak_arity 1;
my ($id) = @_;
if (defined $id) {
my $v;
if (not defined($v = $cache{$id})) {
$v = [&$function($id)];
$cache{$id} = $v;
}
wantarray ? @$v : $$v[$scalarindex] ## no critic
} else {
croak "$methodname: got undefined value";
}
}
}
*caching_getpwuid
= mk_caching_getANYid(sub { getpwuid $_[0] }, 0, "caching_getpwuid");
*caching_getgrgid
= mk_caching_getANYid(sub { getgrgid $_[0] }, 0, "caching_getgrgid");
*caching_getpwnam
= mk_caching_getANYid(sub { getpwnam $_[0] }, 2, "caching_getpwnam");
*caching_getgrnam
= mk_caching_getANYid(sub { getgrnam $_[0] }, 2, "caching_getgrnam");
our $fstype_for_device;
sub fstype_for_device_init {
@_ == 0 or fp_croak_arity 0;
open my $mounts, "<", "/proc/mounts" or die "/proc/mounts: $!";
local $/ = "\n";
my %t;
local $_;
while (<$mounts>) { ## no critic, $_ is localized
my @f = split / /, $_;
my ($_dev, $mountpoint, $fstype) = @f;
if (
(
$fstype eq "rootfs"
# stupid Linux, not only source but also fs type is shown
# as rootfs.
)
or (
$fstype eq "autofs"
# more stupid: entry with systemd-1 source, then later
# with binfmt_misc source and fstype
)
)
{
# Ignore and count on the second entry from /proc/mounts
# for the same mount.
} else {
if (defined(my $s = Xlstat($mountpoint, 1))) {
my $dev = $s->dev;
if (defined $t{$dev}) {
$t{$dev} eq $fstype
or die
"entry for '$dev' was previously set to '$t{$dev}', now '$fstype'";
}
$t{ $s->dev } = $fstype;
}
}
# else silently ignore, presumably the fs should be reachable
# at another location then, or the $dev in question would
# never be found by that user. OK?
}
close $mounts or die $!;
$fstype_for_device = \%t;
}
sub fstype_for_device {
@_ == 1 or fp_croak_arity 1;
my ($dev) = @_;
my $t = $fstype_for_device->{$dev};
lib/Chj/xperlfunc.pm view on Meta::CPAN
and $s->executable_by_uid_gids($uid, $gids))
}
sub type {
my $s = shift;
if ($s->is_dir) {"dir"}
elsif ($s->is_link) {"link"}
elsif ($s->is_file) {"file"}
elsif ($s->is_socket) {"socket"}
elsif ($s->is_chardevice) {"chardevice"}
elsif ($s->is_blockdevice) {"blockdevice"}
elsif ($s->is_pipe) {"pipe"}
else { die "unknown type of filetype: " . $s->filetype }
}
# check whether "a file has changed"
sub equal_content {
my $s = shift;
my ($s2) = @_;
( $s->dev == $s2->dev
and $s->ino == $s2->ino
and $s->size == $s2->size
and $s->mtime == $s2->mtime)
}
# Could also implement FP::Abstract::Equal, but do not just use
# the following *equal for it, rather check *all* the fields!
sub equal {
my $s = shift;
my ($s2) = @_;
# permissions:
( $s->equal_content($s2)
and $s->mode == $s2->mode
and $s->uid == $s2->uid
and $s->gid == $s2->gid)
}
sub same_node {
my $s = shift;
my ($s2) = @_;
($s->ino == $s2->ino and $s->dev == $s2->dev)
}
# for simplicity (and in cases where I copy values in 'rows' (lists of methods)):
# ATTENTION: these are non-caching! see below.
sub username {
my $s = shift;
scalar $s->getpw
}
sub groupname {
my $s = shift;
scalar $s->getgr
}
# note that those are sensitive to list context!:
# (and yes those should 'probably' return such objects as these, too..)
sub getpw {
my $s = shift;
getpwuid($s->uid)
}
sub getgr {
my $s = shift;
getgrgid($s->gid)
}
# for performance:
sub caching_getpw {
my $s = shift;
Chj::xperlfunc::caching_getpwuid($s->uid);
}
sub caching_getgr {
my $s = shift;
Chj::xperlfunc::caching_getgrgid($s->gid);
}
sub caching_username {
my $s = shift;
scalar $s->caching_getpw
}
sub caching_groupname {
my $s = shift;
scalar $s->caching_getgr
}
}
use FP::Div qw(min max); # min just for the backwards-compatible
# re-export
package Chj::xperlfunc::mtimed {
sub path { shift->[0] }
sub mtime { shift->[1] }
sub lstat { shift->[2] }
sub maybe_stat { shift->[3] }
# ---
sub xstat {
my $s = shift;
$$s[3] || die "Xstat gave file not found for: '$$s[0]'";
}
sub is_link {
shift->[2]->is_link
}
sub is_dir {
my $s = shift;
$s->is_link ? $s->xstat->is_dir : $s->lstat->is_dir
}
sub is_file {
my $s = shift;
$s->is_link ? $s->xstat->is_file : $s->lstat->is_file
}
}
sub XLmtimed {
@_ == 1 or fp_croak_arity 1;
my ($path) = @_;
if (my $ls = Xlstat $path) {
bless do {
if ($ls->is_link) {
if (my $s = Xstat $path) {
[$path, max($ls->mtime, $s->mtime), $ls, $s]
} else {
[$path, $ls->mtime, $ls, undef]
}
( run in 0.607 second using v1.01-cache-2.11-cpan-39bf76dae61 )