fs.c

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2007 Krzysztof Kosciuszkiewicz
00003     Copyright (C) 2005 Michael K. McCarty & Fritz Bronner
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with this program; if not, write to the Free Software
00017     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 */
00019 /** \file fs.c
00020  * Implementation of filesystem access functions.
00021  * 
00022  */
00023 
00024 #include "fs.h"
00025 #include "options.h"
00026 #include "pace.h"
00027 #include "utils.h"
00028 #include "logging.h"
00029 #include <assert.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032 #include <sys/stat.h>
00033 #include <stdarg.h>
00034 #include <stdlib.h>
00035 #include <ctype.h>
00036 
00037 /** path separator setup */
00038 #ifndef PATHSEP
00039 # if CONFIG_WIN32
00040 #  define PATHSEP '\\'
00041 # else
00042 #  define PATHSEP '/'
00043 # endif
00044 #endif
00045 
00046 /** see how do we call mkdir */
00047 #if HAVE_MKDIR
00048 # if MKDIR_TAKES_ONE_ARG
00049    /* MinGW32 */
00050 #  define mkdir(a, b) mkdir(a)
00051 # endif
00052 #else
00053 # if HAVE__MKDIR
00054    /* plain Windows 32 */
00055 #  define mkdir(a, b) _mkdir(a)
00056 # else
00057 #  error "Don't know how to create a directory on this system."
00058 # endif
00059 #endif
00060 
00061 /** see how we get the length of a directory's name */
00062 #if HAVE_DIRENT_H
00063 # include <dirent.h>
00064 # define NAMLEN(dirent) strlen((dirent)->d_name)
00065 #else
00066 # define dirent direct
00067 # define NAMLEN(dirent) (dirent)->d_namlen
00068 # if HAVE_SYS_NDIR_H
00069 #  include <sys/ndir.h>
00070 # endif
00071 # if HAVE_SYS_DIR_H
00072 #  include <sys/dir.h>
00073 # endif
00074 # if HAVE_NDIR_H
00075 #  include <ndir.h>
00076 # endif
00077 #endif
00078 
00079 LOG_DEFAULT_CATEGORY(filesys);
00080 
00081 static DIR *save_dir;
00082 
00083 /** used internally to find and open files */
00084 typedef struct file {
00085     FILE *handle;   /**< standard filehandle */
00086     char *path;     /**< path to file */
00087 } file;
00088 
00089 /** 
00090  * gamedata & savedata access functions
00091  */
00092 
00093 void
00094 fix_pathsep(char *name)
00095 {
00096 #if 0
00097     for (; *name; ++name)
00098         if (*name == '/')
00099             *name = PATHSEP;
00100 #endif
00101 }
00102 
00103 static FILE*
00104 try_fopen(const char *fname, const char *mode)
00105 {
00106     FILE* fp = NULL;
00107     assert(fname);
00108     assert(mode);
00109     TRACE3("trying to open `%s' (mode %s)", fname, mode);
00110 
00111     fp = fopen(fname, mode);
00112 
00113     /** \todo ENOENT is POSIX, ANSI equivalent for file does not exist??? */
00114     if (!fp && errno != ENOENT)
00115     {
00116         int esave = errno;
00117         WARNING3("can't access file `%s': %s", fname, strerror(errno));
00118         errno = esave;
00119     }
00120 
00121     return fp;
00122 }
00123 
00124 /** try to open base/xxx/name for xxx = arg4 ... */
00125 static file
00126 s_open_helper(const char *base, const char *name, const char *mode, ...)
00127 {
00128     FILE *fh = NULL;
00129     file f = {NULL, NULL};
00130     int serrno;
00131     char *p = NULL;
00132     char *cooked = xmalloc(1024);
00133     size_t len = 1024, len2 = 0;
00134     size_t len_base = strlen(base), len_name = strlen(name);
00135     va_list ap;
00136 
00137     assert(base);
00138     assert(name);
00139     assert(mode);
00140 
00141     va_start(ap, mode);
00142     for (p = va_arg(ap, char *); p; p = va_arg(ap, char *))
00143     {
00144         char *s = NULL;
00145         int was_upper = 0;
00146         size_t len_p = strlen(p);
00147 
00148         len2 = len_base + len_name + len_p + 3;
00149         if (len2 > len)
00150             cooked = xrealloc(cooked, (len = len2));
00151 
00152         sprintf(cooked, "%s/%s/%s", base, p, name);
00153         fix_pathsep(cooked);
00154 
00155         fh = try_fopen(cooked, mode);
00156         if (fh)
00157             break;
00158 
00159         /* try lowercase version */
00160         for (s = cooked + len_base + len_p + 2; *s; ++s)
00161             if (isupper(*s))
00162             {
00163                 was_upper |= 1;
00164                 *s = tolower(*s);
00165             }
00166 
00167         if (was_upper)
00168             fh = try_fopen(cooked, mode);
00169         if (fh)
00170             break;
00171     }
00172     serrno = errno;
00173     va_end(ap);
00174     if (fh)
00175     {
00176         f.handle = fh;
00177         f.path = cooked;
00178     } else
00179         free(cooked);
00180     errno = serrno;
00181     return f;
00182 }
00183 
00184 /** tries to find a file and open it
00185  * 
00186  * The function knows about the relative 
00187  * position of certain filetypes. It retrieves 
00188  * the position savegamedir and gamedatadir from
00189  * options.
00190  * 
00191  * \param name Name of the file to open
00192  * \param mode mode to file should be opened in
00193  * \param type Type of the file eg. FT_SAVE, FT_DATA, ...
00194  * 
00195  * \return fileinformation including opened filehandle
00196  */
00197 static file
00198 try_find_file(const char *name, const char *mode, int type)
00199 {
00200     file f = {NULL, NULL};
00201     char *gd = options.dir_gamedata;
00202     char *sd = options.dir_savegame;
00203     char *where = "";
00204 
00205     DEBUG2("looking for file `%s'", name);
00206 
00207     /** \note allows write access only to savegame files */
00208     if (type != FT_SAVE)
00209     {
00210         if (strchr(mode, 'b'))
00211             mode = "rb";
00212         else
00213             mode = "r";
00214     }
00215 
00216     switch (type)
00217     {
00218         case FT_DATA:
00219             f = s_open_helper(gd, name, mode,
00220                     "gamedata",
00221                     NULL);
00222             where = "game data";
00223             break;
00224         case FT_SAVE:
00225             f = s_open_helper(sd, name, mode,
00226                     ".",
00227                     NULL);
00228             where = "savegame";
00229             break;
00230         case FT_AUDIO:
00231             f = s_open_helper(gd, name, mode,
00232                     "audio/mission",
00233                     "audio/music",
00234                     "audio/news",
00235                     "audio/sounds",
00236                     NULL);
00237             where = "audio";
00238             break;
00239         case FT_VIDEO:
00240             f = s_open_helper(gd, name, mode,
00241                     "video/mission",
00242                     "video/news",
00243                     "video/training",
00244                     NULL);
00245             where = "video";
00246             break;
00247         case FT_IMAGE:
00248             f = s_open_helper(gd, name, mode,
00249                     "images",
00250                     NULL);
00251             where = "image";
00252             break;
00253         case FT_MIDI:
00254             f = s_open_helper(gd, name, mode,
00255                     "audio/midi",
00256                     "midi",
00257                     "audio/music",
00258                     NULL);
00259             where = "midi";
00260             break;
00261         default:
00262             assert("Unknown FT_* specified");
00263     }
00264 
00265     if (f.handle == NULL)
00266     {
00267         int serrno = errno;
00268         WARNING3("can't find file `%s' in %s dir(s)", name, where);
00269         errno = serrno;
00270     }
00271     return f;
00272 }
00273 
00274 FILE*
00275 sOpen(const char *name, const char *mode, int type)
00276 {
00277     file f = try_find_file(name, mode, type);
00278     if (f.path)
00279     {
00280         INFO3("opened file `%s' (mode %s)", f.path, mode);
00281         free(f.path);
00282     }
00283     return f.handle;
00284 }
00285 
00286 /** Find and open file, if found return full path.
00287  * Caller is responsible for freeing the memory.
00288  */
00289 char*
00290 locate_file(const char *name, int type)
00291 {
00292     file f = try_find_file(name, "rb", type);
00293     if (f.handle)
00294     {
00295         INFO2("found file `%s'", f.path);
00296         fclose(f.handle);
00297     }
00298     return f.path;
00299 }
00300 
00301 int
00302 remove_savedat(const char *name)
00303 {
00304     size_t len_base = strlen(options.dir_savegame) + 1;
00305     size_t len_name = strlen(name) + 1;
00306     char *cooked = xmalloc(len_base + len_name);
00307     int rv = 0;
00308 
00309     sprintf(cooked, "%s/%s", options.dir_savegame, name);
00310     INFO2("removing save game file `%s'", cooked);
00311     fix_pathsep(cooked);
00312     rv = remove(cooked);
00313     if (rv < 0 && errno != ENOENT)
00314         WARNING3("failed to remove save game file `%s': %s",
00315                 cooked, strerror(errno));
00316     free(cooked);
00317     return rv;
00318 }
00319 
00320 FILE *
00321 open_gamedat(const char *name)
00322 {
00323     return sOpen(name, "rb", FT_DATA);
00324 }
00325 
00326 FILE *
00327 open_savedat(const char *name, const char *mode)
00328 {
00329     return sOpen(name, mode, FT_SAVE);
00330 }
00331 
00332 char *
00333 slurp_gamedat(const char *name)
00334 {
00335     FILE *f;
00336     ssize_t len;
00337     char *p = NULL;
00338     size_t buflen = 0;
00339 
00340     f = open_gamedat(name);
00341     if (!f)
00342         return NULL;
00343 
00344     len = fread_dyn(&p, &buflen, f);
00345 
00346     if (len < 0)
00347     {
00348         CRITICAL2("could not read file `%s'", name);
00349         exit(EXIT_FAILURE);
00350     }
00351 
00352     fclose(f);
00353 
00354     return p;
00355 }
00356 
00357 /** Create the savegame directory
00358  * 
00359  * The directory will be created as defined in options.dir_savegame.
00360  * 
00361  * \note The access will be set to 0777 (worldwritable)
00362  * 
00363  * \return -1 on error
00364  * \return 0 on success
00365  */
00366 int
00367 create_save_dir(void)
00368 {
00369     if (mkdir(options.dir_savegame, 0777) < 0 && errno != EEXIST) {
00370         WARNING3("can't create savegame directory `%s': %s",
00371                 options.dir_savegame, strerror(errno));
00372         return -1;
00373     }
00374     return 0;
00375 }
00376 
00377 int
00378 first_saved_game(struct ffblk *ffblk)
00379 {
00380     if (save_dir)
00381     {
00382         closedir(save_dir);
00383         save_dir = NULL;
00384     }
00385 
00386     if ((save_dir = opendir(options.dir_savegame)) == NULL)
00387         return (1);
00388 
00389     return (next_saved_game(ffblk));
00390 }
00391 
00392 int
00393 next_saved_game(struct ffblk *ffblk)
00394 {
00395     struct dirent *dp;
00396     int len;
00397 
00398     memset(ffblk, 0, sizeof *ffblk);
00399 
00400     if (save_dir == NULL)
00401         return (1);
00402 
00403     while ((dp = readdir(save_dir)) != NULL)
00404     {
00405         len = NAMLEN(dp);
00406         if (len < 4)
00407             continue;
00408         if (xstrncasecmp(dp->d_name + len - 4, ".SAV", 4) != 0)
00409             continue;
00410 
00411         strncpy(ffblk->ff_name, dp->d_name, sizeof ffblk->ff_name);
00412         ffblk->ff_name[sizeof ffblk->ff_name - 1] = 0;
00413 
00414         return (0);
00415     }
00416 
00417     return (1);
00418 }
00419 
00420 /* vim: set noet ts=4 sw=4 tw=77: */

Generated on Fri Sep 28 00:35:45 2007 for raceintospace by  doxygen 1.5.3