1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Oct 1, 2011
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module clang.Util;
8 
9 import std.conv;
10 import std.stdio;
11 import std.format;
12 
13 import clang.c.Index;
14 
15 immutable(char*)* strToCArray (const string[] arr)
16 {
17     import std..string : toStringz;
18 
19     if (!arr)
20         return null;
21 
22     immutable(char*)[] cArr;
23     cArr.reserve(arr.length);
24 
25     foreach (str ; arr)
26         cArr ~= str.toStringz;
27 
28     return cArr.ptr;
29 }
30 
31 string toD (CXString cxString)
32 {
33     auto cstr = clang_getCString(cxString);
34     auto str = to!(string)(cstr).idup;
35     clang_disposeString(cxString);
36 
37     return str;
38 }
39 
40 template isCX (T)
41 {
42     enum bool isCX = __traits(hasMember, T, "cx");
43 }
44 
45 template cxName (T)
46 {
47     enum cxName = "CX" ~ T.stringof;
48 }
49 
50 U* toCArray (U, T) (T[] arr)
51 {
52     if (!arr)
53         return null;
54 
55     static if (is(typeof(T.init.cx)))
56         return arr.map!(e => e.cx).toArray.ptr;
57 
58     else
59         return arr.ptr;
60 }
61 
62 mixin template CX ()
63 {
64     mixin("private alias " ~ cxName!(typeof(this)) ~ " CType;");
65 
66     CType cx;
67     alias cx this;
68 
69     void dispose ()
70     {
71         enum methodCall = "clang_dispose" ~ typeof(this).stringof ~ "(cx);";
72 
73         static if (false && __traits(compiles, methodCall))
74             mixin(methodCall);
75     }
76 
77     @property bool isValid ()
78     {
79         return cx !is CType.init;
80     }
81 }
82 
83 string clangVersionString()
84 {
85     import std..string : strip;
86 
87     return strip(clang_getClangVersion().toD);
88 }
89 
90 struct Version
91 {
92     uint major = 0;
93     uint minor = 0;
94     uint release = 0;
95 }
96 
97 Version clangVersion()
98 {
99     import std.algorithm : find;
100     import std.conv : parse;
101     import std.ascii : isDigit;
102     import std.range;
103 
104     Version result;
105     auto verstr = clangVersionString().find!(x => x.isDigit);
106 
107     result.major = verstr.parse!uint;
108     verstr.popFront();
109     result.minor = verstr.parse!uint;
110     verstr.popFront();
111 
112     if (!verstr.empty && verstr.back.isDigit)
113         result.release = verstr.parse!uint;
114 
115     return result;
116 }
117 
118 alias Set(T) = void[0][T];
119 
120 void add(T)(ref void[0][T] self, T value) {
121     self[value] = (void[0]).init;
122 }
123 
124 void add(T)(ref void[0][T] self, void[0][T] set) {
125     foreach (key; set.byKey) {
126         self.add(key);
127     }
128 }
129 
130 Set!T clone(T)(ref void[0][T] self) {
131     Set!T result;
132     result.add(self);
133     return result;
134 }
135 
136 bool contains(T)(inout(void[0][T]) set, T value) {
137     return (value in set) !is null;
138 }
139 
140 auto setFromList(T)(T[] list)
141 {
142     import std.traits;
143 
144     Set!(Unqual!T) result;
145 
146     foreach (item; list)
147         result.add(item);
148 
149     return result;
150 }
151 
152 version (Posix)
153 {
154     private extern (C) int mkstemps(char*, int);
155     private extern (C) int close(int);
156 }
157 else
158 {
159     import core.sys.windows.objbase : CoCreateGuid;
160     import core.sys.windows.basetyps : GUID;
161 
162     string createGUID()
163     {
164         char toHex(uint x)
165         {
166             if (x < 10)
167                 return cast(char) ('0' + x);
168             else
169                 return cast(char) ('A' + x - 10);
170         }
171 
172         GUID guid;
173         CoCreateGuid(&guid);
174 
175         ubyte* data = cast(ubyte*)&guid;
176         char[32] result;
177 
178         foreach (i; 0 .. 16)
179         {
180             result[i * 2 + 0] = toHex(data[i] & 0x0fu);
181             result[i * 2 + 1] = toHex(data[i] >> 16);
182         }
183 
184         return result.idup;
185     }
186 }
187 
188 class NamedTempFileException : object.Exception
189 {
190     immutable string path;
191 
192     this (string path, string file = __FILE__, size_t line = __LINE__)
193     {
194         this.path = path;
195         super(format("Cannot create temporary file \"%s\".", path), file, line);
196     }
197 }
198 
199 File namedTempFile(string prefix, string suffix)
200 {
201     import std.file;
202     import std.path;
203     import std.format;
204 
205     version (Posix)
206     {
207         static void randstr (char[] slice)
208         {
209             import std.random;
210 
211             foreach (i; 0 .. slice.length)
212                 slice[i] = uniform!("[]")('A', 'Z');
213         }
214 
215         string name = format("%sXXXXXXXXXXXXXXXX%s\0", prefix, suffix);
216         char[] path = buildPath(tempDir(), name).dup;
217         const size_t termAnd6XSize = 7;
218 
219         immutable size_t begin = path.length - name.length + prefix.length;
220         immutable size_t end = path.length - suffix.length - termAnd6XSize;
221 
222         randstr(path[begin .. end]);
223 
224         int fd = mkstemps(path.ptr, cast(int) suffix.length);
225         scope (exit) close(fd);
226 
227         path = path[0 .. $ - 1];
228 
229         if (fd == -1)
230             throw new NamedTempFileException(path.idup);
231 
232         return File(path, "wb+");
233     }
234     else
235     {
236         string name = format("%s%s%s", prefix, createGUID(), suffix);
237         string path = buildPath(tempDir(), name);
238         return File(path, "wb+");
239     }
240 }
241 
242 string asAbsNormPath(string path)
243 {
244     import std.path;
245     import std.conv : to;
246 
247     return to!string(path.asAbsolutePath.asNormalizedPath);
248 }