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
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
;
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
;
28 public this () 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.
39 * filename = as supplied by the user
40 * path = path to look for filename
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)
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")
59 if (FileName
.exists(sd
) == 1)
61 scope(exit
) FileName
.free(sd
.ptr
);
63 const si
= FileName
.forceExt(filename
, i_ext
);
64 if (FileName
.exists(si
) == 1)
66 scope(exit
) FileName
.free(si
.ptr
);
68 const sc
= FileName
.forceExt(filename
, c_ext
);
69 if (FileName
.exists(sc
) == 1)
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)
82 FileName
.free(ni
.ptr
);
84 const n
= FileName
.combine(filename
, package_d
);
85 if (FileName
.exists(n
) == 1)
89 if (FileName
.absolute(filename
))
95 const p
= entry
.toDString();
97 const(char)[] n
= FileName
.combine(p
, sdi
);
98 if (FileName
.exists(n
) == 1) {
101 FileName
.free(n
.ptr
);
103 n
= FileName
.combine(p
, sd
);
104 if (FileName
.exists(n
) == 1) {
107 FileName
.free(n
.ptr
);
109 n
= FileName
.combine(p
, si
);
110 if (FileName
.exists(n
) == 1) {
113 FileName
.free(n
.ptr
);
115 n
= FileName
.combine(p
, sc
);
116 if (FileName
.exists(n
) == 1) {
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)
129 FileName
.free(n2i
.ptr
);
130 const n2
= FileName
.combine(n
, package_d
);
131 if (FileName
.exists(n2
) == 1) {
134 FileName
.free(n2
.ptr
);
136 FileName
.free(n
.ptr
);
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,
149 const(FileBuffer
)* lookup(FileName filename
)
151 const name
= filename
.toString
;
152 if (auto val
= files
.lookup(name
))
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`");
163 if (FileName
.exists(name
) != 1)
166 auto readResult
= File
.read(name
);
167 if (!readResult
.success
)
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`");
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,
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
];
193 for (auto i
= 0; i
< slice
.length
; i
++)
196 if (c
== '\n' || c
== '\r')
201 lines
~= cast(const(char)[])slice
[start
.. end
];
203 // Check for Windows-style CRLF newlines
206 if (slice
.length
> i
+ 1 && slice
[i
+ 1] == '\n')
208 // This is a CRLF sequence, skip over two characters
214 // Just a CR sequence
220 // The next line should start after the LF sequence
226 if (slice
[$ - 1] != '\r' && slice
[$ - 1] != '\n')
229 lines
~= cast(const(char)[])slice
[start
.. end
];
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
;
252 import dmd
.root
.rmem
;
254 enum bufIncrement
= 128 * 1024;
256 size_t sz
= bufIncrement
;
258 ubyte* buffer
= null;
261 buffer
= cast(ubyte*)mem
.xrealloc(buffer
, sz
+ 4); // +2 for sentinel and +2 for lexer
267 size_t rlen
= fread(buffer
+ pos
, 1, sz
- pos
, stdin
);
271 import core
.stdc
.errno
;
272 error(Loc
.initial
, "cannot read from stdin, errno = %d", errno
);
278 assert(pos
< sz
+ 2);
279 buffer
[pos
.. pos
+ 4] = '\0';
280 return FileBuffer(buffer
[0 .. pos
]);
284 // Buffer full, expand