OpenFile origFile = ios.getOpenFileChecked();
OpenFile selfFile = getOpenFileChecked();
long pos = 0;
Stream origStream = origFile.getMainStreamSafe();
ChannelDescriptor origDescriptor = origStream.getDescriptor();
boolean origIsSeekable = origDescriptor.isSeekable();
if (origFile.isReadable() && origIsSeekable) {
pos = origFile.getMainStreamSafe().fgetpos();
}
if (origFile.getPipeStream() != null) {
origFile.getPipeStream().fflush();
} else if (origFile.isWritable()) {
origStream.fflush();
}
if (selfFile.isWritable()) {
selfFile.getWriteStreamSafe().fflush();
}
selfFile.setMode(origFile.getMode());
selfFile.setProcess(origFile.getProcess());
selfFile.setLineNumber(origFile.getLineNumber());
selfFile.setPath(origFile.getPath());
selfFile.setFinalizer(origFile.getFinalizer());
Stream selfStream = selfFile.getMainStreamSafe();
ChannelDescriptor selfDescriptor = selfFile.getMainStreamSafe().getDescriptor();
boolean selfIsSeekable = selfDescriptor.isSeekable();
// confirm we're not reopening self's channel
if (selfDescriptor.getChannel() != origDescriptor.getChannel()) {
// check if we're a stdio IO, and ensure we're not badly mutilated
if (runtime.getFileno(selfDescriptor) >= 0 && runtime.getFileno(selfDescriptor) <= 2) {
selfFile.getMainStreamSafe().clearerr();
// dup2 new fd into self to preserve fileno and references to it
origDescriptor.dup2Into(selfDescriptor);
} else {
Stream pipeFile = selfFile.getPipeStream();
int mode = selfFile.getMode();
selfFile.getMainStreamSafe().fclose();
selfFile.setPipeStream(null);
// TODO: turn off readable? am I reading this right?
// This only seems to be used while duping below, since modes gets
// reset to actual modes afterward
//fptr->mode &= (m & FMODE_READABLE) ? ~FMODE_READABLE : ~FMODE_WRITABLE;
if (pipeFile != null) {
selfFile.setMainStream(ChannelStream.fdopen(runtime, origDescriptor, origDescriptor.getOriginalModes()));
selfFile.setPipeStream(pipeFile);
} else {
// only use internal fileno here, stdio is handled above
selfFile.setMainStream(
ChannelStream.open(
runtime,
origDescriptor.dup2(selfDescriptor.getFileno())));
// since we're not actually duping the incoming channel into our handler, we need to
// copy the original sync behavior from the other handler
selfFile.getMainStreamSafe().setSync(selfFile.getMainStreamSafe().isSync());
}
selfFile.setMode(mode);
}
// TODO: anything threads attached to original fd are notified of the close...
// see rb_thread_fd_close
if (origFile.isReadable() && pos >= 0) {
if (selfIsSeekable) {
selfFile.seek(pos, Stream.SEEK_SET);
}
if (origIsSeekable) {
origFile.seek(pos, Stream.SEEK_SET);
}
}
}
// only use internal fileno here, stdio is handled above
if (selfFile.getPipeStream() != null && selfDescriptor.getFileno() != selfFile.getPipeStream().getDescriptor().getFileno()) {
int fd = selfFile.getPipeStream().getDescriptor().getFileno();
if (origFile.getPipeStream() == null) {
selfFile.getPipeStream().fclose();
selfFile.setPipeStream(null);
} else if (fd != origFile.getPipeStream().getDescriptor().getFileno()) {
selfFile.getPipeStream().fclose();
ChannelDescriptor newFD2 = origFile.getPipeStream().getDescriptor().dup2(fd);
selfFile.setPipeStream(ChannelStream.fdopen(runtime, newFD2, newIOOptions(runtime, "w").getModeFlags()));
}
}
// TODO: restore binary mode