# Copyright (c) 2023 Yuki Kimoto
# MIT License

class Sys {
  version "0.532";
  
  use Resource::SocketUtil;
  
  use Format;
  use Sys::IO;
  use Sys::IO::FileStream;
  use Sys::IO::Stat;
  use Sys::IO::Constant as IO;
  use Sys::Ioctl;
  use Sys::Select;
  use Sys::Socket;
  use Sys::Socket::Constant as SOCKET;
  use Sys::OS;
  use Sys::Env;
  use Sys::Time;
  use Sys::Time::Util;
  use Sys::Time::Utimbuf;
  use Sys::Process;
  use Sys::Signal;
  use Sys::User;
  use Sys::Socket::Sockaddr::Storage;
  
  static method STDIN : Sys::IO::FileStream () { return Sys::IO->spvm_stdin; }
  
  static method STDOUT : Sys::IO::FileStream () { return Sys::IO->spvm_stdout; }
  
  static method STDERR : Sys::IO::FileStream () { return Sys::IO->spvm_stderr; }
  
  static method open : void ($stream_ref : Sys::IO::FileStream[], $open_mode : string, $file : string) {
    
    unless ($stream_ref) {
      die "\$stream_ref must be defined.";
    }
    
    unless (@$stream_ref == 1) {
      die "The length of \$stream_ref must be equal to 1.";
    }
    
    my $fopen_mode = &to_fopen_mode($open_mode);
    
    my $stream = Sys::IO->fopen($file, $fopen_mode);
    
    unless (Sys::OS->is_windows) {
      Sys->fcntl(Sys->fileno($stream), IO->F_SETFD, IO->FD_CLOEXEC);
    }
    
    $stream_ref->[0] = $stream;
  }
  
  static method fdopen : void ($stream_ref : Sys::IO::FileStream[], $open_mode : string, $fd : int) {
    
    unless ($stream_ref) {
      die "\$stream_ref must be defined.";
    }
    
    unless (@$stream_ref == 1) {
      die "The length of \$stream_ref must be equal to 1.";
    }
    
    my $fopen_mode = &to_fopen_mode($open_mode);
    
    my $stream = Sys::IO->fdopen($fd, $fopen_mode);
    
    $stream_ref->[0] = $stream;
  }
  
  private static method to_fopen_mode : string ($open_mode : string) {
    
    my $fopen_mode = (string)undef;
    
    if ($open_mode eq "<") {
      $fopen_mode = "rb";
    }
    elsif ($open_mode eq ">") {
      $fopen_mode = "wb";
    }
    elsif ($open_mode eq ">>") {
      $fopen_mode = "wa";
    }
    elsif ($open_mode eq "+<") {
      $fopen_mode = "r+b";
    }
    elsif ($open_mode eq "+>") {
      $fopen_mode = "w+b";
    }
    elsif ($open_mode eq "+>>") {
      $fopen_mode = "a+b";
    }
    else {
      $fopen_mode = $open_mode;
    }
    
    return $fopen_mode;
  }
  
  static method fileno : int ($stream : Sys::IO::FileStream) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    my $fd = Sys::IO->fileno($stream);
    
