Skip to content

Commit

Permalink
Merge pull request #1888 from CyberShadow/std-stdio-handle
Browse files Browse the repository at this point in the history
std.stdio: Add File.windowsHandle, fdopen and windowsHandleOpen
  • Loading branch information
AndrejMitrovic committed Feb 16, 2014
2 parents cc09f11 + b593b30 commit fe532ec
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 84 deletions.
89 changes: 13 additions & 76 deletions std/process.d
Expand Up @@ -124,26 +124,12 @@ version (Windows)
version (DMC_RUNTIME) { } else
{
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
{
STDIN_FILENO = 0,
STDOUT_FILENO = 1,
STDERR_FILENO = 2,
}
enum
{
_O_RDONLY = 0x0000,
_O_APPEND = 0x0004,
_O_TEXT = 0x4000,
}
}
}

Expand Down Expand Up @@ -483,13 +469,9 @@ private Pid spawnProcessImpl(in char[] commandLine,
static void prepareStream(ref File file, DWORD stdHandle, string which,
out int fileDescriptor, out HANDLE handle)
{
fileDescriptor = _fileno(file.getFP());
fileDescriptor = file.isOpen ? file.fileno() : -1;
if (fileDescriptor < 0) handle = GetStdHandle(stdHandle);
else
{
version (DMC_RUNTIME) handle = _fdToHandle(fileDescriptor);
else /* MSVCRT */ handle = _get_osfhandle(fileDescriptor);
}
else handle = file.windowsHandle;

DWORD dwFlags;
if (GetHandleInformation(handle, &dwFlags))
Expand Down Expand Up @@ -1440,69 +1422,24 @@ Pipe pipe() @trusted //TODO: @safe
0);
}

// Create file descriptors from the handles
version (DMC_RUNTIME)
{
auto readFD = _handleToFD(readHandle, FHND_DEVICE);
auto writeFD = _handleToFD(writeHandle, FHND_DEVICE);
}
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)
scope(failure)
{
// 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");
CloseHandle(readHandle);
CloseHandle(writeHandle);
}

// Create FILE pointers from the file descriptors
Pipe p;
version (DMC_RUNTIME)
try
{
// 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.
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");
Pipe p;
p._read .windowsHandleOpen(readHandle , "r");
p._write.windowsHandleOpen(writeHandle, "a");
return p;
}
else // MSVCRT
catch (Exception e)
{
auto readFP = _fdopen(readFD, "r");
auto writeFP = _fdopen(writeFD, "a");
}
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");
throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")",
0);
}
p._read = File(readFP, null);
p._write = File(writeFP, null);
return p;
}


Expand Down
130 changes: 122 additions & 8 deletions std/stdio.d
Expand Up @@ -69,6 +69,8 @@ version(Windows)
/+ Waiting for druntime pull 299
+/
extern (C) nothrow FILE* _wfopen(in wchar* filename, in wchar* mode);

import core.sys.windows.windows : HANDLE;
}

version (DIGITAL_MARS_STDIO)
Expand Down Expand Up @@ -101,7 +103,6 @@ version (DIGITAL_MARS_STDIO)
enum _O_BINARY = 0x8000;
int _fileno(FILE* f) { return f._file; }
alias fileno = _fileno;
alias _get_osfhandle = _fdToHandle;
}
else version (MICROSOFT_STDIO)
{
Expand All @@ -118,6 +119,7 @@ else version (MICROSOFT_STDIO)
void _unlock_file(FILE*);
int _setmode(int, int);
int _fileno(FILE*);
FILE* _fdopen(int, const (char)*);
}
alias FPUTC = _fputc_nolock;
alias FPUTWC = _fputwc_nolock;
Expand All @@ -127,8 +129,13 @@ else version (MICROSOFT_STDIO)
alias FLOCK = _lock_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)
{
Expand Down Expand Up @@ -403,6 +410,94 @@ Throws: $(D ErrnoException) in case of error.
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. */
@property bool isOpen() const pure nothrow
{
Expand Down Expand Up @@ -797,7 +892,7 @@ Throws: $(D Exception) if the file is not opened.
version(Windows)
{
import core.sys.windows.windows;
import std.windows.syserror;

private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
Flags flags)
{
Expand All @@ -810,12 +905,14 @@ Throws: $(D Exception) if the file is not opened.
overlapped.Offset = liStart.LowPart;
overlapped.OffsetHigh = liStart.HighPart;
overlapped.hEvent = null;
return F(cast(HANDLE)_get_osfhandle(fileno), flags, 0,
liLength.LowPart, liLength.HighPart, &overlapped);
return F(windowsHandle, flags, 0, liLength.LowPart,
liLength.HighPart, &overlapped);
}

private static T wenforce(T)(T cond, string str)
{
import std.windows.syserror;

if (cond) return cond;
throw new Exception(str ~ ": " ~ sysErrorString(GetLastError()));
}
Expand Down Expand Up @@ -1380,6 +1477,22 @@ Returns the file number corresponding to this object.
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
/*
Range that reads one line at a time. Returned by $(LREF byLine).
Expand Down Expand Up @@ -3679,8 +3792,9 @@ version(linux)
enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
new StdioException("Connect failed"));

return File(enforce(fdopen(s, "w+".ptr)),
host ~ ":" ~ to!string(port));
File f;
f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
return f;
}
}

Expand Down

0 comments on commit fe532ec

Please sign in to comment.