13514
|
1 /*
|
|
2 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This
|
|
3 * code is released under a tri EPL/GPL/LGPL license. You can use it,
|
|
4 * redistribute it and/or modify it under the terms of the:
|
|
5 *
|
|
6 * Eclipse Public License version 1.0
|
|
7 * GNU General Public License version 2
|
|
8 * GNU Lesser General Public License version 2.1
|
|
9 */
|
|
10 package com.oracle.truffle.ruby.nodes.core;
|
|
11
|
|
12 import java.io.*;
|
|
13 import java.nio.file.*;
|
|
14 import java.nio.file.attribute.*;
|
|
15
|
|
16 import com.oracle.truffle.api.CompilerDirectives.SlowPath;
|
|
17 import com.oracle.truffle.api.*;
|
|
18 import com.oracle.truffle.api.dsl.*;
|
|
19 import com.oracle.truffle.api.frame.*;
|
|
20 import com.oracle.truffle.ruby.runtime.*;
|
|
21 import com.oracle.truffle.ruby.runtime.core.*;
|
|
22 import com.oracle.truffle.ruby.runtime.core.array.*;
|
|
23
|
|
24 @CoreClass(name = "Dir")
|
|
25 public abstract class DirNodes {
|
|
26
|
|
27 @CoreMethod(names = "[]", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1)
|
|
28 public abstract static class GlobNode extends CoreMethodNode {
|
|
29
|
|
30 public GlobNode(RubyContext context, SourceSection sourceSection) {
|
|
31 super(context, sourceSection);
|
|
32 }
|
|
33
|
|
34 public GlobNode(GlobNode prev) {
|
|
35 super(prev);
|
|
36 }
|
|
37
|
|
38 @Specialization
|
|
39 public RubyArray glob(RubyString glob) {
|
|
40 return glob(getContext(), glob.toString());
|
|
41 }
|
|
42
|
|
43 @SlowPath
|
|
44 private static RubyArray glob(final RubyContext context, String glob) {
|
|
45 /*
|
|
46 * Globbing is quite complicated. We've implemented a subset of the functionality that
|
|
47 * satisfies MSpec, but it will likely break for anyone else.
|
|
48 */
|
|
49
|
|
50 context.implementationMessage("globbing %s", glob);
|
|
51
|
|
52 String absoluteGlob;
|
|
53
|
|
54 if (!glob.startsWith("/")) {
|
|
55 absoluteGlob = new File(".", glob).getAbsolutePath().toString();
|
|
56 } else {
|
|
57 absoluteGlob = glob;
|
|
58 }
|
|
59
|
|
60 // Get the first star
|
|
61
|
|
62 final int firstStar = absoluteGlob.indexOf('*');
|
|
63 assert firstStar >= 0;
|
|
64
|
|
65 // Walk back from that to the first / before that star
|
|
66
|
|
67 int prefixLength = firstStar;
|
|
68
|
|
69 while (prefixLength > 0 && absoluteGlob.charAt(prefixLength) == File.separatorChar) {
|
|
70 prefixLength--;
|
|
71 }
|
|
72
|
|
73 final String prefix = absoluteGlob.substring(0, prefixLength - 1);
|
|
74
|
|
75 final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + absoluteGlob.substring(prefixLength));
|
|
76
|
|
77 final RubyArray array = new RubyArray(context.getCoreLibrary().getArrayClass());
|
|
78
|
|
79 try {
|
|
80 Files.walkFileTree(FileSystems.getDefault().getPath(prefix), new SimpleFileVisitor<Path>() {
|
|
81
|
|
82 @Override
|
|
83 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
|
84 if (matcher.matches(file)) {
|
|
85 array.push(context.makeString(file.toString()));
|
|
86 }
|
|
87
|
|
88 return FileVisitResult.CONTINUE;
|
|
89 }
|
|
90
|
|
91 });
|
|
92 } catch (IOException e) {
|
|
93 throw new RuntimeException(e);
|
|
94 }
|
|
95
|
|
96 return array;
|
|
97 }
|
|
98
|
|
99 }
|
|
100
|
|
101 @CoreMethod(names = "chdir", isModuleMethod = true, needsSelf = false, needsBlock = true, minArgs = 1, maxArgs = 1)
|
|
102 public abstract static class ChdirNode extends YieldingCoreMethodNode {
|
|
103
|
|
104 public ChdirNode(RubyContext context, SourceSection sourceSection) {
|
|
105 super(context, sourceSection);
|
|
106 }
|
|
107
|
|
108 public ChdirNode(ChdirNode prev) {
|
|
109 super(prev);
|
|
110 }
|
|
111
|
|
112 @Specialization
|
|
113 public Object chdir(VirtualFrame frame, RubyString path, RubyProc block) {
|
|
114 final RubyContext context = getContext();
|
|
115
|
|
116 final String previous = context.getCurrentDirectory();
|
|
117 context.setCurrentDirectory(path.toString());
|
|
118
|
|
119 if (block != null) {
|
|
120 try {
|
|
121 return yield(frame, block, path);
|
|
122 } finally {
|
|
123 context.setCurrentDirectory(previous);
|
|
124 }
|
|
125 } else {
|
|
126 return 0;
|
|
127 }
|
|
128 }
|
|
129
|
|
130 }
|
|
131
|
|
132 @CoreMethod(names = {"exist?", "exists?"}, isModuleMethod = true, needsSelf = false, maxArgs = 1)
|
|
133 public abstract static class ExistsNode extends CoreMethodNode {
|
|
134
|
|
135 public ExistsNode(RubyContext context, SourceSection sourceSection) {
|
|
136 super(context, sourceSection);
|
|
137 }
|
|
138
|
|
139 public ExistsNode(ExistsNode prev) {
|
|
140 super(prev);
|
|
141 }
|
|
142
|
|
143 @Specialization
|
|
144 public boolean exists(RubyString path) {
|
|
145 return new File(path.toString()).isDirectory();
|
|
146 }
|
|
147
|
|
148 }
|
|
149
|
|
150 @CoreMethod(names = "pwd", isModuleMethod = true, needsSelf = false, maxArgs = 0)
|
|
151 public abstract static class PwdNode extends CoreMethodNode {
|
|
152
|
|
153 public PwdNode(RubyContext context, SourceSection sourceSection) {
|
|
154 super(context, sourceSection);
|
|
155 }
|
|
156
|
|
157 public PwdNode(PwdNode prev) {
|
|
158 super(prev);
|
|
159 }
|
|
160
|
|
161 @Specialization
|
|
162 public RubyString pwd() {
|
|
163 return getContext().makeString(getContext().getCurrentDirectory());
|
|
164 }
|
|
165
|
|
166 }
|
|
167
|
|
168 }
|