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
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
;
24 import dmd
.root
.string
;
26 import dmd
.common
.file
;
27 import dmd
.common
.string
;
31 /// Owns a (rmem-managed) buffer.
45 /// Transfers ownership of the buffer to the caller.
46 ubyte[] extractSlice() pure nothrow @nogc @safe
58 static struct ReadResult
63 /// Transfers ownership of the buffer to the caller.
64 ubyte[] extractSlice() pure nothrow @nogc @safe
66 return buffer
.extractSlice();
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];
79 /// Read the full content of a file.
80 static ReadResult
read(const(char)[] name
)
89 //printf("File::read('%s')\n",name);
90 int fd
= name
.toCStringThen
!(slice
=> open(slice
.ptr
, O_RDONLY
));
93 //perror("\topen error");
96 //printf("\tfile opened\n");
99 //perror("\tfstat error");
103 size
= cast(size_t
)buf
.st_size
;
104 ubyte* buffer
= cast(ubyte*)mem
.xmalloc_noscan(size
+ 4);
105 numread
= .read(fd
, buffer
, size
);
108 //perror("\tread error");
113 //perror("\tclose error");
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
];
130 else version (Windows
)
135 // work around Windows file path length limitation
136 // (see documentation for extendedPathThen).
137 HANDLE h
= name
.extendedPathThen
!
138 (p
=> CreateFileW(p
.ptr
,
143 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN
,
145 if (h
== INVALID_HANDLE_VALUE
)
147 size
= GetFileSize(h
, null);
148 ubyte* buffer
= cast(ubyte*)mem
.xmalloc_noscan(size
+ 4);
149 if (ReadFile(h
, buffer
, size
, &numread
, null) != TRUE
)
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
];
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
);
182 static bool write(const(char)[] name
, const void[] data
)
184 return name
.toCStringThen
!((fname
) => write(fname
.ptr
, data
));
188 extern (C
++) static void remove(const(char)* name
)
194 else version (Windows
)
196 name
.toDString
.extendedPathThen
!(p
=> DeleteFileW(p
.ptr
));
204 /***************************************************
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.
214 * name = name of file to update
215 * data = updated contents of file
219 static bool update(const(char)* namez
, const void[] data
)
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);
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
)
255 if (stat(namez
, &buf
) == 0)
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.
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:
290 version (CRuntime_Glibc
)
292 int fstat64(int, stat_t
*) @trusted;
293 alias fstat
= fstat64
;
294 int stat64(const scope char*, stat_t
*) @system;
299 int fstat(int, stat_t
*) @trusted;
300 int stat(const scope char*, stat_t
*) @system;