1 /***************************************************************************
2                           usmisc.cpp  -  Misc utility functions
3                              -------------------
4     begin                : wo feb 2 2005
5     copyright            : (C) 2005 by CJP
6     email                : cornware-cjp@users.sourceforge.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 
22 #include <cstdio>
23 #include <cstdlib>
24 
25 #include "config.h"
26 
27 //internationalisation:
28 #include <locale.h>
29 #include <libintl.h>
30 #define _(String) gettext (String)
31 
32 #include "usmisc.h"
33 #include "usmacros.h"
34 
35 #include "cstring.h"
36 #include "lconfig.h"
37 #include "filecontrol.h"
38 #include "cfile.h"
39 
40 #ifdef __APPLE__
41 // dynamic data path location on OS X
42 
43 #include <CoreFoundation/CoreFoundation.h>
44 
setPathIfInBundle(CString & returnpath)45 bool setPathIfInBundle(CString& returnpath)
46 {
47 	char path[1024];
48 
49 	CFBundleRef mainBundle = CFBundleGetMainBundle();
50 	assert(mainBundle);
51 
52 	CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
53 	assert(mainBundleURL);
54 
55 	CFStringRef cfStringRef =
56 		CFURLCopyFileSystemPath(mainBundleURL, kCFURLPOSIXPathStyle);
57 	assert(cfStringRef);
58 
59 	CFStringGetCString(cfStringRef, path, 1024, kCFStringEncodingASCII);
60 
61 	CFRelease(mainBundleURL);
62 	CFRelease(cfStringRef);
63 
64 	CString contents = CString(path) + "/Contents/Resources";
65 	if(contents.find(".app") != std::string::npos)
66 	{
67 		// executable is inside an app bundle, use app bundle-relative paths
68 		returnpath = contents;
69 		return true;
70 	}
71 	else
72 	{
73 		// executable is installed Unix-style, use default paths
74 		return false;
75 	}
76 }
77 #endif
78 
79 #ifdef __CYGWIN__
80 
getSystemLocale()81 CString getSystemLocale()
82 {
83 	CString ret;
84 
85 	const char *cmd1 = "locale";
86 	const char *cmd2 = "-u";
87 
88 	//Create a pipe
89 	int pipe_fds[2]; //[0] = reading [1] = writing
90 	if(pipe(pipe_fds) < 0)
91 		return ""; //failed
92 	int out = pipe_fds[1];
93 	int in  = pipe_fds[0];
94 
95 	//Fork this process
96 	pid_t PID = fork(); //is vfork allowed?
97 	if(PID == 0)
98 	{
99 		//we are the child process
100 
101 		close(in); //we don't need this side in this process
102 
103 		//set the output to the pipe
104 		if(out != 1)
105 		{
106 			dup2(out, 1);
107 			close(out);
108 		}
109 
110 		//Jump to the server binary
111 		execlp(cmd1, cmd1, cmd2, NULL);
112 
113 		printf("Error: locale program could not be started\n");
114 		fflush(stdout);
115 
116 		_exit(1); //failed to start the server process
117 	}
118 
119 	//we are in the original process
120 
121 	close(out); //we don't need this side in this process
122 
123 	if(PID < 0) //fork failed
124 	{
125 		close(in);
126 		return "";
127 	}
128 
129 	//Read from the pipe
130 	char c;
131 	while(read(in, &c, 1) == 1 && c != '\n')
132 		ret += c;
133 
134 	close(in);
135 
136 	//wait until the process stops
137 	int wstatus;
138 	int wait_pid = -1;
139 	do
140 	{
141 		wait_pid = waitpid(PID, &wstatus, 0);
142 	}
143 	while(wait_pid == -1 && errno == EINTR);
144 
145 	return ret;
146 }
147 
148 #endif
149 
copyConfiguration(CString & conffile)150 bool copyConfiguration(CString &conffile)
151 {
152 	printf("File %s not found. Searching on alternative locations...\n", conffile.c_str());
153 
154 	CString sysconfdir = SYSCONFDIR;
155 	printf("sysconfdir = %s\n", sysconfdir.c_str());
156 
157 	std::vector<CString> locations;
158 	locations.push_back(sysconfdir + "/ultimatestunts.conf");
159 	locations.push_back("/etc/ultimatestunts.conf");
160 	locations.push_back("/usr/local/etc/ultimatestunts.conf");
161 	locations.push_back("./ultimatestunts.conf");
162 
163 #if defined(__APPLE__)
164 	// find config path dynamically
165 	CString osx_path;
166 	if(setPathIfInBundle(osx_path))
167 		locations.push_back(osx_path + "/etc/ultimatestunts.conf");
168 #endif
169 
170 	CString sourceConffile;
171 	for(unsigned int i=0; i < locations.size(); i++)
172 	{
173 		CString &loc = locations[i];
174 
175 		printf("Trying %s...", loc.c_str());
176 		if(fileExists(loc))
177 		{
178 			printf("found\n");
179 			sourceConffile = loc;
180 			break;
181 		}
182 		else
183 		{
184 			printf("not found\n");
185 		}
186 	}
187 
188 	if(sourceConffile == "")
189 	{
190 		printf("WARNING: no configuration file found!\n");
191 		return false;
192 	}
193 
194 	printf("Copying %s to %s\n", sourceConffile.c_str(), conffile.c_str());
195 	if(!copyFile(sourceConffile, conffile))
196 	{
197 		printf("  copying FAILED! (maybe we don't have the right permissions)\n");
198 		return false;
199 	}
200 
201 	return true;
202 }
203 
204 //exceptions for the development environment:
inDevelopmentTree()205 bool inDevelopmentTree()
206 {
207 	return fileExists("./execselect.sh");
208 }
209 
210 CFileControl *fctl = NULL;
211 CString originalLANGUAGEenv;
212 CString originalSystemLocale;
213 
shared_main(int argc,char * argv[])214 void shared_main(int argc, char *argv[])
215 {
216 	CString conffile;
217 
218 	//First process some commandline arguments
219 	for(int i=0; i < argc; i++)
220 	{
221 		CString arg = argv[i];
222 
223 		if(arg == "--help")
224 		{
225 			printf(
226 			"Usage: %s [--help] [--conf=conffile] [SECTION:OPTION=VALUE]...\n"
227 			"Starts the Ultimate Stunts program %s\n"
228 			"\n"
229 			"  --help                Show this help and exit\n"
230 			"  --conf=conffile       Load settings from conffile\n"
231 			"  SECTION:OPTION=VALUE  Override a conffile setting (NYI!)\n"
232 			"\n",
233 			argv[0], argv[0]
234 			);
235 			exit(EXIT_SUCCESS);
236 		}
237 
238 		if(arg.mid(0, 7) == "--conf=")
239 		{
240 			conffile = arg.mid(7);
241 		}
242 	}
243 
244 
245 #ifdef UNIX_TREE
246 	CString homedir = getenv("HOME");
247 #endif
248 
249 	{
250 		char *env = getenv("LANGUAGE");
251 		if(env != NULL)
252 			originalLANGUAGEenv = env;
253 	}
254 #ifdef __CYGWIN__
255 	originalSystemLocale = getSystemLocale();
256 #endif
257 
258 	//Find the conf file location, if not given on commandline
259 	if(conffile=="")
260 	{
261 		//the default directory of the configuration file
262 		CString confdir;
263 #ifdef UNIX_TREE
264 		confdir = homedir + "/.ultimatestunts/";
265 #else
266 		confdir = "./";
267 #endif
268 
269 		if(inDevelopmentTree())
270 		{
271 			printf(
272 				"execselect.sh detected:\n"
273 				"  We are probably in the Ultimate Stunts SOURCE TREE\n"
274 				"EXECUTED FROM THE SOURCE TREE:\n"
275 				"  Using the conf file in the source tree\n"
276 				);
277 			confdir = "./";
278 		}
279 
280 		//make sure that the directory exists
281 		makeDir(confdir);
282 
283 		//make sure that the conf file exists
284 		conffile = confdir + "ultimatestunts.conf";
285 		if(!fileExists(conffile))
286 			copyConfiguration(conffile);
287 	}
288 
289 	printf("Using configuration file %s\n", conffile.c_str());
290 
291 	theMainConfig = new CLConfig(argc, argv);
292 	if(!theMainConfig->setFilename(conffile))
293 	{
294 		printf("Error: could not read configuration file\n");
295 		//TODO: create a default one
296 	}
297 
298 	update_shared_configuration();
299 }
300 
update_shared_configuration()301 void update_shared_configuration()
302 {
303 	//The data dir and the save dir
304 	CString DataDir, SaveDir;
305 
306 	CString cnf = theMainConfig->getValue("files", "datadir");
307 	if(cnf != "")
308 	{
309 		if(cnf[cnf.length()-1] != '/') cnf += '/';
310 		DataDir = cnf;
311 	}
312 
313 	cnf = theMainConfig->getValue("files", "savedir");
314 	if(cnf != "")
315 	{
316 		if(cnf[cnf.length()-1] != '/') cnf += '/';
317 		SaveDir = cnf;
318 	}
319 
320 #ifdef UNIX_TREE
321 	//fill in home dir for "~/"
322 	CString homedir = getenv("HOME");
323 	if(DataDir.mid(0, 2) == "~/")
324 		DataDir = homedir + DataDir.mid(1);
325 	if(SaveDir.mid(0, 2) == "~/")
326 		SaveDir = homedir + SaveDir.mid(1);
327 #endif
328 
329 	if(inDevelopmentTree())
330 	{
331 		printf("EXECUTED FROM THE SOURCE TREE:\n  Using the data dirs in the source tree\n");
332 		DataDir = "./data/";
333 		SaveDir = "./saveddata/";
334 	}
335 
336 #ifdef __APPLE__
337 
338 	if(DataDir == "@appbundle/")
339 	{
340 		// find data path dynamically
341 		CString osx_path;
342 		if(setPathIfInBundle(osx_path))
343 		{
344 			DataDir = osx_path + "/data/";
345 		}
346 	}
347 
348 #endif
349 
350 	printf("DataDir is \"%s\"\n", DataDir.c_str());
351 	printf("SaveDir is \"%s\"\n", SaveDir.c_str());
352 	if(fctl == NULL) fctl = new CFileControl; //TODO: find a way to delete this object
353 	fctl->setDataDir(DataDir);
354 	fctl->setSaveDir(SaveDir);
355 
356 	printf("Enabling localisation\n");
357 
358 	//select a language
359 	CString conf_lang = theMainConfig->getValue("user_interface", "language");
360 	if(conf_lang == "system")
361 	{
362 
363 #ifdef __CYGWIN__
364 		printf("System locale: %s\n", originalSystemLocale.c_str());
365 		char *retval = setlocale(LC_MESSAGES, originalSystemLocale.c_str());
366 #else
367 		char *retval = setlocale(LC_MESSAGES, "");
368 #endif
369 
370 		if(retval == NULL)
371 		{
372 			printf("Setting the system LC_MESSAGES locale failed\n");
373 		}
374 		else
375 		{
376 			printf("Locale LC_MESSAGES is set to \"%s\"\n", retval);
377 		}
378 
379 #ifdef __CYGWIN__
380 		//F* Cygwin doesn't set the locale correctly
381 		setenv("LC_MESSAGES", originalSystemLocale.c_str(), 1);
382 #endif
383 
384 		setenv("LANGUAGE", originalLANGUAGEenv.c_str(), 1);
385 	}
386 	else
387 	{
388 		char *retval = setlocale(LC_MESSAGES, conf_lang.c_str());
389 		if(retval == NULL)
390 		{
391 			printf("Setting the LC_MESSAGES locale to \"%s\" failed\n", conf_lang.c_str());
392 		}
393 		else
394 		{
395 			printf("Locale LC_MESSAGES is set to \"%s\"\n", retval);
396 		}
397 
398 #ifdef __CYGWIN__
399 		//F* Cygwin doesn't set the locale correctly
400 		setenv("LC_MESSAGES", conf_lang.c_str(), 1);
401 #endif
402 
403 		setenv("LANGUAGE", conf_lang.c_str(), 1);
404 	}
405 
406 	//find the absolute path
407 	//very long paths might cause segfaults
408 	CString absdir = getAbsDir(DataDir);
409 
410 	//select the Ultimate Stunts domain
411 	//We can only use ISO 8859-1 because of the font texture
412 	printf("  Package %s, directory %s\n", PACKAGE, (absdir + "lang").c_str());
413 	printf("  bindtextdomain returns %s\n",
414 	    bindtextdomain(PACKAGE, (absdir + "lang").c_str())
415 	    );
416 	printf("  bind_textdomain_codeset returns %s\n",
417 	    bind_textdomain_codeset(PACKAGE, "ISO-8859-1")
418 	    );
419 	printf("  textdomain returns %s\n",
420 	    textdomain(PACKAGE)
421 	    );
422 }
423 
getCredits()424 vector<CString> getCredits()
425 {
426 #include "credits.h"
427 }
428