Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 837cb739713e
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5bc6c5099ac3
Choose a head ref
  • 7 commits
  • 4 files changed
  • 1 contributor

Commits on May 13, 2015

  1. Copy the full SHA
    3ac859f View commit details
  2. make sure we do not confuse bytes length with end index (due ByteList…

    … usage)
    
    ... also cleanup up the internals a bit
    kares committed May 13, 2015
    Copy the full SHA
    72d6e80 View commit details
  3. Copy the full SHA
    546bc78 View commit details
  4. Copy the full SHA
    eb48185 View commit details
  5. handle exception being thrown when doing a Dir['{}'] (fixes #2922)

    ... in this case we end up with an empty path in Dir's `glob_helper` and it all seems reasonable - thuse removed the length check comment about "" ending up here
    kares committed May 13, 2015
    Copy the full SHA
    0da8bff View commit details
  6. Copy the full SHA
    c81e3eb View commit details
  7. Copy the full SHA
    5bc6c50 View commit details
Showing with 229 additions and 237 deletions.
  1. +16 −17 core/src/main/java/org/jruby/RubyDir.java
  2. +185 −193 core/src/main/java/org/jruby/util/Dir.java
  3. +3 −6 spec/regression/GH-2856_bad_character_transcode_should_not_hang_spec.rb
  4. +25 −21 test/test_dir.rb
33 changes: 16 additions & 17 deletions core/src/main/java/org/jruby/RubyDir.java
Original file line number Diff line number Diff line change
@@ -161,32 +161,31 @@ public IRubyObject initialize19(ThreadContext context, IRubyObject arg) {

// ----- Ruby Class Methods ----------------------------------------------------

private static List<ByteList> dirGlobs(ThreadContext context, String cwd, IRubyObject[] args, int flags) {
List<ByteList> dirs = new ArrayList<ByteList>();
private static ArrayList<ByteList> dirGlobs(ThreadContext context, String cwd, IRubyObject[] args, int flags) {
ArrayList<ByteList> dirs = new ArrayList<ByteList>();

POSIX posix = context.runtime.getPosix();
for (int i = 0; i < args.length; i++) {
for ( int i = 0; i < args.length; i++ ) {
dirs.addAll(Dir.push_glob(posix, cwd, globArgumentAsByteList(context, args[i]), flags));
}

return dirs;
}

private static IRubyObject asRubyStringList(Ruby runtime, List<ByteList> dirs) {
List<RubyString> allFiles = new ArrayList<RubyString>();
private static RubyArray asRubyStringList(Ruby runtime, List<ByteList> dirs) {
final int size = dirs.size();
if ( size == 0 ) return RubyArray.newEmptyArray(runtime);

Encoding enc = runtime.getDefaultExternalEncoding();
if (enc == null) {
enc = UTF8;
}

for (ByteList dir : dirs) {
allFiles.add(RubyString.newString(runtime, dir, enc));
IRubyObject[] dirStrings = new IRubyObject[ size ];
for ( int i = 0; i < size; i++ ) {
dirStrings[i] = RubyString.newString(runtime, dirs.get(i), enc);
}

IRubyObject[] tempFileList = new IRubyObject[allFiles.size()];
allFiles.toArray(tempFileList);

return runtime.newArrayNoCopy(tempFileList);
return RubyArray.newArrayNoCopy(runtime, dirStrings);
}

private static String getCWD(Ruby runtime) {
@@ -463,12 +462,12 @@ public static IRubyObject mkdir19(ThreadContext context, IRubyObject recv, IRuby

private static IRubyObject mkdirCommon(Ruby runtime, String path, IRubyObject[] args) {
File newDir = getDir(runtime, path, false);


String name = path.replace('\\', '/');

boolean startsWithDriveLetterOnWindows = RubyFile.startsWithDriveLetterOnWindows(name);

// don't attempt to create a dir for drive letters
if (startsWithDriveLetterOnWindows) {
// path is just drive letter plus :
@@ -592,11 +591,11 @@ public IRubyObject set_pos(IRubyObject newPos) {
public IRubyObject path(ThreadContext context) {
return path.strDup(context.runtime);
}

@JRubyMethod(compat = CompatVersion.RUBY1_9)
public IRubyObject to_path(ThreadContext context) {
return path(context);
}
}

/** Returns the next entry from this directory. */
@JRubyMethod(name = "read")
378 changes: 185 additions & 193 deletions core/src/main/java/org/jruby/util/Dir.java
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
* rights and limitations under the License.
*
* Copyright (C) 2007, 2008 Ola Bini <ola@ologix.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -38,10 +38,11 @@

import org.jruby.platform.Platform;
import java.io.IOException;
import java.util.Collections;

/**
* This class exists as a counterpart to the dir.c file in
* MRI source. It contains many methods useful for
* This class exists as a counterpart to the dir.c file in
* MRI source. It contains many methods useful for
* File matching and Globbing.
*
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
@@ -89,7 +90,7 @@ private static int fnmatch_helper(byte[] bytes, int pstart, int pend, byte[] str
byte c = bytes[pat++];
switch(c) {
case '?':
if(s >= send || (pathname && isdirsep(string[s])) ||
if(s >= send || (pathname && isdirsep(string[s])) ||
(period && string[s] == '.' && (s == 0 || (pathname && isdirsep(string[s-1]))))) {
return FNM_NOMATCH;
}
@@ -128,7 +129,7 @@ private static int fnmatch_helper(byte[] bytes, int pstart, int pend, byte[] str
}
return FNM_NOMATCH;
case '[':
if(s >= send || (pathname && isdirsep(string[s]) ||
if(s >= send || (pathname && isdirsep(string[s]) ||
(period && string[s] == '.' && (s == 0 || (pathname && isdirsep(string[s-1])))))) {
return FNM_NOMATCH;
}
@@ -156,13 +157,13 @@ private static int fnmatch_helper(byte[] bytes, int pstart, int pend, byte[] str
if(Character.toLowerCase((char)c) != Character.toLowerCase((char)string[s])) {
return FNM_NOMATCH;
}

} else {
if(c != (char)string[s]) {
return FNM_NOMATCH;
}
}

}
s++;
break;
@@ -174,7 +175,7 @@ private static int fnmatch_helper(byte[] bytes, int pstart, int pend, byte[] str
public static int fnmatch(
byte[] bytes, int pstart, int pend,
byte[] string, int sstart, int send, int flags) {

// This method handles '**/' patterns and delegates to
// fnmatch_helper for the main work.

@@ -298,133 +299,133 @@ public static int range(byte[] _pat, int pat, int pend, char test, int flags) {
}

public static List<ByteList> push_glob(POSIX posix, String cwd, ByteList globByteList, int flags) {
List<ByteList> result = new ArrayList<ByteList>();
if (globByteList.length() > 0) {
final ArrayList<ByteList> result = new ArrayList<ByteList>();
push_braces(posix, cwd, result, new GlobPattern(globByteList, flags));
return result;
}

return result;
return Collections.emptyList();
}

private static class GlobPattern {
final byte[] bytes;
final byte[] bytes;
final int begin;
final int end;

int flags;
int index;

public GlobPattern(ByteList bytelist, int flags) {
this(bytelist.getUnsafeBytes(), bytelist.getBegin(), bytelist.getBegin() + bytelist.getRealSize(), flags);
private int index;

private final int flags;

GlobPattern(ByteList bytes, int flags) {
this(bytes.getUnsafeBytes(), bytes.getBegin(), bytes.getBegin() + bytes.getRealSize(), flags);
}
public GlobPattern(byte[] bytes, int index, int end, int flags) {

GlobPattern(byte[] bytes, int index, int end, int flags) {
this.bytes = bytes;
this.index = index;
this.begin = index;
this.end = end;
this.flags = flags;
}

public int findClosingIndexOf(int leftTokenIndex) {
if (leftTokenIndex == -1 || leftTokenIndex > end) return -1;

byte leftToken = bytes[leftTokenIndex];
byte rightToken;

switch (leftToken) {
case '{': rightToken = '}'; break;
case '[': rightToken = ']'; break;
default: return -1;
}

int nest = 1; // leftToken made us start as nest 1
index = leftTokenIndex + 1;
while (hasNext()) {
byte c = next();

if (c == leftToken) {
nest++;
} else if (c == rightToken && --nest == 0) {
return index();
}
}

return -1;
}

public boolean hasNext() {
return index < end;
}

public void reset() {
index = begin;
}

public void setIndex(int value) {
index = value;
}

// Get index of last read byte
public int index() {
return index - 1;
}

public int indexOf(byte c) {
while (hasNext()) if (next() == c) return index();

return -1;
}

public byte next() {
return bytes[index++];
}

}

public static interface GlobFunc {
int call(byte[] ptr, int p, int len, Object ary);
public static interface GlobFunc<T> {
int call(byte[] ptr, int p, int len, T ary);
}

private static class GlobArgs {
GlobFunc func;
int c = -1;
List<ByteList> v;
public GlobArgs(GlobFunc func, List<ByteList> arg) {
final GlobFunc<List<ByteList>> func;
final List<ByteList> arg;
private int c = -1;

GlobArgs(GlobFunc<List<ByteList>> func, List<ByteList> arg) {
this.func = func;
this.v = arg;
this.arg = arg;
}
}

public final static GlobFunc push_pattern = new GlobFunc() {
@SuppressWarnings("unchecked")
public int call(byte[] ptr, int p, int len, Object ary) {
((List) ary).add(new ByteList(ptr, p, len));
return 0;
}
};
public final static GlobFunc glob_caller = new GlobFunc() {
public int call(byte[] ptr, int p, int len, Object ary) {
GlobArgs args = (GlobArgs)ary;
final static GlobFunc<List<ByteList>> push_pattern = new GlobFunc<List<ByteList>>() {
public int call(byte[] ptr, int p, int len, List<ByteList> ary) {
ary.add(new ByteList(ptr, p, len));
return 0;
}
};
private final static GlobFunc<GlobArgs> glob_caller = new GlobFunc<GlobArgs>() {
public int call(byte[] ptr, int p, int len, GlobArgs args) {
args.c = p;
return args.func.call(ptr, args.c, len, args.v);
return args.func.call(ptr, args.c, len, args.arg);
}
};

/*
* Process {}'s (example: Dir.glob("{jruby,jython}/README*")
* Process {}'s (example: Dir.glob("{jruby,jython}/README*")
*/
private static int push_braces(POSIX posix, String cwd, List<ByteList> result, GlobPattern pattern) {
pattern.reset();
int lbrace = pattern.indexOf((byte) '{'); // index of left-most brace
int rbrace = pattern.findClosingIndexOf(lbrace);// index of right-most brace

// No, mismatched or escaped braces..Move along..nothing to see here
if (lbrace == -1 || rbrace == -1 ||
lbrace > 0 && pattern.bytes[lbrace-1] == '\\' ||
if (lbrace == -1 || rbrace == -1 ||
lbrace > 0 && pattern.bytes[lbrace-1] == '\\' ||
rbrace > 0 && pattern.bytes[rbrace-1] == '\\') {
ByteList unescaped = new ByteList(pattern.bytes.length-1);
ByteList unescaped = new ByteList(pattern.bytes.length - 1);
for (int i = pattern.begin; i < pattern.end; i++) {
byte b = pattern.bytes[i];
if (b == '\\' && i < pattern.bytes.length - 1) {
@@ -436,12 +437,12 @@ private static int push_braces(POSIX posix, String cwd, List<ByteList> result, G
unescaped.append(b);
}
}
return push_globs(posix, cwd, result, unescaped.getUnsafeBytes(), unescaped.begin(), unescaped.length(), pattern.flags);
return push_globs(posix, cwd, result, unescaped, pattern.flags);
}

// Peel onion...make subpatterns out of outer layer of glob and recall with each subpattern
// Peel onion...make subpatterns out of outer layer of glob and recall with each subpattern
// Example: foo{a{c},b}bar -> fooa{c}bar, foobbar
ByteList buf = new ByteList(20);
final ByteList bytes = new ByteList(20);
int middleRegionIndex;
int i = lbrace;
while (pattern.bytes[i] != '}') {
@@ -450,20 +451,20 @@ private static int push_braces(POSIX posix, String cwd, List<ByteList> result, G
if (pattern.bytes[i] == '{') i = pattern.findClosingIndexOf(i); // skip inner braces
}

buf.length(0);
buf.append(pattern.bytes, pattern.begin, lbrace - pattern.begin);
buf.append(pattern.bytes, middleRegionIndex, i - middleRegionIndex);
buf.append(pattern.bytes, rbrace + 1, pattern.end - (rbrace + 1));
int status = push_braces(posix, cwd, result, new GlobPattern(buf.getUnsafeBytes(), buf.getBegin(), buf.getRealSize(),pattern.flags));
if(status != 0) return status;
bytes.length(0);
bytes.append(pattern.bytes, pattern.begin, lbrace - pattern.begin);
bytes.append(pattern.bytes, middleRegionIndex, i - middleRegionIndex);
bytes.append(pattern.bytes, rbrace + 1, pattern.end - (rbrace + 1));
int status = push_braces(posix, cwd, result, new GlobPattern(bytes, pattern.flags));
if (status != 0) return status;
}

return 0; // All braces pushed..
}

private static int push_globs(POSIX posix, String cwd, List<ByteList> ary, byte[] pattern, int pbegin, int pend, int pflags) {
pflags |= FNM_SYSCASE;
return glob_helper(posix, cwd, pattern, pbegin, pend, -1, pflags, glob_caller, new GlobArgs(push_pattern, ary));
private static int push_globs(POSIX posix, String cwd, List<ByteList> ary, ByteList pattern, int flags) {
flags |= FNM_SYSCASE;
return glob_helper(posix, cwd, pattern, -1, flags, glob_caller, new GlobArgs(push_pattern, ary));
}

private static boolean has_magic(byte[] bytes, int begin, int end, int flags) {
@@ -472,7 +473,7 @@ private static boolean has_magic(byte[] bytes, int begin, int end, int flags) {
int open = 0;

for (int i = begin; i < end; i++) {
switch(bytes[i]) {
switch (bytes[i]) {
case '?':
case '*':
return true;
@@ -488,36 +489,34 @@ private static boolean has_magic(byte[] bytes, int begin, int end, int flags) {

break;
default:
if (FNM_SYSCASE == 0 && nocase && Character.isLetter((char)(bytes[i]&0xFF))) return true;
if (FNM_SYSCASE == 0 && nocase && Character.isLetter((char)(bytes[i] & 0xFF))) return true;
}
}

return false;
}

private static int remove_backslashes(byte[] bytes, int index, int len) {
int t = index;

for (; index < len; index++, t++) {
if (bytes[index] == '\\' && ++index == len) break;

bytes[t] = bytes[index];
private static int remove_backslashes(byte[] bytes, int index, int end) {
int i = index;
for ( ; index < end; index++, i++ ) {
if (bytes[index] == '\\' && ++index == end) break;

bytes[i] = bytes[index];
}

return t;
return i;
}

private static int strchr(byte[] bytes, int begin, int end, byte ch) {
for (int i = begin; i < end; i++) {
if (bytes[i] == ch) return i;
private static int indexOf(byte[] bytes, int begin, int end, final byte ch) {
for ( int i = begin; i < end; i++ ) {
if ( bytes[i] == ch ) return i;
}

return -1;
}

private static byte[] extract_path(byte[] bytes, int begin, int end) {
int len = end - begin;

if (len > 1 && bytes[end-1] == '/' && (!DOSISH || (len < 2 || bytes[end-2] != ':'))) len--;

byte[] alloc = new byte[len];
@@ -526,60 +525,53 @@ private static byte[] extract_path(byte[] bytes, int begin, int end) {
}

private static byte[] extract_elem(byte[] bytes, int begin, int end) {
int elementEnd = strchr(bytes, begin, end, (byte)'/');
int elementEnd = indexOf(bytes, begin, end, (byte)'/');
if (elementEnd == -1) elementEnd = end;

return extract_path(bytes, begin, elementEnd);
}

// Win drive letter X:/
private static boolean beginsWithDriveLetter(byte[] path, int begin, int end) {
return DOSISH && begin + 2 < end && path[begin + 1] == ':' && isdirsep(path[begin + 2]);
return DOSISH && begin + 2 < end && path[begin + 1] == ':' && isdirsep(path[begin + 2]);
}

// Is this nothing or literally root directory for the OS.
private static boolean isRoot(byte[] base) {
int length = base.length;

return length == 0 || // empty
length == 1 && isdirsep(base[0]) || // Just '/'
length == 3 && beginsWithDriveLetter(base, 0, length); // Just X:/
length == 3 && beginsWithDriveLetter(base, 0, length); // Just X:/
}

private static boolean isAbsolutePath(byte[] path, int begin, int length) {
return isdirsep(path[begin]) || beginsWithDriveLetter(path, begin, length);
}

private static String[] files(FileResource directory) {
String[] files = directory.list();

if (files != null) {
return files;
} else {
return new String[0];
}
private static String[] files(final FileResource directory) {
final String[] files = directory.list();
return files == null ? new String[0] : files;
}

private static final class DirGlobber {
public final ByteList link;

public DirGlobber(ByteList link) {
this.link = link;
}
DirGlobber(ByteList link) { this.link = link; }
}

private static boolean isSpecialFile(String name) {
int length = name.length();

if (length < 1 || length > 3 || name.charAt(0) != '.') return false;
if (length == 1) return true;
char c = name.charAt(1);
if (length == 2 && (c == '.' || c == '/')) return true;
return c == '.' && name.charAt(2) == '/';
}

private static int addToResultIfExists(POSIX posix, String cwd, byte[] bytes, int begin, int end, int flags, GlobFunc func, GlobArgs arg) {
String fileName = newStringFromUTF8(bytes, begin, end - begin);
private static int addToResultIfExists(POSIX posix, String cwd, byte[] bytes, int begin, int end, int flags, GlobFunc<GlobArgs> func, GlobArgs arg) {
final String fileName = newStringFromUTF8(bytes, begin, end - begin);

// FIXME: Ultimately JRubyFile.createResource should do this but all 1.7.x is only selectively honoring raw
// paths and using system drive make it absolute. MRI does this on many methods we don't.
@@ -597,7 +589,7 @@ private static int addToResultIfExists(POSIX posix, String cwd, byte[] bytes, in
// On case-insenstive file systems any case string will 'exists',
// but what does it display as if you ls/dir it?
/* No idea what this is doing =/
if ((flags & FNM_CASEFOLD) != 0 && !isSpecialFile(fileName)) {
try {
String realName = file.getCanonicalFile().getName();
@@ -618,155 +610,155 @@ private static int addToResultIfExists(POSIX posix, String cwd, byte[] bytes, in
end = bytes.length;
} catch (Exception e) {} // Failure will just use what we pass in
}*/

return func.call(bytes, begin, end - begin, arg);
}

return 0;
}

private static int glob_helper(POSIX posix, String cwd, byte[] bytes, int begin, int end, int sub, int flags, GlobFunc func, GlobArgs arg) {
int p,m;
private static int glob_helper(POSIX posix, String cwd, ByteList path, int sub, int flags, GlobFunc<GlobArgs> func, GlobArgs arg) {
final int begin = path.getBegin();
final int end = begin + path.getRealSize();
return glob_helper(posix, cwd, path.getUnsafeBytes(), begin, end, sub, flags, func, arg);
}

private static int glob_helper(POSIX posix, String cwd,
byte[] path, int begin, int end, int sub,
final int flags, GlobFunc<GlobArgs> func, GlobArgs arg) {
int status = 0;
byte[] newpath = null;
FileResource st;
p = sub != -1 ? sub : begin;

if (!has_magic(bytes, p, end, flags)) {
if (DOSISH || (flags & FNM_NOESCAPE) == 0) {
newpath = new byte[end];
System.arraycopy(bytes,0,newpath,0,end);
if (sub != -1) {
p = (sub - begin);
end = remove_backslashes(newpath, p, end);
sub = p;
} else {
end = remove_backslashes(newpath, 0, end);
bytes = newpath;

int p = sub != -1 ? sub : begin;

if ( ! has_magic(path, p, end, flags) ) {
if ( DOSISH || (flags & FNM_NOESCAPE) == 0 ) {
if ( sub != -1 ) { // can modify path (our internal buf[])
end = remove_backslashes(path, sub, end);
}
else {
final int len = end - begin;
final byte[] newPath = new byte[len];
System.arraycopy(path, begin, newPath, 0, len);
begin = 0; end = remove_backslashes(newPath, 0, len);
path = newPath;
}
}

if (isAbsolutePath(bytes, begin, end)) {
status = addToResultIfExists(posix, null, bytes, begin, end, flags, func, arg);
} else if ((end - begin) > 0) { // Length check is a hack. We should not be reeiving "" as a filename ever.
status = addToResultIfExists(posix, cwd, bytes, begin, end, flags, func, arg);
if ( (end - begin) > 0 ) {
if ( isAbsolutePath(path, begin, end) ) {
status = addToResultIfExists(posix, null, path, begin, end, flags, func, arg);
} else {
status = addToResultIfExists(posix, cwd, path, begin, end, flags, func, arg);
}
}

return status;
}

ByteList buf = new ByteList(20);
List<DirGlobber> link = new ArrayList<DirGlobber>();

final ArrayList<DirGlobber> links = new ArrayList<DirGlobber>();

ByteList buf = new ByteList(20); FileResource resource;
mainLoop: while(p != -1 && status == 0) {
if (bytes[p] == '/') p++;
if ( path[p] == '/' ) p++;

m = strchr(bytes, p, end, (byte)'/');
if(has_magic(bytes, p, m == -1 ? end : m, flags)) {
final int s = indexOf(path, p, end, (byte) '/');
if ( has_magic(path, p, s == -1 ? end : s, flags) ) {
finalize: do {
byte[] base = extract_path(bytes, begin, p);
byte[] dir = begin == p ? new byte[]{'.'} : base;
byte[] magic = extract_elem(bytes,p,end);
byte[] base = extract_path(path, begin, p);
byte[] dir = begin == p ? new byte[] { '.' } : base;
byte[] magic = extract_elem(path, p, end);
boolean recursive = false;

st = JRubyFile.createResource(posix, cwd, newStringFromUTF8(dir));
resource = JRubyFile.createResource(posix, cwd, newStringFromUTF8(dir, 0, dir.length));

if (st.isDirectory()) {
if(m != -1 && Arrays.equals(magic, DOUBLE_STAR)) {
int n = base.length;
if ( resource.isDirectory() ) {
if ( s != -1 && Arrays.equals(magic, DOUBLE_STAR) ) {
final int n = base.length;
recursive = true;
buf.length(0);
buf.append(base);
buf.append(bytes, (base.length > 0 ? m : m + 1), end - (base.length > 0 ? m : m + 1));
status = glob_helper(posix, cwd, buf.getUnsafeBytes(), buf.getBegin(), buf.getRealSize(), n, flags, func, arg);
if(status != 0) {
break finalize;
}
buf.append(path, (n > 0 ? s : s + 1), end - (n > 0 ? s : s + 1));
status = glob_helper(posix, cwd, buf, n, flags, func, arg);
if ( status != 0 ) break finalize;
}
} else {
break mainLoop;
}

String[] dirp = files(st);
final String[] files = files(resource);

for(int i=0;i<dirp.length;i++) {
if(recursive) {
byte[] bs = getBytesInUTF8(dirp[i]);
if (fnmatch(STAR,0,1,bs,0,bs.length,flags) != 0) {
for ( int i = 0; i < files.length; i++ ) {
final String file = files[i];
final byte[] fileBytes = getBytesInUTF8(file);
if (recursive) {
if ( fnmatch(STAR, 0, 1, fileBytes, 0, fileBytes.length, flags) != 0) {
continue;
}
buf.length(0);
buf.append(base);
buf.append(isRoot(base) ? EMPTY : SLASH );
buf.append(getBytesInUTF8(dirp[i]));
st = JRubyFile.createResource(posix, cwd, newStringFromUTF8(buf.getUnsafeBytes(), buf.getBegin(), buf.getRealSize()));
if(!st.isSymLink() && st.isDirectory() && !".".equals(dirp[i]) && !"..".equals(dirp[i])) {
int t = buf.getRealSize();
buf.append( isRoot(base) ? EMPTY : SLASH );
buf.append( getBytesInUTF8(file) );
resource = JRubyFile.createResource(posix, cwd, newStringFromUTF8(buf));
if ( !resource.isSymLink() && resource.isDirectory() && !".".equals(file) && !"..".equals(file) ) {
final int len = buf.getRealSize();
buf.append(SLASH);
buf.append(DOUBLE_STAR);
buf.append(bytes, m, end - m);
status = glob_helper(posix, cwd, buf.getUnsafeBytes(), buf.getBegin(), buf.getRealSize(), t, flags, func, arg);
if(status != 0) {
break;
}
buf.append(path, s, end - s);
status = glob_helper(posix, cwd, buf, buf.getBegin() + len, flags, func, arg);
if ( status != 0 ) break;
}
continue;
}
byte[] bs = getBytesInUTF8(dirp[i]);
if(fnmatch(magic,0,magic.length,bs,0, bs.length,flags) == 0) {
if ( fnmatch(magic, 0, magic.length, fileBytes, 0, fileBytes.length, flags) == 0 ) {
buf.length(0);
buf.append(base);
buf.append(isRoot(base) ? EMPTY : SLASH );
buf.append(getBytesInUTF8(dirp[i]));
if(m == -1) {
status = func.call(buf.getUnsafeBytes(),0, buf.getRealSize(),arg);
if(status != 0) {
break;
}
buf.append( isRoot(base) ? EMPTY : SLASH );
buf.append( getBytesInUTF8(file) );
if ( s == -1 ) {
status = func.call(buf.getUnsafeBytes(), 0, buf.getRealSize(), arg);
if ( status != 0 ) break;
continue;
}
link.add(new DirGlobber(buf));
links.add(new DirGlobber(buf));
buf = new ByteList(20);
}
}
} while(false);

if (link.size() > 0) {
for (DirGlobber globber : link) {
ByteList b = globber.link;
if (status == 0) {
st = JRubyFile.createResource(posix, cwd, newStringFromUTF8(b.getUnsafeBytes(), 0, b.getRealSize()));
if(st.isDirectory()) {
int len = b.getRealSize();
if ( links.size() > 0 ) {
for ( DirGlobber globber : links ) {
final ByteList link = globber.link;
if ( status == 0 ) {
resource = JRubyFile.createResource(posix, cwd, newStringFromUTF8(link));
if ( resource.isDirectory() ) {
final int len = link.getRealSize();
buf.length(0);
buf.append(b);
buf.append(bytes, m, end - m);
status = glob_helper(posix, cwd, buf.getUnsafeBytes(),0, buf.getRealSize(),len,flags,func,arg);
buf.append(link);
buf.append(path, s, end - s);
status = glob_helper(posix, cwd, buf, buf.getBegin() + len, flags, func, arg);
}
}
}
break mainLoop;
}
}
p = m;
p = s;
}
return status;
}

private static ByteList fixBytesForJarInUTF8(byte[] buf, int offset, int len) {
String path = newStringFromUTF8(buf, offset, len);
path = path.replace(".jar/", ".jar!");
return new ByteList(path.getBytes());
private static byte[] getBytesInUTF8(final String str) {
return RubyEncoding.encodeUTF8(str);
}

private static byte[] getBytesInUTF8(String s) {
return RubyEncoding.encodeUTF8(s);
private static String newStringFromUTF8(final ByteList bytes) {
final int offset = bytes.getBegin();
final int length = bytes.getRealSize();
return RubyEncoding.decodeUTF8(bytes.getUnsafeBytes(), offset, length);
}

private static String newStringFromUTF8(byte[] buf, int offset, int len) {
return RubyEncoding.decodeUTF8(buf, offset, len);
private static String newStringFromUTF8(final byte[] bytes, int offset, int len) {
return RubyEncoding.decodeUTF8(bytes, offset, len);
}

private static String newStringFromUTF8(byte[] buf) {
return RubyEncoding.decodeUTF8(buf);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
require 'base64'

NUM_BYTES = 1000

rand = Random.new

describe "A badly-encoded UTF-8 String reencoded with replacements as UTF-16 " do
it "completes for all inputs" do
random = Random.new
# We obviously can't test all valid inputs, but we use the script from #2856 to try
10_000.times do
data = rand.bytes(NUM_BYTES)
data = random.bytes(1000)
data.force_encoding("UTF-8")
data = data.encode("UTF-16", :undef => :replace, :invalid => :replace, :replace => '')

expect(data).to_not eq(nil)
end
end
end
end unless RUBY_VERSION.index('1.8') == 0
46 changes: 25 additions & 21 deletions test/test_dir.rb
Original file line number Diff line number Diff line change
@@ -24,22 +24,22 @@ def teardown

# JRUBY-2519
def test_dir_instance_should_not_cache_dir_contents

require 'fileutils'
require 'tmpdir'

testdir = File.join(Dir.tmpdir, Process.pid.to_s)
FileUtils.mkdir_p testdir

FileUtils.touch File.join(testdir, 'fileA.txt')
dir = Dir.new(testdir)
FileUtils.touch File.join(testdir, 'fileB.txt')
dir.rewind # does nothing

assert_equal 'fileA.txt', dir.find {|item| item == 'fileA.txt' }
assert_equal 'fileB.txt', dir.find {|item| item == 'fileB.txt' }
end
end

def test_pwd_and_getwd_equivalent
assert_equal(Dir.pwd, Dir.getwd)
end
@@ -72,7 +72,7 @@ def test_dir_entries
end

def test_bogus_glob
# Test unescaped special char that is meant to be used with another
# Test unescaped special char that is meant to be used with another
# (i.e. bogus glob pattern)
assert_equal([], Dir.glob("{"))
end
@@ -83,7 +83,7 @@ def test_glob_empty_string
end

def test_glob_double_star
# Test that glob expansion of ** works ok with non-patterns as path
# Test that glob expansion of ** works ok with non-patterns as path
# elements. This used to throw NPE.
Dir.mkdir("testDir_2")
open("testDir_2/testDir_tmp1", "w").close
@@ -114,6 +114,10 @@ def test_glob_on_shared_string
Dir["blahtest/test_argf.rb"[4..-1]]
end

def test_glob_empty_parens
assert_equal [], Dir['{}'] # #2922 throwing AIOoBE is <= 1.7.20
end

# http://jira.codehaus.org/browse/JRUBY-300
def test_chdir_and_pwd
java_test_classes = File.expand_path(File.dirname(__FILE__) + '/../test/target/test-classes')
@@ -124,7 +128,7 @@ def test_chdir_and_pwd
pwd.gsub! '\\', '/'
assert_equal("testDir_4", pwd.split("/")[-1].strip)

if (ENV_JAVA['jruby.home'] and not
if (ENV_JAVA['jruby.home'] and not
ENV_JAVA['jruby.home'].match( /!\// ) and not
ENV_JAVA['jruby.home'].match( /:\// ))
pwd = `#{ENV_JAVA['jruby.home']}/bin/jruby -e "puts Dir.pwd"`
@@ -217,7 +221,7 @@ def xxx_test_mktmpdir
# JRUBY-4983
def test_entries_unicode
utf8_dir = "testDir_1/glk\u00a9"

Dir.mkdir("./testDir_1")
Dir.mkdir(utf8_dir)

@@ -269,7 +273,7 @@ def restore_cwd
def test_chdir_exceptions_windows
orig_pwd = Dir.pwd
assert_raise(Errno::EINVAL) {
Dir.chdir('//') # '//' is not a valid thing on Windows
Dir.chdir('//') # '//' is not a valid thing on Windows
}
assert_raise(Errno::ENOENT) {
Dir.chdir('//blah-blah-blah') # doesn't exist
@@ -289,7 +293,7 @@ def test_chdir_exceptions_windows
ensure
Dir.chdir(orig_pwd)
end

def test_new_windows
slashes = ['/', '\\']
slashes.each { |slash|
@@ -306,7 +310,7 @@ def test_new_windows
assert_equal(drive_root_entries, slash_entries, "slash - #{slash}")
}
end

def test_new_with_drive_letter
current_drive_letter = Dir.pwd[0..2]

@@ -318,7 +322,7 @@ def test_new_with_drive_letter
Dir.new(current_drive_letter + "\\").entries,
Dir.new(current_drive_letter).entries)
end

def test_entries_with_drive_letter
current_drive_letter = Dir.pwd[0..2]

@@ -330,7 +334,7 @@ def test_entries_with_drive_letter
Dir.entries(current_drive_letter + "\\"),
Dir.entries(current_drive_letter))
end

def test_open_windows
slashes = ['/', '\\']
slashes.each { |slash|
@@ -347,13 +351,13 @@ def test_open_windows
assert_equal(drive_root_entries, slash_entries, "slash - #{slash}")
}
end

def test_dir_new_exceptions_windows
assert_raise(Errno::ENOENT) {
Dir.new('')
}
assert_raise(Errno::EINVAL) {
Dir.new('//') # '//' is not a valid thing on Windows
Dir.new('//') # '//' is not a valid thing on Windows
}
assert_raise(Errno::ENOENT) {
Dir.new('//blah-blah-blah') # doesn't exist
@@ -371,7 +375,7 @@ def test_dir_new_exceptions_windows
Dir.new('\\\\\\') # doesn't exist
}
end

def test_entries_windows
slashes = ['/', '\\']
slashes.each { |slash|
@@ -387,7 +391,7 @@ def test_entries_exceptions_windows
Dir.entries('')
}
assert_raise(Errno::EINVAL) {
Dir.entries('//') # '//' is not a valid thing on Windows
Dir.entries('//') # '//' is not a valid thing on Windows
}
assert_raise(Errno::ENOENT) {
Dir.entries('//blah-blah-blah') # doesn't exist
@@ -446,11 +450,11 @@ def test_pathname_realpath_works_with_drive_letters
require 'pathname'
win_dir = nil
if FileTest.exist?('C:/windows')
win_dir = "windows"
win_dir = "windows"
elsif FileTest.exist?('C:/winnt')
win_dir = "winnt"
win_dir = "winnt"
end

if (win_dir != nil)
Pathname.new("C:\\#{win_dir}").realpath.to_s
Pathname.new("C:\\#{win_dir}\\..\\#{win_dir}").realpath.to_s