d: Merge upstream dmd 47871363d, druntime, c52e28b7, phobos 99e9c1b77.
[official-gcc.git] / gcc / d / dmd / file_manager.d
blobb86c7995562670a2e9ff67c5031149150f263b7a
1 /**
2 * Read a file from disk and store it in memory.
4 * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
5 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.d, _file_manager.d)
7 * Documentation: https://dlang.org/phobos/dmd_file_manager.html
8 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/file_manager.d
9 */
11 module dmd.file_manager;
13 import dmd.root.stringtable : StringTable;
14 import dmd.root.file : File, FileBuffer;
15 import dmd.root.filename : FileName;
16 import dmd.root.string : toDString;
17 import dmd.globals;
18 import dmd.identifier;
20 enum package_d = "package." ~ mars_ext;
21 enum package_di = "package." ~ hdr_ext;
23 final class FileManager
25 private StringTable!(FileBuffer*) files;
27 ///
28 public this () nothrow
30 this.files._init();
33 nothrow:
34 /********************************************
35 * Look for the source file if it's different from filename.
36 * Look for .di, .d, directory, and along global.path.
37 * Does not open the file.
38 * Params:
39 * filename = as supplied by the user
40 * path = path to look for filename
41 * Returns:
42 * the found file name or
43 * `null` if it is not different from filename.
45 static const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
47 //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr);
48 /* Search along path[] for .di file, then .d file, then .i file, then .c file.
50 const sdi = FileName.forceExt(filename, hdr_ext);
51 if (FileName.exists(sdi) == 1)
52 return sdi;
53 scope(exit) FileName.free(sdi.ptr);
55 const sd = FileName.forceExt(filename, mars_ext);
56 // Special file name representing `stdin`, always assume its presence
57 if (sd == "__stdin.d")
58 return sd;
59 if (FileName.exists(sd) == 1)
60 return sd;
61 scope(exit) FileName.free(sd.ptr);
63 const si = FileName.forceExt(filename, i_ext);
64 if (FileName.exists(si) == 1)
65 return si;
66 scope(exit) FileName.free(si.ptr);
68 const sc = FileName.forceExt(filename, c_ext);
69 if (FileName.exists(sc) == 1)
70 return sc;
71 scope(exit) FileName.free(sc.ptr);
73 if (FileName.exists(filename) == 2)
75 /* The filename exists and it's a directory.
76 * Therefore, the result should be: filename/package.d
77 * iff filename/package.d is a file
79 const ni = FileName.combine(filename, package_di);
80 if (FileName.exists(ni) == 1)
81 return ni;
82 FileName.free(ni.ptr);
84 const n = FileName.combine(filename, package_d);
85 if (FileName.exists(n) == 1)
86 return n;
87 FileName.free(n.ptr);
89 if (FileName.absolute(filename))
90 return null;
91 if (!path.length)
92 return null;
93 foreach (entry; path)
95 const p = entry.toDString();
97 const(char)[] n = FileName.combine(p, sdi);
98 if (FileName.exists(n) == 1) {
99 return n;
101 FileName.free(n.ptr);
103 n = FileName.combine(p, sd);
104 if (FileName.exists(n) == 1) {
105 return n;
107 FileName.free(n.ptr);
109 n = FileName.combine(p, si);
110 if (FileName.exists(n) == 1) {
111 return n;
113 FileName.free(n.ptr);
115 n = FileName.combine(p, sc);
116 if (FileName.exists(n) == 1) {
117 return n;
119 FileName.free(n.ptr);
121 const b = FileName.removeExt(filename);
122 n = FileName.combine(p, b);
123 FileName.free(b.ptr);
124 if (FileName.exists(n) == 2)
126 const n2i = FileName.combine(n, package_di);
127 if (FileName.exists(n2i) == 1)
128 return n2i;
129 FileName.free(n2i.ptr);
130 const n2 = FileName.combine(n, package_d);
131 if (FileName.exists(n2) == 1) {
132 return n2;
134 FileName.free(n2.ptr);
136 FileName.free(n.ptr);
138 return null;
142 * Looks up the given filename from the internal file buffer table.
143 * If the file does not already exist within the table, it will be read from the filesystem.
144 * If it has been read before,
146 * Returns: the loaded source file if it was found in memory,
147 * otherwise `null`
149 const(FileBuffer)* lookup(FileName filename)
151 const name = filename.toString;
152 if (auto val = files.lookup(name))
153 return val.value;
155 if (name == "__stdin.d")
157 auto buffer = new FileBuffer(readFromStdin().extractSlice());
158 if (this.files.insert(name, buffer) is null)
159 assert(0, "stdin: Insert after lookup failure should never return `null`");
160 return buffer;
163 if (FileName.exists(name) != 1)
164 return null;
166 auto readResult = File.read(name);
167 if (!readResult.success)
168 return null;
170 FileBuffer* fb = new FileBuffer(readResult.extractSlice());
171 if (files.insert(name, fb) is null)
172 assert(0, "Insert after lookup failure should never return `null`");
174 return fb;
178 * Looks up the given filename from the internal file buffer table, and returns the lines within the file.
179 * If the file does not already exist within the table, it will be read from the filesystem.
180 * If it has been read before,
182 * Returns: the loaded source file if it was found in memory,
183 * otherwise `null`
185 const(char)[][] getLines(FileName file)
187 const(char)[][] lines;
188 if (const buffer = lookup(file))
190 const slice = buffer.data[0 .. buffer.data.length];
191 size_t start, end;
192 ubyte c;
193 for (auto i = 0; i < slice.length; i++)
195 c = slice[i];
196 if (c == '\n' || c == '\r')
198 if (i != 0)
200 end = i;
201 lines ~= cast(const(char)[])slice[start .. end];
203 // Check for Windows-style CRLF newlines
204 if (c == '\r')
206 if (slice.length > i + 1 && slice[i + 1] == '\n')
208 // This is a CRLF sequence, skip over two characters
209 start = i + 2;
210 i++;
212 else
214 // Just a CR sequence
215 start = i + 1;
218 else
220 // The next line should start after the LF sequence
221 start = i + 1;
226 if (slice[$ - 1] != '\r' && slice[$ - 1] != '\n')
228 end = slice.length;
229 lines ~= cast(const(char)[])slice[start .. end];
233 return lines;
237 * Adds a FileBuffer to the table.
239 * Returns: The FileBuffer added, or null
241 FileBuffer* add(FileName filename, FileBuffer* filebuffer)
243 auto val = files.insert(filename.toString, filebuffer);
244 return val == null ? null : val.value;
248 private FileBuffer readFromStdin() nothrow
250 import core.stdc.stdio;
251 import dmd.errors;
252 import dmd.root.rmem;
254 enum bufIncrement = 128 * 1024;
255 size_t pos = 0;
256 size_t sz = bufIncrement;
258 ubyte* buffer = null;
259 for (;;)
261 buffer = cast(ubyte*)mem.xrealloc(buffer, sz + 4); // +2 for sentinel and +2 for lexer
263 // Fill up buffer
266 assert(sz > pos);
267 size_t rlen = fread(buffer + pos, 1, sz - pos, stdin);
268 pos += rlen;
269 if (ferror(stdin))
271 import core.stdc.errno;
272 error(Loc.initial, "cannot read from stdin, errno = %d", errno);
273 fatal();
275 if (feof(stdin))
277 // We're done
278 assert(pos < sz + 2);
279 buffer[pos .. pos + 4] = '\0';
280 return FileBuffer(buffer[0 .. pos]);
282 } while (pos < sz);
284 // Buffer full, expand
285 sz += bufIncrement;
288 assert(0);