    return $fd;
  }
  
  static method read : int ($stream : Sys::IO::FileStream, $buffer : mutable string, $length : int, $buffer_offset : int = 0) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    my $read_length = Sys::IO->fread($buffer, 1, $length, $stream);
    
    return $read_length;
  }
  
  static method eof : int ($stream : Sys::IO::FileStream) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    my $eof = Sys::IO->feof($stream);
    
    return $eof;
  }
  
  precompile static method readline : mutable string ($stream : Sys::IO::FileStream) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    my $capacity = 80;
    my $buffer = (mutable string)new_string_len $capacity;
    
    my $pos = 0;
    my $end_is_eof = 0;
    my $eof = IO->EOF;
    while (1) {
      my $ch = Sys->getc($stream);
      if ($ch == $eof) {
        $end_is_eof = 1;
        last;
      }
      else {
        if ($pos >= $capacity) {
          my $new_capacity = $capacity * 2;
          my $new_buf = (mutable string)new_string_len $new_capacity;
          
          Fn->memcpy($new_buf, 0, $buffer, 0, $capacity);
          
          $capacity = $new_capacity;
          $buffer = $new_buf;
        }
        
        if ($ch == '\n') {
          $buffer->[$pos] = (byte)$ch;
          $pos++;
          last;
        }
        else {
          $buffer->[$pos] = (byte)$ch;
          $pos++;
        }
      }
    }
    
    my $line = (mutable string)undef;
    if ($pos > 0 || !$end_is_eof) {
      if ($pos == 0) {
        $line = (mutable string)new_string_len 0;
      }
      else {
        $line = (mutable string)new_string_len $pos;
        Fn->memcpy($line, 0, $buffer, 0, $pos);
      }
    }
    
    return $line;
  }
  
  static method getc : int ($stream : Sys::IO::FileStream) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    my $char = Sys::IO->getc($stream);
    
    return $char;
  }
  
  static method print : void ($stream : Sys::IO::FileStream, $string : string) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    Sys::IO->fwrite($string, 1, length $string, $stream);
  }
  
  static method printf : void ($stream : Sys::IO::FileStream, $format : string, $args : object[]) {
    
    my $formated_string = Format->sprintf($format, $args);
    &print($stream, $formated_string);
  }
  
  static method say : void ($stream : Sys::IO::FileStream, $string : string) {
    &print($stream, $string);
    &print($stream, "\n");
  }
  
  static method close : void ($stream : Sys::IO::FileStream) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    Sys::IO->fclose($stream);
  }
  
  static method seek : void ($stream : Sys::IO::FileStream, $offset : long, $whence : int) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    Sys::IO->fseek($stream, $offset, $whence);
  }
  
  static method tell : long ($stream : Sys::IO::FileStream) {
    
    unless ($stream) {
      die "The file stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The file stream \$stream is already closed.";
    }
    
    my $current_position = Sys::IO->ftell($stream);
    return $current_position;
  }
  
  static method sysopen : void ($fd_ref : int*, $file : string, $flags : int, $mode : int = 0) {
    
    my $fd = Sys::IO->open($file, $flags, $mode);
    
    $$fd_ref = $fd;
  }
  
  static method sysread : int ($fd : int, $buffer : mutable string, $length : int, $buffer_offset : int = 0) {
    my $read_length = Sys::IO->read($fd, $buffer, $length, $buffer_offset);
    return $read_length;
  }
  
  static method syswrite : int ($fd : int, $buffer : string, $length : int = -1, $buffer_offset : int = 0) {
    my $write_length = Sys::IO->write($fd, $buffer, $length, $buffer_offset);
    return $write_length;
  }
  
  static method sysseek : long ($fd : int, $offset : long, $whence : int) {
    my $result_offset = Sys::IO->lseek($fd, $offset, $whence);
    return $result_offset;
  }
  
  static method fcntl : int ($fd : int, $command : int, $command_arg : object of Int|Sys::IO::Flock|object = undef) {
    my $fcntl_ret = Sys::IO->fcntl($fd, $command, $command_arg);
    return $fcntl_ret;
  }
  
  static method truncate : void ($fd : int, $legnth : long) {
    
    Sys::IO->ftruncate($fd, $legnth);
  }
  
  static method flock : void ($fd : int, $operation : int) {
    Sys::IO->flock($fd, $operation);
  }
  
  static method mkdir : void ($dir : string, $mode : int = -1) {
    
    if ($mode < 0) {
      $mode = 0777;
    }
    
    Sys::IO->mkdir($dir, $mode);
  }
  
  static method umask : int ($mode : int) {
    my $current_umask = Sys::IO->umask($mode);
    
    return $current_umask;
  }
  
  static method rmdir : void ($dir : string) {
    Sys::IO->rmdir($dir);
  }
  
  static method unlink : void ($file : string) {
    
    if (Sys::OS->is_windows) {
      Sys::IO::Windows->unlink($file);
    }
    else {
      Sys::IO->unlink($file);
    }
  }
  
  static method rename : void ($old_path : string, $new_path : string) {
    if (Sys::OS->is_windows) {
      Sys::IO::Windows->rename($old_path, $new_path);
    }
    else {
      Sys::IO->rename($old_path, $new_path);
    }
  }
  
  static method symlink : int ($old_path : string, $new_path : string) {
    if (Sys::OS->is_windows) {
      Sys::IO::Windows->symlink($old_path, $new_path);
    }
    else {
      Sys::IO->symlink($old_path, $new_path);
    }
    
    my $success = 1;
    
    return $success;
  }
  
  static method readlink : string ($file : string) {
    
    my $stat = Sys->lstat($file);
    
    my $readlink_buffer_size = (int)$stat->st_size;
    
    my $buffer = (mutable string)new_string_len $readlink_buffer_size;
    
    if (Sys::OS->is_windows) {
      Sys::IO::Windows->readlink($file, $buffer, $readlink_buffer_size);
    }
    else {
      Sys::IO->readlink($file, $buffer, $readlink_buffer_size);
    }
    
    return $buffer;
  }
  
  static method chdir : void ($dir : string) {
    Sys::IO->chdir($dir);
  }
  
  static method chmod : void ($mode :int, $file : string) {
    Sys::IO->chmod($file, $mode);
  }
  
  static method chown : void ($owner : int, $group : int, $file : string) {
    Sys::IO->chown($file, $owner, $group);
  }
  
  static method opendir : void ($dir_stream_ref : Sys::IO::DirStream[], $dir : string) {
    
    my $dh = Sys::IO->opendir($dir);
    
    $dir_stream_ref->[0] = $dh;
  }
  
  static method closedir : void ($dir_stream : Sys::IO::DirStream) {
    
    unless ($dir_stream) {
      die "The directory stream \$dir_stream must be defined.";
    }
    
    if ($dir_stream->closed) {
      die "The directory stream \$dir_stream is already closed.";
    }
    
    Sys::IO->closedir($dir_stream);
  }
  
  static method readdir : Sys::IO::Dirent ($dir_stream : Sys::IO::DirStream) {
    
    unless ($dir_stream) {
      die "The directory stream \$dir_stream must be defined.";
    }
    
    if ($dir_stream->closed) {
      die "The directory stream \$dir_stream is already closed.";
    }
    
    my $dirent = Sys::IO->readdir($dir_stream);
    
    return $dirent;
  }
  
  static method rewinddir : void ($dir_stream : Sys::IO::DirStream) {
    
    unless ($dir_stream) {
      die "The directory stream \$dir_stream must be defined.";
    }
    
    if ($dir_stream->closed) {
      die "The directory stream \$dir_stream is already closed.";
    }
    
    Sys::IO->rewinddir($dir_stream);
  }
  
  static method seekdir : void ($dir_stream : Sys::IO::DirStream, $offset : long) {
    
    unless ($dir_stream) {
      die "The directory stream \$dir_stream must be defined.";
    }
    
    if ($dir_stream->closed) {
      die "The directory stream \$dir_stream is already closed.";
    }
    
    Sys::IO->seekdir($dir_stream, $offset);
  }
  
  static method telldir : long ($dir_stream : Sys::IO::DirStream) {
    
    unless ($dir_stream) {
      die "The directory stream \$dir_stream must be defined.";
    }
    
    if ($dir_stream->closed) {
      die "The directory stream \$dir_stream is already closed.";
    }
    
    my $offset = Sys::IO->telldir($dir_stream);
    
    return $offset;
  }
  
  static method popen : void ($stream_ref : Sys::IO::FileStream[], $open_mode : string, $command : string) {
    
    my $type = (string)undef;
    if ($open_mode eq "|-") {
      $type = "wb";
    }
    elsif ($open_mode eq "-|") {
      $type = "rb";
    }
    else {
      die "The open mode \"$open_mode\" is not available.";
    }
    
    my $stream = (Sys::IO::FileStream)undef;
    if (Sys::OS->is_windows) {
      $stream = Sys::IO->_popen($command, $type);
    }
    else {
      $stream = Sys::IO->popen($command, $type);
      Sys->fcntl(Sys->fileno($stream), IO->F_SETFD, IO->FD_CLOEXEC);
    }
    
    $stream_ref->[0] = $stream;
  }
  
  static method pclose : void ($stream : Sys::IO::FileStream) {
    
    unless ($stream) {
      die "The pipe stream \$stream must be defined.";
    }
    
    if ($stream->closed) {
      die "The pipe stream \$stream is already closed.";
    }
    
    if (Sys::OS->is_windows) {
      Sys::IO->_pclose($stream);
    }
    else {
      Sys::IO->pclose($stream);
    }
  }
  
  static method select : int ($read_fds : Sys::Select::Fd_set, $write_fds : Sys::Select::Fd_set, $except_fds : Sys::Select::Fd_set, $timeout : double = 0) {
    my $nfds = 1024;
    
    my $timeout_tv = (Sys::Time::Timeval)undef;
    if ($timeout >= 0) {
      $timeout_tv = Sys::Time::Util->float_seconds_to_timeval($timeout);
    }
    else {
      $timeout_tv = undef;
    }
    
    my $fd_number = Sys::Select->select($nfds, $read_fds, $write_fds, $except_fds, $timeout_tv);
    
    return $fd_number;
  }
  
  static method ioctl : int ($fd : int, $request : int, $request_arg_ref : object of byte[]|short[]|int[]|long[]|float[]|double[]|object = undef) {
    my $ioctl_ret = -1;
    
    if (Sys::OS->is_windows) {
      $ioctl_ret = Sys::Ioctl->ioctlsocket($fd, $request, (int[])$request_arg_ref);
    }
    else {
      $ioctl_ret = Sys::Ioctl->ioctl($fd, $request, $request_arg_ref);
    }
    
    return $ioctl_ret;
  }
  
  static method stat : Sys::IO::Stat ($file : string) {
    my $stat = Sys::IO::Stat->new;
    Sys::IO::Stat->stat($file, $stat);
    return $stat;
  }
  
  static method lstat : Sys::IO::Stat ($file : string) {
    
    my $stat = Sys::IO::Stat->new;
    
    if (Sys::OS->is_windows) {
      Sys::IO::Windows->lstat($file, $stat);
    }
    else {
      Sys::IO::Stat->lstat($file, $stat);
    }
    
    return $stat;
  }
  
  static method fstat : Sys::IO::Stat ($fd : int) {
    my $stat = Sys::IO::Stat->new;
    Sys::IO::Stat->fstat($fd, $stat);
    return $stat;
  }
  
  static method A : double ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = Sys->stat($file);
    
    my $result_time = $stat->A;
    
    return $result_time;
  }
  
  static method C : double ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = Sys->stat($file);
    my $result_time = $stat->C;
    
    return $result_time;
  }
  
  static method M : double ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = Sys->stat($file);
    
    my $result_time = $stat->M;
    
    return $result_time;
  }
  
  static method O : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->O;
    }
    
    return $ok;
  }
  
  static method R : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->R;
    }
    
    return $ok;
  }
  
  static method S : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->S;
    }
    
    return $ok;
  }
  
  static method W : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->W;
    }
    
    return $ok;
  }
  
  static method X : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->X;
    }
    
    return $ok;
  }
  
  static method b : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->b;
    }
    
    return $ok;
  }
  
  static method c : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    # Character device
    my $ok = 0;
    unless ($@) {
      $ok = $stat->c;
    }
    
    return $ok;
  }
  
  static method d : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->d;
    }
    
    return $ok;
  }
  
  static method e : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->e;
    }
    
    return $ok;
  }
  
  static method f : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->f;
    }
    
    return $ok;
  }
  
  static method g : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->g;
    }
    
    return $ok;
  }
  
  static method k : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->k;
    }
    
    return $ok;
  }
  
  static method l : int ($file : string) {
    
    my $ok = 0;
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->lstat($file); }
    
    unless ($@) {
      $ok = $stat->l;
    }
    
    return $ok;
  }
  
  static method o : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->o;
    }
    return $ok;
  }
  
  static method p : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    # FIFO/PIPE
    my $ok = 0;
    unless ($@) {
      $ok = $stat->p;
    }
    
    return $ok;
  }
  
  static method r : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    # Character device
    my $ok = 0;
    unless ($@) {
      $ok = $stat->r;
    }
    
    return $ok;
  }
  
  static method s : long ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = Sys->stat($file);
    
    my $size = $stat->s;
    
    return $size;
  }
  
  static method u : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->u;
    }
    
    return $ok;
  }
  
  static method w : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->w;
    }
    
    return $ok;
  }
  
  static method x : int ($file : string) {
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = (Sys::IO::Stat)undef;
    eval { $stat = Sys->stat($file); }
    
    my $ok = 0;
    unless ($@) {
      $ok = $stat->x;
    }
    
    return $ok;
  }
  
  static method z : int ($file : string) {
    
    unless ($file) {
      die "\$file must be defined.";
    }
    
    my $stat = Sys->stat($file);
    
    my $ok = $stat->z;
    
    return $ok;
  }
  
  static method socket : void ($socket_fd_ref : int*, $domain : int, $type : int, $protocol : int) {
    
    $$socket_fd_ref = Sys::Socket->socket($domain, $type, $protocol);
    
    unless (Sys::OS->is_windows) {
      Sys->fcntl($$socket_fd_ref, IO->F_SETFD, IO->FD_CLOEXEC);
    }
  }
  
  static method connect : void ($socket_fd : int, $sockaddr : Sys::Socket::Sockaddr) {
    
    my $sockaddr_size = $sockaddr->size;
    
    Sys::Socket->connect($socket_fd, $sockaddr, $sockaddr_size);
  }
  
  static method bind : void ($socket_fd : int, $sockaddr : Sys::Socket::Sockaddr) {
    
    my $sockaddr_size = $sockaddr->size;
    
    Sys::Socket->bind($socket_fd, $sockaddr, $sockaddr_size);
  }
  
  static method listen : void ($socket_fd : int, $backlog : int) {
    
    Sys::Socket->listen($socket_fd, $backlog);
  }
  
  static method accept : Sys::Socket::Sockaddr ($client_fd_ref : int*, $server_fd : int) {
    
    my $sockaddr = Sys::Socket::Sockaddr->new;
    
    my $sockaddr_size = Sys::Socket::Sockaddr::Storage->new->size;
    
    $$client_fd_ref = Sys::Socket->accept($server_fd, $sockaddr, \$sockaddr_size);
    
    unless (Sys::OS->is_windows) {
      Sys->fcntl($$client_fd_ref, IO->F_SETFD, IO->FD_CLOEXEC);
    }
    
    $sockaddr = $sockaddr->upgrade;
    
    return $sockaddr;
  }
  
  static method recv : int ($socket_fd : int, $buffer : mutable string, $length : int, $flags : int, $buffer_offset : int = 0) {
    
    my $recv_length = Sys::Socket->recv($socket_fd, $buffer, $length, $flags, $buffer_offset);
    
    return $recv_length;
  }
  
  static method recvfrom : int ($socket_fd : int, $buffer : mutable string, $length : int, $flags : int, $sockaddr_ref : Sys::Socket::Sockaddr[], $buffer_offset : int = 0) {
    
    my $sockaddr = (Sys::Socket::Sockaddr)undef;
    
    my $addrlen = -1;
    
    if ($sockaddr_ref) {
      unless (@$sockaddr_ref == 1) {
        die "If \$sockaddr_ref for an array for a peer socket address for output is defined, the length must be 1.";
      }
      
      $sockaddr = Sys::Socket::Sockaddr->new;
    }
    
    my $recv_length = Sys::Socket->recvfrom($socket_fd, $buffer, $length, $flags, $sockaddr, \$addrlen, $buffer_offset);
    
    if ($sockaddr_ref) {
      $sockaddr = $sockaddr->upgrade;
      
      $sockaddr_ref->[0] = $sockaddr;
    }
    
    return $recv_length;
  }
  
  static method send : int ($socket_fd : int, $buffer : string, $flags : int, $length : int = -1, $buffer_offset : int = 0) {
    
    if ($length < 0) {
      $length = length $buffer;
    }
    
    my $send_length = Sys::Socket->send($socket_fd, $buffer, $length, $flags, $buffer_offset);
    
    return $send_length;
  }
  
  static method sendto : int ($socket_fd : int, $buffer : string, $flags : int, $sockaddr : Sys::Socket::Sockaddr, $length : int = -1, $buffer_offset : int = 0) {
    
    if ($length < 0) {
      $length = length $buffer;
    }
    
    my $addrlen = -1;
    
    if ($sockaddr) {
      $addrlen = $sockaddr->size;
    }
    
    my $send_length = Sys::Socket->sendto($socket_fd, $buffer, $length, $flags, $sockaddr, $addrlen, $buffer_offset);
    
    return $send_length;
  }
  
  static method shutdown : void ($socket_fd : int, $how : int) {
    Sys::Socket->shutdown($socket_fd, $how);
  }
  
  static method getpeername : Sys::Socket::Sockaddr ($socket_fd : int) {
    
    my $sockaddr_size = Sys::Socket::Sockaddr::Storage->new->size;
    
    my $sockaddr = Sys::Socket::Sockaddr->new;
    
    Sys::Socket->getpeername($socket_fd, $sockaddr, \$sockaddr_size);
    
    $sockaddr = $sockaddr->upgrade;
    
    return $sockaddr;
  }
  
  static method getsockname : Sys::Socket::Sockaddr ($socket_fd : int) {
    
    my $sockaddr_size = Sys::Socket::Sockaddr::Storage->new->size;
    
    my $sockaddr = Sys::Socket::Sockaddr->new;
    
    Sys::Socket->getsockname($socket_fd, $sockaddr, \$sockaddr_size);
    
    $sockaddr = $sockaddr->upgrade;
    
    return $sockaddr;
  }
  
  static method socketpair : void ($socket_fd1_ref : int*, $socket_fd2_ref : int*, $domain : int, $type : int, $protocol : int) {
    my $pair = new int[2];
    
    Sys::Socket->socketpair($domain, $type, $protocol, $pair);
    
    $$socket_fd1_ref = $pair->[0];
    
    $$socket_fd2_ref = $pair->[1];
    
    unless (Sys::OS->is_windows) {
      Sys->fcntl($$socket_fd1_ref, IO->F_SETFD, IO->FD_CLOEXEC);
      
      Sys->fcntl($$socket_fd2_ref, IO->F_SETFD, IO->FD_CLOEXEC);
    }
  }
  
  static method setsockopt : void ($socket_fd : int, $level : int, $option_name : int, $option_value : object of string|Int) {
    
    unless ($option_value) {
      die "\$option_value must be defined.";
    }
    
    if ($option_value is_type Int) {
      my $option_value_ref = [$option_value->(Int)->value];
      my $option_value_string = new_string_len 4;
      Fn->memcpy($option_value_string, 0, $option_value_ref, 0, 4);
      
      Sys::Socket->setsockopt($socket_fd, $level, $option_name, $option_value_string, 4);
    }
    elsif ($option_value is_type string) {
      my $option_value_string = (string)$option_value;
      Sys::Socket->setsockopt($socket_fd, $level, $option_name, $option_value_string, length $option_value_string);
    }
    else {
      die "The type of \$option_value must be the Int or string type.";
    }
  }
  
  static method getsockopt : string ($socket_fd : int, $level : int, $option_name : int, $option_value_length : int = -1) {
    
    unless ($option_value_length >= 0) {
      $option_value_length = 4;
    }
    
    my $option_value = (mutable string)new_string_len $option_value_length;
    
    Sys::Socket->getsockopt($socket_fd, $level, $option_name, $option_value, \$option_value_length);
    
    return $option_value;
  }
  
  static method osname : string () {
    my $osname : string;
    
    if (Sys::OS->defined("__linux__")) {
      $osname = "linux";
    }
    elsif (Sys::OS->defined("_WIN32")) {
      $osname = "MSWin32";
    }
    elsif (Sys::OS->defined("__FreeBSD__")) {
      $osname = "freebsd";
    }
    elsif (Sys::OS->defined("__OpenBSD__")) {
      $osname = "openbsd";
    }
    elsif (Sys::OS->defined("__solaris")) {
      $osname = "solaris";
    }
    elsif (Sys::OS->defined("__sun")) {
      $osname = "solaris";
    }
    elsif (Sys::OS->defined("__APPLE__")) {
      $osname = "darwin";
    }
    else {
      die "The OS name could not be determined.";
    }
    
    return $osname;
  }
  
  static method env : string ($name : string) {
    my $env_value = Sys::Env->getenv($name);
    return $env_value;
  }
  
  static method set_env : void ($name : string, $value : string) {
    
    if (Sys::OS->is_windows) {
      unless ($value) {
        $value = "";
      }
      Sys::Env->_putenv_s($name, $value);
    }
    else {
      if ($value && $value ne "") {
        my $overwrite = 1;
        Sys::Env->setenv($name, $value, $overwrite);
      }
      else {
        Sys::Env->unsetenv($name);
      }
    }
  }
  
  static method time : long () {
    
    my $time = Sys::Time->time();
    
    return $time;
  }
  
  static method localtime : Sys::Time::Tm ($epoch : long = -1, $allow_minus : int = 0) {
    
    unless ($allow_minus) {
      if ($epoch < 0) {
        $epoch = Sys->time;
      }
    }
    
    my $tm = Sys::Time->localtime(\$epoch);
    
    return $tm;
  }
  
  static method gmtime : Sys::Time::Tm ($epoch : long = -1, $allow_minus : int = 0) {
    
    unless ($allow_minus) {
      if ($epoch < 0) {
        $epoch = Sys->time;
      }
    }
    
    my $tm = Sys::Time->gmtime(\$epoch);
    
    return $tm;
  }
  
  static method utime : void ($atime : long, $mtime : long, $file : string) {
    
    my $utimbuf = (Sys::Time::Utimbuf)undef;
    
    if ($atime < 0 && $mtime < 0) {
      # Set current time
    }
    else {
      $utimbuf = Sys::Time::Utimbuf->new;
      $utimbuf->set_actime($atime);
      $utimbuf->set_modtime($mtime);
    }
    
    Sys::Time->utime($file, $utimbuf);
  }
  
  static method signal : Sys::Signal::Handler ($signal_number : int, $handler_name : string) {
    
    my $handler = (Sys::Signal::Handler)undef;
    
    if ($handler_name eq "IGNORE") {
      $handler = Sys::Signal->SIG_IGN;
    }
    elsif ($handler_name eq "DEFAULT") {
      $handler = Sys::Signal->SIG_DFL;
    }
    else {
      die "\$handler_name \"$handler_name\" is not available.";
    }
    
    my $old_handler = Sys::Signal->signal($signal_number, $handler);
    
    return $old_handler;
  }
  
  static method kill : void ($signal_number : int, $process_id : int) {
    if (Sys::OS->is_windows) {
      if ($process_id == Sys->process_id) {
        Sys::Signal->raise($signal_number);
      }
      else {
        die "\$process_id must be equal to Sys->process_id in Windows.";
      }
    }
    else {
      Sys::Signal->kill($process_id, $signal_number);
    }
  }
  
  static method alarm : int ($seconds : int) {
    Sys::Signal->alarm($seconds);
  }
  
  static method fork : int () {
    
    my $child_process_id = Sys::Process->fork;
    
    return $child_process_id;
  }
  
  static method getpriority : int ($which : int, $who : int) {
    
    my $priority = Sys::Process->getpriority($which, $who);
    
    return $priority;
  }
  
  static method setpriority : void ($which : int, $who : int, $priority : int) {
    Sys::Process->setpriority($which, $who, $priority);
  }
  
  static method sleep : int ($seconds : int) {
    
    my $slept_number = Sys::Process->sleep($seconds);
    
    return $slept_number;
  }
  
  static method wait : int ($wstatus_ref : int*) {
    
    my $process_id = Sys::Process->wait($wstatus_ref);
    
    return $process_id;
  }
  
  static method waitpid : int ($process_id : int, $options : int, $wstatus_ref : int*) {
    
    my $process_id_status_changed = Sys::Process->waitpid($process_id, $wstatus_ref, $options);
    
    return $process_id_status_changed;
  }
  
  static method system : int ($command : string) {
    
    my $wstatus = Sys::Process->system($command);
    
    return $wstatus;
  }
  
  static method exit : void ($status : int) {
    
    Sys::Process->exit($status);
  }
  
  static method pipe : void ($read_fd_ref : int*, $write_fd_ref : int*) {
    
    my $pipe_fds = new int[2];
    
    if (Sys::OS->is_windows) {
      Sys::Process->_pipe($pipe_fds, 0, IO->O_BINARY);
    }
    else {
      Sys::Process->pipe($pipe_fds);
      
      Sys->fcntl($pipe_fds->[0], IO->F_SETFD, IO->FD_CLOEXEC);
      
      Sys->fcntl($pipe_fds->[1], IO->F_SETFD, IO->FD_CLOEXEC);
    }
    
    $$read_fd_ref = $pipe_fds->[0];
    
    $$write_fd_ref = $pipe_fds->[1];
  }
  
  static method getpgrp : int ($process_id : int) {
    
    my $process_group_id = Sys::Process->getpgid($process_id);
    
    return $process_group_id;
  }
  
  static method setpgrp : void ($process_id : int, $process_group_id : int) {
    
    Sys::Process->setpgid($process_id, $process_group_id);
  }
  
  static method process_id : int () {
    
    my $process_id = Sys::Process->getpid();
    
    return $process_id;
  }
  
  static method getppid : int () {
    
    my $parent_process_id = Sys::Process->getppid();
    
    return $parent_process_id;
  }
  
  static method exec : void ($program : string, $args : string[] = undef) {
    
    my $exec_args_list = StringList->new;
    
    $exec_args_list->push($program);
    
    if ($args) {
      for (my $i = 0; $i < @$args; $i++) {
        my $arg = $args->[$i];
        
        $exec_args_list->push($arg);
      }
    }
    
    my $execv_args = $exec_args_list->to_array;
    
    Sys::Process->execv($program, $execv_args);
  }
  
  static method real_user_id : int () {
    
    my $real_user_id = Sys::User->getuid;
    
    return $real_user_id;
  }
  
  static method effective_user_id : int () {
    
    my $effective_user_id = Sys::User->geteuid;
    
    return $effective_user_id;
  }
  
  static method real_group_id : int () {
    
    my $real_group_id = Sys::User->getgid;
    
    return $real_group_id;
  }
  
  static method effective_group_id : int () {
    
    my $effective_group_id = Sys::User->getegid();
    
    return $effective_group_id;
  }
  
  static method set_real_user_id : void ($uid : int) {
    Sys::User->setuid($uid);
  }
  
  static method set_effective_user_id : void ($euid : int) {
    Sys::User->seteuid($euid);
  }
  
  static method set_real_group_id : void ($real_group_id : int) {
    Sys::User->setgid($real_group_id);
  }
  
  static method set_effective_group_id : void ($effective_group_id : int) {
    Sys::User->setegid($effective_group_id);
  }
  
  static method setpwent : void () {
    Sys::User->setpwent;
  }
  
  static method endpwent : void () {
    Sys::User->endpwent;
  }
  
  static method getpwent : Sys::User::Passwd () {
    
    my $pwent = Sys::User->getpwent;
    
    return $pwent;
  }
  
  static method setgrent : void () {
    Sys::User->setgrent;
  }
  
  static method endgrent : void () {
    Sys::User->endgrent;
  }
  
  static method getgrent : Sys::User::Group () {
    
    my $grent = Sys::User->getgrent;
    
    return $grent;
  }
  
  static method getgroups : int[] () {
    
    my $group_ids_length = Sys::User->getgroups(0, undef);
    
    my $group_ids = new int [$group_ids_length];
    
    Sys::User->getgroups($group_ids_length, $group_ids);
    
    return $group_ids;
  }
  
  static method setgroups : void ($group_ids : int[]) {
    Sys::User->setgroups($group_ids);
  }
  
  static method getpwuid : Sys::User::Passwd ($id : int) {
    
    my $pwent = Sys::User->getpwuid($id);
    
    return $pwent;
  }
  
  static method getpwnam : Sys::User::Passwd ($name : string) {
    
    my $pwent = Sys::User->getpwnam($name);
    
    return $pwent;
  }
  
  static method getgrgid : Sys::User::Group ($id : int) {
    
    my $grent = Sys::User->getgrgid($id);
    
    return $grent;
  }
  
  static method getgrnam : Sys::User::Group ($name : string) {
    
    my $grent = Sys::User->getgrnam($name);
    
    return $grent;
  }
  
  static method srand : void ($seed : int) {
    Fn->set_seed($seed);
  }
  
  static method rand : double ($max : int = 1) {
    
    my $seed_initialized = Fn->seed_initialized;
    my $seed = 0;
    if ($seed_initialized) {
      $seed = Fn->get_seed;
    }
    else {
      $seed = &process_id * (int)&time;
    }
    
    my $random_number = Fn->rand(\$seed, $max);
    
    &srand($seed);
    
    return $random_number;
  }
  
}