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