1 /** 2 * This module defines some utility functions for DMD. 3 * 4 * Copyright: Copyright (C) 1999-2020 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.write(filename, data)) 85 { 86 error(loc, "Error writing file '%*.s'", 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", 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 * Convert string to integer. 145 * 146 * Params: 147 * T = Type of integer to parse 148 * val = Variable to store the result in 149 * p = slice to start of string digits 150 * max = max allowable value (inclusive), defaults to `T.max` 151 * 152 * Returns: 153 * `false` on error, `true` on success 154 */ 155 bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max) 156 @safe pure @nogc nothrow 157 { 158 import core.checkedint : mulu, addu, muls, adds; 159 160 // mul* / add* doesn't support types < int 161 static if (T.sizeof < int.sizeof) 162 { 163 int value; 164 alias add = adds; 165 alias mul = muls; 166 } 167 // unsigned 168 else static if (T.min == 0) 169 { 170 T value; 171 alias add = addu; 172 alias mul = mulu; 173 } 174 else 175 { 176 T value; 177 alias add = adds; 178 alias mul = muls; 179 } 180 181 bool overflow; 182 foreach (char c; p) 183 { 184 if (c > '9' || c < '0') 185 return false; 186 value = mul(value, 10, overflow); 187 value = add(value, uint(c - '0'), overflow); 188 } 189 // If it overflows, value must be > to `max` (since `max` is `T`) 190 val = cast(T) value; 191 return !overflow && value <= max; 192 } 193 194 /// 195 @safe pure nothrow @nogc unittest 196 { 197 byte b; 198 ubyte ub; 199 short s; 200 ushort us; 201 int i; 202 uint ui; 203 long l; 204 ulong ul; 205 206 assert(b.parseDigits("42") && b == 42); 207 assert(ub.parseDigits("42") && ub == 42); 208 209 assert(s.parseDigits("420") && s == 420); 210 assert(us.parseDigits("42000") && us == 42_000); 211 212 assert(i.parseDigits("420000") && i == 420_000); 213 assert(ui.parseDigits("420000") && ui == 420_000); 214 215 assert(l.parseDigits("42000000000") && l == 42_000_000_000); 216 assert(ul.parseDigits("82000000000") && ul == 82_000_000_000); 217 218 assert(!b.parseDigits(ubyte.max.stringof)); 219 assert(!b.parseDigits("WYSIWYG")); 220 assert(!b.parseDigits("-42")); 221 assert(!b.parseDigits("200")); 222 assert(ub.parseDigits("200") && ub == 200); 223 assert(i.parseDigits(int.max.stringof) && i == int.max); 224 assert(i.parseDigits("420", 500) && i == 420); 225 assert(!i.parseDigits("420", 400)); 226 }