diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index eb0374955..57d896d0b 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -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); } diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb index 9ce3e4949..2d508d427 100644 --- a/mrbgems/mruby-io/test/file.rb +++ b/mrbgems/mruby-io/test/file.rb @@ -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/')