]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/d/dmd/utils.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / gcc / d / dmd / utils.d
1 /**
2 * This module defines some utility functions for DMD.
3 *
4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/utils.d, _utils.d)
8 * Documentation: https://dlang.org/phobos/dmd_utils.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/utils.d
10 */
11
12 module dmd.utils;
13
14 import core.stdc.string;
15 import dmd.errors;
16 import dmd.globals;
17 import dmd.root.file;
18 import dmd.root.filename;
19 import dmd.root.outbuffer;
20 import dmd.root.string;
21
22
23 /**
24 * Normalize path by turning forward slashes into backslashes
25 *
26 * Params:
27 * src = Source path, using unix-style ('/') path separators
28 *
29 * Returns:
30 * A newly-allocated string with '/' turned into backslashes
31 */
32 const(char)* toWinPath(const(char)* src)
33 {
34 if (src is null)
35 return null;
36 char* result = strdup(src);
37 char* p = result;
38 while (*p != '\0')
39 {
40 if (*p == '/')
41 *p = '\\';
42 p++;
43 }
44 return result;
45 }
46
47
48 /**
49 * Reads a file, terminate the program on error
50 *
51 * Params:
52 * loc = The line number information from where the call originates
53 * filename = Path to file
54 */
55 FileBuffer readFile(Loc loc, const(char)* filename)
56 {
57 return readFile(loc, filename.toDString());
58 }
59
60 /// Ditto
61 FileBuffer readFile(Loc loc, const(char)[] filename)
62 {
63 auto result = File.read(filename);
64 if (!result.success)
65 {
66 error(loc, "Error reading file `%.*s`", cast(int)filename.length, filename.ptr);
67 fatal();
68 }
69 return FileBuffer(result.extractSlice());
70 }
71
72
73 /**
74 * Writes a file, terminate the program on error
75 *
76 * Params:
77 * loc = The line number information from where the call originates
78 * filename = Path to file
79 * data = Full content of the file to be written
80 */
81 extern (D) void writeFile(Loc loc, const(char)[] filename, const void[] data)
82 {
83 ensurePathToNameExists(Loc.initial, filename);
84 if (!File.update(filename, data))
85 {
86 error(loc, "Error writing file '%*.s'", cast(int) filename.length, filename.ptr);
87 fatal();
88 }
89 }
90
91
92 /**
93 * Ensure the root path (the path minus the name) of the provided path
94 * exists, and terminate the process if it doesn't.
95 *
96 * Params:
97 * loc = The line number information from where the call originates
98 * name = a path to check (the name is stripped)
99 */
100 void ensurePathToNameExists(Loc loc, const(char)[] name)
101 {
102 const char[] pt = FileName.path(name);
103 if (pt.length)
104 {
105 if (!FileName.ensurePathExists(pt))
106 {
107 error(loc, "cannot create directory %*.s", cast(int) pt.length, pt.ptr);
108 fatal();
109 }
110 }
111 FileName.free(pt.ptr);
112 }
113
114
115 /**
116 * Takes a path, and escapes '(', ')' and backslashes
117 *
118 * Params:
119 * buf = Buffer to write the escaped path to
120 * fname = Path to escape
121 */
122 void escapePath(OutBuffer* buf, const(char)* fname)
123 {
124 while (1)
125 {
126 switch (*fname)
127 {
128 case 0:
129 return;
130 case '(':
131 case ')':
132 case '\\':
133 buf.writeByte('\\');
134 goto default;
135 default:
136 buf.writeByte(*fname);
137 break;
138 }
139 fname++;
140 }
141 }
142
143 /**
144 * Takes a path, and make it compatible with GNU Makefile format.
145 *
146 * GNU make uses a weird quoting scheme for white space.
147 * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space;
148 * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name;
149 * and backslashes in other contexts should not be doubled.
150 *
151 * Params:
152 * buf = Buffer to write the escaped path to
153 * fname = Path to escape
154 */
155 void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname)
156 {
157 uint slashes;
158
159 while (*fname)
160 {
161 switch (*fname)
162 {
163 case '\\':
164 slashes++;
165 break;
166 case '$':
167 buf.writeByte('$');
168 goto default;
169 case ' ':
170 case '\t':
171 while (slashes--)
172 buf.writeByte('\\');
173 goto case;
174 case '#':
175 buf.writeByte('\\');
176 goto default;
177 case ':':
178 // ':' not escaped on Windows because it can
179 // create problems with absolute paths (e.g. C:\Project)
180 version (Windows) {}
181 else
182 {
183 buf.writeByte('\\');
184 }
185 goto default;
186 default:
187 slashes = 0;
188 break;
189 }
190
191 buf.writeByte(*fname);
192 fname++;
193 }
194 }
195
196 ///
197 unittest
198 {
199 version (Windows)
200 {
201 enum input = `C:\My Project\file#4$.ext`;
202 enum expected = `C:\My\ Project\file\#4$$.ext`;
203 }
204 else
205 {
206 enum input = `/foo\bar/weird$.:name#\ with spaces.ext`;
207 enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`;
208 }
209
210 OutBuffer buf;
211 buf.writeEscapedMakePath(input);
212 assert(buf[] == expected);
213 }
214
215 /**
216 * Convert string to integer.
217 *
218 * Params:
219 * T = Type of integer to parse
220 * val = Variable to store the result in
221 * p = slice to start of string digits
222 * max = max allowable value (inclusive), defaults to `T.max`
223 *
224 * Returns:
225 * `false` on error, `true` on success
226 */
227 bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max)
228 @safe pure @nogc nothrow
229 {
230 import core.checkedint : mulu, addu, muls, adds;
231
232 // mul* / add* doesn't support types < int
233 static if (T.sizeof < int.sizeof)
234 {
235 int value;
236 alias add = adds;
237 alias mul = muls;
238 }
239 // unsigned
240 else static if (T.min == 0)
241 {
242 T value;
243 alias add = addu;
244 alias mul = mulu;
245 }
246 else
247 {
248 T value;
249 alias add = adds;
250 alias mul = muls;
251 }
252
253 bool overflow;
254 foreach (char c; p)
255 {
256 if (c > '9' || c < '0')
257 return false;
258 value = mul(value, 10, overflow);
259 value = add(value, uint(c - '0'), overflow);
260 }
261 // If it overflows, value must be > to `max` (since `max` is `T`)
262 val = cast(T) value;
263 return !overflow && value <= max;
264 }
265
266 ///
267 @safe pure nothrow @nogc unittest
268 {
269 byte b;
270 ubyte ub;
271 short s;
272 ushort us;
273 int i;
274 uint ui;
275 long l;
276 ulong ul;
277
278 assert(b.parseDigits("42") && b == 42);
279 assert(ub.parseDigits("42") && ub == 42);
280
281 assert(s.parseDigits("420") && s == 420);
282 assert(us.parseDigits("42000") && us == 42_000);
283
284 assert(i.parseDigits("420000") && i == 420_000);
285 assert(ui.parseDigits("420000") && ui == 420_000);
286
287 assert(l.parseDigits("42000000000") && l == 42_000_000_000);
288 assert(ul.parseDigits("82000000000") && ul == 82_000_000_000);
289
290 assert(!b.parseDigits(ubyte.max.stringof));
291 assert(!b.parseDigits("WYSIWYG"));
292 assert(!b.parseDigits("-42"));
293 assert(!b.parseDigits("200"));
294 assert(ub.parseDigits("200") && ub == 200);
295 assert(i.parseDigits(int.max.stringof) && i == int.max);
296 assert(i.parseDigits("420", 500) && i == 420);
297 assert(!i.parseDigits("420", 400));
298 }