[前][次][番号順一覧][スレッド一覧]

ruby-changes:4927

From: ko1@a...
Date: Thu, 15 May 2008 15:34:29 +0900 (JST)
Subject: [ruby-changes:4927] nobu - Ruby:r16420 (trunk, ruby_1_8): * file.c (file_expand_path): support for alternative data stream

nobu	2008-05-15 15:34:02 +0900 (Thu, 15 May 2008)

  New Revision: 16420

  Modified files:
    branches/ruby_1_8/ChangeLog
    branches/ruby_1_8/defines.h
    branches/ruby_1_8/file.c
    branches/ruby_1_8/version.h
    trunk/ChangeLog
    trunk/dir.c
    trunk/file.c
    trunk/include/ruby/defines.h
    trunk/test/ruby/test_file_exhaustive.rb
    trunk/version.h

  Log:
    * file.c (file_expand_path): support for alternative data stream
      and ignored trailing garbages of NTFS.
    
    * file.c (rb_file_s_basename): ditto.
    
    * file.c (rb_file_s_extname): ditto.


  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/ruby/test_file_exhaustive.rb?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/defines.h?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/version.h?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/file.c?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/ChangeLog?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/ChangeLog?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/file.c?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/dir.c?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8/version.h?r1=16420&r2=16419&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/include/ruby/defines.h?r1=16420&r2=16419&diff_format=u

Index: include/ruby/defines.h
===================================================================
--- include/ruby/defines.h	(revision 16419)
+++ include/ruby/defines.h	(revision 16420)
@@ -262,6 +262,14 @@
 #define ENV_IGNORECASE
 #endif
 
+#ifndef CASEFOLD_FILESYSTEM
+# if defined DOSISH || defined __VMS
+#   define CASEFOLD_FILESYSTEM 1
+# else
+#   define CASEFOLD_FILESYSTEM 0
+# endif
+#endif
+
 #ifndef DLEXT_MAXLEN
 #define DLEXT_MAXLEN 4
 #endif
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 16419)
+++ ChangeLog	(revision 16420)
@@ -1,3 +1,12 @@
+Thu May 15 15:33:59 2008  Nobuyoshi Nakada  <nobu@r...>
+
+	* file.c (file_expand_path): support for alternative data stream
+	  and ignored trailing garbages of NTFS.
+
+	* file.c (rb_file_s_basename): ditto.
+
+	* file.c (rb_file_s_extname): ditto.
+
 Wed May 14 22:09:25 2008  Yusuke Endoh  <mame@t...>
 
 	* ChangeLog: fix typo.
Index: dir.c
===================================================================
--- dir.c	(revision 16419)
+++ dir.c	(revision 16420)
@@ -66,14 +66,6 @@
 #define lstat stat
 #endif
 
-#ifndef CASEFOLD_FILESYSTEM
-# if defined DOSISH || defined __VMS
-#   define CASEFOLD_FILESYSTEM 1
-# else
-#   define CASEFOLD_FILESYSTEM 0
-# endif
-#endif
-
 #define FNM_NOESCAPE	0x01
 #define FNM_PATHNAME	0x02
 #define FNM_DOTMATCH	0x04
Index: version.h
===================================================================
--- version.h	(revision 16419)
+++ version.h	(revision 16420)
@@ -1,7 +1,7 @@
 #define RUBY_VERSION "1.9.0"
-#define RUBY_RELEASE_DATE "2008-05-14"
+#define RUBY_RELEASE_DATE "2008-05-15"
 #define RUBY_VERSION_CODE 190
-#define RUBY_RELEASE_CODE 20080514
+#define RUBY_RELEASE_CODE 20080515
 #define RUBY_PATCHLEVEL 0
 
 #define RUBY_VERSION_MAJOR 1
@@ -9,7 +9,7 @@
 #define RUBY_VERSION_TEENY 0
 #define RUBY_RELEASE_YEAR 2008
 #define RUBY_RELEASE_MONTH 5
-#define RUBY_RELEASE_DAY 14
+#define RUBY_RELEASE_DAY 15
 
 #ifdef RUBY_EXTERN
 RUBY_EXTERN const char ruby_version[];
