1 module dscanner.utils; 2 3 import std.array : appender, uninitializedArray; 4 import std.stdio : stdin, stderr, File; 5 import std.conv : to; 6 import std.encoding : BOM, BOMSeq, EncodingException, getBOM; 7 import std.format : format; 8 import std.file : exists, read; 9 10 private void processBOM(ref ubyte[] sourceCode, string fname) 11 { 12 enum spec = "D-Scanner does not support %s-encoded files (%s)"; 13 const BOMSeq bs = sourceCode.getBOM; 14 with(BOM) switch (bs.schema) 15 { 16 case none, utf8: 17 break; 18 default: 19 throw new EncodingException(spec.format(bs.schema, fname)); 20 } 21 sourceCode = sourceCode[bs.sequence.length .. $]; 22 } 23 24 unittest 25 { 26 ubyte[] data = [0xEF, 0xBB, 0xBF, 'h', 'i', '!']; 27 data.processBOM("unittest data"); 28 assert(data[] == cast(ubyte[]) "hi!"); 29 } 30 31 unittest 32 { 33 import std.exception : assertThrown, assertNotThrown; 34 import std.encoding : bomTable; 35 import std.traits : EnumMembers; 36 37 foreach(m ; EnumMembers!BOM) 38 { 39 auto sc = bomTable[m].sequence.dup; 40 if (m != BOM.none && m != BOM.utf8) 41 { 42 assertThrown!(EncodingException)(processBOM(sc, "")); 43 } 44 else 45 { 46 assertNotThrown!(EncodingException)(processBOM(sc, "")); 47 } 48 } 49 } 50 51 ubyte[] readStdin() 52 { 53 auto sourceCode = appender!(ubyte[])(); 54 ubyte[4096] buf; 55 while (true) 56 { 57 auto b = stdin.rawRead(buf); 58 if (b.length == 0) 59 break; 60 sourceCode.put(b); 61 } 62 auto data = sourceCode.data; 63 data.processBOM("stdin"); 64 return data; 65 } 66 67 ubyte[] readFile(string fileName) 68 { 69 if (fileName == "stdin") 70 return readStdin(); 71 if (!exists(fileName)) 72 { 73 stderr.writefln("%s does not exist", fileName); 74 return []; 75 } 76 File f = File(fileName); 77 ubyte[] sourceCode; 78 sourceCode = cast(ubyte[]) fileName.read(); 79 sourceCode.processBOM(fileName); 80 return sourceCode; 81 } 82 83 string[] expandArgs(string[] args) 84 { 85 import std.file : isFile, FileException, dirEntries, SpanMode; 86 import std.algorithm.iteration : map; 87 import std.algorithm.searching : endsWith, canFind; 88 import std.path : dirSeparator; 89 90 // isFile can throw if it's a broken symlink. 91 bool isFileSafe(T)(T a) 92 { 93 try 94 return isFile(a); 95 catch (FileException) 96 return false; 97 } 98 99 string[] rVal; 100 if (args.length == 1) 101 args ~= "."; 102 foreach (arg; args[1 .. $]) 103 { 104 if (arg == "stdin" || isFileSafe(arg)) 105 rVal ~= arg; 106 else 107 foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name)) 108 { 109 if (isFileSafe(item) && (item.endsWith(`.d`) || item.endsWith(`.di`)) && !item.canFind(dirSeparator ~ '.')) 110 rVal ~= item; 111 else 112 continue; 113 } 114 } 115 return rVal; 116 } 117 118 /** 119 * Allows to build access chains of class members as done with the $(D ?.) operator 120 * in other languages. In the chain, any $(D null) member that is a class instance 121 * or that returns one, has for effect to shortcut the complete evaluation. 122 * 123 * This function is copied from https://github.com/BBasile/iz to avoid a new submodule. 124 * Any change made to this copy should also be applied to the origin. 125 * 126 * Params: 127 * M = The class type of the chain entry point. 128 * 129 * Bugs: 130 * Assigning a member only works with $(D unwrap). 131 * 132 */ 133 struct SafeAccess(M) 134 if (is(M == class)) 135 { 136 M m; 137 138 @disable this(); 139 140 /** 141 * Instantiate. 142 * 143 * Params: 144 * m = An instance of the entry point type. It is usually only 145 * $(D null) when the constructor is used internally, to build 146 * the chain. 147 */ 148 this(M m) 149 { 150 this.m = m; 151 } 152 153 alias m this; 154 /// Unprotect the class instance. 155 alias unwrap = m; 156 157 /// Allows cast to interfaces and classes inside the chain. 158 auto ref as(A)() @trusted 159 if (!__traits(hasMember, M, "as") && (is(A == class) || is(A == interface))) 160 { 161 return SafeAccess!(A)(cast(A) m); 162 } 163 164 /// Handles safe access. 165 auto ref opDispatch(string member, A...)(auto ref A a) 166 { 167 import std.traits : ReturnType; 168 alias T = typeof(__traits(getMember, m, member)); 169 static if (is(T == class)) 170 { 171 return (!m || !__traits(getMember, m, member)) 172 ? SafeAccess!T(null) 173 : SafeAccess!T(__traits(getMember, m, member)); 174 } 175 else 176 { 177 import std.traits : ReturnType, Parameters, isFunction; 178 static if (isFunction!T) 179 { 180 // otherwise there's a missing return statement. 181 alias R = ReturnType!T; 182 static if (!is(R == void) && 183 !(is(R == class) && Parameters!T.length == 0)) 184 pragma(msg, __FILE__ ~ "(" ~ __LINE__.stringof ~ "): error, " ~ 185 "only `void function`s or `class` getters can be called without unwrap"); 186 187 static if (is(R == class)) 188 { 189 return (m is null) 190 ? SafeAccess!R(null) 191 : SafeAccess!R(__traits(getMember, m, member)(a)); 192 } 193 else 194 { 195 if (m) 196 __traits(getMember, m, member)(a); 197 } 198 } 199 else 200 { 201 if (m) 202 __traits(getMember, m, member) = a; 203 } 204 } 205 } 206 } 207 /// General usage 208 @safe unittest 209 { 210 class LongLineOfIdent3{int foo; void setFoo(int v) @safe{foo = v;}} 211 class LongLineOfIdent2{LongLineOfIdent3 longLineOfIdent3;} 212 class LongLineOfIdent1{LongLineOfIdent2 longLineOfIdent2;} 213 class Root {LongLineOfIdent1 longLineOfIdent1;} 214 215 SafeAccess!Root sar = SafeAccess!Root(new Root); 216 // without the SafeAccess we would receive a SIGSEGV here 217 sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.setFoo(0xDEADBEEF); 218 219 bool notAccessed = true; 220 // the same with `&&` whould be much longer 221 if (LongLineOfIdent3 a = sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3) 222 { 223 notAccessed = false; 224 } 225 assert(notAccessed); 226 227 // checks that forwarding actually works 228 sar.m.longLineOfIdent1 = new LongLineOfIdent1; 229 sar.m.longLineOfIdent1.longLineOfIdent2 = new LongLineOfIdent2; 230 sar.m.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3 = new LongLineOfIdent3; 231 232 sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.setFoo(42); 233 assert(sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.unwrap.foo == 42); 234 } 235 236 /** 237 * IFTI helper for $(D SafeAccess). 238 * 239 * Returns: 240 * $(D m) with the ability to safely access its members that are class 241 * instances. 242 */ 243 auto ref safeAccess(M)(M m) 244 { 245 return SafeAccess!M(m); 246 }