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; 88 89 // isFile can throw if it's a broken symlink. 90 bool isFileSafe(T)(T a) 91 { 92 try 93 return isFile(a); 94 catch (FileException) 95 return false; 96 } 97 98 string[] rVal; 99 if (args.length == 1) 100 args ~= "."; 101 foreach (arg; args[1 .. $]) 102 { 103 if (arg == "stdin" || isFileSafe(arg)) 104 rVal ~= arg; 105 else 106 foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name)) 107 { 108 if (isFileSafe(item) && (item.endsWith(`.d`) || item.endsWith(`.di`))) 109 rVal ~= item; 110 else 111 continue; 112 } 113 } 114 return rVal; 115 } 116 117 /** 118 * Allows to build access chains of class members as done with the $(D ?.) operator 119 * in other languages. In the chain, any $(D null) member that is a class instance 120 * or that returns one, has for effect to shortcut the complete evaluation. 121 * 122 * This function is copied from https://github.com/BBasile/iz to avoid a new submodule. 123 * Any change made to this copy should also be applied to the origin. 124 * 125 * Params: 126 * M = The class type of the chain entry point. 127 * 128 * Bugs: 129 * Assigning a member only works with $(D unwrap). 130 * 131 */ 132 struct SafeAccess(M) 133 if (is(M == class)) 134 { 135 M m; 136 137 @disable this(); 138 139 /** 140 * Instantiate. 141 * 142 * Params: 143 * m = An instance of the entry point type. It is usually only 144 * $(D null) when the constructor is used internally, to build 145 * the chain. 146 */ 147 this(M m) 148 { 149 this.m = m; 150 } 151 152 alias m this; 153 /// Unprotect the class instance. 154 alias unwrap = m; 155 156 /// Allows cast to interfaces and classes inside the chain. 157 auto ref as(A)() @trusted 158 if (!__traits(hasMember, M, "as") && (is(A == class) || is(A == interface))) 159 { 160 return SafeAccess!(A)(cast(A) m); 161 } 162 163 /// Handles safe access. 164 auto ref opDispatch(string member, A...)(auto ref A a) 165 { 166 import std.traits : ReturnType; 167 alias T = typeof(__traits(getMember, m, member)); 168 static if (is(T == class)) 169 { 170 return (!m || !__traits(getMember, m, member)) 171 ? SafeAccess!T(null) 172 : SafeAccess!T(__traits(getMember, m, member)); 173 } 174 else 175 { 176 import std.traits : ReturnType, Parameters, isFunction; 177 static if (isFunction!T) 178 { 179 // otherwise there's a missing return statement. 180 alias R = ReturnType!T; 181 static if (!is(R == void) && 182 !(is(R == class) && Parameters!T.length == 0)) 183 pragma(msg, __FILE__ ~ "(" ~ __LINE__.stringof ~ "): error, " ~ 184 "only `void function`s or `class` getters can be called without unwrap"); 185 186 static if (is(R == class)) 187 { 188 return (m is null) 189 ? SafeAccess!R(null) 190 : SafeAccess!R(__traits(getMember, m, member)(a)); 191 } 192 else 193 { 194 if (m) 195 __traits(getMember, m, member)(a); 196 } 197 } 198 else 199 { 200 if (m) 201 __traits(getMember, m, member) = a; 202 } 203 } 204 } 205 } 206 /// General usage 207 @safe unittest 208 { 209 class LongLineOfIdent3{int foo; void setFoo(int v) @safe{foo = v;}} 210 class LongLineOfIdent2{LongLineOfIdent3 longLineOfIdent3;} 211 class LongLineOfIdent1{LongLineOfIdent2 longLineOfIdent2;} 212 class Root {LongLineOfIdent1 longLineOfIdent1;} 213 214 SafeAccess!Root sar = SafeAccess!Root(new Root); 215 // without the SafeAccess we would receive a SIGSEGV here 216 sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.setFoo(0xDEADBEEF); 217 218 bool notAccessed = true; 219 // the same with `&&` whould be much longer 220 if (LongLineOfIdent3 a = sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3) 221 { 222 notAccessed = false; 223 } 224 assert(notAccessed); 225 226 // checks that forwarding actually works 227 sar.m.longLineOfIdent1 = new LongLineOfIdent1; 228 sar.m.longLineOfIdent1.longLineOfIdent2 = new LongLineOfIdent2; 229 sar.m.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3 = new LongLineOfIdent3; 230 231 sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.setFoo(42); 232 assert(sar.longLineOfIdent1.longLineOfIdent2.longLineOfIdent3.unwrap.foo == 42); 233 } 234 235 /** 236 * IFTI helper for $(D SafeAccess). 237 * 238 * Returns: 239 * $(D m) with the ability to safely access its members that are class 240 * instances. 241 */ 242 auto ref safeAccess(M)(M m) 243 { 244 return SafeAccess!M(m); 245 }