1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 #define _GNU_SOURCE 1
6 #include <fs/filesys.h>
7 #ifdef WINDOWS
8 //#define WIN32_LEAN_AND_MEAN
9 #include <windows.h>
10 #include <Shlobj.h>
11 #else
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <dirent.h>
18 #include <errno.h>
19 #endif
20 #ifdef MACOSX
21 #include <mach-o/dyld.h>
22 #include <copyfile.h>
23 #endif
24 #include <stdio.h>
25 // #include <fs/log.h>
26 #include <fs/base.h>
27 #include <fs/data.h>
28 #ifdef HAVE_SYS_TIME_H
29 #include <sys/time.h>
30 #endif
31 #ifdef USE_GLIB
32 // FIXME g_atomic_int_get / g_atomic_int_set
33 #include <glib.h>
34 #endif
35 
fse_user_config_dir(void)36 const char *fse_user_config_dir(void)
37 {
38 #ifdef USE_GLIB
39 #ifdef MACOSX
40     static char *path = NULL;
41     if (path == NULL) {
42         path = g_build_filename(g_get_home_dir(), "Library", "Preferences",
43                 NULL);
44     }
45     return path;
46 #else
47     return g_get_user_config_dir();
48 #endif
49 #else
50 #error not implemented
51 #endif
52 }
53 
fse_user_data_dir(void)54 const char *fse_user_data_dir(void)
55 {
56 #ifdef USE_GLIB
57     return g_get_user_data_dir();
58 #else
59 #error not implemented
60 #endif
61 }
62 
fs_get_data_file(const char * relative)63 char *fs_get_data_file(const char *relative)
64 {
65     static int initialized = 0;
66     static char executable_dir[PATH_MAX];
67     if (!initialized) {
68         fs_get_application_exe_dir(executable_dir, PATH_MAX);
69         initialized = 1;
70     }
71     char *path;
72     path = g_build_filename(executable_dir, "share", relative, NULL);
73     if (fs_path_exists(path)) {
74         return path;
75     }
76     g_free(path);
77     path = g_build_filename(executable_dir, "..", "share", relative, NULL);
78     if (fs_path_exists(path)) {
79         return path;
80     }
81     g_free(path);
82 
83 #ifdef MACOSX
84     char buffer[FS_PATH_MAX];
85     fs_get_application_exe_dir(buffer, FS_PATH_MAX);
86     path = g_build_filename(buffer, "..", "Resources", relative, NULL);
87     if (fs_path_exists(path)) {
88         return path;
89     }
90     g_free(path);
91 #endif
92 
93 #ifdef USE_GLIB
94     const char *user_dir = g_get_user_data_dir();
95     path = g_build_filename(user_dir, relative, NULL);
96     if (fs_path_exists(path)) {
97         return path;
98     }
99     g_free(path);
100 
101     const char * const *dirs = g_get_system_data_dirs();
102     while(*dirs) {
103         path = g_build_filename(*dirs, relative, NULL);
104         if (fs_path_exists(path)) {
105             return path;
106         }
107         g_free(path);
108         dirs++;
109     }
110 #endif
111 
112     return NULL;
113 }
114 
115 static const char *g_data_dir;
116 
fs_set_data_dir(const char * path)117 void fs_set_data_dir(const char *path)
118 {
119     g_data_dir = path;
120 }
121 
fs_data_dir(void)122 const char *fs_data_dir(void)
123 {
124     g_assert(g_data_dir != NULL);
125     return g_data_dir;
126 }
127 
fs_get_program_data_file(const char * relative)128 char *fs_get_program_data_file(const char *relative)
129 {
130     char *relative2 = g_build_filename(fs_get_prgname(), relative, NULL);
131     char *result = fs_get_data_file(relative2);
132     g_free(relative2);
133     return result;
134 }
135 
fs_get_program_data(const char * relative,char ** data,int * size)136 int fs_get_program_data(const char *relative, char **data, int *size)
137 {
138     char *name = g_build_filename("share", fs_get_prgname(), relative, NULL);
139     int error = fs_data_file_content(name, data, size);
140     g_free(name);
141     return error;
142 }
143 
fs_get_current_time(fs_time_val * result)144 void fs_get_current_time(fs_time_val *result)
145 {
146     if (result == NULL) {
147         return;
148     }
149 #ifdef USE_GLIB
150     GTimeVal result2;
151     g_get_current_time(&result2);
152     result->tv_sec = result2.tv_sec;
153     result->tv_usec = result2.tv_usec;
154 #else
155 #error not implemented
156 #endif
157 }
158 
fs_get_real_time(void)159 int64_t fs_get_real_time(void)
160 {
161     fs_time_val tv;
162     fs_get_current_time(&tv);
163     return (((int64_t) tv.tv_sec) * 1000000) + tv.tv_usec;
164 }
165 
fs_get_monotonic_time(void)166 int64_t fs_get_monotonic_time(void)
167 {
168 #ifdef USE_GLIB
169     return g_get_monotonic_time();
170 #else
171 #error not implemented
172 #endif
173 }
174 
175 static char** g_argv = NULL;
176 static int g_argc = 0;
177 
fs_set_argv(int argc,char * argv[])178 void fs_set_argv(int argc, char* argv[])
179 {
180     g_argc = argc;
181     g_argv = argv;
182 }
183 
find_program_in_path(const char * prog)184 static char *find_program_in_path(const char *prog)
185 {
186 #ifdef USE_GLIB
187     return g_find_program_in_path(prog);
188 #else
189     const char* c = prog;
190     while(*c) {
191         if (*c++ == '/') {
192             /* path contains / - not started via PATH, it's either
193              * already an absolute path - or a relative path. */
194             return g_strdup(prog);
195         }
196     }
197 
198     const char* env_path = getenv("PATH");
199     if (env_path == NULL) {
200         return NULL;
201     }
202     char *result = NULL;
203     char **dirs = g_strsplit(env_path, ":", 0);
204     char **dir = dirs;
205     while(*dir) {
206         if (*dir) {
207             char *abs = g_build_filename(*dir, prog, NULL);
208             if (fs_path_is_file(abs)) {
209                 result = abs;
210                 break;
211             }
212             g_free(abs);
213         }
214         dir++;
215     }
216     g_strfreev(dirs);
217     return result;
218 #endif
219 }
220 
fs_get_application_exe_path(char * buffer,int size)221 int fs_get_application_exe_path(char *buffer, int size)
222 {
223     /* Possible sources:
224      * Mac OS X: _NSGetExecutablePath() (man 3 dyld)
225      * Linux: readlink /proc/self/exe
226      * Solaris: getexecname()
227      * FreeBSD: sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
228      * BSD with procfs: readlink /proc/curproc/file
229      * Windows: GetModuleFileName() with hModule = NULL
230      * or guess using argv[0] and PATH
231      */
232     if (size < 1) {
233         return 0;
234     }
235     buffer[0] = '\0';
236 
237 #if defined(WINDOWS)
238 
239     wchar_t * temp_buf = g_malloc(sizeof(wchar_t) * PATH_MAX);
240     /* len is the number of characters NOT including the terminating null
241      * character. */
242     int len = GetModuleFileNameW(NULL, temp_buf, PATH_MAX);
243     /* Specify size - 1 to reserve space for a null-terminating byte. */
244     int result = WideCharToMultiByte(CP_UTF8, 0, temp_buf, len,
245             buffer, size - 1, NULL, NULL);
246     g_free(temp_buf);
247     if (result == 0) {
248         return 0;
249     }
250     /* Since len does not include the null-terminator, WideCharToMultiByte
251      * does not null-terminate the buffer. */
252     buffer[result] = '\0';
253     return 1;
254 
255 #elif defined(MACOSX)
256 
257     unsigned int usize = size;
258     int result = _NSGetExecutablePath(buffer, &usize);
259     if (result == 0) {
260         return 1;
261     } else {
262         fs_log("_NSGetExecutablePath failed with result %d\n", result);
263         buffer[0] = '\0';
264         return 0;
265     }
266 
267 #else
268 
269     if (g_argc == 0 || g_argv == NULL) {
270         buffer[0] = '\0';
271         return 0;
272     }
273 
274     char* result;
275     result = find_program_in_path(g_argv[0]);
276     if (result == NULL) {
277         buffer[0] = '\0';
278         return 0;
279     }
280 
281     if (result[0] != '/') {
282         char* old_result = result;
283         char* current_dir = g_get_current_dir();
284         result = g_build_filename(current_dir, old_result, NULL);
285         g_free(old_result);
286         g_free(current_dir);
287     }
288 
289     if (strlen(result) > size - 1) {
290         buffer[0] = '\0';
291         g_free(result);
292         return 0;
293     }
294 
295     /* We have already checked that the buffer is big enough */
296     strcpy(buffer, result);
297     g_free(result);
298     return 1;
299 
300 #endif
301 }
302 
fs_get_application_exe_dir(char * buffer,int size)303 int fs_get_application_exe_dir(char *buffer, int size)
304 {
305     int result = fs_get_application_exe_path(buffer, size);
306     if (result == 0) {
307         return 0;
308     }
309     int pos = strlen(buffer) - 1;
310     while (pos >= 0) {
311         if (buffer[pos] == '\\' || buffer[pos] == '/') {
312             buffer[pos] = '\0';
313             return 1;
314         }
315         pos -= 1;
316     }
317     return 0;
318 }
319 
320 #ifndef USE_GLIB
321 static char *g_prgname = "unnamed-program";
322 static char *g_application_name = "Unnamed Program";
323 #endif
324 
fs_get_application_name(void)325 const char *fs_get_application_name(void)
326 {
327 #ifdef USE_GLIB
328     return g_get_application_name();
329 #else
330     return g_application_name;
331 #endif
332 }
333 
fs_set_application_name(const char * application_name)334 void fs_set_application_name(const char *application_name)
335 {
336 #ifdef USE_GLIB
337     g_set_application_name(application_name);
338 #else
339     g_application_name = fs_strdup(application_name);
340 #endif
341 }
342 
fs_get_prgname(void)343 const char *fs_get_prgname(void)
344 {
345 #ifdef USE_GLIB
346     return g_get_prgname();
347 #else
348     return g_prgname;
349 #endif
350 }
351 
fs_set_prgname(const char * prgname)352 void fs_set_prgname(const char *prgname)
353 {
354 #ifdef USE_GLIB
355     g_set_prgname(prgname);
356 #else
357     g_prgname = fs_strdup(prgname);
358 #endif
359 }
360