53
53
import java .nio .file .Paths ;
54
54
import java .nio .file .attribute .FileTime ;
55
55
import java .nio .file .attribute .PosixFileAttributeView ;
56
+ import java .util .ArrayList ;
56
57
import java .util .Enumeration ;
57
58
import java .util .concurrent .TimeUnit ;
58
59
import java .util .jar .JarFile ;
@@ -199,6 +200,7 @@ public static RubyClass createFileClass(Ruby runtime) {
199
200
constants .setConstant ("FNM_SYSCASE" , runtime .newFixnum (FNM_SYSCASE ));
200
201
constants .setConstant ("FNM_DOTMATCH" , runtime .newFixnum (FNM_DOTMATCH ));
201
202
constants .setConstant ("FNM_PATHNAME" , runtime .newFixnum (FNM_PATHNAME ));
203
+ constants .setConstant ("FNM_EXTGLOB" , runtime .newFixnum (FNM_EXTGLOB ));
202
204
203
205
// flock operations
204
206
constants .setConstant ("LOCK_SH" , runtime .newFixnum (RubyFile .LOCK_SH ));
@@ -765,7 +767,7 @@ public static IRubyObject expand_path(ThreadContext context, IRubyObject recv, I
765
767
766
768
@ JRubyMethod (name = "expand_path" , required = 1 , optional = 1 , meta = true )
767
769
public static IRubyObject expand_path19 (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
768
- RubyString path = (RubyString ) expandPathInternal (context , recv , args , true );
770
+ RubyString path = (RubyString ) expandPathInternal (context , recv , args , true , false );
769
771
path .force_encoding (context , context .runtime .getEncodingService ().getDefaultExternal ());
770
772
771
773
return path ;
@@ -793,25 +795,21 @@ public static IRubyObject expand_path19(ThreadContext context, IRubyObject recv,
793
795
*/
794
796
@ JRubyMethod (required = 1 , optional = 1 , meta = true )
795
797
public static IRubyObject absolute_path (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
796
- return expandPathInternal (context , recv , args , false );
798
+ return expandPathInternal (context , recv , args , false , false );
797
799
}
798
800
799
801
@ JRubyMethod (required = 1 , optional = 1 , meta = true )
800
802
public static IRubyObject realdirpath (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
801
- return expandPathInternal (context , recv , args , false );
803
+ return expandPathInternal (context , recv , args , false , false );
802
804
}
803
805
804
806
@ JRubyMethod (required = 1 , optional = 1 , meta = true )
805
807
public static IRubyObject realpath (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
806
- IRubyObject file = expandPathInternal (context , recv , args , false );
808
+ IRubyObject file = expandPathInternal (context , recv , args , false , true );
807
809
if (!RubyFileTest .exist_p (recv , file ).isTrue ()) {
808
810
throw context .runtime .newErrnoENOENTError (file .toString ());
809
811
}
810
- try {
811
- return context .runtime .newString (new File (file .toString ()).getCanonicalPath ());
812
- } catch (IOException ioex ) {
813
- throw context .runtime .newErrnoENOENTError (file .toString ());
814
- }
812
+ return file ;
815
813
}
816
814
817
815
/**
@@ -826,16 +824,41 @@ public static IRubyObject realpath(ThreadContext context, IRubyObject recv, IRub
826
824
@ JRubyMethod (name = {"fnmatch" , "fnmatch?" }, required = 2 , optional = 1 , meta = true )
827
825
public static IRubyObject fnmatch (ThreadContext context , IRubyObject recv , IRubyObject [] args ) {
828
826
int flags = args .length == 3 ? RubyNumeric .num2int (args [2 ]) : 0 ;
827
+ boolean braces_match = false ;
828
+ boolean extglob = (flags & FNM_EXTGLOB ) != 0 ;
829
829
830
830
ByteList pattern = args [0 ].convertToString ().getByteList ();
831
831
ByteList path = get_path (context , args [1 ]).getByteList ();
832
832
833
- if (org .jruby .util .Dir .fnmatch (pattern .getUnsafeBytes (), pattern .getBegin (), pattern .getBegin ()+pattern .getRealSize (), path .getUnsafeBytes (), path .getBegin (), path .getBegin ()+path .getRealSize (), flags ) == 0 ) {
833
+ if (extglob ) {
834
+ String spattern = args [0 ].asJavaString ();
835
+ ArrayList <String > patterns = org .jruby .util .Dir .braces (spattern , flags , new ArrayList <String >());
836
+
837
+ ArrayList <Boolean > matches = new ArrayList <Boolean >();
838
+ for (int i = 0 ; i < patterns .size (); i ++) {
839
+ String p = patterns .get (i );
840
+ boolean match = dir_fnmatch (new ByteList (p .getBytes ()), path , flags );
841
+ matches .add (match );
842
+ }
843
+ braces_match = matches .contains (true );
844
+ }
845
+
846
+ if (braces_match || dir_fnmatch (pattern , path , flags )) {
834
847
return context .runtime .getTrue ();
835
848
}
836
849
return context .runtime .getFalse ();
837
850
}
838
-
851
+
852
+ private static boolean dir_fnmatch (ByteList pattern , ByteList path , int flags ) {
853
+ return org .jruby .util .Dir .fnmatch (pattern .getUnsafeBytes (),
854
+ pattern .getBegin (),
855
+ pattern .getBegin ()+pattern .getRealSize (),
856
+ path .getUnsafeBytes (),
857
+ path .getBegin (),
858
+ path .getBegin ()+path .getRealSize (),
859
+ flags ) == 0 ;
860
+ }
861
+
839
862
@ JRubyMethod (name = "ftype" , required = 1 , meta = true )
840
863
public static IRubyObject ftype (ThreadContext context , IRubyObject recv , IRubyObject filename ) {
841
864
return context .runtime .newFileStat (get_path (context , filename ).getUnicodeValue (), true ).ftype ();
@@ -1462,7 +1485,7 @@ private static boolean isWindowsDriveLetter(char c) {
1462
1485
return (c >= 'a' && c <= 'z' ) || (c >= 'A' && c <= 'Z' );
1463
1486
}
1464
1487
1465
- private static IRubyObject expandPathInternal (ThreadContext context , IRubyObject recv , IRubyObject [] args , boolean expandUser ) {
1488
+ private static IRubyObject expandPathInternal (ThreadContext context , IRubyObject recv , IRubyObject [] args , boolean expandUser , boolean canonicalize ) {
1466
1489
Ruby runtime = context .runtime ;
1467
1490
1468
1491
String relativePath = get_path (context , args [0 ]).getUnicodeValue ();
@@ -1493,6 +1516,9 @@ private static IRubyObject expandPathInternal(ThreadContext context, IRubyObject
1493
1516
relativePath = uriParts [1 ];
1494
1517
}
1495
1518
1519
+ // Now that we're not treating it as a URI, we need to honor the canonicalize flag.
1520
+ // Do not insert early returns below.
1521
+
1496
1522
// If there's a second argument, it's the path to which the first
1497
1523
// argument is relative.
1498
1524
if (args .length == 2 && !args [1 ].isNil ()) {
@@ -1582,7 +1608,17 @@ private static IRubyObject expandPathInternal(ThreadContext context, IRubyObject
1582
1608
path = JRubyFile .create (cwd , relativePath );
1583
1609
}
1584
1610
1585
- return runtime .newString (padSlashes + canonicalize (path .getAbsolutePath ()));
1611
+ String realPath = padSlashes + canonicalize (path .getAbsolutePath ());
1612
+
1613
+ if (canonicalize ) {
1614
+ try {
1615
+ realPath = new File (realPath ).getCanonicalPath ();
1616
+ } catch (IOException ioe ) {
1617
+ // Earlier canonicalization will have to do.
1618
+ }
1619
+ }
1620
+
1621
+ return runtime .newString (realPath );
1586
1622
}
1587
1623
1588
1624
public static String [] splitURI (String path ) {
@@ -1886,6 +1922,7 @@ public IRubyObject initialize19(IRubyObject[] args, Block block) {
1886
1922
private static final int FNM_PATHNAME = 2 ;
1887
1923
private static final int FNM_DOTMATCH = 4 ;
1888
1924
private static final int FNM_CASEFOLD = 8 ;
1925
+ private static final int FNM_EXTGLOB = 16 ;
1889
1926
private static final int FNM_SYSCASE = Platform .IS_WINDOWS ? FNM_CASEFOLD : 0 ;
1890
1927
1891
1928
private static final String [] SLASHES = {"" , "/" , "//" };
0 commit comments