Index: test/ruby/test_file_exhaustive.rb
===================================================================
--- test/ruby/test_file_exhaustive.rb	(revision 16419)
+++ test/ruby/test_file_exhaustive.rb	(revision 16420)
@@ -374,6 +374,11 @@
 
   def test_expand_path
     assert_equal(@file, File.expand_path(File.basename(@file), File.dirname(@file)))
+    if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
+      assert_equal(@file, File.expand_path(@file + " "))
+      assert_equal(@file, File.expand_path(@file + "."))
+      assert_equal(@file, File.expand_path(@file + "::$DATA"))
+    end
   end
 
   def test_basename
@@ -383,6 +388,19 @@
     assert_equal("foo", File.basename("foo", ".ext"))
     assert_equal("foo", File.basename("foo.ext", ".ext"))
     assert_equal("foo", File.basename("foo.ext", ".*"))
+    if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
+      basename = File.basename(@file)
+      assert_equal(basename, File.basename(@file + " "))
+      assert_equal(basename, File.basename(@file + "."))
+      assert_equal(basename, File.basename(@file + "::$DATA"))
+      basename.chomp!(".test")
+      assert_equal(basename, File.basename(@file + " ", ".test"))
+      assert_equal(basename, File.basename(@file + ".", ".test"))
+      assert_equal(basename, File.basename(@file + "::$DATA", ".test"))
+      assert_equal(basename, File.basename(@file + " ", ".*"))
+      assert_equal(basename, File.basename(@file + ".", ".*"))
+      assert_equal(basename, File.basename(@file + "::$DATA", ".*"))
+    end
   end
 
   def test_dirname
@@ -394,6 +412,13 @@
     assert(".test", File.extname(@file))
     assert_equal("", File.extname("foo"))
     assert_equal("", File.extname(""))
+    if /cygwin|mingw|mswin|bccwin/ =~ RUBY_PLATFORM
+      assert_equal("", File.extname("foo "))
+      assert_equal(".ext", File.extname("foo.ext "))
+      assert_equal(".ext", File.extname("foo.ext."))
+      assert_equal(".ext", File.extname("foo.ext::$DATA"))
+      assert_equal("", File.extname("foo::$DATA.ext"))
+    end
   end
 
   def test_split
Index: file.c
===================================================================
--- file.c	(revision 16419)
+++ file.c	(revision 16420)
@@ -14,6 +14,10 @@
 #ifdef _WIN32
 #include "missing/file.h"
 #endif
+#ifdef __CYGWIN__
+#include <windows.h>
+#include <sys/cygwin.h>
+#endif
 
 #include "ruby/ruby.h"
 #include "ruby/io.h"
@@ -2399,6 +2403,18 @@
 #define isdirsep(x) ((x) == '/')
 #endif
 
+#if defined _WIN32 || defined __CYGWIN__
+#define USE_NTFS 1
+#else
+#define USE_NTFS 0
+#endif
+
+#if USE_NTFS
+#define istrailinggabage(x) ((x) == '.' || (x) == ' ')
+#else
+#define istrailinggabage(x) 0
+#endif
+
 #ifndef CharNext		/* defined as CharNext[AW] on Windows. */
 # if defined(DJGPP)
 #   define CharNext(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
@@ -2535,6 +2551,30 @@
     return chompdirsep(path);
 }
 
+#if USE_NTFS
+static char *
+ntfs_tail(const char *path)
+{
+    while (*path && *path != ':') {
+	if (istrailinggabage(*path)) {
+	    const char *last = path++;
+	    while (istrailinggabage(*path)) path++;
+	    if (!*path || *path == ':') return (char *)last;
+	}
+	else if (isdirsep(*path)) {
+	    const char *last = path++;
+	    while (isdirsep(*path)) path++;
+	    if (!*path) return (char *)last;
+	    if (*path == ':') path++;
+	}
+	else {
+	    path = CharNext(path);
+	}
+    }
+    return (char *)path;
+}
+#endif
+
 #define BUFCHECK(cond) do {\
     long bdiff = p - buf;\
     while (cond) {\
@@ -2717,6 +2757,11 @@
 			}
 			b = ++s;
 		    }
