mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* 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
					
				
					 6 changed files with 227 additions and 36 deletions
				
			
		| 
						 | 
				
			
			@ -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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								dir.c
									
										
									
									
									
								
							
							
						
						
									
										8
									
								
								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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										207
									
								
								file.c
									
										
									
									
									
								
							
							
						
						
									
										207
									
								
								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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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[];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue