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) char* mkdtemp(char*);
156     private extern (C) int close(int);
157 }
158 else
159 {
160     struct GUID {
161         uint Data1;
162         ushort Data2;
163         ushort Data3;
164         ubyte[8] Data4;
165     }
166 
167     private extern (Windows) uint CoCreateGuid(GUID* pguid);
168 
169     private string createGUID()
170     {
171         char toHex(uint x)
172         {
173             if (x < 10)
174                 return cast(char) ('0' + x);
175             else
176                 return cast(char) ('A' + x - 10);
177         }
178 
179         GUID guid;
180         CoCreateGuid(&guid);
181 
182         ubyte* data = cast(ubyte*)&guid;
183         char[32] result;
184 
185         foreach (i; 0 .. 16)
186         {
187             result[i * 2 + 0] = toHex(data[i] & 0x0fu);
188             result[i * 2 + 1] = toHex(data[i] >> 16);
189         }
190 
191         return result.idup;
192     }
193 }
194 
195 class NamedTempFileException : object.Exception
196 {
197     immutable string path;
198 
199     this (string path, string file = __FILE__, size_t line = __LINE__)
200     {
201         this.path = path;
202         super(format("Cannot create temporary file \"%s\".", path), file, line);
203     }
204 }
205 
206 class NamedTempDirException : object.Exception
207 {
208     immutable string path;
209 
210     this (string path, string file = __FILE__, size_t line = __LINE__)
211     {
212         this.path = path;
213         super(
214             format("Cannot create temporary directory \"%s\".", path),
215             file,
216             line);
217     }
218 }
219 
220 private void randstr (char[] slice)
221 {
222     import std.random;
223 
224     foreach (i; 0 .. slice.length)
225         slice[i] = uniform!("[]")('A', 'Z');
226 }
227 
228 File namedTempFile(string prefix, string suffix)
229 {
230     import std.file;
231     import std.path;
232     import std.format;
233 
234     version (Posix)
235     {
236         string name = format("%sXXXXXXXXXXXXXXXX%s\0", prefix, suffix);
237         char[] path = buildPath(tempDir(), name).dup;
238         const size_t termAnd6XSize = 7;
239 
240         immutable size_t begin = path.length - name.length + prefix.length;
241         immutable size_t end = path.length - suffix.length - termAnd6XSize;
242 
243         randstr(path[begin .. end]);
244 
245         int fd = mkstemps(path.ptr, cast(int) suffix.length);
246         scope (exit) close(fd);
247 
248         path = path[0 .. $ - 1];
249 
250         if (fd == -1)
251             throw new NamedTempFileException(path.idup);
252 
253         return File(path, "wb+");
254     }
255     else
256     {
257         string name = format("%s%s%s", prefix, createGUID(), suffix);
258         string path = buildPath(tempDir(), name);
259         return File(path, "wb+");
260     }
261 }
262 
263 string namedTempDir(string prefix)
264 {
265     import std.file;
266     import std.path;
267     import std.format;
268 
269     version (Posix)
270     {
271         string name = format("%sXXXXXXXXXXXXXXXX\0", prefix);
272         char[] path = buildPath(tempDir(), name).dup;
273         const size_t termAnd6XSize = 7;
274 
275         immutable size_t begin = path.length - name.length + prefix.length;
276 
277         randstr(path[begin .. $ - termAnd6XSize]);
278 
279         char* result = mkdtemp(path.ptr);
280 
281         path = path[0..$-1];
282 
283         if (result == null)
284             throw new NamedTempDirException(path.idup);
285 
286         return path.idup;
287     }
288     else
289     {
290         string name = prefix ~ createGUID();
291         string path = buildPath(tempDir(), name);
292 
293         try
294             mkdirRecurse(path);
295         catch (FileException)
296             throw new NamedTempDirException(path);
297 
298         return path;
299     }
300 }
301 
302 string asAbsNormPath(string path)
303 {
304     import std.path;
305     import std.conv : to;
306 
307     return to!string(path.asAbsolutePath.asNormalizedPath);
308 }