Merge pull request #1888 from CyberShadow/std-stdio-handle

std.stdio: Add File.windowsHandle, fdopen and windowsHandleOpen
This commit is contained in:
Andrej Mitrovic 2014-02-16 21:59:34 +01:00
commit fe532ece6f
2 changed files with 135 additions and 84 deletions

View file

@ -124,26 +124,12 @@ version (Windows)
version (DMC_RUNTIME) { } else version (DMC_RUNTIME) { } else
{ {
import core.stdc.stdint; import core.stdc.stdint;
extern(C)
{
int _fileno(FILE* stream);
HANDLE _get_osfhandle(int fd);
int _open_osfhandle(HANDLE osfhandle, int flags);
FILE* _fdopen(int fd, const (char)* mode);
int _close(int fd);
}
enum enum
{ {
STDIN_FILENO = 0, STDIN_FILENO = 0,
STDOUT_FILENO = 1, STDOUT_FILENO = 1,
STDERR_FILENO = 2, STDERR_FILENO = 2,
} }
enum
{
_O_RDONLY = 0x0000,
_O_APPEND = 0x0004,
_O_TEXT = 0x4000,
}
} }
} }
@ -483,13 +469,9 @@ private Pid spawnProcessImpl(in char[] commandLine,
static void prepareStream(ref File file, DWORD stdHandle, string which, static void prepareStream(ref File file, DWORD stdHandle, string which,
out int fileDescriptor, out HANDLE handle) out int fileDescriptor, out HANDLE handle)
{ {
fileDescriptor = _fileno(file.getFP()); fileDescriptor = file.isOpen ? file.fileno() : -1;
if (fileDescriptor < 0) handle = GetStdHandle(stdHandle); if (fileDescriptor < 0) handle = GetStdHandle(stdHandle);
else else handle = file.windowsHandle;
{
version (DMC_RUNTIME) handle = _fdToHandle(fileDescriptor);
else /* MSVCRT */ handle = _get_osfhandle(fileDescriptor);
}
DWORD dwFlags; DWORD dwFlags;
if (GetHandleInformation(handle, &dwFlags)) if (GetHandleInformation(handle, &dwFlags))
@ -1440,69 +1422,24 @@ Pipe pipe() @trusted //TODO: @safe
0); 0);
} }
// Create file descriptors from the handles scope(failure)
version (DMC_RUNTIME)
{ {
auto readFD = _handleToFD(readHandle, FHND_DEVICE); CloseHandle(readHandle);
auto writeFD = _handleToFD(writeHandle, FHND_DEVICE); CloseHandle(writeHandle);
}
else // MSVCRT
{
auto readFD = _open_osfhandle(readHandle, _O_RDONLY);
auto writeFD = _open_osfhandle(writeHandle, _O_APPEND);
}
version (DMC_RUNTIME) alias _close = .close;
if (readFD == -1 || writeFD == -1)
{
// Close file descriptors, then throw.
if (readFD >= 0) _close(readFD);
else CloseHandle(readHandle);
if (writeFD >= 0) _close(writeFD);
else CloseHandle(writeHandle);
throw new StdioException("Error creating pipe");
} }
// Create FILE pointers from the file descriptors try
Pipe p;
version (DMC_RUNTIME)
{ {
// This is a re-implementation of DMC's fdopen, but without the Pipe p;
// mucking with the file descriptor. POSIX standard requires the p._read .windowsHandleOpen(readHandle , "r");
// new fdopen'd file to retain the given file descriptor's p._write.windowsHandleOpen(writeHandle, "a");
// position. return p;
FILE * local_fdopen(int fd, const(char)* mode)
{
auto fp = core.stdc.stdio.fopen("NUL", mode);
if(!fp) return null;
FLOCK(fp);
auto iob = cast(_iobuf*)fp;
.close(iob._file);
iob._file = fd;
iob._flag &= ~_IOTRAN;
FUNLOCK(fp);
return fp;
}
auto readFP = local_fdopen(readFD, "r");
auto writeFP = local_fdopen(writeFD, "a");
} }
else // MSVCRT catch (Exception e)
{ {
auto readFP = _fdopen(readFD, "r"); throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")",
auto writeFP = _fdopen(writeFD, "a"); 0);
} }
if (readFP == null || writeFP == null)
{
// Close streams, then throw.
if (readFP != null) fclose(readFP);
else _close(readFD);
if (writeFP != null) fclose(writeFP);
else _close(writeFD);
throw new StdioException("Cannot open pipe");
}
p._read = File(readFP, null);
p._write = File(writeFP, null);
return p;
} }

View file

