class File: Stream { private: version(Win32) { import std.c.windows.windows; const HANDLE NO_FILE = INVALID_HANDLE_VALUE; } version(linux) { import std.c.linux.linux; alias int HANDLE; const HANDLE NO_FILE = -1; } HANDLE hfile = NO_FILE; public: this() { } this(HANDLE hfile, FileMode mode) in { assert(mode); } body { this.hfile = hfile; readable = (mode & FileMode.In) ? true : false; writeable = (mode & FileMode.Out) ? true : false; } // Open for reading this(char[] filename) { open(filename, FileMode.In); } this(char[] filename, FileMode mode) { open(filename, mode); } ~this() { close(); } // Open for reading void open(char[] filename) { open(filename, FileMode.In); } void open(char[] filename, FileMode mode) { close(); version(Win32) { DWORD access; DWORD flags; if(mode & FileMode.In) { if(mode & FileMode.Out) // in/out { readable = writeable = true; access = GENERIC_READ | GENERIC_WRITE; } else // Just in { readable = true; access = GENERIC_READ; } flags = OPEN_EXISTING; } else if(mode & FileMode.Out) // Just out { writeable = true; access = GENERIC_WRITE; flags = OPEN_ALWAYS; } else { assert(0); } // Always share read/write like fopen(). hfile = CreateFileA(toStringz(filename), access, FILE_SHARE_READ | FILE_SHARE_WRITE, null, flags, FILE_ATTRIBUTE_NORMAL, null); } version(linux) { int access; if(mode & FileMode.In) { if(mode & FileMode.Out) // in/out { readable = writeable = true; access = O_RDWR; } else // Just in { readable = true; access = O_RDONLY; } } else if(mode & FileMode.Out) // Just out { writeable = true; access = O_WRONLY | O_CREAT; } else { assert(0); } // Share = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH. hfile = std.c.linux.linux.open(toStringz(filename), access, 0666); } if(NO_FILE == hfile) { readable = writeable = false; throw new OpenError("File '" ~ filename ~ "' not found."); } seekable = true; } // Create for writing. void create(char[] filename) { create(filename, FileMode.Out); } void create(char[] filename, FileMode mode) { close(); // Require FileMode.Out to create. if(!(mode & FileMode.Out)) goto bad_create; version(Win32) { DWORD access; if(mode & FileMode.In) { if(mode & FileMode.Out) // in/out { readable = writeable = true; access = GENERIC_READ | GENERIC_WRITE; } else // Just in { assert(0); } } else if(mode & FileMode.Out) // Just out { writeable = true; access = GENERIC_WRITE; } else { assert(0); } // Always share read/write like fopen(). hfile = CreateFileA(toStringz(filename), access, FILE_SHARE_READ | FILE_SHARE_WRITE, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null); } version(linux) { int access; if(mode & FileMode.In) { if(mode & FileMode.Out) // in/out { readable = writeable = true; access = O_RDWR | O_CREAT | O_TRUNC; } else // Just in { assert(0); } } else if(mode & FileMode.Out) // Just out { writeable = true; access = O_WRONLY | O_CREAT | O_TRUNC; } else { assert(0); } // Share = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH. hfile = std.c.linux.linux.open(toStringz(filename), access, 0666); } if(NO_FILE == hfile) { readable = writeable = false; bad_create: throw new CreateError("Unable to create file '" ~ filename ~ "'."); } seekable = true; } override void close() { if(NO_FILE != hfile) { version(Win32) { CloseHandle(hfile); } version(linux) { std.c.linux.linux.close(hfile); } hfile = NO_FILE; readable = writeable = seekable = false; } } override uint readBlock(void* buffer, uint size) in { assert(readable); } body { version(Win32) { DWORD result; // result: ReadFile sets this value to zero before doing any work or error checking. ReadFile(hfile, buffer, size, &result, null); } version(linux) { int result; result = std.c.linux.linux.read(hfile, buffer, size); if(-1 == result) result = 0; } return cast(uint)result; } override uint writeBlock(void* buffer, uint size) in { assert(writeable); } body { version(Win32) { DWORD result; // result: WriteFile sets this value to zero before doing any work or error checking. WriteFile(hfile, buffer, size, &result, null); } version(linux) { int result; result = std.c.linux.linux.write(hfile, buffer, size); if(-1 == result) result = 0; } return cast(uint)result; } override ulong seek(long offset, SeekPos rel) in { assert(seekable); } body { version(Win32) { DWORD result; const DWORD BAD_SEEK = 0xFFFFFFFF; result = SetFilePointer(hfile, cast(LONG)offset, null, rel); } version(linux) { off_t result; const off_t BAD_SEEK = cast(off_t)-1; result = std.c.linux.linux.lseek(hfile, cast(off_t)offset, rel); } if(BAD_SEEK == result) throw new SeekError("Unable to seek file."); return cast(ulong)result; } HANDLE handle() { return hfile; } }