DEV Community

 

First release of SPVM::File::Spec - complex regular expressions, file tests, SPVM::Cwd, inheritance

First release of SPVM::File::Spec, a port of Perl's File::Spec to SPVM. The repository is SPVM::File::Spec.

What is File::Spec?

File::Spec is a Perl module to handle file paths in an OS-independent manner, allowing Linux, Mac, and Windows to have similar descriptions of file paths.

File::Spec implementation difficulties.

Numerous regular expressions

One of the difficulties of implementing File::Spec is that it uses many regular expressions to handle file paths.

Therefore, in porting File::Spec to SPVM, it was necessary to be able to use regular expressions.

I ported Google RE2, a regular expression library, to SPVM as Resource::Re2, and created SPVM::Regex, a wrapper for it.

This allows regular expressions to be used.

See some of the complex regular expressions in SPVM::File::Spec. The handling of file paths, especially on Windows, is complex. This is part of the implementation of a method called canonnpath in SPVM::File::Spec::Win32 to make a path a unique expression.

private method _canon_cat : string ($parts : string[]) {
    my $first = $parts->[0];

    my $volume = (string)undef;

    # drive letter - (C:)
    my $re_drive = Regex->new("\A([A-Za-z]:)([\\\\/]?)") ;
    $first = $re_drive->replace($first, "");
    if ($re_drive->replaced_count > 0) {
      $volume = Fn->ucfirst($re_drive->cap1);
      if (length $re_drive->cap2) {
        $volume . = "\\";
      }
    }
    else {
      my $re_unc = Regex->new("\A(? :\\\\\\\\|//)([^\\\\/]+)(? :[\\\\/]([^\\\\/]+))? [\\\\/]?" , "s");
      $first = $re_unc->replace($first, "");

      # UNC volume (\192.168.201.101)
      if ($re_unc->replaced_count > 0) {
        $volume = "\\\\" . $re_unc->cap1;
        if ($re_unc->cap2) {
          $volume . = "\\" . $re_unc->cap2;
        }
        $volume . = "\\" ;
      }
      else {
        my $re_root = Regex->new("\A[\\\\/]");
        $first = $re_root->replace($first, "");

        # root dir (\foo)
        if ($re_root->replaced_count > 0) {
          $volume = "\foo";
        }
        else {
          $volume = "";
        }
      }
    }

    $parts->[0] = $first;

    my $path = Fn->join("\", $parts);

    # /+ to \
    $path = Regex->new("/+")->replace_g($path, "\");

    # /+ to \
    $path = Regex->new("\\\\+")->replace_g($path, "\\");

    # xx\. \. \yy --> xx\yy.
    $path = Regex->new("(? :(? :\A|\\\\)\. (? :\\\\\.) *(? :\\\\|\z))+")->replace_g($path, "\");

    # xx\yy. \zz --> xx\z.
    my $parts_list = StringList->new;
    $parts = Fn->split("\\", $path);
    for (my $i = 0; $i < @$parts; $i++) {
      my $part = $parts->[$i];
      if ($part eq "...") {
        if ($parts_list->length > 0) {
          my $before_part = $parts_list->get($parts_list->length - 1);
          unless ($before_part eq "...") {
            $parts_list->pop;
            next;
          }
        }
      }
      $parts_list->push($part);
    }
    $parts = $parts_list->to_array;
    $path = Fn->join("\\", $parts);

    # \xx --> xx
    $path = Regex->new("\A\\\\")->replace_g($path, "");

    # xx --> xx
    $path = Regex->new("\\\\\z")->replace_g($path, "");

    if (Regex->new("\\\\\z")->match($volume)) {
      # <vol>\. --> <vol>\.
      $path = Regex->new("\A\. \. (? :\\\\\. \.)\.) *(? :\\\\|\z)")->replace($path, "");

      # \HOSTHCOSTHCARE --> \HOSTHCARE
      my $re = Regex->new("\A(\\\\\\\\. *)\\\\\z", "s");
      if (!length $path && $re->match($volume)) {
        $path = $re->cap1;
        return $path;
      }
    }

    if (length $path || length $volume) {
      $path = $volume . $path;
    }
    else {
      $path = "." ;
    }

    return $path;
  }
Enter fullscreen mode Exit fullscreen mode

File Test Operators

File::Spec checks whether a temporary directory is writable in order to locate it. For this reason, file tests must be implemented.

I have implemented the SPVM::Sys::FileTest module for file testing.

  method _tmpdir : string ($dirlist : string[]) {
    my $interface = (File::Spec::Interface)$self;

    my $tmpdir = (string)undef;
    for my $dir (@$dirlist) {
      if ($dir && Sys::FileTest->d($dir) && Sys::FileTest->w($dir)){
        $tmpdir = $dir;
        last;
      }
    }

    unless ($tmpdir) {
      $tmpdir = $interface->curdir;
    }

    $tmpdir = $interface->canonpath($tmpdir);

    if (! $interface->file_name_is_absolute($tmpdir)) {
      $tmpdir = $interface->rel2abs($tmpdir);
      if (! $interface->file_name_is_absolute($tmpdir)) {
        die "A temporary directory must be converted to an absolute path";
      }
    }

    return $tmpdir;
  }
Enter fullscreen mode Exit fullscreen mode

Current directory

File::Spec needs to be able to handle the current directory for absolute and relative path conversion.

For this purpose I have implemented SPVM::Cwd.

The following is an implementation of the rel2abs method that converts a relative path to an absolute path.

  method rel2abs : string ($path : string, $base = undef : string) {
    my $interface = (File::Spec::Interface)$self;

    # Clean up $path
    if (! $interface->file_name_is_absolute($path)) {
      # Figure out the effective $base and clean it up.
      If (! $base || $base eq "") {
        $base = Cwd->getcwd;
      }
      elsif (! $interface->file_name_is_absolute($base)) {
        $base = $interface->rel2abs($base) ;
      }
      else {
        $base = $interface->canonpath($base);
      }
      # Glom them together
      $path = $interface->catdir([$base, $path]);
    }

    my $rel2abs = $interface->canonpath($path);

    return $rel2abs;
  }
Enter fullscreen mode Exit fullscreen mode

Inheritance

Since File::Spec is implemented using inheritance, SPVM also supports inheritance. There are subtle differences between statically and dynamically typed languages in terms of expressive feasibility, and SPVM is designed to be as close as possible to Perl's inheritance.

class File::Spec::Unix extends File::Spec {

}
Enter fullscreen mode Exit fullscreen mode

Immediate Goals

In February 2023, our goal is to port the modules that handle file paths to SPVM.

Porting of File::Copy, FindBin, File::Path, File::Find, File::Temp, etc. to SPVM will be initiated.

Translated with www.DeepL.com/Translator (free version)

Top comments (0)

50 CLI Tools You Can't Live Without

The top 50 must-have CLI tools, including some scripts to help you automate the installation and updating of these tools on various systems/distros.