annotate src/ljpath.c @ 0:c84446dfb3f5

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