1 /**
2  * This file is part of DCD, a development tool for the D programming language.
3  * Copyright (C) 2014 Brian Schott
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 module server.server;
20 
21 import std.algorithm;
22 import std.array;
23 import std.experimental.logger;
24 import std.file;
25 import std.path;
26 import std.process: environment;
27 import std.stdio: File;
28 import std.stdio: KeepTerminator;
29 
30 import dsymbol.modulecache;
31 
32 /// Name of the server configuration file
33 enum CONFIG_FILE_NAME = "dcd.conf";
34 
35 version(linux) version = useXDG;
36 version(BSD) version = useXDG;
37 version(FreeBSD) version = useXDG;
38 version(OSX) version = useXDG;
39 
40 /**
41  * Locates the configuration file
42  */
43 string getConfigurationLocation()
44 {
45 	version (useXDG)
46 	{
47 		string configDir = environment.get("XDG_CONFIG_HOME", null);
48 		if (configDir is null)
49 		{
50 			configDir = environment.get("HOME", null);
51 			if (configDir !is null)
52 				configDir = buildPath(configDir, ".config", "dcd", CONFIG_FILE_NAME);
53 			if (!exists(configDir))
54 				configDir = buildPath("/etc/", CONFIG_FILE_NAME);
55 		}
56 		else
57 		{
58 			configDir = buildPath(configDir, "dcd", CONFIG_FILE_NAME);
59 		}
60 		return configDir;
61 	}
62 	else version(Windows)
63 	{
64 		return CONFIG_FILE_NAME;
65 	}
66 }
67 
68 /**
69  * Prints a warning message to the user when an old config file is detected.
70  */
71 void warnAboutOldConfigLocation()
72 {
73 	version (linux) if ("~/.config/dcd".expandTilde().exists()
74 		&& "~/.config/dcd".expandTilde().isFile())
75 	{
76 		warning("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
77 		warning("!! Upgrade warning:");
78 		warning("!! '~/.config/dcd' should be moved to '$XDG_CONFIG_HOME/dcd/dcd.conf'");
79 		warning("!! or '$HOME/.config/dcd/dcd.conf'");
80 		warning("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
81 	}
82 }
83 
84 import std.regex : ctRegex;
85 alias envVarRegex = ctRegex!(`\$\{([_a-zA-Z][_a-zA-Z 0-9]*)\}`);
86 
87 private unittest
88 {
89 	import std.regex : replaceAll;
90 
91 	enum input = `${HOME}/aaa/${_bb_b}/ccc`;
92 
93 	assert(replaceAll!(m => m[1])(input, envVarRegex) == `HOME/aaa/_bb_b/ccc`);
94 }
95 
96 /**
97  * Loads import directories from the configuration file
98  */
99 string[] loadConfiguredImportDirs()
100 {
101 	string expandEnvVars(string l)
102 	{
103 		import std.regex : replaceAll;
104 		return replaceAll!(m => environment.get(m[1], ""))(l, envVarRegex);
105 	}
106 
107 	warnAboutOldConfigLocation();
108 	immutable string configLocation = getConfigurationLocation();
109 	if (!configLocation.exists())
110 		return [];
111 	info("Loading configuration from ", configLocation);
112 	File f = File(configLocation, "rt");
113 	return f.byLine(KeepTerminator.no)
114 		.filter!(a => a.length > 0 && a[0] != '#')
115 		.map!(a => a.idup)
116 		.map!(expandEnvVars)
117 		.filter!(a => existanceCheck(a))
118 		.array();
119 }