1 /** 2 * Read a file from disk and store it in memory. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: Walter Bright, http://www.digitalmars.com 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/file.d, root/_file.d) 8 * Documentation: https://dlang.org/phobos/dmd_root_file.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/file.d 10 */ 11 12 module dmd.root.file; 13 14 import core.stdc.errno; 15 import core.stdc.stdio; 16 import core.stdc.stdlib; 17 import core.sys.posix.fcntl; 18 import core.sys.posix.unistd; 19 import core.sys.windows.winbase; 20 import core.sys.windows.winnt; 21 import dmd.root.filename; 22 import dmd.root.rmem; 23 import dmd.root..string; 24 25 /// Owns a (rmem-managed) file buffer. 26 struct FileBuffer 27 { 28 ubyte[] data; 29 30 this(this) @disable; 31 32 ~this() pure nothrow 33 { 34 mem.xfree(data.ptr); 35 } 36 37 /// Transfers ownership of the buffer to the caller. 38 ubyte[] extractSlice() pure nothrow @nogc @safe 39 { 40 auto result = data; 41 data = null; 42 return result; 43 } 44 45 extern (C++) static FileBuffer* create() 46 { 47 return new FileBuffer(); 48 } 49 } 50 51 /// 52 struct File 53 { 54 /// 55 static struct ReadResult 56 { 57 bool success; 58 FileBuffer buffer; 59 60 /// Transfers ownership of the buffer to the caller. 61 ubyte[] extractSlice() pure nothrow @nogc @safe 62 { 63 return buffer.extractSlice(); 64 } 65 66 /// ditto 67 /// Include the null-terminator at the end of the buffer in the returned array. 68 ubyte[] extractDataZ() @nogc nothrow pure 69 { 70 auto result = buffer.extractSlice(); 71 return result.ptr[0 .. result.length + 1]; 72 } 73 } 74 75 nothrow: 76 /// Read the full content of a file. 77 extern (C++) static ReadResult read(const(char)* name) 78 { 79 return read(name.toDString()); 80 } 81 82 /// Ditto 83 static ReadResult read(const(char)[] name) 84 { 85 ReadResult result; 86 87 version (Posix) 88 { 89 size_t size; 90 stat_t buf; 91 ssize_t numread; 92 //printf("File::read('%s')\n",name); 93 int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY)); 94 if (fd == -1) 95 { 96 //printf("\topen error, errno = %d\n",errno); 97 return result; 98 } 99 //printf("\tfile opened\n"); 100 if (fstat(fd, &buf)) 101 { 102 printf("\tfstat error, errno = %d\n", errno); 103 close(fd); 104 return result; 105 } 106 size = cast(size_t)buf.st_size; 107 ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 2); 108 numread = .read(fd, buffer, size); 109 if (numread != size) 110 { 111 printf("\tread error, errno = %d\n", errno); 112 goto err2; 113 } 114 if (close(fd) == -1) 115 { 116 printf("\tclose error, errno = %d\n", errno); 117 goto err; 118 } 119 // Always store a wchar ^Z past end of buffer so scanner has a sentinel 120 buffer[size] = 0; // ^Z is obsolete, use 0 121 buffer[size + 1] = 0; 122 result.success = true; 123 result.buffer.data = buffer[0 .. size]; 124 return result; 125 err2: 126 close(fd); 127 err: 128 mem.xfree(buffer); 129 return result; 130 } 131 else version (Windows) 132 { 133 DWORD size; 134 DWORD numread; 135 136 // work around Windows file path length limitation 137 // (see documentation for extendedPathThen). 138 HANDLE h = name.extendedPathThen! 139 (p => CreateFileW(p.ptr, 140 GENERIC_READ, 141 FILE_SHARE_READ, 142 null, 143 OPEN_EXISTING, 144 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 145 null)); 146 if (h == INVALID_HANDLE_VALUE) 147 return result; 148 size = GetFileSize(h, null); 149 ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 2); 150 if (ReadFile(h, buffer, size, &numread, null) != TRUE) 151 goto err2; 152 if (numread != size) 153 goto err2; 154 if (!CloseHandle(h)) 155 goto err; 156 // Always store a wchar ^Z past end of buffer so scanner has a sentinel 157 buffer[size] = 0; // ^Z is obsolete, use 0 158 buffer[size + 1] = 0; 159 result.success = true; 160 result.buffer.data = buffer[0 .. size]; 161 return result; 162 err2: 163 CloseHandle(h); 164 err: 165 mem.xfree(buffer); 166 return result; 167 } 168 else 169 { 170 assert(0); 171 } 172 } 173 174 /// Write a file, returning `true` on success. 175 extern (D) static bool write(const(char)* name, const void[] data) 176 { 177 version (Posix) 178 { 179 ssize_t numwritten; 180 int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4); 181 if (fd == -1) 182 goto err; 183 numwritten = .write(fd, data.ptr, data.length); 184 if (numwritten != data.length) 185 goto err2; 186 if (close(fd) == -1) 187 goto err; 188 return true; 189 err2: 190 close(fd); 191 .remove(name); 192 err: 193 return false; 194 } 195 else version (Windows) 196 { 197 DWORD numwritten; // here because of the gotos 198 const nameStr = name.toDString; 199 // work around Windows file path length limitation 200 // (see documentation for extendedPathThen). 201 HANDLE h = nameStr.extendedPathThen! 202 (p => CreateFileW(p.ptr, 203 GENERIC_WRITE, 204 0, 205 null, 206 CREATE_ALWAYS, 207 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 208 null)); 209 if (h == INVALID_HANDLE_VALUE) 210 goto err; 211 212 if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE) 213 goto err2; 214 if (numwritten != data.length) 215 goto err2; 216 if (!CloseHandle(h)) 217 goto err; 218 return true; 219 err2: 220 CloseHandle(h); 221 nameStr.extendedPathThen!(p => DeleteFileW(p.ptr)); 222 err: 223 return false; 224 } 225 else 226 { 227 assert(0); 228 } 229 } 230 231 ///ditto 232 extern(D) static bool write(const(char)[] name, const void[] data) 233 { 234 return name.toCStringThen!((fname) => write(fname.ptr, data)); 235 } 236 237 /// ditto 238 extern (C++) static bool write(const(char)* name, const(void)* data, size_t size) 239 { 240 return write(name, data[0 .. size]); 241 } 242 243 /// Delete a file. 244 extern (C++) static void remove(const(char)* name) 245 { 246 version (Posix) 247 { 248 .remove(name); 249 } 250 else version (Windows) 251 { 252 name.toDString.extendedPathThen!(p => DeleteFileW(p.ptr)); 253 } 254 else 255 { 256 assert(0); 257 } 258 } 259 }