Add level argument to File.dirname

from Ruby-3.1
https://bugs.ruby-lang.org/issues/12194
This commit is contained in:
dearblue
2025-01-02 18:10:14 +09:00
parent ef11cd94e7
commit 5c91014530
2 changed files with 37 additions and 9 deletions
+17 -9
View File
@@ -169,15 +169,18 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
#define NEXT_DIRSEP(p) for (; *(p) != '\0' && !DIRSEP_P(*(p)); (p)++)
static const char*
scan_dirname(const char *path)
scan_dirname(const char *path, mrb_int level)
{
const char *p = path + strlen(path);
if (level < 1) return p;
for (; p > path && DIRSEP_P(p[-1]); p--)
;
for (; p > path && !DIRSEP_P(p[-1]); p--)
;
for (; p > path && DIRSEP_P(p[-1]); p--)
;
for (; level > 0; level--) {
for (; p > path && !DIRSEP_P(p[-1]); p--)
;
for (; p > path && DIRSEP_P(p[-1]); p--)
;
}
return p > path ? p : path;
}
@@ -185,7 +188,12 @@ static mrb_value
mrb_file_dirname(mrb_state *mrb, mrb_value klass)
{
const char *path;
mrb_get_args(mrb, "z", &path);
mrb_int level = 1;
mrb_get_args(mrb, "z|i", &path, &level);
if (level < 0) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level: %i", level);
}
const char *p = path;
#ifdef _WIN32
@@ -201,7 +209,7 @@ mrb_file_dirname(mrb_state *mrb, mrb_value klass)
}
else {
NEXT_DIRSEP(p);
p = scan_dirname(p);
p = scan_dirname(p, level);
}
return mrb_str_new(mrb, path, p - path);
}
@@ -209,7 +217,7 @@ mrb_file_dirname(mrb_state *mrb, mrb_value klass)
p += 2;
const char *o = p;
SKIP_DIRSEP(p);
p = scan_dirname(p);
p = scan_dirname(p, level);
mrb_value s = mrb_str_new(mrb, path, p - path);
if (p == o) {
mrb_str_cat_lit(mrb, s, ".");
@@ -221,7 +229,7 @@ mrb_file_dirname(mrb_state *mrb, mrb_value klass)
if (p > path) {
path = p - 1; /* if consecutive, point to the trailing slash */
}
p = scan_dirname(p);
p = scan_dirname(p, level);
return (p == path) ? mrb_str_new_lit(mrb, ".") : mrb_str_new(mrb, path, p - path);
}
+20
View File
@@ -3,6 +3,15 @@
MRubyIOTestUtil.io_test_setup
def assert_dirname_with_level(path, results)
assert('dirname with level') do
assert_raise(ArgumentError) { File.dirname path, -1 }
results.each do |level, expect|
assert_equal expect, File.dirname(path, level)
end
end
end
assert('File.class', '15.2.21') do
assert_equal Class, File.class
end
@@ -53,6 +62,17 @@ assert('File.dirname') do
assert_equal '/a/b', File.dirname('/a/b//c//')
end
assert('File.dirname (with level)') do
assert_dirname_with_level '', { 0 => '.', 1 => '.', 2 => '.' }
assert_dirname_with_level 'a', { 0 => 'a', 1 => '.', 2 => '.' }
assert_dirname_with_level '/a', { 0 => '/a', 1 => '/', 2 => '/' }
assert_dirname_with_level '/a/', { 0 => '/a/', 1 => '/', 2 => '/' }
assert_dirname_with_level 'a/b', { 0 => 'a/b', 1 => 'a', 2 => '.' }
assert_dirname_with_level 'a/b/', { 0 => 'a/b/', 1 => 'a', 2 => '.' }
assert_dirname_with_level 'a/b/c', { 0 => 'a/b/c', 1 => 'a/b', 2 => 'a' }
assert_dirname_with_level 'a/b/c/d', { 0 => 'a/b/c/d', 1 => 'a/b/c', 2 => 'a/b' }
end
unless MRubyIOTestUtil.win?
assert('File.dirname (not Windows)') do
assert_equal '/a', File.dirname('//a//b/')