1 /*
2  * This file is part of gir-to-d.
3  *
4  * gir-to-d is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation, either version 3
7  * of the License, or (at your option) any later version.
8  *
9  * gir-to-d is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with gir-to-d.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 module gtd.GirWrapper;
19 
20 import std.algorithm;
21 import std.array;
22 import std.file;
23 import std.uni;
24 import std.path;
25 import std.stdio;
26 import std.string;
27 import std.process : environment;
28 
29 import gtd.DefReader;
30 import gtd.GirField;
31 import gtd.GirFunction;
32 import gtd.GirPackage;
33 import gtd.GirStruct;
34 import gtd.GirType;
35 import gtd.GirVersion;
36 import gtd.GlibTypes;
37 import gtd.IndentedStringBuilder;
38 import gtd.Log;
39 
40 enum PrintFileMethod
41 {
42 	Absolute,
43 	Relative,
44 	Default
45 }
46 
47 class GirWrapper
48 {
49 	bool includeComments = true;
50 	bool useRuntimeLinker;
51 	bool useBindDir;
52 
53 	bool printFiles;
54 	PrintFileMethod printFileMethod = PrintFileMethod.Default;
55 	string cwdOrBaseDirectory;
56 
57 	string inputDir;
58 	string outputDir;
59 	string srcDir = "./";
60 	string commandlineGirPath;
61 
62 	static string licence;
63 	static string[string] aliasses;
64 
65 	static GirPackage[string] packages;
66 
67 	public this(string inputDir, string outputDir)
68 	{
69 		this.inputDir         = inputDir;
70 		this.outputDir       = outputDir;
71 	}
72 
73 	void proccess(string lookupFileName)
74 	{
75 		if ( !exists(buildPath(inputDir, lookupFileName)) )
76 			error(lookupFileName, " not found, check '--help' for more information.");
77 
78 		DefReader defReader = new DefReader( buildPath(inputDir, lookupFileName) );
79 
80 		proccess(defReader);
81 	}
82 
83 	void proccess(DefReader defReader, GirPackage currentPackage = null, bool isDependency = false, GirStruct currentStruct = null)
84 	{
85 		while ( !defReader.empty )
86 		{
87 			if ( !currentPackage && defReader.key.among(
88 					"addAliases", "addConstants", "addEnums", "addFuncts", "addStructs", "file", "move",
89 					"struct", "class", "interface", "namespace", "noAlias", "noConstant", "noEnum", "noCallback") )
90 				error("Found: '", defReader.key, "' before wrap.", defReader);
91 
92 			if ( !currentStruct && defReader.key.among(
93 					"code", "cType", "extend", "implements", "import", "interfaceCode", "merge",
94 					"noCode", "noExternal", "noProperty", "noSignal", "noStruct", "override", "structWrap",
95 					"array", "in", "out", "inout", "ref") )
96 				error("Found: '", defReader.key, "' without an active struct.", defReader);
97 
98 			switch ( defReader.key )
99 			{
100 				//Toplevel keys.
101 				case "bindDir":
102 					warning("Don't use bindDir, it is no longer used since the c definitions have moved.", defReader);
103 					break;
104 				case "includeComments":
105 					includeComments = defReader.valueBool;
106 					break;
107 				case "inputRoot":
108 					warning("Don't use inputRoot, it has been removed as it was never implemented.", defReader);
109 					break;
110 				case "license":
111 					licence = defReader.readBlock().join();
112 					break;
113 				case "outputRoot":
114 					if ( outputDir == "./out" )
115 						outputDir = defReader.value;
116 					break;
117 
118 				//Global keys.
119 				case "alias":
120 					if ( currentStruct )
121 						loadAA(currentStruct.aliases, defReader);
122 					else
123 						loadAA(aliasses, defReader);
124 					break;
125 				case "copy":
126 					try
127 						copyFiles(inputDir, buildPath(outputDir, srcDir), defReader.value);
128 					catch(FileException ex)
129 						error(ex.msg, defReader);
130 					break;
131 				case "dependency":
132 					loadDependency(defReader);
133 					break;
134 				case "lookup":
135 					DefReader reader = new DefReader( buildPath(inputDir, defReader.value) );
136 
137 					proccess(reader, currentPackage, isDependency, currentStruct);
138 					break;
139 				case "srcDir":
140 					srcDir = defReader.value;
141 					break;
142 				case "version":
143 					if ( defReader.value == "end" )
144 						break;
145 
146 					if ( defReader.subKey.empty )
147 						error("No version specified.", defReader);
148 
149 					bool parseVersion = checkOsVersion(defReader.subKey);
150 					bool smalerThen = false;
151 
152 					if ( defReader.subKey.length > 1 && defReader.subKey[0] == '<' && defReader.subKey[1].isNumber() )
153 					{
154 						smalerThen = true;
155 						defReader.subKey.popFront();
156 					}
157 
158 					if ( !parseVersion && defReader.subKey[0].isNumber() )
159 					{
160 						if ( !currentPackage )
161 							error("Only use OS versions before wrap.", defReader);
162 						parseVersion = defReader.subKey <= currentPackage._version;
163 
164 						if ( smalerThen )
165 							parseVersion = !parseVersion;
166 					}
167 
168 					if ( defReader.value == "start" )
169 					{
170 						if ( parseVersion )
171 							break;
172 
173 						defReader.skipBlock();
174 					}
175 
176 					if ( !parseVersion )
177 						break;
178 
179 					size_t index = defReader.value.indexOf(':');
180 					defReader.key = defReader.value[0 .. max(index, 0)].strip();
181 					defReader.value = defReader.value[index +1 .. $].strip();
182 
183 					if ( !defReader.key.empty )
184 						continue;
185 
186 					break;
187 				case "wrap":
188 					if ( isDependency )
189 					{
190 						currentPackage.name = defReader.value;
191 						break;
192 					}
193 
194 					if ( outputDir.empty )
195 						error("Found wrap while outputRoot isn't set", defReader);
196 					if (defReader.value in packages)
197 						error("Package '", defReader.value, "' is already defined.", defReader);
198 
199 					currentStruct = null;
200 					currentPackage = new GirPackage(defReader.value, this, srcDir);
201 					packages[defReader.value] = currentPackage;
202 					break;
203 
204 				//Package keys
205 				case "addAliases":
206 					currentPackage.lookupAliases ~= defReader.readBlock();
207 					break;
208 				case "addConstants":
209 					currentPackage.lookupConstants ~= defReader.readBlock();
210 					break;
211 				case "addEnums":
212 					currentPackage.lookupEnums ~= defReader.readBlock();
213 					break;
214 				case "addFuncts":
215 					currentPackage.lookupFuncts ~= defReader.readBlock();
216 					break;
217 				case "addStructs":
218 					currentPackage.lookupStructs ~= defReader.readBlock();
219 					break;
220 				case "file":
221 					if ( !isAbsolute(defReader.value) )
222 					{
223 						currentPackage.parseGIR(getAbsoluteGirPath(defReader.value));
224 					}
225 					else
226 					{
227 						warning("Don't use absolute paths for specifying gir files.", defReader);
228 
229 						currentPackage.parseGIR(defReader.value);
230 					}
231 					break;
232 				case "move":
233 					string[] vals = defReader.value.split();
234 					if ( vals.length <= 1 )
235 						error("No destination for move: ", defReader.value, defReader);
236 					string newFuncName = ( vals.length == 3 ) ? vals[2] : vals[0];
237 					GirStruct dest = currentPackage.getStruct(vals[1]);
238 					if ( dest is null )
239 						dest = createClass(currentPackage, vals[1]);
240 
241 					if ( currentStruct && vals[0] in currentStruct.functions )
242 					{
243 						currentStruct.functions[vals[0]].strct = dest;
244 						dest.functions[newFuncName] = currentStruct.functions[vals[0]];
245 						dest.functions[newFuncName].name = newFuncName;
246 						if ( newFuncName.startsWith("new") )
247 							dest.functions[newFuncName].type = GirFunctionType.Constructor;
248 						if ( currentStruct.virtualFunctions.canFind(vals[0]) )
249 							dest.virtualFunctions ~= newFuncName;
250 						currentStruct.functions.remove(vals[0]);
251 					}
252 					else if ( vals[0] in currentPackage.collectedFunctions )
253 					{
254 						currentPackage.collectedFunctions[vals[0]].strct = dest;
255 						dest.functions[newFuncName] = currentPackage.collectedFunctions[vals[0]];
256 						dest.functions[newFuncName].name = newFuncName;
257 						currentPackage.collectedFunctions.remove(vals[0]);
258 					}
259 					else
260 						error("Unknown function ", vals[0], defReader);
261 					break;
262 				case "noAlias":
263 					currentPackage.collectedAliases.remove(defReader.value);
264 					break;
265 				case "noConstant":
266 					currentPackage.collectedConstants.remove(defReader.value);
267 					break;
268 				case "noEnum":
269 					currentPackage.collectedEnums.remove(defReader.value);
270 					break;
271 				case "noCallback":
272 					currentPackage.collectedCallbacks.remove(defReader.value);
273 					break;
274 				case "struct":
275 					if ( defReader.value.empty )
276 					{
277 						currentStruct = null;
278 					}
279 					else
280 					{
281 						currentStruct = currentPackage.getStruct(defReader.value);
282 						if ( currentStruct is null )
283 							currentStruct = createClass(currentPackage, defReader.value);
284 					}
285 					break;
286 
287 				//Struct keys.
288 				case "array":
289 					string[] vals = defReader.value.split();
290 
291 					if ( vals[0] in currentStruct.functions )
292 					{
293 						GirFunction func = currentStruct.functions[vals[0]];
294 
295 						if ( vals[1] == "Return" )
296 						{
297 							if ( vals.length < 3 )
298 							{
299 								func.returnType.zeroTerminated = true;
300 								break;
301 							}
302 
303 							GirType elementType = new GirType(this);
304 
305 							elementType.name = func.returnType.name;
306 							elementType.cType = func.returnType.cType[0..$-1];
307 							func.returnType.elementType = elementType;
308 							func.returnType.girArray = true;
309 
310 							foreach( i, p; func.params )
311 							{
312 								if ( p.name == vals[2] )
313 									func.returnType.length = cast(int)i;
314 							}
315 						}
316 						else
317 						{
318 							GirParam param = findParam(currentStruct, vals[0], vals[1]);
319 							GirType elementType = new GirType(this);
320 
321 							if ( !param )
322 								error("Unknown parameter ", vals[1], " in function ", vals[0]);
323 
324 							elementType.name = param.type.name;
325 							elementType.cType = param.type.cType[0..$-1];
326 							param.type.elementType = elementType;
327 							param.type.girArray = true;
328 
329 							if ( vals.length < 3 )
330 							{
331 								param.type.zeroTerminated = true;
332 								break;
333 							}
334 
335 							if ( vals[2] == "Return" )
336 							{
337 								param.type.length = -2;
338 								break;
339 							}
340 
341 							foreach( i, p; func.params )
342 							{
343 								if ( p.name == vals[2] )
344 									param.type.length = cast(int)i;
345 							}
346 						}
347 					}
348 					else if ( currentStruct.fields.map!(a => a.name).canFind(vals[0]) )
349 					{
350 						GirField arrayField;
351 						int lengthID = -1;
352 
353 						foreach ( size_t i, field; currentStruct.fields )
354 						{
355 							if ( field.name == vals[0] )
356 								arrayField = field;
357 							else if ( field.name == vals[1] )
358 								lengthID = cast(int)i;
359 
360 							if ( arrayField && lengthID > -1 )
361 								break;
362 						}
363 
364 						arrayField.type.length = lengthID;
365 						currentStruct.fields[lengthID].isLength = true;
366 
367 						GirType elementType = new GirType(this);
368 						elementType.name = arrayField.type.name;
369 						elementType.cType = arrayField.type.cType[0..$-1];
370 						arrayField.type.elementType = elementType;
371 						arrayField.type.girArray = true;
372 					}
373 					else
374 					{
375 						error("Field or function: `", vals[0], "' is unknown.", defReader);
376 					}
377 					break;
378 				case "class":
379 					if ( currentStruct is null )
380 						currentStruct = createClass(currentPackage, defReader.value);
381 
382 					currentStruct.lookupClass = true;
383 					currentStruct.name = defReader.value;
384 					break;
385 				case "code":
386 					currentStruct.lookupCode ~= defReader.readBlock;
387 					break;
388 				case "cType":
389 					currentStruct.cType = defReader.value;
390 					break;
391 				case "extend":
392 					currentStruct.lookupParent = true;
393 					currentStruct.parent = defReader.value;
394 					break;
395 				case "implements":
396 					if ( defReader.value.empty )
397 						currentStruct.implements = null;
398 					else
399 						currentStruct.implements ~= defReader.value;
400 					break;
401 				case "import":
402 					currentStruct.imports ~= defReader.value;
403 					break;
404 				case "interface":
405 					if ( currentStruct is null )
406 						currentStruct = createClass(currentPackage, defReader.value);
407 
408 					currentStruct.lookupInterface = true;
409 					currentStruct.name = defReader.value;
410 					break;
411 				case "interfaceCode":
412 					currentStruct.lookupInterfaceCode ~= defReader.readBlock;
413 					break;
414 				case "merge":
415 					GirStruct mergeStruct = currentPackage.getStruct(defReader.value);
416 					currentStruct.merge(mergeStruct);
417 					GirStruct copy = currentStruct.dup();
418 					copy.noCode = true;
419 					copy.noExternal = true;
420 					mergeStruct.pack.collectedStructs[defReader.value] = copy;
421 					break;
422 				case "namespace":
423 					currentStruct.type = GirStructType.Record;
424 					currentStruct.lookupClass = false;
425 					currentStruct.lookupInterface = false;
426 
427 					if ( defReader.value.empty )
428 					{
429 						currentStruct.noNamespace = true;
430 					}
431 					else
432 					{
433 						currentStruct.noNamespace = false;
434 						currentStruct.name = defReader.value;
435 					}
436 					break;
437 				case "noCode":
438 					if ( defReader.valueBool )
439 					{
440 						currentStruct.noCode = true;
441 						break;
442 					}
443 					if ( defReader.value !in currentStruct.functions )
444 						error("Unknown function ", defReader.value, ". Possible values: ", currentStruct.functions.keys, defReader);
445 
446 					currentStruct.functions[defReader.value].noCode = true;
447 					break;
448 				case "noExternal":
449 					currentStruct.noExternal = true;
450 					break;
451 				case "noProperty":
452 					foreach ( field; currentStruct.fields )
453 					{
454 						if ( field.name == defReader.value )
455 						{
456 							field.noProperty = true;
457 							break;
458 						}
459 						else if ( field == currentStruct.fields.back )
460 							error("Unknown field ", defReader.value, defReader);
461 					}
462 					break;
463 				case "noSignal":
464 					currentStruct.functions[defReader.value~"-signal"].noCode = true;
465 					break;
466 				case "noStruct":
467 					currentStruct.noDeclaration = true;
468 					break;
469 				case "structWrap":
470 					loadAA(currentStruct.structWrap, defReader);
471 					break;
472 
473 				//Function keys
474 				case "in":
475 					string[] vals = defReader.value.split();
476 					if ( vals[0] !in currentStruct.functions )
477 						error("Unknown function ", vals[0], ". Possible values: ", currentStruct.functions, defReader);
478 					GirParam param = findParam(currentStruct, vals[0], vals[1]);
479 					if ( !param )
480 						error("Unknown parameter ", vals[1], " in function ", vals[0]);
481 					param.direction = GirParamDirection.Default;
482 					break;
483 				case "out":
484 					string[] vals = defReader.value.split();
485 					if ( vals[0] !in currentStruct.functions )
486 						error("Unknown function ", vals[0], ". Possible values: ", currentStruct.functions, defReader);
487 					GirParam param = findParam(currentStruct, vals[0], vals[1]);
488 					if ( !param )
489 						error("Unknown parameter ", vals[1], " in function ", vals[0]);
490 					param.direction = GirParamDirection.Out;
491 					break;
492 				case "override":
493 					currentStruct.functions[defReader.value].lookupOverride = true;
494 					break;
495 				case "inout":
496 				case "ref":
497 					string[] vals = defReader.value.split();
498 					if ( vals[0] !in currentStruct.functions )
499 						error("Unknown function ", vals[0], ". Possible values: ", currentStruct.functions, defReader);
500 					GirParam param = findParam(currentStruct, vals[0], vals[1]);
501 					if ( !param )
502 						error("Unknown parameter ", vals[1], " in function ", vals[0]);
503 					param.direction = GirParamDirection.InOut;
504 					break;
505 
506 				default:
507 					error("Unknown key: ", defReader.key, defReader);
508 			}
509 
510 			defReader.popFront();
511 		}
512 	}
513 
514 	void proccessGIR(string girFile)
515 	{
516 		GirPackage pack = new GirPackage("", this, srcDir);
517 
518 		if ( !isAbsolute(girFile) )
519 		{
520 			girFile = getAbsoluteGirPath(girFile);
521 		}
522 
523 		pack.parseGIR(girFile);
524 		packages[pack.name] = pack;
525 	}
526 
527 	void printFreeFunctions()
528 	{
529 		foreach ( pack; packages )
530 		{
531 			foreach ( func; pack.collectedFunctions )
532 			{
533 				if ( func.movedTo.empty )
534 					writefln("%s: %s", pack.name, func.name);
535 			}
536 		}
537 	}
538 
539 	void writeFile(string fileName, string contents, bool createDirectory = false)
540 	{
541 		if ( createDirectory )
542 		{
543 			try
544 			{
545 				if ( !exists(fileName.dirName()) )
546 					mkdirRecurse(fileName.dirName());
547 			}
548 			catch (FileException ex)
549 			{
550 				error("Failed to create directory: ", ex.msg);
551 			}
552 		}
553 
554 		std.file.write(fileName, contents);
555 
556 		if ( printFiles )
557 			printFilePath(fileName);
558 	}
559 
560 	string getAbsoluteGirPath(string girFile)
561 	{
562 		string[] girDirectories = getGirDirectories();
563 		foreach(dir; girDirectories) 
564 		{
565 			string girFilePath = buildNormalizedPath(dir, girFile);
566 			if (exists(girFilePath) && isFile(girFilePath))
567 				return girFilePath;
568 		}
569 
570 		error("Couldn't find the gir file: ", girFile, " in any of: ", girDirectories.join(", "));
571 		// Error shouldn't return anyways
572 		assert(0);
573 	}
574 
575 	private void printFilePath(string fileName)
576 	{
577 		with (PrintFileMethod) switch(printFileMethod)
578 		{
579 			case Absolute:
580 				writeln(asAbsolutePath(fileName));
581 				break;
582 			case Relative:
583 				writeln(asRelativePath(asAbsolutePath(fileName), cwdOrBaseDirectory));
584 				break;
585 			default:
586 				writeln(fileName);
587 				break;
588 		}
589 	}
590 
591 	private string[] getGirDirectories()
592 	{
593 		static string[] dirs;
594 		if(dirs !is null)
595 		{
596 			return dirs;
597 		}
598 
599 		if ( commandlineGirPath )
600 		{
601 			dirs ~= commandlineGirPath;
602 		}
603 
604 		version(Windows)
605 		{
606 			foreach (p; splitter(environment.get("PATH"), ';'))
607 			{
608 				dirs ~= p.buildNormalizedPath("../share/gir-1.0");
609 			}
610 
611 		}
612 		else version(OSX)
613 		{
614 			string path = environment.get("GTK_BASEPATH");
615 			if(path)
616 			{
617 				dirs ~= path.buildNormalizedPath("../share/gir-1.0");
618 			}
619 
620 			path = environment.get("HOMEBREW_ROOT");
621 			if(path)
622 			{
623 				dirs ~= path.buildNormalizedPath("share/gir-1.0");
624 			}
625 		}
626 		else
627 		{
628 			string xdgDataDirs = environment.get("XDG_DATA_DIRS", "/usr/local/share:/usr/share");
629 		        foreach (p; splitter(xdgDataDirs, ':'))
630 		        {
631 		        	dirs ~= p.buildNormalizedPath("gir-1.0");
632 		        }
633 		}
634 
635 		return dirs.filter!(p=> exists(p) && isDir(p) ).array;
636 	}
637 
638 	private GirParam findParam(GirStruct strct, string func, string name)
639 	{
640 		foreach( param; strct.functions[func].params )
641 		{
642 			if ( param.name == name )
643 				return param;
644 		}
645 
646 		return null;
647 	}
648 
649 	private void loadAA (ref string[string] aa, const DefReader defReader)
650 	{
651 		string[] vals = defReader.value.split();
652 
653 		if ( vals.length == 1 )
654 			vals ~= "";
655 
656 		if ( vals.length == 2 )
657 			aa[vals[0]] = vals[1];
658 		else
659 			error("Worng amount of arguments for key: ", defReader.key, defReader);
660 	}
661 
662 	private void loadDependency(DefReader defReader)
663 	{
664 		if ( defReader.value == "end" )
665 			return;
666 
667 		if ( defReader.subKey.empty )
668 			error("No dependency specified.", defReader);
669 
670 		GirInclude inc = GirPackage.includes.get(defReader.subKey, GirInclude.init);
671 
672 		if ( defReader.value == "skip" )
673 			inc.skip = true;
674 		else if ( defReader.value == "start" )
675 		{
676 			inc.lookupFile = defReader.fileName;
677 			inc.lookupLine = defReader.lineNumber;
678 
679 			inc.lookupText = defReader.readBlock();
680 		}
681 		else
682 			error("Missing 'skip' or 'start' for dependency: ", defReader.subKey, defReader);
683 
684 		GirPackage.includes[defReader.subKey] = inc;
685 	}
686 
687 	private void copyFiles(string srcDir, string destDir, string file)
688 	{
689 		string from = buildNormalizedPath(srcDir, file);
690 		string to = buildNormalizedPath(destDir, file);
691 
692 		if ( !printFiles )
693 			writefln("copying file [%s] to [%s]", from, to);
694 
695 		if ( isFile(from) )
696 		{
697 			if ( printFiles )
698 				writeln(to);
699 
700 			copy(from, to);
701 			return;
702 		}
703 
704 		void copyDir(string from, string to)
705 		{
706 			if ( !exists(to) )
707 				mkdirRecurse(to);
708 
709 			foreach ( entry; dirEntries(from, SpanMode.shallow) )
710 			{
711 				string dst = buildPath(to, entry.name.baseName);
712 
713 				if ( isDir(entry.name) )
714 				{
715 					copyDir(entry.name, dst);
716 				}
717 				else
718 				{
719 					if ( printFiles && !dst.endsWith("functions-runtime.d") && !dst.endsWith("functions-compiletime.d") )
720 						printFilePath(dst);
721 						
722 					copy(entry.name, dst);
723 				}
724 			}
725 		}
726 
727 		copyDir(from, to);
728 
729 		if ( file == "cairo" )
730 		{
731 			if ( printFiles )
732 				printFilePath(buildNormalizedPath(to, "c", "functions.d"));
733 
734 			if ( useRuntimeLinker )
735 				copy(buildNormalizedPath(to, "c", "functions-runtime.d"), buildNormalizedPath(to, "c", "functions.d"));
736 			else
737 				copy(buildNormalizedPath(to, "c", "functions-compiletime.d"), buildNormalizedPath(to, "c", "functions.d"));
738 
739 			remove(buildNormalizedPath(to, "c", "functions-runtime.d"));
740 			remove(buildNormalizedPath(to, "c", "functions-compiletime.d"));
741 		}
742 	}
743 
744 	private GirStruct createClass(GirPackage pack, string name)
745 	{
746 		GirStruct strct = new GirStruct(this, pack);
747 		strct.name = name;
748 		strct.cType = pack.cTypePrefix ~ name;
749 		strct.type = GirStructType.Record;
750 		strct.noDeclaration = true;
751 		pack.collectedStructs["lookup"~name] = strct;
752 
753 		return strct;
754 	}
755 
756 	private bool checkOsVersion(string _version)
757 	{
758 		if ( _version.empty || !(_version[0].isAlpha() || _version[0] == '!') )
759 			return false;
760 
761 		version(Windows)
762 		{
763 			return _version.among("Windows", "!OSX", "!linux", "!Linux", "!Posix") != 0;
764 		}
765 		else version(OSX)
766 		{
767 			return _version.among("!Windows", "OSX", "!linux", "!Linux", "Posix") != 0;
768 		}
769 		else version(linux)
770 		{
771 			return _version.among("!Windows", "!OSX", "linux", "Linux", "Posix") != 0;
772 		}
773 		else version(Posix)
774 		{
775 			return _version.among("!Windows", "!OSX", "!linux", "!Linux", "Posix") != 0;
776 		}
777 		else
778 		{
779 			return false;
780 		}
781 	}
782 
783 }
784 
785 /**
786  * Apply aliasses to the tokens in the string, and
787  * camelCase underscore separated tokens.
788  */
789 string stringToGtkD(string str, string[string] aliases, bool caseConvert = true)
790 {
791 	return stringToGtkD(str, aliases, null, caseConvert);
792 }
793 
794 string stringToGtkD(string str, string[string] aliases, string[string] localAliases, bool caseConvert = true)
795 {
796 	size_t pos, start;
797 	string seps = " \n\r\t\f\v()[]*,;";
798 	auto converted = appender!string();
799 
800 	while ( pos < str.length )
801 	{
802 		if ( !seps.canFind(str[pos]) )
803 		{
804 			start = pos;
805 
806 			while ( pos < str.length && !seps.canFind(str[pos]) )
807 				pos++;
808 
809 			//Workaround for the tm struct, type and variable have the same name.
810 			if ( pos < str.length && str[pos] == '*' && str[start..pos] == "tm" )
811 				converted.put("void");
812 			else
813 				converted.put(tokenToGtkD(str[start..pos], aliases, localAliases, caseConvert));
814 
815 			if ( pos == str.length )
816 				break;
817 		}
818 
819 		converted.put(str[pos]);
820 		pos++;
821 	}
822 
823 	return converted.data;
824 }
825 
826 unittest
827 {
828 	assert(stringToGtkD("token", ["token":"tok"]) == "tok");
829 	assert(stringToGtkD("string token_to_gtkD(string token, string[string] aliases)", ["token":"tok"])
830 	       == "string tokenToGtkD(string tok, string[string] aliases)");
831 }
832 
833 string tokenToGtkD(string token, string[string] aliases, bool caseConvert=true)
834 {
835 	return tokenToGtkD(token, aliases, null, caseConvert);
836 }
837 
838 string tokenToGtkD(string token, string[string] aliases, string[string] localAliases, bool caseConvert=true)
839 {
840 	if ( token in glibTypes )
841 		return glibTypes[token];
842 	else if ( token in localAliases )
843 		return localAliases[token];
844 	else if ( token in aliases )
845 		return aliases[token];
846 	else if ( token.endsWith("_t", "_t*", "_t**") )
847 		return token;
848 	else if ( token == "pid_t" || token == "size_t" )
849 		return token;
850 	else if ( caseConvert )
851 		return tokenToGtkD(removeUnderscore(token), aliases, localAliases, false);
852 	else
853 		return token;
854 }
855 
856 string removeUnderscore(string token)
857 {
858 	char pc;
859 	auto converted = appender!string();
860 
861 	while ( !token.empty )
862 	{
863 		if ( token[0] == '_' )
864 		{
865 			pc = token[0];
866 			token = token[1..$];
867 
868 			continue;
869 		}
870 
871 		if ( pc == '_' )
872 			converted.put(token[0].toUpper());
873 		else
874 			converted.put(token[0]);
875 
876 		pc = token[0];
877 		token = token[1..$];
878 	}
879 
880 	return converted.data;
881 }
882 
883 unittest
884 {
885 	assert(removeUnderscore("this_is_a_test") == "thisIsATest");
886 }