Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO.binwrite doesn't truncate the file if an offset is given #726

Merged
merged 3 commits into from May 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 0 additions & 2 deletions spec/tags/1.9/ruby/core/io/binwrite_tags.txt
@@ -1,5 +1,3 @@
fails:IO.binwrite doesn't truncate the file and writes the given string if an offset is given
fails:IO.binwrite accepts a :mode option
fails:IO.binwrite raises an error if readonly mode is specified
fails:IO.binwrite doesn't truncate and writes at the given offset after passing empty opts
fails:IO.binwrite truncates if empty :opts provided and offset skipped
34 changes: 7 additions & 27 deletions src/org/jruby/RubyFile.java
Expand Up @@ -331,15 +331,7 @@ public IRubyObject initialize19(ThreadContext context, IRubyObject[] args, Block
}
}

RubyBoolean hasOffset = context.runtime.newBoolean(false);
if (args[args.length - 1] instanceof RubyBoolean) {
hasOffset = (RubyBoolean) args[args.length - 1];
IRubyObject[] newArgs = new IRubyObject[args.length - 1];
System.arraycopy(args, 0, newArgs, 0, newArgs.length);
args = newArgs;
}

return openFile19(context, args, hasOffset.isTrue());
return openFile19(context, args);
}

@JRubyMethod(required = 1)
Expand Down Expand Up @@ -1112,11 +1104,8 @@ public void setEncoding(Encoding encoding) {
// :)
}

