xref: /netbsd/external/gpl3/gcc/dist/gcc/d/dmd/common/file.d (revision f0fbc68b)
1*f0fbc68bSmrg /**
2*f0fbc68bSmrg  * File utilities.
3*f0fbc68bSmrg  *
4*f0fbc68bSmrg  * Functions and objects dedicated to file I/O and management. TODO: Move here artifacts
5*f0fbc68bSmrg  * from places such as root/ so both the frontend and the backend have access to them.
6*f0fbc68bSmrg  *
7*f0fbc68bSmrg  * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
8*f0fbc68bSmrg  * Authors:   Walter Bright, https://www.digitalmars.com
9*f0fbc68bSmrg  * License:   $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
10*f0fbc68bSmrg  * Source:    $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d, common/_file.d)
11*f0fbc68bSmrg  * Documentation: https://dlang.org/phobos/dmd_common_file.html
12*f0fbc68bSmrg  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/file.d
13*f0fbc68bSmrg  */
14*f0fbc68bSmrg 
15*f0fbc68bSmrg module dmd.common.file;
16*f0fbc68bSmrg 
17*f0fbc68bSmrg import core.stdc.errno : errno;
18*f0fbc68bSmrg import core.stdc.stdio : fprintf, remove, rename, stderr;
19*f0fbc68bSmrg import core.stdc.stdlib : exit;
20*f0fbc68bSmrg import core.stdc.string : strerror;
21*f0fbc68bSmrg import core.sys.windows.winbase;
22*f0fbc68bSmrg import core.sys.windows.winnt;
23*f0fbc68bSmrg import core.sys.posix.fcntl;
24*f0fbc68bSmrg import core.sys.posix.unistd;
25*f0fbc68bSmrg 
26*f0fbc68bSmrg import dmd.common.string;
27*f0fbc68bSmrg 
28*f0fbc68bSmrg nothrow:
29*f0fbc68bSmrg 
30*f0fbc68bSmrg /**
31*f0fbc68bSmrg Encapsulated management of a memory-mapped file.
32*f0fbc68bSmrg 
33*f0fbc68bSmrg Params:
34*f0fbc68bSmrg Datum = the mapped data type: Use a POD of size 1 for read/write mapping
35*f0fbc68bSmrg and a `const` version thereof for read-only mapping. Other primitive types
36*f0fbc68bSmrg should work, but have not been yet tested.
37*f0fbc68bSmrg */
FileMapping(Datum)38*f0fbc68bSmrg struct FileMapping(Datum)
39*f0fbc68bSmrg {
40*f0fbc68bSmrg     static assert(__traits(isPOD, Datum) && Datum.sizeof == 1,
41*f0fbc68bSmrg         "Not tested with other data types yet. Add new types with care.");
42*f0fbc68bSmrg 
43*f0fbc68bSmrg     version(Posix) enum invalidHandle = -1;
44*f0fbc68bSmrg     else version(Windows) enum invalidHandle = INVALID_HANDLE_VALUE;
45*f0fbc68bSmrg 
46*f0fbc68bSmrg     // state {
47*f0fbc68bSmrg     /// Handle of underlying file
48*f0fbc68bSmrg     private auto handle = invalidHandle;
49*f0fbc68bSmrg     /// File mapping object needed on Windows
50*f0fbc68bSmrg     version(Windows) private HANDLE fileMappingObject = invalidHandle;
51*f0fbc68bSmrg     /// Memory-mapped array
52*f0fbc68bSmrg     private Datum[] data;
53*f0fbc68bSmrg     /// Name of underlying file, zero-terminated
54*f0fbc68bSmrg     private const(char)* name;
55*f0fbc68bSmrg     // state }
56*f0fbc68bSmrg 
57*f0fbc68bSmrg   nothrow:
58*f0fbc68bSmrg 
59*f0fbc68bSmrg     /**
60*f0fbc68bSmrg     Open `filename` and map it in memory. If `Datum` is `const`, opens for
61*f0fbc68bSmrg     read-only and maps the content in memory; no error is issued if the file
62*f0fbc68bSmrg     does not exist. This makes it easy to treat a non-existing file as empty.
63*f0fbc68bSmrg 
64*f0fbc68bSmrg     If `Datum` is mutable, opens for read/write (creates file if it does not
65*f0fbc68bSmrg     exist) and fails fatally on any error.
66*f0fbc68bSmrg 
67*f0fbc68bSmrg     Due to quirks in `mmap`, if the file is empty, `handle` is valid but `data`
68*f0fbc68bSmrg     is `null`. This state is valid and accounted for.
69*f0fbc68bSmrg 
70*f0fbc68bSmrg     Params:
71*f0fbc68bSmrg     filename = the name of the file to be mapped in memory
72*f0fbc68bSmrg     */
73*f0fbc68bSmrg     this(const char* filename)
74*f0fbc68bSmrg     {
75*f0fbc68bSmrg         version (Posix)
76*f0fbc68bSmrg         {
77*f0fbc68bSmrg             import core.sys.posix.sys.mman;
78*f0fbc68bSmrg             import core.sys.posix.fcntl : open, O_CREAT, O_RDONLY, O_RDWR, S_IRGRP, S_IROTH, S_IRUSR, S_IWUSR;
79*f0fbc68bSmrg 
80*f0fbc68bSmrg             handle = open(filename, is(Datum == const) ? O_RDONLY : (O_CREAT | O_RDWR),
81*f0fbc68bSmrg                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
82*f0fbc68bSmrg 
83*f0fbc68bSmrg             if (handle == invalidHandle)
84*f0fbc68bSmrg             {
85*f0fbc68bSmrg                 static if (is(Datum == const))
86*f0fbc68bSmrg                 {
87*f0fbc68bSmrg                     // No error, nonexisting file in read mode behaves like an empty file.
88*f0fbc68bSmrg                     return;
89*f0fbc68bSmrg                 }
90*f0fbc68bSmrg                 else
91*f0fbc68bSmrg                 {
92*f0fbc68bSmrg                     fprintf(stderr, "open(\"%s\") failed: %s\n", filename, strerror(errno));
93*f0fbc68bSmrg                     exit(1);
94*f0fbc68bSmrg                 }
95*f0fbc68bSmrg             }
96*f0fbc68bSmrg 
97*f0fbc68bSmrg             const size = fileSize(handle);
98*f0fbc68bSmrg 
99*f0fbc68bSmrg             if (size > 0 && size != ulong.max && size <= size_t.max)
100*f0fbc68bSmrg             {
101*f0fbc68bSmrg                 auto p = mmap(null, cast(size_t) size, is(Datum == const) ? PROT_READ : PROT_WRITE, MAP_SHARED, handle, 0);
102*f0fbc68bSmrg                 if (p == MAP_FAILED)
103*f0fbc68bSmrg                 {
104*f0fbc68bSmrg                     fprintf(stderr, "mmap(null, %zu) for \"%s\" failed: %s\n", cast(size_t) size, filename, strerror(errno));
105*f0fbc68bSmrg                     exit(1);
106*f0fbc68bSmrg                 }
107*f0fbc68bSmrg                 // The cast below will always work because it's gated by the `size <= size_t.max` condition.
108*f0fbc68bSmrg                 data = cast(Datum[]) p[0 .. cast(size_t) size];
109*f0fbc68bSmrg             }
110*f0fbc68bSmrg         }
111*f0fbc68bSmrg         else version(Windows)
112*f0fbc68bSmrg         {
113*f0fbc68bSmrg             static if (is(Datum == const))
114*f0fbc68bSmrg             {
115*f0fbc68bSmrg                 enum createFileMode = GENERIC_READ;
116*f0fbc68bSmrg                 enum openFlags = OPEN_EXISTING;
117*f0fbc68bSmrg             }
118*f0fbc68bSmrg             else
119*f0fbc68bSmrg             {
120*f0fbc68bSmrg                 enum createFileMode = GENERIC_READ | GENERIC_WRITE;
121*f0fbc68bSmrg                 enum openFlags = CREATE_ALWAYS;
122*f0fbc68bSmrg             }
123*f0fbc68bSmrg 
124*f0fbc68bSmrg             handle = filename.asDString.extendedPathThen!(p => CreateFileW(p.ptr, createFileMode, 0, null, openFlags, FILE_ATTRIBUTE_NORMAL, null));
125*f0fbc68bSmrg             if (handle == invalidHandle)
126*f0fbc68bSmrg             {
127*f0fbc68bSmrg                 static if (is(Datum == const))
128*f0fbc68bSmrg                 {
129*f0fbc68bSmrg                     return;
130*f0fbc68bSmrg                 }
131*f0fbc68bSmrg                 else
132*f0fbc68bSmrg                 {
133*f0fbc68bSmrg                     fprintf(stderr, "CreateFileW() failed for \"%s\": %d\n", filename, GetLastError());
134*f0fbc68bSmrg                     exit(1);
135*f0fbc68bSmrg                 }
136*f0fbc68bSmrg             }
137*f0fbc68bSmrg             createMapping(filename, fileSize(handle));
138*f0fbc68bSmrg         }
139*f0fbc68bSmrg         else static assert(0);
140*f0fbc68bSmrg 
141*f0fbc68bSmrg         // Save the name for later. Technically there's no need: on Linux one can use readlink on /proc/self/fd/NNN.
142*f0fbc68bSmrg         // On BSD and OSX one can use fcntl with F_GETPATH. On Windows one can use GetFileInformationByHandleEx.
143*f0fbc68bSmrg         // But just saving the name is simplest, fastest, and most portable...
144*f0fbc68bSmrg         import core.stdc.string : strlen;
145*f0fbc68bSmrg         import core.stdc.stdlib : malloc;
146*f0fbc68bSmrg         import core.stdc.string : memcpy;
147*f0fbc68bSmrg         auto totalNameLength = filename.strlen() + 1;
148*f0fbc68bSmrg         name = cast(char*) memcpy(malloc(totalNameLength), filename, totalNameLength);
149*f0fbc68bSmrg         name || assert(0, "FileMapping: Out of memory.");
150*f0fbc68bSmrg     }
151*f0fbc68bSmrg 
152*f0fbc68bSmrg     /**
153*f0fbc68bSmrg     Common code factored opportunistically. Windows only. Assumes `handle` is
154*f0fbc68bSmrg     already pointing to an opened file. Initializes the `fileMappingObject`
155*f0fbc68bSmrg     and `data` members.
156*f0fbc68bSmrg 
157*f0fbc68bSmrg     Params:
158*f0fbc68bSmrg     filename = the file to be mapped
159*f0fbc68bSmrg     size = the size of the file in bytes
160*f0fbc68bSmrg     */
161*f0fbc68bSmrg     version(Windows) private void createMapping(const char* filename, ulong size)
162*f0fbc68bSmrg     {
163*f0fbc68bSmrg         assert(size <= size_t.max || size == ulong.max);
164*f0fbc68bSmrg         assert(handle != invalidHandle);
165*f0fbc68bSmrg         assert(data is null);
166*f0fbc68bSmrg         assert(fileMappingObject == invalidHandle);
167*f0fbc68bSmrg 
168*f0fbc68bSmrg         if (size == 0 || size == ulong.max)
169*f0fbc68bSmrg             return;
170*f0fbc68bSmrg 
171*f0fbc68bSmrg         static if (is(Datum == const))
172*f0fbc68bSmrg         {
173*f0fbc68bSmrg             enum fileMappingFlags = PAGE_READONLY;
174*f0fbc68bSmrg             enum mapViewFlags = FILE_MAP_READ;
175*f0fbc68bSmrg         }
176*f0fbc68bSmrg         else
177*f0fbc68bSmrg         {
178*f0fbc68bSmrg             enum fileMappingFlags = PAGE_READWRITE;
179*f0fbc68bSmrg             enum mapViewFlags = FILE_MAP_WRITE;
180*f0fbc68bSmrg         }
181*f0fbc68bSmrg 
182*f0fbc68bSmrg         fileMappingObject = CreateFileMappingW(handle, null, fileMappingFlags, 0, 0, null);
183*f0fbc68bSmrg         if (!fileMappingObject)
184*f0fbc68bSmrg         {
185*f0fbc68bSmrg             fprintf(stderr, "CreateFileMappingW(%p) failed for %llu bytes of \"%s\": %d\n",
186*f0fbc68bSmrg                 handle, size, filename, GetLastError());
187*f0fbc68bSmrg             fileMappingObject = invalidHandle;  // by convention always use invalidHandle, not null
188*f0fbc68bSmrg             exit(1);
189*f0fbc68bSmrg         }
190*f0fbc68bSmrg         auto p = MapViewOfFile(fileMappingObject, mapViewFlags, 0, 0, 0);
191*f0fbc68bSmrg         if (!p)
192*f0fbc68bSmrg         {
193*f0fbc68bSmrg             fprintf(stderr, "MapViewOfFile() failed for \"%s\": %d\n", filename, GetLastError());
194*f0fbc68bSmrg             exit(1);
195*f0fbc68bSmrg         }
196*f0fbc68bSmrg         data = cast(Datum[]) p[0 .. cast(size_t) size];
197*f0fbc68bSmrg     }
198*f0fbc68bSmrg 
199*f0fbc68bSmrg     // Not copyable or assignable (for now).
200*f0fbc68bSmrg     @disable this(const FileMapping!Datum rhs);
201*f0fbc68bSmrg     @disable void opAssign(const ref FileMapping!Datum rhs);
202*f0fbc68bSmrg 
203*f0fbc68bSmrg     /**
204*f0fbc68bSmrg     Frees resources associated with this mapping. However, it does not deallocate the name.
205*f0fbc68bSmrg     */
206*f0fbc68bSmrg     ~this() pure nothrow
207*f0fbc68bSmrg     {
208*f0fbc68bSmrg         if (!active)
209*f0fbc68bSmrg             return;
210*f0fbc68bSmrg         fakePure({
211*f0fbc68bSmrg             version (Posix)
212*f0fbc68bSmrg             {
213*f0fbc68bSmrg                 import core.sys.posix.sys.mman : munmap;
214*f0fbc68bSmrg                 import core.sys.posix.unistd : close;
215*f0fbc68bSmrg 
216*f0fbc68bSmrg                 // Cannot call fprintf from inside a destructor, so exiting silently.
217*f0fbc68bSmrg 
218*f0fbc68bSmrg                 if (data.ptr && munmap(cast(void*) data.ptr, data.length) != 0)
219*f0fbc68bSmrg                 {
220*f0fbc68bSmrg                     exit(1);
221*f0fbc68bSmrg                 }
222*f0fbc68bSmrg                 data = null;
223*f0fbc68bSmrg                 if (handle != invalidHandle && close(handle) != 0)
224*f0fbc68bSmrg                 {
225*f0fbc68bSmrg                     exit(1);
226*f0fbc68bSmrg                 }
227*f0fbc68bSmrg                 handle = invalidHandle;
228*f0fbc68bSmrg             }
229*f0fbc68bSmrg             else version(Windows)
230*f0fbc68bSmrg             {
231*f0fbc68bSmrg                 if (data.ptr !is null && UnmapViewOfFile(cast(void*) data.ptr) == 0)
232*f0fbc68bSmrg                 {
233*f0fbc68bSmrg                     exit(1);
234*f0fbc68bSmrg                 }
235*f0fbc68bSmrg                 data = null;
236*f0fbc68bSmrg                 if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
237*f0fbc68bSmrg                 {
238*f0fbc68bSmrg                     exit(1);
239*f0fbc68bSmrg                 }
240*f0fbc68bSmrg                 fileMappingObject = invalidHandle;
241*f0fbc68bSmrg                 if (handle != invalidHandle && CloseHandle(handle) == 0)
242*f0fbc68bSmrg                 {
243*f0fbc68bSmrg                     exit(1);
244*f0fbc68bSmrg                 }
245*f0fbc68bSmrg                 handle = invalidHandle;
246*f0fbc68bSmrg             }
247*f0fbc68bSmrg             else static assert(0);
248*f0fbc68bSmrg         });
249*f0fbc68bSmrg     }
250*f0fbc68bSmrg 
251*f0fbc68bSmrg     /**
252*f0fbc68bSmrg     Returns the zero-terminated file name associated with the mapping. Can NOT
253*f0fbc68bSmrg     be saved beyond the lifetime of `this`.
254*f0fbc68bSmrg     */
255*f0fbc68bSmrg     private const(char)* filename() const pure @nogc @safe nothrow { return name; }
256*f0fbc68bSmrg 
257*f0fbc68bSmrg     /**
258*f0fbc68bSmrg     Frees resources associated with this mapping. However, it does not deallocate the name.
259*f0fbc68bSmrg     Reinitializes `this` as a fresh object that can be reused.
260*f0fbc68bSmrg     */
261*f0fbc68bSmrg     void close()
262*f0fbc68bSmrg     {
263*f0fbc68bSmrg         __dtor();
264*f0fbc68bSmrg         handle = invalidHandle;
265*f0fbc68bSmrg         version(Windows) fileMappingObject = invalidHandle;
266*f0fbc68bSmrg         data = null;
267*f0fbc68bSmrg         name = null;
268*f0fbc68bSmrg     }
269*f0fbc68bSmrg 
270*f0fbc68bSmrg     /**
271*f0fbc68bSmrg     Deletes the underlying file and frees all resources associated.
272*f0fbc68bSmrg     Reinitializes `this` as a fresh object that can be reused.
273*f0fbc68bSmrg 
274*f0fbc68bSmrg     This function does not abort if the file cannot be deleted, but does print
275*f0fbc68bSmrg     a message on `stderr` and returns `false` to the caller. The underlying
276*f0fbc68bSmrg     rationale is to give the caller the option to continue execution if
277*f0fbc68bSmrg     deleting the file is not important.
278*f0fbc68bSmrg 
279*f0fbc68bSmrg     Returns: `true` iff the file was successfully deleted. If the file was not
280*f0fbc68bSmrg     deleted, prints a message to `stderr` and returns `false`.
281*f0fbc68bSmrg     */
282*f0fbc68bSmrg     static if (!is(Datum == const))
283*f0fbc68bSmrg     bool discard()
284*f0fbc68bSmrg     {
285*f0fbc68bSmrg         // Truncate file to zero so unflushed buffers are not flushed unnecessarily.
286*f0fbc68bSmrg         resize(0);
287*f0fbc68bSmrg         auto deleteme = name;
288*f0fbc68bSmrg         close();
289*f0fbc68bSmrg         // In-memory resource freed, now get rid of the underlying temp file.
290*f0fbc68bSmrg         version(Posix)
291*f0fbc68bSmrg         {
292*f0fbc68bSmrg             import core.sys.posix.unistd : unlink;
293*f0fbc68bSmrg             if (unlink(deleteme) != 0)
294*f0fbc68bSmrg             {
295*f0fbc68bSmrg                 fprintf(stderr, "unlink(\"%s\") failed: %s\n", filename, strerror(errno));
296*f0fbc68bSmrg                 return false;
297*f0fbc68bSmrg             }
298*f0fbc68bSmrg         }
299*f0fbc68bSmrg         else version(Windows)
300*f0fbc68bSmrg         {
301*f0fbc68bSmrg             import core.sys.windows.winbase;
302*f0fbc68bSmrg             if (deleteme.asDString.extendedPathThen!(p => DeleteFileW(p.ptr)) == 0)
303*f0fbc68bSmrg             {
304*f0fbc68bSmrg                 fprintf(stderr, "DeleteFileW error %d\n", GetLastError());
305*f0fbc68bSmrg                 return false;
306*f0fbc68bSmrg             }
307*f0fbc68bSmrg         }
308*f0fbc68bSmrg         else static assert(0);
309*f0fbc68bSmrg         return true;
310*f0fbc68bSmrg     }
311*f0fbc68bSmrg 
312*f0fbc68bSmrg     /**
313*f0fbc68bSmrg     Queries whether `this` is currently associated with a file.
314*f0fbc68bSmrg 
315*f0fbc68bSmrg     Returns: `true` iff there is an active mapping.
316*f0fbc68bSmrg     */
317*f0fbc68bSmrg     bool active() const pure @nogc nothrow
318*f0fbc68bSmrg     {
319*f0fbc68bSmrg         return handle !is invalidHandle;
320*f0fbc68bSmrg     }
321*f0fbc68bSmrg 
322*f0fbc68bSmrg     /**
323*f0fbc68bSmrg     Queries the length of the file associated with this mapping.  If not
324*f0fbc68bSmrg     active, returns 0.
325*f0fbc68bSmrg 
326*f0fbc68bSmrg     Returns: the length of the file, or 0 if no file associated.
327*f0fbc68bSmrg     */
328*f0fbc68bSmrg     size_t length() const pure @nogc @safe nothrow { return data.length; }
329*f0fbc68bSmrg 
330*f0fbc68bSmrg     /**
331*f0fbc68bSmrg     Get a slice to the contents of the entire file.
332*f0fbc68bSmrg 
333*f0fbc68bSmrg     Returns: the contents of the file. If not active, returns the `null` slice.
334*f0fbc68bSmrg     */
335*f0fbc68bSmrg     auto opSlice() pure @nogc @safe nothrow { return data; }
336*f0fbc68bSmrg 
337*f0fbc68bSmrg     /**
338*f0fbc68bSmrg     Resizes the file and mapping to the specified `size`.
339*f0fbc68bSmrg 
340*f0fbc68bSmrg     Params:
341*f0fbc68bSmrg     size = new length requested
342*f0fbc68bSmrg     */
343*f0fbc68bSmrg     static if (!is(Datum == const))
344*f0fbc68bSmrg     void resize(size_t size) pure
345*f0fbc68bSmrg     {
346*f0fbc68bSmrg         assert(handle != invalidHandle);
347*f0fbc68bSmrg         fakePure({
348*f0fbc68bSmrg             version(Posix)
349*f0fbc68bSmrg             {
350*f0fbc68bSmrg                 import core.sys.posix.unistd : ftruncate;
351*f0fbc68bSmrg                 import core.sys.posix.sys.mman;
352*f0fbc68bSmrg 
353*f0fbc68bSmrg                 if (data.length)
354*f0fbc68bSmrg                 {
355*f0fbc68bSmrg                     assert(data.ptr, "Corrupt memory mapping");
356*f0fbc68bSmrg                     // assert(0) here because it would indicate an internal error
357*f0fbc68bSmrg                     munmap(cast(void*) data.ptr, data.length) == 0 || assert(0);
358*f0fbc68bSmrg                     data = null;
359*f0fbc68bSmrg                 }
360*f0fbc68bSmrg                 if (ftruncate(handle, size) != 0)
361*f0fbc68bSmrg                 {
362*f0fbc68bSmrg                     fprintf(stderr, "ftruncate() failed for \"%s\": %s\n", filename, strerror(errno));
363*f0fbc68bSmrg                     exit(1);
364*f0fbc68bSmrg                 }
365*f0fbc68bSmrg                 if (size > 0)
366*f0fbc68bSmrg                 {
367*f0fbc68bSmrg                     auto p = mmap(null, size, PROT_WRITE, MAP_SHARED, handle, 0);
368*f0fbc68bSmrg                     if (cast(ssize_t) p == -1)
369*f0fbc68bSmrg                     {
370*f0fbc68bSmrg                         fprintf(stderr, "mmap() failed for \"%s\": %s\n", filename, strerror(errno));
371*f0fbc68bSmrg                         exit(1);
372*f0fbc68bSmrg                     }
373*f0fbc68bSmrg                     data = cast(Datum[]) p[0 .. size];
374*f0fbc68bSmrg                 }
375*f0fbc68bSmrg             }
376*f0fbc68bSmrg             else version(Windows)
377*f0fbc68bSmrg             {
378*f0fbc68bSmrg                 // Per documentation, must unmap first.
379*f0fbc68bSmrg                 if (data.length > 0 && UnmapViewOfFile(cast(void*) data.ptr) == 0)
380*f0fbc68bSmrg                 {
381*f0fbc68bSmrg                     fprintf(stderr, "UnmapViewOfFile(%p) failed for memory mapping of \"%s\": %d\n",
382*f0fbc68bSmrg                         data.ptr, filename, GetLastError());
383*f0fbc68bSmrg                     exit(1);
384*f0fbc68bSmrg                 }
385*f0fbc68bSmrg                 data = null;
386*f0fbc68bSmrg                 if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
387*f0fbc68bSmrg                 {
388*f0fbc68bSmrg                     fprintf(stderr, "CloseHandle() failed for memory mapping of \"%s\": %d\n", filename, GetLastError());
389*f0fbc68bSmrg                     exit(1);
390*f0fbc68bSmrg                 }
391*f0fbc68bSmrg                 fileMappingObject = invalidHandle;
392*f0fbc68bSmrg                 LARGE_INTEGER biggie;
393*f0fbc68bSmrg                 biggie.QuadPart = size;
394*f0fbc68bSmrg                 if (SetFilePointerEx(handle, biggie, null, FILE_BEGIN) == 0 || SetEndOfFile(handle) == 0)
395*f0fbc68bSmrg                 {
396*f0fbc68bSmrg                     fprintf(stderr, "SetFilePointer() failed for \"%s\": %d\n", filename, GetLastError());
397*f0fbc68bSmrg                     exit(1);
398*f0fbc68bSmrg                 }
399*f0fbc68bSmrg                 createMapping(name, size);
400*f0fbc68bSmrg             }
401*f0fbc68bSmrg             else static assert(0);
402*f0fbc68bSmrg         });
403*f0fbc68bSmrg     }
404*f0fbc68bSmrg 
405*f0fbc68bSmrg     /**
406*f0fbc68bSmrg     Unconditionally and destructively moves the underlying file to `filename`.
407*f0fbc68bSmrg     If the operation succeeds, returns true. Upon failure, prints a message to
408*f0fbc68bSmrg     `stderr` and returns `false`. In all cases it closes the underlying file.
409*f0fbc68bSmrg 
410*f0fbc68bSmrg     Params: filename = zero-terminated name of the file to move to.
411*f0fbc68bSmrg 
412*f0fbc68bSmrg     Returns: `true` iff the operation was successful.
413*f0fbc68bSmrg     */
414*f0fbc68bSmrg     bool moveToFile(const char* filename)
415*f0fbc68bSmrg     {
416*f0fbc68bSmrg         assert(name !is null);
417*f0fbc68bSmrg 
418*f0fbc68bSmrg         // Fetch the name and then set it to `null` so it doesn't get deallocated
419*f0fbc68bSmrg         auto oldname = name;
420*f0fbc68bSmrg         import core.stdc.stdlib;
421*f0fbc68bSmrg         scope(exit) free(cast(void*) oldname);
422*f0fbc68bSmrg         name = null;
423*f0fbc68bSmrg         close();
424*f0fbc68bSmrg 
425*f0fbc68bSmrg         // Rename the underlying file to the target, no copy necessary.
426*f0fbc68bSmrg         version(Posix)
427*f0fbc68bSmrg         {
428*f0fbc68bSmrg             if (.rename(oldname, filename) != 0)
429*f0fbc68bSmrg             {
430*f0fbc68bSmrg                 fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", oldname, filename, strerror(errno));
431*f0fbc68bSmrg                 return false;
432*f0fbc68bSmrg             }
433*f0fbc68bSmrg         }
434*f0fbc68bSmrg         else version(Windows)
435*f0fbc68bSmrg         {
436*f0fbc68bSmrg             import core.sys.windows.winbase;
437*f0fbc68bSmrg             auto r = oldname.asDString.extendedPathThen!(
438*f0fbc68bSmrg                 p1 => filename.asDString.extendedPathThen!(p2 => MoveFileExW(p1.ptr, p2.ptr, MOVEFILE_REPLACE_EXISTING))
439*f0fbc68bSmrg             );
440*f0fbc68bSmrg             if (r == 0)
441*f0fbc68bSmrg             {
442*f0fbc68bSmrg                 fprintf(stderr, "MoveFileExW(\"%s\", \"%s\") failed: %d\n", oldname, filename, GetLastError());
443*f0fbc68bSmrg                 return false;
444*f0fbc68bSmrg             }
445*f0fbc68bSmrg         }
446*f0fbc68bSmrg         else static assert(0);
447*f0fbc68bSmrg         return true;
448*f0fbc68bSmrg     }
449*f0fbc68bSmrg }
450*f0fbc68bSmrg 
451*f0fbc68bSmrg /// Write a file, returning `true` on success.
writeFile(const (char)* name,const void[]data)452*f0fbc68bSmrg extern(D) static bool writeFile(const(char)* name, const void[] data) nothrow
453*f0fbc68bSmrg {
454*f0fbc68bSmrg     version (Posix)
455*f0fbc68bSmrg     {
456*f0fbc68bSmrg         int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
457*f0fbc68bSmrg         if (fd == -1)
458*f0fbc68bSmrg             goto err;
459*f0fbc68bSmrg         if (.write(fd, data.ptr, data.length) != data.length)
460*f0fbc68bSmrg             goto err2;
461*f0fbc68bSmrg         if (close(fd) == -1)
462*f0fbc68bSmrg             goto err;
463*f0fbc68bSmrg         return true;
464*f0fbc68bSmrg     err2:
465*f0fbc68bSmrg         close(fd);
466*f0fbc68bSmrg         .remove(name);
467*f0fbc68bSmrg     err:
468*f0fbc68bSmrg         return false;
469*f0fbc68bSmrg     }
470*f0fbc68bSmrg     else version (Windows)
471*f0fbc68bSmrg     {
472*f0fbc68bSmrg         DWORD numwritten; // here because of the gotos
473*f0fbc68bSmrg         const nameStr = name.asDString;
474*f0fbc68bSmrg         // work around Windows file path length limitation
475*f0fbc68bSmrg         // (see documentation for extendedPathThen).
476*f0fbc68bSmrg         HANDLE h = nameStr.extendedPathThen!
477*f0fbc68bSmrg             (p => CreateFileW(p.ptr,
478*f0fbc68bSmrg                                 GENERIC_WRITE,
479*f0fbc68bSmrg                                 0,
480*f0fbc68bSmrg                                 null,
481*f0fbc68bSmrg                                 CREATE_ALWAYS,
482*f0fbc68bSmrg                                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
483*f0fbc68bSmrg                                 null));
484*f0fbc68bSmrg         if (h == INVALID_HANDLE_VALUE)
485*f0fbc68bSmrg             goto err;
486*f0fbc68bSmrg 
487*f0fbc68bSmrg         if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE)
488*f0fbc68bSmrg             goto err2;
489*f0fbc68bSmrg         if (numwritten != data.length)
490*f0fbc68bSmrg             goto err2;
491*f0fbc68bSmrg         if (!CloseHandle(h))
492*f0fbc68bSmrg             goto err;
493*f0fbc68bSmrg         return true;
494*f0fbc68bSmrg     err2:
495*f0fbc68bSmrg         CloseHandle(h);
496*f0fbc68bSmrg         nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
497*f0fbc68bSmrg     err:
498*f0fbc68bSmrg         return false;
499*f0fbc68bSmrg     }
500*f0fbc68bSmrg     else
501*f0fbc68bSmrg     {
502*f0fbc68bSmrg         static assert(0);
503*f0fbc68bSmrg     }
504*f0fbc68bSmrg }
505*f0fbc68bSmrg 
506*f0fbc68bSmrg /// Touch a file to current date
touchFile(const char * namez)507*f0fbc68bSmrg bool touchFile(const char* namez)
508*f0fbc68bSmrg {
509*f0fbc68bSmrg     version (Windows)
510*f0fbc68bSmrg     {
511*f0fbc68bSmrg         FILETIME ft = void;
512*f0fbc68bSmrg         SYSTEMTIME st = void;
513*f0fbc68bSmrg         GetSystemTime(&st);
514*f0fbc68bSmrg         SystemTimeToFileTime(&st, &ft);
515*f0fbc68bSmrg 
516*f0fbc68bSmrg         import core.stdc.string : strlen;
517*f0fbc68bSmrg 
518*f0fbc68bSmrg         // get handle to file
519*f0fbc68bSmrg         HANDLE h = namez[0 .. namez.strlen()].extendedPathThen!(p => CreateFile(p.ptr,
520*f0fbc68bSmrg             FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
521*f0fbc68bSmrg             null, OPEN_EXISTING,
522*f0fbc68bSmrg             FILE_ATTRIBUTE_NORMAL, null));
523*f0fbc68bSmrg         if (h == INVALID_HANDLE_VALUE)
524*f0fbc68bSmrg             return false;
525*f0fbc68bSmrg 
526*f0fbc68bSmrg         const f = SetFileTime(h, null, null, &ft); // set last write time
527*f0fbc68bSmrg 
528*f0fbc68bSmrg         if (!CloseHandle(h))
529*f0fbc68bSmrg             return false;
530*f0fbc68bSmrg 
531*f0fbc68bSmrg         return f != 0;
532*f0fbc68bSmrg     }
533*f0fbc68bSmrg     else version (Posix)
534*f0fbc68bSmrg     {
535*f0fbc68bSmrg         import core.sys.posix.utime;
536*f0fbc68bSmrg         return utime(namez, null) == 0;
537*f0fbc68bSmrg     }
538*f0fbc68bSmrg     else
539*f0fbc68bSmrg         static assert(0);
540*f0fbc68bSmrg }
541*f0fbc68bSmrg 
542*f0fbc68bSmrg // Feel free to make these public if used elsewhere.
543*f0fbc68bSmrg /**
544*f0fbc68bSmrg Size of a file in bytes.
545*f0fbc68bSmrg Params: fd = file handle
546*f0fbc68bSmrg Returns: file size in bytes, or `ulong.max` on any error.
547*f0fbc68bSmrg */
version(Posix)548*f0fbc68bSmrg version (Posix)
549*f0fbc68bSmrg private ulong fileSize(int fd)
550*f0fbc68bSmrg {
551*f0fbc68bSmrg     import core.sys.posix.sys.stat;
552*f0fbc68bSmrg     stat_t buf;
553*f0fbc68bSmrg     if (fstat(fd, &buf) == 0)
554*f0fbc68bSmrg         return buf.st_size;
555*f0fbc68bSmrg     return ulong.max;
556*f0fbc68bSmrg }
557*f0fbc68bSmrg 
558*f0fbc68bSmrg /// Ditto
version(Windows)559*f0fbc68bSmrg version (Windows)
560*f0fbc68bSmrg private ulong fileSize(HANDLE fd)
561*f0fbc68bSmrg {
562*f0fbc68bSmrg     ulong result;
563*f0fbc68bSmrg     if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0)
564*f0fbc68bSmrg         return result;
565*f0fbc68bSmrg     return ulong.max;
566*f0fbc68bSmrg }
567*f0fbc68bSmrg 
568*f0fbc68bSmrg /**
569*f0fbc68bSmrg Runs a non-pure function or delegate as pure code. Use with caution.
570*f0fbc68bSmrg 
571*f0fbc68bSmrg Params:
572*f0fbc68bSmrg fun = the delegate to run, usually inlined: `fakePure({ ... });`
573*f0fbc68bSmrg 
574*f0fbc68bSmrg Returns: whatever `fun` returns.
575*f0fbc68bSmrg */
fakePure(F)576*f0fbc68bSmrg private auto ref fakePure(F)(scope F fun) pure
577*f0fbc68bSmrg {
578*f0fbc68bSmrg     mixin("alias PureFun = " ~ F.stringof ~ " pure;");
579*f0fbc68bSmrg     return (cast(PureFun) fun)();
580*f0fbc68bSmrg }
581