aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJörg Sommrey <28217714+jo-37@users.noreply.github.com>2021-05-10 14:38:40 +0200
committerJörg Sommrey <28217714+jo-37@users.noreply.github.com>2021-05-12 20:53:46 +0200
commit2c28471a3f7edab1f863f7ec22404b8f7ac2eb67 (patch)
treead8de5e7921bbd08671b2541b5d869213a93205a
parentbef7b6d2e9c85f53aa646ab4d59bb52214406536 (diff)
downloadperlweeklychallenge-club-2c28471a3f7edab1f863f7ec22404b8f7ac2eb67.tar.gz
perlweeklychallenge-club-2c28471a3f7edab1f863f7ec22404b8f7ac2eb67.tar.bz2
perlweeklychallenge-club-2c28471a3f7edab1f863f7ec22404b8f7ac2eb67.zip
Solution to task 1
-rwxr-xr-xchallenge-112/jo-37/perl/ch-1.pl98
1 files changed, 98 insertions, 0 deletions
diff --git a/challenge-112/jo-37/perl/ch-1.pl b/challenge-112/jo-37/perl/ch-1.pl
new file mode 100755
index 0000000000..a85ec6bc68
--- /dev/null
+++ b/challenge-112/jo-37/perl/ch-1.pl
@@ -0,0 +1,98 @@
+#!/usr/bin/perl -s
+
+use v5.16;
+use Test2::V0;
+
+our ($tests, $examples);
+
+run_tests() if $tests || $examples; # does not return
+
+die <<EOS unless @ARGV;
+usage: $0 [-examples] [-tests] [path...]
+
+-examples
+ run the examples from the challenge
+
+-tests
+ run some tests
+
+path...
+ path name(s)
+
+EOS
+
+
+### Input and Output
+
+say c_path($_) // "path not valid: $_" for @ARGV;
+
+
+### Implementation
+
+# - Remove leading slashes or give up.
+# - Split the path into parts delimited by (possibly multiple) slashes.
+# - Reverse the parts.
+#
+# - Increment the skip count if the current part is '..' and suppress
+# the current part.
+# - Suppress the current part if it is '.' or the skip count is positive
+# (decrementing it).
+# - Pass the current part otherwise.
+#
+# - Reverse the parts.
+# - Give up if the skip count is still positive after all parts have
+# been processed.
+# - Prepend an empty part to produce a leading slash.
+# - Provide an empty part if the given path resolves to the
+# root path i.e. it has no parts.
+# - Join the parts with slashes.
+sub c_path {
+ local $_ = shift;
+ s{^/+}{} or return;
+
+ my $skip;
+ my @part = reverse grep {
+ /^\.\.\z/ ? !++$skip : /^\.\z/ || $skip && $skip-- ? 0 : 1;
+ } reverse split m{/+};
+
+ $skip ? undef : join '/', '', @part, ('') x !@part;
+}
+
+
+### Examples and tests
+
+sub run_tests {
+ SKIP: {
+ skip "examples" unless $examples;
+
+ is c_path('/a/'), '/a', 'example 1';
+ is c_path('/a/b//c/'), '/a/b/c', 'example 2';
+ is c_path("/a/b/c/../.."), '/a', 'example 3';
+ }
+
+ SKIP: {
+ skip "tests" unless $tests;
+
+ is c_path('a'), U(), 'not an absolute path';
+ is c_path('/a/../..'), U(), 'ascend beyond root';
+ is c_path('/a/b/../././../c'), '/c', 'mixed dots';
+ is c_path('/../'), U(), 'ascend from root';
+ is c_path('/0/../1'), '/1', 'accept "0"';
+ is c_path('/a/.//b/'), '/a/b', 'skip current dir';
+ is c_path('/.a/.b/'), '/.a/.b', 'dot names';
+ is c_path('/a/.../b/'), '/a/.../b', 'three dots';
+ is c_path('/'), '/', 'root';
+ is c_path('///'), '/', 'repeated root';
+ is c_path("\n/a"), U(), 'leading newline part';
+ is c_path("/a/\n/b"), "/a/\n/b", 'embedded newline part';
+ is c_path("/a/\n"), "/a/\n", 'trailing newline part';
+ is c_path("/a/b\nc/d"), "/a/b\nc/d", 'embedded newline';
+ is c_path("/a/\n./b"), "/a/\n./b", 'newline before dot';
+ is c_path("/a/.\n/b"), "/a/.\n/b", 'newline after dot';
+ is c_path("/a/\n../b"), "/a/\n../b", 'newline before two dots';
+ is c_path("/a/..\n/b"), "/a/..\n/b", 'newline after two dots';
+ }
+
+ done_testing;
+ exit;
+}