Skip to content

Commit 59b6392

Browse files
authored
Merge pull request #4822 from CyberShadow/pull-20160926-152950
Fix Issue 16544 - Add File.reopen
2 parents ee1110d + d63572f commit 59b6392

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

std/stdio.d

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ version(Windows)
9696
/+ Waiting for druntime pull 299
9797
+/
9898
extern (C) nothrow @nogc FILE* _wfopen(in wchar* filename, in wchar* mode);
99+
extern (C) nothrow @nogc FILE* _wfreopen(in wchar* filename, in wchar* mode, FILE* fp);
99100

100101
import core.sys.windows.windows : HANDLE;
101102
}
@@ -488,6 +489,86 @@ Throws: $(D ErrnoException) in case of error.
488489
this = File(name, stdioOpenmode);
489490
}
490491

492+
/**
493+
Reuses the `File` object to either open a different file, or change
494+
the file mode. If `name` is `null`, the mode of the currently open
495+
file is changed; otherwise, a new file is opened, reusing the C
496+
`FILE*`. The function has the same semantics as in the C standard
497+
library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen)
498+
function.
499+
500+
Note: Calling `reopen` with a `null` `name` is not implemented
501+
in all C runtimes.
502+
503+
Throws: $(D ErrnoException) in case of error.
504+
*/
505+
void reopen(string name, in char[] stdioOpenmode = "rb") @trusted
506+
{
507+
import std.internal.cstring : tempCString;
508+
import std.exception : enforce, errnoEnforce;
509+
import std.conv : text;
510+
511+
enforce(isOpen, "Attempting to reopen() an unopened file");
512+
513+
auto namez = name.tempCString!FSChar();
514+
auto modez = stdioOpenmode.tempCString!FSChar();
515+
516+
FILE* fd = _p.handle;
517+
version (Windows)
518+
fd = _wfreopen(namez, modez, fd);
519+
else
520+
fd = freopen(namez, modez, fd);
521+
522+
errnoEnforce(fd, name
523+
? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'")
524+
: text("Cannot reopen file in mode `", stdioOpenmode, "'"));
525+
526+
if (name !is null)
527+
_name = name;
528+
}
529+
530+
unittest // Test changing filename
531+
{
532+
static import std.file;
533+
import std.exception : assertThrown, assertNotThrown;
534+
535+
auto deleteme = testFilename();
536+
std.file.write(deleteme, "foo");
537+
scope(exit) std.file.remove(deleteme);
538+
auto f = File(deleteme);
539+
assert(f.readln() == "foo");
540+
541+
auto deleteme2 = testFilename();
542+
std.file.write(deleteme2, "bar");
543+
scope(exit) std.file.remove(deleteme2);
544+
f.reopen(deleteme2);
545+
assert(f.name == deleteme2);
546+
assert(f.readln() == "bar");
547+
f.close();
548+
}
549+
550+
version (CRuntime_DigitalMars) {} else // Not implemented
551+
version (CRuntime_Microsoft) {} else // Not implemented
552+
unittest // Test changing mode
553+
{
554+
static import std.file;
555+
import std.exception : assertThrown, assertNotThrown;
556+
557+
auto deleteme = testFilename();
558+
std.file.write(deleteme, "foo");
559+
scope(exit) std.file.remove(deleteme);
560+
auto f = File(deleteme, "r+");
561+
assert(f.readln() == "foo");
562+
f.reopen(null, "w");
563+
f.write("bar");
564+
f.seek(0);
565+
f.reopen(null, "a");
566+
f.write("baz");
567+
assert(f.name == deleteme);
568+
f.close();
569+
assert(std.file.readText(deleteme) == "barbaz");
570+
}
571+
491572
/**
492573
First calls $(D detach) (throwing on failure), and then runs a command
493574
by calling the C standard library function $(HTTP

0 commit comments

Comments
 (0)