+#if USE_NTFS
+		    else {
+			do *++s; while (istrailinggabage(*s));
+		    }
+#endif
 		    break;
 		  case '/':
 #if defined DOSISH || defined __CYGWIN__
@@ -2729,6 +2774,19 @@
 		    break;
 		}
 	    }
+#if USE_NTFS
+	    else {
+		--s;
+	      case ' ': {
+		const char *e = s;
+		while (istrailinggabage(*s)) s++;
+		if (!*s) {
+		    s = e;
+		    goto endpath;
+		}
+	      }
+	    }
+#endif
 	    break;
 	  case '/':
 #if defined DOSISH || defined __CYGWIN__
@@ -2751,14 +2809,75 @@
     }
 
     if (s > b) {
+#if USE_NTFS
+      endpath:
+	if (s > b + 6 && strncasecmp(s - 6, ":$DATA", 6) == 0) {
+	    /* alias of stream */
+	    /* get rid of a bug of x64 VC++ */
+	    if (*(s-7) == ':') s -= 7;			/* prime */
+	    else if (memchr(b, ':', s - 6 - b)) s -= 6; /* alternative */
+	}
+#endif
 	BUFCHECK(bdiff + (s-b) >= buflen);
 	memcpy(++p, b, s-b);
 	p += s-b;
     }
     if (p == skiproot(buf) - 1) p++;
+    buflen = p - buf;
 
+#if USE_NTFS
+    *p = '\0';
+    if (!strpbrk(b = buf, "*?")) {
+	size_t len;
+	WIN32_FIND_DATA wfd;
+#ifdef __CYGWIN__
+	int lnk_added = 0;
+	struct stat st;
+	char w32buf[MAXPATHLEN], sep = 0;
+	p = 0;
+	if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
+	    p = strrdirsep(buf);
+	    if (!p) p = skipprefix(buf);
+	    if (p) {
+		sep = *p;
+		*p = '\0';
+	    }
+	}
+	if (cygwin_conv_to_win32_path(buf, w32buf) == 0) {
+	    b = w32buf;
+	}
+	if (p) *p = sep;
+	else p = buf;
+	if (b == w32buf) {
+	    strlcat(w32buf, p, sizeof(w32buf));
+	    len = strlen(p);
+	    if (len > 4 && STRCASECMP(p + len - 4, ".lnk") != 0) {
+		lnk_added = 1;
+		strlcat(w32buf, ".lnk", sizeof(w32buf));
+	    }
+	}
+#endif
+	HANDLE h = FindFirstFile(b, &wfd);
+	if (h != INVALID_HANDLE_VALUE) {
+	    FindClose(h);
+	    p = strrdirsep(buf);
+	    len = strlen(wfd.cFileName);
+#ifdef __CYGWIN__
+	    if (lnk_added && len > 4 &&
+		STRCASECMP(wfd.cFileName + len - 4, ".lnk") == 0) {
+		len -= 4;
+	    }
+#endif
+	    if (!p) p = buf;
+	    buflen = ++p - buf + len;
+	    rb_str_resize(result, buflen);
+	    memcpy(p, wfd.cFileName, len + 1);
+	}
+    }
+#endif
+
     if (tainted) OBJ_TAINT(result);
-    rb_str_set_len(result, p - buf);
+    rb_str_set_len(result, buflen);
     rb_enc_check(fname, result);
     return result;
 }
@@ -2800,22 +2919,29 @@
 }
 
 static int
