1 /*
2  * Copyright (C) 2007-2016 Tim Mayberry <mojofunk@gmail.com>
3  * Copyright (C) 2008-2011 David Robillard <d@drobilla.net>
4  * Copyright (C) 2008-2016 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2013-2014 John Emmas <john@creativepost.co.uk>
7  * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 #include <cstdlib>
24 #include <iostream>
25 
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
28 #include "pbd/string_convert.h"
29 
30 #include <glibmm/miscutils.h>
31 #include <glibmm/fileutils.h>
32 
33 #include "ardour/directory_names.h"
34 #include "ardour/filesystem_paths.h"
35 
36 #include "pbd/i18n.h"
37 
38 #ifdef PLATFORM_WINDOWS
39 #include "shlobj.h"
40 #include "pbd/windows_special_dirs.h"
41 #endif
42 
43 using namespace PBD;
44 
45 namespace ARDOUR {
46 
47 using std::string;
48 
49 static std::string
user_config_directory_name(int version=-1)50 user_config_directory_name (int version = -1)
51 {
52 	if (version < 0) {
53 		version = string_to<int32_t>(X_(PROGRAM_VERSION));
54 	}
55 
56 	/* ARDOUR::Profile may not be available when this is
57 	   called, so rely on build-time detection of the
58 	   product name etc.
59 	*/
60 
61 	const string config_dir_name = string_compose ("%1%2", X_(PROGRAM_NAME), version);
62 
63 #if defined (__APPLE__) || defined (PLATFORM_WINDOWS)
64 	/* Use mixed-case folder name on OS X and Windows */
65 	return config_dir_name;
66 #else
67 	/* use lower case folder name on Linux */
68 	return downcase (config_dir_name);
69 #endif
70 }
71 
72 std::string
user_config_directory(int version)73 user_config_directory (int version)
74 {
75 	std::string p;
76 
77 #ifdef __APPLE__
78 
79 	p = Glib::build_filename (Glib::get_home_dir(), "Library/Preferences");
80 
81 #else
82 
83 	const char* c = 0;
84 	/* adopt freedesktop standards, and put .ardour3 into $XDG_CONFIG_HOME or ~/.config */
85 	if ((c = getenv ("XDG_CONFIG_HOME")) != 0) {
86 		p = c;
87 	} else {
88 
89 #ifdef PLATFORM_WINDOWS
90 		// Not technically the home dir (since it needs to be a writable folder)
91 		const string home_dir = Glib::get_user_config_dir();
92 #else
93 		const string home_dir = Glib::get_home_dir();
94 #endif
95 		if (home_dir.empty ()) {
96 			error << "Unable to determine home directory" << endmsg;
97 			exit (EXIT_FAILURE);
98 		}
99 		p = home_dir;
100 
101 #ifndef PLATFORM_WINDOWS
102 		p = Glib::build_filename (p, ".config");
103 #endif
104 
105 	}
106 #endif // end not __APPLE__
107 
108 	p = Glib::build_filename (p, user_config_directory_name (version));
109 
110 	if (version < 0) {
111 		/* Only create the user config dir if the version was negative,
112 		   meaning "for the current version.
113 		*/
114 		if (!Glib::file_test (p, Glib::FILE_TEST_EXISTS)) {
115 			if (g_mkdir_with_parents (p.c_str(), 0755)) {
116 				error << string_compose (_("Cannot create Configuration directory %1 - cannot run"),
117 				                         p) << endmsg;
118 				exit (EXIT_FAILURE);
119 			}
120 			} else if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
121 			fatal << string_compose (_("Configuration directory %1 already exists and is not a directory/folder - cannot run"),
122 			                         p) << endmsg;
123 			abort(); /*NOTREACHED*/
124 		}
125 	}
126 
127 	return p;
128 }
129 
130 std::string
user_cache_directory(std::string cachename)131 user_cache_directory (std::string cachename)
132 {
133 	std::string p;
134 
135 #ifdef __APPLE__
136 	p = Glib::build_filename (Glib::get_home_dir(), "Library/Caches");
137 #else
138 	const char* c = 0;
139 
140 	/* adopt freedesktop standards, and put .ardour3 into $XDG_CACHE_HOME
141 	 * defaulting to or ~/.config
142 	 */
143 	if ((c = getenv ("XDG_CACHE_HOME")) != 0) {
144 		p = c;
145 	} else {
146 
147 #ifdef PLATFORM_WINDOWS
148 		// Not technically the home dir (since it needs to be a writable folder)
149 		const string home_dir = Glib::get_user_data_dir();
150 #else
151 		const string home_dir = Glib::get_home_dir();
152 #endif
153 		if (home_dir.empty ()) {
154 			error << "Unable to determine home directory" << endmsg;
155 			exit (EXIT_FAILURE);
156 		}
157 		p = home_dir;
158 
159 #ifndef PLATFORM_WINDOWS
160 		p = Glib::build_filename (p, ".cache");
161 #endif
162 
163 	}
164 #endif // end not __APPLE__
165 
166 	if (cachename.empty ()) {
167 		p = Glib::build_filename (p, user_config_directory_name ());
168 	} else {
169 		p = Glib::build_filename (p, cachename);
170 	}
171 
172 #ifdef PLATFORM_WINDOWS
173 	 /* On Windows Glib::get_user_data_dir is the folder to use for local
174 		* (as opposed to roaming) application data.
175 		* See documentation for CSIDL_LOCAL_APPDATA.
176 		* Glib::get_user_data_dir() == GLib::get_user_config_dir()
177 		* so we add an extra subdir *below* the config dir.
178 		*/
179 	p = Glib::build_filename (p, "cache");
180 #endif
181 
182 	if (!Glib::file_test (p, Glib::FILE_TEST_EXISTS)) {
183 		if (g_mkdir_with_parents (p.c_str(), 0755)) {
184 			error << string_compose (_("Cannot create cache directory %1 - cannot run"),
185 						   p) << endmsg;
186 			exit (EXIT_FAILURE);
187 		}
188 	} else if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
189 		fatal << string_compose (_("Cache directory %1 already exists and is not a directory/folder - cannot run"),
190 					   p) << endmsg;
191 		abort(); /*NOTREACHED*/
192 	}
193 
194 	return p;
195 }
196 
197 std::string
ardour_dll_directory()198 ardour_dll_directory ()
199 {
200 #ifdef PLATFORM_WINDOWS
201 	std::string dll_dir_path(windows_package_directory_path());
202 	dll_dir_path = Glib::build_filename (dll_dir_path, "lib");
203 	return Glib::build_filename (dll_dir_path, LIBARDOUR);
204 #else
205 	std::string s = Glib::getenv("ARDOUR_DLL_PATH");
206 	if (s.empty()) {
207 		std::cerr << _("ARDOUR_DLL_PATH not set in environment - exiting\n");
208 		::exit (EXIT_FAILURE);
209 	}
210 	return s;
211 #endif
212 }
213 
214 #ifdef PLATFORM_WINDOWS
215 Searchpath
windows_search_path()216 windows_search_path ()
217 {
218 	std::string dll_dir_path(windows_package_directory_path());
219 	dll_dir_path = Glib::build_filename (dll_dir_path, "share");
220 	return Glib::build_filename (dll_dir_path, LIBARDOUR);
221 }
222 
223 std::string
windows_package_directory_path()224 windows_package_directory_path ()
225 {
226 	char* package_dir =
227 	    g_win32_get_package_installation_directory_of_module (NULL);
228 
229 	if (package_dir == NULL) {
230 		fatal << string_compose (_("Cannot determine %1 package directory"),
231 		                           PROGRAM_NAME) << endmsg;
232 		abort(); /*NOTREACHED*/
233 	}
234 
235 	std::string package_dir_path(package_dir);
236 	g_free(package_dir);
237 	return package_dir_path;
238 }
239 #endif
240 
241 Searchpath
ardour_config_search_path()242 ardour_config_search_path ()
243 {
244 	static Searchpath search_path;
245 
246 	if (search_path.empty()) {
247 		// Start by adding the user's personal config folder
248 		search_path += user_config_directory();
249 #ifdef PLATFORM_WINDOWS
250 		// On Windows, add am intermediate configuration folder
251 		// (one that's guaranteed to be writable by all users).
252 		const gchar* const *all_users_folder = g_get_system_config_dirs();
253 		// Despite its slightly odd name, the above returns a single entry which
254 		// corresponds to 'All Users' on Windows (according to the documentation)
255 
256 		if (all_users_folder) {
257 			std::string writable_all_users_path = all_users_folder[0];
258 			writable_all_users_path += "\\";
259 			writable_all_users_path += PROGRAM_NAME;
260 			writable_all_users_path += "\\.config";
261 #ifdef _WIN64
262 			writable_all_users_path += "\\win64";
263 #else
264 			writable_all_users_path += "\\win32";
265 #endif
266 			search_path += writable_all_users_path;
267 		}
268 
269 		// now add a suitable config path from the bundle
270 		search_path += windows_search_path ();
271 #endif
272 		// finally, add any paths from ARDOUR_CONFIG_PATH if it exists
273 		std::string s = Glib::getenv("ARDOUR_CONFIG_PATH");
274 		if (s.empty()) {
275 			std::cerr << _("ARDOUR_CONFIG_PATH not set in environment\n");
276 		} else {
277 			search_path += Searchpath (s);
278 		}
279 	}
280 
281 	return search_path;
282 }
283 
284 Searchpath
ardour_data_search_path()285 ardour_data_search_path ()
286 {
287 	static Searchpath search_path;
288 
289 	if (search_path.empty()) {
290 		search_path += user_config_directory();
291 #ifdef PLATFORM_WINDOWS
292 		search_path += windows_search_path ();
293 #else
294 		std::string s = Glib::getenv("ARDOUR_DATA_PATH");
295 		if (s.empty()) {
296 			std::cerr << _("ARDOUR_DATA_PATH not set in environment\n");
297 		} else {
298 			search_path += Searchpath (s);
299 		}
300 #endif
301 	}
302 
303 	return search_path;
304 }
305 
306 string
been_here_before_path(int version)307 been_here_before_path (int version)
308 {
309 	if (version < 0) {
310 		version = atoi (PROGRAM_VERSION);
311 	}
312 
313 	return Glib::build_filename (user_config_directory (version), string (".a") + to_string (version));
314 }
315 
316 
317 } // namespace ARDOUR
318