diff options
Diffstat (limited to 'gcc/d/dmd/utils.d')
-rw-r--r-- | gcc/d/dmd/utils.d | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d new file mode 100644 index 00000000000..600521fbd49 --- /dev/null +++ b/gcc/d/dmd/utils.d @@ -0,0 +1,298 @@ +/** + * This module defines some utility functions for DMD. + * + * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/utils.d, _utils.d) + * Documentation: https://dlang.org/phobos/dmd_utils.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/utils.d + */ + +module dmd.utils; + +import core.stdc.string; +import dmd.errors; +import dmd.globals; +import dmd.root.file; +import dmd.root.filename; +import dmd.root.outbuffer; +import dmd.root.string; + + +/** + * Normalize path by turning forward slashes into backslashes + * + * Params: + * src = Source path, using unix-style ('/') path separators + * + * Returns: + * A newly-allocated string with '/' turned into backslashes + */ +const(char)* toWinPath(const(char)* src) +{ + if (src is null) + return null; + char* result = strdup(src); + char* p = result; + while (*p != '\0') + { + if (*p == '/') + *p = '\\'; + p++; + } + return result; +} + + +/** + * Reads a file, terminate the program on error + * + * Params: + * loc = The line number information from where the call originates + * filename = Path to file + */ +FileBuffer readFile(Loc loc, const(char)* filename) +{ + return readFile(loc, filename.toDString()); +} + +/// Ditto +FileBuffer readFile(Loc loc, const(char)[] filename) +{ + auto result = File.read(filename); + if (!result.success) + { + error(loc, "Error reading file `%.*s`", cast(int)filename.length, filename.ptr); + fatal(); + } + return FileBuffer(result.extractSlice()); +} + + +/** + * Writes a file, terminate the program on error + * + * Params: + * loc = The line number information from where the call originates + * filename = Path to file + * data = Full content of the file to be written + */ +extern (D) void writeFile(Loc loc, const(char)[] filename, const void[] data) +{ + ensurePathToNameExists(Loc.initial, filename); + if (!File.update(filename, data)) + { + error(loc, "Error writing file '%*.s'", cast(int) filename.length, filename.ptr); + fatal(); + } +} + + +/** + * Ensure the root path (the path minus the name) of the provided path + * exists, and terminate the process if it doesn't. + * + * Params: + * loc = The line number information from where the call originates + * name = a path to check (the name is stripped) + */ +void ensurePathToNameExists(Loc loc, const(char)[] name) +{ + const char[] pt = FileName.path(name); + if (pt.length) + { + if (!FileName.ensurePathExists(pt)) + { + error(loc, "cannot create directory %*.s", cast(int) pt.length, pt.ptr); + fatal(); + } + } + FileName.free(pt.ptr); +} + + +/** + * Takes a path, and escapes '(', ')' and backslashes + * + * Params: + * buf = Buffer to write the escaped path to + * fname = Path to escape + */ +void escapePath(OutBuffer* buf, const(char)* fname) +{ + while (1) + { + switch (*fname) + { + case 0: + return; + case '(': + case ')': + case '\\': + buf.writeByte('\\'); + goto default; + default: + buf.writeByte(*fname); + break; + } + fname++; + } +} + +/** + * Takes a path, and make it compatible with GNU Makefile format. + * + * GNU make uses a weird quoting scheme for white space. + * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space; + * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name; + * and backslashes in other contexts should not be doubled. + * + * Params: + * buf = Buffer to write the escaped path to + * fname = Path to escape + */ +void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname) +{ + uint slashes; + + while (*fname) + { + switch (*fname) + { + case '\\': + slashes++; + break; + case '$': + buf.writeByte('$'); + goto default; + case ' ': + case '\t': + while (slashes--) + buf.writeByte('\\'); + goto case; + case '#': + buf.writeByte('\\'); + goto default; + case ':': + // ':' not escaped on Windows because it can + // create problems with absolute paths (e.g. C:\Project) + version (Windows) {} + else + { + buf.writeByte('\\'); + } + goto default; + default: + slashes = 0; + break; + } + + buf.writeByte(*fname); + fname++; + } +} + +/// +unittest +{ + version (Windows) + { + enum input = `C:\My Project\file#4$.ext`; + enum expected = `C:\My\ Project\file\#4$$.ext`; + } + else + { + enum input = `/foo\bar/weird$.:name#\ with spaces.ext`; + enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`; + } + + OutBuffer buf; + buf.writeEscapedMakePath(input); + assert(buf[] == expected); +} + +/** + * Convert string to integer. + * + * Params: + * T = Type of integer to parse + * val = Variable to store the result in + * p = slice to start of string digits + * max = max allowable value (inclusive), defaults to `T.max` + * + * Returns: + * `false` on error, `true` on success + */ +bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max) + @safe pure @nogc nothrow +{ + import core.checkedint : mulu, addu, muls, adds; + + // mul* / add* doesn't support types < int + static if (T.sizeof < int.sizeof) + { + int value; + alias add = adds; + alias mul = muls; + } + // unsigned + else static if (T.min == 0) + { + T value; + alias add = addu; + alias mul = mulu; + } + else + { + T value; + alias add = adds; + alias mul = muls; + } + + bool overflow; + foreach (char c; p) + { + if (c > '9' || c < '0') + return false; + value = mul(value, 10, overflow); + value = add(value, uint(c - '0'), overflow); + } + // If it overflows, value must be > to `max` (since `max` is `T`) + val = cast(T) value; + return !overflow && value <= max; +} + +/// +@safe pure nothrow @nogc unittest +{ + byte b; + ubyte ub; + short s; + ushort us; + int i; + uint ui; + long l; + ulong ul; + + assert(b.parseDigits("42") && b == 42); + assert(ub.parseDigits("42") && ub == 42); + + assert(s.parseDigits("420") && s == 420); + assert(us.parseDigits("42000") && us == 42_000); + + assert(i.parseDigits("420000") && i == 420_000); + assert(ui.parseDigits("420000") && ui == 420_000); + + assert(l.parseDigits("42000000000") && l == 42_000_000_000); + assert(ul.parseDigits("82000000000") && ul == 82_000_000_000); + + assert(!b.parseDigits(ubyte.max.stringof)); + assert(!b.parseDigits("WYSIWYG")); + assert(!b.parseDigits("-42")); + assert(!b.parseDigits("200")); + assert(ub.parseDigits("200") && ub == 200); + assert(i.parseDigits(int.max.stringof) && i == int.max); + assert(i.parseDigits("420", 500) && i == 420); + assert(!i.parseDigits("420", 400)); +} |