-rmext(const char *p, const char *e)
+rmext(const char *p, int l1, const char *e)
 {
-    int l1, l2;
+    int l2;
 
     if (!e) return 0;
 
-    l1 = chompdirsep(p) - p;
     l2 = strlen(e);
     if (l2 == 2 && e[1] == '*') {
-	e = strrchr(p, *e);
-	if (!e) return 0;
+	unsigned char c = *e;
+	e = p + l1;
+	do {
+	    if (e <= p) return 0;
+	} while (*--e != c);
 	return e - p;
     }
     if (l1 < l2) return l1;
 
-    if (strncmp(p+l1-l2, e, l2) == 0) {
+#if CASEFOLD_FILESYSTEM
+#define fncomp strncasecmp
+#else
+#define fncomp strncmp
+#endif
+    if (fncomp(p+l1-l2, e, l2) == 0) {
 	return l1-l2;
     }
     return 0;
@@ -2843,7 +2969,7 @@
 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
     char *root;
 #endif
-    int f;
+    int f, n;
 
     if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
 	StringValue(fext);
@@ -2877,18 +3003,22 @@
 #endif
 #endif
     }
-    else if (!(p = strrdirsep(name))) {
-	if (NIL_P(fext) || !(f = rmext(name, StringValueCStr(fext)))) {
-	    f = chompdirsep(name) - name;
-	    if (f == RSTRING_LEN(fname)) return fname;
-	}
-	p = name;
-    }
     else {
-	while (isdirsep(*p)) p++; /* skip last / */
-	if (NIL_P(fext) || !(f = rmext(p, StringValueCStr(fext)))) {
-	    f = chompdirsep(p) - p;
+	if (!(p = strrdirsep(name))) {
+	    p = name;
 	}
+	else {
+	    while (isdirsep(*p)) p++; /* skip last / */
+	}
+#if USE_NTFS
+	n = ntfs_tail(p) - p;
+#else
+	n = chompdirsep(p) - p;
+#endif
+	if (NIL_P(fext) || !(f = rmext(p, n, StringValueCStr(fext)))) {
+	    f = n;
+	}
+	if (f == RSTRING_LEN(fname)) return fname;
     }
     basename = rb_str_new(p, f);
     rb_enc_copy(basename, fname);
@@ -2965,21 +3095,48 @@
 static VALUE
 rb_file_s_extname(VALUE klass, VALUE fname)
 {
-    char *name, *p, *e;
+    const char *name, *p, *e;
     VALUE extname;
 
     FilePathStringValue(fname);
     name = StringValueCStr(fname);
     p = strrdirsep(name);	/* get the last path component */
     if (!p)
- 	p = name;
+	p = name;
     else
- 	p++;
- 
-    e = strrchr(p, '.');	/* get the last dot of the last component */
-    if (!e || e == p || !e[1])	/* no dot, or the only dot is first or end? */
+	p++;
+
+    e = 0;
+    while (*p) {
+	if (*p == '.' || istrailinggabage(*p)) {
+#if USE_NTFS
+	    const char *last = p++, *dot = last;
+	    while (istrailinggabage(*p)) {
+		if (*p == '.') dot = p;
+		p++;
+	    }
+	    if (!*p || *p == ':') {
+		p = last;
+		break;
+	    }
+	    e = dot;
+	    continue;
+#else
+	    e = p;	  /* get the last dot of the last component */
+#endif
+	}
+#if USE_NTFS
+	else if (*p == ':') {
+	    break;
+	}
+#endif
+	else if (isdirsep(*p))
+	    break;
+	p = CharNext(p);
+    }
+    if (!e || e+1 == p)	/* no dot, or the only dot is first or end? */
 	return rb_str_new(0, 0);
-    extname = rb_str_new(e, chompdirsep(e) - e);	/* keep the dot, too! */
+    extname = rb_str_new(e, p - e);	/* keep the dot, too! */
     rb_enc_copy(extname, fname);
     OBJ_INFECT(extname, fname);
     return extname;
Index: ruby_1_8/ChangeLog
===================================================================
--- ruby_1_8/ChangeLog	(revision 16419)
+++ ruby_1_8/ChangeLog	(revision 16420)
@@ -1,3 +1,12 @@
+Thu May 15 15:33:59 2008  Nobuyoshi Nakada  <nobu@r...>
+
+	* file.c (file_expand_path): support for alternative data stream
+	  and ignored trailing garbages of NTFS.
+
+	* file.c (rb_file_s_basename): ditto.
+
+	* file.c (rb_file_s_extname): ditto.
+
 Wed May 14 19:24:59 2008  Akinori MUSHA  <knu@i...>
 
 	* array.c (rb_ary_count): Override Enumerable#count for better
Index: ruby_1_8/version.h
===================================================================
--- ruby_1_8/version.h	(revision 16419)
+++ ruby_1_8/version.h	(revision 16420)
@@ -1,7 +1,7 @@
 #define RUBY_VERSION "1.8.7"
-#define RUBY_RELEASE_DATE "2008-05-14"
+#define RUBY_RELEASE_DATE "2008-05-15"
 #define RUBY_VERSION_CODE 187
-#define RUBY_RELEASE_CODE 20080514
+#define RUBY_RELEASE_CODE 20080515
 #define RUBY_PATCHLEVEL 5000
 
 #define RUBY_VERSION_MAJOR 1
@@ -9,7 +9,7 @@
 #define RUBY_VERSION_TEENY 7
 #define RUBY_RELEASE_YEAR 2008
 #define RUBY_RELEASE_MONTH 5
-#define RUBY_RELEASE_DAY 14
+#define RUBY_RELEASE_DAY 15
 
 #ifdef RUBY_EXTERN
 RUBY_EXTERN const char ruby_version[];
Index: ruby_1_8/defines.h
===================================================================
--- ruby_1_8/defines.h	(revision 16419)
+++ ruby_1_8/defines.h	(revision 16420)
@@ -254,6 +254,14 @@
 #define ENV_IGNORECASE
 #endif
 
+#ifndef CASEFOLD_FILESYSTEM
+# if defined DOSISH || defined __VMS
+#   define CASEFOLD_FILESYSTEM 1
+# else
+#   define CASEFOLD_FILESYSTEM 0
+# endif
+#endif
+
 #ifndef DLEXT_MAXLEN
 #define DLEXT_MAXLEN 4
 #endif
Index: ruby_1_8/file.c
===================================================================
--- ruby_1_8/file.c	(revision 16419)
+++ ruby_1_8/file.c	(revision 16420)
@@ -15,6 +15,10 @@
 #ifdef _WIN32
 #include "missing/file.h"
 #endif
+#ifdef __CYGWIN__
+#include <windows.h>
+#include <sys/cygwin.h>
+#endif
 
 #include "ruby.h"
 #include "rubyio.h"
@@ -2310,6 +2314,18 @@
 #define isdirsep(x) ((x) == '/')
 #endif
 
+#if defined _WIN32 || defined __CYGWIN__
+#define USE_NTFS 1
+#else
+#define USE_NTFS 0
+#endif
+
+#if USE_NTFS
+#define istrailinggabage(x) ((x) == '.' || (x) == ' ')
+#else
+#define istrailinggabage(x) 0
+#endif
+
 #ifndef CharNext		/* defined as CharNext[AW] on Windows. */
 # if defined(DJGPP)
 #   define CharNext(p) ((p) + mblen(p, MB_CUR_MAX))
@@ -2454,6 +2470,30 @@
     return chompdirsep(path);
 }
 
+#if USE_NTFS
+static char *
+ntfs_tail(const char *path)
+{
+    while (*path && *path != ':') {
+	if (istrailinggabage(*path)) {
+	    const char *last = path++;
+	    while (istrailinggabage(*path)) path++;
+	    if (!*path || *path == ':') return (char *)last;
+	}
+	else if (isdirsep(*path)) {
+	    const char *last = path++;
+	    while (isdirsep(*path)) path++;
+	    if (!*path) return (char *)last;
+	    if (*path == ':') path++;
+	}
+	else {
+	    path = CharNext(path);
+	}
+    }
+    return (char *)path;
+}
+#endif
+
 #define BUFCHECK(cond) do {\
     long bdiff = p - buf;\
     while (cond) {\
@@ -2480,7 +2520,8 @@
 file_expand_path(fname, dname, result)
     VALUE fname, dname, result;
 {
-    char *s, *buf, *b, *p, *pend, *root;
+    const char *s, *b;
+    char *buf, *p, *pend, *root;
     long buflen, dirlen;
     int tainted;
 
@@ -2621,15 +2662,21 @@
 		  case '.':
 		    if (*(s+1) == '\0' || isdirsep(*(s+1))) {
 			/* We must go back to the parent */
+			char *n;
 			*p = '\0';
-			if (!(b = strrdirsep(root))) {
+			if (!(n = strrdirsep(root))) {
 			    *p = '/';
 			}
 			else {
-			    p = b;
+			    p = n;
 			}
 			b = ++s;
 		    }
+#if USE_NTFS
+		    else {
+			do *++s; while (istrailinggabage(*s));
+		    }
+#endif
 		    break;
 		  case '/':
 #if defined DOSISH || defined __CYGWIN__
@@ -2642,6 +2689,19 @@
 		    break;
 		}
 	    }
+#if USE_NTFS
+	    else {
+		--s;
+	      case ' ': {
+		const char *e = s;
+		while (istrailinggabage(*s)) s++;
+		if (!*s) {
+		    s = e;
+		    goto endpath;
+		}
+	      }
+	    }
+#endif
 	    break;
 	  case '/':
 #if defined DOSISH || defined __CYGWIN__
@@ -2664,15 +2724,75 @@
     }
 
     if (s > b) {
+#if USE_NTFS
+      endpath:
+	if (s > b + 6 && strncasecmp(s - 6, ":$DATA", 6) == 0) {
+	    /* alias of stream */
+	    /* get rid of a bug of x64 VC++ */
+	    if (*(s-7) == ':') s -= 7;			/* prime */
+	    else if (memchr(b, ':', s - 6 - b)) s -= 6; /* alternative */
+	}
+#endif
 	BUFCHECK(bdiff + (s-b) >= buflen);
 	memcpy(++p, b, s-b);
 	p += s-b;
     }
     if (p == skiproot(buf) - 1) p++;
+    buflen = p - buf;
 
+#if USE_NTFS
+    *p = '\0';
+    if (!strpbrk(b = buf, "*?")) {
+	size_t len;
+	WIN32_FIND_DATA wfd;
+#ifdef __CYGWIN__
+	int lnk_added = 0;
+	struct stat st;
+	char w32buf[MAXPATHLEN], sep = 0;
+	p = 0;
+	if (lstat(buf, &st) == 0 && S_ISLNK(st.st_mode)) {
+	    p = strrdirsep(buf);
+	    if (!p) p = skipprefix(buf);
+	    if (p) {
+		sep = *p;
+		*p = '\0';
+	    }
+	}
+	if (cygwin_conv_to_win32_path(buf, w32buf) == 0) {
+	    b = w32buf;
+	}
+	if (p) *p = sep;
+	else p = buf;
+	if (b == w32buf) {
+	    strlcat(w32buf, p, sizeof(w32buf));
+	    len = strlen(p);
+	    if (len > 4 && strcasecmp(p + len - 4, ".lnk") != 0) {
+		lnk_added = 1;
+		strlcat(w32buf, ".lnk", sizeof(w32buf));
+	    }
+	}
+#endif
+	HANDLE h = FindFirstFile(b, &wfd);
+	if (h != INVALID_HANDLE_VALUE) {
+	    FindClose(h);
+	    p = strrdirsep(buf);
+	    len = strlen(wfd.cFileName);
+#ifdef __CYGWIN__
+	    if (lnk_added && len > 4 &&
+		strcasecmp(wfd.cFileName + len - 4, ".lnk") == 0) {
+		len -= 4;
+	    }
+#endif
+	    if (!p) p = buf;
+	    buflen = ++p - buf + len;
+	    rb_str_resize(result, buflen);
+	    memcpy(p, wfd.cFileName, len + 1);
+	}
+    }
+#endif
+
     if (tainted) OBJ_TAINT(result);
-    RSTRING(result)->len = p - buf;
-    *p = '\0';
+    rb_str_set_len(result, buflen);
     return result;
 }
 
@@ -2716,23 +2836,31 @@
 }
 
 static int
