2 * This module defines some utility functions for DMD.
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
14 import core.stdc.string;
18 import dmd.root.filename;
19 import dmd.root.outbuffer;
20 import dmd.root.string;
24 * Normalize path by turning forward slashes into backslashes
27 * src = Source path, using unix-style ('/') path separators
30 * A newly-allocated string with '/' turned into backslashes
32 const(char)* toWinPath(const(char)* src)
36 char* result = strdup(src);
49 * Reads a file, terminate the program on error
52 * loc = The line number information from where the call originates
53 * filename = Path to file
55 FileBuffer readFile(Loc loc, const(char)* filename)
57 return readFile(loc, filename.toDString());
61 FileBuffer readFile(Loc loc, const(char)[] filename)
63 auto result = File.read(filename);
66 error(loc, "Error reading file `%.*s`", cast(int)filename.length, filename.ptr);
69 return FileBuffer(result.extractSlice());
74 * Writes a file, terminate the program on error
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
81 extern (D) void writeFile(Loc loc, const(char)[] filename, const void[] data)
83 ensurePathToNameExists(Loc.initial, filename);
84 if (!File.update(filename, data))
86 error(loc, "Error writing file '%*.s'", cast(int) filename.length, filename.ptr);
93 * Ensure the root path (the path minus the name) of the provided path
94 * exists, and terminate the process if it doesn't.
97 * loc = The line number information from where the call originates
98 * name = a path to check (the name is stripped)
100 void ensurePathToNameExists(Loc loc, const(char)[] name)
102 const char[] pt = FileName.path(name);
105 if (!FileName.ensurePathExists(pt))
107 error(loc, "cannot create directory %*.s", cast(int) pt.length, pt.ptr);
111 FileName.free(pt.ptr);
116 * Takes a path, and escapes '(', ')' and backslashes
119 * buf = Buffer to write the escaped path to
120 * fname = Path to escape
122 void escapePath(OutBuffer* buf, const(char)* fname)
136 buf.writeByte(*fname);
144 * Takes a path, and make it compatible with GNU Makefile format.
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.
152 * buf = Buffer to write the escaped path to
153 * fname = Path to escape
155 void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname)
178 // ':' not escaped on Windows because it can
179 // create problems with absolute paths (e.g. C:\Project)
191 buf.writeByte(*fname);
201 enum input = `C:\My Project\file#4$.ext`;
202 enum expected = `C:\My\ Project\file\#4$$.ext`;
206 enum input = `/foo\bar/weird$.:name#\ with spaces.ext`;
207 enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`;
211 buf.writeEscapedMakePath(input);
212 assert(buf[] == expected);
216 * Convert string to integer.
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`
225 * `false` on error, `true` on success
227 bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max)
228 @safe pure @nogc nothrow
230 import core.checkedint : mulu, addu, muls, adds;
232 // mul* / add* doesn't support types < int
233 static if (T.sizeof < int.sizeof)
240 else static if (T.min == 0)
256 if (c > '9' || c < '0')
258 value = mul(value, 10, overflow);
259 value = add(value, uint(c - '0'), overflow);
261 // If it overflows, value must be > to `max` (since `max` is `T`)
263 return !overflow && value <= max;
267 @safe pure nothrow @nogc unittest
278 assert(b.parseDigits("42") && b == 42);
279 assert(ub.parseDigits("42") && ub == 42);
281 assert(s.parseDigits("420") && s == 420);
282 assert(us.parseDigits("42000") && us == 42_000);
284 assert(i.parseDigits("420000") && i == 420_000);
285 assert(ui.parseDigits("420000") && ui == 420_000);
287 assert(l.parseDigits("42000000000") && l == 42_000_000_000);
288 assert(ul.parseDigits("82000000000") && ul == 82_000_000_000);
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));