]> git.lizzy.rs Git - minetest.git/blob - src/porting.cpp
945aea6eb3887085ab20dea571c40d236ad91707
[minetest.git] / src / porting.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21         Random portability stuff
22
23         See comments in porting.h
24 */
25
26 #include "porting.h"
27 #include "config.h"
28 #include "debug.h"
29 #include "filesys.h"
30 #include "log.h"
31 #include "util/string.h"
32 #include <list>
33
34 #ifdef __APPLE__
35         #include "CoreFoundation/CoreFoundation.h"
36 #endif
37
38 namespace porting
39 {
40
41 /*
42         Signal handler (grabs Ctrl-C on POSIX systems)
43 */
44
45 bool g_killed = false;
46
47 bool * signal_handler_killstatus(void)
48 {
49         return &g_killed;
50 }
51
52 #if !defined(_WIN32) // POSIX
53         #include <signal.h>
54
55 void sigint_handler(int sig)
56 {
57         if(g_killed == false)
58         {
59                 dstream<<DTIME<<"INFO: sigint_handler(): "
60                                 <<"Ctrl-C pressed, shutting down."<<std::endl;
61                 
62                 // Comment out for less clutter when testing scripts
63                 /*dstream<<DTIME<<"INFO: sigint_handler(): "
64                                 <<"Printing debug stacks"<<std::endl;
65                 debug_stacks_print();*/
66
67                 g_killed = true;
68         }
69         else
70         {
71                 (void)signal(SIGINT, SIG_DFL);
72         }
73 }
74
75 void signal_handler_init(void)
76 {
77         (void)signal(SIGINT, sigint_handler);
78 }
79
80 #else // _WIN32
81         #include <signal.h>
82         #include <windows.h>
83         
84         BOOL WINAPI event_handler(DWORD sig)
85         {
86                 switch(sig)
87                 {
88                 case CTRL_C_EVENT:
89                 case CTRL_CLOSE_EVENT:
90                 case CTRL_LOGOFF_EVENT:
91                 case CTRL_SHUTDOWN_EVENT:
92
93                         if(g_killed == false)
94                         {
95                                 dstream<<DTIME<<"INFO: event_handler(): "
96                                                 <<"Ctrl+C, Close Event, Logoff Event or Shutdown Event, shutting down."<<std::endl;
97                                 // Comment out for less clutter when testing scripts
98                                 /*dstream<<DTIME<<"INFO: event_handler(): "
99                                                 <<"Printing debug stacks"<<std::endl;
100                                 debug_stacks_print();*/
101
102                                 g_killed = true;
103                         }
104                         else
105                         {
106                                 (void)signal(SIGINT, SIG_DFL);
107                         }
108
109                         break;
110                 case CTRL_BREAK_EVENT:
111                         break;
112                 }
113                 
114                 return TRUE;
115         }
116         
117 void signal_handler_init(void)
118 {
119         SetConsoleCtrlHandler( (PHANDLER_ROUTINE)event_handler,TRUE);
120 }
121
122 #endif
123
124 /*
125         Path mangler
126 */
127
128 // Default to RUN_IN_PLACE style relative paths
129 std::string path_share = "..";
130 std::string path_user = "..";
131
132 std::string getDataPath(const char *subpath)
133 {
134         return path_share + DIR_DELIM + subpath;
135 }
136
137 void pathRemoveFile(char *path, char delim)
138 {
139         // Remove filename and path delimiter
140         int i;
141         for(i = strlen(path)-1; i>=0; i--)
142         {
143                 if(path[i] == delim)
144                         break;
145         }
146         path[i] = 0;
147 }
148
149 bool detectMSVCBuildDir(char *c_path)
150 {
151         std::string path(c_path);
152         const char *ends[] = {"bin\\Release", "bin\\Build", NULL};
153         return (removeStringEnd(path, ends) != "");
154 }
155
156 void initializePaths()
157 {
158 #if RUN_IN_PLACE
159         /*
160                 Use relative paths if RUN_IN_PLACE
161         */
162
163         infostream<<"Using relative paths (RUN_IN_PLACE)"<<std::endl;
164
165         /*
166                 Windows
167         */
168         #if defined(_WIN32)
169                 #include <windows.h>
170
171         const DWORD buflen = 1000;
172         char buf[buflen];
173         DWORD len;
174         
175         // Find path of executable and set path_share relative to it
176         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
177         assert(len < buflen);
178         pathRemoveFile(buf, '\\');
179         
180         if(detectMSVCBuildDir(buf)){
181                 infostream<<"MSVC build directory detected"<<std::endl;
182                 path_share = std::string(buf) + "\\..\\..";
183                 path_user = std::string(buf) + "\\..\\..";
184         }
185         else{
186                 path_share = std::string(buf) + "\\..";
187                 path_user = std::string(buf) + "\\..";
188         }
189
190         /*
191                 Linux
192         */
193         #elif defined(linux)
194                 #include <unistd.h>
195         
196         char buf[BUFSIZ];
197         memset(buf, 0, BUFSIZ);
198         // Get path to executable
199         assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
200         
201         pathRemoveFile(buf, '/');
202
203         path_share = std::string(buf) + "/..";
204         path_user = std::string(buf) + "/..";
205         
206         /*
207                 OS X
208         */
209         #elif defined(__APPLE__) || defined(__FreeBSD__)
210         
211         //TODO: Get path of executable. This assumes working directory is bin/
212         dstream<<"WARNING: Relative path not properly supported on OS X and FreeBSD"
213                         <<std::endl;
214         path_share = std::string("..");
215         path_user = std::string("..");
216
217         #endif
218
219 #else // RUN_IN_PLACE
220
221         /*
222                 Use platform-specific paths otherwise
223         */
224
225         infostream<<"Using system-wide paths (NOT RUN_IN_PLACE)"<<std::endl;
226
227         /*
228                 Windows
229         */
230         #if defined(_WIN32)
231                 #include <windows.h>
232
233         const DWORD buflen = 1000;
234         char buf[buflen];
235         DWORD len;
236         
237         // Find path of executable and set path_share relative to it
238         len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
239         assert(len < buflen);
240         pathRemoveFile(buf, '\\');
241         
242         // Use ".\bin\.."
243         path_share = std::string(buf) + "\\..";
244                 
245         // Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
246         len = GetEnvironmentVariable("APPDATA", buf, buflen);
247         assert(len < buflen);
248         path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
249
250         /*
251                 Linux
252         */
253         #elif defined(linux)
254                 #include <unistd.h>
255         
256         // Get path to executable
257         std::string bindir = "";
258         {
259                 char buf[BUFSIZ];
260                 memset(buf, 0, BUFSIZ);
261                 assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
262                 pathRemoveFile(buf, '/');
263                 bindir = buf;
264         }
265
266         // Find share directory from these.
267         // It is identified by containing the subdirectory "builtin".
268         std::list<std::string> trylist;
269         std::string static_sharedir = STATIC_SHAREDIR;
270         if(static_sharedir != "" && static_sharedir != ".")
271                 trylist.push_back(static_sharedir);
272         trylist.push_back(bindir + "/../share/" + PROJECT_NAME);
273         trylist.push_back(bindir + "/..");
274         
275         for(std::list<std::string>::const_iterator i = trylist.begin();
276                         i != trylist.end(); i++)
277         {
278                 const std::string &trypath = *i;
279                 if(!fs::PathExists(trypath) || !fs::PathExists(trypath + "/builtin")){
280                         dstream<<"WARNING: system-wide share not found at \""
281                                         <<trypath<<"\""<<std::endl;
282                         continue;
283                 }
284                 // Warn if was not the first alternative
285                 if(i != trylist.begin()){
286                         dstream<<"WARNING: system-wide share found at \""
287                                         <<trypath<<"\""<<std::endl;
288                 }
289                 path_share = trypath;
290                 break;
291         }
292         
293         path_user = std::string(getenv("HOME")) + "/." + PROJECT_NAME;
294
295         /*
296                 OS X
297         */
298         #elif defined(__APPLE__)
299                 #include <unistd.h>
300
301     // Code based on
302     // http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
303     CFBundleRef main_bundle = CFBundleGetMainBundle();
304     CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
305     char path[PATH_MAX];
306     if(CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX))
307         {
308                 dstream<<"Bundle resource path: "<<path<<std::endl;
309                 //chdir(path);
310                 path_share = std::string(path) + "/share";
311         }
312         else
313     {
314         // error!
315                 dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
316     }
317     CFRelease(resources_url);
318         
319         path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
320
321         #elif defined(__FreeBSD__)
322
323         path_share = STATIC_SHAREDIR;
324         path_user = std::string(getenv("HOME")) + "/." + PROJECT_NAME;
325     
326         #endif
327
328 #endif // RUN_IN_PLACE
329 }
330
331 } //namespace porting
332