@ -69,6 +69,8 @@ version(Windows)
/+ Waiting for druntime pull 299 /+ Waiting for druntime pull 299
+/ +/
extern (C) nothrow FILE* _wfopen(in wchar* filename, in wchar* mode); extern (C) nothrow FILE* _wfopen(in wchar* filename, in wchar* mode);
import core.sys.windows.windows : HANDLE;
} }
version (DIGITAL_MARS_STDIO) version (DIGITAL_MARS_STDIO)
@ -101,7 +103,6 @@ version (DIGITAL_MARS_STDIO)
enum _O_BINARY = 0x8000; enum _O_BINARY = 0x8000;
int _fileno(FILE* f) { return f._file; } int _fileno(FILE* f) { return f._file; }
alias fileno = _fileno; alias fileno = _fileno;
alias _get_osfhandle = _fdToHandle;
} }
else version (MICROSOFT_STDIO) else version (MICROSOFT_STDIO)
{ {
@ -118,6 +119,7 @@ else version (MICROSOFT_STDIO)
void _unlock_file(FILE*); void _unlock_file(FILE*);
int _setmode(int, int); int _setmode(int, int);
int _fileno(FILE*); int _fileno(FILE*);
FILE* _fdopen(int, const (char)*);
} }
alias FPUTC = _fputc_nolock; alias FPUTC = _fputc_nolock;
alias FPUTWC = _fputwc_nolock; alias FPUTWC = _fputwc_nolock;
@ -127,8 +129,13 @@ else version (MICROSOFT_STDIO)
alias FLOCK = _lock_file; alias FLOCK = _lock_file;
alias FUNLOCK = _unlock_file; alias FUNLOCK = _unlock_file;
enum _O_BINARY = 0x8000; enum
{
_O_RDONLY = 0x0000,
_O_APPEND = 0x0004,
_O_TEXT = 0x4000,
_O_BINARY = 0x8000,
}
} }
else version (GCC_IO) else version (GCC_IO)
{ {
@ -403,6 +410,94 @@ Throws: $(D ErrnoException) in case of error.
command, 1, true); command, 1, true);
} }
/**
First calls $(D detach) (throwing on failure), and then attempts to
associate the given file descriptor with the $(D File). The mode must
be compatible with the mode of the file descriptor.
Throws: $(D ErrnoException) in case of error.
*/
void fdopen(int fd, in char[] stdioOpenmode = "rb")
{
fdopen(fd, stdioOpenmode, null);
}
package void fdopen(int fd, in char[] stdioOpenmode, string name)
{
import std.string : toStringz;
import std.exception : errnoEnforce;
detach();
version (DIGITAL_MARS_STDIO)
{
// This is a re-implementation of DMC's fdopen, but without the
// mucking with the file descriptor. POSIX standard requires the
// new fdopen'd file to retain the given file descriptor's
// position.
auto fp = core.stdc.stdio.fopen("NUL", toStringz(stdioOpenmode));
errnoEnforce(fp, "Cannot open placeholder NUL stream");
FLOCK(fp);
auto iob = cast(_iobuf*)fp;
.close(iob._file);
iob._file = fd;
iob._flag &= ~_IOTRAN;
FUNLOCK(fp);
}
else
{
version (Windows) // MSVCRT
auto fp = _fdopen(fd, toStringz(stdioOpenmode));
else
auto fp = .fdopen(fd, toStringz(stdioOpenmode));
errnoEnforce(fp);
}
this = File(fp, name);
}
/**
First calls $(D detach) (throwing on failure), and then attempts to
associate the given Windows $(D HANDLE) with the $(D File). The mode must
be compatible with the access attributes of the handle. Windows only.
Throws: $(D ErrnoException) in case of error.
*/
version(StdDdoc)
void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode);
version(Windows)
void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode)
{
import std.exception : errnoEnforce;
import std.string : format;
// Create file descriptors from the handles
version (DIGITAL_MARS_STDIO)
auto fd = _handleToFD(handle, FHND_DEVICE);
else // MSVCRT
{
int mode;
modeLoop:
foreach (c; stdioOpenmode)
switch (c)
{
case 'r': mode |= _O_RDONLY; break;
case '+': mode &=~_O_RDONLY; break;
case 'a': mode |= _O_APPEND; break;
case 'b': mode |= _O_BINARY; break;
case 't': mode |= _O_TEXT; break;
case ',': break modeLoop;
default: break;
}
auto fd = _open_osfhandle(cast(intptr_t)handle, mode);
}
errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
}
/** Returns $(D true) if the file is opened. */ /** Returns $(D true) if the file is opened. */
@property bool isOpen() const pure nothrow @property bool isOpen() const pure nothrow
{ {
@ -797,7 +892,7 @@ Throws: $(D Exception) if the file is not opened.
version(Windows) version(Windows)
{ {
import core.sys.windows.windows; import core.sys.windows.windows;
import std.windows.syserror;
private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
Flags flags) Flags flags)
{ {
@ -810,12 +905,14 @@ Throws: $(D Exception) if the file is not opened.
overlapped.Offset = liStart.LowPart; overlapped.Offset = liStart.LowPart;
overlapped.OffsetHigh = liStart.HighPart; overlapped.OffsetHigh = liStart.HighPart;
overlapped.hEvent = null; overlapped.hEvent = null;
return F(cast(HANDLE)_get_osfhandle(fileno), flags, 0, return F(windowsHandle, flags, 0, liLength.LowPart,
liLength.LowPart, liLength.HighPart, &overlapped); liLength.HighPart, &overlapped);
} }
private static T wenforce(T)(T cond, string str) private static T wenforce(T)(T cond, string str)
{ {
import std.windows.syserror;
if (cond) return cond; if (cond) return cond;
throw new Exception(str ~ ": " ~ sysErrorString(GetLastError())); throw new Exception(str ~ ": " ~ sysErrorString(GetLastError()));
} }
@ -1380,6 +1477,22 @@ Returns the file number corresponding to this object.
return .fileno(cast(FILE*) _p.handle); return .fileno(cast(FILE*) _p.handle);
} }
/**
Returns the underlying operating system $(D HANDLE) (Windows only).
*/
version(StdDdoc)
@property HANDLE windowsHandle();
version(Windows)
@property HANDLE windowsHandle()
{
version (DIGITAL_MARS_STDIO)
return _fdToHandle(fileno);
else
return cast(HANDLE)_get_osfhandle(fileno);
}
// Note: This was documented until 2013/08 // Note: This was documented until 2013/08
/* /*
Range that reads one line at a time. Returned by $(LREF byLine). Range that reads one line at a time. Returned by $(LREF byLine).
@ -3679,8 +3792,9 @@ version(linux)
enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1, enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
new StdioException("Connect failed")); new StdioException("Connect failed"));
return File(enforce(fdopen(s, "w+".ptr)), File f;
host ~ ":" ~ to!string(port)); f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
return f;
} }
} }