# Copyright (c) 2023 Yuki Kimoto
# MIT License
class Sys::IO::Stat : pointer {
use Sys::User;
use Sys::IO::Constant as IO;
use Hash;
use Sys;
use Fn;
use Sys::IO;
# Class methods
native static method new : Sys::IO::Stat ();
native static method stat : int ($path : string, $stat : Sys::IO::Stat);
native static method lstat : int ($path : string, $stat : Sys::IO::Stat);
native static method fstat : int ($fd : int, $stat : Sys::IO::Stat);
# Instance Methods
native method DESTROY : void ();
native method st_dev : long ();
native method st_ino : long ();
native method st_mode : int ();
native method st_nlink : long ();
native method st_uid : int ();
native method st_gid : int ();
native method st_rdev : long ();
native method st_size : long ();
native method st_blksize : long ();
native method st_blocks : long ();
native method st_mtime : long ();
native method st_atime : long ();
native method st_ctime : long ();
native method st_mtim_tv_nsec : long ();
native method st_atim_tv_nsec : long ();
native method st_ctim_tv_nsec : long ();
method A : double () {
my $base_time = CommandInfo->BASE_TIME;
my $atime = $self->st_atime;
my $result_time = ($base_time - $atime) / 86400.0;
return $result_time;
}
method C : double () {
my $base_time = CommandInfo->BASE_TIME;
my $ctime = $self->st_ctime;
my $result_time = ($base_time - $ctime) / 86400.0;
return $result_time;
}
method M : double () {
my $base_time = CommandInfo->BASE_TIME;
my $mtime = $self->st_mtime;
my $result_time = ($base_time - $mtime) / 86400.0;
return $result_time;
}
method O : int () {
my $ok = 0;
if (Sys::OS->is_windows) {
if ($self->st_uid == 0) {
$ok = 1;
}
}
else {
if ($self->st_uid == Sys::User->getuid) {
$ok = 1;
}
}
return $ok;
}
method S : int () {
my $ok = 0;
unless (Sys::OS->is_windows) {
# Socket
if (($self->st_mode & IO->S_IFMT) == IO->S_IFSOCK) {
$ok = 1;
}
}
return $ok;
}
method b : int () {
# Block device
my $ok = 0;
if (($self->st_mode & IO->S_IFMT) == IO->S_IFBLK) {
$ok = 1;
}
return $ok;
}
method c : int () {
# Character device
my $ok = 0;
if (($self->st_mode & IO->S_IFMT) == IO->S_IFCHR) {
$ok = 1;
}
return $ok;
}
method d : int () {
my $ok = 0;
if (($self->st_mode & IO->S_IFMT) == IO->S_IFDIR) {
$ok = 1;
}
return $ok;
}
method e : int () {
my $ok = 1;
return $ok;
}
method f : int () {
my $ok = 0;
if (($self->st_mode & IO->S_IFMT) == IO->S_IFREG) {
$ok = 1;
}
return $ok;
}
method g : int () {
my $ok = 0;
unless (Sys::OS->is_windows) {
if ($self->st_mode & IO->S_ISGID) {
$ok = 1;
}
}
return $ok;
}
method k : int () {
my $ok = 0;
unless (Sys::OS->is_windows) {
if ($self->st_mode & IO->S_ISVTX) {
$ok = 1;
}
}
return $ok;
}
method l : int () {
# Symbolic link
my $ok = 0;
if (($self->st_mode & IO->S_IFMT) == IO->S_IFLNK) {
$ok = 1;
}
return $ok;
}
method o : int () {
if (Sys::OS->is_windows) {
return $self->O;
}
my $ok = 0;
if ($self->st_uid == Sys::User->geteuid) {
$ok = 1;
}
return $ok;
}
method p : int () {
# FIFO/PIPE
my $ok = 0;
if (($self->st_mode & IO->S_IFMT) == IO->S_IFIFO) {
$ok = 1;
}
return $ok;
}
method s : long () {
my $size = $self->st_size;
return $size;
}
method u : int () {
# Character device
my $ok = 0;
unless (Sys::OS->is_windows) {
if ($self->st_mode & IO->S_ISUID) {
$ok = 1;
}
}
return $ok;
}
method z : int () {
my $ok = 0;
my $size = $self->st_size;
if ($size == 0) {
$ok = 1;
}
return $ok;
}
method r : int () {
my $effective = 1;
my $ok = $self->cando(IO->S_IRUSR, $effective);
return $ok;
}
method w : int () {
my $effective = 1;
my $ok = $self->cando(IO->S_IWUSR, $effective);
return $ok;
}
method x : int () {
my $effective = 1;
my $ok = $self->cando(IO->S_IXUSR, $effective);
return $ok;
}
method R : int () {
my $effective = 0;
my $ok = $self->cando(IO->S_IRUSR, $effective);
return $ok;
}
method W : int () {
my $effective = 0;
my $ok = $self->cando(IO->S_IWUSR, $effective);
return $ok;
}
method X : int () {
my $effective = 0;
my $ok = $self->cando(IO->S_IXUSR, $effective);
return $ok;
}
private static method _ingroup : int ($gid : int, $effective : int) {
my $egid = Sys::User->getegid;
my $supp_length = Sys::User->getgroups(0, undef);
my $supp = new int[$supp_length];
Sys::User->getgroups($supp_length, $supp);
my $rgid = Sys::User->getgid;
my $ingroup = 0;
my $cmp_gid = 0;
if ($effective) {
$cmp_gid = $egid;
}
else {
$cmp_gid = $rgid;
}
if ($gid == $cmp_gid) {
$ingroup = 1;
}
else {
for my $sup (@$supp) {
if ($gid == $sup) {
$ingroup = 1;
last;
}
}
}
return $ingroup;
}
private method cando : int ($mode : int, $effective : int) {
my $stmode = $self->st_mode;
my $cando = 0;
if (Sys::OS->is_windows) {
$cando = !!($mode & $stmode);
}
else {
my $stuid = $self->st_uid;
my $stgid = $self->st_gid;
my $uid = 0;
if ($effective) {
$uid = Sys::User->geteuid;
}
else {
$uid = Sys::User->getuid;
}
# $uid becomes 0 in i386/ubuntu in non-root user, but this logic return the same result as Perl
if ($uid == 0) {
# If we're root on unix
# not testing for executable status => all file tests are true
if (!($mode & 0111)) {
$cando = 1;
}
# testing for executable status =>
# for a file, any x bit will do
# for a directory, always true
elsif ($stmode & 0111 || (($stmode & IO->S_IFMT) == IO->S_IFDIR)) {
$cando = 1;
}
}
else {
if ($stuid == $uid) {
$cando = !!($stmode & $mode);
}
elsif (&_ingroup($stgid, $effective)) {
$cando = !!($stmode & ($mode >> 3));
}
else {
$cando = !!($stmode & ($mode >> 6));
}
}
}
return $cando;
}
}
=pod