ruby-changes:50640
From: nagachika <ko1@a...>
Date: Sun, 18 Mar 2018 12:57:44 +0900 (JST)
Subject: [ruby-changes:50640] nagachika:r62801 (ruby_2_4): revert r62797, r62784. [Bug #13863]
nagachika 2018-03-18 12:57:37 +0900 (Sun, 18 Mar 2018) New Revision: 62801 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=62801 Log: revert r62797, r62784. [Bug #13863] Modified directories: branches/ruby_2_4/ Modified files: branches/ruby_2_4/dir.c branches/ruby_2_4/file.c branches/ruby_2_4/internal.h branches/ruby_2_4/load.c branches/ruby_2_4/ruby.c branches/ruby_2_4/test/ruby/test_rubyoptions.rb branches/ruby_2_4/util.c branches/ruby_2_4/version.h branches/ruby_2_4/win32/dir.h branches/ruby_2_4/win32/win32.c Index: ruby_2_4/ruby.c =================================================================== --- ruby_2_4/ruby.c (revision 62800) +++ ruby_2_4/ruby.c (revision 62801) @@ -175,10 +175,8 @@ cmdline_options_init(ruby_cmdline_option https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L175 return opt; } -static NODE *load_file(VALUE parser, VALUE fname, VALUE f, int script, - ruby_cmdline_options_t *opt); -static VALUE open_load_file(VALUE fname_v, int *xflag); -static void forbid_setid(const char *, const ruby_cmdline_options_t *); +static NODE *load_file(VALUE, VALUE, int, ruby_cmdline_options_t *); +static void forbid_setid(const char *, ruby_cmdline_options_t *); #define forbid_setid(s) forbid_setid((s), opt) static struct { @@ -425,8 +423,6 @@ str_conv_enc(VALUE str, rb_encoding *fro https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L423 ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE, Qnil); } -#else -# define str_conv_enc(str, from, to) (str) #endif void ruby_init_loadpath_safe(int safe_level); @@ -1054,7 +1050,6 @@ proc_options(long argc, char **argv, rub https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1050 case 'x': if (envopt) goto noenvopt; - forbid_setid("-x"); opt->xflag = TRUE; s++; if (*s && chdir(s) < 0) { @@ -1444,7 +1439,6 @@ process_options(int argc, char **argv, r https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1439 { NODE *tree = 0; VALUE parser; - VALUE script_name; const rb_iseq_t *iseq; rb_encoding *enc, *lenc; #if UTF8_PATH @@ -1520,9 +1514,6 @@ process_options(int argc, char **argv, r https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1514 argc--; argv++; } - if (opt->script[0] == '-' && !opt->script[1]) { - forbid_setid("program input from stdin"); - } } opt->script_name = rb_str_new_cstr(opt->script); @@ -1569,17 +1560,9 @@ process_options(int argc, char **argv, r https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1560 ienc = enc; #endif } - script_name = opt->script_name; - rb_enc_associate(opt->script_name, - IF_UTF8_PATH(uenc = rb_utf8_encoding(), lenc)); -#if UTF8_PATH - if (uenc != lenc) { - opt->script_name = str_conv_enc(opt->script_name, uenc, lenc); - opt->script = RSTRING_PTR(opt->script_name); - } -#endif + rb_enc_associate(opt->script_name, lenc); rb_obj_freeze(opt->script_name); - if (IF_UTF8_PATH(uenc != lenc, 1)) { + if (IF_UTF8_PATH((uenc = rb_utf8_encoding()) != lenc, 1)) { long i; VALUE load_path = GET_VM()->load_path; const ID id_initial_load_path_mark = INITIAL_LOAD_PATH_MARK; @@ -1615,6 +1598,12 @@ process_options(int argc, char **argv, r https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1598 rb_funcallv(rb_cISeq, rb_intern_const("compile_option="), 1, &option); #undef SET_COMPILE_OPTION } +#if UTF8_PATH + if (uenc != lenc) { + opt->script_name = str_conv_enc(opt->script_name, uenc, lenc); + opt->script = RSTRING_PTR(opt->script_name); + } +#endif ruby_set_argv(argc, argv); process_sflag(&opt->sflag); @@ -1652,11 +1641,13 @@ process_options(int argc, char **argv, r https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1641 tree = rb_parser_compile_string(parser, opt->script, opt->e_script, 1); } else { - VALUE f; + if (opt->script[0] == '-' && !opt->script[1]) { + forbid_setid("program input from stdin"); + } + base_block = toplevel_context(toplevel_binding); rb_parser_set_context(parser, base_block, TRUE); - f = open_load_file(script_name, &opt->xflag); - tree = load_file(parser, opt->script_name, f, 1, opt); + tree = load_file(parser, opt->script_name, 1, opt); } ruby_set_script_name(opt->script_name); if (dump & DUMP_BIT(yydebug)) { @@ -1713,7 +1704,7 @@ process_options(int argc, char **argv, r https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1704 { VALUE path = Qnil; if (!opt->e_script && strcmp(opt->script, "-")) { - path = rb_realpath_internal(Qnil, script_name, 1); + path = rb_realpath_internal(Qnil, opt->script_name, 1); } base_block = toplevel_context(toplevel_binding); iseq = rb_iseq_new_main(tree, opt->script_name, path, vm_block_iseq(base_block)); @@ -1752,6 +1743,7 @@ struct load_file_arg { https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1743 VALUE parser; VALUE fname; int script; + int xflag; ruby_cmdline_options_t *opt; VALUE f; }; @@ -1769,6 +1761,7 @@ load_file_internal(VALUE argp_v) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1761 NODE *tree = 0; rb_encoding *enc; ID set_encoding; + int xflag = argp->xflag; argp->script = 0; CONST_ID(set_encoding, "set_encoding"); @@ -1784,9 +1777,11 @@ load_file_internal(VALUE argp_v) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1777 enc = rb_ascii8bit_encoding(); rb_funcall(f, set_encoding, 1, rb_enc_from_encoding(enc)); - if (opt->xflag) { + if (xflag || opt->xflag) { line_start--; search_shebang: + forbid_setid("-x"); + opt->xflag = FALSE; while (!NIL_P(line = rb_io_gets(f))) { line_start++; RSTRING_GETMEM(line, str, len); @@ -1879,8 +1874,7 @@ load_file_internal(VALUE argp_v) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1874 static VALUE open_load_file(VALUE fname_v, int *xflag) { - const char *fname = (fname_v = rb_str_encode_ospath(fname_v), - StringValueCStr(fname_v)); + const char *fname = StringValueCStr(fname_v); long flen = RSTRING_LEN(fname_v); VALUE f; int e; @@ -1981,14 +1975,15 @@ restore_load_file(VALUE arg) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1975 } static NODE * -load_file(VALUE parser, VALUE fname, VALUE f, int script, ruby_cmdline_options_t *opt) +load_file(VALUE parser, VALUE fname, int script, ruby_cmdline_options_t *opt) { struct load_file_arg arg; arg.parser = parser; arg.fname = fname; arg.script = script; arg.opt = opt; - arg.f = f; + arg.xflag = 0; + arg.f = open_load_file(rb_str_encode_ospath(fname), &arg.xflag); return (NODE *)rb_ensure(load_file_internal, (VALUE)&arg, restore_load_file, (VALUE)&arg); } @@ -2003,15 +1998,17 @@ rb_load_file(const char *fname) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L1998 void * rb_load_file_str(VALUE fname_v) { - return rb_parser_load_file(rb_parser_new(), fname_v); + ruby_cmdline_options_t opt; + + return load_file(rb_parser_new(), fname_v, 0, cmdline_options_init(&opt)); } void * rb_parser_load_file(VALUE parser, VALUE fname_v) { ruby_cmdline_options_t opt; - VALUE f = open_load_file(fname_v, &cmdline_options_init(&opt)->xflag); - return load_file(parser, fname_v, f, 0, &opt); + + return load_file(parser, fname_v, 0, cmdline_options_init(&opt)); } /* @@ -2120,7 +2117,7 @@ init_ids(ruby_cmdline_options_t *opt) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/ruby.c#L2117 #undef forbid_setid static void -forbid_setid(const char *s, const ruby_cmdline_options_t *opt) +forbid_setid(const char *s, ruby_cmdline_options_t *opt) { if (opt->setids & 1) rb_raise(rb_eSecurityError, "no %s allowed while running setuid", s); Index: ruby_2_4/file.c =================================================================== --- ruby_2_4/file.c (revision 62800) +++ ruby_2_4/file.c (revision 62801) @@ -126,14 +126,6 @@ int flock(int, int); https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L126 #define STAT(p, s) stat((p), (s)) #endif -#if defined _WIN32 || defined __APPLE__ -# define USE_OSPATH 1 -# define TO_OSPATH(str) rb_str_encode_ospath(str) -#else -# define USE_OSPATH 0 -# define TO_OSPATH(str) (str) -#endif - VALUE rb_cFile; VALUE rb_mFileTest; VALUE rb_cStat; @@ -230,7 +222,7 @@ rb_get_path(VALUE obj) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L222 VALUE rb_str_encode_ospath(VALUE path) { -#if USE_OSPATH +#if defined _WIN32 || defined __APPLE__ int encidx = ENCODING_GET(path); #ifdef _WIN32 if (encidx == ENCINDEX_ASCII) { @@ -3841,10 +3833,11 @@ realpath_rec(long *prefixlenp, VALUE *re https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L3833 else { struct stat sbuf; int ret; + VALUE testpath2 = rb_str_encode_ospath(testpath); #ifdef __native_client__ - ret = stat(RSTRING_PTR(testpath), &sbuf); + ret = stat(RSTRING_PTR(testpath2), &sbuf); #else - ret = lstat(RSTRING_PTR(testpath), &sbuf); + ret = lstat(RSTRING_PTR(testpath2), &sbuf); #endif if (ret == -1) { int e = errno; @@ -3916,12 +3909,9 @@ rb_check_realpath_internal(VALUE basedir https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L3909 if (!NIL_P(basedir)) { FilePathValue(basedir); - basedir = TO_OSPATH(rb_str_dup_frozen(basedir)); + basedir = rb_str_dup_frozen(basedir); } - enc = rb_enc_get(unresolved_path); - origenc = enc; - unresolved_path = TO_OSPATH(unresolved_path); RSTRING_GETMEM(unresolved_path, ptr, len); path_names = skipprefixroot(ptr, ptr + len, rb_enc_get(unresolved_path)); if (ptr != path_names) { @@ -3938,7 +3928,7 @@ rb_check_realpath_internal(VALUE basedir https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L3928 } } - curdir = rb_dir_getwd_ospath(); + curdir = rb_dir_getwd(); RSTRING_GETMEM(curdir, ptr, len); curdir_names = skipprefixroot(ptr, ptr + len, rb_enc_get(curdir)); resolved = rb_str_subseq(curdir, 0, curdir_names - ptr); @@ -3946,6 +3936,7 @@ rb_check_realpath_internal(VALUE basedir https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L3936 root_found: RSTRING_GETMEM(resolved, prefixptr, prefixlen); pend = prefixptr + prefixlen; + enc = rb_enc_get(resolved); ptr = chompdirsep(prefixptr, pend, enc); if (ptr < pend) { prefixlen = ++ptr - prefixptr; @@ -3960,6 +3951,7 @@ rb_check_realpath_internal(VALUE basedir https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L3951 } #endif + origenc = enc; switch (rb_enc_to_index(enc)) { case ENCINDEX_ASCII: case ENCINDEX_US_ASCII: @@ -3978,14 +3970,8 @@ rb_check_realpath_internal(VALUE basedir https://github.com/ruby/ruby/blob/trunk/ruby_2_4/file.c#L3970 if (realpath_rec(&prefixlen, &resolved, path_names, loopcheck, mode, 1)) return Qnil; - if (origenc != rb_enc_get(resolved)) { - if (rb_enc_str_asciionly_p(resolved)) { - rb_enc_associate(resolved, origenc); - } - else { - resolved = rb_str_conv_enc(resolved, NULL, origenc); - } - } + if (origenc != enc && rb_enc_str_asciionly_p(resolved)) + rb_enc_associate(resolved, origenc); OBJ_TAINT(resolved); return resolved; Index: ruby_2_4/version.h =================================================================== --- ruby_2_4/version.h (revision 62800) +++ ruby_2_4/version.h (revision 62801) @@ -1,6 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/ruby_2_4/version.h#L1 #define RUBY_VERSION "2.4.4" #define RUBY_RELEASE_DATE "2018-03-17" -#define RUBY_PATCHLEVEL 261 +#define RUBY_PATCHLEVEL 259 #define RUBY_RELEASE_YEAR 2018 #define RUBY_RELEASE_MONTH 3 Index: ruby_2_4/load.c =================================================================== --- ruby_2_4/load.c (revision 62800) +++ ruby_2_4/load.c (revision 62801) @@ -95,6 +95,15 @@ rb_construct_expanded_load_path(enum exp https://github.com/ruby/ruby/blob/trunk/ruby_2_4/load.c#L95 rb_ary_replace(vm->load_path_snapshot, vm->load_path); } +static VALUE +load_path_getcwd(void) +{ + char *cwd = my_getcwd(); + VALUE cwd_str = rb_filesystem_str_new_cstr(cwd); + xfree(cwd); + return cwd_str; +} + VALUE rb_get_expanded_load_path(void) { @@ -106,7 +115,7 @@ rb_get_expanded_load_path(void) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/load.c#L115 int has_relative = 0, has_non_cache = 0; rb_construct_expanded_load_path(EXPAND_ALL, &has_relative, &has_non_cache); if (has_relative) { - vm->load_path_check_cache = rb_dir_getwd_ospath(); + vm->load_path_check_cache = load_path_getcwd(); } else if (has_non_cache) { /* Non string object. */ @@ -124,7 +133,7 @@ rb_get_expanded_load_path(void) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/load.c#L133 } else if (vm->load_path_check_cache) { int has_relative = 1, has_non_cache = 1; - VALUE cwd = rb_dir_getwd_ospath(); + VALUE cwd = load_path_getcwd(); if (!rb_str_equal(vm->load_path_check_cache, cwd)) { /* Current working directory or filesystem encoding was changed. Expand relative load path and non-cacheable objects again. */ Index: ruby_2_4/test/ruby/test_rubyoptions.rb =================================================================== --- ruby_2_4/test/ruby/test_rubyoptions.rb (revision 62800) +++ ruby_2_4/test/ruby/test_rubyoptions.rb (revision 62801) @@ -920,16 +920,4 @@ class TestRubyOptions < Test::Unit::Test https://github.com/ruby/ruby/blob/trunk/ruby_2_4/test/ruby/test_rubyoptions.rb#L920 end end end - - def test_cwd_encoding - with_tmpchdir do - testdir = "\u30c6\u30b9\u30c8" - Dir.mkdir(testdir) - Dir.chdir(testdir) do - File.write("a.rb", "require './b'") - File.write("b.rb", "puts 'ok'") - assert_ruby_status([{"RUBYLIB"=>"."}, *%w[-E cp932:utf-8 a.rb]]) - end - end - end end Index: ruby_2_4/util.c =================================================================== --- ruby_2_4/util.c (revision 62800) +++ ruby_2_4/util.c (revision 62801) @@ -511,10 +511,7 @@ ruby_getcwd(void) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/util.c#L511 char *buf = xmalloc(2); strcpy(buf, "."); #elif defined HAVE_GETCWD -# undef RUBY_UNTYPED_DATA_WARNING -# define RUBY_UNTYPED_DATA_WARNING 0 # if defined NO_GETCWD_MALLOC - VALUE guard = Data_Wrap_Struct((VALUE)0, NULL, RUBY_DEFAULT_FREE, NULL); int size = 200; char *buf = xmalloc(size); @@ -522,22 +519,17 @@ ruby_getcwd(void) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/util.c#L519 int e = errno; if (e != ERANGE) { xfree(buf); - DATA_PTR(guard) = NULL; rb_syserr_fail(e, "getcwd"); } size *= 2; - DATA_PTR(guard) = buf; buf = xrealloc(buf, size); } # else - VALUE guard = Data_Wrap_Struct((VALUE)0, NULL, free, NULL); char *buf, *cwd = getcwd(NULL, 0); - DATA_PTR(guard) = cwd; if (!cwd) rb_sys_fail("getcwd"); buf = ruby_strdup(cwd); /* allocate by xmalloc */ free(cwd); # endif - DATA_PTR(RB_GC_GUARD(guard)) = NULL; #else # ifndef PATH_MAX # define PATH_MAX 8192 Index: ruby_2_4/dir.c =================================================================== --- ruby_2_4/dir.c (revision 62800) +++ ruby_2_4/dir.c (revision 62801) @@ -74,7 +74,6 @@ char *strchr(char*,char); https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L74 #define rmdir(p) rb_w32_urmdir(p) #undef opendir #define opendir(p) rb_w32_uopendir(p) -#define ruby_getcwd() rb_w32_ugetcwd(NULL, 0) #define IS_WIN32 1 #else #define IS_WIN32 0 @@ -1049,52 +1048,26 @@ dir_s_chdir(int argc, VALUE *argv, VALUE https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1048 } VALUE -rb_dir_getwd_ospath(void) +rb_dir_getwd(void) { char *path; VALUE cwd; - VALUE path_guard; + int fsenc = rb_enc_to_index(rb_filesystem_encoding()); -#undef RUBY_UNTYPED_DATA_WARNING -#define RUBY_UNTYPED_DATA_WARNING 0 - path_guard = Data_Wrap_Struct((VALUE)0, NULL, RUBY_DEFAULT_FREE, NULL); + if (fsenc == ENCINDEX_US_ASCII) fsenc = ENCINDEX_ASCII; path = my_getcwd(); - DATA_PTR(path_guard) = path; -#ifdef _WIN32 - cwd = rb_utf8_str_new_cstr(path); - OBJ_TAINT(cwd); -#elif defined __APPLE__ +#ifdef __APPLE__ cwd = rb_str_normalize_ospath(path, strlen(path)); OBJ_TAINT(cwd); #else cwd = rb_tainted_str_new2(path); #endif - DATA_PTR(path_guard) = 0; + rb_enc_associate_index(cwd, fsenc); xfree(path); return cwd; } -VALUE -rb_dir_getwd(void) -{ - rb_encoding *fs = rb_filesystem_encoding(); - int fsenc = rb_enc_to_index(fs); - VALUE cwd = rb_dir_getwd_ospath(); - - switch (fsenc) { - case ENCINDEX_US_ASCII: - fsenc = ENCINDEX_ASCII; - case ENCINDEX_ASCII: - break; -#if defined _WIN32 || defined __APPLE__ - default: - return rb_str_conv_enc(cwd, NULL, fs); -#endif - } - return rb_enc_associate_index(cwd, fsenc); -} - /* * call-seq: * Dir.getwd -> string Index: ruby_2_4/win32/dir.h =================================================================== --- ruby_2_4/win32/dir.h (revision 62800) +++ ruby_2_4/win32/dir.h (revision 62801) @@ -33,7 +33,6 @@ long rb_w32_telldir(DIR *); https://github.com/ruby/ruby/blob/trunk/ruby_2_4/win32/dir.h#L33 void rb_w32_seekdir(DIR *, long); void rb_w32_rewinddir(DIR *); void rb_w32_closedir(DIR *); -char *rb_w32_ugetcwd(char *, int); #define opendir(s) rb_w32_opendir((s)) #define readdir(d) rb_w32_readdir((d), 0) Index: ruby_2_4/win32/win32.c =================================================================== --- ruby_2_4/win32/win32.c (revision 62800) +++ ruby_2_4/win32/win32.c (revision 62801) @@ -4654,61 +4654,43 @@ clock_getres(clockid_t clock_id, struct https://github.com/ruby/ruby/blob/trunk/ruby_2_4/win32/win32.c#L4654 } /* License: Ruby's */ -static char * -w32_getcwd(char *buffer, int size, UINT cp) +char * +rb_w32_getcwd(char *buffer, int size) { - WCHAR *p; - int wlen, len; + char *p = buffer; + int len; - len = GetCurrentDirectoryW(0, NULL); + len = GetCurrentDirectory(0, NULL); if (!len) { errno = map_errno(GetLastError()); return NULL; } - if (buffer && size < len) { - errno = ERANGE; - return NULL; - } - - p = ALLOCA_N(WCHAR, len); - if (!GetCurrentDirectoryW(len, p)) { - errno = map_errno(GetLastError()); - return NULL; - } - - wlen = translate_wchar(p, L'\\', L'/') - p + 1; - len = WideCharToMultiByte(cp, 0, p, wlen, NULL, 0, NULL, NULL); - if (buffer) { + if (p) { if (size < len) { errno = ERANGE; return NULL; } } else { - buffer = malloc(len); - if (!buffer) { + p = malloc(len); + size = len; + if (!p) { errno = ENOMEM; return NULL; } } - WideCharToMultiByte(cp, 0, p, wlen, buffer, len, NULL, NULL); - return buffer; -} + if (!GetCurrentDirectory(size, p)) { + errno = map_errno(GetLastError()); + if (!buffer) + free(p); + return NULL; + } -/* License: Ruby's */ -char * -rb_w32_getcwd(char *buffer, int size) -{ - return w32_getcwd(buffer, size, filecp()); -} + translate_char(p, '\\', '/', filecp()); -/* License: Ruby's */ -char * -rb_w32_ugetcwd(char *buffer, int size) -{ - return w32_getcwd(buffer, size, CP_UTF8); + return p; } /* License: Artistic or GPL */ Index: ruby_2_4/internal.h =================================================================== --- ruby_2_4/internal.h (revision 62800) +++ ruby_2_4/internal.h (revision 62801) @@ -982,9 +982,6 @@ void ruby_register_rollback_func_for_ens https://github.com/ruby/ruby/blob/trunk/ruby_2_4/internal.h#L982 /* debug.c */ PRINTF_ARGS(void ruby_debug_printf(const char*, ...), 1, 2); -/* dir.c */ -VALUE rb_dir_getwd_ospath(void); - /* dmyext.c */ void Init_enc(void); void Init_ext(void); Index: ruby_2_4 =================================================================== --- ruby_2_4 (revision 62800) +++ ruby_2_4 (revision 62801) Property changes on: ruby_2_4 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,1 +0,0 ## Reverse-merged /trunk:r57484,58745,58767,58780,58938,59040-59041,60743 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/