Mercurial > projects > ldc
diff lphobos/std/file.d @ 473:373489eeaf90
Applied downs' lphobos update
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Mon, 04 Aug 2008 19:28:49 +0200 |
parents | |
children | 88e23f8c2354 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lphobos/std/file.d Mon Aug 04 19:28:49 2008 +0200 @@ -0,0 +1,1588 @@ +// Written in the D programming language. + +/** + * Macros: + * WIKI = Phobos/StdFile + */ + +/* + * Copyright (C) 2001-2004 by Digital Mars, www.digitalmars.com + * Written by Walter Bright, Christopher E. Miller, Andre Fornacon + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +/* NOTE: This file has been patched from the original DMD distribution to + work with the GDC compiler. + + Modified by David Friedman, March 2006 +*/ + +module std.file; + +private import std.c.stdio; +private import std.c.stdlib; +private import std.c.string; +private import std.path; +private import std.string; +private import std.regexp; +private import std.gc; + +/* =========================== Win32 ======================= */ + +version (Win32) +{ + +private import std.c.windows.windows; +private import std.utf; +private import std.windows.syserror; +private import std.windows.charset; +private import std.date; + +int useWfuncs = 1; + +static this() +{ + // Win 95, 98, ME do not implement the W functions + useWfuncs = (GetVersion() < 0x80000000); +} + +/*********************************** + * Exception thrown for file I/O errors. + */ + +class FileException : Exception +{ + + uint errno; // operating system error code + + this(char[] name) + { + this(name, "file I/O"); + } + + this(char[] name, char[] message) + { + super(name ~ ": " ~ message); + } + + this(char[] name, uint errno) + { + this(name, sysErrorString(errno)); + this.errno = errno; + } +} + +/* ********************************** + * Basic File operations. + */ + +/******************************************** + * Read file name[], return array of bytes read. + * Throws: + * FileException on error. + */ + +void[] read(char[] name) +{ + DWORD numread; + HANDLE h; + + if (useWfuncs) + { + wchar* namez = std.utf.toUTF16z(name); + h = CreateFileW(namez,GENERIC_READ,FILE_SHARE_READ,null,OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); + } + else + { + char* namez = toMBSz(name); + h = CreateFileA(namez,GENERIC_READ,FILE_SHARE_READ,null,OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); + } + + if (h == INVALID_HANDLE_VALUE) + goto err1; + + auto size = GetFileSize(h, null); + if (size == INVALID_FILE_SIZE) + goto err2; + + auto buf = std.gc.malloc(size); + if (buf) + std.gc.hasNoPointers(buf.ptr); + + if (ReadFile(h,buf.ptr,size,&numread,null) != 1) + goto err2; + + if (numread != size) + goto err2; + + if (!CloseHandle(h)) + goto err; + + return buf[0 .. size]; + +err2: + CloseHandle(h); +err: + delete buf; +err1: + throw new FileException(name, GetLastError()); +} + +/********************************************* + * Write buffer[] to file name[]. + * Throws: FileException on error. + */ + +void write(char[] name, void[] buffer) +{ + HANDLE h; + DWORD numwritten; + + if (useWfuncs) + { + wchar* namez = std.utf.toUTF16z(name); + h = CreateFileW(namez,GENERIC_WRITE,0,null,CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); + } + else + { + char* namez = toMBSz(name); + h = CreateFileA(namez,GENERIC_WRITE,0,null,CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); + } + if (h == INVALID_HANDLE_VALUE) + goto err; + + if (WriteFile(h,buffer.ptr,buffer.length,&numwritten,null) != 1) + goto err2; + + if (buffer.length != numwritten) + goto err2; + + if (!CloseHandle(h)) + goto err; + return; + +err2: + CloseHandle(h); +err: + throw new FileException(name, GetLastError()); +} + + +/********************************************* + * Append buffer[] to file name[]. + * Throws: FileException on error. + */ + +void append(char[] name, void[] buffer) +{ + HANDLE h; + DWORD numwritten; + + if (useWfuncs) + { + wchar* namez = std.utf.toUTF16z(name); + h = CreateFileW(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); + } + else + { + char* namez = toMBSz(name); + h = CreateFileA(namez,GENERIC_WRITE,0,null,OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,cast(HANDLE)null); + } + if (h == INVALID_HANDLE_VALUE) + goto err; + + SetFilePointer(h, 0, null, FILE_END); + + if (WriteFile(h,buffer.ptr,buffer.length,&numwritten,null) != 1) + goto err2; + + if (buffer.length != numwritten) + goto err2; + + if (!CloseHandle(h)) + goto err; + return; + +err2: + CloseHandle(h); +err: + throw new FileException(name, GetLastError()); +} + + +/*************************************************** + * Rename file from[] to to[]. + * Throws: FileException on error. + */ + +void rename(char[] from, char[] to) +{ + BOOL result; + + if (useWfuncs) + result = MoveFileW(std.utf.toUTF16z(from), std.utf.toUTF16z(to)); + else + result = MoveFileA(toMBSz(from), toMBSz(to)); + if (!result) + throw new FileException(to, GetLastError()); +} + + +/*************************************************** + * Delete file name[]. + * Throws: FileException on error. + */ + +void remove(char[] name) +{ + BOOL result; + + if (useWfuncs) + result = DeleteFileW(std.utf.toUTF16z(name)); + else + result = DeleteFileA(toMBSz(name)); + if (!result) + throw new FileException(name, GetLastError()); +} + + +/*************************************************** + * Get size of file name[]. + * Throws: FileException on error. + */ + +ulong getSize(char[] name) +{ + HANDLE findhndl; + uint resulth; + uint resultl; + + if (useWfuncs) + { + WIN32_FIND_DATAW filefindbuf; + + findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf); + resulth = filefindbuf.nFileSizeHigh; + resultl = filefindbuf.nFileSizeLow; + } + else + { + WIN32_FIND_DATA filefindbuf; + + findhndl = FindFirstFileA(toMBSz(name), &filefindbuf); + resulth = filefindbuf.nFileSizeHigh; + resultl = filefindbuf.nFileSizeLow; + } + + if (findhndl == cast(HANDLE)-1) + { + throw new FileException(name, GetLastError()); + } + FindClose(findhndl); + return (cast(ulong)resulth << 32) + resultl; +} + +/************************* + * Get creation/access/modified times of file name[]. + * Throws: FileException on error. + */ + +void getTimes(char[] name, out d_time ftc, out d_time fta, out d_time ftm) +{ + HANDLE findhndl; + + if (useWfuncs) + { + WIN32_FIND_DATAW filefindbuf; + + findhndl = FindFirstFileW(std.utf.toUTF16z(name), &filefindbuf); + ftc = std.date.FILETIME2d_time(&filefindbuf.ftCreationTime); + fta = std.date.FILETIME2d_time(&filefindbuf.ftLastAccessTime); + ftm = std.date.FILETIME2d_time(&filefindbuf.ftLastWriteTime); + } + else + { + WIN32_FIND_DATA filefindbuf; + + findhndl = FindFirstFileA(toMBSz(name), &filefindbuf); + ftc = std.date.FILETIME2d_time(&filefindbuf.ftCreationTime); + fta = std.date.FILETIME2d_time(&filefindbuf.ftLastAccessTime); + ftm = std.date.FILETIME2d_time(&filefindbuf.ftLastWriteTime); + } + + if (findhndl == cast(HANDLE)-1) + { + throw new FileException(name, GetLastError()); + } + FindClose(findhndl); +} + + +/*************************************************** + * Does file name[] (or directory) exist? + * Return 1 if it does, 0 if not. + */ + +int exists(char[] name) +{ + uint result; + + if (useWfuncs) + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/getfileattributes.asp + result = GetFileAttributesW(std.utf.toUTF16z(name)); + else + result = GetFileAttributesA(toMBSz(name)); + + return (result == 0xFFFFFFFF) ? 0 : 1; +} + +/*************************************************** + * Get file name[] attributes. + * Throws: FileException on error. + */ + +uint getAttributes(char[] name) +{ + uint result; + + if (useWfuncs) + result = GetFileAttributesW(std.utf.toUTF16z(name)); + else + result = GetFileAttributesA(toMBSz(name)); + if (result == 0xFFFFFFFF) + { + throw new FileException(name, GetLastError()); + } + return result; +} + +/**************************************************** + * Is name[] a file? + * Throws: FileException if name[] doesn't exist. + */ + +int isfile(char[] name) +{ + return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) == 0; +} + +/**************************************************** + * Is name[] a directory? + * Throws: FileException if name[] doesn't exist. + */ + +int isdir(char[] name) +{ + return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0; +} + +/**************************************************** + * Change directory to pathname[]. + * Throws: FileException on error. + */ + +void chdir(char[] pathname) +{ BOOL result; + + if (useWfuncs) + result = SetCurrentDirectoryW(std.utf.toUTF16z(pathname)); + else + result = SetCurrentDirectoryA(toMBSz(pathname)); + + if (!result) + { + throw new FileException(pathname, GetLastError()); + } +} + +/**************************************************** + * Make directory pathname[]. + * Throws: FileException on error. + */ + +void mkdir(char[] pathname) +{ BOOL result; + + if (useWfuncs) + result = CreateDirectoryW(std.utf.toUTF16z(pathname), null); + else + result = CreateDirectoryA(toMBSz(pathname), null); + + if (!result) + { + throw new FileException(pathname, GetLastError()); + } +} + +/**************************************************** + * Remove directory pathname[]. + * Throws: FileException on error. + */ + +void rmdir(char[] pathname) +{ BOOL result; + + if (useWfuncs) + result = RemoveDirectoryW(std.utf.toUTF16z(pathname)); + else + result = RemoveDirectoryA(toMBSz(pathname)); + + if (!result) + { + throw new FileException(pathname, GetLastError()); + } +} + +/**************************************************** + * Get current directory. + * Throws: FileException on error. + */ + +char[] getcwd() +{ + if (useWfuncs) + { + wchar c; + + auto len = GetCurrentDirectoryW(0, &c); + if (!len) + goto Lerr; + auto dir = new wchar[len]; + len = GetCurrentDirectoryW(len, dir.ptr); + if (!len) + goto Lerr; + return std.utf.toUTF8(dir[0 .. len]); // leave off terminating 0 + } + else + { + char c; + + auto len = GetCurrentDirectoryA(0, &c); + if (!len) + goto Lerr; + auto dir = new char[len]; + len = GetCurrentDirectoryA(len, dir.ptr); + if (!len) + goto Lerr; + return dir[0 .. len]; // leave off terminating 0 + } + +Lerr: + throw new FileException("getcwd", GetLastError()); +} + +/*************************************************** + * Directory Entry + */ + +struct DirEntry +{ + char[] name; /// file or directory name + ulong size = ~0UL; /// size of file in bytes + d_time creationTime = d_time_nan; /// time of file creation + d_time lastAccessTime = d_time_nan; /// time file was last accessed + d_time lastWriteTime = d_time_nan; /// time file was last written to + uint attributes; // Windows file attributes OR'd together + + void init(char[] path, WIN32_FIND_DATA *fd) + { + wchar[] wbuf; + size_t clength; + size_t wlength; + size_t n; + + clength = std.string.strlen(fd.cFileName.ptr); + + // Convert cFileName[] to unicode + wlength = MultiByteToWideChar(0,0,fd.cFileName.ptr,clength,null,0); + if (wlength > wbuf.length) + wbuf.length = wlength; + n = MultiByteToWideChar(0,0,fd.cFileName.ptr,clength,cast(wchar*)wbuf,wlength); + assert(n == wlength); + // toUTF8() returns a new buffer + name = std.path.join(path, std.utf.toUTF8(wbuf[0 .. wlength])); + + size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow; + creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime); + lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime); + lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime); + attributes = fd.dwFileAttributes; + } + + void init(char[] path, WIN32_FIND_DATAW *fd) + { + size_t clength = std.string.wcslen(fd.cFileName.ptr); + name = std.path.join(path, std.utf.toUTF8(fd.cFileName[0 .. clength])); + size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow; + creationTime = std.date.FILETIME2d_time(&fd.ftCreationTime); + lastAccessTime = std.date.FILETIME2d_time(&fd.ftLastAccessTime); + lastWriteTime = std.date.FILETIME2d_time(&fd.ftLastWriteTime); + attributes = fd.dwFileAttributes; + } + + /**** + * Return !=0 if DirEntry is a directory. + */ + uint isdir() + { + return attributes & FILE_ATTRIBUTE_DIRECTORY; + } + + /**** + * Return !=0 if DirEntry is a file. + */ + uint isfile() + { + return !(attributes & FILE_ATTRIBUTE_DIRECTORY); + } +} + + +/*************************************************** + * Return contents of directory pathname[]. + * The names in the contents do not include the pathname. + * Throws: FileException on error + * Example: + * This program lists all the files and subdirectories in its + * path argument. + * ---- + * import std.stdio; + * import std.file; + * + * void main(char[][] args) + * { + * auto dirs = std.file.listdir(args[1]); + * + * foreach (d; dirs) + * writefln(d); + * } + * ---- + */ + +char[][] listdir(char[] pathname) +{ + char[][] result; + + bool listing(char[] filename) + { + result ~= filename; + return true; // continue + } + + listdir(pathname, &listing); + return result; +} + + +/***************************************************** + * Return all the files in the directory and its subdirectories + * that match pattern or regular expression r. + * Params: + * pathname = Directory name + * pattern = String with wildcards, such as $(RED "*.d"). The supported + * wildcard strings are described under fnmatch() in + * $(LINK2 std_path.html, std.path). + * r = Regular expression, for more powerful _pattern matching. + * Example: + * This program lists all the files with a "d" extension in + * the path passed as the first argument. + * ---- + * import std.stdio; + * import std.file; + * + * void main(char[][] args) + * { + * auto d_source_files = std.file.listdir(args[1], "*.d"); + * + * foreach (d; d_source_files) + * writefln(d); + * } + * ---- + * A regular expression version that searches for all files with "d" or + * "obj" extensions: + * ---- + * import std.stdio; + * import std.file; + * import std.regexp; + * + * void main(char[][] args) + * { + * auto d_source_files = std.file.listdir(args[1], RegExp(r"\.(d|obj)$")); + * + * foreach (d; d_source_files) + * writefln(d); + * } + * ---- + */ + +char[][] listdir(char[] pathname, char[] pattern) +{ char[][] result; + + bool callback(DirEntry* de) + { + if (de.isdir) + listdir(de.name, &callback); + else + { if (std.path.fnmatch(de.name, pattern)) + result ~= de.name; + } + return true; // continue + } + + listdir(pathname, &callback); + return result; +} + +/** Ditto */ + +char[][] listdir(char[] pathname, RegExp r) +{ char[][] result; + + bool callback(DirEntry* de) + { + if (de.isdir) + listdir(de.name, &callback); + else + { if (r.test(de.name)) + result ~= de.name; + } + return true; // continue + } + + listdir(pathname, &callback); + return result; +} + +/****************************************************** + * For each file and directory name in pathname[], + * pass it to the callback delegate. + * Params: + * callback = Delegate that processes each + * filename in turn. Returns true to + * continue, false to stop. + * Example: + * This program lists all the files in its + * path argument, including the path. + * ---- + * import std.stdio; + * import std.path; + * import std.file; + * + * void main(char[][] args) + * { + * auto pathname = args[1]; + * char[][] result; + * + * bool listing(char[] filename) + * { + * result ~= std.path.join(pathname, filename); + * return true; // continue + * } + * + * listdir(pathname, &listing); + * + * foreach (name; result) + * writefln("%s", name); + * } + * ---- + */ + +void listdir(char[] pathname, bool delegate(char[] filename) callback) +{ + bool listing(DirEntry* de) + { + return callback(std.path.getBaseName(de.name)); + } + + listdir(pathname, &listing); +} + +/****************************************************** + * For each file and directory DirEntry in pathname[], + * pass it to the callback delegate. + * Params: + * callback = Delegate that processes each + * DirEntry in turn. Returns true to + * continue, false to stop. + * Example: + * This program lists all the files in its + * path argument and all subdirectories thereof. + * ---- + * import std.stdio; + * import std.file; + * + * void main(char[][] args) + * { + * bool callback(DirEntry* de) + * { + * if (de.isdir) + * listdir(de.name, &callback); + * else + * writefln(de.name); + * return true; + * } + * + * listdir(args[1], &callback); + * } + * ---- + */ + +void listdir(char[] pathname, bool delegate(DirEntry* de) callback) +{ + char[] c; + HANDLE h; + DirEntry de; + + c = std.path.join(pathname, "*.*"); + if (useWfuncs) + { + WIN32_FIND_DATAW fileinfo; + + h = FindFirstFileW(std.utf.toUTF16z(c), &fileinfo); + if (h != INVALID_HANDLE_VALUE) + { + try + { + do + { + // Skip "." and ".." + if (std.string.wcscmp(fileinfo.cFileName.ptr, ".") == 0 || + std.string.wcscmp(fileinfo.cFileName.ptr, "..") == 0) + continue; + + de.init(pathname, &fileinfo); + if (!callback(&de)) + break; + } while (FindNextFileW(h,&fileinfo) != FALSE); + } + finally + { + FindClose(h); + } + } + } + else + { + WIN32_FIND_DATA fileinfo; + + h = FindFirstFileA(toMBSz(c), &fileinfo); + if (h != INVALID_HANDLE_VALUE) // should we throw exception if invalid? + { + try + { + do + { + // Skip "." and ".." + if (std.string.strcmp(fileinfo.cFileName.ptr, ".") == 0 || + std.string.strcmp(fileinfo.cFileName.ptr, "..") == 0) + continue; + + de.init(pathname, &fileinfo); + if (!callback(&de)) + break; + } while (FindNextFileA(h,&fileinfo) != FALSE); + } + finally + { + FindClose(h); + } + } + } +} + +/****************************************** + * Since Win 9x does not support the "W" API's, first convert + * to wchar, then convert to multibyte using the current code + * page. + * (Thanks to yaneurao for this) + * Deprecated: use std.windows.charset.toMBSz instead. + */ + +char* toMBSz(char[] s) +{ + return std.windows.charset.toMBSz(s); +} + + +/*************************************************** + * Copy a file from[] to[]. + */ + +void copy(char[] from, char[] to) +{ + BOOL result; + + if (useWfuncs) + result = CopyFileW(std.utf.toUTF16z(from), std.utf.toUTF16z(to), false); + else + result = CopyFileA(toMBSz(from), toMBSz(to), false); + if (!result) + throw new FileException(to, GetLastError()); +} + + +} + +/* =========================== linux ======================= */ + +else version (Unix) +{ + +version(linux) { + import std.c.linux.linux; + alias std.c.linux.linux sys; +} else { + import std.c.unix.unix; + alias std.c.unix.unix sys; +} +import std.date, std.c.string; + +/*********************************** + */ + +extern(C) char* strerror_r(int errnum, char* buf, size_t buflen); +class FileException : Exception +{ + + uint errno; // operating system error code + + this(char[] name) + { + this(name, "file I/O"); + } + + this(char[] name, char[] message) + { + super(name ~ ": " ~ message); + } + + this(char[] name, uint errno) + { char[80] buf = void; + auto s = strerror_r(errno, buf.ptr, buf.length); + this(name, std.string.toString(s).dup); + this.errno = errno; + } +} + +/******************************************** + * Read a file. + * Returns: + * array of bytes read + */ + +void[] read(char[] name) +{ + uint numread; + struct_stat statbuf; + + auto namez = toStringz(name); + //printf("file.read('%s')\n",namez); + auto fd = sys.open(namez, O_RDONLY); + if (fd == -1) + { + //printf("\topen error, errno = %d\n",getErrno()); + goto err1; + } + + //printf("\tfile opened\n"); + if (sys.fstat(fd, &statbuf)) + { + //printf("\tfstat error, errno = %d\n",getErrno()); + goto err2; + } + auto size = statbuf.st_size; + if (size > size_t.max) + goto err2; + + auto buf = std.gc.malloc(size); + if (buf.ptr) + std.gc.hasNoPointers(buf.ptr); + + numread = sys.read(fd, buf.ptr, size); + if (numread != size) + { + //printf("\tread error, errno = %d\n",getErrno()); + goto err2; + } + + if (sys.close(fd) == -1) + { + //printf("\tclose error, errno = %d\n",getErrno()); + goto err; + } + + return buf[0 .. size]; + +err2: + sys.close(fd); +err: + delete buf; + +err1: + throw new FileException(name, getErrno()); +} + +/********************************************* + * Write a file. + * Returns: + * 0 success + */ + +void write(char[] name, void[] buffer) +{ + int fd; + char *namez; + + namez = toStringz(name); + fd = sys.open(namez, O_CREAT | O_WRONLY | O_TRUNC, 0660); + if (fd == -1) + goto err; + + auto numwritten = sys.write(fd, buffer.ptr, buffer.length); + if (buffer.length != numwritten) + goto err2; + + if (sys.close(fd) == -1) + goto err; + + return; + +err2: + sys.close(fd); +err: + throw new FileException(name, getErrno()); +} + + +/********************************************* + * Append to a file. + */ + +void append(char[] name, void[] buffer) +{ + int fd; + char *namez; + + namez = toStringz(name); + fd = sys.open(namez, O_APPEND | O_WRONLY | O_CREAT, 0660); + if (fd == -1) + goto err; + + auto numwritten = sys.write(fd, buffer.ptr, buffer.length); + if (buffer.length != numwritten) + goto err2; + + if (sys.close(fd) == -1) + goto err; + + return; + +err2: + sys.close(fd); +err: + throw new FileException(name, getErrno()); +} + + +/*************************************************** + * Rename a file. + */ + +void rename(char[] from, char[] to) +{ + char *fromz = toStringz(from); + char *toz = toStringz(to); + + if (std.c.stdio.rename(fromz, toz) == -1) + throw new FileException(to, getErrno()); +} + + +/*************************************************** + * Delete a file. + */ + +void remove(char[] name) +{ + if (std.c.stdio.remove(toStringz(name)) == -1) + throw new FileException(name, getErrno()); +} + + +/*************************************************** + * Get file size. + */ + +ulong getSize(char[] name) +{ + uint size; + int fd; + struct_stat statbuf; + char *namez; + + namez = toStringz(name); + //printf("file.getSize('%s')\n",namez); + fd = sys.open(namez, O_RDONLY); + if (fd == -1) + { + //printf("\topen error, errno = %d\n",getErrno()); + goto err1; + } + + //printf("\tfile opened\n"); + if (sys.fstat(fd, &statbuf)) + { + //printf("\tfstat error, errno = %d\n",getErrno()); + goto err2; + } + size = statbuf.st_size; + + if (sys.close(fd) == -1) + { + //printf("\tclose error, errno = %d\n",getErrno()); + goto err; + } + + return size; + +err2: + sys.close(fd); +err: +err1: + throw new FileException(name, getErrno()); +} + + +/*************************************************** + * Get file attributes. + */ + +uint getAttributes(char[] name) +{ + struct_stat statbuf; + char *namez; + + namez = toStringz(name); + if (sys.stat(namez, &statbuf)) + { + throw new FileException(name, getErrno()); + } + + return statbuf.st_mode; +} + +/************************* + * Get creation/access/modified times of file name[]. + * Throws: FileException on error. + */ + +void getTimes(char[] name, out d_time ftc, out d_time fta, out d_time ftm) +{ + struct_stat statbuf; + char *namez; + + namez = toStringz(name); + if (sys.stat(namez, &statbuf)) + { + throw new FileException(name, getErrno()); + } + + ftc = cast(d_time)statbuf.st_ctime * std.date.TicksPerSecond; + fta = cast(d_time)statbuf.st_atime * std.date.TicksPerSecond; + ftm = cast(d_time)statbuf.st_mtime * std.date.TicksPerSecond; +} + + +/**************************************************** + * Does file/directory exist? + */ + +int exists(char[] name) +{ + return access(toStringz(name),0) == 0; + +/+ + struct_stat statbuf; + char *namez; + + namez = toStringz(name); + if (sys.stat(namez, &statbuf)) + { + return 0; + } + return 1; ++/ +} + +unittest +{ + assert(exists(".")); +} + +/**************************************************** + * Is name a file? + */ + +int isfile(char[] name) +{ + return (getAttributes(name) & S_IFMT) == S_IFREG; // regular file +} + +/**************************************************** + * Is name a directory? + */ + +int isdir(char[] name) +{ + return (getAttributes(name) & S_IFMT) == S_IFDIR; +} + +/**************************************************** + * Change directory. + */ + +void chdir(char[] pathname) +{ + if (sys.chdir(toStringz(pathname))) + { + throw new FileException(pathname, getErrno()); + } +} + +/**************************************************** + * Make directory. + */ + +void mkdir(char[] pathname) +{ + if (sys.mkdir(toStringz(pathname), 0777)) + { + throw new FileException(pathname, getErrno()); + } +} + +/**************************************************** + * Remove directory. + */ + +void rmdir(char[] pathname) +{ + if (sys.rmdir(toStringz(pathname))) + { + throw new FileException(pathname, getErrno()); + } +} + +/**************************************************** + * Get current directory. + */ + +char[] getcwd() +{ + version(all) + { + const PATH_MAX=4096; + char buf[PATH_MAX]; + if (! sys.getcwd(buf.ptr, buf.length)) + { + throw new FileException("cannot get cwd", getErrno()); + } + size_t len = strlen(buf.ptr); + char[] result = new char[len]; + result[] = buf[0..len]; + return result; + } + else + { + auto p = sys.getcwd(null, 0); + if (!p) + { + throw new FileException("cannot get cwd", getErrno()); + } + auto len = std.string.strlen(p); + auto buf = new char[len]; + buf[] = p[0 .. len]; + std.c.stdlib.free(p); + return buf; + } + +} + +/*************************************************** + * Directory Entry + */ + +struct DirEntry +{ + char[] name; /// file or directory name + ulong _size = ~0UL; // size of file in bytes + d_time _creationTime = d_time_nan; // time of file creation + d_time _lastAccessTime = d_time_nan; // time file was last accessed + d_time _lastWriteTime = d_time_nan; // time file was last written to + version (GNU) + typeof(struct_stat.st_mode) _st_mode; + else + ubyte d_type; + ubyte didstat; // done lazy evaluation of stat() + + void init(char[] path, dirent *fd) + { size_t len = std.string.strlen(fd.d_name.ptr); + name = std.path.join(path, fd.d_name[0 .. len]); + version(GNU) + { } + else + d_type = fd.d_type; + didstat = 0; + } + + int isdir() + { + version(GNU) + { + if (!didstat) + doStat(); + return (_st_mode & S_IFMT) == S_IFDIR; + } + else + return d_type & DT_DIR; + } + + int isfile() + { + version(GNU) + { + if (!didstat) + doStat(); + return (_st_mode & S_IFMT) == S_IFREG; + } + else + return d_type & DT_REG; + } + + ulong size() + { + if (!didstat) + doStat(); + return _size; + } + + d_time creationTime() + { + if (!didstat) + doStat(); + return _creationTime; + } + + d_time lastAccessTime() + { + if (!didstat) + doStat(); + return _lastAccessTime; + } + + d_time lastWriteTime() + { + if (!didstat) + doStat(); + return _lastWriteTime; + } + + /* This is to support lazy evaluation, because doing stat's is + * expensive and not always needed. + */ + + void doStat() + { + int fd; + struct_stat statbuf; + char* namez; + + namez = toStringz(name); + if (sys.stat(namez, &statbuf)) + { + //printf("\tstat error, errno = %d\n",getErrno()); + return; + } + _size = statbuf.st_size; + _creationTime = cast(d_time)statbuf.st_ctime * std.date.TicksPerSecond; + _lastAccessTime = cast(d_time)statbuf.st_atime * std.date.TicksPerSecond; + _lastWriteTime = cast(d_time)statbuf.st_mtime * std.date.TicksPerSecond; + version(GNU) _st_mode = statbuf.st_mode; + didstat = 1; + } +} + + +/*************************************************** + * Return contents of directory. + */ + +char[][] listdir(char[] pathname) +{ + char[][] result; + + bool listing(char[] filename) + { + result ~= filename; + return true; // continue + } + + listdir(pathname, &listing); + return result; +} + +char[][] listdir(char[] pathname, char[] pattern) +{ char[][] result; + + bool callback(DirEntry* de) + { + if (de.isdir) + listdir(de.name, &callback); + else + { if (std.path.fnmatch(de.name, pattern)) + result ~= de.name; + } + return true; // continue + } + + listdir(pathname, &callback); + return result; +} + +char[][] listdir(char[] pathname, RegExp r) +{ char[][] result; + + bool callback(DirEntry* de) + { + if (de.isdir) + listdir(de.name, &callback); + else + { if (r.test(de.name)) + result ~= de.name; + } + return true; // continue + } + + listdir(pathname, &callback); + return result; +} + +void listdir(char[] pathname, bool delegate(char[] filename) callback) +{ + bool listing(DirEntry* de) + { + return callback(std.path.getBaseName(de.name)); + } + + listdir(pathname, &listing); +} + +void listdir(char[] pathname, bool delegate(DirEntry* de) callback) +{ + DIR* h; + dirent* fdata; + DirEntry de; + + h = opendir(toStringz(pathname)); + if (h) + { + try + { + while((fdata = readdir(h)) != null) + { + // Skip "." and ".." + if (!std.string.strcmp(fdata.d_name.ptr, ".") || + !std.string.strcmp(fdata.d_name.ptr, "..")) + continue; + + de.init(pathname, fdata); + if (!callback(&de)) + break; + } + } + finally + { + closedir(h); + } + } + else + { + throw new FileException(pathname, getErrno()); + } +} + + +/*************************************************** + * Copy a file. File timestamps are preserved. + */ + +void copy(char[] from, char[] to) +{ + version (all) + { + struct_stat statbuf; + + char* fromz = toStringz(from); + char* toz = toStringz(to); + //printf("file.copy(from='%s', to='%s')\n", fromz, toz); + + int fd = sys.open(fromz, O_RDONLY); + if (fd == -1) + { + //printf("\topen error, errno = %d\n",getErrno()); + goto err1; + } + + //printf("\tfile opened\n"); + if (sys.fstat(fd, &statbuf)) + { + //printf("\tfstat error, errno = %d\n",getErrno()); + goto err2; + } + + int fdw = sys.open(toz, O_CREAT | O_WRONLY | O_TRUNC, 0660); + if (fdw == -1) + { + //printf("\topen error, errno = %d\n",getErrno()); + goto err2; + } + + size_t BUFSIZ = 4096 * 16; + void* buf = std.c.stdlib.malloc(BUFSIZ); + if (!buf) + { BUFSIZ = 4096; + buf = std.c.stdlib.malloc(BUFSIZ); + } + if (!buf) + { + //printf("\topen error, errno = %d\n",getErrno()); + goto err4; + } + + for (auto size = statbuf.st_size; size; ) + { size_t toread = (size > BUFSIZ) ? BUFSIZ : size; + + auto n = sys.read(fd, buf, toread); + if (n != toread) + { + //printf("\tread error, errno = %d\n",getErrno()); + goto err5; + } + n = sys.write(fdw, buf, toread); + if (n != toread) + { + //printf("\twrite error, errno = %d\n",getErrno()); + goto err5; + } + size -= toread; + } + + std.c.stdlib.free(buf); + + if (sys.close(fdw) == -1) + { + //printf("\tclose error, errno = %d\n",getErrno()); + goto err2; + } + + utimbuf utim; + utim.actime = cast(typeof(utim.actime))statbuf.st_atime; + utim.modtime = cast(typeof(utim.modtime))statbuf.st_mtime; + if (utime(toz, &utim) == -1) + { + //printf("\tutime error, errno = %d\n",getErrno()); + goto err3; + } + + if (sys.close(fd) == -1) + { + //printf("\tclose error, errno = %d\n",getErrno()); + goto err1; + } + + return; + +err5: + std.c.stdlib.free(buf); +err4: + sys.close(fdw); +err3: + std.c.stdio.remove(toz); +err2: + sys.close(fd); +err1: + throw new FileException(from, getErrno()); + } + else + { + void[] buffer; + + buffer = read(from); + write(to, buffer); + delete buffer; + } +} + + + +} + +unittest +{ + //printf("std.file.unittest\n"); + void[] buf; + + buf = new void[10]; + (cast(byte[])buf)[] = 3; + write("unittest_write.tmp", buf); + void buf2[] = read("unittest_write.tmp"); + assert(buf == buf2); + + copy("unittest_write.tmp", "unittest_write2.tmp"); + buf2 = read("unittest_write2.tmp"); + assert(buf == buf2); + + remove("unittest_write.tmp"); + if (exists("unittest_write.tmp")) + assert(0); + remove("unittest_write2.tmp"); + if (exists("unittest_write2.tmp")) + assert(0); +} + +unittest +{ + listdir (".", delegate bool (DirEntry * de) + { + auto s = std.string.format("%s : c %s, w %s, a %s", de.name, + toUTCString (de.creationTime), + toUTCString (de.lastWriteTime), + toUTCString (de.lastAccessTime)); + return true; + } + ); +} + +