1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus-core - osal/files_unix.c * |
3 | * Mupen64Plus homepage: https://mupen64plus.org/ * |
4 | * Copyright (C) 2009 Richard Goedeken * |
5 | * * |
6 | * This program is free software; you can redistribute it and/or modify * |
7 | * it under the terms of the GNU General Public License as published by * |
8 | * the Free Software Foundation; either version 2 of the License, or * |
9 | * (at your option) any later version. * |
10 | * * |
11 | * This program is distributed in the hope that it will be useful, * |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
14 | * GNU General Public License for more details. * |
15 | * * |
16 | * You should have received a copy of the GNU General Public License * |
17 | * along with this program; if not, write to the * |
18 | * Free Software Foundation, Inc., * |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
20 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
21 | |
22 | /* This file contains the definitions for the unix-specific file handling |
23 | * functions |
24 | */ |
25 | |
26 | #include <limits.h> |
27 | #include <stdio.h> |
28 | #include <stdlib.h> |
29 | #include <string.h> |
30 | #include <sys/stat.h> |
31 | #include <sys/types.h> |
32 | #include <unistd.h> |
33 | |
34 | #include "api/callbacks.h" |
35 | #include "api/m64p_types.h" |
36 | #include "files.h" |
37 | |
38 | /* definitions for system directories to search when looking for shared data files */ |
39 | #if defined(SHAREDIR) |
40 | #define XSTR(S) STR(S) /* this wacky preprocessor thing is necessary to generate a quote-enclosed */ |
41 | #define STR(S) #S /* copy of the SHAREDIR macro, which is defined by the makefile via gcc -DSHAREDIR="..." */ |
42 | static const int datasearchdirs = 4; |
43 | static const char *datasearchpath[4] = { XSTR(SHAREDIR), "/usr/local/share/mupen64plus" , "/usr/share/mupen64plus" , "./" }; |
44 | #undef STR |
45 | #undef XSTR |
46 | #else |
47 | static const int datasearchdirs = 3; |
48 | static const char *datasearchpath[3] = { "/usr/local/share/mupen64plus" , "/usr/share/mupen64plus" , "./" }; |
49 | #endif |
50 | |
51 | /* local functions */ |
52 | |
53 | static int get_xdg_dir(char *destpath, const char *envvar, const char *subdir) |
54 | { |
55 | struct stat fileinfo; |
56 | const char *envpath = getenv(envvar); |
57 | |
58 | /* error if this environment variable doesn't return a good string */ |
59 | if (envpath == NULL || strlen(envpath) < 1) |
60 | return 1; |
61 | |
62 | /* error if path returned by the environemnt variable isn't a valid path to a directory */ |
63 | if (stat(envpath, &fileinfo) != 0 || !S_ISDIR(fileinfo.st_mode)) |
64 | return 2; |
65 | |
66 | /* append the given sub-directory to the path given by the environment variable */ |
67 | strcpy(destpath, envpath); |
68 | if (destpath[strlen(destpath)-1] != '/') |
69 | strcat(destpath, "/" ); |
70 | strcat(destpath, subdir); |
71 | |
72 | /* try to create the resulting directory tree, or return successfully if it already exists */ |
73 | if (osal_mkdirp(destpath, 0700) != 0) |
74 | { |
75 | DebugMessage(M64MSG_ERROR, "Couldn't create directory: %s" , destpath); |
76 | return 3; |
77 | } |
78 | |
79 | /* Success */ |
80 | return 0; |
81 | } |
82 | |
83 | static int search_dir_file(char *destpath, const char *path, const char *filename) |
84 | { |
85 | struct stat fileinfo; |
86 | |
87 | /* sanity check to start */ |
88 | if (destpath == NULL || path == NULL || filename == NULL) |
89 | return 1; |
90 | |
91 | /* build the full filepath */ |
92 | strcpy(destpath, path); |
93 | /* if the path is empty, don't add / between it and the file name */ |
94 | if (destpath[0] != '\0' && destpath[strlen(destpath)-1] != '/') |
95 | strcat(destpath, "/" ); |
96 | strcat(destpath, filename); |
97 | |
98 | /* test for a valid file */ |
99 | if (stat(destpath, &fileinfo) != 0) |
100 | return 2; |
101 | if (!S_ISREG(fileinfo.st_mode)) |
102 | return 3; |
103 | |
104 | /* success - file exists and is a regular file */ |
105 | return 0; |
106 | } |
107 | |
108 | /* global functions */ |
109 | |
110 | int osal_mkdirp(const char *dirpath, int mode) |
111 | { |
112 | char *mypath, *currpath; |
113 | struct stat fileinfo; |
114 | |
115 | // Terminate quickly if the path already exists |
116 | if (stat(dirpath, &fileinfo) == 0 && S_ISDIR(fileinfo.st_mode)) |
117 | return 0; |
118 | |
119 | // Create partial paths |
120 | mypath = currpath = strdup(dirpath); |
121 | if (mypath == NULL) |
122 | return 1; |
123 | |
124 | while ((currpath = strpbrk(currpath + 1, OSAL_DIR_SEPARATORS)) != NULL) |
125 | { |
126 | *currpath = '\0'; |
127 | if (stat(mypath, &fileinfo) != 0) |
128 | { |
129 | if (mkdir(mypath, mode) != 0) |
130 | break; |
131 | } |
132 | else |
133 | { |
134 | if (!S_ISDIR(fileinfo.st_mode)) |
135 | break; |
136 | } |
137 | *currpath = OSAL_DIR_SEPARATORS[0]; |
138 | } |
139 | free(mypath); |
140 | if (currpath != NULL) |
141 | return 1; |
142 | |
143 | // Create full path |
144 | if (stat(dirpath, &fileinfo) != 0 && mkdir(dirpath, mode) != 0) |
145 | return 1; |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | const char * osal_get_shared_filepath(const char *filename, const char *firstsearch, const char *secondsearch) |
151 | { |
152 | static char retpath[PATH_MAX]; |
153 | int i; |
154 | |
155 | /* if caller gave us any directories to search, then look there first */ |
156 | if (firstsearch != NULL && search_dir_file(retpath, firstsearch, filename) == 0) |
157 | return retpath; |
158 | if (secondsearch != NULL && search_dir_file(retpath, secondsearch, filename) == 0) |
159 | return retpath; |
160 | |
161 | /* otherwise check our standard paths */ |
162 | for (i = 0; i < datasearchdirs; i++) |
163 | { |
164 | if (search_dir_file(retpath, datasearchpath[i], filename) == 0) |
165 | return retpath; |
166 | } |
167 | |
168 | /* we couldn't find the file */ |
169 | return NULL; |
170 | } |
171 | |
172 | const char * osal_get_user_configpath(void) |
173 | { |
174 | static char retpath[PATH_MAX]; |
175 | int rval; |
176 | |
177 | /* first, try the XDG_CONFIG_HOME environment variable */ |
178 | rval = get_xdg_dir(retpath, "XDG_CONFIG_HOME" , "mupen64plus/" ); |
179 | if (rval == 0) |
180 | return retpath; |
181 | |
182 | /* then try the HOME environment variable */ |
183 | rval = get_xdg_dir(retpath, "HOME" , ".config/mupen64plus/" ); |
184 | if (rval == 0) |
185 | return retpath; |
186 | |
187 | /* otherwise we are in trouble */ |
188 | if (rval < 3) |
189 | DebugMessage(M64MSG_ERROR, "Failed to get configuration directory; $HOME is undefined or invalid." ); |
190 | return NULL; |
191 | } |
192 | |
193 | const char * osal_get_user_datapath(void) |
194 | { |
195 | static char retpath[PATH_MAX]; |
196 | int rval; |
197 | |
198 | /* first, try the XDG_DATA_HOME environment variable */ |
199 | rval = get_xdg_dir(retpath, "XDG_DATA_HOME" , "mupen64plus/" ); |
200 | if (rval == 0) |
201 | return retpath; |
202 | |
203 | /* then try the HOME environment variable */ |
204 | rval = get_xdg_dir(retpath, "HOME" , ".local/share/mupen64plus/" ); |
205 | if (rval == 0) |
206 | return retpath; |
207 | |
208 | /* otherwise we are in trouble */ |
209 | if (rval < 3) |
210 | DebugMessage(M64MSG_ERROR, "Failed to get data directory; $HOME is undefined or invalid." ); |
211 | return NULL; |
212 | } |
213 | |
214 | const char * osal_get_user_cachepath(void) |
215 | { |
216 | static char retpath[PATH_MAX]; |
217 | int rval; |
218 | |
219 | /* first, try the XDG_CACHE_HOME environment variable */ |
220 | rval = get_xdg_dir(retpath, "XDG_CACHE_HOME" , "mupen64plus/" ); |
221 | if (rval == 0) |
222 | return retpath; |
223 | |
224 | /* then try the HOME environment variable */ |
225 | rval = get_xdg_dir(retpath, "HOME" , ".cache/mupen64plus/" ); |
226 | if (rval == 0) |
227 | return retpath; |
228 | |
229 | /* otherwise we are in trouble */ |
230 | if (rval < 3) |
231 | DebugMessage(M64MSG_ERROR, "Failed to get cache directory; $HOME is undefined or invalid." ); |
232 | return NULL; |
233 | } |
234 | |
235 | |
236 | |