1 /*
2 Hatari - paths.c
3
4 This file is distributed under the GNU General Public License, version 2
5 or at your option any later version. Read the file gpl.txt for details.
6
7 Set up the various path strings.
8 */
9 const char Paths_fileid[] = "Hatari paths.c : " __DATE__ " " __TIME__;
10
11 #include <unistd.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14
15 #include "main.h"
16 #include "file.h"
17 #include "paths.h"
18 #include "str.h"
19
20 #if defined(WIN32) && !defined(mkdir)
21 #define mkdir(name,mode) mkdir(name)
22 #endif /* WIN32 */
23
24 #if defined(__MACOSX__)
25 #define HATARI_HOME_DIR "Library/Application Support/Hatari"
26 #elif defined(WIN32)
27 #define HATARI_HOME_DIR "AppData\\Local\\Hatari"
28 #else
29 #define HATARI_HOME_DIR ".config/hatari"
30 #endif
31
32 static char *sWorkingDir; /* Working directory */
33 static char *sDataDir; /* Directory where data files of Hatari can be found */
34 static char *sUserHomeDir; /* User's home directory ($HOME) */
35 static char *sHatariHomeDir; /* Hatari's home directory ($HOME/.hatari/) */
36 static char *sScreenShotDir; /* Directory to use for screenshots */
37
38 /**
39 * Return pointer to current working directory string
40 */
Paths_GetWorkingDir(void)41 const char *Paths_GetWorkingDir(void)
42 {
43 return sWorkingDir;
44 }
45
46 /**
47 * Return pointer to data directory string
48 */
Paths_GetDataDir(void)49 const char *Paths_GetDataDir(void)
50 {
51 return sDataDir;
52 }
53
54 /**
55 * Return pointer to user's home directory string
56 */
Paths_GetUserHome(void)57 const char *Paths_GetUserHome(void)
58 {
59 return sUserHomeDir;
60 }
61
62 /**
63 * Return pointer to Hatari's home directory string
64 */
Paths_GetHatariHome(void)65 const char *Paths_GetHatariHome(void)
66 {
67 return sHatariHomeDir;
68 }
69
70 /**
71 * Return pointer to screenshot directory string
72 */
Paths_GetScreenShotDir(void)73 const char *Paths_GetScreenShotDir(void)
74 {
75 return sScreenShotDir;
76 }
77
78
79 /**
80 * Explore the PATH environment variable to see where our executable is
81 * installed.
82 */
Paths_GetExecDirFromPATH(const char * argv0,char * pExecDir,int nMaxLen)83 static void Paths_GetExecDirFromPATH(const char *argv0, char *pExecDir, int nMaxLen)
84 {
85 char *pPathEnv;
86 char *pAct;
87 char *pTmpName;
88 const char *pToken;
89
90 /* Get the PATH environment string */
91 pPathEnv = getenv("PATH");
92 if (!pPathEnv)
93 return;
94 /* Duplicate the string because strtok destroys it later */
95 pPathEnv = strdup(pPathEnv);
96 if (!pPathEnv)
97 return;
98
99 pTmpName = malloc(FILENAME_MAX);
100 if (!pTmpName)
101 {
102 perror("Paths_GetExecDirFromPATH");
103 free(pPathEnv);
104 return;
105 }
106
107 /* If there is a semicolon in the PATH, we assume it is the PATH
108 * separator token (like on Windows), otherwise we use a colon. */
109 if (strchr((pPathEnv), ';'))
110 pToken = ";";
111 else
112 pToken = ":";
113
114 pAct = strtok (pPathEnv, pToken);
115 while (pAct)
116 {
117 snprintf(pTmpName, FILENAME_MAX, "%s%c%s",
118 pAct, PATHSEP, argv0);
119 if (File_Exists(pTmpName))
120 {
121 /* Found the executable - so use the corresponding path: */
122 strlcpy(pExecDir, pAct, nMaxLen);
123 break;
124 }
125 pAct = strtok (NULL, pToken);
126 }
127
128 free(pPathEnv);
129 free(pTmpName);
130 }
131
132
133 /**
134 * Locate the directory where the hatari executable resides
135 */
Paths_InitExecDir(const char * argv0)136 static char *Paths_InitExecDir(const char *argv0)
137 {
138 char *psExecDir; /* Path string where the hatari executable can be found */
139
140 /* Allocate memory for storing the path string of the executable */
141 psExecDir = malloc(FILENAME_MAX);
142 if (!psExecDir)
143 {
144 fprintf(stderr, "Out of memory (Paths_Init)\n");
145 exit(-1);
146 }
147
148 /* Determine the bindir...
149 * Start with empty string, then try to use OS specific functions,
150 * and finally analyze the PATH variable if it has not been found yet. */
151 psExecDir[0] = '\0';
152
153 #if defined(__linux__)
154 {
155 int i;
156 /* On Linux, we can analyze the symlink /proc/self/exe */
157 i = readlink("/proc/self/exe", psExecDir, FILENAME_MAX-1);
158 if (i > 0)
159 {
160 char *p;
161 psExecDir[i] = '\0';
162 p = strrchr(psExecDir, '/'); /* Search last slash */
163 if (p)
164 *p = 0; /* Strip file name from path */
165 }
166 }
167 //#elif defined(WIN32) || defined(__CEGCC__)
168 // /* On Windows we can use GetModuleFileName for getting the exe path */
169 // GetModuleFileName(NULL, psExecDir, FILENAME_MAX);
170 #endif
171
172 /* If we do not have the execdir yet, analyze argv[0] and the PATH: */
173 if (psExecDir[0] == 0)
174 {
175 if (strchr(argv0, PATHSEP) == NULL)
176 {
177 /* No separator in argv[0], we have to explore PATH... */
178 Paths_GetExecDirFromPATH(argv0, psExecDir, FILENAME_MAX);
179 }
180 else
181 {
182 /* There was a path separator in argv[0], so let's assume a
183 * relative or absolute path to the current directory in argv[0] */
184 char *p;
185 strlcpy(psExecDir, argv0, FILENAME_MAX);
186 p = strrchr(psExecDir, PATHSEP); /* Search last slash */
187 if (p)
188 *p = 0; /* Strip file name from path */
189 }
190 }
191
192 return psExecDir;
193 }
194
195
196 /**
197 * Initialize the users home directory string
198 * and Hatari's home directory (~/.hatari)
199 */
Paths_InitHomeDirs(void)200 static void Paths_InitHomeDirs(void)
201 {
202 char *psHome;
203
204 psHome = getenv("HOME");
205 if (psHome)
206 {
207 sUserHomeDir = Str_Dup(psHome);
208 }
209 #if defined(WIN32)
210 else
211 {
212 char *psDrive;
213 int len = 0;
214 /* Windows home path? */
215 psDrive = getenv("HOMEDRIVE");
216 if (psDrive)
217 len = strlen(psDrive);
218 psHome = getenv("HOMEPATH");
219 if (psHome)
220 len += strlen(psHome);
221 if (len > 0)
222 {
223 sUserHomeDir = Str_Alloc(len);
224 if (psDrive)
225 strcpy(sUserHomeDir, psDrive);
226 if (psHome)
227 strcat(sUserHomeDir, psHome);
228 }
229 }
230 #endif
231 if (!sUserHomeDir)
232 {
233 /* $HOME not set, so let's use current working dir as home */
234 sUserHomeDir = Str_Dup(sWorkingDir);
235 sHatariHomeDir = Str_Dup(sWorkingDir);
236 return;
237 }
238
239 sHatariHomeDir = Str_Alloc(strlen(sUserHomeDir) + 1 + strlen(HATARI_HOME_DIR));
240
241 /* Try to use a private hatari directory in the users home directory */
242 sprintf(sHatariHomeDir, "%s%c%s", sUserHomeDir, PATHSEP, HATARI_HOME_DIR);
243 if (File_DirExists(sHatariHomeDir))
244 {
245 return;
246 }
247 /* Try legacy location ~/.hatari */
248 sprintf(sHatariHomeDir, "%s%c.hatari", sUserHomeDir, PATHSEP);
249 if (File_DirExists(sHatariHomeDir))
250 {
251 return;
252 }
253
254 /* Hatari home directory does not exists yet...
255 * ... so let's try to create it: */
256 #if !defined(__MACOSX__) && !defined(WIN32)
257 sprintf(sHatariHomeDir, "%s%c.config", sUserHomeDir, PATHSEP);
258 if (!File_DirExists(sHatariHomeDir))
259 {
260 /* ~/.config does not exist yet, create it first */
261 if (mkdir(sHatariHomeDir, 0700) != 0)
262 {
263 perror("Failed to create ~/.config directory");
264 }
265 }
266 #endif
267 sprintf(sHatariHomeDir, "%s%c%s", sUserHomeDir, PATHSEP, HATARI_HOME_DIR);
268 if (mkdir(sHatariHomeDir, 0750) != 0)
269 {
270 /* Failed to create, so use user's home dir instead */
271 strcpy(sHatariHomeDir, sUserHomeDir);
272 }
273 }
274
275
276 /**
277 * Initialize directory names
278 *
279 * The datadir will be initialized relative to the bindir (where the executable
280 * has been installed to). This means a lot of additional effort since we first
281 * have to find out where the executable is. But thanks to this effort, we get
282 * a relocatable package (we don't have any absolute path names in the program)!
283 */
Paths_Init(const char * argv0)284 void Paths_Init(const char *argv0)
285 {
286 char *psExecDir; /* Path string where the hatari executable can be found */
287
288 /* Init working directory string */
289 sWorkingDir = malloc(FILENAME_MAX);
290 if (!sWorkingDir || getcwd(sWorkingDir, FILENAME_MAX) == NULL)
291 {
292 /* This should never happen... just in case... */
293 sWorkingDir = Str_Dup(".");
294 }
295
296 /* Init the user's home directory string */
297 Paths_InitHomeDirs();
298
299 /* Init screenshot directory string */
300 #if !defined(__MACOSX__)
301 sScreenShotDir = Str_Dup(sWorkingDir);
302 #else
303 sScreenShotDir = Paths_GetMacScreenShotDir();
304 if (!sScreenShotDir)
305 {
306 /* Failsafe, but should not be able to happen */
307 sScreenShotDir = Str_Dup(sWorkingDir);
308 }
309 #endif
310
311 /* Get the directory where the executable resides */
312 psExecDir = Paths_InitExecDir(argv0);
313
314 /* Now create the datadir path name from the bindir path name: */
315 sDataDir = Str_Alloc(FILENAME_MAX);
316 if (psExecDir && strlen(psExecDir) > 0)
317 {
318 sprintf(sDataDir, "%s%c%s", psExecDir, PATHSEP, BIN2DATADIR);
319 }
320 else
321 {
322 /* bindir could not be determined, let's assume datadir is relative
323 * to current working directory... */
324 strcpy(sDataDir, BIN2DATADIR);
325 }
326
327 /* And finally make a proper absolute path out of datadir: */
328 File_MakeAbsoluteName(sDataDir);
329
330 free(psExecDir);
331
332 /* fprintf(stderr, " WorkingDir = %s\n DataDir = %s\n UserHomeDir = %s\n HatariHomeDir = %s\n ScrenShotDir = %s\n",
333 sWorkingDir, sDataDir, sUserHomeDir, sHatariHomeDir, sScreenShotDir); */
334 }
335
Paths_UnInit(void)336 void Paths_UnInit(void)
337 {
338 Str_Free(sWorkingDir);
339 Str_Free(sDataDir);
340 Str_Free(sUserHomeDir);
341 Str_Free(sHatariHomeDir);
342 Str_Free(sScreenShotDir);
343 }
344