fileio.cpp

Go to the documentation of this file.
00001 /* $Id: fileio.cpp 14656 2008-12-05 18:02:04Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "fileio_func.h"
00008 #include "variables.h"
00009 #include "debug.h"
00010 #include "fios.h"
00011 #include "core/alloc_func.hpp"
00012 #include "core/math_func.hpp"
00013 #include "string_func.h"
00014 #include "tar_type.h"
00015 #ifdef WIN32
00016 #include <windows.h>
00017 #else
00018 #include <pwd.h>
00019 #include <unistd.h>
00020 #endif
00021 #include <sys/stat.h>
00022 
00023 /*************************************************/
00024 /* FILE IO ROUTINES ******************************/
00025 /*************************************************/
00026 
00027 #define FIO_BUFFER_SIZE 512
00028 
00029 struct Fio {
00030   byte *buffer, *buffer_end;             
00031   size_t pos;                            
00032   FILE *cur_fh;                          
00033   const char *filename;                  
00034   FILE *handles[MAX_FILE_SLOTS];         
00035   byte buffer_start[FIO_BUFFER_SIZE];    
00036   const char *filenames[MAX_FILE_SLOTS]; 
00037   char *shortnames[MAX_FILE_SLOTS];
00038 #if defined(LIMITED_FDS)
00039   uint open_handles;                     
00040   uint usage_count[MAX_FILE_SLOTS];      
00041 #endif /* LIMITED_FDS */
00042 };
00043 
00044 static Fio _fio;
00045 
00046 /* Get current position in file */
00047 size_t FioGetPos()
00048 {
00049   return _fio.pos + (_fio.buffer - _fio.buffer_end);
00050 }
00051 
00052 const char *FioGetFilename(uint8 slot)
00053 {
00054   return _fio.shortnames[slot];
00055 }
00056 
00057 void FioSeekTo(size_t pos, int mode)
00058 {
00059   if (mode == SEEK_CUR) pos += FioGetPos();
00060   _fio.buffer = _fio.buffer_end = _fio.buffer_start + FIO_BUFFER_SIZE;
00061   _fio.pos = pos;
00062   fseek(_fio.cur_fh, _fio.pos, SEEK_SET);
00063 }
00064 
00065 #if defined(LIMITED_FDS)
00066 static void FioRestoreFile(int slot)
00067 {
00068   /* Do we still have the file open, or should we reopen it? */
00069   if (_fio.handles[slot] == NULL) {
00070     DEBUG(misc, 6, "Restoring file '%s' in slot '%d' from disk", _fio.filenames[slot], slot);
00071     FioOpenFile(slot, _fio.filenames[slot]);
00072   }
00073   _fio.usage_count[slot]++;
00074 }
00075 #endif /* LIMITED_FDS */
00076 
00077 /* Seek to a file and a position */
00078 void FioSeekToFile(uint8 slot, size_t pos)
00079 {
00080   FILE *f;
00081 #if defined(LIMITED_FDS)
00082   /* Make sure we have this file open */
00083   FioRestoreFile(slot);
00084 #endif /* LIMITED_FDS */
00085   f = _fio.handles[slot];
00086   assert(f != NULL);
00087   _fio.cur_fh = f;
00088   _fio.filename = _fio.filenames[slot];
00089   FioSeekTo(pos, SEEK_SET);
00090 }
00091 
00092 byte FioReadByte()
00093 {
00094   if (_fio.buffer == _fio.buffer_end) {
00095     _fio.buffer = _fio.buffer_start;
00096     size_t size = fread(_fio.buffer, 1, FIO_BUFFER_SIZE, _fio.cur_fh);
00097     _fio.pos += size;
00098     _fio.buffer_end = _fio.buffer_start + size;
00099 
00100     if (size == 0) return 0;
00101   }
00102   return *_fio.buffer++;
00103 }
00104 
00105 void FioSkipBytes(int n)
00106 {
00107   for (;;) {
00108     int m = min(_fio.buffer_end - _fio.buffer, n);
00109     _fio.buffer += m;
00110     n -= m;
00111     if (n == 0) break;
00112     FioReadByte();
00113     n--;
00114   }
00115 }
00116 
00117 uint16 FioReadWord()
00118 {
00119   byte b = FioReadByte();
00120   return (FioReadByte() << 8) | b;
00121 }
00122 
00123 uint32 FioReadDword()
00124 {
00125   uint b = FioReadWord();
00126   return (FioReadWord() << 16) | b;
00127 }
00128 
00129 void FioReadBlock(void *ptr, size_t size)
00130 {
00131   FioSeekTo(FioGetPos(), SEEK_SET);
00132   _fio.pos += fread(ptr, 1, size, _fio.cur_fh);
00133 }
00134 
00135 static inline void FioCloseFile(int slot)
00136 {
00137   if (_fio.handles[slot] != NULL) {
00138     fclose(_fio.handles[slot]);
00139 
00140     free(_fio.shortnames[slot]);
00141     _fio.shortnames[slot] = NULL;
00142 
00143     _fio.handles[slot] = NULL;
00144 #if defined(LIMITED_FDS)
00145     _fio.open_handles--;
00146 #endif /* LIMITED_FDS */
00147   }
00148 }
00149 
00150 void FioCloseAll()
00151 {
00152   int i;
00153 
00154   for (i = 0; i != lengthof(_fio.handles); i++)
00155     FioCloseFile(i);
00156 }
00157 
00158 #if defined(LIMITED_FDS)
00159 static void FioFreeHandle()
00160 {
00161   /* If we are about to open a file that will exceed the limit, close a file */
00162   if (_fio.open_handles + 1 == LIMITED_FDS) {
00163     uint i, count;
00164     int slot;
00165 
00166     count = UINT_MAX;
00167     slot = -1;
00168     /* Find the file that is used the least */
00169     for (i = 0; i < lengthof(_fio.handles); i++) {
00170       if (_fio.handles[i] != NULL && _fio.usage_count[i] < count) {
00171         count = _fio.usage_count[i];
00172         slot  = i;
00173       }
00174     }
00175     assert(slot != -1);
00176     DEBUG(misc, 6, "Closing filehandler '%s' in slot '%d' because of fd-limit", _fio.filenames[slot], slot);
00177     FioCloseFile(slot);
00178   }
00179 }
00180 #endif /* LIMITED_FDS */
00181 
00182 void FioOpenFile(int slot, const char *filename)
00183 {
00184   FILE *f;
00185 
00186 #if defined(LIMITED_FDS)
00187   FioFreeHandle();
00188 #endif /* LIMITED_FDS */
00189   f = FioFOpenFile(filename);
00190   if (f == NULL) usererror("Cannot open file '%s'", filename);
00191   uint32 pos = ftell(f);
00192 
00193   FioCloseFile(slot); // if file was opened before, close it
00194   _fio.handles[slot] = f;
00195   _fio.filenames[slot] = filename;
00196 
00197   /* Store the filename without path and extension */
00198   const char *t = strrchr(filename, PATHSEPCHAR);
00199   _fio.shortnames[slot] = strdup(t == NULL ? filename : t);
00200   char *t2 = strrchr(_fio.shortnames[slot], '.');
00201   if (t2 != NULL) *t2 = '\0';
00202   strtolower(_fio.shortnames[slot]);
00203 
00204 #if defined(LIMITED_FDS)
00205   _fio.usage_count[slot] = 0;
00206   _fio.open_handles++;
00207 #endif /* LIMITED_FDS */
00208   FioSeekToFile(slot, pos);
00209 }
00210 
00211 const char *_subdirs[NUM_SUBDIRS] = {
00212   "",
00213   "save" PATHSEP,
00214   "save" PATHSEP "autosave" PATHSEP,
00215   "scenario" PATHSEP,
00216   "scenario" PATHSEP "heightmap" PATHSEP,
00217   "gm" PATHSEP,
00218   "data" PATHSEP,
00219   "lang" PATHSEP
00220 };
00221 
00222 const char *_searchpaths[NUM_SEARCHPATHS];
00223 TarList _tar_list;
00224 TarFileList _tar_filelist;
00225 
00226 typedef std::map<std::string, std::string> TarLinkList;
00227 static TarLinkList _tar_linklist; 
00228 
00235 bool FioCheckFileExists(const char *filename, Subdirectory subdir)
00236 {
00237   FILE *f = FioFOpenFile(filename, "rb", subdir);
00238   if (f == NULL) return false;
00239 
00240   FioFCloseFile(f);
00241   return true;
00242 }
00243 
00247 void FioFCloseFile(FILE *f)
00248 {
00249   fclose(f);
00250 }
00251 
00252 char *FioGetFullPath(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir, const char *filename)
00253 {
00254   assert(subdir < NUM_SUBDIRS);
00255   assert(sp < NUM_SEARCHPATHS);
00256 
00257   snprintf(buf, buflen, "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00258   return buf;
00259 }
00260 
00261 char *FioFindFullPath(char *buf, size_t buflen, Subdirectory subdir, const char *filename)
00262 {
00263   Searchpath sp;
00264   assert(subdir < NUM_SUBDIRS);
00265 
00266   FOR_ALL_SEARCHPATHS(sp) {
00267     FioGetFullPath(buf, buflen, sp, subdir, filename);
00268     if (FileExists(buf)) break;
00269   }
00270 
00271   return buf;
00272 }
00273 
00274 char *FioAppendDirectory(char *buf, size_t buflen, Searchpath sp, Subdirectory subdir)
00275 {
00276   assert(subdir < NUM_SUBDIRS);
00277   assert(sp < NUM_SEARCHPATHS);
00278 
00279   snprintf(buf, buflen, "%s%s", _searchpaths[sp], _subdirs[subdir]);
00280   return buf;
00281 }
00282 
00283 char *FioGetDirectory(char *buf, size_t buflen, Subdirectory subdir)
00284 {
00285   Searchpath sp;
00286 
00287   /* Find and return the first valid directory */
00288   FOR_ALL_SEARCHPATHS(sp) {
00289     char *ret = FioAppendDirectory(buf, buflen, sp, subdir);
00290     if (FileExists(buf)) return ret;
00291   }
00292 
00293   /* Could not find the directory, fall back to a base path */
00294   ttd_strlcpy(buf, _personal_dir, buflen);
00295 
00296   return buf;
00297 }
00298 
00299 FILE *FioFOpenFileSp(const char *filename, const char *mode, Searchpath sp, Subdirectory subdir, size_t *filesize)
00300 {
00301 #if defined(WIN32) && defined(UNICODE)
00302   /* fopen is implemented as a define with ellipses for
00303    * Unicode support (prepend an L). As we are not sending
00304    * a string, but a variable, it 'renames' the variable,
00305    * so make that variable to makes it compile happily */
00306   wchar_t Lmode[5];
00307   MultiByteToWideChar(CP_ACP, 0, mode, -1, Lmode, lengthof(Lmode));
00308 #endif
00309   FILE *f = NULL;
00310   char buf[MAX_PATH];
00311 
00312   if (subdir == NO_DIRECTORY) {
00313     strecpy(buf, filename, lastof(buf));
00314   } else {
00315     snprintf(buf, lengthof(buf), "%s%s%s", _searchpaths[sp], _subdirs[subdir], filename);
00316   }
00317 
00318 #if defined(WIN32)
00319   if (mode[0] == 'r' && GetFileAttributes(OTTD2FS(buf)) == INVALID_FILE_ATTRIBUTES) return NULL;
00320 #endif
00321 
00322   f = fopen(buf, mode);
00323 #if !defined(WIN32)
00324   if (f == NULL) {
00325     strtolower(buf + ((subdir == NO_DIRECTORY) ? 0 : strlen(_searchpaths[sp]) - 1));
00326     f = fopen(buf, mode);
00327   }
00328 #endif
00329   if (f != NULL && filesize != NULL) {
00330     /* Find the size of the file */
00331     fseek(f, 0, SEEK_END);
00332     *filesize = ftell(f);
00333     fseek(f, 0, SEEK_SET);
00334   }
00335   return f;
00336 }
00337 
00338 FILE *FioFOpenFileTar(TarFileListEntry *entry, size_t *filesize)
00339 {
00340   FILE *f = fopen(entry->tar_filename, "rb");
00341   assert(f != NULL);
00342 
00343   fseek(f, entry->position, SEEK_SET);
00344   if (filesize != NULL) *filesize = entry->size;
00345   return f;
00346 }
00347 
00349 FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
00350 {
00351   FILE *f = NULL;
00352   Searchpath sp;
00353 
00354   assert(subdir < NUM_SUBDIRS || subdir == NO_DIRECTORY);
00355 
00356   FOR_ALL_SEARCHPATHS(sp) {
00357     f = FioFOpenFileSp(filename, mode, sp, subdir, filesize);
00358     if (f != NULL || subdir == NO_DIRECTORY) break;
00359   }
00360 
00361   /* We can only use .tar in case of data-dir, and read-mode */
00362   if (f == NULL && subdir == DATA_DIR && mode[0] == 'r') {
00363     static const uint MAX_RESOLVED_LENGTH = 2 * (100 + 100 + 155) + 1; // Enough space to hold two filenames plus link. See 'TarHeader'.
00364     char resolved_name[MAX_RESOLVED_LENGTH];
00365 
00366     /* Filenames in tars are always forced to be lowercase */
00367     strcpy(resolved_name, filename);
00368     strtolower(resolved_name);
00369 
00370     size_t resolved_len = strlen(resolved_name);
00371 
00372     /* Resolve ONE directory link */
00373     for (TarLinkList::iterator link = _tar_linklist.begin(); link != _tar_linklist.end(); link++) {
00374       const std::string &src = link->first;
00375       size_t len = src.length();
00376       if (resolved_len >= len && resolved_name[len - 1] == PATHSEPCHAR && strncmp(src.c_str(), resolved_name, len) == 0) {
00377         /* Apply link */
00378         char resolved_name2[MAX_RESOLVED_LENGTH];
00379         const std::string &dest = link->second;
00380         strcpy(resolved_name2, &(resolved_name[len]));
00381         strcpy(resolved_name, dest.c_str());
00382         strcpy(&(resolved_name[dest.length()]), resolved_name2);
00383         break; // Only resolve one level
00384       }
00385     }
00386 
00387     TarFileList::iterator it = _tar_filelist.find(resolved_name);
00388     if (it != _tar_filelist.end()) {
00389       f = FioFOpenFileTar(&((*it).second), filesize);
00390     }
00391   }
00392 
00393   /* Sometimes a full path is given. To support
00394    * the 'subdirectory' must be 'removed'. */
00395   if (f == NULL && subdir != NO_DIRECTORY) {
00396     f = FioFOpenFile(filename, mode, NO_DIRECTORY, filesize);
00397   }
00398 
00399   return f;
00400 }
00401 
00406 void FioCreateDirectory(const char *name)
00407 {
00408 #if defined(WIN32) || defined(WINCE)
00409   CreateDirectory(OTTD2FS(name), NULL);
00410 #elif defined(OS2) && !defined(__INNOTEK_LIBC__)
00411   mkdir(OTTD2FS(name));
00412 #elif defined(__MORPHOS__) || defined(__AMIGAOS__)
00413   char buf[MAX_PATH];
00414   ttd_strlcpy(buf, name, MAX_PATH);
00415 
00416   size_t len = strlen(name) - 1;
00417   if (buf[len] == '/') {
00418     buf[len] = '\0'; // Kill pathsep, so mkdir() will not fail
00419   }
00420 
00421   mkdir(OTTD2FS(buf), 0755);
00422 #else
00423   mkdir(OTTD2FS(name), 0755);
00424 #endif
00425 }
00426 
00433 void AppendPathSeparator(char *buf, size_t buflen)
00434 {
00435   size_t s = strlen(buf);
00436 
00437   /* Length of string + path separator + '\0' */
00438   if (s != 0 && buf[s - 1] != PATHSEPCHAR && s + 2 < buflen) {
00439     buf[s]     = PATHSEPCHAR;
00440     buf[s + 1] = '\0';
00441   }
00442 }
00443 
00450 char *BuildWithFullPath(const char *dir)
00451 {
00452   char *dest = MallocT<char>(MAX_PATH);
00453   ttd_strlcpy(dest, dir, MAX_PATH);
00454 
00455   /* Check if absolute or relative path */
00456   const char *s = strchr(dest, PATHSEPCHAR);
00457 
00458   /* Add absolute path */
00459   if (s == NULL || dest != s) {
00460     if (getcwd(dest, MAX_PATH) == NULL) *dest = '\0';
00461     AppendPathSeparator(dest, MAX_PATH);
00462     ttd_strlcat(dest, dir, MAX_PATH);
00463   }
00464   AppendPathSeparator(dest, MAX_PATH);
00465 
00466   return dest;
00467 }
00468 
00474 static void SimplifyFileName(char *name)
00475 {
00476   /* Force lowercase */
00477   strtolower(name);
00478 
00479   /* Tar-files always have '/' path-seperator, but we want our PATHSEPCHAR */
00480 #if (PATHSEPCHAR != '/')
00481   for (char *n = name; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR;
00482 #endif
00483 }
00484 
00485 static bool TarListAddFile(const char *filename)
00486 {
00487   /* The TAR-header, repeated for every file */
00488   typedef struct TarHeader {
00489     char name[100];      
00490     char mode[8];
00491     char uid[8];
00492     char gid[8];
00493     char size[12];       
00494     char mtime[12];
00495     char chksum[8];
00496     char typeflag;
00497     char linkname[100];
00498     char magic[6];
00499     char version[2];
00500     char uname[32];
00501     char gname[32];
00502     char devmajor[8];
00503     char devminor[8];
00504     char prefix[155];    
00505 
00506     char unused[12];
00507   } TarHeader;
00508 
00509   /* Check if we already seen this file */
00510   TarList::iterator it = _tar_list.find(filename);
00511   if (it != _tar_list.end()) return false;
00512 
00513   FILE *f = fopen(filename, "rb");
00514   assert(f != NULL);
00515 
00516   const char *dupped_filename = strdup(filename);
00517   _tar_list[filename].filename = dupped_filename;
00518 
00519   TarLinkList links; 
00520 
00521   TarHeader th;
00522   char buf[sizeof(th.name) + 1], *end;
00523   char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
00524   char link[sizeof(th.linkname) + 1];
00525   char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1];
00526   size_t num = 0, pos = 0;
00527 
00528   /* Make a char of 512 empty bytes */
00529   char empty[512];
00530   memset(&empty[0], 0, sizeof(empty));
00531 
00532   for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it?
00533     size_t num_bytes_read = fread(&th, 1, 512, f);
00534     if (num_bytes_read != 512) break;
00535     pos += num_bytes_read;
00536 
00537     /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */
00538     if (strncmp(th.magic, "ustar", 5) != 0 && memcmp(&th.magic, &empty[0], 512 - offsetof(TarHeader, magic)) != 0) {
00539       /* If we have only zeros in the block, it can be an end-of-file indicator */
00540       if (memcmp(&th, &empty[0], 512) == 0) continue;
00541 
00542       DEBUG(misc, 0, "The file '%s' isn't a valid tar-file", filename);
00543       return false;
00544     }
00545 
00546     name[0] = '\0';
00547     size_t len = 0;
00548 
00549     /* The prefix contains the directory-name */
00550     if (th.prefix[0] != '\0') {
00551       memcpy(name, th.prefix, sizeof(th.prefix));
00552       name[sizeof(th.prefix)] = '\0';
00553       len = strlen(name);
00554       name[len] = PATHSEPCHAR;
00555       len++;
00556     }
00557 
00558     /* Copy the name of the file in a safe way at the end of 'name' */
00559     memcpy(&name[len], th.name, sizeof(th.name));
00560     name[len + sizeof(th.name)] = '\0';
00561 
00562     /* Calculate the size of the file.. for some strange reason this is stored as a string */
00563     memcpy(buf, th.size, sizeof(th.size));
00564     buf[sizeof(th.size)] = '\0';
00565     int skip = strtol(buf, &end, 8);
00566 
00567     switch (th.typeflag) {
00568       case '\0':
00569       case '0': { // regular file
00570         /* Ignore empty files */
00571         if (skip == 0) break;
00572 
00573         if (strlen(name) == 0) break;
00574 
00575         /* Store this entry in the list */
00576         TarFileListEntry entry;
00577         entry.tar_filename = dupped_filename;
00578         entry.size         = skip;
00579         entry.position     = pos;
00580 
00581         /* Convert to lowercase and our PATHSEPCHAR */
00582         SimplifyFileName(name);
00583 
00584         DEBUG(misc, 6, "Found file in tar: %s (%d bytes, %d offset)", name, skip, pos);
00585         if (_tar_filelist.insert(TarFileList::value_type(name, entry)).second) num++;
00586 
00587         break;
00588       }
00589 
00590       case '1': // hard links
00591       case '2': { // symbolic links
00592         /* Copy the destination of the link in a safe way at the end of 'linkname' */
00593         memcpy(link, th.linkname, sizeof(th.linkname));
00594         link[sizeof(th.linkname)] = '\0';
00595 
00596         if (strlen(name) == 0 || strlen(link) == 0) break;
00597 
00598         /* Convert to lowercase and our PATHSEPCHAR */
00599         SimplifyFileName(name);
00600         SimplifyFileName(link);
00601 
00602         /* Only allow relative links */
00603         if (link[0] == PATHSEPCHAR) {
00604           DEBUG(misc, 1, "Ignoring absolute link in tar: %s -> %s", name, link);
00605           break;
00606         }
00607 
00608         /* Process relative path.
00609          * Note: The destination of links must not contain any directory-links. */
00610         strcpy(dest, name);
00611         char *destpos = strrchr(dest, PATHSEPCHAR);
00612         if (destpos == NULL) destpos = dest;
00613         *destpos = '\0';
00614 
00615         char *pos = link;
00616         while (*pos != '\0') {
00617           char *next = strchr(link, PATHSEPCHAR);
00618           if (next == NULL) next = pos + strlen(pos);
00619 
00620           /* Skip '.' (current dir) */
00621           if (next != pos + 1 || pos[0] != '.') {
00622             if (next == pos + 2 && pos[0] == '.' && pos[1] == '.') {
00623               /* level up */
00624               if (dest[0] == '\0') {
00625                 DEBUG(misc, 1, "Ignoring link pointing outside of data directory: %s -> %s", name, link);
00626                 break;
00627               }
00628 
00629               /* Truncate 'dest' after last PATHSEPCHAR.
00630                * This assumes, that the truncated part is a real directory and not a link */
00631               destpos = strrchr(dest, PATHSEPCHAR);
00632               if (destpos == NULL) destpos = dest;
00633             } else {
00634               /* Append at end of 'dest' */
00635               if (destpos != dest) *(destpos++) = PATHSEPCHAR;
00636               strncpy(destpos, pos, next - pos); // Safe as we do '\0'-termination ourselves
00637               destpos += next - pos;
00638             }
00639             *destpos = '\0';
00640           }
00641 
00642           pos = next;
00643         }
00644 
00645         /* Store links in temporary list */
00646         DEBUG(misc, 6, "Found link in tar: %s -> %s", name, dest);
00647         links.insert(TarLinkList::value_type(name, dest));
00648 
00649         break;
00650       }
00651 
00652       default:
00653         /* Ignore other types */
00654         break;
00655     }
00656 
00657     /* Skip to the next block.. */
00658     skip = Align(skip, 512);
00659     fseek(f, skip, SEEK_CUR);
00660     pos += skip;
00661   }
00662 
00663   DEBUG(misc, 1, "Found tar '%s' with %d new files", filename, num);
00664   fclose(f);
00665 
00666   /* Resolve file links and store directory links.
00667    * We restrict usage of links to two cases:
00668    *  1) Links to directories:
00669    *      Both the source path and the destination path must NOT contain any further links.
00670    *      When resolving files at most one directory link is resolved.
00671    *  2) Links to files:
00672    *      The destination path must NOT contain any links.
00673    *      The source path may contain one directory link.
00674    */
00675   for (TarLinkList::iterator link = links.begin(); link != links.end(); link++) {
00676     const std::string &src = link->first;
00677     const std::string &dest = link->second;
00678 
00679     TarFileList::iterator dest_file = _tar_filelist.find(dest);
00680     if (dest_file != _tar_filelist.end()) {
00681       /* Link to file. Process the link like the destination file. */
00682       _tar_filelist.insert(TarFileList::value_type(src, dest_file->second));
00683     } else {
00684       /* Destination file not found. Assume 'link to directory' */
00685       /* Append PATHSEPCHAR to 'src' and 'dest' */
00686       const std::string src_path = src + PATHSEPCHAR;
00687       const std::string dst_path = (dest.length() == 0 ? "" : dest + PATHSEPCHAR);
00688       _tar_linklist.insert(TarLinkList::value_type(src_path, dst_path));
00689     }
00690   }
00691 
00692   return true;
00693 }
00694 
00695 static int ScanPathForTarFiles(const char *path, size_t basepath_length)
00696 {
00697   extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00698 
00699   uint num = 0;
00700   struct stat sb;
00701   struct dirent *dirent;
00702   DIR *dir;
00703 
00704   if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
00705 
00706   while ((dirent = readdir(dir)) != NULL) {
00707     const char *d_name = FS2OTTD(dirent->d_name);
00708     char filename[MAX_PATH];
00709 
00710     if (!FiosIsValidFile(path, dirent, &sb)) continue;
00711 
00712     snprintf(filename, lengthof(filename), "%s%s", path, d_name);
00713 
00714     if (S_ISDIR(sb.st_mode)) {
00715       /* Directory */
00716       if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
00717       AppendPathSeparator(filename, lengthof(filename));
00718       num += ScanPathForTarFiles(filename, basepath_length);
00719     } else if (S_ISREG(sb.st_mode)) {
00720       /* File */
00721       char *ext = strrchr(filename, '.');
00722 
00723       /* If no extension or extension isn't .tar, skip the file */
00724       if (ext == NULL) continue;
00725       if (strcasecmp(ext, ".tar") != 0) continue;
00726 
00727       if (TarListAddFile(filename)) num++;
00728     }
00729   }
00730 
00731   closedir(dir);
00732   return num;
00733 }
00734 
00735 void ScanForTarFiles()
00736 {
00737   Searchpath sp;
00738   char path[MAX_PATH];
00739   uint num = 0;
00740 
00741   DEBUG(misc, 1, "Scanning for tars");
00742   FOR_ALL_SEARCHPATHS(sp) {
00743     FioAppendDirectory(path, MAX_PATH, sp, DATA_DIR);
00744     num += ScanPathForTarFiles(path, strlen(path));
00745   }
00746   DEBUG(misc, 1, "Scan complete, found %d files", num);
00747 }
00748 
00749 #if defined(WIN32) || defined(WINCE)
00750 
00755 extern void DetermineBasePaths(const char *exe);
00756 #else /* defined(WIN32) || defined(WINCE) */
00757 
00765 void ChangeWorkingDirectory(const char *exe)
00766 {
00767 #ifdef WITH_COCOA
00768   char *app_bundle = strchr(exe, '.');
00769   while (app_bundle != NULL && strncasecmp(app_bundle, ".app", 4) != 0) app_bundle = strchr(&app_bundle[1], '.');
00770 
00771   if (app_bundle != NULL) app_bundle[0] = '\0';
00772 #endif /* WITH_COCOA */
00773   char *s = strrchr(exe, PATHSEPCHAR);
00774   if (s != NULL) {
00775     *s = '\0';
00776 #if defined(__DJGPP__)
00777     /* If we want to go to the root, we can't use cd C:, but we must use '/' */
00778     if (s[-1] == ':') chdir("/");
00779 #endif
00780     if (chdir(exe) != 0) DEBUG(misc, 0, "Directory with the binary does not exist?");
00781     *s = PATHSEPCHAR;
00782   }
00783 #ifdef WITH_COCOA
00784   if (app_bundle != NULL) app_bundle[0] = '.';
00785 #endif /* WITH_COCOA */
00786 }
00787 
00792 void DetermineBasePaths(const char *exe)
00793 {
00794   char tmp[MAX_PATH];
00795 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS) || !defined(WITH_PERSONAL_DIR)
00796   _searchpaths[SP_PERSONAL_DIR] = NULL;
00797 #else
00798   const char *homedir = getenv("HOME");
00799 
00800   if (homedir == NULL) {
00801     const struct passwd *pw = getpwuid(getuid());
00802     homedir = (pw == NULL) ? "" : pw->pw_dir;
00803   }
00804 
00805   snprintf(tmp, MAX_PATH, "%s" PATHSEP "%s", homedir, PERSONAL_DIR);
00806   AppendPathSeparator(tmp, MAX_PATH);
00807 
00808   _searchpaths[SP_PERSONAL_DIR] = strdup(tmp);
00809 #endif
00810 
00811 #if defined(WITH_SHARED_DIR)
00812   snprintf(tmp, MAX_PATH, "%s", SHARED_DIR);
00813   AppendPathSeparator(tmp, MAX_PATH);
00814   _searchpaths[SP_SHARED_DIR] = strdup(tmp);
00815 #else
00816   _searchpaths[SP_SHARED_DIR] = NULL;
00817 #endif
00818 
00819 #if defined(__MORPHOS__) || defined(__AMIGA__)
00820   _searchpaths[SP_WORKING_DIR] = NULL;
00821 #else
00822   if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00823   AppendPathSeparator(tmp, MAX_PATH);
00824   _searchpaths[SP_WORKING_DIR] = strdup(tmp);
00825 #endif
00826 
00827   /* Change the working directory to that one of the executable */
00828   ChangeWorkingDirectory((char*)exe);
00829   if (getcwd(tmp, MAX_PATH) == NULL) *tmp = '\0';
00830   AppendPathSeparator(tmp, MAX_PATH);
00831   _searchpaths[SP_BINARY_DIR] = strdup(tmp);
00832 
00833 #if defined(__MORPHOS__) || defined(__AMIGA__) || defined(DOS)
00834   _searchpaths[SP_INSTALLATION_DIR] = NULL;
00835 #else
00836   snprintf(tmp, MAX_PATH, "%s", GLOBAL_DATA_DIR);
00837   AppendPathSeparator(tmp, MAX_PATH);
00838   _searchpaths[SP_INSTALLATION_DIR] = strdup(tmp);
00839 #endif
00840 #ifdef WITH_COCOA
00841 extern void cocoaSetApplicationBundleDir();
00842   cocoaSetApplicationBundleDir();
00843 #else
00844   _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
00845 #endif
00846 
00847   ScanForTarFiles();
00848 }
00849 #endif /* defined(WIN32) || defined(WINCE) */
00850 
00851 char *_personal_dir;
00852 
00859 void DeterminePaths(const char *exe)
00860 {
00861   DetermineBasePaths(exe);
00862 
00863   Searchpath sp;
00864   FOR_ALL_SEARCHPATHS(sp) DEBUG(misc, 4, "%s added as search path", _searchpaths[sp]);
00865 
00866   if (_config_file != NULL) {
00867     _personal_dir = strdup(_config_file);
00868     char *end = strrchr(_personal_dir , PATHSEPCHAR);
00869     if (end == NULL) {
00870       _personal_dir[0] = '\0';
00871     } else {
00872       end[1] = '\0';
00873     }
00874   } else {
00875     char personal_dir[MAX_PATH];
00876     FioFindFullPath(personal_dir, lengthof(personal_dir), BASE_DIR, "openttd.cfg");
00877 
00878     if (FileExists(personal_dir)) {
00879       char *end = strrchr(personal_dir, PATHSEPCHAR);
00880       if (end != NULL) end[1] = '\0';
00881       _personal_dir = strdup(personal_dir);
00882       _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
00883     } else {
00884       static const Searchpath new_openttd_cfg_order[] = {
00885           SP_PERSONAL_DIR, SP_BINARY_DIR, SP_WORKING_DIR, SP_SHARED_DIR, SP_INSTALLATION_DIR
00886         };
00887 
00888       for (uint i = 0; i < lengthof(new_openttd_cfg_order); i++) {
00889         if (IsValidSearchPath(new_openttd_cfg_order[i])) {
00890           _personal_dir = strdup(_searchpaths[new_openttd_cfg_order[i]]);
00891           _config_file = str_fmt("%sopenttd.cfg", _personal_dir);
00892           break;
00893         }
00894       }
00895     }
00896   }
00897 
00898   DEBUG(misc, 3, "%s found as personal directory", _personal_dir);
00899 
00900   _highscore_file = str_fmt("%shs.dat", _personal_dir);
00901   _log_file = str_fmt("%sopenttd.log",  _personal_dir);
00902 
00903   char *save_dir     = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(SAVE_DIR));
00904   char *autosave_dir = str_fmt("%s%s", _personal_dir, FioGetSubdirectory(AUTOSAVE_DIR));
00905 
00906   /* Make the necessary folders */
00907 #if !defined(__MORPHOS__) && !defined(__AMIGA__) && defined(WITH_PERSONAL_DIR)
00908   FioCreateDirectory(_personal_dir);
00909 #endif
00910 
00911   FioCreateDirectory(save_dir);
00912   FioCreateDirectory(autosave_dir);
00913 
00914   free(save_dir);
00915   free(autosave_dir);
00916 }
00917 
00922 void SanitizeFilename(char *filename)
00923 {
00924   for (; *filename != '\0'; filename++) {
00925     switch (*filename) {
00926       /* The following characters are not allowed in filenames
00927        * on at least one of the supported operating systems: */
00928       case ':': case '\\': case '*': case '?': case '/':
00929       case '<': case '>': case '|': case '"':
00930         *filename = '_';
00931         break;
00932     }
00933   }
00934 }
00935 
00936 void *ReadFileToMem(const char *filename, size_t *lenp, size_t maxsize)
00937 {
00938   FILE *in;
00939   byte *mem;
00940   size_t len;
00941 
00942   in = fopen(filename, "rb");
00943   if (in == NULL) return NULL;
00944 
00945   fseek(in, 0, SEEK_END);
00946   len = ftell(in);
00947   fseek(in, 0, SEEK_SET);
00948   if (len > maxsize || (mem = MallocT<byte>(len + 1)) == NULL) {
00949     fclose(in);
00950     return NULL;
00951   }
00952   mem[len] = 0;
00953   if (fread(mem, len, 1, in) != 1) {
00954     fclose(in);
00955     free(mem);
00956     return NULL;
00957   }
00958   fclose(in);
00959 
00960   *lenp = len;
00961   return mem;
00962 }
00963 
00964 
00972 static uint ScanPath(FileScanner *fs, const char *extension, const char *path, size_t basepath_length)
00973 {
00974   extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
00975 
00976   uint num = 0;
00977   struct stat sb;
00978   struct dirent *dirent;
00979   DIR *dir;
00980 
00981   if (path == NULL || (dir = ttd_opendir(path)) == NULL) return 0;
00982 
00983   while ((dirent = readdir(dir)) != NULL) {
00984     const char *d_name = FS2OTTD(dirent->d_name);
00985     char filename[MAX_PATH];
00986 
00987     if (!FiosIsValidFile(path, dirent, &sb)) continue;
00988 
00989     snprintf(filename, lengthof(filename), "%s%s", path, d_name);
00990 
00991     if (S_ISDIR(sb.st_mode)) {
00992       /* Directory */
00993       if (strcmp(d_name, ".") == 0 || strcmp(d_name, "..") == 0) continue;
00994       AppendPathSeparator(filename, lengthof(filename));
00995       num += ScanPath(fs, extension, filename, basepath_length);
00996     } else if (S_ISREG(sb.st_mode)) {
00997       /* File */
00998       char *ext = strrchr(filename, '.');
00999 
01000       /* If no extension or extension isn't .grf, skip the file */
01001       if (ext == NULL) continue;
01002       if (strcasecmp(ext, extension) != 0) continue;
01003 
01004       if (fs->AddFile(filename, basepath_length)) num++;
01005     }
01006   }
01007 
01008   closedir(dir);
01009 
01010   return num;
01011 }
01012 
01018 static uint ScanTar(FileScanner *fs, const char *extension, TarFileList::iterator tar)
01019 {
01020   uint num = 0;
01021   const char *filename = (*tar).first.c_str();
01022   const char *ext = strrchr(filename, '.');
01023 
01024   /* If no extension or extension isn't .grf, skip the file */
01025   if (ext == NULL) return false;
01026   if (strcasecmp(ext, extension) != 0) return false;
01027 
01028   if (fs->AddFile(filename, 0)) num++;
01029 
01030   return num;
01031 }
01032 
01041 uint FileScanner::Scan(const char *extension, Subdirectory sd, bool tars)
01042 {
01043   Searchpath sp;
01044   char path[MAX_PATH];
01045   TarFileList::iterator tar;
01046   uint num = 0;
01047 
01048   FOR_ALL_SEARCHPATHS(sp) {
01049     FioAppendDirectory(path, MAX_PATH, sp, sd);
01050     num += ScanPath(this, extension, path, strlen(path));
01051   }
01052   FOR_ALL_TARS(tar) {
01053     num += ScanTar(this, extension, tar);
01054   }
01055 
01056   return num;
01057 }

Generated on Tue Jan 6 19:01:36 2009 for openttd by  doxygen 1.5.6