Mercurial > hg > index.fcgi > lj > lj046
comparison src/ljpath.c @ 0:c84446dfb3f5
initial add
author | paulo@localhost |
---|---|
date | Fri, 13 Mar 2009 00:39:12 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e9350bde14f1 |
---|---|
1 /* | |
2 ljpath - functions to support an application that can be | |
3 installed to either a read-write folder ("portable" config) | |
4 or to a read-only folder ("installed" config) | |
5 | |
6 Copyright 2008 Damian Yerrick | |
7 | |
8 Insert zlib license here. | |
9 | |
10 */ | |
11 | |
12 | |
13 #include <stdlib.h> | |
14 #include <string.h> | |
15 #include "ljpath.h" | |
16 #include <limits.h> // for PATH_MAX | |
17 #include <sys/stat.h> // for mkdir() | |
18 #include <errno.h> | |
19 | |
20 #ifdef WIN32 | |
21 #include <shlobj.h> | |
22 #include <direct.h> | |
23 #define USE_WINDOWS_APPDATA | |
24 #endif | |
25 | |
26 // on the DS | |
27 #ifdef ARM9 | |
28 #include <fat.h> // Chishm's libfat | |
29 static char hasFAT; | |
30 #include <unistd.h> // for MAXPATHLEN | |
31 #else | |
32 #define hasFAT 1 | |
33 #endif | |
34 | |
35 // http://forum.gbadev.org/viewtopic.php?p=147795#147795 | |
36 #if !defined(PATH_MAX) && defined(MAXPATHLEN) | |
37 #define PATH_MAX MAXPATHLEN | |
38 #endif | |
39 | |
40 static char readWriteFolder[PATH_MAX]; | |
41 static char skinFolder[PATH_MAX]; | |
42 static char readOnlyFolder[PATH_MAX]; | |
43 | |
44 | |
45 #ifdef WIN32 | |
46 static int ljmkdir(const char *path) { | |
47 //allegro_message("mkdir(\"%s\")\n", path); | |
48 return _mkdir(path); | |
49 } | |
50 #else | |
51 static int ljmkdir(const char *path) { | |
52 //allegro_message("mkdir(\"%s\")\n", path); | |
53 return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); | |
54 } | |
55 #endif | |
56 | |
57 /** | |
58 * Wrapper around mkdir() that doesn't error if the | |
59 * directory already exists. | |
60 */ | |
61 static int makeFolder(const char *path) { | |
62 if (ljmkdir(path) < 0) { | |
63 if (errno != EEXIST) { | |
64 return -1; | |
65 } | |
66 } | |
67 return 0; | |
68 } | |
69 | |
70 | |
71 | |
72 /** | |
73 * Computes the name of the folder containing filename. | |
74 * @param dst destination to receive the folder name, of size PATH_MAX | |
75 * @param filename name of a file in this folder | |
76 */ | |
77 static void ljpathSetFolder(char *dst, const char *filename) { | |
78 const char *lastPathSep = NULL; | |
79 | |
80 // I considered strrchr, but it would need two passes: | |
81 // one for '/' and one for the drive-letter ':' under Windows. | |
82 for (const char *here = filename; *here != 0; ++here) { | |
83 if (*here == '/' || *here == '\\' || *here == ':') { | |
84 lastPathSep = here; | |
85 } | |
86 } | |
87 if (lastPathSep == NULL || lastPathSep - filename > PATH_MAX - 1) { | |
88 strcpy(dst, "."); | |
89 } else { | |
90 memcpy(dst, filename, lastPathSep - filename); | |
91 dst[lastPathSep - filename] = 0; | |
92 } | |
93 } | |
94 | |
95 #if defined(USE_WINDOWS_APPDATA) | |
96 /** | |
97 * Retrieves the read/write path on a Windows system. | |
98 * SHGetFolderPath() is described in | |
99 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shgetfolderpath.asp | |
100 */ | |
101 static void ljpathSetRWFolder(void) { | |
102 static const char suffix1[] = "/Pin Eight"; | |
103 static const char suffix2[] = "/lockjaw"; | |
104 char path[MAX_PATH + sizeof(suffix1) + sizeof(suffix2)] = ""; | |
105 | |
106 int rVal = SHGetFolderPath( | |
107 NULL, // owner window used with obscure dial-up functionality | |
108 CSIDL_APPDATA, // type of folder to retrieve | |
109 NULL, // user ID when impersonating another user | |
110 0, // get the current path, not the default path | |
111 path | |
112 ); | |
113 if (rVal) { | |
114 return; | |
115 } | |
116 | |
117 strcat(path, suffix1); | |
118 ljmkdir(path); | |
119 strcat(path, suffix2); | |
120 ljmkdir(path); | |
121 if (strlen(path) >= PATH_MAX) { | |
122 return; | |
123 } | |
124 | |
125 strncpy(readWriteFolder, path, PATH_MAX - 1); | |
126 readWriteFolder[PATH_MAX - 1] = 0; | |
127 } | |
128 #else | |
129 /** | |
130 * Retrieves the read/write path on a UNIX system using the HOME | |
131 * environment variable. | |
132 */ | |
133 static void ljpathSetRWFolder(void) { | |
134 static const char suffix[] = "/.lockjaw"; | |
135 const char *value = getenv("HOME"); | |
136 if (!value) { | |
137 return; | |
138 } | |
139 | |
140 unsigned int len = strlen(value) + sizeof(suffix); | |
141 if (len > PATH_MAX) { | |
142 return; | |
143 } | |
144 | |
145 strcpy(readWriteFolder, value); | |
146 strcat(readWriteFolder, suffix); | |
147 ljmkdir(readWriteFolder); | |
148 //allegro_message("readWriteFolder is now \"%s\"\n", skinFolder); | |
149 } | |
150 #endif | |
151 | |
152 void ljpathSetSkinFolder(const char *filename) { | |
153 //allegro_message("ljpathSetFolder(skinFolder, \"%s\")\n", filename); | |
154 ljpathSetFolder(skinFolder, filename); | |
155 //allegro_message("skinFolder is now \"%s\"\n", skinFolder); | |
156 } | |
157 | |
158 static int ljpathJoin(char *restrict dst, | |
159 const char *restrict folder, | |
160 const char *restrict filename) { | |
161 size_t len = 0; | |
162 | |
163 //allegro_message("ljpathJoin(\"%s\", \"%s\")\n", folder, filename); | |
164 | |
165 // If the path is not absolute, copy the folder and the | |
166 // path separator into the destination string. | |
167 if (filename[0] != '\\' && filename[0] != '\\' | |
168 && filename[1] != ':') { | |
169 while (len < PATH_MAX - 1 && *folder != 0) { | |
170 dst[len++] = *folder++; | |
171 } | |
172 /* The path separator '/' is standard under Linux and Mac OS X. | |
173 It also works on MS-DOS and Windows API, notwithstanding | |
174 certain Microsoft command-line utilities. */ | |
175 dst[len++] = '/'; | |
176 while (len < PATH_MAX - 1 && *filename != 0) { | |
177 dst[len++] = *filename++; | |
178 } | |
179 } | |
180 | |
181 if (len < PATH_MAX - 1) { | |
182 dst[len] = 0; | |
183 //allegro_message("equals \"%s\"\n", dst); | |
184 return 1; | |
185 } | |
186 //allegro_message("failed\n"); | |
187 return 0; | |
188 } | |
189 | |
190 /** | |
191 * Determines whether a file exists and is readable. | |
192 * @param path the path of a file | |
193 * @return nonzero iff readable | |
194 */ | |
195 static int fexists(const char *path) { | |
196 struct stat st; | |
197 if (!hasFAT) { | |
198 return 0; | |
199 } | |
200 int rval = stat(path, &st); | |
201 //allegro_message("stat(\"%s\") returned %d\n", path, rval); | |
202 return rval == 0; | |
203 } | |
204 | |
205 int ljpathInit(const char *argv0) { | |
206 #ifdef ARM9 | |
207 hasFAT = fatInitDefault(); | |
208 if (hasFAT) { | |
209 if (makeFolder("/data") < 0 || makeFolder("/data/lockjaw") < 0) { | |
210 hasFAT = 0; | |
211 } | |
212 } | |
213 strcpy(readOnlyFolder, "/data/lockjaw"); | |
214 strcpy(readWriteFolder, "/data/lockjaw"); | |
215 strcpy(skinFolder, "/data/lockjaw"); | |
216 return hasFAT; | |
217 #else | |
218 char path[PATH_MAX]; | |
219 int installed; | |
220 | |
221 //allegro_message("argv[0] = \"%s\"", argv0); | |
222 ljpathSetFolder(readOnlyFolder, argv0); | |
223 strcpy(readWriteFolder, "."); | |
224 strcpy(skinFolder, "."); | |
225 | |
226 // determine whether "installed.ini" (an empty text file) | |
227 // exists | |
228 installed = ljpathJoin(path, readOnlyFolder, "installed.ini") >= 0 | |
229 && fexists(path); | |
230 if (installed) { | |
231 ljpathSetRWFolder(); | |
232 } | |
233 return installed; | |
234 #endif | |
235 } | |
236 | |
237 /** | |
238 * Finds a file name for reading. | |
239 * @param dst buffer for path, of size PATH_MAX | |
240 * @param src name of file | |
241 * @return 0 if not found, nonzero if found | |
242 */ | |
243 int ljpathFind_r(char *restrict dst, const char *restrict filename) { | |
244 if (!hasFAT) { | |
245 return 0; | |
246 } | |
247 if (ljpathJoin(dst, readWriteFolder, filename) >= 0 | |
248 && fexists(dst)) { | |
249 return 1; | |
250 } | |
251 if (ljpathJoin(dst, skinFolder, filename) >= 0 | |
252 && fexists(dst)) { | |
253 return 1; | |
254 } | |
255 if (ljpathJoin(dst, readOnlyFolder, filename) >= 0 | |
256 && fexists(dst)) { | |
257 return 1; | |
258 } | |
259 return 0; | |
260 } | |
261 | |
262 int ljpathFind_w(char *restrict dst, const char *restrict filename) { | |
263 return ljpathJoin(dst, readWriteFolder, filename); | |
264 } | |
265 | |
266 FILE *ljfopen(const char *restrict filename, const char *restrict mode) { | |
267 char path[PATH_MAX]; | |
268 FILE *fp; | |
269 | |
270 if (!hasFAT) { | |
271 return NULL; | |
272 } | |
273 | |
274 //allegro_message("ljfopen(\"%s\", \"%s\")\n", filename, mode); | |
275 if (mode[0] == 'r') { | |
276 | |
277 // For read-only files, search all three paths. | |
278 if (ljpathFind_r(path, filename) >= 0 | |
279 && (fp = fopen(path, mode)) != 0) { | |
280 //allegro_message("fopen(\"%s\", \"%s\") for reading\n", path, mode); | |
281 return fp; | |
282 } | |
283 } else { | |
284 | |
285 // For read/write files, check only the read/write path. | |
286 if (ljpathFind_w(path, filename) >= 0 | |
287 && (fp = fopen(path, mode)) != 0) { | |
288 //allegro_message("fopen(\"%s\", \"%s\") for writing\n", path, mode); | |
289 return fp; | |
290 } | |
291 } | |
292 return NULL; | |
293 } | |
294 |