Skip to content

Commit a6b9e84

Browse files
committed
[JVM] Support opening files with :read and :append
Makes some more test in S32-io/open.t pass. Compare also RT #131145. Thanks to cygx++ for giving a pointer on how this could be done.
1 parent a90c011 commit a6b9e84

File tree

1 file changed

+66
-15
lines changed

1 file changed

+66
-15
lines changed

src/vm/jvm/runtime/org/perl6/nqp/io/FileHandle.java

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import java.nio.file.Path;
1111
import java.nio.file.StandardOpenOption;
1212
import java.util.ArrayList;
13-
import java.util.Arrays;
1413
import java.util.List;
1514

1615
import org.perl6.nqp.runtime.ExceptionHandling;
@@ -20,8 +19,11 @@ public class FileHandle extends SyncHandle implements IIOSeekable, IIOLockable {
2019

2120
FileChannel fc;
2221
FileLock lock;
22+
private boolean truncate = false;
23+
private boolean create = false;
24+
private boolean append = false;
2325

24-
public static OpenOption[] resolveOpenMode(String mode) {
26+
public OpenOption[] resolveOpenMode(String mode) {
2527
if(mode.length() == 0)
2628
return null;
2729

@@ -52,6 +54,34 @@ public static OpenOption[] resolveOpenMode(String mode) {
5254
default : return null;
5355
}
5456

57+
/* work around differences between Perl 6 and FileChannel.open */
58+
List<OpenOption> optsToRemove = new ArrayList<OpenOption>();
59+
if (opts.contains(StandardOpenOption.READ)) {
60+
if (!opts.contains(StandardOpenOption.WRITE)) {
61+
/* TRUNCATE_EXISTING is ignored when the file is opened only for reading. */
62+
if (opts.contains(StandardOpenOption.TRUNCATE_EXISTING)) {
63+
truncate = true;
64+
optsToRemove.add(StandardOpenOption.TRUNCATE_EXISTING);
65+
}
66+
/* CREATE is ignored when the file is opened only for reading. */
67+
if (opts.contains(StandardOpenOption.CREATE)) {
68+
create = true;
69+
optsToRemove.add(StandardOpenOption.CREATE);
70+
}
71+
}
72+
/* APPEND may not be used in conjunction with READ. */
73+
if (opts.contains(StandardOpenOption.APPEND)) {
74+
append = true;
75+
optsToRemove.add(StandardOpenOption.APPEND);
76+
}
77+
}
78+
/* APPEND may not be used in conjunction with TRUNCATE_EXISTING. */
79+
else if (opts.contains(StandardOpenOption.TRUNCATE_EXISTING) && opts.contains(StandardOpenOption.APPEND)) {
80+
append = true;
81+
optsToRemove.add(StandardOpenOption.APPEND);
82+
}
83+
opts.removeAll(optsToRemove);
84+
5585
return opts.toArray(new OpenOption[opts.size()]);
5686
}
5787

@@ -63,19 +93,12 @@ public FileHandle(ThreadContext tc, String filename, String mode) {
6393
OpenOption[] opts = resolveOpenMode(mode);
6494
if(opts == null)
6595
ExceptionHandling.dieInternal(tc, "Unhandled file open mode '" + mode + "'");
66-
67-
// work around differences between Perl 6 and FileChannel.open
68-
if (Arrays.asList(opts).contains(StandardOpenOption.READ) && !Arrays.asList(opts).contains(StandardOpenOption.WRITE)) {
69-
// TRUNCATE_EXISTING is ignored when the file is opened only for reading.
70-
if (Arrays.asList(opts).contains(StandardOpenOption.TRUNCATE_EXISTING))
71-
if (Files.exists(p))
72-
Files.write(p, new byte[0]);
73-
// CREATE is ignored when the file is opened only for reading.
74-
if (Arrays.asList(opts).contains(StandardOpenOption.CREATE))
75-
if (!Files.exists(p))
76-
Files.createFile(p);
77-
}
78-
96+
if (truncate)
97+
if (Files.exists(p))
98+
Files.write(p, new byte[0]);
99+
if (create)
100+
if (!Files.exists(p))
101+
Files.createFile(p);
79102
fc = FileChannel.open(p, opts);
80103
chan = fc;
81104
setEncoding(tc, Charset.forName("UTF-8"));
@@ -84,6 +107,34 @@ public FileHandle(ThreadContext tc, String filename, String mode) {
84107
}
85108
}
86109

110+
public long write(ThreadContext tc, byte[] array) {
111+
if (append) {
112+
try {
113+
fc.position(fc.size());
114+
} catch (IOException e) {
115+
throw ExceptionHandling.dieInternal(tc, e);
116+
}
117+
/* Reset readBuffer and eof after calling fc.position. */
118+
readBuffer = null;
119+
eof = false;
120+
}
121+
return super.write(tc, array);
122+
}
123+
124+
public long print(ThreadContext tc, String s) {
125+
if (append) {
126+
try {
127+
fc.position(fc.size());
128+
} catch (IOException e) {
129+
throw ExceptionHandling.dieInternal(tc, e);
130+
}
131+
/* Reset readBuffer and eof after calling fc.position. */
132+
readBuffer = null;
133+
eof = false;
134+
}
135+
return super.print(tc, s);
136+
}
137+
87138
public void seek(ThreadContext tc, long offset, long whence) {
88139
try {
89140
switch ((int)whence) {

0 commit comments

Comments
 (0)