* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@16420 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
40a98ca132
commit
22361a3d1c
@ -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>
|
Wed May 14 22:09:25 2008 Yusuke Endoh <mame@tsg.ne.jp>
|
||||||
|
|
||||||
* ChangeLog: fix typo.
|
* ChangeLog: fix typo.
|
||||||
|
8
dir.c
8
dir.c
@ -66,14 +66,6 @@ char *strchr(char*,char);
|
|||||||
#define lstat stat
|
#define lstat stat
|
||||||
#endif
|
#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_NOESCAPE 0x01
|
||||||
#define FNM_PATHNAME 0x02
|
#define FNM_PATHNAME 0x02
|
||||||
#define FNM_DOTMATCH 0x04
|
#define FNM_DOTMATCH 0x04
|
||||||
|
205
file.c
205
file.c
@ -14,6 +14,10 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "missing/file.h"
|
#include "missing/file.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
#include <windows.h>
|
||||||
|
#include <sys/cygwin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ruby/ruby.h"
|
#include "ruby/ruby.h"
|
||||||
#include "ruby/io.h"
|
#include "ruby/io.h"
|
||||||
@ -2399,6 +2403,18 @@ rb_file_s_umask(int argc, VALUE *argv)
|
|||||||
#define isdirsep(x) ((x) == '/')
|
#define isdirsep(x) ((x) == '/')
|
||||||
#endif
|
#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. */
|
#ifndef CharNext /* defined as CharNext[AW] on Windows. */
|
||||||
# if defined(DJGPP)
|
# if defined(DJGPP)
|
||||||
# define CharNext(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
|
# define CharNext(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
|
||||||
@ -2535,6 +2551,30 @@ rb_path_end(const char *path)
|
|||||||
return chompdirsep(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 {\
|
#define BUFCHECK(cond) do {\
|
||||||
long bdiff = p - buf;\
|
long bdiff = p - buf;\
|
||||||
while (cond) {\
|
while (cond) {\
|
||||||
@ -2717,6 +2757,11 @@ file_expand_path(VALUE fname, VALUE dname, VALUE result)
|
|||||||
}
|
}
|
||||||
b = ++s;
|
b = ++s;
|
||||||
}
|
}
|
||||||
|
#if USE_NTFS
|
||||||
|
else {
|
||||||
|
do *++s; while (istrailinggabage(*s));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case '/':
|
case '/':
|
||||||
#if defined DOSISH || defined __CYGWIN__
|
#if defined DOSISH || defined __CYGWIN__
|
||||||
@ -2729,6 +2774,19 @@ file_expand_path(VALUE fname, VALUE dname, VALUE result)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if USE_NTFS
|
||||||
|
else {
|
||||||
|
--s;
|
||||||
|
case ' ': {
|
||||||
|
const char *e = s;
|
||||||
|
while (istrailinggabage(*s)) s++;
|
||||||
|
if (!*s) {
|
||||||
|
s = e;
|
||||||
|
goto endpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case '/':
|
case '/':
|
||||||
#if defined DOSISH || defined __CYGWIN__
|
#if defined DOSISH || defined __CYGWIN__
|
||||||
@ -2751,14 +2809,75 @@ file_expand_path(VALUE fname, VALUE dname, VALUE result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (s > b) {
|
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);
|
BUFCHECK(bdiff + (s-b) >= buflen);
|
||||||
memcpy(++p, b, s-b);
|
memcpy(++p, b, s-b);
|
||||||
p += s-b;
|
p += s-b;
|
||||||
}
|
}
|
||||||
if (p == skiproot(buf) - 1) p++;
|
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);
|
if (tainted) OBJ_TAINT(result);
|
||||||
rb_str_set_len(result, p - buf);
|
rb_str_set_len(result, buflen);
|
||||||
rb_enc_check(fname, result);
|
rb_enc_check(fname, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -2800,22 +2919,29 @@ rb_file_s_expand_path(int argc, VALUE *argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
if (!e) return 0;
|
||||||
|
|
||||||
l1 = chompdirsep(p) - p;
|
|
||||||
l2 = strlen(e);
|
l2 = strlen(e);
|
||||||
if (l2 == 2 && e[1] == '*') {
|
if (l2 == 2 && e[1] == '*') {
|
||||||
e = strrchr(p, *e);
|
unsigned char c = *e;
|
||||||
if (!e) return 0;
|
e = p + l1;
|
||||||
|
do {
|
||||||
|
if (e <= p) return 0;
|
||||||
|
} while (*--e != c);
|
||||||
return e - p;
|
return e - p;
|
||||||
}
|
}
|
||||||
if (l1 < l2) return l1;
|
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 l1-l2;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -2843,7 +2969,7 @@ rb_file_s_basename(int argc, VALUE *argv)
|
|||||||
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
|
#if defined DOSISH_DRIVE_LETTER || defined DOSISH_UNC
|
||||||
char *root;
|
char *root;
|
||||||
#endif
|
#endif
|
||||||
int f;
|
int f, n;
|
||||||
|
|
||||||
if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
|
if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
|
||||||
StringValue(fext);
|
StringValue(fext);
|
||||||
@ -2877,18 +3003,22 @@ rb_file_s_basename(int argc, VALUE *argv)
|
|||||||
#endif
|
#endif
|
||||||
#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 {
|
else {
|
||||||
while (isdirsep(*p)) p++; /* skip last / */
|
if (!(p = strrdirsep(name))) {
|
||||||
if (NIL_P(fext) || !(f = rmext(p, StringValueCStr(fext)))) {
|
p = name;
|
||||||
f = chompdirsep(p) - p;
|
|
||||||
}
|
}
|
||||||
|
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);
|
basename = rb_str_new(p, f);
|
||||||
rb_enc_copy(basename, fname);
|
rb_enc_copy(basename, fname);
|
||||||
@ -2965,21 +3095,48 @@ rb_file_s_dirname(VALUE klass, VALUE fname)
|
|||||||
static VALUE
|
static VALUE
|
||||||
rb_file_s_extname(VALUE klass, VALUE fname)
|
rb_file_s_extname(VALUE klass, VALUE fname)
|
||||||
{
|
{
|
||||||
char *name, *p, *e;
|
const char *name, *p, *e;
|
||||||
VALUE extname;
|
VALUE extname;
|
||||||
|
|
||||||
FilePathStringValue(fname);
|
FilePathStringValue(fname);
|
||||||
name = StringValueCStr(fname);
|
name = StringValueCStr(fname);
|
||||||
p = strrdirsep(name); /* get the last path component */
|
p = strrdirsep(name); /* get the last path component */
|
||||||
if (!p)
|
if (!p)
|
||||||
p = name;
|
p = name;
|
||||||
else
|
else
|
||||||
p++;
|
p++;
|
||||||
|
|
||||||
e = strrchr(p, '.'); /* get the last dot of the last component */
|
e = 0;
|
||||||
if (!e || e == p || !e[1]) /* no dot, or the only dot is first or end? */
|
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);
|
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);
|
rb_enc_copy(extname, fname);
|
||||||
OBJ_INFECT(extname, fname);
|
OBJ_INFECT(extname, fname);
|
||||||
return extname;
|
return extname;
|
||||||
|
@ -262,6 +262,14 @@ void rb_ia64_flushrs(void);
|
|||||||
#define ENV_IGNORECASE
|
#define ENV_IGNORECASE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CASEFOLD_FILESYSTEM
|
||||||
|
# if defined DOSISH || defined __VMS
|
||||||
|
# define CASEFOLD_FILESYSTEM 1
|
||||||
|
# else
|
||||||
|
# define CASEFOLD_FILESYSTEM 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DLEXT_MAXLEN
|
#ifndef DLEXT_MAXLEN
|
||||||
#define DLEXT_MAXLEN 4
|
#define DLEXT_MAXLEN 4
|
||||||
#endif
|
#endif
|
||||||
|
@ -374,6 +374,11 @@ class TestFileExhaustive < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_expand_path
|
def test_expand_path
|
||||||
assert_equal(@file, File.expand_path(File.basename(@file), File.dirname(@file)))
|
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
|
end
|
||||||
|
|
||||||
def test_basename
|
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"))
|
||||||
assert_equal("foo", File.basename("foo.ext", ".ext"))
|
assert_equal("foo", File.basename("foo.ext", ".ext"))
|
||||||
assert_equal("foo", File.basename("foo.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
|
end
|
||||||
|
|
||||||
def test_dirname
|
def test_dirname
|
||||||
@ -394,6 +412,13 @@ class TestFileExhaustive < Test::Unit::TestCase
|
|||||||
assert(".test", File.extname(@file))
|
assert(".test", File.extname(@file))
|
||||||
assert_equal("", File.extname("foo"))
|
assert_equal("", File.extname("foo"))
|
||||||
assert_equal("", File.extname(""))
|
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
|
end
|
||||||
|
|
||||||
def test_split
|
def test_split
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#define RUBY_VERSION "1.9.0"
|
#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_VERSION_CODE 190
|
||||||
#define RUBY_RELEASE_CODE 20080514
|
#define RUBY_RELEASE_CODE 20080515
|
||||||
#define RUBY_PATCHLEVEL 0
|
#define RUBY_PATCHLEVEL 0
|
||||||
|
|
||||||
#define RUBY_VERSION_MAJOR 1
|
#define RUBY_VERSION_MAJOR 1
|
||||||
@ -9,7 +9,7 @@
|
|||||||
#define RUBY_VERSION_TEENY 0
|
#define RUBY_VERSION_TEENY 0
|
||||||
#define RUBY_RELEASE_YEAR 2008
|
#define RUBY_RELEASE_YEAR 2008
|
||||||
#define RUBY_RELEASE_MONTH 5
|
#define RUBY_RELEASE_MONTH 5
|
||||||
#define RUBY_RELEASE_DAY 14
|
#define RUBY_RELEASE_DAY 15
|
||||||
|
|
||||||
#ifdef RUBY_EXTERN
|
#ifdef RUBY_EXTERN
|
||||||
RUBY_EXTERN const char ruby_version[];
|
RUBY_EXTERN const char ruby_version[];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user