-rmext(p, e)
+rmext(p, l1, e)
     const char *p, *e;
+    int l1;
 {
-    int l1, l2;
+    int l2;
 
     if (!e) return 0;
 
-    l1 = chompdirsep(p) - p;
     l2 = strlen(e);
     if (l2 == 2 && e[1] == '*') {
-	e = strrchr(p, *e);
-	if (!e) return 0;
+	unsigned char c = *e;
+	e = p + l1;
+	do {
+	    if (e <= p) return 0;
+	} while (*--e != c);
 	return e - p;
     }
     if (l1 < l2) return l1;
 
-    if (strncmp(p+l1-l2, e, l2) == 0) {
+#if CASEFOLD_FILESYSTEM
+#define fncomp strncasecmp
+#else
+#define fncomp strncmp
+#endif
+    if (fncomp(p+l1-l2, e, l2) == 0) {
 	return l1-l2;
     }
     return 0;
@@ -2762,7 +2890,7 @@
 #if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
     char *root;
 #endif
-    int f;
+    int f, n;
 
     if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
 	StringValue(fext);
@@ -2796,18 +2924,22 @@
 #endif
 #endif
     }
-    else if (!(p = strrdirsep(name))) {
-	if (NIL_P(fext) || !(f = rmext(name, StringValueCStr(fext)))) {
-	    f = chompdirsep(name) - name;
-	    if (f == RSTRING(fname)->len) return fname;
-	}
-	p = name;
-    }
     else {
-	while (isdirsep(*p)) p++; /* skip last / */
-	if (NIL_P(fext) || !(f = rmext(p, StringValueCStr(fext)))) {
-	    f = chompdirsep(p) - p;
+	if (!(p = strrdirsep(name))) {
+	    p = name;
 	}
+	else {
+	    while (isdirsep(*p)) p++; /* skip last / */
+	}
+#if USE_NTFS
+	n = ntfs_tail(p) - p;
+#else
+	n = chompdirsep(p) - p;
+#endif
+	if (NIL_P(fext) || !(f = rmext(p, n, StringValueCStr(fext)))) {
+	    f = n;
+	}
+	if (f == RSTRING_LEN(fname)) return fname;
     }
     basename = rb_str_new(p, f);
     OBJ_INFECT(basename, fname);
@@ -2883,22 +3015,49 @@
 rb_file_s_extname(klass, fname)
     VALUE klass, fname;
 {
-    char *name, *p, *e;
+    const char *name, *p, *e;
     VALUE extname;
 
     name = StringValueCStr(fname);
     p = strrdirsep(name);	/* get the last path component */
     if (!p)
- 	p = name;
+	p = name;
     else
- 	p++;
- 
-     e = strrchr(p, '.');	/* get the last dot of the last component */
-     if (!e || e == p || !e[1])	/* no dot, or the only dot is first or end? */
-	 return rb_str_new2("");
-     extname = rb_str_new(e, chompdirsep(e) - e);	/* keep the dot, too! */
-     OBJ_INFECT(extname, fname);
-     return extname;
+	p++;
+
+    e = 0;
+    while (*p) {
+	if (*p == '.' || istrailinggabage(*p)) {
+#if USE_NTFS
+	    const char *last = p++, *dot = last;
+	    while (istrailinggabage(*p)) {
+		if (*p == '.') dot = p;
+		p++;
+	    }
+	    if (!*p || *p == ':') {
+		p = last;
+		break;
+	    }
+	    e = dot;
+	    continue;
+#else
+	    e = p;	  /* get the last dot of the last component */
+#endif
+	}
+#if USE_NTFS
+	else if (*p == ':') {
+	    break;
+	}
+#endif
+	else if (isdirsep(*p))
+	    break;
+	p = CharNext(p);
+    }
+    if (!e || e+1 == p)	/* no dot, or the only dot is first or end? */
+	return rb_str_new(0, 0);
+    extname = rb_str_new(e, p - e);	/* keep the dot, too! */
+    OBJ_INFECT(extname, fname);
+    return extname;
 }
 
 /*

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]