Skip to content

Commit 21ae96d

Browse files
committed
File.split with extension like as File.basename
1 parent edc132c commit 21ae96d

File tree

3 files changed

+79
-15
lines changed

3 files changed

+79
-15
lines changed

file.c

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4931,13 +4931,14 @@ enc_find_basename(const char *name, long *baselen, long *alllen, bool mb_enc, rb
49314931
root = name;
49324932
#endif
49334933

4934-
while (isdirsep(*name)) {
4934+
while (name < end && isdirsep(*name)) {
49354935
name++;
49364936
}
49374937

4938-
if (!*name) {
4938+
if (name == end) {
49394939
p = name - 1;
49404940
f = 1;
4941+
n = 1;
49414942
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
49424943
if (name != root) {
49434944
/* has slashes */
@@ -5048,9 +5049,7 @@ rb_file_s_basename(int argc, VALUE *argv, VALUE _)
50485049
f = n;
50495050
}
50505051
else {
5051-
const char *fp;
5052-
fp = StringValueCStr(fext);
5053-
if (!(f = rmext(p, f, n, fp, RSTRING_LEN(fext), enc))) {
5052+
if (!(f = rmext(p, f, n, RSTRING_PTR(fext), RSTRING_LEN(fext), enc))) {
50545053
f = n;
50555054
}
50565055
RB_GC_GUARD(fext);
@@ -5064,6 +5063,7 @@ rb_file_s_basename(int argc, VALUE *argv, VALUE _)
50645063
}
50655064

50665065
static VALUE rb_file_dirname_n(VALUE fname, int n);
5066+
static VALUE dirname_part(const char **namep, const char *end, bool mb_enc, rb_encoding *enc, int n);
50675067

50685068
/*
50695069
* call-seq:
@@ -5103,15 +5103,22 @@ rb_file_dirname(VALUE fname)
51035103
static VALUE
51045104
rb_file_dirname_n(VALUE fname, int n)
51055105
{
5106-
const char *name, *root, *p, *end;
5107-
VALUE dirname;
5106+
const char *name;
51085107

51095108
if (n < 0) rb_raise(rb_eArgError, "negative level: %d", n);
51105109
CheckPath(fname, name);
5111-
end = name + RSTRING_LEN(fname);
51125110

5113-
bool mb_enc = !rb_str_enc_fastpath(fname);
5114-
rb_encoding *enc = rb_str_enc_get(fname);
5111+
int encidx = ENCODING_GET_INLINED(fname);
5112+
bool mb_enc = !rb_str_encindex_fastpath(encidx);
5113+
rb_encoding *enc = rb_enc_from_index(encidx);
5114+
return dirname_part(&name, name + RSTRING_LEN(fname), mb_enc, enc, n);
5115+
}
5116+
5117+
static VALUE
5118+
dirname_part(const char **namep, const char *end, bool mb_enc, rb_encoding *enc, int n)
5119+
{
5120+
VALUE dirname;
5121+
const char *name = *namep, *root, *p;
51155122

51165123
root = skiproot(name, end);
51175124
#ifdef DOSISH_UNC
@@ -5135,6 +5142,7 @@ rb_file_dirname_n(VALUE fname, int n)
51355142
}
51365143
}
51375144

5145+
*namep = p == root && p > name ? p - 1 : p;
51385146
if (p == name) {
51395147
return rb_enc_str_new(".", 1, enc);
51405148
}
@@ -5314,10 +5322,43 @@ rb_file_s_path(VALUE klass, VALUE fname)
53145322
*/
53155323

53165324
static VALUE
5317-
rb_file_s_split(VALUE klass, VALUE path)
5325+
rb_file_s_split(int argc, VALUE *argv, VALUE klass)
53185326
{
5319-
FilePathStringValue(path); /* get rid of converting twice */
5320-
return rb_assoc_new(rb_file_dirname(path), rb_file_s_basename(1,&path,Qundef));
5327+
const char *name, *suffix = 0;
5328+
long sufflen = 0;
5329+
VALUE path, dir, fext = Qnil;
5330+
rb_encoding *enc;
5331+
argc = rb_check_arity(argc, 1, 2);
5332+
path = argv[0];
5333+
CheckPath(path, name);
5334+
if (argc > 1 && !NIL_P(fext = argv[1])) {
5335+
suffix = StringValueCStr(fext);
5336+
sufflen = RSTRING_LEN(fext);
5337+
if (!(enc = rb_enc_compatible(path, fext))) {
5338+
enc = rb_str_enc_get(path);
5339+
suffix = 0;
5340+
}
5341+
}
5342+
else {
5343+
enc = rb_str_enc_get(path);
5344+
}
5345+
const char *base = name;
5346+
const char *end = name + RSTRING_LEN(path);
5347+
bool mb_enc = !rb_str_encindex_fastpath(rb_enc_to_index(enc));
5348+
dir = dirname_part(&base, end, mb_enc, enc, 1);
5349+
long f = 0, n = (long)(end - base);
5350+
base = enc_find_basename(base, &f, &n, mb_enc, enc);
5351+
if (!suffix) {
5352+
return rb_assoc_new(dir, rb_enc_str_new(base, n, enc));
5353+
}
5354+
else {
5355+
long b = rmext(base, f, n, suffix, sufflen, enc);
5356+
if (!b) b = n;
5357+
RB_GC_GUARD(fext);
5358+
VALUE basename = rb_enc_str_new(base, b, enc);
5359+
VALUE ext = rb_enc_str_new(base + f, n - b, enc);
5360+
return rb_ary_new_from_args(3, dir, basename, ext);
5361+
}
53215362
}
53225363

53235364
static VALUE rb_file_join_ary(VALUE ary);
@@ -7721,7 +7762,7 @@ Init_File(void)
77217762
rb_define_const(rb_cFile, "Separator", separator);
77227763
/* separates directory parts in path */
77237764
rb_define_const(rb_cFile, "SEPARATOR", separator);
7724-
rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, 1);
7765+
rb_define_singleton_method(rb_cFile, "split", rb_file_s_split, -1);
77257766
rb_define_singleton_method(rb_cFile, "join", rb_file_s_join, -1);
77267767

77277768
#ifdef DOSISH

spec/ruby/core/file/split_spec.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@
4545

4646
it "raises an ArgumentError when not passed a single argument" do
4747
-> { File.split }.should raise_error(ArgumentError)
48-
-> { File.split('string', 'another string') }.should raise_error(ArgumentError)
48+
end
49+
50+
ruby_version_is "4.1" do
51+
it "splits the string into directory, basename and extension when the extension argument is given" do
52+
File.split("/foo/bar/baz.c", "").should == ["/foo/bar", "baz.c", ""]
53+
File.split("/foo/bar/baz.c", ".c").should == ["/foo/bar", "baz", ".c"]
54+
File.split("/foo/bar/baz.c", ".*").should == ["/foo/bar", "baz", ".c"]
55+
end
4956
end
5057

5158
it "raises a TypeError if the argument is not a String type" do

test/ruby/test_file_exhaustive.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,22 @@ def test_split
13561356
end
13571357
end
13581358

1359+
def test_split_ext
1360+
[
1361+
regular_file, utf8_file,
1362+
"/", "//", "dir///base", "dir///base/", "dir///base.c/",
1363+
"/.dot", "/home/.config/",
1364+
].each do |file|
1365+
["", ".txt", ".*"].each do |ext|
1366+
mesg = ->{"(#{file.dump}, #{ext.dump})"}
1367+
d, b, e = File.split(file, ext)
1368+
assert_equal(File.dirname(file), d, mesg)
1369+
assert_equal(File.basename(file, ext), b, mesg)
1370+
assert_equal(File.basename(file), b + e, mesg)
1371+
end
1372+
end
1373+
end
1374+
13591375
def test_join
13601376
s = "foo" + File::SEPARATOR + "bar" + File::SEPARATOR + "baz"
13611377
assert_equal(s, File.join("foo", "bar", "baz"))

0 commit comments

Comments
 (0)