xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/dmd/file_manager.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /**
2  * Read a file from disk and store it in memory.
3  *
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  */
10 
11 module dmd.file_manager;
12 
13 import dmd.root.stringtable : StringTable;
14 import dmd.root.file : File, Buffer;
15 import dmd.root.filename : FileName;
16 import dmd.root.string : toDString;
17 import dmd.globals;
18 import dmd.identifier;
19 
20 enum package_d  = "package." ~ mars_ext;
21 enum package_di = "package." ~ hdr_ext;
22 
23 final class FileManager
24 {
25     private StringTable!(const(ubyte)[]) files;
26 
27     ///
this()28     public this () nothrow
29     {
30         this.files._init();
31     }
32 
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.
44     */
lookForSourceFile(const char[]filename,const char * []path)45     static const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
46     {
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.
49         */
50         const sdi = FileName.forceExt(filename, hdr_ext);
51         if (FileName.exists(sdi) == 1)
52             return sdi;
53         scope(exit) FileName.free(sdi.ptr);
54 
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);
62 
63         const si = FileName.forceExt(filename, i_ext);
64         if (FileName.exists(si) == 1)
65             return si;
66         scope(exit) FileName.free(si.ptr);
67 
68         const sc = FileName.forceExt(filename, c_ext);
69         if (FileName.exists(sc) == 1)
70             return sc;
71         scope(exit) FileName.free(sc.ptr);
72 
73         if (FileName.exists(filename) == 2)
74         {
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
78             */
79             const ni = FileName.combine(filename, package_di);
80             if (FileName.exists(ni) == 1)
81                 return ni;
82             FileName.free(ni.ptr);
83 
84             const n = FileName.combine(filename, package_d);
85             if (FileName.exists(n) == 1)
86                 return n;
87             FileName.free(n.ptr);
88         }
89         if (FileName.absolute(filename))
90             return null;
91         if (!path.length)
92             return null;
93         foreach (entry; path)
94         {
95             const p = entry.toDString();
96 
97             const(char)[] n = FileName.combine(p, sdi);
98             if (FileName.exists(n) == 1) {
99                 return n;
100             }
101             FileName.free(n.ptr);
102 
103             n = FileName.combine(p, sd);
104             if (FileName.exists(n) == 1) {
105                 return n;
106             }
107             FileName.free(n.ptr);
108 
109             n = FileName.combine(p, si);
110             if (FileName.exists(n) == 1) {
111                 return n;
112             }
113             FileName.free(n.ptr);
114 
115             n = FileName.combine(p, sc);
116             if (FileName.exists(n) == 1) {
117                 return n;
118             }
119             FileName.free(n.ptr);
120 
121             const b = FileName.removeExt(filename);
122             n = FileName.combine(p, b);
123             FileName.free(b.ptr);
124             if (FileName.exists(n) == 2)
125             {
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;
133                 }
134                 FileName.free(n2.ptr);
135             }
136             FileName.free(n.ptr);
137         }
138         return null;
139     }
140 
141     /**
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,
145      *
146      * Returns: the loaded source file if it was found in memory,
147      *      otherwise `null`
148      */
lookup(FileName filename)149     const(ubyte)[] lookup(FileName filename)
150     {
151         const name = filename.toString;
152         if (auto val = files.lookup(name))
153             return val.value;
154 
155         if (name == "__stdin.d")
156         {
157             auto buffer = 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;
161         }
162 
163         if (FileName.exists(name) != 1)
164             return null;
165 
166         auto readResult = File.read(name);
167         if (!readResult.success)
168             return null;
169 
170         auto fb = readResult.extractSlice();
171         if (files.insert(name, fb) is null)
172             assert(0, "Insert after lookup failure should never return `null`");
173 
174         return fb;
175     }
176 
177     /**
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,
181      *
182      * Returns: the loaded source file if it was found in memory,
183      *      otherwise `null`
184      */
getLines(FileName file)185     const(char)[][] getLines(FileName file)
186     {
187         const(char)[][] lines;
188         if (const buffer = lookup(file))
189         {
190             const slice = buffer;
191             size_t start, end;
192             for (auto i = 0; i < slice.length; i++)
193             {
194                 const c = slice[i];
195                 if (c == '\n' || c == '\r')
196                 {
197                     if (i != 0)
198                     {
199                         end = i;
200                         // Appending lines one at a time will certainly be slow
201                         lines ~= cast(const(char)[])slice[start .. end];
202                     }
203                     // Check for Windows-style CRLF newlines
204                     if (c == '\r')
205                     {
206                         if (slice.length > i + 1 && slice[i + 1] == '\n')
207                         {
208                             // This is a CRLF sequence, skip over two characters
209                             start = i + 2;
210                             i++;
211                         }
212                         else
213                         {
214                             // Just a CR sequence
215                             start = i + 1;
216                         }
217                     }
218                     else
219                     {
220                         // The next line should start after the LF sequence
221                         start = i + 1;
222                     }
223                 }
224             }
225 
226             if (slice[$ - 1] != '\r' && slice[$ - 1] != '\n')
227             {
228                 end = slice.length;
229                 lines ~= cast(const(char)[])slice[start .. end];
230             }
231         }
232 
233         return lines;
234     }
235 
236     /**
237      * Adds the contents of a file to the table.
238      * Params:
239      *  filename = name of the file
240      *  buffer = contents of the file
241      * Returns:
242      *  the buffer added, or null
243      */
add(FileName filename,const (ubyte)[]buffer)244     const(ubyte)[] add(FileName filename, const(ubyte)[] buffer)
245     {
246         auto val = files.insert(filename.toString, buffer);
247         return val == null ? null : val.value;
248     }
249 }
250 
readFromStdin()251 private Buffer readFromStdin() nothrow
252 {
253     import core.stdc.stdio;
254     import dmd.errors;
255     import dmd.root.rmem;
256 
257     enum bufIncrement = 128 * 1024;
258     size_t pos = 0;
259     size_t sz = bufIncrement;
260 
261     ubyte* buffer = null;
262     for (;;)
263     {
264         buffer = cast(ubyte*)mem.xrealloc(buffer, sz + 4); // +2 for sentinel and +2 for lexer
265 
266         // Fill up buffer
267         do
268         {
269             assert(sz > pos);
270             size_t rlen = fread(buffer + pos, 1, sz - pos, stdin);
271             pos += rlen;
272             if (ferror(stdin))
273             {
274                 import core.stdc.errno;
275                 error(Loc.initial, "cannot read from stdin, errno = %d", errno);
276                 fatal();
277             }
278             if (feof(stdin))
279             {
280                 // We're done
281                 assert(pos < sz + 2);
282                 buffer[pos .. pos + 4] = '\0';
283                 return Buffer(buffer[0 .. pos]);
284             }
285         } while (pos < sz);
286 
287         // Buffer full, expand
288         sz += bufIncrement;
289     }
290 
291     assert(0);
292 }
293