d: Merge upstream dmd, druntime 4c18eed967, phobos d945686a4.
[official-gcc.git] / gcc / d / dmd / root / file.d
blob1fb105682ea39f92aadca13ab2a3c6be18c83ea0
1 /**
2 * Read a file from disk and store it in memory.
4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5 * Authors: Walter Bright, https://www.digitalmars.com
6 * License: $(LINK2 https://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
12 module dmd.root.file;
14 import core.stdc.errno;
15 import core.stdc.stdio;
16 import core.stdc.stdlib;
17 import core.stdc.string : strerror;
18 import core.sys.posix.fcntl;
19 import core.sys.posix.unistd;
20 import core.sys.windows.winbase;
21 import core.sys.windows.winnt;
22 import dmd.root.filename;
23 import dmd.root.rmem;
24 import dmd.root.string;
26 import dmd.common.file;
27 import dmd.common.string;
29 nothrow:
31 /// Owns a (rmem-managed) buffer.
32 struct Buffer
34 ubyte[] data;
36 nothrow:
38 this(this) @disable;
40 ~this() pure nothrow
42 mem.xfree(data.ptr);
45 /// Transfers ownership of the buffer to the caller.
46 ubyte[] extractSlice() pure nothrow @nogc @safe
48 auto result = data;
49 data = null;
50 return result;
54 ///
55 struct File
57 ///
58 static struct ReadResult
60 bool success;
61 Buffer buffer;
63 /// Transfers ownership of the buffer to the caller.
64 ubyte[] extractSlice() pure nothrow @nogc @safe
66 return buffer.extractSlice();
69 /// ditto
70 /// Include the null-terminator at the end of the buffer in the returned array.
71 ubyte[] extractDataZ() @nogc nothrow pure
73 auto result = buffer.extractSlice();
74 return result.ptr[0 .. result.length + 1];
78 nothrow:
79 /// Read the full content of a file.
80 static ReadResult read(const(char)[] name)
82 ReadResult result;
84 version (Posix)
86 size_t size;
87 stat_t buf;
88 ssize_t numread;
89 //printf("File::read('%s')\n",name);
90 int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY));
91 if (fd == -1)
93 //perror("\topen error");
94 return result;
96 //printf("\tfile opened\n");
97 if (fstat(fd, &buf))
99 //perror("\tfstat error");
100 close(fd);
101 return result;
103 size = cast(size_t)buf.st_size;
104 ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4);
105 numread = .read(fd, buffer, size);
106 if (numread != size)
108 //perror("\tread error");
109 goto err2;
111 if (close(fd) == -1)
113 //perror("\tclose error");
114 goto err;
116 // Always store a wchar ^Z past end of buffer so scanner has a
117 // sentinel, although ^Z got obselete, so fill with two 0s and add
118 // two more so lexer doesn't read pass the buffer.
119 buffer[size .. size + 4] = 0;
121 result.success = true;
122 result.buffer.data = buffer[0 .. size];
123 return result;
124 err2:
125 close(fd);
126 err:
127 mem.xfree(buffer);
128 return result;
130 else version (Windows)
132 DWORD size;
133 DWORD numread;
135 // work around Windows file path length limitation
136 // (see documentation for extendedPathThen).
137 HANDLE h = name.extendedPathThen!
138 (p => CreateFileW(p.ptr,
139 GENERIC_READ,
140 FILE_SHARE_READ,
141 null,
142 OPEN_EXISTING,
143 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
144 null));
145 if (h == INVALID_HANDLE_VALUE)
146 return result;
147 size = GetFileSize(h, null);
148 ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4);
149 if (ReadFile(h, buffer, size, &numread, null) != TRUE)
150 goto err2;
151 if (numread != size)
152 goto err2;
153 if (!CloseHandle(h))
154 goto err;
155 // Always store a wchar ^Z past end of buffer so scanner has a
156 // sentinel, although ^Z got obselete, so fill with two 0s and add
157 // two more so lexer doesn't read pass the buffer.
158 buffer[size .. size + 4] = 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;
168 else
170 assert(0);
174 /// Write a file, returning `true` on success.
175 static bool write(const(char)* name, const void[] data)
177 import dmd.common.file : writeFile;
178 return writeFile(name, data);
181 ///ditto
182 static bool write(const(char)[] name, const void[] data)
184 return name.toCStringThen!((fname) => write(fname.ptr, data));
187 /// Delete a file.
188 extern (C++) static void remove(const(char)* name)
190 version (Posix)
192 .remove(name);
194 else version (Windows)
196 name.toDString.extendedPathThen!(p => DeleteFileW(p.ptr));
198 else
200 static assert(0);
204 /***************************************************
205 * Update file
207 * If the file exists and is identical to what is to be written,
208 * merely update the timestamp on the file.
209 * Otherwise, write the file.
211 * The idea is writes are much slower than reads, and build systems
212 * often wind up generating identical files.
213 * Params:
214 * name = name of file to update
215 * data = updated contents of file
216 * Returns:
217 * `true` on success
219 static bool update(const(char)* namez, const void[] data)
221 enum log = false;
222 if (log) printf("update %s\n", namez);
224 if (data.length != File.size(namez))
225 return write(namez, data); // write new file
227 if (log) printf("same size\n");
229 /* The file already exists, and is the same size.
230 * Read it in, and compare for equality.
232 //if (FileMapping!(const ubyte)(namez)[] != data[])
233 return write(namez, data); // contents not same, so write new file
234 //if (log) printf("same contents\n");
236 /* Contents are identical, so set timestamp of existing file to current time
238 //return touch(namez);
241 ///ditto
242 static bool update(const(char)[] name, const void[] data)
244 return name.toCStringThen!(fname => update(fname.ptr, data));
247 /// Size of a file in bytes.
248 /// Params: namez = null-terminated filename
249 /// Returns: `ulong.max` on any error, the length otherwise.
250 static ulong size(const char* namez)
252 version (Posix)
254 stat_t buf;
255 if (stat(namez, &buf) == 0)
256 return buf.st_size;
258 else version (Windows)
260 const nameStr = namez.toDString();
261 import core.sys.windows.windows;
262 WIN32_FILE_ATTRIBUTE_DATA fad = void;
263 // Doesn't exist, not a regular file, different size
264 if (nameStr.extendedPathThen!(p => GetFileAttributesExW(p.ptr, GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, &fad)) != 0)
265 return (ulong(fad.nFileSizeHigh) << 32UL) | fad.nFileSizeLow;
267 else static assert(0);
268 // Error cases go here.
269 return ulong.max;
273 private
275 version (linux) version (PPC)
277 // https://issues.dlang.org/show_bug.cgi?id=22823
278 // Define our own version of stat_t, as older versions of the compiler
279 // had the st_size field at the wrong offset on PPC.
280 alias stat_t_imported = core.sys.posix.sys.stat.stat_t;
281 static if (stat_t_imported.st_size.offsetof != 48)
283 extern (C) nothrow @nogc:
284 struct stat_t
286 ulong[6] __pad1;
287 ulong st_size;
288 ulong[6] __pad2;
290 version (CRuntime_Glibc)
292 int fstat64(int, stat_t*) @trusted;
293 alias fstat = fstat64;
294 int stat64(const scope char*, stat_t*) @system;
295 alias stat = stat64;
297 else
299 int fstat(int, stat_t*) @trusted;
300 int stat(const scope char*, stat_t*) @system;