summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--dir.c8
-rw-r--r--file.c207
-rw-r--r--include/ruby/defines.h8
-rw-r--r--test/ruby/test_file_exhaustive.rb25
-rw-r--r--version.h6
6 files changed, 227 insertions, 36 deletions
diff --git a/ChangeLog b/ChangeLog
index 35641067b0..b7e7055994 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Thu May 15 15:33:59 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * 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@tsg.ne.jp>
* ChangeLog: fix typo.
diff --git a/dir.c b/dir.c
index b056849e0d..672720167c 100644
--- a/dir.c
+++ b/dir.c
@@ -66,14 +66,6 @@ char *strchr(char*,char);
#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
diff --git a/file.c b/file.c
index 21741a63e9..f89248b521 100644
--- a/file.c
+++ b/file.c
@@ -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 @@ rb_file_s_umask(int argc, VALUE *argv)
#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 @@ rb_path_end(const char *path)
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 @@ file_expand_path(VALUE fname, VALUE dname, VALUE result)
}
b = ++s;
}
+#if USE_NTFS
+ else {
+ do *++s; while (istrailinggabage(*s));
+ }
+#endif
break;
case '/':
#if defined DOSISH || defined __CYGWIN__
@@ -2729,6 +2774,19 @@ file_expand_path(VALUE fname, VALUE dname, VALUE result)
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 @@ file_expand_path(VALUE fname, VALUE dname, VALUE result)
}
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 @@ rb_file_s_expand_path(int argc, VALUE *argv)
}
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 @@ rb_file_s_basename(int argc, VALUE *argv)
#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 @@ rb_file_s_basename(int argc, VALUE *argv)
#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 @@ rb_file_s_dirname(VALUE klass, VALUE fname)
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;
diff --git a/include/ruby/defines.h b/include/ruby/defines.h
index 00708e1039..2be89de5ff 100644
--- a/include/ruby/defines.h
+++ b/include/ruby/defines.h
@@ -262,6 +262,14 @@ void rb_ia64_flushrs(void);
#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
diff --git a/test/ruby/test_file_exhaustive.rb b/test/ruby/test_file_exhaustive.rb
index 7f579cee43..be28fc6dd9 100644
--- a/test/ruby/test_file_exhaustive.rb
+++ b/test/ruby/test_file_exhaustive.rb
@@ -374,6 +374,11 @@ class TestFileExhaustive < Test::Unit::TestCase
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 @@ class TestFileExhaustive < Test::Unit::TestCase
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 @@ class TestFileExhaustive < Test::Unit::TestCase
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
diff --git a/version.h b/version.h
index 0216f9f79b..473b606dc9 100644
--- a/version.h
+++ b/version.h
@@ -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[];