Mercurial > hg > index.fcgi > lj > lj046
diff src/ljpath.c @ 0:c84446dfb3f5
initial add
author | paulo@localhost |
---|---|
date | Fri, 13 Mar 2009 00:39:12 -0700 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/ljpath.c Fri Mar 13 00:39:12 2009 -0700 1.3 @@ -0,0 +1,294 @@ 1.4 +/* 1.5 +ljpath - functions to support an application that can be 1.6 +installed to either a read-write folder ("portable" config) 1.7 +or to a read-only folder ("installed" config) 1.8 + 1.9 +Copyright 2008 Damian Yerrick 1.10 + 1.11 +Insert zlib license here. 1.12 + 1.13 +*/ 1.14 + 1.15 + 1.16 +#include <stdlib.h> 1.17 +#include <string.h> 1.18 +#include "ljpath.h" 1.19 +#include <limits.h> // for PATH_MAX 1.20 +#include <sys/stat.h> // for mkdir() 1.21 +#include <errno.h> 1.22 + 1.23 +#ifdef WIN32 1.24 +#include <shlobj.h> 1.25 +#include <direct.h> 1.26 +#define USE_WINDOWS_APPDATA 1.27 +#endif 1.28 + 1.29 +// on the DS 1.30 +#ifdef ARM9 1.31 +#include <fat.h> // Chishm's libfat 1.32 +static char hasFAT; 1.33 +#include <unistd.h> // for MAXPATHLEN 1.34 +#else 1.35 +#define hasFAT 1 1.36 +#endif 1.37 + 1.38 +// http://forum.gbadev.org/viewtopic.php?p=147795#147795 1.39 +#if !defined(PATH_MAX) && defined(MAXPATHLEN) 1.40 +#define PATH_MAX MAXPATHLEN 1.41 +#endif 1.42 + 1.43 +static char readWriteFolder[PATH_MAX]; 1.44 +static char skinFolder[PATH_MAX]; 1.45 +static char readOnlyFolder[PATH_MAX]; 1.46 + 1.47 + 1.48 +#ifdef WIN32 1.49 +static int ljmkdir(const char *path) { 1.50 + //allegro_message("mkdir(\"%s\")\n", path); 1.51 + return _mkdir(path); 1.52 +} 1.53 +#else 1.54 +static int ljmkdir(const char *path) { 1.55 + //allegro_message("mkdir(\"%s\")\n", path); 1.56 + return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 1.57 +} 1.58 +#endif 1.59 + 1.60 +/** 1.61 + * Wrapper around mkdir() that doesn't error if the 1.62 + * directory already exists. 1.63 + */ 1.64 +static int makeFolder(const char *path) { 1.65 + if (ljmkdir(path) < 0) { 1.66 + if (errno != EEXIST) { 1.67 + return -1; 1.68 + } 1.69 + } 1.70 + return 0; 1.71 +} 1.72 + 1.73 + 1.74 + 1.75 +/** 1.76 + * Computes the name of the folder containing filename. 1.77 + * @param dst destination to receive the folder name, of size PATH_MAX 1.78 + * @param filename name of a file in this folder 1.79 + */ 1.80 +static void ljpathSetFolder(char *dst, const char *filename) { 1.81 + const char *lastPathSep = NULL; 1.82 + 1.83 + // I considered strrchr, but it would need two passes: 1.84 + // one for '/' and one for the drive-letter ':' under Windows. 1.85 + for (const char *here = filename; *here != 0; ++here) { 1.86 + if (*here == '/' || *here == '\\' || *here == ':') { 1.87 + lastPathSep = here; 1.88 + } 1.89 + } 1.90 + if (lastPathSep == NULL || lastPathSep - filename > PATH_MAX - 1) { 1.91 + strcpy(dst, "."); 1.92 + } else { 1.93 + memcpy(dst, filename, lastPathSep - filename); 1.94 + dst[lastPathSep - filename] = 0; 1.95 + } 1.96 +} 1.97 + 1.98 +#if defined(USE_WINDOWS_APPDATA) 1.99 +/** 1.100 + * Retrieves the read/write path on a Windows system. 1.101 + * SHGetFolderPath() is described in 1.102 + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shgetfolderpath.asp 1.103 + */ 1.104 +static void ljpathSetRWFolder(void) { 1.105 + static const char suffix1[] = "/Pin Eight"; 1.106 + static const char suffix2[] = "/lockjaw"; 1.107 + char path[MAX_PATH + sizeof(suffix1) + sizeof(suffix2)] = ""; 1.108 + 1.109 + int rVal = SHGetFolderPath( 1.110 + NULL, // owner window used with obscure dial-up functionality 1.111 + CSIDL_APPDATA, // type of folder to retrieve 1.112 + NULL, // user ID when impersonating another user 1.113 + 0, // get the current path, not the default path 1.114 + path 1.115 + ); 1.116 + if (rVal) { 1.117 + return; 1.118 + } 1.119 + 1.120 + strcat(path, suffix1); 1.121 + ljmkdir(path); 1.122 + strcat(path, suffix2); 1.123 + ljmkdir(path); 1.124 + if (strlen(path) >= PATH_MAX) { 1.125 + return; 1.126 + } 1.127 + 1.128 + strncpy(readWriteFolder, path, PATH_MAX - 1); 1.129 + readWriteFolder[PATH_MAX - 1] = 0; 1.130 +} 1.131 +#else 1.132 +/** 1.133 + * Retrieves the read/write path on a UNIX system using the HOME 1.134 + * environment variable. 1.135 + */ 1.136 +static void ljpathSetRWFolder(void) { 1.137 + static const char suffix[] = "/.lockjaw"; 1.138 + const char *value = getenv("HOME"); 1.139 + if (!value) { 1.140 + return; 1.141 + } 1.142 + 1.143 + unsigned int len = strlen(value) + sizeof(suffix); 1.144 + if (len > PATH_MAX) { 1.145 + return; 1.146 + } 1.147 + 1.148 + strcpy(readWriteFolder, value); 1.149 + strcat(readWriteFolder, suffix); 1.150 + ljmkdir(readWriteFolder); 1.151 + //allegro_message("readWriteFolder is now \"%s\"\n", skinFolder); 1.152 +} 1.153 +#endif 1.154 + 1.155 +void ljpathSetSkinFolder(const char *filename) { 1.156 + //allegro_message("ljpathSetFolder(skinFolder, \"%s\")\n", filename); 1.157 + ljpathSetFolder(skinFolder, filename); 1.158 + //allegro_message("skinFolder is now \"%s\"\n", skinFolder); 1.159 +} 1.160 + 1.161 +static int ljpathJoin(char *restrict dst, 1.162 + const char *restrict folder, 1.163 + const char *restrict filename) { 1.164 + size_t len = 0; 1.165 + 1.166 + //allegro_message("ljpathJoin(\"%s\", \"%s\")\n", folder, filename); 1.167 + 1.168 + // If the path is not absolute, copy the folder and the 1.169 + // path separator into the destination string. 1.170 + if (filename[0] != '\\' && filename[0] != '\\' 1.171 + && filename[1] != ':') { 1.172 + while (len < PATH_MAX - 1 && *folder != 0) { 1.173 + dst[len++] = *folder++; 1.174 + } 1.175 + /* The path separator '/' is standard under Linux and Mac OS X. 1.176 + It also works on MS-DOS and Windows API, notwithstanding 1.177 + certain Microsoft command-line utilities. */ 1.178 + dst[len++] = '/'; 1.179 + while (len < PATH_MAX - 1 && *filename != 0) { 1.180 + dst[len++] = *filename++; 1.181 + } 1.182 + } 1.183 + 1.184 + if (len < PATH_MAX - 1) { 1.185 + dst[len] = 0; 1.186 + //allegro_message("equals \"%s\"\n", dst); 1.187 + return 1; 1.188 + } 1.189 + //allegro_message("failed\n"); 1.190 + return 0; 1.191 +} 1.192 + 1.193 +/** 1.194 + * Determines whether a file exists and is readable. 1.195 + * @param path the path of a file 1.196 + * @return nonzero iff readable 1.197 + */ 1.198 +static int fexists(const char *path) { 1.199 + struct stat st; 1.200 + if (!hasFAT) { 1.201 + return 0; 1.202 + } 1.203 + int rval = stat(path, &st); 1.204 + //allegro_message("stat(\"%s\") returned %d\n", path, rval); 1.205 + return rval == 0; 1.206 +} 1.207 + 1.208 +int ljpathInit(const char *argv0) { 1.209 +#ifdef ARM9 1.210 + hasFAT = fatInitDefault(); 1.211 + if (hasFAT) { 1.212 + if (makeFolder("/data") < 0 || makeFolder("/data/lockjaw") < 0) { 1.213 + hasFAT = 0; 1.214 + } 1.215 + } 1.216 + strcpy(readOnlyFolder, "/data/lockjaw"); 1.217 + strcpy(readWriteFolder, "/data/lockjaw"); 1.218 + strcpy(skinFolder, "/data/lockjaw"); 1.219 + return hasFAT; 1.220 +#else 1.221 + char path[PATH_MAX]; 1.222 + int installed; 1.223 + 1.224 + //allegro_message("argv[0] = \"%s\"", argv0); 1.225 + ljpathSetFolder(readOnlyFolder, argv0); 1.226 + strcpy(readWriteFolder, "."); 1.227 + strcpy(skinFolder, "."); 1.228 + 1.229 + // determine whether "installed.ini" (an empty text file) 1.230 + // exists 1.231 + installed = ljpathJoin(path, readOnlyFolder, "installed.ini") >= 0 1.232 + && fexists(path); 1.233 + if (installed) { 1.234 + ljpathSetRWFolder(); 1.235 + } 1.236 + return installed; 1.237 +#endif 1.238 +} 1.239 + 1.240 +/** 1.241 + * Finds a file name for reading. 1.242 + * @param dst buffer for path, of size PATH_MAX 1.243 + * @param src name of file 1.244 + * @return 0 if not found, nonzero if found 1.245 + */ 1.246 +int ljpathFind_r(char *restrict dst, const char *restrict filename) { 1.247 + if (!hasFAT) { 1.248 + return 0; 1.249 + } 1.250 + if (ljpathJoin(dst, readWriteFolder, filename) >= 0 1.251 + && fexists(dst)) { 1.252 + return 1; 1.253 + } 1.254 + if (ljpathJoin(dst, skinFolder, filename) >= 0 1.255 + && fexists(dst)) { 1.256 + return 1; 1.257 + } 1.258 + if (ljpathJoin(dst, readOnlyFolder, filename) >= 0 1.259 + && fexists(dst)) { 1.260 + return 1; 1.261 + } 1.262 + return 0; 1.263 +} 1.264 + 1.265 +int ljpathFind_w(char *restrict dst, const char *restrict filename) { 1.266 + return ljpathJoin(dst, readWriteFolder, filename); 1.267 +} 1.268 + 1.269 +FILE *ljfopen(const char *restrict filename, const char *restrict mode) { 1.270 + char path[PATH_MAX]; 1.271 + FILE *fp; 1.272 + 1.273 + if (!hasFAT) { 1.274 + return NULL; 1.275 + } 1.276 + 1.277 + //allegro_message("ljfopen(\"%s\", \"%s\")\n", filename, mode); 1.278 + if (mode[0] == 'r') { 1.279 + 1.280 + // For read-only files, search all three paths. 1.281 + if (ljpathFind_r(path, filename) >= 0 1.282 + && (fp = fopen(path, mode)) != 0) { 1.283 + //allegro_message("fopen(\"%s\", \"%s\") for reading\n", path, mode); 1.284 + return fp; 1.285 + } 1.286 + } else { 1.287 + 1.288 + // For read/write files, check only the read/write path. 1.289 + if (ljpathFind_w(path, filename) >= 0 1.290 + && (fp = fopen(path, mode)) != 0) { 1.291 + //allegro_message("fopen(\"%s\", \"%s\") for writing\n", path, mode); 1.292 + return fp; 1.293 + } 1.294 + } 1.295 + return NULL; 1.296 +} 1.297 +