private IRubyObject openFile19(ThreadContext context, IRubyObject args[]) {
return openFile19(context, args, false);
}
// mri: rb_open_file + rb_scan_open_args
private IRubyObject openFile19(ThreadContext context, IRubyObject args[], boolean hasOffset) {
private IRubyObject openFile19(ThreadContext context, IRubyObject args[]) {
Ruby runtime = context.runtime;
RubyString filename = get_path(context, args[0]);

Expand Down Expand Up @@ -1157,8 +1146,8 @@ private IRubyObject openFile19(ThreadContext context, IRubyObject args[], boolea
int oflags = EncodingUtils.extractModeEncoding(context, this, pm, options, false);
int perm = (pm[EncodingUtils.PERM] != null && !pm[EncodingUtils.PERM].isNil()) ?
RubyNumeric.num2int(pm[EncodingUtils.PERM]) : 0666;
sysopenInternal(path, ModeFlags.createModeFlags(oflags), perm, hasOffset);

sysopenInternal(path, ModeFlags.createModeFlags(oflags), perm);

return this;
}
Expand Down Expand Up @@ -1195,12 +1184,8 @@ private int getFilePermissions(IRubyObject[] args) {
return (args.length > 2 && !args[2].isNil()) ? RubyNumeric.num2int(args[2]) : 438;
}

protected void sysopenInternal(String path, ModeFlags modes, int perm) {
sysopenInternal(path, modes, perm, false);
}

// mri: rb_file_open_generic
protected void sysopenInternal(String path, ModeFlags modes, int perm, boolean hasOffset) {
protected void sysopenInternal(String path, ModeFlags modes, int perm) {
if (path.startsWith("jar:")) path = path.substring(4);

openFile = new OpenFile();
Expand All @@ -1211,7 +1196,7 @@ protected void sysopenInternal(String path, ModeFlags modes, int perm, boolean h
int umask = getUmaskSafe( getRuntime() );
perm = perm - (perm & umask);

ChannelDescriptor descriptor = sysopen(path, modes, perm, hasOffset);
ChannelDescriptor descriptor = sysopen(path, modes, perm);
openFile.setMainStream(fdopen(descriptor, modes));
if (hasBom) {
// FIXME: Wonky that we acquire RubyEncoding to pass these encodings through
Expand Down Expand Up @@ -1253,19 +1238,14 @@ protected void openInternal(String path, String modeString) {
}

private ChannelDescriptor sysopen(String path, ModeFlags modes, int perm) {
return sysopen(path,modes,perm,false);
}

private ChannelDescriptor sysopen(String path, ModeFlags modes, int perm, boolean hasOffset) {
try {
ChannelDescriptor descriptor = ChannelDescriptor.open(
getRuntime().getCurrentDirectory(),
path,
modes,
perm,
getRuntime().getPosix(),
getRuntime().getJRubyClassLoader(),
hasOffset);
getRuntime().getJRubyClassLoader());

// TODO: check if too many open files, GC and try again

Expand Down
23 changes: 13 additions & 10 deletions src/org/jruby/RubyIO.java
Expand Up @@ -3605,17 +3605,11 @@ private static IRubyObject read19(ThreadContext context, IRubyObject recv, IRuby
*/
private static IRubyObject write19(ThreadContext context, IRubyObject recv, IRubyObject path, IRubyObject str, IRubyObject offset, RubyHash options) {
// FIXME: process options
Ruby runtime = context.runtime;

IRubyObject hasOffset = runtime.getNil();

if (!offset.isNil()) hasOffset = runtime.newBoolean(true);
else hasOffset = runtime.newBoolean(false);

RubyString pathStr = RubyFile.get_path(context, path);

Ruby runtime = context.runtime;
failIfDirectory(runtime, pathStr);
RubyIO file = newFile(context, recv, pathStr, context.runtime.newString("w"), hasOffset);
RubyIO file = newFile(context, recv, pathStr, context.runtime.newString("w"));

try {
if (!offset.isNil()) file.seek(context, offset);
Expand Down Expand Up @@ -3693,7 +3687,7 @@ public static IRubyObject read19(ThreadContext context, IRubyObject recv, IRubyO
return read19(context, recv, path, length, offset, options);
}

@JRubyMethod(meta = true, required = 2, optional = 1, compat = RUBY1_9)
@JRubyMethod(meta = true, required = 2, optional = 2, compat = RUBY1_9)
public static IRubyObject binwrite(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
IRubyObject nil = context.runtime.getNil();
IRubyObject path = args[0];
Expand All @@ -3704,7 +3698,16 @@ public static IRubyObject binwrite(ThreadContext context, IRubyObject recv, IRub
if (args.length > 2) {
offset = args[2];
}
RubyIO file = (RubyIO) Helpers.invoke(context, runtime.getFile(), "new", path, runtime.newString("wb:ASCII-8BIT"));

long mode = ModeFlags.CREAT | ModeFlags.BINARY;

if (offset.isNil()) {
mode |= ModeFlags.WRONLY;
} else {
mode |= ModeFlags.RDWR;
}

RubyIO file = (RubyIO) Helpers.invoke(context, runtime.getFile(), "new", path, RubyFixnum.newFixnum(runtime, mode));

try {
if (!offset.isNil()) file.seek(context, offset);
Expand Down
8 changes: 2 additions & 6 deletions src/org/jruby/util/io/ChannelDescriptor.java
Expand Up @@ -790,10 +790,6 @@ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, i
* @throws java.io.IOException if there is an exception during IO
*/
public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, int perm, POSIX posix, ClassLoader classLoader) throws FileNotFoundException, DirectoryAsFileException, FileExistsException, IOException {
return open(cwd, path, flags, perm, posix, classLoader, false);
}

public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, int perm, POSIX posix, ClassLoader classLoader, boolean hasOffset) throws FileNotFoundException, DirectoryAsFileException, FileExistsException, IOException {
boolean fileCreated = false;
if (path.equals("/dev/null") || path.equalsIgnoreCase("nul:") || path.equalsIgnoreCase("nul")) {
Channel nullChannel = new NullChannel();
Expand Down Expand Up @@ -881,7 +877,7 @@ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, i
* we need manual seeking.
*/
boolean isInAppendMode;
if (flags.isWritable() && !flags.isReadable() && !hasOffset) {
if (flags.isWritable() && !flags.isReadable()) {
FileOutputStream fos = new FileOutputStream(theFile, flags.isAppendable());
fileChannel = fos.getChannel();
fileDescriptor = fos.getFD();
Expand All @@ -904,7 +900,7 @@ public static ChannelDescriptor open(String cwd, String path, ModeFlags flags, i
}

try {
if (flags.isTruncate() && !hasOffset) fileChannel.truncate(0);
if (flags.isTruncate()) fileChannel.truncate(0);
} catch (IOException ioe) {
if (ioe.getMessage().equals("Illegal seek")) {
// ignore; it's a pipe or fifo that can't be truncated
Expand Down