diff --git a/mrbgems/mruby-io/mrblib/file.rb b/mrbgems/mruby-io/mrblib/file.rb index 25fc33bdf..9269ecc7b 100644 --- a/mrbgems/mruby-io/mrblib/file.rb +++ b/mrbgems/mruby-io/mrblib/file.rb @@ -79,59 +79,6 @@ class File < IO "<#{self.class}:#{@path}>" end - # - # call-seq: - # File.join(string, ...) -> string - # - # Returns a new string formed by joining the strings using the operating - # system's path separator (File::SEPARATOR). - # - # File.join("usr", "mail", "gumby") #=> "usr/mail/gumby" - # File.join("usr", "mail", "gumby") #=> "usr\\mail\\gumby" (on Windows) - # - def self.join(*names) - return "" if names.empty? - - names.map! do |name| - case name - when String - name - when Array - if names == name - raise ArgumentError, "recursive array" - end - join(*name) - else - raise TypeError, "no implicit conversion of #{name.class} into String" - end - end - - return names[0] if names.size == 1 - - if names[0][-1] == File::SEPARATOR - s = names[0][0..-2] - else - s = names[0].dup - end - - (1..names.size-2).each { |i| - t = names[i] - if t[0] == File::SEPARATOR and t[-1] == File::SEPARATOR - t = t[1..-2] - elsif t[0] == File::SEPARATOR - t = t[1..-1] - elsif t[-1] == File::SEPARATOR - t = t[0..-2] - end - s += File::SEPARATOR + t if t != "" - } - if names[-1][0] == File::SEPARATOR - s += File::SEPARATOR + names[-1][1..-1] - else - s += File::SEPARATOR + names[-1] - end - s - end # # call-seq: diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c index 25f60dbd9..c92976979 100644 --- a/mrbgems/mruby-io/src/file.c +++ b/mrbgems/mruby-io/src/file.c @@ -1036,6 +1036,138 @@ mrb_file_path(mrb_state *mrb, mrb_value klass) return filename; } +// Forward declaration for recursive join processing +static mrb_value mrb_file_join_process_args(mrb_state *mrb, const mrb_value *argv, mrb_int argc); + +static mrb_value +mrb_file_join_process_args(mrb_state *mrb, const mrb_value *argv, mrb_int argc) +{ + mrb_value result = mrb_ary_new_capa(mrb, argc); + + for (mrb_int i = 0; i < argc; i++) { + mrb_value arg = argv[i]; + + if (mrb_array_p(arg)) { + // Check for recursive arrays using mruby's built-in detection + if (MRB_RECURSIVE_UNARY_P(mrb, MRB_SYM(join), arg)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "recursive array"); + } + + // Recursively process the array + mrb_value nested = mrb_file_join_process_args(mrb, RARRAY_PTR(arg), RARRAY_LEN(arg)); + + // Append nested results to our result + mrb_int nested_len = RARRAY_LEN(nested); + for (mrb_int k = 0; k < nested_len; k++) { + mrb_ary_push(mrb, result, RARRAY_PTR(nested)[k]); + } + } + else { + // Convert to string (raises TypeError if not convertible) + mrb_ensure_string_type(mrb, arg); + mrb_ary_push(mrb, result, arg); + } + } + + return result; +} + +/* + * call-seq: + * File.join(string, ...) -> string + * + * Returns a new string formed by joining the strings using the operating + * system's path separator (File::SEPARATOR). + * + * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby" + * File.join("usr", "mail", "gumby") #=> "usr\\mail\\gumby" (on Windows) + */ +static mrb_value +mrb_file_join(mrb_state *mrb, mrb_value klass) +{ + const mrb_value *argv; + mrb_int argc; + + mrb_get_args(mrb, "*", &argv, &argc); + + // Handle empty case + if (argc == 0) { + return mrb_str_new_lit(mrb, ""); + } + + // Process arguments and flatten arrays + mrb_value names = mrb_file_join_process_args(mrb, argv, argc); + + mrb_int names_len = RARRAY_LEN(names); + if (names_len == 0) { + return mrb_str_new_lit(mrb, ""); + } + + // Handle single element case + if (names_len == 1) { + return RARRAY_PTR(names)[0]; + } + + // Start building the result + mrb_value first = RARRAY_PTR(names)[0]; + mrb_value result = mrb_str_dup(mrb, first); + + // Remove trailing separator from first component + const char *sep = FILE_SEPARATOR; + mrb_int sep_len = strlen(sep); + if (RSTRING_LEN(result) > 0 && + RSTRING_LEN(result) >= sep_len && + memcmp(RSTRING_PTR(result) + RSTRING_LEN(result) - sep_len, sep, sep_len) == 0) { + mrb_str_resize(mrb, result, RSTRING_LEN(result) - sep_len); + } + + // Process middle components + for (mrb_int i = 1; i < names_len - 1; i++) { + mrb_value component = RARRAY_PTR(names)[i]; + const char *comp_str = RSTRING_PTR(component); + mrb_int comp_len = RSTRING_LEN(component); + + // Skip empty components + if (comp_len == 0) continue; + + // Remove leading separator + if (comp_len >= sep_len && memcmp(comp_str, sep, sep_len) == 0) { + comp_str += sep_len; + comp_len -= sep_len; + } + + // Remove trailing separator + if (comp_len >= sep_len && memcmp(comp_str + comp_len - sep_len, sep, sep_len) == 0) { + comp_len -= sep_len; + } + + // Add separator and component if not empty + if (comp_len > 0) { + mrb_str_cat_cstr(mrb, result, sep); + mrb_str_cat(mrb, result, comp_str, comp_len); + } + } + + // Process last component + if (names_len > 1) { + mrb_value last = RARRAY_PTR(names)[names_len - 1]; + const char *last_str = RSTRING_PTR(last); + mrb_int last_len = RSTRING_LEN(last); + + // Remove leading separator from last component + if (last_len >= sep_len && memcmp(last_str, sep, sep_len) == 0) { + last_str += sep_len; + last_len -= sep_len; + } + + // Add separator and last component + mrb_str_cat_cstr(mrb, result, sep); + mrb_str_cat(mrb, result, last_str, last_len); + } + + return result; +} + void mrb_init_file(mrb_state *mrb) { @@ -1053,6 +1185,7 @@ mrb_init_file(mrb_state *mrb) mrb_define_class_method_id(mrb, file, MRB_SYM(dirname), mrb_file_dirname, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(basename), mrb_file_basename, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(extname), mrb_file_extname, MRB_ARGS_REQ(1)); + mrb_define_class_method_id(mrb, file, MRB_SYM(join), mrb_file_join, MRB_ARGS_ANY()); mrb_define_class_method_id(mrb, file, MRB_SYM(path), mrb_file_path, MRB_ARGS_REQ(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(realpath), mrb_file_realpath, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1)); mrb_define_class_method_id(mrb, file, MRB_SYM(absolute_path), mrb_file_absolute_path, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));