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 + 4); 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 buffer[size + 2] = 0; //add two more so lexer doesnt read pass the buffer 123 buffer[size + 3] = 0; 124 125 result.success = true; 126 result.buffer.data = buffer[0 .. size]; 127 return result; 128 err2: 129 close(fd); 130 err: 131 mem.xfree(buffer); 132 return result; 133 } 134 else version (Windows) 135 { 136 DWORD size; 137 DWORD numread; 138 139 // work around Windows file path length limitation 140 // (see documentation for extendedPathThen). 141 HANDLE h = name.extendedPathThen! 142 (p => CreateFileW(p.ptr, 143 GENERIC_READ, 144 FILE_SHARE_READ, 145 null, 146 OPEN_EXISTING, 147 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 148 null)); 149 if (h == INVALID_HANDLE_VALUE) 150 return result; 151 size = GetFileSize(h, null); 152 ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4); 153 if (ReadFile(h, buffer, size, &numread, null) != TRUE) 154 goto err2; 155 if (numread != size) 156 goto err2; 157 if (!CloseHandle(h)) 158 goto err; 159 // Always store a wchar ^Z past end of buffer so scanner has a sentinel 160 buffer[size] = 0; // ^Z is obsolete, use 0 161 buffer[size + 1] = 0; 162 buffer[size + 2] = 0; //add two more so lexer doesnt read pass the buffer 163 buffer[size + 3] = 0; 164 result.success = true; 165 result.buffer.data = buffer[0 .. size]; 166 return result; 167 err2: 168 CloseHandle(h); 169 err: 170 mem.xfree(buffer); 171 return result; 172 } 173 else 174 { 175 assert(0); 176 } 177 } 178 179 /// Write a file, returning `true` on success. 180 extern (D) static bool write(const(char)* name, const void[] data) 181 { 182 version (Posix) 183 { 184 ssize_t numwritten; 185 int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4); 186 if (fd == -1) 187 goto err; 188 numwritten = .write(fd, data.ptr, data.length); 189 if (numwritten != data.length) 190 goto err2; 191 if (close(fd) == -1) 192 goto err; 193 return true; 194 err2: 195 close(fd); 196 .remove(name); 197 err: 198 return false; 199 } 200 else version (Windows) 201 { 202 DWORD numwritten; // here because of the gotos 203 const nameStr = name.toDString; 204 // work around Windows file path length limitation 205 // (see documentation for extendedPathThen). 206 HANDLE h = nameStr.extendedPathThen! 207 (p => CreateFileW(p.ptr, 208 GENERIC_WRITE, 209 0, 210 null, 211 CREATE_ALWAYS, 212 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 213 null)); 214 if (h == INVALID_HANDLE_VALUE) 215 goto err; 216 217 if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE) 218 goto err2; 219 if (numwritten != data.length) 220 goto err2; 221 if (!CloseHandle(h)) 222 goto err; 223 return true; 224 err2: 225 CloseHandle(h); 226 nameStr.extendedPathThen!(p => DeleteFileW(p.ptr)); 227 err: 228 return false; 229 } 230 else 231 { 232 assert(0); 233 } 234 } 235 236 ///ditto 237 extern(D) static bool write(const(char)[] name, const void[] data) 238 { 239 return name.toCStringThen!((fname) => write(fname.ptr, data)); 240 } 241 242 /// ditto 243 extern (C++) static bool write(const(char)* name, const(void)* data, size_t size) 244 { 245 return write(name, data[0 .. size]); 246 } 247 248 /// Delete a file. 249 extern (C++) static void remove(const(char)* name) 250 { 251 version (Posix) 252 { 253 .remove(name); 254 } 255 else version (Windows) 256 { 257 name.toDString.extendedPathThen!(p => DeleteFileW(p.ptr)); 258 } 259 else 260 { 261 assert(0); 262 } 263 } 264 }